111 lines
3.6 KiB
C#
111 lines
3.6 KiB
C#
using System.Collections.Generic;
|
|
using Northbound.Gameplay.Enemy;
|
|
using Northbound.Gameplay.World;
|
|
using UnityEngine;
|
|
|
|
namespace Northbound.Gameplay.Waves
|
|
{
|
|
public sealed class WaveDirector : MonoBehaviour
|
|
{
|
|
[SerializeField] private float firstWaveDelay = 15f;
|
|
[SerializeField] private float waveInterval = 90f;
|
|
[SerializeField] private Material gruntMaterial;
|
|
[SerializeField] private Material raiderMaterial;
|
|
[SerializeField] private Material eliteMaterial;
|
|
[SerializeField] private EnemyPursuitDefinition pursuitDefinition;
|
|
|
|
private float nextWaveAt;
|
|
private int wave;
|
|
|
|
public int CurrentWave => wave;
|
|
public float NextWaveIn => Mathf.Max(0f, nextWaveAt - Time.time);
|
|
|
|
private void Start()
|
|
{
|
|
nextWaveAt = Time.time + firstWaveDelay;
|
|
PublishStatus(0);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
var state = SharedGameState.Instance;
|
|
if (state == null || state.IsDefeated || state.IsVictorious)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (Time.time >= nextWaveAt)
|
|
{
|
|
BeginWave();
|
|
}
|
|
|
|
PublishStatus(state.ResonanceSnapshot);
|
|
}
|
|
|
|
public void Configure(Material grunt, Material raider, Material elite, EnemyPursuitDefinition pursuit)
|
|
{
|
|
gruntMaterial = grunt;
|
|
raiderMaterial = raider;
|
|
eliteMaterial = elite;
|
|
pursuitDefinition = pursuit;
|
|
}
|
|
|
|
private void BeginWave()
|
|
{
|
|
wave++;
|
|
var state = SharedGameState.Instance;
|
|
var resonance = Mathf.Min(3, state.CountEnemyPortals());
|
|
var enemyPortals = new List<PortalSite>();
|
|
foreach (var portal in state.Portals)
|
|
{
|
|
if (portal.State == PortalState.Enemy)
|
|
{
|
|
enemyPortals.Add(portal);
|
|
}
|
|
}
|
|
|
|
var spawnCount = 3 + wave * 2;
|
|
for (var index = 0; index < spawnCount && enemyPortals.Count > 0; index++)
|
|
{
|
|
var portal = enemyPortals[index % enemyPortals.Count];
|
|
var role = ResolveRole(index);
|
|
var offset = new Vector3((index % 3 - 1) * 1.4f, 1f, 2f + index / 3f);
|
|
SpawnEnemy(portal.transform.position + offset, role, resonance);
|
|
}
|
|
|
|
nextWaveAt = Time.time + waveInterval;
|
|
PublishStatus(resonance);
|
|
}
|
|
|
|
private EnemyRole ResolveRole(int index)
|
|
{
|
|
if (wave >= 3 && index == 0)
|
|
{
|
|
return EnemyRole.Elite;
|
|
}
|
|
|
|
return index % 4 == 3 ? EnemyRole.Raider : EnemyRole.Grunt;
|
|
}
|
|
|
|
private void SpawnEnemy(Vector3 position, EnemyRole role, int resonance)
|
|
{
|
|
var enemy = GameObject.CreatePrimitive(role == EnemyRole.Elite ? PrimitiveType.Cube : PrimitiveType.Capsule);
|
|
enemy.name = $"Wave {wave} {role}";
|
|
enemy.transform.position = position;
|
|
enemy.transform.localScale = role == EnemyRole.Elite ? Vector3.one * 1.6f : Vector3.one;
|
|
var material = role switch
|
|
{
|
|
EnemyRole.Raider => raiderMaterial,
|
|
EnemyRole.Elite => eliteMaterial,
|
|
_ => gruntMaterial
|
|
};
|
|
enemy.AddComponent<EnemyAgent>().Configure(role, resonance, material, pursuitDefinition);
|
|
}
|
|
|
|
private void PublishStatus(int resonance)
|
|
{
|
|
SharedGameState.Instance?.SetWaveStatus(wave, resonance, NextWaveIn);
|
|
}
|
|
}
|
|
}
|