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 float animparseerror;
\r
58 vector animparseline(float animfile)
\r
65 line = fgets(animfile);
\r
66 c = tokenize_console(line);
\r
69 animparseerror = TRUE;
\r
72 anim_x = stof(argv(0));
\r
73 anim_y = stof(argv(1));
\r
74 anim_z = stof(argv(2));
\r
75 // don't allow completely bogus values
\r
76 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
\r
88 void SUB_Remove (void)
\r
97 Applies some friction to self
\r
101 void SUB_Friction (void)
\r
103 self.nextthink = time;
\r
104 if(self.flags & FL_ONGROUND)
\r
105 self.velocity = self.velocity * (1 - frametime * self.friction);
\r
112 Makes client invisible or removes non-client
\r
115 void SUB_VanishOrRemove (entity ent)
\r
117 if (ent.flags & FL_CLIENT)
\r
132 void SUB_SetFade_Think (void)
\r
134 self.think = SUB_SetFade_Think;
\r
135 self.nextthink = self.fade_time;
\r
136 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
\r
137 if (self.alpha < 0.01)
\r
138 SUB_VanishOrRemove(self);
\r
139 self.alpha = bound(0.01, self.alpha, 1);
\r
146 Fade 'ent' out when time >= 'when'
\r
149 void SUB_SetFade (entity ent, float when, float fadetime)
\r
151 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
\r
154 ent.fade_rate = 1/fadetime;
\r
155 ent.fade_time = when;
\r
156 ent.think = SUB_SetFade_Think;
\r
157 ent.nextthink = when;
\r
164 calculate self.velocity and self.nextthink to reach dest from
\r
165 self.origin traveling at speed
\r
168 void SUB_CalcMoveDone (void)
\r
170 // After moving, set origin to exact final destination
\r
172 setorigin (self, self.finaldest);
\r
173 self.velocity = '0 0 0';
\r
174 self.nextthink = -1;
\r
179 void SUB_CalcMove (vector tdest, float tspeed, void() func)
\r
185 objerror ("No speed is defined!");
\r
187 self.think1 = func;
\r
188 self.finaldest = tdest;
\r
189 self.think = SUB_CalcMoveDone;
\r
191 if (tdest == self.origin)
\r
193 self.velocity = '0 0 0';
\r
194 self.nextthink = self.ltime + 0.1;
\r
198 delta = tdest - self.origin;
\r
199 traveltime = vlen (delta) / tspeed;
\r
201 if (traveltime < 0.1)
\r
203 self.velocity = '0 0 0';
\r
204 self.nextthink = self.ltime + 0.1;
\r
208 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
\r
210 self.nextthink = self.ltime + traveltime;
\r
213 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
\r
220 SUB_CalcMove (tdest, tspeed, func);
\r
229 calculate self.avelocity and self.nextthink to reach destangle from
\r
230 self.angles rotating
\r
232 The calling function should make sure self.think is valid
\r
235 void SUB_CalcAngleMoveDone (void)
\r
237 // After rotating, set angle to exact final angle
\r
238 self.angles = self.finalangle;
\r
239 self.avelocity = '0 0 0';
\r
240 self.nextthink = -1;
\r
245 // FIXME: I fixed this function only for rotation around the main axes
\r
246 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
\r
252 objerror ("No speed is defined!");
\r
254 // take the shortest distance for the angles
\r
255 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
\r
256 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
\r
257 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
\r
258 delta = destangle - self.angles;
\r
259 traveltime = vlen (delta) / tspeed;
\r
261 self.think1 = func;
\r
262 self.finalangle = destangle;
\r
263 self.think = SUB_CalcAngleMoveDone;
\r
265 if (traveltime < 0.1)
\r
267 self.avelocity = '0 0 0';
\r
268 self.nextthink = self.ltime + 0.1;
\r
272 self.avelocity = delta * (1 / traveltime);
\r
273 self.nextthink = self.ltime + traveltime;
\r
276 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
\r
283 SUB_CalcAngleMove (destangle, tspeed, func);
\r
292 unused but required by the engine
\r
306 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
\r
307 Additionally it moves players back into the past before the trace and restores them afterward.
\r
310 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
\r
312 local entity player;
\r
313 local float oldsolid;
\r
315 // check whether antilagged traces are enabled
\r
318 if (clienttype(forent) != CLIENTTYPE_REAL)
\r
319 lag = 0; // only antilag for clients
\r
321 // change shooter to SOLID_BBOX so the shot can hit corpses
\r
324 oldsolid = source.dphitcontentsmask;
\r
325 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
\r
330 // take players back into the past
\r
331 player = player_list;
\r
334 antilag_takeback(player, time - lag);
\r
335 player = player.nextplayer;
\r
341 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
\r
343 tracebox (v1, mi, ma, v2, nomonst, forent);
\r
345 // restore players to current positions
\r
348 player = player_list;
\r
351 antilag_restore(player);
\r
352 player = player.nextplayer;
\r
356 // restore shooter solid type
\r
358 source.dphitcontentsmask = oldsolid;
\r
360 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
362 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
\r
364 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
366 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
368 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
\r
370 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
\r
372 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
374 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
\r
376 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
378 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
\r
380 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
382 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
384 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
\r
386 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
\r
388 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
390 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
\r
393 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
\r
395 vector pos, dir, t;
\r
398 //nudge = 2 * cvar("collision_impactnudge"); // why not?
\r
401 dir = normalize(v2 - v1);
\r
403 pos = v1 + dir * nudge;
\r
410 if((pos - v1) * dir >= (v2 - v1) * dir)
\r
413 trace_fraction = 1;
\r
418 tracebox(pos, mi, ma, v2, nomonsters, forent);
\r
423 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
\r
424 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
\r
425 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
\r
426 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
\r
429 if(trace_startsolid)
\r
431 // we started inside solid.
\r
432 // then trace from endpos to pos
\r
434 tracebox(t, mi, ma, pos, nomonsters, forent);
\r
436 if(trace_startsolid)
\r
438 // t is still inside solid? bad
\r
439 // force advance, then, and retry
\r
440 pos = t + dir * nudge;
\r
444 // we actually LEFT solid!
\r
445 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
451 // pos is outside solid?!? but why?!? never mind, just return it.
\r
452 trace_endpos = pos;
\r
453 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
459 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
\r
462 vector pos, dir, t;
\r
465 //nudge = 2 * cvar("collision_impactnudge"); // why not?
\r
468 dir = normalize(v2 - v1);
\r
470 pos = v1 + dir * nudge;
\r
474 if((pos - v1) * dir >= (v2 - v1) * dir)
\r
477 trace_fraction = 1;
\r
481 traceline(pos, v2, nomonsters, forent);
\r
483 if(trace_startsolid)
\r
485 // we started inside solid.
\r
486 // then trace from endpos to pos
\r
488 traceline(t, pos, nomonsters, forent);
\r
489 if(trace_startsolid)
\r
491 // t is inside solid? bad
\r
492 // force advance, then
\r
493 pos = pos + dir * nudge;
\r
497 // we actually LEFT solid!
\r
498 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
504 // pos is outside solid?!? but why?!? never mind, just return it.
\r
505 trace_endpos = pos;
\r
506 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
511 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
\r
518 Returns a point at least 12 units away from walls
\r
519 (useful for explosion animations, although the blast is performed where it really happened)
\r
523 vector findbetterlocation (vector org, float mindist)
\r
529 vec = mindist * '1 0 0';
\r
533 traceline (org, org + vec, TRUE, world);
\r
535 if (trace_fraction < 1)
\r
537 loc = trace_endpos;
\r
538 traceline (loc, loc + vec, TRUE, world);
\r
539 if (trace_fraction >= 1)
\r
559 Returns a random number between -1.0 and 1.0
\r
562 float crandom (void)
\r
564 return 2 * (random () - 0.5);
\r
569 Angc used for animations
\r
574 float angc (float a1, float a2)
\r
597 vector NearestPointOnBox(entity box, vector org);
\r
599 void SetBrushEntityModel()
\r
601 if(self.model != "")
\r
603 precache_model(self.model);
\r
604 setmodel(self, self.model); // no precision needed
\r
606 setorigin(self, self.origin);
\r
608 setsize(self, self.mins * self.scale, self.maxs * self.scale);
\r
610 setsize(self, self.mins, self.maxs);
\r
613 void SetBrushEntityModelNoLOD()
\r
615 if(self.model != "")
\r
617 precache_model(self.model);
\r
618 setmodel(self, self.model); // no precision needed
\r
620 setorigin(self, self.origin);
\r
622 setsize(self, self.mins * self.scale, self.maxs * self.scale);
\r
624 setsize(self, self.mins, self.maxs);
\r
635 if (self.movedir != '0 0 0')
\r
636 self.movedir = normalize(self.movedir);
\r
639 makevectors (self.angles);
\r
640 self.movedir = v_forward;
\r
643 self.angles = '0 0 0';
\r
648 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
649 // to mean no restrictions, so use a yaw of 360 instead.
\r
650 if (self.movedir == '0 0 0')
\r
651 if (self.angles != '0 0 0')
\r
653 self.solid = SOLID_TRIGGER;
\r
654 SetBrushEntityModel();
\r
655 self.movetype = MOVETYPE_NONE;
\r
656 self.modelindex = 0;
\r
660 void InitSolidBSPTrigger()
\r
662 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
663 // to mean no restrictions, so use a yaw of 360 instead.
\r
664 if (self.movedir == '0 0 0')
\r
665 if (self.angles != '0 0 0')
\r
667 self.solid = SOLID_BSP;
\r
668 SetBrushEntityModel();
\r
669 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
\r
670 // self.modelindex = 0;
\r
674 float InitMovingBrushTrigger()
\r
676 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
677 // to mean no restrictions, so use a yaw of 360 instead.
\r
678 self.solid = SOLID_BSP;
\r
679 SetBrushEntityModel();
\r
680 self.movetype = MOVETYPE_PUSH;
\r
681 if(self.modelindex == 0)
\r
683 objerror("InitMovingBrushTrigger: no brushes found!");
\r