Properly support team field on trigger_multiple
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / g_subs.qc
1 #include "g_subs.qh"
2
3 #include <server/defs.qh>
4 #include <server/miscfunctions.qh>
5 #include "antilag.qh"
6 #include "command/common.qh"
7 #include "../common/state.qh"
8 #include "../lib/warpzone/common.qh"
9 #include "../common/triggers/subs.qh"
10
11 spawnfunc(info_null)
12 {
13         delete(this);
14         // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
15 }
16
17 /*
18 ==================
19 main
20
21 unused but required by the engine
22 ==================
23 */
24 void main ()
25 {
26
27 }
28
29 // Misc
30
31 /*
32 ==================
33 traceline_antilag
34
35 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
36 Additionally it moves players back into the past before the trace and restores them afterward.
37 ==================
38 */
39 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
40 {
41         // check whether antilagged traces are enabled
42         if (lag < 0.001)
43                 lag = 0;
44         if (!IS_REAL_CLIENT(forent))
45                 lag = 0; // only antilag for clients
46
47         // change shooter to SOLID_BBOX so the shot can hit corpses
48         int oldsolid = source.dphitcontentsmask;
49         if(source)
50                 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
51
52         if (lag)
53                 antilag_takeback_all(forent, lag);
54
55         // do the trace
56         if(wz)
57                 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
58         else
59                 tracebox (v1, mi, ma, v2, nomonst, forent);
60
61         // restore players to current positions
62         if (lag)
63                 antilag_restore_all(forent);
64
65         // restore shooter solid type
66         if(source)
67                 source.dphitcontentsmask = oldsolid;
68 }
69 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
70 {
71         tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, false);
72 }
73 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
74 {
75         bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false);
76         if (autocvar_g_antilag != 2 || noantilag)
77                 lag = 0;
78         traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
79 }
80 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
81 {
82         bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false);
83         if (autocvar_g_antilag != 2 || noantilag)
84                 lag = 0;
85         tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, false);
86 }
87 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
88 {
89         tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, true);
90 }
91 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
92 {
93         bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false);
94         if (autocvar_g_antilag != 2 || noantilag)
95                 lag = 0;
96         WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
97 }
98 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
99 {
100         bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false);
101         if (autocvar_g_antilag != 2 || noantilag)
102                 lag = 0;
103         tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, true);
104 }
105
106 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity) // returns the number of traces done, for benchmarking
107 {
108         vector pos, dir, t;
109         float nudge;
110         entity stopentity;
111
112         //nudge = 2 * cvar("collision_impactnudge"); // why not?
113         nudge = 0.5;
114
115         dir = normalize(v2 - v1);
116
117         pos = v1 + dir * nudge;
118
119         float c;
120         c = 0;
121
122         for (;;)
123         {
124                 if(pos * dir >= v2 * dir)
125                 {
126                         // went too far
127                         trace_fraction = 1;
128                         trace_endpos = v2;
129                         return c;
130                 }
131
132                 tracebox(pos, mi, ma, v2, nomonsters, forent);
133                 ++c;
134
135                 if(c == 50)
136                 {
137                         LOG_TRACE("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2));
138                         LOG_TRACE("  Nudging gets us nowhere at ", vtos(pos));
139                         LOG_TRACE("  trace_endpos is ", vtos(trace_endpos));
140                         LOG_TRACE("  trace distance is ", ftos(vlen(pos - trace_endpos)));
141                 }
142
143                 stopentity = trace_ent;
144
145                 if(trace_startsolid)
146                 {
147                         // we started inside solid.
148                         // then trace from endpos to pos
149                         t = trace_endpos;
150                         tracebox(t, mi, ma, pos, nomonsters, forent);
151                         ++c;
152                         if(trace_startsolid)
153                         {
154                                 // t is still inside solid? bad
155                                 // force advance, then, and retry
156                                 pos = t + dir * nudge;
157
158                                 // but if we hit an entity, stop RIGHT before it
159                                 if(stopatentity && stopentity && stopentity != ignorestopatentity)
160                                 {
161                                         trace_ent = stopentity;
162                                         trace_endpos = t;
163                                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
164                                         return c;
165                                 }
166                         }
167                         else
168                         {
169                                 // we actually LEFT solid!
170                                 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
171                                 return c;
172                         }
173                 }
174                 else
175                 {
176                         // pos is outside solid?!? but why?!? never mind, just return it.
177                         trace_endpos = pos;
178                         trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
179                         return c;
180                 }
181         }
182 }
183
184 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity)
185 {
186         tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity, ignorestopatentity);
187 }
188
189 /*
190 ==================
191 findbetterlocation
192
193 Returns a point at least 12 units away from walls
194 (useful for explosion animations, although the blast is performed where it really happened)
195 Ripped from DPMod
196 ==================
197 */
198 vector findbetterlocation (vector org, float mindist)
199 {
200         vector vec = mindist * '1 0 0';
201         int c = 0;
202         while (c < 6)
203         {
204                 traceline (org, org + vec, true, NULL);
205                 vec = vec * -1;
206                 if (trace_fraction < 1)
207                 {
208                         vector loc = trace_endpos;
209                         traceline (loc, loc + vec, true, NULL);
210                         if (trace_fraction >= 1)
211                                 org = loc + vec;
212                 }
213                 if (c & 1)
214                 {
215                         float h = vec.y;
216                         vec.y = vec.x;
217                         vec.x = vec.z;
218                         vec.z = h;
219                 }
220                 c = c + 1;
221         }
222
223         return org;
224 }
225
226 bool LOD_customize(entity this, entity client)
227 {
228         if(autocvar_loddebug)
229         {
230                 int d = autocvar_loddebug;
231                 if(d == 1)
232                         this.modelindex = this.lodmodelindex0;
233                 else if(d == 2 || !this.lodmodelindex2)
234                         this.modelindex = this.lodmodelindex1;
235                 else // if(d == 3)
236                         this.modelindex = this.lodmodelindex2;
237                 return true;
238         }
239
240         // TODO csqc network this so it only gets sent once
241         vector near_point = NearestPointOnBox(this, client.origin);
242         if(vdist(near_point - client.origin, <, this.loddistance1))
243                 this.modelindex = this.lodmodelindex0;
244         else if(!this.lodmodelindex2 || vdist(near_point - client.origin, <, this.loddistance2))
245                 this.modelindex = this.lodmodelindex1;
246         else
247                 this.modelindex = this.lodmodelindex2;
248
249         return true;
250 }
251
252 void LOD_uncustomize(entity this)
253 {
254         this.modelindex = this.lodmodelindex0;
255 }
256
257 void LODmodel_attach(entity this)
258 {
259         entity e;
260
261         if(!this.loddistance1)
262                 this.loddistance1 = 1000;
263         if(!this.loddistance2)
264                 this.loddistance2 = 2000;
265         this.lodmodelindex0 = this.modelindex;
266
267         if(this.lodtarget1 != "")
268         {
269                 e = find(NULL, targetname, this.lodtarget1);
270                 if(e)
271                 {
272                         this.lodmodel1 = e.model;
273                         delete(e);
274                 }
275         }
276         if(this.lodtarget2 != "")
277         {
278                 e = find(NULL, targetname, this.lodtarget2);
279                 if(e)
280                 {
281                         this.lodmodel2 = e.model;
282                         delete(e);
283                 }
284         }
285
286         if(autocvar_loddebug < 0)
287         {
288                 this.lodmodel1 = this.lodmodel2 = ""; // don't even initialize
289         }
290
291         if(this.lodmodel1 != "")
292         {
293                 vector mi, ma;
294                 mi = this.mins;
295                 ma = this.maxs;
296
297                 precache_model(this.lodmodel1);
298                 _setmodel(this, this.lodmodel1);
299                 this.lodmodelindex1 = this.modelindex;
300
301                 if(this.lodmodel2 != "")
302                 {
303                         precache_model(this.lodmodel2);
304                         _setmodel(this, this.lodmodel2);
305                         this.lodmodelindex2 = this.modelindex;
306                 }
307
308                 this.modelindex = this.lodmodelindex0;
309                 setsize(this, mi, ma);
310         }
311
312         if(this.lodmodelindex1)
313                 if (!getSendEntity(this))
314                         SetCustomizer(this, LOD_customize, LOD_uncustomize);
315 }
316
317 void ApplyMinMaxScaleAngles(entity e)
318 {
319         if(e.angles.x != 0 || e.angles.z != 0 || e.avelocity.x != 0 || e.avelocity.z != 0) // "weird" rotation
320         {
321                 e.maxs = '1 1 1' * vlen(
322                         '1 0 0' * max(-e.mins.x, e.maxs.x) +
323                         '0 1 0' * max(-e.mins.y, e.maxs.y) +
324                         '0 0 1' * max(-e.mins.z, e.maxs.z)
325                 );
326                 e.mins = -e.maxs;
327         }
328         else if(e.angles.y != 0 || e.avelocity.y != 0) // yaw only is a bit better
329         {
330                 e.maxs_x = vlen(
331                         '1 0 0' * max(-e.mins.x, e.maxs.x) +
332                         '0 1 0' * max(-e.mins.y, e.maxs.y)
333                 );
334                 e.maxs_y = e.maxs.x;
335                 e.mins_x = -e.maxs.x;
336                 e.mins_y = -e.maxs.x;
337         }
338         if(e.scale)
339                 setsize(e, e.mins * e.scale, e.maxs * e.scale);
340         else
341                 setsize(e, e.mins, e.maxs);
342 }
343
344 void SetBrushEntityModel(entity this)
345 {
346         if(this.model != "")
347         {
348                 precache_model(this.model);
349                 if(this.mins != '0 0 0' || this.maxs != '0 0 0')
350                 {
351                         vector mi = this.mins;
352                         vector ma = this.maxs;
353                         _setmodel(this, this.model); // no precision needed
354                         setsize(this, mi, ma);
355                 }
356                 else
357                         _setmodel(this, this.model); // no precision needed
358                 InitializeEntity(this, LODmodel_attach, INITPRIO_FINDTARGET);
359         }
360         setorigin(this, this.origin);
361         ApplyMinMaxScaleAngles(this);
362 }
363
364 void SetBrushEntityModelNoLOD(entity this)
365 {
366         if(this.model != "")
367         {
368                 precache_model(this.model);
369                 if(this.mins != '0 0 0' || this.maxs != '0 0 0')
370                 {
371                         vector mi = this.mins;
372                         vector ma = this.maxs;
373                         _setmodel(this, this.model); // no precision needed
374                         setsize(this, mi, ma);
375                 }
376                 else
377                         _setmodel(this, this.model); // no precision needed
378         }
379         setorigin(this, this.origin);
380         ApplyMinMaxScaleAngles(this);
381 }
382
383 /*
384 ================
385 InitTrigger
386 ================
387 */
388
389 void SetMovedir(entity this)
390 {
391         if(this.movedir != '0 0 0')
392                 this.movedir = normalize(this.movedir);
393         else
394         {
395                 makevectors(this.angles);
396                 this.movedir = v_forward;
397         }
398
399         this.angles = '0 0 0';
400 }
401
402 void InitTrigger(entity this)
403 {
404 // trigger angles are used for one-way touches.  An angle of 0 is assumed
405 // to mean no restrictions, so use a yaw of 360 instead.
406         SetMovedir(this);
407         this.solid = SOLID_TRIGGER;
408         SetBrushEntityModel(this);
409         set_movetype(this, MOVETYPE_NONE);
410         this.modelindex = 0;
411         this.model = "";
412 }
413
414 void InitSolidBSPTrigger(entity this)
415 {
416 // trigger angles are used for one-way touches.  An angle of 0 is assumed
417 // to mean no restrictions, so use a yaw of 360 instead.
418         SetMovedir(this);
419         this.solid = SOLID_BSP;
420         SetBrushEntityModel(this);
421         set_movetype(this, MOVETYPE_NONE); // why was this PUSH? -div0
422 //      this.modelindex = 0;
423         this.model = "";
424 }
425
426 bool InitMovingBrushTrigger(entity this)
427 {
428 // trigger angles are used for one-way touches.  An angle of 0 is assumed
429 // to mean no restrictions, so use a yaw of 360 instead.
430         this.solid = SOLID_BSP;
431         SetBrushEntityModel(this);
432         set_movetype(this, MOVETYPE_PUSH);
433         if(this.modelindex == 0)
434         {
435                 objerror(this, "InitMovingBrushTrigger: no brushes found!");
436                 return false;
437         }
438         return true;
439 }