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";
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 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);
}
// 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");
}
}
///
/// Check if TeamMarker is near any settlement
///
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();
}
}
///
/// 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");
}
}
}