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