#include "common.qh" #include "protocol.qh" #include "misc.qh" #include "teleport.qh" // ===================================================================== // 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 = NULL_string; 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; WriteByte(MSG_ONE, SVC_PRINT); WriteByte(MSG_ONE, _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; }; // ===================================================================== //