Files
Click-PointRPG/Assets/Scripts/Gate.cs

199 lines
5.0 KiB
C#

using UnityEngine;
using System.Collections;
using UnityEngine.AI;
public class Gate : MonoBehaviour, IInteractiveObject
{
[Header("Gate Settings")]
public bool startsOpen = false;
public bool isLocked = false;
public string requiredKeyId = ""; // empty = no key required
public string gateName = "Gate";
[Header("Animation")]
public Transform hinge; // pivot for rotation
public float openAngle = 90f;
public float closedAngle = 0f;
public float swingSpeed = 180f; // degrees per second
public bool rotateAroundLocalY = true;
[Header("Interaction")]
public Transform interactionPoint; // Where player should stand to interact (defaults to this transform)
[Header("NavMesh")]
public NavMeshObstacle navMeshObstacle;
bool targetOpen;
Coroutine swingRoutine;
void Awake()
{
if (navMeshObstacle == null)
navMeshObstacle = GetComponent<NavMeshObstacle>();
}
void Start()
{
if (hinge == null)
{
Debug.LogError("Gate: Hinge not assigned!", gameObject);
return;
}
targetOpen = startsOpen;
SetImmediateRotation(targetOpen ? openAngle : closedAngle);
// Initialize NavMesh obstacle state based on gate state
if (navMeshObstacle != null)
navMeshObstacle.enabled = !startsOpen; // Disable obstacle if gate starts open
}
// Public API
public void Interact()
{
if (isLocked)
{
OnLockedInteract();
return;
}
Toggle();
}
public bool TryUnlock(string keyId)
{
if (!isLocked) return true;
if (string.IsNullOrEmpty(requiredKeyId) || requiredKeyId != keyId) return false;
isLocked = false;
return true;
}
public void Lock(string keyId)
{
isLocked = true;
requiredKeyId = keyId;
}
public void Unlock()
{
isLocked = false;
}
public void Open()
{
if (isLocked) return;
SetOpen(true);
UpdateNavMesh(false); // Disable obstacle when opening
}
public void Close()
{
SetOpen(false);
UpdateNavMesh(true); // Enable obstacle when closing
}
public void Toggle()
{
SetOpen(!targetOpen);
}
public bool IsOpen() => targetOpen;
void SetOpen(bool open)
{
if (targetOpen == open) return; // Already in target state
targetOpen = open;
if (swingRoutine != null) StopCoroutine(swingRoutine);
swingRoutine = StartCoroutine(SwingToTarget(open ? openAngle : closedAngle));
}
/// <summary>
/// Update NavMesh obstacle when gate opens/closes
/// </summary>
void UpdateNavMesh(bool enableObstacle)
{
// Update the obstacle to carve/uncarve the NavMesh
if (navMeshObstacle != null)
{
navMeshObstacle.enabled = enableObstacle;
Debug.Log($"Gate {gateName}: NavMesh obstacle {(enableObstacle ? "enabled" : "disabled")}", gameObject);
}
}
IEnumerator SwingToTarget(float targetAngle)
{
if (hinge == null) yield break;
float current = GetHingeAngle();
float difference = Mathf.DeltaAngle(current, targetAngle);
if (Mathf.Abs(difference) < 0.5f) yield break; // Already at target
float sign = Mathf.Sign(difference);
while (Mathf.Abs(Mathf.DeltaAngle(current, targetAngle)) > 0.5f)
{
float step = swingSpeed * Time.deltaTime * sign;
current += step;
SetHingeAngle(current);
yield return null;
}
SetHingeAngle(targetAngle);
swingRoutine = null;
}
void SetImmediateRotation(float angle)
{
if (hinge == null) return;
SetHingeAngle(angle);
}
float GetHingeAngle()
{
if (hinge == null) return 0f;
return rotateAroundLocalY ? hinge.localEulerAngles.y : hinge.localEulerAngles.z;
}
void SetHingeAngle(float angle)
{
if (hinge == null) return;
Vector3 e = hinge.localEulerAngles;
if (rotateAroundLocalY) e.y = angle;
else e.z = angle;
hinge.localEulerAngles = e;
}
void OnLockedInteract()
{
Debug.Log($"Gate is locked. Requires key: {(string.IsNullOrEmpty(requiredKeyId) ? "None" : requiredKeyId)}", gameObject);
}
void OnDestroy()
{
if (swingRoutine != null)
{
StopCoroutine(swingRoutine);
swingRoutine = null;
}
}
// IInteractiveObject implementation
public Vector3 GetInteractionPoint()
{
if (interactionPoint != null)
return interactionPoint.position;
return transform.position;
}
public bool CanInteract()
{
return !isLocked;
}
public void Interact(GameObject player)
{
Open();
Debug.Log($"Gate {gateName} opened by {player.name}");
}
public string GetDisplayName() => gateName;
}