From: Mario Date: Fri, 30 Jan 2015 10:08:20 +0000 (+1100) Subject: More fun stuff X-Git-Tag: xonotic-v0.8.1~38^2~38 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=28368f51696277dc00533de59defa97c790b77d5;p=xonotic%2Fxonotic-data.pk3dir.git More fun stuff --- diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc new file mode 100644 index 000000000..c3890d142 --- /dev/null +++ b/qcsrc/common/triggers/func/breakable.qc @@ -0,0 +1,288 @@ +#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 diff --git a/qcsrc/common/triggers/func/include.qc b/qcsrc/common/triggers/func/include.qc index 4fad7b412..dc0369353 100644 --- a/qcsrc/common/triggers/func/include.qc +++ b/qcsrc/common/triggers/func/include.qc @@ -1,4 +1,5 @@ #include "bobbing.qc" +#include "breakable.qc" #include "button.qc" #include "conveyor.qc" #include "door.qc" diff --git a/qcsrc/common/triggers/target/include.qc b/qcsrc/common/triggers/target/include.qc index 43e4741cb..a8c876b9c 100644 --- a/qcsrc/common/triggers/target/include.qc +++ b/qcsrc/common/triggers/target/include.qc @@ -1,3 +1,5 @@ #include "changelevel.qc" +#include "music.qc" +#include "spawn.qc" #include "speaker.qc" #include "voicescript.qc" diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc new file mode 100644 index 000000000..5aa095d12 --- /dev/null +++ b/qcsrc/common/triggers/target/music.qc @@ -0,0 +1,136 @@ +#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 diff --git a/qcsrc/common/triggers/target/spawn.qc b/qcsrc/common/triggers/target/spawn.qc new file mode 100644 index 000000000..d661b98eb --- /dev/null +++ b/qcsrc/common/triggers/target/spawn.qc @@ -0,0 +1,337 @@ +#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 diff --git a/qcsrc/common/triggers/trigger/include.qc b/qcsrc/common/triggers/trigger/include.qc index 0de1d26d6..5a0e83f5a 100644 --- a/qcsrc/common/triggers/trigger/include.qc +++ b/qcsrc/common/triggers/trigger/include.qc @@ -14,5 +14,7 @@ #include "multivibrator.qc" #include "relay.qc" #include "relay_activators.qc" +#include "relay_if.qc" #include "relay_teamcheck.qc" +#include "secret.qc" #include "swamp.qc" diff --git a/qcsrc/common/triggers/trigger/include.qh b/qcsrc/common/triggers/trigger/include.qh index dd841de14..5ec31751c 100644 --- a/qcsrc/common/triggers/trigger/include.qh +++ b/qcsrc/common/triggers/trigger/include.qh @@ -1,3 +1,4 @@ #include "multi.qh" #include "jumppads.qh" +#include "secret.qh" #include "swamp.qh" diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc index f2074f031..67fd6a91d 100644 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ b/qcsrc/common/triggers/trigger/jumppads.qc @@ -2,14 +2,6 @@ #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) diff --git a/qcsrc/common/triggers/trigger/jumppads.qh b/qcsrc/common/triggers/trigger/jumppads.qh index 2c5363b59..6330dc3c4 100644 --- a/qcsrc/common/triggers/trigger/jumppads.qh +++ b/qcsrc/common/triggers/trigger/jumppads.qh @@ -2,3 +2,11 @@ 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; diff --git a/qcsrc/common/triggers/trigger/relay_if.qc b/qcsrc/common/triggers/trigger/relay_if.qc new file mode 100644 index 000000000..ade56c1e8 --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay_if.qc @@ -0,0 +1,20 @@ +#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 diff --git a/qcsrc/common/triggers/trigger/secret.qc b/qcsrc/common/triggers/trigger/secret.qc new file mode 100644 index 000000000..48dc360af --- /dev/null +++ b/qcsrc/common/triggers/trigger/secret.qc @@ -0,0 +1,75 @@ +#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 diff --git a/qcsrc/common/triggers/trigger/secret.qh b/qcsrc/common/triggers/trigger/secret.qh new file mode 100644 index 000000000..e289f57ec --- /dev/null +++ b/qcsrc/common/triggers/trigger/secret.qh @@ -0,0 +1,20 @@ +#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 diff --git a/qcsrc/server/func_breakable.qc b/qcsrc/server/func_breakable.qc deleted file mode 100644 index 0bf71059e..000000000 --- a/qcsrc/server/func_breakable.qc +++ /dev/null @@ -1,286 +0,0 @@ -.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(); -} diff --git a/qcsrc/server/progs.src b/qcsrc/server/progs.src index 5aa07a33d..8da8a6fc5 100644 --- a/qcsrc/server/progs.src +++ b/qcsrc/server/progs.src @@ -104,7 +104,6 @@ round_handler.qh // singleplayer stuff item_key.qh -secret.qh scores_rules.qc @@ -150,7 +149,6 @@ g_models.qc // singleplayer stuff item_key.qc -secret.qc weapons/accuracy.qc weapons/common.qc @@ -221,10 +219,6 @@ spawnpoints.qc portals.qc -target_spawn.qc -func_breakable.qc -target_music.qc - ../common/nades.qc ../common/buffs.qc diff --git a/qcsrc/server/secret.qc b/qcsrc/server/secret.qc deleted file mode 100644 index 06f7e075d..000000000 --- a/qcsrc/server/secret.qc +++ /dev/null @@ -1,76 +0,0 @@ - - -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; -} - diff --git a/qcsrc/server/secret.qh b/qcsrc/server/secret.qh deleted file mode 100644 index dd94b1921..000000000 --- a/qcsrc/server/secret.qh +++ /dev/null @@ -1,19 +0,0 @@ -/** - * 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(); - diff --git a/qcsrc/server/target_music.qc b/qcsrc/server/target_music.qc deleted file mode 100644 index cd1b374e6..000000000 --- a/qcsrc/server/target_music.qc +++ /dev/null @@ -1,134 +0,0 @@ -.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); -} diff --git a/qcsrc/server/target_spawn.qc b/qcsrc/server/target_spawn.qc deleted file mode 100644 index 4da6b13bb..000000000 --- a/qcsrc/server/target_spawn.qc +++ /dev/null @@ -1,355 +0,0 @@ -// 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; -} diff --git a/qcsrc/warpzonelib/common.qc b/qcsrc/warpzonelib/common.qc index 521cf343b..16e026d48 100644 --- a/qcsrc/warpzonelib/common.qc +++ b/qcsrc/warpzonelib/common.qc @@ -564,6 +564,45 @@ vector WarpZoneLib_NearestPointOnBox(vector mi, vector ma, vector org) 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) @@ -582,6 +621,9 @@ void WarpZone_FindRadius_Recurse(vector org, float rad, vector org0, 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) { @@ -617,6 +659,11 @@ void WarpZone_FindRadius_Recurse(vector org, float rad, vector org0, } 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;