#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"
-#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"
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "bloodloss.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "breakablehook.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "buffs.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "campcheck.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "dodging.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "hook.qc"
+#endif
#include "items.qc"
+#ifdef SVQC
+float autocvar_g_instagib_invis_alpha;
+#endif
+
#endif
#ifdef IMPLEMENTATION
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "invincibleproj.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "melee_only.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "midair.qc"
+#endif
--- /dev/null
+#ifndef MENUQC
+#include "multijump.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "nades.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "new_toys.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "nix.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "physical_items.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "pinata.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "random_gravity.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "rocketflying.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "rocketminsta.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "sandbox.qc"
+
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "spawn_near_teammate.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "superspec.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "touchexplode.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "vampire.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "vampirehook.qc"
+#endif
--- /dev/null
+#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
--- /dev/null
+#ifdef SVQC
+#include "weaponarena_random.qc"
+#endif
--- /dev/null
+#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
+#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"
{
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));
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;
}
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;
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;
#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;
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;
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;
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;
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;
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); }
#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"
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;
}
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;
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;
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
+++ /dev/null
-#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
#include "command/all.qc"
-#include "mutators/all.qc"
-
#include "pathlib/_all.inc"
#include "weapons/accuracy.qc"
#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"