BattleSetup.cs 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.AI;
  4. public class BattleSetup : MonoBehaviour
  5. {
  6. List<GameObject> unplacedEnemyCharacters = new List<GameObject>();
  7. List<GameObject> placedPlayerCharacters = new List<GameObject>();
  8. List<GameObject> placedEnemyCharacters = new List<GameObject>();
  9. private List<GameObject> playerCharacters = new List<GameObject>();
  10. private List<GameObject> enemyCharacters = new List<GameObject>();
  11. private List<CharacterSelection> playerSelections = new List<CharacterSelection>();
  12. private List<CharacterSelection> enemySelections = new List<CharacterSelection>();
  13. [Tooltip("Select the layer(s) that represent the ground for raycasting.")]
  14. public LayerMask groundLayerMask;
  15. public GameObject playerPrefab; // Assign in the inspector or through GameManager
  16. public GameObject enemyPrefab; // Assign in the inspector or through GameManager
  17. public GameObject SwordPrefab; // Assign in the inspector or through GameManager
  18. public GameObject BowPrefab; // Assign in the inspector or through GameManager
  19. GameObject playerSpawnArea;
  20. GameObject enemySpawnArea;
  21. Bounds playerSpawnBounds;
  22. private Camera mainCamera;
  23. private GameObject currentPlacingCharacterInstance;
  24. private int nextPlayerCharacterPrefabIndex = 0;
  25. private bool isPlacingPlayerCharacters = false;
  26. void Awake()
  27. {
  28. mainCamera = Camera.main;
  29. if (mainCamera == null)
  30. {
  31. Debug.LogError("Main camera not found. Please ensure there is a camera tagged as 'MainCamera' in the scene.");
  32. enabled = false;
  33. return;
  34. }
  35. playerCharacters.Clear();
  36. enemyCharacters.Clear();
  37. // Store the selections for use during placement
  38. playerSelections = new List<CharacterSelection>(BattleSetupData.playerSelections);
  39. enemySelections = new List<CharacterSelection>(BattleSetupData.enemySelections);
  40. // Initialize the lists with characters from the game manager or other sources
  41. playerSpawnArea = GameObject.Find("PlayerSpawnArea");
  42. enemySpawnArea = GameObject.Find("EnemySpawnArea");
  43. if (playerSpawnArea == null)
  44. {
  45. Debug.LogError("PlayerSpawnArea GameObject not found in scene. Please ensure there is a GameObject named 'PlayerSpawnArea' in the scene.");
  46. enabled = false;
  47. return;
  48. }
  49. Collider playerSpawnAreaCollider = playerSpawnArea.GetComponent<Collider>();
  50. if (playerSpawnAreaCollider == null)
  51. {
  52. Debug.LogError("PlayerSpawnArea does not have a collider component.");
  53. enabled = false;
  54. return;
  55. }
  56. playerSpawnBounds = playerSpawnAreaCollider.bounds;
  57. if (enemySpawnArea == null)
  58. {
  59. // Enemy placement might still work if unplacedEnemyCharacters is empty
  60. Debug.LogWarning("EnemySpawnArea GameObject not found in the scene. Enemy placement might be affected.");
  61. }
  62. // Call the setup methods to place characters
  63. PlaceEnemyCharacters();
  64. InitiatePlayerCharacterPlacement();
  65. }
  66. // Helper method to get WeaponItem from CombatDataTransfer if available
  67. WeaponItem GetEnhancedWeaponItem(string characterName, bool isPlayer)
  68. {
  69. if (!CombatDataTransfer.HasValidSession())
  70. {
  71. return null;
  72. }
  73. var session = CombatDataTransfer.GetCurrentSession();
  74. if (session == null)
  75. {
  76. return null;
  77. }
  78. if (isPlayer)
  79. {
  80. // Find player character in enhanced data (match by base name, ignoring suffixes)
  81. foreach (var player in session.playerTeam)
  82. {
  83. if (player.characterName == characterName && player.equippedWeaponItem != null)
  84. {
  85. return player.equippedWeaponItem;
  86. }
  87. // Also try matching without the numbered suffix (e.g., "Player 1" vs "Player")
  88. if (characterName.StartsWith(player.characterName) && player.equippedWeaponItem != null)
  89. {
  90. return player.equippedWeaponItem;
  91. }
  92. }
  93. }
  94. else
  95. {
  96. // Find enemy character in enhanced data (match by base name, ignoring suffixes)
  97. foreach (var enemy in session.enemies)
  98. {
  99. if (enemy.enemyName == characterName && enemy.preferredWeaponItem != null)
  100. {
  101. return enemy.preferredWeaponItem;
  102. }
  103. // Also try matching the base name (e.g., "Skeleton Warrior_1" vs "Skeleton Warrior")
  104. if (characterName.StartsWith(enemy.enemyType) && enemy.preferredWeaponItem != null)
  105. {
  106. return enemy.preferredWeaponItem;
  107. }
  108. }
  109. }
  110. return null;
  111. }
  112. /// <summary>
  113. /// Apply team character stats from combat data to a Character instance
  114. /// </summary>
  115. private void ApplyTeamDataToCharacter(Character character, string characterName)
  116. {
  117. var sessionData = CombatDataTransfer.GetCurrentSession();
  118. if (sessionData == null)
  119. {
  120. Debug.LogWarning($"No combat session data found for character: {characterName}");
  121. return;
  122. }
  123. // Find the team character data (match by exact name or base name)
  124. var teamData = sessionData.playerTeam?.Find(p => p.characterName == characterName);
  125. if (teamData == null)
  126. {
  127. // Try to match base name without suffix
  128. teamData = sessionData.playerTeam?.Find(p => characterName.StartsWith(p.characterName));
  129. }
  130. if (teamData != null)
  131. {
  132. character.ApplyStatsFromCombatData(teamData);
  133. }
  134. else
  135. {
  136. Debug.LogWarning($"No team data found for character: {characterName}");
  137. }
  138. }
  139. /// <summary>
  140. /// Apply enemy character stats from combat data to a Character instance
  141. /// </summary>
  142. private void ApplyEnemyDataToCharacter(Character character, string characterName)
  143. {
  144. var sessionData = CombatDataTransfer.GetCurrentSession();
  145. if (sessionData == null)
  146. {
  147. Debug.LogWarning($"No combat session data found for enemy: {characterName}");
  148. return;
  149. }
  150. // Find the enemy character data (match by exact name or base type)
  151. var enemyData = sessionData.enemies?.Find(e => e.enemyName == characterName);
  152. if (enemyData == null)
  153. {
  154. // Try to match base type without suffix
  155. enemyData = sessionData.enemies?.Find(e => characterName.StartsWith(e.enemyType));
  156. }
  157. if (enemyData != null)
  158. {
  159. character.ApplyStatsFromCombatData(enemyData);
  160. }
  161. else
  162. {
  163. Debug.LogWarning($"No enemy data found for character: {characterName}");
  164. }
  165. }
  166. // Enhanced weapon equipping with WeaponItem support
  167. void EquipWeapon(Character character, WeaponItem weaponItem)
  168. {
  169. Weapon weapon = null;
  170. if (weaponItem != null)
  171. {
  172. // Use the WeaponItem's CreateWeaponInstance method to preserve all attributes
  173. weapon = weaponItem.CreateWeaponInstance(character.transform);
  174. if (weapon != null)
  175. {
  176. weapon.SetWielder(character);
  177. }
  178. else
  179. {
  180. Debug.LogWarning($"⚠️ WeaponItem.CreateWeaponInstance() returned null for {weaponItem.name}. Falling back to string-based creation.");
  181. // Fallback to string-based weapon creation
  182. EquipWeapon(character, weaponItem.weaponType.ToString());
  183. return;
  184. }
  185. }
  186. else
  187. {
  188. Debug.LogWarning($"No WeaponItem provided for {character.CharacterName}. Equipping with fists as fallback.");
  189. EquipWeapon(character, "Fists");
  190. return;
  191. }
  192. if (weapon != null)
  193. {
  194. // Attach weapon to the character
  195. Transform attachPoint = character.transform.Find("WeaponAttachPoint");
  196. if (attachPoint != null)
  197. {
  198. weapon.transform.SetParent(attachPoint, false);
  199. weapon.transform.localPosition = Vector3.zero;
  200. weapon.transform.localRotation = Quaternion.identity;
  201. }
  202. else
  203. {
  204. weapon.transform.SetParent(character.transform, false);
  205. weapon.transform.localPosition = new Vector3(0.5f, 0, 0);
  206. }
  207. character.Weapon = weapon;
  208. weapon.SetWielder(character);
  209. }
  210. else
  211. {
  212. Debug.LogError($"❌ Failed to create weapon for {character.CharacterName}");
  213. }
  214. }
  215. // Fallback weapon equipping using string types (for backward compatibility)
  216. void EquipWeapon(Character character, string weaponType)
  217. {
  218. Weapon weapon = null;
  219. // Handle null or empty weapon types
  220. if (string.IsNullOrEmpty(weaponType))
  221. {
  222. Debug.LogWarning($"No weapon type specified for {character.CharacterName}. Equipping with fists as fallback.");
  223. weaponType = "Fists";
  224. }
  225. if (weaponType == "Sword")
  226. {
  227. var weaponObj = Instantiate(SwordPrefab, character.transform);
  228. weapon = weaponObj.GetComponent<Weapon>();
  229. }
  230. else if (weaponType == "Bow")
  231. {
  232. var weaponObj = Instantiate(BowPrefab, character.transform);
  233. weapon = weaponObj.GetComponent<Weapon>();
  234. }
  235. else if (weaponType == "Fists")
  236. {
  237. // Create a fists weapon directly using SimpleSword as base
  238. GameObject fistsObj = new GameObject("Fists");
  239. fistsObj.transform.SetParent(character.transform, false);
  240. SimpleSword fistsWeapon = fistsObj.AddComponent<SimpleSword>();
  241. // Override the sword's properties to make it act like fists
  242. fistsWeapon.weaponName = "Fists";
  243. fistsWeapon.description = "Bare fists - a basic unarmed attack.";
  244. fistsWeapon.attackSpeed = 1.0f;
  245. weapon = fistsWeapon;
  246. }
  247. else
  248. {
  249. Debug.LogWarning($"❓ Unknown weapon type: '{weaponType}' for {character.CharacterName}. Equipping with fists as fallback.");
  250. // Create a fists weapon as fallback using SimpleSword as base
  251. GameObject fistsObj = new GameObject("Fists");
  252. fistsObj.transform.SetParent(character.transform, false);
  253. SimpleSword fistsWeapon = fistsObj.AddComponent<SimpleSword>();
  254. // Override the sword's properties to make it act like fists
  255. fistsWeapon.weaponName = "Fists";
  256. fistsWeapon.description = "Bare fists - a basic unarmed attack.";
  257. fistsWeapon.attackSpeed = 1.0f;
  258. weapon = fistsWeapon;
  259. }
  260. if (weapon != null)
  261. {
  262. Transform attachPoint = character.transform.Find("WeaponAttachPoint");
  263. if (attachPoint != null)
  264. {
  265. weapon.transform.SetParent(attachPoint, false);
  266. weapon.transform.localPosition = Vector3.zero;
  267. weapon.transform.localRotation = Quaternion.identity;
  268. }
  269. else
  270. {
  271. weapon.transform.SetParent(character.transform, false);
  272. weapon.transform.localPosition = new Vector3(0.5f, 0, 0);
  273. }
  274. character.Weapon = weapon;
  275. weapon.SetWielder(character);
  276. }
  277. else
  278. {
  279. Debug.LogError($"❌ Failed to create weapon for {character.CharacterName}");
  280. }
  281. }
  282. void Update()
  283. {
  284. if (!isPlacingPlayerCharacters || currentPlacingCharacterInstance == null)
  285. {
  286. return;
  287. }
  288. HandleCharacterPlacement();
  289. }
  290. private void InitiatePlayerCharacterPlacement()
  291. {
  292. if (playerSelections.Count == 0)
  293. {
  294. return;
  295. }
  296. isPlacingPlayerCharacters = true;
  297. nextPlayerCharacterPrefabIndex = 0;
  298. SpawnNextPlayerCharacterForPlacement();
  299. }
  300. private void SpawnNextPlayerCharacterForPlacement()
  301. {
  302. if (currentPlacingCharacterInstance != null)
  303. {
  304. // This case should ideally not happen if logic flows correctly,
  305. // but as a safeguard if a previous instance wasn't cleaned up.
  306. Destroy(currentPlacingCharacterInstance);
  307. currentPlacingCharacterInstance = null;
  308. }
  309. if (nextPlayerCharacterPrefabIndex < playerSelections.Count)
  310. {
  311. var selection = playerSelections[nextPlayerCharacterPrefabIndex];
  312. currentPlacingCharacterInstance = Instantiate(playerPrefab);
  313. currentPlacingCharacterInstance.name = selection.characterName;
  314. var character = currentPlacingCharacterInstance.GetComponent<Character>();
  315. if (character != null)
  316. {
  317. // Apply team data stats to the character
  318. ApplyTeamDataToCharacter(character, selection.characterName);
  319. // Try to get enhanced weapon data first
  320. WeaponItem enhancedWeapon = GetEnhancedWeaponItem(selection.characterName, true);
  321. if (enhancedWeapon != null)
  322. {
  323. EquipWeapon(character, enhancedWeapon);
  324. }
  325. else
  326. {
  327. EquipWeapon(character, selection.weaponType);
  328. }
  329. character.CharacterName = selection.characterName + " " + (nextPlayerCharacterPrefabIndex + 1);
  330. }
  331. currentPlacingCharacterInstance.GetComponent<NavMeshAgent>().enabled = false;
  332. // Optionally disable AI or other components that might interfere with placement
  333. // e.g., currentPlacingCharacterInstance.GetComponent<AIController>()?.enabled = false;
  334. }
  335. else
  336. {
  337. FinalizePlayerPlacement();
  338. }
  339. }
  340. private void HandleCharacterPlacement()
  341. {
  342. Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition);
  343. Debug.DrawRay(ray.origin, ray.direction * 200f, Color.yellow); // Visualize the ray
  344. if (Physics.Raycast(ray, out RaycastHit hit, 200f, groundLayerMask))
  345. {
  346. Vector3 targetPosition = hit.point;
  347. // Clamp position to playerSpawnArea bound (X and Z axis)
  348. targetPosition.x = Mathf.Clamp(targetPosition.x, playerSpawnBounds.min.x, playerSpawnBounds.max.x);
  349. targetPosition.z = Mathf.Clamp(targetPosition.z, playerSpawnBounds.min.z, playerSpawnBounds.max.z);
  350. // Y is determined by the raycast hit on the ground
  351. // --- Direct Y Position Calculation for Mouse-Following Character ---
  352. // This approach was found to be more stable than repeatedly calling AdjustCharacterOnGround.
  353. // It assumes a standard Unity CapsuleCollider where the pivot is at the center.
  354. Vector3 finalCharacterPosition = targetPosition; // targetPosition.y is from the ground hit (hit.point.y)
  355. CapsuleCollider capCollider = currentPlacingCharacterInstance.GetComponent<CapsuleCollider>();
  356. if (capCollider != null)
  357. {
  358. // For a capsule, its pivot is typically at its center.
  359. // To place its bottom on targetPosition.y (ground level),
  360. // its center (pivot) needs to be at targetPosition.y + (height / 2).
  361. // We also account for the character's local scale.
  362. finalCharacterPosition.y = targetPosition.y + (capCollider.height * currentPlacingCharacterInstance.transform.localScale.y / 2f);
  363. }
  364. else
  365. {
  366. // Fallback if not a capsule or if a different pivot is used.
  367. // This might need adjustment based on your character's actual pivot and collider.
  368. Debug.LogWarning($"'{currentPlacingCharacterInstance.name}' does not have a CapsuleCollider. Using default Y offset for placement. Adjust if incorrect.");
  369. finalCharacterPosition.y = targetPosition.y + 0.5f; // Example offset, adjust as needed
  370. }
  371. currentPlacingCharacterInstance.transform.position = finalCharacterPosition;
  372. // AdjustCharacterOnGround(currentPlacingCharacterInstance); // No longer called every frame for mouse-follow
  373. }
  374. else
  375. {
  376. }
  377. // Else mouse is not over valid ground, character stays at last valid position or initial spawn
  378. if (Input.GetMouseButtonDown(0))
  379. { // Left-click to place character
  380. // Check if the current position is valid (within bounds and not overlapping)
  381. // The position is already clamped and on ground due to the logic above.
  382. // We primarily need to check for overlaps.
  383. if (!IsOverlappingOtherCharacters(currentPlacingCharacterInstance))
  384. {
  385. PlaceCurrentCharacter();
  386. }
  387. else
  388. {
  389. Debug.LogWarning("Cannot place character: Overlapping with another character.");
  390. // Optionally, you could provide feedback to the player about the overlap (e.g. change color?).
  391. }
  392. }
  393. if (Input.GetMouseButtonDown(1))
  394. { // Right-click to cancel placement
  395. if (placedPlayerCharacters.Count > 0)
  396. {
  397. FinalizePlayerPlacement();
  398. }
  399. else
  400. {
  401. }
  402. }
  403. }
  404. private void PlaceCurrentCharacter()
  405. {
  406. placedPlayerCharacters.Add(currentPlacingCharacterInstance);
  407. currentPlacingCharacterInstance = null;
  408. nextPlayerCharacterPrefabIndex++;
  409. SpawnNextPlayerCharacterForPlacement(); // Spawn next character for placement
  410. }
  411. private void AdjustCharacterOnGround(GameObject character)
  412. {
  413. if (character == null)
  414. {
  415. return;
  416. }
  417. Collider charCollider = character.GetComponent<Collider>();
  418. if (charCollider != null)
  419. {
  420. // This ensured the lowest point of the collider is at character.transform.position.y
  421. // (which should be the ground hit point)
  422. // currentY is the Y-coordinate of the character's pivot.
  423. // We want the character's collider bottom (feet) to be at this Y-level.
  424. float currentY = character.transform.position.y;
  425. float colliderMinYWorld = charCollider.bounds.min.y;
  426. float verticalOffset = currentY - colliderMinYWorld;
  427. const float tolerance = 0.001f; // Small tolerance to avoid floating point issues
  428. if (Mathf.Abs(verticalOffset) > tolerance)
  429. {
  430. character.transform.position += Vector3.up * verticalOffset;
  431. }
  432. }
  433. else
  434. {
  435. Debug.LogWarning($"Character: {character.name} has no collider. Cannot accurately adjust to ground.");
  436. }
  437. }
  438. private bool IsOverlappingOtherCharacters(GameObject character)
  439. {
  440. Collider newCharCollider = character.GetComponent<Collider>();
  441. if (newCharCollider == null)
  442. {
  443. Debug.LogWarning($"Character: {character.name} has no collider. Cannot check for overlaps.");
  444. return false; // Or true to precent placement if no collider
  445. }
  446. Bounds newCharBounds = newCharCollider.bounds;
  447. foreach (GameObject placedCharacter in placedPlayerCharacters)
  448. {
  449. Collider otherCollider = placedCharacter.GetComponent<Collider>();
  450. if (otherCollider != null && newCharBounds.Intersects(otherCollider.bounds))
  451. {
  452. return true;
  453. }
  454. }
  455. foreach (GameObject placedEnemy in placedEnemyCharacters)
  456. {
  457. Collider otherCollider = placedEnemy.GetComponent<Collider>();
  458. if (otherCollider != null && newCharBounds.Intersects(otherCollider.bounds))
  459. {
  460. return true;
  461. }
  462. }
  463. return false;
  464. }
  465. private void FinalizePlayerPlacement()
  466. {
  467. isPlacingPlayerCharacters = false;
  468. if (currentPlacingCharacterInstance != null)
  469. {
  470. Destroy(currentPlacingCharacterInstance); // Destroy the preview instance if it wasn't placed
  471. currentPlacingCharacterInstance = null;
  472. }
  473. EndPlacementPhase();
  474. // Here you might want to trigger the next phase of your game, e.g., start the battle.
  475. GameManager.Instance.StartBattle(placedPlayerCharacters, placedEnemyCharacters);
  476. Destroy(playerSpawnArea);
  477. Destroy(enemySpawnArea);
  478. playerSpawnArea = null;
  479. enemySpawnArea = null;
  480. }
  481. private void EndPlacementPhase()
  482. {
  483. // This method can be used to clean up or finalize the placement phase.
  484. // For example, you might want to enable AI, re-enable NavMeshAgents, etc.
  485. foreach (GameObject character in placedPlayerCharacters)
  486. {
  487. NavMeshAgent agent = character.GetComponent<NavMeshAgent>();
  488. if (agent != null)
  489. {
  490. agent.enabled = true; // Re-enable NavMeshAgent
  491. // Configure movement speed based on character's MovementSpeed stat
  492. Character characterComponent = character.GetComponent<Character>();
  493. if (characterComponent != null)
  494. {
  495. // Convert character movement speed to NavMeshAgent speed
  496. // Base conversion: MovementSpeed 10 = 3.5 units/sec, scale from there
  497. agent.speed = (characterComponent.MovementSpeed / 10f) * 3.5f;
  498. }
  499. else
  500. {
  501. agent.speed = 3.5f; // Default speed if no character component
  502. }
  503. }
  504. // Add carry capacity system to player characters
  505. AddCarryCapacityComponent(character);
  506. // Enable AI or other components as needed
  507. // e.g., character.GetComponent<AIController>()?.enabled = true;
  508. }
  509. foreach (GameObject character in enemyCharacters)
  510. {
  511. NavMeshAgent agent = character.GetComponent<NavMeshAgent>();
  512. if (agent != null)
  513. {
  514. agent.enabled = true; // Re-enable NavMeshAgent
  515. // Configure movement speed based on character's MovementSpeed stat
  516. Character characterComponent = character.GetComponent<Character>();
  517. if (characterComponent != null)
  518. {
  519. // Convert character movement speed to NavMeshAgent speed
  520. // Base conversion: MovementSpeed 10 = 3.5 units/sec, scale from there
  521. agent.speed = (characterComponent.MovementSpeed / 10f) * 3.5f;
  522. }
  523. else
  524. {
  525. agent.speed = 3.5f; // Default speed if no character component
  526. }
  527. }
  528. // Add carry capacity system to enemy characters too (for consistency)
  529. AddCarryCapacityComponent(character);
  530. // Enable AI or other components as needed
  531. // e.g., character.GetComponent<AIController>()?.enabled = true;
  532. }
  533. }
  534. /// <summary>
  535. /// Add CharacterCarryCapacity component to a character GameObject
  536. /// </summary>
  537. private void AddCarryCapacityComponent(GameObject characterObj)
  538. {
  539. if (characterObj == null) return;
  540. // Only add if it doesn't already exist
  541. var existingCapacity = characterObj.GetComponent<CharacterCarryCapacity>();
  542. if (existingCapacity != null) return;
  543. var character = characterObj.GetComponent<Character>();
  544. if (character == null) return;
  545. // Add the carry capacity component
  546. var carryCapacity = characterObj.AddComponent<CharacterCarryCapacity>();
  547. // Configure based on character type/name
  548. ConfigureCarryCapacity(carryCapacity, character);
  549. }
  550. /// <summary>
  551. /// Configure carry capacity settings based on character
  552. /// </summary>
  553. private void ConfigureCarryCapacity(CharacterCarryCapacity carryCapacity, Character character)
  554. {
  555. if (carryCapacity == null || character == null) return;
  556. // Adjust settings based on character type or name
  557. string characterName = character.CharacterName.ToLower();
  558. if (characterName.Contains("warrior") || characterName.Contains("fighter"))
  559. {
  560. // Warriors can carry more
  561. carryCapacity.carrySystem.baseCarryCapacity = 60;
  562. carryCapacity.carrySystem.strengthMultiplier = 6f;
  563. }
  564. else if (characterName.Contains("rogue") || characterName.Contains("thief"))
  565. {
  566. // Rogues are more agile but carry less
  567. carryCapacity.carrySystem.baseCarryCapacity = 40;
  568. carryCapacity.carrySystem.strengthMultiplier = 4f;
  569. carryCapacity.carrySystem.lightLoadThreshold = 0.4f; // Stay light more often
  570. }
  571. else if (characterName.Contains("mage") || characterName.Contains("wizard"))
  572. {
  573. // Mages have lower physical capacity
  574. carryCapacity.carrySystem.baseCarryCapacity = 35;
  575. carryCapacity.carrySystem.strengthMultiplier = 3f;
  576. }
  577. else
  578. {
  579. // Default settings are already good for most characters
  580. carryCapacity.carrySystem.baseCarryCapacity = 50;
  581. carryCapacity.carrySystem.strengthMultiplier = 5f;
  582. }
  583. // Enable debug info for player characters
  584. if (character.CompareTag("Player"))
  585. {
  586. carryCapacity.showDebugInfo = true;
  587. }
  588. }
  589. private void PlaceEnemyCharacters()
  590. {
  591. Collider spawnAreaCollider = enemySpawnArea.GetComponent<Collider>();
  592. if (spawnAreaCollider == null)
  593. {
  594. Debug.LogError("Enemy spawn area does not have a collider component.");
  595. return;
  596. }
  597. Bounds spawnBounds = spawnAreaCollider.bounds;
  598. for (int i = enemySelections.Count - 1; i >= 0; i--)
  599. {
  600. var selection = enemySelections[i];
  601. float randomX = UnityEngine.Random.Range(spawnBounds.min.x, spawnBounds.max.x);
  602. float randomZ = UnityEngine.Random.Range(spawnBounds.min.z, spawnBounds.max.z);
  603. Vector3 rayOrigin = new Vector3(randomX, enemySpawnArea.transform.position.y + 10f, randomZ);
  604. Vector3 spawnPosition = new Vector3(randomX, enemySpawnArea.transform.position.y, randomZ);
  605. // Raycast to find the ground
  606. if (Physics.Raycast(rayOrigin, Vector3.down, out RaycastHit hit, 200f, groundLayerMask))
  607. {
  608. spawnPosition = hit.point;
  609. }
  610. else
  611. {
  612. Debug.LogWarning($"Raycast did not hit ground below ({randomX}, {randomZ}). Placing enemy at spawn area Y level.");
  613. }
  614. GameObject placedEnemy = Instantiate(enemyPrefab, spawnPosition, Quaternion.identity);
  615. Character character = placedEnemy.GetComponent<Character>();
  616. if (character != null)
  617. {
  618. // Apply enemy data stats to the character
  619. ApplyEnemyDataToCharacter(character, selection.characterName);
  620. // Try to get enhanced weapon data first
  621. WeaponItem enhancedWeapon = GetEnhancedWeaponItem(selection.characterName, false);
  622. if (enhancedWeapon != null)
  623. {
  624. EquipWeapon(character, enhancedWeapon);
  625. }
  626. else
  627. {
  628. EquipWeapon(character, selection.weaponType);
  629. }
  630. character.CharacterName = selection.characterName + "_" + (i + 1);
  631. }
  632. AdjustCharacterOnGround(placedEnemy); // Adjust the enemy to the ground
  633. placedEnemyCharacters.Add(placedEnemy);
  634. }
  635. }
  636. }