using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// GAME 1303 Tier 5 — Crossy Road traffic. Spawns things at one end of a lane that travel
/// across and despawn at the other. Last Delivery's wasteland hazards and roaming monsters.
///
/// SETUP
/// 1. Empty GameObject placed at the START of the lane (where things appear).
/// 2. Direction & Lane Length: the arrow gizmo shows where they travel. Objects are
/// destroyed after Lane Length units.
/// 3. Prefabs: drag in one or more (a truck, a mutant, a rolling barrel). Each spawn
/// picks one at random — give a prefab more entries to make it more common.
/// 4. Timing: Interval with Interval Variance for irregular traffic. Start Delay
/// staggers parallel lanes so they don't all fire together.
/// 5. Speed with Speed Variance. Tick Face Direction to flip/rotate the sprite.
/// 6. Max Alive caps how many exist at once.
/// 7. The spawned object needs its own DamageOnContact (or a monster script) — this
/// script only moves things; it doesn't decide what they do on contact.
///
/// For Chris's "monsters try to intercept you", mix two lanes: fast straight-line traffic
/// (this script) plus a couple of Tier 2 EnemyChase monsters that actually hunt. The
/// contrast between predictable and unpredictable threats is what makes the route a puzzle.
/// </summary>
public class LaneSpawner : MonoBehaviour
{
[Header("Lane")]
public Vector2 direction = Vector2.right;
public float laneLength = 24f;
[Header("What")]
public List<GameObject> prefabs = new List<GameObject>();
public int maxAlive = 6;
[Header("When")]
public float startDelay = 0f;
public float interval = 2f;
public float intervalVariance = 0.5f;
public bool spawnOnStart = true;
[Header("How fast")]
public float speed = 4f;
public float speedVariance = 1f;
public bool faceDirection = true;
[Tooltip("Rotate the sprite (vehicles) instead of flipping it (creatures).")]
public bool rotateInsteadOfFlip = false;
public float rotationOffset = 0f;
[Header("Events")]
public UnityEvent<GameObject> OnSpawned;
private readonly List<GameObject> alive = new List<GameObject>();
void Start()
{
if (spawnOnStart) StartCoroutine(SpawnLoop());
}
IEnumerator SpawnLoop()
{
yield return new WaitForSeconds(startDelay);
while (true)
{
alive.RemoveAll(o => o == null);
if (alive.Count < maxAlive) Spawn();
yield return new WaitForSeconds(Mathf.Max(0.1f, interval + Random.Range(-intervalVariance, intervalVariance)));
}
}
public void Spawn()
{
if (prefabs.Count == 0) return;
var prefab = prefabs[Random.Range(0, prefabs.Count)];
if (prefab == null) return;
var obj = Instantiate(prefab, transform.position, Quaternion.identity);
Vector2 dir = direction.normalized;
float spd = Mathf.Max(0.1f, speed + Random.Range(-speedVariance, speedVariance));
if (faceDirection)
{
if (rotateInsteadOfFlip)
obj.transform.rotation = Quaternion.Euler(0f, 0f, Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg + rotationOffset);
else if (dir.x < 0f)
{
Vector3 s = obj.transform.localScale; s.x *= -1f; obj.transform.localScale = s;
}
}
// Give it a mover. A Rigidbody2D is used if present (so it can collide properly).
var rb = obj.GetComponent<Rigidbody2D>();
if (rb != null)
{
rb.gravityScale = 0f;
rb.linearVelocity = dir * spd;
}
else
{
var mover = obj.AddComponent<LaneTraveller>();
mover.velocity = dir * spd;
}
// Clean up at the far end
var life = obj.AddComponent<LaneDespawner>();
life.origin = transform.position;
life.maxDistance = laneLength;
alive.Add(obj);
OnSpawned?.Invoke(obj);
}
void OnDrawGizmos()
{
Gizmos.color = new Color(1f, 0.4f, 0.2f, 0.9f);
Vector3 d = ((Vector3)direction.normalized) * laneLength;
Gizmos.DrawLine(transform.position, transform.position + d);
Gizmos.DrawWireSphere(transform.position, 0.25f);
Gizmos.DrawWireCube(transform.position + d, Vector3.one * 0.3f);
}
}