using UnityEngine; /// /// Handles proximity-based interactions for the player. /// Automatically triggers interactions when the player gets within range. /// Scalable for buildings, gates, bridges, ledges, and other interactive objects. /// public class PlayerInteractionController : MonoBehaviour { [Header("Interaction Settings")] [SerializeField] private float interactionRange = 1.5f; // "Arm's reach" distance [SerializeField] private float stoppingDistance = 0.5f; // How close to the entry point before triggering the interaction [Header("Debug")] [SerializeField] private bool enableDebugLogs = false; private NavMeshMovementController movementController; private Building pendingBuildingInteraction; private IInteractiveObject pendingInteraction; private NPC pendingNPCInteraction; void Awake() { movementController = GetComponent(); if (movementController == null) Debug.LogWarning("PlayerInteractionController requires NavMeshMovementController on the same GameObject.", gameObject); } void Update() { // Check if we should trigger pending building interaction if (pendingBuildingInteraction != null) { CheckBuildingInteractionProximity(); } // Check for generic interactive objects in range if (pendingInteraction != null) { CheckGenericInteractionProximity(); } // Check for NPC interactions in range if (pendingNPCInteraction != null) { CheckNPCInteractionProximity(); } } /// /// Set a building as the pending interaction target. /// The player will move to the building's entry point and enter when close enough. /// public void SetPendingBuildingInteraction(Building building) { if (building == null) return; // Clear any previous interaction ClearPendingInteraction(); pendingBuildingInteraction = building; // Move to the building's entry point and look at the building if (movementController != null && building.entryPoint != null) { movementController.MoveToAndLookAt(building.entryPoint.position, building.transform.position); Log($"PlayerInteractionController: Moving to {building.buildingName}"); } } /// /// Set a generic interactive object as the pending interaction target. /// Scalable for gates, bridges, ledges, etc. /// public void SetPendingInteraction(IInteractiveObject interactiveObject, Vector3 targetPosition) { if (interactiveObject == null) return; ClearPendingInteraction(); pendingInteraction = interactiveObject; if (movementController != null) { movementController.MoveToAndLookAt(targetPosition, targetPosition); Log("PlayerInteractionController: Moving to interactive object"); } } /// /// Set an NPC as the pending interaction target. /// The player will move to the NPC and trigger interaction when close enough. /// public void SetPendingNPCInteraction(NPC npc) { if (npc == null) return; ClearPendingInteraction(); pendingNPCInteraction = npc; if (movementController != null) { movementController.MoveToAndLookAt(npc.transform.position, npc.transform.position); Log($"PlayerInteractionController: Moving to interact with {npc.npcName}"); } } /// /// Clear any pending interactions /// public void ClearPendingInteraction() { pendingBuildingInteraction = null; pendingInteraction = null; pendingNPCInteraction = null; } private void CheckBuildingInteractionProximity() { if (pendingBuildingInteraction == null || pendingBuildingInteraction.entryPoint == null) { pendingBuildingInteraction = null; return; } float distanceToEntry = Vector3.Distance(transform.position, pendingBuildingInteraction.entryPoint.position); // Check if player is close enough to interact if (distanceToEntry <= stoppingDistance) { // Check if building can be entered if (pendingBuildingInteraction.canEnter) { // Check gate status if (pendingBuildingInteraction.gate != null && pendingBuildingInteraction.gate.isLocked) { Log($"Cannot enter {pendingBuildingInteraction.buildingName}: Gate is locked"); pendingBuildingInteraction = null; return; } // Check if we need to open the gate if (pendingBuildingInteraction.gate != null && !pendingBuildingInteraction.gate.IsOpen()) { pendingBuildingInteraction.gate.Open(); Log($"Opening gate for {pendingBuildingInteraction.buildingName}"); // Wait a moment for the gate to open before entering return; } // Enter the building pendingBuildingInteraction.Enter(); Log($"Entering {pendingBuildingInteraction.buildingName}"); pendingBuildingInteraction = null; // Stop movement if (movementController != null) movementController.Stop(); } else { Log($"Cannot enter {pendingBuildingInteraction.buildingName}: Building is not enterable"); pendingBuildingInteraction = null; } } } private void CheckGenericInteractionProximity() { if (pendingInteraction == null) return; Vector3 interactionPoint = pendingInteraction.GetInteractionPoint(); float distanceToInteraction = Vector3.Distance(transform.position, interactionPoint); if (distanceToInteraction <= interactionRange) { if (pendingInteraction.CanInteract()) { pendingInteraction.Interact(gameObject); Log("Triggered interaction"); pendingInteraction = null; if (movementController != null) movementController.Stop(); } else { Log("Cannot interact: conditions not met"); pendingInteraction = null; } } } private void CheckNPCInteractionProximity() { if (pendingNPCInteraction == null) return; float distanceToNPC = Vector3.Distance(transform.position, pendingNPCInteraction.transform.position); // Check if player is close enough to the NPC if (distanceToNPC <= interactionRange) { // Trigger the NPC interaction pendingNPCInteraction.OnInteract(gameObject); Log($"Triggered NPC interaction: {pendingNPCInteraction.npcName}"); // Clear the pending interaction pendingNPCInteraction = null; // Stop player movement if (movementController != null) movementController.Stop(); } } /// /// Get the current interaction range setting /// public float GetInteractionRange() => interactionRange; /// /// Set the interaction range (useful for difficulty settings or equipment modifiers) /// public void SetInteractionRange(float range) { interactionRange = Mathf.Max(0.1f, range); } private void Log(string message) { if (enableDebugLogs) { Debug.Log(message); } } }