#include "common.qh" #include "protocol.qh" #include "misc.qh" #include "server.qh" #include "teleport.qh" #include "weapon.qh" .float tl_notsolid; // ===================================================================== // entity (string clname) spawn = { local entity e; e = BUILTIN_spawn (); e.classname = clname; return e; }; // ===================================================================== // void () SUB_remove = { remove (self); }; void (entity e) safe_remove = { e.takedamage = DAMAGE_NO; e.solid = SOLID_NOT; e.model = nil; e.modelindex = 0; e.think = SUB_remove; e.nextthink = time; }; // ===================================================================== // void () SUB_makestatic = { makestatic (self); }; void (entity e) safe_makestatic = { if (sv_spawning) { makestatic (self); return; } e.takedamage = DAMAGE_NO; e.solid = SOLID_NOT; e.think = SUB_makestatic; e.nextthink = time; }; // ===================================================================== // void () _sound_vector_think = { sound (self, CHAN_VOICE, self.noise1, self.volume, self.attenuation); remove (self); }; void (vector org, string samp, float vol, float atten) sound_vector = { local entity e; e = spawn("SOUND"); e.origin = org; e.noise1 = samp; e.volume = vol; e.attenuation = atten; e.think = _sound_vector_think; e.nextthink = time; }; // ===================================================================== // float (vector org) _missile_th_teleport = { self.owner = world; return TRUE; }; void (entity mis) missile_check_teleport; void () _missile_always_trigger = { self.think = SUB_remove; self.nextthink = self.attack_finished; if ((self.movetype != MOVETYPE_FLYMISSILE) && (self.movetype != MOVETYPE_FLY)) return; tl_proj_begin (); /* Make sure we're still going to hit the thing */ traceline (self.origin, self.origin + self.velocity * sv_maxtic * 2, FALSE, self); if (trace_ent == self.enemy) tl_touch (trace_ent, self); else self.enemy = world; tl_proj_end (); missile_check_teleport (self); }; void (entity mis) missile_check_teleport = { local float duration; tl_proj_begin (); trace_ent = mis; trace_endpos = mis.origin; duration = mis.attack_finished - time; while (duration > sv_maxtic) { traceline (trace_endpos, trace_endpos + mis.velocity * duration, FALSE, trace_ent); if (!trace_ent || trace_ent.solid == SOLID_BSP) { /* Don't bother tracing through BSP, it won't happen. */ break; } if (tl_issolid (trace_ent) || mis.enemy == trace_ent || (duration * trace_fraction) <= sv_maxtic) { /* We hit a triggered trigger, a solid ent, or something we _just_ hit */ if (trace_fraction == 0) { /* We're not going anywhere. Fudge it. */ break; } /* Trace on past it. */ duration -= duration * trace_fraction; continue; } /* Reached a [new] trigger */ mis.enemy = trace_ent; mis.think = _missile_always_trigger; mis.nextthink = time + duration * trace_fraction - sv_maxtic; break; } tl_proj_end (); }; entity (string clname, string mod, vector org, vector dir, float spd) spawn_missile = { newmis = spawn (clname); newmis.owner = self; newmis.goalentity = self; newmis.movetype = MOVETYPE_FLYMISSILE; newmis.solid = SOLID_BBOX; newmis.deadflag = DEAD_NONLIVING; newmis.th_teleport = _missile_th_teleport; setmodel (newmis, mod); setsize (newmis, '0 0 0', '0 0 0'); setorigin (newmis, org); newmis.angles = vectoangles (dir); newmis.speed = spd; newmis.velocity = dir * spd; newmis.attack_finished = time + (6000 / spd); newmis.think = SUB_remove; newmis.nextthink = newmis.attack_finished; missile_check_teleport (newmis); return newmis; }; // ===================================================================== // entity tl_first; .entity tl_next; void () tl_proj_begin = { local entity walk; tl_first = world; for (walk = nextent (world); walk; walk = nextent (walk)) { if (walk.solid != SOLID_TRIGGER) continue; walk.tl_next = tl_first; tl_first = walk; walk.solid = SOLID_BBOX; walk.tl_notsolid = TRUE; setorigin (walk, walk.origin); // relink } }; void () tl_proj_end = { local entity walk; for (walk = tl_first; walk; walk = walk.tl_next) { walk.solid = SOLID_TRIGGER; walk.tl_notsolid = FALSE; setorigin (walk, walk.origin); // relink } }; void (entity trigger, entity fake_proj) tl_touch = { local entity oldself, oldother; if (!trigger.touch) return; tl_proj_end (); oldself = self; oldother = other; self = trigger; other = fake_proj; self.touch (); self = oldself; other = oldother; tl_proj_begin (); }; // ===================================================================== // string (entity e) name = { if (e.netname) return e.netname; if (e.classname) return e.classname; if (e.velocity) return "an unidentified flying object"; return "an unidentified stationary object"; }; // ===================================================================== // void (.string fld, string match, void (entity e) func) foreach = { local entity ent; ent = world; while ((ent = find (ent, fld, match))) { if (!func) { dprint ("NULL function in foreach, classname: ", ent.classname, "\n"); continue; } func (ent); } }; void (.string fld, string match, .void () func) foreach_field = { local entity oldself; oldself = self; self = world; while ((self = find (self, fld, match))) { if (!self.func) { dprint ("NULL function in foreach_field, classname: ", self.classname, "\n"); continue; } self.func (); } self = oldself; }; // ===================================================================== // float (entity newself, entity newother, float () func) switcheroo = { local entity oldself, oldother; local float ret; oldself = self; oldother = other; self = newself; other = newother; ret = func (); self = oldself; other = oldother; return ret; }; // ===================================================================== // float (float current, float increment, float max) increase_bound = { local float diff; diff = max - current; if (diff <= 0) return current; if (diff > increment) diff = increment; return current + diff; }; float (.float fld, float increment, float max) increase_field = { local float new; new = increase_bound (self.fld, increment, max); if (new == self.fld) return FALSE; self.fld = new; return TRUE; }; // ===================================================================== // /* Not to be confused with maxspeed */ float (entity e) calc_max_speed = { local float spd; spd = e.maxspeed; if (self.waterlevel >= 2) spd = spd * 0.7; // Bah. Hard coded in engine. else if (!self.waterlevel && !(self.flags & FL_ONGROUND)) { if (spd > 30) spd = 30; // Also hard coded. } return spd; }; // ===================================================================== // entity _xprint_client; float _xprint_level; void (entity client, float level) xprint_start = { _xprint_client = client; _xprint_level = level; }; void (string str) xprint_str = { msg_entity = _xprint_client; WriteBytes (MSG_ONE, SVC_PRINT, _xprint_level); WriteString (MSG_ONE, str); }; // ===================================================================== // entity (.string fld, string str) find_random = { local float r, numents; local entity ent; numents = 0; r = floor (random () * 512); while (1) { ent = find (world, fld, str); if (!ent) return world; while (ent) { numents++; r--; if (r <= 0) return ent; ent = find (ent, fld, str); } r -= numents * floor (r / numents); } return world; }; // ===================================================================== // entity (entity ech) random_enemy_chain = { local entity walk; local float tot, r; r = floor (random () * 64) + 1; tot = 1; for (walk = ech.enemy; walk != ech; walk = walk.enemy) { r--; if (r <= 0) return walk; tot++; } if (!tot) return ech; /* Ok, only look at half the remaining ones */ tot = floor (tot * 0.5); if (!tot) return ech.enemy; r -= tot * floor(r / tot); for (walk = ech.enemy; walk; walk = walk.enemy) { if (r <= 0) return walk; r--; } return world; };