223 lines
7.8 KiB
C#
223 lines
7.8 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Drawing;
|
|
using Pathfinding;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEngine;
|
|
using Random = UnityEngine.Random;
|
|
|
|
namespace Reset.Units{
|
|
public class EnemySpawn : MonoBehaviour{
|
|
enum SpawnerState{
|
|
Idle = 0,
|
|
TargetAcquired,
|
|
TargetDropped,
|
|
InCombatUnlocked,
|
|
InCombatLocked,
|
|
Cooldown,
|
|
}
|
|
|
|
public float radius = 30f;
|
|
|
|
public int minimumEnemies = 1;
|
|
public int maximumEnemies = 5;
|
|
|
|
public Vector2 enemyCount;
|
|
|
|
// TODO: Replace this with an Enemy selector based on difficulty, random chance, etc?
|
|
public GameObject enemy;
|
|
|
|
public List<GameObject> enemies;
|
|
|
|
public GridGraph relatedGraph;
|
|
|
|
private SpawnerState spawnerState;
|
|
private float timeInState;
|
|
|
|
void Start(){
|
|
CreateAstarGraph();
|
|
SpawnEnemies();
|
|
}
|
|
|
|
void CreateAstarGraph(){
|
|
relatedGraph = AstarPath.active.data.AddGraph(typeof(GridGraph)) as GridGraph;
|
|
|
|
relatedGraph.SetDimensions(Mathf.FloorToInt(radius) * 2, Mathf.FloorToInt(radius) * 2, 1f);
|
|
relatedGraph.collision.diameter = 3f;
|
|
|
|
AstarPath.active.Scan(relatedGraph);
|
|
|
|
GetComponent<ProceduralGraphMover>().graph = relatedGraph;
|
|
}
|
|
|
|
void SpawnEnemies(){
|
|
int count = Random.Range(minimumEnemies, maximumEnemies + 1);
|
|
|
|
for (int i = 0; i < count; i++) {
|
|
Vector3 newPosition = transform.position;
|
|
|
|
float randomX = Random.Range(-(radius / 2f), radius / 2f);
|
|
float randomZ = Random.Range(-(radius / 2f), radius / 2f);
|
|
|
|
newPosition += new Vector3(randomX, transform.position.y, randomZ);
|
|
|
|
float randomRot = Random.Range(0f, 360f);
|
|
|
|
GameObject newEnemy = Instantiate(enemy, newPosition, Quaternion.AngleAxis(randomRot, Vector3.up));
|
|
newEnemy.GetComponent<Enemy>().relatedSpawner = this;
|
|
|
|
enemies.Add(newEnemy);
|
|
}
|
|
}
|
|
|
|
void Update(){
|
|
ProcessStateUpdate();
|
|
DrawSpawnStateGizmos();
|
|
}
|
|
|
|
// Set the new state of the spawner and also reset the timer for state management
|
|
void ChangeState(SpawnerState newState){
|
|
spawnerState = newState;
|
|
ProcessStateStart();
|
|
timeInState = 0f;
|
|
}
|
|
|
|
|
|
// Does initial actions on state switch
|
|
void ProcessStateStart(){
|
|
switch (spawnerState) {
|
|
case SpawnerState.TargetDropped:
|
|
foreach (GameObject thisEnemy in enemies) {
|
|
thisEnemy.GetComponent<EnemyCombat>().DropTarget();
|
|
}
|
|
break;
|
|
|
|
case SpawnerState.InCombatUnlocked:
|
|
foreach (GameObject thisEnemy in enemies) {
|
|
thisEnemy.GetComponent<Enemy>().Graph.SendEvent("Enter Combat");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Actively processes the state of the spawner and its assocaited enemies
|
|
void ProcessStateUpdate(){
|
|
switch (spawnerState) {
|
|
// Check if player is in range of this spawner. If so, set them as the new target
|
|
case SpawnerState.Idle:
|
|
if (PlayerIsInRange(radius, radius / 2f)) {
|
|
foreach (GameObject thisEnemy in enemies) {
|
|
thisEnemy.GetComponent<EnemyCombat>().SetNewTarget(PlayerManager.Player);
|
|
}
|
|
ChangeState(SpawnerState.TargetAcquired);
|
|
}
|
|
break;
|
|
|
|
// After grabbing a player in range, make sure they stay in range. If they aren't in range after 5 seconds, disengage. If they are or get too close, engage.
|
|
case SpawnerState.TargetAcquired:
|
|
if (PlayerIsInRange(8f, 8f) && timeInState > 1f) {
|
|
ChangeState(SpawnerState.InCombatUnlocked);
|
|
return;
|
|
}
|
|
|
|
if (timeInState > 5f) {
|
|
if (PlayerIsInRange(radius + 2f, radius * .6f)) {
|
|
ChangeState(SpawnerState.InCombatUnlocked);
|
|
return;
|
|
}
|
|
|
|
ChangeState(SpawnerState.TargetDropped);
|
|
return;
|
|
}
|
|
break;
|
|
|
|
// Wait a few seconds after dropping the target before doing anything else
|
|
case SpawnerState.TargetDropped:
|
|
if (timeInState > 3f) {
|
|
ChangeState(SpawnerState.Idle);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
// Set all units to combat. Drop combat if the target gets too far
|
|
case SpawnerState.InCombatUnlocked:
|
|
if (!PlayerIsInRange(radius * 1.5f, radius * 1.5f) && timeInState > 5f) {
|
|
ChangeState(SpawnerState.TargetDropped);
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
// Lock the player to the spawn. Currently unused.
|
|
case SpawnerState.InCombatLocked:
|
|
break;
|
|
|
|
|
|
// Long cooldown for after all units have been killed. Currently unused.
|
|
case SpawnerState.Cooldown:
|
|
break;
|
|
}
|
|
|
|
// Increment state timer
|
|
timeInState += 1f * Time.deltaTime;
|
|
}
|
|
|
|
|
|
|
|
GameObject PlayerIsInRange(float spawnerRange, float enemyRange){
|
|
// TODO: Make compatible with all players
|
|
if (!PlayerManager.Player) {
|
|
return null;
|
|
}
|
|
|
|
Vector3 playerPos = PlayerManager.Player.transform.position;
|
|
|
|
// Skip checking and return null/false if the player is nowhere near the input range
|
|
if (Vector3.Distance(playerPos, transform.position) > spawnerRange * 2f) {
|
|
return null;
|
|
}
|
|
|
|
// If they are in range, check if the player is close enough to either an enemy or the input range
|
|
if (Vector3.Distance(playerPos, transform.position) < spawnerRange) {
|
|
return PlayerManager.Player;
|
|
}
|
|
|
|
foreach (GameObject thisEnemy in enemies) {
|
|
if (Vector3.Distance(playerPos, thisEnemy.transform.position) < enemyRange) {
|
|
return PlayerManager.Player;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private void DrawSpawnStateGizmos(){
|
|
Color cylinderColor = Color.blue;
|
|
|
|
switch (spawnerState) {
|
|
case SpawnerState.Idle:
|
|
break;
|
|
case SpawnerState.TargetAcquired:
|
|
cylinderColor = Color.coral;
|
|
break;
|
|
case SpawnerState.TargetDropped:
|
|
cylinderColor = Color.khaki;
|
|
break;
|
|
case SpawnerState.InCombatUnlocked:
|
|
cylinderColor = Color.red;
|
|
break;
|
|
case SpawnerState.InCombatLocked:
|
|
cylinderColor = Color.violetRed;
|
|
break;
|
|
case SpawnerState.Cooldown:
|
|
cylinderColor = Color.gray;
|
|
break;
|
|
}
|
|
|
|
Draw.WireCylinder(transform.position, transform.position + Vector3.up * 7f, radius, cylinderColor);
|
|
}
|
|
|
|
}
|
|
|
|
} |