/* server/entities/triggers.qc Misc. Trigger functions Copyright (C) 2021 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; } #define SPAWNFLAG_TRIGGERCMD_NOPLAYER 1 #define SPAWNFLAG_TRIGGERCMD_NOSPEC 2 void() trigger_stuffcmd_touch = { // First, lets make sure this is a client if (other.classname != "player" && other.classname != "spectator") return; // Don't interact with players if ((self.spawnflags & SPAWNFLAG_TRIGGERCMD_NOPLAYER) && other.classname == "player") return; // Don't interact with spectators if ((self.spawnflags & SPAWNFLAG_TRIGGERCMD_NOSPEC) && other.classname == "spectator") return; // Run the command. stuffcmd(other, strcat(self.message, "\n")); } void() trigger_stuffcmd = { InitTrigger(); self.touch = trigger_stuffcmd_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; } // // 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; };