ExplorationManager.cs 74 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. /// <summary>
  5. /// Manages map exploration using a pre-generated large map with fog of war
  6. /// </summary>
  7. public class ExplorationManager
  8. {
  9. private MapMaker2 mapMaker;
  10. private MapData fullMapData; // The complete pre-generated map
  11. private bool[,] exploredMask; // Tracks which areas have been revealed
  12. private float lastExplorationTime = 0f; // Cooldown tracking
  13. // Performance settings for large maps
  14. private bool performanceMode = false; // Enable for maps larger than 200x200
  15. private int lodLevel = 1; // Level of Detail: 1 = full detail, 2 = half detail, 4 = quarter detail
  16. // Exploration Settings - Public properties for MapMaker2 to set
  17. public int fullMapWidth { get; set; } = 300;
  18. public int fullMapHeight { get; set; } = 300;
  19. public int initialVisibleSize { get; set; } = 80; // Starting visible area
  20. public int explorationChunkSize { get; set; } = 40; // Size of chunks to reveal
  21. public float explorationDistance { get; set; } = 10f; // Distance from edge to trigger exploration (reduced from 20f)
  22. public float explorationCooldown { get; set; } = 3f; // Cooldown between explorations
  23. public ExplorationManager(MapMaker2 mapMaker)
  24. {
  25. this.mapMaker = mapMaker;
  26. }
  27. /// <summary>
  28. /// Initialize the exploration system with a large pre-generated map
  29. /// </summary>
  30. public void InitializeExploration()
  31. {
  32. Debug.Log("🗺️ Initializing Exploration System...");
  33. // Check if we need performance mode for large maps
  34. if (fullMapWidth > 200 || fullMapHeight > 200)
  35. {
  36. performanceMode = true;
  37. lodLevel = CalculateOptimalLOD(fullMapWidth, fullMapHeight);
  38. Debug.Log($"⚡ Performance mode enabled with LOD level {lodLevel} for large map");
  39. }
  40. // Generate the full large map
  41. GenerateFullMap();
  42. // Initialize exploration mask
  43. exploredMask = new bool[fullMapWidth, fullMapHeight];
  44. Debug.Log($"🔍 Created exploration mask: {fullMapWidth}x{fullMapHeight}");
  45. // Reveal initial area around the center
  46. RevealInitialArea();
  47. // Update the visible map data
  48. UpdateVisibleMap();
  49. Debug.Log($"✅ Exploration System initialized - Full map: {fullMapWidth}x{fullMapHeight}, Initial visible: {initialVisibleSize}x{initialVisibleSize}");
  50. }
  51. /// <summary>
  52. /// Calculate optimal Level of Detail for given map size
  53. /// </summary>
  54. private int CalculateOptimalLOD(int width, int height)
  55. {
  56. int totalTiles = width * height;
  57. if (totalTiles > 250000) // 500x500+
  58. return 4; // Quarter detail
  59. else if (totalTiles > 100000) // 316x316+
  60. return 2; // Half detail
  61. else
  62. return 1; // Full detail
  63. }
  64. /// <summary>
  65. /// DEBUG: Show the entire 300x300 map for testing purposes
  66. /// </summary>
  67. public void ShowFullMap()
  68. {
  69. if (fullMapData == null)
  70. {
  71. Debug.LogError("❌ Full map data not available!");
  72. return;
  73. }
  74. Debug.Log("🌍 Loading full 300x300 map for debugging...");
  75. // Get the current position in full map coordinates BEFORE switching to full map
  76. Vector2Int currentFullMapPosition = GetCurrentTeamPositionInFullMapImproved();
  77. Debug.Log($"🎯 Team position before full map: Full map coordinates ({currentFullMapPosition})");
  78. // Mark entire map as explored
  79. for (int x = 0; x < fullMapWidth; x++)
  80. {
  81. for (int y = 0; y < fullMapHeight; y++)
  82. {
  83. exploredMask[x, y] = true;
  84. }
  85. }
  86. // Update the visible map to show everything
  87. UpdateVisibleMap();
  88. // Restore team marker to the correct full map position
  89. RestoreTeamMarkerToFullMapPosition(currentFullMapPosition);
  90. Debug.Log($"✅ Full map is now visible!");
  91. }
  92. /// <summary>
  93. /// TEST: Debug coordinate conversion to find the issue
  94. /// </summary>
  95. public void TestCoordinateConversion()
  96. {
  97. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  98. if (teamPlacement == null)
  99. {
  100. Debug.LogError("❌ SimpleTeamPlacement not found!");
  101. return;
  102. }
  103. GameObject teamMarker = GameObject.Find("TeamMarker");
  104. if (teamMarker == null)
  105. {
  106. Debug.LogError("❌ TeamMarker not found!");
  107. return;
  108. }
  109. // Get current state
  110. Vector3 worldPos = teamMarker.transform.position;
  111. Vector2Int storedPos = teamPlacement.GetCurrentTeamPosition();
  112. Rect exploredBounds = GetExploredBounds();
  113. bool isShowingFullMap = exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5;
  114. Debug.Log("=== COORDINATE CONVERSION TEST ===");
  115. Debug.Log($"Team Marker World Position: ({worldPos.x:F1}, {worldPos.y:F1})");
  116. Debug.Log($"Team Placement Stored Position: ({storedPos.x}, {storedPos.y})");
  117. Debug.Log($"Explored Bounds: {exploredBounds}");
  118. Debug.Log($"Is Showing Full Map: {isShowingFullMap}");
  119. Debug.Log($"Full Map Size: {fullMapWidth}x{fullMapHeight}");
  120. // Test the conversion
  121. Vector2Int convertedPos = GetCurrentTeamPositionInFullMapImproved();
  122. Debug.Log($"Converted Full Map Position: ({convertedPos.x}, {convertedPos.y})");
  123. // Expected position for center of map
  124. Vector2Int expectedCenter = new Vector2Int(fullMapWidth / 2, fullMapHeight / 2);
  125. Debug.Log($"Expected Map Center: ({expectedCenter.x}, {expectedCenter.y})");
  126. // Check if the stored position makes sense
  127. if (!isShowingFullMap)
  128. {
  129. Vector2Int expectedFullMapPos = new Vector2Int(
  130. (int)exploredBounds.x + storedPos.x,
  131. (int)exploredBounds.y + storedPos.y
  132. );
  133. Debug.Log($"Expected Full Map Pos from Stored: Bounds({exploredBounds.x}, {exploredBounds.y}) + Stored({storedPos.x}, {storedPos.y}) = ({expectedFullMapPos.x}, {expectedFullMapPos.y})");
  134. }
  135. Debug.Log("=====================================");
  136. }
  137. /// <summary>
  138. /// TEST: Simple coordinate conversion test
  139. /// </summary>
  140. public void TestSimpleCoordinateConversion()
  141. {
  142. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  143. if (teamPlacement == null)
  144. {
  145. Debug.LogError("❌ SimpleTeamPlacement not found!");
  146. return;
  147. }
  148. Vector2Int storedPos = teamPlacement.GetCurrentTeamPosition();
  149. Rect exploredBounds = GetExploredBounds();
  150. Debug.Log("=== SIMPLE COORDINATE TEST ===");
  151. Debug.Log($"Stored team position: ({storedPos.x}, {storedPos.y})");
  152. Debug.Log($"Explored bounds: {exploredBounds}");
  153. // Expected full map position
  154. Vector2Int expectedFullMapPos = new Vector2Int(
  155. (int)exploredBounds.x + storedPos.x,
  156. (int)exploredBounds.y + storedPos.y
  157. );
  158. Debug.Log($"Expected full map position: ({expectedFullMapPos.x}, {expectedFullMapPos.y})");
  159. Debug.Log($"Map center: ({fullMapWidth / 2}, {fullMapHeight / 2})");
  160. bool isNearCenter = Vector2Int.Distance(expectedFullMapPos, new Vector2Int(fullMapWidth / 2, fullMapHeight / 2)) < 10;
  161. Debug.Log($"Is near map center: {(isNearCenter ? "✅ YES" : "❌ NO")}");
  162. Debug.Log("===============================");
  163. }
  164. /// <summary>
  165. /// DEBUG: Test positioning after camera and tile fixes
  166. /// </summary>
  167. public void TestPositioningAfterFix()
  168. {
  169. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  170. if (teamPlacement == null)
  171. {
  172. Debug.LogError("❌ SimpleTeamPlacement not found!");
  173. return;
  174. }
  175. GameObject teamMarker = GameObject.Find("TeamMarker");
  176. if (teamMarker == null)
  177. {
  178. Debug.LogError("❌ TeamMarker not found!");
  179. return;
  180. }
  181. Vector2Int storedPos = teamPlacement.GetCurrentTeamPosition();
  182. Rect exploredBounds = GetExploredBounds();
  183. Vector3 teamMarkerWorldPos = teamMarker.transform.position;
  184. Debug.Log("=== POSITIONING TEST AFTER FIX ===");
  185. Debug.Log($"📍 Team Marker World Position: ({teamMarkerWorldPos.x:F1}, {teamMarkerWorldPos.y:F1})");
  186. Debug.Log($"🗂️ Stored Team Position: ({storedPos.x}, {storedPos.y})");
  187. Debug.Log($"🗺️ Explored Bounds: {exploredBounds}");
  188. // Calculate expected world position for the center tile
  189. Vector3 expectedCenterTileWorldPos = new Vector3(
  190. (exploredBounds.x + exploredBounds.width / 2f),
  191. (exploredBounds.y + exploredBounds.height / 2f),
  192. 0
  193. );
  194. Debug.Log($"🎯 Expected Center Tile World Position: ({expectedCenterTileWorldPos.x:F1}, {expectedCenterTileWorldPos.y:F1})");
  195. // Check camera position
  196. Camera mainCam = Camera.main;
  197. if (mainCam != null)
  198. {
  199. Debug.Log($"📹 Camera Position: ({mainCam.transform.position.x:F1}, {mainCam.transform.position.y:F1})");
  200. Debug.Log($"📹 Camera Size: {mainCam.orthographicSize:F1}");
  201. }
  202. // Check if team marker should be visible
  203. float distanceFromExpectedCenter = Vector3.Distance(teamMarkerWorldPos, expectedCenterTileWorldPos);
  204. Debug.Log($"📏 Distance from expected center: {distanceFromExpectedCenter:F1}");
  205. bool shouldBeVisible = distanceFromExpectedCenter < 40f; // Within 40 units of center
  206. Debug.Log($"👁️ Should be visible: {(shouldBeVisible ? "✅ YES" : "❌ NO")}");
  207. Debug.Log("===================================");
  208. }
  209. /// <summary>
  210. /// DEBUG: Test full map tile coverage to find missing quadrants
  211. /// </summary>
  212. public void TestFullMapTileCoverage()
  213. {
  214. Debug.Log("=== FULL MAP TILE COVERAGE TEST ===");
  215. // Get current map data from MapMaker2
  216. var currentMapData = mapMaker?.GetMapData();
  217. if (currentMapData == null)
  218. {
  219. Debug.LogError("❌ No current map data available!");
  220. return;
  221. }
  222. Debug.Log($"📊 Current Map Data Size: {currentMapData.Width}x{currentMapData.Height}");
  223. // Check if we're showing full map
  224. Rect exploredBounds = GetExploredBounds();
  225. bool isShowingFullMap = exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5;
  226. Debug.Log($"🗺️ Is Showing Full Map: {isShowingFullMap}");
  227. Debug.Log($"🗺️ Explored Bounds: {exploredBounds}");
  228. Debug.Log($"🗺️ Full Map Size: {fullMapWidth}x{fullMapHeight}");
  229. if (isShowingFullMap)
  230. {
  231. // Test if we have tiles in each quadrant
  232. int centerX = currentMapData.Width / 2;
  233. int centerY = currentMapData.Height / 2;
  234. // Test each quadrant
  235. Vector2Int[] testPoints = {
  236. new Vector2Int(centerX / 2, centerY / 2), // Lower-left
  237. new Vector2Int(centerX + centerX / 2, centerY / 2), // Lower-right
  238. new Vector2Int(centerX / 2, centerY + centerY / 2), // Upper-left
  239. new Vector2Int(centerX + centerX / 2, centerY + centerY / 2) // Upper-right
  240. };
  241. string[] quadrantNames = { "Lower-Left", "Lower-Right", "Upper-Left", "Upper-Right" };
  242. for (int i = 0; i < testPoints.Length; i++)
  243. {
  244. var testPoint = testPoints[i];
  245. var tile = currentMapData.GetTile(testPoint.x, testPoint.y);
  246. bool hasValidTile = tile != null;
  247. Debug.Log($"🔍 {quadrantNames[i]} Quadrant ({testPoint.x}, {testPoint.y}): {(hasValidTile ? "✅ HAS TILE" : "❌ NO TILE")}");
  248. if (hasValidTile)
  249. {
  250. Debug.Log($" Terrain: {tile.terrainType}, Feature: {tile.featureType}");
  251. }
  252. }
  253. // Test specific areas that might be missing
  254. Debug.Log("🔍 Testing specific problematic areas:");
  255. Vector2Int[] problematicPoints = {
  256. new Vector2Int(0, 0), // Bottom-left corner
  257. new Vector2Int(currentMapData.Width-1, 0), // Bottom-right corner
  258. new Vector2Int(0, currentMapData.Height-1), // Top-left corner
  259. new Vector2Int(currentMapData.Width-1, currentMapData.Height-1) // Top-right corner
  260. };
  261. for (int i = 0; i < problematicPoints.Length; i++)
  262. {
  263. var testPoint = problematicPoints[i];
  264. var tile = currentMapData.GetTile(testPoint.x, testPoint.y);
  265. bool hasValidTile = tile != null;
  266. Debug.Log($"🔍 Corner ({testPoint.x}, {testPoint.y}): {(hasValidTile ? "✅ HAS TILE" : "❌ NO TILE")}");
  267. }
  268. }
  269. else
  270. {
  271. Debug.Log("⚠️ Not currently showing full map - switch to full map first to test coverage");
  272. }
  273. Debug.Log("=====================================");
  274. }
  275. /// <summary>
  276. /// DEBUG: Test visual tile rendering to find missing areas
  277. /// </summary>
  278. public void TestVisualTileRendering()
  279. {
  280. Debug.Log("=== VISUAL TILE RENDERING TEST ===");
  281. // Check if we're showing full map
  282. Rect exploredBounds = GetExploredBounds();
  283. bool isShowingFullMap = exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5;
  284. Debug.Log($"🗺️ Is Showing Full Map: {isShowingFullMap}");
  285. Debug.Log($"🗺️ Explored Bounds: {exploredBounds}");
  286. if (isShowingFullMap)
  287. {
  288. // Check for actual GameObjects in each quadrant
  289. string[] quadrantNames = { "Lower-Left", "Lower-Right", "Upper-Left", "Upper-Right" };
  290. Vector2Int[] testPoints = {
  291. new Vector2Int(75, 75), // Lower-left
  292. new Vector2Int(225, 75), // Lower-right
  293. new Vector2Int(75, 225), // Upper-left
  294. new Vector2Int(225, 225) // Upper-right
  295. };
  296. for (int i = 0; i < testPoints.Length; i++)
  297. {
  298. var testPoint = testPoints[i];
  299. // Look for actual tile GameObjects at these world positions
  300. string terrainTileName = $"TerrainTile_{testPoint.x}_{testPoint.y}";
  301. GameObject terrainTile = GameObject.Find(terrainTileName);
  302. string featureTileName = $"FeatureTile_{testPoint.x}_{testPoint.y}";
  303. GameObject featureTile = GameObject.Find(featureTileName);
  304. Debug.Log($"🎮 {quadrantNames[i]} Quadrant ({testPoint.x}, {testPoint.y}):");
  305. Debug.Log($" Terrain GameObject: {(terrainTile != null ? "✅ EXISTS" : "❌ MISSING")}");
  306. Debug.Log($" Feature GameObject: {(featureTile != null ? "✅ EXISTS" : "❌ MISSING")}");
  307. if (terrainTile != null)
  308. {
  309. Debug.Log($" Terrain Position: ({terrainTile.transform.position.x:F1}, {terrainTile.transform.position.y:F1})");
  310. Debug.Log($" Terrain Active: {terrainTile.activeInHierarchy}");
  311. }
  312. }
  313. // Check corner tiles specifically
  314. Debug.Log("🔍 Testing corner tile GameObjects:");
  315. Vector2Int[] corners = {
  316. new Vector2Int(0, 0), // Bottom-left corner
  317. new Vector2Int(299, 0), // Bottom-right corner
  318. new Vector2Int(0, 299), // Top-left corner
  319. new Vector2Int(299, 299) // Top-right corner
  320. };
  321. string[] cornerNames = { "Bottom-Left", "Bottom-Right", "Top-Left", "Top-Right" };
  322. for (int i = 0; i < corners.Length; i++)
  323. {
  324. var corner = corners[i];
  325. string tileName = $"TerrainTile_{corner.x}_{corner.y}";
  326. GameObject tile = GameObject.Find(tileName);
  327. Debug.Log($"🎮 {cornerNames[i]} Corner ({corner.x}, {corner.y}): {(tile != null ? "✅ EXISTS" : "❌ MISSING")}");
  328. if (tile != null)
  329. {
  330. Debug.Log($" Position: ({tile.transform.position.x:F1}, {tile.transform.position.y:F1})");
  331. Debug.Log($" Active: {tile.activeInHierarchy}");
  332. }
  333. }
  334. // Count total rendered tiles
  335. var allObjects = Object.FindObjectsByType<GameObject>(FindObjectsSortMode.None);
  336. var terrainTiles = allObjects.Where(go => go.name.StartsWith("TerrainTile_")).ToArray();
  337. Debug.Log($"📊 Total rendered terrain tiles: {terrainTiles.Length}");
  338. Debug.Log($"📊 Expected tiles for 300x300: {300 * 300}");
  339. if (terrainTiles.Length < 300 * 300)
  340. {
  341. Debug.Log($"⚠️ Missing {(300 * 300) - terrainTiles.Length} tiles!");
  342. // Sample some missing tile names
  343. for (int y = 0; y < 10; y++)
  344. {
  345. for (int x = 0; x < 10; x++)
  346. {
  347. string tileName = $"TerrainTile_{x}_{y}";
  348. if (GameObject.Find(tileName) == null)
  349. {
  350. Debug.Log($" Missing: {tileName}");
  351. }
  352. }
  353. }
  354. }
  355. }
  356. else
  357. {
  358. Debug.Log("⚠️ Not currently showing full map - switch to full map first");
  359. }
  360. Debug.Log("===================================");
  361. }
  362. /// <summary>
  363. /// Restore team marker to correct position in full map coordinates
  364. /// </summary>
  365. private void RestoreTeamMarkerToFullMapPosition(Vector2Int fullMapPosition)
  366. {
  367. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  368. if (teamPlacement == null) return;
  369. // In full map mode, full map coordinates = visible coordinates
  370. Debug.Log($"🔧 Restoring team marker to full map position: ({fullMapPosition})");
  371. // Update team marker position
  372. teamPlacement.UpdateMarkerAfterMapChange(fullMapPosition);
  373. }
  374. /// <summary>
  375. /// Get current team marker position in full map coordinates (improved version)
  376. /// </summary>
  377. private Vector2Int GetCurrentTeamPositionInFullMapImproved()
  378. {
  379. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  380. if (teamPlacement == null) return new Vector2Int(fullMapWidth / 2, fullMapHeight / 2);
  381. // Instead of reading world position, use the stored position and convert it properly
  382. Vector2Int storedPosition = teamPlacement.GetCurrentTeamPosition();
  383. // Check if we're currently showing the full map or a partial exploration view
  384. Rect exploredBounds = GetExploredBounds();
  385. bool isShowingFullMap = exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5;
  386. Vector2Int fullMapPos;
  387. if (isShowingFullMap)
  388. {
  389. // If already showing full map, stored position = full map coordinates
  390. fullMapPos = storedPosition;
  391. Debug.Log($"🎯 Current team position (Full Map): Stored({storedPosition}) = FullMap({fullMapPos})");
  392. }
  393. else
  394. {
  395. // If showing partial exploration view, convert exploration coordinates to full map coordinates
  396. fullMapPos = new Vector2Int(
  397. (int)exploredBounds.x + storedPosition.x,
  398. (int)exploredBounds.y + storedPosition.y
  399. );
  400. Debug.Log($"🎯 Current team position (Exploration): Stored({storedPosition}) + Bounds({exploredBounds.x}, {exploredBounds.y}) → FullMap({fullMapPos})");
  401. Debug.Log($"🔍 DEBUG: ExploredBounds = {exploredBounds}, IsFullMap = {isShowingFullMap}");
  402. }
  403. return fullMapPos;
  404. }
  405. /// <summary>
  406. /// Restore team marker to correct position after map view changes
  407. /// </summary>
  408. private void RestoreTeamMarkerPosition(Vector2Int fullMapPosition)
  409. {
  410. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  411. if (teamPlacement == null) return;
  412. // Check if we're currently showing the full map
  413. Rect exploredBounds = GetExploredBounds();
  414. bool isShowingFullMap = exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5;
  415. Vector2Int visiblePosition;
  416. if (isShowingFullMap)
  417. {
  418. // If showing full map, full map coordinates = visible coordinates
  419. visiblePosition = fullMapPosition;
  420. Debug.Log($"🔧 Restored team marker (Full Map): FullMap({fullMapPosition}) → Visible({visiblePosition})");
  421. }
  422. else if (performanceMode)
  423. {
  424. // Account for LOD scaling in exploration mode
  425. visiblePosition = new Vector2Int(
  426. (fullMapPosition.x - (int)exploredBounds.x) / lodLevel,
  427. (fullMapPosition.y - (int)exploredBounds.y) / lodLevel
  428. );
  429. int visibleWidth = (int)exploredBounds.width / lodLevel;
  430. int visibleHeight = (int)exploredBounds.height / lodLevel;
  431. visiblePosition.x = Mathf.Clamp(visiblePosition.x, 0, visibleWidth - 1);
  432. visiblePosition.y = Mathf.Clamp(visiblePosition.y, 0, visibleHeight - 1);
  433. Debug.Log($"🔧 Restored team marker (Exploration LOD): FullMap({fullMapPosition}) → Visible({visiblePosition}) [LOD:{lodLevel}]");
  434. }
  435. else
  436. {
  437. // Standard coordinate conversion for exploration mode
  438. visiblePosition = new Vector2Int(
  439. fullMapPosition.x - (int)exploredBounds.x,
  440. fullMapPosition.y - (int)exploredBounds.y
  441. );
  442. int visibleWidth = (int)exploredBounds.width;
  443. int visibleHeight = (int)exploredBounds.height;
  444. visiblePosition.x = Mathf.Clamp(visiblePosition.x, 0, visibleWidth - 1);
  445. visiblePosition.y = Mathf.Clamp(visiblePosition.y, 0, visibleHeight - 1);
  446. Debug.Log($"🔧 Restored team marker (Exploration): FullMap({fullMapPosition}) → Visible({visiblePosition})");
  447. }
  448. // Update team marker position
  449. teamPlacement.UpdateMarkerAfterMapChange(visiblePosition);
  450. }
  451. /// <summary>
  452. /// Toggle performance mode for testing different map sizes
  453. /// </summary>
  454. public void TogglePerformanceMode()
  455. {
  456. performanceMode = !performanceMode;
  457. if (performanceMode)
  458. {
  459. lodLevel = CalculateOptimalLOD(fullMapWidth, fullMapHeight);
  460. Debug.Log($"⚡ Performance mode enabled with LOD {lodLevel}");
  461. }
  462. else
  463. {
  464. lodLevel = 1;
  465. Debug.Log("🎯 Performance mode disabled - using full detail");
  466. }
  467. // Refresh the visible map
  468. UpdateVisibleMap();
  469. }
  470. /// <summary>
  471. /// Set custom map size for scalability testing
  472. /// </summary>
  473. public void SetMapSize(int width, int height)
  474. {
  475. fullMapWidth = width;
  476. fullMapHeight = height;
  477. // Auto-enable performance mode for large maps
  478. if (width > 200 || height > 200)
  479. {
  480. performanceMode = true;
  481. lodLevel = CalculateOptimalLOD(width, height);
  482. Debug.Log($"⚡ Auto-enabled performance mode for {width}x{height} map (LOD {lodLevel})");
  483. }
  484. else
  485. {
  486. performanceMode = false;
  487. lodLevel = 1;
  488. Debug.Log($"🎯 Standard mode for {width}x{height} map");
  489. }
  490. }
  491. /// <summary>
  492. /// Convert world coordinates to exploration-system-aware tile coordinates
  493. /// This handles the coordinate system differences between journey and exploration systems
  494. /// </summary>
  495. public Vector2Int ConvertWorldToExplorationCoordinates(Vector3 worldPosition)
  496. {
  497. // Convert world position to base tile coordinates
  498. Vector2Int baseTilePos = new Vector2Int(
  499. Mathf.RoundToInt(worldPosition.x),
  500. Mathf.RoundToInt(worldPosition.y)
  501. );
  502. // Get current exploration bounds to determine coordinate system
  503. Rect exploredBounds = GetExploredBounds();
  504. // Check if we're showing the full map (when bounds = full map size)
  505. bool isShowingFullMap = (exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5);
  506. if (isShowingFullMap)
  507. {
  508. // When showing full map, world coordinates ARE the exploration coordinates
  509. // Just apply LOD scaling if needed
  510. Vector2Int explorationCoords = baseTilePos;
  511. if (performanceMode && lodLevel > 1)
  512. {
  513. explorationCoords.x = explorationCoords.x / lodLevel;
  514. explorationCoords.y = explorationCoords.y / lodLevel;
  515. // Clamp to LOD-scaled bounds
  516. int lodWidth = (int)exploredBounds.width / lodLevel;
  517. int lodHeight = (int)exploredBounds.height / lodLevel;
  518. explorationCoords.x = Mathf.Clamp(explorationCoords.x, 0, lodWidth - 1);
  519. explorationCoords.y = Mathf.Clamp(explorationCoords.y, 0, lodHeight - 1);
  520. }
  521. else
  522. {
  523. // Clamp to full map bounds
  524. explorationCoords.x = Mathf.Clamp(explorationCoords.x, 0, (int)exploredBounds.width - 1);
  525. explorationCoords.y = Mathf.Clamp(explorationCoords.y, 0, (int)exploredBounds.height - 1);
  526. }
  527. Debug.Log($"🌍 FULL MAP: World({worldPosition.x:F1}, {worldPosition.y:F1}) → Exploration({explorationCoords}) [LOD:{lodLevel}]");
  528. return explorationCoords;
  529. }
  530. else
  531. {
  532. // Normal exploration mode - convert to relative coordinates within visible area
  533. Vector2Int explorationCoords = new Vector2Int(
  534. baseTilePos.x - (int)exploredBounds.x,
  535. baseTilePos.y - (int)exploredBounds.y
  536. );
  537. if (performanceMode && lodLevel > 1)
  538. {
  539. // Apply LOD scaling
  540. explorationCoords.x = explorationCoords.x / lodLevel;
  541. explorationCoords.y = explorationCoords.y / lodLevel;
  542. int visibleWidth = (int)exploredBounds.width / lodLevel;
  543. int visibleHeight = (int)exploredBounds.height / lodLevel;
  544. explorationCoords.x = Mathf.Clamp(explorationCoords.x, 0, visibleWidth - 1);
  545. explorationCoords.y = Mathf.Clamp(explorationCoords.y, 0, visibleHeight - 1);
  546. }
  547. else
  548. {
  549. explorationCoords.x = Mathf.Clamp(explorationCoords.x, 0, (int)exploredBounds.width - 1);
  550. explorationCoords.y = Mathf.Clamp(explorationCoords.y, 0, (int)exploredBounds.height - 1);
  551. }
  552. Debug.Log($"🔄 PARTIAL: World({worldPosition.x:F1}, {worldPosition.y:F1}) → Base({baseTilePos}) → Exploration({explorationCoords}) [LOD:{lodLevel}]");
  553. return explorationCoords;
  554. }
  555. }
  556. /// <summary>
  557. /// Convert exploration coordinates back to world coordinates
  558. /// </summary>
  559. public Vector3 ConvertExplorationToWorldCoordinates(Vector2Int explorationCoords)
  560. {
  561. Rect exploredBounds = GetExploredBounds();
  562. // Check if we're showing the full map
  563. bool isShowingFullMap = (exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5);
  564. Vector2Int fullMapCoords;
  565. if (isShowingFullMap)
  566. {
  567. // When showing full map, exploration coordinates ARE the world coordinates (with LOD scaling)
  568. if (performanceMode && lodLevel > 1)
  569. {
  570. // Scale up from LOD coordinates
  571. fullMapCoords = new Vector2Int(
  572. explorationCoords.x * lodLevel,
  573. explorationCoords.y * lodLevel
  574. );
  575. }
  576. else
  577. {
  578. // Direct mapping
  579. fullMapCoords = explorationCoords;
  580. }
  581. }
  582. else
  583. {
  584. // Normal exploration mode - add bounds offset
  585. if (performanceMode && lodLevel > 1)
  586. {
  587. // Scale up coordinates and add bounds offset
  588. fullMapCoords = new Vector2Int(
  589. (explorationCoords.x * lodLevel) + (int)exploredBounds.x,
  590. (explorationCoords.y * lodLevel) + (int)exploredBounds.y
  591. );
  592. }
  593. else
  594. {
  595. // Direct conversion with bounds offset
  596. fullMapCoords = new Vector2Int(
  597. explorationCoords.x + (int)exploredBounds.x,
  598. explorationCoords.y + (int)exploredBounds.y
  599. );
  600. }
  601. }
  602. Vector3 worldPos = new Vector3(fullMapCoords.x, fullMapCoords.y, 0);
  603. Debug.Log($"🔄 Exploration({explorationCoords}) → FullMap({fullMapCoords}) → World({worldPos.x:F1}, {worldPos.y:F1}) [LOD:{lodLevel}, FullMap:{isShowingFullMap}]");
  604. return worldPos;
  605. }
  606. /// <summary>
  607. /// Synchronize team marker position between coordinate systems
  608. /// Call this method when switching between journey and exploration systems
  609. /// </summary>
  610. public void SynchronizeTeamMarkerPosition()
  611. {
  612. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  613. if (teamPlacement == null) return;
  614. GameObject teamMarker = GameObject.Find("TeamMarker");
  615. if (teamMarker == null) return;
  616. // Get current world position from the marker
  617. Vector3 currentWorldPos = teamMarker.transform.position;
  618. // Convert to proper exploration coordinates
  619. Vector2Int correctExplorationCoords = ConvertWorldToExplorationCoordinates(currentWorldPos);
  620. // Get the current stored position in team placement
  621. Vector2Int storedPosition = teamPlacement.GetCurrentTeamPosition();
  622. // Check if there's a mismatch that needs fixing
  623. if (storedPosition != correctExplorationCoords)
  624. {
  625. Debug.Log($"🔧 Coordinate mismatch detected: Stored({storedPosition}) ≠ Calculated({correctExplorationCoords})");
  626. Debug.Log($" World position: ({currentWorldPos.x:F1}, {currentWorldPos.y:F1})");
  627. // Fix the mismatch by updating the stored position to match the world position
  628. // This preserves the actual visual location of the marker
  629. teamPlacement.UpdateMarkerAfterMapChange(correctExplorationCoords);
  630. Debug.Log($"✅ Fixed coordinate mismatch: Updated stored position to ({correctExplorationCoords})");
  631. }
  632. else
  633. {
  634. Debug.Log($"✅ Coordinates are synchronized: World({currentWorldPos.x:F1}, {currentWorldPos.y:F1}) → Exploration({correctExplorationCoords})");
  635. }
  636. }
  637. /// <summary>
  638. /// Debug method to show current coordinate state
  639. /// </summary>
  640. public void DebugCoordinateState()
  641. {
  642. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  643. if (teamPlacement == null) return;
  644. GameObject teamMarker = GameObject.Find("TeamMarker");
  645. if (teamMarker == null) return;
  646. Vector3 worldPos = teamMarker.transform.position;
  647. Vector2Int storedPos = teamPlacement.GetCurrentTeamPosition();
  648. Vector2Int calculatedPos = ConvertWorldToExplorationCoordinates(worldPos);
  649. Rect exploredBounds = GetExploredBounds();
  650. bool isShowingFullMap = (exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5);
  651. Debug.Log("=== COORDINATE DEBUG ===");
  652. Debug.Log($"World Position: ({worldPos.x:F1}, {worldPos.y:F1})");
  653. Debug.Log($"Stored Position: ({storedPos.x}, {storedPos.y})");
  654. Debug.Log($"Calculated Position: ({calculatedPos.x}, {calculatedPos.y})");
  655. Debug.Log($"Explored Bounds: {exploredBounds}");
  656. Debug.Log($"Showing Full Map: {isShowingFullMap}");
  657. Debug.Log($"Performance Mode: {performanceMode}, LOD Level: {lodLevel}");
  658. Debug.Log($"Coordinate Match: {(storedPos == calculatedPos ? "✅ YES" : "❌ NO")}");
  659. if (storedPos != calculatedPos)
  660. {
  661. Debug.Log($"🔧 MISMATCH DETECTED! Use 'FIX: Force Coordinate Sync' to fix this.");
  662. }
  663. Debug.Log("========================");
  664. }
  665. /// <summary>
  666. /// Force coordinate synchronization regardless of current state
  667. /// </summary>
  668. public void ForceCoordinateSync()
  669. {
  670. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  671. if (teamPlacement == null) return;
  672. GameObject teamMarker = GameObject.Find("TeamMarker");
  673. if (teamMarker == null) return;
  674. // Get current world position from the marker
  675. Vector3 currentWorldPos = teamMarker.transform.position;
  676. // Convert to proper exploration coordinates
  677. Vector2Int correctExplorationCoords = ConvertWorldToExplorationCoordinates(currentWorldPos);
  678. // Force update the stored position
  679. teamPlacement.UpdateMarkerAfterMapChange(correctExplorationCoords);
  680. Debug.Log($"🔧 FORCED coordinate sync: World({currentWorldPos.x:F1}, {currentWorldPos.y:F1}) → Exploration({correctExplorationCoords})");
  681. Debug.Log($"✅ Team marker position forcibly synchronized");
  682. }
  683. /// <summary>
  684. /// Reset team marker to the center of the map (useful when coordinates get messed up)
  685. /// </summary>
  686. public void ResetTeamMarkerToMapCenter()
  687. {
  688. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  689. if (teamPlacement == null) return;
  690. // Calculate the center of the full map
  691. int centerX = fullMapWidth / 2;
  692. int centerY = fullMapHeight / 2;
  693. Vector2Int mapCenter = new Vector2Int(centerX, centerY);
  694. // Check if we're currently showing the full map
  695. Rect exploredBounds = GetExploredBounds();
  696. bool isShowingFullMap = exploredBounds.width >= fullMapWidth - 5 && exploredBounds.height >= fullMapHeight - 5;
  697. Vector2Int targetPosition;
  698. if (isShowingFullMap)
  699. {
  700. // In full map mode, place at actual center coordinates
  701. targetPosition = mapCenter;
  702. Debug.Log($"🎯 Resetting team marker to full map center: ({targetPosition})");
  703. }
  704. else
  705. {
  706. // In exploration mode, place at center of visible area
  707. targetPosition = new Vector2Int(
  708. centerX - (int)exploredBounds.x,
  709. centerY - (int)exploredBounds.y
  710. );
  711. Debug.Log($"🎯 Resetting team marker to exploration center: Full({mapCenter}) → Visible({targetPosition})");
  712. }
  713. // Update team marker position
  714. teamPlacement.UpdateMarkerAfterMapChange(targetPosition);
  715. Debug.Log($"✅ Team marker reset to map center");
  716. } /// <summary>
  717. /// Generate the complete large map using existing MapMaker2 systems
  718. /// </summary>
  719. private void GenerateFullMap()
  720. {
  721. Debug.Log($"🌍 Generating full map of size {fullMapWidth}x{fullMapHeight}...");
  722. // Temporarily modify MapMaker2 settings for large map generation
  723. int originalWidth = mapMaker.mapWidth;
  724. int originalHeight = mapMaker.mapHeight;
  725. int originalSeed = mapMaker.seed;
  726. mapMaker.mapWidth = fullMapWidth;
  727. mapMaker.mapHeight = fullMapHeight;
  728. // Use the same seed for consistency, but initialize it properly
  729. Random.InitState(originalSeed);
  730. UnityEngine.Random.InitState(originalSeed);
  731. // Generate the full map
  732. fullMapData = new MapData(fullMapWidth, fullMapHeight);
  733. Debug.Log($"📊 Created MapData with size: {fullMapData.Width}x{fullMapData.Height}");
  734. // Generate the complete map using MapMaker2's generation system
  735. mapMaker.GenerateCompleteMap(fullMapData);
  736. // Ensure roads and features connect properly across the entire map
  737. PostProcessMapConnectivity();
  738. // Restore original settings
  739. mapMaker.mapWidth = originalWidth;
  740. mapMaker.mapHeight = originalHeight;
  741. // Debug settlement count in full map
  742. int townCount = fullMapData.GetTowns().Count;
  743. int villageCount = fullMapData.GetVillages().Count;
  744. Debug.Log($"🏘️ Generated full map with {townCount} towns and {villageCount} villages");
  745. Debug.Log("✅ Full map generation complete");
  746. }
  747. /// <summary>
  748. /// Post-process the map to ensure better connectivity of roads and features
  749. /// </summary>
  750. private void PostProcessMapConnectivity()
  751. {
  752. Debug.Log("🔧 Post-processing map for better connectivity...");
  753. // Ensure all settlements are connected by roads
  754. ConnectSettlementsWithRoads();
  755. // Smooth terrain transitions to prevent harsh breaks
  756. SmoothTerrainTransitions();
  757. // Validate water bodies and rivers for continuity
  758. ValidateWaterFeatures();
  759. Debug.Log("✅ Map connectivity post-processing complete");
  760. }
  761. /// <summary>
  762. /// Connect all settlements with roads for better navigation
  763. /// </summary>
  764. private void ConnectSettlementsWithRoads()
  765. {
  766. var allSettlements = new List<Settlement>();
  767. allSettlements.AddRange(fullMapData.GetTowns());
  768. allSettlements.AddRange(fullMapData.GetVillages());
  769. Debug.Log($"🛣️ Connecting {allSettlements.Count} settlements with roads...");
  770. for (int i = 0; i < allSettlements.Count - 1; i++)
  771. {
  772. for (int j = i + 1; j < allSettlements.Count; j++)
  773. {
  774. Settlement from = allSettlements[i];
  775. Settlement to = allSettlements[j];
  776. // Only connect nearby settlements (within reasonable distance)
  777. float distance = Vector2.Distance(from.position, to.position);
  778. if (distance < 50f) // Adjust distance threshold as needed
  779. {
  780. CreateRoadBetweenPoints(from.position, to.position);
  781. }
  782. }
  783. }
  784. }
  785. /// <summary>
  786. /// Create a road between two points
  787. /// </summary>
  788. private void CreateRoadBetweenPoints(Vector2Int start, Vector2Int end)
  789. {
  790. // Simple pathfinding - create L-shaped or direct path
  791. List<Vector2Int> roadPath = new List<Vector2Int>();
  792. // Direct line approach (can be enhanced with A* pathfinding later)
  793. Vector2Int current = start;
  794. Vector2Int diff = end - start;
  795. // Horizontal movement first
  796. int stepX = diff.x > 0 ? 1 : -1;
  797. while (current.x != end.x)
  798. {
  799. roadPath.Add(current);
  800. current.x += stepX;
  801. }
  802. // Vertical movement
  803. int stepY = diff.y > 0 ? 1 : -1;
  804. while (current.y != end.y)
  805. {
  806. roadPath.Add(current);
  807. current.y += stepY;
  808. }
  809. roadPath.Add(end);
  810. // Place road tiles
  811. foreach (var point in roadPath)
  812. {
  813. if (IsValidExplorationPosition(point.x, point.y))
  814. {
  815. MapTile tile = fullMapData.GetTile(point.x, point.y);
  816. if (tile != null && !tile.IsWater())
  817. {
  818. // Set road feature (assuming FeatureType.Road exists)
  819. tile.featureType = FeatureType.Road;
  820. }
  821. }
  822. }
  823. }
  824. /// <summary>
  825. /// Smooth terrain transitions to prevent harsh breaks
  826. /// </summary>
  827. private void SmoothTerrainTransitions()
  828. {
  829. Debug.Log("🌄 Smoothing terrain transitions...");
  830. // Apply smoothing filter to height values
  831. for (int x = 1; x < fullMapWidth - 1; x++)
  832. {
  833. for (int y = 1; y < fullMapHeight - 1; y++)
  834. {
  835. MapTile center = fullMapData.GetTile(x, y);
  836. if (center == null) continue;
  837. // Get surrounding tiles
  838. List<MapTile> neighbors = new List<MapTile>();
  839. for (int dx = -1; dx <= 1; dx++)
  840. {
  841. for (int dy = -1; dy <= 1; dy++)
  842. {
  843. if (dx == 0 && dy == 0) continue;
  844. MapTile neighbor = fullMapData.GetTile(x + dx, y + dy);
  845. if (neighbor != null) neighbors.Add(neighbor);
  846. }
  847. }
  848. if (neighbors.Count > 0)
  849. {
  850. // Average the height with neighbors for smoother transitions
  851. float avgHeight = neighbors.Average(t => t.height);
  852. center.height = (center.height + avgHeight) * 0.5f;
  853. }
  854. }
  855. }
  856. }
  857. /// <summary>
  858. /// Validate and fix water features for better continuity
  859. /// </summary>
  860. private void ValidateWaterFeatures()
  861. {
  862. Debug.Log("🌊 Validating water features...");
  863. // Ensure water tiles form connected bodies
  864. // This is a simplified version - can be enhanced for better water flow
  865. for (int x = 0; x < fullMapWidth; x++)
  866. {
  867. for (int y = 0; y < fullMapHeight; y++)
  868. {
  869. MapTile tile = fullMapData.GetTile(x, y);
  870. if (tile != null && tile.IsWater())
  871. {
  872. // Ensure isolated water tiles have proper connections
  873. EnsureWaterConnection(x, y);
  874. }
  875. }
  876. }
  877. }
  878. /// <summary>
  879. /// Ensure water tile has proper connections to other water
  880. /// </summary>
  881. private void EnsureWaterConnection(int x, int y)
  882. {
  883. // Count water neighbors
  884. int waterNeighbors = 0;
  885. for (int dx = -1; dx <= 1; dx++)
  886. {
  887. for (int dy = -1; dy <= 1; dy++)
  888. {
  889. if (dx == 0 && dy == 0) continue;
  890. if (IsValidExplorationPosition(x + dx, y + dy))
  891. {
  892. MapTile neighbor = fullMapData.GetTile(x + dx, y + dy);
  893. if (neighbor != null && neighbor.IsWater())
  894. {
  895. waterNeighbors++;
  896. }
  897. }
  898. }
  899. }
  900. // If isolated water tile, convert to land or extend water
  901. if (waterNeighbors == 0)
  902. {
  903. MapTile tile = fullMapData.GetTile(x, y);
  904. if (tile != null)
  905. {
  906. // Convert single water tiles to plains to prevent isolated lakes
  907. tile.terrainType = TerrainType.Plains;
  908. }
  909. }
  910. }
  911. /// <summary>
  912. /// Reveal the initial area around the map center
  913. /// </summary>
  914. private void RevealInitialArea()
  915. {
  916. int centerX = fullMapWidth / 2;
  917. int centerY = fullMapHeight / 2;
  918. int halfSize = initialVisibleSize / 2;
  919. for (int x = centerX - halfSize; x < centerX + halfSize; x++)
  920. {
  921. for (int y = centerY - halfSize; y < centerY + halfSize; y++)
  922. {
  923. if (IsValidExplorationPosition(x, y))
  924. {
  925. exploredMask[x, y] = true;
  926. }
  927. }
  928. }
  929. // Ensure we have at least one settlement in the initial area
  930. EnsureSettlementsInInitialArea(centerX, centerY, halfSize);
  931. Debug.Log($"🔍 Initial area revealed: {initialVisibleSize}x{initialVisibleSize} around center ({centerX}, {centerY})");
  932. }
  933. /// <summary>
  934. /// Get the correct initial team position in visible map coordinates
  935. /// </summary>
  936. public Vector2Int GetInitialTeamPosition()
  937. {
  938. // The team should be placed at the center of the visible map
  939. // Since we start with the full map center revealed, and the visible map shows just the explored area
  940. Rect exploredBounds = GetExploredBounds();
  941. int centerX = fullMapWidth / 2;
  942. int centerY = fullMapHeight / 2;
  943. // Convert full map center to visible map coordinates
  944. Vector2Int visiblePosition = new Vector2Int(
  945. centerX - (int)exploredBounds.x,
  946. centerY - (int)exploredBounds.y
  947. );
  948. Debug.Log($"🎯 Initial team position: Full({centerX}, {centerY}) → Visible({visiblePosition.x}, {visiblePosition.y})");
  949. return visiblePosition;
  950. }
  951. /// <summary>
  952. /// Ensure there are settlements in the initial revealed area
  953. /// </summary>
  954. private void EnsureSettlementsInInitialArea(int centerX, int centerY, int halfSize)
  955. {
  956. // Check if there are already settlements in the initial area
  957. bool hasSettlements = false;
  958. var allSettlements = new List<Settlement>();
  959. allSettlements.AddRange(fullMapData.GetTowns());
  960. allSettlements.AddRange(fullMapData.GetVillages());
  961. foreach (var settlement in allSettlements)
  962. {
  963. if (settlement.position.x >= centerX - halfSize && settlement.position.x < centerX + halfSize &&
  964. settlement.position.y >= centerY - halfSize && settlement.position.y < centerY + halfSize)
  965. {
  966. hasSettlements = true;
  967. break;
  968. }
  969. }
  970. // If no settlements in initial area, place one near the center
  971. if (!hasSettlements)
  972. {
  973. Debug.Log("⚠️ No settlements in initial area, creating one...");
  974. // Find a good spot for a settlement near the center
  975. Vector2Int settlementPos = FindSuitableSettlementPosition(centerX, centerY, halfSize - 5);
  976. if (settlementPos != Vector2Int.zero)
  977. {
  978. Settlement newSettlement = new Settlement("Starting Village", SettlementType.Village, settlementPos);
  979. fullMapData.AddSettlement(newSettlement);
  980. Debug.Log($"🏘️ Created starting settlement at ({settlementPos.x}, {settlementPos.y})");
  981. }
  982. }
  983. }
  984. /// <summary>
  985. /// Find a suitable position for a settlement within the given area
  986. /// </summary>
  987. private Vector2Int FindSuitableSettlementPosition(int centerX, int centerY, int searchRadius)
  988. {
  989. for (int radius = 5; radius <= searchRadius; radius += 5)
  990. {
  991. for (int angle = 0; angle < 360; angle += 45)
  992. {
  993. float radians = angle * Mathf.Deg2Rad;
  994. int x = centerX + (int)(radius * Mathf.Cos(radians));
  995. int y = centerY + (int)(radius * Mathf.Sin(radians));
  996. if (IsValidExplorationPosition(x, y))
  997. {
  998. MapTile tile = fullMapData.GetTile(x, y);
  999. if (tile != null && !tile.IsWater())
  1000. {
  1001. return new Vector2Int(x, y);
  1002. }
  1003. }
  1004. }
  1005. }
  1006. // If no suitable position found, use center as fallback
  1007. return new Vector2Int(centerX, centerY);
  1008. }
  1009. /// <summary>
  1010. /// Check if exploration should be triggered based on player position
  1011. /// </summary>
  1012. public bool ShouldExplore(Vector2 playerPosition, MapData currentMapData)
  1013. {
  1014. // Check cooldown first
  1015. if (Time.time - lastExplorationTime < explorationCooldown)
  1016. {
  1017. return false;
  1018. }
  1019. // Convert player position to full map coordinates
  1020. Vector2Int playerFullMapPos = GetPlayerPositionInFullMap(playerPosition, currentMapData);
  1021. // Check distance to unexplored edges
  1022. bool shouldExplore = IsNearUnexploredEdge(playerFullMapPos);
  1023. if (shouldExplore)
  1024. {
  1025. Debug.Log($"✅ Exploration triggered! Player at {playerFullMapPos} near unexplored edge");
  1026. }
  1027. return shouldExplore;
  1028. }
  1029. /// <summary>
  1030. /// Reveal new areas around the player position
  1031. /// </summary>
  1032. public void ExploreNewAreas(Vector2 playerPosition, MapData currentMapData)
  1033. {
  1034. // Set exploration cooldown
  1035. lastExplorationTime = Time.time;
  1036. Vector2Int playerFullMapPos = GetPlayerPositionInFullMap(playerPosition, currentMapData);
  1037. Debug.Log($"🔍 Exploring new areas around player position {playerFullMapPos}");
  1038. // Determine which direction to explore
  1039. ExplorationDirection direction = GetExplorationDirection(playerFullMapPos);
  1040. // Reveal new chunk in that direction
  1041. RevealChunk(playerFullMapPos, direction);
  1042. // Update the visible map
  1043. UpdateVisibleMap();
  1044. }
  1045. /// <summary>
  1046. /// Convert player position from current map to full map coordinates
  1047. /// </summary>
  1048. private Vector2Int GetPlayerPositionInFullMap(Vector2 playerPosition, MapData currentMapData)
  1049. {
  1050. // Find the offset of the current visible area within the full map
  1051. Vector2Int visibleOffset = GetCurrentVisibleOffset(currentMapData);
  1052. // Ensure player position is valid within current map bounds
  1053. int clampedX = Mathf.Clamp((int)playerPosition.x, 0, currentMapData.Width - 1);
  1054. int clampedY = Mathf.Clamp((int)playerPosition.y, 0, currentMapData.Height - 1);
  1055. Vector2Int fullMapPos = new Vector2Int(
  1056. visibleOffset.x + clampedX,
  1057. visibleOffset.y + clampedY
  1058. );
  1059. return fullMapPos;
  1060. }
  1061. /// <summary>
  1062. /// Get the offset of the current visible area within the full map
  1063. /// </summary>
  1064. private Vector2Int GetCurrentVisibleOffset(MapData currentMapData)
  1065. {
  1066. // Find the bounds of the explored area
  1067. Rect exploredBounds = GetExploredBounds();
  1068. Vector2Int offset = new Vector2Int((int)exploredBounds.x, (int)exploredBounds.y);
  1069. return offset;
  1070. }
  1071. /// <summary>
  1072. /// Check if player is near an unexplored edge
  1073. /// </summary>
  1074. private bool IsNearUnexploredEdge(Vector2Int playerPos)
  1075. {
  1076. // Validate player position is within full map bounds
  1077. if (!IsValidExplorationPosition(playerPos.x, playerPos.y))
  1078. {
  1079. Debug.LogWarning($"⚠️ Player position {playerPos} is outside full map bounds!");
  1080. return false;
  1081. }
  1082. // First check: Is player near the edge of the currently explored area?
  1083. Rect exploredBounds = GetExploredBounds();
  1084. // Calculate distances to each edge of explored area
  1085. float distToLeft = playerPos.x - exploredBounds.x;
  1086. float distToRight = (exploredBounds.x + exploredBounds.width - 1) - playerPos.x;
  1087. float distToBottom = playerPos.y - exploredBounds.y;
  1088. float distToTop = (exploredBounds.y + exploredBounds.height - 1) - playerPos.y;
  1089. float minDistToExploredEdge = Mathf.Min(distToLeft, distToRight, distToBottom, distToTop);
  1090. if (minDistToExploredEdge <= explorationDistance)
  1091. {
  1092. Debug.Log($"🎯 Player at {playerPos} is {minDistToExploredEdge} tiles from explored edge - triggering exploration!");
  1093. return true;
  1094. }
  1095. // Second check: Look for unexplored areas within exploration distance
  1096. for (int dx = -(int)explorationDistance; dx <= explorationDistance; dx++)
  1097. {
  1098. for (int dy = -(int)explorationDistance; dy <= explorationDistance; dy++)
  1099. {
  1100. int checkX = playerPos.x + dx;
  1101. int checkY = playerPos.y + dy;
  1102. if (IsValidExplorationPosition(checkX, checkY) && !exploredMask[checkX, checkY])
  1103. {
  1104. Debug.Log($"🎯 Found unexplored area at ({checkX}, {checkY}) near player {playerPos}");
  1105. return true;
  1106. }
  1107. }
  1108. }
  1109. return false;
  1110. }
  1111. /// <summary>
  1112. /// Determine which direction to explore based on player position
  1113. /// </summary>
  1114. private ExplorationDirection GetExplorationDirection(Vector2Int playerPos)
  1115. {
  1116. // Find the closest unexplored edge
  1117. float minDistance = float.MaxValue;
  1118. ExplorationDirection closestDirection = ExplorationDirection.North;
  1119. // Check each direction
  1120. float northDist = CheckDirectionDistance(playerPos, ExplorationDirection.North);
  1121. float southDist = CheckDirectionDistance(playerPos, ExplorationDirection.South);
  1122. float eastDist = CheckDirectionDistance(playerPos, ExplorationDirection.East);
  1123. float westDist = CheckDirectionDistance(playerPos, ExplorationDirection.West);
  1124. // Find the minimum distance
  1125. if (northDist <= minDistance) { minDistance = northDist; closestDirection = ExplorationDirection.North; }
  1126. if (southDist <= minDistance) { minDistance = southDist; closestDirection = ExplorationDirection.South; }
  1127. if (eastDist <= minDistance) { minDistance = eastDist; closestDirection = ExplorationDirection.East; }
  1128. if (westDist <= minDistance) { minDistance = westDist; closestDirection = ExplorationDirection.West; }
  1129. Debug.Log($"🧭 Exploration direction: {closestDirection} (N:{northDist:F0}, S:{southDist:F0}, E:{eastDist:F0}, W:{westDist:F0})");
  1130. return closestDirection;
  1131. }
  1132. private float CheckDirectionDistance(Vector2Int playerPos, ExplorationDirection direction)
  1133. {
  1134. Vector2Int checkPos = playerPos;
  1135. float distance = 0;
  1136. // Move in the specified direction until we hit unexplored area
  1137. Vector2Int dirVector = GetDirectionVector(direction);
  1138. while (IsValidExplorationPosition(checkPos.x, checkPos.y) && exploredMask[checkPos.x, checkPos.y])
  1139. {
  1140. checkPos += dirVector;
  1141. distance++;
  1142. // Safety check to prevent infinite loops
  1143. if (distance > 100)
  1144. {
  1145. Debug.LogWarning($"⚠️ Direction check exceeded 100 steps for {direction}, breaking");
  1146. break;
  1147. }
  1148. }
  1149. return distance;
  1150. }
  1151. private Vector2Int GetDirectionVector(ExplorationDirection direction)
  1152. {
  1153. switch (direction)
  1154. {
  1155. case ExplorationDirection.North: return new Vector2Int(0, 1);
  1156. case ExplorationDirection.South: return new Vector2Int(0, -1);
  1157. case ExplorationDirection.East: return new Vector2Int(1, 0);
  1158. case ExplorationDirection.West: return new Vector2Int(-1, 0);
  1159. default: return Vector2Int.zero;
  1160. }
  1161. }
  1162. /// <summary>
  1163. /// Reveal a chunk of the map in the specified direction
  1164. /// </summary>
  1165. private void RevealChunk(Vector2Int playerPos, ExplorationDirection direction)
  1166. {
  1167. Vector2Int chunkCenter = GetChunkCenterForDirection(playerPos, direction);
  1168. int halfChunk = explorationChunkSize / 2;
  1169. for (int x = chunkCenter.x - halfChunk; x < chunkCenter.x + halfChunk; x++)
  1170. {
  1171. for (int y = chunkCenter.y - halfChunk; y < chunkCenter.y + halfChunk; y++)
  1172. {
  1173. if (IsValidExplorationPosition(x, y))
  1174. {
  1175. exploredMask[x, y] = true;
  1176. }
  1177. }
  1178. }
  1179. Debug.Log($"✅ Revealed {explorationChunkSize}x{explorationChunkSize} chunk at {chunkCenter} in direction {direction}");
  1180. }
  1181. private Vector2Int GetChunkCenterForDirection(Vector2Int playerPos, ExplorationDirection direction)
  1182. {
  1183. switch (direction)
  1184. {
  1185. case ExplorationDirection.North:
  1186. return new Vector2Int(playerPos.x, playerPos.y + explorationChunkSize);
  1187. case ExplorationDirection.South:
  1188. return new Vector2Int(playerPos.x, playerPos.y - explorationChunkSize);
  1189. case ExplorationDirection.East:
  1190. return new Vector2Int(playerPos.x + explorationChunkSize, playerPos.y);
  1191. case ExplorationDirection.West:
  1192. return new Vector2Int(playerPos.x - explorationChunkSize, playerPos.y);
  1193. default:
  1194. return playerPos;
  1195. }
  1196. }
  1197. /// <summary>
  1198. /// Update the visible map data based on explored areas
  1199. /// </summary>
  1200. private void UpdateVisibleMap()
  1201. {
  1202. if (performanceMode)
  1203. {
  1204. UpdateVisibleMapOptimized();
  1205. }
  1206. else
  1207. {
  1208. UpdateVisibleMapStandard();
  1209. }
  1210. }
  1211. /// <summary>
  1212. /// Standard map update for smaller maps
  1213. /// </summary>
  1214. private void UpdateVisibleMapStandard()
  1215. {
  1216. // Find the bounds of the explored area
  1217. Rect exploredBounds = GetExploredBounds();
  1218. // Create new map data for the visible area
  1219. int visibleWidth = (int)exploredBounds.width;
  1220. int visibleHeight = (int)exploredBounds.height;
  1221. MapData newVisibleMap = new MapData(visibleWidth, visibleHeight);
  1222. // Copy explored tiles to the visible map
  1223. for (int x = 0; x < visibleWidth; x++)
  1224. {
  1225. for (int y = 0; y < visibleHeight; y++)
  1226. {
  1227. int fullMapX = (int)exploredBounds.x + x;
  1228. int fullMapY = (int)exploredBounds.y + y;
  1229. if (IsValidExplorationPosition(fullMapX, fullMapY) && exploredMask[fullMapX, fullMapY])
  1230. {
  1231. MapTile sourceTile = fullMapData.GetTile(fullMapX, fullMapY);
  1232. MapTile destTile = newVisibleMap.GetTile(x, y);
  1233. if (sourceTile != null && destTile != null)
  1234. {
  1235. CopyTileData(sourceTile, destTile);
  1236. }
  1237. }
  1238. }
  1239. }
  1240. // Copy settlements from the full map to the visible map (adjusted coordinates)
  1241. CopySettlementsToVisibleMap(newVisibleMap, exploredBounds);
  1242. // Update the map maker's current map data
  1243. mapMaker.SetMapData(newVisibleMap);
  1244. // Fix team marker position after map bounds change
  1245. FixTeamMarkerPosition(exploredBounds);
  1246. Debug.Log($"🗺️ Updated visible map: {visibleWidth}x{visibleHeight}");
  1247. // Debug settlement count
  1248. int townCount = newVisibleMap.GetTowns().Count;
  1249. int villageCount = newVisibleMap.GetVillages().Count;
  1250. Debug.Log($"🏘️ Visible settlements: {townCount} towns, {villageCount} villages");
  1251. }
  1252. /// <summary>
  1253. /// Optimized map update for large maps using Level of Detail
  1254. /// </summary>
  1255. private void UpdateVisibleMapOptimized()
  1256. {
  1257. Debug.Log($"⚡ Using optimized map update with LOD level {lodLevel}...");
  1258. // Find the bounds of the explored area
  1259. Rect exploredBounds = GetExploredBounds();
  1260. // Calculate downsampled dimensions
  1261. int visibleWidth = (int)exploredBounds.width / lodLevel;
  1262. int visibleHeight = (int)exploredBounds.height / lodLevel;
  1263. // Ensure minimum size
  1264. visibleWidth = Mathf.Max(visibleWidth, 50);
  1265. visibleHeight = Mathf.Max(visibleHeight, 50);
  1266. MapData newVisibleMap = new MapData(visibleWidth, visibleHeight);
  1267. // Copy tiles with Level of Detail sampling
  1268. for (int x = 0; x < visibleWidth; x++)
  1269. {
  1270. for (int y = 0; y < visibleHeight; y++)
  1271. {
  1272. // Sample from the full map using LOD
  1273. int fullMapX = (int)exploredBounds.x + (x * lodLevel);
  1274. int fullMapY = (int)exploredBounds.y + (y * lodLevel);
  1275. if (IsValidExplorationPosition(fullMapX, fullMapY) && exploredMask[fullMapX, fullMapY])
  1276. {
  1277. // Average surrounding tiles for better representation
  1278. MapTile sampledTile = SampleTileWithLOD(fullMapX, fullMapY, lodLevel);
  1279. MapTile destTile = newVisibleMap.GetTile(x, y);
  1280. if (sampledTile != null && destTile != null)
  1281. {
  1282. CopyTileData(sampledTile, destTile);
  1283. }
  1284. }
  1285. }
  1286. }
  1287. // Copy settlements with adjusted coordinates for LOD
  1288. CopySettlementsToVisibleMapLOD(newVisibleMap, exploredBounds, lodLevel);
  1289. // Update the map maker's current map data
  1290. mapMaker.SetMapData(newVisibleMap);
  1291. // Fix team marker position with LOD adjustment
  1292. FixTeamMarkerPositionLOD(exploredBounds, lodLevel);
  1293. Debug.Log($"⚡ Optimized visible map updated: {visibleWidth}x{visibleHeight} (LOD {lodLevel})");
  1294. }
  1295. /// <summary>
  1296. /// Sample a tile from the full map using Level of Detail
  1297. /// </summary>
  1298. private MapTile SampleTileWithLOD(int centerX, int centerY, int sampleSize)
  1299. {
  1300. List<MapTile> sampleTiles = new List<MapTile>();
  1301. // Collect tiles in the sample area
  1302. for (int dx = 0; dx < sampleSize && dx + centerX < fullMapWidth; dx++)
  1303. {
  1304. for (int dy = 0; dy < sampleSize && dy + centerY < fullMapHeight; dy++)
  1305. {
  1306. MapTile tile = fullMapData.GetTile(centerX + dx, centerY + dy);
  1307. if (tile != null && exploredMask[centerX + dx, centerY + dy])
  1308. {
  1309. sampleTiles.Add(tile);
  1310. }
  1311. }
  1312. }
  1313. if (sampleTiles.Count == 0)
  1314. return fullMapData.GetTile(centerX, centerY);
  1315. // Return the most common terrain type in the sample
  1316. var terrainGroups = sampleTiles.GroupBy(t => t.terrainType);
  1317. var mostCommonTerrain = terrainGroups.OrderByDescending(g => g.Count()).First().Key;
  1318. // Create a representative tile
  1319. MapTile representativeTile = fullMapData.GetTile(centerX, centerY);
  1320. if (representativeTile != null)
  1321. {
  1322. representativeTile.terrainType = mostCommonTerrain;
  1323. representativeTile.height = sampleTiles.Average(t => t.height);
  1324. }
  1325. return representativeTile;
  1326. }
  1327. /// <summary>
  1328. /// Copy settlements to visible map with LOD adjustment
  1329. /// </summary>
  1330. private void CopySettlementsToVisibleMapLOD(MapData visibleMap, Rect exploredBounds, int lod)
  1331. {
  1332. var fullTowns = fullMapData.GetTowns();
  1333. var fullVillages = fullMapData.GetVillages();
  1334. foreach (var town in fullTowns)
  1335. {
  1336. if (town.position.x >= exploredBounds.x && town.position.x < exploredBounds.x + exploredBounds.width &&
  1337. town.position.y >= exploredBounds.y && town.position.y < exploredBounds.y + exploredBounds.height)
  1338. {
  1339. Vector2Int adjustedPos = new Vector2Int(
  1340. (town.position.x - (int)exploredBounds.x) / lod,
  1341. (town.position.y - (int)exploredBounds.y) / lod
  1342. );
  1343. Settlement adjustedTown = new Settlement(town.name, town.Type, adjustedPos);
  1344. visibleMap.AddSettlement(adjustedTown);
  1345. }
  1346. }
  1347. foreach (var village in fullVillages)
  1348. {
  1349. if (village.position.x >= exploredBounds.x && village.position.x < exploredBounds.x + exploredBounds.width &&
  1350. village.position.y >= exploredBounds.y && village.position.y < exploredBounds.y + exploredBounds.height)
  1351. {
  1352. Vector2Int adjustedPos = new Vector2Int(
  1353. (village.position.x - (int)exploredBounds.x) / lod,
  1354. (village.position.y - (int)exploredBounds.y) / lod
  1355. );
  1356. Settlement adjustedVillage = new Settlement(village.name, village.Type, adjustedPos);
  1357. visibleMap.AddSettlement(adjustedVillage);
  1358. }
  1359. }
  1360. }
  1361. /// <summary>
  1362. /// Fix team marker position with LOD adjustment
  1363. /// </summary>
  1364. private void FixTeamMarkerPositionLOD(Rect exploredBounds, int lod)
  1365. {
  1366. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  1367. if (teamPlacement == null) return;
  1368. GameObject teamMarker = GameObject.Find("TeamMarker");
  1369. if (teamMarker == null) return;
  1370. Vector3 currentWorldPos = teamMarker.transform.position;
  1371. // Convert to LOD-adjusted coordinates
  1372. Vector2Int newTilePos = new Vector2Int(
  1373. Mathf.RoundToInt(currentWorldPos.x) / lod,
  1374. Mathf.RoundToInt(currentWorldPos.y) / lod
  1375. );
  1376. int visibleWidth = (int)exploredBounds.width / lod;
  1377. int visibleHeight = (int)exploredBounds.height / lod;
  1378. newTilePos.x = Mathf.Clamp(newTilePos.x, 0, visibleWidth - 1);
  1379. newTilePos.y = Mathf.Clamp(newTilePos.y, 0, visibleHeight - 1);
  1380. teamPlacement.UpdateMarkerAfterMapChange(newTilePos);
  1381. Debug.Log($"⚡ Fixed team marker with LOD {lod}: World({currentWorldPos.x:F1}, {currentWorldPos.y:F1}) → Tile({newTilePos.x}, {newTilePos.y})");
  1382. }
  1383. /// <summary>
  1384. /// Fix team marker position after map bounds change
  1385. /// </summary>
  1386. private void FixTeamMarkerPosition(Rect exploredBounds)
  1387. {
  1388. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  1389. if (teamPlacement == null) return;
  1390. // Get the team marker GameObject to preserve world position
  1391. GameObject teamMarker = GameObject.Find("TeamMarker");
  1392. if (teamMarker == null) return;
  1393. // Get current world position
  1394. Vector3 currentWorldPos = teamMarker.transform.position;
  1395. // Convert world position to new tile coordinates
  1396. // Assuming 1 unit = 1 tile (adjust if your tile scale is different)
  1397. Vector2Int newTilePos = new Vector2Int(
  1398. Mathf.RoundToInt(currentWorldPos.x),
  1399. Mathf.RoundToInt(currentWorldPos.y)
  1400. );
  1401. // Ensure the position is within the new visible map bounds
  1402. int visibleWidth = (int)exploredBounds.width;
  1403. int visibleHeight = (int)exploredBounds.height;
  1404. newTilePos.x = Mathf.Clamp(newTilePos.x, 0, visibleWidth - 1);
  1405. newTilePos.y = Mathf.Clamp(newTilePos.y, 0, visibleHeight - 1);
  1406. // Update the team placement with the corrected position
  1407. teamPlacement.UpdateMarkerAfterMapChange(newTilePos);
  1408. Debug.Log($"🔧 Fixed team marker: World({currentWorldPos.x:F1}, {currentWorldPos.y:F1}) → Tile({newTilePos.x}, {newTilePos.y})");
  1409. } /// <summary>
  1410. /// Copy settlements from full map to visible map, adjusting coordinates
  1411. /// </summary>
  1412. private void CopySettlementsToVisibleMap(MapData visibleMap, Rect exploredBounds)
  1413. {
  1414. // Get settlements from full map
  1415. var fullTowns = fullMapData.GetTowns();
  1416. var fullVillages = fullMapData.GetVillages();
  1417. foreach (var town in fullTowns)
  1418. {
  1419. // Check if settlement is within explored bounds
  1420. if (town.position.x >= exploredBounds.x && town.position.x < exploredBounds.x + exploredBounds.width &&
  1421. town.position.y >= exploredBounds.y && town.position.y < exploredBounds.y + exploredBounds.height)
  1422. {
  1423. // Adjust coordinates to visible map
  1424. Vector2Int adjustedPos = new Vector2Int(
  1425. town.position.x - (int)exploredBounds.x,
  1426. town.position.y - (int)exploredBounds.y
  1427. );
  1428. // Create new settlement with adjusted position
  1429. Settlement adjustedTown = new Settlement(town.name, town.Type, adjustedPos);
  1430. visibleMap.AddSettlement(adjustedTown);
  1431. }
  1432. }
  1433. foreach (var village in fullVillages)
  1434. {
  1435. // Check if settlement is within explored bounds
  1436. if (village.position.x >= exploredBounds.x && village.position.x < exploredBounds.x + exploredBounds.width &&
  1437. village.position.y >= exploredBounds.y && village.position.y < exploredBounds.y + exploredBounds.height)
  1438. {
  1439. // Adjust coordinates to visible map
  1440. Vector2Int adjustedPos = new Vector2Int(
  1441. village.position.x - (int)exploredBounds.x,
  1442. village.position.y - (int)exploredBounds.y
  1443. );
  1444. // Create new settlement with adjusted position
  1445. Settlement adjustedVillage = new Settlement(village.name, village.Type, adjustedPos);
  1446. visibleMap.AddSettlement(adjustedVillage);
  1447. }
  1448. }
  1449. }
  1450. private Rect GetExploredBounds()
  1451. {
  1452. int minX = fullMapWidth, maxX = 0;
  1453. int minY = fullMapHeight, maxY = 0;
  1454. for (int x = 0; x < fullMapWidth; x++)
  1455. {
  1456. for (int y = 0; y < fullMapHeight; y++)
  1457. {
  1458. if (exploredMask[x, y])
  1459. {
  1460. minX = Mathf.Min(minX, x);
  1461. maxX = Mathf.Max(maxX, x);
  1462. minY = Mathf.Min(minY, y);
  1463. maxY = Mathf.Max(maxY, y);
  1464. }
  1465. }
  1466. }
  1467. return new Rect(minX, minY, maxX - minX + 1, maxY - minY + 1);
  1468. }
  1469. /// <summary>
  1470. /// Public method to get explored bounds for camera positioning
  1471. /// </summary>
  1472. public Rect GetExploredBoundsForCamera()
  1473. {
  1474. return GetExploredBounds();
  1475. }
  1476. /// <summary>
  1477. /// Update team marker position to account for new visible map coordinates
  1478. /// </summary>
  1479. private void UpdateTeamMarkerPosition(Rect exploredBounds)
  1480. {
  1481. // Get the SimpleTeamPlacement instance
  1482. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  1483. if (teamPlacement == null)
  1484. {
  1485. Debug.LogWarning("⚠️ SimpleTeamPlacement not found - cannot update team marker position");
  1486. return;
  1487. }
  1488. // Get current team position in full map coordinates
  1489. Vector2Int currentFullMapPos = GetCurrentTeamPositionInFullMap();
  1490. if (currentFullMapPos == Vector2Int.zero)
  1491. {
  1492. Debug.LogWarning("⚠️ Could not determine current team position in full map");
  1493. return;
  1494. }
  1495. // Convert full map position to new visible map coordinates
  1496. Vector2Int newVisiblePos = new Vector2Int(
  1497. currentFullMapPos.x - (int)exploredBounds.x,
  1498. currentFullMapPos.y - (int)exploredBounds.y
  1499. );
  1500. // Validate the new position is within visible bounds
  1501. if (newVisiblePos.x >= 0 && newVisiblePos.x < exploredBounds.width &&
  1502. newVisiblePos.y >= 0 && newVisiblePos.y < exploredBounds.height)
  1503. {
  1504. // Update the team marker position
  1505. teamPlacement.UpdateMarkerAfterMapChange(newVisiblePos);
  1506. Debug.Log($"🎯 Updated team marker from full map pos {currentFullMapPos} to visible pos {newVisiblePos}");
  1507. }
  1508. else
  1509. {
  1510. Debug.LogWarning($"⚠️ Calculated team position {newVisiblePos} is outside visible bounds {exploredBounds}");
  1511. }
  1512. }
  1513. /// <summary>
  1514. /// Get current team position in full map coordinates
  1515. /// </summary>
  1516. private Vector2Int GetCurrentTeamPositionInFullMap()
  1517. {
  1518. // Try to get team position from SimpleTeamPlacement
  1519. SimpleTeamPlacement teamPlacement = Object.FindFirstObjectByType<SimpleTeamPlacement>();
  1520. if (teamPlacement == null) return Vector2Int.zero;
  1521. // Get current visible map position
  1522. Vector2Int currentVisiblePos = teamPlacement.GetCurrentTeamPosition();
  1523. if (currentVisiblePos == Vector2Int.zero) return Vector2Int.zero;
  1524. // Convert to full map coordinates
  1525. Vector2Int visibleOffset = GetCurrentVisibleOffset(mapMaker.GetMapData());
  1526. Vector2Int fullMapPos = new Vector2Int(
  1527. visibleOffset.x + currentVisiblePos.x,
  1528. visibleOffset.y + currentVisiblePos.y
  1529. );
  1530. return fullMapPos;
  1531. }
  1532. /// <summary>
  1533. /// Copy tile data from source to destination
  1534. /// </summary>
  1535. private void CopyTileData(MapTile source, MapTile destination)
  1536. {
  1537. destination.terrainType = source.terrainType;
  1538. destination.height = source.height;
  1539. destination.isWalkable = source.isWalkable;
  1540. destination.featureType = source.featureType;
  1541. destination.name = source.name;
  1542. // Copy any other tile properties as needed
  1543. }
  1544. private bool IsValidExplorationPosition(int x, int y)
  1545. {
  1546. return x >= 0 && x < fullMapWidth && y >= 0 && y < fullMapHeight;
  1547. }
  1548. /// <summary>
  1549. /// Get exploration progress information
  1550. /// </summary>
  1551. public ExplorationInfo GetExplorationInfo()
  1552. {
  1553. int exploredTiles = 0;
  1554. for (int x = 0; x < fullMapWidth; x++)
  1555. {
  1556. for (int y = 0; y < fullMapHeight; y++)
  1557. {
  1558. if (exploredMask[x, y])
  1559. exploredTiles++;
  1560. }
  1561. }
  1562. int totalTiles = fullMapWidth * fullMapHeight;
  1563. float explorationPercentage = (float)exploredTiles / totalTiles * 100f;
  1564. return new ExplorationInfo
  1565. {
  1566. exploredTiles = exploredTiles,
  1567. totalTiles = totalTiles,
  1568. explorationPercentage = explorationPercentage,
  1569. fullMapSize = new Vector2Int(fullMapWidth, fullMapHeight)
  1570. };
  1571. }
  1572. /// <summary>
  1573. /// DEBUG: Get detailed exploration info for debugging
  1574. /// </summary>
  1575. public void DebugExplorationState(Vector2 playerPos, MapData currentMapData)
  1576. {
  1577. Vector2Int playerFullMapPos = GetPlayerPositionInFullMap(playerPos, currentMapData);
  1578. Rect exploredBounds = GetExploredBounds();
  1579. Debug.Log("=== EXPLORATION DEBUG ===");
  1580. Debug.Log($"Player Visible Pos: {playerPos}");
  1581. Debug.Log($"Player Full Map Pos: {playerFullMapPos}");
  1582. Debug.Log($"Explored Bounds: {exploredBounds}");
  1583. Debug.Log($"Current Map Size: {currentMapData.Width}x{currentMapData.Height}");
  1584. Debug.Log($"Full Map Size: {fullMapWidth}x{fullMapHeight}");
  1585. // Check distances to edges
  1586. float distToLeft = playerFullMapPos.x - exploredBounds.x;
  1587. float distToRight = (exploredBounds.x + exploredBounds.width - 1) - playerFullMapPos.x;
  1588. float distToBottom = playerFullMapPos.y - exploredBounds.y;
  1589. float distToTop = (exploredBounds.y + exploredBounds.height - 1) - playerFullMapPos.y;
  1590. Debug.Log($"Distance to edges - Left: {distToLeft}, Right: {distToRight}, Bottom: {distToBottom}, Top: {distToTop}");
  1591. Debug.Log($"Exploration Distance Threshold: {explorationDistance}");
  1592. Debug.Log($"Should Explore: {IsNearUnexploredEdge(playerFullMapPos)}");
  1593. Debug.Log($"Last Exploration Time: {lastExplorationTime}, Current Time: {Time.time}, Cooldown: {explorationCooldown}");
  1594. Debug.Log("========================");
  1595. }
  1596. public enum ExplorationDirection
  1597. {
  1598. North, South, East, West
  1599. }
  1600. }
  1601. /// <summary>
  1602. /// Information about exploration progress
  1603. /// </summary>
  1604. public struct ExplorationInfo
  1605. {
  1606. public int exploredTiles;
  1607. public int totalTiles;
  1608. public float explorationPercentage;
  1609. public Vector2Int fullMapSize;
  1610. }