using System.Collections;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// GAME 1303 Tier 4 — A piston that slams down, holds, then rises slowly. Cutthroat Course.
///
/// SETUP
/// 1. Parent "Crusher" (empty) at the TOP of the shaft — the resting position. This script here.
/// 2. Child "Head": the block sprite + BoxCollider2D (Is Trigger ON — the player
/// shouldn't be able to ride it) + DamageOnContact (Instant Kill ✓). Drag into Head.
/// 3. Slam Distance: how far down it travels, OR tick Auto Distance to raycast down on
/// Ground Layer and stop just above the floor.
/// 4. Timing: Wait At Top → (Warning: rumble) → Slam (fast) → Hold At Bottom → Rise (slow).
/// Start Delay staggers a row of crushers.
/// 5. Damage only while slamming and holding — a rising crusher is safe to walk under.
///
/// Also the "vertical spikes/pillars" in Nicholas's sketch can be Crushers with a spike sprite.
/// </summary>
public class Crusher : MonoBehaviour
{
public Transform head;
public float slamDistance = 4f;
public bool autoDistance = true;
public LayerMask groundLayer;
[Header("Timing")]
public float startDelay = 0f;
public float waitAtTop = 1.5f;
public float warningTime = 0.4f;
public float slamSpeed = 25f;
public float holdAtBottom = 0.6f;
public float riseSpeed = 3f;
[Header("Feel")]
public float rumbleAmount = 0.04f;
public string slamSfx = "crusher_slam";
public string warningSfx = "";
public UnityEvent OnSlammed; // camera shake, dust particles
private Vector3 topLocal;
private Vector3 bottomLocal;
private DamageOnContact damage;
void Start()
{
if (head == null) { Debug.LogWarning("Crusher: assign the Head child."); enabled = false; return; }
damage = head.GetComponent<DamageOnContact>();
topLocal = head.localPosition;
float dist = slamDistance;
if (autoDistance)
{
var col = head.GetComponent<Collider2D>();
float halfH = col != null ? col.bounds.extents.y : 0.5f;
RaycastHit2D hit = Physics2D.Raycast(head.position, Vector2.down, 50f, groundLayer);
if (hit.collider != null) dist = Mathf.Max(0.1f, hit.distance - halfH);
}
bottomLocal = topLocal + Vector3.down * dist;
if (damage) damage.enabled = false;
StartCoroutine(Loop());
}
IEnumerator Loop()
{
yield return new WaitForSeconds(startDelay);
while (true)
{
yield return new WaitForSeconds(Mathf.Max(0f, waitAtTop - warningTime));
// Warning rumble
if (!string.IsNullOrEmpty(warningSfx)) AudioManager.Instance?.PlaySFX(warningSfx);
float t = 0f;
while (t < warningTime)
{
t += Time.deltaTime;
head.localPosition = topLocal + (Vector3)Random.insideUnitCircle * rumbleAmount;
yield return null;
}
head.localPosition = topLocal;
// Slam
if (damage) damage.enabled = true;
yield return MoveTo(bottomLocal, slamSpeed);
if (!string.IsNullOrEmpty(slamSfx)) AudioManager.Instance?.PlaySFX(slamSfx);
OnSlammed?.Invoke();
yield return new WaitForSeconds(holdAtBottom);
// Rise
if (damage) damage.enabled = false;
yield return MoveTo(topLocal, riseSpeed);
}
}
IEnumerator MoveTo(Vector3 target, float speed)
{
while (Vector3.Distance(head.localPosition, target) > 0.01f)
{
head.localPosition = Vector3.MoveTowards(head.localPosition, target, speed * Time.deltaTime);
yield return null;
}
head.localPosition = target;
}
void OnDrawGizmosSelected()
{
if (head == null) return;
Gizmos.color = Color.red;
Vector3 top = Application.isPlaying ? transform.TransformPoint(topLocal) : head.position;
Gizmos.DrawLine(top, top + Vector3.down * slamDistance);
}
}