Properly support team field on trigger_multiple
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / triggers / trigger / multi.qc
1 #include "multi.qh"
2 // NOTE: also contains trigger_once at bottom
3
4 #ifdef SVQC
5 // the wait time has passed, so set back up for another activation
6 void multi_wait(entity this)
7 {
8         if (this.max_health)
9         {
10                 this.health = this.max_health;
11                 this.takedamage = DAMAGE_YES;
12                 this.solid = SOLID_BBOX;
13         }
14 }
15
16
17 // the trigger was just touched/killed/used
18 // this.enemy should be set to the activator so it can be held through a delay
19 // so wait for the delay time before firing
20 void multi_trigger(entity this)
21 {
22         if (this.nextthink > time)
23         {
24                 return;         // allready been triggered
25         }
26
27         if(this.spawnflags & 16384)
28         if(!IS_PLAYER(this.enemy))
29                 return; // only players
30
31         if (this.classname == "trigger_secret")
32         {
33                 if (!IS_PLAYER(this.enemy))
34                         return;
35                 found_secrets = found_secrets + 1;
36                 WriteByte (MSG_ALL, SVC_FOUNDSECRET);
37         }
38
39         if (this.noise)
40                 _sound (this.enemy, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM);
41
42 // don't trigger again until reset
43         this.takedamage = DAMAGE_NO;
44
45         SUB_UseTargets(this, this.enemy, this.goalentity);
46
47         if (this.wait > 0)
48         {
49                 setthink(this, multi_wait);
50                 this.nextthink = time + this.wait;
51         }
52         else if (this.wait == 0)
53         {
54                 multi_wait(this); // waiting finished
55         }
56         else
57         {       // we can't just delete(this) here, because this is a touch function
58                 // called while C code is looping through area links...
59                 settouch(this, func_null);
60         }
61 }
62
63 void multi_use(entity this, entity actor, entity trigger)
64 {
65         this.goalentity = trigger;
66         this.enemy = actor;
67         multi_trigger(this);
68 }
69
70 void multi_touch(entity this, entity toucher)
71 {
72         if(!(this.spawnflags & 2))
73         if(!toucher.iscreature)
74                         return;
75
76         if(this.team)
77                 if(((this.spawnflags & 4) == 0) == (this.team != toucher.team))
78                         return;
79
80 // if the trigger has an angles field, check player's facing direction
81         if (this.movedir != '0 0 0')
82         {
83                 makevectors (toucher.angles);
84                 if (v_forward * this.movedir < 0)
85                         return;         // not facing the right way
86         }
87
88         // if the trigger has pressed keys, check that the player is pressing those keys
89         if(this.pressedkeys && IS_PLAYER(toucher)) // only for players
90         if(!(CS(toucher).pressedkeys & this.pressedkeys))
91                 return;
92
93         EXACTTRIGGER_TOUCH(this, toucher);
94
95         this.enemy = toucher;
96         this.goalentity = toucher;
97         multi_trigger(this);
98 }
99
100 void multi_eventdamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force)
101 {
102         if(!this.takedamage)
103                 return;
104         if(this.spawnflags & DOOR_NOSPLASH)
105                 if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
106                         return;
107         if(this.team)
108                 if(((this.spawnflags & 4) == 0) == (this.team != attacker.team))
109                         return;
110         this.health = this.health - damage;
111         if (this.health <= 0)
112         {
113                 this.enemy = attacker;
114                 this.goalentity = inflictor;
115                 multi_trigger(this);
116         }
117 }
118
119 void multi_reset(entity this)
120 {
121         if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) )
122                 settouch(this, multi_touch);
123         if (this.max_health)
124         {
125                 this.health = this.max_health;
126                 this.takedamage = DAMAGE_YES;
127                 this.solid = SOLID_BBOX;
128         }
129         setthink(this, func_null);
130         this.nextthink = 0;
131         this.team = this.team_saved;
132 }
133
134 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
135 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.
136 If "delay" is set, the trigger waits some time after activating before firing.
137 "wait" : Seconds between triggerings. (.2 default)
138 If notouch is set, the trigger is only fired by other entities, not by touching.
139 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
140 sounds
141 1)      secret
142 2)      beep beep
143 3)      large switch
144 4)
145 set "message" to text string
146 */
147 spawnfunc(trigger_multiple)
148 {
149         this.reset = multi_reset;
150         if (this.sounds == 1)
151                 this.noise = "misc/secret.wav";
152         else if (this.sounds == 2)
153                 this.noise = strzone(SND(TALK));
154         else if (this.sounds == 3)
155                 this.noise = "misc/trigger1.wav";
156
157         if(this.noise)
158                 precache_sound(this.noise);
159
160         if (!this.wait)
161                 this.wait = 0.2;
162         else if(this.wait < -1)
163                 this.wait = 0;
164         this.use = multi_use;
165
166         EXACTTRIGGER_INIT;
167
168         this.team_saved = this.team;
169         IL_PUSH(g_saved_team, this);
170
171         if (this.health)
172         {
173                 if (this.spawnflags & SPAWNFLAG_NOTOUCH)
174                         objerror (this, "health and notouch don't make sense\n");
175                 this.canteamdamage = true;
176                 this.max_health = this.health;
177                 this.event_damage = multi_eventdamage;
178                 this.takedamage = DAMAGE_YES;
179                 this.solid = SOLID_BBOX;
180                 setorigin(this, this.origin);   // make sure it links into the world
181         }
182         else
183         {
184                 if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) )
185                 {
186                         settouch(this, multi_touch);
187                         setorigin(this, this.origin);   // make sure it links into the world
188                 }
189         }
190 }
191
192
193 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
194 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
195 "targetname".  If "health" is set, the trigger must be killed to activate.
196 If notouch is set, the trigger is only fired by other entities, not by touching.
197 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
198 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.
199 sounds
200 1)      secret
201 2)      beep beep
202 3)      large switch
203 4)
204 set "message" to text string
205 */
206 spawnfunc(trigger_once)
207 {
208         this.wait = -1;
209         spawnfunc_trigger_multiple(this);
210 }
211 #endif