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