SimpleTeamPlacement.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. /// <summary>
  5. /// Simple standalone team placement script that randomly places the team on a settlement
  6. /// and creates a visible marker in front of the map.
  7. /// </summary>
  8. public class SimpleTeamPlacement : MonoBehaviour
  9. {
  10. [Header("Team Marker Settings")]
  11. public GameObject teamMarkerPrefab;
  12. public Material teamMarkerMaterial;
  13. [Range(0.5f, 3f)]
  14. public float markerSize = 1.5f;
  15. [Range(-5f, 5f)]
  16. public float markerHeight = 0.5f; // Y coordinate height above map tiles
  17. public Color markerColor = Color.green;
  18. [Header("Animation Settings")]
  19. public bool enableBlinking = true;
  20. [Range(0.1f, 2f)]
  21. public float blinkSpeed = 0.5f;
  22. // Private variables
  23. private GameObject teamMarkerInstance;
  24. private Vector2Int currentTeamPosition;
  25. private bool isTeamPositionSet = false;
  26. private bool isBlinking = false;
  27. private MapMaker2 mapMaker;
  28. public static SimpleTeamPlacement Instance { get; private set; }
  29. void Awake()
  30. {
  31. if (Instance == null)
  32. {
  33. Instance = this;
  34. }
  35. else
  36. {
  37. Debug.LogWarning("Multiple SimpleTeamPlacement instances found. Destroying this one.");
  38. Destroy(gameObject);
  39. return;
  40. }
  41. }
  42. void Start()
  43. {
  44. // Find MapMaker2
  45. mapMaker = FindFirstObjectByType<MapMaker2>();
  46. if (mapMaker == null)
  47. {
  48. return;
  49. }
  50. // Wait a bit for map generation, then place team
  51. StartCoroutine(WaitAndPlaceTeam());
  52. }
  53. void Update()
  54. {
  55. // Debug keys
  56. if (Input.GetKeyDown(KeyCode.R))
  57. {
  58. RandomlyPlaceTeam();
  59. }
  60. if (Input.GetKeyDown(KeyCode.M))
  61. {
  62. ShowMarkerInfo();
  63. }
  64. // Debug key to check saved positions
  65. if (Input.GetKeyDown(KeyCode.P))
  66. {
  67. DebugSavedPositions();
  68. }
  69. // Travel system integration - allow clicking on map to plan travel
  70. if (Input.GetKeyDown(KeyCode.T))
  71. {
  72. ShowTravelInfo();
  73. }
  74. }
  75. private IEnumerator WaitAndPlaceTeam()
  76. {
  77. // Wait for map generation
  78. yield return new WaitForSeconds(1.5f);
  79. // Check if this is a new game that should force random placement
  80. // First ensure GameStateManager exists, create it if needed
  81. if (GameStateManager.Instance == null)
  82. {
  83. GameObject gameStateObj = new GameObject("GameStateManager");
  84. gameStateObj.AddComponent<GameStateManager>();
  85. // Wait a frame for the GameStateManager to initialize
  86. yield return null;
  87. }
  88. bool isNewGame = GameStateManager.Instance?.IsNewGameForceRandom() ?? false;
  89. // ALWAYS show new game detection debug logs (even if showDebugLogs is false)
  90. if (isNewGame)
  91. {
  92. RandomlyPlaceTeam();
  93. // Clear the new game flag after placement
  94. GameStateManager.Instance?.ClearNewGameFlag();
  95. yield break;
  96. }
  97. // Try multiple times to load saved position (in case MapMaker2 isn't ready yet)
  98. int attempts = 0;
  99. const int maxAttempts = 5;
  100. while (attempts < maxAttempts)
  101. {
  102. attempts++;
  103. // Try to load saved position first
  104. if (TryLoadSavedPosition())
  105. {
  106. yield break; // Exit successfully
  107. }
  108. // If failed and MapMaker seed is still 0, wait a bit more
  109. if (mapMaker?.seed == 0 && attempts < maxAttempts)
  110. {
  111. yield return new WaitForSeconds(0.5f);
  112. }
  113. else
  114. {
  115. break; // Either loaded successfully or MapMaker is ready but no saved position
  116. }
  117. }
  118. // No saved position or invalid, place randomly
  119. RandomlyPlaceTeam();
  120. }
  121. private bool TryLoadSavedPosition()
  122. {
  123. // Get the current map seed to make position specific to this map
  124. int currentSeed = mapMaker?.seed ?? 0;
  125. string positionKey = $"TeamPosition_{currentSeed}";
  126. if (currentSeed == 0)
  127. {
  128. return false;
  129. }
  130. if (!PlayerPrefs.HasKey($"{positionKey}_X") || !PlayerPrefs.HasKey($"{positionKey}_Y"))
  131. {
  132. return false;
  133. }
  134. Vector2Int savedPosition = new Vector2Int(
  135. PlayerPrefs.GetInt($"{positionKey}_X"),
  136. PlayerPrefs.GetInt($"{positionKey}_Y")
  137. );
  138. // Verify position is still valid (any valid map position, not just settlements)
  139. if (IsValidMapPosition(savedPosition))
  140. {
  141. PlaceTeamAt(savedPosition);
  142. return true;
  143. }
  144. return false;
  145. }
  146. public void RandomlyPlaceTeam()
  147. {
  148. if (mapMaker?.GetMapData() == null)
  149. {
  150. return;
  151. }
  152. // For new games, ensure we're using the current random state for truly random placement
  153. bool isNewGame = GameStateManager.Instance?.IsNewGameForceRandom() ?? false;
  154. var mapData = mapMaker.GetMapData();
  155. var allSettlements = new List<Settlement>();
  156. // Combine towns and villages into one list for random selection
  157. allSettlements.AddRange(mapData.GetTowns());
  158. allSettlements.AddRange(mapData.GetVillages());
  159. if (allSettlements.Count == 0)
  160. {
  161. return;
  162. }
  163. // Randomly select a settlement
  164. int randomIndex = Random.Range(0, allSettlements.Count);
  165. Settlement selectedSettlement = allSettlements[randomIndex];
  166. PlaceTeamAt(selectedSettlement.position);
  167. }
  168. public void PlaceTeamAt(Vector2Int position)
  169. {
  170. currentTeamPosition = position;
  171. isTeamPositionSet = true;
  172. // Save position with map seed for persistence
  173. int currentSeed = mapMaker?.seed ?? 0;
  174. string positionKey = $"TeamPosition_{currentSeed}";
  175. bool isNewGame = GameStateManager.Instance?.IsNewGameForceRandom() ?? false;
  176. PlayerPrefs.SetInt($"{positionKey}_X", position.x);
  177. PlayerPrefs.SetInt($"{positionKey}_Y", position.y);
  178. PlayerPrefs.Save();
  179. // Create/update visual marker
  180. CreateTeamMarker();
  181. // Notify MapMaker2 of position change for event-driven exploration
  182. if (mapMaker != null)
  183. {
  184. mapMaker.OnTeamPositionChanged(position);
  185. }
  186. }
  187. /// <summary>
  188. /// Cleans up position data for old map seeds (optional maintenance)
  189. /// </summary>
  190. public void CleanupOldPositionData()
  191. {
  192. int currentSeed = mapMaker?.seed ?? 0;
  193. int cleanedCount = 0;
  194. // This is a simple cleanup - in a real game you might want more sophisticated management
  195. for (int i = 0; i < 10; i++) // Check last 10 potential seeds for cleanup
  196. {
  197. int testSeed = currentSeed - i;
  198. if (testSeed != currentSeed && testSeed > 0)
  199. {
  200. string positionKey = $"TeamPosition_{testSeed}";
  201. if (PlayerPrefs.HasKey($"{positionKey}_X"))
  202. {
  203. PlayerPrefs.DeleteKey($"{positionKey}_X");
  204. PlayerPrefs.DeleteKey($"{positionKey}_Y");
  205. cleanedCount++;
  206. }
  207. }
  208. }
  209. }
  210. /// <summary>
  211. /// Debug method to check what positions are saved in PlayerPrefs
  212. /// </summary>
  213. private void DebugSavedPositions()
  214. {
  215. int currentSeed = mapMaker?.seed ?? 0;
  216. // Check current seed position
  217. string currentKey = $"TeamPosition_{currentSeed}";
  218. bool hasCurrentPosition = PlayerPrefs.HasKey($"{currentKey}_X") && PlayerPrefs.HasKey($"{currentKey}_Y");
  219. if (hasCurrentPosition)
  220. {
  221. Vector2Int currentPos = new Vector2Int(
  222. PlayerPrefs.GetInt($"{currentKey}_X"),
  223. PlayerPrefs.GetInt($"{currentKey}_Y")
  224. );
  225. }
  226. else
  227. {
  228. }
  229. // Check for other potential seeds (common ones)
  230. int[] testSeeds = { 0, 12345, 1000, 5000, 9999 };
  231. foreach (int testSeed in testSeeds)
  232. {
  233. if (testSeed == currentSeed) continue; // Already checked
  234. string testKey = $"TeamPosition_{testSeed}";
  235. if (PlayerPrefs.HasKey($"{testKey}_X") && PlayerPrefs.HasKey($"{testKey}_Y"))
  236. {
  237. Vector2Int testPos = new Vector2Int(
  238. PlayerPrefs.GetInt($"{testKey}_X"),
  239. PlayerPrefs.GetInt($"{testKey}_Y")
  240. );
  241. }
  242. }
  243. }
  244. private void CreateTeamMarker()
  245. {
  246. // Stop any existing blinking first
  247. if (isBlinking)
  248. {
  249. StopAllCoroutines(); // This will stop the BlinkMarker coroutine
  250. isBlinking = false;
  251. }
  252. // Remove existing marker
  253. if (teamMarkerInstance != null)
  254. {
  255. DestroyImmediate(teamMarkerInstance);
  256. }
  257. // Create new marker
  258. if (teamMarkerPrefab != null)
  259. {
  260. teamMarkerInstance = Instantiate(teamMarkerPrefab);
  261. }
  262. else
  263. {
  264. // Create simple sphere marker
  265. teamMarkerInstance = GameObject.CreatePrimitive(PrimitiveType.Sphere);
  266. teamMarkerInstance.transform.localScale = Vector3.one * markerSize;
  267. teamMarkerInstance.tag = "Player";
  268. }
  269. // Position marker
  270. PositionMarker();
  271. // Apply material/color
  272. ApplyMarkerMaterial();
  273. // Set name
  274. teamMarkerInstance.name = "TeamMarker";
  275. // Start blinking if enabled
  276. if (enableBlinking)
  277. {
  278. StartBlinking();
  279. }
  280. // Notify camera to center on the newly created team marker
  281. var cameraController = FindFirstObjectByType<MMCameraController>();
  282. if (cameraController != null)
  283. {
  284. cameraController.OnTeamMarkerCreated();
  285. }
  286. }
  287. /// <summary>
  288. /// Get current team position
  289. /// </summary>
  290. public Vector2Int GetCurrentTeamPosition()
  291. {
  292. return currentTeamPosition;
  293. }
  294. /// <summary>
  295. /// Update team marker position when map coordinates change (called by exploration system)
  296. /// </summary>
  297. public void UpdateMarkerAfterMapChange(Vector2Int newVisiblePosition)
  298. {
  299. if (!isTeamPositionSet || teamMarkerInstance == null)
  300. {
  301. return;
  302. }
  303. // Store the world position before updating coordinates
  304. Vector3 currentWorldPos = teamMarkerInstance.transform.position;
  305. // Update the current team position to the new visible coordinates
  306. Vector2Int oldPosition = currentTeamPosition;
  307. currentTeamPosition = newVisiblePosition;
  308. // Reposition the marker to maintain world position consistency
  309. PositionMarker();
  310. }
  311. private void PositionMarker()
  312. {
  313. // Get tile size from MapVisualizer
  314. float tileSize = 1f;
  315. if (mapMaker?.mapVisualizer != null)
  316. {
  317. tileSize = mapMaker.mapVisualizer.tileSize;
  318. }
  319. // Calculate world position:
  320. // In exploration mode, we need to convert exploration coordinates to full map coordinates for world positioning
  321. Vector2Int worldCoordinates = currentTeamPosition;
  322. // Check if we're using exploration system
  323. if (mapMaker != null && mapMaker.useExplorationSystem)
  324. {
  325. var explorationManager = mapMaker.GetExplorationManager();
  326. if (explorationManager != null)
  327. {
  328. // Use exploration manager to convert coordinates properly
  329. Vector3 correctWorldPos = explorationManager.ConvertExplorationToWorldCoordinates(currentTeamPosition);
  330. teamMarkerInstance.transform.position = new Vector3(
  331. correctWorldPos.x,
  332. markerHeight, // Y coordinate height above map tiles
  333. correctWorldPos.z // Use Z coordinate for map Y position
  334. );
  335. return;
  336. }
  337. }
  338. // Fallback for non-exploration mode: use direct coordinate mapping
  339. Vector3 worldPosition = new Vector3(
  340. worldCoordinates.x * tileSize,
  341. markerHeight, // Y coordinate height above map tiles
  342. worldCoordinates.y * tileSize // Map Y becomes world Z for top-down view
  343. );
  344. teamMarkerInstance.transform.position = worldPosition;
  345. }
  346. private void ApplyMarkerMaterial()
  347. {
  348. Renderer renderer = teamMarkerInstance.GetComponent<Renderer>();
  349. if (renderer == null) return;
  350. if (teamMarkerMaterial != null)
  351. {
  352. renderer.material = teamMarkerMaterial;
  353. }
  354. else
  355. {
  356. // Create simple colored material using URP shaders first
  357. Shader shader = null;
  358. // Try URP shaders first
  359. shader = Shader.Find("Universal Render Pipeline/Lit");
  360. if (shader == null)
  361. shader = Shader.Find("Universal Render Pipeline/Simple Lit");
  362. if (shader == null)
  363. shader = Shader.Find("Universal Render Pipeline/Unlit");
  364. // Fallback to built-in shaders
  365. if (shader == null)
  366. shader = Shader.Find("Standard");
  367. if (shader == null)
  368. shader = Shader.Find("Legacy Shaders/Diffuse");
  369. if (shader == null)
  370. shader = Shader.Find("Unlit/Color");
  371. // Last resort fallback
  372. if (shader == null)
  373. {
  374. return;
  375. }
  376. Material material = new Material(shader);
  377. // Set base color (works for both URP and built-in)
  378. if (material.HasProperty("_BaseColor"))
  379. {
  380. material.SetColor("_BaseColor", markerColor); // URP property
  381. }
  382. else if (material.HasProperty("_Color"))
  383. {
  384. material.SetColor("_Color", markerColor); // Built-in property
  385. }
  386. material.color = markerColor; // Fallback
  387. // Add emission for better visibility
  388. if (material.HasProperty("_EmissionColor"))
  389. {
  390. material.EnableKeyword("_EMISSION");
  391. material.SetColor("_EmissionColor", markerColor * 0.3f);
  392. }
  393. renderer.material = material;
  394. }
  395. }
  396. private void StartBlinking()
  397. {
  398. if (!isBlinking)
  399. {
  400. StartCoroutine(BlinkMarker());
  401. }
  402. else
  403. {
  404. // Force restart blinking as a fallback
  405. StopAllCoroutines();
  406. isBlinking = false;
  407. StartCoroutine(BlinkMarker());
  408. }
  409. }
  410. private IEnumerator BlinkMarker()
  411. {
  412. isBlinking = true;
  413. Renderer markerRenderer = teamMarkerInstance?.GetComponent<Renderer>();
  414. if (markerRenderer == null)
  415. {
  416. isBlinking = false;
  417. yield break;
  418. }
  419. // Capture the enableBlinking state at start to avoid timing issues
  420. bool shouldBlink = enableBlinking;
  421. int blinkCount = 0;
  422. while (teamMarkerInstance != null && markerRenderer != null && shouldBlink)
  423. {
  424. // Hide marker
  425. markerRenderer.enabled = false;
  426. yield return new WaitForSeconds(blinkSpeed);
  427. // Show marker
  428. if (markerRenderer != null && teamMarkerInstance != null)
  429. {
  430. markerRenderer.enabled = true;
  431. yield return new WaitForSeconds(blinkSpeed);
  432. blinkCount++;
  433. }
  434. else
  435. {
  436. break;
  437. }
  438. // Re-check enableBlinking periodically (every 10 blinks) in case user changes it
  439. if (blinkCount % 10 == 0)
  440. {
  441. shouldBlink = enableBlinking;
  442. }
  443. }
  444. isBlinking = false;
  445. }
  446. private bool IsValidSettlementPosition(Vector2Int position)
  447. {
  448. if (mapMaker?.GetMapData() == null) return false;
  449. var mapData = mapMaker.GetMapData();
  450. if (!mapData.IsValidPosition(position.x, position.y)) return false;
  451. // Check if position has a settlement
  452. foreach (var town in mapData.GetTowns())
  453. {
  454. if (town.position == position) return true;
  455. }
  456. foreach (var village in mapData.GetVillages())
  457. {
  458. if (village.position == position) return true;
  459. }
  460. return false;
  461. }
  462. /// <summary>
  463. /// Checks if a position is valid for team placement (any valid map position)
  464. /// Used for persistent positioning - team can be anywhere on the map
  465. /// </summary>
  466. private bool IsValidMapPosition(Vector2Int position)
  467. {
  468. if (mapMaker?.GetMapData() == null)
  469. {
  470. return false;
  471. }
  472. var mapData = mapMaker.GetMapData();
  473. return mapData.IsValidPosition(position.x, position.y);
  474. }
  475. private void ShowMarkerInfo()
  476. {
  477. if (teamMarkerInstance == null)
  478. {
  479. return;
  480. }
  481. }
  482. private void ShowTravelInfo()
  483. {
  484. var travelSystem = TeamTravelSystem.Instance;
  485. if (travelSystem != null)
  486. {
  487. }
  488. else
  489. {
  490. }
  491. }
  492. // Public methods for external control
  493. public Vector2Int GetTeamPosition() => currentTeamPosition;
  494. public bool IsTeamPlaced() => isTeamPositionSet;
  495. public GameObject GetTeamMarker() => teamMarkerInstance;
  496. [ContextMenu("Randomly Place Team")]
  497. public void ContextRandomlyPlaceTeam()
  498. {
  499. RandomlyPlaceTeam();
  500. }
  501. [ContextMenu("Show Marker Info")]
  502. public void ContextShowMarkerInfo()
  503. {
  504. ShowMarkerInfo();
  505. }
  506. [ContextMenu("Force New Game Random Placement")]
  507. public void ContextForceNewGamePlacement()
  508. {
  509. // Temporarily set new game flag for testing
  510. if (GameStateManager.Instance != null)
  511. {
  512. GameStateManager.Instance.isNewGame = true;
  513. }
  514. RandomlyPlaceTeam();
  515. // Clear the flag after testing
  516. GameStateManager.Instance?.ClearNewGameFlag();
  517. }
  518. }