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;
272 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
279 SUB_CalcMove (tdest, tspeed, func);
288 calculate self.avelocity and self.nextthink to reach destangle from
291 The calling function should make sure self.think is valid
294 void SUB_CalcAngleMoveDone (void)
296 // After rotating, set angle to exact final angle
297 self.angles = self.finalangle;
298 self.avelocity = '0 0 0';
304 // FIXME: I fixed this function only for rotation around the main axes
305 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
311 objerror ("No speed is defined!");
313 // take the shortest distance for the angles
314 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
315 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
316 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
317 delta = destangle - self.angles;
318 traveltime = vlen (delta) / tspeed;
321 self.finalangle = destangle;
322 self.think = SUB_CalcAngleMoveDone;
324 if (traveltime < 0.1)
326 self.avelocity = '0 0 0';
327 self.nextthink = self.ltime + 0.1;
331 self.avelocity = delta * (1 / traveltime);
332 self.nextthink = self.ltime + traveltime;
335 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
342 SUB_CalcAngleMove (destangle, tspeed, func);
351 unused but required by the engine
365 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
366 Additionally it moves players back into the past before the trace and restores them afterward.
369 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
372 local float oldsolid;
374 // check whether antilagged traces are enabled
377 if (clienttype(forent) != CLIENTTYPE_REAL)
378 lag = 0; // only antilag for clients
380 // change shooter to SOLID_BBOX so the shot can hit corpses
383 oldsolid = source.dphitcontentsmask;
384 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
389 // take players back into the past
390 player = player_list;
393 antilag_takeback(player, time - lag);
394 player = player.nextplayer;
400 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
402 tracebox (v1, mi, ma, v2, nomonst, forent);
404 // restore players to current positions
407 player = player_list;
410 antilag_restore(player);
411 player = player.nextplayer;
415 // restore shooter solid type
417 source.dphitcontentsmask = oldsolid;
419 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
421 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
423 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
425 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
427 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
429 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
431 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
433 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
435 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
437 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
439 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
441 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
443 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
445 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
447 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
449 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
452 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
457 //nudge = 2 * cvar("collision_impactnudge"); // why not?
460 dir = normalize(v2 - v1);
462 pos = v1 + dir * nudge;
469 if((pos - v1) * dir >= (v2 - v1) * dir)
477 tracebox(pos, mi, ma, v2, nomonsters, forent);
482 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
483 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
484 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
485 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
490 // we started inside solid.
491 // then trace from endpos to pos
493 tracebox(t, mi, ma, pos, nomonsters, forent);
497 // t is still inside solid? bad
498 // force advance, then, and retry
499 pos = t + dir * nudge;
503 // we actually LEFT solid!
504 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
510 // pos is outside solid?!? but why?!? never mind, just return it.
512 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
518 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
524 //nudge = 2 * cvar("collision_impactnudge"); // why not?
527 dir = normalize(v2 - v1);
529 pos = v1 + dir * nudge;
533 if((pos - v1) * dir >= (v2 - v1) * dir)
540 traceline(pos, v2, nomonsters, forent);
544 // we started inside solid.
545 // then trace from endpos to pos
547 traceline(t, pos, nomonsters, forent);
550 // t is inside solid? bad
551 // force advance, then
552 pos = pos + dir * nudge;
556 // we actually LEFT solid!
557 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
563 // pos is outside solid?!? but why?!? never mind, just return it.
565 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
570 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
577 Returns a point at least 12 units away from walls
578 (useful for explosion animations, although the blast is performed where it really happened)
582 vector findbetterlocation (vector org, float mindist)
588 vec = mindist * '1 0 0';
592 traceline (org, org + vec, TRUE, world);
594 if (trace_fraction < 1)
597 traceline (loc, loc + vec, TRUE, world);
598 if (trace_fraction >= 1)
618 Returns a random number between -1.0 and 1.0
623 return 2 * (random () - 0.5);
628 Angc used for animations
633 float angc (float a1, float a2)
660 .float lodmodelindex0;
661 .float lodmodelindex1;
662 .float lodmodelindex2;
666 float LOD_customize()
672 d = cvar("loddebug");
674 self.modelindex = self.lodmodelindex0;
675 else if(d == 2 || !self.lodmodelindex2)
676 self.modelindex = self.lodmodelindex1;
678 self.modelindex = self.lodmodelindex2;
682 // TODO csqc network this so it only gets sent once
683 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
684 if(d < self.loddistance1)
685 self.modelindex = self.lodmodelindex0;
686 else if(!self.lodmodelindex2 || d < self.loddistance2)
687 self.modelindex = self.lodmodelindex1;
689 self.modelindex = self.lodmodelindex2;
694 void LOD_uncustomize()
696 self.modelindex = self.lodmodelindex0;
699 void LODmodel_attach()
703 if(!self.loddistance1)
704 self.loddistance1 = 1000;
705 if(!self.loddistance2)
706 self.loddistance2 = 2000;
707 self.lodmodelindex0 = self.modelindex;
709 if(self.lodtarget1 != "")
711 e = find(world, targetname, self.lodtarget1);
714 self.lodmodel1 = e.model;
718 if(self.lodtarget2 != "")
720 e = find(world, targetname, self.lodtarget2);
723 self.lodmodel2 = e.model;
728 if(cvar("loddebug") < 0)
730 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
733 if(self.lodmodel1 != "")
739 precache_model(self.lodmodel1);
740 setmodel(self, self.lodmodel1);
741 self.lodmodelindex1 = self.modelindex;
743 if(self.lodmodel2 != "")
745 precache_model(self.lodmodel2);
746 setmodel(self, self.lodmodel2);
747 self.lodmodelindex2 = self.modelindex;
750 self.modelindex = self.lodmodelindex0;
751 setsize(self, mi, ma);
754 if(self.lodmodelindex1)
755 if not(self.SendEntity)
756 SetCustomizer(self, LOD_customize, LOD_uncustomize);
759 void SetBrushEntityModel()
763 precache_model(self.model);
764 setmodel(self, self.model); // no precision needed
765 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
767 setorigin(self, self.origin);
769 setsize(self, self.mins * self.scale, self.maxs * self.scale);
771 setsize(self, self.mins, self.maxs);
774 void SetBrushEntityModelNoLOD()
778 precache_model(self.model);
779 setmodel(self, self.model); // no precision needed
781 setorigin(self, self.origin);
783 setsize(self, self.mins * self.scale, self.maxs * self.scale);
785 setsize(self, self.mins, self.maxs);
796 if (self.movedir != '0 0 0')
797 self.movedir = normalize(self.movedir);
800 makevectors (self.angles);
801 self.movedir = v_forward;
804 self.angles = '0 0 0';
809 // trigger angles are used for one-way touches. An angle of 0 is assumed
810 // to mean no restrictions, so use a yaw of 360 instead.
811 if (self.movedir == '0 0 0')
812 if (self.angles != '0 0 0')
814 self.solid = SOLID_TRIGGER;
815 SetBrushEntityModel();
816 self.movetype = MOVETYPE_NONE;
821 void InitSolidBSPTrigger()
823 // trigger angles are used for one-way touches. An angle of 0 is assumed
824 // to mean no restrictions, so use a yaw of 360 instead.
825 if (self.movedir == '0 0 0')
826 if (self.angles != '0 0 0')
828 self.solid = SOLID_BSP;
829 SetBrushEntityModel();
830 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
831 // self.modelindex = 0;
835 float InitMovingBrushTrigger()
837 // trigger angles are used for one-way touches. An angle of 0 is assumed
838 // to mean no restrictions, so use a yaw of 360 instead.
839 self.solid = SOLID_BSP;
840 SetBrushEntityModel();
841 self.movetype = MOVETYPE_PUSH;
842 if(self.modelindex == 0)
844 objerror("InitMovingBrushTrigger: no brushes found!");