Perk component that grants a Dash Slash ability. It modifies player stats for dash damage, length and invulnerability, plays a sound when dashing, makes the player temporarily ignore physics while dashing, and damages enemies the player passes through exactly once per enemy.
using System;
using Sandbox;
[Perk( Rarity.Uncommon, alwaysOfferDebug: false )]
public class PerkDashSlash : Perk
{
private enum Mod { DamagePercent, DashLength };
private List<Enemy> _hitEnemies = new List<Enemy>();
private bool _ignorePhysicsActive;
static PerkDashSlash()
{
Register<PerkDashSlash>(
name: "Dash Slash",
imagePath: "textures/icons/vector/dash_slash.png",
description: level => $"Dash through enemies\nfor {GetValue( level, Mod.DamagePercent, true )}% of bullet dmg\n+{GetValue( level, Mod.DashLength, true )}% dash distance",
upgradeDescription: level => $"Dash through enemies\nfor {GetValue( level - 1, Mod.DamagePercent, true )}%→{GetValue( level, Mod.DamagePercent, true )}% of bullet dmg\n+{GetValue( level - 1, Mod.DashLength, true )}%→{GetValue( level, Mod.DashLength, true )}% dash distance"
);
}
public override void Start()
{
base.Start();
}
public override void Refresh()
{
base.Refresh();
Player.Modify( this, PlayerStat.DashSlashBulletDamagePercent, GetValue( Level, Mod.DamagePercent ), ModifierType.Add );
Player.Modify( this, PlayerStat.DashStrength, GetValue( Level, Mod.DashLength ), ModifierType.Mult );
Player.Modify( this, PlayerStat.DashInvulnTime, GetValue( Level, Mod.DashLength ), ModifierType.Mult );
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.DamagePercent:
default:
return isPercent
? 70f + 50f * level + (level == 6 ? 20f : 0f)
: 0.7f + 0.5f * level + (level == 6 ? 0.2f : 0f);
case Mod.DashLength:
return isPercent
? 10f * level
: 1f + 0.10f * level;
}
}
public override void OnDashStarted( Vector2 dir )
{
base.OnDashStarted( dir );
Manager.Instance.PlaySfxNearbyRpc( "player.dash.slash", Player.Position2D, pitch: Game.Random.Float( 1.05f, 1.1f ), volume: 0.7f, maxDist: 200f );
_hitEnemies.Clear();
if( !_ignorePhysicsActive )
{
Player.IgnorePhysicsAmount++;
_ignorePhysicsActive = true;
}
}
public override void OnDashFinished( Vector2 dir )
{
base.OnDashFinished( dir );
if ( _ignorePhysicsActive )
{
Player.IgnorePhysicsAmount--;
_ignorePhysicsActive = false;
}
}
public override void Colliding( Thing other, float percent, float dt )
{
base.Colliding( other, percent, dt );
if ( Player.IsDashing && other is Enemy enemy && !enemy.IsDying && !_hitEnemies.Contains( enemy ) )
{
float damage = Player.GetBulletDamage( isFromClip: false, isLastAmmo: false ) * GetValue( Level, Mod.DamagePercent );
Vector2 dir = (other.Position2D - Player.Position2D).LengthSquared > Manager.TOUCH_DIST_REQUIRED_SQR
? (other.Position2D - Player.Position2D).Normal
: Utils.GetRandomVector();
var hitPos = enemy.Position2D - dir * enemy.Radius;
enemy.DamageRpc( damage, Player, DamageType.DashSlash, new Vector3( hitPos.x, hitPos.y, enemy.Height ), force: dir * 50f, isCrit: false, shouldFlinch: true );
_hitEnemies.Add( enemy );
}
}
public override void Remove( bool restart = false )
{
base.Remove( restart );
if ( _ignorePhysicsActive )
Player.IgnorePhysicsAmount--;
}
}