using System.Collections;
using UnityEngine;
using UnityEngine.Events;
/// <summary>
/// GAME 1303 Tier 4 — A door that can be locked. Opens when its requirement is met, or
/// when something calls Open(). Commission Witch, Zap Attack (generator door),
/// Spark of Corruption, Roach Invasion, Detective Liz, Anabasis.
///
/// SETUP
/// 1. Door sprite + Collider2D (SOLID, not a trigger — it blocks the player) + this script.
/// 2. Requirement:
/// None – opens whenever Open() is called (a switch, a boss defeated).
/// Collect – needs Required Count of Required Item in the CollectionTracker.
/// Consume On Open ✓ spends them (a key), ✗ just checks (a quota).
/// Flag – opens when ChoiceFlagManager has the named flag (Tier 6).
/// Until Tier 6 exists this behaves like None.
/// 3. Auto Check: with a trigger child ("ProximityZone") the door checks the requirement
/// when the player walks up. Otherwise call TryOpen() from an Interactable (Tier 6)
/// or a UnityEvent.
/// 4. Open Mode: Slide (moves by Open Offset), Fade, or Disable (just vanishes).
/// 5. Locked Feedback: a rattle sound and OnOpenFailed event so the player learns
/// "I need something." Show a HUD hint here.
///
/// Zap Attack's generator door: Requirement = Collect, Item = Green Electricity,
/// Required Count = 5, Consume ✗.
/// </summary>
public class Door : MonoBehaviour
{
public enum Requirement { None, Collect, Flag }
public enum OpenMode { Slide, Fade, Disable }
[Header("Lock")]
public Requirement requirement = Requirement.None;
public ItemType requiredItem;
public int requiredCount = 1;
public bool consumeOnOpen = true;
[Tooltip("Flag mode: the flag name that must be set (Tier 6 ChoiceFlagManager).")]
public string requiredFlag = "";
public bool startOpen = false;
[Header("Opening")]
public OpenMode openMode = OpenMode.Slide;
public Vector2 openOffset = new Vector2(0f, 3f);
public float openSpeed = 4f;
public bool canCloseAgain = false;
[Header("Auto check")]
[Tooltip("Optional trigger child. When the player is inside, the door tries to open by itself.")]
public Collider2D proximityZone;
[Header("Feedback")]
public string openSfx = "door_open";
public string lockedSfx = "door_locked";
public UnityEvent OnOpened;
public UnityEvent OnClosed;
public UnityEvent OnOpenFailed; // "You need 5 batteries" hint
public UnityEvent<int, int> OnProgress; // (have, needed) — for a HUD readout
public bool IsOpen { get; private set; }
private Vector3 closedPos;
private Vector3 openPos;
private Collider2D blocker;
private SpriteRenderer sr;
private Coroutine motion;
private readonly Collider2D[] overlap = new Collider2D[8];
private ContactFilter2D noFilter;
void Awake()
{
closedPos = transform.position;
openPos = closedPos + (Vector3)openOffset;
blocker = GetComponent<Collider2D>();
sr = GetComponentInChildren<SpriteRenderer>();
noFilter = new ContactFilter2D();
noFilter.NoFilter();
if (proximityZone != null) proximityZone.isTrigger = true;
}
void Start()
{
if (startOpen) SetOpenInstant(true);
}
void Update()
{
if (IsOpen || proximityZone == null) return;
int n = proximityZone.Overlap(noFilter, overlap);
for (int i = 0; i < n; i++)
if (overlap[i] != null && overlap[i].CompareTag("Player")) { TryOpen(); break; }
}
/// <summary>Check the requirement and open if it's satisfied. Safe to call every frame.</summary>
public bool TryOpen()
{
if (IsOpen) return true;
if (requirement == Requirement.Collect && requiredItem != null)
{
int have = CollectionTracker.Instance != null ? CollectionTracker.Instance.GetCount(requiredItem) : 0;
OnProgress?.Invoke(have, requiredCount);
if (have < requiredCount) { Refuse(); return false; }
if (consumeOnOpen) CollectionTracker.Instance.Spend(requiredItem, requiredCount);
}
else if (requirement == Requirement.Flag && !string.IsNullOrEmpty(requiredFlag))
{
// Tier 6 hook. Until ChoiceFlagManager exists, Flag doors open freely.
var flags = FindFlagManager();
if (flags != null && !flags.HasFlag(requiredFlag)) { Refuse(); return false; }
}
Open();
return true;
}
/// <summary>Open regardless of the requirement (switch, boss defeated, cutscene).</summary>
public void Open()
{
if (IsOpen) return;
IsOpen = true;
if (!string.IsNullOrEmpty(openSfx)) AudioManager.Instance?.PlaySFX(openSfx);
if (motion != null) StopCoroutine(motion);
motion = StartCoroutine(Animate(true));
OnOpened?.Invoke();
}
public void Close()
{
if (!IsOpen || !canCloseAgain) return;
IsOpen = false;
if (motion != null) StopCoroutine(motion);
motion = StartCoroutine(Animate(false));
OnClosed?.Invoke();
}
void Refuse()
{
if (!string.IsNullOrEmpty(lockedSfx)) AudioManager.Instance?.PlaySFX(lockedSfx);
OnOpenFailed?.Invoke();
}
IEnumerator Animate(bool opening)
{
if (openMode == OpenMode.Disable)
{
gameObject.SetActive(!opening);
yield break;
}
if (opening && blocker != null) blocker.enabled = false;
if (openMode == OpenMode.Slide)
{
Vector3 target = opening ? openPos : closedPos;
while (Vector3.Distance(transform.position, target) > 0.01f)
{
transform.position = Vector3.MoveTowards(transform.position, target, openSpeed * Time.deltaTime);
yield return null;
}
transform.position = target;
}
else if (sr != null) // Fade
{
float from = sr.color.a, to = opening ? 0f : 1f, t = 0f;
while (t < 1f)
{
t += Time.deltaTime * openSpeed;
Color c = sr.color; c.a = Mathf.Lerp(from, to, t); sr.color = c;
yield return null;
}
}
if (!opening && blocker != null) blocker.enabled = true;
}
void SetOpenInstant(bool open)
{
IsOpen = open;
if (blocker != null) blocker.enabled = !open;
if (openMode == OpenMode.Slide) transform.position = open ? openPos : closedPos;
else if (openMode == OpenMode.Disable) gameObject.SetActive(!open);
else if (sr != null) { Color c = sr.color; c.a = open ? 0f : 1f; sr.color = c; }
}
// Tier 6 forward-compatibility without a hard dependency.
ChoiceFlagManager FindFlagManager() => ChoiceFlagManager.Instance;
void OnDrawGizmosSelected()
{
Gizmos.color = Color.green;
Vector3 from = Application.isPlaying ? closedPos : transform.position;
Gizmos.DrawLine(from, from + (Vector3)openOffset);
Gizmos.DrawWireCube(from + (Vector3)openOffset, Vector3.one * 0.3f);
}
}