9a7181d3a2250a530466a1d3dcbf59a9e9d1f0d8
[xonotic/xonotic-data.pk3dir.git] / qcsrc / common / mapobjects / triggers.qc
1 #include "triggers.qh"
2
3 void SUB_DontUseTargets(entity this, entity actor, entity trigger) { }
4
5 void SUB_UseTargets(entity this, entity actor, entity trigger);
6
7 void DelayThink(entity this)
8 {
9         SUB_UseTargets (this, this.enemy, NULL);
10         delete(this);
11 }
12
13 #ifdef SVQC
14 void generic_setactive(entity this, int act)
15 {
16         if(act == ACTIVE_TOGGLE)
17         {
18                 if(this.active == ACTIVE_ACTIVE)
19                 {
20                         this.active = ACTIVE_NOT;
21                 }
22                 else
23                 {
24                         this.active = ACTIVE_ACTIVE;
25                 }
26         }
27         else
28         {
29                 this.active = act;
30         }
31 }
32
33 void generic_netlinked_setactive(entity this, int act)
34 {
35         int old_status = this.active;
36         generic_setactive(this, act);
37
38         if (this.active != old_status)
39         {
40                 this.SendFlags |= SF_TRIGGER_UPDATE;
41         }
42 }
43
44 void generic_netlinked_reset(entity this)
45 {
46         IFTARGETED
47         {
48                 if(this.spawnflags & START_ENABLED)
49                 {
50                         this.active = ACTIVE_ACTIVE;
51                 }
52                 else
53                 {
54                         this.active = ACTIVE_NOT;
55                 }
56         }
57         else
58         {
59                 this.active = ACTIVE_ACTIVE;
60         }
61
62         this.SendFlags |= SF_TRIGGER_UPDATE;
63 }
64
65 // Compatibility with old maps
66 void generic_netlinked_legacy_use(entity this, entity actor, entity trigger)
67 {
68         //LOG_WARNF("Entity %s was (de)activated by a trigger, please update map to use relays", this.targetname);
69         this.setactive(this, ACTIVE_TOGGLE);
70 }
71
72 bool autocvar_g_triggers_debug = true;
73
74 void trigger_init(entity this)
75 {
76         string m = this.model;
77         EXACTTRIGGER_INIT;
78         if(autocvar_g_triggers_debug)
79         {
80                 if(m != "")
81                 {
82                         precache_model(m);
83                         _setmodel(this, m); // no precision needed
84                 }
85                 setorigin(this, this.origin);
86                 if(this.scale)
87                         setsize(this, this.mins * this.scale, this.maxs * this.scale);
88                 else
89                         setsize(this, this.mins, this.maxs);
90         }
91
92         if(autocvar_g_triggers_debug)
93                 BITSET_ASSIGN(this.effects, EF_NODEPTHTEST);
94 }
95
96 void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc)
97 {
98         setSendEntity(this, sendfunc);
99         this.SendFlags = 0xFFFFFF;
100 }
101
102 void trigger_common_write(entity this, bool withtarget)
103 {
104         int f = 0;
105         if(this.warpzone_isboxy)
106                 BITSET_ASSIGN(f, 1);
107         if(this.origin != '0 0 0')
108                 BITSET_ASSIGN(f, 4);
109         if(this.movedir != '0 0 0')
110                 BITSET_ASSIGN(f, 8);
111         if(this.angles != '0 0 0')
112                 BITSET_ASSIGN(f, 16);
113         WriteByte(MSG_ENTITY, f);
114
115         if(withtarget)
116         {
117                 // probably some way to clean this up...
118                 int targbits = 0;
119                 if(this.target && this.target != "") targbits |= BIT(0);
120                 if(this.target2 && this.target2 != "") targbits |= BIT(1);
121                 if(this.target3 && this.target3 != "") targbits |= BIT(2);
122                 if(this.target4 && this.target4 != "") targbits |= BIT(3);
123                 if(this.targetname && this.targetname != "") targbits |= BIT(4);
124                 if(this.killtarget && this.killtarget != "") targbits |= BIT(5);
125
126                 WriteByte(MSG_ENTITY, targbits);
127
128                 if(targbits & BIT(0))
129                         WriteString(MSG_ENTITY, this.target);
130                 if(targbits & BIT(1))
131                         WriteString(MSG_ENTITY, this.target2);
132                 if(targbits & BIT(2))
133                         WriteString(MSG_ENTITY, this.target3);
134                 if(targbits & BIT(3))
135                         WriteString(MSG_ENTITY, this.target4);
136                 if(targbits & BIT(4))
137                         WriteString(MSG_ENTITY, this.targetname);
138                 if(targbits & BIT(5))
139                         WriteString(MSG_ENTITY, this.killtarget);
140         }
141
142         if(f & 4)
143                 WriteVector(MSG_ENTITY, this.origin);
144
145         if(f & 8)
146                 WriteVector(MSG_ENTITY, this.movedir);
147
148         if(f & 16)
149                 WriteVector(MSG_ENTITY, this.angles);
150
151         WriteShort(MSG_ENTITY, this.modelindex);
152         WriteVector(MSG_ENTITY, this.mins);
153         WriteVector(MSG_ENTITY, this.maxs);
154         WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255));
155 }
156
157 #elif defined(CSQC)
158
159 void trigger_common_read(entity this, bool withtarget)
160 {
161         int f = ReadByte();
162         this.warpzone_isboxy = (f & 1);
163
164         if(withtarget)
165         {
166                 strfree(this.target);
167                 strfree(this.target2);
168                 strfree(this.target3);
169                 strfree(this.target4);
170                 strfree(this.targetname);
171                 strfree(this.killtarget);
172
173                 int targbits = ReadByte();
174
175                 this.target = ((targbits & BIT(0)) ? strzone(ReadString()) : string_null);
176                 this.target2 = ((targbits & BIT(1)) ? strzone(ReadString()) : string_null);
177                 this.target3 = ((targbits & BIT(2)) ? strzone(ReadString()) : string_null);
178                 this.target4 = ((targbits & BIT(3)) ? strzone(ReadString()) : string_null);
179                 this.targetname = ((targbits & BIT(4)) ? strzone(ReadString()) : string_null);
180                 this.killtarget = ((targbits & BIT(5)) ? strzone(ReadString()) : string_null);
181         }
182
183         if(f & 4)
184                 this.origin = ReadVector();
185         else
186                 this.origin = '0 0 0';
187         setorigin(this, this.origin);
188
189         if(f & 8)
190                 this.movedir = ReadVector();
191         else
192                 this.movedir = '0 0 0';
193
194         if(f & 16)
195                 this.angles = ReadVector();
196         else
197                 this.angles = '0 0 0';
198
199         this.modelindex = ReadShort();
200         this.mins = ReadVector();
201         this.maxs = ReadVector();
202         this.scale = ReadByte() / 16;
203         setsize(this, this.mins, this.maxs);
204 }
205
206 void trigger_remove_generic(entity this)
207 {
208         strfree(this.target);
209         strfree(this.target2);
210         strfree(this.target3);
211         strfree(this.target4);
212         strfree(this.targetname);
213         strfree(this.killtarget);
214 }
215 #endif
216
217
218 /*
219 ==============================
220 SUB_UseTargets
221
222 the global "activator" should be set to the entity that initiated the firing.
223
224 If this.delay is set, a DelayedUse entity will be created that will actually
225 do the SUB_UseTargets after that many seconds have passed.
226
227 Centerprints any this.message to the activator.
228
229 Removes all entities with a targetname that match this.killtarget,
230 and removes them, so some events can remove other triggers.
231
232 Search for (string)targetname in all entities that
233 match (string)this.target and call their .use function
234
235 ==============================
236 */
237
238 void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse)
239 {
240 //
241 // check for a delay
242 //
243         if (this.delay)
244         {
245         // create a temp object to fire at a later time
246                 entity t = new(DelayedUse);
247                 t.nextthink = time + this.delay;
248                 setthink(t, DelayThink);
249                 t.enemy = actor;
250                 t.message = this.message;
251                 t.killtarget = this.killtarget;
252                 t.target = this.target;
253                 t.target2 = this.target2;
254                 t.target3 = this.target3;
255                 t.target4 = this.target4;
256                 t.antiwall_flag = this.antiwall_flag;
257                 return;
258         }
259
260         string s;
261
262 //
263 // print the message
264 //
265 #ifdef SVQC
266         if(this)
267         if(IS_PLAYER(actor) && this.message != "")
268         if(IS_REAL_CLIENT(actor))
269         {
270                 centerprint(actor, this.message);
271                 if (this.noise == "")
272                         play2(actor, SND(TALK));
273         }
274
275 //
276 // kill the killtagets
277 //
278         s = this.killtarget;
279         if (s != "")
280         {
281                 for(entity t = NULL; (t = find(t, targetname, s)); )
282                         delete(t);
283         }
284 #endif
285
286 //
287 // fire targets
288 //
289
290         if(this.target_random)
291                 RandomSelection_Init();
292
293         for(int i = 0; i < 4; ++i)
294         {
295                 switch(i)
296                 {
297                         default:
298                         case 0: s = this.target; break;
299                         case 1: s = this.target2; break;
300                         case 2: s = this.target3; break;
301                         case 3: s = this.target4; break;
302                 }
303                 if (s != "")
304                 {
305                         for(entity t = NULL; (t = find(t, targetname, s)); )
306                         {
307                                 if(t != this && t.use && (t.sub_target_used != time || !preventReuse))
308                                 {
309                                         if(this.target_random)
310                                         {
311                                                 RandomSelection_AddEnt(t, 1, 0);
312                                         }
313                                         else
314                                         {
315                                                 t.use(t, actor, this);
316                                                 if(preventReuse)
317                                                         t.sub_target_used = time;
318                                         }
319                                 }
320                         }
321                 }
322         }
323
324         if(this.target_random && RandomSelection_chosen_ent)
325         {
326                 RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this);
327                 if(preventReuse)
328                         RandomSelection_chosen_ent.sub_target_used = time;
329         }
330 }
331
332 void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); }
333 void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); }