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
20 n = tokenizebyseparator(self.model, ".");
\r
21 if(argv(1) == "md3")
\r
24 if (anim_x == e.animstate_startframe)
\r
25 if (anim_y == e.animstate_numframes)
\r
26 if (anim_z == e.animstate_framerate)
\r
31 if(anim_y == 1) // ZYM animation
\r
32 BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
\r
37 e.animstate_startframe = anim_x;
\r
38 e.animstate_numframes = anim_y;
\r
39 e.animstate_framerate = anim_z;
\r
40 e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
\r
41 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
\r
42 e.animstate_looping = looping;
\r
43 e.animstate_override = override;
\r
44 e.frame = e.animstate_startframe;
\r
47 void updateanim(entity e)
\r
49 if (time >= e.animstate_endtime)
\r
51 if (e.animstate_looping)
\r
53 e.animstate_starttime = e.animstate_endtime;
\r
54 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
\r
56 e.animstate_override = FALSE;
\r
58 e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
\r
59 //print(ftos(time), " -> ", ftos(e.frame), "\n");
\r
62 float animparseerror;
\r
63 vector animparseline(float animfile)
\r
70 line = fgets(animfile);
\r
71 c = tokenize_console(line);
\r
74 animparseerror = TRUE;
\r
77 anim_x = stof(argv(0));
\r
78 anim_y = stof(argv(1));
\r
79 anim_z = stof(argv(2));
\r
80 // don't allow completely bogus values
\r
81 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
\r
93 void SUB_Remove (void)
\r
102 Applies some friction to self
\r
106 void SUB_Friction (void)
\r
108 self.nextthink = time;
\r
109 if(self.flags & FL_ONGROUND)
\r
110 self.velocity = self.velocity * (1 - frametime * self.friction);
\r
117 Makes client invisible or removes non-client
\r
120 void SUB_VanishOrRemove (entity ent)
\r
122 if (ent.flags & FL_CLIENT)
\r
137 void SUB_SetFade_Think (void)
\r
139 self.think = SUB_SetFade_Think;
\r
140 self.nextthink = self.fade_time;
\r
141 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
\r
142 if (self.alpha < 0.01)
\r
143 SUB_VanishOrRemove(self);
\r
144 self.alpha = bound(0.01, self.alpha, 1);
\r
151 Fade 'ent' out when time >= 'when'
\r
154 void SUB_SetFade (entity ent, float when, float fadetime)
\r
156 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
\r
159 ent.fade_rate = 1/fadetime;
\r
160 ent.fade_time = when;
\r
161 ent.think = SUB_SetFade_Think;
\r
162 ent.nextthink = when;
\r
169 calculate self.velocity and self.nextthink to reach dest from
\r
170 self.origin traveling at speed
\r
173 void SUB_CalcMoveDone (void)
\r
175 // After moving, set origin to exact final destination
\r
177 setorigin (self, self.finaldest);
\r
178 self.velocity = '0 0 0';
\r
179 self.nextthink = -1;
\r
184 void SUB_CalcMove (vector tdest, float tspeed, void() func)
\r
190 objerror ("No speed is defined!");
\r
192 self.think1 = func;
\r
193 self.finaldest = tdest;
\r
194 self.think = SUB_CalcMoveDone;
\r
196 if (tdest == self.origin)
\r
198 self.velocity = '0 0 0';
\r
199 self.nextthink = self.ltime + 0.1;
\r
203 delta = tdest - self.origin;
\r
204 traveltime = vlen (delta) / tspeed;
\r
206 if (traveltime < 0.1)
\r
208 self.velocity = '0 0 0';
\r
209 self.nextthink = self.ltime + 0.1;
\r
213 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
\r
215 self.nextthink = self.ltime + traveltime;
\r
218 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
\r
225 SUB_CalcMove (tdest, tspeed, func);
\r
234 calculate self.avelocity and self.nextthink to reach destangle from
\r
235 self.angles rotating
\r
237 The calling function should make sure self.think is valid
\r
240 void SUB_CalcAngleMoveDone (void)
\r
242 // After rotating, set angle to exact final angle
\r
243 self.angles = self.finalangle;
\r
244 self.avelocity = '0 0 0';
\r
245 self.nextthink = -1;
\r
250 // FIXME: I fixed this function only for rotation around the main axes
\r
251 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
\r
257 objerror ("No speed is defined!");
\r
259 // take the shortest distance for the angles
\r
260 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
\r
261 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
\r
262 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
\r
263 delta = destangle - self.angles;
\r
264 traveltime = vlen (delta) / tspeed;
\r
266 self.think1 = func;
\r
267 self.finalangle = destangle;
\r
268 self.think = SUB_CalcAngleMoveDone;
\r
270 if (traveltime < 0.1)
\r
272 self.avelocity = '0 0 0';
\r
273 self.nextthink = self.ltime + 0.1;
\r
277 self.avelocity = delta * (1 / traveltime);
\r
278 self.nextthink = self.ltime + traveltime;
\r
281 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
\r
288 SUB_CalcAngleMove (destangle, tspeed, func);
\r
297 unused but required by the engine
\r
311 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
\r
312 Additionally it moves players back into the past before the trace and restores them afterward.
\r
315 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
\r
317 local entity player;
\r
318 local float oldsolid;
\r
320 // check whether antilagged traces are enabled
\r
323 if (clienttype(forent) != CLIENTTYPE_REAL)
\r
324 lag = 0; // only antilag for clients
\r
326 // change shooter to SOLID_BBOX so the shot can hit corpses
\r
329 oldsolid = source.dphitcontentsmask;
\r
330 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
\r
335 // take players back into the past
\r
336 player = player_list;
\r
339 antilag_takeback(player, time - lag);
\r
340 player = player.nextplayer;
\r
346 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
\r
348 tracebox (v1, mi, ma, v2, nomonst, forent);
\r
350 // restore players to current positions
\r
353 player = player_list;
\r
356 antilag_restore(player);
\r
357 player = player.nextplayer;
\r
361 // restore shooter solid type
\r
363 source.dphitcontentsmask = oldsolid;
\r
365 void 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, FALSE);
\r
369 void 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 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
\r
375 void 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, FALSE);
\r
381 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
383 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
\r
385 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
387 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
389 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
\r
391 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
\r
393 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
395 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
\r
398 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
\r
400 vector pos, dir, t;
\r
403 //nudge = 2 * cvar("collision_impactnudge"); // why not?
\r
406 dir = normalize(v2 - v1);
\r
408 pos = v1 + dir * nudge;
\r
415 if((pos - v1) * dir >= (v2 - v1) * dir)
\r
418 trace_fraction = 1;
\r
423 tracebox(pos, mi, ma, v2, nomonsters, forent);
\r
428 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
\r
429 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
\r
430 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
\r
431 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
\r
434 if(trace_startsolid)
\r
436 // we started inside solid.
\r
437 // then trace from endpos to pos
\r
439 tracebox(t, mi, ma, pos, nomonsters, forent);
\r
441 if(trace_startsolid)
\r
443 // t is still inside solid? bad
\r
444 // force advance, then, and retry
\r
445 pos = t + dir * nudge;
\r
449 // we actually LEFT solid!
\r
450 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
456 // pos is outside solid?!? but why?!? never mind, just return it.
\r
457 trace_endpos = pos;
\r
458 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
464 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
\r
467 vector pos, dir, t;
\r
470 //nudge = 2 * cvar("collision_impactnudge"); // why not?
\r
473 dir = normalize(v2 - v1);
\r
475 pos = v1 + dir * nudge;
\r
479 if((pos - v1) * dir >= (v2 - v1) * dir)
\r
482 trace_fraction = 1;
\r
486 traceline(pos, v2, nomonsters, forent);
\r
488 if(trace_startsolid)
\r
490 // we started inside solid.
\r
491 // then trace from endpos to pos
\r
493 traceline(t, pos, nomonsters, forent);
\r
494 if(trace_startsolid)
\r
496 // t is inside solid? bad
\r
497 // force advance, then
\r
498 pos = pos + dir * nudge;
\r
502 // we actually LEFT solid!
\r
503 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
509 // pos is outside solid?!? but why?!? never mind, just return it.
\r
510 trace_endpos = pos;
\r
511 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
516 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
\r
523 Returns a point at least 12 units away from walls
\r
524 (useful for explosion animations, although the blast is performed where it really happened)
\r
528 vector findbetterlocation (vector org, float mindist)
\r
534 vec = mindist * '1 0 0';
\r
538 traceline (org, org + vec, TRUE, world);
\r
540 if (trace_fraction < 1)
\r
542 loc = trace_endpos;
\r
543 traceline (loc, loc + vec, TRUE, world);
\r
544 if (trace_fraction >= 1)
\r
564 Returns a random number between -1.0 and 1.0
\r
567 float crandom (void)
\r
569 return 2 * (random () - 0.5);
\r
574 Angc used for animations
\r
579 float angc (float a1, float a2)
\r
602 vector NearestPointOnBox(entity box, vector org);
\r
604 void SetBrushEntityModel()
\r
606 if(self.model != "")
\r
608 precache_model(self.model);
\r
609 setmodel(self, self.model); // no precision needed
\r
611 setorigin(self, self.origin);
\r
613 setsize(self, self.mins * self.scale, self.maxs * self.scale);
\r
615 setsize(self, self.mins, self.maxs);
\r
618 void SetBrushEntityModelNoLOD()
\r
620 if(self.model != "")
\r
622 precache_model(self.model);
\r
623 setmodel(self, self.model); // no precision needed
\r
625 setorigin(self, self.origin);
\r
627 setsize(self, self.mins * self.scale, self.maxs * self.scale);
\r
629 setsize(self, self.mins, self.maxs);
\r
640 if (self.movedir != '0 0 0')
\r
641 self.movedir = normalize(self.movedir);
\r
644 makevectors (self.angles);
\r
645 self.movedir = v_forward;
\r
648 self.angles = '0 0 0';
\r
653 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
654 // to mean no restrictions, so use a yaw of 360 instead.
\r
655 if (self.movedir == '0 0 0')
\r
656 if (self.angles != '0 0 0')
\r
658 self.solid = SOLID_TRIGGER;
\r
659 SetBrushEntityModel();
\r
660 self.movetype = MOVETYPE_NONE;
\r
661 self.modelindex = 0;
\r
665 void InitSolidBSPTrigger()
\r
667 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
668 // to mean no restrictions, so use a yaw of 360 instead.
\r
669 if (self.movedir == '0 0 0')
\r
670 if (self.angles != '0 0 0')
\r
672 self.solid = SOLID_BSP;
\r
673 SetBrushEntityModel();
\r
674 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
\r
675 // self.modelindex = 0;
\r
679 float InitMovingBrushTrigger()
\r
681 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
682 // to mean no restrictions, so use a yaw of 360 instead.
\r
683 self.solid = SOLID_BSP;
\r
684 SetBrushEntityModel();
\r
685 self.movetype = MOVETYPE_PUSH;
\r
686 if(self.modelindex == 0)
\r
688 objerror("InitMovingBrushTrigger: no brushes found!");
\r