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)
187 if(time < self.animstate_endtime) {
188 delta = self.finaldest - self.origin;
189 traveltime = self.animstate_endtime - self.animstate_starttime;
190 movephase = (time - self.animstate_starttime) / traveltime;
192 //bprint(ftos(movephase));
195 // TODO: Don't mess with the velocity, instead compute where
196 // we want to be next tick and compute velocity from that
198 veloc = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
200 // scale velocity with pi/2 so integrated over time we
201 // still have the original velocity
202 veloc = veloc * 1.5708;
204 // scale velocity with sin(phase)
205 phasepos = movephase * 3.1416; // phase * pi
206 phasepos = sin(phasepos);
207 veloc = veloc * phasepos;
209 self.owner.velocity = veloc;
210 self.nextthink = time + 0.1;
214 void SUB_CalcMove (vector tdest, float tspeed, void() func)
221 objerror ("No speed is defined!");
224 self.finaldest = tdest;
225 self.think = SUB_CalcMoveDone;
227 if (tdest == self.origin)
229 self.velocity = '0 0 0';
230 self.nextthink = self.ltime + 0.1;
234 delta = tdest - self.origin;
235 traveltime = vlen (delta) / tspeed;
237 if (traveltime < 0.1)
239 self.velocity = '0 0 0';
240 self.nextthink = self.ltime + 0.1;
244 controller = spawn();
245 controller.classname = "SUB_CalcMove_controller";
246 controller.owner = self;
247 controller.origin = self.origin; // starting point
248 controller.finaldest = tdest; // where do we want to end?
249 controller.animstate_starttime = time;
250 controller.animstate_endtime = time + traveltime;
251 controller.think = SUB_CalcMove_controller_think;
253 // let the controller handle the velocity compuation
254 //self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
255 self.nextthink = self.ltime + traveltime;
262 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
269 SUB_CalcMove (tdest, tspeed, func);
278 calculate self.avelocity and self.nextthink to reach destangle from
281 The calling function should make sure self.think is valid
284 void SUB_CalcAngleMoveDone (void)
286 // After rotating, set angle to exact final angle
287 self.angles = self.finalangle;
288 self.avelocity = '0 0 0';
294 // FIXME: I fixed this function only for rotation around the main axes
295 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
301 objerror ("No speed is defined!");
303 // take the shortest distance for the angles
304 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
305 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
306 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
307 delta = destangle - self.angles;
308 traveltime = vlen (delta) / tspeed;
311 self.finalangle = destangle;
312 self.think = SUB_CalcAngleMoveDone;
314 if (traveltime < 0.1)
316 self.avelocity = '0 0 0';
317 self.nextthink = self.ltime + 0.1;
321 self.avelocity = delta * (1 / traveltime);
322 self.nextthink = self.ltime + traveltime;
325 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
332 SUB_CalcAngleMove (destangle, tspeed, func);
341 unused but required by the engine
355 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
356 Additionally it moves players back into the past before the trace and restores them afterward.
359 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
362 local float oldsolid;
364 // check whether antilagged traces are enabled
367 if (clienttype(forent) != CLIENTTYPE_REAL)
368 lag = 0; // only antilag for clients
370 // change shooter to SOLID_BBOX so the shot can hit corpses
373 oldsolid = source.dphitcontentsmask;
374 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
379 // take players back into the past
380 player = player_list;
383 antilag_takeback(player, time - lag);
384 player = player.nextplayer;
390 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
392 tracebox (v1, mi, ma, v2, nomonst, forent);
394 // restore players to current positions
397 player = player_list;
400 antilag_restore(player);
401 player = player.nextplayer;
405 // restore shooter solid type
407 source.dphitcontentsmask = oldsolid;
409 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
411 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
413 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
415 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
417 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
419 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
421 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
423 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
425 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
427 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
429 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
431 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
433 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
435 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
437 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
439 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
442 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
447 //nudge = 2 * cvar("collision_impactnudge"); // why not?
450 dir = normalize(v2 - v1);
452 pos = v1 + dir * nudge;
459 if((pos - v1) * dir >= (v2 - v1) * dir)
467 tracebox(pos, mi, ma, v2, nomonsters, forent);
472 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
473 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
474 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
475 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
480 // we started inside solid.
481 // then trace from endpos to pos
483 tracebox(t, mi, ma, pos, nomonsters, forent);
487 // t is still inside solid? bad
488 // force advance, then, and retry
489 pos = t + dir * nudge;
493 // we actually LEFT solid!
494 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
500 // pos is outside solid?!? but why?!? never mind, just return it.
502 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
508 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
514 //nudge = 2 * cvar("collision_impactnudge"); // why not?
517 dir = normalize(v2 - v1);
519 pos = v1 + dir * nudge;
523 if((pos - v1) * dir >= (v2 - v1) * dir)
530 traceline(pos, v2, nomonsters, forent);
534 // we started inside solid.
535 // then trace from endpos to pos
537 traceline(t, pos, nomonsters, forent);
540 // t is inside solid? bad
541 // force advance, then
542 pos = pos + dir * nudge;
546 // we actually LEFT solid!
547 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
553 // pos is outside solid?!? but why?!? never mind, just return it.
555 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
560 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
567 Returns a point at least 12 units away from walls
568 (useful for explosion animations, although the blast is performed where it really happened)
572 vector findbetterlocation (vector org, float mindist)
578 vec = mindist * '1 0 0';
582 traceline (org, org + vec, TRUE, world);
584 if (trace_fraction < 1)
587 traceline (loc, loc + vec, TRUE, world);
588 if (trace_fraction >= 1)
608 Returns a random number between -1.0 and 1.0
613 return 2 * (random () - 0.5);
618 Angc used for animations
623 float angc (float a1, float a2)
650 .float lodmodelindex0;
651 .float lodmodelindex1;
652 .float lodmodelindex2;
656 float LOD_customize()
662 d = cvar("loddebug");
664 self.modelindex = self.lodmodelindex0;
665 else if(d == 2 || !self.lodmodelindex2)
666 self.modelindex = self.lodmodelindex1;
668 self.modelindex = self.lodmodelindex2;
672 // TODO csqc network this so it only gets sent once
673 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
674 if(d < self.loddistance1)
675 self.modelindex = self.lodmodelindex0;
676 else if(!self.lodmodelindex2 || d < self.loddistance2)
677 self.modelindex = self.lodmodelindex1;
679 self.modelindex = self.lodmodelindex2;
684 void LOD_uncustomize()
686 self.modelindex = self.lodmodelindex0;
689 void LODmodel_attach()
693 if(!self.loddistance1)
694 self.loddistance1 = 1000;
695 if(!self.loddistance2)
696 self.loddistance2 = 2000;
697 self.lodmodelindex0 = self.modelindex;
699 if(self.lodtarget1 != "")
701 e = find(world, targetname, self.lodtarget1);
704 self.lodmodel1 = e.model;
708 if(self.lodtarget2 != "")
710 e = find(world, targetname, self.lodtarget2);
713 self.lodmodel2 = e.model;
718 if(cvar("loddebug") < 0)
720 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
723 if(self.lodmodel1 != "")
729 precache_model(self.lodmodel1);
730 setmodel(self, self.lodmodel1);
731 self.lodmodelindex1 = self.modelindex;
733 if(self.lodmodel2 != "")
735 precache_model(self.lodmodel2);
736 setmodel(self, self.lodmodel2);
737 self.lodmodelindex2 = self.modelindex;
740 self.modelindex = self.lodmodelindex0;
741 setsize(self, mi, ma);
744 if(self.lodmodelindex1)
745 if not(self.SendEntity)
746 SetCustomizer(self, LOD_customize, LOD_uncustomize);
749 void SetBrushEntityModel()
753 precache_model(self.model);
754 setmodel(self, self.model); // no precision needed
755 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
757 setorigin(self, self.origin);
759 setsize(self, self.mins * self.scale, self.maxs * self.scale);
761 setsize(self, self.mins, self.maxs);
764 void SetBrushEntityModelNoLOD()
768 precache_model(self.model);
769 setmodel(self, self.model); // no precision needed
771 setorigin(self, self.origin);
773 setsize(self, self.mins * self.scale, self.maxs * self.scale);
775 setsize(self, self.mins, self.maxs);
786 if (self.movedir != '0 0 0')
787 self.movedir = normalize(self.movedir);
790 makevectors (self.angles);
791 self.movedir = v_forward;
794 self.angles = '0 0 0';
799 // trigger angles are used for one-way touches. An angle of 0 is assumed
800 // to mean no restrictions, so use a yaw of 360 instead.
801 if (self.movedir == '0 0 0')
802 if (self.angles != '0 0 0')
804 self.solid = SOLID_TRIGGER;
805 SetBrushEntityModel();
806 self.movetype = MOVETYPE_NONE;
811 void InitSolidBSPTrigger()
813 // trigger angles are used for one-way touches. An angle of 0 is assumed
814 // to mean no restrictions, so use a yaw of 360 instead.
815 if (self.movedir == '0 0 0')
816 if (self.angles != '0 0 0')
818 self.solid = SOLID_BSP;
819 SetBrushEntityModel();
820 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
821 // self.modelindex = 0;
825 float InitMovingBrushTrigger()
827 // trigger angles are used for one-way touches. An angle of 0 is assumed
828 // to mean no restrictions, so use a yaw of 360 instead.
829 self.solid = SOLID_BSP;
830 SetBrushEntityModel();
831 self.movetype = MOVETYPE_PUSH;
832 if(self.modelindex == 0)
834 objerror("InitMovingBrushTrigger: no brushes found!");