CombatSceneManager.cs 14 KB

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