things/enemies/MinibossTroll.cs

Enemy subclass for a miniboss troll. Configures stats, movement, spawn visuals, healing behavior, retreat logic, and animation selection.

using System;
using Sandbox;

public class MinibossTroll : Enemy
{
	public override EnemyType EnemyType => EnemyType.MinibossTroll;
	public override string GibFolder => "miniboss_troll";
	public override float OverrideGibChance => 1f;
	public override int ExtraDeathBloodSprayAmount => 25;
	public override float GetMaxHealth() => MinibossBaseHealth;

	public override Vector3 SpawnScale => new Vector3( 1.4f );
	public override bool ShowHealthbar => true;
	public override float HealthbarOffset => 100f;
	public override float HealthbarOpacity => Utils.EasePercent( SpawnProgress, EasingType.QuadIn );
	public override float HealthbarArmorOpacity => Utils.EasePercent( SpawnProgress, EasingType.QuadIn );

	protected override bool ShouldRetreatFromTarget => IsFearful || (_isRetreating && !IsAttacking);

	private float _healTimer;
	private bool _isRetreating;

	public override bool IsBoss => true;
	public override bool IsMiniboss => true;

	private const float ACCELERATION_MIN = 320f;
	private const float ACCELERATION_MAX = 500f;

	protected override void OnStart()
	{
		base.OnStart();

		CoinValueMin = 12;
		CoinValueMax = 22;
		CoinChance = 1f;

		PushStrength = 9000f;
		Weight = 1.75f;

		_personalSpeedScale = 1.5f;
		_personalSpeedFreq = 12f;

		if ( IsProxy )
			return;

		AggroRange = 90f;
		DetectTargetRange = 850f;
		LoseTargetRange = 1300f;
		LoseTargetTime = 7f;
		MeleeDamage = Utils.Select( Manager.Instance.Difficulty, 10f, 14f, 15f );
		DamageTargetDelay = 0.8f;
		_personalTurnSpeed = 4.5f;
		Acceleration = ACCELERATION_MIN * Utils.Select( Manager.Instance.Difficulty, 0.9f, 1f, 1.05f );
		AccelerationAttacking = ACCELERATION_MAX * Utils.Select( Manager.Instance.Difficulty, 0.9f, 1f, 1.05f );
		Deceleration = 1.4f;
		DecelerationAttacking = 1.5f;
	}

	protected override void OnUpdate()
	{
		base.OnUpdate();

		//Gizmo.Draw.Color = Color.White;
		//Gizmo.Draw.Text( $"Health: {Health}", new global::Transform( WorldPosition ) );

		_personalSpeedScale = Utils.Map( Health, MaxHealth, 0f, 1.25f, 2f, EasingType.QuadOut );

		if ( IsProxy )
			return;

		if ( IsDying )
			return;

		Acceleration = AccelerationAttacking = Utils.Map( Health, MaxHealth, 0f, ACCELERATION_MIN, ACCELERATION_MAX, EasingType.QuadOut ) * Utils.Select( Manager.Instance.Difficulty, 0.9f, 1f, 1.05f );

		if ( _isRetreating )
		{
			float stopRetreatThreshold = 0.6f + Utils.FastSin( TimeSinceSpawn * 0.8f ) * 0.15f;
			if ( Health > MaxHealth * stopRetreatThreshold )
				_isRetreating = false;
		}
		else
		{
			float retreatThreshold = 0.2f + Utils.FastSin( TimeSinceSpawn * 0.5f ) * 0.1f;
			if ( Health < MaxHealth * retreatThreshold )
				_isRetreating = true;
		}

		_healTimer += Time.Delta;
		if ( _healTimer > Utils.Map( Health, MaxHealth, 0f, 2f, 0.5f, EasingType.QuadIn ) )
		{
			var healAmount = Utils.Select( Manager.Instance.Difficulty, 5f, 8f, 12f );
			Heal( Utils.Map( Health, MaxHealth, 0f, 1f, healAmount, EasingType.Linear ) );
			_healTimer = 0f;
		}
	}

	protected override float GetMoveSpeedFactor()
	{
		var progress = ModelRenderer.SceneModel.CurrentSequence.TimeNormalized;
		return Zombie.GetZombieMoveSpeedFactor( progress );
	}

	protected override void PlayWalkAnim()
	{
		SetAnim( "Walk" );
	}
}