2 float SUB_True() { return 1; }
\r
3 float SUB_False() { return 0; }
\r
5 void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;
\r
6 void() SUB_CalcMoveDone;
\r
7 void() SUB_CalcAngleMoveDone;
\r
8 //void() SUB_UseTargets;
\r
11 void spawnfunc_info_null (void)
\r
14 // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
\r
17 void setanim(entity e, vector anim, float looping, float override, float restart)
\r
19 if (anim_x == e.animstate_startframe)
\r
20 if (anim_y == e.animstate_numframes)
\r
21 if (anim_z == e.animstate_framerate)
\r
26 if(anim_y == 1) // ZYM animation
\r
27 BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
\r
32 e.animstate_startframe = anim_x;
\r
33 e.animstate_numframes = anim_y;
\r
34 e.animstate_framerate = anim_z;
\r
35 e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
\r
36 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
\r
37 e.animstate_looping = looping;
\r
38 e.animstate_override = override;
\r
39 e.frame = e.animstate_startframe;
\r
42 void updateanim(entity e)
\r
44 if (time >= e.animstate_endtime)
\r
46 if (e.animstate_looping)
\r
48 e.animstate_starttime = e.animstate_endtime;
\r
49 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
\r
51 e.animstate_override = FALSE;
\r
53 e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
\r
54 //print(ftos(time), " -> ", ftos(e.frame), "\n");
\r
57 vector animfixfps(entity e, vector a)
\r
59 // multi-frame anim: keep as-is
\r
63 dur = frameduration(e.modelindex, a_x);
\r
77 void SUB_Remove (void)
\r
86 Applies some friction to self
\r
90 void SUB_Friction (void)
\r
92 self.nextthink = time;
\r
93 if(self.flags & FL_ONGROUND)
\r
94 self.velocity = self.velocity * (1 - frametime * self.friction);
\r
101 Makes client invisible or removes non-client
\r
104 void SUB_VanishOrRemove (entity ent)
\r
106 if (ent.flags & FL_CLIENT)
\r
121 void SUB_SetFade_Think (void)
\r
123 self.think = SUB_SetFade_Think;
\r
124 self.nextthink = self.fade_time;
\r
125 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
\r
126 if (self.alpha < 0.01)
\r
127 SUB_VanishOrRemove(self);
\r
128 self.alpha = bound(0.01, self.alpha, 1);
\r
135 Fade 'ent' out when time >= 'when'
\r
138 void SUB_SetFade (entity ent, float when, float fadetime)
\r
140 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
\r
143 ent.fade_rate = 1/fadetime;
\r
144 ent.fade_time = when;
\r
145 ent.think = SUB_SetFade_Think;
\r
146 ent.nextthink = when;
\r
153 calculate self.velocity and self.nextthink to reach dest from
\r
154 self.origin traveling at speed
\r
157 void SUB_CalcMoveDone (void)
\r
159 // After moving, set origin to exact final destination
\r
161 setorigin (self, self.finaldest);
\r
162 self.velocity = '0 0 0';
\r
163 self.nextthink = -1;
\r
168 void SUB_CalcMove (vector tdest, float tspeed, void() func)
\r
174 objerror ("No speed is defined!");
\r
176 self.think1 = func;
\r
177 self.finaldest = tdest;
\r
178 self.think = SUB_CalcMoveDone;
\r
180 if (tdest == self.origin)
\r
182 self.velocity = '0 0 0';
\r
183 self.nextthink = self.ltime + 0.1;
\r
187 delta = tdest - self.origin;
\r
188 traveltime = vlen (delta) / tspeed;
\r
190 if (traveltime < 0.1)
\r
192 self.velocity = '0 0 0';
\r
193 self.nextthink = self.ltime + 0.1;
\r
197 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
\r
199 self.nextthink = self.ltime + traveltime;
\r
202 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
\r
209 SUB_CalcMove (tdest, tspeed, func);
\r
218 calculate self.avelocity and self.nextthink to reach destangle from
\r
219 self.angles rotating
\r
221 The calling function should make sure self.think is valid
\r
224 void SUB_CalcAngleMoveDone (void)
\r
226 // After rotating, set angle to exact final angle
\r
227 self.angles = self.finalangle;
\r
228 self.avelocity = '0 0 0';
\r
229 self.nextthink = -1;
\r
234 // FIXME: I fixed this function only for rotation around the main axes
\r
235 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
\r
241 objerror ("No speed is defined!");
\r
243 // take the shortest distance for the angles
\r
244 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
\r
245 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
\r
246 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
\r
247 delta = destangle - self.angles;
\r
248 traveltime = vlen (delta) / tspeed;
\r
250 self.think1 = func;
\r
251 self.finalangle = destangle;
\r
252 self.think = SUB_CalcAngleMoveDone;
\r
254 if (traveltime < 0.1)
\r
256 self.avelocity = '0 0 0';
\r
257 self.nextthink = self.ltime + 0.1;
\r
261 self.avelocity = delta * (1 / traveltime);
\r
262 self.nextthink = self.ltime + traveltime;
\r
265 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
\r
272 SUB_CalcAngleMove (destangle, tspeed, func);
\r
281 unused but required by the engine
\r
295 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
\r
296 Additionally it moves players back into the past before the trace and restores them afterward.
\r
299 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
\r
301 local entity player;
\r
302 local float oldsolid;
\r
304 // check whether antilagged traces are enabled
\r
307 if (clienttype(forent) != CLIENTTYPE_REAL)
\r
308 lag = 0; // only antilag for clients
\r
310 // change shooter to SOLID_BBOX so the shot can hit corpses
\r
313 oldsolid = source.dphitcontentsmask;
\r
314 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
\r
319 // take players back into the past
\r
320 player = player_list;
\r
323 antilag_takeback(player, time - lag);
\r
324 player = player.nextplayer;
\r
330 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
\r
332 tracebox (v1, mi, ma, v2, nomonst, forent);
\r
334 // restore players to current positions
\r
337 player = player_list;
\r
340 antilag_restore(player);
\r
341 player = player.nextplayer;
\r
345 // restore shooter solid type
\r
347 source.dphitcontentsmask = oldsolid;
\r
349 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
351 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
\r
353 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
355 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
357 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
\r
359 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
\r
361 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
363 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
\r
365 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
367 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
\r
369 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
371 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
373 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
\r
375 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
\r
377 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
379 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
\r
382 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
\r
384 vector pos, dir, t;
\r
387 //nudge = 2 * cvar("collision_impactnudge"); // why not?
\r
390 dir = normalize(v2 - v1);
\r
392 pos = v1 + dir * nudge;
\r
399 if((pos - v1) * dir >= (v2 - v1) * dir)
\r
402 trace_fraction = 1;
\r
407 tracebox(pos, mi, ma, v2, nomonsters, forent);
\r
412 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
\r
413 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
\r
414 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
\r
415 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
\r
418 if(trace_startsolid)
\r
420 // we started inside solid.
\r
421 // then trace from endpos to pos
\r
423 tracebox(t, mi, ma, pos, nomonsters, forent);
\r
425 if(trace_startsolid)
\r
427 // t is still inside solid? bad
\r
428 // force advance, then, and retry
\r
429 pos = t + dir * nudge;
\r
433 // we actually LEFT solid!
\r
434 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
440 // pos is outside solid?!? but why?!? never mind, just return it.
\r
441 trace_endpos = pos;
\r
442 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
448 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
\r
451 vector pos, dir, t;
\r
454 //nudge = 2 * cvar("collision_impactnudge"); // why not?
\r
457 dir = normalize(v2 - v1);
\r
459 pos = v1 + dir * nudge;
\r
463 if((pos - v1) * dir >= (v2 - v1) * dir)
\r
466 trace_fraction = 1;
\r
470 traceline(pos, v2, nomonsters, forent);
\r
472 if(trace_startsolid)
\r
474 // we started inside solid.
\r
475 // then trace from endpos to pos
\r
477 traceline(t, pos, nomonsters, forent);
\r
478 if(trace_startsolid)
\r
480 // t is inside solid? bad
\r
481 // force advance, then
\r
482 pos = pos + dir * nudge;
\r
486 // we actually LEFT solid!
\r
487 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
493 // pos is outside solid?!? but why?!? never mind, just return it.
\r
494 trace_endpos = pos;
\r
495 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
500 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
\r
507 Returns a point at least 12 units away from walls
\r
508 (useful for explosion animations, although the blast is performed where it really happened)
\r
512 vector findbetterlocation (vector org, float mindist)
\r
518 vec = mindist * '1 0 0';
\r
522 traceline (org, org + vec, TRUE, world);
\r
524 if (trace_fraction < 1)
\r
526 loc = trace_endpos;
\r
527 traceline (loc, loc + vec, TRUE, world);
\r
528 if (trace_fraction >= 1)
\r
548 Returns a random number between -1.0 and 1.0
\r
551 float crandom (void)
\r
553 return 2 * (random () - 0.5);
\r
558 Angc used for animations
\r
563 float angc (float a1, float a2)
\r
586 vector NearestPointOnBox(entity box, vector org);
\r
588 void SetBrushEntityModel()
\r
590 if(self.model != "")
\r
592 precache_model(self.model);
\r
593 setmodel(self, self.model); // no precision needed
\r
595 setorigin(self, self.origin);
\r
597 setsize(self, self.mins * self.scale, self.maxs * self.scale);
\r
599 setsize(self, self.mins, self.maxs);
\r
602 void SetBrushEntityModelNoLOD()
\r
604 if(self.model != "")
\r
606 precache_model(self.model);
\r
607 setmodel(self, self.model); // no precision needed
\r
609 setorigin(self, self.origin);
\r
611 setsize(self, self.mins * self.scale, self.maxs * self.scale);
\r
613 setsize(self, self.mins, self.maxs);
\r
624 if (self.movedir != '0 0 0')
\r
625 self.movedir = normalize(self.movedir);
\r
628 makevectors (self.angles);
\r
629 self.movedir = v_forward;
\r
632 self.angles = '0 0 0';
\r
637 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
638 // to mean no restrictions, so use a yaw of 360 instead.
\r
639 if (self.movedir == '0 0 0')
\r
640 if (self.angles != '0 0 0')
\r
642 self.solid = SOLID_TRIGGER;
\r
643 SetBrushEntityModel();
\r
644 self.movetype = MOVETYPE_NONE;
\r
645 self.modelindex = 0;
\r
649 void InitSolidBSPTrigger()
\r
651 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
652 // to mean no restrictions, so use a yaw of 360 instead.
\r
653 if (self.movedir == '0 0 0')
\r
654 if (self.angles != '0 0 0')
\r
656 self.solid = SOLID_BSP;
\r
657 SetBrushEntityModel();
\r
658 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
\r
659 // self.modelindex = 0;
\r
663 float InitMovingBrushTrigger()
\r
665 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
666 // to mean no restrictions, so use a yaw of 360 instead.
\r
667 self.solid = SOLID_BSP;
\r
668 SetBrushEntityModel();
\r
669 self.movetype = MOVETYPE_PUSH;
\r
670 if(self.modelindex == 0)
\r
672 objerror("InitMovingBrushTrigger: no brushes found!");
\r