]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Mutators: self containment
authorTimePath <andrew.hardaker1995@gmail.com>
Thu, 5 Nov 2015 00:20:09 +0000 (11:20 +1100)
committerTimePath <andrew.hardaker1995@gmail.com>
Thu, 5 Nov 2015 00:20:09 +0000 (11:20 +1100)
86 files changed:
qcsrc/client/progs.inc
qcsrc/common/mutators/all.inc
qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/bloodloss/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/breakablehook/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/buffs/buffs.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/buffs/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/campcheck/campcheck.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/campcheck/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/dodging/dodging.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/dodging/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/hook/hook.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/hook/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/instagib/instagib.qc
qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/invincibleproj/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/melee_only/melee_only.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/melee_only/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/midair/midair.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/midair/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/multijump/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/multijump/multijump.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/nades/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/nades/nades.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/new_toys/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/new_toys/new_toys.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/nix/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/nix/nix.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/physical_items/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/physical_items/physical_items.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/pinata/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/pinata/pinata.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/random_gravity/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/rocketflying/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/rocketminsta/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/sandbox/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/sandbox/sandbox.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/superspec/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/superspec/superspec.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/touchexplode/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/vampire/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/vampire/vampire.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/vampirehook/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/weaponarena_random/module.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc [new file with mode: 0644]
qcsrc/common/nades/all.qc
qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc
qcsrc/server/autocvars.qh
qcsrc/server/cl_client.qc
qcsrc/server/mutators/all.inc
qcsrc/server/mutators/mutator/gamemode_freezetag.qc
qcsrc/server/mutators/mutator/gamemode_keyhunt.qc
qcsrc/server/mutators/mutator/mutator_bloodloss.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_breakablehook.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_buffs.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_campcheck.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_dodging.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_hook.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_invincibleproj.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_melee_only.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_midair.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_multijump.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_nades.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_new_toys.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_nix.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_physical_items.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_pinata.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_random_gravity.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_rocketflying.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_rocketminsta.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_spawn_near_teammate.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_superspec.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_touchexplode.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_vampire.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_vampirehook.qc [deleted file]
qcsrc/server/mutators/mutator/mutator_weaponarena_random.qc [deleted file]
qcsrc/server/mutators/mutator/sandbox.qc [deleted file]
qcsrc/server/progs.inc

index e3e8c00c067a151710e8f6b4c156be1c2394e67a..7f473f4815014bb6a9174c51d586fcff8aa6c0d4 100644 (file)
 #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"
