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_sane(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 traceline_antilag_force (entity source, vector v1, 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
312 oldsolid = source.dphitcontentsmask;
313 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
317 // take players back into the past
318 player = player_list;
321 antilag_takeback(player, time - lag);
322 player = player.nextplayer;
327 traceline (v1, v2, nomonst, forent);
329 // restore players to current positions
332 player = player_list;
335 antilag_restore(player);
336 player = player.nextplayer;
340 // restore shooter solid type
341 source.dphitcontentsmask = oldsolid;
343 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
345 if (cvar("g_antilag") != 2)
347 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
350 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
355 //nudge = 2 * cvar("collision_impactnudge"); // why not?
358 dir = normalize(v2 - v1);
360 pos = v1 + dir * nudge;
367 if((pos - v1) * dir >= (v2 - v1) * dir)
375 tracebox(pos, mi, ma, v2, nomonsters, forent);
380 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
381 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
382 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
383 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
388 // we started inside solid.
389 // then trace from endpos to pos
391 tracebox(t, mi, ma, pos, nomonsters, forent);
395 // t is still inside solid? bad
396 // force advance, then, and retry
397 pos = t + dir * nudge;
401 // we actually LEFT solid!
402 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
408 // pos is outside solid?!? but why?!? never mind, just return it.
410 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
416 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
422 //nudge = 2 * cvar("collision_impactnudge"); // why not?
425 dir = normalize(v2 - v1);
427 pos = v1 + dir * nudge;
431 if((pos - v1) * dir >= (v2 - v1) * dir)
438 traceline(pos, v2, nomonsters, forent);
442 // we started inside solid.
443 // then trace from endpos to pos
445 traceline(t, pos, nomonsters, forent);
448 // t is inside solid? bad
449 // force advance, then
450 pos = pos + dir * nudge;
454 // we actually LEFT solid!
455 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
461 // pos is outside solid?!? but why?!? never mind, just return it.
463 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
468 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
475 Returns a point at least 12 units away from walls
476 (useful for explosion animations, although the blast is performed where it really happened)
480 vector findbetterlocation (vector org, float mindist)
486 vec = mindist * '1 0 0';
490 traceline (org, org + vec, TRUE, world);
492 if (trace_fraction < 1)
495 traceline (loc, loc + vec, TRUE, world);
496 if (trace_fraction >= 1)
516 Returns a random number between -1.0 and 1.0
521 return 2 * (random () - 0.5);
526 Angc used for animations
531 float angc (float a1, float a2)
558 .float lodmodelindex0;
559 .float lodmodelindex1;
560 .float lodmodelindex2;
564 vector NearestPointOnBox(entity box, vector org);
565 float LOD_customize()
571 d = cvar("loddebug");
573 self.modelindex = self.lodmodelindex0;
575 self.modelindex = self.lodmodelindex1;
577 self.modelindex = self.lodmodelindex2;
581 // TODO csqc network this so it only gets sent once
582 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
583 if(d < self.loddistance1)
584 self.modelindex = self.lodmodelindex0;
585 else if(!self.lodmodelindex2 || d < self.loddistance2)
586 self.modelindex = self.lodmodelindex1;
588 self.modelindex = self.lodmodelindex2;
593 void LOD_uncustomize()
595 self.modelindex = self.lodmodelindex0;
598 void LODmodel_attach()
602 if(!self.loddistance1)
603 self.loddistance1 = 1000;
604 if(!self.loddistance2)
605 self.loddistance2 = 2000;
606 self.lodmodelindex0 = self.modelindex;
608 if(self.lodtarget1 != "")
610 e = find(world, targetname, self.lodtarget1);
613 self.lodmodel1 = e.model;
617 if(self.lodtarget2 != "")
619 e = find(world, targetname, self.lodtarget2);
622 self.lodmodel2 = e.model;
627 if(cvar("loddebug") < 0)
629 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
632 if(self.lodmodel1 != "")
638 precache_model(self.lodmodel1);
639 setmodel(self, self.lodmodel1);
640 self.lodmodelindex1 = self.modelindex;
642 if(self.lodmodel2 != "")
644 precache_model(self.lodmodel2);
645 setmodel(self, self.lodmodel2);
646 self.lodmodelindex2 = self.modelindex;
649 self.modelindex = self.lodmodelindex0;
650 setsize(self, mi, ma);
653 if(self.lodmodelindex1)
654 SetCustomizer(self, LOD_customize, LOD_uncustomize);
657 void SetBrushEntityModel()
661 precache_model(self.model);
662 setmodel(self, self.model); // no precision needed
663 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
665 setorigin(self, self.origin);
667 setsize(self, self.mins * self.scale, self.maxs * self.scale);
669 setsize(self, self.mins, self.maxs);
672 void SetBrushEntityModelNoLOD()
676 precache_model(self.model);
677 setmodel(self, self.model); // no precision needed
679 setorigin(self, self.origin);
681 setsize(self, self.mins * self.scale, self.maxs * self.scale);
683 setsize(self, self.mins, self.maxs);
694 if (self.movedir != '0 0 0')
695 self.movedir = normalize(self.movedir);
698 makevectors (self.angles);
699 self.movedir = v_forward;
702 self.angles = '0 0 0';
707 // trigger angles are used for one-way touches. An angle of 0 is assumed
708 // to mean no restrictions, so use a yaw of 360 instead.
709 if (self.movedir == '0 0 0')
710 if (self.angles != '0 0 0')
712 self.solid = SOLID_TRIGGER;
713 SetBrushEntityModel();
714 self.movetype = MOVETYPE_NONE;
719 void InitSolidBSPTrigger()
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_BSP;
727 SetBrushEntityModel();
728 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
729 // self.modelindex = 0;
733 float InitMovingBrushTrigger()
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 self.solid = SOLID_BSP;
738 SetBrushEntityModel();
739 self.movetype = MOVETYPE_PUSH;
740 if(self.modelindex == 0)
742 objerror("InitMovingBrushTrigger: no brushes found!");