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 if (anim_x == e.animstate_startframe)
21 if (anim_y == e.animstate_numframes)
22 if (anim_z == e.animstate_framerate)
24 e.animstate_startframe = anim_x;
25 e.animstate_numframes = anim_y;
26 e.animstate_framerate = anim_z;
27 e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
28 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
29 e.animstate_looping = looping;
30 e.animstate_override = override;
31 e.frame = e.animstate_startframe;
34 void updateanim(entity e)
36 if (time >= e.animstate_endtime)
38 if (e.animstate_looping)
40 e.animstate_starttime = e.animstate_endtime;
41 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
43 e.animstate_override = FALSE;
45 e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
46 //print(ftos(time), " -> ", ftos(e.frame), "\n");
50 vector animparseline(float animfile)
57 line = fgets(animfile);
58 c = tokenize_console(line);
61 animparseerror = TRUE;
64 anim_x = stof(argv(0));
65 anim_y = stof(argv(1));
66 anim_z = stof(argv(2));
67 // don't allow completely bogus values
68 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
80 void SUB_Remove (void)
89 Applies some friction to self
93 void SUB_Friction (void)
95 self.nextthink = time;
96 if(self.flags & FL_ONGROUND)
97 self.velocity = self.velocity * (1 - frametime * self.friction);
104 Makes client invisible or removes non-client
107 void SUB_VanishOrRemove (entity ent)
109 if (ent.flags & FL_CLIENT)
124 void SUB_SetFade_Think (void)
126 self.think = SUB_SetFade_Think;
127 self.nextthink = self.fade_time;
128 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
129 if (self.alpha < 0.01)
130 SUB_VanishOrRemove(self);
131 self.alpha = bound(0.01, self.alpha, 1);
138 Fade 'ent' out when time >= 'when'
141 void SUB_SetFade (entity ent, float when, float fadetime)
143 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
146 ent.fade_rate = 1/fadetime;
147 ent.fade_time = when;
148 ent.think = SUB_SetFade_Think;
149 ent.nextthink = when;
156 calculate self.velocity and self.nextthink to reach dest from
157 self.origin traveling at speed
160 void SUB_CalcMoveDone (void)
162 // After moving, set origin to exact final destination
164 setorigin (self, self.finaldest);
165 self.velocity = '0 0 0';
171 void SUB_CalcMove (vector tdest, float tspeed, void() func)
177 objerror ("No speed is defined!");
180 self.finaldest = tdest;
181 self.think = SUB_CalcMoveDone;
183 if (tdest == self.origin)
185 self.velocity = '0 0 0';
186 self.nextthink = self.ltime + 0.1;
190 delta = tdest - self.origin;
191 traveltime = vlen (delta) / tspeed;
193 if (traveltime < 0.1)
195 self.velocity = '0 0 0';
196 self.nextthink = self.ltime + 0.1;
200 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
202 self.nextthink = self.ltime + traveltime;
205 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
212 SUB_CalcMove (tdest, tspeed, func);
221 calculate self.avelocity and self.nextthink to reach destangle from
224 The calling function should make sure self.think is valid
227 void SUB_CalcAngleMoveDone (void)
229 // After rotating, set angle to exact final angle
230 self.angles = self.finalangle;
231 self.avelocity = '0 0 0';
237 // FIXME: I fixed this function only for rotation around the main axes
238 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
244 objerror ("No speed is defined!");
246 // take the shortest distance for the angles
247 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
248 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
249 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
250 delta = destangle - self.angles;
251 traveltime = vlen (delta) / tspeed;
254 self.finalangle = destangle;
255 self.think = SUB_CalcAngleMoveDone;
257 if (traveltime < 0.1)
259 self.avelocity = '0 0 0';
260 self.nextthink = self.ltime + 0.1;
264 self.avelocity = delta * (1 / traveltime);
265 self.nextthink = self.ltime + traveltime;
268 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
275 SUB_CalcAngleMove (destangle, tspeed, func);
284 unused but required by the engine
298 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
299 Additionally it moves players back into the past before the trace and restores them afterward.
302 void tracebox_antilag_force (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
305 local float oldsolid;
307 // check whether antilagged traces are enabled
310 if (clienttype(forent) != CLIENTTYPE_REAL)
311 lag = 0; // only antilag for clients
313 // change shooter to SOLID_BBOX so the shot can hit corpses
316 oldsolid = source.dphitcontentsmask;
317 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
322 // take players back into the past
323 player = player_list;
326 antilag_takeback(player, time - lag);
327 player = player.nextplayer;
332 tracebox (v1, mi, ma, v2, nomonst, forent);
334 // restore players to current positions
337 player = player_list;
340 antilag_restore(player);
341 player = player.nextplayer;
345 // restore shooter solid type
347 source.dphitcontentsmask = oldsolid;
349 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
351 tracebox_antilag_force(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag);
353 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
355 if (cvar("g_antilag") != 2)
357 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
359 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
361 if (cvar("g_antilag") != 2)
363 tracebox_antilag_force(source, v1, mi, ma, v2, nomonst, forent, lag);
366 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
371 //nudge = 2 * cvar("collision_impactnudge"); // why not?
374 dir = normalize(v2 - v1);
376 pos = v1 + dir * nudge;
383 if((pos - v1) * dir >= (v2 - v1) * dir)
391 tracebox(pos, mi, ma, v2, nomonsters, forent);
396 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
397 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
398 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
399 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
404 // we started inside solid.
405 // then trace from endpos to pos
407 tracebox(t, mi, ma, pos, nomonsters, forent);
411 // t is still inside solid? bad
412 // force advance, then, and retry
413 pos = t + dir * nudge;
417 // we actually LEFT solid!
418 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
424 // pos is outside solid?!? but why?!? never mind, just return it.
426 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
432 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
438 //nudge = 2 * cvar("collision_impactnudge"); // why not?
441 dir = normalize(v2 - v1);
443 pos = v1 + dir * nudge;
447 if((pos - v1) * dir >= (v2 - v1) * dir)
454 traceline(pos, v2, nomonsters, forent);
458 // we started inside solid.
459 // then trace from endpos to pos
461 traceline(t, pos, nomonsters, forent);
464 // t is inside solid? bad
465 // force advance, then
466 pos = pos + dir * nudge;
470 // we actually LEFT solid!
471 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
477 // pos is outside solid?!? but why?!? never mind, just return it.
479 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
484 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
491 Returns a point at least 12 units away from walls
492 (useful for explosion animations, although the blast is performed where it really happened)
496 vector findbetterlocation (vector org, float mindist)
502 vec = mindist * '1 0 0';
506 traceline (org, org + vec, TRUE, world);
508 if (trace_fraction < 1)
511 traceline (loc, loc + vec, TRUE, world);
512 if (trace_fraction >= 1)
532 Returns a random number between -1.0 and 1.0
537 return 2 * (random () - 0.5);
542 Angc used for animations
547 float angc (float a1, float a2)
574 .float lodmodelindex0;
575 .float lodmodelindex1;
576 .float lodmodelindex2;
580 vector NearestPointOnBox(entity box, vector org);
581 float LOD_customize()
587 d = cvar("loddebug");
589 self.modelindex = self.lodmodelindex0;
591 self.modelindex = self.lodmodelindex1;
593 self.modelindex = self.lodmodelindex2;
597 // TODO csqc network this so it only gets sent once
598 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
599 if(d < self.loddistance1)
600 self.modelindex = self.lodmodelindex0;
601 else if(!self.lodmodelindex2 || d < self.loddistance2)
602 self.modelindex = self.lodmodelindex1;
604 self.modelindex = self.lodmodelindex2;
609 void LOD_uncustomize()
611 self.modelindex = self.lodmodelindex0;
614 void LODmodel_attach()
618 if(!self.loddistance1)
619 self.loddistance1 = 1000;
620 if(!self.loddistance2)
621 self.loddistance2 = 2000;
622 self.lodmodelindex0 = self.modelindex;
624 if(self.lodtarget1 != "")
626 e = find(world, targetname, self.lodtarget1);
629 self.lodmodel1 = e.model;
633 if(self.lodtarget2 != "")
635 e = find(world, targetname, self.lodtarget2);
638 self.lodmodel2 = e.model;
643 if(cvar("loddebug") < 0)
645 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
648 if(self.lodmodel1 != "")
654 precache_model(self.lodmodel1);
655 setmodel(self, self.lodmodel1);
656 self.lodmodelindex1 = self.modelindex;
658 if(self.lodmodel2 != "")
660 precache_model(self.lodmodel2);
661 setmodel(self, self.lodmodel2);
662 self.lodmodelindex2 = self.modelindex;
665 self.modelindex = self.lodmodelindex0;
666 setsize(self, mi, ma);
669 if(self.lodmodelindex1)
670 SetCustomizer(self, LOD_customize, LOD_uncustomize);
673 void SetBrushEntityModel()
677 precache_model(self.model);
678 setmodel(self, self.model); // no precision needed
679 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
681 setorigin(self, self.origin);
683 setsize(self, self.mins * self.scale, self.maxs * self.scale);
685 setsize(self, self.mins, self.maxs);
688 void SetBrushEntityModelNoLOD()
692 precache_model(self.model);
693 setmodel(self, self.model); // no precision needed
695 setorigin(self, self.origin);
697 setsize(self, self.mins * self.scale, self.maxs * self.scale);
699 setsize(self, self.mins, self.maxs);
710 if (self.movedir != '0 0 0')
711 self.movedir = normalize(self.movedir);
714 makevectors (self.angles);
715 self.movedir = v_forward;
718 self.angles = '0 0 0';
723 // trigger angles are used for one-way touches. An angle of 0 is assumed
724 // to mean no restrictions, so use a yaw of 360 instead.
725 if (self.movedir == '0 0 0')
726 if (self.angles != '0 0 0')
728 self.solid = SOLID_TRIGGER;
729 SetBrushEntityModel();
730 self.movetype = MOVETYPE_NONE;
735 void InitSolidBSPTrigger()
737 // trigger angles are used for one-way touches. An angle of 0 is assumed
738 // to mean no restrictions, so use a yaw of 360 instead.
739 if (self.movedir == '0 0 0')
740 if (self.angles != '0 0 0')
742 self.solid = SOLID_BSP;
743 SetBrushEntityModel();
744 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
745 // self.modelindex = 0;
749 float InitMovingBrushTrigger()
751 // trigger angles are used for one-way touches. An angle of 0 is assumed
752 // to mean no restrictions, so use a yaw of 360 instead.
753 self.solid = SOLID_BSP;
754 SetBrushEntityModel();
755 self.movetype = MOVETYPE_PUSH;
756 if(self.modelindex == 0)
758 objerror("InitMovingBrushTrigger: no brushes found!");