2 float SUB_True() { return 1; }
3 float SUB_False() { return 0; }
5 void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;
6 void() SUB_CalcMoveDone;
7 void() SUB_CalcAngleMoveDone;
8 //void() SUB_UseTargets;
11 void spawnfunc_info_null (void)
14 // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
17 void setanim(entity e, vector anim, float looping, float override, float restart)
19 if (anim_x == e.animstate_startframe)
20 if (anim_y == e.animstate_numframes)
21 if (anim_z == e.animstate_framerate)
26 if(anim_y == 1) // ZYM animation
27 BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
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;
43 void updateanim(entity e)
45 if (time >= e.animstate_endtime)
47 if (e.animstate_looping)
49 e.animstate_starttime = e.animstate_endtime;
50 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
52 e.animstate_override = FALSE;
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");
59 vector animparseline(float animfile)
66 line = fgets(animfile);
67 c = tokenize_console(line);
70 animparseerror = TRUE;
73 anim_x = stof(argv(0));
74 anim_y = stof(argv(1));
75 anim_z = stof(argv(2));
76 // don't allow completely bogus values
77 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
89 void SUB_Remove (void)
98 Applies some friction to self
102 void SUB_Friction (void)
104 self.nextthink = time;
105 if(self.flags & FL_ONGROUND)
106 self.velocity = self.velocity * (1 - frametime * self.friction);
113 Makes client invisible or removes non-client
116 void SUB_VanishOrRemove (entity ent)
118 if (ent.flags & FL_CLIENT)
133 void SUB_SetFade_Think (void)
135 self.think = SUB_SetFade_Think;
136 self.nextthink = self.fade_time;
137 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
138 if (self.alpha < 0.01)
139 SUB_VanishOrRemove(self);
140 self.alpha = bound(0.01, self.alpha, 1);
147 Fade 'ent' out when time >= 'when'
150 void SUB_SetFade (entity ent, float when, float fadetime)
152 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
155 ent.fade_rate = 1/fadetime;
156 ent.fade_time = when;
157 ent.think = SUB_SetFade_Think;
158 ent.nextthink = when;
165 calculate self.velocity and self.nextthink to reach dest from
166 self.origin traveling at speed
169 void SUB_CalcMoveDone (void)
171 // After moving, set origin to exact final destination
173 setorigin (self, self.finaldest);
174 self.velocity = '0 0 0';
180 void SUB_CalcMove_controller_think (void)
185 if(time < self.animstate_endtime) {
186 delta = self.finaldest - self.origin;
187 traveltime = self.animstate_endtime - self.animstate_starttime;
189 self.owner.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
190 self.nextthink = time;
197 void SUB_CalcMove (vector tdest, float tspeed, void() func)
204 objerror ("No speed is defined!");
207 self.finaldest = tdest;
208 self.think = SUB_CalcMoveDone;
210 if (tdest == self.origin)
212 self.velocity = '0 0 0';
213 self.nextthink = self.ltime + 0.1;
217 delta = tdest - self.origin;
218 traveltime = vlen (delta) / tspeed;
220 if (traveltime < 0.1)
222 self.velocity = '0 0 0';
223 self.nextthink = self.ltime + 0.1;
227 controller = spawn();
228 controller.classname = "SUB_CalcMove_controller";
229 controller.owner = self;
230 controller.origin = self.origin; // starting point
231 controller.finaldest = tdest; // where do we want to end?
232 controller.animstate_starttime = self.ltime;
233 controller.animstate_endtime = self.ltime + traveltime;
234 controller.think = SUB_CalcMove_controller_think;
235 controller.nextthink = time;
237 //self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
239 //self.nextthink = self.ltime + traveltime;
242 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
249 SUB_CalcMove (tdest, tspeed, func);
258 calculate self.avelocity and self.nextthink to reach destangle from
261 The calling function should make sure self.think is valid
264 void SUB_CalcAngleMoveDone (void)
266 // After rotating, set angle to exact final angle
267 self.angles = self.finalangle;
268 self.avelocity = '0 0 0';
274 // FIXME: I fixed this function only for rotation around the main axes
275 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
281 objerror ("No speed is defined!");
283 // take the shortest distance for the angles
284 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
285 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
286 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
287 delta = destangle - self.angles;
288 traveltime = vlen (delta) / tspeed;
291 self.finalangle = destangle;
292 self.think = SUB_CalcAngleMoveDone;
294 if (traveltime < 0.1)
296 self.avelocity = '0 0 0';
297 self.nextthink = self.ltime + 0.1;
301 self.avelocity = delta * (1 / traveltime);
302 self.nextthink = self.ltime + traveltime;
305 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
312 SUB_CalcAngleMove (destangle, tspeed, func);
321 unused but required by the engine
335 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
336 Additionally it moves players back into the past before the trace and restores them afterward.
339 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
342 local float oldsolid;
344 // check whether antilagged traces are enabled
347 if (clienttype(forent) != CLIENTTYPE_REAL)
348 lag = 0; // only antilag for clients
350 // change shooter to SOLID_BBOX so the shot can hit corpses
353 oldsolid = source.dphitcontentsmask;
354 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
359 // take players back into the past
360 player = player_list;
363 antilag_takeback(player, time - lag);
364 player = player.nextplayer;
370 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
372 tracebox (v1, mi, ma, v2, nomonst, forent);
374 // restore players to current positions
377 player = player_list;
380 antilag_restore(player);
381 player = player.nextplayer;
385 // restore shooter solid type
387 source.dphitcontentsmask = oldsolid;
389 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
391 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
393 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
395 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
397 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
399 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
401 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
403 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
405 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
407 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
409 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
411 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
413 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
415 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
417 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
419 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
422 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
427 //nudge = 2 * cvar("collision_impactnudge"); // why not?
430 dir = normalize(v2 - v1);
432 pos = v1 + dir * nudge;
439 if((pos - v1) * dir >= (v2 - v1) * dir)
447 tracebox(pos, mi, ma, v2, nomonsters, forent);
452 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
453 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
454 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
455 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
460 // we started inside solid.
461 // then trace from endpos to pos
463 tracebox(t, mi, ma, pos, nomonsters, forent);
467 // t is still inside solid? bad
468 // force advance, then, and retry
469 pos = t + dir * nudge;
473 // we actually LEFT solid!
474 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
480 // pos is outside solid?!? but why?!? never mind, just return it.
482 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
488 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
494 //nudge = 2 * cvar("collision_impactnudge"); // why not?
497 dir = normalize(v2 - v1);
499 pos = v1 + dir * nudge;
503 if((pos - v1) * dir >= (v2 - v1) * dir)
510 traceline(pos, v2, nomonsters, forent);
514 // we started inside solid.
515 // then trace from endpos to pos
517 traceline(t, pos, nomonsters, forent);
520 // t is inside solid? bad
521 // force advance, then
522 pos = pos + dir * nudge;
526 // we actually LEFT solid!
527 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
533 // pos is outside solid?!? but why?!? never mind, just return it.
535 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
540 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
547 Returns a point at least 12 units away from walls
548 (useful for explosion animations, although the blast is performed where it really happened)
552 vector findbetterlocation (vector org, float mindist)
558 vec = mindist * '1 0 0';
562 traceline (org, org + vec, TRUE, world);
564 if (trace_fraction < 1)
567 traceline (loc, loc + vec, TRUE, world);
568 if (trace_fraction >= 1)
588 Returns a random number between -1.0 and 1.0
593 return 2 * (random () - 0.5);
598 Angc used for animations
603 float angc (float a1, float a2)
630 .float lodmodelindex0;
631 .float lodmodelindex1;
632 .float lodmodelindex2;
636 float LOD_customize()
642 d = cvar("loddebug");
644 self.modelindex = self.lodmodelindex0;
645 else if(d == 2 || !self.lodmodelindex2)
646 self.modelindex = self.lodmodelindex1;
648 self.modelindex = self.lodmodelindex2;
652 // TODO csqc network this so it only gets sent once
653 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
654 if(d < self.loddistance1)
655 self.modelindex = self.lodmodelindex0;
656 else if(!self.lodmodelindex2 || d < self.loddistance2)
657 self.modelindex = self.lodmodelindex1;
659 self.modelindex = self.lodmodelindex2;
664 void LOD_uncustomize()
666 self.modelindex = self.lodmodelindex0;
669 void LODmodel_attach()
673 if(!self.loddistance1)
674 self.loddistance1 = 1000;
675 if(!self.loddistance2)
676 self.loddistance2 = 2000;
677 self.lodmodelindex0 = self.modelindex;
679 if(self.lodtarget1 != "")
681 e = find(world, targetname, self.lodtarget1);
684 self.lodmodel1 = e.model;
688 if(self.lodtarget2 != "")
690 e = find(world, targetname, self.lodtarget2);
693 self.lodmodel2 = e.model;
698 if(cvar("loddebug") < 0)
700 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
703 if(self.lodmodel1 != "")
709 precache_model(self.lodmodel1);
710 setmodel(self, self.lodmodel1);
711 self.lodmodelindex1 = self.modelindex;
713 if(self.lodmodel2 != "")
715 precache_model(self.lodmodel2);
716 setmodel(self, self.lodmodel2);
717 self.lodmodelindex2 = self.modelindex;
720 self.modelindex = self.lodmodelindex0;
721 setsize(self, mi, ma);
724 if(self.lodmodelindex1)
725 if not(self.SendEntity)
726 SetCustomizer(self, LOD_customize, LOD_uncustomize);
729 void SetBrushEntityModel()
733 precache_model(self.model);
734 setmodel(self, self.model); // no precision needed
735 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
737 setorigin(self, self.origin);
739 setsize(self, self.mins * self.scale, self.maxs * self.scale);
741 setsize(self, self.mins, self.maxs);
744 void SetBrushEntityModelNoLOD()
748 precache_model(self.model);
749 setmodel(self, self.model); // no precision needed
751 setorigin(self, self.origin);
753 setsize(self, self.mins * self.scale, self.maxs * self.scale);
755 setsize(self, self.mins, self.maxs);
766 if (self.movedir != '0 0 0')
767 self.movedir = normalize(self.movedir);
770 makevectors (self.angles);
771 self.movedir = v_forward;
774 self.angles = '0 0 0';
779 // trigger angles are used for one-way touches. An angle of 0 is assumed
780 // to mean no restrictions, so use a yaw of 360 instead.
781 if (self.movedir == '0 0 0')
782 if (self.angles != '0 0 0')
784 self.solid = SOLID_TRIGGER;
785 SetBrushEntityModel();
786 self.movetype = MOVETYPE_NONE;
791 void InitSolidBSPTrigger()
793 // trigger angles are used for one-way touches. An angle of 0 is assumed
794 // to mean no restrictions, so use a yaw of 360 instead.
795 if (self.movedir == '0 0 0')
796 if (self.angles != '0 0 0')
798 self.solid = SOLID_BSP;
799 SetBrushEntityModel();
800 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
801 // self.modelindex = 0;
805 float InitMovingBrushTrigger()
807 // trigger angles are used for one-way touches. An angle of 0 is assumed
808 // to mean no restrictions, so use a yaw of 360 instead.
809 self.solid = SOLID_BSP;
810 SetBrushEntityModel();
811 self.movetype = MOVETYPE_PUSH;
812 if(self.modelindex == 0)
814 objerror("InitMovingBrushTrigger: no brushes found!");