A player perk class named PerkShootOnlyWhenClick (Trigger Discipline) that accumulates bonus shot damage over time when the player is not shooting or reloading, shows that as UI (icon, text, cooldown color/scale), and applies the accumulated damage to the next shot. It resets the accumulation on shoot and modifies player stats while active.
using System;
using Sandbox;
[Perk( Rarity.Unique, locked: true, minUnlocksReq: 2, alwaysOfferDebug: false )]
public class PerkShootOnlyWhenClick : Perk
{
private enum Mod { DamageGainSpeed, MaxDamage };
private float _accumulatedDamage;
private int _currDmgInt;
static PerkShootOnlyWhenClick()
{
Register<PerkShootOnlyWhenClick>(
name: "Trigger Discipline",
imagePath: "textures/icons/vector/shoot_only_when_click.png",
description: level => $"Shoot with left-click\nWhile not shooting or reloading,\n+{GetValue( level, Mod.DamageGainSpeed ).ToString("0.##")} dmg/s for next shot (max: [n]+{(int)GetValue( level, Mod.MaxDamage )}[/n])"
);
}
public override void Start()
{
base.Start();
ShouldUpdate = true;
}
public override void Refresh()
{
base.Refresh();
Player.Modify( this, PlayerStat.OnlyShootWithMouse1, 1f, ModifierType.Add );
DisplayCooldownColor = new Color( 0.4f, 0.4f, 1f );
}
public override void Update( float dt )
{
base.Update( dt );
if ( !Player.IsReloading )
{
var maxDmg = GetValue( Level, Mod.MaxDamage );
_accumulatedDamage = Math.Min( _accumulatedDamage + GetValue( Level, Mod.DamageGainSpeed ) * dt, maxDmg );
Player.Modify( this, PlayerStat.ShotDamageAdd, _accumulatedDamage, ModifierType.Add );
DisplayCooldown = Utils.Map( _accumulatedDamage, 0f, maxDmg, 0f, 1f );
}
DisplayText = $"+{_accumulatedDamage.ToString("0.#")}";
var targetScale = Input.Down( "click" )
? 1f
: 1.3f;
IconScale = MathX.Lerp( IconScale, targetScale, Time.Delta * 20f );
var newDmg = _accumulatedDamage.FloorToInt();
if( newDmg != _currDmgInt )
{
if(!Input.Down( "click" ) )
{
IconScale *= 1.1f;
Manager.Instance.PlaySfxUI( "click", pitch: Utils.Map( _currDmgInt, 1, 50, 1f, 3f ), volume: Utils.Map( _currDmgInt, 1, 20, 0.05f, 0.3f ) );
}
_currDmgInt = newDmg;
}
// todo: different holdtype while not shooting
}
public override void OnShoot()
{
base.OnShoot();
_accumulatedDamage = 0f;
Player.StopModifying( this, PlayerStat.ShotDamageAdd );
DisplayCooldown = 0f;
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.DamageGainSpeed:
default:
return 1f;
case Mod.MaxDamage:
return 30f;
}
}
}