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