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)
20 return; // no animation was given to us! We can't use this.
22 if (anim_x == e.animstate_startframe)
23 if (anim_y == e.animstate_numframes)
24 if (anim_z == e.animstate_framerate)
29 if(anim_y == 1) // ZYM animation
30 BITXOR_ASSIGN(e.effects, EF_RESTARTANIM_BIT);
35 e.animstate_startframe = anim_x;
36 e.animstate_numframes = anim_y;
37 e.animstate_framerate = anim_z;
38 e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
39 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
40 e.animstate_looping = looping;
41 e.animstate_override = override;
42 e.frame = e.animstate_startframe;
43 e.frame1time = servertime;
46 void updateanim(entity e)
48 if (time >= e.animstate_endtime)
50 if (e.animstate_looping)
52 e.animstate_starttime = e.animstate_endtime;
53 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
55 e.animstate_override = FALSE;
57 e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
58 //print(ftos(time), " -> ", ftos(e.frame), "\n");
68 void SUB_Remove (void)
77 Applies some friction to self
81 void SUB_Friction (void)
83 self.nextthink = time;
84 if(self.flags & FL_ONGROUND)
85 self.velocity = self.velocity * (1 - frametime * self.friction);
92 Makes client invisible or removes non-client
95 void SUB_VanishOrRemove (entity ent)
97 if (ent.flags & FL_CLIENT)
112 void SUB_SetFade_Think (void)
114 self.think = SUB_SetFade_Think;
115 self.nextthink = self.fade_time;
116 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
117 if (self.alpha < 0.01)
118 SUB_VanishOrRemove(self);
119 self.alpha = bound(0.01, self.alpha, 1);
126 Fade 'ent' out when time >= 'when'
129 void SUB_SetFade (entity ent, float when, float fadetime)
131 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
134 ent.fade_rate = 1/fadetime;
135 ent.fade_time = when;
136 ent.think = SUB_SetFade_Think;
137 ent.nextthink = when;
144 calculate self.velocity and self.nextthink to reach dest from
145 self.origin traveling at speed
148 void SUB_CalcMoveDone (void)
150 // After moving, set origin to exact final destination
152 setorigin (self, self.finaldest);
153 self.velocity = '0 0 0';
159 void SUB_CalcMove_controller_think (void)
168 if(time < self.animstate_endtime) {
169 delta = self.destvec;
170 nexttick = time + sys_frametime;
172 if(nexttick < self.animstate_endtime) {
173 traveltime = self.animstate_endtime - self.animstate_starttime;
174 phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
175 phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi]
176 phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1]
177 phasepos = phasepos + 1; // correct range to [0, 2]
178 phasepos = phasepos / 2; // correct range to [0, 1]
179 nextpos = self.origin + (delta * phasepos);
181 veloc = nextpos - self.owner.origin;
182 veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
185 veloc = self.finaldest - self.owner.origin;
186 veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
188 self.owner.velocity = veloc;
189 self.nextthink = nexttick;
192 self.owner.think = self.think1;
199 void SUB_CalcMove (vector tdest, float tspeed, void() func)
206 objerror ("No speed is defined!");
209 self.finaldest = tdest;
210 self.think = SUB_CalcMoveDone;
212 if (tdest == self.origin)
214 self.velocity = '0 0 0';
215 self.nextthink = self.ltime + 0.1;
219 delta = tdest - self.origin;
220 traveltime = vlen (delta) / tspeed;
222 if (traveltime < 0.1)
224 self.velocity = '0 0 0';
225 self.nextthink = self.ltime + 0.1;
229 // Very short animations don't really show off the effect
230 // of controlled animation, so let's just use linear movement.
231 // Alternatively entities can choose to specify non-controlled movement.
232 // The only currently implemented alternative movement is linear (value 1)
233 if (traveltime < 0.15 || self.platmovetype == 1)
235 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
236 self.nextthink = self.ltime + traveltime;
240 controller = spawn();
241 controller.classname = "SUB_CalcMove_controller";
242 controller.owner = self;
243 controller.origin = self.origin; // starting point
244 controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
245 controller.destvec = delta;
246 controller.animstate_starttime = time;
247 controller.animstate_endtime = time + traveltime;
248 controller.think = SUB_CalcMove_controller_think;
249 controller.think1 = self.think;
251 // the thinking is now done by the controller
252 self.think = SUB_Null;
253 self.nextthink = self.ltime + traveltime;
261 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
268 SUB_CalcMove (tdest, tspeed, func);
277 calculate self.avelocity and self.nextthink to reach destangle from
280 The calling function should make sure self.think is valid
283 void SUB_CalcAngleMoveDone (void)
285 // After rotating, set angle to exact final angle
286 self.angles = self.finalangle;
287 self.avelocity = '0 0 0';
293 // FIXME: I fixed this function only for rotation around the main axes
294 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
300 objerror ("No speed is defined!");
302 // take the shortest distance for the angles
303 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
304 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
305 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
306 delta = destangle - self.angles;
307 traveltime = vlen (delta) / tspeed;
310 self.finalangle = destangle;
311 self.think = SUB_CalcAngleMoveDone;
313 if (traveltime < 0.1)
315 self.avelocity = '0 0 0';
316 self.nextthink = self.ltime + 0.1;
320 self.avelocity = delta * (1 / traveltime);
321 self.nextthink = self.ltime + traveltime;
324 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
331 SUB_CalcAngleMove (destangle, tspeed, func);
340 unused but required by the engine
354 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
355 Additionally it moves players back into the past before the trace and restores them afterward.
358 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
361 local float oldsolid;
363 // check whether antilagged traces are enabled
366 if (clienttype(forent) != CLIENTTYPE_REAL)
367 lag = 0; // only antilag for clients
369 // change shooter to SOLID_BBOX so the shot can hit corpses
372 oldsolid = source.dphitcontentsmask;
373 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
378 // take players back into the past
379 player = player_list;
382 antilag_takeback(player, time - lag);
383 player = player.nextplayer;
389 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
391 tracebox (v1, mi, ma, v2, nomonst, forent);
393 // restore players to current positions
396 player = player_list;
399 antilag_restore(player);
400 player = player.nextplayer;
404 // restore shooter solid type
406 source.dphitcontentsmask = oldsolid;
408 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
410 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
412 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
414 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
416 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
418 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
420 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
422 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
424 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
426 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
428 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
430 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
432 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
434 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
436 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
438 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
441 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
446 //nudge = 2 * cvar("collision_impactnudge"); // why not?
449 dir = normalize(v2 - v1);
451 pos = v1 + dir * nudge;
458 if((pos - v1) * dir >= (v2 - v1) * dir)
466 tracebox(pos, mi, ma, v2, nomonsters, forent);
471 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
472 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
473 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
474 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
479 // we started inside solid.
480 // then trace from endpos to pos
482 tracebox(t, mi, ma, pos, nomonsters, forent);
486 // t is still inside solid? bad
487 // force advance, then, and retry
488 pos = t + dir * nudge;
492 // we actually LEFT solid!
493 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
499 // pos is outside solid?!? but why?!? never mind, just return it.
501 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
507 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
513 //nudge = 2 * cvar("collision_impactnudge"); // why not?
516 dir = normalize(v2 - v1);
518 pos = v1 + dir * nudge;
522 if((pos - v1) * dir >= (v2 - v1) * dir)
529 traceline(pos, v2, nomonsters, forent);
533 // we started inside solid.
534 // then trace from endpos to pos
536 traceline(t, pos, nomonsters, forent);
539 // t is inside solid? bad
540 // force advance, then
541 pos = pos + dir * nudge;
545 // we actually LEFT solid!
546 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
552 // pos is outside solid?!? but why?!? never mind, just return it.
554 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
559 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
566 Returns a point at least 12 units away from walls
567 (useful for explosion animations, although the blast is performed where it really happened)
571 vector findbetterlocation (vector org, float mindist)
577 vec = mindist * '1 0 0';
581 traceline (org, org + vec, TRUE, world);
583 if (trace_fraction < 1)
586 traceline (loc, loc + vec, TRUE, world);
587 if (trace_fraction >= 1)
607 Returns a random number between -1.0 and 1.0
612 return 2 * (random () - 0.5);
617 Angc used for animations
622 float angc (float a1, float a2)
649 .float lodmodelindex0;
650 .float lodmodelindex1;
651 .float lodmodelindex2;
655 float LOD_customize()
659 if(autocvar_loddebug)
661 d = autocvar_loddebug;
663 self.modelindex = self.lodmodelindex0;
664 else if(d == 2 || !self.lodmodelindex2)
665 self.modelindex = self.lodmodelindex1;
667 self.modelindex = self.lodmodelindex2;
671 // TODO csqc network this so it only gets sent once
672 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
673 if(d < self.loddistance1)
674 self.modelindex = self.lodmodelindex0;
675 else if(!self.lodmodelindex2 || d < self.loddistance2)
676 self.modelindex = self.lodmodelindex1;
678 self.modelindex = self.lodmodelindex2;
683 void LOD_uncustomize()
685 self.modelindex = self.lodmodelindex0;
688 void LODmodel_attach()
692 if(!self.loddistance1)
693 self.loddistance1 = 1000;
694 if(!self.loddistance2)
695 self.loddistance2 = 2000;
696 self.lodmodelindex0 = self.modelindex;
698 if(self.lodtarget1 != "")
700 e = find(world, targetname, self.lodtarget1);
703 self.lodmodel1 = e.model;
707 if(self.lodtarget2 != "")
709 e = find(world, targetname, self.lodtarget2);
712 self.lodmodel2 = e.model;
717 if(autocvar_loddebug < 0)
719 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
722 if(self.lodmodel1 != "")
728 precache_model(self.lodmodel1);
729 setmodel(self, self.lodmodel1);
730 self.lodmodelindex1 = self.modelindex;
732 if(self.lodmodel2 != "")
734 precache_model(self.lodmodel2);
735 setmodel(self, self.lodmodel2);
736 self.lodmodelindex2 = self.modelindex;
739 self.modelindex = self.lodmodelindex0;
740 setsize(self, mi, ma);
743 if(self.lodmodelindex1)
744 if not(self.SendEntity)
745 SetCustomizer(self, LOD_customize, LOD_uncustomize);
748 void ApplyMinMaxScaleAngles(entity e)
750 if(e.angles_x != 0 || e.angles_z != 0 || self.avelocity_x != 0 || self.avelocity_z != 0) // "weird" rotation
752 e.maxs = '1 1 1' * vlen(
753 '1 0 0' * max(-e.mins_x, e.maxs_x) +
754 '0 1 0' * max(-e.mins_y, e.maxs_y) +
755 '0 0 1' * max(-e.mins_z, e.maxs_z)
759 else if(e.angles_y != 0 || self.avelocity_y != 0) // yaw only is a bit better
762 '1 0 0' * max(-e.mins_x, e.maxs_x) +
763 '0 1 0' * max(-e.mins_y, e.maxs_y)
766 e.mins_x = -e.maxs_x;
767 e.mins_y = -e.maxs_x;
770 setsize(e, e.mins * e.scale, e.maxs * e.scale);
772 setsize(e, e.mins, e.maxs);
775 void SetBrushEntityModel()
779 precache_model(self.model);
780 setmodel(self, self.model); // no precision needed
781 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
783 setorigin(self, self.origin);
784 ApplyMinMaxScaleAngles(self);
787 void SetBrushEntityModelNoLOD()
791 precache_model(self.model);
792 setmodel(self, self.model); // no precision needed
794 setorigin(self, self.origin);
795 ApplyMinMaxScaleAngles(self);
806 if (self.movedir != '0 0 0')
807 self.movedir = normalize(self.movedir);
810 makevectors (self.angles);
811 self.movedir = v_forward;
814 self.angles = '0 0 0';
819 // trigger angles are used for one-way touches. An angle of 0 is assumed
820 // to mean no restrictions, so use a yaw of 360 instead.
822 self.solid = SOLID_TRIGGER;
823 SetBrushEntityModel();
824 self.movetype = MOVETYPE_NONE;
829 void InitSolidBSPTrigger()
831 // trigger angles are used for one-way touches. An angle of 0 is assumed
832 // to mean no restrictions, so use a yaw of 360 instead.
834 self.solid = SOLID_BSP;
835 SetBrushEntityModel();
836 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
837 // self.modelindex = 0;
841 float InitMovingBrushTrigger()
843 // trigger angles are used for one-way touches. An angle of 0 is assumed
844 // to mean no restrictions, so use a yaw of 360 instead.
845 self.solid = SOLID_BSP;
846 SetBrushEntityModel();
847 self.movetype = MOVETYPE_PUSH;
848 if(self.modelindex == 0)
850 objerror("InitMovingBrushTrigger: no brushes found!");