| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422 |
- using UnityEngine;
- using UnityEngine.SceneManagement;
- using System.Collections.Generic;
- using System.Linq;
- /// <summary>
- /// Manages settlement interaction on the map - detects when TeamMarker is over a settlement
- /// and allows entering the town scene with proper context setup
- /// </summary>
- 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";
- public bool enableDebugLogs = false;
- [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<Settlement> OnSettlementDetected;
- public System.Action OnSettlementLeft;
- public System.Action<Settlement> OnSettlementEntered;
- void Awake()
- {
- // Check if we're returning from a town scene
- var settlementContext = FindFirstObjectByType<SettlementContext>();
- 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();
- }
- /// <summary>
- /// Initialize required components and references
- /// </summary>
- 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<MapMaker2>();
- 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);
- }
- // Initialize team marker position tracking if teamMarker is found
- if (teamMarker != null)
- {
- lastTeamMarkerPosition = teamMarker.position;
- }
- else
- {
- LogDebug("WARNING: TeamMarker is null - position tracking will not work until teamMarker is assigned");
- }
- }
- /// <summary>
- /// Check if TeamMarker is near any settlement
- /// </summary>
- private void CheckForSettlementInteraction()
- {
- // Check if teamMarker is assigned
- if (teamMarker == null)
- {
- return; // Skip interaction check if no team marker
- }
- 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();
- }
- }
- /// <summary>
- /// Convert world coordinates to map tile coordinates
- /// </summary>
- private Vector2Int WorldToMapCoordinates(Vector2 worldPos)
- {
- // Get tile size from MapVisualizer if available
- float tileSize = 1f;
- var mapVisualizer = FindFirstObjectByType<MapVisualizer>();
- if (mapVisualizer != null && mapVisualizer.tileSize > 0)
- {
- tileSize = mapVisualizer.tileSize;
- }
- return new Vector2Int(
- Mathf.RoundToInt(worldPos.x / tileSize),
- Mathf.RoundToInt(worldPos.y / tileSize)
- );
- }
- /// <summary>
- /// Find the nearest settlement to the given map position
- /// </summary>
- 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;
- }
- /// <summary>
- /// Handle input for settlement interaction
- /// </summary>
- private void HandleInput()
- {
- if (currentNearbySettlement != null && Input.GetKeyDown(enterSettlementKey))
- {
- EnterSettlement(currentNearbySettlement);
- }
- }
- /// <summary>
- /// Handle automatic settlement entry when stationary
- /// </summary>
- 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;
- }
- /// <summary>
- /// Enter the specified settlement - setup context and load town scene
- /// </summary>
- 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();
- }
- /// <summary>
- /// Determine if settlement should have a harbor based on nearby terrain
- /// </summary>
- 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;
- }
- /// <summary>
- /// Setup SettlementContext for scene transition
- /// </summary>
- private void SetupSettlementContext(Settlement settlement, bool hasHarbor)
- {
- // Find or create SettlementContext
- var existingContext = FindFirstObjectByType<SettlementContext>();
- SettlementContext context;
- if (existingContext != null)
- {
- context = existingContext;
- }
- else
- {
- var contextObject = new GameObject("SettlementContext");
- context = contextObject.AddComponent<SettlementContext>();
- DontDestroyOnLoad(contextObject);
- }
- // Set settlement data
- context.SetSettlement(settlement.name, settlement.Type, settlement.position, hasHarbor);
- LogDebug($"SettlementContext configured: {settlement.name} ({settlement.Type}) - Harbor: {hasHarbor}");
- }
- /// <summary>
- /// Load the town scene
- /// </summary>
- private void LoadTownScene()
- {
- LogDebug($"Loading town scene: {townSceneName}");
- SceneManager.LoadScene(townSceneName);
- }
- /// <summary>
- /// Update UI based on current settlement state
- /// </summary>
- 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);
- }
- }
- /// <summary>
- /// Log debug message if debug logging is enabled
- /// </summary>
- private void LogDebug(string message)
- {
- if (enableDebugLogs)
- {
- Debug.Log($"[SettlementInteraction] {message}");
- }
- }
- /// <summary>
- /// Force enter a settlement by name (for testing)
- /// </summary>
- [ContextMenu("Test Enter Current Settlement")]
- public void TestEnterCurrentSettlement()
- {
- if (currentNearbySettlement != null)
- {
- EnterSettlement(currentNearbySettlement);
- }
- else
- {
- LogDebug("No settlement nearby to enter");
- }
- }
- /// <summary>
- /// Get current nearby settlement (for external scripts)
- /// </summary>
- public Settlement GetCurrentNearbySettlement()
- {
- return currentNearbySettlement;
- }
- /// <summary>
- /// Manually set team marker reference (for external scripts)
- /// </summary>
- public void SetTeamMarker(Transform marker)
- {
- teamMarker = marker;
- if (marker != null)
- {
- lastTeamMarkerPosition = marker.position;
- LogDebug("Team marker reference updated");
- }
- }
- }
|