A Legendary perk component called PerkAutoReroll. It tracks time since the player last rerolled or chose a perk while choosing a level-up reward, and after a time limit deals self-damage and forces a free reroll. It updates UI cooldown visuals and resets its timer on various player events.
using System;
using Sandbox;
[Perk( Rarity.Legendary, onlyUnpausedChoosing: true, locked: true, minUnlocksReq: 3, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.SelfDmg })]
public class PerkAutoReroll : Perk
{
private enum Mod { TimeLimit, HpLoss };
private TimeSince _timeSinceTrigger;
static PerkAutoReroll()
{
Register<PerkAutoReroll>(
name: "Rerolling Machine",
imagePath: "textures/icons/vector/auto_reroll.png",
description: level => $"Every {(int)GetValue( level, Mod.TimeLimit )}s that you haven't rerolled or chosen a perk, lose {(int)GetValue( level, Mod.HpLoss)} hp and reroll for free",
upgradeDescription: level => $"Every {(int)GetValue( level - 1, Mod.TimeLimit )}→{(int)GetValue( level, Mod.TimeLimit )}s that you haven't rerolled or chosen a perk, lose {(int)GetValue( level - 1, Mod.HpLoss )}→{(int)GetValue( level, Mod.HpLoss )} hp and reroll for free"
);
}
public override void Start()
{
base.Start();
ShouldUpdate = true;
_timeSinceTrigger = 0f;
HighlightColor = new Color( 1f, 0.5f, 0.7f );
HighlightDuration = 0.3f;
HighlightOpacity = 5f;
}
public override void Refresh()
{
base.Refresh();
}
public override void Update( float dt )
{
base.Update( dt );
if ( Player.IsChoosingLevelUpReward && _timeSinceTrigger > GetValue( Level, Mod.TimeLimit ) )
{
Player.UseReroll( free: true );
_timeSinceTrigger = 0f;
// todo: this can work with paused choosing too?
Player.Damage( GetValue( Level, Mod.HpLoss ), DamageType.Self, Player.Position2D, Utils.GetRandomVector(), upwardAmount: 0f, force: 0f, ragdollForce: 0.5f, enemySource: null, enemyType: EnemyType.None );
Highlight();
Player.ScaleHeightRpc( amount: 1.4f, time: Game.Random.Float( 0.05f, 0.06f ) );
}
if ( Player.IsChoosingLevelUpReward )
{
DisplayCooldown = Utils.Map( _timeSinceTrigger, 0f, GetValue( Level, Mod.TimeLimit ), 0f, 1f );
DisplayCooldownColor = Color.Lerp(
Color.Black,
Color.Lerp( Color.White, Color.Red, 0.5f + Utils.FastSin( RealTime.Now * 24f ) * 0.5f ),
Utils.Map( _timeSinceTrigger, 0f, GetValue( Level, Mod.TimeLimit ), 0f, 1f, EasingType.SineIn )
);
}
else
{
DisplayCooldown = 0f;
}
}
public override void OnChoosePerk( TypeDescription type )
{
base.OnChoosePerk( type );
_timeSinceTrigger = 0f;
}
public override void OnRerollAfter()
{
base.OnRerollAfter();
_timeSinceTrigger = 0f;
}
public override void OnPlayerLevelUp()
{
base.OnPlayerLevelUp();
if( Player.NumPerkPointsAvailable <= 1 )
_timeSinceTrigger = 0f;
}
public override void OnRevive()
{
base.OnRevive();
_timeSinceTrigger = 0f;
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.TimeLimit:
default:
return 20f - 5f * level;
case Mod.HpLoss:
return 20f - 5f * level;
}
}
}