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)
25 if(anim_y == 1) // ZYM animation
26 BITXOR_ASSIGN(e.effects, EF_TELEPORT_BIT);
31 e.animstate_startframe = anim_x;
32 e.animstate_numframes = anim_y;
33 e.animstate_framerate = anim_z;
34 e.animstate_starttime = servertime - 0.1 * serverframetime; // shift it a little bit into the past to prevent float inaccuracy hiccups
35 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
36 e.animstate_looping = looping;
37 e.animstate_override = override;
38 e.frame = e.animstate_startframe;
41 void updateanim(entity e)
43 if (time >= e.animstate_endtime)
45 if (e.animstate_looping)
47 e.animstate_starttime = e.animstate_endtime;
48 e.animstate_endtime = e.animstate_starttime + e.animstate_numframes / e.animstate_framerate;
50 e.animstate_override = FALSE;
52 e.frame = e.animstate_startframe + bound(0, (time - e.animstate_starttime) * e.animstate_framerate, e.animstate_numframes - 1);
53 //print(ftos(time), " -> ", ftos(e.frame), "\n");
57 vector animparseline(float animfile)
64 line = fgets(animfile);
65 c = tokenize_console(line);
68 animparseerror = TRUE;
71 anim_x = stof(argv(0));
72 anim_y = stof(argv(1));
73 anim_z = stof(argv(2));
74 // don't allow completely bogus values
75 if (anim_x < 0 || anim_y < 1 || anim_z < 0.001)
87 void SUB_Remove (void)
96 Applies some friction to self
100 void SUB_Friction (void)
102 self.nextthink = time;
103 if(self.flags & FL_ONGROUND)
104 self.velocity = self.velocity * (1 - frametime * self.friction);
111 Makes client invisible or removes non-client
114 void SUB_VanishOrRemove (entity ent)
116 if (ent.flags & FL_CLIENT)
131 void SUB_SetFade_Think (void)
133 self.think = SUB_SetFade_Think;
134 self.nextthink = self.fade_time;
135 self.alpha = 1 - (time - self.fade_time) * self.fade_rate;
136 if (self.alpha < 0.01)
137 SUB_VanishOrRemove(self);
138 self.alpha = bound(0.01, self.alpha, 1);
145 Fade 'ent' out when time >= 'when'
148 void SUB_SetFade (entity ent, float when, float fadetime)
150 //if (ent.flags & FL_CLIENT) // && ent.deadflag != DEAD_NO)
153 ent.fade_rate = 1/fadetime;
154 ent.fade_time = when;
155 ent.think = SUB_SetFade_Think;
156 ent.nextthink = when;
163 calculate self.velocity and self.nextthink to reach dest from
164 self.origin traveling at speed
167 void SUB_CalcMoveDone (void)
169 // After moving, set origin to exact final destination
171 setorigin (self, self.finaldest);
172 self.velocity = '0 0 0';
178 void SUB_CalcMove (vector tdest, float tspeed, void() func)
184 objerror ("No speed is defined!");
187 self.finaldest = tdest;
188 self.think = SUB_CalcMoveDone;
190 if (tdest == self.origin)
192 self.velocity = '0 0 0';
193 self.nextthink = self.ltime + 0.1;
197 delta = tdest - self.origin;
198 traveltime = vlen (delta) / tspeed;
200 if (traveltime < 0.1)
202 self.velocity = '0 0 0';
203 self.nextthink = self.ltime + 0.1;
207 self.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division
209 self.nextthink = self.ltime + traveltime;
212 void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeed, void() func)
219 SUB_CalcMove (tdest, tspeed, func);
228 calculate self.avelocity and self.nextthink to reach destangle from
231 The calling function should make sure self.think is valid
234 void SUB_CalcAngleMoveDone (void)
236 // After rotating, set angle to exact final angle
237 self.angles = self.finalangle;
238 self.avelocity = '0 0 0';
244 // FIXME: I fixed this function only for rotation around the main axes
245 void SUB_CalcAngleMove (vector destangle, float tspeed, void() func)
251 objerror ("No speed is defined!");
253 // take the shortest distance for the angles
254 self.angles_x -= 360 * floor((self.angles_x - destangle_x) / 360 + 0.5);
255 self.angles_y -= 360 * floor((self.angles_y - destangle_y) / 360 + 0.5);
256 self.angles_z -= 360 * floor((self.angles_z - destangle_z) / 360 + 0.5);
257 delta = destangle - self.angles;
258 traveltime = vlen (delta) / tspeed;
261 self.finalangle = destangle;
262 self.think = SUB_CalcAngleMoveDone;
264 if (traveltime < 0.1)
266 self.avelocity = '0 0 0';
267 self.nextthink = self.ltime + 0.1;
271 self.avelocity = delta * (1 / traveltime);
272 self.nextthink = self.ltime + traveltime;
275 void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeed, void() func)
282 SUB_CalcAngleMove (destangle, tspeed, func);
291 unused but required by the engine
305 A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack
306 Additionally it moves players back into the past before the trace and restores them afterward.
309 void tracebox_antilag_force (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
312 local float oldsolid;
314 // check whether antilagged traces are enabled
317 if (clienttype(forent) != CLIENTTYPE_REAL)
318 lag = 0; // only antilag for clients
320 // change shooter to SOLID_BBOX so the shot can hit corpses
323 oldsolid = source.dphitcontentsmask;
324 source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
329 // take players back into the past
330 player = player_list;
333 antilag_takeback(player, time - lag);
334 player = player.nextplayer;
339 tracebox (v1, mi, ma, v2, nomonst, forent);
341 // restore players to current positions
344 player = player_list;
347 antilag_restore(player);
348 player = player.nextplayer;
352 // restore shooter solid type
354 source.dphitcontentsmask = oldsolid;
356 void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
358 tracebox_antilag_force(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag);
360 void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag)
362 if (cvar("g_antilag") != 2)
364 traceline_antilag_force(source, v1, v2, nomonst, forent, lag);
366 void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag)
368 if (cvar("g_antilag") != 2)
370 tracebox_antilag_force(source, v1, mi, ma, v2, nomonst, forent, lag);
373 float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent) // returns the number of traces done, for benchmarking
378 //nudge = 2 * cvar("collision_impactnudge"); // why not?
381 dir = normalize(v2 - v1);
383 pos = v1 + dir * nudge;
390 if((pos - v1) * dir >= (v2 - v1) * dir)
398 tracebox(pos, mi, ma, v2, nomonsters, forent);
403 dprint("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n");
404 dprint(" Nudging gets us nowhere at ", vtos(pos), "\n");
405 dprint(" trace_endpos is ", vtos(trace_endpos), "\n");
406 dprint(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n");
411 // we started inside solid.
412 // then trace from endpos to pos
414 tracebox(t, mi, ma, pos, nomonsters, forent);
418 // t is still inside solid? bad
419 // force advance, then, and retry
420 pos = t + dir * nudge;
424 // we actually LEFT solid!
425 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
431 // pos is outside solid?!? but why?!? never mind, just return it.
433 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
439 void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent)
445 //nudge = 2 * cvar("collision_impactnudge"); // why not?
448 dir = normalize(v2 - v1);
450 pos = v1 + dir * nudge;
454 if((pos - v1) * dir >= (v2 - v1) * dir)
461 traceline(pos, v2, nomonsters, forent);
465 // we started inside solid.
466 // then trace from endpos to pos
468 traceline(t, pos, nomonsters, forent);
471 // t is inside solid? bad
472 // force advance, then
473 pos = pos + dir * nudge;
477 // we actually LEFT solid!
478 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
484 // pos is outside solid?!? but why?!? never mind, just return it.
486 trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir);
491 tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent);
498 Returns a point at least 12 units away from walls
499 (useful for explosion animations, although the blast is performed where it really happened)
503 vector findbetterlocation (vector org, float mindist)
509 vec = mindist * '1 0 0';
513 traceline (org, org + vec, TRUE, world);
515 if (trace_fraction < 1)
518 traceline (loc, loc + vec, TRUE, world);
519 if (trace_fraction >= 1)
539 Returns a random number between -1.0 and 1.0
544 return 2 * (random () - 0.5);
549 Angc used for animations
554 float angc (float a1, float a2)
581 .float lodmodelindex0;
582 .float lodmodelindex1;
583 .float lodmodelindex2;
587 vector NearestPointOnBox(entity box, vector org);
588 float LOD_customize()
594 d = cvar("loddebug");
596 self.modelindex = self.lodmodelindex0;
598 self.modelindex = self.lodmodelindex1;
600 self.modelindex = self.lodmodelindex2;
604 // TODO csqc network this so it only gets sent once
605 d = vlen(NearestPointOnBox(self, other.origin) - other.origin);
606 if(d < self.loddistance1)
607 self.modelindex = self.lodmodelindex0;
608 else if(!self.lodmodelindex2 || d < self.loddistance2)
609 self.modelindex = self.lodmodelindex1;
611 self.modelindex = self.lodmodelindex2;
616 void LOD_uncustomize()
618 self.modelindex = self.lodmodelindex0;
621 void LODmodel_attach()
625 if(!self.loddistance1)
626 self.loddistance1 = 1000;
627 if(!self.loddistance2)
628 self.loddistance2 = 2000;
629 self.lodmodelindex0 = self.modelindex;
631 if(self.lodtarget1 != "")
633 e = find(world, targetname, self.lodtarget1);
636 self.lodmodel1 = e.model;
640 if(self.lodtarget2 != "")
642 e = find(world, targetname, self.lodtarget2);
645 self.lodmodel2 = e.model;
650 if(cvar("loddebug") < 0)
652 self.lodmodel1 = self.lodmodel2 = ""; // don't even initialize
655 if(self.lodmodel1 != "")
661 precache_model(self.lodmodel1);
662 setmodel(self, self.lodmodel1);
663 self.lodmodelindex1 = self.modelindex;
665 if(self.lodmodel2 != "")
667 precache_model(self.lodmodel2);
668 setmodel(self, self.lodmodel2);
669 self.lodmodelindex2 = self.modelindex;
672 self.modelindex = self.lodmodelindex0;
673 setsize(self, mi, ma);
676 if(self.lodmodelindex1)
677 SetCustomizer(self, LOD_customize, LOD_uncustomize);
680 void SetBrushEntityModel()
684 precache_model(self.model);
685 setmodel(self, self.model); // no precision needed
686 InitializeEntity(self, LODmodel_attach, INITPRIO_FINDTARGET);
688 setorigin(self, self.origin);
690 setsize(self, self.mins * self.scale, self.maxs * self.scale);
692 setsize(self, self.mins, self.maxs);
695 void SetBrushEntityModelNoLOD()
699 precache_model(self.model);
700 setmodel(self, self.model); // no precision needed
702 setorigin(self, self.origin);
704 setsize(self, self.mins * self.scale, self.maxs * self.scale);
706 setsize(self, self.mins, self.maxs);
717 if (self.movedir != '0 0 0')
718 self.movedir = normalize(self.movedir);
721 makevectors (self.angles);
722 self.movedir = v_forward;
725 self.angles = '0 0 0';
730 // trigger angles are used for one-way touches. An angle of 0 is assumed
731 // to mean no restrictions, so use a yaw of 360 instead.
732 if (self.movedir == '0 0 0')
733 if (self.angles != '0 0 0')
735 self.solid = SOLID_TRIGGER;
736 SetBrushEntityModel();
737 self.movetype = MOVETYPE_NONE;
742 void InitSolidBSPTrigger()
744 // trigger angles are used for one-way touches. An angle of 0 is assumed
745 // to mean no restrictions, so use a yaw of 360 instead.
746 if (self.movedir == '0 0 0')
747 if (self.angles != '0 0 0')
749 self.solid = SOLID_BSP;
750 SetBrushEntityModel();
751 self.movetype = MOVETYPE_NONE; // why was this PUSH? -div0
752 // self.modelindex = 0;
756 float InitMovingBrushTrigger()
758 // trigger angles are used for one-way touches. An angle of 0 is assumed
759 // to mean no restrictions, so use a yaw of 360 instead.
760 self.solid = SOLID_BSP;
761 SetBrushEntityModel();
762 self.movetype = MOVETYPE_PUSH;
763 if(self.modelindex == 0)
765 objerror("InitMovingBrushTrigger: no brushes found!");