index d3e63de453851d05388b757a119da8f49054cce7..21a23b069432197d8e000529a41a15e3996d26dd 100644 (file)
@@ -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 (file)
index 0000000..ca37166
--- /dev/null
@@ -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 (file)
index 0000000..d3f665a
--- /dev/null
@@ -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 (file)
index 0000000..3ee077c
--- /dev/null
@@ -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 (file)
index 0000000..484eb4c
--- /dev/null
@@ -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 (file)
index 0000000..d6bfb70
--- /dev/null
@@ -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 (file)
index 0000000..89d31a1
--- /dev/null
@@ -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 (file)
index 0000000..0ba0bb6
--- /dev/null
@@ -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 (file)
index 0000000..9f4181f
--- /dev/null
@@ -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 (file)
index 0000000..f014ebb
--- /dev/null
@@ -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 (file)
index 0000000..6308a21
--- /dev/null
@@ -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 (file)
index 0000000..b298e7b
--- /dev/null
@@ -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 (file)
index 0000000..61600c4
--- /dev/null
@@ -0,0 +1,3 @@
+#ifdef SVQC
+#include "hook.qc"
+#endif
index 16b5b86d1a565ba14a10f5fc950b76d7093b9159..2a46dd699e1b5426c9735b7a0b3a0ec58424556f 100644 (file)
@@ -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 (file)
index 0000000..5a781a8
--- /dev/null
@@ -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 (file)
index 0000000..61d0383
--- /dev/null
@@ -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 (file)
index 0000000..5b03f46
--- /dev/null
@@ -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 (file)
index 0000000..c711556
--- /dev/null
@@ -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 (file)
index 0000000..aaae006
--- /dev/null
@@ -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 (file)
index 0000000..10b1789
--- /dev/null
@@ -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 (file)
index 0000000..3103320
--- /dev/null
@@ -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 (file)
index 0000000..00e22af
--- /dev/null
@@ -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 (file)
index 0000000..b9c0eb3
--- /dev/null
@@ -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 (file)
index 0000000..556265f
--- /dev/null
@@ -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 (file)
index 0000000..1217177
--- /dev/null
@@ -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 (file)
index 0000000..78904ff
--- /dev/null
@@ -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 (file)
index 0000000..fb4f9ec
--- /dev/null
@@ -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 (file)
index 0000000..1a8089c
--- /dev/null
@@ -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 (file)
index 0000000..7ed9b03
--- /dev/null
@@ -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 (file)
index 0000000..6fbe77b
--- /dev/null
@@ -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 (file)
index 0000000..4e22966
--- /dev/null
@@ -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 (file)
index 0000000..a806b29
--- /dev/null
@@ -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 (file)
index 0000000..91baa43
--- /dev/null
@@ -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 (file)
index 0000000..80abfe3
--- /dev/null
@@ -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 (file)
index 0000000..7036bc4
--- /dev/null
@@ -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 (file)
index 0000000..f23d991
--- /dev/null
@@ -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 (file)
index 0000000..b7d02a9
--- /dev/null
@@ -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 (file)
index 0000000..6ddd6db
--- /dev/null
@@ -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 (file)
index 0000000..0715d5b
--- /dev/null
@@ -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 (file)
index 0000000..e1decc8
--- /dev/null
@@ -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 (file)
index 0000000..f88a768
--- /dev/null
@@ -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 (file)
index 0000000..cd78b40
--- /dev/null
@@ -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 (file)
index 0000000..8e0a998
--- /dev/null
@@ -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 (file)
index 0000000..416df75
--- /dev/null
@@ -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 (file)
index 0000000..d3b0ea5
--- /dev/null
@@ -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 (file)
index 0000000..ec43948
--- /dev/null
@@ -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 (file)
index 0000000..864ea28
--- /dev/null
@@ -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 (file)
index 0000000..315da7d
--- /dev/null
@@ -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 (file)
index 0000000..17ecf60
--- /dev/null
@@ -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 (file)
index 0000000..f669f6a
--- /dev/null
@@ -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 (file)
index 0000000..b7a5f66
--- /dev/null
@@ -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 (file)
index 0000000..5c82100
--- /dev/null
@@ -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
index 7dfae12ac432b692ce17b2cfe9e1323cf7c1f593..4ba13408afe90de0284fc9b4320fbd87a1ceb03b 100644 (file)
@@ -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"
index 408cb950b43da825c4366ed3e4619713be3c0b0d..2f3cd47b02e592696c1daf4e6974b83220b19b62 100644 (file)
@@ -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;
        }
index d72617fac90f0087fb2d5eb74d32d7110f47fa04..5d9c8dc7148ab4b51a9bf0577b5faba4b31310ee 100644 (file)
@@ -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;
index 462d42722697c889e463c75ea7764e23c2320d07..38203f52e9a42362f6e424cb1eda68a38e89c001 100644 (file)
@@ -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); }
index e12e9c58bf944855fa471ec38714ad6e640534b4..b334137bd3a6bcc288278ef04a64829d2db4692f 100644 (file)
 #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"
index 14e345046b2db29a9011d60db40c594bdbea0fb8..29ae0066848980f495ffb70b8a2bbce775230e5b 100644 (file)
@@ -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;
index bcc14eca962d64b13c9d1197294a3b87aaac43ce..341d133084664ebf1148b7a06465bcfa2b1e315f 100644 (file)
@@ -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 (file)
index ca37166..0000000
+++ /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 (file)
index cb9463b..0000000
+++ /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 (file)
index fd64e58..0000000
+++ /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 (file)
index be5bfce..0000000
+++ /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 (file)
index 85b9fea..0000000
+++ /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 (file)
index b298e7b..0000000
+++ /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 (file)
index 5a781a8..0000000
+++ /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 (file)
index 5b03f46..0000000
+++ /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 (file)
index bf34283..0000000
+++ /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 (file)
index f01a801..0000000
+++ /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 (file)
index 9cfc2ec..0000000
+++ /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 (file)
index 78904ff..0000000
+++ /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 (file)
index 259547a..0000000
+++ /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 (file)
index 58a01ca..0000000
+++ /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 (file)
index a806b29..0000000
+++ /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 (file)
index 1b17c9f..0000000
+++ /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 (file)
index f23d991..0000000
+++ /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 (file)
index f8a1709..0000000
+++ /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 (file)
index 24147b2..0000000
+++ /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 (file)
index 416df75..0000000
+++ /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 (file)
index 29d9a2c..0000000
+++ /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 (file)
index 315da7d..0000000
+++ /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 (file)
index f669f6a..0000000
+++ /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 (file)
index 5c82100..0000000
+++ /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 (file)
index 9369da4..0000000
+++ /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
index b51ae73a6b3bdff08e3d6031f5b9244275ceabbf..a68f9dd7148d59679f8f7a6922cb7fee6d19f821 100644 (file)
@@ -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"