RoguelikeKit/Assets/Common/Scripts/Enemies/RangedEnemyBehavior.cs

175 lines
5.8 KiB
C#

using OctoberStudio.Extensions;
using OctoberStudio.Pool;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace OctoberStudio.Enemy
{
public class RangedEnemyBehavior : EnemyBehavior
{
private static readonly int RANGED_ATTACK_TRIGGER = Animator.StringToHash("Ranged Attack");
[Header("Ranged Enemy Data")]
[SerializeField] Animator animator;
[SerializeField] AttackType attackType = AttackType.AtPlayer;
[Space]
[SerializeField, Min(1)] int minProjectilesPerAttack = 1;
[SerializeField, Min(1)] int maxProjectilesPerAttack = 1;
[SerializeField] float timeBetweenProjectiles = 0f;
[SerializeField] float projectileDamage = 1f;
[SerializeField, Range(0, 90f)] float projectileSpread = 0f;
[Header("Overrides")]
[SerializeField] bool overrideProjectileSpeed = false;
[SerializeField, Min(0.01f)] float projectileSpeed = 1f;
[SerializeField] bool overrideProjectileLifetime = false;
[SerializeField, Min(0.01f)] float projectileLifetime = 1f;
[Space]
[SerializeField] GameObject projectilePrefab;
[Header("Shooting Behavior")]
[SerializeField, Min(0)] float rangedAttackCooldown = 3;
[SerializeField] bool useAttackAnimation = false;
[SerializeField, Min(0)] float minDistanceToPlayerForAttacking = 5f;
private float minDistanceToPlayerForAttackingSqr;
[Space]
[Tooltip("Only works if 'useAttackAnimation' is true")]
[SerializeField] bool changeSpeedDuringAttackAnimation = false;
[SerializeField, Min(0)] float speedDuringAttackAnimation = 1f;
private static Dictionary<GameObject, PoolComponent<SimpleEnemyProjectileBehavior>> projectilePools = new Dictionary<GameObject, PoolComponent<SimpleEnemyProjectileBehavior>>();
private float lastTimeAttacked = 0;
protected override void Awake()
{
base.Awake();
if (!projectilePools.ContainsKey(projectilePrefab))
{
var pool = new PoolComponent<SimpleEnemyProjectileBehavior>(projectilePrefab, maxProjectilesPerAttack * 2);
projectilePools.Add(projectilePrefab, pool);
}
minDistanceToPlayerForAttackingSqr = minDistanceToPlayerForAttacking * 2;
}
public override void Play()
{
base.Play();
lastTimeAttacked = Time.time;
}
protected override void Update()
{
base.Update();
var distanceToPlayer = (Center - PlayerBehavior.CenterPosition).sqrMagnitude;
if(distanceToPlayer < minDistanceToPlayerForAttackingSqr && lastTimeAttacked + rangedAttackCooldown < Time.time)
{
if (useAttackAnimation)
{
animator.SetTrigger(RANGED_ATTACK_TRIGGER);
if (changeSpeedDuringAttackAnimation)
{
Speed = speedDuringAttackAnimation;
}
} else
{
Attack();
}
lastTimeAttacked = Time.time;
}
}
public virtual void Attack()
{
if (!IsAlive) return;
var projectilesCount = Random.Range(minProjectilesPerAttack, maxProjectilesPerAttack + 1);
if(attackType == AttackType.Circular)
{
StartCoroutine(CircularAttack(projectilesCount));
} else
{
StartCoroutine(AtPlayerAttack(projectilesCount));
}
}
protected virtual IEnumerator CircularAttack(int projectilesCount)
{
var wait = new WaitForSeconds(timeBetweenProjectiles);
var enemyToPlayerDirection = (PlayerBehavior.CenterPosition - Center).normalized;
var step = 360f / projectilesCount;
for (int i = 0; i < projectilesCount; i++)
{
var projectileDirection = Quaternion.Euler(0, 0, i * step + Random.Range(-projectileSpread, projectileSpread)) * enemyToPlayerDirection;
LaunchProjectile(projectileDirection);
if (timeBetweenProjectiles > 0) yield return wait;
}
}
protected virtual IEnumerator AtPlayerAttack(int projectilesCount)
{
var wait = new WaitForSeconds(timeBetweenProjectiles);
var enemyToPlayerDirection = (PlayerBehavior.CenterPosition - Center).normalized;
for (int i = 0; i < projectilesCount; i++)
{
var projectileDirection = Quaternion.Euler(0, 0, Random.Range(-projectileSpread, projectileSpread)) * enemyToPlayerDirection;
LaunchProjectile(projectileDirection);
if (timeBetweenProjectiles > 0) yield return wait;
}
}
protected virtual void LaunchProjectile(Vector2 direction)
{
var projectile = projectilePools[projectilePrefab].GetEntity();
if (overrideProjectileLifetime) projectile.LifeTime = projectileLifetime;
if (overrideProjectileSpeed) projectile.Speed = projectileSpeed;
projectile.Damage = projectileDamage;
projectile.Init(Center, direction);
}
public void OnAttackAnimationEnded()
{
if (changeSpeedDuringAttackAnimation)
{
Speed = speed;
}
}
public enum AttackType
{
AtPlayer = 0,
Circular = 1,
}
protected virtual void OnDestroy()
{
if (projectilePools.ContainsKey(projectilePrefab))
{
projectilePools[projectilePrefab].Destroy();
projectilePools.Remove(projectilePrefab);
}
}
}
}