using UnityEngine; using System.Collections; using System.Collections.Generic; /// /// Simple standalone team placement script that randomly places the team on a settlement /// and creates a visible marker in front of the map. /// public class SimpleTeamPlacement : MonoBehaviour { [Header("Team Marker Settings")] public GameObject teamMarkerPrefab; public Material teamMarkerMaterial; [Range(0.5f, 3f)] public float markerSize = 1.5f; [Range(-5f, 5f)] public float markerHeight = 0.5f; // Y coordinate height above map tiles public Color markerColor = Color.green; [Header("Animation Settings")] public bool enableBlinking = true; [Range(0.1f, 2f)] public float blinkSpeed = 0.5f; // Private variables private GameObject teamMarkerInstance; private Vector2Int currentTeamPosition; private bool isTeamPositionSet = false; private bool isBlinking = false; private MapMaker2 mapMaker; public static SimpleTeamPlacement Instance { get; private set; } void Awake() { if (Instance == null) { Instance = this; } else { Debug.LogWarning("Multiple SimpleTeamPlacement instances found. Destroying this one."); Destroy(gameObject); return; } } void Start() { // Find MapMaker2 mapMaker = FindFirstObjectByType(); if (mapMaker == null) { return; } // Wait a bit for map generation, then place team StartCoroutine(WaitAndPlaceTeam()); } void Update() { // Debug keys if (Input.GetKeyDown(KeyCode.R)) { RandomlyPlaceTeam(); } if (Input.GetKeyDown(KeyCode.M)) { ShowMarkerInfo(); } // Debug key to check saved positions if (Input.GetKeyDown(KeyCode.P)) { DebugSavedPositions(); } // Travel system integration - allow clicking on map to plan travel if (Input.GetKeyDown(KeyCode.T)) { ShowTravelInfo(); } } private IEnumerator WaitAndPlaceTeam() { // Wait for map generation yield return new WaitForSeconds(1.5f); // Check if this is a new game that should force random placement // First ensure GameStateManager exists, create it if needed if (GameStateManager.Instance == null) { GameObject gameStateObj = new GameObject("GameStateManager"); gameStateObj.AddComponent(); // Wait a frame for the GameStateManager to initialize yield return null; } bool isNewGame = GameStateManager.Instance?.IsNewGameForceRandom() ?? false; // ALWAYS show new game detection debug logs (even if showDebugLogs is false) if (isNewGame) { RandomlyPlaceTeam(); // Clear the new game flag after placement GameStateManager.Instance?.ClearNewGameFlag(); yield break; } // Try multiple times to load saved position (in case MapMaker2 isn't ready yet) int attempts = 0; const int maxAttempts = 5; while (attempts < maxAttempts) { attempts++; // Try to load saved position first if (TryLoadSavedPosition()) { yield break; // Exit successfully } // If failed and MapMaker seed is still 0, wait a bit more if (mapMaker?.seed == 0 && attempts < maxAttempts) { yield return new WaitForSeconds(0.5f); } else { break; // Either loaded successfully or MapMaker is ready but no saved position } } // No saved position or invalid, place randomly RandomlyPlaceTeam(); } private bool TryLoadSavedPosition() { // Get the current map seed to make position specific to this map int currentSeed = mapMaker?.seed ?? 0; string positionKey = $"TeamPosition_{currentSeed}"; if (currentSeed == 0) { return false; } if (!PlayerPrefs.HasKey($"{positionKey}_X") || !PlayerPrefs.HasKey($"{positionKey}_Y")) { return false; } Vector2Int savedPosition = new Vector2Int( PlayerPrefs.GetInt($"{positionKey}_X"), PlayerPrefs.GetInt($"{positionKey}_Y") ); // Verify position is still valid (any valid map position, not just settlements) if (IsValidMapPosition(savedPosition)) { PlaceTeamAt(savedPosition); return true; } return false; } public void RandomlyPlaceTeam() { if (mapMaker?.GetMapData() == null) { return; } // For new games, ensure we're using the current random state for truly random placement bool isNewGame = GameStateManager.Instance?.IsNewGameForceRandom() ?? false; var mapData = mapMaker.GetMapData(); var allSettlements = new List(); // Combine towns and villages into one list for random selection allSettlements.AddRange(mapData.GetTowns()); allSettlements.AddRange(mapData.GetVillages()); if (allSettlements.Count == 0) { return; } // Randomly select a settlement int randomIndex = Random.Range(0, allSettlements.Count); Settlement selectedSettlement = allSettlements[randomIndex]; PlaceTeamAt(selectedSettlement.position); } public void PlaceTeamAt(Vector2Int position) { currentTeamPosition = position; isTeamPositionSet = true; // Save position with map seed for persistence int currentSeed = mapMaker?.seed ?? 0; string positionKey = $"TeamPosition_{currentSeed}"; bool isNewGame = GameStateManager.Instance?.IsNewGameForceRandom() ?? false; PlayerPrefs.SetInt($"{positionKey}_X", position.x); PlayerPrefs.SetInt($"{positionKey}_Y", position.y); PlayerPrefs.Save(); // Create/update visual marker CreateTeamMarker(); // Notify MapMaker2 of position change for event-driven exploration if (mapMaker != null) { mapMaker.OnTeamPositionChanged(position); } } /// /// Cleans up position data for old map seeds (optional maintenance) /// public void CleanupOldPositionData() { int currentSeed = mapMaker?.seed ?? 0; int cleanedCount = 0; // This is a simple cleanup - in a real game you might want more sophisticated management for (int i = 0; i < 10; i++) // Check last 10 potential seeds for cleanup { int testSeed = currentSeed - i; if (testSeed != currentSeed && testSeed > 0) { string positionKey = $"TeamPosition_{testSeed}"; if (PlayerPrefs.HasKey($"{positionKey}_X")) { PlayerPrefs.DeleteKey($"{positionKey}_X"); PlayerPrefs.DeleteKey($"{positionKey}_Y"); cleanedCount++; } } } } /// /// Debug method to check what positions are saved in PlayerPrefs /// private void DebugSavedPositions() { int currentSeed = mapMaker?.seed ?? 0; // Check current seed position string currentKey = $"TeamPosition_{currentSeed}"; bool hasCurrentPosition = PlayerPrefs.HasKey($"{currentKey}_X") && PlayerPrefs.HasKey($"{currentKey}_Y"); if (hasCurrentPosition) { Vector2Int currentPos = new Vector2Int( PlayerPrefs.GetInt($"{currentKey}_X"), PlayerPrefs.GetInt($"{currentKey}_Y") ); } else { } // Check for other potential seeds (common ones) int[] testSeeds = { 0, 12345, 1000, 5000, 9999 }; foreach (int testSeed in testSeeds) { if (testSeed == currentSeed) continue; // Already checked string testKey = $"TeamPosition_{testSeed}"; if (PlayerPrefs.HasKey($"{testKey}_X") && PlayerPrefs.HasKey($"{testKey}_Y")) { Vector2Int testPos = new Vector2Int( PlayerPrefs.GetInt($"{testKey}_X"), PlayerPrefs.GetInt($"{testKey}_Y") ); } } } private void CreateTeamMarker() { // Stop any existing blinking first if (isBlinking) { StopAllCoroutines(); // This will stop the BlinkMarker coroutine isBlinking = false; } // Remove existing marker if (teamMarkerInstance != null) { DestroyImmediate(teamMarkerInstance); } // Create new marker if (teamMarkerPrefab != null) { teamMarkerInstance = Instantiate(teamMarkerPrefab); } else { // Create simple sphere marker teamMarkerInstance = GameObject.CreatePrimitive(PrimitiveType.Sphere); teamMarkerInstance.transform.localScale = Vector3.one * markerSize; teamMarkerInstance.tag = "Player"; } // Position marker PositionMarker(); // Apply material/color ApplyMarkerMaterial(); // Set name teamMarkerInstance.name = "TeamMarker"; // Start blinking if enabled if (enableBlinking) { StartBlinking(); } // Notify camera to center on the newly created team marker var cameraController = FindFirstObjectByType(); if (cameraController != null) { cameraController.OnTeamMarkerCreated(); } } /// /// Get current team position /// public Vector2Int GetCurrentTeamPosition() { return currentTeamPosition; } /// /// Update team marker position when map coordinates change (called by exploration system) /// public void UpdateMarkerAfterMapChange(Vector2Int newVisiblePosition) { if (!isTeamPositionSet || teamMarkerInstance == null) { return; } // Store the world position before updating coordinates Vector3 currentWorldPos = teamMarkerInstance.transform.position; // Update the current team position to the new visible coordinates Vector2Int oldPosition = currentTeamPosition; currentTeamPosition = newVisiblePosition; // Reposition the marker to maintain world position consistency PositionMarker(); } private void PositionMarker() { // Get tile size from MapVisualizer float tileSize = 1f; if (mapMaker?.mapVisualizer != null) { tileSize = mapMaker.mapVisualizer.tileSize; } // Calculate world position: // In exploration mode, we need to convert exploration coordinates to full map coordinates for world positioning Vector2Int worldCoordinates = currentTeamPosition; // Check if we're using exploration system if (mapMaker != null && mapMaker.useExplorationSystem) { var explorationManager = mapMaker.GetExplorationManager(); if (explorationManager != null) { // Use exploration manager to convert coordinates properly Vector3 correctWorldPos = explorationManager.ConvertExplorationToWorldCoordinates(currentTeamPosition); teamMarkerInstance.transform.position = new Vector3( correctWorldPos.x, markerHeight, // Y coordinate height above map tiles correctWorldPos.z // Use Z coordinate for map Y position ); return; } } // Fallback for non-exploration mode: use direct coordinate mapping Vector3 worldPosition = new Vector3( worldCoordinates.x * tileSize, markerHeight, // Y coordinate height above map tiles worldCoordinates.y * tileSize // Map Y becomes world Z for top-down view ); teamMarkerInstance.transform.position = worldPosition; } private void ApplyMarkerMaterial() { Renderer renderer = teamMarkerInstance.GetComponent(); if (renderer == null) return; if (teamMarkerMaterial != null) { renderer.material = teamMarkerMaterial; } else { // Create simple colored material using URP shaders first Shader shader = null; // Try URP shaders first shader = Shader.Find("Universal Render Pipeline/Lit"); if (shader == null) shader = Shader.Find("Universal Render Pipeline/Simple Lit"); if (shader == null) shader = Shader.Find("Universal Render Pipeline/Unlit"); // Fallback to built-in shaders if (shader == null) shader = Shader.Find("Standard"); if (shader == null) shader = Shader.Find("Legacy Shaders/Diffuse"); if (shader == null) shader = Shader.Find("Unlit/Color"); // Last resort fallback if (shader == null) { return; } Material material = new Material(shader); // Set base color (works for both URP and built-in) if (material.HasProperty("_BaseColor")) { material.SetColor("_BaseColor", markerColor); // URP property } else if (material.HasProperty("_Color")) { material.SetColor("_Color", markerColor); // Built-in property } material.color = markerColor; // Fallback // Add emission for better visibility if (material.HasProperty("_EmissionColor")) { material.EnableKeyword("_EMISSION"); material.SetColor("_EmissionColor", markerColor * 0.3f); } renderer.material = material; } } private void StartBlinking() { if (!isBlinking) { StartCoroutine(BlinkMarker()); } else { // Force restart blinking as a fallback StopAllCoroutines(); isBlinking = false; StartCoroutine(BlinkMarker()); } } private IEnumerator BlinkMarker() { isBlinking = true; Renderer markerRenderer = teamMarkerInstance?.GetComponent(); if (markerRenderer == null) { isBlinking = false; yield break; } // Capture the enableBlinking state at start to avoid timing issues bool shouldBlink = enableBlinking; int blinkCount = 0; while (teamMarkerInstance != null && markerRenderer != null && shouldBlink) { // Hide marker markerRenderer.enabled = false; yield return new WaitForSeconds(blinkSpeed); // Show marker if (markerRenderer != null && teamMarkerInstance != null) { markerRenderer.enabled = true; yield return new WaitForSeconds(blinkSpeed); blinkCount++; } else { break; } // Re-check enableBlinking periodically (every 10 blinks) in case user changes it if (blinkCount % 10 == 0) { shouldBlink = enableBlinking; } } isBlinking = false; } private bool IsValidSettlementPosition(Vector2Int position) { if (mapMaker?.GetMapData() == null) return false; var mapData = mapMaker.GetMapData(); if (!mapData.IsValidPosition(position.x, position.y)) return false; // Check if position has a settlement foreach (var town in mapData.GetTowns()) { if (town.position == position) return true; } foreach (var village in mapData.GetVillages()) { if (village.position == position) return true; } return false; } /// /// Checks if a position is valid for team placement (any valid map position) /// Used for persistent positioning - team can be anywhere on the map /// private bool IsValidMapPosition(Vector2Int position) { if (mapMaker?.GetMapData() == null) { return false; } var mapData = mapMaker.GetMapData(); return mapData.IsValidPosition(position.x, position.y); } private void ShowMarkerInfo() { if (teamMarkerInstance == null) { return; } } private void ShowTravelInfo() { var travelSystem = TeamTravelSystem.Instance; if (travelSystem != null) { } else { } } // Public methods for external control public Vector2Int GetTeamPosition() => currentTeamPosition; public bool IsTeamPlaced() => isTeamPositionSet; public GameObject GetTeamMarker() => teamMarkerInstance; [ContextMenu("Randomly Place Team")] public void ContextRandomlyPlaceTeam() { RandomlyPlaceTeam(); } [ContextMenu("Show Marker Info")] public void ContextShowMarkerInfo() { ShowMarkerInfo(); } [ContextMenu("Force New Game Random Placement")] public void ContextForceNewGamePlacement() { // Temporarily set new game flag for testing if (GameStateManager.Instance != null) { GameStateManager.Instance.isNewGame = true; } RandomlyPlaceTeam(); // Clear the flag after testing GameStateManager.Instance?.ClearNewGameFlag(); } }