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 // don't attempt to animate the stomach model
\r
20 if(substring(self.model, strlen(self.model) - 8 - 4, 8) == "_stomach") // - 4 is the extension
\r
23 if (anim_x == e.animstate_startframe)
\r
24 if (anim_y == e.animstate_numframes)
\r
25 if (anim_z == e.animstate_framerate)
\r
30 if(anim_y == 1) // ZYM animation
\r
31 BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
\r
36 e.animstate_startframe = anim_x;
\r
37 e.animstate_numframes = anim_y;
\r
38 e.animstate_framerate = anim_z;
\r
39 e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
\r
40 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
\r
41 e.animstate_looping = looping;
\r
42 e.animstate_override = override;
\r
43 e.frame = e.animstate_startframe;
\r
46 void updateanim(entity e)
\r
48 if (time >= e.animstate_endtime)
\r
50 if (e.animstate_looping)
\r
52 e.animstate_starttime = e.animstate_endtime;
\r
53 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
\r
55 e.animstate_override = FALSE;
\r
57 e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
\r
58 //print(ftos(time), " -> ", ftos(e.frame), "\n");
\r
61 float animparseerror;
\r
62 vector animparseline(float animfile)
\r
69 line = fgets(animfile);
\r
70 c = tokenize_console(line);
\r
73 animparseerror = TRUE;
\r
76 anim_x = stof(argv(0));
\r
77 anim_y = stof(argv(1));
\r
78 anim_z = stof(argv(2));
\r
79 // don't allow completely bogus values
\r
80 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
\r
92 void SUB_Remove (void)
\r
101 Applies some friction to self
\r
105 void SUB_Friction (void)
\r
107 self.nextthink = time;
\r
108 if(self.flags & FL_ONGROUND)
\r
109 self.velocity = self.velocity * (1 - frametime * self.friction);
\r
116 Makes client invisible or removes non-client
\r
119 void SUB_VanishOrRemove (entity ent)
\r
121 if (ent.flags & FL_CLIENT)
\r
136 void SUB_SetFade_Think (void)
\r
138 self.think = SUB_SetFade_Think;
\r
139 self.nextthink = self.fade_time;
\r
140 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
\r
141 if (self.alpha < 0.01)
\r
142 SUB_VanishOrRemove(self);
\r
143 self.alpha = bound(0.01, self.alpha, 1);
\r
150 Fade 'ent' out when time >= 'when'
\r
153 void SUB_SetFade (entity ent, float when, float fadetime)
\r
155 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
\r
158 ent.fade_rate = 1/fadetime;
\r
159 ent.fade_time = when;
\r
160 ent.think = SUB_SetFade_Think;
\r
161 ent.nextthink = when;
\r
168 calculate self.velocity and self.nextthink to reach dest from
\r
169 self.origin traveling at speed
\r
172 void SUB_CalcMoveDone (void)
\r
174 // After moving, set origin to exact final destination
\r
176 setorigin (self, self.finaldest);
\r
177 self.velocity = '0 0 0';
\r
178 self.nextthink = -1;
\r
183 void SUB_CalcMove (vector tdest, float tspeed, void() func)
\r
189 objerror ("No speed is defined!");
\r
191 self.think1 = func;
\r
192 self.finaldest = tdest;
\r
193 self.think = SUB_CalcMoveDone;
\r
195 if (tdest == self.origin)
\r
197 self.velocity = '0 0 0';
\r
198 self.nextthink = self.ltime + 0.1;
\r
202 delta = tdest - self.origin;
\r
203 traveltime = vlen (delta) / tspeed;
\r
205 if (traveltime < 0.1)
\r
207 self.velocity = '0 0 0';
\r
208 self.nextthink = self.ltime + 0.1;
\r
212 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
\r
214 self.nextthink = self.ltime + traveltime;
\r
217 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
\r
224 SUB_CalcMove (tdest, tspeed, func);
\r
233 calculate self.avelocity and self.nextthink to reach destangle from
\r
234 self.angles rotating
\r
236 The calling function should make sure self.think is valid
\r
239 void SUB_CalcAngleMoveDone (void)
\r
241 // After rotating, set angle to exact final angle
\r
242 self.angles = self.finalangle;
\r
243 self.avelocity = '0 0 0';
\r
244 self.nextthink = -1;
\r
249 // FIXME: I fixed this function only for rotation around the main axes
\r
250 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
\r
256 objerror ("No speed is defined!");
\r
258 // take the shortest distance for the angles
\r
259 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
\r
260 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
\r
261 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
\r
262 delta = destangle - self.angles;
\r
263 traveltime = vlen (delta) / tspeed;
\r
265 self.think1 = func;
\r
266 self.finalangle = destangle;
\r
267 self.think = SUB_CalcAngleMoveDone;
\r
269 if (traveltime < 0.1)
\r
271 self.avelocity = '0 0 0';
\r
272 self.nextthink = self.ltime + 0.1;
\r
276 self.avelocity = delta * (1 / traveltime);
\r
277 self.nextthink = self.ltime + traveltime;
\r
280 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
\r
287 SUB_CalcAngleMove (destangle, tspeed, func);
\r
296 unused but required by the engine
\r
310 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
\r
311 Additionally it moves players back into the past before the trace and restores them afterward.
\r
314 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
\r
316 local entity player;
\r
317 local float oldsolid;
\r
319 // check whether antilagged traces are enabled
\r
322 if (clienttype(forent) != CLIENTTYPE_REAL)
\r
323 lag = 0; // only antilag for clients
\r
325 // change shooter to SOLID_BBOX so the shot can hit corpses
\r
328 oldsolid = source.dphitcontentsmask;
\r
329 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
\r
334 // take players back into the past
\r
335 player = player_list;
\r
338 antilag_takeback(player, time - lag);
\r
339 player = player.nextplayer;
\r
345 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
\r
347 tracebox (v1, mi, ma, v2, nomonst, forent);
\r
349 // restore players to current positions
\r
352 player = player_list;
\r
355 antilag_restore(player);
\r
356 player = player.nextplayer;
\r
360 // restore shooter solid type
\r
362 source.dphitcontentsmask = oldsolid;
\r
364 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
366 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
\r
368 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
370 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
372 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
\r
374 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
\r
376 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
378 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
\r
380 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
382 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
\r
384 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
\r
386 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
388 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
\r
390 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
\r
392 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
\r
394 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
\r
397 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
\r
399 vector pos, dir, t;
\r
402 //nudge = 2 * cvar("collision_impactnudge"); // why not?
\r
405 dir = normalize(v2 - v1);
\r
407 pos = v1 + dir * nudge;
\r
414 if((pos - v1) * dir >= (v2 - v1) * dir)
\r
417 trace_fraction = 1;
\r
422 tracebox(pos, mi, ma, v2, nomonsters, forent);
\r
427 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
\r
428 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
\r
429 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
\r
430 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
\r
433 if(trace_startsolid)
\r
435 // we started inside solid.
\r
436 // then trace from endpos to pos
\r
438 tracebox(t, mi, ma, pos, nomonsters, forent);
\r
440 if(trace_startsolid)
\r
442 // t is still inside solid? bad
\r
443 // force advance, then, and retry
\r
444 pos = t + dir * nudge;
\r
448 // we actually LEFT solid!
\r
449 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
455 // pos is outside solid?!? but why?!? never mind, just return it.
\r
456 trace_endpos = pos;
\r
457 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
463 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
\r
466 vector pos, dir, t;
\r
469 //nudge = 2 * cvar("collision_impactnudge"); // why not?
\r
472 dir = normalize(v2 - v1);
\r
474 pos = v1 + dir * nudge;
\r
478 if((pos - v1) * dir >= (v2 - v1) * dir)
\r
481 trace_fraction = 1;
\r
485 traceline(pos, v2, nomonsters, forent);
\r
487 if(trace_startsolid)
\r
489 // we started inside solid.
\r
490 // then trace from endpos to pos
\r
492 traceline(t, pos, nomonsters, forent);
\r
493 if(trace_startsolid)
\r
495 // t is inside solid? bad
\r
496 // force advance, then
\r
497 pos = pos + dir * nudge;
\r
501 // we actually LEFT solid!
\r
502 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
508 // pos is outside solid?!? but why?!? never mind, just return it.
\r
509 trace_endpos = pos;
\r
510 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
\r
515 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
\r
522 Returns a point at least 12 units away from walls
\r
523 (useful for explosion animations, although the blast is performed where it really happened)
\r
527 vector findbetterlocation (vector org, float mindist)
\r
533 vec = mindist * '1 0 0';
\r
537 traceline (org, org + vec, TRUE, world);
\r
539 if (trace_fraction < 1)
\r
541 loc = trace_endpos;
\r
542 traceline (loc, loc + vec, TRUE, world);
\r
543 if (trace_fraction >= 1)
\r
563 Returns a random number between -1.0 and 1.0
\r
566 float crandom (void)
\r
568 return 2 * (random () - 0.5);
\r
573 Angc used for animations
\r
578 float angc (float a1, float a2)
\r
601 vector NearestPointOnBox(entity box, vector org);
\r
603 void SetBrushEntityModel()
\r
605 if(self.model != "")
\r
607 precache_model(self.model);
\r
608 setmodel(self, self.model); // no precision needed
\r
610 setorigin(self, self.origin);
\r
612 setsize(self, self.mins * self.scale, self.maxs * self.scale);
\r
614 setsize(self, self.mins, self.maxs);
\r
617 void SetBrushEntityModelNoLOD()
\r
619 if(self.model != "")
\r
621 precache_model(self.model);
\r
622 setmodel(self, self.model); // no precision needed
\r
624 setorigin(self, self.origin);
\r
626 setsize(self, self.mins * self.scale, self.maxs * self.scale);
\r
628 setsize(self, self.mins, self.maxs);
\r
639 if (self.movedir != '0 0 0')
\r
640 self.movedir = normalize(self.movedir);
\r
643 makevectors (self.angles);
\r
644 self.movedir = v_forward;
\r
647 self.angles = '0 0 0';
\r
652 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
653 // to mean no restrictions, so use a yaw of 360 instead.
\r
654 if (self.movedir == '0 0 0')
\r
655 if (self.angles != '0 0 0')
\r
657 self.solid = SOLID_TRIGGER;
\r
658 SetBrushEntityModel();
\r
659 self.movetype = MOVETYPE_NONE;
\r
660 self.modelindex = 0;
\r
664 void InitSolidBSPTrigger()
\r
666 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
667 // to mean no restrictions, so use a yaw of 360 instead.
\r
668 if (self.movedir == '0 0 0')
\r
669 if (self.angles != '0 0 0')
\r
671 self.solid = SOLID_BSP;
\r
672 SetBrushEntityModel();
\r
673 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
\r
674 // self.modelindex = 0;
\r
678 float InitMovingBrushTrigger()
\r
680 // trigger angles are used for one-way touches. An angle of 0 is assumed
\r
681 // to mean no restrictions, so use a yaw of 360 instead.
\r
682 self.solid = SOLID_BSP;
\r
683 SetBrushEntityModel();
\r
684 self.movetype = MOVETYPE_PUSH;
\r
685 if(self.modelindex == 0)
\r
687 objerror("InitMovingBrushTrigger: no brushes found!");
\r