3 void(vector destangle, float tspeed, void() func) SUB_CalcAngleMove;
4 void() SUB_CalcMoveDone;
5 void() SUB_CalcAngleMoveDone;
6 //void() SUB_UseTargets;
9 void spawnfunc_info_null (void)
12 // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately.
15 void setanim(entity e, vector anim, float looping, float override, float restart)
18 if (anim_x == e.animstate_startframe)
19 if (anim_y == e.animstate_numframes)
20 if (anim_z == e.animstate_framerate)
22 e.animstate_startframe = anim_x;
23 e.animstate_numframes = anim_y;
24 e.animstate_framerate = anim_z;
25 e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
26 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
27 e.animstate_looping = looping;
28 e.animstate_override = override;
29 e.frame = e.animstate_startframe;
32 void updateanim(entity e)
34 if (time >= e.animstate_endtime)
36 if (e.animstate_looping)
38 e.animstate_starttime = e.animstate_endtime;
39 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
41 e.animstate_override = FALSE;
43 e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
44 //print(ftos(time), " -> ", ftos(e.frame), "\n");
48 vector animparseline(float animfile)
55 line = fgets(animfile);
56 c = tokenize_console(line);
59 animparseerror = TRUE;
62 anim_x = stof(argv(0));
63 anim_y = stof(argv(1));
64 anim_z = stof(argv(2));
65 // don't allow completely bogus values
66 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
78 void SUB_Remove (void)
87 Applies some friction to self
91 void SUB_Friction (void)
93 self.nextthink = time;
94 if(self.flags & FL_ONGROUND)
95 self.velocity = self.velocity * (1 - frametime * self.friction);
102 Makes client invisible or removes non-client
105 void SUB_VanishOrRemove (entity ent)
107 if (ent.flags & FL_CLIENT)
122 void SUB_SetFade_Think (void)
124 self.think = SUB_SetFade_Think;
125 self.nextthink = self.fade_time;
126 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
127 if (self.alpha < 0.01)
128 SUB_VanishOrRemove(self);
129 self.alpha = bound(0.01, self.alpha, 1);
136 Fade 'ent' out when time >= 'when'
139 void SUB_SetFade (entity ent, float when, float fadetime)
141 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
144 ent.fade_rate = 1/fadetime;
145 ent.fade_time = when;
146 ent.think = SUB_SetFade_Think;
147 ent.nextthink = when;
154 calculate self.velocity and self.nextthink to reach dest from
155 self.origin traveling at speed
158 void SUB_CalcMoveDone (void)
160 // After moving, set origin to exact final destination
162 setorigin (self, self.finaldest);
163 self.velocity = '0 0 0';
169 void SUB_CalcMove (vector tdest, float tspeed, void() func)
175 objerror ("No speed is defined!");
178 self.finaldest = tdest;
179 self.think = SUB_CalcMoveDone;
181 if (tdest == self.origin)
183 self.velocity = '0 0 0';
184 self.nextthink = self.ltime + 0.1;
188 delta = tdest - self.origin;
189 traveltime = vlen (delta) / tspeed;
191 if (traveltime < 0.1)
193 self.velocity = '0 0 0';
194 self.nextthink = self.ltime + 0.1;
198 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
200 self.nextthink = self.ltime + traveltime;
203 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
210 SUB_CalcMove (tdest, tspeed, func);
219 calculate self.avelocity and self.nextthink to reach destangle from
222 The calling function should make sure self.think is valid
225 void SUB_CalcAngleMoveDone (void)
227 // After rotating, set angle to exact final angle
228 self.angles = self.finalangle;
229 self.avelocity = '0 0 0';
235 // FIXME: I fixed this function only for rotation around the main axes
236 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
242 objerror ("No speed is defined!");
244 // take the shortest distance for the angles
245 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
246 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
247 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
248 delta = destangle - self.angles;
249 traveltime = vlen (delta) / tspeed;
252 self.finalangle = destangle;
253 self.think = SUB_CalcAngleMoveDone;
255 if (traveltime < 0.1)
257 self.avelocity = '0 0 0';
258 self.nextthink = self.ltime + 0.1;
262 self.avelocity = delta * (1 / traveltime);
263 self.nextthink = self.ltime + traveltime;
266 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
273 SUB_CalcAngleMove (destangle, tspeed, func);
282 unused but required by the engine
296 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
297 Additionally it moves players back into the past before the trace and restores them afterward.
300 void tracebox_antilag_force (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
303 local float oldsolid;
305 // check whether antilagged traces are enabled
308 if (clienttype(forent) != CLIENTTYPE_REAL)
309 lag = 0; // only antilag for clients
311 // change shooter to SOLID_BBOX so the shot can hit corpses
314 oldsolid = source.dphitcontentsmask;
315 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
320 // take players back into the past
321 player = player_list;
324 antilag_takeback(player, time - lag);
325 player = player.nextplayer;
330 tracebox (v1, mi, ma, v2, nomonst, forent);
332 // restore players to current positions
335 player = player_list;
338 antilag_restore(player);
339 player = player.nextplayer;
343 // restore shooter solid type
345 source.dphitcontentsmask = oldsolid;
347 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
349 tracebox_antilag_force(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag);
351 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
353 if (cvar("g_antilag") != 2)
355 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
357 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
359 if (cvar("g_antilag") != 2)
361 tracebox_antilag_force(source, v1, mi, ma, v2, nomonst, forent, lag);
364 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
369 //nudge = 2 * cvar("collision_impactnudge"); // why not?
372 dir = normalize(v2 - v1);
374 pos = v1 + dir * nudge;
381 if((pos - v1) * dir >= (v2 - v1) * dir)
389 tracebox(pos, mi, ma, v2, nomonsters, forent);
394 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
395 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
396 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
397 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
402 // we started inside solid.
403 // then trace from endpos to pos
405 tracebox(t, mi, ma, pos, nomonsters, forent);
409 // t is still inside solid? bad
410 // force advance, then, and retry
411 pos = t + dir * nudge;
415 // we actually LEFT solid!
416 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
422 // pos is outside solid?!? but why?!? never mind, just return it.
424 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
430 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
436 //nudge = 2 * cvar("collision_impactnudge"); // why not?
439 dir = normalize(v2 - v1);
441 pos = v1 + dir * nudge;
445 if((pos - v1) * dir >= (v2 - v1) * dir)
452 traceline(pos, v2, nomonsters, forent);
456 // we started inside solid.
457 // then trace from endpos to pos
459 traceline(t, pos, nomonsters, forent);
462 // t is inside solid? bad
463 // force advance, then
464 pos = pos + dir * nudge;
468 // we actually LEFT solid!
469 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
475 // pos is outside solid?!? but why?!? never mind, just return it.
477 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
482 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
489 Returns a point at least 12 units away from walls
490 (useful for explosion animations, although the blast is performed where it really happened)
494 vector findbetterlocation (vector org, float mindist)
500 vec = mindist * '1 0 0';
504 traceline (org, org + vec, TRUE, world);
506 if (trace_fraction < 1)
509 traceline (loc, loc + vec, TRUE, world);
510 if (trace_fraction >= 1)
530 Returns a random number between -1.0 and 1.0
535 return 2 * (random () - 0.5);
540 Angc used for animations
545 float angc (float a1, float a2)
572 .float lodmodelindex0;
573 .float lodmodelindex1;
574 .float lodmodelindex2;
578 vector NearestPointOnBox(entity box, vector org);
579 float LOD_customize()
585 d = cvar("loddebug");
587 self.modelindex = self.lodmodelindex0;
589 self.modelindex = self.lodmodelindex1;
591 self.modelindex = self.lodmodelindex2;
595 // TODO csqc network this so it only gets sent once
596 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
597 if(d < self.loddistance1)
598 self.modelindex = self.lodmodelindex0;
599 else if(!self.lodmodelindex2 || d < self.loddistance2)
600 self.modelindex = self.lodmodelindex1;
602 self.modelindex = self.lodmodelindex2;
607 void LOD_uncustomize()
609 self.modelindex = self.lodmodelindex0;
612 void LODmodel_attach()
616 if(!self.loddistance1)
617 self.loddistance1 = 1000;
618 if(!self.loddistance2)
619 self.loddistance2 = 2000;
620 self.lodmodelindex0 = self.modelindex;
622 if(self.lodtarget1 != "")
624 e = find(world, targetname, self.lodtarget1);
627 self.lodmodel1 = e.model;
631 if(self.lodtarget2 != "")
633 e = find(world, targetname, self.lodtarget2);
636 self.lodmodel2 = e.model;
641 if(cvar("loddebug") < 0)
643 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
646 if(self.lodmodel1 != "")
652 precache_model(self.lodmodel1);
653 setmodel(self, self.lodmodel1);
654 self.lodmodelindex1 = self.modelindex;
656 if(self.lodmodel2 != "")
658 precache_model(self.lodmodel2);
659 setmodel(self, self.lodmodel2);
660 self.lodmodelindex2 = self.modelindex;
663 self.modelindex = self.lodmodelindex0;
664 setsize(self, mi, ma);
667 if(self.lodmodelindex1)
668 SetCustomizer(self, LOD_customize, LOD_uncustomize);
671 void SetBrushEntityModel()
675 precache_model(self.model);
676 setmodel(self, self.model); // no precision needed
677 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
679 setorigin(self, self.origin);
681 setsize(self, self.mins * self.scale, self.maxs * self.scale);
683 setsize(self, self.mins, self.maxs);
686 void SetBrushEntityModelNoLOD()
690 precache_model(self.model);
691 setmodel(self, self.model); // no precision needed
693 setorigin(self, self.origin);
695 setsize(self, self.mins * self.scale, self.maxs * self.scale);
697 setsize(self, self.mins, self.maxs);
708 if (self.movedir != '0 0 0')
709 self.movedir = normalize(self.movedir);
712 makevectors (self.angles);
713 self.movedir = v_forward;
716 self.angles = '0 0 0';
721 // trigger angles are used for one-way touches. An angle of 0 is assumed
722 // to mean no restrictions, so use a yaw of 360 instead.
723 if (self.movedir == '0 0 0')
724 if (self.angles != '0 0 0')
726 self.solid = SOLID_TRIGGER;
727 SetBrushEntityModel();
728 self.movetype = MOVETYPE_NONE;
733 void InitSolidBSPTrigger()
735 // trigger angles are used for one-way touches. An angle of 0 is assumed
736 // to mean no restrictions, so use a yaw of 360 instead.
737 if (self.movedir == '0 0 0')
738 if (self.angles != '0 0 0')
740 self.solid = SOLID_BSP;
741 SetBrushEntityModel();
742 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
743 // self.modelindex = 0;
747 float InitMovingBrushTrigger()
749 // trigger angles are used for one-way touches. An angle of 0 is assumed
750 // to mean no restrictions, so use a yaw of 360 instead.
751 self.solid = SOLID_BSP;
752 SetBrushEntityModel();
753 self.movetype = MOVETYPE_PUSH;
754 if(self.modelindex == 0)
756 objerror("InitMovingBrushTrigger: no brushes found!");