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 = time - frametime * 0.1; // shift it a 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);
47 vector animparseline(float animfile)
54 line = fgets(animfile);
55 c = tokenize_sane(line);
58 animparseerror = TRUE;
61 anim_x = stof(argv(0));
62 anim_y = stof(argv(1));
63 anim_z = stof(argv(2));
64 // don't allow completely bogus values
65 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
77 void SUB_Remove (void)
86 Applies some friction to self
90 void SUB_Friction (void)
92 self.nextthink = time;
93 if(self.flags & FL_ONGROUND)
94 self.velocity = self.velocity * (1 - frametime * self.friction);
101 Makes client invisible or removes non-client
104 void SUB_VanishOrRemove (entity ent)
106 if (ent.flags & FL_CLIENT)
121 void SUB_SetFade_Think (void)
123 self.think = SUB_SetFade_Think;
124 self.nextthink = self.fade_time;
125 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
126 if (self.alpha < 0.01)
127 SUB_VanishOrRemove(self);
128 self.alpha = bound(0.01, self.alpha, 1);
135 Fade 'ent' out when time >= 'when'
138 void SUB_SetFade (entity ent, float when, float fadetime)
140 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
143 ent.fade_rate = 1/fadetime;
144 ent.fade_time = when;
145 ent.think = SUB_SetFade_Think;
146 ent.nextthink = when;
153 calculate self.velocity and self.nextthink to reach dest from
154 self.origin traveling at speed
157 void SUB_CalcMoveDone (void)
159 // After moving, set origin to exact final destination
161 setorigin (self, self.finaldest);
162 self.velocity = '0 0 0';
168 void SUB_CalcMove (vector tdest, float tspeed, void() func)
174 objerror ("No speed is defined!");
177 self.finaldest = tdest;
178 self.think = SUB_CalcMoveDone;
180 if (tdest == self.origin)
182 self.velocity = '0 0 0';
183 self.nextthink = self.ltime + 0.1;
187 delta = tdest - self.origin;
188 traveltime = vlen (delta) / tspeed;
190 if (traveltime < 0.1)
192 self.velocity = '0 0 0';
193 self.nextthink = self.ltime + 0.1;
197 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
199 self.nextthink = self.ltime + traveltime;
202 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
209 SUB_CalcMove (tdest, tspeed, func);
218 calculate self.avelocity and self.nextthink to reach destangle from
221 The calling function should make sure self.think is valid
224 void SUB_CalcAngleMoveDone (void)
226 // After rotating, set angle to exact final angle
227 self.angles = self.finalangle;
228 self.avelocity = '0 0 0';
234 // FIXME: I fixed this function only for rotation around the main axes
235 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
241 objerror ("No speed is defined!");
243 // take the shortest distance for the angles
244 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
245 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
246 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
247 delta = destangle - self.angles;
248 traveltime = vlen (delta) / tspeed;
251 self.finalangle = destangle;
252 self.think = SUB_CalcAngleMoveDone;
254 if (traveltime < 0.1)
256 self.avelocity = '0 0 0';
257 self.nextthink = self.ltime + 0.1;
261 self.avelocity = delta * (1 / traveltime);
262 self.nextthink = self.ltime + traveltime;
265 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
272 SUB_CalcAngleMove (destangle, tspeed, func);
281 unused but required by the engine
295 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
296 Additionally it moves players back into the past before the trace and restores them afterward.
299 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
302 local float oldsolid;
304 // check whether antilagged traces are enabled
307 if (clienttype(forent) != CLIENTTYPE_REAL)
308 lag = 0; // only antilag for clients
310 // change shooter to SOLID_BBOX so the shot can hit corpses
311 oldsolid = source.dphitcontentsmask;
312 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
316 // take players back into the past
317 player = player_list;
320 antilag_takeback(player, time - lag);
321 player = player.nextplayer;
326 traceline (v1, v2, nomonst, forent);
328 // restore players to current positions
331 player = player_list;
334 antilag_restore(player);
335 player = player.nextplayer;
339 // restore shooter solid type
340 source.dphitcontentsmask = oldsolid;
342 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
344 if (cvar("g_antilag") != 2)
346 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
349 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
354 //nudge = 2 * cvar("collision_impactnudge"); // why not?
357 dir = normalize(v2 - v1);
359 pos = v1 + dir * nudge;
366 if((pos - v1) * dir >= (v2 - v1) * dir)
374 tracebox(pos, mi, ma, v2, nomonsters, forent);
379 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
380 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
381 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
382 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
387 // we started inside solid.
388 // then trace from endpos to pos
390 tracebox(t, mi, ma, pos, nomonsters, forent);
394 // t is still inside solid? bad
395 // force advance, then, and retry
396 pos = t + dir * nudge;
400 // we actually LEFT solid!
401 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
407 // pos is outside solid?!? but why?!? never mind, just return it.
409 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
415 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
421 //nudge = 2 * cvar("collision_impactnudge"); // why not?
424 dir = normalize(v2 - v1);
426 pos = v1 + dir * nudge;
430 if((pos - v1) * dir >= (v2 - v1) * dir)
437 traceline(pos, v2, nomonsters, forent);
441 // we started inside solid.
442 // then trace from endpos to pos
444 traceline(t, pos, nomonsters, forent);
447 // t is inside solid? bad
448 // force advance, then
449 pos = pos + dir * nudge;
453 // we actually LEFT solid!
454 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
460 // pos is outside solid?!? but why?!? never mind, just return it.
462 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
467 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
474 Returns a point at least 12 units away from walls
475 (useful for explosion animations, although the blast is performed where it really happened)
479 vector findbetterlocation (vector org, float mindist)
485 vec = mindist * '1 0 0';
489 traceline (org, org + vec, TRUE, world);
491 if (trace_fraction < 1)
494 traceline (loc, loc + vec, TRUE, world);
495 if (trace_fraction >= 1)
515 Returns a random number between -1.0 and 1.0
520 return 2 * (random () - 0.5);
525 Angc used for animations
530 float angc (float a1, float a2)
557 .float lodmodelindex0;
558 .float lodmodelindex1;
559 .float lodmodelindex2;
563 vector NearestPointOnBox(entity box, vector org);
564 float LOD_customize()
570 d = cvar("loddebug");
572 self.modelindex = self.lodmodelindex0;
574 self.modelindex = self.lodmodelindex1;
576 self.modelindex = self.lodmodelindex2;
580 // TODO csqc network this so it only gets sent once
581 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
582 if(d < self.loddistance1)
583 self.modelindex = self.lodmodelindex0;
584 else if(!self.lodmodelindex2 || d < self.loddistance2)
585 self.modelindex = self.lodmodelindex1;
587 self.modelindex = self.lodmodelindex2;
592 void LOD_uncustomize()
594 self.modelindex = self.lodmodelindex0;
597 void LODmodel_attach()
601 if(!self.loddistance1)
602 self.loddistance1 = 1000;
603 if(!self.loddistance2)
604 self.loddistance2 = 2000;
605 self.lodmodelindex0 = self.modelindex;
607 if(self.lodtarget1 != "")
609 e = find(world, targetname, self.lodtarget1);
612 self.lodmodel1 = e.model;
616 if(self.lodtarget2 != "")
618 e = find(world, targetname, self.lodtarget2);
621 self.lodmodel2 = e.model;
626 if(cvar("loddebug") < 0)
628 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
631 if(self.lodmodel1 != "")
637 precache_model(self.lodmodel1);
638 setmodel(self, self.lodmodel1);
639 self.lodmodelindex1 = self.modelindex;
641 if(self.lodmodel2 != "")
643 precache_model(self.lodmodel2);
644 setmodel(self, self.lodmodel2);
645 self.lodmodelindex2 = self.modelindex;
648 self.modelindex = self.lodmodelindex0;
649 setsize(self, mi, ma);
652 if(self.lodmodelindex1)
653 SetCustomizer(self, LOD_customize, LOD_uncustomize);
656 void SetBrushEntityModel()
660 precache_model(self.model);
661 setmodel(self, self.model); // no precision needed
662 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
664 setorigin(self, self.origin);
666 setsize(self, self.mins * self.scale, self.maxs * self.scale);
668 setsize(self, self.mins, self.maxs);
679 if (self.movedir != '0 0 0')
680 self.movedir = normalize(self.movedir);
683 if (self.angles == '0 -1 0')
684 self.movedir = '0 0 1';
685 else if (self.angles == '0 -2 0')
686 self.movedir = '0 0 -1';
689 makevectors (self.angles);
690 self.movedir = v_forward;
694 self.angles = '0 0 0';
699 // trigger angles are used for one-way touches. An angle of 0 is assumed
700 // to mean no restrictions, so use a yaw of 360 instead.
701 if (self.movedir == '0 0 0')
702 if (self.angles != '0 0 0')
704 self.solid = SOLID_TRIGGER;
705 SetBrushEntityModel();
706 self.movetype = MOVETYPE_NONE;
711 void InitSolidBSPTrigger()
713 // trigger angles are used for one-way touches. An angle of 0 is assumed
714 // to mean no restrictions, so use a yaw of 360 instead.
715 if (self.movedir == '0 0 0')
716 if (self.angles != '0 0 0')
718 self.solid = SOLID_BSP;
719 SetBrushEntityModel();
720 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
721 // self.modelindex = 0;
725 float InitMovingBrushTrigger()
727 // trigger angles are used for one-way touches. An angle of 0 is assumed
728 // to mean no restrictions, so use a yaw of 360 instead.
729 self.solid = SOLID_BSP;
730 SetBrushEntityModel();
731 self.movetype = MOVETYPE_PUSH;
732 if(self.modelindex == 0)
734 objerror("InitMovingBrushTrigger: no brushes found!");