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