TurnBasedStrategyCourse/Assets/Scripts/Actions/ShootAction.cs

140 lines
5.6 KiB
C#

using System;
using System.Collections.Generic;
using Grid;
using UnityEngine;
namespace Actions {
public class ShootAction : BaseAction {
private const float ROTATION_SPEED = 10f;
private const float SHOOTING_STATE_TIME = .2f;
private const float COOL_OFF_STATE_TIME = 1f;
private const float AIMING_STATE_TIME = 1.5f;
private const float UNIT_SHOULDER_HEIGHT = 1.7f;
[SerializeField] private LayerMask obstaclesLayerMask;
private bool canShootBullet;
private State state;
private float stateTimer;
public Unit TargetUnit { get; private set; }
public Unit ShootingUnit { get; private set; }
public int MaxShootDistance { get; private set; }
protected override void Awake() {
base.Awake();
MaxShootDistance = 7;
ActionName = "Shoot";
}
private void Update() {
if (!IsActive) return;
stateTimer -= Time.deltaTime;
switch (state) {
case State.Aiming:
Vector3 aimDirection = (TargetUnit.GetWorldPosition() - Unit.GetWorldPosition()).normalized;
aimDirection.y = 0f;
transform.forward = Vector3.Slerp(transform.forward, aimDirection, ROTATION_SPEED * Time.deltaTime);
break;
case State.Shooting:
if (canShootBullet) {
Shoot();
canShootBullet = false;
}
break;
case State.Cooloff:
break;
}
if (stateTimer <= 0f) NextState();
}
public event EventHandler<ShootEventArgs> OnShoot;
public static event EventHandler<ShootEventArgs> OnAnyShoot;
private void Shoot() {
OnAnyShoot?.Invoke(this, new() { TargetUnit = TargetUnit, ShootingUnit = Unit });
OnShoot?.Invoke(this, new() { TargetUnit = TargetUnit, ShootingUnit = Unit });
TargetUnit.Damage(40);
}
private void NextState() {
switch (state) {
case State.Aiming:
state = State.Shooting;
stateTimer = SHOOTING_STATE_TIME;
break;
case State.Shooting:
state = State.Cooloff;
stateTimer = COOL_OFF_STATE_TIME;
break;
case State.Cooloff:
ActionComplete();
break;
}
}
public override void TakeAction(GridPosition gridPosition, Action onActionComplete) {
IsActive = true;
TargetUnit = LevelGrid.Instance.GetUnitAtGridPosition(gridPosition);
canShootBullet = true;
state = State.Aiming;
stateTimer = AIMING_STATE_TIME;
ActionStart(onActionComplete);
}
public override List<GridPosition> GetValidActionGridPositionList() => GetValidActionGridPositionList(Unit.GridPosition);
private List<GridPosition> GetValidActionGridPositionList(GridPosition unitGridPosition) {
List<GridPosition> validGridPositionList = new();
for (int xPosition = -MaxShootDistance; xPosition <= MaxShootDistance; xPosition++) {
for (int zPosition = -MaxShootDistance; zPosition <= MaxShootDistance; zPosition++) {
for (int floor = -MaxShootDistance; floor < MaxShootDistance; floor++) {
GridPosition offsetGridPosition = new(xPosition, zPosition, floor);
GridPosition testGridPosition = unitGridPosition + offsetGridPosition;
if (!LevelGrid.Instance.IsValidGridPosition(testGridPosition)) continue; //Only return valid grid positions
if (Mathf.Abs(xPosition) + Mathf.Abs(zPosition) > MaxShootDistance) continue; //Out of Range
if (!LevelGrid.Instance.HasAnyUnitOnGridPosition(testGridPosition)) continue; //Grid position is empty, no unit
Unit unitAtGridPosition = LevelGrid.Instance.GetUnitAtGridPosition(testGridPosition);
if (unitAtGridPosition.IsEnemy == Unit.IsEnemy) continue; //Both units are on the same 'team'
TargetUnit = unitAtGridPosition;
Vector3 unitWorldPosition = LevelGrid.Instance.GetWorldPosition(unitGridPosition);
Vector3 shootDir = (TargetUnit.GetWorldPosition() - unitWorldPosition).normalized;
if (Physics.Raycast(unitWorldPosition + Vector3.up * UNIT_SHOULDER_HEIGHT, shootDir, Vector3.Distance(unitWorldPosition, TargetUnit.GetWorldPosition()), obstaclesLayerMask)) continue; // Blocked by an obstacle
validGridPositionList.Add(testGridPosition);
}
}
}
return validGridPositionList;
}
protected override EnemyAIAction GetEnemyAIAction(GridPosition gridPosition) {
Unit targetUnit = LevelGrid.Instance.GetUnitAtGridPosition(gridPosition);
return new(gridPosition, 100 + Mathf.RoundToInt((1 - targetUnit.HealthSystem.HealthNormalized) * 100f));
}
public int GetTargetCountAtPosition(GridPosition gridPosition) => GetValidActionGridPositionList(gridPosition).Count;
private enum State {
Aiming,
Shooting,
Cooloff
}
public class ShootEventArgs : EventArgs {
public Unit ShootingUnit;
public Unit TargetUnit;
}
}
}