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