quakec/source/server/entities/triggers.qc
2024-01-07 18:24:48 -05:00

404 lines
No EOL
8.7 KiB
C++

/*
server/entities/triggers.qc
Misc. Trigger functions
Copyright (C) 2021-2024 NZ:P Team
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
*/
#define SPAWNFLAG_NOMESSAGE 1
#define SPAWNFLAG_NOTOUCH 1
void() Zombie_ReassignSpawnIDs;
// the wait time has passed, so set back up for another activation
void() multi_wait =
{
if (self.max_health)
{
self.health = self.max_health;
self.takedamage = DAMAGE_YES;
self.solid = SOLID_BBOX;
}
};
// the trigger was just touched/killed/used
// self.enemy should be set to the activator so it can be held through a delay
// so wait for the delay time before firing
void() multi_trigger =
{
if (self.nextthink > time)
{
return; // already been triggered
}
// don't trigger again until reset
self.takedamage = DAMAGE_NO;
activator = self.enemy;
SUB_UseTargets();
if (self.wait > 0)
{
self.think = multi_wait;
self.nextthink = time + self.wait;
}
else
{
Ent_FakeRemove(self);
}
};
void() multi_killed =
{
// motolegacy - FIXME
//self.enemy = damage_attacker;
multi_trigger();
};
void() multi_use =
{
self.enemy = activator;
multi_trigger();
};
void() multi_touch =
{
if (other.classname != "player")
return;
// if the trigger has an angles field, check player's facing direction
if (self.movedir != '0 0 0')
{
makevectors (other.angles);
if (v_forward * self.movedir < 0)
return; // not facing the right way
}
self.enemy = other;
multi_trigger();
};
void() SetMovedir =
{
if (self.angles == '0 -1 0')
self.movedir = '0 0 1';
else if (self.angles == '0 -2 0')
self.movedir = '0 0 -1';
else
{
makevectors (self.angles);
self.movedir = v_forward;
}
self.angles = '0 0 0';
};
void() InitTrigger =
{
// trigger angles are used for one-way touches. An angle of 0 is assumed
// to mean no restrictions, so use a yaw of 360 instead.
if (self.angles != '0 0 0')
SetMovedir ();
self.solid = SOLID_TRIGGER;
setmodel (self, self.model); // set size and link into world
self.movetype = MOVETYPE_NONE;
self.modelindex = 0;
self.model = "";
};
entity last_act_trigger;
void() trigger_activator_touch =
{
other.cost = other.cost +1; //hack, we can only touch one of thease at the time
if (other.classname != "player" || last_act_trigger == self || other.cost > 1)
return;
last_act_trigger = self;
entity t;
float tempcount, temptotal,breakthis;
string tempstring;
temptotal = 0;
breakthis = 0;
tempcount = 1;
tempstring = "";
t = world;
do
{
t = find (t, classname, "spawn_zombie");
if (!t)
{
breakthis = 1;
}
if (t.classname == "spawn_zombie")
{
t.classname = "spawn_zombie_away";
/*if (cvar("developer"))
setmodel(t, "progs/player.mdl");*/
}
} while (!breakthis);
if (self.target2)
tempcount =+ 1;
if (self.target3)
tempcount =+ 1;
if (self.target4)
tempcount =+ 1;
if (self.target5)
tempcount =+ 1;
if (self.target6)
tempcount =+ 1;
if (self.target7)
tempcount =+ 1;
if (self.target8)
tempcount =+ 1;
if (self.target2)
tempcount = tempcount + 1;
if (self.target3)
tempcount = tempcount + 1;
if (self.target4)
tempcount = tempcount + 1;
if (self.target5)
tempcount = tempcount + 1;
if (self.target6)
tempcount = tempcount + 1;
if (self.target7)
tempcount = tempcount + 1;
if (self.target8)
tempcount = tempcount + 1;
while(tempcount > temptotal)
{
temptotal = temptotal + 1;
if (temptotal == 1)
tempstring = self.target;
if (temptotal == 2)
tempstring = self.target2;
if (temptotal == 3)
tempstring = self.target3;
if (temptotal == 4)
tempstring = self.target4;
if (temptotal == 5)
tempstring = self.target5;
if (temptotal == 6)
tempstring = self.target6;
if (temptotal == 7)
tempstring = self.target7;
if (temptotal == 8)
tempstring = self.target8;
if (tempstring)
{
t = find (world, targetname, tempstring);
breakthis = 0;
while (!breakthis)
{
if (!t)
{
breakthis = true;
}
if (t.classname == "spawn_zombie_away")
{
t.classname = "spawn_zombie";
t.spawn_id = zombie_spawn_points;
zombie_spawn_points++;
/*if (cvar("developer"))
setmodel(t, "progs/ai/zfull.mdl");*/
}
t = find (t, targetname, tempstring);
}
}
}
Zombie_ReassignSpawnIDs();
}
void() trigger_activator =
{
InitTrigger ();
self.touch = trigger_activator_touch;
}
void() trigger_interact_touch =
{
if (other.classname != "player" || other.downed || other.isBuying == true || !PlayerIsLooking(other, self))
return;
if (other.button7) {
if (self.noise)
sound (other, CHAN_ITEM, self.noise, 1, ATTN_NORM);
multi_trigger();
}
}
void() trigger_interact =
{
InitTrigger();
self.touch = trigger_interact_touch;
}
/* ===================
Custom Teddy Triggers
===================*/
void() teddy_react =
{
local entity t;
if (self.spawnflags & 1) {
t = find (world, teddyremovetarget, self.target);
if (t)
Ent_FakeRemove(t);
} else if (self.target) {
SUB_UseTargets();
}
entity dummy = spawn();
setorigin(dummy, self.origin);
dummy.think = SUB_Remove;
dummy.nextthink = time + 10;
if (self.noise)
sound (dummy, CHAN_ITEM, self.noise, 1, ATTN_NORM);
Ent_FakeRemove(self);
}
/*
Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time.
If "delay" is set, the trigger waits some time after activating before firing.
"wait" : Seconds between triggerings. (.2 default)
If notouch is set, the trigger is only fired by other entities, not by touching.
NOTOUCH has been obsoleted by trigger_relay!
set "message" to text string
*/
void() trigger_multiple =
{
if (!self.wait)
self.wait = 0.2;
self.use = multi_use;
InitTrigger ();
if(self.health)
{
if (self.spawnflags & SPAWNFLAG_NOTOUCH)
objerror("health and notouch don't make sense\n");
self.max_health = self.health;
self.th_die = multi_killed;
self.takedamage = DAMAGE_YES;
self.solid = SOLID_BBOX;
setorigin(self, self.origin); // make sure it links into the world
}
else
{
if (!(self.spawnflags & SPAWNFLAG_NOTOUCH))
{
self.touch = multi_touch;
}
}
};
/*
Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching
"targetname". If "health" is set, the trigger must be killed to activate.
If notouch is set, the trigger is only fired by other entities, not by touching.
if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0.
set "message" to text string
*/
void() trigger_once =
{
self.wait = -1;
trigger_multiple();
}
void() trigger_atroundend =
{
self.classname = "trigger_atroundend";
//self.use = multi_use;
}
#define SPAWNFLAG_TRIGGERFIRE_PLAYER 1
#define SPAWNFLAG_TRIGGERFIRE_NODAMAGE 2
#define SPAWNFLAG_TRIGGERFIRE_HEALTHNERF 4
void() trigger_setfire_touch =
{
if (other.aistatus != "1" && (other.classname == "player" && !(self.spawnflags & SPAWNFLAG_TRIGGERFIRE_PLAYER)))
return;
other.onfire = true;
other.fire_timeout = time + self.fire_timeout;
if ((self.spawnflags & SPAWNFLAG_TRIGGERFIRE_NODAMAGE) && other.aistatus == "1")
other.ltime = time + 10000;
}
void() trigger_setfire =
{
InitTrigger ();
self.touch = trigger_setfire_touch;
}
//
// Quake Triggers
//
// NZ:P START -- Spawnflags for not hurting clients and not hurting AI
#define HURT_SPAWNFLAG_NOAI 1
#define HURT_SPAWNFLAG_NOCLIENT 2
// NZ:P END
void() hurt_on =
{
self.solid = SOLID_TRIGGER;
self.nextthink = -1;
};
void() hurt_touch =
{
if ((other.classname == "player" && !(self.spawnflags & HURT_SPAWNFLAG_NOCLIENT)) ||
(other.aistatus == "1" && !(self.spawnflags & HURT_SPAWNFLAG_NOAI)))
{
self.solid = SOLID_NOT;
DamageHandler(other, self, self.dmg, S_ZOMBIE);
self.think = hurt_on;
self.nextthink = time + 1;
if (self.message)
centerprint(other, strcat(self.message, "\n"));
}
return;
};
void() trigger_hurt =
{
InitTrigger ();
self.touch = hurt_touch;
if (!self.dmg)
self.dmg = 5;
};