--- /dev/null
+#ifdef SVQC
+.entity sprite;
+
+.float dmg;
+.float dmg_edge;
+.float dmg_radius;
+.float dmg_force;
+.float debrismovetype;
+.float debrissolid;
+.vector debrisvelocity;
+.vector debrisvelocityjitter;
+.vector debrisavelocityjitter;
+.float debristime;
+.float debristimejitter;
+.float debrisfadetime;
+.float debrisdamageforcescale;
+.float debrisskin;
+
+.string mdl_dead; // or "" to hide when broken
+.string debris; // space separated list of debris models
+// other fields:
+// mdl = particle effect name
+// count = particle effect multiplier
+// targetname = target to trigger to unbreak the model
+// target = targets to trigger when broken
+// health = amount of damage it can take
+// spawnflags:
+// 1 = start disabled (needs to be triggered to activate)
+// 2 = indicate damage
+// notes:
+// for mdl_dead to work, origin must be set (using a common/origin brush).
+// Otherwise mdl_dead will be displayed at the map origin, and nobody would
+// want that!
+
+void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
+
+//
+// func_breakable
+// - basically func_assault_destructible for general gameplay use
+//
+void LaunchDebris (string debrisname, vector force)
+{
+ local entity dbr;
+
+ dbr = spawn();
+ setorigin(dbr, self.absmin
+ + '1 0 0' * random() * (self.absmax_x - self.absmin_x)
+ + '0 1 0' * random() * (self.absmax_y - self.absmin_y)
+ + '0 0 1' * random() * (self.absmax_z - self.absmin_z));
+ setmodel (dbr, debrisname );
+ dbr.skin = self.debrisskin;
+ dbr.colormap = self.colormap; // inherit team colors
+ dbr.owner = self; // do not be affected by our own explosion
+ dbr.movetype = self.debrismovetype;
+ dbr.solid = self.debrissolid;
+ if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out
+ setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it
+ dbr.velocity_x = self.debrisvelocity_x + self.debrisvelocityjitter_x * crandom();
+ dbr.velocity_y = self.debrisvelocity_y + self.debrisvelocityjitter_y * crandom();
+ dbr.velocity_z = self.debrisvelocity_z + self.debrisvelocityjitter_z * crandom();
+ self.velocity = self.velocity + force * self.debrisdamageforcescale;
+ dbr.avelocity_x = random()*self.debrisavelocityjitter_x;
+ dbr.avelocity_y = random()*self.debrisavelocityjitter_y;
+ dbr.avelocity_z = random()*self.debrisavelocityjitter_z;
+ dbr.damageforcescale = self.debrisdamageforcescale;
+ if(dbr.damageforcescale)
+ dbr.takedamage = DAMAGE_YES;
+ SUB_SetFade(dbr, time + self.debristime + crandom() * self.debristimejitter, self.debrisfadetime);
+}
+
+void func_breakable_colormod()
+{
+ float h;
+ if (!(self.spawnflags & 2))
+ return;
+ h = self.health / self.max_health;
+ if(h < 0.25)
+ self.colormod = '1 0 0';
+ else if(h <= 0.75)
+ self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
+ else
+ self.colormod = '1 1 1';
+
+ CSQCMODEL_AUTOUPDATE();
+}
+
+void func_breakable_look_destroyed()
+{
+ float floor_z;
+
+ if(self.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first
+ self.dropped_origin = self.origin;
+
+ if(self.mdl_dead == "")
+ self.model = "";
+ else {
+ if (self.origin == '0 0 0') { // probably no origin brush, so don't spawn in the middle of the map..
+ floor_z = self.absmin_z;
+ setorigin(self,((self.absmax+self.absmin)*.5));
+ self.origin_z = floor_z;
+ }
+ setmodel(self, self.mdl_dead);
+ }
+
+ self.solid = SOLID_NOT;
+}
+
+void func_breakable_look_restore()
+{
+ setmodel(self, self.mdl);
+ if(self.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow
+ setorigin(self, self.dropped_origin);
+ self.solid = SOLID_BSP;
+}
+
+void func_breakable_behave_destroyed()
+{
+ self.health = self.max_health;
+ self.takedamage = DAMAGE_NO;
+ self.bot_attack = FALSE;
+ self.event_damage = func_null;
+ self.state = 1;
+ func_breakable_colormod();
+}
+
+void func_breakable_behave_restore()
+{
+ self.health = self.max_health;
+ if(self.sprite)
+ {
+ WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+ }
+ self.takedamage = DAMAGE_AIM;
+ self.bot_attack = TRUE;
+ self.event_damage = func_breakable_damage;
+ self.state = 0;
+ self.nextthink = 0; // cancel auto respawn
+ func_breakable_colormod();
+}
+
+void func_breakable_destroyed()
+{
+ func_breakable_look_destroyed();
+ func_breakable_behave_destroyed();
+
+ CSQCMODEL_AUTOUPDATE();
+}
+
+void func_breakable_restore()
+{
+ func_breakable_look_restore();
+ func_breakable_behave_restore();
+
+ CSQCMODEL_AUTOUPDATE();
+}
+
+vector debrisforce; // global, set before calling this
+void func_breakable_destroy() {
+ float n, i;
+ string oldmsg;
+
+ activator = self.owner;
+ self.owner = world; // set by W_PrepareExplosionByDamage
+
+ // now throw around the debris
+ n = tokenize_console(self.debris);
+ for(i = 0; i < n; ++i)
+ LaunchDebris(argv(i), debrisforce);
+
+ func_breakable_destroyed();
+
+ if(self.noise)
+ sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
+
+ if(self.dmg)
+ RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world);
+
+ if(self.cnt)
+ pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
+
+ if(self.respawntime)
+ {
+ self.think = func_breakable_restore;
+ self.nextthink = time + self.respawntime + crandom() * self.respawntimejitter;
+ }
+
+ oldmsg = self.message;
+ self.message = "";
+ SUB_UseTargets();
+ self.message = oldmsg;
+}
+
+void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
+{
+ if(self.state == 1)
+ return;
+ if(self.spawnflags & DOOR_NOSPLASH)
+ if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
+ return;
+ if(self.team)
+ if(attacker.team == self.team)
+ return;
+ self.health = self.health - damage;
+ if(self.sprite)
+ {
+ WaypointSprite_Ping(self.sprite);
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+ }
+ func_breakable_colormod();
+
+ if(self.health <= 0)
+ {
+ debrisforce = force;
+ W_PrepareExplosionByDamage(attacker, func_breakable_destroy);
+ }
+}
+
+void func_breakable_reset()
+{
+ self.team = self.team_saved;
+ func_breakable_look_restore();
+ if(self.spawnflags & 1)
+ func_breakable_behave_destroyed();
+ else
+ func_breakable_behave_restore();
+
+ CSQCMODEL_AUTOUPDATE();
+}
+
+// destructible walls that can be used to trigger target_objective_decrease
+void spawnfunc_func_breakable() {
+ float n, i;
+ if(!self.health)
+ self.health = 100;
+ self.max_health = self.health;
+
+ // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway
+ if(!self.debrismovetype) self.debrismovetype = MOVETYPE_BOUNCE;
+ if(!self.debrissolid) self.debrissolid = SOLID_NOT;
+ if(self.debrisvelocity == '0 0 0') self.debrisvelocity = '0 0 140';
+ if(self.debrisvelocityjitter == '0 0 0') self.debrisvelocityjitter = '70 70 70';
+ if(self.debrisavelocityjitter == '0 0 0') self.debrisavelocityjitter = '600 600 600';
+ if(!self.debristime) self.debristime = 3.5;
+ if(!self.debristimejitter) self.debristime = 2.5;
+
+ if(self.mdl != "")
+ self.cnt = particleeffectnum(self.mdl);
+ if(self.count == 0)
+ self.count = 1;
+
+ if(self.message == "")
+ self.message = "got too close to an explosion";
+ if(self.message2 == "")
+ self.message2 = "was pushed into an explosion by";
+ if(!self.dmg_radius)
+ self.dmg_radius = 150;
+ if(!self.dmg_force)
+ self.dmg_force = 200;
+
+ self.mdl = self.model;
+ SetBrushEntityModel();
+
+ self.use = func_breakable_restore;
+
+ // precache all the models
+ if (self.mdl_dead)
+ precache_model(self.mdl_dead);
+ n = tokenize_console(self.debris);
+ for(i = 0; i < n; ++i)
+ precache_model(argv(i));
+ if(self.noise)
+ precache_sound(self.noise);
+
+ self.team_saved = self.team;
+ self.dropped_origin = self.origin;
+
+ self.reset = func_breakable_reset;
+ func_breakable_reset();
+
+ CSQCMODEL_AUTOINIT();
+}
+
+// for use in maps with a "model" key set
+void spawnfunc_misc_breakablemodel() {
+ spawnfunc_func_breakable();
+}
+#endif
#include "bobbing.qc"
+#include "breakable.qc"
#include "button.qc"
#include "conveyor.qc"
#include "door.qc"
#include "changelevel.qc"
+#include "music.qc"
+#include "spawn.qc"
#include "speaker.qc"
#include "voicescript.qc"
--- /dev/null
+#ifdef SVQC
+.float lifetime;
+// values:
+// volume
+// noise
+// targetname
+// lifetime
+// fade_time
+// fade_rate
+// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0)
+// when targetname is not set, THIS ONE is default
+void target_music_sendto(float to, float is)
+{
+ WriteByte(to, SVC_TEMPENTITY);
+ WriteByte(to, TE_CSQC_TARGET_MUSIC);
+ WriteShort(to, num_for_edict(self));
+ WriteByte(to, self.volume * 255.0 * is);
+ WriteByte(to, self.fade_time * 16.0);
+ WriteByte(to, self.fade_rate * 16.0);
+ WriteByte(to, self.lifetime);
+ WriteString(to, self.noise);
+}
+void target_music_reset()
+{
+ if(self.targetname == "")
+ target_music_sendto(MSG_ALL, 1);
+}
+void target_music_use()
+{
+ if(!activator)
+ return;
+ if(IS_REAL_CLIENT(activator))
+ {
+ msg_entity = activator;
+ target_music_sendto(MSG_ONE, 1);
+ }
+ entity head;
+ FOR_EACH_SPEC(head) if(head.enemy == activator) { msg_entity = head; target_music_sendto(MSG_ONE, 1); }
+}
+void spawnfunc_target_music()
+{
+ self.use = target_music_use;
+ self.reset = target_music_reset;
+ if(!self.volume)
+ self.volume = 1;
+ if(self.targetname == "")
+ target_music_sendto(MSG_INIT, 1);
+ else
+ target_music_sendto(MSG_INIT, 0);
+}
+void TargetMusic_RestoreGame()
+{
+ for(self = world; (self = find(self, classname, "target_music")); )
+ {
+ if(self.targetname == "")
+ target_music_sendto(MSG_INIT, 1);
+ else
+ target_music_sendto(MSG_INIT, 0);
+ }
+}
+// values:
+// volume
+// noise
+// targetname
+// fade_time
+// spawnflags:
+// 1 = START_OFF
+// when triggered, it is disabled/enabled for everyone
+float trigger_music_SendEntity(entity to, float sf)
+{
+ WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
+ sf &= ~0x80;
+ if(self.cnt)
+ sf |= 0x80;
+ WriteByte(MSG_ENTITY, sf);
+ if(sf & 4)
+ {
+ WriteCoord(MSG_ENTITY, self.origin_x);
+ WriteCoord(MSG_ENTITY, self.origin_y);
+ WriteCoord(MSG_ENTITY, self.origin_z);
+ }
+ if(sf & 1)
+ {
+ if(self.model != "null")
+ {
+ WriteShort(MSG_ENTITY, self.modelindex);
+ WriteCoord(MSG_ENTITY, self.mins_x);
+ WriteCoord(MSG_ENTITY, self.mins_y);
+ WriteCoord(MSG_ENTITY, self.mins_z);
+ WriteCoord(MSG_ENTITY, self.maxs_x);
+ WriteCoord(MSG_ENTITY, self.maxs_y);
+ WriteCoord(MSG_ENTITY, self.maxs_z);
+ }
+ else
+ {
+ WriteShort(MSG_ENTITY, 0);
+ WriteCoord(MSG_ENTITY, self.maxs_x);
+ WriteCoord(MSG_ENTITY, self.maxs_y);
+ WriteCoord(MSG_ENTITY, self.maxs_z);
+ }
+ WriteByte(MSG_ENTITY, self.volume * 255.0);
+ WriteByte(MSG_ENTITY, self.fade_time * 16.0);
+ WriteByte(MSG_ENTITY, self.fade_rate * 16.0);
+ WriteString(MSG_ENTITY, self.noise);
+ }
+ return 1;
+}
+void trigger_music_reset()
+{
+ self.cnt = !(self.spawnflags & 1);
+ self.SendFlags |= 0x80;
+}
+void trigger_music_use()
+{
+ self.cnt = !self.cnt;
+ self.SendFlags |= 0x80;
+}
+void spawnfunc_trigger_music()
+{
+ if(self.model != "")
+ setmodel(self, self.model);
+ if(!self.volume)
+ self.volume = 1;
+ if(!self.modelindex)
+ {
+ setorigin(self, self.origin + self.mins);
+ setsize(self, '0 0 0', self.maxs - self.mins);
+ }
+ trigger_music_reset();
+
+ self.use = trigger_music_use;
+ self.reset = trigger_music_reset;
+
+ Net_LinkEntity(self, FALSE, 0, trigger_music_SendEntity);
+}
+#endif
--- /dev/null
+#ifdef SVQC
+// spawner entity
+// "classname" "target_spawn"
+// "message" "fieldname value fieldname value ..."
+// "spawnflags"
+// 1 = call the spawn function
+// 2 = trigger on map load
+
+float target_spawn_initialized;
+.void() target_spawn_spawnfunc;
+float target_spawn_spawnfunc_field;
+.entity target_spawn_activator;
+.float target_spawn_id;
+float target_spawn_count;
+
+void target_spawn_helper_setmodel()
+{
+ setmodel(self, self.model);
+}
+
+void target_spawn_helper_setsize()
+{
+ setsize(self, self.mins, self.maxs);
+}
+
+void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act)
+{
+ float i, n, valuefieldpos;
+ string key, value, valuefield, valueoffset, valueoffsetrandom;
+ entity valueent;
+ vector data, data2;
+ entity oldself;
+ entity oldactivator;
+
+ n = tokenize_console(msg);
+
+ for(i = 0; i < n-1; i += 2)
+ {
+ key = argv(i);
+ value = argv(i+1);
+ if(key == "$")
+ {
+ data_x = -1;
+ data_y = FIELD_STRING;
+ }
+ else
+ {
+ data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key)));
+ if(data_y == 0) // undefined field, i.e., invalid type
+ {
+ print("target_spawn: invalid/unknown entity key ", key, " specified, ignored!\n");
+ continue;
+ }
+ }
+ if(substring(value, 0, 1) == "$")
+ {
+ value = substring(value, 1, strlen(value) - 1);
+ if(substring(value, 0, 1) == "$")
+ {
+ // deferred replacement
+ // do nothing
+ // useful for creating target_spawns with this!
+ }
+ else
+ {
+ // replace me!
+ valuefieldpos = strstrofs(value, "+", 0);
+ valueoffset = "";
+ if(valuefieldpos != -1)
+ {
+ valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
+ value = substring(value, 0, valuefieldpos);
+ }
+
+ valuefieldpos = strstrofs(valueoffset, "+", 0);
+ valueoffsetrandom = "";
+ if(valuefieldpos != -1)
+ {
+ valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1);
+ valueoffset = substring(valueoffset, 0, valuefieldpos);
+ }
+
+ valuefieldpos = strstrofs(value, ".", 0);
+ valuefield = "";
+ if(valuefieldpos != -1)
+ {
+ valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
+ value = substring(value, 0, valuefieldpos);
+ }
+
+ if(value == "self")
+ {
+ valueent = self;
+ value = "";
+ }
+ else if(value == "activator")
+ {
+ valueent = act;
+ value = "";
+ }
+ else if(value == "other")
+ {
+ valueent = other;
+ value = "";
+ }
+ else if(value == "pusher")
+ {
+ if(time < act.pushltime)
+ valueent = act.pusher;
+ else
+ valueent = world;
+ value = "";
+ }
+ else if(value == "target")
+ {
+ valueent = e;
+ value = "";
+ }
+ else if(value == "killtarget")
+ {
+ valueent = kt;
+ value = "";
+ }
+ else if(value == "target2")
+ {
+ valueent = t2;
+ value = "";
+ }
+ else if(value == "target3")
+ {
+ valueent = t3;
+ value = "";
+ }
+ else if(value == "target4")
+ {
+ valueent = t4;
+ value = "";
+ }
+ else if(value == "time")
+ {
+ valueent = world;
+ value = ftos(time);
+ }
+ else
+ {
+ print("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!\n");
+ continue;
+ }
+
+ if(valuefield == "")
+ {
+ if(value == "")
+ value = ftos(num_for_edict(valueent));
+ }
+ else
+ {
+ if(value != "")
+ {
+ print("target_spawn: try to get a field of a non-entity, ignored!\n");
+ continue;
+ }
+ data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield)));
+ if(data2_y == 0) // undefined field, i.e., invalid type
+ {
+ print("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!\n");
+ continue;
+ }
+ value = getentityfieldstring(data2_x, valueent);
+ }
+
+ if(valueoffset != "")
+ {
+ switch(data_y)
+ {
+ case FIELD_STRING:
+ value = strcat(value, valueoffset);
+ break;
+ case FIELD_FLOAT:
+ value = ftos(stof(value) + stof(valueoffset));
+ break;
+ case FIELD_VECTOR:
+ value = vtos(stov(value) + stov(valueoffset));
+ break;
+ default:
+ print("target_spawn: only string, float and vector fields can do calculations, calculation ignored!\n");
+ break;
+ }
+ }
+
+ if(valueoffsetrandom != "")
+ {
+ switch(data_y)
+ {
+ case FIELD_FLOAT:
+ value = ftos(stof(value) + random() * stof(valueoffsetrandom));
+ break;
+ case FIELD_VECTOR:
+ data2 = stov(valueoffsetrandom);
+ value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1');
+ break;
+ default:
+ print("target_spawn: only float and vector fields can do random calculations, calculation ignored!\n");
+ break;
+ }
+ }
+ }
+ }
+ if(key == "$")
+ {
+ if(substring(value, 0, 1) == "_")
+ value = strcat("target_spawn_helper", value);
+ putentityfieldstring(target_spawn_spawnfunc_field, e, value);
+
+ oldself = self;
+ oldactivator = activator;
+
+ self = e;
+ activator = act;
+
+ self.target_spawn_spawnfunc();
+
+ self = oldself;
+ activator = oldactivator;
+ }
+ else
+ {
+ if(data_y == FIELD_VECTOR)
+ value = strreplace("'", "", value); // why?!?
+ putentityfieldstring(data_x, e, value);
+ }
+ }
+}
+
+void target_spawn_useon(entity e)
+{
+ self.target_spawn_activator = activator;
+ target_spawn_edit_entity(
+ e,
+ self.message,
+ find(world, targetname, self.killtarget),
+ find(world, targetname, self.target2),
+ find(world, targetname, self.target3),
+ find(world, targetname, self.target4),
+ activator
+ );
+}
+
+float target_spawn_cancreate()
+{
+ float c;
+ entity e;
+
+ c = self.count;
+ if(c == 0) // no limit?
+ return 1;
+
+ ++c; // increase count to not include MYSELF
+ for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c)
+ ;
+
+ // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more
+ if(c == 0)
+ return 0;
+ return 1;
+}
+
+void target_spawn_use()
+{
+ entity e;
+
+ if(self.target == "")
+ {
+ // spawn new entity
+ if(!target_spawn_cancreate())
+ return;
+ e = spawn();
+ target_spawn_useon(e);
+ e.target_spawn_id = self.target_spawn_id;
+ }
+ else if(self.target == "*activator")
+ {
+ // edit entity
+ if(activator)
+ target_spawn_useon(activator);
+ }
+ else
+ {
+ // edit entity
+ for(e = world; (e = find(e, targetname, self.target)); )
+ target_spawn_useon(e);
+ }
+}
+
+void target_spawn_spawnfirst()
+{
+ activator = self.target_spawn_activator;
+ if(self.spawnflags & 2)
+ target_spawn_use();
+}
+
+void initialize_field_db()
+{
+ if(!target_spawn_initialized)
+ {
+ float n, i;
+ string fn;
+ vector prev, new;
+ float ft;
+
+ n = numentityfields();
+ for(i = 0; i < n; ++i)
+ {
+ fn = entityfieldname(i);
+ ft = entityfieldtype(i);
+ new = i * '1 0 0' + ft * '0 1 0' + '0 0 1';
+ prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn)));
+ if(prev_y == 0)
+ {
+ db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(new));
+ if(fn == "target_spawn_spawnfunc")
+ target_spawn_spawnfunc_field = i;
+ }
+ }
+
+ target_spawn_initialized = 1;
+ }
+}
+
+void spawnfunc_target_spawn()
+{
+ initialize_field_db();
+ self.use = target_spawn_use;
+ self.message = strzone(strreplace("'", "\"", self.message));
+ self.target_spawn_id = ++target_spawn_count;
+ InitializeEntity(self, target_spawn_spawnfirst, INITPRIO_LAST);
+}
+#endif
#include "multivibrator.qc"
#include "relay.qc"
#include "relay_activators.qc"
+#include "relay_if.qc"
#include "relay_teamcheck.qc"
+#include "secret.qc"
#include "swamp.qc"
#include "multi.qh"
#include "jumppads.qh"
+#include "secret.qh"
#include "swamp.qh"
#ifdef SVQC
-const float PUSH_ONCE = 1;
-const float PUSH_SILENT = 2;
-
-.float pushltime;
-.float istypefrag;
-
-void() SUB_UseTargets;
-
void trigger_push_use()
{
if(teamplay)
void ent_trigger_push();
void ent_target_push();
#endif
+
+const float PUSH_ONCE = 1;
+const float PUSH_SILENT = 2;
+
+.float pushltime;
+.float istypefrag;
+
+void() SUB_UseTargets;
--- /dev/null
+#ifdef SVQC
+void trigger_relay_if_use()
+{
+ float n;
+ n = self.count;
+
+ // TODO make this generic AND faster than nextent()ing through all, if somehow possible
+ n = (cvar_string(self.netname) == cvar_string(self.message));
+ if(self.spawnflags & 1)
+ n = !n;
+
+ if(n)
+ SUB_UseTargets();
+}
+
+void spawnfunc_trigger_relay_if()
+{
+ self.use = trigger_relay_if_use;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+void secrets_setstatus() {
+ self.stat_secrets_total = secrets_total;
+ self.stat_secrets_found = secrets_found;
+}
+
+/**
+ * A secret has been found (maybe :P)
+ */
+void trigger_secret_touch() {
+ // only a player can trigger this
+ if (!IS_PLAYER(other))
+ return;
+
+ // update secrets found counter
+ secrets_found += 1;
+ //print("Secret found: ", ftos(secret_counter.cnt), "/");
+ //print(ftos(secret_counter.count), "\n");
+
+ // centerprint message (multi_touch() doesn't always call centerprint())
+ centerprint(other, self.message);
+ self.message = "";
+
+ // handle normal trigger features
+ multi_touch();
+ remove(self);
+}
+
+/*QUAKED trigger_secret (.5 .5 .5) ?
+Variable sized secret trigger. Can be targeted at one or more entities.
+Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found.
+-------- KEYS --------
+sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1)
+noise: path to sound file, if you want to play something else
+target: trigger all entities with this targetname when triggered
+message: print this message to the player who activated the trigger instead of the standard 'You found a secret!'
+killtarget: remove all entities with this targetname when triggered
+-------- NOTES --------
+You should create a common/trigger textured brush covering the entrance to a secret room/area.
+Trigger secret can only be trigger by a player's touch and can not be a target itself.
+*/
+void spawnfunc_trigger_secret() {
+ // FIXME: should it be disabled in most modes?
+
+ // update secrets count
+ secrets_total += 1;
+
+ // add default message
+ if (self.message == "")
+ self.message = "You found a secret!";
+
+ // set default sound
+ if (self.noise == "")
+ if (!self.sounds)
+ self.sounds = 1; // misc/secret.wav
+
+ // this entity can't be a target itself!!!!
+ self.targetname = "";
+
+ // you can't just shoot a room to find it, can you?
+ self.health = 0;
+
+ // a secret can not be delayed
+ self.delay = 0;
+
+ // convert this trigger to trigger_once
+ self.classname = "trigger_once";
+ spawnfunc_trigger_once();
+
+ // take over the touch() function, so we can mark secret as found
+ self.touch = trigger_secret_touch;
+ // ignore triggering;
+ self.use = func_null;
+}
+#endif
--- /dev/null
+#ifdef SVQC
+/**
+ * Total number of secrets on the map.
+ */
+float secrets_total;
+
+/**
+ * Total numbe of secrets found on the map.
+ */
+float secrets_found;
+
+
+.float stat_secrets_total;
+.float stat_secrets_found;
+
+/**
+ * update secrets status.
+ */
+void secrets_setstatus();
+#endif
+++ /dev/null
-.entity sprite;
-
-.float dmg;
-.float dmg_edge;
-.float dmg_radius;
-.float dmg_force;
-.float debrismovetype;
-.float debrissolid;
-.vector debrisvelocity;
-.vector debrisvelocityjitter;
-.vector debrisavelocityjitter;
-.float debristime;
-.float debristimejitter;
-.float debrisfadetime;
-.float debrisdamageforcescale;
-.float debrisskin;
-
-.string mdl_dead; // or "" to hide when broken
-.string debris; // space separated list of debris models
-// other fields:
-// mdl = particle effect name
-// count = particle effect multiplier
-// targetname = target to trigger to unbreak the model
-// target = targets to trigger when broken
-// health = amount of damage it can take
-// spawnflags:
-// 1 = start disabled (needs to be triggered to activate)
-// 2 = indicate damage
-// notes:
-// for mdl_dead to work, origin must be set (using a common/origin brush).
-// Otherwise mdl_dead will be displayed at the map origin, and nobody would
-// want that!
-
-void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force);
-
-//
-// func_breakable
-// - basically func_assault_destructible for general gameplay use
-//
-void LaunchDebris (string debrisname, vector force)
-{
- local entity dbr;
-
- dbr = spawn();
- setorigin(dbr, self.absmin
- + '1 0 0' * random() * (self.absmax_x - self.absmin_x)
- + '0 1 0' * random() * (self.absmax_y - self.absmin_y)
- + '0 0 1' * random() * (self.absmax_z - self.absmin_z));
- setmodel (dbr, debrisname );
- dbr.skin = self.debrisskin;
- dbr.colormap = self.colormap; // inherit team colors
- dbr.owner = self; // do not be affected by our own explosion
- dbr.movetype = self.debrismovetype;
- dbr.solid = self.debrissolid;
- if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out
- setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it
- dbr.velocity_x = self.debrisvelocity_x + self.debrisvelocityjitter_x * crandom();
- dbr.velocity_y = self.debrisvelocity_y + self.debrisvelocityjitter_y * crandom();
- dbr.velocity_z = self.debrisvelocity_z + self.debrisvelocityjitter_z * crandom();
- self.velocity = self.velocity + force * self.debrisdamageforcescale;
- dbr.avelocity_x = random()*self.debrisavelocityjitter_x;
- dbr.avelocity_y = random()*self.debrisavelocityjitter_y;
- dbr.avelocity_z = random()*self.debrisavelocityjitter_z;
- dbr.damageforcescale = self.debrisdamageforcescale;
- if(dbr.damageforcescale)
- dbr.takedamage = DAMAGE_YES;
- SUB_SetFade(dbr, time + self.debristime + crandom() * self.debristimejitter, self.debrisfadetime);
-}
-
-void func_breakable_colormod()
-{
- float h;
- if (!(self.spawnflags & 2))
- return;
- h = self.health / self.max_health;
- if(h < 0.25)
- self.colormod = '1 0 0';
- else if(h <= 0.75)
- self.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5);
- else
- self.colormod = '1 1 1';
-
- CSQCMODEL_AUTOUPDATE();
-}
-
-void func_breakable_look_destroyed()
-{
- float floor_z;
-
- if(self.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first
- self.dropped_origin = self.origin;
-
- if(self.mdl_dead == "")
- self.model = "";
- else {
- if (self.origin == '0 0 0') { // probably no origin brush, so don't spawn in the middle of the map..
- floor_z = self.absmin_z;
- setorigin(self,((self.absmax+self.absmin)*.5));
- self.origin_z = floor_z;
- }
- setmodel(self, self.mdl_dead);
- }
-
- self.solid = SOLID_NOT;
-}
-
-void func_breakable_look_restore()
-{
- setmodel(self, self.mdl);
- if(self.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow
- setorigin(self, self.dropped_origin);
- self.solid = SOLID_BSP;
-}
-
-void func_breakable_behave_destroyed()
-{
- self.health = self.max_health;
- self.takedamage = DAMAGE_NO;
- self.bot_attack = FALSE;
- self.event_damage = func_null;
- self.state = 1;
- func_breakable_colormod();
-}
-
-void func_breakable_behave_restore()
-{
- self.health = self.max_health;
- if(self.sprite)
- {
- WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.sprite, self.health);
- }
- self.takedamage = DAMAGE_AIM;
- self.bot_attack = TRUE;
- self.event_damage = func_breakable_damage;
- self.state = 0;
- self.nextthink = 0; // cancel auto respawn
- func_breakable_colormod();
-}
-
-void func_breakable_destroyed()
-{
- func_breakable_look_destroyed();
- func_breakable_behave_destroyed();
-
- CSQCMODEL_AUTOUPDATE();
-}
-
-void func_breakable_restore()
-{
- func_breakable_look_restore();
- func_breakable_behave_restore();
-
- CSQCMODEL_AUTOUPDATE();
-}
-
-vector debrisforce; // global, set before calling this
-void func_breakable_destroy() {
- float n, i;
- string oldmsg;
-
- activator = self.owner;
- self.owner = world; // set by W_PrepareExplosionByDamage
-
- // now throw around the debris
- n = tokenize_console(self.debris);
- for(i = 0; i < n; ++i)
- LaunchDebris(argv(i), debrisforce);
-
- func_breakable_destroyed();
-
- if(self.noise)
- sound (self, CH_TRIGGER, self.noise, VOL_BASE, ATTEN_NORM);
-
- if(self.dmg)
- RadiusDamage(self, activator, self.dmg, self.dmg_edge, self.dmg_radius, self, world, self.dmg_force, DEATH_HURTTRIGGER, world);
-
- if(self.cnt)
- pointparticles(self.cnt, self.absmin * 0.5 + self.absmax * 0.5, '0 0 0', self.count);
-
- if(self.respawntime)
- {
- self.think = func_breakable_restore;
- self.nextthink = time + self.respawntime + crandom() * self.respawntimejitter;
- }
-
- oldmsg = self.message;
- self.message = "";
- SUB_UseTargets();
- self.message = oldmsg;
-}
-
-void func_breakable_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
-{
- if(self.state == 1)
- return;
- if(self.spawnflags & DOOR_NOSPLASH)
- if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
- return;
- if(self.team)
- if(attacker.team == self.team)
- return;
- self.health = self.health - damage;
- if(self.sprite)
- {
- WaypointSprite_Ping(self.sprite);
- WaypointSprite_UpdateHealth(self.sprite, self.health);
- }
- func_breakable_colormod();
-
- if(self.health <= 0)
- {
- debrisforce = force;
- W_PrepareExplosionByDamage(attacker, func_breakable_destroy);
- }
-}
-
-void func_breakable_reset()
-{
- self.team = self.team_saved;
- func_breakable_look_restore();
- if(self.spawnflags & 1)
- func_breakable_behave_destroyed();
- else
- func_breakable_behave_restore();
-
- CSQCMODEL_AUTOUPDATE();
-}
-
-// destructible walls that can be used to trigger target_objective_decrease
-void spawnfunc_func_breakable() {
- float n, i;
- if(!self.health)
- self.health = 100;
- self.max_health = self.health;
-
- // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway
- if(!self.debrismovetype) self.debrismovetype = MOVETYPE_BOUNCE;
- if(!self.debrissolid) self.debrissolid = SOLID_NOT;
- if(self.debrisvelocity == '0 0 0') self.debrisvelocity = '0 0 140';
- if(self.debrisvelocityjitter == '0 0 0') self.debrisvelocityjitter = '70 70 70';
- if(self.debrisavelocityjitter == '0 0 0') self.debrisavelocityjitter = '600 600 600';
- if(!self.debristime) self.debristime = 3.5;
- if(!self.debristimejitter) self.debristime = 2.5;
-
- if(self.mdl != "")
- self.cnt = particleeffectnum(self.mdl);
- if(self.count == 0)
- self.count = 1;
-
- if(self.message == "")
- self.message = "got too close to an explosion";
- if(self.message2 == "")
- self.message2 = "was pushed into an explosion by";
- if(!self.dmg_radius)
- self.dmg_radius = 150;
- if(!self.dmg_force)
- self.dmg_force = 200;
-
- self.mdl = self.model;
- SetBrushEntityModel();
-
- self.use = func_breakable_restore;
-
- // precache all the models
- if (self.mdl_dead)
- precache_model(self.mdl_dead);
- n = tokenize_console(self.debris);
- for(i = 0; i < n; ++i)
- precache_model(argv(i));
- if(self.noise)
- precache_sound(self.noise);
-
- self.team_saved = self.team;
- self.dropped_origin = self.origin;
-
- self.reset = func_breakable_reset;
- func_breakable_reset();
-
- CSQCMODEL_AUTOINIT();
-}
-
-// for use in maps with a "model" key set
-void spawnfunc_misc_breakablemodel() {
- spawnfunc_func_breakable();
-}
// singleplayer stuff
item_key.qh
-secret.qh
scores_rules.qc
// singleplayer stuff
item_key.qc
-secret.qc
weapons/accuracy.qc
weapons/common.qc
portals.qc
-target_spawn.qc
-func_breakable.qc
-target_music.qc
-
../common/nades.qc
../common/buffs.qc
+++ /dev/null
-
-
-void secrets_setstatus() {
- self.stat_secrets_total = secrets_total;
- self.stat_secrets_found = secrets_found;
-}
-
-/**
- * A secret has been found (maybe :P)
- */
-void trigger_secret_touch() {
- // only a player can trigger this
- if (!IS_PLAYER(other))
- return;
-
- // update secrets found counter
- secrets_found += 1;
- //print("Secret found: ", ftos(secret_counter.cnt), "/");
- //print(ftos(secret_counter.count), "\n");
-
- // centerprint message (multi_touch() doesn't always call centerprint())
- centerprint(other, self.message);
- self.message = "";
-
- // handle normal trigger features
- multi_touch();
- remove(self);
-}
-
-/*QUAKED trigger_secret (.5 .5 .5) ?
-Variable sized secret trigger. Can be targeted at one or more entities.
-Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found.
--------- KEYS --------
-sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1)
-noise: path to sound file, if you want to play something else
-target: trigger all entities with this targetname when triggered
-message: print this message to the player who activated the trigger instead of the standard 'You found a secret!'
-killtarget: remove all entities with this targetname when triggered
--------- NOTES --------
-You should create a common/trigger textured brush covering the entrance to a secret room/area.
-Trigger secret can only be trigger by a player's touch and can not be a target itself.
-*/
-void spawnfunc_trigger_secret() {
- // FIXME: should it be disabled in most modes?
-
- // update secrets count
- secrets_total += 1;
-
- // add default message
- if (self.message == "")
- self.message = "You found a secret!";
-
- // set default sound
- if (self.noise == "")
- if (!self.sounds)
- self.sounds = 1; // misc/secret.wav
-
- // this entity can't be a target itself!!!!
- self.targetname = "";
-
- // you can't just shoot a room to find it, can you?
- self.health = 0;
-
- // a secret can not be delayed
- self.delay = 0;
-
- // convert this trigger to trigger_once
- self.classname = "trigger_once";
- spawnfunc_trigger_once();
-
- // take over the touch() function, so we can mark secret as found
- self.touch = trigger_secret_touch;
- // ignore triggering;
- self.use = func_null;
-}
-
+++ /dev/null
-/**
- * Total number of secrets on the map.
- */
-float secrets_total;
-
-/**
- * Total numbe of secrets found on the map.
- */
-float secrets_found;
-
-
-.float stat_secrets_total;
-.float stat_secrets_found;
-
-/**
- * update secrets status.
- */
-void secrets_setstatus();
-
+++ /dev/null
-.float lifetime;
-// values:
-// volume
-// noise
-// targetname
-// lifetime
-// fade_time
-// fade_rate
-// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0)
-// when targetname is not set, THIS ONE is default
-void target_music_sendto(float to, float is)
-{
- WriteByte(to, SVC_TEMPENTITY);
- WriteByte(to, TE_CSQC_TARGET_MUSIC);
- WriteShort(to, num_for_edict(self));
- WriteByte(to, self.volume * 255.0 * is);
- WriteByte(to, self.fade_time * 16.0);
- WriteByte(to, self.fade_rate * 16.0);
- WriteByte(to, self.lifetime);
- WriteString(to, self.noise);
-}
-void target_music_reset()
-{
- if(self.targetname == "")
- target_music_sendto(MSG_ALL, 1);
-}
-void target_music_use()
-{
- if(!activator)
- return;
- if(IS_REAL_CLIENT(activator))
- {
- msg_entity = activator;
- target_music_sendto(MSG_ONE, 1);
- }
- entity head;
- FOR_EACH_SPEC(head) if(head.enemy == activator) { msg_entity = head; target_music_sendto(MSG_ONE, 1); }
-}
-void spawnfunc_target_music()
-{
- self.use = target_music_use;
- self.reset = target_music_reset;
- if(!self.volume)
- self.volume = 1;
- if(self.targetname == "")
- target_music_sendto(MSG_INIT, 1);
- else
- target_music_sendto(MSG_INIT, 0);
-}
-void TargetMusic_RestoreGame()
-{
- for(self = world; (self = find(self, classname, "target_music")); )
- {
- if(self.targetname == "")
- target_music_sendto(MSG_INIT, 1);
- else
- target_music_sendto(MSG_INIT, 0);
- }
-}
-// values:
-// volume
-// noise
-// targetname
-// fade_time
-// spawnflags:
-// 1 = START_OFF
-// when triggered, it is disabled/enabled for everyone
-float trigger_music_SendEntity(entity to, float sf)
-{
- WriteByte(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC);
- sf &= ~0x80;
- if(self.cnt)
- sf |= 0x80;
- WriteByte(MSG_ENTITY, sf);
- if(sf & 4)
- {
- WriteCoord(MSG_ENTITY, self.origin_x);
- WriteCoord(MSG_ENTITY, self.origin_y);
- WriteCoord(MSG_ENTITY, self.origin_z);
- }
- if(sf & 1)
- {
- if(self.model != "null")
- {
- WriteShort(MSG_ENTITY, self.modelindex);
- WriteCoord(MSG_ENTITY, self.mins_x);
- WriteCoord(MSG_ENTITY, self.mins_y);
- WriteCoord(MSG_ENTITY, self.mins_z);
- WriteCoord(MSG_ENTITY, self.maxs_x);
- WriteCoord(MSG_ENTITY, self.maxs_y);
- WriteCoord(MSG_ENTITY, self.maxs_z);
- }
- else
- {
- WriteShort(MSG_ENTITY, 0);
- WriteCoord(MSG_ENTITY, self.maxs_x);
- WriteCoord(MSG_ENTITY, self.maxs_y);
- WriteCoord(MSG_ENTITY, self.maxs_z);
- }
- WriteByte(MSG_ENTITY, self.volume * 255.0);
- WriteByte(MSG_ENTITY, self.fade_time * 16.0);
- WriteByte(MSG_ENTITY, self.fade_rate * 16.0);
- WriteString(MSG_ENTITY, self.noise);
- }
- return 1;
-}
-void trigger_music_reset()
-{
- self.cnt = !(self.spawnflags & 1);
- self.SendFlags |= 0x80;
-}
-void trigger_music_use()
-{
- self.cnt = !self.cnt;
- self.SendFlags |= 0x80;
-}
-void spawnfunc_trigger_music()
-{
- if(self.model != "")
- setmodel(self, self.model);
- if(!self.volume)
- self.volume = 1;
- if(!self.modelindex)
- {
- setorigin(self, self.origin + self.mins);
- setsize(self, '0 0 0', self.maxs - self.mins);
- }
- trigger_music_reset();
-
- self.use = trigger_music_use;
- self.reset = trigger_music_reset;
-
- Net_LinkEntity(self, FALSE, 0, trigger_music_SendEntity);
-}
+++ /dev/null
-// spawner entity
-// "classname" "target_spawn"
-// "message" "fieldname value fieldname value ..."
-// "spawnflags"
-// 1 = call the spawn function
-// 2 = trigger on map load
-
-float target_spawn_initialized;
-.void() target_spawn_spawnfunc;
-float target_spawn_spawnfunc_field;
-.entity target_spawn_activator;
-.float target_spawn_id;
-float target_spawn_count;
-
-void target_spawn_helper_setmodel()
-{
- setmodel(self, self.model);
-}
-
-void target_spawn_helper_setsize()
-{
- setsize(self, self.mins, self.maxs);
-}
-
-void target_spawn_edit_entity(entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act)
-{
- float i, n, valuefieldpos;
- string key, value, valuefield, valueoffset, valueoffsetrandom;
- entity valueent;
- vector data, data2;
- entity oldself;
- entity oldactivator;
-
- n = tokenize_console(msg);
-
- for(i = 0; i < n-1; i += 2)
- {
- key = argv(i);
- value = argv(i+1);
- if(key == "$")
- {
- data_x = -1;
- data_y = FIELD_STRING;
- }
- else
- {
- data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key)));
- if(data_y == 0) // undefined field, i.e., invalid type
- {
- print("target_spawn: invalid/unknown entity key ", key, " specified, ignored!\n");
- continue;
- }
- }
- if(substring(value, 0, 1) == "$")
- {
- value = substring(value, 1, strlen(value) - 1);
- if(substring(value, 0, 1) == "$")
- {
- // deferred replacement
- // do nothing
- // useful for creating target_spawns with this!
- }
- else
- {
- // replace me!
- valuefieldpos = strstrofs(value, "+", 0);
- valueoffset = "";
- if(valuefieldpos != -1)
- {
- valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
- value = substring(value, 0, valuefieldpos);
- }
-
- valuefieldpos = strstrofs(valueoffset, "+", 0);
- valueoffsetrandom = "";
- if(valuefieldpos != -1)
- {
- valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1);
- valueoffset = substring(valueoffset, 0, valuefieldpos);
- }
-
- valuefieldpos = strstrofs(value, ".", 0);
- valuefield = "";
- if(valuefieldpos != -1)
- {
- valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1);
- value = substring(value, 0, valuefieldpos);
- }
-
- if(value == "self")
- {
- valueent = self;
- value = "";
- }
- else if(value == "activator")
- {
- valueent = act;
- value = "";
- }
- else if(value == "other")
- {
- valueent = other;
- value = "";
- }
- else if(value == "pusher")
- {
- if(time < act.pushltime)
- valueent = act.pusher;
- else
- valueent = world;
- value = "";
- }
- else if(value == "target")
- {
- valueent = e;
- value = "";
- }
- else if(value == "killtarget")
- {
- valueent = kt;
- value = "";
- }
- else if(value == "target2")
- {
- valueent = t2;
- value = "";
- }
- else if(value == "target3")
- {
- valueent = t3;
- value = "";
- }
- else if(value == "target4")
- {
- valueent = t4;
- value = "";
- }
- else if(value == "time")
- {
- valueent = world;
- value = ftos(time);
- }
- else
- {
- print("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!\n");
- continue;
- }
-
- if(valuefield == "")
- {
- if(value == "")
- value = ftos(num_for_edict(valueent));
- }
- else
- {
- if(value != "")
- {
- print("target_spawn: try to get a field of a non-entity, ignored!\n");
- continue;
- }
- data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield)));
- if(data2_y == 0) // undefined field, i.e., invalid type
- {
- print("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!\n");
- continue;
- }
- value = getentityfieldstring(data2_x, valueent);
- }
-
- if(valueoffset != "")
- {
- switch(data_y)
- {
- case FIELD_STRING:
- value = strcat(value, valueoffset);
- break;
- case FIELD_FLOAT:
- value = ftos(stof(value) + stof(valueoffset));
- break;
- case FIELD_VECTOR:
- value = vtos(stov(value) + stov(valueoffset));
- break;
- default:
- print("target_spawn: only string, float and vector fields can do calculations, calculation ignored!\n");
- break;
- }
- }
-
- if(valueoffsetrandom != "")
- {
- switch(data_y)
- {
- case FIELD_FLOAT:
- value = ftos(stof(value) + random() * stof(valueoffsetrandom));
- break;
- case FIELD_VECTOR:
- data2 = stov(valueoffsetrandom);
- value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1');
- break;
- default:
- print("target_spawn: only float and vector fields can do random calculations, calculation ignored!\n");
- break;
- }
- }
- }
- }
- if(key == "$")
- {
- if(substring(value, 0, 1) == "_")
- value = strcat("target_spawn_helper", value);
- putentityfieldstring(target_spawn_spawnfunc_field, e, value);
-
- oldself = self;
- oldactivator = activator;
-
- self = e;
- activator = act;
-
- self.target_spawn_spawnfunc();
-
- self = oldself;
- activator = oldactivator;
- }
- else
- {
- if(data_y == FIELD_VECTOR)
- value = strreplace("'", "", value); // why?!?
- putentityfieldstring(data_x, e, value);
- }
- }
-}
-
-void target_spawn_useon(entity e)
-{
- self.target_spawn_activator = activator;
- target_spawn_edit_entity(
- e,
- self.message,
- find(world, targetname, self.killtarget),
- find(world, targetname, self.target2),
- find(world, targetname, self.target3),
- find(world, targetname, self.target4),
- activator
- );
-}
-
-float target_spawn_cancreate()
-{
- float c;
- entity e;
-
- c = self.count;
- if(c == 0) // no limit?
- return 1;
-
- ++c; // increase count to not include MYSELF
- for(e = world; (e = findfloat(e, target_spawn_id, self.target_spawn_id)); --c)
- ;
-
- // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more
- if(c == 0)
- return 0;
- return 1;
-}
-
-void target_spawn_use()
-{
- entity e;
-
- if(self.target == "")
- {
- // spawn new entity
- if(!target_spawn_cancreate())
- return;
- e = spawn();
- target_spawn_useon(e);
- e.target_spawn_id = self.target_spawn_id;
- }
- else if(self.target == "*activator")
- {
- // edit entity
- if(activator)
- target_spawn_useon(activator);
- }
- else
- {
- // edit entity
- for(e = world; (e = find(e, targetname, self.target)); )
- target_spawn_useon(e);
- }
-}
-
-void target_spawn_spawnfirst()
-{
- activator = self.target_spawn_activator;
- if(self.spawnflags & 2)
- target_spawn_use();
-}
-
-void initialize_field_db()
-{
- if(!target_spawn_initialized)
- {
- float n, i;
- string fn;
- vector prev, new;
- float ft;
-
- n = numentityfields();
- for(i = 0; i < n; ++i)
- {
- fn = entityfieldname(i);
- ft = entityfieldtype(i);
- new = i * '1 0 0' + ft * '0 1 0' + '0 0 1';
- prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn)));
- if(prev_y == 0)
- {
- db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(new));
- if(fn == "target_spawn_spawnfunc")
- target_spawn_spawnfunc_field = i;
- }
- }
-
- target_spawn_initialized = 1;
- }
-}
-
-void spawnfunc_target_spawn()
-{
- initialize_field_db();
- self.use = target_spawn_use;
- self.message = strzone(strreplace("'", "\"", self.message));
- self.target_spawn_id = ++target_spawn_count;
- InitializeEntity(self, target_spawn_spawnfirst, INITPRIO_LAST);
-}
-
-
-void trigger_relay_if_use()
-{
- float n;
- n = self.count;
-
- // TODO make this generic AND faster than nextent()ing through all, if somehow possible
- n = (cvar_string(self.netname) == cvar_string(self.message));
- if(self.spawnflags & 1)
- n = !n;
-
- if(n)
- SUB_UseTargets();
-}
-
-void spawnfunc_trigger_relay_if()
-{
- self.use = trigger_relay_if_use;
-}
return nearest;
}
+float WarpZoneLib_BadClassname(string myclassname)
+{
+ switch(myclassname)
+ {
+ case "weapon_info":
+ case "monster_info":
+ case "deathtype":
+ case "callback":
+ case "callbackchain":
+ case "weaponentity":
+ case "exteriorweaponentity":
+ case "csqc_score_team":
+ case "pingplreport":
+ case "ent_client_scoreinfo":
+ case "saved_cvar_value":
+ case "accuracy":
+ case "entcs_sender_v2":
+ case "entcs_receiver_v2":
+ case "clientinit":
+ case "sprite_waypoint":
+ case "waypoint":
+ case "gibsplash":
+ //case "net_linked": // actually some real entities are linked without classname, fail
+ case "":
+ return TRUE;
+ }
+
+ if(startsWith(myclassname, "msg_"))
+ return TRUE;
+
+ if(startsWith(myclassname, "target_"))
+ return TRUE;
+
+ if(startsWith(myclassname, "info_"))
+ return TRUE;
+
+ return FALSE;
+}
+
.float WarpZone_findradius_hit;
.entity WarpZone_findradius_next;
void WarpZone_FindRadius_Recurse(vector org, float rad, vector org0, vector transform, vector shift, float needlineofsight)
for(e = e0; e; e = e.chain)
{
+ if(WarpZoneLib_BadClassname(e.classname))
+ continue;
+ print(e.classname, ", first check\n");
p = WarpZoneLib_NearestPointOnBox(e.origin + e.mins, e.origin + e.maxs, org0);
if(needlineofsight)
{
}
for(e = wz; e; e = e.WarpZone_findradius_next)
{
+ if(WarpZoneLib_BadClassname(e.classname))
+ continue;
+
+ print(e.classname, ", second check\n");
+
org0_new = WarpZone_TransformOrigin(e, org);
traceline(e.warpzone_targetorigin, org0_new, MOVE_NOMONSTERS, e);
org_new = trace_endpos;