things/enemies/SpitterElite.cs

Enemy subclass SpitterElite. Configures stats, AI ranges, timers and behavior overrides for an elite spitter enemy, including health, movement/shooting parameters, spawning scale, shooting logic and reactions to damage.

NetworkingFile Access
using System;
using Sandbox;

public class SpitterElite : Spitter
{
	public override EnemyType EnemyType => EnemyType.SpitterElite;
	public override float GetMaxHealth()
	{
		switch ( Manager.Instance.Difficulty )
		{
			case 0: default: return 82f;
			case 1: return 90f;
			case 2: return 90f;
		}
	}

	public override Vector3 SpawnScale => new Vector3( 1.2f );

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

		CoinValueMin = 3;
		CoinValueMax = 5;
		CoinChance = 1f;

		PushStrength = 8000f;
		Weight = 1.6f;

		_personalSpeedScale = Game.Random.Float( 1.1f, 1.2f );
		_personalSpeedFreq = Game.Random.Float( 9f, 11f );

		if ( IsProxy )
			return;

		DetectTargetRange = 800f;
		LoseTargetRange = 1300f;
		LoseTargetTime = 5f;
		MeleeDamage = Utils.Select( Manager.Instance.Difficulty, 10f, 11f, 12f );
		DamageTargetDelay = 0.6f;
		_personalTurnSpeed = Game.Random.Float( 6f, 7.5f );
		Acceleration = Utils.Select( Manager.Instance.Difficulty, 200f, 220f, 225f );
		AccelerationAttacking = Utils.Select(Manager.Instance.Difficulty, 240f, 275f, 275f);
		Deceleration = 1.9f;
		DecelerationAttacking = 1.8f;

		_shootDelayMin = Utils.Select( Manager.Instance.Difficulty, 6f, 4f, 1f );
		_shootDelayMax = Utils.Select( Manager.Instance.Difficulty, 15f, 13f, 12f); ;
		_shootRange = Utils.Select( Manager.Instance.Difficulty, 480f, 500f, 530f );
		_shootDelayTimer = Game.Random.Float( _shootDelayMin, _shootDelayMax );
		_prepareShootTime = 0.9f;
		_shootTime = 0.6f;

		_blinkDelayMin = 9f;
		_blinkDelayMax = 16f;
		_blinkPrepareDelay = 0.45f;
		_blinkRange = 800f;
		_blinkDelayTimer = Game.Random.Float( _blinkDelayMin, _blinkDelayMax );

		_personalRetreatRange = Game.Random.Float( 230f, 250f );
		_personalStopRetreatRange = Game.Random.Float( 320f, 350f );
	}

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


	}

	protected override Vector2 GetTargetOffset()
	{
		var offset = TargetUnit.Velocity * (0.5f + Utils.FastSin( TimeSinceSpawn * 2.6f ) * 0.5f) * (TargetUnit.Position2D - Position2D).Length * 0.012f;

		if ( State == SpitterState.Default && !_isRetreating )
			offset += new Vector2( Utils.FastSin( TimeSinceSpawn * 0.73f ), Utils.FastSin( TimeSinceSpawn * 0.64f ) ) * 85f;

		return offset;
	}

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

		//SetPlaybackRate( 1.43f );
		SetPlaybackRate( 1f );
	}

	protected override void Shoot()
	{
		Manager.Instance.PlaySfxNearby( "spitter.shoot", Position2D, pitch: Game.Random.Float( 1.2f, 1.3f ), volume: 0.85f, maxDist: 450f );

		if ( IsProxy )
			return;

		var dir = (Vector2)WorldRotation.Forward;
		var pos = Position2D + dir * 40f;
		Manager.Instance.SpawnEnemyHomingProjectile( pos, dir, shooter: this, enemyType: this.EnemyType, startVel: Game.Random.Float( 50f, 70f ), projectileType: ProjectileType );

		// todo: scale up the flying skull after spawn so it doesn't just snap in?
	}

	protected override void Damage( float damage, Player player, DamageType damageType, Vector3 hitPos, Vector2 force, bool isCrit = false, bool shouldFlinch = true, DamageResultFlags damageFlags = DamageResultFlags.None )
	{
		base.Damage( damage, player, damageType, hitPos, force, isCrit, shouldFlinch, damageFlags );

		if ( IsProxy || IsDying )
			return;

		if ( State == SpitterState.Default && !IsStunned && _timeSinceBlinking > Game.Random.Float( 4f, 8f ) && Game.Random.Float( 0f, 1f ) < Utils.Map( HpPercent, 1f, 0f, 0f, 1f ) )
			SetState( SpitterState.BlinkPrepare );
	}
}