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 (vector tdest, float tspeed, void() func)
186 objerror ("No speed is defined!");
189 self.finaldest = tdest;
190 self.think = SUB_CalcMoveDone;
192 if (tdest == self.origin)
194 self.velocity = '0 0 0';
195 self.nextthink = self.ltime + 0.1;
199 delta = tdest - self.origin;
200 traveltime = vlen (delta) / tspeed;
202 if (traveltime < 0.1)
204 self.velocity = '0 0 0';
205 self.nextthink = self.ltime + 0.1;
209 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
211 self.nextthink = self.ltime + traveltime;
214 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
221 SUB_CalcMove (tdest, tspeed, func);
230 calculate self.avelocity and self.nextthink to reach destangle from
233 The calling function should make sure self.think is valid
236 void SUB_CalcAngleMoveDone (void)
238 // After rotating, set angle to exact final angle
239 self.angles = self.finalangle;
240 self.avelocity = '0 0 0';
246 // FIXME: I fixed this function only for rotation around the main axes
247 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
253 objerror ("No speed is defined!");
255 // take the shortest distance for the angles
256 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
257 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
258 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
259 delta = destangle - self.angles;
260 traveltime = vlen (delta) / tspeed;
263 self.finalangle = destangle;
264 self.think = SUB_CalcAngleMoveDone;
266 if (traveltime < 0.1)
268 self.avelocity = '0 0 0';
269 self.nextthink = self.ltime + 0.1;
273 self.avelocity = delta * (1 / traveltime);
274 self.nextthink = self.ltime + traveltime;
277 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
284 SUB_CalcAngleMove (destangle, tspeed, func);
293 unused but required by the engine
307 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
308 Additionally it moves players back into the past before the trace and restores them afterward.
311 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
314 local float oldsolid;
316 // check whether antilagged traces are enabled
319 if (clienttype(forent) != CLIENTTYPE_REAL)
320 lag = 0; // only antilag for clients
322 // change shooter to SOLID_BBOX so the shot can hit corpses
325 oldsolid = source.dphitcontentsmask;
326 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
331 // take players back into the past
332 player = player_list;
335 antilag_takeback(player, time - lag);
336 player = player.nextplayer;
342 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
344 tracebox (v1, mi, ma, v2, nomonst, forent);
346 // restore players to current positions
349 player = player_list;
352 antilag_restore(player);
353 player = player.nextplayer;
357 // restore shooter solid type
359 source.dphitcontentsmask = oldsolid;
361 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
363 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
365 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
367 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
369 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
371 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
373 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
375 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
377 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
379 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
381 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
383 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
385 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
387 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
389 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
391 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
394 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
399 //nudge = 2 * cvar("collision_impactnudge"); // why not?
402 dir = normalize(v2 - v1);
404 pos = v1 + dir * nudge;
411 if((pos - v1) * dir >= (v2 - v1) * dir)
419 tracebox(pos, mi, ma, v2, nomonsters, forent);
424 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
425 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
426 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
427 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
432 // we started inside solid.
433 // then trace from endpos to pos
435 tracebox(t, mi, ma, pos, nomonsters, forent);
439 // t is still inside solid? bad
440 // force advance, then, and retry
441 pos = t + dir * nudge;
445 // we actually LEFT solid!
446 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
452 // pos is outside solid?!? but why?!? never mind, just return it.
454 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
460 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
466 //nudge = 2 * cvar("collision_impactnudge"); // why not?
469 dir = normalize(v2 - v1);
471 pos = v1 + dir * nudge;
475 if((pos - v1) * dir >= (v2 - v1) * dir)
482 traceline(pos, v2, nomonsters, forent);
486 // we started inside solid.
487 // then trace from endpos to pos
489 traceline(t, pos, nomonsters, forent);
492 // t is inside solid? bad
493 // force advance, then
494 pos = pos + dir * nudge;
498 // we actually LEFT solid!
499 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
505 // pos is outside solid?!? but why?!? never mind, just return it.
507 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
512 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
519 Returns a point at least 12 units away from walls
520 (useful for explosion animations, although the blast is performed where it really happened)
524 vector findbetterlocation (vector org, float mindist)
530 vec = mindist * '1 0 0';
534 traceline (org, org + vec, TRUE, world);
536 if (trace_fraction < 1)
539 traceline (loc, loc + vec, TRUE, world);
540 if (trace_fraction >= 1)
560 Returns a random number between -1.0 and 1.0
565 return 2 * (random () - 0.5);
570 Angc used for animations
575 float angc (float a1, float a2)
602 .float lodmodelindex0;
603 .float lodmodelindex1;
604 .float lodmodelindex2;
608 float LOD_customize()
612 if(autocvar_loddebug)
614 d = autocvar_loddebug;
616 self.modelindex = self.lodmodelindex0;
617 else if(d == 2 || !self.lodmodelindex2)
618 self.modelindex = self.lodmodelindex1;
620 self.modelindex = self.lodmodelindex2;
624 // TODO csqc network this so it only gets sent once
625 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
626 if(d < self.loddistance1)
627 self.modelindex = self.lodmodelindex0;
628 else if(!self.lodmodelindex2 || d < self.loddistance2)
629 self.modelindex = self.lodmodelindex1;
631 self.modelindex = self.lodmodelindex2;
636 void LOD_uncustomize()
638 self.modelindex = self.lodmodelindex0;
641 void LODmodel_attach()
645 if(!self.loddistance1)
646 self.loddistance1 = 1000;
647 if(!self.loddistance2)
648 self.loddistance2 = 2000;
649 self.lodmodelindex0 = self.modelindex;
651 if(self.lodtarget1 != "")
653 e = find(world, targetname, self.lodtarget1);
656 self.lodmodel1 = e.model;
660 if(self.lodtarget2 != "")
662 e = find(world, targetname, self.lodtarget2);
665 self.lodmodel2 = e.model;
670 if(autocvar_loddebug < 0)
672 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
675 if(self.lodmodel1 != "")
681 precache_model(self.lodmodel1);
682 setmodel(self, self.lodmodel1);
683 self.lodmodelindex1 = self.modelindex;
685 if(self.lodmodel2 != "")
687 precache_model(self.lodmodel2);
688 setmodel(self, self.lodmodel2);
689 self.lodmodelindex2 = self.modelindex;
692 self.modelindex = self.lodmodelindex0;
693 setsize(self, mi, ma);
696 if(self.lodmodelindex1)
697 if not(self.SendEntity)
698 SetCustomizer(self, LOD_customize, LOD_uncustomize);
701 void ApplyMinMaxScaleAngles(entity e)
703 if(e.angles_x != 0 || e.angles_z != 0 || self.avelocity_x != 0 || self.avelocity_z != 0) // "weird" rotation
705 e.maxs = '1 1 1' * vlen(
706 '1 0 0' * max(-e.mins_x, e.maxs_x) +
707 '0 1 0' * max(-e.mins_y, e.maxs_y) +
708 '0 0 1' * max(-e.mins_z, e.maxs_z)
712 else if(e.angles_y != 0 || self.avelocity_y != 0) // yaw only is a bit better
715 '1 0 0' * max(-e.mins_x, e.maxs_x) +
716 '0 1 0' * max(-e.mins_y, e.maxs_y)
719 e.mins_x = -e.maxs_x;
720 e.mins_y = -e.maxs_x;
723 setsize(e, e.mins * e.scale, e.maxs * e.scale);
725 setsize(e, e.mins, e.maxs);
728 void SetBrushEntityModel()
732 precache_model(self.model);
733 setmodel(self, self.model); // no precision needed
734 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
736 setorigin(self, self.origin);
737 ApplyMinMaxScaleAngles(self);
740 void SetBrushEntityModelNoLOD()
744 precache_model(self.model);
745 setmodel(self, self.model); // no precision needed
747 setorigin(self, self.origin);
748 ApplyMinMaxScaleAngles(self);
759 if (self.movedir != '0 0 0')
760 self.movedir = normalize(self.movedir);
763 makevectors (self.angles);
764 self.movedir = v_forward;
767 self.angles = '0 0 0';
772 // trigger angles are used for one-way touches. An angle of 0 is assumed
773 // to mean no restrictions, so use a yaw of 360 instead.
775 self.solid = SOLID_TRIGGER;
776 SetBrushEntityModel();
777 self.movetype = MOVETYPE_NONE;
782 void InitSolidBSPTrigger()
784 // trigger angles are used for one-way touches. An angle of 0 is assumed
785 // to mean no restrictions, so use a yaw of 360 instead.
787 self.solid = SOLID_BSP;
788 SetBrushEntityModel();
789 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
790 // self.modelindex = 0;
794 float InitMovingBrushTrigger()
796 // trigger angles are used for one-way touches. An angle of 0 is assumed
797 // to mean no restrictions, so use a yaw of 360 instead.
798 self.solid = SOLID_BSP;
799 SetBrushEntityModel();
800 self.movetype = MOVETYPE_PUSH;
801 if(self.modelindex == 0)
803 objerror("InitMovingBrushTrigger: no brushes found!");