CombatSceneManager.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434
  1. using UnityEngine;
  2. using UnityEngine.SceneManagement;
  3. using System.Collections.Generic;
  4. using System.Collections;
  5. /// <summary>
  6. /// Manages the transition from travel events to battle scenes
  7. /// Handles terrain setup, team data transfer, and battle initialization
  8. /// </summary>
  9. public class CombatSceneManager : MonoBehaviour
  10. {
  11. [Header("Scene Management")]
  12. [Tooltip("Name of the battle scene to load")]
  13. public string battleSceneName = "BattleScene";
  14. [Header("Terrain Generation")]
  15. [Tooltip("Reference to BFM Terrain Generator for battle field terrain")]
  16. public BFMTerrainGenerator terrainGenerator;
  17. [Header("Debug Settings")]
  18. public bool showDebugLogs = true;
  19. // Singleton instance
  20. public static CombatSceneManager Instance { get; private set; }
  21. // Events for other systems to hook into
  22. public System.Action<CombatDataTransfer.CombatSessionData> OnCombatSessionStarted;
  23. public System.Action OnBattleSceneLoaded;
  24. private void Awake()
  25. {
  26. // Ensure singleton pattern
  27. if (Instance == null)
  28. {
  29. Instance = this;
  30. DontDestroyOnLoad(gameObject);
  31. }
  32. else
  33. {
  34. Destroy(gameObject);
  35. return;
  36. }
  37. }
  38. private void Start()
  39. {
  40. // Try to find terrain generator if not assigned
  41. if (terrainGenerator == null)
  42. {
  43. terrainGenerator = FindFirstObjectByType<BFMTerrainGenerator>();
  44. }
  45. }
  46. /// <summary>
  47. /// Start a combat encounter by transitioning to the battle scene
  48. /// </summary>
  49. public void StartCombatEncounter(BattleEventData battleData, TravelEventContext context)
  50. {
  51. if (showDebugLogs)
  52. {
  53. Debug.Log("⚔️ CombatSceneManager: Starting combat encounter...");
  54. }
  55. // Get team members from the game state
  56. List<TeamCharacter> teamMembers = GetCurrentTeamMembers();
  57. if (teamMembers == null || teamMembers.Count == 0)
  58. {
  59. Debug.LogError("❌ No team members found! Cannot start combat.");
  60. return;
  61. }
  62. // Initialize the combat session data
  63. CombatDataTransfer.InitializeCombatSession(battleData, context, teamMembers);
  64. if (showDebugLogs)
  65. {
  66. CombatDataTransfer.DebugLogSession();
  67. }
  68. // Populate legacy battle setup data for compatibility
  69. CombatDataTransfer.PopulateLegacyBattleSetupData();
  70. // Notify listeners
  71. OnCombatSessionStarted?.Invoke(CombatDataTransfer.GetCurrentSession());
  72. // Load the battle scene
  73. StartCoroutine(LoadBattleSceneCoroutine());
  74. }
  75. /// <summary>
  76. /// Get current team members from various sources
  77. /// </summary>
  78. private List<TeamCharacter> GetCurrentTeamMembers()
  79. {
  80. List<TeamCharacter> teamMembers = new List<TeamCharacter>();
  81. // Method 1: Try MainTeamSelectScript first (if available)
  82. var teamSelectScript = FindFirstObjectByType<MainTeamSelectScript>();
  83. if (teamSelectScript != null)
  84. {
  85. var characters = teamSelectScript.GetConfiguredCharacters();
  86. if (characters != null && characters.Count > 0)
  87. {
  88. teamMembers.AddRange(characters);
  89. if (showDebugLogs)
  90. {
  91. Debug.Log($"📋 Found {teamMembers.Count} team members from MainTeamSelectScript");
  92. }
  93. return teamMembers;
  94. }
  95. }
  96. // Method 2: Try GameStateManager
  97. if (GameStateManager.Instance != null && GameStateManager.Instance.savedTeam != null)
  98. {
  99. foreach (var character in GameStateManager.Instance.savedTeam)
  100. {
  101. if (character != null)
  102. {
  103. teamMembers.Add(character);
  104. }
  105. }
  106. if (teamMembers.Count > 0)
  107. {
  108. if (showDebugLogs)
  109. {
  110. Debug.Log($"📋 Found {teamMembers.Count} team members from GameStateManager");
  111. }
  112. return teamMembers;
  113. }
  114. }
  115. // Method 3: Try loading from PlayerPrefs as fallback
  116. for (int i = 0; i < 4; i++)
  117. {
  118. string prefix = $"Character{i}_";
  119. if (PlayerPrefs.HasKey(prefix + "Exists") && PlayerPrefs.GetInt(prefix + "Exists") == 1)
  120. {
  121. var character = new TeamCharacter();
  122. character.name = PlayerPrefs.GetString(prefix + "Name", "");
  123. character.isMale = PlayerPrefs.GetInt(prefix + "IsMale", 1) == 1;
  124. character.strength = PlayerPrefs.GetInt(prefix + "Strength", 10);
  125. character.dexterity = PlayerPrefs.GetInt(prefix + "Dexterity", 10);
  126. character.constitution = PlayerPrefs.GetInt(prefix + "Constitution", 10);
  127. character.wisdom = PlayerPrefs.GetInt(prefix + "Wisdom", 10);
  128. character.perception = PlayerPrefs.GetInt(prefix + "Perception", 10);
  129. character.gold = PlayerPrefs.GetInt(prefix + "Gold", 25);
  130. character.silver = PlayerPrefs.GetInt(prefix + "Silver", 0);
  131. character.copper = PlayerPrefs.GetInt(prefix + "Copper", 0);
  132. character.equippedWeapon = PlayerPrefs.GetString(prefix + "EquippedWeapon", "");
  133. character.equippedArmor = PlayerPrefs.GetString(prefix + "EquippedArmor", "");
  134. // Load weapon list using the same format as MainTeamSelectScript
  135. character.weapons = new List<string>();
  136. int weaponCount = PlayerPrefs.GetInt(prefix + "WeaponCount", 0);
  137. for (int j = 0; j < weaponCount; j++)
  138. {
  139. string weapon = PlayerPrefs.GetString(prefix + $"Weapon{j}", "");
  140. if (!string.IsNullOrEmpty(weapon))
  141. {
  142. character.weapons.Add(weapon);
  143. }
  144. }
  145. // Load armor list using the same format as MainTeamSelectScript
  146. character.armor = new List<string>();
  147. int armorCount = PlayerPrefs.GetInt(prefix + "ArmorCount", 0);
  148. for (int j = 0; j < armorCount; j++)
  149. {
  150. string armor = PlayerPrefs.GetString(prefix + $"Armor{j}", "");
  151. if (!string.IsNullOrEmpty(armor))
  152. {
  153. character.armor.Add(armor);
  154. }
  155. }
  156. // Debug the loaded character data
  157. if (showDebugLogs)
  158. {
  159. Debug.Log($"🔧 Loaded character '{character.name}': equippedWeapon='{character.equippedWeapon}', weapons=[{string.Join(", ", character.weapons)}]");
  160. }
  161. teamMembers.Add(character);
  162. }
  163. }
  164. if (teamMembers.Count > 0)
  165. {
  166. if (showDebugLogs)
  167. {
  168. Debug.Log($"📋 Found {teamMembers.Count} team members from PlayerPrefs");
  169. }
  170. }
  171. else
  172. {
  173. Debug.LogWarning("⚠️ No team members found in any data source!");
  174. }
  175. return teamMembers;
  176. }
  177. /// <summary>
  178. /// Coroutine to handle battle scene loading
  179. /// </summary>
  180. private IEnumerator LoadBattleSceneCoroutine()
  181. {
  182. if (showDebugLogs)
  183. {
  184. Debug.Log($"🎬 Loading battle scene: {battleSceneName}");
  185. }
  186. // Check if scene exists in build settings
  187. if (!IsSceneInBuildSettings(battleSceneName))
  188. {
  189. Debug.LogError($"❌ Battle scene '{battleSceneName}' not found in Build Settings!");
  190. yield break;
  191. }
  192. // Start loading the scene asynchronously
  193. AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(battleSceneName);
  194. // Wait for the scene to load
  195. while (!asyncLoad.isDone)
  196. {
  197. // You could update a loading bar here
  198. yield return null;
  199. }
  200. if (showDebugLogs)
  201. {
  202. Debug.Log("✅ Battle scene loaded successfully");
  203. }
  204. // Notify that the battle scene has loaded
  205. OnBattleSceneLoaded?.Invoke();
  206. // Set up the terrain in the battle scene
  207. yield return StartCoroutine(SetupBattleTerrainCoroutine());
  208. }
  209. /// <summary>
  210. /// Set up the battle terrain based on the combat session data
  211. /// </summary>
  212. private IEnumerator SetupBattleTerrainCoroutine()
  213. {
  214. // Wait a frame to ensure the scene is fully loaded
  215. yield return null;
  216. var session = CombatDataTransfer.GetCurrentSession();
  217. if (session == null)
  218. {
  219. Debug.LogWarning("⚠️ No combat session data available for terrain setup");
  220. yield break;
  221. }
  222. // Find or create terrain generator in the new scene
  223. if (terrainGenerator == null)
  224. {
  225. terrainGenerator = FindFirstObjectByType<BFMTerrainGenerator>();
  226. }
  227. if (terrainGenerator != null)
  228. {
  229. // Convert TerrainType to BFMTerrainType
  230. BFMTerrainType bfmTerrain = ConvertToBFMTerrainType(session.battleTerrain);
  231. if (showDebugLogs)
  232. {
  233. Debug.Log($"🌍 Setting battle terrain to: {bfmTerrain} (from {session.battleTerrain})");
  234. }
  235. // Set the terrain type
  236. terrainGenerator.SetTerrainType(bfmTerrain);
  237. // Regenerate the terrain if there's a method for it
  238. if (terrainGenerator.GetComponent<MonoBehaviour>() != null)
  239. {
  240. // You might need to call a regeneration method here
  241. // terrainGenerator.RegenerateTerrain(); // If such method exists
  242. }
  243. }
  244. else
  245. {
  246. if (showDebugLogs)
  247. {
  248. Debug.LogWarning("⚠️ No BFMTerrainGenerator found in battle scene - terrain setup skipped");
  249. }
  250. }
  251. // Log the battle setup information
  252. if (showDebugLogs)
  253. {
  254. string terrainDesc = CombatDataTransfer.GetTerrainDescription(session.battleTerrain, session.battleFeature);
  255. Debug.Log($"🎯 Battle setup complete: Fighting {terrainDesc}");
  256. Debug.Log($"⏰ Time: {session.timeOfDay:F1}h, Weather: {session.weather}");
  257. }
  258. }
  259. /// <summary>
  260. /// Convert TerrainType to BFMTerrainType for battle field generation
  261. /// </summary>
  262. private BFMTerrainType ConvertToBFMTerrainType(TerrainType terrainType)
  263. {
  264. return terrainType switch
  265. {
  266. TerrainType.Plains => BFMTerrainType.Plain,
  267. TerrainType.Forest => BFMTerrainType.Forest,
  268. TerrainType.Mountain => BFMTerrainType.Mountain,
  269. TerrainType.ForestRiver => BFMTerrainType.Forest, // Forest with river features
  270. _ => BFMTerrainType.Plain // Default to plains for other terrain types
  271. };
  272. }
  273. /// <summary>
  274. /// Check if a scene exists in build settings
  275. /// </summary>
  276. private bool IsSceneInBuildSettings(string sceneName)
  277. {
  278. for (int i = 0; i < SceneManager.sceneCountInBuildSettings; i++)
  279. {
  280. string scenePath = UnityEngine.SceneManagement.SceneUtility.GetScenePathByBuildIndex(i);
  281. string sceneNameFromPath = System.IO.Path.GetFileNameWithoutExtension(scenePath);
  282. if (sceneNameFromPath.Equals(sceneName, System.StringComparison.OrdinalIgnoreCase))
  283. {
  284. return true;
  285. }
  286. }
  287. return false;
  288. }
  289. /// <summary>
  290. /// End the current combat session and return to the map
  291. /// </summary>
  292. public void EndCombatSession(bool playerVictory)
  293. {
  294. if (showDebugLogs)
  295. {
  296. Debug.Log($"🏆 Combat session ended - Player victory: {playerVictory}");
  297. }
  298. // Clear the combat session data
  299. CombatDataTransfer.ClearSession();
  300. // Return to the map scene
  301. // You might want to load "MapScene2" or whatever your main map scene is called
  302. StartCoroutine(ReturnToMapCoroutine());
  303. }
  304. /// <summary>
  305. /// Return to the map scene after combat
  306. /// </summary>
  307. private IEnumerator ReturnToMapCoroutine()
  308. {
  309. yield return new WaitForSeconds(2f); // Brief delay for any end-of-combat UI
  310. string mapSceneName = "MapScene2"; // Adjust this to your actual map scene name
  311. if (IsSceneInBuildSettings(mapSceneName))
  312. {
  313. if (showDebugLogs)
  314. {
  315. Debug.Log($"🗺️ Returning to map scene: {mapSceneName}");
  316. }
  317. SceneManager.LoadScene(mapSceneName);
  318. }
  319. else
  320. {
  321. Debug.LogError($"❌ Map scene '{mapSceneName}' not found in Build Settings!");
  322. }
  323. }
  324. /// <summary>
  325. /// Get current battle context for UI display
  326. /// </summary>
  327. public string GetBattleContextDescription()
  328. {
  329. var session = CombatDataTransfer.GetCurrentSession();
  330. if (session == null) return "Unknown battle";
  331. string terrainDesc = CombatDataTransfer.GetTerrainDescription(session.battleTerrain, session.battleFeature);
  332. string timeDesc = GetTimeDescription(session.timeOfDay);
  333. string weatherDesc = session.weather.ToString().ToLower();
  334. return $"Battle {terrainDesc} during {timeDesc} ({weatherDesc} weather)";
  335. }
  336. /// <summary>
  337. /// Convert time of day to readable description
  338. /// </summary>
  339. private string GetTimeDescription(float timeOfDay)
  340. {
  341. return timeOfDay switch
  342. {
  343. >= 6f and < 12f => "morning",
  344. >= 12f and < 18f => "afternoon",
  345. >= 18f and < 22f => "evening",
  346. _ => "night"
  347. };
  348. }
  349. /// <summary>
  350. /// Debug method to manually start a test combat
  351. /// </summary>
  352. [ContextMenu("Start Test Combat")]
  353. public void StartTestCombat()
  354. {
  355. // Create test battle data
  356. var testBattleData = new BattleEventData
  357. {
  358. enemyCount = 2,
  359. enemyType = "Test Bandit",
  360. battleDescription = "A test combat encounter"
  361. };
  362. // Create test context
  363. var testContext = new TravelEventContext(Vector2Int.zero, null, null)
  364. {
  365. currentWeather = Weather.Clear,
  366. timeOfDay = 14f
  367. };
  368. // Use fake tile data
  369. testContext.currentTile = new MapTile(0, 0)
  370. {
  371. terrainType = TerrainType.Plains,
  372. featureType = FeatureType.Road
  373. };
  374. StartCombatEncounter(testBattleData, testContext);
  375. }
  376. }