--- /dev/null
+#include "multi.qh"
+// NOTE: also contains trigger_once at bottom
+
+#ifdef SVQC
+// the wait time has passed, so set back up for another activation
+void multi_wait(entity this)
+{
+ if (this.max_health)
+ {
+ this.health = this.max_health;
+ this.takedamage = DAMAGE_YES;
+ this.solid = SOLID_BBOX;
+ }
+}
+
+
+// the trigger was just touched/killed/used
+// this.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(entity this)
+{
+ if (this.nextthink > time)
+ {
+ return; // allready been triggered
+ }
+
+ if(this.spawnflags & ONLY_PLAYERS && !IS_PLAYER(this.enemy))
+ {
+ return; // only players
+ }
+
+ // TODO: restructure this so that trigger_secret is more independent
+ if (this.classname == "trigger_secret")
+ {
+ if (!IS_PLAYER(this.enemy))
+ return;
+ found_secrets = found_secrets + 1;
+ WriteByte (MSG_ALL, SVC_FOUNDSECRET);
+ }
+
+ if (this.noise)
+ {
+ _sound (this.enemy, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
+ }
+
+ // don't trigger again until reset
+ this.takedamage = DAMAGE_NO;
+
+ SUB_UseTargets(this, this.enemy, this.goalentity);
+
+ if (this.wait > 0)
+ {
+ setthink(this, multi_wait);
+ this.nextthink = time + this.wait;
+ }
+ else if (this.wait == 0)
+ {
+ multi_wait(this); // waiting finished
+ }
+ else
+ { // we can't just delete(this) here, because this is a touch function
+ // called while C code is looping through area links...
+ settouch(this, func_null);
+ }
+}
+
+void multi_use(entity this, entity actor, entity trigger)
+{
+ this.goalentity = trigger;
+ this.enemy = actor;
+ multi_trigger(this);
+}
+
+void multi_touch(entity this, entity toucher)
+{
+ if(!(this.spawnflags & ALL_ENTITIES) && !toucher.iscreature)
+ {
+ return;
+ }
+
+ if(this.team)
+ {
+ if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team))
+ {
+ return;
+ }
+ }
+
+ // if the trigger has an angles field, check player's facing direction
+ if (this.movedir != '0 0 0')
+ {
+ makevectors (toucher.angles);
+ if (v_forward * this.movedir < 0)
+ return; // not facing the right way
+ }
+
+ // if the trigger has pressed keys, check that the player is pressing those keys
+ if(this.pressedkeys && IS_PLAYER(toucher)) // only for players
+ {
+ if(!(CS(toucher).pressedkeys & this.pressedkeys))
+ {
+ return;
+ }
+ }
+
+ EXACTTRIGGER_TOUCH(this, toucher);
+
+ this.enemy = toucher;
+ this.goalentity = toucher;
+ multi_trigger(this);
+}
+
+void multi_eventdamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
+{
+ if(!this.takedamage)
+ return;
+ if(this.spawnflags & NOSPLASH)
+ if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+ return;
+ if(this.team)
+ if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != attacker.team))
+ return;
+ this.health = this.health - damage;
+ if (this.health <= 0)
+ {
+ this.enemy = attacker;
+ this.goalentity = inflictor;
+ multi_trigger(this);
+ }
+}
+
+void multi_reset(entity this)
+{
+ if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) )
+ settouch(this, multi_touch);
+ if (this.max_health)
+ {
+ this.health = this.max_health;
+ this.takedamage = DAMAGE_YES;
+ this.solid = SOLID_BBOX;
+ }
+ setthink(this, func_null);
+ this.nextthink = 0;
+ this.team = this.team_saved;
+}
+
+/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
+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 spawnfunc_trigger_relay!
+sounds
+1) secret
+2) beep beep
+3) large switch
+4)
+set "message" to text string
+*/
+spawnfunc(trigger_multiple)
+{
+ this.reset = multi_reset;
+ if (this.sounds == 1)
+ this.noise = "misc/secret.wav";
+ else if (this.sounds == 2)
+ this.noise = strzone(SND(TALK));
+ else if (this.sounds == 3)
+ this.noise = "misc/trigger1.wav";
+
+ if(this.noise)
+ precache_sound(this.noise);
+
+ if (!this.wait)
+ this.wait = 0.2;
+ else if(this.wait < -1)
+ this.wait = 0;
+ this.use = multi_use;
+
+ EXACTTRIGGER_INIT;
+
+ this.team_saved = this.team;
+ IL_PUSH(g_saved_team, this);
+
+ if (this.health)
+ {
+ if (this.spawnflags & SPAWNFLAG_NOTOUCH)
+ objerror (this, "health and notouch don't make sense\n");
+ this.canteamdamage = true;
+ this.max_health = this.health;
+ this.event_damage = multi_eventdamage;
+ this.takedamage = DAMAGE_YES;
+ this.solid = SOLID_BBOX;
+ setorigin(this, this.origin); // make sure it links into the world
+ }
+ else
+ {
+ if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) )
+ {
+ settouch(this, multi_touch);
+ setorigin(this, this.origin); // make sure it links into the world
+ }
+ }
+}
+
+
+/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
+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.
+sounds
+1) secret
+2) beep beep
+3) large switch
+4)
+set "message" to text string
+*/
+spawnfunc(trigger_once)
+{
+ this.wait = -1;
+ spawnfunc_trigger_multiple(this);
+}
+#endif