MiniGames/Assets/Scripts/CrateEscape/LaserBeamController.cs
2025-09-09 16:46:17 +05:00

138 lines
4.8 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using static LaserBeam;
public class LaserBeamController : MonoBehaviour
{
[Header("Cycle Durations")]
public float interval = 20f; // how long each batch runs before switching
public float idleBeforeCharge = 3f; // Idle time before Charging
public float chargingWindup = 1f; // Charging duration (blink)
public float fireDuration = 1f; // Firing duration (lethal)
[Header("Beams (auto-populated)")]
public List<LaserBeam> allLasers = new List<LaserBeam>();
private readonly List<List<LaserBeam>> batches = new();
private readonly List<LaserBeam> activeLasers = new();
void Start()
{
// Use active-only; if you need inactive too, use FindObjectsOfType<LaserBeam>(true)
allLasers.AddRange(FindObjectsOfType<LaserBeam>());
foreach (var lb in allLasers)
{
if (!lb) continue;
lb.externalControl = true; // controller owns timing
lb.ResetCycle(); // Idle + line off
lb.enabled = false; // completely inert until selected
}
// Shuffle
for (int i = 0; i < allLasers.Count; i++)
{
int r = Random.Range(i, allLasers.Count);
(allLasers[i], allLasers[r]) = (allLasers[r], allLasers[i]);
}
// Split into 3 batches
int batchSize = Mathf.CeilToInt(Mathf.Max(1, allLasers.Count) / 3f);
for (int i = 0; i < allLasers.Count; i += batchSize)
batches.Add(allLasers.GetRange(i, Mathf.Min(batchSize, allLasers.Count - i)));
StartCoroutine(RunBatches());
}
IEnumerator RunBatches()
{
for (int bi = 0; bi < batches.Count; bi++)
{
// Disable everything first
foreach (var lb in allLasers)
if (lb) { lb.enabled = false; lb.ResetCycle(); }
// Activate this batch
activeLasers.Clear();
activeLasers.AddRange(batches[bi]);
foreach (var l in activeLasers)
{
if (!l) continue;
l.enabled = true;
l.ResetCycle();
l.SetLaserPhase(LaserPhase.Idle);
}
float batchEnd = Time.time + interval;
// Loop phases for the duration of this batch
while (Time.time < batchEnd)
{
// Compute runway required to complete a Charge->Fire sequence
float runway = Mathf.Max(0f, chargingWindup) + Mathf.Max(0f, fireDuration);
float timeLeft = batchEnd - Time.time;
// If not enough time to do Charging+Firing, just stay Idle until the batch ends.
if (timeLeft < runway)
{
yield return PhaseBlock(LaserPhase.Idle, timeLeft, batchEnd);
break;
}
// IDLE
float idleTime = Mathf.Max(0f, idleBeforeCharge);
// If idleTime itself would consume too much and leave < runway, trim idle to fit
if (idleTime > 0f && idleTime > timeLeft - runway)
idleTime = Mathf.Max(0f, timeLeft - runway);
if (idleTime > 0f)
{
yield return PhaseBlock(LaserPhase.Idle, idleTime, batchEnd);
if (Time.time >= batchEnd) break;
}
// CHARGING (blink)
if (chargingWindup > 0f)
{
yield return PhaseBlock(LaserPhase.Charging, chargingWindup, batchEnd);
// we guaranteed runway, so we should still have time left for Firing
}
// FIRING (lethal)
if (fireDuration > 0f)
{
yield return PhaseBlock(LaserPhase.Firing, fireDuration, batchEnd);
}
}
}
}
// Set phase for all active lasers and tick per-frame for 'duration' (clamped by batch end)
IEnumerator PhaseBlock(LaserPhase phase, float duration, float batchHardEnd)
{
SetPhaseForAll(phase);
float end = Mathf.Min(Time.time + duration, batchHardEnd);
while (Time.time < end)
{
if (phase == LaserPhase.Charging)
{
foreach (var l in activeLasers) if (l && l.isActiveAndEnabled) l.TickLaserDuringCharging();
}
else if (phase == LaserPhase.Firing)
{
foreach (var l in activeLasers) if (l && l.isActiveAndEnabled) l.TickLaserDuringFiring();
}
yield return null;
}
}
void SetPhaseForAll(LaserPhase p)
{
foreach (var l in activeLasers)
if (l && l.isActiveAndEnabled) l.SetLaserPhase(p);
}
}