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