using UnityEngine; using UnityEngine.SceneManagement; using System.Collections.Generic; using System.Linq; /// /// Manages settlement interaction on the map - detects when TeamMarker is over a settlement /// and allows entering the town scene with proper context setup /// public class SettlementInteractionManager : MonoBehaviour { [Header("Settlement Detection")] [Tooltip("The TeamMarker GameObject to track")] public Transform teamMarker; [Tooltip("How close the team marker needs to be to settlement center to interact")] public float interactionDistance = 2f; [Tooltip("Key to press to enter settlement")] public KeyCode enterSettlementKey = KeyCode.E; [Tooltip("Automatically enter settlement when TeamMarker stops moving")] public bool autoEnterOnStop = false; [Tooltip("Time to wait before auto-entering (if enabled)")] public float autoEnterDelay = 1f; [Header("UI")] [Tooltip("UI element to show when near settlement (optional)")] public GameObject interactionPrompt; [Header("Scene Loading")] [Tooltip("Name of the town scene to load")] public string townSceneName = "TownSceen"; [Header("Debug")] public bool enableDebugLogs = true; [Header("Exit Protection")] [Tooltip("Cooldown period after exiting a settlement before allowing re-entry")] public float exitCooldown = 3f; // Private fields private MapMaker2 mapMaker; private MapData mapData; private Settlement currentNearbySettlement; private Vector3 lastTeamMarkerPosition; private float timeStationary = 0f; private float lastExitTime = -999f; // Time when last exited a settlement private string lastExitedSettlement = ""; // Name of last exited settlement // Events public System.Action OnSettlementDetected; public System.Action OnSettlementLeft; public System.Action OnSettlementEntered; void Awake() { // Check if we're returning from a town scene var settlementContext = FindFirstObjectByType(); if (settlementContext != null && !string.IsNullOrEmpty(settlementContext.settlementName)) { // We just returned from a settlement - set cooldown lastExitTime = Time.time; lastExitedSettlement = settlementContext.settlementName; LogDebug($"COOLDOWN: Detected return from settlement: {settlementContext.settlementName} - applying {exitCooldown}s cooldown"); } else { LogDebug("COOLDOWN: No settlement context found - normal startup"); } } void Start() { InitializeComponents(); } void Update() { // Retry MapData connection if it failed initially if (mapData == null && mapMaker != null) { mapData = mapMaker.GetMapData(); if (mapData != null) { LogDebug($"MapData connection retry successful - Map size: {mapData.Width}x{mapData.Height}"); } } if (teamMarker == null || mapData == null) return; CheckForSettlementInteraction(); HandleInput(); // Remove auto-enter functionality - only manual E-key entry // HandleAutoEnter(); } /// /// Initialize required components and references /// private void InitializeComponents() { // Find TeamMarker if not assigned if (teamMarker == null) { GameObject teamMarkerObj = GameObject.Find("TeamMarker"); if (teamMarkerObj != null) { teamMarker = teamMarkerObj.transform; LogDebug("Found TeamMarker automatically"); } else { LogDebug("TeamMarker not found - assign manually or create GameObject named 'TeamMarker'"); // Don't return - continue to find MapMaker2 so we can retry later } } // Find MapMaker2 mapMaker = FindFirstObjectByType(); if (mapMaker != null) { mapData = mapMaker.GetMapData(); LogDebug($"Connected to MapMaker2 - Map size: {mapData?.Width}x{mapData?.Height}"); } else { LogDebug("MapMaker2 not found - settlement detection will not work"); } // Initialize UI if (interactionPrompt != null) { interactionPrompt.SetActive(false); } lastTeamMarkerPosition = teamMarker.position; } /// /// Check if TeamMarker is near any settlement /// private void CheckForSettlementInteraction() { Vector2 teamWorldPos = new Vector2(teamMarker.position.x, teamMarker.position.z); // Convert world position to map coordinates Vector2Int mapPos = WorldToMapCoordinates(teamWorldPos); // Find nearby settlement Settlement nearbySettlement = FindNearestSettlement(mapPos); // Handle settlement detection changes if (nearbySettlement != currentNearbySettlement) { if (currentNearbySettlement != null) { OnSettlementLeft?.Invoke(); LogDebug($"Left settlement: {currentNearbySettlement.name}"); } currentNearbySettlement = nearbySettlement; if (currentNearbySettlement != null) { OnSettlementDetected?.Invoke(currentNearbySettlement); LogDebug($"Near settlement: {currentNearbySettlement.name} ({currentNearbySettlement.Type})"); } UpdateUI(); } } /// /// Convert world coordinates to map tile coordinates /// private Vector2Int WorldToMapCoordinates(Vector2 worldPos) { // Get tile size from MapVisualizer if available float tileSize = 1f; var mapVisualizer = FindFirstObjectByType(); if (mapVisualizer != null && mapVisualizer.tileSize > 0) { tileSize = mapVisualizer.tileSize; } return new Vector2Int( Mathf.RoundToInt(worldPos.x / tileSize), Mathf.RoundToInt(worldPos.y / tileSize) ); } /// /// Find the nearest settlement to the given map position /// private Settlement FindNearestSettlement(Vector2Int mapPos) { if (mapData == null) return null; // Only detect settlements when directly on the settlement tile (exact match) Settlement exactMatch = mapData.GetSettlementAt(mapPos); if (exactMatch != null) { // Check exit cooldown if (exactMatch.name == lastExitedSettlement && Time.time - lastExitTime < exitCooldown) { float remainingCooldown = exitCooldown - (Time.time - lastExitTime); LogDebug($"Settlement {exactMatch.name} on cooldown for {remainingCooldown:F1}s more"); return null; } LogDebug($"Direct settlement match: {exactMatch.name} at {mapPos}"); return exactMatch; } // No nearby search - only exact tile matches return null; } /// /// Handle input for settlement interaction /// private void HandleInput() { if (currentNearbySettlement != null && Input.GetKeyDown(enterSettlementKey)) { EnterSettlement(currentNearbySettlement); } } /// /// Handle automatic settlement entry when stationary /// private void HandleAutoEnter() { if (!autoEnterOnStop || currentNearbySettlement == null) return; // Check if team marker has moved Vector3 currentPosition = teamMarker.position; float movementDistance = Vector3.Distance(currentPosition, lastTeamMarkerPosition); if (movementDistance < 0.1f) // Very small threshold for "stationary" { timeStationary += Time.deltaTime; if (timeStationary >= autoEnterDelay) { EnterSettlement(currentNearbySettlement); timeStationary = 0f; // Reset to prevent multiple entries } } else { timeStationary = 0f; // Reset timer if moving } lastTeamMarkerPosition = currentPosition; } /// /// Enter the specified settlement - setup context and load town scene /// public void EnterSettlement(Settlement settlement) { if (settlement == null) return; LogDebug($"Entering settlement: {settlement.name} ({settlement.Type})"); // Determine if settlement has harbor (check if near water) bool hasHarbor = DetermineHarborPresence(settlement); // Set up SettlementContext SetupSettlementContext(settlement, hasHarbor); // Trigger event OnSettlementEntered?.Invoke(settlement); // Load town scene LoadTownScene(); } /// /// Determine if settlement should have a harbor based on nearby terrain /// private bool DetermineHarborPresence(Settlement settlement) { if (mapData == null) return false; // Check nearby tiles for water for (int dx = -2; dx <= 2; dx++) { for (int dy = -2; dy <= 2; dy++) { int checkX = settlement.position.x + dx; int checkY = settlement.position.y + dy; var tile = mapData.GetTile(checkX, checkY); if (tile != null && (tile.terrainType == TerrainType.Ocean || tile.terrainType == TerrainType.Lake || tile.terrainType == TerrainType.River)) { return true; } } } return false; } /// /// Setup SettlementContext for scene transition /// private void SetupSettlementContext(Settlement settlement, bool hasHarbor) { // Find or create SettlementContext var existingContext = FindFirstObjectByType(); SettlementContext context; if (existingContext != null) { context = existingContext; } else { var contextObject = new GameObject("SettlementContext"); context = contextObject.AddComponent(); DontDestroyOnLoad(contextObject); } // Set settlement data context.SetSettlement(settlement.name, settlement.Type, settlement.position, hasHarbor); LogDebug($"SettlementContext configured: {settlement.name} ({settlement.Type}) - Harbor: {hasHarbor}"); } /// /// Load the town scene /// private void LoadTownScene() { LogDebug($"Loading town scene: {townSceneName}"); SceneManager.LoadScene(townSceneName); } /// /// Update UI based on current settlement state /// private void UpdateUI() { if (interactionPrompt == null) return; if (currentNearbySettlement != null) { interactionPrompt.SetActive(true); // You could update text here to show settlement name and interaction key } else { interactionPrompt.SetActive(false); } } /// /// Log debug message if debug logging is enabled /// private void LogDebug(string message) { if (enableDebugLogs) { Debug.Log($"[SettlementInteraction] {message}"); } } /// /// Force enter a settlement by name (for testing) /// [ContextMenu("Test Enter Current Settlement")] public void TestEnterCurrentSettlement() { if (currentNearbySettlement != null) { EnterSettlement(currentNearbySettlement); } else { LogDebug("No settlement nearby to enter"); } } /// /// Get current nearby settlement (for external scripts) /// public Settlement GetCurrentNearbySettlement() { return currentNearbySettlement; } /// /// Manually set team marker reference (for external scripts) /// public void SetTeamMarker(Transform marker) { teamMarker = marker; if (marker != null) { lastTeamMarkerPosition = marker.position; LogDebug("Team marker reference updated"); } } }