197 lines
6.4 KiB
C#
197 lines
6.4 KiB
C#
using System;
|
|
using BumpCombat.Enemies;
|
|
using UnityEngine;
|
|
|
|
namespace BumpCombat.Defense
|
|
{
|
|
[DisallowMultipleComponent]
|
|
public sealed class DefenseRouteAgent : MonoBehaviour
|
|
{
|
|
private EnemyController enemy;
|
|
private Vector2[] waypoints = Array.Empty<Vector2>();
|
|
private Action<DefenseRouteAgent> arrivalHandler;
|
|
private Transform player;
|
|
private BumpCombat.Player.PlayerHealth playerHealth;
|
|
private float routeSpeed;
|
|
private float playerEngagementRange;
|
|
private float postAttackProgressSeconds;
|
|
private float postAttackProgressRemaining;
|
|
private bool wasAttackSequenceInProgress;
|
|
private bool configured;
|
|
private int waypointIndex;
|
|
|
|
public EnemyController Enemy => enemy;
|
|
public int Lane { get; private set; }
|
|
public int LifetimeId => enemy != null ? enemy.SpawnLifetimeIdentity : -1;
|
|
public DefenseTowerKind ImmuneTowerKind { get; private set; }
|
|
public Vector2 AnchorPosition => waypoints.Length > 0 ? waypoints[0] : transform.position;
|
|
public bool HasArrived { get; private set; }
|
|
|
|
public void Configure(
|
|
EnemyController configuredEnemy,
|
|
int lane,
|
|
Vector2[] route,
|
|
float speed,
|
|
float engagementRange,
|
|
float postAttackProgress,
|
|
DefenseTowerKind immuneTowerKind,
|
|
Action<DefenseRouteAgent> onArrived)
|
|
{
|
|
enemy = configuredEnemy;
|
|
Lane = lane;
|
|
waypoints = route ?? Array.Empty<Vector2>();
|
|
routeSpeed = Mathf.Max(0.1f, speed);
|
|
playerEngagementRange = Mathf.Max(0.1f, engagementRange);
|
|
postAttackProgressSeconds = Mathf.Max(0f, postAttackProgress);
|
|
postAttackProgressRemaining = 0f;
|
|
wasAttackSequenceInProgress = false;
|
|
ImmuneTowerKind = immuneTowerKind;
|
|
arrivalHandler = onArrived;
|
|
waypointIndex = 0;
|
|
configured = enemy != null && waypoints.Length > 0;
|
|
HasArrived = false;
|
|
player = FindAnyObjectByType<BumpCombat.Player.PlayerController>()?.transform;
|
|
playerHealth = player != null
|
|
? player.GetComponent<BumpCombat.Player.PlayerHealth>()
|
|
: null;
|
|
if (configured)
|
|
{
|
|
enemy.TryReposition(waypoints[0]);
|
|
}
|
|
}
|
|
|
|
public bool TryDriveFixedUpdate(float deltaTime)
|
|
{
|
|
if (!configured || HasArrived || enemy == null || enemy.IsDead)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (enemy.State == EnemyState.Spawn)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool attackSequenceActive = enemy.IsAttackSequenceInProgress;
|
|
if (attackSequenceActive)
|
|
{
|
|
wasAttackSequenceInProgress = true;
|
|
return false;
|
|
}
|
|
|
|
if (wasAttackSequenceInProgress)
|
|
{
|
|
wasAttackSequenceInProgress = false;
|
|
postAttackProgressRemaining = postAttackProgressSeconds;
|
|
}
|
|
|
|
if (enemy.IsStunned)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
postAttackProgressRemaining = Mathf.Max(
|
|
0f,
|
|
postAttackProgressRemaining - Mathf.Max(0f, deltaTime));
|
|
if (player == null)
|
|
{
|
|
player = FindAnyObjectByType<BumpCombat.Player.PlayerController>()?.transform;
|
|
playerHealth = player != null
|
|
? player.GetComponent<BumpCombat.Player.PlayerHealth>()
|
|
: null;
|
|
}
|
|
|
|
if (postAttackProgressRemaining <= 0f
|
|
&& player != null
|
|
&& playerHealth != null
|
|
&& playerHealth.CurrentHealth > 0f
|
|
&& (player.position - transform.position).sqrMagnitude
|
|
<= playerEngagementRange * playerEngagementRange)
|
|
{
|
|
enemy.Alert();
|
|
return false;
|
|
}
|
|
|
|
if (waypointIndex >= waypoints.Length)
|
|
{
|
|
CompleteArrival();
|
|
return true;
|
|
}
|
|
|
|
Vector2 current = transform.position;
|
|
Vector2 target = waypoints[waypointIndex];
|
|
Vector2 next = Vector2.MoveTowards(
|
|
current,
|
|
target,
|
|
routeSpeed * Mathf.Max(0f, deltaTime));
|
|
if (!enemy.TryReposition(next))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
Vector2 movementDirection = next - current;
|
|
if (movementDirection.sqrMagnitude > 0.000001f)
|
|
{
|
|
enemy.SetFacingDirection(movementDirection);
|
|
}
|
|
Animator animator = enemy.GetComponent<Animator>();
|
|
animator?.SetBool("IsMoving", (next - current).sqrMagnitude > 0.000001f);
|
|
if ((target - next).sqrMagnitude <= 0.01f)
|
|
{
|
|
waypointIndex++;
|
|
if (waypointIndex >= waypoints.Length)
|
|
{
|
|
CompleteArrival();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool TryTakeTowerDamage(
|
|
DefenseTowerKind sourceKind,
|
|
float requestedDamage,
|
|
float distanceToPlayer,
|
|
float sightRadius,
|
|
float outsideSightEfficiency,
|
|
Vector2 sourcePosition)
|
|
{
|
|
if (!configured
|
|
|| HasArrived
|
|
|| enemy == null
|
|
|| enemy.IsDead
|
|
|| DefensePrototypeRules.IsTowerImmune(
|
|
sourceKind,
|
|
ImmuneTowerKind))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
float efficiency = DefensePrototypeRules.GetTowerEfficiency(
|
|
distanceToPlayer,
|
|
sightRadius,
|
|
1f,
|
|
outsideSightEfficiency);
|
|
Vector2 direction = ((Vector2)transform.position - sourcePosition).normalized;
|
|
return enemy.TryTakeDamage(
|
|
Mathf.Max(0f, requestedDamage) * efficiency,
|
|
direction,
|
|
0f,
|
|
false,
|
|
false,
|
|
EnemyDamageSource.Tower);
|
|
}
|
|
|
|
private void CompleteArrival()
|
|
{
|
|
if (HasArrived)
|
|
{
|
|
return;
|
|
}
|
|
|
|
HasArrived = true;
|
|
arrivalHandler?.Invoke(this);
|
|
}
|
|
}
|
|
}
|