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)
116 self.think = SUB_SetFade_Think;
117 self.nextthink = time;
118 self.alpha -= frametime * self.fade_rate;
119 if (self.alpha < 0.01)
120 SUB_VanishOrRemove(self);
122 self.nextthink = time;
129 Fade 'ent' out when time >= 'when'
132 void SUB_SetFade (entity ent, float when, float fadetime)
134 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
137 ent.fade_rate = 1/fadetime;
138 ent.think = SUB_SetFade_Think;
139 ent.nextthink = when;
146 calculate self.velocity and self.nextthink to reach dest from
147 self.origin traveling at speed
150 void SUB_CalcMoveDone (void)
152 // After moving, set origin to exact final destination
154 setorigin (self, self.finaldest);
155 self.velocity = '0 0 0';
161 void SUB_CalcMove_controller_think (void)
171 delta = self.destvec;
172 delta2 = self.destvec2;
173 if(time < self.animstate_endtime) {
174 nexttick = time + sys_frametime;
176 traveltime = self.animstate_endtime - self.animstate_starttime;
177 phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
178 if(self.platmovetype != 1)
180 phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi]
181 phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1]
182 phasepos = phasepos + 1; // correct range to [0, 2]
183 phasepos = phasepos / 2; // correct range to [0, 1]
185 nextpos = self.origin + (delta * phasepos) + (delta2 * phasepos * phasepos);
186 // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning)
188 if(nexttick < self.animstate_endtime) {
189 veloc = nextpos - self.owner.origin;
190 veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
192 veloc = self.finaldest - self.owner.origin;
193 veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
195 self.owner.velocity = veloc;
196 self.nextthink = nexttick;
198 // derivative: delta + 2 * delta2 (e.g. for angle positioning)
200 self.owner.think = self.think1;
207 void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest)
209 // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
210 // 2 * control * t - 2 * control * t * t + dest * t * t
211 // 2 * control * t + (dest - 2 * control) * t * t
213 controller.origin = org; // starting point
217 controller.destvec = 2 * control; // control point
218 controller.destvec2 = dest - 2 * control; // quadratic part required to reach end point
221 void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest)
223 // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + dest * t * t
224 // 2 * control * t - 2 * control * t * t + dest * t * t
225 // 2 * control * t + (dest - 2 * control) * t * t
227 controller.origin = org; // starting point
230 controller.destvec = dest; // end point
231 controller.destvec2 = '0 0 0';
234 void SUB_CalcMove_Bezier (vector tcontrol, vector tdest, float tspeed, void() func)
240 objerror ("No speed is defined!");
243 self.finaldest = tdest;
244 self.think = SUB_CalcMoveDone;
246 if(tspeed > 0) // positive: start speed
247 traveltime = 2 * vlen(tcontrol - self.origin) / tspeed;
248 else // negative: end speed
249 traveltime = 2 * vlen(tcontrol - tdest) / -tspeed;
251 if (traveltime < 0.1) // useless anim
253 self.velocity = '0 0 0';
254 self.nextthink = self.ltime + 0.1;
258 controller = spawn();
259 controller.classname = "SUB_CalcMove_controller";
260 controller.owner = self;
261 controller.platmovetype = self.platmovetype;
262 SUB_CalcMove_controller_setbezier(controller, self.origin, tcontrol, tdest);
263 controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit.
264 controller.animstate_starttime = time;
265 controller.animstate_endtime = time + traveltime;
266 controller.think = SUB_CalcMove_controller_think;
267 controller.think1 = self.think;
269 // the thinking is now done by the controller
270 self.think = SUB_Null;
271 self.nextthink = self.ltime + traveltime;
279 void SUB_CalcMove (vector tdest, float tspeed, void() func)
285 objerror ("No speed is defined!");
288 self.finaldest = tdest;
289 self.think = SUB_CalcMoveDone;
291 if (tdest == self.origin)
293 self.velocity = '0 0 0';
294 self.nextthink = self.ltime + 0.1;
298 delta = tdest - self.origin;
299 traveltime = vlen (delta) / tspeed;
301 // Very short animations don't really show off the effect
302 // of controlled animation, so let's just use linear movement.
303 // Alternatively entities can choose to specify non-controlled movement.
304 // The only currently implemented alternative movement is linear (value 1)
305 if (traveltime < 0.15 || self.platmovetype == 1)
307 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
308 self.nextthink = self.ltime + traveltime;
312 // now just run like a bezier curve...
313 SUB_CalcMove_Bezier((self.origin + tdest) * 0.5, tdest, tspeed, func);
316 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
323 SUB_CalcMove (tdest, tspeed, func);
332 calculate self.avelocity and self.nextthink to reach destangle from
335 The calling function should make sure self.think is valid
338 void SUB_CalcAngleMoveDone (void)
340 // After rotating, set angle to exact final angle
341 self.angles = self.finalangle;
342 self.avelocity = '0 0 0';
348 // FIXME: I fixed this function only for rotation around the main axes
349 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
355 objerror ("No speed is defined!");
357 // take the shortest distance for the angles
358 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
359 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
360 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
361 delta = destangle - self.angles;
362 traveltime = vlen (delta) / tspeed;
365 self.finalangle = destangle;
366 self.think = SUB_CalcAngleMoveDone;
368 if (traveltime < 0.1)
370 self.avelocity = '0 0 0';
371 self.nextthink = self.ltime + 0.1;
375 self.avelocity = delta * (1 / traveltime);
376 self.nextthink = self.ltime + traveltime;
379 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
386 SUB_CalcAngleMove (destangle, tspeed, func);
395 unused but required by the engine
409 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
410 Additionally it moves players back into the past before the trace and restores them afterward.
413 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
418 // check whether antilagged traces are enabled
421 if (clienttype(forent) != CLIENTTYPE_REAL)
422 lag = 0; // only antilag for clients
424 // change shooter to SOLID_BBOX so the shot can hit corpses
425 oldsolid = source.dphitcontentsmask;
427 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
431 // take players back into the past
432 FOR_EACH_PLAYER(player)
434 antilag_takeback(player, time - lag);
439 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
441 tracebox (v1, mi, ma, v2, nomonst, forent);
443 // restore players to current positions
446 FOR_EACH_PLAYER(player)
448 antilag_restore(player);
451 // restore shooter solid type
453 source.dphitcontentsmask = oldsolid;
455 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
457 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
459 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
461 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
463 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
465 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
467 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
469 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
471 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
473 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
475 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
477 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
479 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
481 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
483 if (autocvar_g_antilag != 2 || source.cvar_cl_noantilag)
485 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
488 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity) // returns the number of traces done, for benchmarking
494 //nudge = 2 * cvar("collision_impactnudge"); // why not?
497 dir = normalize(v2 - v1);
499 pos = v1 + dir * nudge;
506 if((pos - v1) * dir >= (v2 - v1) * dir)
514 tracebox(pos, mi, ma, v2, nomonsters, forent);
519 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
520 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
521 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
522 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
525 stopentity = trace_ent;
529 // we started inside solid.
530 // then trace from endpos to pos
532 tracebox(t, mi, ma, pos, nomonsters, forent);
536 // t is still inside solid? bad
537 // force advance, then, and retry
538 pos = t + dir * nudge;
540 // but if we hit an entity, stop RIGHT before it
541 if(stopatentity && stopentity)
543 trace_ent = stopentity;
545 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
551 // we actually LEFT solid!
552 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
558 // pos is outside solid?!? but why?!? never mind, just return it.
560 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
566 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity)
568 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity);
575 Returns a point at least 12 units away from walls
576 (useful for explosion animations, although the blast is performed where it really happened)
580 vector findbetterlocation (vector org, float mindist)
586 vec = mindist * '1 0 0';
590 traceline (org, org + vec, TRUE, world);
592 if (trace_fraction < 1)
595 traceline (loc, loc + vec, TRUE, world);
596 if (trace_fraction >= 1)
616 Returns a random number between -1.0 and 1.0
621 return 2 * (random () - 0.5);
626 Angc used for animations
631 float angc (float a1, float a2)
658 .float lodmodelindex0;
659 .float lodmodelindex1;
660 .float lodmodelindex2;
664 float LOD_customize()
668 if(autocvar_loddebug)
670 d = autocvar_loddebug;
672 self.modelindex = self.lodmodelindex0;
673 else if(d == 2 || !self.lodmodelindex2)
674 self.modelindex = self.lodmodelindex1;
676 self.modelindex = self.lodmodelindex2;
680 // TODO csqc network this so it only gets sent once
681 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
682 if(d < self.loddistance1)
683 self.modelindex = self.lodmodelindex0;
684 else if(!self.lodmodelindex2 || d < self.loddistance2)
685 self.modelindex = self.lodmodelindex1;
687 self.modelindex = self.lodmodelindex2;
692 void LOD_uncustomize()
694 self.modelindex = self.lodmodelindex0;
697 void LODmodel_attach()
701 if(!self.loddistance1)
702 self.loddistance1 = 1000;
703 if(!self.loddistance2)
704 self.loddistance2 = 2000;
705 self.lodmodelindex0 = self.modelindex;
707 if(self.lodtarget1 != "")
709 e = find(world, targetname, self.lodtarget1);
712 self.lodmodel1 = e.model;
716 if(self.lodtarget2 != "")
718 e = find(world, targetname, self.lodtarget2);
721 self.lodmodel2 = e.model;
726 if(autocvar_loddebug < 0)
728 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
731 if(self.lodmodel1 != "")
737 precache_model(self.lodmodel1);
738 setmodel(self, self.lodmodel1);
739 self.lodmodelindex1 = self.modelindex;
741 if(self.lodmodel2 != "")
743 precache_model(self.lodmodel2);
744 setmodel(self, self.lodmodel2);
745 self.lodmodelindex2 = self.modelindex;
748 self.modelindex = self.lodmodelindex0;
749 setsize(self, mi, ma);
752 if(self.lodmodelindex1)
753 if not(self.SendEntity)
754 SetCustomizer(self, LOD_customize, LOD_uncustomize);
757 void ApplyMinMaxScaleAngles(entity e)
759 if(e.angles_x != 0 || e.angles_z != 0 || self.avelocity_x != 0 || self.avelocity_z != 0) // "weird" rotation
761 e.maxs = '1 1 1' * vlen(
762 '1 0 0' * max(-e.mins_x, e.maxs_x) +
763 '0 1 0' * max(-e.mins_y, e.maxs_y) +
764 '0 0 1' * max(-e.mins_z, e.maxs_z)
768 else if(e.angles_y != 0 || self.avelocity_y != 0) // yaw only is a bit better
771 '1 0 0' * max(-e.mins_x, e.maxs_x) +
772 '0 1 0' * max(-e.mins_y, e.maxs_y)
775 e.mins_x = -e.maxs_x;
776 e.mins_y = -e.maxs_x;
779 setsize(e, e.mins * e.scale, e.maxs * e.scale);
781 setsize(e, e.mins, e.maxs);
784 void SetBrushEntityModel()
788 precache_model(self.model);
789 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
791 vector mi = self.mins;
792 vector ma = self.maxs;
793 setmodel(self, self.model); // no precision needed
794 setsize(self, mi, ma);
797 setmodel(self, self.model); // no precision needed
798 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
800 setorigin(self, self.origin);
801 ApplyMinMaxScaleAngles(self);
804 void SetBrushEntityModelNoLOD()
808 precache_model(self.model);
809 if(self.mins != '0 0 0' || self.maxs != '0 0 0')
811 vector mi = self.mins;
812 vector ma = self.maxs;
813 setmodel(self, self.model); // no precision needed
814 setsize(self, mi, ma);
817 setmodel(self, self.model); // no precision needed
819 setorigin(self, self.origin);
820 ApplyMinMaxScaleAngles(self);
831 if (self.movedir != '0 0 0')
832 self.movedir = normalize(self.movedir);
835 makevectors (self.angles);
836 self.movedir = v_forward;
839 self.angles = '0 0 0';
844 // trigger angles are used for one-way touches. An angle of 0 is assumed
845 // to mean no restrictions, so use a yaw of 360 instead.
847 self.solid = SOLID_TRIGGER;
848 SetBrushEntityModel();
849 self.movetype = MOVETYPE_NONE;
854 void InitSolidBSPTrigger()
856 // trigger angles are used for one-way touches. An angle of 0 is assumed
857 // to mean no restrictions, so use a yaw of 360 instead.
859 self.solid = SOLID_BSP;
860 SetBrushEntityModel();
861 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
862 // self.modelindex = 0;
866 float InitMovingBrushTrigger()
868 // trigger angles are used for one-way touches. An angle of 0 is assumed
869 // to mean no restrictions, so use a yaw of 360 instead.
870 self.solid = SOLID_BSP;
871 SetBrushEntityModel();
872 self.movetype = MOVETYPE_PUSH;
873 if(self.modelindex == 0)
875 objerror("InitMovingBrushTrigger: no brushes found!");