A unique curse perk for a player that deals self-damage when the player turns counterclockwise a certain accumulated amount. It tracks yaw changes, accumulates positive (counterclockwise) rotation, and applies damage and visual feedback when a threshold is crossed.
using System;
using Sandbox;
[Perk( Rarity.Unique, curse: true, alwaysOfferDebug: false, IncludedCategories = new[] { PerkCategory.SelfDmg, PerkCategory.OneHpLeft })]
public class CurseHurtTurnCounterclockwise : Perk
{
private enum Mod { HpLoss };
// Track player rotation over time
private float _lastYaw;
private float _accumulatedCCW;
private TimeSince _timeSinceHurt;
static CurseHurtTurnCounterclockwise()
{
Register<CurseHurtTurnCounterclockwise>(
name: "Forbidden Glance",
imagePath: "textures/icons/vector/curse_hurt_turn_counterclockwise.png",
description: level => $"-{(int)GetValue( level, Mod.HpLoss )} hp while turning counterclockwise"
);
}
public override void Start()
{
base.Start();
ShouldUpdate = true;
_lastYaw = Player.Model.LocalRotation.Yaw();
_accumulatedCCW = 0f;
HighlightColor = new Color( 1f, 0.3f, 0.6f );
HighlightDuration = 0.75f;
HighlightOpacity = 6f;
}
public override void Refresh()
{
base.Refresh();
}
public override void Update( float dt )
{
base.Update( dt );
// Track turning
float currentYaw = Player.Model.LocalRotation.Yaw();
float deltaYaw = AngleDelta( _lastYaw, currentYaw );
_lastYaw = currentYaw;
// Counterclockwise is positive deltaYaw
if ( deltaYaw > 0f )
_accumulatedCCW += deltaYaw;
var turnThreshold = 45f; // degrees to trigger damage
if ( _accumulatedCCW > turnThreshold && _timeSinceHurt > 0.25f )
{
var dir = Utils.GetPerpendicularVector( Player.FacingDir );
Player.Damage( GetValue( Level, Mod.HpLoss ), DamageType.Self, Player.Position2D, dir, upwardAmount: 0f, force: 0f, ragdollForce: 1f, enemySource: null, enemyType: EnemyType.None );
Highlight();
_accumulatedCCW -= turnThreshold;
_timeSinceHurt = 0f;
IconScale = Game.Random.Float( 1.2f, 1.3f );
IconAngleOffset = Game.Random.Float( 10f, 20f ) * (Game.Random.Int( 0, 1 ) == 0 ? -1f : 1f);
}
_accumulatedCCW *= (1f - Time.Delta * 1.5f);
}
private static float GetValue( int level, Mod mod, bool isPercent = false )
{
switch ( mod )
{
case Mod.HpLoss:
default:
return 3f;
//switch( level )
//{
// case 1: default: return 1;
// case 2: return 5;
// case 3: return 10;
// case 4: return 25;
//}
}
}
private static float AngleDelta( float from, float to )
{
float delta = (to - from) % 360f;
if ( delta > 180f ) delta -= 360f;
if ( delta < -180f ) delta += 360f;
return delta;
}
}