5e8c641be542735efe020f155f2d7ff556f7dcbc
[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         this.health = this.health - damage;
108         if (this.health <= 0)
109         {
110                 this.enemy = attacker;
111                 this.goalentity = inflictor;
112                 multi_trigger(this);
113         }
114 }
115
116 void multi_reset(entity this)
117 {
118         if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) )
119                 settouch(this, multi_touch);
120         if (this.max_health)
121         {
122                 this.health = this.max_health;
123                 this.takedamage = DAMAGE_YES;
124                 this.solid = SOLID_BBOX;
125         }
126         setthink(this, func_null);
127         this.nextthink = 0;
128         this.team = this.team_saved;
129 }
130
131 /*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch
132 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.
133 If "delay" is set, the trigger waits some time after activating before firing.
134 "wait" : Seconds between triggerings. (.2 default)
135 If notouch is set, the trigger is only fired by other entities, not by touching.
136 NOTOUCH has been obsoleted by spawnfunc_trigger_relay!
137 sounds
138 1)      secret
139 2)      beep beep
140 3)      large switch
141 4)
142 set "message" to text string
143 */
144 spawnfunc(trigger_multiple)
145 {
146         this.reset = multi_reset;
147         if (this.sounds == 1)
148                 this.noise = "misc/secret.wav";
149         else if (this.sounds == 2)
150                 this.noise = strzone(SND(TALK));
151         else if (this.sounds == 3)
152                 this.noise = "misc/trigger1.wav";
153
154         if(this.noise)
155                 precache_sound(this.noise);
156
157         if (!this.wait)
158                 this.wait = 0.2;
159         else if(this.wait < -1)
160                 this.wait = 0;
161         this.use = multi_use;
162
163         EXACTTRIGGER_INIT;
164
165         this.team_saved = this.team;
166         IL_PUSH(g_saved_team, this);
167
168         if (this.health)
169         {
170                 if (this.spawnflags & SPAWNFLAG_NOTOUCH)
171                         objerror (this, "health and notouch don't make sense\n");
172                 this.max_health = this.health;
173                 this.event_damage = multi_eventdamage;
174                 this.takedamage = DAMAGE_YES;
175                 this.solid = SOLID_BBOX;
176                 setorigin(this, this.origin);   // make sure it links into the world
177         }
178         else
179         {
180                 if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) )
181                 {
182                         settouch(this, multi_touch);
183                         setorigin(this, this.origin);   // make sure it links into the world
184                 }
185         }
186 }
187
188
189 /*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch
190 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
191 "targetname".  If "health" is set, the trigger must be killed to activate.
192 If notouch is set, the trigger is only fired by other entities, not by touching.
193 if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired.
194 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.
195 sounds
196 1)      secret
197 2)      beep beep
198 3)      large switch
199 4)
200 set "message" to text string
201 */
202 spawnfunc(trigger_once)
203 {
204         this.wait = -1;
205         spawnfunc_trigger_multiple(this);
206 }
207 #endif