Merge branch 'master' into Mario/vaporizer_damage
[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                         LOG_TRACE("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
191                         LOG_TRACE("  Nudging gets us nowhere at ", vtos(pos), "\n");
192                         LOG_TRACE("  trace_endpos is ", vtos(trace_endpos), "\n");
193                         LOG_TRACE("  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 float LOD_customize()
284 {
285         float d;
286
287         if(autocvar_loddebug)
288         {
289                 d = autocvar_loddebug;
290                 if(d == 1)
291                         self.modelindex = self.lodmodelindex0;
292                 else if(d == 2 || !self.lodmodelindex2)
293                         self.modelindex = self.lodmodelindex1;
294                 else // if(d == 3)
295                         self.modelindex = self.lodmodelindex2;
296                 return true;
297         }
298
299         // TODO csqc network this so it only gets sent once
300         d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
301         if(d < self.loddistance1)
302                 self.modelindex = self.lodmodelindex0;
303         else if(!self.lodmodelindex2 || d < self.loddistance2)
304                 self.modelindex = self.lodmodelindex1;
305         else
306                 self.modelindex = self.lodmodelindex2;
307
308         return true;
309 }
310
311 void LOD_uncustomize()
312 {
313         self.modelindex = self.lodmodelindex0;
314 }
315
316 void LODmodel_attach()
317 {
318         entity e;
319
320         if(!self.loddistance1)
321                 self.loddistance1 = 1000;
322         if(!self.loddistance2)
323                 self.loddistance2 = 2000;
324         self.lodmodelindex0 = self.modelindex;
325
326         if(self.lodtarget1 != "")
327         {
328                 e = find(world, targetname, self.lodtarget1);
329                 if(e)
330                 {
331                         self.lodmodel1 = e.model;
332                         remove(e);
333                 }
334         }
335         if(self.lodtarget2 != "")
336         {
337                 e = find(world, targetname, self.lodtarget2);
338                 if(e)
339                 {
340                         self.lodmodel2 = e.model;
341                         remove(e);
342                 }
343         }
344
345         if(autocvar_loddebug < 0)
346         {
347                 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
348         }
349
350         if(self.lodmodel1 != "")
351         {
352                 vector mi, ma;
353                 mi = self.mins;
354                 ma = self.maxs;
355
356                 precache_model(self.lodmodel1);
357                 setmodel(self, self.lodmodel1);
358                 self.lodmodelindex1 = self.modelindex;
359
360                 if(self.lodmodel2 != "")
361                 {
362                         precache_model(self.lodmodel2);
363                         setmodel(self, self.lodmodel2);
364                         self.lodmodelindex2 = self.modelindex;
365                 }
366
367                 self.modelindex = self.lodmodelindex0;
368                 setsize(self, mi, ma);
369         }
370
371         if(self.lodmodelindex1)
372                 if (!self.SendEntity)
373                         SetCustomizer(self, LOD_customize, LOD_uncustomize);
374 }
375
376 void ApplyMinMaxScaleAngles(entity e)
377 {
378         if(e.angles.x != 0 || e.angles.z != 0 || self.avelocity.x != 0 || self.avelocity.z != 0) // "weird" rotation
379         {
380                 e.maxs = '1 1 1' * vlen(
381                         '1 0 0' * max(-e.mins.x, e.maxs.x) +
382                         '0 1 0' * max(-e.mins.y, e.maxs.y) +
383                         '0 0 1' * max(-e.mins.z, e.maxs.z)
384                 );
385                 e.mins = -e.maxs;
386         }
387         else if(e.angles.y != 0 || self.avelocity.y != 0) // yaw only is a bit better
388         {
389                 e.maxs_x = vlen(
390                         '1 0 0' * max(-e.mins.x, e.maxs.x) +
391                         '0 1 0' * max(-e.mins.y, e.maxs.y)
392                 );
393                 e.maxs_y = e.maxs.x;
394                 e.mins_x = -e.maxs.x;
395                 e.mins_y = -e.maxs.x;
396         }
397         if(e.scale)
398                 setsize(e, e.mins * e.scale, e.maxs * e.scale);
399         else
400                 setsize(e, e.mins, e.maxs);
401 }
402
403 void SetBrushEntityModel()
404 {
405         if(self.model != "")
406         {
407                 precache_model(self.model);
408                 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
409                 {
410                         vector mi = self.mins;
411                         vector ma = self.maxs;
412                         setmodel(self, self.model); // no precision needed
413                         setsize(self, mi, ma);
414                 }
415                 else
416                         setmodel(self, self.model); // no precision needed
417                 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
418         }
419         setorigin(self, self.origin);
420         ApplyMinMaxScaleAngles(self);
421 }
422
423 void SetBrushEntityModelNoLOD()
424 {
425         if(self.model != "")
426         {
427                 precache_model(self.model);
428                 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
429                 {
430                         vector mi = self.mins;
431                         vector ma = self.maxs;
432                         setmodel(self, self.model); // no precision needed
433                         setsize(self, mi, ma);
434                 }
435                 else
436                         setmodel(self, self.model); // no precision needed
437         }
438         setorigin(self, self.origin);
439         ApplyMinMaxScaleAngles(self);
440 }
441
442 /*
443 ================
444 InitTrigger
445 ================
446 */
447
448 void SetMovedir()
449 {
450         if (self.movedir != '0 0 0')
451                 self.movedir = normalize(self.movedir);
452         else
453         {
454                 makevectors (self.angles);
455                 self.movedir = v_forward;
456         }
457
458         self.angles = '0 0 0';
459 }
460
461 void InitTrigger()
462 {
463 // trigger angles are used for one-way touches.  An angle of 0 is assumed
464 // to mean no restrictions, so use a yaw of 360 instead.
465         SetMovedir ();
466         self.solid = SOLID_TRIGGER;
467         SetBrushEntityModel();
468         self.movetype = MOVETYPE_NONE;
469         self.modelindex = 0;
470         self.model = "";
471 }
472
473 void InitSolidBSPTrigger()
474 {
475 // trigger angles are used for one-way touches.  An angle of 0 is assumed
476 // to mean no restrictions, so use a yaw of 360 instead.
477         SetMovedir ();
478         self.solid = SOLID_BSP;
479         SetBrushEntityModel();
480         self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
481 //      self.modelindex = 0;
482         self.model = "";
483 }
484
485 float InitMovingBrushTrigger()
486 {
487 // trigger angles are used for one-way touches.  An angle of 0 is assumed
488 // to mean no restrictions, so use a yaw of 360 instead.
489         self.solid = SOLID_BSP;
490         SetBrushEntityModel();
491         self.movetype = MOVETYPE_PUSH;
492         if(self.modelindex == 0)
493         {
494                 objerror("InitMovingBrushTrigger: no brushes found!");
495                 return 0;
496         }
497         return 1;
498 }