using System; using System.Collections.Generic; using UnityEngine; [DisallowMultipleComponent] public sealed class MapTargetController : MonoBehaviour { [Header("Target Types")] [SerializeField] private TargetConfig[] randomTargetConfigs = Array.Empty(); [SerializeField] private bool showLiveTargetVisuals; [Header("Current Wave (Runtime)")] [SerializeField, Min(0)] private int currentWave; [SerializeField] private List currentWaveTargets = new(); private readonly List pooledTargets = new(); private readonly Dictionary targetsById = new(); private readonly HashSet synchronizedTargetIds = new(); private MapCoordinateSystem coordinateSystem; private Transform targetParent; private MapPlayer player; public IReadOnlyList Targets => currentWaveTargets; public IReadOnlyList TargetConfigs => randomTargetConfigs; public int CurrentWave => currentWave; private void OnValidate() { currentWave = Mathf.Max(0, currentWave); } public void Configure( MapCoordinateSystem newCoordinateSystem, Transform newTargetParent, MapPlayer newPlayer) { coordinateSystem = newCoordinateSystem; targetParent = newTargetParent != null ? newTargetParent : transform; player = newPlayer; pooledTargets.Clear(); targetsById.Clear(); currentWaveTargets.Clear(); foreach (MapTarget existingTarget in targetParent.GetComponentsInChildren(true)) { existingTarget.SetVisualsVisible(false); existingTarget.gameObject.SetActive(false); pooledTargets.Add(existingTarget); } } public void ApplyNetworkTargets( IReadOnlyList states, int wave) { currentWave = Mathf.Max(0, wave); synchronizedTargetIds.Clear(); for (int index = 0; index < states.Count; index++) { MapTargetNetworkState state = states[index]; TargetConfig config = GetConfig(state.ConfigIndex); if (config == null || config.TargetPrefab == null) continue; synchronizedTargetIds.Add(state.TargetId); if (!targetsById.TryGetValue(state.TargetId, out MapTarget target) || target == null) { target = GetPooledTarget(config); if (target == null) target = Instantiate(config.TargetPrefab, targetParent); targetsById[state.TargetId] = target; currentWaveTargets.Add(target); } target.name = $"Wave {currentWave} - {config.name} - {state.TargetId}"; target.Configure(config, coordinateSystem, player); target.ApplyNetworkState(state); target.SetVisualsVisible(showLiveTargetVisuals); target.gameObject.SetActive(true); } for (int index = currentWaveTargets.Count - 1; index >= 0; index--) { MapTarget target = currentWaveTargets[index]; if (target != null && synchronizedTargetIds.Contains(target.TargetId)) continue; RetireTarget(target); currentWaveTargets.RemoveAt(index); } } public TargetConfig GetConfig(int index) => index >= 0 && index < randomTargetConfigs.Length ? randomTargetConfigs[index] : null; public int GetRandomValidConfigIndex() { if (randomTargetConfigs == null || randomTargetConfigs.Length == 0) return -1; int startIndex = UnityEngine.Random.Range(0, randomTargetConfigs.Length); for (int offset = 0; offset < randomTargetConfigs.Length; offset++) { int index = (startIndex + offset) % randomTargetConfigs.Length; TargetConfig config = randomTargetConfigs[index]; if (config != null && config.TargetPrefab != null) return index; } return -1; } public bool HasValidConfiguration() => coordinateSystem != null && coordinateSystem.HasValidCorners() && GetRandomValidConfigIndex() >= 0; public void SetTargetVisualsVisible(bool isVisible) { showLiveTargetVisuals = isVisible; foreach (MapTarget target in currentWaveTargets) { if (target != null) target.SetVisualsVisible(isVisible); } } public MapTarget GetTarget(int index) => index >= 0 && index < currentWaveTargets.Count ? currentWaveTargets[index] : null; public bool StartNextWave() { MapController mapController = FindFirstObjectByType(); return mapController != null && mapController.StartNextWave(); } private MapTarget GetPooledTarget(TargetConfig config) { for (int index = pooledTargets.Count - 1; index >= 0; index--) { MapTarget candidate = pooledTargets[index]; if (candidate == null) { pooledTargets.RemoveAt(index); continue; } if (candidate.GetType() != config.TargetPrefab.GetType()) continue; pooledTargets.RemoveAt(index); return candidate; } return null; } private void RetireTarget(MapTarget target) { if (target == null) return; targetsById.Remove(target.TargetId); target.SetVisualsVisible(false); target.gameObject.SetActive(false); if (!pooledTargets.Contains(target)) pooledTargets.Add(target); } }