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)
188 if(time < self.animstate_endtime) {
189 delta = self.destvec;
190 traveltime = self.animstate_endtime - self.animstate_starttime;
191 movephase = (time - self.animstate_starttime) / traveltime;
193 //bprint(ftos(movephase));
196 // TODO: Don't mess with the velocity, instead compute where
197 // we want to be next tick and compute velocity from that
199 veloc = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
201 // scale velocity with pi/2 so integrated over time we
202 // still have the original velocity
203 veloc = veloc * 1.5708;
205 // scale velocity with sin(phase)
206 phasepos = movephase * 3.1416; // phase * pi
207 phasepos = sin(phasepos);
208 veloc = veloc * phasepos;
210 self.owner.velocity = veloc;
211 self.nextthink = time + 0.1;
214 self.owner.think = self.think1;
221 void SUB_CalcMove (vector tdest, float tspeed, void() func)
228 objerror ("No speed is defined!");
231 self.finaldest = tdest;
232 self.think = SUB_CalcMoveDone;
234 if (tdest == self.origin)
236 self.velocity = '0 0 0';
237 self.nextthink = self.ltime + 0.1;
241 delta = tdest - self.origin;
242 traveltime = vlen (delta) / tspeed;
244 if (traveltime < 0.1)
246 self.velocity = '0 0 0';
247 self.nextthink = self.ltime + 0.1;
251 controller = spawn();
252 controller.classname = "SUB_CalcMove_controller";
253 controller.owner = self;
254 controller.origin = self.origin; // starting point
255 controller.finaldest = tdest; // where do we want to end?
256 controller.destvec = delta;
257 controller.animstate_starttime = time;
258 controller.animstate_endtime = time + traveltime;
259 controller.think = SUB_CalcMove_controller_think;
260 controller.think1 = self.think;
262 // let the controller handle the velocity compuation
263 //self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
264 self.think = SUB_Null;
265 self.nextthink = self.ltime + traveltime;
273 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
280 SUB_CalcMove (tdest, tspeed, func);
289 calculate self.avelocity and self.nextthink to reach destangle from
292 The calling function should make sure self.think is valid
295 void SUB_CalcAngleMoveDone (void)
297 // After rotating, set angle to exact final angle
298 self.angles = self.finalangle;
299 self.avelocity = '0 0 0';
305 // FIXME: I fixed this function only for rotation around the main axes
306 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
312 objerror ("No speed is defined!");
314 // take the shortest distance for the angles
315 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
316 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
317 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
318 delta = destangle - self.angles;
319 traveltime = vlen (delta) / tspeed;
322 self.finalangle = destangle;
323 self.think = SUB_CalcAngleMoveDone;
325 if (traveltime < 0.1)
327 self.avelocity = '0 0 0';
328 self.nextthink = self.ltime + 0.1;
332 self.avelocity = delta * (1 / traveltime);
333 self.nextthink = self.ltime + traveltime;
336 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
343 SUB_CalcAngleMove (destangle, tspeed, func);
352 unused but required by the engine
366 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
367 Additionally it moves players back into the past before the trace and restores them afterward.
370 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
373 local float oldsolid;
375 // check whether antilagged traces are enabled
378 if (clienttype(forent) != CLIENTTYPE_REAL)
379 lag = 0; // only antilag for clients
381 // change shooter to SOLID_BBOX so the shot can hit corpses
384 oldsolid = source.dphitcontentsmask;
385 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
390 // take players back into the past
391 player = player_list;
394 antilag_takeback(player, time - lag);
395 player = player.nextplayer;
401 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
403 tracebox (v1, mi, ma, v2, nomonst, forent);
405 // restore players to current positions
408 player = player_list;
411 antilag_restore(player);
412 player = player.nextplayer;
416 // restore shooter solid type
418 source.dphitcontentsmask = oldsolid;
420 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
422 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
424 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
426 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
428 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
430 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
432 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
434 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
436 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
438 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
440 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
442 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
444 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
446 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
448 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
450 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
453 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
458 //nudge = 2 * cvar("collision_impactnudge"); // why not?
461 dir = normalize(v2 - v1);
463 pos = v1 + dir * nudge;
470 if((pos - v1) * dir >= (v2 - v1) * dir)
478 tracebox(pos, mi, ma, v2, nomonsters, forent);
483 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
484 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
485 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
486 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
491 // we started inside solid.
492 // then trace from endpos to pos
494 tracebox(t, mi, ma, pos, nomonsters, forent);
498 // t is still inside solid? bad
499 // force advance, then, and retry
500 pos = t + dir * nudge;
504 // we actually LEFT solid!
505 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
511 // pos is outside solid?!? but why?!? never mind, just return it.
513 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
519 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
525 //nudge = 2 * cvar("collision_impactnudge"); // why not?
528 dir = normalize(v2 - v1);
530 pos = v1 + dir * nudge;
534 if((pos - v1) * dir >= (v2 - v1) * dir)
541 traceline(pos, v2, nomonsters, forent);
545 // we started inside solid.
546 // then trace from endpos to pos
548 traceline(t, pos, nomonsters, forent);
551 // t is inside solid? bad
552 // force advance, then
553 pos = pos + dir * nudge;
557 // we actually LEFT solid!
558 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
564 // pos is outside solid?!? but why?!? never mind, just return it.
566 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
571 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
578 Returns a point at least 12 units away from walls
579 (useful for explosion animations, although the blast is performed where it really happened)
583 vector findbetterlocation (vector org, float mindist)
589 vec = mindist * '1 0 0';
593 traceline (org, org + vec, TRUE, world);
595 if (trace_fraction < 1)
598 traceline (loc, loc + vec, TRUE, world);
599 if (trace_fraction >= 1)
619 Returns a random number between -1.0 and 1.0
624 return 2 * (random () - 0.5);
629 Angc used for animations
634 float angc (float a1, float a2)
661 .float lodmodelindex0;
662 .float lodmodelindex1;
663 .float lodmodelindex2;
667 float LOD_customize()
673 d = cvar("loddebug");
675 self.modelindex = self.lodmodelindex0;
676 else if(d == 2 || !self.lodmodelindex2)
677 self.modelindex = self.lodmodelindex1;
679 self.modelindex = self.lodmodelindex2;
683 // TODO csqc network this so it only gets sent once
684 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
685 if(d < self.loddistance1)
686 self.modelindex = self.lodmodelindex0;
687 else if(!self.lodmodelindex2 || d < self.loddistance2)
688 self.modelindex = self.lodmodelindex1;
690 self.modelindex = self.lodmodelindex2;
695 void LOD_uncustomize()
697 self.modelindex = self.lodmodelindex0;
700 void LODmodel_attach()
704 if(!self.loddistance1)
705 self.loddistance1 = 1000;
706 if(!self.loddistance2)
707 self.loddistance2 = 2000;
708 self.lodmodelindex0 = self.modelindex;
710 if(self.lodtarget1 != "")
712 e = find(world, targetname, self.lodtarget1);
715 self.lodmodel1 = e.model;
719 if(self.lodtarget2 != "")
721 e = find(world, targetname, self.lodtarget2);
724 self.lodmodel2 = e.model;
729 if(cvar("loddebug") < 0)
731 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
734 if(self.lodmodel1 != "")
740 precache_model(self.lodmodel1);
741 setmodel(self, self.lodmodel1);
742 self.lodmodelindex1 = self.modelindex;
744 if(self.lodmodel2 != "")
746 precache_model(self.lodmodel2);
747 setmodel(self, self.lodmodel2);
748 self.lodmodelindex2 = self.modelindex;
751 self.modelindex = self.lodmodelindex0;
752 setsize(self, mi, ma);
755 if(self.lodmodelindex1)
756 if not(self.SendEntity)
757 SetCustomizer(self, LOD_customize, LOD_uncustomize);
760 void SetBrushEntityModel()
764 precache_model(self.model);
765 setmodel(self, self.model); // no precision needed
766 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
768 setorigin(self, self.origin);
770 setsize(self, self.mins * self.scale, self.maxs * self.scale);
772 setsize(self, self.mins, self.maxs);
775 void SetBrushEntityModelNoLOD()
779 precache_model(self.model);
780 setmodel(self, self.model); // no precision needed
782 setorigin(self, self.origin);
784 setsize(self, self.mins * self.scale, self.maxs * self.scale);
786 setsize(self, self.mins, self.maxs);
797 if (self.movedir != '0 0 0')
798 self.movedir = normalize(self.movedir);
801 makevectors (self.angles);
802 self.movedir = v_forward;
805 self.angles = '0 0 0';
810 // trigger angles are used for one-way touches. An angle of 0 is assumed
811 // to mean no restrictions, so use a yaw of 360 instead.
812 if (self.movedir == '0 0 0')
813 if (self.angles != '0 0 0')
815 self.solid = SOLID_TRIGGER;
816 SetBrushEntityModel();
817 self.movetype = MOVETYPE_NONE;
822 void InitSolidBSPTrigger()
824 // trigger angles are used for one-way touches. An angle of 0 is assumed
825 // to mean no restrictions, so use a yaw of 360 instead.
826 if (self.movedir == '0 0 0')
827 if (self.angles != '0 0 0')
829 self.solid = SOLID_BSP;
830 SetBrushEntityModel();
831 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
832 // self.modelindex = 0;
836 float InitMovingBrushTrigger()
838 // trigger angles are used for one-way touches. An angle of 0 is assumed
839 // to mean no restrictions, so use a yaw of 360 instead.
840 self.solid = SOLID_BSP;
841 SetBrushEntityModel();
842 self.movetype = MOVETYPE_PUSH;
843 if(self.modelindex == 0)
845 objerror("InitMovingBrushTrigger: no brushes found!");