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)
189 if(time < self.animstate_endtime) {
190 delta = self.destvec;
191 nexttick = time + sys_frametime;
193 if(nexttick < self.animstate_endtime) {
194 traveltime = self.animstate_endtime - self.animstate_starttime;
195 phasepos = (nexttick - self.animstate_starttime) / traveltime; // range: [0, 1]
196 phasepos = 3.14159265 + (phasepos * 3.14159265); // range: [pi, 2pi]
197 phasepos = cos(phasepos); // cos [pi, 2pi] is in [-1, 1]
198 phasepos = phasepos + 1; // correct range to [0, 2]
199 phasepos = phasepos / 2; // correct range to [0, 1]
200 nextpos = self.origin + (delta * phasepos);
202 veloc = nextpos - self.owner.origin;
203 veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
206 veloc = self.finaldest - self.owner.origin;
207 veloc = veloc * (1 / sys_frametime); // so it arrives for the next frame
209 self.owner.velocity = veloc;
210 self.nextthink = nexttick;
213 self.owner.think = self.think1;
220 void SUB_CalcMove (vector tdest, float tspeed, void() func)
227 objerror ("No speed is defined!");
230 self.finaldest = tdest;
231 self.think = SUB_CalcMoveDone;
233 if (tdest == self.origin)
235 self.velocity = '0 0 0';
236 self.nextthink = self.ltime + 0.1;
240 delta = tdest - self.origin;
241 traveltime = vlen (delta) / tspeed;
243 if (traveltime < 0.1)
245 self.velocity = '0 0 0';
246 self.nextthink = self.ltime + 0.1;
250 // very short animations don't really show off the effect
251 // of controlled animation, so let's just use linear movement
252 if (traveltime < 0.15)
254 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
255 self.nextthink = self.ltime + traveltime;
259 controller = spawn();
260 controller.classname = "SUB_CalcMove_controller";
261 controller.owner = self;
262 controller.origin = self.origin; // starting point
263 controller.finaldest = tdest; // where do we want to end?
264 controller.destvec = delta;
265 controller.animstate_starttime = time;
266 controller.animstate_endtime = time + traveltime;
267 controller.think = SUB_CalcMove_controller_think;
268 controller.think1 = self.think;
270 // the thinking is now done by the controller
271 self.think = SUB_Null;
272 self.nextthink = self.ltime + traveltime;
280 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
287 SUB_CalcMove (tdest, tspeed, func);
296 calculate self.avelocity and self.nextthink to reach destangle from
299 The calling function should make sure self.think is valid
302 void SUB_CalcAngleMoveDone (void)
304 // After rotating, set angle to exact final angle
305 self.angles = self.finalangle;
306 self.avelocity = '0 0 0';
312 // FIXME: I fixed this function only for rotation around the main axes
313 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
319 objerror ("No speed is defined!");
321 // take the shortest distance for the angles
322 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
323 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
324 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
325 delta = destangle - self.angles;
326 traveltime = vlen (delta) / tspeed;
329 self.finalangle = destangle;
330 self.think = SUB_CalcAngleMoveDone;
332 if (traveltime < 0.1)
334 self.avelocity = '0 0 0';
335 self.nextthink = self.ltime + 0.1;
339 self.avelocity = delta * (1 / traveltime);
340 self.nextthink = self.ltime + traveltime;
343 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
350 SUB_CalcAngleMove (destangle, tspeed, func);
359 unused but required by the engine
373 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
374 Additionally it moves players back into the past before the trace and restores them afterward.
377 void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz)
380 local float oldsolid;
382 // check whether antilagged traces are enabled
385 if (clienttype(forent) != CLIENTTYPE_REAL)
386 lag = 0; // only antilag for clients
388 // change shooter to SOLID_BBOX so the shot can hit corpses
391 oldsolid = source.dphitcontentsmask;
392 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
397 // take players back into the past
398 player = player_list;
401 antilag_takeback(player, time - lag);
402 player = player.nextplayer;
408 WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent);
410 tracebox (v1, mi, ma, v2, nomonst, forent);
412 // restore players to current positions
415 player = player_list;
418 antilag_restore(player);
419 player = player.nextplayer;
423 // restore shooter solid type
425 source.dphitcontentsmask = oldsolid;
427 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
429 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, FALSE);
431 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
433 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
435 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
437 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
439 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
441 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, FALSE);
443 void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
445 tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, TRUE);
447 void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
449 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
451 WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
453 void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
455 if (cvar("g_antilag") != 2 || source.cvar_cl_noantilag)
457 tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, TRUE);
460 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
465 //nudge = 2 * cvar("collision_impactnudge"); // why not?
468 dir = normalize(v2 - v1);
470 pos = v1 + dir * nudge;
477 if((pos - v1) * dir >= (v2 - v1) * dir)
485 tracebox(pos, mi, ma, v2, nomonsters, forent);
490 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
491 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
492 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
493 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
498 // we started inside solid.
499 // then trace from endpos to pos
501 tracebox(t, mi, ma, pos, nomonsters, forent);
505 // t is still inside solid? bad
506 // force advance, then, and retry
507 pos = t + dir * nudge;
511 // we actually LEFT solid!
512 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
518 // pos is outside solid?!? but why?!? never mind, just return it.
520 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
526 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
532 //nudge = 2 * cvar("collision_impactnudge"); // why not?
535 dir = normalize(v2 - v1);
537 pos = v1 + dir * nudge;
541 if((pos - v1) * dir >= (v2 - v1) * dir)
548 traceline(pos, v2, nomonsters, forent);
552 // we started inside solid.
553 // then trace from endpos to pos
555 traceline(t, pos, nomonsters, forent);
558 // t is inside solid? bad
559 // force advance, then
560 pos = pos + dir * nudge;
564 // we actually LEFT solid!
565 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
571 // pos is outside solid?!? but why?!? never mind, just return it.
573 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
578 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
585 Returns a point at least 12 units away from walls
586 (useful for explosion animations, although the blast is performed where it really happened)
590 vector findbetterlocation (vector org, float mindist)
596 vec = mindist * '1 0 0';
600 traceline (org, org + vec, TRUE, world);
602 if (trace_fraction < 1)
605 traceline (loc, loc + vec, TRUE, world);
606 if (trace_fraction >= 1)
626 Returns a random number between -1.0 and 1.0
631 return 2 * (random () - 0.5);
636 Angc used for animations
641 float angc (float a1, float a2)
668 .float lodmodelindex0;
669 .float lodmodelindex1;
670 .float lodmodelindex2;
674 float LOD_customize()
680 d = cvar("loddebug");
682 self.modelindex = self.lodmodelindex0;
683 else if(d == 2 || !self.lodmodelindex2)
684 self.modelindex = self.lodmodelindex1;
686 self.modelindex = self.lodmodelindex2;
690 // TODO csqc network this so it only gets sent once
691 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
692 if(d < self.loddistance1)
693 self.modelindex = self.lodmodelindex0;
694 else if(!self.lodmodelindex2 || d < self.loddistance2)
695 self.modelindex = self.lodmodelindex1;
697 self.modelindex = self.lodmodelindex2;
702 void LOD_uncustomize()
704 self.modelindex = self.lodmodelindex0;
707 void LODmodel_attach()
711 if(!self.loddistance1)
712 self.loddistance1 = 1000;
713 if(!self.loddistance2)
714 self.loddistance2 = 2000;
715 self.lodmodelindex0 = self.modelindex;
717 if(self.lodtarget1 != "")
719 e = find(world, targetname, self.lodtarget1);
722 self.lodmodel1 = e.model;
726 if(self.lodtarget2 != "")
728 e = find(world, targetname, self.lodtarget2);
731 self.lodmodel2 = e.model;
736 if(cvar("loddebug") < 0)
738 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
741 if(self.lodmodel1 != "")
747 precache_model(self.lodmodel1);
748 setmodel(self, self.lodmodel1);
749 self.lodmodelindex1 = self.modelindex;
751 if(self.lodmodel2 != "")
753 precache_model(self.lodmodel2);
754 setmodel(self, self.lodmodel2);
755 self.lodmodelindex2 = self.modelindex;
758 self.modelindex = self.lodmodelindex0;
759 setsize(self, mi, ma);
762 if(self.lodmodelindex1)
763 if not(self.SendEntity)
764 SetCustomizer(self, LOD_customize, LOD_uncustomize);
767 void SetBrushEntityModel()
771 precache_model(self.model);
772 setmodel(self, self.model); // no precision needed
773 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
775 setorigin(self, self.origin);
777 setsize(self, self.mins * self.scale, self.maxs * self.scale);
779 setsize(self, self.mins, self.maxs);
782 void SetBrushEntityModelNoLOD()
786 precache_model(self.model);
787 setmodel(self, self.model); // no precision needed
789 setorigin(self, self.origin);
791 setsize(self, self.mins * self.scale, self.maxs * self.scale);
793 setsize(self, self.mins, self.maxs);
804 if (self.movedir != '0 0 0')
805 self.movedir = normalize(self.movedir);
808 makevectors (self.angles);
809 self.movedir = v_forward;
812 self.angles = '0 0 0';
817 // trigger angles are used for one-way touches. An angle of 0 is assumed
818 // to mean no restrictions, so use a yaw of 360 instead.
819 if (self.movedir == '0 0 0')
820 if (self.angles != '0 0 0')
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.
833 if (self.movedir == '0 0 0')
834 if (self.angles != '0 0 0')
836 self.solid = SOLID_BSP;
837 SetBrushEntityModel();
838 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
839 // self.modelindex = 0;
843 float InitMovingBrushTrigger()
845 // trigger angles are used for one-way touches. An angle of 0 is assumed
846 // to mean no restrictions, so use a yaw of 360 instead.
847 self.solid = SOLID_BSP;
848 SetBrushEntityModel();
849 self.movetype = MOVETYPE_PUSH;
850 if(self.modelindex == 0)
852 objerror("InitMovingBrushTrigger: no brushes found!");