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_TELEPORT_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;
42 void updateanim(entity e)
44 if (time >= e.animstate_endtime)
46 if (e.animstate_looping)
48 e.animstate_starttime = e.animstate_endtime;
49 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
51 e.animstate_override = FALSE;
53 e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
54 //print(ftos(time), " -> ", ftos(e.frame), "\n");
58 vector animparseline(float animfile)
65 line = fgets(animfile);
66 c = tokenize_console(line);
69 animparseerror = TRUE;
72 anim_x = stof(argv(0));
73 anim_y = stof(argv(1));
74 anim_z = stof(argv(2));
75 // don't allow completely bogus values
76 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
88 void SUB_Remove (void)
97 Applies some friction to self
101 void SUB_Friction (void)
103 self.nextthink = time;
104 if(self.flags & FL_ONGROUND)
105 self.velocity = self.velocity * (1 - frametime * self.friction);
112 Makes client invisible or removes non-client
115 void SUB_VanishOrRemove (entity ent)
117 if (ent.flags & FL_CLIENT)
132 void SUB_SetFade_Think (void)
134 self.think = SUB_SetFade_Think;
135 self.nextthink = self.fade_time;
136 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
137 if (self.alpha < 0.01)
138 SUB_VanishOrRemove(self);
139 self.alpha = bound(0.01, self.alpha, 1);
146 Fade 'ent' out when time >= 'when'
149 void SUB_SetFade (entity ent, float when, float fadetime)
151 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
154 ent.fade_rate = 1/fadetime;
155 ent.fade_time = when;
156 ent.think = SUB_SetFade_Think;
157 ent.nextthink = when;
164 calculate self.velocity and self.nextthink to reach dest from
165 self.origin traveling at speed
168 void SUB_CalcMoveDone (void)
170 // After moving, set origin to exact final destination
172 setorigin (self, self.finaldest);
173 self.velocity = '0 0 0';
179 void SUB_CalcMove (vector tdest, float tspeed, void() func)
185 objerror ("No speed is defined!");
188 self.finaldest = tdest;
189 self.think = SUB_CalcMoveDone;
191 if (tdest == self.origin)
193 self.velocity = '0 0 0';
194 self.nextthink = self.ltime + 0.1;
198 delta = tdest - self.origin;
199 traveltime = vlen (delta) / tspeed;
201 if (traveltime < 0.1)
203 self.velocity = '0 0 0';
204 self.nextthink = self.ltime + 0.1;
208 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
210 self.nextthink = self.ltime + traveltime;
213 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
220 SUB_CalcMove (tdest, tspeed, func);
229 calculate self.avelocity and self.nextthink to reach destangle from
232 The calling function should make sure self.think is valid
235 void SUB_CalcAngleMoveDone (void)
237 // After rotating, set angle to exact final angle
238 self.angles = self.finalangle;
239 self.avelocity = '0 0 0';
245 // FIXME: I fixed this function only for rotation around the main axes
246 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
252 objerror ("No speed is defined!");
254 // take the shortest distance for the angles
255 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
256 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
257 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
258 delta = destangle - self.angles;
259 traveltime = vlen (delta) / tspeed;
262 self.finalangle = destangle;
263 self.think = SUB_CalcAngleMoveDone;
265 if (traveltime < 0.1)
267 self.avelocity = '0 0 0';
268 self.nextthink = self.ltime + 0.1;
272 self.avelocity = delta * (1 / traveltime);
273 self.nextthink = self.ltime + traveltime;
276 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
283 SUB_CalcAngleMove (destangle, tspeed, func);
292 unused but required by the engine
306 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
307 Additionally it moves players back into the past before the trace and restores them afterward.
310 void tracebox_antilag_force (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
313 local float oldsolid;
315 // check whether antilagged traces are enabled
318 if (clienttype(forent) != CLIENTTYPE_REAL)
319 lag = 0; // only antilag for clients
321 // change shooter to SOLID_BBOX so the shot can hit corpses
324 oldsolid = source.dphitcontentsmask;
325 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
330 // take players back into the past
331 player = player_list;
334 antilag_takeback(player, time - lag);
335 player = player.nextplayer;
340 tracebox (v1, mi, ma, v2, nomonst, forent);
342 // restore players to current positions
345 player = player_list;
348 antilag_restore(player);
349 player = player.nextplayer;
353 // restore shooter solid type
355 source.dphitcontentsmask = oldsolid;
357 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
359 tracebox_antilag_force(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag);
361 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
363 if (cvar("g_antilag") != 2)
365 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
367 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
369 if (cvar("g_antilag") != 2)
371 tracebox_antilag_force(source, v1, mi, ma, v2, nomonst, forent, lag);
374 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
379 //nudge = 2 * cvar("collision_impactnudge"); // why not?
382 dir = normalize(v2 - v1);
384 pos = v1 + dir * nudge;
391 if((pos - v1) * dir >= (v2 - v1) * dir)
399 tracebox(pos, mi, ma, v2, nomonsters, forent);
404 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
405 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
406 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
407 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
412 // we started inside solid.
413 // then trace from endpos to pos
415 tracebox(t, mi, ma, pos, nomonsters, forent);
419 // t is still inside solid? bad
420 // force advance, then, and retry
421 pos = t + dir * nudge;
425 // we actually LEFT solid!
426 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
432 // pos is outside solid?!? but why?!? never mind, just return it.
434 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
440 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
446 //nudge = 2 * cvar("collision_impactnudge"); // why not?
449 dir = normalize(v2 - v1);
451 pos = v1 + dir * nudge;
455 if((pos - v1) * dir >= (v2 - v1) * dir)
462 traceline(pos, v2, nomonsters, forent);
466 // we started inside solid.
467 // then trace from endpos to pos
469 traceline(t, pos, nomonsters, forent);
472 // t is inside solid? bad
473 // force advance, then
474 pos = pos + dir * nudge;
478 // we actually LEFT solid!
479 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
485 // pos is outside solid?!? but why?!? never mind, just return it.
487 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
492 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
499 Returns a point at least 12 units away from walls
500 (useful for explosion animations, although the blast is performed where it really happened)
504 vector findbetterlocation (vector org, float mindist)
510 vec = mindist * '1 0 0';
514 traceline (org, org + vec, TRUE, world);
516 if (trace_fraction < 1)
519 traceline (loc, loc + vec, TRUE, world);
520 if (trace_fraction >= 1)
540 Returns a random number between -1.0 and 1.0
545 return 2 * (random () - 0.5);
550 Angc used for animations
555 float angc (float a1, float a2)
582 .float lodmodelindex0;
583 .float lodmodelindex1;
584 .float lodmodelindex2;
588 vector NearestPointOnBox(entity box, vector org);
589 float LOD_customize()
595 d = cvar("loddebug");
597 self.modelindex = self.lodmodelindex0;
599 self.modelindex = self.lodmodelindex1;
601 self.modelindex = self.lodmodelindex2;
605 // TODO csqc network this so it only gets sent once
606 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
607 if(d < self.loddistance1)
608 self.modelindex = self.lodmodelindex0;
609 else if(!self.lodmodelindex2 || d < self.loddistance2)
610 self.modelindex = self.lodmodelindex1;
612 self.modelindex = self.lodmodelindex2;
617 void LOD_uncustomize()
619 self.modelindex = self.lodmodelindex0;
622 void LODmodel_attach()
626 if(!self.loddistance1)
627 self.loddistance1 = 1000;
628 if(!self.loddistance2)
629 self.loddistance2 = 2000;
630 self.lodmodelindex0 = self.modelindex;
632 if(self.lodtarget1 != "")
634 e = find(world, targetname, self.lodtarget1);
637 self.lodmodel1 = e.model;
641 if(self.lodtarget2 != "")
643 e = find(world, targetname, self.lodtarget2);
646 self.lodmodel2 = e.model;
651 if(cvar("loddebug") < 0)
653 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
656 if(self.lodmodel1 != "")
662 precache_model(self.lodmodel1);
663 setmodel(self, self.lodmodel1);
664 self.lodmodelindex1 = self.modelindex;
666 if(self.lodmodel2 != "")
668 precache_model(self.lodmodel2);
669 setmodel(self, self.lodmodel2);
670 self.lodmodelindex2 = self.modelindex;
673 self.modelindex = self.lodmodelindex0;
674 setsize(self, mi, ma);
677 if(self.lodmodelindex1)
678 SetCustomizer(self, LOD_customize, LOD_uncustomize);
681 void SetBrushEntityModel()
685 precache_model(self.model);
686 setmodel(self, self.model); // no precision needed
687 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
689 setorigin(self, self.origin);
691 setsize(self, self.mins * self.scale, self.maxs * self.scale);
693 setsize(self, self.mins, self.maxs);
696 void SetBrushEntityModelNoLOD()
700 precache_model(self.model);
701 setmodel(self, self.model); // no precision needed
703 setorigin(self, self.origin);
705 setsize(self, self.mins * self.scale, self.maxs * self.scale);
707 setsize(self, self.mins, self.maxs);
718 if (self.movedir != '0 0 0')
719 self.movedir = normalize(self.movedir);
722 makevectors (self.angles);
723 self.movedir = v_forward;
726 self.angles = '0 0 0';
731 // trigger angles are used for one-way touches. An angle of 0 is assumed
732 // to mean no restrictions, so use a yaw of 360 instead.
733 if (self.movedir == '0 0 0')
734 if (self.angles != '0 0 0')
736 self.solid = SOLID_TRIGGER;
737 SetBrushEntityModel();
738 self.movetype = MOVETYPE_NONE;
743 void InitSolidBSPTrigger()
745 // trigger angles are used for one-way touches. An angle of 0 is assumed
746 // to mean no restrictions, so use a yaw of 360 instead.
747 if (self.movedir == '0 0 0')
748 if (self.angles != '0 0 0')
750 self.solid = SOLID_BSP;
751 SetBrushEntityModel();
752 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
753 // self.modelindex = 0;
757 float InitMovingBrushTrigger()
759 // trigger angles are used for one-way touches. An angle of 0 is assumed
760 // to mean no restrictions, so use a yaw of 360 instead.
761 self.solid = SOLID_BSP;
762 SetBrushEntityModel();
763 self.movetype = MOVETYPE_PUSH;
764 if(self.modelindex == 0)
766 objerror("InitMovingBrushTrigger: no brushes found!");