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