From: TimePath Date: Thu, 5 Nov 2015 00:20:09 +0000 (+1100) Subject: Mutators: self containment X-Git-Tag: xonotic-v0.8.2~1718 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=65cb7f7edc8fd2de641b1221156efb4cd132ba34;p=xonotic%2Fxonotic-data.pk3dir.git Mutators: self containment --- diff --git a/qcsrc/client/progs.inc b/qcsrc/client/progs.inc index e3e8c00c0..7f473f481 100644 --- a/qcsrc/client/progs.inc +++ b/qcsrc/client/progs.inc @@ -68,12 +68,6 @@ #include "../lib/csqcmodel/cl_player.qc" #include "../lib/csqcmodel/interpolate.qc" -// TODO: move to common -#include "../server/mutators/mutator/mutator_multijump.qc" -#define IMPLEMENTATION -#include "../server/mutators/mutator/mutator_multijump.qc" -#undef IMPLEMENTATION - #include "../lib/warpzone/anglestransform.qc" #include "../lib/warpzone/client.qc" #include "../lib/warpzone/common.qc" diff --git a/qcsrc/common/mutators/all.inc b/qcsrc/common/mutators/all.inc index d3e63de45..21a23b069 100644 --- a/qcsrc/common/mutators/all.inc +++ b/qcsrc/common/mutators/all.inc @@ -1,8 +1,33 @@ -#include "mutator/instagib/module.inc" +#include "mutator/buffs/module.inc" #include "mutator/itemstime.qc" +#include "mutator/multijump/module.inc" +#include "mutator/nades/module.inc" +#include "mutator/superspec/module.inc" #include "mutator/waypoints/module.inc" // completely self contained +#include "mutator/bloodloss/module.inc" +#include "mutator/breakablehook/module.inc" +#include "mutator/campcheck/module.inc" #include "mutator/damagetext/module.inc" +#include "mutator/dodging/module.inc" +#include "mutator/hook/module.inc" +#include "mutator/instagib/module.inc" +#include "mutator/invincibleproj/module.inc" +#include "mutator/melee_only/module.inc" +#include "mutator/midair/module.inc" +#include "mutator/new_toys/module.inc" +#include "mutator/nix/module.inc" #include "mutator/overkill/module.inc" +#include "mutator/physical_items/module.inc" +#include "mutator/pinata/module.inc" +#include "mutator/random_gravity/module.inc" +#include "mutator/rocketflying/module.inc" +#include "mutator/rocketminsta/module.inc" +#include "mutator/sandbox/module.inc" +#include "mutator/spawn_near_teammate/module.inc" +#include "mutator/touchexplode/module.inc" +#include "mutator/vampirehook/module.inc" +#include "mutator/vampire/module.inc" +#include "mutator/weaponarena_random/module.inc" diff --git a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc new file mode 100644 index 000000000..ca3716669 --- /dev/null +++ b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc @@ -0,0 +1,45 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss")); + +.float bloodloss_timer; + +MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink) +{SELFPARAM(); + if(IS_PLAYER(self)) + if(self.health <= autocvar_g_bloodloss && self.deadflag == DEAD_NO) + { + self.BUTTON_CROUCH = true; + + if(time >= self.bloodloss_timer) + { + if(self.vehicle) + vehicles_exit(VHEF_RELEASE); + if(self.event_damage) + self.event_damage(self, self, 1, DEATH_ROT.m_id, self.origin, '0 0 0'); + self.bloodloss_timer = time + 0.5 + random() * 0.5; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump) +{SELFPARAM(); + if(self.health <= autocvar_g_bloodloss) + return true; + + return false; +} + +MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":bloodloss"); + return false; +} + +MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Blood loss"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/module.inc b/qcsrc/common/mutators/mutator/bloodloss/module.inc new file mode 100644 index 000000000..d3f665a18 --- /dev/null +++ b/qcsrc/common/mutators/mutator/bloodloss/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "bloodloss.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc b/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc new file mode 100644 index 000000000..3ee077c72 --- /dev/null +++ b/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc @@ -0,0 +1,29 @@ +#ifdef IMPLEMENTATION +#include "../../../deathtypes/all.qh" +#include "../../../../server/g_hook.qh" + +REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook")); + +bool autocvar_g_breakablehook; // allow toggling mid match? +bool autocvar_g_breakablehook_owner; + +MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate) +{ + if(frag_target.classname == "grapplinghook") + { + if((!autocvar_g_breakablehook) + || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner) + ) { frag_damage = 0; } + + // hurt the owner of the hook + if(DIFF_TEAM(frag_attacker, frag_target.realowner)) + { + Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0'); + RemoveGrapplingHook(frag_target.realowner); + return false; // dead + } + } + + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/module.inc b/qcsrc/common/mutators/mutator/breakablehook/module.inc new file mode 100644 index 000000000..484eb4c56 --- /dev/null +++ b/qcsrc/common/mutators/mutator/breakablehook/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "breakablehook.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qc b/qcsrc/common/mutators/mutator/buffs/buffs.qc new file mode 100644 index 000000000..d6bfb702c --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qc @@ -0,0 +1,1046 @@ +#ifndef MUTATOR_BUFFS_H +#define MUTATOR_BUFFS_H + +#include "../instagib/module.inc" + +bool autocvar_g_buffs_effects; +float autocvar_g_buffs_waypoint_distance; +bool autocvar_g_buffs_randomize; +float autocvar_g_buffs_random_lifetime; +bool autocvar_g_buffs_random_location; +int autocvar_g_buffs_random_location_attempts; +int autocvar_g_buffs_spawn_count; +bool autocvar_g_buffs_replace_powerups; +float autocvar_g_buffs_cooldown_activate; +float autocvar_g_buffs_cooldown_respawn; +float autocvar_g_buffs_resistance_blockpercent; +float autocvar_g_buffs_medic_survive_chance; +float autocvar_g_buffs_medic_survive_health; +float autocvar_g_buffs_medic_rot; +float autocvar_g_buffs_medic_max; +float autocvar_g_buffs_medic_regen; +float autocvar_g_buffs_vengeance_damage_multiplier; +float autocvar_g_buffs_bash_force; +float autocvar_g_buffs_bash_force_self; +float autocvar_g_buffs_disability_slowtime; +float autocvar_g_buffs_disability_speed; +float autocvar_g_buffs_disability_rate; +float autocvar_g_buffs_disability_weaponspeed; +float autocvar_g_buffs_speed_speed; +float autocvar_g_buffs_speed_rate; +float autocvar_g_buffs_speed_weaponspeed; +float autocvar_g_buffs_speed_damage_take; +float autocvar_g_buffs_speed_regen; +float autocvar_g_buffs_vampire_damage_steal; +float autocvar_g_buffs_invisible_alpha; +float autocvar_g_buffs_flight_gravity; +float autocvar_g_buffs_jump_height; +float autocvar_g_buffs_inferno_burntime_factor; +float autocvar_g_buffs_inferno_burntime_min_time; +float autocvar_g_buffs_inferno_burntime_target_damage; +float autocvar_g_buffs_inferno_burntime_target_time; +float autocvar_g_buffs_inferno_damagemultiplier; +float autocvar_g_buffs_swapper_range; +float autocvar_g_buffs_magnet_range_item; + +// ammo +.float buff_ammo_prev_infitems; +.int buff_ammo_prev_clipload; +// invisible +.float buff_invisible_prev_alpha; +// flight +.float buff_flight_prev_gravity; +// disability +.float buff_disability_time; +.float buff_disability_effect_time; +// common buff variables +.float buff_effect_delay; + +// buff definitions +.float buff_active; +.float buff_activetime; +.float buff_activetime_updated; +.entity buff_waypoint; +.int oldbuffs; // for updating effects +.entity buff_model; // controls effects (TODO: make csqc) + +const vector BUFF_MIN = ('-16 -16 -20'); +const vector BUFF_MAX = ('16 16 20'); + +// client side options +.float cvar_cl_buffs_autoreplace; +#endif + +#ifdef IMPLEMENTATION + +#include "../../../triggers/target/music.qh" +#include "../../../gamemodes/all.qh" +#include "../../../buffs/all.qh" + +.float buff_time; +void buffs_DelayedInit(); + +REGISTER_MUTATOR(buffs, cvar("g_buffs")) +{ + MUTATOR_ONADD + { + addstat(STAT_BUFFS, AS_INT, buffs); + addstat(STAT_BUFF_TIME, AS_FLOAT, buff_time); + + InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET); + } +} + +entity buff_FirstFromFlags(int _buffs) +{ + if (flags) + { + FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it)); + } + return BUFF_Null; +} + +bool buffs_BuffModel_Customize() +{SELFPARAM(); + entity player, myowner; + bool same_team; + + player = WaypointSprite_getviewentity(other); + myowner = self.owner; + same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); + + if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) + return false; + + if(MUTATOR_CALLHOOK(BuffModel_Customize, self, player)) + return false; + + if(player == myowner || (IS_SPEC(other) && other.enemy == myowner)) + { + // somewhat hide the model, but keep the glow + self.effects = 0; + self.alpha = -1; + } + else + { + self.effects = EF_FULLBRIGHT | EF_LOWPRECISION; + self.alpha = 1; + } + return true; +} + +void buffs_BuffModel_Spawn(entity player) +{ + player.buff_model = spawn(); + setmodel(player.buff_model, MDL_BUFF); + setsize(player.buff_model, '0 0 -40', '0 0 40'); + setattachment(player.buff_model, player, ""); + setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); + player.buff_model.owner = player; + player.buff_model.scale = 0.7; + player.buff_model.pflags = PFLAGS_FULLDYNAMIC; + player.buff_model.light_lev = 200; + player.buff_model.customizeentityforclient = buffs_BuffModel_Customize; +} + +vector buff_GlowColor(entity buff) +{ + //if(buff.team) { return Team_ColorRGB(buff.team); } + return buff.m_color; +} + +void buff_Effect(entity player, string eff) +{SELFPARAM(); + if(!autocvar_g_buffs_effects) { return; } + + if(time >= self.buff_effect_delay) + { + Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); + self.buff_effect_delay = time + 0.05; // prevent spam + } +} + +// buff item +float buff_Waypoint_visible_for_player(entity plr) +{SELFPARAM(); + if(!self.owner.buff_active && !self.owner.buff_activetime) + return false; + + if (plr.buffs) + { + return plr.cvar_cl_buffs_autoreplace == false || plr.buffs != self.owner.buffs; + } + + return WaypointSprite_visible_for_player(plr); +} + +void buff_Waypoint_Spawn(entity e) +{ + entity buff = buff_FirstFromFlags(e.buffs); + entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, world, e.team, e, buff_waypoint, true, RADARICON_Buff); + wp.wp_extra = buff.m_id; + WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod); + e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; +} + +void buff_SetCooldown(float cd) +{SELFPARAM(); + cd = max(0, cd); + + if(!self.buff_waypoint) + buff_Waypoint_Spawn(self); + + WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd); + self.buff_activetime = cd; + self.buff_active = !cd; +} + +void buff_Respawn(entity ent) +{SELFPARAM(); + if(gameover) { return; } + + vector oldbufforigin = ent.origin; + + if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256)) + { + entity spot = SelectSpawnPoint(true); + setorigin(ent, ((spot.origin + '0 0 200') + (randomvec() * 300))); + ent.angles = spot.angles; + } + + tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent); + + setorigin(ent, trace_endpos); // attempt to unstick + + ent.movetype = MOVETYPE_TOSS; + + makevectors(ent.angles); + ent.velocity = '0 0 200'; + ent.angles = '0 0 0'; + if(autocvar_g_buffs_random_lifetime > 0) + ent.lifetime = time + autocvar_g_buffs_random_lifetime; + + Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(ent), '0 0 0', 1); + + WaypointSprite_Ping(ent.buff_waypoint); + + sound(ent, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) +} + +void buff_Touch() +{SELFPARAM(); + if(gameover) { return; } + + if(ITEM_TOUCH_NEEDKILL()) + { + buff_Respawn(self); + return; + } + + if((self.team && DIFF_TEAM(other, self)) + || (other.frozen) + || (other.vehicle) + || (!self.buff_active) + ) + { + // can't touch this + return; + } + + if(MUTATOR_CALLHOOK(BuffTouch, self, other)) + return; + + if(!IS_PLAYER(other)) + return; // incase mutator changed other + + if (other.buffs) + { + if (other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs) + { + int buffid = buff_FirstFromFlags(other.buffs).m_id; + //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs); + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid); + + other.buffs = 0; + //sound(other, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + } + else { return; } // do nothing + } + + self.owner = other; + self.buff_active = false; + self.lifetime = 0; + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid); + + Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1); + sound(other, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); + other.buffs |= (self.buffs); +} + +float buff_Available(entity buff) +{ + if (buff == BUFF_Null) + return false; + if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) + return false; + if (buff == BUFF_VAMPIRE && cvar("g_vampire")) + return false; + return cvar(strcat("g_buffs_", buff.m_name)); +} + +.int buff_seencount; + +void buff_NewType(entity ent, float cb) +{ + RandomSelection_Init(); + FOREACH(Buffs, buff_Available(it), LAMBDA( + it.buff_seencount += 1; + // if it's already been chosen, give it a lower priority + RandomSelection_Add(world, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount)); + )); + ent.buffs = RandomSelection_chosen_float; +} + +void buff_Think() +{SELFPARAM(); + if(self.buffs != self.oldbuffs) + { + entity buff = buff_FirstFromFlags(self.buffs); + self.color = buff.m_color; + self.glowmod = buff_GlowColor(buff); + self.skin = buff.m_skin; + + setmodel(self, MDL_BUFF); + + if(self.buff_waypoint) + { + //WaypointSprite_Disown(self.buff_waypoint, 1); + WaypointSprite_Kill(self.buff_waypoint); + buff_Waypoint_Spawn(self); + if(self.buff_activetime) + WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime); + } + + self.oldbuffs = self.buffs; + } + + if(!gameover) + if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) + if(!self.buff_activetime_updated) + { + buff_SetCooldown(self.buff_activetime); + self.buff_activetime_updated = true; + } + + if(!self.buff_active && !self.buff_activetime) + if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs)) + { + buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime); + self.owner = world; + if(autocvar_g_buffs_randomize) + buff_NewType(self, self.buffs); + + if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) + buff_Respawn(self); + } + + if(self.buff_activetime) + if(!gameover) + if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) + { + self.buff_activetime = max(0, self.buff_activetime - frametime); + + if(!self.buff_activetime) + { + self.buff_active = true; + sound(self, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM); + Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1); + } + } + + if(self.buff_active) + { + if(self.team && !self.buff_waypoint) + buff_Waypoint_Spawn(self); + + if(self.lifetime) + if(time >= self.lifetime) + buff_Respawn(self); + } + + self.nextthink = time; + //self.angles_y = time * 110.1; +} + +void buff_Waypoint_Reset() +{SELFPARAM(); + WaypointSprite_Kill(self.buff_waypoint); + + if(self.buff_activetime) { buff_Waypoint_Spawn(self); } +} + +void buff_Reset() +{SELFPARAM(); + if(autocvar_g_buffs_randomize) + buff_NewType(self, self.buffs); + self.owner = world; + buff_SetCooldown(autocvar_g_buffs_cooldown_activate); + buff_Waypoint_Reset(); + self.buff_activetime_updated = false; + + if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) + buff_Respawn(self); +} + +float buff_Customize() +{SELFPARAM(); + entity player = WaypointSprite_getviewentity(other); + if(!self.buff_active || (self.team && DIFF_TEAM(player, self))) + { + self.alpha = 0.3; + if(self.effects & EF_FULLBRIGHT) { self.effects &= ~(EF_FULLBRIGHT); } + self.pflags = 0; + } + else + { + self.alpha = 1; + if(!(self.effects & EF_FULLBRIGHT)) { self.effects |= EF_FULLBRIGHT; } + self.light_lev = 220 + 36 * sin(time); + self.pflags = PFLAGS_FULLDYNAMIC; + } + return true; +} + +void buff_Init(entity ent) +{SELFPARAM(); + if(!cvar("g_buffs")) { remove(ent); return; } + + if(!teamplay && ent.team) { ent.team = 0; } + + entity buff = buff_FirstFromFlags(self.buffs); + + setself(ent); + if(!self.buffs || buff_Available(buff)) + buff_NewType(self, 0); + + self.classname = "item_buff"; + self.solid = SOLID_TRIGGER; + self.flags = FL_ITEM; + self.think = buff_Think; + self.touch = buff_Touch; + self.reset = buff_Reset; + self.nextthink = time + 0.1; + self.gravity = 1; + self.movetype = MOVETYPE_TOSS; + self.scale = 1; + self.skin = buff.m_skin; + self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; + self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; + self.customizeentityforclient = buff_Customize; + //self.gravity = 100; + self.color = buff.m_color; + self.glowmod = buff_GlowColor(self); + buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime); + self.buff_active = !self.buff_activetime; + self.pflags = PFLAGS_FULLDYNAMIC; + + if(self.spawnflags & 1) + self.noalign = true; + + if(self.noalign) + self.movetype = MOVETYPE_NONE; // reset by random location + + setmodel(self, MDL_BUFF); + setsize(self, BUFF_MIN, BUFF_MAX); + + if(cvar("g_buffs_random_location") || (self.spawnflags & 64)) + buff_Respawn(self); + + setself(this); +} + +void buff_Init_Compat(entity ent, entity replacement) +{ + if (ent.spawnflags & 2) + ent.team = NUM_TEAM_1; + else if (ent.spawnflags & 4) + ent.team = NUM_TEAM_2; + + ent.buffs = replacement.m_itemid; + + buff_Init(ent); +} + +void buff_SpawnReplacement(entity ent, entity old) +{ + setorigin(ent, old.origin); + ent.angles = old.angles; + ent.noalign = (old.noalign || (old.spawnflags & 1)); + + buff_Init(ent); +} + +void buff_Vengeance_DelayedDamage() +{SELFPARAM(); + if(self.enemy) + Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF.m_id, self.enemy.origin, '0 0 0'); + + remove(self); + return; +} + +float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) +{ + return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base); +} + +// mutator hooks +MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) +{ + if(frag_deathtype == DEATH_BUFF.m_id) { return false; } + + if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) + { + vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); + damage_take = v.x; + damage_save = v.y; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate) +{ + if(frag_deathtype == DEATH_BUFF.m_id) { return false; } + + if(frag_target.buffs & BUFF_SPEED.m_itemid) + if(frag_target != frag_attacker) + frag_damage *= autocvar_g_buffs_speed_damage_take; + + if(frag_target.buffs & BUFF_MEDIC.m_itemid) + if((frag_target.health - frag_damage) <= 0) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + if(frag_attacker) + if(random() <= autocvar_g_buffs_medic_survive_chance) + frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); + + if(frag_target.buffs & BUFF_JUMP.m_itemid) + if(frag_deathtype == DEATH_FALL.m_id) + frag_damage = 0; + + if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) + if(frag_attacker) + if(frag_attacker != frag_target) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + { + entity dmgent = spawn(); + + dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier; + dmgent.enemy = frag_attacker; + dmgent.owner = frag_target; + dmgent.think = buff_Vengeance_DelayedDamage; + dmgent.nextthink = time + 0.1; + } + + if(frag_target.buffs & BUFF_BASH.m_itemid) + if(frag_attacker != frag_target) + if(vlen(frag_force)) + frag_force = '0 0 0'; + + if(frag_attacker.buffs & BUFF_BASH.m_itemid) + if(vlen(frag_force)) + if(frag_attacker == frag_target) + frag_force *= autocvar_g_buffs_bash_force_self; + else + frag_force *= autocvar_g_buffs_bash_force; + + if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid) + if(frag_target != frag_attacker) + frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; + + if(frag_attacker.buffs & BUFF_MEDIC.m_itemid) + if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) + if(SAME_TEAM(frag_attacker, frag_target)) + if(frag_attacker != frag_target) + { + frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage); + frag_damage = 0; + } + + if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) + if(frag_target != frag_attacker) { + float time = buff_Inferno_CalculateTime( + frag_damage, + 0, + autocvar_g_buffs_inferno_burntime_min_time, + autocvar_g_buffs_inferno_burntime_target_damage, + autocvar_g_buffs_inferno_burntime_target_time, + autocvar_g_buffs_inferno_burntime_factor + ); + Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier) * time, time, DEATH_BUFF.m_id); + } + + // this... is ridiculous (TODO: fix!) + if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) + if(!frag_target.vehicle) + if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + if(frag_target.deadflag == DEAD_NO) + if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target)) + if(frag_attacker != frag_target) + if(!frag_target.frozen) + if(frag_target.takedamage) + if(DIFF_TEAM(frag_attacker, frag_target)) + { + frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max); + if(frag_target.armorvalue) + frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs,PlayerSpawn) +{SELFPARAM(); + self.buffs = 0; + // reset timers here to prevent them continuing after re-spawn + self.buff_disability_time = 0; + self.buff_disability_effect_time = 0; + return false; +} + +.float stat_sv_maxspeed; +.float stat_sv_airspeedlimit_nonqw; +.float stat_sv_jumpvelocity; + +MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics) +{SELFPARAM(); + if(self.buffs & BUFF_SPEED.m_itemid) + { + self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; + self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; + } + + if(time < self.buff_disability_time) + { + self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed; + self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; + } + + if(self.buffs & BUFF_JUMP.m_itemid) + { + // automatically reset, no need to worry + self.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerJump) +{SELFPARAM(); + if(self.buffs & BUFF_JUMP.m_itemid) + player_jumpheight = autocvar_g_buffs_jump_height; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, MonsterMove) +{SELFPARAM(); + if(time < self.buff_disability_time) + { + monster_speed_walk *= autocvar_g_buffs_disability_speed; + monster_speed_run *= autocvar_g_buffs_disability_speed; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerDies) +{SELFPARAM(); + if(self.buffs) + { + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); + self.buffs = 0; + + if(self.buff_model) + { + remove(self.buff_model); + self.buff_model = world; + } + } + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) +{SELFPARAM(); + if(MUTATOR_RETURNVALUE || gameover) { return false; } + if(self.buffs) + { + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); + + self.buffs = 0; + sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon) +{SELFPARAM(); + if(MUTATOR_RETURNVALUE || gameover) { return false; } + + if(self.buffs & BUFF_SWAPPER.m_itemid) + { + float best_distance = autocvar_g_buffs_swapper_range; + entity closest = world; + entity player; + FOR_EACH_PLAYER(player) + if(DIFF_TEAM(self, player)) + if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle) + if(vlen(self.origin - player.origin) <= best_distance) + { + best_distance = vlen(self.origin - player.origin); + closest = player; + } + + if(closest) + { + vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; + + my_org = self.origin; + my_vel = self.velocity; + my_ang = self.angles; + their_org = closest.origin; + their_vel = closest.velocity; + their_ang = closest.angles; + + Drop_Special_Items(closest); + + MUTATOR_CALLHOOK(PortalTeleport, self); // initiate flag dropper + + setorigin(self, their_org); + setorigin(closest, my_org); + + closest.velocity = my_vel; + closest.angles = my_ang; + closest.fixangle = true; + closest.oldorigin = my_org; + closest.oldvelocity = my_vel; + self.velocity = their_vel; + self.angles = their_ang; + self.fixangle = true; + self.oldorigin = their_org; + self.oldvelocity = their_vel; + + // set pusher so self gets the kill if they fall into void + closest.pusher = self; + closest.pushltime = time + autocvar_g_maxpushtime; + closest.istypefrag = closest.BUTTON_CHAT; + + Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1); + + sound(self, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); + sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); + + // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam + self.buffs = 0; + return true; + } + } + return false; +} + +bool buffs_RemovePlayer(entity player) +{ + if(player.buff_model) + { + remove(player.buff_model); + player.buff_model = world; + } + + // also reset timers here to prevent them continuing after spectating + player.buff_disability_time = 0; + player.buff_disability_effect_time = 0; + + return false; +} +MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { return buffs_RemovePlayer(self); } +MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { return buffs_RemovePlayer(self); } + +MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint) +{SELFPARAM(); + entity e = WaypointSprite_getviewentity(other); + + // if you have the invisibility powerup, sprites ALWAYS are restricted to your team + // but only apply this to real players, not to spectators + if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == other)) + if(DIFF_TEAM(self.owner, e)) + return true; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST) +{SELFPARAM(); + if(autocvar_g_buffs_replace_powerups) + switch(self.classname) + { + case "item_strength": + case "item_invincible": + { + entity e = spawn(); + buff_SpawnReplacement(e, self); + return true; + } + } + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor) +{SELFPARAM(); + if(self.buffs & BUFF_SPEED.m_itemid) + weapon_rate *= autocvar_g_buffs_speed_rate; + + if(time < self.buff_disability_time) + weapon_rate *= autocvar_g_buffs_disability_rate; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor) +{SELFPARAM(); + if(self.buffs & BUFF_SPEED.m_itemid) + ret_float *= autocvar_g_buffs_speed_weaponspeed; + + if(time < self.buff_disability_time) + ret_float *= autocvar_g_buffs_disability_weaponspeed; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) +{SELFPARAM(); + if(gameover || self.deadflag != DEAD_NO) { return false; } + + if(time < self.buff_disability_time) + if(time >= self.buff_disability_effect_time) + { + Send_Effect(EFFECT_SMOKING, self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1); + self.buff_disability_effect_time = time + 0.5; + } + + // handle buff lost status + // 1: notify everyone else + // 2: notify carrier as well + int buff_lost = 0; + + if(self.buff_time) + if(time >= self.buff_time) + buff_lost = 2; + + if(self.frozen) { buff_lost = 1; } + + if(buff_lost) + { + if(self.buffs) + { + int buffid = buff_FirstFromFlags(self.buffs).m_id; + Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); + if(buff_lost >= 2) + { + Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? + sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + } + self.buffs = 0; + } + } + + if(self.buffs & BUFF_MAGNET.m_itemid) + { + vector pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; + for(other = world; (other = findflags(other, flags, FL_ITEM)); ) + if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, other.absmin, other.absmax)) + { + setself(other); + other = this; + if(self.touch) + self.touch(); + other = self; + setself(this); + } + } + + if(self.buffs & BUFF_AMMO.m_itemid) + if(self.clip_size) + self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; + + if((self.buffs & BUFF_INVISIBLE.m_itemid) && (self.oldbuffs & BUFF_INVISIBLE.m_itemid)) + if(self.alpha != autocvar_g_buffs_invisible_alpha) + self.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) + +#define BUFF_ONADD(b) if ( (self.buffs & (b).m_itemid) && !(self.oldbuffs & (b).m_itemid)) +#define BUFF_ONREM(b) if (!(self.buffs & (b).m_itemid) && (self.oldbuffs & (b).m_itemid)) + + if(self.buffs != self.oldbuffs) + { + entity buff = buff_FirstFromFlags(self.buffs); + float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; + self.buff_time = (bufftime) ? time + bufftime : 0; + + BUFF_ONADD(BUFF_AMMO) + { + self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO); + self.items |= IT_UNLIMITED_WEAPON_AMMO; + + if(self.clip_load) + self.buff_ammo_prev_clipload = self.clip_load; + self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; + } + + BUFF_ONREM(BUFF_AMMO) + { + if(self.buff_ammo_prev_infitems) + self.items |= IT_UNLIMITED_WEAPON_AMMO; + else + self.items &= ~IT_UNLIMITED_WEAPON_AMMO; + + if(self.buff_ammo_prev_clipload) + self.clip_load = self.buff_ammo_prev_clipload; + } + + BUFF_ONADD(BUFF_INVISIBLE) + { + if(time < self.strength_finished && g_instagib) + self.alpha = autocvar_g_instagib_invis_alpha; + else + self.alpha = self.buff_invisible_prev_alpha; + self.alpha = autocvar_g_buffs_invisible_alpha; + } + + BUFF_ONREM(BUFF_INVISIBLE) + self.alpha = self.buff_invisible_prev_alpha; + + BUFF_ONADD(BUFF_FLIGHT) + { + self.buff_flight_prev_gravity = self.gravity; + self.gravity = autocvar_g_buffs_flight_gravity; + } + + BUFF_ONREM(BUFF_FLIGHT) + self.gravity = self.buff_flight_prev_gravity; + + self.oldbuffs = self.buffs; + if(self.buffs) + { + if(!self.buff_model) + buffs_BuffModel_Spawn(self); + + self.buff_model.color = buff.m_color; + self.buff_model.glowmod = buff_GlowColor(self.buff_model); + self.buff_model.skin = buff.m_skin; + + self.effects |= EF_NOSHADOW; + } + else + { + remove(self.buff_model); + self.buff_model = world; + + self.effects &= ~(EF_NOSHADOW); + } + } + + if(self.buff_model) + { + self.buff_model.effects = self.effects; + self.buff_model.effects |= EF_LOWPRECISION; + self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance + + self.buff_model.alpha = self.alpha; + } + +#undef BUFF_ONADD +#undef BUFF_ONREM + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, SpectateCopy) +{SELFPARAM(); + self.buffs = other.buffs; + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, VehicleEnter) +{ + vh_vehicle.buffs = vh_player.buffs; + vh_player.buffs = 0; + vh_vehicle.buff_time = vh_player.buff_time - time; + vh_player.buff_time = 0; + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, VehicleExit) +{ + vh_player.buffs = vh_player.oldbuffs = vh_vehicle.buffs; + vh_vehicle.buffs = 0; + vh_player.buff_time = time + vh_vehicle.buff_time; + vh_vehicle.buff_time = 0; + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerRegen) +{SELFPARAM(); + if(self.buffs & BUFF_MEDIC.m_itemid) + { + regen_mod_rot = autocvar_g_buffs_medic_rot; + regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max; + regen_mod_regen = autocvar_g_buffs_medic_regen; + } + + if(self.buffs & BUFF_SPEED.m_itemid) + regen_mod_regen = autocvar_g_buffs_speed_regen; + + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace"); + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Buffs"); + return false; +} + +MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Buffs"); + return false; +} + +void buffs_DelayedInit() +{ + if(autocvar_g_buffs_spawn_count > 0) + if(find(world, classname, "item_buff") == world) + { + float i; + for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) + { + entity e = spawn(); + e.spawnflags |= 64; // always randomize + e.velocity = randomvec() * 250; // this gets reset anyway if random location works + buff_Init(e); + } + } +} +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/module.inc b/qcsrc/common/mutators/mutator/buffs/module.inc new file mode 100644 index 000000000..89d31a1b3 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "buffs.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/campcheck.qc new file mode 100644 index 000000000..0ba0bb64e --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/campcheck.qc @@ -0,0 +1,90 @@ +#ifdef IMPLEMENTATION +float autocvar_g_campcheck_damage; +float autocvar_g_campcheck_distance; +float autocvar_g_campcheck_interval; + +REGISTER_MUTATOR(campcheck, cvar("g_campcheck")); + +.float campcheck_nextcheck; +.float campcheck_traveled_distance; + +MUTATOR_HOOKFUNCTION(campcheck, PlayerDies) +{SELFPARAM(); + Kill_Notification(NOTIF_ONE, self, MSG_CENTER_CPID, CPID_CAMPCHECK); + + return false; +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate) +{ + if(IS_PLAYER(frag_target)) + if(IS_PLAYER(frag_attacker)) + if(frag_attacker != frag_target) + { + frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance; + frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink) +{SELFPARAM(); + if(!gameover) + if(!warmup_stage) // don't consider it camping during warmup? + if(time >= game_starttime) + if(IS_PLAYER(self)) + if(IS_REAL_CLIENT(self)) // bots may camp, but that's no reason to constantly kill them + if(self.deadflag == DEAD_NO) + if(!self.frozen) + if(!self.BUTTON_CHAT) + if(autocvar_g_campcheck_interval) + { + vector dist; + + // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement) + dist = self.prevorigin - self.origin; + dist.z = 0; + self.campcheck_traveled_distance += fabs(vlen(dist)); + + if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted())) + { + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; + self.campcheck_traveled_distance = 0; + } + + if(time > self.campcheck_nextcheck) + { + if(self.campcheck_traveled_distance < autocvar_g_campcheck_distance) + { + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_CAMPCHECK); + if(self.vehicle) + Damage(self.vehicle, self, self, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, self.vehicle.origin, '0 0 0'); + else + Damage(self, self, self, bound(0, autocvar_g_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, self.origin, '0 0 0'); + } + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; + self.campcheck_traveled_distance = 0; + } + + return false; + } + + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date + return false; +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn) +{SELFPARAM(); + self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; + self.campcheck_traveled_distance = 0; + + return false; +} + +MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":CampCheck"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/module.inc b/qcsrc/common/mutators/mutator/campcheck/module.inc new file mode 100644 index 000000000..9f4181f16 --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "campcheck.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/dodging.qc b/qcsrc/common/mutators/mutator/dodging/dodging.qc new file mode 100644 index 000000000..f014ebbc5 --- /dev/null +++ b/qcsrc/common/mutators/mutator/dodging/dodging.qc @@ -0,0 +1,334 @@ +#ifdef IMPLEMENTATION + +#ifdef CSQC + #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) + #define PHYS_DODGING getstati(STAT_DODGING) + #define PHYS_DODGING_DELAY getstatf(STAT_DODGING_DELAY) + #define PHYS_DODGING_TIMEOUT(s) getstatf(STAT_DODGING_TIMEOUT) + #define PHYS_DODGING_HORIZ_SPEED_FROZEN getstatf(STAT_DODGING_HORIZ_SPEED_FROZEN) + #define PHYS_DODGING_FROZEN_NODOUBLETAP getstati(STAT_DODGING_FROZEN_NO_DOUBLETAP) + #define PHYS_DODGING_HORIZ_SPEED getstatf(STAT_DODGING_HORIZ_SPEED) + #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys + #define PHYS_DODGING_HEIGHT_THRESHOLD getstatf(STAT_DODGING_HEIGHT_THRESHOLD) + #define PHYS_DODGING_DISTANCE_THRESHOLD getstatf(STAT_DODGING_DISTANCE_THRESHOLD) + #define PHYS_DODGING_RAMP_TIME getstatf(STAT_DODGING_RAMP_TIME) + #define PHYS_DODGING_UP_SPEED getstatf(STAT_DODGING_UP_SPEED) + #define PHYS_DODGING_WALL getstatf(STAT_DODGING_WALL) +#elif defined(SVQC) + #define PHYS_DODGING_FRAMETIME sys_frametime + #define PHYS_DODGING g_dodging + #define PHYS_DODGING_DELAY autocvar_sv_dodging_delay + #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout + #define PHYS_DODGING_HORIZ_SPEED_FROZEN autocvar_sv_dodging_horiz_speed_frozen + #define PHYS_DODGING_FROZEN_NODOUBLETAP autocvar_sv_dodging_frozen_doubletap + #define PHYS_DODGING_HORIZ_SPEED autocvar_sv_dodging_horiz_speed + #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys + #define PHYS_DODGING_HEIGHT_THRESHOLD autocvar_sv_dodging_height_threshold + #define PHYS_DODGING_DISTANCE_THRESHOLD autocvar_sv_dodging_wall_distance_threshold + #define PHYS_DODGING_RAMP_TIME autocvar_sv_dodging_ramp_time + #define PHYS_DODGING_UP_SPEED autocvar_sv_dodging_up_speed + #define PHYS_DODGING_WALL autocvar_sv_dodging_wall_dodging + + float autocvar_sv_dodging_delay; + float autocvar_sv_dodging_height_threshold; + float autocvar_sv_dodging_horiz_speed; + float autocvar_sv_dodging_horiz_speed_frozen; + float autocvar_sv_dodging_ramp_time; + bool autocvar_sv_dodging_sound; + float autocvar_sv_dodging_up_speed; + float autocvar_sv_dodging_wall_distance_threshold; + bool autocvar_sv_dodging_wall_dodging; + bool autocvar_sv_dodging_frozen_doubletap; +#endif + +#ifdef SVQC + +float g_dodging; + +// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. +.float dodging_action; + +// the jump part of the dodge cannot be ramped +.float dodging_single_action; + +#include "../../../animdecide.qh" +#include "../../../physics.qh" + +.float cvar_cl_dodging_timeout; + +.float stat_dodging; +.float stat_dodging_delay; +.float stat_dodging_horiz_speed_frozen; +.float stat_dodging_frozen_nodoubletap; +.float stat_dodging_frozen; +.float stat_dodging_horiz_speed; +.float stat_dodging_height_threshold; +.float stat_dodging_distance_threshold; +.float stat_dodging_ramp_time; +.float stat_dodging_up_speed; +.float stat_dodging_wall; + +REGISTER_MUTATOR(dodging, cvar("g_dodging")) +{ + // this just turns on the cvar. + MUTATOR_ONADD + { + g_dodging = cvar("g_dodging"); + addstat(STAT_DODGING, AS_INT, stat_dodging); + addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay); + addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos) + addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap); + addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen); + addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen); + addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed); + addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold); + addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold); + addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time); + addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed); + addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall); + } + + // this just turns off the cvar. + MUTATOR_ONROLLBACK_OR_REMOVE + { + g_dodging = 0; + } + + return false; +} + +#endif + +// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. +.float dodging_action; + +// the jump part of the dodge cannot be ramped +.float dodging_single_action; + + +// these are used to store the last key press time for each of the keys.. +.float last_FORWARD_KEY_time; +.float last_BACKWARD_KEY_time; +.float last_LEFT_KEY_time; +.float last_RIGHT_KEY_time; + +// these store the movement direction at the time of the dodge action happening. +.vector dodging_direction; + +// this indicates the last time a dodge was executed. used to check if another one is allowed +// and to ramp up the dodge acceleration in the physics hook. +.float last_dodging_time; + +// This is the velocity gain to be added over the ramp time. +// It will decrease from frame to frame during dodging_action = 1 +// until it's 0. +.float dodging_velocity_gain; + +#ifdef CSQC +.int pressedkeys; + +#elif defined(SVQC) + +void dodging_UpdateStats() +{SELFPARAM(); + self.stat_dodging = PHYS_DODGING; + self.stat_dodging_delay = PHYS_DODGING_DELAY; + self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN; + self.stat_dodging_frozen = PHYS_DODGING_FROZEN; + self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP; + self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD; + self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD; + self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME; + self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED; + self.stat_dodging_wall = PHYS_DODGING_WALL; +} + +#endif + +// returns 1 if the player is close to a wall +bool check_close_to_wall(float threshold) +{SELFPARAM(); + if (PHYS_DODGING_WALL == 0) { return false; } + + #define X(OFFSET) \ + tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, true, self); \ + if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold) \ + return true; + X(1000*v_right); + X(-1000*v_right); + X(1000*v_forward); + X(-1000*v_forward); + #undef X + + return false; +} + +bool check_close_to_ground(float threshold) +{SELFPARAM(); + return IS_ONGROUND(self) ? true : false; +} + +float PM_dodging_checkpressedkeys() +{SELFPARAM(); + if(!PHYS_DODGING) + return false; + + float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN); + float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP); + + // first check if the last dodge is far enough back in time so we can dodge again + if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY) + return false; + + makevectors(self.angles); + + if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1 + && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1) + return true; + + float tap_direction_x = 0; + float tap_direction_y = 0; + float dodge_detected = 0; + + #define X(COND,BTN,RESULT) \ + if (self.movement_##COND) \ + /* is this a state change? */ \ + if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) { \ + tap_direction_##RESULT; \ + if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self)) \ + dodge_detected = 1; \ + self.last_##BTN##_KEY_time = time; \ + } + X(x < 0, BACKWARD, x--); + X(x > 0, FORWARD, x++); + X(y < 0, LEFT, y--); + X(y > 0, RIGHT, y++); + #undef X + + if (dodge_detected == 1) + { + self.last_dodging_time = time; + + self.dodging_action = 1; + self.dodging_single_action = 1; + + self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED; + + self.dodging_direction_x = tap_direction_x; + self.dodging_direction_y = tap_direction_y; + + // normalize the dodging_direction vector.. (unlike UT99) XD + float length = self.dodging_direction_x * self.dodging_direction_x + + self.dodging_direction_y * self.dodging_direction_y; + length = sqrt(length); + + self.dodging_direction_x = self.dodging_direction_x * 1.0 / length; + self.dodging_direction_y = self.dodging_direction_y * 1.0 / length; + return true; + } + return false; +} + +void PM_dodging() +{SELFPARAM(); + if (!PHYS_DODGING) + return; + +#ifdef SVQC + dodging_UpdateStats(); +#endif + + if (PHYS_DEAD(self)) + return; + + // when swimming, no dodging allowed.. + if (self.waterlevel >= WATERLEVEL_SWIMMING) + { + self.dodging_action = 0; + self.dodging_direction_x = 0; + self.dodging_direction_y = 0; + return; + } + + // make sure v_up, v_right and v_forward are sane + makevectors(self.angles); + + // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code + // will be called ramp_time/frametime times = 2 times. so, we need to + // add 0.5 * the total speed each frame until the dodge action is done.. + float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME; + + // if ramp time is smaller than frametime we get problems ;D + common_factor = min(common_factor, 1); + + float horiz_speed = PHYS_FROZEN(self) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED; + float new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed); + new_velocity_gain = max(0, new_velocity_gain); + + float velocity_difference = self.dodging_velocity_gain - new_velocity_gain; + + // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D + if (self.dodging_action == 1) + { + //disable jump key during dodge accel phase + if(self.movement_z > 0) { self.movement_z = 0; } + + self.velocity += ((self.dodging_direction_y * velocity_difference) * v_right) + + ((self.dodging_direction_x * velocity_difference) * v_forward); + + self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference; + } + + // the up part of the dodge is a single shot action + if (self.dodging_single_action == 1) + { + UNSET_ONGROUND(self); + + self.velocity += PHYS_DODGING_UP_SPEED * v_up; + +#ifdef SVQC + if (autocvar_sv_dodging_sound) + PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); + + animdecide_setaction(self, ANIMACTION_JUMP, true); +#endif + + self.dodging_single_action = 0; + } + + // are we done with the dodging ramp yet? + if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME)) + { + // reset state so next dodge can be done correctly + self.dodging_action = 0; + self.dodging_direction_x = 0; + self.dodging_direction_y = 0; + } +} + +#ifdef SVQC + +MUTATOR_HOOKFUNCTION(dodging, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout"); + return false; +} + +MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics) +{ + // print("dodging_PlayerPhysics\n"); + PM_dodging(); + + return false; +} + +MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys) +{ + PM_dodging_checkpressedkeys(); + + return false; +} + +#endif + +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/module.inc b/qcsrc/common/mutators/mutator/dodging/module.inc new file mode 100644 index 000000000..6308a2182 --- /dev/null +++ b/qcsrc/common/mutators/mutator/dodging/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "dodging.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/hook/hook.qc b/qcsrc/common/mutators/mutator/hook/hook.qc new file mode 100644 index 000000000..b298e7b2e --- /dev/null +++ b/qcsrc/common/mutators/mutator/hook/hook.qc @@ -0,0 +1,42 @@ +#ifdef IMPLEMENTATION +AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up")); +#ifdef SVQC +REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) { + MUTATOR_ONADD { + g_grappling_hook = true; + WEP_HOOK.ammo_factor = 0; + } + MUTATOR_ONROLLBACK_OR_REMOVE { + g_grappling_hook = false; + WEP_HOOK.ammo_factor = 1; + } +} + +MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":grappling_hook"); +} + +MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Hook"); +} + +MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString) +{ + ret_string = strcat(ret_string, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n"); +} + +MUTATOR_HOOKFUNCTION(hook, PlayerSpawn) +{ + SELFPARAM(); + self.offhand = OFFHAND_HOOK; +} + +MUTATOR_HOOKFUNCTION(hook, FilterItem) +{ + return self.weapon == WEP_HOOK.m_id; +} + +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/hook/module.inc b/qcsrc/common/mutators/mutator/hook/module.inc new file mode 100644 index 000000000..61600c47b --- /dev/null +++ b/qcsrc/common/mutators/mutator/hook/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "hook.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/instagib/instagib.qc b/qcsrc/common/mutators/mutator/instagib/instagib.qc index 16b5b86d1..2a46dd699 100644 --- a/qcsrc/common/mutators/mutator/instagib/instagib.qc +++ b/qcsrc/common/mutators/mutator/instagib/instagib.qc @@ -3,6 +3,10 @@ #include "items.qc" +#ifdef SVQC +float autocvar_g_instagib_invis_alpha; +#endif + #endif #ifdef IMPLEMENTATION diff --git a/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc new file mode 100644 index 000000000..5a781a881 --- /dev/null +++ b/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc @@ -0,0 +1,25 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles")); + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) +{ + if(other.health) + { + // disable health which in effect disables damage calculations + other.health = 0; + } + return 0; +} + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":InvincibleProjectiles"); + return 0; +} + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Invincible Projectiles"); + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/module.inc b/qcsrc/common/mutators/mutator/invincibleproj/module.inc new file mode 100644 index 000000000..61d038350 --- /dev/null +++ b/qcsrc/common/mutators/mutator/invincibleproj/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "invincibleproj.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/melee_only.qc new file mode 100644 index 000000000..5b03f46ec --- /dev/null +++ b/qcsrc/common/mutators/mutator/melee_only/melee_only.qc @@ -0,0 +1,40 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball); + +MUTATOR_HOOKFUNCTION(melee_only, SetStartItems) +{ + start_ammo_shells = warmup_start_ammo_shells = 0; + start_weapons = warmup_start_weapons = WEPSET(SHOTGUN); + + return false; +} + +MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(melee_only, FilterItem) +{SELFPARAM(); + switch (self.items) + { + case ITEM_HealthSmall.m_itemid: + case ITEM_ArmorSmall.m_itemid: + return false; + } + + return true; +} + +MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":MeleeOnly"); + return false; +} + +MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Melee Only Arena"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/module.inc b/qcsrc/common/mutators/mutator/melee_only/module.inc new file mode 100644 index 000000000..c711556cc --- /dev/null +++ b/qcsrc/common/mutators/mutator/melee_only/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "melee_only.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/midair/midair.qc b/qcsrc/common/mutators/mutator/midair/midair.qc new file mode 100644 index 000000000..aaae006f5 --- /dev/null +++ b/qcsrc/common/mutators/mutator/midair/midair.qc @@ -0,0 +1,50 @@ +#ifdef IMPLEMENTATION + +float autocvar_g_midair_shieldtime; + +REGISTER_MUTATOR(midair, cvar("g_midair")); + +.float midair_shieldtime; + +MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate) +{SELFPARAM(); + if(IS_PLAYER(frag_attacker)) + if(IS_PLAYER(frag_target)) + if(time < self.midair_shieldtime) + frag_damage = false; + + return false; +} + +MUTATOR_HOOKFUNCTION(midair, PlayerPowerups) +{SELFPARAM(); + if(time >= game_starttime) + if(self.flags & FL_ONGROUND) + { + self.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); + self.midair_shieldtime = max(self.midair_shieldtime, time + autocvar_g_midair_shieldtime); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(midair, PlayerSpawn) +{SELFPARAM(); + if(IS_BOT_CLIENT(self)) + self.bot_moveskill = 0; // disable bunnyhopping + + return false; +} + +MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":midair"); + return false; +} + +MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Midair"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/midair/module.inc b/qcsrc/common/mutators/mutator/midair/module.inc new file mode 100644 index 000000000..10b178915 --- /dev/null +++ b/qcsrc/common/mutators/mutator/midair/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "midair.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/multijump/module.inc b/qcsrc/common/mutators/mutator/multijump/module.inc new file mode 100644 index 000000000..310332099 --- /dev/null +++ b/qcsrc/common/mutators/mutator/multijump/module.inc @@ -0,0 +1,3 @@ +#ifndef MENUQC +#include "multijump.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/multijump/multijump.qc b/qcsrc/common/mutators/mutator/multijump/multijump.qc new file mode 100644 index 000000000..00e22af72 --- /dev/null +++ b/qcsrc/common/mutators/mutator/multijump/multijump.qc @@ -0,0 +1,187 @@ +#ifdef IMPLEMENTATION +#ifdef SVQC + #include "../../../../server/antilag.qh" +#endif +#include "../../../physics.qh" + +.int multijump_count; +.bool multijump_ready; +.bool cvar_cl_multijump; + +#ifdef CSQC + +#define PHYS_MULTIJUMP getstati(STAT_MULTIJUMP) +#define PHYS_MULTIJUMP_SPEED getstatf(STAT_MULTIJUMP_SPEED) +#define PHYS_MULTIJUMP_ADD getstati(STAT_MULTIJUMP_ADD) +#define PHYS_MULTIJUMP_MAXSPEED getstatf(STAT_MULTIJUMP_MAXSPEED) +#define PHYS_MULTIJUMP_DODGING getstati(STAT_MULTIJUMP_DODGING) + +#elif defined(SVQC) + +int autocvar_g_multijump; +float autocvar_g_multijump_add; +float autocvar_g_multijump_speed; +float autocvar_g_multijump_maxspeed; +float autocvar_g_multijump_dodging = 1; + +#define PHYS_MULTIJUMP autocvar_g_multijump +#define PHYS_MULTIJUMP_SPEED autocvar_g_multijump_speed +#define PHYS_MULTIJUMP_ADD autocvar_g_multijump_add +#define PHYS_MULTIJUMP_MAXSPEED autocvar_g_multijump_maxspeed +#define PHYS_MULTIJUMP_DODGING autocvar_g_multijump_dodging + +.float stat_multijump; +.float stat_multijump_speed; +.float stat_multijump_add; +.float stat_multijump_maxspeed; +.float stat_multijump_dodging; + +void multijump_UpdateStats() +{SELFPARAM(); + self.stat_multijump = PHYS_MULTIJUMP; + self.stat_multijump_speed = PHYS_MULTIJUMP_SPEED; + self.stat_multijump_add = PHYS_MULTIJUMP_ADD; + self.stat_multijump_maxspeed = PHYS_MULTIJUMP_MAXSPEED; + self.stat_multijump_dodging = PHYS_MULTIJUMP_DODGING; +} + +void multijump_AddStats() +{ + addstat(STAT_MULTIJUMP, AS_INT, stat_multijump); + addstat(STAT_MULTIJUMP_SPEED, AS_FLOAT, stat_multijump_speed); + addstat(STAT_MULTIJUMP_ADD, AS_INT, stat_multijump_add); + addstat(STAT_MULTIJUMP_MAXSPEED, AS_FLOAT, stat_multijump_maxspeed); + addstat(STAT_MULTIJUMP_DODGING, AS_INT, stat_multijump_dodging); +} + +#endif + +void PM_multijump() +{SELFPARAM(); + if(!PHYS_MULTIJUMP) { return; } + + if(IS_ONGROUND(self)) + { + self.multijump_count = 0; + } +} + +bool PM_multijump_checkjump() +{SELFPARAM(); + if(!PHYS_MULTIJUMP) { return false; } + +#ifdef SVQC + bool client_multijump = self.cvar_cl_multijump; +#elif defined(CSQC) + bool client_multijump = cvar("cl_multijump"); + + if(cvar("cl_multijump") > 1) + return false; // nope +#endif + + if (!IS_JUMP_HELD(self) && !IS_ONGROUND(self) && client_multijump) // jump button pressed this frame and we are in midair + self.multijump_ready = true; // this is necessary to check that we released the jump button and pressed it again + else + self.multijump_ready = false; + + int phys_multijump = PHYS_MULTIJUMP; + +#ifdef CSQC + phys_multijump = (PHYS_MULTIJUMP) ? -1 : 0; +#endif + + if(!player_multijump && self.multijump_ready && (self.multijump_count < phys_multijump || phys_multijump == -1) && self.velocity_z > PHYS_MULTIJUMP_SPEED && (!PHYS_MULTIJUMP_MAXSPEED || vlen(self.velocity) <= PHYS_MULTIJUMP_MAXSPEED)) + { + if (PHYS_MULTIJUMP) + { + if (!PHYS_MULTIJUMP_ADD) // in this case we make the z velocity == jumpvelocity + { + if (self.velocity_z < PHYS_JUMPVELOCITY) + { + player_multijump = true; + self.velocity_z = 0; + } + } + else + player_multijump = true; + + if(player_multijump) + { + if(PHYS_MULTIJUMP_DODGING) + if(self.movement_x != 0 || self.movement_y != 0) // don't remove all speed if player isnt pressing any movement keys + { + float curspeed; + vector wishvel, wishdir; + +/*#ifdef SVQC + curspeed = max( + vlen(vec2(self.velocity)), // current xy speed + vlen(vec2(antilag_takebackavgvelocity(self, max(self.lastteleporttime + sys_frametime, time - 0.25), time))) // average xy topspeed over the last 0.25 secs + ); +#elif defined(CSQC)*/ + curspeed = vlen(vec2(self.velocity)); +//#endif + + makevectors(self.v_angle_y * '0 1 0'); + wishvel = v_forward * self.movement_x + v_right * self.movement_y; + wishdir = normalize(wishvel); + + self.velocity_x = wishdir_x * curspeed; // allow "dodging" at a multijump + self.velocity_y = wishdir_y * curspeed; + // keep velocity_z unchanged! + } + if (PHYS_MULTIJUMP > 0) + { + self.multijump_count += 1; + } + } + } + self.multijump_ready = false; // require releasing and pressing the jump button again for the next jump + } + + return false; +} + +#ifdef SVQC +REGISTER_MUTATOR(multijump, cvar("g_multijump")) +{ + MUTATOR_ONADD + { + multijump_AddStats(); + } + return false; +} + +MUTATOR_HOOKFUNCTION(multijump, PlayerPhysics) +{ + multijump_UpdateStats(); + PM_multijump(); + + return false; +} + +MUTATOR_HOOKFUNCTION(multijump, PlayerJump) +{ + return PM_multijump_checkjump(); +} + +MUTATOR_HOOKFUNCTION(multijump, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_multijump, "cl_multijump"); + return false; +} + +MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":multijump"); + return false; +} + +MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Multi jump"); + return false; +} + +#endif +#endif diff --git a/qcsrc/common/mutators/mutator/nades/module.inc b/qcsrc/common/mutators/mutator/nades/module.inc new file mode 100644 index 000000000..b9c0eb308 --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "nades.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc new file mode 100644 index 000000000..556265f1f --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -0,0 +1,1248 @@ +#ifndef MUTATOR_NADES_H +#define MUTATOR_NADES_H + +#ifdef SVQC +#include "../../../../server/mutators/mutator/gamemode_freezetag.qc" +#endif + +.entity nade; +.entity fake_nade; +.float nade_timer; +.float nade_refire; +.float bonus_nades; +.float nade_special_time; +.float bonus_nade_score; +.float nade_type; +.string pokenade_type; +.entity nade_damage_target; +.float cvar_cl_nade_type; +.string cvar_cl_pokenade_type; +.float toss_time; +.float stat_healing_orb; +.float stat_healing_orb_alpha; +.float nade_show_particles; + +// Remove nades that are being thrown +void nades_Clear(entity player); + +// Give a bonus grenade to a player +void(entity player, float score) nades_GiveBonus; + +/** + * called to adjust nade damage and force on hit + */ +#define EV_Nade_Damage(i, o) \ + /** weapon */ i(entity, MUTATOR_ARGV_0_entity) \ + /** force */ i(vector, MUTATOR_ARGV_0_vector) \ + /**/ o(vector, MUTATOR_ARGV_0_vector) \ + /** damage */ i(float, MUTATOR_ARGV_0_float) \ + /**/ o(float, MUTATOR_ARGV_0_float) \ + /**/ +MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage); + +#endif + +#ifdef IMPLEMENTATION + +#include "../../../nades/all.qh" +#include "../../../gamemodes/all.qh" +#include "../../../monsters/spawn.qh" +#include "../../../monsters/sv_monsters.qh" +#include "../../../../server/g_subs.qh" + +REGISTER_MUTATOR(nades, cvar("g_nades")) +{ + MUTATOR_ONADD + { + addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer); + addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades); + addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type); + addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score); + addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb); + addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha); + } + + return false; +} + +.float nade_time_primed; + +.entity nade_spawnloc; + +void nade_timer_think() +{SELFPARAM(); + self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10); + self.nextthink = time; + if(!self.owner || wasfreed(self.owner)) + remove(self); +} + +void nade_burn_spawn(entity _nade) +{ + CSQCProjectile(_nade, true, Nades_from(_nade.nade_type).m_projectile[true], true); +} + +void nade_spawn(entity _nade) +{ + entity timer = new(nade_timer); + setmodel(timer, MDL_NADE_TIMER); + setattachment(timer, _nade, ""); + timer.colormap = _nade.colormap; + timer.glowmod = _nade.glowmod; + timer.think = nade_timer_think; + timer.nextthink = time; + timer.wait = _nade.wait; + timer.owner = _nade; + timer.skin = 10; + + _nade.effects |= EF_LOWPRECISION; + + CSQCProjectile(_nade, true, Nades_from(_nade.nade_type).m_projectile[false], true); +} + +void napalm_damage(float dist, float damage, float edgedamage, float burntime) +{SELFPARAM(); + entity e; + float d; + vector p; + + if ( damage < 0 ) + return; + + RandomSelection_Init(); + for(e = WarpZone_FindRadius(self.origin, dist, true); e; e = e.chain) + if(e.takedamage == DAMAGE_AIM) + if(self.realowner != e || autocvar_g_nades_napalm_selfdamage) + if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self)) + if(!e.frozen) + { + p = e.origin; + p.x += e.mins.x + random() * (e.maxs.x - e.mins.x); + p.y += e.mins.y + random() * (e.maxs.y - e.mins.y); + p.z += e.mins.z + random() * (e.maxs.z - e.mins.z); + d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p); + if(d < dist) + { + e.fireball_impactvec = p; + RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e)); + } + } + if(RandomSelection_chosen_ent) + { + d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec); + d = damage + (edgedamage - damage) * (d / dist); + Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE); + //trailparticles(self, particleeffectnum(EFFECT_FIREBALL_LASER), self.origin, RandomSelection_chosen_ent.fireball_impactvec); + Send_Effect(EFFECT_FIREBALL_LASER, self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1); + } +} + + +void napalm_ball_think() +{SELFPARAM(); + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + { + remove(self); + return; + } + + if(time > self.pushltime) + { + remove(self); + return; + } + + vector midpoint = ((self.absmin + self.absmax) * 0.5); + if(pointcontents(midpoint) == CONTENT_WATER) + { + self.velocity = self.velocity * 0.5; + + if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) + { self.velocity_z = 200; } + } + + self.angles = vectoangles(self.velocity); + + napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage, + autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime); + + self.nextthink = time + 0.1; +} + + +void nade_napalm_ball() +{SELFPARAM(); + entity proj; + vector kick; + + spamsound(self, CH_SHOTS, SND(FIREBALL_FIRE), VOL_BASE, ATTEN_NORM); + + proj = new(grenade); + proj.owner = self.owner; + proj.realowner = self.realowner; + proj.team = self.owner.team; + proj.bot_dodge = true; + proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage; + proj.movetype = MOVETYPE_BOUNCE; + proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id; + PROJECTILE_MAKETRIGGER(proj); + setmodel(proj, MDL_Null); + proj.scale = 1;//0.5; + setsize(proj, '-4 -4 -4', '4 4 4'); + setorigin(proj, self.origin); + proj.think = napalm_ball_think; + proj.nextthink = time; + proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale; + proj.effects = EF_LOWPRECISION | EF_FLAME; + + kick.x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; + kick.y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; + kick.z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread; + proj.velocity = kick; + + proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime; + + proj.angles = vectoangles(proj.velocity); + proj.flags = FL_PROJECTILE; + proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; + + //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true); +} + + +void napalm_fountain_think() +{SELFPARAM(); + + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + { + remove(self); + return; + } + + if(time >= self.ltime) + { + remove(self); + return; + } + + vector midpoint = ((self.absmin + self.absmax) * 0.5); + if(pointcontents(midpoint) == CONTENT_WATER) + { + self.velocity = self.velocity * 0.5; + + if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) + { self.velocity_z = 200; } + + UpdateCSQCProjectile(self); + } + + napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage, + autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime); + + self.nextthink = time + 0.1; + if(time >= self.nade_special_time) + { + self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay; + nade_napalm_ball(); + } +} + +void nade_napalm_boom() +{SELFPARAM(); + entity fountain; + int c; + for (c = 0; c < autocvar_g_nades_napalm_ball_count; c++) + nade_napalm_ball(); + + + fountain = spawn(); + fountain.owner = self.owner; + fountain.realowner = self.realowner; + fountain.origin = self.origin; + setorigin(fountain, fountain.origin); + fountain.think = napalm_fountain_think; + fountain.nextthink = time; + fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime; + fountain.pushltime = fountain.ltime; + fountain.team = self.team; + fountain.movetype = MOVETYPE_TOSS; + fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id; + fountain.bot_dodge = true; + fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage; + fountain.nade_special_time = time; + setsize(fountain, '-16 -16 -16', '16 16 16'); + CSQCProjectile(fountain, true, PROJECTILE_NAPALM_FOUNTAIN, true); +} + +void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time) +{ + frost_target.frozen_by = freezefield.realowner; + Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1); + Freeze(frost_target, 1/freeze_time, 3, false); + + Drop_Special_Items(frost_target); +} + +void nade_ice_think() +{SELFPARAM(); + + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + { + remove(self); + return; + } + + if(time >= self.ltime) + { + if ( autocvar_g_nades_ice_explode ) + { + entity expef = EFFECT_NADE_EXPLODE(self.realowner.team); + Send_Effect(expef, self.origin + '0 0 1', '0 0 0', 1); + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + + RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, + autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); + Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, + autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); + } + remove(self); + return; + } + + + self.nextthink = time+0.1; + + // gaussian + float randomr; + randomr = random(); + randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius; + float randomw; + randomw = random()*M_PI*2; + vector randomp; + randomp.x = randomr*cos(randomw); + randomp.y = randomr*sin(randomw); + randomp.z = 1; + Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, self.origin + randomp, '0 0 0', 1); + + if(time >= self.nade_special_time) + { + self.nade_special_time = time+0.7; + + Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1); + Send_Effect(EFFECT_ICEFIELD, self.origin, '0 0 0', 1); + } + + + float current_freeze_time = self.ltime - time - 0.1; + + entity e; + for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain) + if(e != self) + if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner)) + if(e.takedamage && e.deadflag == DEAD_NO) + if(e.health > 0) + if(!e.revival_time || ((time - e.revival_time) >= 1.5)) + if(!e.frozen) + if(current_freeze_time > 0) + nade_ice_freeze(self, e, current_freeze_time); +} + +void nade_ice_boom() +{SELFPARAM(); + entity fountain; + fountain = spawn(); + fountain.owner = self.owner; + fountain.realowner = self.realowner; + fountain.origin = self.origin; + setorigin(fountain, fountain.origin); + fountain.think = nade_ice_think; + fountain.nextthink = time; + fountain.ltime = time + autocvar_g_nades_ice_freeze_time; + fountain.pushltime = fountain.wait = fountain.ltime; + fountain.team = self.team; + fountain.movetype = MOVETYPE_TOSS; + fountain.projectiledeathtype = DEATH_NADE_ICE.m_id; + fountain.bot_dodge = false; + setsize(fountain, '-16 -16 -16', '16 16 16'); + fountain.nade_special_time = time+0.3; + fountain.angles = self.angles; + + if ( autocvar_g_nades_ice_explode ) + { + setmodel(fountain, MDL_PROJECTILE_GRENADE); + entity timer = new(nade_timer); + setmodel(timer, MDL_NADE_TIMER); + setattachment(timer, fountain, ""); + timer.colormap = self.colormap; + timer.glowmod = self.glowmod; + timer.think = nade_timer_think; + timer.nextthink = time; + timer.wait = fountain.ltime; + timer.owner = fountain; + timer.skin = 10; + } + else + setmodel(fountain, MDL_Null); +} + +void nade_translocate_boom() +{SELFPARAM(); + if(self.realowner.vehicle) + return; + + vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins.z - 24); + tracebox(locout, self.realowner.mins, self.realowner.maxs, locout, MOVE_NOMONSTERS, self.realowner); + locout = trace_endpos; + + makevectors(self.realowner.angles); + + MUTATOR_CALLHOOK(PortalTeleport, self.realowner); + + TeleportPlayer(self, self.realowner, locout, self.realowner.angles, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); +} + +void nade_spawn_boom() +{SELFPARAM(); + entity spawnloc = spawn(); + setorigin(spawnloc, self.origin); + setsize(spawnloc, self.realowner.mins, self.realowner.maxs); + spawnloc.movetype = MOVETYPE_NONE; + spawnloc.solid = SOLID_NOT; + spawnloc.drawonlytoclient = self.realowner; + spawnloc.effects = EF_STARDUST; + spawnloc.cnt = autocvar_g_nades_spawn_count; + + if(self.realowner.nade_spawnloc) + { + remove(self.realowner.nade_spawnloc); + self.realowner.nade_spawnloc = world; + } + + self.realowner.nade_spawnloc = spawnloc; +} + +void nade_heal_think() +{SELFPARAM(); + if(time >= self.ltime) + { + remove(self); + return; + } + + self.nextthink = time; + + if(time >= self.nade_special_time) + { + self.nade_special_time = time+0.25; + self.nade_show_particles = 1; + } + else + self.nade_show_particles = 0; +} + +void nade_heal_touch() +{SELFPARAM(); + float maxhealth; + float health_factor; + if(IS_PLAYER(other) || IS_MONSTER(other)) + if(other.deadflag == DEAD_NO) + if(!other.frozen) + { + health_factor = autocvar_g_nades_heal_rate*frametime/2; + if ( other != self.realowner ) + { + if ( SAME_TEAM(other,self) ) + health_factor *= autocvar_g_nades_heal_friend; + else + health_factor *= autocvar_g_nades_heal_foe; + } + if ( health_factor > 0 ) + { + maxhealth = (IS_MONSTER(other)) ? other.max_health : g_pickup_healthmega_max; + if ( other.health < maxhealth ) + { + if ( self.nade_show_particles ) + Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1); + other.health = min(other.health+health_factor, maxhealth); + } + other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); + } + else if ( health_factor < 0 ) + { + Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL.m_id,other.origin,'0 0 0'); + } + + } + + if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) ) + { + entity show_red = (IS_VEHICLE(other)) ? other.owner : other; + show_red.stat_healing_orb = time+0.1; + show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime; + } +} + +void nade_heal_boom() +{SELFPARAM(); + entity healer; + healer = spawn(); + healer.owner = self.owner; + healer.realowner = self.realowner; + setorigin(healer, self.origin); + healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar + healer.ltime = time + healer.healer_lifetime; + healer.team = self.realowner.team; + healer.bot_dodge = false; + healer.solid = SOLID_TRIGGER; + healer.touch = nade_heal_touch; + + setmodel(healer, MDL_NADE_HEAL); + healer.healer_radius = autocvar_g_nades_nade_radius; + vector size = '1 1 1' * healer.healer_radius / 2; + setsize(healer,-size,size); + + Net_LinkEntity(healer, true, 0, healer_send); + + healer.think = nade_heal_think; + healer.nextthink = time; + healer.SendFlags |= 1; +} + +void nade_monster_boom() +{SELFPARAM(); + entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, false, false, 1); + + if(autocvar_g_nades_pokenade_monster_lifetime > 0) + e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime; + e.monster_skill = MONSTER_SKILL_INSANE; +} + +void nade_boom() +{SELFPARAM(); + entity expef = NULL; + bool nade_blast = true; + + switch ( Nades_from(self.nade_type) ) + { + case NADE_TYPE_NAPALM: + nade_blast = autocvar_g_nades_napalm_blast; + expef = EFFECT_EXPLOSION_MEDIUM; + break; + case NADE_TYPE_ICE: + nade_blast = false; + expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact + break; + case NADE_TYPE_TRANSLOCATE: + nade_blast = false; + break; + case NADE_TYPE_MONSTER: + case NADE_TYPE_SPAWN: + nade_blast = false; + switch(self.realowner.team) + { + case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break; + case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break; + case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break; + case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break; + default: expef = EFFECT_SPAWN_NEUTRAL; break; + } + break; + case NADE_TYPE_HEAL: + nade_blast = false; + expef = EFFECT_SPAWN_RED; + break; + + default: + case NADE_TYPE_NORMAL: + expef = EFFECT_NADE_EXPLODE(self.realowner.team); + break; + } + + if(expef) + Send_Effect(expef, findbetterlocation(self.origin, 8), '0 0 0', 1); + + sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); + sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + + self.event_damage = func_null; // prevent somehow calling damage in the next call + + if(nade_blast) + { + RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, + autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); + Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); + } + + if(self.takedamage) + switch ( Nades_from(self.nade_type) ) + { + case NADE_TYPE_NAPALM: nade_napalm_boom(); break; + case NADE_TYPE_ICE: nade_ice_boom(); break; + case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break; + case NADE_TYPE_SPAWN: nade_spawn_boom(); break; + case NADE_TYPE_HEAL: nade_heal_boom(); break; + case NADE_TYPE_MONSTER: nade_monster_boom(); break; + } + + entity head; + for(head = world; (head = find(head, classname, "grapplinghook")); ) + if(head.aiment == self) + RemoveGrapplingHook(head.realowner); + + remove(self); +} + +void nade_touch() +{SELFPARAM(); + /*float is_weapclip = 0; + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) + if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) + is_weapclip = 1;*/ + if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip) + { + entity head; + for(head = world; (head = find(head, classname, "grapplinghook")); ) + if(head.aiment == self) + RemoveGrapplingHook(head.realowner); + remove(self); + return; + } + + PROJECTILE_TOUCH; + + //setsize(self, '-2 -2 -2', '2 2 2'); + //UpdateCSQCProjectile(self); + if(self.health == self.max_health) + { + spamsound(self, CH_SHOTS, SND(GRENADE_BOUNCE_RANDOM()), VOL_BASE, ATTEN_NORM); + return; + } + + self.enemy = other; + nade_boom(); +} + +void nade_beep() +{SELFPARAM(); + sound(self, CH_SHOTS_SINGLE, SND_NADE_BEEP, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); + self.think = nade_boom; + self.nextthink = max(self.wait, time); +} + +void nade_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{SELFPARAM(); + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + self.takedamage = DAMAGE_NO; + nade_boom(); + return; + } + + if(self.nade_type == NADE_TYPE_TRANSLOCATE.m_id || self.nade_type == NADE_TYPE_SPAWN.m_id) + return; + + if (MUTATOR_CALLHOOK(Nade_Damage, DEATH_WEAPONOF(deathtype), force, damage)) {} + else if(DEATH_ISWEAPON(deathtype, WEP_BLASTER)) + { + force *= 1.5; + damage = 0; + } + else if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER) && (deathtype & HITTYPE_SECONDARY)) + { + force *= 0.5; // too much + damage = 0; + } + else if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) + { + force *= 6; + damage = self.max_health * 0.55; + } + else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) + damage = self.max_health * 0.1; + else if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) // WEAPONTODO + { + if(deathtype & HITTYPE_SECONDARY) + { + damage = self.max_health * 0.1; + force *= 10; + } + else + damage = self.max_health * 1.15; + } + + self.velocity += force; + UpdateCSQCProjectile(self); + + if(damage <= 0 || ((self.flags & FL_ONGROUND) && IS_PLAYER(attacker))) + return; + + if(self.health == self.max_health) + { + sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); + self.nextthink = max(time + autocvar_g_nades_nade_lifetime, time); + self.think = nade_beep; + } + + self.health -= damage; + + if ( self.nade_type != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker) ) + self.realowner = attacker; + + if(self.health <= 0) + W_PrepareExplosionByDamage(attacker, nade_boom); + else + nade_burn_spawn(self); +} + +void toss_nade(entity e, vector _velocity, float _time) +{SELFPARAM(); + if(e.nade == world) + return; + + entity _nade = e.nade; + e.nade = world; + + remove(e.fake_nade); + e.fake_nade = world; + + makevectors(e.v_angle); + + W_SetupShot(e, false, false, "", CH_WEAPON_A, 0); + + Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES); + + vector offset = (v_forward * autocvar_g_nades_throw_offset.x) + + (v_right * autocvar_g_nades_throw_offset.y) + + (v_up * autocvar_g_nades_throw_offset.z); + if(autocvar_g_nades_throw_offset == '0 0 0') + offset = '0 0 0'; + + setorigin(_nade, w_shotorg + offset + (v_right * 25) * -1); + //setmodel(_nade, MDL_PROJECTILE_NADE); + //setattachment(_nade, world, ""); + PROJECTILE_MAKETRIGGER(_nade); + setsize(_nade, '-16 -16 -16', '16 16 16'); + _nade.movetype = MOVETYPE_BOUNCE; + + tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade); + if (trace_startsolid) + setorigin(_nade, e.origin); + + if(self.v_angle.x >= 70 && self.v_angle.x <= 110 && self.BUTTON_CROUCH) + _nade.velocity = '0 0 100'; + else if(autocvar_g_nades_nade_newton_style == 1) + _nade.velocity = e.velocity + _velocity; + else if(autocvar_g_nades_nade_newton_style == 2) + _nade.velocity = _velocity; + else + _nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, true); + + _nade.touch = nade_touch; + _nade.health = autocvar_g_nades_nade_health; + _nade.max_health = _nade.health; + _nade.takedamage = DAMAGE_AIM; + _nade.event_damage = nade_damage; + _nade.customizeentityforclient = func_null; + _nade.exteriormodeltoclient = world; + _nade.traileffectnum = 0; + _nade.teleportable = true; + _nade.pushable = true; + _nade.gravity = 1; + _nade.missile_flags = MIF_SPLASH | MIF_ARC; + _nade.damagedbycontents = true; + _nade.angles = vectoangles(_nade.velocity); + _nade.flags = FL_PROJECTILE; + _nade.projectiledeathtype = DEATH_NADE.m_id; + _nade.toss_time = time; + _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX); + + if(_nade.nade_type == NADE_TYPE_TRANSLOCATE.m_id || _nade.nade_type == NADE_TYPE_SPAWN.m_id) + _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + else + _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; + + nade_spawn(_nade); + + if(_time) + { + _nade.think = nade_boom; + _nade.nextthink = _time; + } + + e.nade_refire = time + autocvar_g_nades_nade_refire; + e.nade_timer = 0; +} + +void nades_GiveBonus(entity player, float score) +{ + if (autocvar_g_nades) + if (autocvar_g_nades_bonus) + if (IS_REAL_CLIENT(player)) + if (IS_PLAYER(player) && player.bonus_nades < autocvar_g_nades_bonus_max) + if (player.frozen == 0) + if (player.deadflag == DEAD_NO) + { + if ( player.bonus_nade_score < 1 ) + player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max; + + if ( player.bonus_nade_score >= 1 ) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS); + play2(player, SND(KH_ALARM)); + player.bonus_nades++; + player.bonus_nade_score -= 1; + } + } +} + +/** Remove all bonus nades from a player */ +void nades_RemoveBonus(entity player) +{ + player.bonus_nades = player.bonus_nade_score = 0; +} + +MUTATOR_HOOKFUNCTION(nades, PutClientInServer) +{ + nades_RemoveBonus(self); +} + +float nade_customize() +{SELFPARAM(); + //if(IS_SPEC(other)) { return false; } + if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner)) + { + // somewhat hide the model, but keep the glow + //self.effects = 0; + if(self.traileffectnum) + self.traileffectnum = 0; + self.alpha = -1; + } + else + { + //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + if(!self.traileffectnum) + self.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades_from(self.nade_type).m_projectile[false], self.team).eent_eff_name); + self.alpha = 1; + } + + return true; +} + +void nade_prime() +{SELFPARAM(); + if(autocvar_g_nades_bonus_only) + if(!self.bonus_nades) + return; // only allow bonus nades + + if(self.nade) + remove(self.nade); + + if(self.fake_nade) + remove(self.fake_nade); + + entity n = new(nade), fn = new(fake_nade); + + if(self.items & ITEM_Strength.m_itemid && autocvar_g_nades_bonus_onstrength) + n.nade_type = self.nade_type; + else if (self.bonus_nades >= 1) + { + n.nade_type = self.nade_type; + n.pokenade_type = self.pokenade_type; + self.bonus_nades -= 1; + } + else + { + n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type); + n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type); + } + + n.nade_type = bound(1, n.nade_type, Nades_COUNT); + + setmodel(n, MDL_PROJECTILE_NADE); + //setattachment(n, self, "bip01 l hand"); + n.exteriormodeltoclient = self; + n.customizeentityforclient = nade_customize; + n.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades_from(n.nade_type).m_projectile[false], self.team).eent_eff_name); + n.colormod = Nades_from(n.nade_type).m_color; + n.realowner = self; + n.colormap = self.colormap; + n.glowmod = self.glowmod; + n.wait = time + autocvar_g_nades_nade_lifetime; + n.nade_time_primed = time; + n.think = nade_beep; + n.nextthink = max(n.wait - 3, time); + n.projectiledeathtype = DEATH_NADE.m_id; + + setmodel(fn, MDL_NADE_VIEW); + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + setattachment(fn, self.(weaponentity), ""); + fn.realowner = fn.owner = self; + fn.colormod = Nades_from(n.nade_type).m_color; + fn.colormap = self.colormap; + fn.glowmod = self.glowmod; + fn.think = SUB_Remove; + fn.nextthink = n.wait; + + self.nade = n; + self.fake_nade = fn; +} + +float CanThrowNade() +{SELFPARAM(); + if(self.vehicle) + return false; + + if(gameover) + return false; + + if(self.deadflag != DEAD_NO) + return false; + + if (!autocvar_g_nades) + return false; // allow turning them off mid match + + if(forbidWeaponUse(self)) + return false; + + if (!IS_PLAYER(self)) + return false; + + return true; +} + +.bool nade_altbutton; + +void nades_CheckThrow() +{SELFPARAM(); + if(!CanThrowNade()) + return; + + entity held_nade = self.nade; + if (!held_nade) + { + self.nade_altbutton = true; + if(time > self.nade_refire) + { + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_NADE_THROW); + nade_prime(); + self.nade_refire = time + autocvar_g_nades_nade_refire; + } + } + else + { + self.nade_altbutton = false; + if (time >= held_nade.nade_time_primed + 1) { + makevectors(self.v_angle); + float _force = time - held_nade.nade_time_primed; + _force /= autocvar_g_nades_nade_lifetime; + _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); + toss_nade(self, (v_forward * 0.75 + v_up * 0.2 + v_right * 0.05) * _force, 0); + } + } +} + +void nades_Clear(entity player) +{ + if(player.nade) + remove(player.nade); + if(player.fake_nade) + remove(player.fake_nade); + + player.nade = player.fake_nade = world; + player.nade_timer = 0; +} + +MUTATOR_HOOKFUNCTION(nades, VehicleEnter) +{ + if(vh_player.nade) + toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05)); + + return false; +} + +CLASS(NadeOffhand, OffhandWeapon) + METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed)) + { + entity held_nade = player.nade; + if (held_nade) + { + player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1); + // LOG_TRACEF("%d %d\n", player.nade_timer, time - held_nade.nade_time_primed); + makevectors(player.angles); + held_nade.velocity = player.velocity; + setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0); + held_nade.angles_y = player.angles.y; + + if (time + 0.1 >= held_nade.wait) + toss_nade(player, '0 0 0', time + 0.05); + } + + if (!CanThrowNade()) return; + if (!(time > player.nade_refire)) return; + if (key_pressed) { + if (!held_nade) { + nade_prime(); + held_nade = player.nade; + } + } else if (time >= held_nade.nade_time_primed + 1) { + if (held_nade) { + makevectors(player.v_angle); + float _force = time - held_nade.nade_time_primed; + _force /= autocvar_g_nades_nade_lifetime; + _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); + toss_nade(player, (v_forward * 0.7 + v_up * 0.2 + v_right * 0.1) * _force, 0); + } + } + } +ENDCLASS(NadeOffhand) +NadeOffhand OFFHAND_NADE; STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); } + +MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST) +{ + if (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) { + nades_CheckThrow(); + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) +{SELFPARAM(); + if (!IS_PLAYER(self)) { return false; } + + if (self.nade && (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, self, self.nade_altbutton); + + if(IS_PLAYER(self)) + { + if ( autocvar_g_nades_bonus && autocvar_g_nades ) + { + entity key; + float key_count = 0; + FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; } + + float time_score; + if(self.flagcarried || self.ballcarried) // this player is important + time_score = autocvar_g_nades_bonus_score_time_flagcarrier; + else + time_score = autocvar_g_nades_bonus_score_time; + + if(key_count) + time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding + + if(autocvar_g_nades_bonus_client_select) + { + self.nade_type = self.cvar_cl_nade_type; + self.pokenade_type = self.cvar_cl_pokenade_type; + } + else + { + self.nade_type = autocvar_g_nades_bonus_type; + self.pokenade_type = autocvar_g_nades_pokenade_monster_type; + } + + self.nade_type = bound(1, self.nade_type, Nades_COUNT); + + if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max) + nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max); + } + else + { + self.bonus_nades = self.bonus_nade_score = 0; + } + } + + float n = 0; + entity o = world; + if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout) + n = -1; + else + { + vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; + n = 0; + FOR_EACH_PLAYER(other) if(self != other) + { + if(other.deadflag == DEAD_NO) + if(other.frozen == 0) + if(SAME_TEAM(other, self)) + if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax)) + { + if(!o) + o = other; + if(self.frozen == 1) + other.reviving = true; + ++n; + } + } + } + + if(n && self.frozen == 3) // OK, there is at least one teammate reviving us + { + self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); + self.health = max(1, self.revive_progress * start_health); + + if(self.revive_progress >= 1) + { + Unfreeze(self); + + Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname); + Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname); + } + + FOR_EACH_PLAYER(other) if(other.reviving) + { + other.revive_progress = self.revive_progress; + other.reviving = false; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) +{SELFPARAM(); + if(autocvar_g_nades_spawn) + self.nade_refire = time + autocvar_g_spawnshieldtime; + else + self.nade_refire = time + autocvar_g_nades_nade_refire; + + if(autocvar_g_nades_bonus_client_select) + self.nade_type = self.cvar_cl_nade_type; + + self.nade_timer = 0; + + if (!self.offhand) self.offhand = OFFHAND_NADE; + + if(self.nade_spawnloc) + { + setorigin(self, self.nade_spawnloc.origin); + self.nade_spawnloc.cnt -= 1; + + if(self.nade_spawnloc.cnt <= 0) + { + remove(self.nade_spawnloc); + self.nade_spawnloc = world; + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST) +{ + if(frag_target.nade) + if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade) + toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05)); + + float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor); + + if(IS_PLAYER(frag_attacker)) + { + if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target) + nades_RemoveBonus(frag_attacker); + else if(frag_target.flagcarried) + nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium); + else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1) + { + #define SPREE_ITEM(counta,countb,center,normal,gentle) \ + case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; } + switch(frag_attacker.killcount) + { + KILL_SPREE_LIST + default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break; + } + #undef SPREE_ITEM + } + else + nades_GiveBonus(frag_attacker, killcount_bonus); + } + + nades_RemoveBonus(frag_target); + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, PlayerDamage_Calculate) +{ + if(frag_target.frozen) + if(autocvar_g_freezetag_revive_nade) + if(frag_attacker == frag_target) + if(frag_deathtype == DEATH_NADE.m_id) + if(time - frag_inflictor.toss_time <= 0.1) + { + Unfreeze(frag_target); + frag_target.health = autocvar_g_freezetag_revive_nade_health; + Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3); + frag_damage = 0; + frag_force = '0 0 0'; + Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname); + Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, MonsterDies) +{SELFPARAM(); + if(IS_PLAYER(frag_attacker)) + if(DIFF_TEAM(frag_attacker, self)) + if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) + nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, DropSpecialItems) +{ + if(frag_target.nade) + toss_nade(frag_target, '0 0 0', time + 0.05); + + return false; +} + +bool nades_RemovePlayer() +{SELFPARAM(); + nades_Clear(self); + nades_RemoveBonus(self); + return false; +} + +MUTATOR_HOOKFUNCTION(nades, MakePlayerObserver) { nades_RemovePlayer(); } +MUTATOR_HOOKFUNCTION(nades, ClientDisconnect) { nades_RemovePlayer(); } +MUTATOR_HOOKFUNCTION(nades, reset_map_global) { nades_RemovePlayer(); } + +MUTATOR_HOOKFUNCTION(nades, SpectateCopy) +{SELFPARAM(); + self.nade_timer = other.nade_timer; + self.nade_type = other.nade_type; + self.pokenade_type = other.pokenade_type; + self.bonus_nades = other.bonus_nades; + self.bonus_nade_score = other.bonus_nade_score; + self.stat_healing_orb = other.stat_healing_orb; + self.stat_healing_orb_alpha = other.stat_healing_orb_alpha; + return false; +} + +MUTATOR_HOOKFUNCTION(nades, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type"); + GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type"); + + return false; +} + +MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Nades"); + return false; +} + +MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Nades"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/module.inc b/qcsrc/common/mutators/mutator/new_toys/module.inc new file mode 100644 index 000000000..1217177c5 --- /dev/null +++ b/qcsrc/common/mutators/mutator/new_toys/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "new_toys.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/new_toys.qc new file mode 100644 index 000000000..78904ffae --- /dev/null +++ b/qcsrc/common/mutators/mutator/new_toys/new_toys.qc @@ -0,0 +1,227 @@ +#ifdef IMPLEMENTATION +/* + +CORE laser vortex lg rl cry gl elec hagar fireb hook + vaporizer porto + tuba + +NEW rifle hlac minel seeker +IDEAS OPEN flak OPEN FUN FUN FUN FUN + + + +How this mutator works: + ======================= + +When a gun tries to spawn, this mutator is called. It will provide alternate +weaponreplace lists. + +Entity: + +{ +"classname" "weapon_vortex" +"new_toys" "rifle" +} +-> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. + +{ +"classname" "weapon_vortext" +"new_toys" "vortex rifle" +} +-> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. + +{ +"classname" "weapon_vortex" +"new_toys" "vortex" +} +-> This is always a Vortex. + +If the map specifies no "new_toys" argument + +There will be two default replacements selectable: "replace all" and "replace random". +In "replace all" mode, e.g. Vortex will have the default replacement "rifle". +In "replace random" mode, Vortex will have the default replacement "vortex rifle". + +This mutator's replacements run BEFORE regular weaponreplace! + +The New Toys guns do NOT get a spawn function, so they can only ever be spawned +when this mutator is active. + +Likewise, warmup, give all, give ALL and impulse 99 will not give them unless +this mutator is active. + +Outside this mutator, they still can be spawned by: +- setting their start weapon cvar to 1 +- give weaponname +- weaponreplace +- weaponarena (but all and most weapons arena again won't include them) + +This mutator performs the default replacements on the DEFAULTS of the +start weapon selection. + +These weapons appear in the menu's priority list, BUT get a suffix +"(Mutator weapon)". + +Picking up a "new toys" weapon will not play standard weapon pickup sound, but +roflsound "New toys, new toys!" sound. + +*/ + +bool nt_IsNewToy(int w); + +REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill")) +{ + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This cannot be added at runtime\n"); + + // mark the guns as ok to use by e.g. impulse 99 + for(int i = WEP_FIRST; i <= WEP_LAST; ++i) + if(nt_IsNewToy(i)) + get_weaponinfo(i).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + for(int i = WEP_FIRST; i <= WEP_LAST; ++i) + if(nt_IsNewToy(i)) + get_weaponinfo(i).spawnflags |= WEP_FLAG_MUTATORBLOCKED; + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This cannot be removed at runtime\n"); + return -1; + } + + return 0; +} + +.string new_toys; + +float autocvar_g_new_toys_autoreplace; +bool autocvar_g_new_toys_use_pickupsound = true; +const float NT_AUTOREPLACE_NEVER = 0; +const float NT_AUTOREPLACE_ALWAYS = 1; +const float NT_AUTOREPLACE_RANDOM = 2; + +MUTATOR_HOOKFUNCTION(nt, SetModname) +{ + modname = "NewToys"; + return 0; +} + +bool nt_IsNewToy(int w) +{ + switch(w) + { + case WEP_SEEKER.m_id: + case WEP_MINE_LAYER.m_id: + case WEP_HLAC.m_id: + case WEP_RIFLE.m_id: + case WEP_SHOCKWAVE.m_id: + return true; + default: + return false; + } +} + +string nt_GetFullReplacement(string w) +{ + switch(w) + { + case "hagar": return "seeker"; + case "devastator": return "minelayer"; + case "machinegun": return "hlac"; + case "vortex": return "rifle"; + //case "shotgun": return "shockwave"; + default: return string_null; + } +} + +string nt_GetReplacement(string w, float m) +{ + if(m == NT_AUTOREPLACE_NEVER) + return w; + string s = nt_GetFullReplacement(w); + if (!s) + return w; + if(m == NT_AUTOREPLACE_RANDOM) + s = strcat(w, " ", s); + return s; +} + +MUTATOR_HOOKFUNCTION(nt, SetStartItems) +{ + // rearrange start_weapon_default + // apply those bits that are set by start_weapon_defaultmask + // same for warmup + + float i, j, k, n; + + WepSet newdefault; + WepSet warmup_newdefault; + + newdefault = '0 0 0'; + warmup_newdefault = '0 0 0'; + + for(i = WEP_FIRST; i <= WEP_LAST; ++i) + { + entity e = get_weaponinfo(i); + if(!e.weapon) + continue; + + n = tokenize_console(nt_GetReplacement(e.netname, autocvar_g_new_toys_autoreplace)); + + for(j = 0; j < n; ++j) + for(k = WEP_FIRST; k <= WEP_LAST; ++k) + if(get_weaponinfo(k).netname == argv(j)) + { + if(start_weapons & WepSet_FromWeapon(i)) + newdefault |= WepSet_FromWeapon(k); + if(warmup_start_weapons & WepSet_FromWeapon(i)) + warmup_newdefault |= WepSet_FromWeapon(k); + } + } + + newdefault &= start_weapons_defaultmask; + start_weapons &= ~start_weapons_defaultmask; + start_weapons |= newdefault; + + warmup_newdefault &= warmup_start_weapons_defaultmask; + warmup_start_weapons &= ~warmup_start_weapons_defaultmask; + warmup_start_weapons |= warmup_newdefault; + + return 0; +} + +MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) +{SELFPARAM(); + // otherwise, we do replace + if(self.new_toys) + { + // map defined replacement: + ret_string = self.new_toys; + } + else + { + // auto replacement: + ret_string = nt_GetReplacement(other.netname, autocvar_g_new_toys_autoreplace); + } + + // apply regular weaponreplace + ret_string = W_Apply_Weaponreplace(ret_string); + + return 0; +} + +MUTATOR_HOOKFUNCTION(nt, FilterItem) +{SELFPARAM(); + if(nt_IsNewToy(self.weapon) && autocvar_g_new_toys_use_pickupsound) { + self.item_pickupsound = string_null; + self.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS; + } + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/nix/module.inc b/qcsrc/common/mutators/mutator/nix/module.inc new file mode 100644 index 000000000..fb4f9ec2b --- /dev/null +++ b/qcsrc/common/mutators/mutator/nix/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "nix.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/nix/nix.qc b/qcsrc/common/mutators/mutator/nix/nix.qc new file mode 100644 index 000000000..1a8089ca6 --- /dev/null +++ b/qcsrc/common/mutators/mutator/nix/nix.qc @@ -0,0 +1,291 @@ +#ifdef IMPLEMENTATION +int autocvar_g_balance_nix_ammo_cells; +int autocvar_g_balance_nix_ammo_plasma; +int autocvar_g_balance_nix_ammo_fuel; +int autocvar_g_balance_nix_ammo_nails; +int autocvar_g_balance_nix_ammo_rockets; +int autocvar_g_balance_nix_ammo_shells; +int autocvar_g_balance_nix_ammoincr_cells; +int autocvar_g_balance_nix_ammoincr_plasma; +int autocvar_g_balance_nix_ammoincr_fuel; +int autocvar_g_balance_nix_ammoincr_nails; +int autocvar_g_balance_nix_ammoincr_rockets; +int autocvar_g_balance_nix_ammoincr_shells; +float autocvar_g_balance_nix_incrtime; +float autocvar_g_balance_nix_roundtime; +bool autocvar_g_nix_with_healtharmor; +bool autocvar_g_nix_with_blaster; +bool autocvar_g_nix_with_powerups; +int autocvar_g_pickup_cells_max; +int autocvar_g_pickup_plasma_max; +int autocvar_g_pickup_fuel_max; +int autocvar_g_pickup_nails_max; +int autocvar_g_pickup_rockets_max; +int autocvar_g_pickup_shells_max; + +float g_nix_with_blaster; +// WEAPONTODO +int nix_weapon; +float nix_nextchange; +float nix_nextweapon; +.float nix_lastchange_id; +.float nix_lastinfotime; +.float nix_nextincr; + +bool NIX_CanChooseWeapon(int wpn); + +REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill")) +{ + MUTATOR_ONADD + { + g_nix_with_blaster = autocvar_g_nix_with_blaster; + + nix_nextchange = 0; + nix_nextweapon = 0; + + for (int i = WEP_FIRST; i <= WEP_LAST; ++i) + if (NIX_CanChooseWeapon(i)) { + Weapon w = get_weaponinfo(i); + w.wr_init(w); + } + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + // as the PlayerSpawn hook will no longer run, NIX is turned off by this! + entity e; + FOR_EACH_PLAYER(e) if(e.deadflag == DEAD_NO) + { + e.ammo_cells = start_ammo_cells; + e.ammo_plasma = start_ammo_plasma; + e.ammo_shells = start_ammo_shells; + e.ammo_nails = start_ammo_nails; + e.ammo_rockets = start_ammo_rockets; + e.ammo_fuel = start_ammo_fuel; + e.weapons = start_weapons; + if(!client_hasweapon(e, e.weapon, true, false)) + e.switchweapon = w_getbestweapon(self); + } + } + + return 0; +} + +bool NIX_CanChooseWeapon(int wpn) +{ + entity e = get_weaponinfo(wpn); + if(!e.weapon) // skip dummies + return false; + if(g_weaponarena) + { + if(!(g_weaponarena_weapons & WepSet_FromWeapon(wpn))) + return false; + } + else + { + if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster) + return false; + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + return false; + if (!(e.spawnflags & WEP_FLAG_NORMAL)) + return false; + } + return true; +} +void NIX_ChooseNextWeapon() +{ + float j; + RandomSelection_Init(); + for(j = WEP_FIRST; j <= WEP_LAST; ++j) + if(NIX_CanChooseWeapon(j)) + RandomSelection_Add(world, j, string_null, 1, (j != nix_weapon)); + nix_nextweapon = RandomSelection_chosen_float; +} + +void NIX_GiveCurrentWeapon() +{SELFPARAM(); + float dt; + + if(!nix_nextweapon) + NIX_ChooseNextWeapon(); + + dt = ceil(nix_nextchange - time); + + if(dt <= 0) + { + nix_weapon = nix_nextweapon; + nix_nextweapon = 0; + if (!nix_nextchange) // no round played yet? + nix_nextchange = time; // start the first round now! + else + nix_nextchange = time + autocvar_g_balance_nix_roundtime; + // Weapon w = get_weaponinfo(nix_weapon); + // w.wr_init(w); // forget it, too slow + } + + // get weapon info + entity e = get_weaponinfo(nix_weapon); + + if(nix_nextchange != self.nix_lastchange_id) // this shall only be called once per round! + { + self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = self.ammo_plasma = self.ammo_fuel = 0; + + if(self.items & IT_UNLIMITED_WEAPON_AMMO) + { + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells = autocvar_g_pickup_shells_max; break; + case ammo_nails: self.ammo_nails = autocvar_g_pickup_nails_max; break; + case ammo_rockets: self.ammo_rockets = autocvar_g_pickup_rockets_max; break; + case ammo_cells: self.ammo_cells = autocvar_g_pickup_cells_max; break; + case ammo_plasma: self.ammo_plasma = autocvar_g_pickup_plasma_max; break; + case ammo_fuel: self.ammo_fuel = autocvar_g_pickup_fuel_max; break; + } + } + else + { + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; + case ammo_nails: self.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; + case ammo_rockets: self.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; + case ammo_cells: self.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; + case ammo_plasma: self.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; + case ammo_fuel: self.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; + } + } + + self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; + if(dt >= 1 && dt <= 5) + self.nix_lastinfotime = -42; + else + Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); + + Weapon w = get_weaponinfo(nix_weapon); + w.wr_resetplayer(w); + + // all weapons must be fully loaded when we spawn + if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars + self.(weapon_load[nix_weapon]) = e.reloading_ammo; + + // vortex too + if(WEP_CVAR(vortex, charge)) + { + if(WEP_CVAR_SEC(vortex, chargepool)) + self.vortex_chargepool_ammo = 1; + self.vortex_charge = WEP_CVAR(vortex, charge_start); + } + + // set last change info + self.nix_lastchange_id = nix_nextchange; + } + if(self.nix_lastinfotime != dt) + { + self.nix_lastinfotime = dt; // initial value 0 should count as "not seen" + if(dt >= 1 && dt <= 5) + Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt); + } + + if(!(self.items & IT_UNLIMITED_WEAPON_AMMO) && time > self.nix_nextincr) + { + switch(e.ammo_field) + { + case ammo_shells: self.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; + case ammo_nails: self.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; + case ammo_rockets: self.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; + case ammo_cells: self.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; + case ammo_plasma: self.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; + case ammo_fuel: self.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; + } + + self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; + } + + self.weapons = '0 0 0'; + if(g_nix_with_blaster) + self.weapons |= WEPSET(BLASTER); + self.weapons |= WepSet_FromWeapon(nix_weapon); + + if(self.switchweapon != nix_weapon) + if(!client_hasweapon(self, self.switchweapon, true, false)) + if(client_hasweapon(self, nix_weapon, true, false)) + W_SwitchWeapon(nix_weapon); +} + +MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon) +{ + return 1; // no throwing in NIX +} + +MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":NIX"); + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", NIX"); + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, FilterItem) +{SELFPARAM(); + switch (self.items) + { + case ITEM_HealthSmall.m_itemid: + case ITEM_HealthMedium.m_itemid: + case ITEM_HealthLarge.m_itemid: + case ITEM_HealthMega.m_itemid: + case ITEM_ArmorSmall.m_itemid: + case ITEM_ArmorMedium.m_itemid: + case ITEM_ArmorLarge.m_itemid: + case ITEM_ArmorMega.m_itemid: + if (autocvar_g_nix_with_healtharmor) + return 0; + break; + case ITEM_Strength.m_itemid: + case ITEM_Shield.m_itemid: + if (autocvar_g_nix_with_powerups) + return 0; + break; + } + + return 1; // delete all other items +} + +MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn) +{SELFPARAM(); + if(self.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo) + return 1; + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) +{SELFPARAM(); + if(!intermission_running) + if(self.deadflag == DEAD_NO) + if(IS_PLAYER(self)) + NIX_GiveCurrentWeapon(); + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, PlayerSpawn) +{SELFPARAM(); + self.nix_lastchange_id = -1; + NIX_GiveCurrentWeapon(); // overrides the weapons you got when spawning + self.items |= IT_UNLIMITED_SUPERWEAPONS; + return 0; +} + +MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST) +{ + modname = "NIX"; + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/module.inc b/qcsrc/common/mutators/mutator/physical_items/module.inc new file mode 100644 index 000000000..7ed9b039b --- /dev/null +++ b/qcsrc/common/mutators/mutator/physical_items/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "physical_items.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/physical_items.qc new file mode 100644 index 000000000..6fbe77bce --- /dev/null +++ b/qcsrc/common/mutators/mutator/physical_items/physical_items.qc @@ -0,0 +1,143 @@ +#ifdef IMPLEMENTATION +int autocvar_g_physical_items; +float autocvar_g_physical_items_damageforcescale; +float autocvar_g_physical_items_reset; + +REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) +{ + // check if we have a physics engine + MUTATOR_ONADD + { + if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) + { + LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n"); + return -1; + } + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This cannot be removed at runtime\n"); + return -1; + } + + return 0; +} + +.vector spawn_origin, spawn_angles; + +void physical_item_think() +{SELFPARAM(); + self.nextthink = time; + + self.alpha = self.owner.alpha; // apply fading and ghosting + + if(!self.cnt) // map item, not dropped + { + // copy ghost item properties + self.colormap = self.owner.colormap; + self.colormod = self.owner.colormod; + self.glowmod = self.owner.glowmod; + + // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there + if(autocvar_g_physical_items_reset) + { + if(self.owner.wait > time) // awaiting respawn + { + setorigin(self, self.spawn_origin); + self.angles = self.spawn_angles; + self.solid = SOLID_NOT; + self.alpha = -1; + self.movetype = MOVETYPE_NONE; + } + else + { + self.alpha = 1; + self.solid = SOLID_CORPSE; + self.movetype = MOVETYPE_PHYSICS; + } + } + } + + if(!self.owner.modelindex) + remove(self); // the real item is gone, remove this +} + +void physical_item_touch() +{SELFPARAM(); + if(!self.cnt) // not for dropped items + if (ITEM_TOUCH_NEEDKILL()) + { + setorigin(self, self.spawn_origin); + self.angles = self.spawn_angles; + } +} + +void physical_item_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{SELFPARAM(); + if(!self.cnt) // not for dropped items + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + setorigin(self, self.spawn_origin); + self.angles = self.spawn_angles; + } +} + +MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) +{SELFPARAM(); + if(self.owner == world && autocvar_g_physical_items <= 1) + return false; + if (self.spawnflags & 1) // floating item + return false; + + // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. + // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. + entity wep; + wep = spawn(); + _setmodel(wep, self.model); + setsize(wep, self.mins, self.maxs); + setorigin(wep, self.origin); + wep.angles = self.angles; + wep.velocity = self.velocity; + + wep.owner = self; + wep.solid = SOLID_CORPSE; + wep.movetype = MOVETYPE_PHYSICS; + wep.takedamage = DAMAGE_AIM; + wep.effects |= EF_NOMODELFLAGS; // disable the spinning + wep.colormap = self.owner.colormap; + wep.glowmod = self.owner.glowmod; + wep.damageforcescale = autocvar_g_physical_items_damageforcescale; + wep.dphitcontentsmask = self.dphitcontentsmask; + wep.cnt = (self.owner != world); + + wep.think = physical_item_think; + wep.nextthink = time; + wep.touch = physical_item_touch; + wep.event_damage = physical_item_damage; + + if(!wep.cnt) + { + // fix the spawn origin + setorigin(wep, wep.origin + '0 0 1'); + entity oldself; + oldself = self; + WITH(entity, self, wep, builtin_droptofloor()); + } + + wep.spawn_origin = wep.origin; + wep.spawn_angles = self.angles; + + self.effects |= EF_NODRAW; // hide the original weapon + self.movetype = MOVETYPE_FOLLOW; + self.aiment = wep; // attach the original weapon + self.SendEntity = func_null; + + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/pinata/module.inc b/qcsrc/common/mutators/mutator/pinata/module.inc new file mode 100644 index 000000000..4e2296686 --- /dev/null +++ b/qcsrc/common/mutators/mutator/pinata/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "pinata.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/pinata/pinata.qc b/qcsrc/common/mutators/mutator/pinata/pinata.qc new file mode 100644 index 000000000..a806b2958 --- /dev/null +++ b/qcsrc/common/mutators/mutator/pinata/pinata.qc @@ -0,0 +1,27 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill")); + +MUTATOR_HOOKFUNCTION(pinata, PlayerDies) +{SELFPARAM(); + for(int j = WEP_FIRST; j <= WEP_LAST; ++j) + if(self.weapons & WepSet_FromWeapon(j)) + if(self.switchweapon != j) + if(W_IsWeaponThrowable(j)) + W_ThrowNewWeapon(self, j, false, self.origin + (self.mins + self.maxs) * 0.5, randomvec() * 175 + '0 0 325'); + + return true; +} + +MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Pinata"); + return false; +} + +MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Piñata"); + return false; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/module.inc b/qcsrc/common/mutators/mutator/random_gravity/module.inc new file mode 100644 index 000000000..91baa4310 --- /dev/null +++ b/qcsrc/common/mutators/mutator/random_gravity/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "random_gravity.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc b/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc new file mode 100644 index 000000000..80abfe343 --- /dev/null +++ b/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc @@ -0,0 +1,56 @@ +#ifdef IMPLEMENTATION +// Random Gravity +// +// Mutator by Mario +// Inspired by Player 2 + +float autocvar_g_random_gravity_negative_chance; +float autocvar_g_random_gravity_min; +float autocvar_g_random_gravity_max; +float autocvar_g_random_gravity_positive; +float autocvar_g_random_gravity_negative; +float autocvar_g_random_gravity_delay; + +REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity")) +{ + MUTATOR_ONADD + { + cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end + } + + return false; +} + +float gravity_delay; + +MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame) +{ + if(gameover || !cvar("g_random_gravity")) return false; + if(time < gravity_delay) return false; + if(time < game_starttime) return false; + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false; + + if(random() >= autocvar_g_random_gravity_negative_chance) + cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max))); + else + cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max))); + + gravity_delay = time + autocvar_g_random_gravity_delay; + + LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity), "\n"); + + return false; +} + +MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":RandomGravity"); + return 0; +} + +MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Random gravity"); + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/module.inc b/qcsrc/common/mutators/mutator/rocketflying/module.inc new file mode 100644 index 000000000..7036bc49d --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketflying/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "rocketflying.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc new file mode 100644 index 000000000..f23d9918b --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc @@ -0,0 +1,25 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying")); + +MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile) +{ + if(other.classname == "rocket" || other.classname == "mine") + { + // kill detonate delay of rockets + other.spawnshieldtime = time; + } + return 0; +} + +MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":RocketFlying"); + return 0; +} + +MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Rocket Flying"); + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/module.inc b/qcsrc/common/mutators/mutator/rocketminsta/module.inc new file mode 100644 index 000000000..b7d02a9f6 --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketminsta/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "rocketminsta.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc new file mode 100644 index 000000000..6ddd6db94 --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc @@ -0,0 +1,35 @@ +#ifdef IMPLEMENTATION +#include "../../../deathtypes/all.qh" +#include "../../../../server/round_handler.qh" + +REGISTER_MUTATOR(rm, cvar("g_instagib")); + +MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate) +{ + // we do it this way, so rm can be toggled during the match + if(!autocvar_g_rm) { return false; } + + if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) + if(frag_attacker == frag_target || frag_target.classname == "nade") + frag_damage = 0; + + if(autocvar_g_rm_laser) + if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted())) + frag_damage = 0; + + return false; +} + +MUTATOR_HOOKFUNCTION(rm, PlayerDies) +{ + // we do it this way, so rm can be toggled during the match + if(!autocvar_g_rm) { return false; } + + if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + frag_damage = 1000; // always gib if it was a vaporizer death + + return false; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/module.inc b/qcsrc/common/mutators/mutator/sandbox/module.inc new file mode 100644 index 000000000..0715d5b40 --- /dev/null +++ b/qcsrc/common/mutators/mutator/sandbox/module.inc @@ -0,0 +1,4 @@ +#ifdef SVQC +#include "sandbox.qc" + +#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sandbox.qc new file mode 100644 index 000000000..e1decc8a0 --- /dev/null +++ b/qcsrc/common/mutators/mutator/sandbox/sandbox.qc @@ -0,0 +1,842 @@ +#ifdef IMPLEMENTATION +int autocvar_g_sandbox_info; +bool autocvar_g_sandbox_readonly; +string autocvar_g_sandbox_storage_name; +float autocvar_g_sandbox_storage_autosave; +bool autocvar_g_sandbox_storage_autoload; +float autocvar_g_sandbox_editor_flood; +int autocvar_g_sandbox_editor_maxobjects; +int autocvar_g_sandbox_editor_free; +float autocvar_g_sandbox_editor_distance_spawn; +float autocvar_g_sandbox_editor_distance_edit; +float autocvar_g_sandbox_object_scale_min; +float autocvar_g_sandbox_object_scale_max; +float autocvar_g_sandbox_object_material_velocity_min; +float autocvar_g_sandbox_object_material_velocity_factor; + +float autosave_time; +void sandbox_Database_Load(); + +REGISTER_MUTATOR(sandbox, cvar("g_sandbox")) +{ + MUTATOR_ONADD + { + autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame + if(autocvar_g_sandbox_storage_autoload) + sandbox_Database_Load(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + // nothing to remove + } + + return false; +} + +const float MAX_STORAGE_ATTACHMENTS = 16; +float object_count; +.float object_flood; +.entity object_attach; +.string material; + +.float touch_timer; +void sandbox_ObjectFunction_Touch() +{SELFPARAM(); + // apply material impact effects + + if(!self.material) + return; + if(self.touch_timer > time) + return; // don't execute each frame + self.touch_timer = time + 0.1; + + // make particle count and sound volume depend on impact speed + float intensity; + intensity = vlen(self.velocity) + vlen(other.velocity); + if(intensity) // avoid divisions by 0 + intensity /= 2; // average the two velocities + if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min)) + return; // impact not strong enough to do anything + // now offset intensity and apply it to the effects + intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity + intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); + + _sound(self, CH_TRIGGER, strcat("object/impact_", self.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM); + Send_Effect_(strcat("impact_", self.material), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 +} + +void sandbox_ObjectFunction_Think() +{SELFPARAM(); + entity e; + + // decide if and how this object can be grabbed + if(autocvar_g_sandbox_readonly) + self.grab = 0; // no grabbing + else if(autocvar_g_sandbox_editor_free < 2 && self.crypto_idfp) + self.grab = 1; // owner only + else + self.grab = 3; // anyone + + // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server). + // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this, + // since if the owning player disconnects, the object's owner should also be reset. + FOR_EACH_REALPLAYER(e) // bots can't have objects + { + if(self.crypto_idfp == e.crypto_idfp) + { + self.realowner = e; + break; + } + self.realowner = world; + } + + self.nextthink = time; + + CSQCMODEL_AUTOUPDATE(self); +} + +.float old_solid, old_movetype; +entity sandbox_ObjectEdit_Get(float permissions) +{SELFPARAM(); + // Returns the traced entity if the player can edit it, and world if not. + // If permissions if false, the object is returned regardless of editing rights. + // Attached objects are SOLID_NOT and do not get traced. + + crosshair_trace_plusvisibletriggers(self); + if(vlen(self.origin - trace_ent.origin) > autocvar_g_sandbox_editor_distance_edit) + return world; // out of trace range + if(trace_ent.classname != "object") + return world; // entity is not an object + if(!permissions) + return trace_ent; // don't check permissions, anyone can edit this object + if(trace_ent.crypto_idfp == "") + return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it + if (!(trace_ent.realowner != self && autocvar_g_sandbox_editor_free < 2)) + return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server + return world; +} + +void sandbox_ObjectEdit_Scale(entity e, float f) +{ + e.scale = f; + if(e.scale) + { + e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max); + _setmodel(e, e.model); // reset mins and maxs based on mesh + setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size + } +} + +void sandbox_ObjectAttach_Remove(entity e); +void sandbox_ObjectAttach_Set(entity e, entity parent, string s) +{ + // attaches e to parent on string s + + // we can't attach to an attachment, for obvious reasons + sandbox_ObjectAttach_Remove(e); + + e.old_solid = e.solid; // persist solidity + e.old_movetype = e.movetype; // persist physics + e.movetype = MOVETYPE_FOLLOW; + e.solid = SOLID_NOT; + e.takedamage = DAMAGE_NO; + + setattachment(e, parent, s); + e.owner = parent; +} + +void sandbox_ObjectAttach_Remove(entity e) +{ + // detaches any object attached to e + + entity head; + for(head = world; (head = find(head, classname, "object")); ) + { + if(head.owner == e) + { + vector org; + org = gettaginfo(head, 0); + setattachment(head, world, ""); + head.owner = world; + + // objects change origin and angles when detached, so apply previous position + setorigin(head, org); + head.angles = e.angles; // don't allow detached objects to spin or roll + + head.solid = head.old_solid; // restore persisted solidity + head.movetype = head.old_movetype; // restore persisted physics + head.takedamage = DAMAGE_AIM; + } + } +} + +entity sandbox_ObjectSpawn(float database) +{SELFPARAM(); + // spawn a new object with default properties + + entity e = new(object); + e.takedamage = DAMAGE_AIM; + e.damageforcescale = 1; + e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly + e.movetype = MOVETYPE_TOSS; + e.frame = 0; + e.skin = 0; + e.material = string_null; + e.touch = sandbox_ObjectFunction_Touch; + e.think = sandbox_ObjectFunction_Think; + e.nextthink = time; + //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects? + + if(!database) + { + // set the object's owner via player UID + // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone + if(self.crypto_idfp != "") + e.crypto_idfp = strzone(self.crypto_idfp); + else + print_to(self, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!"); + + // set public object information + e.netname = strzone(self.netname); // name of the owner + e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time + e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time + + // set origin and direction based on player position and view angle + makevectors(self.v_angle); + WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, self); + setorigin(e, trace_endpos); + e.angles_y = self.v_angle.y; + } + + WITH(entity, self, e, CSQCMODEL_AUTOINIT(e)); + + object_count += 1; + return e; +} + +void sandbox_ObjectRemove(entity e) +{ + sandbox_ObjectAttach_Remove(e); // detach child objects + + // if the object being removed has been selected for attachment by a player, unset it + entity head; + FOR_EACH_REALPLAYER(head) // bots can't have objects + { + if(head.object_attach == e) + head.object_attach = world; + } + + if(e.material) { strunzone(e.material); e.material = string_null; } + if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; } + if(e.netname) { strunzone(e.netname); e.netname = string_null; } + if(e.message) { strunzone(e.message); e.message = string_null; } + if(e.message2) { strunzone(e.message2); e.message2 = string_null; } + remove(e); + e = world; + + object_count -= 1; +} + +string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global + +string sandbox_ObjectPort_Save(entity e, float database) +{ + // save object properties, and return them as a string + float i = 0; + string s; + entity head; + + for(head = world; (head = find(head, classname, "object")); ) + { + // the main object needs to be first in the array [0] with attached objects following + float slot, physics, solidity; + if(head == e) // this is the main object, place it first + { + slot = 0; + solidity = head.solid; // applied solidity is normal solidity for children + physics = head.movetype; // applied physics are normal physics for parents + } + else if(head.owner == e) // child object, list them in order + { + i += 1; // children start from 1 + slot = i; + solidity = head.old_solid; // persisted solidity is normal solidity for children + physics = head.old_movetype; // persisted physics are normal physics for children + gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below + } + else + continue; + + // ---------------- OBJECT PROPERTY STORAGE: SAVE ---------------- + if(slot) + { + // properties stored only for child objects + if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + } + else + { + // properties stored only for parent objects + if(database) + { + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " "); + } + } + // properties stored for all objects + port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" "); + port_string[slot] = strcat(port_string[slot], ftos(head.skin), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.frame), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.scale), " "); + port_string[slot] = strcat(port_string[slot], ftos(solidity), " "); + port_string[slot] = strcat(port_string[slot], ftos(physics), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " "); + if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + if(database) + { + // properties stored only for the database + if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none + port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" "); + port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" "); + port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" "); + } + } + + // now apply the array to a simple string, with the ; symbol separating objects + s = ""; + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + { + if(port_string[i]) + s = strcat(s, port_string[i], "; "); + port_string[i] = string_null; // fully clear the string + } + + return s; +} + +entity sandbox_ObjectPort_Load(string s, float database) +{ + // load object properties, and spawn a new object with them + float n, i; + entity e = world, parent = world; + + // separate objects between the ; symbols + n = tokenizebyseparator(s, "; "); + for(i = 0; i < n; ++i) + port_string[i] = argv(i); + + // now separate and apply the properties of each object + for(i = 0; i < n; ++i) + { + float argv_num; + string tagname = string_null; + argv_num = 0; + tokenize_console(port_string[i]); + e = sandbox_ObjectSpawn(database); + + // ---------------- OBJECT PROPERTY STORAGE: LOAD ---------------- + if(i) + { + // properties stored only for child objects + if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; + } + else + { + // properties stored only for parent objects + if(database) + { + setorigin(e, stov(argv(argv_num))); ++argv_num; + e.angles = stov(argv(argv_num)); ++argv_num; + } + parent = e; // mark parent objects as such + } + // properties stored for all objects + _setmodel(e, argv(argv_num)); ++argv_num; + e.skin = stof(argv(argv_num)); ++argv_num; + e.alpha = stof(argv(argv_num)); ++argv_num; + e.colormod = stov(argv(argv_num)); ++argv_num; + e.glowmod = stov(argv(argv_num)); ++argv_num; + e.frame = stof(argv(argv_num)); ++argv_num; + sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num; + e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num; + e.movetype = e.old_movetype = stof(argv(argv_num)); ++argv_num; + e.damageforcescale = stof(argv(argv_num)); ++argv_num; + if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num; + if(database) + { + // properties stored only for the database + if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num; + if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num; + if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num; + if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num; + } + + // attach last + if(i) + sandbox_ObjectAttach_Set(e, parent, tagname); + } + + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + port_string[i] = string_null; // fully clear the string + + return e; +} + +void sandbox_Database_Save() +{ + // saves all objects to the database file + entity head; + string file_name; + float file_get; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_WRITE); + fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"))); + fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); + + for(head = world; (head = find(head, classname, "object")); ) + { + // attached objects are persisted separately, ignore them here + if(head.owner != world) + continue; + + // use a line of text for each object, listing all properties + fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n")); + } + fclose(file_get); +} + +void sandbox_Database_Load() +{ + // loads all objects from the database file + string file_read, file_name; + float file_get, i; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_READ); + if(file_get < 0) + { + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); + } + else + { + for (;;) + { + file_read = fgets(file_get); + if(file_read == "") + break; + if(substring(file_read, 0, 2) == "//") + continue; + if(substring(file_read, 0, 1) == "#") + continue; + + entity e; + e = sandbox_ObjectPort_Load(file_read, true); + + if(e.material) + { + // since objects are being loaded for the first time, precache material sounds for each + for (i = 1; i <= 5; i++) // 5 sounds in total + precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); + } + } + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); + } + fclose(file_get); +} + +MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) +{SELFPARAM(); + if(MUTATOR_RETURNVALUE) // command was already handled? + return false; + if(cmd_name == "g_sandbox") + { + if(autocvar_g_sandbox_readonly) + { + print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used"); + return true; + } + if(cmd_argc < 2) + { + print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'"); + return true; + } + + switch(argv(1)) + { + entity e; + float i; + string s; + + // ---------------- COMMAND: HELP ---------------- + case "help": + print_to(self, "You can use the following sandbox commands:"); + print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); + print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); + print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original"); + print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar"); + print_to(self, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\""); + print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); + print_to(self, "^3get ^7- selects the object you are facing as the object to be attached"); + print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone"); + print_to(self, "^3remove ^7- detaches all objects from the object you are facing"); + print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects"); + print_to(self, "^3skin value ^7- changes the skin of the object"); + print_to(self, "^3alpha value ^7- sets object transparency"); + print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color"); + print_to(self, "^3glowmod \"value_x value_y value_z\" ^7- glow object color"); + print_to(self, "^3frame value ^7- object animation frame, for self-animated models"); + print_to(self, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size"); + print_to(self, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid"); + print_to(self, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); + print_to(self, "^3force value ^7- amount of force applied to objects that are shot"); + print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); + print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); + print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object"); + print_to(self, "^3object ^7- prints general information about the object, such as owner and creation / editing date"); + print_to(self, "^3mesh ^7- prints information about the object's mesh, including skeletal bones"); + print_to(self, "^3attachments ^7- prints information about the object's attachments"); + print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); + return true; + + // ---------------- COMMAND: OBJECT, SPAWN ---------------- + case "object_spawn": + if(time < self.object_flood) + { + print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); + return true; + } + self.object_flood = time + autocvar_g_sandbox_editor_flood; + if(object_count >= autocvar_g_sandbox_editor_maxobjects) + { + print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); + return true; + } + if(cmd_argc < 3) + { + print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command"); + return true; + } + if (!(fexists(argv(2)))) + { + print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); + return true; + } + + e = sandbox_ObjectSpawn(false); + _setmodel(e, argv(2)); + + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); + return true; + + // ---------------- COMMAND: OBJECT, REMOVE ---------------- + case "object_remove": + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " removed an object at origin ^3", vtos(e.origin), "\n")); + sandbox_ObjectRemove(e); + return true; + } + + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- + case "object_duplicate": + switch(argv(2)) + { + case "copy": + // copies customizable properties of the selected object to the clipboard cvar + e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? + if(e != world) + { + s = sandbox_ObjectPort_Save(e, false); + s = strreplace("\"", "\\\"", s); + stuffcmd(self, strcat("set ", argv(3), " \"", s, "\"")); + + print_to(self, "^2SANDBOX - INFO: ^7Object copied to clipboard"); + return true; + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); + return true; + + case "paste": + // spawns a new object using the properties in the player's clipboard cvar + if(time < self.object_flood) + { + print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); + return true; + } + self.object_flood = time + autocvar_g_sandbox_editor_flood; + if(argv(3) == "") // no object in clipboard + { + print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it"); + return true; + } + if(object_count >= autocvar_g_sandbox_editor_maxobjects) + { + print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); + return true; + } + e = sandbox_ObjectPort_Load(argv(3), false); + + print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully"); + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " pasted an object at origin ^3", vtos(e.origin), "\n")); + return true; + } + return true; + + // ---------------- COMMAND: OBJECT, ATTACH ---------------- + case "object_attach": + switch(argv(2)) + { + case "get": + // select e as the object as meant to be attached + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + self.object_attach = e; + print_to(self, "^2SANDBOX - INFO: ^7Object selected for attachment"); + return true; + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over"); + return true; + case "set": + if(self.object_attach == world) + { + print_to(self, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first."); + return true; + } + + // attaches the previously selected object to e + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + sandbox_ObjectAttach_Set(self.object_attach, e, argv(3)); + self.object_attach = world; // object was attached, no longer keep it scheduled for attachment + print_to(self, "^2SANDBOX - INFO: ^7Object attached successfully"); + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " attached objects at origin ^3", vtos(e.origin), "\n")); + return true; + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over"); + return true; + case "remove": + // removes e if it was attached + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + sandbox_ObjectAttach_Remove(e); + print_to(self, "^2SANDBOX - INFO: ^7Child objects detached successfully"); + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " detached objects at origin ^3", vtos(e.origin), "\n")); + return true; + } + print_to(self, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over"); + return true; + } + return true; + + // ---------------- COMMAND: OBJECT, EDIT ---------------- + case "object_edit": + if(argv(2) == "") + { + print_to(self, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit"); + return true; + } + + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + switch(argv(2)) + { + case "skin": + e.skin = stof(argv(3)); + break; + case "alpha": + e.alpha = stof(argv(3)); + break; + case "color_main": + e.colormod = stov(argv(3)); + break; + case "color_glow": + e.glowmod = stov(argv(3)); + break; + case "frame": + e.frame = stof(argv(3)); + break; + case "scale": + sandbox_ObjectEdit_Scale(e, stof(argv(3))); + break; + case "solidity": + switch(argv(3)) + { + case "0": // non-solid + e.solid = SOLID_TRIGGER; + break; + case "1": // solid + e.solid = SOLID_BBOX; + break; + default: + break; + } + case "physics": + switch(argv(3)) + { + case "0": // static + e.movetype = MOVETYPE_NONE; + break; + case "1": // movable + e.movetype = MOVETYPE_TOSS; + break; + case "2": // physical + e.movetype = MOVETYPE_PHYSICS; + break; + default: + break; + } + break; + case "force": + e.damageforcescale = stof(argv(3)); + break; + case "material": + if(e.material) strunzone(e.material); + if(argv(3)) + { + for (i = 1; i <= 5; i++) // precache material sounds, 5 in total + precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav")); + e.material = strzone(argv(3)); + } + else + e.material = string_null; // no material + break; + default: + print_to(self, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'"); + return true; + } + + // update last editing time + if(e.message2) strunzone(e.message2); + e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); + + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n")); + return true; + } + + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, CLAIM ---------------- + case "object_claim": + // if the player can edit an object but is not its owner, this can be used to claim that object + if(self.crypto_idfp == "") + { + print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); + return true; + } + e = sandbox_ObjectEdit_Get(true); + if(e != world) + { + // update the owner's name + // Do this before checking if you're already the owner and skipping if such, so we + // also update the player's nickname if he changed it (but has the same player UID) + if(e.netname != self.netname) + { + if(e.netname) strunzone(e.netname); + e.netname = strzone(self.netname); + print_to(self, "^2SANDBOX - INFO: ^7Object owner name updated"); + } + + if(e.crypto_idfp == self.crypto_idfp) + { + print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); + return true; + } + + if(e.crypto_idfp) strunzone(e.crypto_idfp); + e.crypto_idfp = strzone(self.crypto_idfp); + + print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully"); + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, INFO ---------------- + case "object_info": + // prints public information about the object to the player + e = sandbox_ObjectEdit_Get(false); + if(e != world) + { + switch(argv(2)) + { + case "object": + print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); + return true; + case "mesh": + s = ""; + FOR_EACH_TAG(e) + s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); + print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); + return true; + case "attachments": + // this should show the same info as 'mesh' but for attachments + s = ""; + entity head; + i = 0; + for(head = world; (head = find(head, classname, "object")); ) + { + if(head.owner == e) + { + ++i; // start from 1 + gettaginfo(e, head.tag_index); + s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame)); + s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", "); + } + } + if(i) // object contains attachments + print_to(self, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s)); + else + print_to(self, "^2SANDBOX - INFO: ^7Object contains no attachments"); + return true; + } + } + print_to(self, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object"); + return true; + + // ---------------- COMMAND: DEFAULT ---------------- + default: + print_to(self, "Invalid command. For usage information, type 'sandbox help'"); + return true; + } + } + return false; +} + +MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame) +{ + if(!autocvar_g_sandbox_storage_autosave) + return false; + if(time < autosave_time) + return false; + autosave_time = time + autocvar_g_sandbox_storage_autosave; + + sandbox_Database_Save(); + + return true; +} +#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc b/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc new file mode 100644 index 000000000..f88a768a2 --- /dev/null +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "spawn_near_teammate.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc new file mode 100644 index 000000000..cd78b4082 --- /dev/null +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc @@ -0,0 +1,175 @@ +#ifdef IMPLEMENTATION + +float autocvar_g_spawn_near_teammate_distance; +int autocvar_g_spawn_near_teammate_ignore_spawnpoint; +float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; +float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; +int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; +bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; + +REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate") && teamplay); + +.entity msnt_lookat; + +.float msnt_timer; +.vector msnt_deathloc; + +.float cvar_cl_spawn_near_teammate; + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score) +{SELFPARAM(); + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate)) + return 0; + + entity p; + + spawn_spot.msnt_lookat = world; + + if(!teamplay) + return 0; + + RandomSelection_Init(); + FOR_EACH_PLAYER(p) if(p != self) if(p.team == self.team) if(!p.deadflag) + { + float l = vlen(spawn_spot.origin - p.origin); + if(l > autocvar_g_spawn_near_teammate_distance) + continue; + if(l < 48) + continue; + if(!checkpvs(spawn_spot.origin, p)) + continue; + RandomSelection_Add(p, 0, string_null, 1, 1); + } + + if(RandomSelection_chosen_ent) + { + spawn_spot.msnt_lookat = RandomSelection_chosen_ent; + spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; + } + else if(self.team == spawn_spot.team) + spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate + + return 0; +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) +{SELFPARAM(); + // Note: when entering this, fixangle is already set. + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate)) + { + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death) + self.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; + + entity team_mate, best_mate = world; + vector best_spot = '0 0 0'; + float pc = 0, best_dist = 0, dist = 0; + FOR_EACH_PLAYER(team_mate) + { + if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && team_mate.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0) + if(team_mate.deadflag == DEAD_NO) + if(team_mate.msnt_timer < time) + if(SAME_TEAM(self, team_mate)) + if(time > team_mate.spawnshieldtime) // spawn shielding + if(team_mate.frozen == 0) + if(team_mate != self) + { + tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate); + if(trace_fraction != 1.0) + if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) + { + pc = pointcontents(trace_endpos + '0 0 1'); + if(pc == CONTENT_EMPTY) + { + if(vlen(team_mate.velocity) > 5) + fixedmakevectors(vectoangles(team_mate.velocity)); + else + fixedmakevectors(team_mate.angles); + + for(pc = 0; pc != 5; ++pc) // test 5 diffrent spots close to mate + { + switch(pc) + { + case 0: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 128, MOVE_NORMAL, team_mate); + break; + case 1: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 128 , MOVE_NORMAL, team_mate); + break; + case 2: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate); + break; + case 3: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate); + break; + case 4: + tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_forward * 128, MOVE_NORMAL, team_mate); + break; + } + + if(trace_fraction == 1.0) + { + traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NORMAL, team_mate); + if(trace_fraction != 1.0) + { + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) + { + dist = vlen(trace_endpos - self.msnt_deathloc); + if(dist < best_dist || best_dist == 0) + { + best_dist = dist; + best_spot = trace_endpos; + best_mate = team_mate; + } + } + else + { + setorigin(self, trace_endpos); + self.angles = team_mate.angles; + self.angles_z = 0; // never spawn tilted even if the spot says to + team_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; + return 0; + } + } + } + } + } + } + } + } + + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) + if(best_dist) + { + setorigin(self, best_spot); + self.angles = best_mate.angles; + self.angles_z = 0; // never spawn tilted even if the spot says to + best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; + } + } + else if(spawn_spot.msnt_lookat) + { + self.angles = vectoangles(spawn_spot.msnt_lookat.origin - self.origin); + self.angles_x = -self.angles.x; + self.angles_z = 0; // never spawn tilted even if the spot says to + /* + sprint(self, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n"); + sprint(self, "distance: ", vtos(spawn_spot.msnt_lookat.origin - self.origin), "\n"); + sprint(self, "angles: ", vtos(self.angles), "\n"); + */ + } + + return 0; +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies) +{SELFPARAM(); + self.msnt_deathloc = self.origin; + return 0; +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, GetCvars) +{ + GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_spawn_near_teammate, "cl_spawn_near_teammate"); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/superspec/module.inc b/qcsrc/common/mutators/mutator/superspec/module.inc new file mode 100644 index 000000000..8e0a998c2 --- /dev/null +++ b/qcsrc/common/mutators/mutator/superspec/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "superspec.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/superspec/superspec.qc b/qcsrc/common/mutators/mutator/superspec/superspec.qc new file mode 100644 index 000000000..416df75b4 --- /dev/null +++ b/qcsrc/common/mutators/mutator/superspec/superspec.qc @@ -0,0 +1,480 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(superspec, cvar("g_superspectate")); + +#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" +#define _ISLOCAL ((edict_num(1) == self) ? true : false) + +const float ASF_STRENGTH = BIT(0); +const float ASF_SHIELD = BIT(1); +const float ASF_MEGA_AR = BIT(2); +const float ASF_MEGA_HP = BIT(3); +const float ASF_FLAG_GRAB = BIT(4); +const float ASF_OBSERVER_ONLY = BIT(5); +const float ASF_SHOWWHAT = BIT(6); +const float ASF_SSIM = BIT(7); +const float ASF_FOLLOWKILLER = BIT(8); +const float ASF_ALL = 0xFFFFFF; +.float autospec_flags; + +const float SSF_SILENT = 1; +const float SSF_VERBOSE = 2; +const float SSF_ITEMMSG = 4; +.float superspec_flags; + +.string superspec_itemfilter; //"classname1 classname2 ..." + +bool superspec_Spectate(entity _player) +{SELFPARAM(); + if(Spectate(_player) == 1) + self.classname = STR_SPECTATOR; + + return true; +} + +void superspec_save_client_conf() +{SELFPARAM(); + string fn = "superspec-local.options"; + float fh; + + if (!_ISLOCAL) + { + if(self.crypto_idfp == "") + return; + + fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); + } + + fh = fopen(fn, FILE_WRITE); + if(fh < 0) + { + LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n"); + } + else + { + fputs(fh, _SSMAGIX); + fputs(fh, "\n"); + fputs(fh, ftos(self.autospec_flags)); + fputs(fh, "\n"); + fputs(fh, ftos(self.superspec_flags)); + fputs(fh, "\n"); + fputs(fh, self.superspec_itemfilter); + fputs(fh, "\n"); + fclose(fh); + } +} + +void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) +{ + sprint(_to, strcat(_con_title, _msg)); + + if(_to.superspec_flags & SSF_SILENT) + return; + + if(_spamlevel > 1) + if (!(_to.superspec_flags & SSF_VERBOSE)) + return; + + centerprint(_to, strcat(_center_title, _msg)); +} + +float superspec_filteritem(entity _for, entity _item) +{ + float i; + + if(_for.superspec_itemfilter == "") + return true; + + if(_for.superspec_itemfilter == "") + return true; + + float l = tokenize_console(_for.superspec_itemfilter); + for(i = 0; i < l; ++i) + { + if(argv(i) == _item.classname) + return true; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(superspec, ItemTouch) +{SELFPARAM(); + entity _item = self; + + entity e; + FOR_EACH_SPEC(e) + { + setself(e); + if(self.superspec_flags & SSF_ITEMMSG) + if(superspec_filteritem(self, _item)) + { + if(self.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n", other.netname, _item.netname), 1); + else + superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", other.netname, _item.netname, _item.classname), 1); + if((self.autospec_flags & ASF_SSIM) && self.enemy != other) + { + superspec_Spectate(other); + + setself(this); + return MUT_ITEMTOUCH_CONTINUE; + } + } + + if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) || + (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) || + (self.autospec_flags & ASF_MEGA_AR && _item.itemdef == ITEM_ArmorMega) || + (self.autospec_flags & ASF_MEGA_HP && _item.itemdef == ITEM_HealthMega) || + (self.autospec_flags & ASF_FLAG_GRAB && _item.classname == "item_flag_team")) + { + + if((self.enemy != other) || IS_OBSERVER(self)) + { + if(self.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(self)) + { + if(self.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", self, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", other.netname, _item.netname), 2); + } + else + { + if(self.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", self, sprintf("^7Following %s^7 due to picking up %s\n", other.netname, _item.netname), 2); + + superspec_Spectate(other); + } + } + } + } + + setself(this); + + return MUT_ITEMTOUCH_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand) +{SELFPARAM(); +#define OPTIONINFO(flag,var,test,text,long,short) \ + var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ + var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") + + if(MUTATOR_RETURNVALUE) // command was already handled? + return false; + + if(IS_PLAYER(self)) + return false; + + if(cmd_name == "superspec_itemfilter") + { + if(argv(1) == "help") + { + string _aspeco; + _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"; + _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); + _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); + superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", self, _aspeco, 1); + } + else if(argv(1) == "clear") + { + if(self.superspec_itemfilter != "") + strunzone(self.superspec_itemfilter); + + self.superspec_itemfilter = ""; + } + else if(argv(1) == "show" || argv(1) == "") + { + if(self.superspec_itemfilter == "") + { + superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", self, "", 1); + return true; + } + float i; + float l = tokenize_console(self.superspec_itemfilter); + string _msg = ""; + for(i = 0; i < l; ++i) + _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); + //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); + + _msg = strcat(_msg,"\n"); + + superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", self, _msg, 1); + } + else + { + if(self.superspec_itemfilter != "") + strunzone(self.superspec_itemfilter); + + self.superspec_itemfilter = strzone(argv(1)); + } + + return true; + } + + if(cmd_name == "superspec") + { + string _aspeco; + + if(cmd_argc > 1) + { + float i, _bits = 0, _start = 1; + if(argv(1) == "help") + { + _aspeco = "use cmd superspec [option] [on|off] to set options\n\n"; + _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n"); + _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); + _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n"); + _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); + superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", self, _aspeco, 1); + return true; + } + + if(argv(1) == "clear") + { + self.superspec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + self.superspec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + self.superspec_flags &= ~_bits; + + _bits = 0; + } + else + { + if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; + if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; + if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; + } + } + } + + _aspeco = ""; + OPTIONINFO(self.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); + OPTIONINFO(self.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); + OPTIONINFO(self.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); + + superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", self, _aspeco, 1); + + return true; + } + +///////////////////// + + if(cmd_name == "autospec") + { + string _aspeco; + if(cmd_argc > 1) + { + if(argv(1) == "help") + { + _aspeco = "use cmd autospec [option] [on|off] to set options\n\n"; + _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); + _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); + _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); + _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); + _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); + _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n"); + _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n"); + _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n"); + _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n"); + _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n"); + superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", self, _aspeco, 1); + return true; + } + + float i, _bits = 0, _start = 1; + if(argv(1) == "clear") + { + self.autospec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + self.autospec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + self.autospec_flags &= ~_bits; + + _bits = 0; + } + else + { + if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; + if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; + if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; + if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; + if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; + if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; + if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; + if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; + if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER; + if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; + } + } + } + + _aspeco = ""; + OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); + OPTIONINFO(self.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk"); + + superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", self, _aspeco, 1); + return true; + } + + if(cmd_name == "followpowerup") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.strength_finished > time || _player.invincible_finished > time) + return superspec_Spectate(_player); + } + + superspec_msg("", "", self, "No active powerup\n", 1); + return true; + } + + if(cmd_name == "followstrength") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.strength_finished > time) + return superspec_Spectate(_player); + } + + superspec_msg("", "", self, "No active Strength\n", 1); + return true; + } + + if(cmd_name == "followshield") + { + entity _player; + FOR_EACH_PLAYER(_player) + { + if(_player.invincible_finished > time) + return superspec_Spectate(_player); + } + + superspec_msg("", "", self, "No active Shield\n", 1); + return true; + } + + return false; +#undef OPTIONINFO +} + +MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":SS"); + return 0; +} + +MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Super Spectators"); + return 0; +} + +void superspec_hello() +{SELFPARAM(); + if(self.enemy.crypto_idfp == "") + Send_Notification(NOTIF_ONE_ONLY, self.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID); + + remove(self); +} + +MUTATOR_HOOKFUNCTION(superspec, ClientConnect) +{SELFPARAM(); + if(!IS_REAL_CLIENT(self)) + return false; + + string fn = "superspec-local.options"; + float fh; + + self.superspec_flags = SSF_VERBOSE; + self.superspec_itemfilter = ""; + + entity _hello = spawn(); + _hello.enemy = self; + _hello.think = superspec_hello; + _hello.nextthink = time + 5; + + if (!_ISLOCAL) + { + if(self.crypto_idfp == "") + return false; + + fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); + } + + fh = fopen(fn, FILE_READ); + if(fh < 0) + { + LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n"); + } + else + { + string _magic = fgets(fh); + if(_magic != _SSMAGIX) + { + LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic\n"); + } + else + { + self.autospec_flags = stof(fgets(fh)); + self.superspec_flags = stof(fgets(fh)); + self.superspec_itemfilter = strzone(fgets(fh)); + } + fclose(fh); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(superspec, PlayerDies) +{SELFPARAM(); + entity e; + FOR_EACH_SPEC(e) + { + setself(e); + if(self.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && self.enemy == this) + { + if(self.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", self, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2); + + superspec_Spectate(frag_attacker); + } + } + + setself(this); + return false; +} + +MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect) +{ + superspec_save_client_conf(); + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/module.inc b/qcsrc/common/mutators/mutator/touchexplode/module.inc new file mode 100644 index 000000000..d3b0ea5af --- /dev/null +++ b/qcsrc/common/mutators/mutator/touchexplode/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "touchexplode.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc new file mode 100644 index 000000000..ec439489d --- /dev/null +++ b/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc @@ -0,0 +1,48 @@ +#ifdef IMPLEMENTATION +float autocvar_g_touchexplode_radius; +float autocvar_g_touchexplode_damage; +float autocvar_g_touchexplode_edgedamage; +float autocvar_g_touchexplode_force; + +REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode")); + +.float touchexplode_time; + +void PlayerTouchExplode(entity p1, entity p2) +{SELFPARAM(); + vector org = (p1.origin + p2.origin) * 0.5; + org.z += (p1.mins.z + p2.mins.z) * 0.5; + + sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); + + entity e = spawn(); + setorigin(e, org); + RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, world); + remove(e); +} + +MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink) +{SELFPARAM(); + if(time > self.touchexplode_time) + if(!gameover) + if(!self.frozen) + if(IS_PLAYER(self)) + if(self.deadflag == DEAD_NO) + if (!IS_INDEPENDENT_PLAYER(self)) + FOR_EACH_PLAYER(other) if(self != other) + { + if(time > other.touchexplode_time) + if(!other.frozen) + if(other.deadflag == DEAD_NO) + if (!IS_INDEPENDENT_PLAYER(other)) + if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax)) + { + PlayerTouchExplode(self, other); + self.touchexplode_time = other.touchexplode_time = time + 0.2; + } + } + + return false; +} +#endif diff --git a/qcsrc/common/mutators/mutator/vampire/module.inc b/qcsrc/common/mutators/mutator/vampire/module.inc new file mode 100644 index 000000000..864ea28b2 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampire/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "vampire.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/vampire/vampire.qc b/qcsrc/common/mutators/mutator/vampire/vampire.qc new file mode 100644 index 000000000..315da7dc6 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampire/vampire.qc @@ -0,0 +1,28 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib")); + +MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) +{ + if(time >= frag_target.spawnshieldtime) + if(frag_target != frag_attacker) + if(frag_target.deadflag == DEAD_NO) + { + frag_attacker.health += bound(0, damage_take, frag_target.health); + frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit); + } + + return false; +} + +MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString) +{ + ret_string = strcat(ret_string, ":Vampire"); + return 0; +} + +MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString) +{ + ret_string = strcat(ret_string, ", Vampire"); + return 0; +} +#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/module.inc b/qcsrc/common/mutators/mutator/vampirehook/module.inc new file mode 100644 index 000000000..17ecf6005 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampirehook/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "vampirehook.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc new file mode 100644 index 000000000..f669f6a96 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc @@ -0,0 +1,38 @@ +#ifdef IMPLEMENTATION +REGISTER_MUTATOR(vh, cvar("g_vampirehook")); + +bool autocvar_g_vampirehook_teamheal; +float autocvar_g_vampirehook_damage; +float autocvar_g_vampirehook_damagerate; +float autocvar_g_vampirehook_health_steal; + +.float last_dmg; + +MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) +{SELFPARAM(); + entity dmgent = ((SAME_TEAM(self.owner, self.aiment) && autocvar_g_vampirehook_teamheal) ? self.owner : self.aiment); + + if(IS_PLAYER(self.aiment)) + if(self.last_dmg < time) + if(!self.aiment.frozen) + if(time >= game_starttime) + if(DIFF_TEAM(self.owner, self.aiment) || autocvar_g_vampirehook_teamheal) + if(self.aiment.health > 0) + if(autocvar_g_vampirehook_damage) + { + self.last_dmg = time + autocvar_g_vampirehook_damagerate; + self.owner.damage_dealt += autocvar_g_vampirehook_damage; + Damage(dmgent, self, self.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, self.origin, '0 0 0'); + if(SAME_TEAM(self.owner, self.aiment)) + self.aiment.health = min(self.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + else + self.owner.health = min(self.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + + if(dmgent == self.owner) + dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! + } + + return false; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/module.inc b/qcsrc/common/mutators/mutator/weaponarena_random/module.inc new file mode 100644 index 000000000..b7a5f6690 --- /dev/null +++ b/qcsrc/common/mutators/mutator/weaponarena_random/module.inc @@ -0,0 +1,3 @@ +#ifdef SVQC +#include "weaponarena_random.qc" +#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc new file mode 100644 index 000000000..5c8210077 --- /dev/null +++ b/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc @@ -0,0 +1,13 @@ +#ifdef IMPLEMENTATION +// WEAPONTODO: rename the cvars +REGISTER_MUTATOR(weaponarena_random, true); + +MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) { + SELFPARAM(); + if (!g_weaponarena_random) return; + if (g_weaponarena_random_with_blaster) this.weapons &= ~WEPSET(BLASTER); + W_RandomWeapons(this, g_weaponarena_random); + if (g_weaponarena_random_with_blaster) this.weapons |= WEPSET(BLASTER); +} + +#endif diff --git a/qcsrc/common/nades/all.qc b/qcsrc/common/nades/all.qc index 7dfae12ac..4ba13408a 100644 --- a/qcsrc/common/nades/all.qc +++ b/qcsrc/common/nades/all.qc @@ -1,6 +1,7 @@ +#include "all.qh" + #if defined(CSQC) #include "../../client/defs.qh" - #include "all.qh" #include "../buffs/all.qh" #include "../movetypes/movetypes.qh" #include "../../client/main.qh" diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc index 408cb950b..2f3cd47b0 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc @@ -54,7 +54,7 @@ string WeaponArenaString() { e = get_weaponinfo(j); if(argv(i) == e.netname) - s = strcat(s, " & ", e.message); + s = strcat(s, " & ", e.m_name); } } s = sprintf(_("%s Arena"), substring(s, 3, strlen(s) - 3)); @@ -259,7 +259,7 @@ void XonoticMutatorsDialog_fill(entity me) if((j & 1) == 0) me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.message))); + me.TD(me, 1, 1.8, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.m_name))); setDependentWeird(e, checkCompatibility_weaponarena_weapon); ++j; } diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index d72617fac..5d9c8dc71 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -121,20 +121,6 @@ float autocvar_g_balance_health_rotlinear; float autocvar_g_balance_health_rotstable; float autocvar_g_balance_kill_delay; float autocvar_g_balance_kill_antispam; -int autocvar_g_balance_nix_ammo_cells; -int autocvar_g_balance_nix_ammo_plasma; -int autocvar_g_balance_nix_ammo_fuel; -int autocvar_g_balance_nix_ammo_nails; -int autocvar_g_balance_nix_ammo_rockets; -int autocvar_g_balance_nix_ammo_shells; -int autocvar_g_balance_nix_ammoincr_cells; -int autocvar_g_balance_nix_ammoincr_plasma; -int autocvar_g_balance_nix_ammoincr_fuel; -int autocvar_g_balance_nix_ammoincr_nails; -int autocvar_g_balance_nix_ammoincr_rockets; -int autocvar_g_balance_nix_ammoincr_shells; -float autocvar_g_balance_nix_incrtime; -float autocvar_g_balance_nix_roundtime; float autocvar_g_balance_pause_armor_rot; float autocvar_g_balance_pause_armor_rot_spawn; float autocvar_g_balance_pause_fuel_regen; @@ -234,9 +220,7 @@ int autocvar_g_maxplayers; float autocvar_g_maxplayers_spectator_blocktime; float autocvar_g_maxpushtime; float autocvar_g_maxspeed; -float autocvar_g_midair_shieldtime; #define autocvar_g_instagib cvar("g_instagib") -float autocvar_g_instagib_invis_alpha; bool autocvar_g_instagib_damagedbycontents = true; bool autocvar_g_instagib_blaster_keepdamage = false; bool autocvar_g_instagib_blaster_keepforce = false; @@ -244,31 +228,17 @@ bool autocvar_g_instagib_blaster_keepforce = false; #define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual") float autocvar_g_movement_highspeed = 1; -int autocvar_g_multijump; -float autocvar_g_multijump_add; -float autocvar_g_multijump_speed; -float autocvar_g_multijump_maxspeed; -float autocvar_g_multijump_dodging = 1; string autocvar_g_mutatormsg; //float autocvar_g_nick_flood_penalty; int autocvar_g_nick_flood_penalty_red; int autocvar_g_nick_flood_penalty_yellow; //float autocvar_g_nick_flood_timeout; -bool autocvar_g_nix_with_healtharmor; -bool autocvar_g_nix_with_blaster; -bool autocvar_g_nix_with_powerups; bool autocvar_g_nodepthtestitems; bool autocvar_g_nodepthtestplayers; bool autocvar_g_norecoil; float autocvar_g_items_mindist; float autocvar_g_items_maxdist; -int autocvar_g_pickup_cells_max; -int autocvar_g_pickup_plasma_max; -int autocvar_g_pickup_fuel_max; int autocvar_g_pickup_items; -int autocvar_g_pickup_nails_max; -int autocvar_g_pickup_rockets_max; -int autocvar_g_pickup_shells_max; float autocvar_g_player_alpha; float autocvar_g_player_brightness; bool autocvar_g_playerclip_collisions; @@ -379,17 +349,7 @@ string autocvar_sv_defaultplayermodel_pink; string autocvar_sv_defaultplayermodel_red; string autocvar_sv_defaultplayermodel_yellow; int autocvar_sv_defaultplayerskin; -float autocvar_sv_dodging_delay; -float autocvar_sv_dodging_height_threshold; -float autocvar_sv_dodging_horiz_speed; -float autocvar_sv_dodging_horiz_speed_frozen; -float autocvar_sv_dodging_ramp_time; -bool autocvar_sv_dodging_sound; -float autocvar_sv_dodging_up_speed; -float autocvar_sv_dodging_wall_distance_threshold; -bool autocvar_sv_dodging_wall_dodging; bool autocvar_sv_dodging_frozen; -bool autocvar_sv_dodging_frozen_doubletap; bool autocvar_sv_doublejump; bool autocvar_sv_eventlog; bool autocvar_sv_eventlog_console; @@ -482,25 +442,8 @@ bool autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag; float autocvar_g_trueaim_minrange; bool autocvar_g_debug_defaultsounds; float autocvar_g_grab_range; -int autocvar_g_sandbox_info; -bool autocvar_g_sandbox_readonly; -string autocvar_g_sandbox_storage_name; -float autocvar_g_sandbox_storage_autosave; -bool autocvar_g_sandbox_storage_autoload; -float autocvar_g_sandbox_editor_flood; -int autocvar_g_sandbox_editor_maxobjects; -int autocvar_g_sandbox_editor_free; -float autocvar_g_sandbox_editor_distance_spawn; -float autocvar_g_sandbox_editor_distance_edit; -float autocvar_g_sandbox_object_scale_min; -float autocvar_g_sandbox_object_scale_max; -float autocvar_g_sandbox_object_material_velocity_min; -float autocvar_g_sandbox_object_material_velocity_factor; int autocvar_g_max_info_autoscreenshot; bool autocvar_physics_ode; -int autocvar_g_physical_items; -float autocvar_g_physical_items_damageforcescale; -float autocvar_g_physical_items_reset; float autocvar_g_monsters; bool autocvar_g_monsters_edit; bool autocvar_g_monsters_sounds; @@ -527,17 +470,7 @@ bool autocvar_g_monsters_respawn; float autocvar_g_monsters_armor_blockpercent; float autocvar_g_monsters_healthbars; float autocvar_g_monsters_lineofsight; -float autocvar_g_touchexplode_radius; -float autocvar_g_touchexplode_damage; -float autocvar_g_touchexplode_edgedamage; -float autocvar_g_touchexplode_force; #define autocvar_g_bloodloss cvar("g_bloodloss") -float autocvar_g_random_gravity_negative_chance; -float autocvar_g_random_gravity_min; -float autocvar_g_random_gravity_max; -float autocvar_g_random_gravity_positive; -float autocvar_g_random_gravity_negative; -float autocvar_g_random_gravity_delay; bool autocvar_g_nades; bool autocvar_g_nades_override_dropweapon = true; vector autocvar_g_nades_throw_offset; @@ -593,60 +526,12 @@ float autocvar_g_nades_heal_friend; float autocvar_g_nades_heal_foe; string autocvar_g_nades_pokenade_monster_type; float autocvar_g_nades_pokenade_monster_lifetime; -float autocvar_g_campcheck_damage; -float autocvar_g_campcheck_distance; -float autocvar_g_campcheck_interval; bool autocvar_g_jump_grunt; -float autocvar_g_spawn_near_teammate_distance; -int autocvar_g_spawn_near_teammate_ignore_spawnpoint; -float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; -float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; -int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; -bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; bool autocvar_g_physics_clientselect; string autocvar_g_physics_clientselect_options; string autocvar_g_physics_clientselect_default; -bool autocvar_g_buffs_effects; -float autocvar_g_buffs_waypoint_distance; -bool autocvar_g_buffs_randomize; -float autocvar_g_buffs_random_lifetime; -bool autocvar_g_buffs_random_location; -int autocvar_g_buffs_random_location_attempts; -int autocvar_g_buffs_spawn_count; -bool autocvar_g_buffs_replace_powerups; -float autocvar_g_buffs_cooldown_activate; -float autocvar_g_buffs_cooldown_respawn; -float autocvar_g_buffs_resistance_blockpercent; -float autocvar_g_buffs_medic_survive_chance; -float autocvar_g_buffs_medic_survive_health; -float autocvar_g_buffs_medic_rot; -float autocvar_g_buffs_medic_max; -float autocvar_g_buffs_medic_regen; -float autocvar_g_buffs_vengeance_damage_multiplier; -float autocvar_g_buffs_bash_force; -float autocvar_g_buffs_bash_force_self; -float autocvar_g_buffs_disability_slowtime; -float autocvar_g_buffs_disability_speed; -float autocvar_g_buffs_disability_rate; -float autocvar_g_buffs_disability_weaponspeed; -float autocvar_g_buffs_speed_speed; -float autocvar_g_buffs_speed_rate; -float autocvar_g_buffs_speed_weaponspeed; -float autocvar_g_buffs_speed_damage_take; -float autocvar_g_buffs_speed_regen; -float autocvar_g_buffs_vampire_damage_steal; -float autocvar_g_buffs_invisible_alpha; -float autocvar_g_buffs_flight_gravity; -float autocvar_g_buffs_jump_height; bool autocvar_sv_minigames; bool autocvar_sv_minigames_observer; -float autocvar_g_buffs_inferno_burntime_factor; -float autocvar_g_buffs_inferno_burntime_min_time; -float autocvar_g_buffs_inferno_burntime_target_damage; -float autocvar_g_buffs_inferno_burntime_target_time; -float autocvar_g_buffs_inferno_damagemultiplier; -float autocvar_g_buffs_swapper_range; -float autocvar_g_buffs_magnet_range_item; float autocvar_sv_player_scale; float autocvar_g_rm; float autocvar_g_rm_damage; diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc index 462d42722..38203f52e 100644 --- a/qcsrc/server/cl_client.qc +++ b/qcsrc/server/cl_client.qc @@ -1871,7 +1871,6 @@ void LeaveSpectatorMode() if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0) { self.classname = STR_PLAYER; - nades_RemoveBonus(self); if(autocvar_g_campaign || autocvar_g_balance_teams) { JoinBestTeam(self, false, true); } diff --git a/qcsrc/server/mutators/all.inc b/qcsrc/server/mutators/all.inc index e12e9c58b..b334137bd 100644 --- a/qcsrc/server/mutators/all.inc +++ b/qcsrc/server/mutators/all.inc @@ -12,30 +12,3 @@ #include "mutator/gamemode_onslaught.qc" #include "mutator/gamemode_race.qc" #include "mutator/gamemode_tdm.qc" - -#include "mutator/mutator_bloodloss.qc" -#include "mutator/mutator_breakablehook.qc" -#include "mutator/mutator_buffs.qc" -#include "mutator/mutator_campcheck.qc" -#include "mutator/mutator_dodging.qc" -#include "mutator/mutator_hook.qc" -#include "mutator/mutator_invincibleproj.qc" -#include "mutator/mutator_melee_only.qc" -#include "mutator/mutator_midair.qc" -#include "mutator/mutator_multijump.qc" -#include "mutator/mutator_nades.qc" -#include "mutator/mutator_new_toys.qc" -#include "mutator/mutator_nix.qc" -#include "mutator/mutator_physical_items.qc" -#include "mutator/mutator_pinata.qc" -#include "mutator/mutator_random_gravity.qc" -#include "mutator/mutator_rocketflying.qc" -#include "mutator/mutator_rocketminsta.qc" -#include "mutator/mutator_spawn_near_teammate.qc" -#include "mutator/mutator_superspec.qc" -#include "mutator/mutator_touchexplode.qc" -#include "mutator/mutator_vampirehook.qc" -#include "mutator/mutator_vampire.qc" -#include "mutator/mutator_weaponarena_random.qc" - -#include "mutator/sandbox.qc" diff --git a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc index 14e345046..29ae00668 100644 --- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc @@ -44,15 +44,16 @@ const float ICE_MIN_ALPHA = 0.1; float freezetag_teams; .float reviving; // temp var -#endif +float autocvar_g_freezetag_revive_extra_size; +float autocvar_g_freezetag_revive_speed; +bool autocvar_g_freezetag_revive_nade; +float autocvar_g_freezetag_revive_nade_health; + +#endif #ifdef IMPLEMENTATION float autocvar_g_freezetag_frozen_maxtime; -bool autocvar_g_freezetag_revive_nade; -float autocvar_g_freezetag_revive_nade_health; -float autocvar_g_freezetag_revive_extra_size; -float autocvar_g_freezetag_revive_speed; float autocvar_g_freezetag_revive_clearspeed; float autocvar_g_freezetag_round_timelimit; int autocvar_g_freezetag_teams; diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc index bcc14eca9..341d13308 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc @@ -800,7 +800,7 @@ void key_reset() } const string STR_ITEM_KH_KEY = "item_kh_key"; -void kh_Key_Spawn(entity initial_owner, float angle, float i) // runs every time a new flag is created, ie after all the keys have been collected +void kh_Key_Spawn(entity initial_owner, float _angle, float i) // runs every time a new flag is created, ie after all the keys have been collected { entity key = spawn(); key.count = i; @@ -809,7 +809,7 @@ void kh_Key_Spawn(entity initial_owner, float angle, float i) // runs every tim key.think = kh_Key_Think; key.nextthink = time; key.items = IT_KEY1 | IT_KEY2; - key.cnt = angle; + key.cnt = _angle; key.angles = '0 360 0' * random(); key.event_damage = kh_Key_Damage; key.takedamage = DAMAGE_YES; diff --git a/qcsrc/server/mutators/mutator/mutator_bloodloss.qc b/qcsrc/server/mutators/mutator/mutator_bloodloss.qc deleted file mode 100644 index ca3716669..000000000 --- a/qcsrc/server/mutators/mutator/mutator_bloodloss.qc +++ /dev/null @@ -1,45 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss")); - -.float bloodloss_timer; - -MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink) -{SELFPARAM(); - if(IS_PLAYER(self)) - if(self.health <= autocvar_g_bloodloss && self.deadflag == DEAD_NO) - { - self.BUTTON_CROUCH = true; - - if(time >= self.bloodloss_timer) - { - if(self.vehicle) - vehicles_exit(VHEF_RELEASE); - if(self.event_damage) - self.event_damage(self, self, 1, DEATH_ROT.m_id, self.origin, '0 0 0'); - self.bloodloss_timer = time + 0.5 + random() * 0.5; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump) -{SELFPARAM(); - if(self.health <= autocvar_g_bloodloss) - return true; - - return false; -} - -MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":bloodloss"); - return false; -} - -MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Blood loss"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_breakablehook.qc b/qcsrc/server/mutators/mutator/mutator_breakablehook.qc deleted file mode 100644 index cb9463b86..000000000 --- a/qcsrc/server/mutators/mutator/mutator_breakablehook.qc +++ /dev/null @@ -1,29 +0,0 @@ -#ifdef IMPLEMENTATION -#include "../../../common/deathtypes/all.qh" -#include "../../g_hook.qh" - -REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook")); - -bool autocvar_g_breakablehook; // allow toggling mid match? -bool autocvar_g_breakablehook_owner; - -MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate) -{ - if(frag_target.classname == "grapplinghook") - { - if((!autocvar_g_breakablehook) - || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner) - ) { frag_damage = 0; } - - // hurt the owner of the hook - if(DIFF_TEAM(frag_attacker, frag_target.realowner)) - { - Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0'); - RemoveGrapplingHook(frag_target.realowner); - return false; // dead - } - } - - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_buffs.qc b/qcsrc/server/mutators/mutator/mutator_buffs.qc deleted file mode 100644 index fd64e5862..000000000 --- a/qcsrc/server/mutators/mutator/mutator_buffs.qc +++ /dev/null @@ -1,1004 +0,0 @@ -#ifndef MUTATOR_BUFFS_H -#define MUTATOR_BUFFS_H - -// ammo -.float buff_ammo_prev_infitems; -.int buff_ammo_prev_clipload; -// invisible -.float buff_invisible_prev_alpha; -// flight -.float buff_flight_prev_gravity; -// disability -.float buff_disability_time; -.float buff_disability_effect_time; -// common buff variables -.float buff_effect_delay; - -// buff definitions -.float buff_active; -.float buff_activetime; -.float buff_activetime_updated; -.entity buff_waypoint; -.int oldbuffs; // for updating effects -.entity buff_model; // controls effects (TODO: make csqc) - -const vector BUFF_MIN = ('-16 -16 -20'); -const vector BUFF_MAX = ('16 16 20'); - -// client side options -.float cvar_cl_buffs_autoreplace; -#endif - -#ifdef IMPLEMENTATION - -#include "../../../common/triggers/target/music.qh" -#include "../../../common/gamemodes/all.qh" -#include "../../../common/buffs/all.qh" - -.float buff_time; -void buffs_DelayedInit(); - -REGISTER_MUTATOR(buffs, cvar("g_buffs")) -{ - MUTATOR_ONADD - { - addstat(STAT_BUFFS, AS_INT, buffs); - addstat(STAT_BUFF_TIME, AS_FLOAT, buff_time); - - InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET); - } -} - -entity buff_FirstFromFlags(int _buffs) -{ - if (flags) - { - FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it)); - } - return BUFF_Null; -} - -bool buffs_BuffModel_Customize() -{SELFPARAM(); - entity player, myowner; - bool same_team; - - player = WaypointSprite_getviewentity(other); - myowner = self.owner; - same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); - - if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) - return false; - - if(MUTATOR_CALLHOOK(BuffModel_Customize, self, player)) - return false; - - if(player == myowner || (IS_SPEC(other) && other.enemy == myowner)) - { - // somewhat hide the model, but keep the glow - self.effects = 0; - self.alpha = -1; - } - else - { - self.effects = EF_FULLBRIGHT | EF_LOWPRECISION; - self.alpha = 1; - } - return true; -} - -void buffs_BuffModel_Spawn(entity player) -{ - player.buff_model = spawn(); - setmodel(player.buff_model, MDL_BUFF); - setsize(player.buff_model, '0 0 -40', '0 0 40'); - setattachment(player.buff_model, player, ""); - setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); - player.buff_model.owner = player; - player.buff_model.scale = 0.7; - player.buff_model.pflags = PFLAGS_FULLDYNAMIC; - player.buff_model.light_lev = 200; - player.buff_model.customizeentityforclient = buffs_BuffModel_Customize; -} - -vector buff_GlowColor(entity buff) -{ - //if(buff.team) { return Team_ColorRGB(buff.team); } - return buff.m_color; -} - -void buff_Effect(entity player, string eff) -{SELFPARAM(); - if(!autocvar_g_buffs_effects) { return; } - - if(time >= self.buff_effect_delay) - { - Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); - self.buff_effect_delay = time + 0.05; // prevent spam - } -} - -// buff item -float buff_Waypoint_visible_for_player(entity plr) -{SELFPARAM(); - if(!self.owner.buff_active && !self.owner.buff_activetime) - return false; - - if (plr.buffs) - { - return plr.cvar_cl_buffs_autoreplace == false || plr.buffs != self.owner.buffs; - } - - return WaypointSprite_visible_for_player(plr); -} - -void buff_Waypoint_Spawn(entity e) -{ - entity buff = buff_FirstFromFlags(e.buffs); - entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, world, e.team, e, buff_waypoint, true, RADARICON_Buff); - wp.wp_extra = buff.m_id; - WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod); - e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; -} - -void buff_SetCooldown(float cd) -{SELFPARAM(); - cd = max(0, cd); - - if(!self.buff_waypoint) - buff_Waypoint_Spawn(self); - - WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd); - self.buff_activetime = cd; - self.buff_active = !cd; -} - -void buff_Respawn(entity ent) -{SELFPARAM(); - if(gameover) { return; } - - vector oldbufforigin = ent.origin; - - if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256)) - { - entity spot = SelectSpawnPoint(true); - setorigin(ent, ((spot.origin + '0 0 200') + (randomvec() * 300))); - ent.angles = spot.angles; - } - - tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent); - - setorigin(ent, trace_endpos); // attempt to unstick - - ent.movetype = MOVETYPE_TOSS; - - makevectors(ent.angles); - ent.velocity = '0 0 200'; - ent.angles = '0 0 0'; - if(autocvar_g_buffs_random_lifetime > 0) - ent.lifetime = time + autocvar_g_buffs_random_lifetime; - - Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(ent), '0 0 0', 1); - - WaypointSprite_Ping(ent.buff_waypoint); - - sound(ent, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) -} - -void buff_Touch() -{SELFPARAM(); - if(gameover) { return; } - - if(ITEM_TOUCH_NEEDKILL()) - { - buff_Respawn(self); - return; - } - - if((self.team && DIFF_TEAM(other, self)) - || (other.frozen) - || (other.vehicle) - || (!self.buff_active) - ) - { - // can't touch this - return; - } - - if(MUTATOR_CALLHOOK(BuffTouch, self, other)) - return; - - if(!IS_PLAYER(other)) - return; // incase mutator changed other - - if (other.buffs) - { - if (other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs) - { - int buffid = buff_FirstFromFlags(other.buffs).m_id; - //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs); - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid); - - other.buffs = 0; - //sound(other, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - } - else { return; } // do nothing - } - - self.owner = other; - self.buff_active = false; - self.lifetime = 0; - int buffid = buff_FirstFromFlags(self.buffs).m_id; - Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid); - - Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1); - sound(other, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); - other.buffs |= (self.buffs); -} - -float buff_Available(entity buff) -{ - if (buff == BUFF_Null) - return false; - if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) - return false; - if (buff == BUFF_VAMPIRE && cvar("g_vampire")) - return false; - return cvar(strcat("g_buffs_", buff.m_name)); -} - -.int buff_seencount; - -void buff_NewType(entity ent, float cb) -{ - RandomSelection_Init(); - FOREACH(Buffs, buff_Available(it), LAMBDA( - it.buff_seencount += 1; - // if it's already been chosen, give it a lower priority - RandomSelection_Add(world, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount)); - )); - ent.buffs = RandomSelection_chosen_float; -} - -void buff_Think() -{SELFPARAM(); - if(self.buffs != self.oldbuffs) - { - entity buff = buff_FirstFromFlags(self.buffs); - self.color = buff.m_color; - self.glowmod = buff_GlowColor(buff); - self.skin = buff.m_skin; - - setmodel(self, MDL_BUFF); - - if(self.buff_waypoint) - { - //WaypointSprite_Disown(self.buff_waypoint, 1); - WaypointSprite_Kill(self.buff_waypoint); - buff_Waypoint_Spawn(self); - if(self.buff_activetime) - WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime); - } - - self.oldbuffs = self.buffs; - } - - if(!gameover) - if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) - if(!self.buff_activetime_updated) - { - buff_SetCooldown(self.buff_activetime); - self.buff_activetime_updated = true; - } - - if(!self.buff_active && !self.buff_activetime) - if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs)) - { - buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime); - self.owner = world; - if(autocvar_g_buffs_randomize) - buff_NewType(self, self.buffs); - - if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) - buff_Respawn(self); - } - - if(self.buff_activetime) - if(!gameover) - if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) - { - self.buff_activetime = max(0, self.buff_activetime - frametime); - - if(!self.buff_activetime) - { - self.buff_active = true; - sound(self, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM); - Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1); - } - } - - if(self.buff_active) - { - if(self.team && !self.buff_waypoint) - buff_Waypoint_Spawn(self); - - if(self.lifetime) - if(time >= self.lifetime) - buff_Respawn(self); - } - - self.nextthink = time; - //self.angles_y = time * 110.1; -} - -void buff_Waypoint_Reset() -{SELFPARAM(); - WaypointSprite_Kill(self.buff_waypoint); - - if(self.buff_activetime) { buff_Waypoint_Spawn(self); } -} - -void buff_Reset() -{SELFPARAM(); - if(autocvar_g_buffs_randomize) - buff_NewType(self, self.buffs); - self.owner = world; - buff_SetCooldown(autocvar_g_buffs_cooldown_activate); - buff_Waypoint_Reset(); - self.buff_activetime_updated = false; - - if(autocvar_g_buffs_random_location || (self.spawnflags & 64)) - buff_Respawn(self); -} - -float buff_Customize() -{SELFPARAM(); - entity player = WaypointSprite_getviewentity(other); - if(!self.buff_active || (self.team && DIFF_TEAM(player, self))) - { - self.alpha = 0.3; - if(self.effects & EF_FULLBRIGHT) { self.effects &= ~(EF_FULLBRIGHT); } - self.pflags = 0; - } - else - { - self.alpha = 1; - if(!(self.effects & EF_FULLBRIGHT)) { self.effects |= EF_FULLBRIGHT; } - self.light_lev = 220 + 36 * sin(time); - self.pflags = PFLAGS_FULLDYNAMIC; - } - return true; -} - -void buff_Init(entity ent) -{SELFPARAM(); - if(!cvar("g_buffs")) { remove(ent); return; } - - if(!teamplay && ent.team) { ent.team = 0; } - - entity buff = buff_FirstFromFlags(self.buffs); - - setself(ent); - if(!self.buffs || buff_Available(buff)) - buff_NewType(self, 0); - - self.classname = "item_buff"; - self.solid = SOLID_TRIGGER; - self.flags = FL_ITEM; - self.think = buff_Think; - self.touch = buff_Touch; - self.reset = buff_Reset; - self.nextthink = time + 0.1; - self.gravity = 1; - self.movetype = MOVETYPE_TOSS; - self.scale = 1; - self.skin = buff.m_skin; - self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; - self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - self.customizeentityforclient = buff_Customize; - //self.gravity = 100; - self.color = buff.m_color; - self.glowmod = buff_GlowColor(self); - buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime); - self.buff_active = !self.buff_activetime; - self.pflags = PFLAGS_FULLDYNAMIC; - - if(self.spawnflags & 1) - self.noalign = true; - - if(self.noalign) - self.movetype = MOVETYPE_NONE; // reset by random location - - setmodel(self, MDL_BUFF); - setsize(self, BUFF_MIN, BUFF_MAX); - - if(cvar("g_buffs_random_location") || (self.spawnflags & 64)) - buff_Respawn(self); - - setself(this); -} - -void buff_Init_Compat(entity ent, entity replacement) -{ - if (ent.spawnflags & 2) - ent.team = NUM_TEAM_1; - else if (ent.spawnflags & 4) - ent.team = NUM_TEAM_2; - - ent.buffs = replacement.m_itemid; - - buff_Init(ent); -} - -void buff_SpawnReplacement(entity ent, entity old) -{ - setorigin(ent, old.origin); - ent.angles = old.angles; - ent.noalign = (old.noalign || (old.spawnflags & 1)); - - buff_Init(ent); -} - -void buff_Vengeance_DelayedDamage() -{SELFPARAM(); - if(self.enemy) - Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF.m_id, self.enemy.origin, '0 0 0'); - - remove(self); - return; -} - -float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) -{ - return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base); -} - -// mutator hooks -MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) -{ - if(frag_deathtype == DEATH_BUFF.m_id) { return false; } - - if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) - { - vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); - damage_take = v.x; - damage_save = v.y; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate) -{ - if(frag_deathtype == DEATH_BUFF.m_id) { return false; } - - if(frag_target.buffs & BUFF_SPEED.m_itemid) - if(frag_target != frag_attacker) - frag_damage *= autocvar_g_buffs_speed_damage_take; - - if(frag_target.buffs & BUFF_MEDIC.m_itemid) - if((frag_target.health - frag_damage) <= 0) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - if(frag_attacker) - if(random() <= autocvar_g_buffs_medic_survive_chance) - frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); - - if(frag_target.buffs & BUFF_JUMP.m_itemid) - if(frag_deathtype == DEATH_FALL.m_id) - frag_damage = 0; - - if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) - if(frag_attacker) - if(frag_attacker != frag_target) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - { - entity dmgent = spawn(); - - dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier; - dmgent.enemy = frag_attacker; - dmgent.owner = frag_target; - dmgent.think = buff_Vengeance_DelayedDamage; - dmgent.nextthink = time + 0.1; - } - - if(frag_target.buffs & BUFF_BASH.m_itemid) - if(frag_attacker != frag_target) - if(vlen(frag_force)) - frag_force = '0 0 0'; - - if(frag_attacker.buffs & BUFF_BASH.m_itemid) - if(vlen(frag_force)) - if(frag_attacker == frag_target) - frag_force *= autocvar_g_buffs_bash_force_self; - else - frag_force *= autocvar_g_buffs_bash_force; - - if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid) - if(frag_target != frag_attacker) - frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; - - if(frag_attacker.buffs & BUFF_MEDIC.m_itemid) - if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) - if(SAME_TEAM(frag_attacker, frag_target)) - if(frag_attacker != frag_target) - { - frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage); - frag_damage = 0; - } - - if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) - if(frag_target != frag_attacker) { - float time = buff_Inferno_CalculateTime( - frag_damage, - 0, - autocvar_g_buffs_inferno_burntime_min_time, - autocvar_g_buffs_inferno_burntime_target_damage, - autocvar_g_buffs_inferno_burntime_target_time, - autocvar_g_buffs_inferno_burntime_factor - ); - Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier) * time, time, DEATH_BUFF.m_id); - } - - // this... is ridiculous (TODO: fix!) - if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) - if(!frag_target.vehicle) - if(DEATH_WEAPONOF(frag_deathtype) != WEP_ARC) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - if(frag_target.deadflag == DEAD_NO) - if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target)) - if(frag_attacker != frag_target) - if(!frag_target.frozen) - if(frag_target.takedamage) - if(DIFF_TEAM(frag_attacker, frag_target)) - { - frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max); - if(frag_target.armorvalue) - frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs,PlayerSpawn) -{SELFPARAM(); - self.buffs = 0; - // reset timers here to prevent them continuing after re-spawn - self.buff_disability_time = 0; - self.buff_disability_effect_time = 0; - return false; -} - -.float stat_sv_maxspeed; -.float stat_sv_airspeedlimit_nonqw; -.float stat_sv_jumpvelocity; - -MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics) -{SELFPARAM(); - if(self.buffs & BUFF_SPEED.m_itemid) - { - self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; - self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; - } - - if(time < self.buff_disability_time) - { - self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed; - self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; - } - - if(self.buffs & BUFF_JUMP.m_itemid) - { - // automatically reset, no need to worry - self.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerJump) -{SELFPARAM(); - if(self.buffs & BUFF_JUMP.m_itemid) - player_jumpheight = autocvar_g_buffs_jump_height; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, MonsterMove) -{SELFPARAM(); - if(time < self.buff_disability_time) - { - monster_speed_walk *= autocvar_g_buffs_disability_speed; - monster_speed_run *= autocvar_g_buffs_disability_speed; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerDies) -{SELFPARAM(); - if(self.buffs) - { - int buffid = buff_FirstFromFlags(self.buffs).m_id; - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); - self.buffs = 0; - - if(self.buff_model) - { - remove(self.buff_model); - self.buff_model = world; - } - } - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) -{SELFPARAM(); - if(MUTATOR_RETURNVALUE || gameover) { return false; } - if(self.buffs) - { - int buffid = buff_FirstFromFlags(self.buffs).m_id; - Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); - - self.buffs = 0; - sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - return true; - } - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon) -{SELFPARAM(); - if(MUTATOR_RETURNVALUE || gameover) { return false; } - - if(self.buffs & BUFF_SWAPPER.m_itemid) - { - float best_distance = autocvar_g_buffs_swapper_range; - entity closest = world; - entity player; - FOR_EACH_PLAYER(player) - if(DIFF_TEAM(self, player)) - if(player.deadflag == DEAD_NO && !player.frozen && !player.vehicle) - if(vlen(self.origin - player.origin) <= best_distance) - { - best_distance = vlen(self.origin - player.origin); - closest = player; - } - - if(closest) - { - vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; - - my_org = self.origin; - my_vel = self.velocity; - my_ang = self.angles; - their_org = closest.origin; - their_vel = closest.velocity; - their_ang = closest.angles; - - Drop_Special_Items(closest); - - MUTATOR_CALLHOOK(PortalTeleport, self); // initiate flag dropper - - setorigin(self, their_org); - setorigin(closest, my_org); - - closest.velocity = my_vel; - closest.angles = my_ang; - closest.fixangle = true; - closest.oldorigin = my_org; - closest.oldvelocity = my_vel; - self.velocity = their_vel; - self.angles = their_ang; - self.fixangle = true; - self.oldorigin = their_org; - self.oldvelocity = their_vel; - - // set pusher so self gets the kill if they fall into void - closest.pusher = self; - closest.pushltime = time + autocvar_g_maxpushtime; - closest.istypefrag = closest.BUTTON_CHAT; - - Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1); - - sound(self, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); - sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); - - // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam - self.buffs = 0; - return true; - } - } - return false; -} - -bool buffs_RemovePlayer(entity player) -{ - if(player.buff_model) - { - remove(player.buff_model); - player.buff_model = world; - } - - // also reset timers here to prevent them continuing after spectating - player.buff_disability_time = 0; - player.buff_disability_effect_time = 0; - - return false; -} -MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { return buffs_RemovePlayer(self); } -MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { return buffs_RemovePlayer(self); } - -MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint) -{SELFPARAM(); - entity e = WaypointSprite_getviewentity(other); - - // if you have the invisibility powerup, sprites ALWAYS are restricted to your team - // but only apply this to real players, not to spectators - if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == other)) - if(DIFF_TEAM(self.owner, e)) - return true; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST) -{SELFPARAM(); - if(autocvar_g_buffs_replace_powerups) - switch(self.classname) - { - case "item_strength": - case "item_invincible": - { - entity e = spawn(); - buff_SpawnReplacement(e, self); - return true; - } - } - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor) -{SELFPARAM(); - if(self.buffs & BUFF_SPEED.m_itemid) - weapon_rate *= autocvar_g_buffs_speed_rate; - - if(time < self.buff_disability_time) - weapon_rate *= autocvar_g_buffs_disability_rate; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor) -{SELFPARAM(); - if(self.buffs & BUFF_SPEED.m_itemid) - ret_float *= autocvar_g_buffs_speed_weaponspeed; - - if(time < self.buff_disability_time) - ret_float *= autocvar_g_buffs_disability_weaponspeed; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) -{SELFPARAM(); - if(gameover || self.deadflag != DEAD_NO) { return false; } - - if(time < self.buff_disability_time) - if(time >= self.buff_disability_effect_time) - { - Send_Effect(EFFECT_SMOKING, self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1); - self.buff_disability_effect_time = time + 0.5; - } - - // handle buff lost status - // 1: notify everyone else - // 2: notify carrier as well - int buff_lost = 0; - - if(self.buff_time) - if(time >= self.buff_time) - buff_lost = 2; - - if(self.frozen) { buff_lost = 1; } - - if(buff_lost) - { - if(self.buffs) - { - int buffid = buff_FirstFromFlags(self.buffs).m_id; - Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, buffid); - if(buff_lost >= 2) - { - Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? - sound(self, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - } - self.buffs = 0; - } - } - - if(self.buffs & BUFF_MAGNET.m_itemid) - { - vector pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; - for(other = world; (other = findflags(other, flags, FL_ITEM)); ) - if(boxesoverlap(self.absmin - pickup_size, self.absmax + pickup_size, other.absmin, other.absmax)) - { - setself(other); - other = this; - if(self.touch) - self.touch(); - other = self; - setself(this); - } - } - - if(self.buffs & BUFF_AMMO.m_itemid) - if(self.clip_size) - self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; - - if((self.buffs & BUFF_INVISIBLE.m_itemid) && (self.oldbuffs & BUFF_INVISIBLE.m_itemid)) - if(self.alpha != autocvar_g_buffs_invisible_alpha) - self.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) - -#define BUFF_ONADD(b) if ( (self.buffs & (b).m_itemid) && !(self.oldbuffs & (b).m_itemid)) -#define BUFF_ONREM(b) if (!(self.buffs & (b).m_itemid) && (self.oldbuffs & (b).m_itemid)) - - if(self.buffs != self.oldbuffs) - { - entity buff = buff_FirstFromFlags(self.buffs); - float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; - self.buff_time = (bufftime) ? time + bufftime : 0; - - BUFF_ONADD(BUFF_AMMO) - { - self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO); - self.items |= IT_UNLIMITED_WEAPON_AMMO; - - if(self.clip_load) - self.buff_ammo_prev_clipload = self.clip_load; - self.clip_load = self.(weapon_load[self.switchweapon]) = self.clip_size; - } - - BUFF_ONREM(BUFF_AMMO) - { - if(self.buff_ammo_prev_infitems) - self.items |= IT_UNLIMITED_WEAPON_AMMO; - else - self.items &= ~IT_UNLIMITED_WEAPON_AMMO; - - if(self.buff_ammo_prev_clipload) - self.clip_load = self.buff_ammo_prev_clipload; - } - - BUFF_ONADD(BUFF_INVISIBLE) - { - if(time < self.strength_finished && g_instagib) - self.alpha = autocvar_g_instagib_invis_alpha; - else - self.alpha = self.buff_invisible_prev_alpha; - self.alpha = autocvar_g_buffs_invisible_alpha; - } - - BUFF_ONREM(BUFF_INVISIBLE) - self.alpha = self.buff_invisible_prev_alpha; - - BUFF_ONADD(BUFF_FLIGHT) - { - self.buff_flight_prev_gravity = self.gravity; - self.gravity = autocvar_g_buffs_flight_gravity; - } - - BUFF_ONREM(BUFF_FLIGHT) - self.gravity = self.buff_flight_prev_gravity; - - self.oldbuffs = self.buffs; - if(self.buffs) - { - if(!self.buff_model) - buffs_BuffModel_Spawn(self); - - self.buff_model.color = buff.m_color; - self.buff_model.glowmod = buff_GlowColor(self.buff_model); - self.buff_model.skin = buff.m_skin; - - self.effects |= EF_NOSHADOW; - } - else - { - remove(self.buff_model); - self.buff_model = world; - - self.effects &= ~(EF_NOSHADOW); - } - } - - if(self.buff_model) - { - self.buff_model.effects = self.effects; - self.buff_model.effects |= EF_LOWPRECISION; - self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance - - self.buff_model.alpha = self.alpha; - } - -#undef BUFF_ONADD -#undef BUFF_ONREM - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, SpectateCopy) -{SELFPARAM(); - self.buffs = other.buffs; - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, VehicleEnter) -{ - vh_vehicle.buffs = vh_player.buffs; - vh_player.buffs = 0; - vh_vehicle.buff_time = vh_player.buff_time - time; - vh_player.buff_time = 0; - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, VehicleExit) -{ - vh_player.buffs = vh_player.oldbuffs = vh_vehicle.buffs; - vh_vehicle.buffs = 0; - vh_player.buff_time = time + vh_vehicle.buff_time; - vh_vehicle.buff_time = 0; - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerRegen) -{SELFPARAM(); - if(self.buffs & BUFF_MEDIC.m_itemid) - { - regen_mod_rot = autocvar_g_buffs_medic_rot; - regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max; - regen_mod_regen = autocvar_g_buffs_medic_regen; - } - - if(self.buffs & BUFF_SPEED.m_itemid) - regen_mod_regen = autocvar_g_buffs_speed_regen; - - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace"); - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":Buffs"); - return false; -} - -MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Buffs"); - return false; -} - -void buffs_DelayedInit() -{ - if(autocvar_g_buffs_spawn_count > 0) - if(find(world, classname, "item_buff") == world) - { - float i; - for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) - { - entity e = spawn(); - e.spawnflags |= 64; // always randomize - e.velocity = randomvec() * 250; // this gets reset anyway if random location works - buff_Init(e); - } - } -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_campcheck.qc b/qcsrc/server/mutators/mutator/mutator_campcheck.qc deleted file mode 100644 index be5bfceb8..000000000 --- a/qcsrc/server/mutators/mutator/mutator_campcheck.qc +++ /dev/null @@ -1,86 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(campcheck, cvar("g_campcheck")); - -.float campcheck_nextcheck; -.float campcheck_traveled_distance; - -MUTATOR_HOOKFUNCTION(campcheck, PlayerDies) -{SELFPARAM(); - Kill_Notification(NOTIF_ONE, self, MSG_CENTER_CPID, CPID_CAMPCHECK); - - return false; -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate) -{ - if(IS_PLAYER(frag_target)) - if(IS_PLAYER(frag_attacker)) - if(frag_attacker != frag_target) - { - frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance; - frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink) -{SELFPARAM(); - if(!gameover) - if(!warmup_stage) // don't consider it camping during warmup? - if(time >= game_starttime) - if(IS_PLAYER(self)) - if(IS_REAL_CLIENT(self)) // bots may camp, but that's no reason to constantly kill them - if(self.deadflag == DEAD_NO) - if(!self.frozen) - if(!self.BUTTON_CHAT) - if(autocvar_g_campcheck_interval) - { - vector dist; - - // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement) - dist = self.prevorigin - self.origin; - dist.z = 0; - self.campcheck_traveled_distance += fabs(vlen(dist)); - - if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted())) - { - self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; - self.campcheck_traveled_distance = 0; - } - - if(time > self.campcheck_nextcheck) - { - if(self.campcheck_traveled_distance < autocvar_g_campcheck_distance) - { - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_CAMPCHECK); - if(self.vehicle) - Damage(self.vehicle, self, self, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, self.vehicle.origin, '0 0 0'); - else - Damage(self, self, self, bound(0, autocvar_g_campcheck_damage, self.health + self.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, self.origin, '0 0 0'); - } - self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; - self.campcheck_traveled_distance = 0; - } - - return false; - } - - self.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date - return false; -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn) -{SELFPARAM(); - self.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; - self.campcheck_traveled_distance = 0; - - return false; -} - -MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":CampCheck"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_dodging.qc b/qcsrc/server/mutators/mutator/mutator_dodging.qc deleted file mode 100644 index 85b9fea61..000000000 --- a/qcsrc/server/mutators/mutator/mutator_dodging.qc +++ /dev/null @@ -1,323 +0,0 @@ -#ifdef IMPLEMENTATION - -#ifdef CSQC - #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) - #define PHYS_DODGING getstati(STAT_DODGING) - #define PHYS_DODGING_DELAY getstatf(STAT_DODGING_DELAY) - #define PHYS_DODGING_TIMEOUT(s) getstatf(STAT_DODGING_TIMEOUT) - #define PHYS_DODGING_HORIZ_SPEED_FROZEN getstatf(STAT_DODGING_HORIZ_SPEED_FROZEN) - #define PHYS_DODGING_FROZEN_NODOUBLETAP getstati(STAT_DODGING_FROZEN_NO_DOUBLETAP) - #define PHYS_DODGING_HORIZ_SPEED getstatf(STAT_DODGING_HORIZ_SPEED) - #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys - #define PHYS_DODGING_HEIGHT_THRESHOLD getstatf(STAT_DODGING_HEIGHT_THRESHOLD) - #define PHYS_DODGING_DISTANCE_THRESHOLD getstatf(STAT_DODGING_DISTANCE_THRESHOLD) - #define PHYS_DODGING_RAMP_TIME getstatf(STAT_DODGING_RAMP_TIME) - #define PHYS_DODGING_UP_SPEED getstatf(STAT_DODGING_UP_SPEED) - #define PHYS_DODGING_WALL getstatf(STAT_DODGING_WALL) -#elif defined(SVQC) - #define PHYS_DODGING_FRAMETIME sys_frametime - #define PHYS_DODGING g_dodging - #define PHYS_DODGING_DELAY autocvar_sv_dodging_delay - #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout - #define PHYS_DODGING_HORIZ_SPEED_FROZEN autocvar_sv_dodging_horiz_speed_frozen - #define PHYS_DODGING_FROZEN_NODOUBLETAP autocvar_sv_dodging_frozen_doubletap - #define PHYS_DODGING_HORIZ_SPEED autocvar_sv_dodging_horiz_speed - #define PHYS_DODGING_PRESSED_KEYS(s) s.pressedkeys - #define PHYS_DODGING_HEIGHT_THRESHOLD autocvar_sv_dodging_height_threshold - #define PHYS_DODGING_DISTANCE_THRESHOLD autocvar_sv_dodging_wall_distance_threshold - #define PHYS_DODGING_RAMP_TIME autocvar_sv_dodging_ramp_time - #define PHYS_DODGING_UP_SPEED autocvar_sv_dodging_up_speed - #define PHYS_DODGING_WALL autocvar_sv_dodging_wall_dodging -#endif - -#ifdef SVQC - -float g_dodging; - -// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. -.float dodging_action; - -// the jump part of the dodge cannot be ramped -.float dodging_single_action; - -#include "../../../common/animdecide.qh" -#include "../../../common/physics.qh" - -.float cvar_cl_dodging_timeout; - -.float stat_dodging; -.float stat_dodging_delay; -.float stat_dodging_horiz_speed_frozen; -.float stat_dodging_frozen_nodoubletap; -.float stat_dodging_frozen; -.float stat_dodging_horiz_speed; -.float stat_dodging_height_threshold; -.float stat_dodging_distance_threshold; -.float stat_dodging_ramp_time; -.float stat_dodging_up_speed; -.float stat_dodging_wall; - -REGISTER_MUTATOR(dodging, cvar("g_dodging")) -{ - // this just turns on the cvar. - MUTATOR_ONADD - { - g_dodging = cvar("g_dodging"); - addstat(STAT_DODGING, AS_INT, stat_dodging); - addstat(STAT_DODGING_DELAY, AS_FLOAT, stat_dodging_delay); - addstat(STAT_DODGING_TIMEOUT, AS_FLOAT, cvar_cl_dodging_timeout); // we stat this, so it is updated on the client when updated on server (otherwise, chaos) - addstat(STAT_DODGING_FROZEN_NO_DOUBLETAP, AS_INT, stat_dodging_frozen_nodoubletap); - addstat(STAT_DODGING_HORIZ_SPEED_FROZEN, AS_FLOAT, stat_dodging_horiz_speed_frozen); - addstat(STAT_DODGING_FROZEN, AS_INT, stat_dodging_frozen); - addstat(STAT_DODGING_HORIZ_SPEED, AS_FLOAT, stat_dodging_horiz_speed); - addstat(STAT_DODGING_HEIGHT_THRESHOLD, AS_FLOAT, stat_dodging_height_threshold); - addstat(STAT_DODGING_DISTANCE_THRESHOLD, AS_FLOAT, stat_dodging_distance_threshold); - addstat(STAT_DODGING_RAMP_TIME, AS_FLOAT, stat_dodging_ramp_time); - addstat(STAT_DODGING_UP_SPEED, AS_FLOAT, stat_dodging_up_speed); - addstat(STAT_DODGING_WALL, AS_FLOAT, stat_dodging_wall); - } - - // this just turns off the cvar. - MUTATOR_ONROLLBACK_OR_REMOVE - { - g_dodging = 0; - } - - return false; -} - -#endif - -// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. -.float dodging_action; - -// the jump part of the dodge cannot be ramped -.float dodging_single_action; - - -// these are used to store the last key press time for each of the keys.. -.float last_FORWARD_KEY_time; -.float last_BACKWARD_KEY_time; -.float last_LEFT_KEY_time; -.float last_RIGHT_KEY_time; - -// these store the movement direction at the time of the dodge action happening. -.vector dodging_direction; - -// this indicates the last time a dodge was executed. used to check if another one is allowed -// and to ramp up the dodge acceleration in the physics hook. -.float last_dodging_time; - -// This is the velocity gain to be added over the ramp time. -// It will decrease from frame to frame during dodging_action = 1 -// until it's 0. -.float dodging_velocity_gain; - -#ifdef CSQC -.int pressedkeys; - -#elif defined(SVQC) - -void dodging_UpdateStats() -{SELFPARAM(); - self.stat_dodging = PHYS_DODGING; - self.stat_dodging_delay = PHYS_DODGING_DELAY; - self.stat_dodging_horiz_speed_frozen = PHYS_DODGING_HORIZ_SPEED_FROZEN; - self.stat_dodging_frozen = PHYS_DODGING_FROZEN; - self.stat_dodging_frozen_nodoubletap = PHYS_DODGING_FROZEN_NODOUBLETAP; - self.stat_dodging_height_threshold = PHYS_DODGING_HEIGHT_THRESHOLD; - self.stat_dodging_distance_threshold = PHYS_DODGING_DISTANCE_THRESHOLD; - self.stat_dodging_ramp_time = PHYS_DODGING_RAMP_TIME; - self.stat_dodging_up_speed = PHYS_DODGING_UP_SPEED; - self.stat_dodging_wall = PHYS_DODGING_WALL; -} - -#endif - -// returns 1 if the player is close to a wall -bool check_close_to_wall(float threshold) -{SELFPARAM(); - if (PHYS_DODGING_WALL == 0) { return false; } - - #define X(OFFSET) \ - tracebox(self.origin, self.mins, self.maxs, self.origin + OFFSET, true, self); \ - if (trace_fraction < 1 && vlen (self.origin - trace_endpos) < threshold) \ - return true; - X(1000*v_right); - X(-1000*v_right); - X(1000*v_forward); - X(-1000*v_forward); - #undef X - - return false; -} - -bool check_close_to_ground(float threshold) -{SELFPARAM(); - return IS_ONGROUND(self) ? true : false; -} - -float PM_dodging_checkpressedkeys() -{SELFPARAM(); - if(!PHYS_DODGING) - return false; - - float frozen_dodging = (PHYS_FROZEN(self) && PHYS_DODGING_FROZEN); - float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP); - - // first check if the last dodge is far enough back in time so we can dodge again - if ((time - self.last_dodging_time) < PHYS_DODGING_DELAY) - return false; - - makevectors(self.angles); - - if (check_close_to_ground(PHYS_DODGING_HEIGHT_THRESHOLD) != 1 - && check_close_to_wall(PHYS_DODGING_DISTANCE_THRESHOLD) != 1) - return true; - - float tap_direction_x = 0; - float tap_direction_y = 0; - float dodge_detected = 0; - - #define X(COND,BTN,RESULT) \ - if (self.movement_##COND) \ - /* is this a state change? */ \ - if(!(PHYS_DODGING_PRESSED_KEYS(self) & KEY_##BTN) || frozen_no_doubletap) { \ - tap_direction_##RESULT; \ - if ((time - self.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(self)) \ - dodge_detected = 1; \ - self.last_##BTN##_KEY_time = time; \ - } - X(x < 0, BACKWARD, x--); - X(x > 0, FORWARD, x++); - X(y < 0, LEFT, y--); - X(y > 0, RIGHT, y++); - #undef X - - if (dodge_detected == 1) - { - self.last_dodging_time = time; - - self.dodging_action = 1; - self.dodging_single_action = 1; - - self.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED; - - self.dodging_direction_x = tap_direction_x; - self.dodging_direction_y = tap_direction_y; - - // normalize the dodging_direction vector.. (unlike UT99) XD - float length = self.dodging_direction_x * self.dodging_direction_x - + self.dodging_direction_y * self.dodging_direction_y; - length = sqrt(length); - - self.dodging_direction_x = self.dodging_direction_x * 1.0 / length; - self.dodging_direction_y = self.dodging_direction_y * 1.0 / length; - return true; - } - return false; -} - -void PM_dodging() -{SELFPARAM(); - if (!PHYS_DODGING) - return; - -#ifdef SVQC - dodging_UpdateStats(); -#endif - - if (PHYS_DEAD(self)) - return; - - // when swimming, no dodging allowed.. - if (self.waterlevel >= WATERLEVEL_SWIMMING) - { - self.dodging_action = 0; - self.dodging_direction_x = 0; - self.dodging_direction_y = 0; - return; - } - - // make sure v_up, v_right and v_forward are sane - makevectors(self.angles); - - // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code - // will be called ramp_time/frametime times = 2 times. so, we need to - // add 0.5 * the total speed each frame until the dodge action is done.. - float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME; - - // if ramp time is smaller than frametime we get problems ;D - common_factor = min(common_factor, 1); - - float horiz_speed = PHYS_FROZEN(self) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED; - float new_velocity_gain = self.dodging_velocity_gain - (common_factor * horiz_speed); - new_velocity_gain = max(0, new_velocity_gain); - - float velocity_difference = self.dodging_velocity_gain - new_velocity_gain; - - // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D - if (self.dodging_action == 1) - { - //disable jump key during dodge accel phase - if(self.movement_z > 0) { self.movement_z = 0; } - - self.velocity += ((self.dodging_direction_y * velocity_difference) * v_right) - + ((self.dodging_direction_x * velocity_difference) * v_forward); - - self.dodging_velocity_gain = self.dodging_velocity_gain - velocity_difference; - } - - // the up part of the dodge is a single shot action - if (self.dodging_single_action == 1) - { - UNSET_ONGROUND(self); - - self.velocity += PHYS_DODGING_UP_SPEED * v_up; - -#ifdef SVQC - if (autocvar_sv_dodging_sound) - PlayerSound(playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); - - animdecide_setaction(self, ANIMACTION_JUMP, true); -#endif - - self.dodging_single_action = 0; - } - - // are we done with the dodging ramp yet? - if((self.dodging_action == 1) && ((time - self.last_dodging_time) > PHYS_DODGING_RAMP_TIME)) - { - // reset state so next dodge can be done correctly - self.dodging_action = 0; - self.dodging_direction_x = 0; - self.dodging_direction_y = 0; - } -} - -#ifdef SVQC - -MUTATOR_HOOKFUNCTION(dodging, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_dodging_timeout, "cl_dodging_timeout"); - return false; -} - -MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics) -{ - // print("dodging_PlayerPhysics\n"); - PM_dodging(); - - return false; -} - -MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys) -{ - PM_dodging_checkpressedkeys(); - - return false; -} - -#endif - -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_hook.qc b/qcsrc/server/mutators/mutator/mutator_hook.qc deleted file mode 100644 index b298e7b2e..000000000 --- a/qcsrc/server/mutators/mutator/mutator_hook.qc +++ /dev/null @@ -1,42 +0,0 @@ -#ifdef IMPLEMENTATION -AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up")); -#ifdef SVQC -REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) { - MUTATOR_ONADD { - g_grappling_hook = true; - WEP_HOOK.ammo_factor = 0; - } - MUTATOR_ONROLLBACK_OR_REMOVE { - g_grappling_hook = false; - WEP_HOOK.ammo_factor = 1; - } -} - -MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":grappling_hook"); -} - -MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Hook"); -} - -MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString) -{ - ret_string = strcat(ret_string, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n"); -} - -MUTATOR_HOOKFUNCTION(hook, PlayerSpawn) -{ - SELFPARAM(); - self.offhand = OFFHAND_HOOK; -} - -MUTATOR_HOOKFUNCTION(hook, FilterItem) -{ - return self.weapon == WEP_HOOK.m_id; -} - -#endif -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_invincibleproj.qc b/qcsrc/server/mutators/mutator/mutator_invincibleproj.qc deleted file mode 100644 index 5a781a881..000000000 --- a/qcsrc/server/mutators/mutator/mutator_invincibleproj.qc +++ /dev/null @@ -1,25 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles")); - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) -{ - if(other.health) - { - // disable health which in effect disables damage calculations - other.health = 0; - } - return 0; -} - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":InvincibleProjectiles"); - return 0; -} - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Invincible Projectiles"); - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_melee_only.qc b/qcsrc/server/mutators/mutator/mutator_melee_only.qc deleted file mode 100644 index 5b03f46ec..000000000 --- a/qcsrc/server/mutators/mutator/mutator_melee_only.qc +++ /dev/null @@ -1,40 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball); - -MUTATOR_HOOKFUNCTION(melee_only, SetStartItems) -{ - start_ammo_shells = warmup_start_ammo_shells = 0; - start_weapons = warmup_start_weapons = WEPSET(SHOTGUN); - - return false; -} - -MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(melee_only, FilterItem) -{SELFPARAM(); - switch (self.items) - { - case ITEM_HealthSmall.m_itemid: - case ITEM_ArmorSmall.m_itemid: - return false; - } - - return true; -} - -MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":MeleeOnly"); - return false; -} - -MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Melee Only Arena"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_midair.qc b/qcsrc/server/mutators/mutator/mutator_midair.qc deleted file mode 100644 index bf3428334..000000000 --- a/qcsrc/server/mutators/mutator/mutator_midair.qc +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(midair, cvar("g_midair")); - -.float midair_shieldtime; - -MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate) -{SELFPARAM(); - if(IS_PLAYER(frag_attacker)) - if(IS_PLAYER(frag_target)) - if(time < self.midair_shieldtime) - frag_damage = false; - - return false; -} - -MUTATOR_HOOKFUNCTION(midair, PlayerPowerups) -{SELFPARAM(); - if(time >= game_starttime) - if(self.flags & FL_ONGROUND) - { - self.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); - self.midair_shieldtime = max(self.midair_shieldtime, time + autocvar_g_midair_shieldtime); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(midair, PlayerSpawn) -{SELFPARAM(); - if(IS_BOT_CLIENT(self)) - self.bot_moveskill = 0; // disable bunnyhopping - - return false; -} - -MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":midair"); - return false; -} - -MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Midair"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_multijump.qc b/qcsrc/server/mutators/mutator/mutator_multijump.qc deleted file mode 100644 index f01a801aa..000000000 --- a/qcsrc/server/mutators/mutator/mutator_multijump.qc +++ /dev/null @@ -1,182 +0,0 @@ -#ifdef IMPLEMENTATION -#ifdef SVQC - #include "../../antilag.qh" -#endif -#include "../../../common/physics.qh" - -.int multijump_count; -.bool multijump_ready; -.bool cvar_cl_multijump; - -#ifdef CSQC - -#define PHYS_MULTIJUMP getstati(STAT_MULTIJUMP) -#define PHYS_MULTIJUMP_SPEED getstatf(STAT_MULTIJUMP_SPEED) -#define PHYS_MULTIJUMP_ADD getstati(STAT_MULTIJUMP_ADD) -#define PHYS_MULTIJUMP_MAXSPEED getstatf(STAT_MULTIJUMP_MAXSPEED) -#define PHYS_MULTIJUMP_DODGING getstati(STAT_MULTIJUMP_DODGING) - -#elif defined(SVQC) - -#define PHYS_MULTIJUMP autocvar_g_multijump -#define PHYS_MULTIJUMP_SPEED autocvar_g_multijump_speed -#define PHYS_MULTIJUMP_ADD autocvar_g_multijump_add -#define PHYS_MULTIJUMP_MAXSPEED autocvar_g_multijump_maxspeed -#define PHYS_MULTIJUMP_DODGING autocvar_g_multijump_dodging - - -.float stat_multijump; -.float stat_multijump_speed; -.float stat_multijump_add; -.float stat_multijump_maxspeed; -.float stat_multijump_dodging; - -void multijump_UpdateStats() -{SELFPARAM(); - self.stat_multijump = PHYS_MULTIJUMP; - self.stat_multijump_speed = PHYS_MULTIJUMP_SPEED; - self.stat_multijump_add = PHYS_MULTIJUMP_ADD; - self.stat_multijump_maxspeed = PHYS_MULTIJUMP_MAXSPEED; - self.stat_multijump_dodging = PHYS_MULTIJUMP_DODGING; -} - -void multijump_AddStats() -{ - addstat(STAT_MULTIJUMP, AS_INT, stat_multijump); - addstat(STAT_MULTIJUMP_SPEED, AS_FLOAT, stat_multijump_speed); - addstat(STAT_MULTIJUMP_ADD, AS_INT, stat_multijump_add); - addstat(STAT_MULTIJUMP_MAXSPEED, AS_FLOAT, stat_multijump_maxspeed); - addstat(STAT_MULTIJUMP_DODGING, AS_INT, stat_multijump_dodging); -} - -#endif - -void PM_multijump() -{SELFPARAM(); - if(!PHYS_MULTIJUMP) { return; } - - if(IS_ONGROUND(self)) - { - self.multijump_count = 0; - } -} - -bool PM_multijump_checkjump() -{SELFPARAM(); - if(!PHYS_MULTIJUMP) { return false; } - -#ifdef SVQC - bool client_multijump = self.cvar_cl_multijump; -#elif defined(CSQC) - bool client_multijump = cvar("cl_multijump"); - - if(cvar("cl_multijump") > 1) - return false; // nope -#endif - - if (!IS_JUMP_HELD(self) && !IS_ONGROUND(self) && client_multijump) // jump button pressed this frame and we are in midair - self.multijump_ready = true; // this is necessary to check that we released the jump button and pressed it again - else - self.multijump_ready = false; - - int phys_multijump = PHYS_MULTIJUMP; - -#ifdef CSQC - phys_multijump = (PHYS_MULTIJUMP) ? -1 : 0; -#endif - - if(!player_multijump && self.multijump_ready && (self.multijump_count < phys_multijump || phys_multijump == -1) && self.velocity_z > PHYS_MULTIJUMP_SPEED && (!PHYS_MULTIJUMP_MAXSPEED || vlen(self.velocity) <= PHYS_MULTIJUMP_MAXSPEED)) - { - if (PHYS_MULTIJUMP) - { - if (!PHYS_MULTIJUMP_ADD) // in this case we make the z velocity == jumpvelocity - { - if (self.velocity_z < PHYS_JUMPVELOCITY) - { - player_multijump = true; - self.velocity_z = 0; - } - } - else - player_multijump = true; - - if(player_multijump) - { - if(PHYS_MULTIJUMP_DODGING) - if(self.movement_x != 0 || self.movement_y != 0) // don't remove all speed if player isnt pressing any movement keys - { - float curspeed; - vector wishvel, wishdir; - -/*#ifdef SVQC - curspeed = max( - vlen(vec2(self.velocity)), // current xy speed - vlen(vec2(antilag_takebackavgvelocity(self, max(self.lastteleporttime + sys_frametime, time - 0.25), time))) // average xy topspeed over the last 0.25 secs - ); -#elif defined(CSQC)*/ - curspeed = vlen(vec2(self.velocity)); -//#endif - - makevectors(self.v_angle_y * '0 1 0'); - wishvel = v_forward * self.movement_x + v_right * self.movement_y; - wishdir = normalize(wishvel); - - self.velocity_x = wishdir_x * curspeed; // allow "dodging" at a multijump - self.velocity_y = wishdir_y * curspeed; - // keep velocity_z unchanged! - } - if (PHYS_MULTIJUMP > 0) - { - self.multijump_count += 1; - } - } - } - self.multijump_ready = false; // require releasing and pressing the jump button again for the next jump - } - - return false; -} - -#ifdef SVQC -REGISTER_MUTATOR(multijump, cvar("g_multijump")) -{ - MUTATOR_ONADD - { - multijump_AddStats(); - } - return false; -} - -MUTATOR_HOOKFUNCTION(multijump, PlayerPhysics) -{ - multijump_UpdateStats(); - PM_multijump(); - - return false; -} - -MUTATOR_HOOKFUNCTION(multijump, PlayerJump) -{ - return PM_multijump_checkjump(); -} - -MUTATOR_HOOKFUNCTION(multijump, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_multijump, "cl_multijump"); - return false; -} - -MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":multijump"); - return false; -} - -MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Multi jump"); - return false; -} - -#endif -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_nades.qc b/qcsrc/server/mutators/mutator/mutator_nades.qc deleted file mode 100644 index 9cfc2ec41..000000000 --- a/qcsrc/server/mutators/mutator/mutator_nades.qc +++ /dev/null @@ -1,1239 +0,0 @@ -#ifndef MUTATOR_NADES_H -#define MUTATOR_NADES_H - -.entity nade; -.entity fake_nade; -.float nade_timer; -.float nade_refire; -.float bonus_nades; -.float nade_special_time; -.float bonus_nade_score; -.float nade_type; -.string pokenade_type; -.entity nade_damage_target; -.float cvar_cl_nade_type; -.string cvar_cl_pokenade_type; -.float toss_time; -.float stat_healing_orb; -.float stat_healing_orb_alpha; -.float nade_show_particles; - -// Remove nades that are being thrown -void(entity player) nades_Clear; - -// Give a bonus grenade to a player -void(entity player, float score) nades_GiveBonus; -// Remove all bonus nades from a player -void(entity player) nades_RemoveBonus; - -/** - * called to adjust nade damage and force on hit - */ -#define EV_Nade_Damage(i, o) \ - /** weapon */ i(entity, MUTATOR_ARGV_0_entity) \ - /** force */ i(vector, MUTATOR_ARGV_0_vector) \ - /**/ o(vector, MUTATOR_ARGV_0_vector) \ - /** damage */ i(float, MUTATOR_ARGV_0_float) \ - /**/ o(float, MUTATOR_ARGV_0_float) \ - /**/ -MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage); - -#endif -#ifdef IMPLEMENTATION - -#include "../../../common/nades/all.qh" -#include "../../../common/gamemodes/all.qh" -#include "../../../common/monsters/spawn.qh" -#include "../../../common/monsters/sv_monsters.qh" -#include "../../g_subs.qh" - -REGISTER_MUTATOR(nades, cvar("g_nades")) -{ - MUTATOR_ONADD - { - addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer); - addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades); - addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type); - addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score); - addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb); - addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha); - } - - return false; -} - -.float nade_time_primed; - -.entity nade_spawnloc; - -void nade_timer_think() -{SELFPARAM(); - self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10); - self.nextthink = time; - if(!self.owner || wasfreed(self.owner)) - remove(self); -} - -void nade_burn_spawn(entity _nade) -{ - CSQCProjectile(_nade, true, Nades_from(_nade.nade_type).m_projectile[true], true); -} - -void nade_spawn(entity _nade) -{ - entity timer = new(nade_timer); - setmodel(timer, MDL_NADE_TIMER); - setattachment(timer, _nade, ""); - timer.colormap = _nade.colormap; - timer.glowmod = _nade.glowmod; - timer.think = nade_timer_think; - timer.nextthink = time; - timer.wait = _nade.wait; - timer.owner = _nade; - timer.skin = 10; - - _nade.effects |= EF_LOWPRECISION; - - CSQCProjectile(_nade, true, Nades_from(_nade.nade_type).m_projectile[false], true); -} - -void napalm_damage(float dist, float damage, float edgedamage, float burntime) -{SELFPARAM(); - entity e; - float d; - vector p; - - if ( damage < 0 ) - return; - - RandomSelection_Init(); - for(e = WarpZone_FindRadius(self.origin, dist, true); e; e = e.chain) - if(e.takedamage == DAMAGE_AIM) - if(self.realowner != e || autocvar_g_nades_napalm_selfdamage) - if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self)) - if(!e.frozen) - { - p = e.origin; - p.x += e.mins.x + random() * (e.maxs.x - e.mins.x); - p.y += e.mins.y + random() * (e.maxs.y - e.mins.y); - p.z += e.mins.z + random() * (e.maxs.z - e.mins.z); - d = vlen(WarpZone_UnTransformOrigin(e, self.origin) - p); - if(d < dist) - { - e.fireball_impactvec = p; - RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e)); - } - } - if(RandomSelection_chosen_ent) - { - d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec); - d = damage + (edgedamage - damage) * (d / dist); - Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE); - //trailparticles(self, particleeffectnum(EFFECT_FIREBALL_LASER), self.origin, RandomSelection_chosen_ent.fireball_impactvec); - Send_Effect(EFFECT_FIREBALL_LASER, self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1); - } -} - - -void napalm_ball_think() -{SELFPARAM(); - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - remove(self); - return; - } - - if(time > self.pushltime) - { - remove(self); - return; - } - - vector midpoint = ((self.absmin + self.absmax) * 0.5); - if(pointcontents(midpoint) == CONTENT_WATER) - { - self.velocity = self.velocity * 0.5; - - if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) - { self.velocity_z = 200; } - } - - self.angles = vectoangles(self.velocity); - - napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage, - autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime); - - self.nextthink = time + 0.1; -} - - -void nade_napalm_ball() -{SELFPARAM(); - entity proj; - vector kick; - - spamsound(self, CH_SHOTS, SND(FIREBALL_FIRE), VOL_BASE, ATTEN_NORM); - - proj = new(grenade); - proj.owner = self.owner; - proj.realowner = self.realowner; - proj.team = self.owner.team; - proj.bot_dodge = true; - proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage; - proj.movetype = MOVETYPE_BOUNCE; - proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id; - PROJECTILE_MAKETRIGGER(proj); - setmodel(proj, MDL_Null); - proj.scale = 1;//0.5; - setsize(proj, '-4 -4 -4', '4 4 4'); - setorigin(proj, self.origin); - proj.think = napalm_ball_think; - proj.nextthink = time; - proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale; - proj.effects = EF_LOWPRECISION | EF_FLAME; - - kick.x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; - kick.y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; - kick.z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread; - proj.velocity = kick; - - proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime; - - proj.angles = vectoangles(proj.velocity); - proj.flags = FL_PROJECTILE; - proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; - - //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true); -} - - -void napalm_fountain_think() -{SELFPARAM(); - - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - remove(self); - return; - } - - if(time >= self.ltime) - { - remove(self); - return; - } - - vector midpoint = ((self.absmin + self.absmax) * 0.5); - if(pointcontents(midpoint) == CONTENT_WATER) - { - self.velocity = self.velocity * 0.5; - - if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) - { self.velocity_z = 200; } - - UpdateCSQCProjectile(self); - } - - napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage, - autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime); - - self.nextthink = time + 0.1; - if(time >= self.nade_special_time) - { - self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay; - nade_napalm_ball(); - } -} - -void nade_napalm_boom() -{SELFPARAM(); - entity fountain; - int c; - for (c = 0; c < autocvar_g_nades_napalm_ball_count; c++) - nade_napalm_ball(); - - - fountain = spawn(); - fountain.owner = self.owner; - fountain.realowner = self.realowner; - fountain.origin = self.origin; - setorigin(fountain, fountain.origin); - fountain.think = napalm_fountain_think; - fountain.nextthink = time; - fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime; - fountain.pushltime = fountain.ltime; - fountain.team = self.team; - fountain.movetype = MOVETYPE_TOSS; - fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id; - fountain.bot_dodge = true; - fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage; - fountain.nade_special_time = time; - setsize(fountain, '-16 -16 -16', '16 16 16'); - CSQCProjectile(fountain, true, PROJECTILE_NAPALM_FOUNTAIN, true); -} - -void nade_ice_freeze(entity freezefield, entity frost_target, float freeze_time) -{ - frost_target.frozen_by = freezefield.realowner; - Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1); - Freeze(frost_target, 1/freeze_time, 3, false); - - Drop_Special_Items(frost_target); -} - -void nade_ice_think() -{SELFPARAM(); - - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - remove(self); - return; - } - - if(time >= self.ltime) - { - if ( autocvar_g_nades_ice_explode ) - { - entity expef = EFFECT_NADE_EXPLODE(self.realowner.team); - Send_Effect(expef, self.origin + '0 0 1', '0 0 0', 1); - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - - RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); - Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); - } - remove(self); - return; - } - - - self.nextthink = time+0.1; - - // gaussian - float randomr; - randomr = random(); - randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius; - float randomw; - randomw = random()*M_PI*2; - vector randomp; - randomp.x = randomr*cos(randomw); - randomp.y = randomr*sin(randomw); - randomp.z = 1; - Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, self.origin + randomp, '0 0 0', 1); - - if(time >= self.nade_special_time) - { - self.nade_special_time = time+0.7; - - Send_Effect(EFFECT_ELECTRO_IMPACT, self.origin, '0 0 0', 1); - Send_Effect(EFFECT_ICEFIELD, self.origin, '0 0 0', 1); - } - - - float current_freeze_time = self.ltime - time - 0.1; - - entity e; - for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain) - if(e != self) - if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner)) - if(e.takedamage && e.deadflag == DEAD_NO) - if(e.health > 0) - if(!e.revival_time || ((time - e.revival_time) >= 1.5)) - if(!e.frozen) - if(current_freeze_time > 0) - nade_ice_freeze(self, e, current_freeze_time); -} - -void nade_ice_boom() -{SELFPARAM(); - entity fountain; - fountain = spawn(); - fountain.owner = self.owner; - fountain.realowner = self.realowner; - fountain.origin = self.origin; - setorigin(fountain, fountain.origin); - fountain.think = nade_ice_think; - fountain.nextthink = time; - fountain.ltime = time + autocvar_g_nades_ice_freeze_time; - fountain.pushltime = fountain.wait = fountain.ltime; - fountain.team = self.team; - fountain.movetype = MOVETYPE_TOSS; - fountain.projectiledeathtype = DEATH_NADE_ICE.m_id; - fountain.bot_dodge = false; - setsize(fountain, '-16 -16 -16', '16 16 16'); - fountain.nade_special_time = time+0.3; - fountain.angles = self.angles; - - if ( autocvar_g_nades_ice_explode ) - { - setmodel(fountain, MDL_PROJECTILE_GRENADE); - entity timer = new(nade_timer); - setmodel(timer, MDL_NADE_TIMER); - setattachment(timer, fountain, ""); - timer.colormap = self.colormap; - timer.glowmod = self.glowmod; - timer.think = nade_timer_think; - timer.nextthink = time; - timer.wait = fountain.ltime; - timer.owner = fountain; - timer.skin = 10; - } - else - setmodel(fountain, MDL_Null); -} - -void nade_translocate_boom() -{SELFPARAM(); - if(self.realowner.vehicle) - return; - - vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins.z - 24); - tracebox(locout, self.realowner.mins, self.realowner.maxs, locout, MOVE_NOMONSTERS, self.realowner); - locout = trace_endpos; - - makevectors(self.realowner.angles); - - MUTATOR_CALLHOOK(PortalTeleport, self.realowner); - - TeleportPlayer(self, self.realowner, locout, self.realowner.angles, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); -} - -void nade_spawn_boom() -{SELFPARAM(); - entity spawnloc = spawn(); - setorigin(spawnloc, self.origin); - setsize(spawnloc, self.realowner.mins, self.realowner.maxs); - spawnloc.movetype = MOVETYPE_NONE; - spawnloc.solid = SOLID_NOT; - spawnloc.drawonlytoclient = self.realowner; - spawnloc.effects = EF_STARDUST; - spawnloc.cnt = autocvar_g_nades_spawn_count; - - if(self.realowner.nade_spawnloc) - { - remove(self.realowner.nade_spawnloc); - self.realowner.nade_spawnloc = world; - } - - self.realowner.nade_spawnloc = spawnloc; -} - -void nade_heal_think() -{SELFPARAM(); - if(time >= self.ltime) - { - remove(self); - return; - } - - self.nextthink = time; - - if(time >= self.nade_special_time) - { - self.nade_special_time = time+0.25; - self.nade_show_particles = 1; - } - else - self.nade_show_particles = 0; -} - -void nade_heal_touch() -{SELFPARAM(); - float maxhealth; - float health_factor; - if(IS_PLAYER(other) || IS_MONSTER(other)) - if(other.deadflag == DEAD_NO) - if(!other.frozen) - { - health_factor = autocvar_g_nades_heal_rate*frametime/2; - if ( other != self.realowner ) - { - if ( SAME_TEAM(other,self) ) - health_factor *= autocvar_g_nades_heal_friend; - else - health_factor *= autocvar_g_nades_heal_foe; - } - if ( health_factor > 0 ) - { - maxhealth = (IS_MONSTER(other)) ? other.max_health : g_pickup_healthmega_max; - if ( other.health < maxhealth ) - { - if ( self.nade_show_particles ) - Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1); - other.health = min(other.health+health_factor, maxhealth); - } - other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); - } - else if ( health_factor < 0 ) - { - Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL.m_id,other.origin,'0 0 0'); - } - - } - - if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) ) - { - entity show_red = (IS_VEHICLE(other)) ? other.owner : other; - show_red.stat_healing_orb = time+0.1; - show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime; - } -} - -void nade_heal_boom() -{SELFPARAM(); - entity healer; - healer = spawn(); - healer.owner = self.owner; - healer.realowner = self.realowner; - setorigin(healer, self.origin); - healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar - healer.ltime = time + healer.healer_lifetime; - healer.team = self.realowner.team; - healer.bot_dodge = false; - healer.solid = SOLID_TRIGGER; - healer.touch = nade_heal_touch; - - setmodel(healer, MDL_NADE_HEAL); - healer.healer_radius = autocvar_g_nades_nade_radius; - vector size = '1 1 1' * healer.healer_radius / 2; - setsize(healer,-size,size); - - Net_LinkEntity(healer, true, 0, healer_send); - - healer.think = nade_heal_think; - healer.nextthink = time; - healer.SendFlags |= 1; -} - -void nade_monster_boom() -{SELFPARAM(); - entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, false, false, 1); - - if(autocvar_g_nades_pokenade_monster_lifetime > 0) - e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime; - e.monster_skill = MONSTER_SKILL_INSANE; -} - -void nade_boom() -{SELFPARAM(); - entity expef = NULL; - bool nade_blast = true; - - switch ( Nades_from(self.nade_type) ) - { - case NADE_TYPE_NAPALM: - nade_blast = autocvar_g_nades_napalm_blast; - expef = EFFECT_EXPLOSION_MEDIUM; - break; - case NADE_TYPE_ICE: - nade_blast = false; - expef = EFFECT_ELECTRO_COMBO; // hookbomb_explode electro_combo bigplasma_impact - break; - case NADE_TYPE_TRANSLOCATE: - nade_blast = false; - break; - case NADE_TYPE_MONSTER: - case NADE_TYPE_SPAWN: - nade_blast = false; - switch(self.realowner.team) - { - case NUM_TEAM_1: expef = EFFECT_SPAWN_RED; break; - case NUM_TEAM_2: expef = EFFECT_SPAWN_BLUE; break; - case NUM_TEAM_3: expef = EFFECT_SPAWN_YELLOW; break; - case NUM_TEAM_4: expef = EFFECT_SPAWN_PINK; break; - default: expef = EFFECT_SPAWN_NEUTRAL; break; - } - break; - case NADE_TYPE_HEAL: - nade_blast = false; - expef = EFFECT_SPAWN_RED; - break; - - default: - case NADE_TYPE_NORMAL: - expef = EFFECT_NADE_EXPLODE(self.realowner.team); - break; - } - - if(expef) - Send_Effect(expef, findbetterlocation(self.origin, 8), '0 0 0', 1); - - sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); - sound(self, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - - self.event_damage = func_null; // prevent somehow calling damage in the next call - - if(nade_blast) - { - RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, self, world, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy); - Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self); - } - - if(self.takedamage) - switch ( Nades_from(self.nade_type) ) - { - case NADE_TYPE_NAPALM: nade_napalm_boom(); break; - case NADE_TYPE_ICE: nade_ice_boom(); break; - case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break; - case NADE_TYPE_SPAWN: nade_spawn_boom(); break; - case NADE_TYPE_HEAL: nade_heal_boom(); break; - case NADE_TYPE_MONSTER: nade_monster_boom(); break; - } - - entity head; - for(head = world; (head = find(head, classname, "grapplinghook")); ) - if(head.aiment == self) - RemoveGrapplingHook(head.realowner); - - remove(self); -} - -void nade_touch() -{SELFPARAM(); - /*float is_weapclip = 0; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) - if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) - if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) - is_weapclip = 1;*/ - if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip) - { - entity head; - for(head = world; (head = find(head, classname, "grapplinghook")); ) - if(head.aiment == self) - RemoveGrapplingHook(head.realowner); - remove(self); - return; - } - - PROJECTILE_TOUCH; - - //setsize(self, '-2 -2 -2', '2 2 2'); - //UpdateCSQCProjectile(self); - if(self.health == self.max_health) - { - spamsound(self, CH_SHOTS, SND(GRENADE_BOUNCE_RANDOM()), VOL_BASE, ATTEN_NORM); - return; - } - - self.enemy = other; - nade_boom(); -} - -void nade_beep() -{SELFPARAM(); - sound(self, CH_SHOTS_SINGLE, SND_NADE_BEEP, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - self.think = nade_boom; - self.nextthink = max(self.wait, time); -} - -void nade_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - self.takedamage = DAMAGE_NO; - nade_boom(); - return; - } - - if(self.nade_type == NADE_TYPE_TRANSLOCATE.m_id || self.nade_type == NADE_TYPE_SPAWN.m_id) - return; - - if (MUTATOR_CALLHOOK(Nade_Damage, DEATH_WEAPONOF(deathtype), force, damage)) {} - else if(DEATH_ISWEAPON(deathtype, WEP_BLASTER)) - { - force *= 1.5; - damage = 0; - } - else if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER) && (deathtype & HITTYPE_SECONDARY)) - { - force *= 0.5; // too much - damage = 0; - } - else if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) - { - force *= 6; - damage = self.max_health * 0.55; - } - else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) - damage = self.max_health * 0.1; - else if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) // WEAPONTODO - { - if(deathtype & HITTYPE_SECONDARY) - { - damage = self.max_health * 0.1; - force *= 10; - } - else - damage = self.max_health * 1.15; - } - - self.velocity += force; - UpdateCSQCProjectile(self); - - if(damage <= 0 || ((self.flags & FL_ONGROUND) && IS_PLAYER(attacker))) - return; - - if(self.health == self.max_health) - { - sound(self, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - self.nextthink = max(time + autocvar_g_nades_nade_lifetime, time); - self.think = nade_beep; - } - - self.health -= damage; - - if ( self.nade_type != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker) ) - self.realowner = attacker; - - if(self.health <= 0) - W_PrepareExplosionByDamage(attacker, nade_boom); - else - nade_burn_spawn(self); -} - -void toss_nade(entity e, vector _velocity, float _time) -{SELFPARAM(); - if(e.nade == world) - return; - - entity _nade = e.nade; - e.nade = world; - - remove(e.fake_nade); - e.fake_nade = world; - - makevectors(e.v_angle); - - W_SetupShot(e, false, false, "", CH_WEAPON_A, 0); - - Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES); - - vector offset = (v_forward * autocvar_g_nades_throw_offset.x) - + (v_right * autocvar_g_nades_throw_offset.y) - + (v_up * autocvar_g_nades_throw_offset.z); - if(autocvar_g_nades_throw_offset == '0 0 0') - offset = '0 0 0'; - - setorigin(_nade, w_shotorg + offset + (v_right * 25) * -1); - //setmodel(_nade, MDL_PROJECTILE_NADE); - //setattachment(_nade, world, ""); - PROJECTILE_MAKETRIGGER(_nade); - setsize(_nade, '-16 -16 -16', '16 16 16'); - _nade.movetype = MOVETYPE_BOUNCE; - - tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade); - if (trace_startsolid) - setorigin(_nade, e.origin); - - if(self.v_angle.x >= 70 && self.v_angle.x <= 110 && self.BUTTON_CROUCH) - _nade.velocity = '0 0 100'; - else if(autocvar_g_nades_nade_newton_style == 1) - _nade.velocity = e.velocity + _velocity; - else if(autocvar_g_nades_nade_newton_style == 2) - _nade.velocity = _velocity; - else - _nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, true); - - _nade.touch = nade_touch; - _nade.health = autocvar_g_nades_nade_health; - _nade.max_health = _nade.health; - _nade.takedamage = DAMAGE_AIM; - _nade.event_damage = nade_damage; - _nade.customizeentityforclient = func_null; - _nade.exteriormodeltoclient = world; - _nade.traileffectnum = 0; - _nade.teleportable = true; - _nade.pushable = true; - _nade.gravity = 1; - _nade.missile_flags = MIF_SPLASH | MIF_ARC; - _nade.damagedbycontents = true; - _nade.angles = vectoangles(_nade.velocity); - _nade.flags = FL_PROJECTILE; - _nade.projectiledeathtype = DEATH_NADE.m_id; - _nade.toss_time = time; - _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX); - - if(_nade.nade_type == NADE_TYPE_TRANSLOCATE.m_id || _nade.nade_type == NADE_TYPE_SPAWN.m_id) - _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - else - _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - - nade_spawn(_nade); - - if(_time) - { - _nade.think = nade_boom; - _nade.nextthink = _time; - } - - e.nade_refire = time + autocvar_g_nades_nade_refire; - e.nade_timer = 0; -} - -void nades_GiveBonus(entity player, float score) -{ - if (autocvar_g_nades) - if (autocvar_g_nades_bonus) - if (IS_REAL_CLIENT(player)) - if (IS_PLAYER(player) && player.bonus_nades < autocvar_g_nades_bonus_max) - if (player.frozen == 0) - if (player.deadflag == DEAD_NO) - { - if ( player.bonus_nade_score < 1 ) - player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max; - - if ( player.bonus_nade_score >= 1 ) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS); - play2(player, SND(KH_ALARM)); - player.bonus_nades++; - player.bonus_nade_score -= 1; - } - } -} - -void nades_RemoveBonus(entity player) -{ - player.bonus_nades = player.bonus_nade_score = 0; -} - -float nade_customize() -{SELFPARAM(); - //if(IS_SPEC(other)) { return false; } - if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner)) - { - // somewhat hide the model, but keep the glow - //self.effects = 0; - if(self.traileffectnum) - self.traileffectnum = 0; - self.alpha = -1; - } - else - { - //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - if(!self.traileffectnum) - self.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades_from(self.nade_type).m_projectile[false], self.team).eent_eff_name); - self.alpha = 1; - } - - return true; -} - -void nade_prime() -{SELFPARAM(); - if(autocvar_g_nades_bonus_only) - if(!self.bonus_nades) - return; // only allow bonus nades - - if(self.nade) - remove(self.nade); - - if(self.fake_nade) - remove(self.fake_nade); - - entity n = new(nade), fn = new(fake_nade); - - if(self.items & ITEM_Strength.m_itemid && autocvar_g_nades_bonus_onstrength) - n.nade_type = self.nade_type; - else if (self.bonus_nades >= 1) - { - n.nade_type = self.nade_type; - n.pokenade_type = self.pokenade_type; - self.bonus_nades -= 1; - } - else - { - n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type); - n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type); - } - - n.nade_type = bound(1, n.nade_type, Nades_COUNT); - - setmodel(n, MDL_PROJECTILE_NADE); - //setattachment(n, self, "bip01 l hand"); - n.exteriormodeltoclient = self; - n.customizeentityforclient = nade_customize; - n.traileffectnum = _particleeffectnum(Nade_TrailEffect(Nades_from(n.nade_type).m_projectile[false], self.team).eent_eff_name); - n.colormod = Nades_from(n.nade_type).m_color; - n.realowner = self; - n.colormap = self.colormap; - n.glowmod = self.glowmod; - n.wait = time + autocvar_g_nades_nade_lifetime; - n.nade_time_primed = time; - n.think = nade_beep; - n.nextthink = max(n.wait - 3, time); - n.projectiledeathtype = DEATH_NADE.m_id; - - setmodel(fn, MDL_NADE_VIEW); - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - setattachment(fn, self.(weaponentity), ""); - fn.realowner = fn.owner = self; - fn.colormod = Nades_from(n.nade_type).m_color; - fn.colormap = self.colormap; - fn.glowmod = self.glowmod; - fn.think = SUB_Remove; - fn.nextthink = n.wait; - - self.nade = n; - self.fake_nade = fn; -} - -float CanThrowNade() -{SELFPARAM(); - if(self.vehicle) - return false; - - if(gameover) - return false; - - if(self.deadflag != DEAD_NO) - return false; - - if (!autocvar_g_nades) - return false; // allow turning them off mid match - - if(forbidWeaponUse(self)) - return false; - - if (!IS_PLAYER(self)) - return false; - - return true; -} - -.bool nade_altbutton; - -void nades_CheckThrow() -{SELFPARAM(); - if(!CanThrowNade()) - return; - - entity held_nade = self.nade; - if (!held_nade) - { - self.nade_altbutton = true; - if(time > self.nade_refire) - { - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_NADE_THROW); - nade_prime(); - self.nade_refire = time + autocvar_g_nades_nade_refire; - } - } - else - { - self.nade_altbutton = false; - if (time >= held_nade.nade_time_primed + 1) { - makevectors(self.v_angle); - float _force = time - held_nade.nade_time_primed; - _force /= autocvar_g_nades_nade_lifetime; - _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); - toss_nade(self, (v_forward * 0.75 + v_up * 0.2 + v_right * 0.05) * _force, 0); - } - } -} - -void nades_Clear(entity player) -{ - if(player.nade) - remove(player.nade); - if(player.fake_nade) - remove(player.fake_nade); - - player.nade = player.fake_nade = world; - player.nade_timer = 0; -} - -MUTATOR_HOOKFUNCTION(nades, VehicleEnter) -{ - if(vh_player.nade) - toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05)); - - return false; -} - -CLASS(NadeOffhand, OffhandWeapon) - METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed)) - { - entity held_nade = player.nade; - if (held_nade) - { - player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1); - // LOG_TRACEF("%d %d\n", player.nade_timer, time - held_nade.nade_time_primed); - makevectors(player.angles); - held_nade.velocity = player.velocity; - setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0); - held_nade.angles_y = player.angles.y; - - if (time + 0.1 >= held_nade.wait) - toss_nade(player, '0 0 0', time + 0.05); - } - - if (!CanThrowNade()) return; - if (!(time > player.nade_refire)) return; - if (key_pressed) { - if (!held_nade) { - nade_prime(); - held_nade = player.nade; - } - } else if (time >= held_nade.nade_time_primed + 1) { - if (held_nade) { - makevectors(player.v_angle); - float _force = time - held_nade.nade_time_primed; - _force /= autocvar_g_nades_nade_lifetime; - _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); - toss_nade(player, (v_forward * 0.7 + v_up * 0.2 + v_right * 0.1) * _force, 0); - } - } - } -ENDCLASS(NadeOffhand) -NadeOffhand OFFHAND_NADE; STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); } - -MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST) -{ - if (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) { - nades_CheckThrow(); - return true; - } - return false; -} - -MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) -{SELFPARAM(); - if (!IS_PLAYER(self)) { return false; } - - if (self.nade && (self.offhand != OFFHAND_NADE || (self.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, self, self.nade_altbutton); - - if(IS_PLAYER(self)) - { - if ( autocvar_g_nades_bonus && autocvar_g_nades ) - { - entity key; - float key_count = 0; - FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; } - - float time_score; - if(self.flagcarried || self.ballcarried) // this player is important - time_score = autocvar_g_nades_bonus_score_time_flagcarrier; - else - time_score = autocvar_g_nades_bonus_score_time; - - if(key_count) - time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding - - if(autocvar_g_nades_bonus_client_select) - { - self.nade_type = self.cvar_cl_nade_type; - self.pokenade_type = self.cvar_cl_pokenade_type; - } - else - { - self.nade_type = autocvar_g_nades_bonus_type; - self.pokenade_type = autocvar_g_nades_pokenade_monster_type; - } - - self.nade_type = bound(1, self.nade_type, Nades_COUNT); - - if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max) - nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max); - } - else - { - self.bonus_nades = self.bonus_nade_score = 0; - } - } - - float n = 0; - entity o = world; - if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout) - n = -1; - else - { - vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; - n = 0; - FOR_EACH_PLAYER(other) if(self != other) - { - if(other.deadflag == DEAD_NO) - if(other.frozen == 0) - if(SAME_TEAM(other, self)) - if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax)) - { - if(!o) - o = other; - if(self.frozen == 1) - other.reviving = true; - ++n; - } - } - } - - if(n && self.frozen == 3) // OK, there is at least one teammate reviving us - { - self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); - self.health = max(1, self.revive_progress * start_health); - - if(self.revive_progress >= 1) - { - Unfreeze(self); - - Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname); - Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname); - } - - FOR_EACH_PLAYER(other) if(other.reviving) - { - other.revive_progress = self.revive_progress; - other.reviving = false; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) -{SELFPARAM(); - if(autocvar_g_nades_spawn) - self.nade_refire = time + autocvar_g_spawnshieldtime; - else - self.nade_refire = time + autocvar_g_nades_nade_refire; - - if(autocvar_g_nades_bonus_client_select) - self.nade_type = self.cvar_cl_nade_type; - - self.nade_timer = 0; - - if (!self.offhand) self.offhand = OFFHAND_NADE; - - if(self.nade_spawnloc) - { - setorigin(self, self.nade_spawnloc.origin); - self.nade_spawnloc.cnt -= 1; - - if(self.nade_spawnloc.cnt <= 0) - { - remove(self.nade_spawnloc); - self.nade_spawnloc = world; - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST) -{ - if(frag_target.nade) - if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade) - toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05)); - - float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor); - - if(IS_PLAYER(frag_attacker)) - { - if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target) - nades_RemoveBonus(frag_attacker); - else if(frag_target.flagcarried) - nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium); - else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1) - { - #define SPREE_ITEM(counta,countb,center,normal,gentle) \ - case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; } - switch(frag_attacker.killcount) - { - KILL_SPREE_LIST - default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break; - } - #undef SPREE_ITEM - } - else - nades_GiveBonus(frag_attacker, killcount_bonus); - } - - nades_RemoveBonus(frag_target); - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, PlayerDamage_Calculate) -{ - if(frag_target.frozen) - if(autocvar_g_freezetag_revive_nade) - if(frag_attacker == frag_target) - if(frag_deathtype == DEATH_NADE.m_id) - if(time - frag_inflictor.toss_time <= 0.1) - { - Unfreeze(frag_target); - frag_target.health = autocvar_g_freezetag_revive_nade_health; - Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3); - frag_damage = 0; - frag_force = '0 0 0'; - Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname); - Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, MonsterDies) -{SELFPARAM(); - if(IS_PLAYER(frag_attacker)) - if(DIFF_TEAM(frag_attacker, self)) - if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) - nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, DropSpecialItems) -{ - if(frag_target.nade) - toss_nade(frag_target, '0 0 0', time + 0.05); - - return false; -} - -bool nades_RemovePlayer() -{SELFPARAM(); - nades_Clear(self); - nades_RemoveBonus(self); - return false; -} - -MUTATOR_HOOKFUNCTION(nades, MakePlayerObserver) { nades_RemovePlayer(); } -MUTATOR_HOOKFUNCTION(nades, ClientDisconnect) { nades_RemovePlayer(); } -MUTATOR_HOOKFUNCTION(nades, reset_map_global) { nades_RemovePlayer(); } - -MUTATOR_HOOKFUNCTION(nades, SpectateCopy) -{SELFPARAM(); - self.nade_timer = other.nade_timer; - self.nade_type = other.nade_type; - self.pokenade_type = other.pokenade_type; - self.bonus_nades = other.bonus_nades; - self.bonus_nade_score = other.bonus_nade_score; - self.stat_healing_orb = other.stat_healing_orb; - self.stat_healing_orb_alpha = other.stat_healing_orb_alpha; - return false; -} - -MUTATOR_HOOKFUNCTION(nades, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type"); - GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type"); - - return false; -} - -MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":Nades"); - return false; -} - -MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Nades"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_new_toys.qc b/qcsrc/server/mutators/mutator/mutator_new_toys.qc deleted file mode 100644 index 78904ffae..000000000 --- a/qcsrc/server/mutators/mutator/mutator_new_toys.qc +++ /dev/null @@ -1,227 +0,0 @@ -#ifdef IMPLEMENTATION -/* - -CORE laser vortex lg rl cry gl elec hagar fireb hook - vaporizer porto - tuba - -NEW rifle hlac minel seeker -IDEAS OPEN flak OPEN FUN FUN FUN FUN - - - -How this mutator works: - ======================= - -When a gun tries to spawn, this mutator is called. It will provide alternate -weaponreplace lists. - -Entity: - -{ -"classname" "weapon_vortex" -"new_toys" "rifle" -} --> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. - -{ -"classname" "weapon_vortext" -"new_toys" "vortex rifle" -} --> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. - -{ -"classname" "weapon_vortex" -"new_toys" "vortex" -} --> This is always a Vortex. - -If the map specifies no "new_toys" argument - -There will be two default replacements selectable: "replace all" and "replace random". -In "replace all" mode, e.g. Vortex will have the default replacement "rifle". -In "replace random" mode, Vortex will have the default replacement "vortex rifle". - -This mutator's replacements run BEFORE regular weaponreplace! - -The New Toys guns do NOT get a spawn function, so they can only ever be spawned -when this mutator is active. - -Likewise, warmup, give all, give ALL and impulse 99 will not give them unless -this mutator is active. - -Outside this mutator, they still can be spawned by: -- setting their start weapon cvar to 1 -- give weaponname -- weaponreplace -- weaponarena (but all and most weapons arena again won't include them) - -This mutator performs the default replacements on the DEFAULTS of the -start weapon selection. - -These weapons appear in the menu's priority list, BUT get a suffix -"(Mutator weapon)". - -Picking up a "new toys" weapon will not play standard weapon pickup sound, but -roflsound "New toys, new toys!" sound. - -*/ - -bool nt_IsNewToy(int w); - -REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill")) -{ - MUTATOR_ONADD - { - if(time > 1) // game loads at time 1 - error("This cannot be added at runtime\n"); - - // mark the guns as ok to use by e.g. impulse 99 - for(int i = WEP_FIRST; i <= WEP_LAST; ++i) - if(nt_IsNewToy(i)) - get_weaponinfo(i).spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - for(int i = WEP_FIRST; i <= WEP_LAST; ++i) - if(nt_IsNewToy(i)) - get_weaponinfo(i).spawnflags |= WEP_FLAG_MUTATORBLOCKED; - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This cannot be removed at runtime\n"); - return -1; - } - - return 0; -} - -.string new_toys; - -float autocvar_g_new_toys_autoreplace; -bool autocvar_g_new_toys_use_pickupsound = true; -const float NT_AUTOREPLACE_NEVER = 0; -const float NT_AUTOREPLACE_ALWAYS = 1; -const float NT_AUTOREPLACE_RANDOM = 2; - -MUTATOR_HOOKFUNCTION(nt, SetModname) -{ - modname = "NewToys"; - return 0; -} - -bool nt_IsNewToy(int w) -{ - switch(w) - { - case WEP_SEEKER.m_id: - case WEP_MINE_LAYER.m_id: - case WEP_HLAC.m_id: - case WEP_RIFLE.m_id: - case WEP_SHOCKWAVE.m_id: - return true; - default: - return false; - } -} - -string nt_GetFullReplacement(string w) -{ - switch(w) - { - case "hagar": return "seeker"; - case "devastator": return "minelayer"; - case "machinegun": return "hlac"; - case "vortex": return "rifle"; - //case "shotgun": return "shockwave"; - default: return string_null; - } -} - -string nt_GetReplacement(string w, float m) -{ - if(m == NT_AUTOREPLACE_NEVER) - return w; - string s = nt_GetFullReplacement(w); - if (!s) - return w; - if(m == NT_AUTOREPLACE_RANDOM) - s = strcat(w, " ", s); - return s; -} - -MUTATOR_HOOKFUNCTION(nt, SetStartItems) -{ - // rearrange start_weapon_default - // apply those bits that are set by start_weapon_defaultmask - // same for warmup - - float i, j, k, n; - - WepSet newdefault; - WepSet warmup_newdefault; - - newdefault = '0 0 0'; - warmup_newdefault = '0 0 0'; - - for(i = WEP_FIRST; i <= WEP_LAST; ++i) - { - entity e = get_weaponinfo(i); - if(!e.weapon) - continue; - - n = tokenize_console(nt_GetReplacement(e.netname, autocvar_g_new_toys_autoreplace)); - - for(j = 0; j < n; ++j) - for(k = WEP_FIRST; k <= WEP_LAST; ++k) - if(get_weaponinfo(k).netname == argv(j)) - { - if(start_weapons & WepSet_FromWeapon(i)) - newdefault |= WepSet_FromWeapon(k); - if(warmup_start_weapons & WepSet_FromWeapon(i)) - warmup_newdefault |= WepSet_FromWeapon(k); - } - } - - newdefault &= start_weapons_defaultmask; - start_weapons &= ~start_weapons_defaultmask; - start_weapons |= newdefault; - - warmup_newdefault &= warmup_start_weapons_defaultmask; - warmup_start_weapons &= ~warmup_start_weapons_defaultmask; - warmup_start_weapons |= warmup_newdefault; - - return 0; -} - -MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) -{SELFPARAM(); - // otherwise, we do replace - if(self.new_toys) - { - // map defined replacement: - ret_string = self.new_toys; - } - else - { - // auto replacement: - ret_string = nt_GetReplacement(other.netname, autocvar_g_new_toys_autoreplace); - } - - // apply regular weaponreplace - ret_string = W_Apply_Weaponreplace(ret_string); - - return 0; -} - -MUTATOR_HOOKFUNCTION(nt, FilterItem) -{SELFPARAM(); - if(nt_IsNewToy(self.weapon) && autocvar_g_new_toys_use_pickupsound) { - self.item_pickupsound = string_null; - self.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS; - } - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_nix.qc b/qcsrc/server/mutators/mutator/mutator_nix.qc deleted file mode 100644 index 259547a05..000000000 --- a/qcsrc/server/mutators/mutator/mutator_nix.qc +++ /dev/null @@ -1,267 +0,0 @@ -#ifdef IMPLEMENTATION -float g_nix_with_blaster; -// WEAPONTODO -int nix_weapon; -float nix_nextchange; -float nix_nextweapon; -.float nix_lastchange_id; -.float nix_lastinfotime; -.float nix_nextincr; - -bool NIX_CanChooseWeapon(int wpn); - -REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill")) -{ - MUTATOR_ONADD - { - g_nix_with_blaster = autocvar_g_nix_with_blaster; - - nix_nextchange = 0; - nix_nextweapon = 0; - - for (int i = WEP_FIRST; i <= WEP_LAST; ++i) - if (NIX_CanChooseWeapon(i)) { - Weapon w = get_weaponinfo(i); - w.wr_init(w); - } - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - // as the PlayerSpawn hook will no longer run, NIX is turned off by this! - entity e; - FOR_EACH_PLAYER(e) if(e.deadflag == DEAD_NO) - { - e.ammo_cells = start_ammo_cells; - e.ammo_plasma = start_ammo_plasma; - e.ammo_shells = start_ammo_shells; - e.ammo_nails = start_ammo_nails; - e.ammo_rockets = start_ammo_rockets; - e.ammo_fuel = start_ammo_fuel; - e.weapons = start_weapons; - if(!client_hasweapon(e, e.weapon, true, false)) - e.switchweapon = w_getbestweapon(self); - } - } - - return 0; -} - -bool NIX_CanChooseWeapon(int wpn) -{ - entity e = get_weaponinfo(wpn); - if(!e.weapon) // skip dummies - return false; - if(g_weaponarena) - { - if(!(g_weaponarena_weapons & WepSet_FromWeapon(wpn))) - return false; - } - else - { - if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster) - return false; - if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) - return false; - if (!(e.spawnflags & WEP_FLAG_NORMAL)) - return false; - } - return true; -} -void NIX_ChooseNextWeapon() -{ - float j; - RandomSelection_Init(); - for(j = WEP_FIRST; j <= WEP_LAST; ++j) - if(NIX_CanChooseWeapon(j)) - RandomSelection_Add(world, j, string_null, 1, (j != nix_weapon)); - nix_nextweapon = RandomSelection_chosen_float; -} - -void NIX_GiveCurrentWeapon() -{SELFPARAM(); - float dt; - - if(!nix_nextweapon) - NIX_ChooseNextWeapon(); - - dt = ceil(nix_nextchange - time); - - if(dt <= 0) - { - nix_weapon = nix_nextweapon; - nix_nextweapon = 0; - if (!nix_nextchange) // no round played yet? - nix_nextchange = time; // start the first round now! - else - nix_nextchange = time + autocvar_g_balance_nix_roundtime; - // Weapon w = get_weaponinfo(nix_weapon); - // w.wr_init(w); // forget it, too slow - } - - // get weapon info - entity e = get_weaponinfo(nix_weapon); - - if(nix_nextchange != self.nix_lastchange_id) // this shall only be called once per round! - { - self.ammo_shells = self.ammo_nails = self.ammo_rockets = self.ammo_cells = self.ammo_plasma = self.ammo_fuel = 0; - - if(self.items & IT_UNLIMITED_WEAPON_AMMO) - { - switch(e.ammo_field) - { - case ammo_shells: self.ammo_shells = autocvar_g_pickup_shells_max; break; - case ammo_nails: self.ammo_nails = autocvar_g_pickup_nails_max; break; - case ammo_rockets: self.ammo_rockets = autocvar_g_pickup_rockets_max; break; - case ammo_cells: self.ammo_cells = autocvar_g_pickup_cells_max; break; - case ammo_plasma: self.ammo_plasma = autocvar_g_pickup_plasma_max; break; - case ammo_fuel: self.ammo_fuel = autocvar_g_pickup_fuel_max; break; - } - } - else - { - switch(e.ammo_field) - { - case ammo_shells: self.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; - case ammo_nails: self.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; - case ammo_rockets: self.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; - case ammo_cells: self.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; - case ammo_plasma: self.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; - case ammo_fuel: self.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; - } - } - - self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; - if(dt >= 1 && dt <= 5) - self.nix_lastinfotime = -42; - else - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); - - Weapon w = get_weaponinfo(nix_weapon); - w.wr_resetplayer(w); - - // all weapons must be fully loaded when we spawn - if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars - self.(weapon_load[nix_weapon]) = e.reloading_ammo; - - // vortex too - if(WEP_CVAR(vortex, charge)) - { - if(WEP_CVAR_SEC(vortex, chargepool)) - self.vortex_chargepool_ammo = 1; - self.vortex_charge = WEP_CVAR(vortex, charge_start); - } - - // set last change info - self.nix_lastchange_id = nix_nextchange; - } - if(self.nix_lastinfotime != dt) - { - self.nix_lastinfotime = dt; // initial value 0 should count as "not seen" - if(dt >= 1 && dt <= 5) - Send_Notification(NOTIF_ONE_ONLY, self, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt); - } - - if(!(self.items & IT_UNLIMITED_WEAPON_AMMO) && time > self.nix_nextincr) - { - switch(e.ammo_field) - { - case ammo_shells: self.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; - case ammo_nails: self.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; - case ammo_rockets: self.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; - case ammo_cells: self.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; - case ammo_plasma: self.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; - case ammo_fuel: self.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; - } - - self.nix_nextincr = time + autocvar_g_balance_nix_incrtime; - } - - self.weapons = '0 0 0'; - if(g_nix_with_blaster) - self.weapons |= WEPSET(BLASTER); - self.weapons |= WepSet_FromWeapon(nix_weapon); - - if(self.switchweapon != nix_weapon) - if(!client_hasweapon(self, self.switchweapon, true, false)) - if(client_hasweapon(self, nix_weapon, true, false)) - W_SwitchWeapon(nix_weapon); -} - -MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon) -{ - return 1; // no throwing in NIX -} - -MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":NIX"); - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", NIX"); - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, FilterItem) -{SELFPARAM(); - switch (self.items) - { - case ITEM_HealthSmall.m_itemid: - case ITEM_HealthMedium.m_itemid: - case ITEM_HealthLarge.m_itemid: - case ITEM_HealthMega.m_itemid: - case ITEM_ArmorSmall.m_itemid: - case ITEM_ArmorMedium.m_itemid: - case ITEM_ArmorLarge.m_itemid: - case ITEM_ArmorMega.m_itemid: - if (autocvar_g_nix_with_healtharmor) - return 0; - break; - case ITEM_Strength.m_itemid: - case ITEM_Shield.m_itemid: - if (autocvar_g_nix_with_powerups) - return 0; - break; - } - - return 1; // delete all other items -} - -MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn) -{SELFPARAM(); - if(self.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo) - return 1; - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) -{SELFPARAM(); - if(!intermission_running) - if(self.deadflag == DEAD_NO) - if(IS_PLAYER(self)) - NIX_GiveCurrentWeapon(); - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, PlayerSpawn) -{SELFPARAM(); - self.nix_lastchange_id = -1; - NIX_GiveCurrentWeapon(); // overrides the weapons you got when spawning - self.items |= IT_UNLIMITED_SUPERWEAPONS; - return 0; -} - -MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST) -{ - modname = "NIX"; - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_physical_items.qc b/qcsrc/server/mutators/mutator/mutator_physical_items.qc deleted file mode 100644 index 58a01ca2e..000000000 --- a/qcsrc/server/mutators/mutator/mutator_physical_items.qc +++ /dev/null @@ -1,139 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) -{ - // check if we have a physics engine - MUTATOR_ONADD - { - if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) - { - LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n"); - return -1; - } - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This cannot be removed at runtime\n"); - return -1; - } - - return 0; -} - -.vector spawn_origin, spawn_angles; - -void physical_item_think() -{SELFPARAM(); - self.nextthink = time; - - self.alpha = self.owner.alpha; // apply fading and ghosting - - if(!self.cnt) // map item, not dropped - { - // copy ghost item properties - self.colormap = self.owner.colormap; - self.colormod = self.owner.colormod; - self.glowmod = self.owner.glowmod; - - // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there - if(autocvar_g_physical_items_reset) - { - if(self.owner.wait > time) // awaiting respawn - { - setorigin(self, self.spawn_origin); - self.angles = self.spawn_angles; - self.solid = SOLID_NOT; - self.alpha = -1; - self.movetype = MOVETYPE_NONE; - } - else - { - self.alpha = 1; - self.solid = SOLID_CORPSE; - self.movetype = MOVETYPE_PHYSICS; - } - } - } - - if(!self.owner.modelindex) - remove(self); // the real item is gone, remove this -} - -void physical_item_touch() -{SELFPARAM(); - if(!self.cnt) // not for dropped items - if (ITEM_TOUCH_NEEDKILL()) - { - setorigin(self, self.spawn_origin); - self.angles = self.spawn_angles; - } -} - -void physical_item_damage(entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{SELFPARAM(); - if(!self.cnt) // not for dropped items - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - setorigin(self, self.spawn_origin); - self.angles = self.spawn_angles; - } -} - -MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) -{SELFPARAM(); - if(self.owner == world && autocvar_g_physical_items <= 1) - return false; - if (self.spawnflags & 1) // floating item - return false; - - // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. - // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. - entity wep; - wep = spawn(); - _setmodel(wep, self.model); - setsize(wep, self.mins, self.maxs); - setorigin(wep, self.origin); - wep.angles = self.angles; - wep.velocity = self.velocity; - - wep.owner = self; - wep.solid = SOLID_CORPSE; - wep.movetype = MOVETYPE_PHYSICS; - wep.takedamage = DAMAGE_AIM; - wep.effects |= EF_NOMODELFLAGS; // disable the spinning - wep.colormap = self.owner.colormap; - wep.glowmod = self.owner.glowmod; - wep.damageforcescale = autocvar_g_physical_items_damageforcescale; - wep.dphitcontentsmask = self.dphitcontentsmask; - wep.cnt = (self.owner != world); - - wep.think = physical_item_think; - wep.nextthink = time; - wep.touch = physical_item_touch; - wep.event_damage = physical_item_damage; - - if(!wep.cnt) - { - // fix the spawn origin - setorigin(wep, wep.origin + '0 0 1'); - entity oldself; - oldself = self; - WITH(entity, self, wep, builtin_droptofloor()); - } - - wep.spawn_origin = wep.origin; - wep.spawn_angles = self.angles; - - self.effects |= EF_NODRAW; // hide the original weapon - self.movetype = MOVETYPE_FOLLOW; - self.aiment = wep; // attach the original weapon - self.SendEntity = func_null; - - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_pinata.qc b/qcsrc/server/mutators/mutator/mutator_pinata.qc deleted file mode 100644 index a806b2958..000000000 --- a/qcsrc/server/mutators/mutator/mutator_pinata.qc +++ /dev/null @@ -1,27 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill")); - -MUTATOR_HOOKFUNCTION(pinata, PlayerDies) -{SELFPARAM(); - for(int j = WEP_FIRST; j <= WEP_LAST; ++j) - if(self.weapons & WepSet_FromWeapon(j)) - if(self.switchweapon != j) - if(W_IsWeaponThrowable(j)) - W_ThrowNewWeapon(self, j, false, self.origin + (self.mins + self.maxs) * 0.5, randomvec() * 175 + '0 0 325'); - - return true; -} - -MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":Pinata"); - return false; -} - -MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Piñata"); - return false; -} - -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_random_gravity.qc b/qcsrc/server/mutators/mutator/mutator_random_gravity.qc deleted file mode 100644 index 1b17c9f69..000000000 --- a/qcsrc/server/mutators/mutator/mutator_random_gravity.qc +++ /dev/null @@ -1,49 +0,0 @@ -#ifdef IMPLEMENTATION -// Random Gravity -// -// Mutator by Mario -// Inspired by Player 2 - -REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity")) -{ - MUTATOR_ONADD - { - cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end - } - - return false; -} - -float gravity_delay; - -MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame) -{ - if(gameover || !cvar("g_random_gravity")) return false; - if(time < gravity_delay) return false; - if(time < game_starttime) return false; - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false; - - if(random() >= autocvar_g_random_gravity_negative_chance) - cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max))); - else - cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max))); - - gravity_delay = time + autocvar_g_random_gravity_delay; - - LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity), "\n"); - - return false; -} - -MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":RandomGravity"); - return 0; -} - -MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Random gravity"); - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_rocketflying.qc b/qcsrc/server/mutators/mutator/mutator_rocketflying.qc deleted file mode 100644 index f23d9918b..000000000 --- a/qcsrc/server/mutators/mutator/mutator_rocketflying.qc +++ /dev/null @@ -1,25 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying")); - -MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile) -{ - if(other.classname == "rocket" || other.classname == "mine") - { - // kill detonate delay of rockets - other.spawnshieldtime = time; - } - return 0; -} - -MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":RocketFlying"); - return 0; -} - -MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Rocket Flying"); - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_rocketminsta.qc b/qcsrc/server/mutators/mutator/mutator_rocketminsta.qc deleted file mode 100644 index f8a1709da..000000000 --- a/qcsrc/server/mutators/mutator/mutator_rocketminsta.qc +++ /dev/null @@ -1,35 +0,0 @@ -#ifdef IMPLEMENTATION -#include "../../../common/deathtypes/all.qh" -#include "../../round_handler.qh" - -REGISTER_MUTATOR(rm, cvar("g_instagib")); - -MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate) -{ - // we do it this way, so rm can be toggled during the match - if(!autocvar_g_rm) { return false; } - - if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) - if(frag_attacker == frag_target || frag_target.classname == "nade") - frag_damage = 0; - - if(autocvar_g_rm_laser) - if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) - if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted())) - frag_damage = 0; - - return false; -} - -MUTATOR_HOOKFUNCTION(rm, PlayerDies) -{ - // we do it this way, so rm can be toggled during the match - if(!autocvar_g_rm) { return false; } - - if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) - frag_damage = 1000; // always gib if it was a vaporizer death - - return false; -} - -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_spawn_near_teammate.qc b/qcsrc/server/mutators/mutator/mutator_spawn_near_teammate.qc deleted file mode 100644 index 24147b279..000000000 --- a/qcsrc/server/mutators/mutator/mutator_spawn_near_teammate.qc +++ /dev/null @@ -1,167 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate") && teamplay); - -.entity msnt_lookat; - -.float msnt_timer; -.vector msnt_deathloc; - -.float cvar_cl_spawn_near_teammate; - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score) -{SELFPARAM(); - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate)) - return 0; - - entity p; - - spawn_spot.msnt_lookat = world; - - if(!teamplay) - return 0; - - RandomSelection_Init(); - FOR_EACH_PLAYER(p) if(p != self) if(p.team == self.team) if(!p.deadflag) - { - float l = vlen(spawn_spot.origin - p.origin); - if(l > autocvar_g_spawn_near_teammate_distance) - continue; - if(l < 48) - continue; - if(!checkpvs(spawn_spot.origin, p)) - continue; - RandomSelection_Add(p, 0, string_null, 1, 1); - } - - if(RandomSelection_chosen_ent) - { - spawn_spot.msnt_lookat = RandomSelection_chosen_ent; - spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; - } - else if(self.team == spawn_spot.team) - spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate - - return 0; -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) -{SELFPARAM(); - // Note: when entering this, fixangle is already set. - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && self.cvar_cl_spawn_near_teammate)) - { - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death) - self.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; - - entity team_mate, best_mate = world; - vector best_spot = '0 0 0'; - float pc = 0, best_dist = 0, dist = 0; - FOR_EACH_PLAYER(team_mate) - { - if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && team_mate.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0) - if(team_mate.deadflag == DEAD_NO) - if(team_mate.msnt_timer < time) - if(SAME_TEAM(self, team_mate)) - if(time > team_mate.spawnshieldtime) // spawn shielding - if(team_mate.frozen == 0) - if(team_mate != self) - { - tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate); - if(trace_fraction != 1.0) - if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) - { - pc = pointcontents(trace_endpos + '0 0 1'); - if(pc == CONTENT_EMPTY) - { - if(vlen(team_mate.velocity) > 5) - fixedmakevectors(vectoangles(team_mate.velocity)); - else - fixedmakevectors(team_mate.angles); - - for(pc = 0; pc != 5; ++pc) // test 5 diffrent spots close to mate - { - switch(pc) - { - case 0: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 128, MOVE_NORMAL, team_mate); - break; - case 1: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 128 , MOVE_NORMAL, team_mate); - break; - case 2: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin + v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate); - break; - case 3: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_right * 64 - v_forward * 64, MOVE_NORMAL, team_mate); - break; - case 4: - tracebox(team_mate.origin , PL_MIN, PL_MAX, team_mate.origin - v_forward * 128, MOVE_NORMAL, team_mate); - break; - } - - if(trace_fraction == 1.0) - { - traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NORMAL, team_mate); - if(trace_fraction != 1.0) - { - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) - { - dist = vlen(trace_endpos - self.msnt_deathloc); - if(dist < best_dist || best_dist == 0) - { - best_dist = dist; - best_spot = trace_endpos; - best_mate = team_mate; - } - } - else - { - setorigin(self, trace_endpos); - self.angles = team_mate.angles; - self.angles_z = 0; // never spawn tilted even if the spot says to - team_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; - return 0; - } - } - } - } - } - } - } - } - - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) - if(best_dist) - { - setorigin(self, best_spot); - self.angles = best_mate.angles; - self.angles_z = 0; // never spawn tilted even if the spot says to - best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; - } - } - else if(spawn_spot.msnt_lookat) - { - self.angles = vectoangles(spawn_spot.msnt_lookat.origin - self.origin); - self.angles_x = -self.angles.x; - self.angles_z = 0; // never spawn tilted even if the spot says to - /* - sprint(self, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n"); - sprint(self, "distance: ", vtos(spawn_spot.msnt_lookat.origin - self.origin), "\n"); - sprint(self, "angles: ", vtos(self.angles), "\n"); - */ - } - - return 0; -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies) -{SELFPARAM(); - self.msnt_deathloc = self.origin; - return 0; -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, GetCvars) -{ - GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_spawn_near_teammate, "cl_spawn_near_teammate"); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_superspec.qc b/qcsrc/server/mutators/mutator/mutator_superspec.qc deleted file mode 100644 index 416df75b4..000000000 --- a/qcsrc/server/mutators/mutator/mutator_superspec.qc +++ /dev/null @@ -1,480 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(superspec, cvar("g_superspectate")); - -#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" -#define _ISLOCAL ((edict_num(1) == self) ? true : false) - -const float ASF_STRENGTH = BIT(0); -const float ASF_SHIELD = BIT(1); -const float ASF_MEGA_AR = BIT(2); -const float ASF_MEGA_HP = BIT(3); -const float ASF_FLAG_GRAB = BIT(4); -const float ASF_OBSERVER_ONLY = BIT(5); -const float ASF_SHOWWHAT = BIT(6); -const float ASF_SSIM = BIT(7); -const float ASF_FOLLOWKILLER = BIT(8); -const float ASF_ALL = 0xFFFFFF; -.float autospec_flags; - -const float SSF_SILENT = 1; -const float SSF_VERBOSE = 2; -const float SSF_ITEMMSG = 4; -.float superspec_flags; - -.string superspec_itemfilter; //"classname1 classname2 ..." - -bool superspec_Spectate(entity _player) -{SELFPARAM(); - if(Spectate(_player) == 1) - self.classname = STR_SPECTATOR; - - return true; -} - -void superspec_save_client_conf() -{SELFPARAM(); - string fn = "superspec-local.options"; - float fh; - - if (!_ISLOCAL) - { - if(self.crypto_idfp == "") - return; - - fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); - } - - fh = fopen(fn, FILE_WRITE); - if(fh < 0) - { - LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n"); - } - else - { - fputs(fh, _SSMAGIX); - fputs(fh, "\n"); - fputs(fh, ftos(self.autospec_flags)); - fputs(fh, "\n"); - fputs(fh, ftos(self.superspec_flags)); - fputs(fh, "\n"); - fputs(fh, self.superspec_itemfilter); - fputs(fh, "\n"); - fclose(fh); - } -} - -void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) -{ - sprint(_to, strcat(_con_title, _msg)); - - if(_to.superspec_flags & SSF_SILENT) - return; - - if(_spamlevel > 1) - if (!(_to.superspec_flags & SSF_VERBOSE)) - return; - - centerprint(_to, strcat(_center_title, _msg)); -} - -float superspec_filteritem(entity _for, entity _item) -{ - float i; - - if(_for.superspec_itemfilter == "") - return true; - - if(_for.superspec_itemfilter == "") - return true; - - float l = tokenize_console(_for.superspec_itemfilter); - for(i = 0; i < l; ++i) - { - if(argv(i) == _item.classname) - return true; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(superspec, ItemTouch) -{SELFPARAM(); - entity _item = self; - - entity e; - FOR_EACH_SPEC(e) - { - setself(e); - if(self.superspec_flags & SSF_ITEMMSG) - if(superspec_filteritem(self, _item)) - { - if(self.superspec_flags & SSF_VERBOSE) - superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n", other.netname, _item.netname), 1); - else - superspec_msg("", "", self, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", other.netname, _item.netname, _item.classname), 1); - if((self.autospec_flags & ASF_SSIM) && self.enemy != other) - { - superspec_Spectate(other); - - setself(this); - return MUT_ITEMTOUCH_CONTINUE; - } - } - - if((self.autospec_flags & ASF_SHIELD && _item.invincible_finished) || - (self.autospec_flags & ASF_STRENGTH && _item.strength_finished) || - (self.autospec_flags & ASF_MEGA_AR && _item.itemdef == ITEM_ArmorMega) || - (self.autospec_flags & ASF_MEGA_HP && _item.itemdef == ITEM_HealthMega) || - (self.autospec_flags & ASF_FLAG_GRAB && _item.classname == "item_flag_team")) - { - - if((self.enemy != other) || IS_OBSERVER(self)) - { - if(self.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(self)) - { - if(self.superspec_flags & SSF_VERBOSE) - superspec_msg("", "", self, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", other.netname, _item.netname), 2); - } - else - { - if(self.autospec_flags & ASF_SHOWWHAT) - superspec_msg("", "", self, sprintf("^7Following %s^7 due to picking up %s\n", other.netname, _item.netname), 2); - - superspec_Spectate(other); - } - } - } - } - - setself(this); - - return MUT_ITEMTOUCH_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand) -{SELFPARAM(); -#define OPTIONINFO(flag,var,test,text,long,short) \ - var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ - var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") - - if(MUTATOR_RETURNVALUE) // command was already handled? - return false; - - if(IS_PLAYER(self)) - return false; - - if(cmd_name == "superspec_itemfilter") - { - if(argv(1) == "help") - { - string _aspeco; - _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"; - _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); - _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); - superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", self, _aspeco, 1); - } - else if(argv(1) == "clear") - { - if(self.superspec_itemfilter != "") - strunzone(self.superspec_itemfilter); - - self.superspec_itemfilter = ""; - } - else if(argv(1) == "show" || argv(1) == "") - { - if(self.superspec_itemfilter == "") - { - superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", self, "", 1); - return true; - } - float i; - float l = tokenize_console(self.superspec_itemfilter); - string _msg = ""; - for(i = 0; i < l; ++i) - _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); - //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); - - _msg = strcat(_msg,"\n"); - - superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", self, _msg, 1); - } - else - { - if(self.superspec_itemfilter != "") - strunzone(self.superspec_itemfilter); - - self.superspec_itemfilter = strzone(argv(1)); - } - - return true; - } - - if(cmd_name == "superspec") - { - string _aspeco; - - if(cmd_argc > 1) - { - float i, _bits = 0, _start = 1; - if(argv(1) == "help") - { - _aspeco = "use cmd superspec [option] [on|off] to set options\n\n"; - _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n"); - _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); - _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n"); - _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); - superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", self, _aspeco, 1); - return true; - } - - if(argv(1) == "clear") - { - self.superspec_flags = 0; - _start = 2; - } - - for(i = _start; i < cmd_argc; ++i) - { - if(argv(i) == "on" || argv(i) == "1") - { - self.superspec_flags |= _bits; - _bits = 0; - } - else if(argv(i) == "off" || argv(i) == "0") - { - if(_start == 1) - self.superspec_flags &= ~_bits; - - _bits = 0; - } - else - { - if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; - if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; - if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; - } - } - } - - _aspeco = ""; - OPTIONINFO(self.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); - OPTIONINFO(self.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); - OPTIONINFO(self.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); - - superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", self, _aspeco, 1); - - return true; - } - -///////////////////// - - if(cmd_name == "autospec") - { - string _aspeco; - if(cmd_argc > 1) - { - if(argv(1) == "help") - { - _aspeco = "use cmd autospec [option] [on|off] to set options\n\n"; - _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); - _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); - _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); - _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); - _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); - _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n"); - _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n"); - _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n"); - _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n"); - _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n"); - superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", self, _aspeco, 1); - return true; - } - - float i, _bits = 0, _start = 1; - if(argv(1) == "clear") - { - self.autospec_flags = 0; - _start = 2; - } - - for(i = _start; i < cmd_argc; ++i) - { - if(argv(i) == "on" || argv(i) == "1") - { - self.autospec_flags |= _bits; - _bits = 0; - } - else if(argv(i) == "off" || argv(i) == "0") - { - if(_start == 1) - self.autospec_flags &= ~_bits; - - _bits = 0; - } - else - { - if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; - if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; - if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; - if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; - if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; - if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; - if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; - if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; - if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER; - if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; - } - } - } - - _aspeco = ""; - OPTIONINFO(self.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); - OPTIONINFO(self.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk"); - - superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", self, _aspeco, 1); - return true; - } - - if(cmd_name == "followpowerup") - { - entity _player; - FOR_EACH_PLAYER(_player) - { - if(_player.strength_finished > time || _player.invincible_finished > time) - return superspec_Spectate(_player); - } - - superspec_msg("", "", self, "No active powerup\n", 1); - return true; - } - - if(cmd_name == "followstrength") - { - entity _player; - FOR_EACH_PLAYER(_player) - { - if(_player.strength_finished > time) - return superspec_Spectate(_player); - } - - superspec_msg("", "", self, "No active Strength\n", 1); - return true; - } - - if(cmd_name == "followshield") - { - entity _player; - FOR_EACH_PLAYER(_player) - { - if(_player.invincible_finished > time) - return superspec_Spectate(_player); - } - - superspec_msg("", "", self, "No active Shield\n", 1); - return true; - } - - return false; -#undef OPTIONINFO -} - -MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":SS"); - return 0; -} - -MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Super Spectators"); - return 0; -} - -void superspec_hello() -{SELFPARAM(); - if(self.enemy.crypto_idfp == "") - Send_Notification(NOTIF_ONE_ONLY, self.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID); - - remove(self); -} - -MUTATOR_HOOKFUNCTION(superspec, ClientConnect) -{SELFPARAM(); - if(!IS_REAL_CLIENT(self)) - return false; - - string fn = "superspec-local.options"; - float fh; - - self.superspec_flags = SSF_VERBOSE; - self.superspec_itemfilter = ""; - - entity _hello = spawn(); - _hello.enemy = self; - _hello.think = superspec_hello; - _hello.nextthink = time + 5; - - if (!_ISLOCAL) - { - if(self.crypto_idfp == "") - return false; - - fn = sprintf("superspec-%s.options", uri_escape(self.crypto_idfp)); - } - - fh = fopen(fn, FILE_READ); - if(fh < 0) - { - LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n"); - } - else - { - string _magic = fgets(fh); - if(_magic != _SSMAGIX) - { - LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic\n"); - } - else - { - self.autospec_flags = stof(fgets(fh)); - self.superspec_flags = stof(fgets(fh)); - self.superspec_itemfilter = strzone(fgets(fh)); - } - fclose(fh); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(superspec, PlayerDies) -{SELFPARAM(); - entity e; - FOR_EACH_SPEC(e) - { - setself(e); - if(self.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && self.enemy == this) - { - if(self.autospec_flags & ASF_SHOWWHAT) - superspec_msg("", "", self, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2); - - superspec_Spectate(frag_attacker); - } - } - - setself(this); - return false; -} - -MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect) -{ - superspec_save_client_conf(); - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_touchexplode.qc b/qcsrc/server/mutators/mutator/mutator_touchexplode.qc deleted file mode 100644 index 29d9a2c60..000000000 --- a/qcsrc/server/mutators/mutator/mutator_touchexplode.qc +++ /dev/null @@ -1,43 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode")); - -.float touchexplode_time; - -void PlayerTouchExplode(entity p1, entity p2) -{SELFPARAM(); - vector org = (p1.origin + p2.origin) * 0.5; - org.z += (p1.mins.z + p2.mins.z) * 0.5; - - sound(self, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); - Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); - - entity e = spawn(); - setorigin(e, org); - RadiusDamage(e, world, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, world, world, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, world); - remove(e); -} - -MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink) -{SELFPARAM(); - if(time > self.touchexplode_time) - if(!gameover) - if(!self.frozen) - if(IS_PLAYER(self)) - if(self.deadflag == DEAD_NO) - if (!IS_INDEPENDENT_PLAYER(self)) - FOR_EACH_PLAYER(other) if(self != other) - { - if(time > other.touchexplode_time) - if(!other.frozen) - if(other.deadflag == DEAD_NO) - if (!IS_INDEPENDENT_PLAYER(other)) - if(boxesoverlap(self.absmin, self.absmax, other.absmin, other.absmax)) - { - PlayerTouchExplode(self, other); - self.touchexplode_time = other.touchexplode_time = time + 0.2; - } - } - - return false; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_vampire.qc b/qcsrc/server/mutators/mutator/mutator_vampire.qc deleted file mode 100644 index 315da7dc6..000000000 --- a/qcsrc/server/mutators/mutator/mutator_vampire.qc +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib")); - -MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) -{ - if(time >= frag_target.spawnshieldtime) - if(frag_target != frag_attacker) - if(frag_target.deadflag == DEAD_NO) - { - frag_attacker.health += bound(0, damage_take, frag_target.health); - frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit); - } - - return false; -} - -MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString) -{ - ret_string = strcat(ret_string, ":Vampire"); - return 0; -} - -MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString) -{ - ret_string = strcat(ret_string, ", Vampire"); - return 0; -} -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_vampirehook.qc b/qcsrc/server/mutators/mutator/mutator_vampirehook.qc deleted file mode 100644 index f669f6a96..000000000 --- a/qcsrc/server/mutators/mutator/mutator_vampirehook.qc +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(vh, cvar("g_vampirehook")); - -bool autocvar_g_vampirehook_teamheal; -float autocvar_g_vampirehook_damage; -float autocvar_g_vampirehook_damagerate; -float autocvar_g_vampirehook_health_steal; - -.float last_dmg; - -MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) -{SELFPARAM(); - entity dmgent = ((SAME_TEAM(self.owner, self.aiment) && autocvar_g_vampirehook_teamheal) ? self.owner : self.aiment); - - if(IS_PLAYER(self.aiment)) - if(self.last_dmg < time) - if(!self.aiment.frozen) - if(time >= game_starttime) - if(DIFF_TEAM(self.owner, self.aiment) || autocvar_g_vampirehook_teamheal) - if(self.aiment.health > 0) - if(autocvar_g_vampirehook_damage) - { - self.last_dmg = time + autocvar_g_vampirehook_damagerate; - self.owner.damage_dealt += autocvar_g_vampirehook_damage; - Damage(dmgent, self, self.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, self.origin, '0 0 0'); - if(SAME_TEAM(self.owner, self.aiment)) - self.aiment.health = min(self.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); - else - self.owner.health = min(self.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); - - if(dmgent == self.owner) - dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! - } - - return false; -} - -#endif diff --git a/qcsrc/server/mutators/mutator/mutator_weaponarena_random.qc b/qcsrc/server/mutators/mutator/mutator_weaponarena_random.qc deleted file mode 100644 index 5c8210077..000000000 --- a/qcsrc/server/mutators/mutator/mutator_weaponarena_random.qc +++ /dev/null @@ -1,13 +0,0 @@ -#ifdef IMPLEMENTATION -// WEAPONTODO: rename the cvars -REGISTER_MUTATOR(weaponarena_random, true); - -MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) { - SELFPARAM(); - if (!g_weaponarena_random) return; - if (g_weaponarena_random_with_blaster) this.weapons &= ~WEPSET(BLASTER); - W_RandomWeapons(this, g_weaponarena_random); - if (g_weaponarena_random_with_blaster) this.weapons |= WEPSET(BLASTER); -} - -#endif diff --git a/qcsrc/server/mutators/mutator/sandbox.qc b/qcsrc/server/mutators/mutator/sandbox.qc deleted file mode 100644 index 9369da457..000000000 --- a/qcsrc/server/mutators/mutator/sandbox.qc +++ /dev/null @@ -1,827 +0,0 @@ -#ifdef IMPLEMENTATION -float autosave_time; -void sandbox_Database_Load(); - -REGISTER_MUTATOR(sandbox, cvar("g_sandbox")) -{ - MUTATOR_ONADD - { - autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame - if(autocvar_g_sandbox_storage_autoload) - sandbox_Database_Load(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - // nothing to remove - } - - return false; -} - -const float MAX_STORAGE_ATTACHMENTS = 16; -float object_count; -.float object_flood; -.entity object_attach; -.string material; - -.float touch_timer; -void sandbox_ObjectFunction_Touch() -{SELFPARAM(); - // apply material impact effects - - if(!self.material) - return; - if(self.touch_timer > time) - return; // don't execute each frame - self.touch_timer = time + 0.1; - - // make particle count and sound volume depend on impact speed - float intensity; - intensity = vlen(self.velocity) + vlen(other.velocity); - if(intensity) // avoid divisions by 0 - intensity /= 2; // average the two velocities - if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min)) - return; // impact not strong enough to do anything - // now offset intensity and apply it to the effects - intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity - intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); - - _sound(self, CH_TRIGGER, strcat("object/impact_", self.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM); - Send_Effect_(strcat("impact_", self.material), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 -} - -void sandbox_ObjectFunction_Think() -{SELFPARAM(); - entity e; - - // decide if and how this object can be grabbed - if(autocvar_g_sandbox_readonly) - self.grab = 0; // no grabbing - else if(autocvar_g_sandbox_editor_free < 2 && self.crypto_idfp) - self.grab = 1; // owner only - else - self.grab = 3; // anyone - - // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server). - // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this, - // since if the owning player disconnects, the object's owner should also be reset. - FOR_EACH_REALPLAYER(e) // bots can't have objects - { - if(self.crypto_idfp == e.crypto_idfp) - { - self.realowner = e; - break; - } - self.realowner = world; - } - - self.nextthink = time; - - CSQCMODEL_AUTOUPDATE(self); -} - -.float old_solid, old_movetype; -entity sandbox_ObjectEdit_Get(float permissions) -{SELFPARAM(); - // Returns the traced entity if the player can edit it, and world if not. - // If permissions if false, the object is returned regardless of editing rights. - // Attached objects are SOLID_NOT and do not get traced. - - crosshair_trace_plusvisibletriggers(self); - if(vlen(self.origin - trace_ent.origin) > autocvar_g_sandbox_editor_distance_edit) - return world; // out of trace range - if(trace_ent.classname != "object") - return world; // entity is not an object - if(!permissions) - return trace_ent; // don't check permissions, anyone can edit this object - if(trace_ent.crypto_idfp == "") - return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it - if (!(trace_ent.realowner != self && autocvar_g_sandbox_editor_free < 2)) - return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server - return world; -} - -void sandbox_ObjectEdit_Scale(entity e, float f) -{ - e.scale = f; - if(e.scale) - { - e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max); - _setmodel(e, e.model); // reset mins and maxs based on mesh - setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size - } -} - -void sandbox_ObjectAttach_Remove(entity e); -void sandbox_ObjectAttach_Set(entity e, entity parent, string s) -{ - // attaches e to parent on string s - - // we can't attach to an attachment, for obvious reasons - sandbox_ObjectAttach_Remove(e); - - e.old_solid = e.solid; // persist solidity - e.old_movetype = e.movetype; // persist physics - e.movetype = MOVETYPE_FOLLOW; - e.solid = SOLID_NOT; - e.takedamage = DAMAGE_NO; - - setattachment(e, parent, s); - e.owner = parent; -} - -void sandbox_ObjectAttach_Remove(entity e) -{ - // detaches any object attached to e - - entity head; - for(head = world; (head = find(head, classname, "object")); ) - { - if(head.owner == e) - { - vector org; - org = gettaginfo(head, 0); - setattachment(head, world, ""); - head.owner = world; - - // objects change origin and angles when detached, so apply previous position - setorigin(head, org); - head.angles = e.angles; // don't allow detached objects to spin or roll - - head.solid = head.old_solid; // restore persisted solidity - head.movetype = head.old_movetype; // restore persisted physics - head.takedamage = DAMAGE_AIM; - } - } -} - -entity sandbox_ObjectSpawn(float database) -{SELFPARAM(); - // spawn a new object with default properties - - entity e = new(object); - e.takedamage = DAMAGE_AIM; - e.damageforcescale = 1; - e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly - e.movetype = MOVETYPE_TOSS; - e.frame = 0; - e.skin = 0; - e.material = string_null; - e.touch = sandbox_ObjectFunction_Touch; - e.think = sandbox_ObjectFunction_Think; - e.nextthink = time; - //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects? - - if(!database) - { - // set the object's owner via player UID - // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone - if(self.crypto_idfp != "") - e.crypto_idfp = strzone(self.crypto_idfp); - else - print_to(self, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!"); - - // set public object information - e.netname = strzone(self.netname); // name of the owner - e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time - - // set origin and direction based on player position and view angle - makevectors(self.v_angle); - WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, self); - setorigin(e, trace_endpos); - e.angles_y = self.v_angle.y; - } - - WITH(entity, self, e, CSQCMODEL_AUTOINIT(e)); - - object_count += 1; - return e; -} - -void sandbox_ObjectRemove(entity e) -{ - sandbox_ObjectAttach_Remove(e); // detach child objects - - // if the object being removed has been selected for attachment by a player, unset it - entity head; - FOR_EACH_REALPLAYER(head) // bots can't have objects - { - if(head.object_attach == e) - head.object_attach = world; - } - - if(e.material) { strunzone(e.material); e.material = string_null; } - if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; } - if(e.netname) { strunzone(e.netname); e.netname = string_null; } - if(e.message) { strunzone(e.message); e.message = string_null; } - if(e.message2) { strunzone(e.message2); e.message2 = string_null; } - remove(e); - e = world; - - object_count -= 1; -} - -string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global - -string sandbox_ObjectPort_Save(entity e, float database) -{ - // save object properties, and return them as a string - float i = 0; - string s; - entity head; - - for(head = world; (head = find(head, classname, "object")); ) - { - // the main object needs to be first in the array [0] with attached objects following - float slot, physics, solidity; - if(head == e) // this is the main object, place it first - { - slot = 0; - solidity = head.solid; // applied solidity is normal solidity for children - physics = head.movetype; // applied physics are normal physics for parents - } - else if(head.owner == e) // child object, list them in order - { - i += 1; // children start from 1 - slot = i; - solidity = head.old_solid; // persisted solidity is normal solidity for children - physics = head.old_movetype; // persisted physics are normal physics for children - gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below - } - else - continue; - - // ---------------- OBJECT PROPERTY STORAGE: SAVE ---------------- - if(slot) - { - // properties stored only for child objects - if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - } - else - { - // properties stored only for parent objects - if(database) - { - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " "); - } - } - // properties stored for all objects - port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" "); - port_string[slot] = strcat(port_string[slot], ftos(head.skin), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.frame), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.scale), " "); - port_string[slot] = strcat(port_string[slot], ftos(solidity), " "); - port_string[slot] = strcat(port_string[slot], ftos(physics), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " "); - if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - if(database) - { - // properties stored only for the database - if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" "); - port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" "); - port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" "); - } - } - - // now apply the array to a simple string, with the ; symbol separating objects - s = ""; - for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) - { - if(port_string[i]) - s = strcat(s, port_string[i], "; "); - port_string[i] = string_null; // fully clear the string - } - - return s; -} - -entity sandbox_ObjectPort_Load(string s, float database) -{ - // load object properties, and spawn a new object with them - float n, i; - entity e = world, parent = world; - - // separate objects between the ; symbols - n = tokenizebyseparator(s, "; "); - for(i = 0; i < n; ++i) - port_string[i] = argv(i); - - // now separate and apply the properties of each object - for(i = 0; i < n; ++i) - { - float argv_num; - string tagname = string_null; - argv_num = 0; - tokenize_console(port_string[i]); - e = sandbox_ObjectSpawn(database); - - // ---------------- OBJECT PROPERTY STORAGE: LOAD ---------------- - if(i) - { - // properties stored only for child objects - if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; - } - else - { - // properties stored only for parent objects - if(database) - { - setorigin(e, stov(argv(argv_num))); ++argv_num; - e.angles = stov(argv(argv_num)); ++argv_num; - } - parent = e; // mark parent objects as such - } - // properties stored for all objects - _setmodel(e, argv(argv_num)); ++argv_num; - e.skin = stof(argv(argv_num)); ++argv_num; - e.alpha = stof(argv(argv_num)); ++argv_num; - e.colormod = stov(argv(argv_num)); ++argv_num; - e.glowmod = stov(argv(argv_num)); ++argv_num; - e.frame = stof(argv(argv_num)); ++argv_num; - sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num; - e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num; - e.movetype = e.old_movetype = stof(argv(argv_num)); ++argv_num; - e.damageforcescale = stof(argv(argv_num)); ++argv_num; - if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num; - if(database) - { - // properties stored only for the database - if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num; - if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num; - if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num; - if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num; - } - - // attach last - if(i) - sandbox_ObjectAttach_Set(e, parent, tagname); - } - - for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) - port_string[i] = string_null; // fully clear the string - - return e; -} - -void sandbox_Database_Save() -{ - // saves all objects to the database file - entity head; - string file_name; - float file_get; - - file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); - file_get = fopen(file_name, FILE_WRITE); - fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"))); - fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); - - for(head = world; (head = find(head, classname, "object")); ) - { - // attached objects are persisted separately, ignore them here - if(head.owner != world) - continue; - - // use a line of text for each object, listing all properties - fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n")); - } - fclose(file_get); -} - -void sandbox_Database_Load() -{ - // loads all objects from the database file - string file_read, file_name; - float file_get, i; - - file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); - file_get = fopen(file_name, FILE_READ); - if(file_get < 0) - { - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); - } - else - { - for (;;) - { - file_read = fgets(file_get); - if(file_read == "") - break; - if(substring(file_read, 0, 2) == "//") - continue; - if(substring(file_read, 0, 1) == "#") - continue; - - entity e; - e = sandbox_ObjectPort_Load(file_read, true); - - if(e.material) - { - // since objects are being loaded for the first time, precache material sounds for each - for (i = 1; i <= 5; i++) // 5 sounds in total - precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); - } - } - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); - } - fclose(file_get); -} - -MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) -{SELFPARAM(); - if(MUTATOR_RETURNVALUE) // command was already handled? - return false; - if(cmd_name == "g_sandbox") - { - if(autocvar_g_sandbox_readonly) - { - print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used"); - return true; - } - if(cmd_argc < 2) - { - print_to(self, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'"); - return true; - } - - switch(argv(1)) - { - entity e; - float i; - string s; - - // ---------------- COMMAND: HELP ---------------- - case "help": - print_to(self, "You can use the following sandbox commands:"); - print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); - print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); - print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original"); - print_to(self, "^3copy value ^7- copies the properties of the object to the specified client cvar"); - print_to(self, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\""); - print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); - print_to(self, "^3get ^7- selects the object you are facing as the object to be attached"); - print_to(self, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone"); - print_to(self, "^3remove ^7- detaches all objects from the object you are facing"); - print_to(self, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects"); - print_to(self, "^3skin value ^7- changes the skin of the object"); - print_to(self, "^3alpha value ^7- sets object transparency"); - print_to(self, "^3colormod \"value_x value_y value_z\" ^7- main object color"); - print_to(self, "^3glowmod \"value_x value_y value_z\" ^7- glow object color"); - print_to(self, "^3frame value ^7- object animation frame, for self-animated models"); - print_to(self, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size"); - print_to(self, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid"); - print_to(self, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); - print_to(self, "^3force value ^7- amount of force applied to objects that are shot"); - print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); - print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); - print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object"); - print_to(self, "^3object ^7- prints general information about the object, such as owner and creation / editing date"); - print_to(self, "^3mesh ^7- prints information about the object's mesh, including skeletal bones"); - print_to(self, "^3attachments ^7- prints information about the object's attachments"); - print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); - return true; - - // ---------------- COMMAND: OBJECT, SPAWN ---------------- - case "object_spawn": - if(time < self.object_flood) - { - print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); - return true; - } - self.object_flood = time + autocvar_g_sandbox_editor_flood; - if(object_count >= autocvar_g_sandbox_editor_maxobjects) - { - print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); - return true; - } - if(cmd_argc < 3) - { - print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command"); - return true; - } - if (!(fexists(argv(2)))) - { - print_to(self, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); - return true; - } - - e = sandbox_ObjectSpawn(false); - _setmodel(e, argv(2)); - - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); - return true; - - // ---------------- COMMAND: OBJECT, REMOVE ---------------- - case "object_remove": - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " removed an object at origin ^3", vtos(e.origin), "\n")); - sandbox_ObjectRemove(e); - return true; - } - - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- - case "object_duplicate": - switch(argv(2)) - { - case "copy": - // copies customizable properties of the selected object to the clipboard cvar - e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? - if(e != world) - { - s = sandbox_ObjectPort_Save(e, false); - s = strreplace("\"", "\\\"", s); - stuffcmd(self, strcat("set ", argv(3), " \"", s, "\"")); - - print_to(self, "^2SANDBOX - INFO: ^7Object copied to clipboard"); - return true; - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); - return true; - - case "paste": - // spawns a new object using the properties in the player's clipboard cvar - if(time < self.object_flood) - { - print_to(self, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(self.object_flood - time), " ^7seconds beofore spawning another object")); - return true; - } - self.object_flood = time + autocvar_g_sandbox_editor_flood; - if(argv(3) == "") // no object in clipboard - { - print_to(self, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it"); - return true; - } - if(object_count >= autocvar_g_sandbox_editor_maxobjects) - { - print_to(self, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); - return true; - } - e = sandbox_ObjectPort_Load(argv(3), false); - - print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully"); - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " pasted an object at origin ^3", vtos(e.origin), "\n")); - return true; - } - return true; - - // ---------------- COMMAND: OBJECT, ATTACH ---------------- - case "object_attach": - switch(argv(2)) - { - case "get": - // select e as the object as meant to be attached - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - self.object_attach = e; - print_to(self, "^2SANDBOX - INFO: ^7Object selected for attachment"); - return true; - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over"); - return true; - case "set": - if(self.object_attach == world) - { - print_to(self, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first."); - return true; - } - - // attaches the previously selected object to e - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - sandbox_ObjectAttach_Set(self.object_attach, e, argv(3)); - self.object_attach = world; // object was attached, no longer keep it scheduled for attachment - print_to(self, "^2SANDBOX - INFO: ^7Object attached successfully"); - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " attached objects at origin ^3", vtos(e.origin), "\n")); - return true; - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over"); - return true; - case "remove": - // removes e if it was attached - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - sandbox_ObjectAttach_Remove(e); - print_to(self, "^2SANDBOX - INFO: ^7Child objects detached successfully"); - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " detached objects at origin ^3", vtos(e.origin), "\n")); - return true; - } - print_to(self, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over"); - return true; - } - return true; - - // ---------------- COMMAND: OBJECT, EDIT ---------------- - case "object_edit": - if(argv(2) == "") - { - print_to(self, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit"); - return true; - } - - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - switch(argv(2)) - { - case "skin": - e.skin = stof(argv(3)); - break; - case "alpha": - e.alpha = stof(argv(3)); - break; - case "color_main": - e.colormod = stov(argv(3)); - break; - case "color_glow": - e.glowmod = stov(argv(3)); - break; - case "frame": - e.frame = stof(argv(3)); - break; - case "scale": - sandbox_ObjectEdit_Scale(e, stof(argv(3))); - break; - case "solidity": - switch(argv(3)) - { - case "0": // non-solid - e.solid = SOLID_TRIGGER; - break; - case "1": // solid - e.solid = SOLID_BBOX; - break; - default: - break; - } - case "physics": - switch(argv(3)) - { - case "0": // static - e.movetype = MOVETYPE_NONE; - break; - case "1": // movable - e.movetype = MOVETYPE_TOSS; - break; - case "2": // physical - e.movetype = MOVETYPE_PHYSICS; - break; - default: - break; - } - break; - case "force": - e.damageforcescale = stof(argv(3)); - break; - case "material": - if(e.material) strunzone(e.material); - if(argv(3)) - { - for (i = 1; i <= 5; i++) // precache material sounds, 5 in total - precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav")); - e.material = strzone(argv(3)); - } - else - e.material = string_null; // no material - break; - default: - print_to(self, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'"); - return true; - } - - // update last editing time - if(e.message2) strunzone(e.message2); - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); - - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", self.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n")); - return true; - } - - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, CLAIM ---------------- - case "object_claim": - // if the player can edit an object but is not its owner, this can be used to claim that object - if(self.crypto_idfp == "") - { - print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); - return true; - } - e = sandbox_ObjectEdit_Get(true); - if(e != world) - { - // update the owner's name - // Do this before checking if you're already the owner and skipping if such, so we - // also update the player's nickname if he changed it (but has the same player UID) - if(e.netname != self.netname) - { - if(e.netname) strunzone(e.netname); - e.netname = strzone(self.netname); - print_to(self, "^2SANDBOX - INFO: ^7Object owner name updated"); - } - - if(e.crypto_idfp == self.crypto_idfp) - { - print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); - return true; - } - - if(e.crypto_idfp) strunzone(e.crypto_idfp); - e.crypto_idfp = strzone(self.crypto_idfp); - - print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully"); - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, INFO ---------------- - case "object_info": - // prints public information about the object to the player - e = sandbox_ObjectEdit_Get(false); - if(e != world) - { - switch(argv(2)) - { - case "object": - print_to(self, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); - return true; - case "mesh": - s = ""; - FOR_EACH_TAG(e) - s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); - print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); - return true; - case "attachments": - // this should show the same info as 'mesh' but for attachments - s = ""; - entity head; - i = 0; - for(head = world; (head = find(head, classname, "object")); ) - { - if(head.owner == e) - { - ++i; // start from 1 - gettaginfo(e, head.tag_index); - s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame)); - s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", "); - } - } - if(i) // object contains attachments - print_to(self, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s)); - else - print_to(self, "^2SANDBOX - INFO: ^7Object contains no attachments"); - return true; - } - } - print_to(self, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object"); - return true; - - // ---------------- COMMAND: DEFAULT ---------------- - default: - print_to(self, "Invalid command. For usage information, type 'sandbox help'"); - return true; - } - } - return false; -} - -MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame) -{ - if(!autocvar_g_sandbox_storage_autosave) - return false; - if(time < autosave_time) - return false; - autosave_time = time + autocvar_g_sandbox_storage_autosave; - - sandbox_Database_Save(); - - return true; -} -#endif diff --git a/qcsrc/server/progs.inc b/qcsrc/server/progs.inc index b51ae73a6..a68f9dd71 100644 --- a/qcsrc/server/progs.inc +++ b/qcsrc/server/progs.inc @@ -43,8 +43,6 @@ #include "command/all.qc" -#include "mutators/all.qc" - #include "pathlib/_all.inc" #include "weapons/accuracy.qc" @@ -88,6 +86,7 @@ #include "../common/vehicles/all.qc" #include "../common/weapons/all.qc" #include "../common/mutators/all.qc" +#include "mutators/all.qc" #include "../common/turrets/sv_turrets.qc" #include "../common/turrets/config.qc"