.float buff_time = _STAT(BUFF_TIME);
void buffs_DelayedInit(entity this);
-REGISTER_MUTATOR(buffs, cvar("g_buffs"))
+AUTOCVAR(g_buffs, int, -1, "Enable buffs, -1: enabled but no auto location or replacing powerups, 1: enabled and can replace them");
+
+REGISTER_MUTATOR(buffs, autocvar_g_buffs)
{
MUTATOR_ONADD
{
- InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
+ if(autocvar_g_buffs > 0)
+ InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET);
}
}
if (view.buffs)
{
- return view.cvar_cl_buffs_autoreplace == false || view.buffs != this.owner.buffs;
+ return CS(view).cvar_cl_buffs_autoreplace == false || view.buffs != this.owner.buffs;
}
return WaypointSprite_visible_for_player(this, player, view);
void buff_Respawn(entity this)
{
- if(gameover) { return; }
+ if(game_stopped) return;
vector oldbufforigin = this.origin;
this.velocity = '0 0 200';
void buff_Touch(entity this, entity toucher)
{
- if(gameover) { return; }
+ if(game_stopped) return;
if(ITEM_TOUCH_NEEDKILL())
{
return;
}
- if((this.team && DIFF_TEAM(toucher, this))
- || (STAT(FROZEN, toucher))
- || (toucher.vehicle)
- || (!this.buff_active)
- )
- {
- // can't touch this
+ if(!this.buff_active)
return;
- }
if(MUTATOR_CALLHOOK(BuffTouch, this, toucher))
return;
if(!IS_PLAYER(toucher))
return; // incase mutator changed toucher
+ if((this.team && DIFF_TEAM(toucher, this))
+ || (STAT(FROZEN, toucher))
+ || (toucher.vehicle)
+ || (time < PS(toucher).buff_shield)
+ )
+ {
+ // can't touch this
+ return;
+ }
+
if (toucher.buffs)
{
- if (toucher.cvar_cl_buffs_autoreplace && toucher.buffs != this.buffs)
+ if (CS(toucher).cvar_cl_buffs_autoreplace && toucher.buffs != this.buffs)
{
int buffid = buff_FirstFromFlags(toucher.buffs).m_id;
//Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_DROP, toucher.buffs);
.int buff_seencount;
-void buff_NewType(entity ent, float cb)
+void buff_NewType(entity ent)
{
RandomSelection_Init();
- FOREACH(Buffs, buff_Available(it), LAMBDA(
- it.buff_seencount += 1;
+ FOREACH(Buffs, buff_Available(it),
+ {
// if it's already been chosen, give it a lower priority
- RandomSelection_AddFloat(it.m_itemid, 1, max(0.2, 1 / it.buff_seencount));
- ));
- ent.buffs = RandomSelection_chosen_float;
+ RandomSelection_AddEnt(it, max(0.2, 1 / it.buff_seencount), 1);
+ });
+ entity newbuff = RandomSelection_chosen_ent;
+ newbuff.buff_seencount += 1; // lower chances of seeing this buff again soon
+ ent.buffs = newbuff.m_itemid;
}
void buff_Think(entity this)
this.skin = buff.m_skin;
setmodel(this, MDL_BUFF);
+ setsize(this, BUFF_MIN, BUFF_MAX);
if(this.buff_waypoint)
{
this.oldbuffs = this.buffs;
}
- if(!gameover)
+ if(!game_stopped)
if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
if(!this.buff_activetime_updated)
{
}
if(!this.buff_active && !this.buff_activetime)
- if(!this.owner || STAT(FROZEN, this.owner) || IS_DEAD(this.owner) || !this.owner.iscreature || !(this.owner.buffs & this.buffs))
+ if(!this.owner || STAT(FROZEN, this.owner) || IS_DEAD(this.owner) || !this.owner.iscreature || this.owner.vehicle || !(this.owner.buffs & this.buffs) || this.pickup_anyway > 0 || (this.pickup_anyway >= 0 && autocvar_g_buffs_pickup_anyway))
{
buff_SetCooldown(this, autocvar_g_buffs_cooldown_respawn + frametime);
this.owner = NULL;
if(autocvar_g_buffs_randomize)
- buff_NewType(this, this.buffs);
+ buff_NewType(this);
if(autocvar_g_buffs_random_location || (this.spawnflags & 64))
buff_Respawn(this);
}
if(this.buff_activetime)
- if(!gameover)
+ if(!game_stopped)
if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
{
this.buff_activetime = max(0, this.buff_activetime - frametime);
void buff_Reset(entity this)
{
if(autocvar_g_buffs_randomize)
- buff_NewType(this, this.buffs);
+ buff_NewType(this);
this.owner = NULL;
buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate);
buff_Waypoint_Reset(this);
entity buff = buff_FirstFromFlags(this.buffs);
- if(!this.buffs || buff_Available(buff))
- buff_NewType(this, 0);
+ if(!this.buffs || !buff_Available(buff))
+ buff_NewType(this);
this.classname = "item_buff";
this.solid = SOLID_TRIGGER;
this.flags = FL_ITEM;
this.bot_pickup = true;
- this.bot_pickupevalfunc = commodity_pickupevalfunc;
+ this.bot_pickupevalfunc = generic_pickupevalfunc;
this.bot_pickupbasevalue = 1000;
IL_PUSH(g_items, this);
setthink(this, buff_Think);
//this.gravity = 100;
this.color = buff.m_color;
this.glowmod = buff_GlowColor(this);
- buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + game_starttime);
+ buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + max(0, game_starttime - time));
this.buff_active = !this.buff_activetime;
this.pflags = PFLAGS_FULLDYNAMIC;
{
FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range),
{
- if(SAME_TEAM(it, this))
- if(it.health < autocvar_g_balance_health_regenstable)
+ if (!SAME_TEAM(it, this))
+ {
+ continue;
+ }
+ float hp = GetResourceAmount(it, RESOURCE_HEALTH);
+ if(hp >= autocvar_g_balance_health_regenstable)
{
- Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
- it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable);
+ continue;
}
+ Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1);
+ SetResourceAmount(it, RESOURCE_HEALTH, bound(0, hp + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable));
});
}
-float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base)
+float buff_Inferno_CalculateTime(float damg, 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);
+ return offset_y + (intersect_y - offset_y) * logn(((damg - offset_x) * ((base - 1) / intersect_x)) + 1, base);
}
// mutator hooks
-MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor)
-{
- entity frag_target = M_ARGV(2, entity);
- float frag_deathtype = M_ARGV(6, float);
- float frag_damage = M_ARGV(7, float);
-
- if(frag_deathtype == DEATH_BUFF.m_id) { return; }
-
- if(frag_target.buffs & BUFF_RESISTANCE.m_itemid)
- {
- vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage);
- M_ARGV(4, float) = v.x; // take
- M_ARGV(5, float) = v.y; // save
- }
-}
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_Calculate)
+MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate)
{
entity frag_attacker = M_ARGV(1, entity);
entity frag_target = M_ARGV(2, entity);
if(frag_deathtype == DEATH_BUFF.m_id) { return; }
+ if(frag_target.buffs & BUFF_RESISTANCE.m_itemid)
+ {
+ float reduced = frag_damage * autocvar_g_buffs_resistance_blockpercent;
+ frag_damage = bound(0, frag_damage - reduced, frag_damage);
+ }
+
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((GetResourceAmount(frag_target, RESOURCE_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);
+ frag_damage = max(5, GetResourceAmount(frag_target, RESOURCE_HEALTH) - autocvar_g_buffs_medic_survive_health);
if(frag_target.buffs & BUFF_JUMP.m_itemid)
if(frag_deathtype == DEATH_FALL.m_id)
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);
+ float amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal,
+ GetResourceAmount(frag_target, RESOURCE_HEALTH));
+ GiveResourceWithLimit(frag_attacker, RESOURCE_HEALTH, amount, g_pickup_healthsmall_max);
+ if (frag_target.armorvalue)
+ {
+ amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal,
+ GetResourceAmount(frag_target, RESOURCE_ARMOR));
+ GiveResourceWithLimit(frag_attacker, RESOURCE_ARMOR, amount, g_pickup_armorsmall_max);
+ }
}
M_ARGV(4, float) = frag_damage;
player.buffs = 0;
player.buff_time = 0;
+ PS(player).buff_shield = time + 0.5; // prevent picking up buffs immediately
// reset timers here to prevent them continuing after re-spawn
player.buff_disability_time = 0;
player.buff_disability_effect_time = 0;
}
-.float stat_sv_maxspeed;
-.float stat_sv_airspeedlimit_nonqw;
-.float stat_sv_jumpvelocity;
-
-MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics)
+MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics_UpdateStats)
{
entity player = M_ARGV(0, entity);
+ // these automatically reset, no need to worry
if(player.buffs & BUFF_SPEED.m_itemid)
- {
- player.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
- player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
- }
+ STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_buffs_speed_speed;
if(time < player.buff_disability_time)
- {
- player.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
- player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
- }
-
- if(player.buffs & BUFF_JUMP.m_itemid)
- {
- // automatically reset, no need to worry
- player.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height;
- }
+ STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_buffs_disability_speed;
}
-MUTATOR_HOOKFUNCTION(buffs, PlayerJump)
+MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics)
{
entity player = M_ARGV(0, entity);
+ // these automatically reset, no need to worry
if(player.buffs & BUFF_JUMP.m_itemid)
- M_ARGV(1, float) = autocvar_g_buffs_jump_height;
+ STAT(MOVEVARS_JUMPVELOCITY, player) = autocvar_g_buffs_jump_height;
}
MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST)
{
- if(MUTATOR_RETURNVALUE || gameover) { return; }
+ if(MUTATOR_RETURNVALUE || game_stopped || !autocvar_g_buffs_drop) return;
entity player = M_ARGV(0, entity);
Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid);
player.buffs = 0;
- player.buff_time = 0; // already notified
+ PS(player).buff_shield = time + max(0, autocvar_g_buffs_pickup_delay);
+ //player.buff_time = 0; // already notified
sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
return true;
}
MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon)
{
- if(MUTATOR_RETURNVALUE || gameover) { return; }
+ if(MUTATOR_RETURNVALUE || game_stopped) return;
entity player = M_ARGV(0, entity);
if(player.buffs & BUFF_SWAPPER.m_itemid)
{
float best_distance = autocvar_g_buffs_swapper_range;
entity closest = NULL;
- FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(
+ FOREACH_CLIENT(IS_PLAYER(it), {
if(!IS_DEAD(it) && !STAT(FROZEN, it) && !it.vehicle)
if(DIFF_TEAM(it, player))
{
closest = it;
}
}
- ));
+ });
if(closest)
{
MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST)
{
+ if(autocvar_g_buffs < 0)
+ return; // no auto replacing of entities in this mode
+
entity ent = M_ARGV(0, entity);
if(autocvar_g_buffs_replace_powerups)
switch(ent.classname)
{
case "item_strength":
- case "item_invincible":
+ case "item_shield":
{
entity e = spawn();
buff_SpawnReplacement(e, ent);
M_ARGV(0, float) *= autocvar_g_buffs_disability_weaponspeed;
}
+.bool buff_flight_crouchheld;
+
MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
{
entity player = M_ARGV(0, entity);
- if(gameover || IS_DEAD(player)) { return; }
+ if(game_stopped || IS_DEAD(player) || frametime || !IS_PLAYER(player)) return;
+
+ if(player.buffs & BUFF_FLIGHT.m_itemid)
+ {
+ if(!PHYS_INPUT_BUTTON_CROUCH(player))
+ player.buff_flight_crouchheld = false;
+ else if(!player.buff_flight_crouchheld)
+ {
+ player.buff_flight_crouchheld = true;
+ player.gravity *= -1;
+ }
+ }
if(time < player.buff_disability_time)
if(time >= player.buff_disability_effect_time)
else
Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid);
player.buffs = 0;
+ PS(player).buff_shield = time + max(0, autocvar_g_buffs_pickup_delay); // always put in a delay, even if small
}
}
if(player.buffs & BUFF_MAGNET.m_itemid)
{
vector pickup_size;
- IL_EACH(g_items, it.classname != "item_flag_team" && it.classname != "item_kh_key",
+ IL_EACH(g_items, it.itemdef,
{
if(it.buffs)
pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff;
}
if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid))
- if(player.alpha != autocvar_g_buffs_invisible_alpha)
- player.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO)
+ player.alpha = ((autocvar_g_buffs_invisible_alpha) ? autocvar_g_buffs_invisible_alpha : -1); // powerups reset alpha, so we must enforce this (TODO)
if(player.buffs & BUFF_MEDIC.m_itemid)
if(time >= player.buff_medic_healtime)
BUFF_ONADD(BUFF_INVISIBLE)
{
if(time < player.strength_finished && g_instagib)
- player.alpha = autocvar_g_instagib_invis_alpha;
+ player.buff_invisible_prev_alpha = default_player_alpha; // we don't want to save the powerup's alpha, as player may lose the powerup while holding the buff
else
- player.alpha = player.buff_invisible_prev_alpha;
+ player.buff_invisible_prev_alpha = player.alpha;
player.alpha = autocvar_g_buffs_invisible_alpha;
}
BUFF_ONREM(BUFF_INVISIBLE)
- player.alpha = player.buff_invisible_prev_alpha;
+ {
+ if(time < player.strength_finished && g_instagib)
+ player.alpha = autocvar_g_instagib_invis_alpha;
+ else
+ player.alpha = player.buff_invisible_prev_alpha;
+ }
+
+ BUFF_ONADD(BUFF_FLIGHT)
+ {
+ player.buff_flight_oldgravity = player.gravity;
+ if(!player.gravity)
+ player.gravity = 1;
+ }
+
+ BUFF_ONREM(BUFF_FLIGHT)
+ player.gravity = ((player.trigger_gravity_check) ? player.trigger_gravity_check.enemy.gravity : player.buff_flight_oldgravity);
player.oldbuffs = player.buffs;
if(player.buffs)
}
else
{
- delete(player.buff_model);
+ if(player.buff_model)
+ delete(player.buff_model);
player.buff_model = NULL;
player.effects &= ~(EF_NOSHADOW);
entity client = M_ARGV(1, entity);
client.buffs = spectatee.buffs;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, VehicleEnter)
-{
- entity player = M_ARGV(0, entity);
- entity veh = M_ARGV(1, entity);
-
- veh.buffs = player.buffs;
- player.buffs = 0;
- veh.buff_time = max(0, player.buff_time - time);
- player.buff_time = 0;
-}
-
-MUTATOR_HOOKFUNCTION(buffs, VehicleExit)
-{
- entity player = M_ARGV(0, entity);
- entity veh = M_ARGV(1, entity);
-
- player.buffs = player.oldbuffs = veh.buffs;
- veh.buffs = 0;
- player.buff_time = time + veh.buff_time;
- veh.buff_time = 0;
+ client.buff_time = spectatee.buff_time;
}
MUTATOR_HOOKFUNCTION(buffs, PlayerRegen)
MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString)
{
- M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs");
+ if(autocvar_g_buffs > 0) // only report as a mutator if they're enabled
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs");
}
MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString)
{
- M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs");
+ if(autocvar_g_buffs > 0)
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs");
}
void buffs_DelayedInit(entity this)