using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; public class BattleSetup : MonoBehaviour { List unplacedEnemyCharacters = new List(); List placedPlayerCharacters = new List(); List placedEnemyCharacters = new List(); private List playerCharacters = new List(); private List enemyCharacters = new List(); private List playerSelections = new List(); private List enemySelections = new List(); [Tooltip("Select the layer(s) that represent the ground for raycasting.")] public LayerMask groundLayerMask; public GameObject playerPrefab; // Assign in the inspector or through GameManager public GameObject enemyPrefab; // Assign in the inspector or through GameManager public GameObject SwordPrefab; // Assign in the inspector or through GameManager public GameObject BowPrefab; // Assign in the inspector or through GameManager GameObject playerSpawnArea; GameObject enemySpawnArea; Bounds playerSpawnBounds; private Camera mainCamera; private GameObject currentPlacingCharacterInstance; private int nextPlayerCharacterPrefabIndex = 0; private bool isPlacingPlayerCharacters = false; void Awake() { mainCamera = Camera.main; if (mainCamera == null) { Debug.LogError("Main camera not found. Please ensure there is a camera tagged as 'MainCamera' in the scene."); enabled = false; return; } playerCharacters.Clear(); enemyCharacters.Clear(); // Store the selections for use during placement playerSelections = new List(BattleSetupData.playerSelections); enemySelections = new List(BattleSetupData.enemySelections); // Initialize the lists with characters from the game manager or other sources playerSpawnArea = GameObject.Find("PlayerSpawnArea"); enemySpawnArea = GameObject.Find("EnemySpawnArea"); if (playerSpawnArea == null) { Debug.LogError("PlayerSpawnArea GameObject not found in scene. Please ensure there is a GameObject named 'PlayerSpawnArea' in the scene."); enabled = false; return; } Collider playerSpawnAreaCollider = playerSpawnArea.GetComponent(); if (playerSpawnAreaCollider == null) { Debug.LogError("PlayerSpawnArea does not have a collider component."); enabled = false; return; } playerSpawnBounds = playerSpawnAreaCollider.bounds; if (enemySpawnArea == null) { // Enemy placement might still work if unplacedEnemyCharacters is empty Debug.LogWarning("EnemySpawnArea GameObject not found in the scene. Enemy placement might be affected."); } // Call the setup methods to place characters PlaceEnemyCharacters(); InitiatePlayerCharacterPlacement(); } // Helper method to get WeaponItem from CombatDataTransfer if available WeaponItem GetEnhancedWeaponItem(string characterName, bool isPlayer) { Debug.Log($"🔍 GetEnhancedWeaponItem called for '{characterName}', isPlayer: {isPlayer}"); if (!CombatDataTransfer.HasValidSession()) { Debug.Log($"🔍 No valid CombatDataTransfer session"); return null; } var session = CombatDataTransfer.GetCurrentSession(); if (session == null) { Debug.Log($"🔍 CombatDataTransfer session is null"); return null; } Debug.Log($"🔍 Session has {session.playerTeam.Count} players and {session.enemies.Count} enemies"); if (isPlayer) { // Debug: List all available player characters Debug.Log($"🔍 Available players in session:"); foreach (var player in session.playerTeam) { Debug.Log($" - Player: '{player.characterName}', Weapon: {(player.equippedWeaponItem != null ? player.equippedWeaponItem.name : "NULL")}"); } // Find player character in enhanced data (match by base name, ignoring suffixes) foreach (var player in session.playerTeam) { if (player.characterName == characterName && player.equippedWeaponItem != null) { Debug.Log($"🎯 Found enhanced WeaponItem for player {characterName}: {player.equippedWeaponItem.name}"); return player.equippedWeaponItem; } // Also try matching without the numbered suffix (e.g., "Player 1" vs "Player") if (characterName.StartsWith(player.characterName) && player.equippedWeaponItem != null) { Debug.Log($"🎯 Found enhanced WeaponItem for player {characterName} (matched base name {player.characterName}): {player.equippedWeaponItem.name}"); return player.equippedWeaponItem; } } Debug.Log($"🔍 No matching player found for '{characterName}'"); } else { // Find enemy character in enhanced data (match by base name, ignoring suffixes) foreach (var enemy in session.enemies) { if (enemy.enemyName == characterName && enemy.preferredWeaponItem != null) { Debug.Log($"🎯 Found enhanced WeaponItem for enemy {characterName}: {enemy.preferredWeaponItem.name}"); return enemy.preferredWeaponItem; } // Also try matching the base name (e.g., "Skeleton Warrior_1" vs "Skeleton Warrior") if (characterName.StartsWith(enemy.enemyType) && enemy.preferredWeaponItem != null) { Debug.Log($"🎯 Found enhanced WeaponItem for enemy {characterName} (matched base type {enemy.enemyType}): {enemy.preferredWeaponItem.name}"); return enemy.preferredWeaponItem; } } } return null; } /// /// Apply team character stats from combat data to a Character instance /// private void ApplyTeamDataToCharacter(Character character, string characterName) { var sessionData = CombatDataTransfer.GetCurrentSession(); if (sessionData == null) { Debug.LogWarning($"No combat session data found for character: {characterName}"); return; } // Find the team character data (match by exact name or base name) var teamData = sessionData.playerTeam?.Find(p => p.characterName == characterName); if (teamData == null) { // Try to match base name without suffix teamData = sessionData.playerTeam?.Find(p => characterName.StartsWith(p.characterName)); } if (teamData != null) { character.ApplyStatsFromCombatData(teamData); Debug.Log($"📊 Applied team stats to {characterName}: STR:{teamData.strength} DEX:{teamData.dexterity} CON:{teamData.constitution} WIS:{teamData.wisdom} PER:{teamData.perception}"); } else { Debug.LogWarning($"No team data found for character: {characterName}"); } } /// /// Apply enemy character stats from combat data to a Character instance /// private void ApplyEnemyDataToCharacter(Character character, string characterName) { var sessionData = CombatDataTransfer.GetCurrentSession(); if (sessionData == null) { Debug.LogWarning($"No combat session data found for enemy: {characterName}"); return; } // Find the enemy character data (match by exact name or base type) var enemyData = sessionData.enemies?.Find(e => e.enemyName == characterName); if (enemyData == null) { // Try to match base type without suffix enemyData = sessionData.enemies?.Find(e => characterName.StartsWith(e.enemyType)); } if (enemyData != null) { character.ApplyStatsFromCombatData(enemyData); Debug.Log($"📊 Applied enemy stats to {characterName}"); } else { Debug.LogWarning($"No enemy data found for character: {characterName}"); } } // Enhanced weapon equipping with WeaponItem support void EquipWeapon(Character character, WeaponItem weaponItem) { Debug.Log($"🔧 EquipWeapon called for {character.CharacterName} with WeaponItem: {(weaponItem != null ? weaponItem.name : "null")}"); Weapon weapon = null; if (weaponItem != null) { Debug.Log($"🔧 Using WeaponItem.CreateWeaponInstance() for {weaponItem.name}"); // Use the WeaponItem's CreateWeaponInstance method to preserve all attributes weapon = weaponItem.CreateWeaponInstance(character.transform); if (weapon != null) { weapon.SetWielder(character); Debug.Log($"✅ Successfully created weapon from WeaponItem: {weapon.weaponName}"); } else { Debug.LogWarning($"⚠️ WeaponItem.CreateWeaponInstance() returned null for {weaponItem.name}. Falling back to string-based creation."); // Fallback to string-based weapon creation EquipWeapon(character, weaponItem.weaponType.ToString()); return; } } else { Debug.LogWarning($"No WeaponItem provided for {character.CharacterName}. Equipping with fists as fallback."); EquipWeapon(character, "Fists"); return; } if (weapon != null) { // Attach weapon to the character Transform attachPoint = character.transform.Find("WeaponAttachPoint"); if (attachPoint != null) { weapon.transform.SetParent(attachPoint, false); weapon.transform.localPosition = Vector3.zero; weapon.transform.localRotation = Quaternion.identity; } else { weapon.transform.SetParent(character.transform, false); weapon.transform.localPosition = new Vector3(0.5f, 0, 0); } character.Weapon = weapon; weapon.SetWielder(character); Debug.Log($"✅ Successfully equipped {weapon.weaponName} to {character.CharacterName}"); } else { Debug.LogError($"❌ Failed to create weapon for {character.CharacterName}"); } } // Fallback weapon equipping using string types (for backward compatibility) void EquipWeapon(Character character, string weaponType) { Debug.Log($"🔧 EquipWeapon called for {character.CharacterName} with weaponType: '{weaponType}'"); Weapon weapon = null; // Handle null or empty weapon types if (string.IsNullOrEmpty(weaponType)) { Debug.LogWarning($"No weapon type specified for {character.CharacterName}. Equipping with fists as fallback."); weaponType = "Fists"; } Debug.Log($"🔧 Processing weapon type: '{weaponType}' for {character.CharacterName}"); if (weaponType == "Sword") { Debug.Log($"⚔️ Creating Sword for {character.CharacterName}"); var weaponObj = Instantiate(SwordPrefab, character.transform); weapon = weaponObj.GetComponent(); } else if (weaponType == "Bow") { Debug.Log($"🏹 Creating Bow for {character.CharacterName}"); var weaponObj = Instantiate(BowPrefab, character.transform); weapon = weaponObj.GetComponent(); } else if (weaponType == "Fists") { Debug.Log($"👊 Creating Fists for {character.CharacterName}"); // Create a fists weapon directly using SimpleSword as base GameObject fistsObj = new GameObject("Fists"); fistsObj.transform.SetParent(character.transform, false); SimpleSword fistsWeapon = fistsObj.AddComponent(); // Override the sword's properties to make it act like fists fistsWeapon.weaponName = "Fists"; fistsWeapon.description = "Bare fists - a basic unarmed attack."; fistsWeapon.attackSpeed = 1.0f; weapon = fistsWeapon; } else { Debug.LogWarning($"❓ Unknown weapon type: '{weaponType}' for {character.CharacterName}. Equipping with fists as fallback."); // Create a fists weapon as fallback using SimpleSword as base GameObject fistsObj = new GameObject("Fists"); fistsObj.transform.SetParent(character.transform, false); SimpleSword fistsWeapon = fistsObj.AddComponent(); // Override the sword's properties to make it act like fists fistsWeapon.weaponName = "Fists"; fistsWeapon.description = "Bare fists - a basic unarmed attack."; fistsWeapon.attackSpeed = 1.0f; weapon = fistsWeapon; } if (weapon != null) { Transform attachPoint = character.transform.Find("WeaponAttachPoint"); if (attachPoint != null) { weapon.transform.SetParent(attachPoint, false); weapon.transform.localPosition = Vector3.zero; weapon.transform.localRotation = Quaternion.identity; } else { weapon.transform.SetParent(character.transform, false); weapon.transform.localPosition = new Vector3(0.5f, 0, 0); } character.Weapon = weapon; weapon.SetWielder(character); Debug.Log($"✅ Successfully equipped {weapon.weaponName} to {character.CharacterName}"); } else { Debug.LogError($"❌ Failed to create weapon for {character.CharacterName}"); } } void Update() { if (!isPlacingPlayerCharacters || currentPlacingCharacterInstance == null) { return; } HandleCharacterPlacement(); } private void InitiatePlayerCharacterPlacement() { if (playerSelections.Count == 0) { return; } isPlacingPlayerCharacters = true; nextPlayerCharacterPrefabIndex = 0; SpawnNextPlayerCharacterForPlacement(); } private void SpawnNextPlayerCharacterForPlacement() { if (currentPlacingCharacterInstance != null) { // This case should ideally not happen if logic flows correctly, // but as a safeguard if a previous instance wasn't cleaned up. Destroy(currentPlacingCharacterInstance); currentPlacingCharacterInstance = null; } if (nextPlayerCharacterPrefabIndex < playerSelections.Count) { var selection = playerSelections[nextPlayerCharacterPrefabIndex]; currentPlacingCharacterInstance = Instantiate(playerPrefab); currentPlacingCharacterInstance.name = selection.characterName; var character = currentPlacingCharacterInstance.GetComponent(); if (character != null) { Debug.Log($"🔍 SpawnNextPlayerCharacterForPlacement: Character '{selection.characterName}' with weapon '{selection.weaponType}'"); // Apply team data stats to the character ApplyTeamDataToCharacter(character, selection.characterName); // Try to get enhanced weapon data first WeaponItem enhancedWeapon = GetEnhancedWeaponItem(selection.characterName, true); Debug.Log($"🔍 Enhanced weapon lookup for '{selection.characterName}': {(enhancedWeapon != null ? enhancedWeapon.name : "NULL")}"); if (enhancedWeapon != null) { Debug.Log($"🎯 Using enhanced weapon: {enhancedWeapon.name}"); EquipWeapon(character, enhancedWeapon); } else { Debug.Log($"🔍 No enhanced weapon found, falling back to selection.weaponType: '{selection.weaponType}'"); EquipWeapon(character, selection.weaponType); } character.CharacterName = selection.characterName + " " + (nextPlayerCharacterPrefabIndex + 1); } currentPlacingCharacterInstance.GetComponent().enabled = false; // Optionally disable AI or other components that might interfere with placement // e.g., currentPlacingCharacterInstance.GetComponent()?.enabled = false; } else { FinalizePlayerPlacement(); } } private void HandleCharacterPlacement() { Ray ray = mainCamera.ScreenPointToRay(Input.mousePosition); Debug.DrawRay(ray.origin, ray.direction * 200f, Color.yellow); // Visualize the ray if (Physics.Raycast(ray, out RaycastHit hit, 200f, groundLayerMask)) { Vector3 targetPosition = hit.point; // Clamp position to playerSpawnArea bound (X and Z axis) targetPosition.x = Mathf.Clamp(targetPosition.x, playerSpawnBounds.min.x, playerSpawnBounds.max.x); targetPosition.z = Mathf.Clamp(targetPosition.z, playerSpawnBounds.min.z, playerSpawnBounds.max.z); // Y is determined by the raycast hit on the ground // --- Direct Y Position Calculation for Mouse-Following Character --- // This approach was found to be more stable than repeatedly calling AdjustCharacterOnGround. // It assumes a standard Unity CapsuleCollider where the pivot is at the center. Vector3 finalCharacterPosition = targetPosition; // targetPosition.y is from the ground hit (hit.point.y) CapsuleCollider capCollider = currentPlacingCharacterInstance.GetComponent(); if (capCollider != null) { // For a capsule, its pivot is typically at its center. // To place its bottom on targetPosition.y (ground level), // its center (pivot) needs to be at targetPosition.y + (height / 2). // We also account for the character's local scale. finalCharacterPosition.y = targetPosition.y + (capCollider.height * currentPlacingCharacterInstance.transform.localScale.y / 2f); } else { // Fallback if not a capsule or if a different pivot is used. // This might need adjustment based on your character's actual pivot and collider. Debug.LogWarning($"'{currentPlacingCharacterInstance.name}' does not have a CapsuleCollider. Using default Y offset for placement. Adjust if incorrect."); finalCharacterPosition.y = targetPosition.y + 0.5f; // Example offset, adjust as needed } currentPlacingCharacterInstance.transform.position = finalCharacterPosition; // AdjustCharacterOnGround(currentPlacingCharacterInstance); // No longer called every frame for mouse-follow } else { } // Else mouse is not over valid ground, character stays at last valid position or initial spawn if (Input.GetMouseButtonDown(0)) { // Left-click to place character // Check if the current position is valid (within bounds and not overlapping) // The position is already clamped and on ground due to the logic above. // We primarily need to check for overlaps. if (!IsOverlappingOtherCharacters(currentPlacingCharacterInstance)) { PlaceCurrentCharacter(); } else { Debug.LogWarning("Cannot place character: Overlapping with another character."); // Optionally, you could provide feedback to the player about the overlap (e.g. change color?). } } if (Input.GetMouseButtonDown(1)) { // Right-click to cancel placement if (placedPlayerCharacters.Count > 0) { FinalizePlayerPlacement(); } else { } } } private void PlaceCurrentCharacter() { placedPlayerCharacters.Add(currentPlacingCharacterInstance); currentPlacingCharacterInstance = null; nextPlayerCharacterPrefabIndex++; SpawnNextPlayerCharacterForPlacement(); // Spawn next character for placement } private void AdjustCharacterOnGround(GameObject character) { if (character == null) { return; } Collider charCollider = character.GetComponent(); if (charCollider != null) { // This ensured the lowest point of the collider is at character.transform.position.y // (which should be the ground hit point) // currentY is the Y-coordinate of the character's pivot. // We want the character's collider bottom (feet) to be at this Y-level. float currentY = character.transform.position.y; float colliderMinYWorld = charCollider.bounds.min.y; float verticalOffset = currentY - colliderMinYWorld; const float tolerance = 0.001f; // Small tolerance to avoid floating point issues if (Mathf.Abs(verticalOffset) > tolerance) { character.transform.position += Vector3.up * verticalOffset; } } else { Debug.LogWarning($"Character: {character.name} has no collider. Cannot accurately adjust to ground."); } } private bool IsOverlappingOtherCharacters(GameObject character) { Collider newCharCollider = character.GetComponent(); if (newCharCollider == null) { Debug.LogWarning($"Character: {character.name} has no collider. Cannot check for overlaps."); return false; // Or true to precent placement if no collider } Bounds newCharBounds = newCharCollider.bounds; foreach (GameObject placedCharacter in placedPlayerCharacters) { Collider otherCollider = placedCharacter.GetComponent(); if (otherCollider != null && newCharBounds.Intersects(otherCollider.bounds)) { return true; } } foreach (GameObject placedEnemy in placedEnemyCharacters) { Collider otherCollider = placedEnemy.GetComponent(); if (otherCollider != null && newCharBounds.Intersects(otherCollider.bounds)) { return true; } } return false; } private void FinalizePlayerPlacement() { isPlacingPlayerCharacters = false; if (currentPlacingCharacterInstance != null) { Destroy(currentPlacingCharacterInstance); // Destroy the preview instance if it wasn't placed currentPlacingCharacterInstance = null; } EndPlacementPhase(); // Here you might want to trigger the next phase of your game, e.g., start the battle. GameManager.Instance.StartBattle(placedPlayerCharacters, placedEnemyCharacters); Destroy(playerSpawnArea); Destroy(enemySpawnArea); playerSpawnArea = null; enemySpawnArea = null; } private void EndPlacementPhase() { // This method can be used to clean up or finalize the placement phase. // For example, you might want to enable AI, re-enable NavMeshAgents, etc. foreach (GameObject character in placedPlayerCharacters) { NavMeshAgent agent = character.GetComponent(); if (agent != null) { agent.enabled = true; // Re-enable NavMeshAgent } // Add carry capacity system to player characters AddCarryCapacityComponent(character); // Enable AI or other components as needed // e.g., character.GetComponent()?.enabled = true; } foreach (GameObject character in enemyCharacters) { NavMeshAgent agent = character.GetComponent(); if (agent != null) { agent.enabled = true; // Re-enable NavMeshAgent } // Add carry capacity system to enemy characters too (for consistency) AddCarryCapacityComponent(character); // Enable AI or other components as needed // e.g., character.GetComponent()?.enabled = true; } } /// /// Add CharacterCarryCapacity component to a character GameObject /// private void AddCarryCapacityComponent(GameObject characterObj) { if (characterObj == null) return; // Only add if it doesn't already exist var existingCapacity = characterObj.GetComponent(); if (existingCapacity != null) return; var character = characterObj.GetComponent(); if (character == null) return; // Add the carry capacity component var carryCapacity = characterObj.AddComponent(); // Configure based on character type/name ConfigureCarryCapacity(carryCapacity, character); Debug.Log($"📦 Added carry capacity system to {character.CharacterName}"); } /// /// Configure carry capacity settings based on character /// private void ConfigureCarryCapacity(CharacterCarryCapacity carryCapacity, Character character) { if (carryCapacity == null || character == null) return; // Adjust settings based on character type or name string characterName = character.CharacterName.ToLower(); if (characterName.Contains("warrior") || characterName.Contains("fighter")) { // Warriors can carry more carryCapacity.carrySystem.baseCarryCapacity = 60; carryCapacity.carrySystem.strengthMultiplier = 6f; } else if (characterName.Contains("rogue") || characterName.Contains("thief")) { // Rogues are more agile but carry less carryCapacity.carrySystem.baseCarryCapacity = 40; carryCapacity.carrySystem.strengthMultiplier = 4f; carryCapacity.carrySystem.lightLoadThreshold = 0.4f; // Stay light more often } else if (characterName.Contains("mage") || characterName.Contains("wizard")) { // Mages have lower physical capacity carryCapacity.carrySystem.baseCarryCapacity = 35; carryCapacity.carrySystem.strengthMultiplier = 3f; } else { // Default settings are already good for most characters carryCapacity.carrySystem.baseCarryCapacity = 50; carryCapacity.carrySystem.strengthMultiplier = 5f; } // Enable debug info for player characters if (character.CompareTag("Player")) { carryCapacity.showDebugInfo = true; } } private void PlaceEnemyCharacters() { Collider spawnAreaCollider = enemySpawnArea.GetComponent(); if (spawnAreaCollider == null) { Debug.LogError("Enemy spawn area does not have a collider component."); return; } Bounds spawnBounds = spawnAreaCollider.bounds; for (int i = enemySelections.Count - 1; i >= 0; i--) { var selection = enemySelections[i]; float randomX = UnityEngine.Random.Range(spawnBounds.min.x, spawnBounds.max.x); float randomZ = UnityEngine.Random.Range(spawnBounds.min.z, spawnBounds.max.z); Vector3 rayOrigin = new Vector3(randomX, enemySpawnArea.transform.position.y + 10f, randomZ); Vector3 spawnPosition = new Vector3(randomX, enemySpawnArea.transform.position.y, randomZ); // Raycast to find the ground if (Physics.Raycast(rayOrigin, Vector3.down, out RaycastHit hit, 200f, groundLayerMask)) { spawnPosition = hit.point; } else { Debug.LogWarning($"Raycast did not hit ground below ({randomX}, {randomZ}). Placing enemy at spawn area Y level."); } GameObject placedEnemy = Instantiate(enemyPrefab, spawnPosition, Quaternion.identity); Character character = placedEnemy.GetComponent(); if (character != null) { // Apply enemy data stats to the character ApplyEnemyDataToCharacter(character, selection.characterName); // Try to get enhanced weapon data first WeaponItem enhancedWeapon = GetEnhancedWeaponItem(selection.characterName, false); if (enhancedWeapon != null) { EquipWeapon(character, enhancedWeapon); } else { EquipWeapon(character, selection.weaponType); } character.CharacterName = selection.characterName + "_" + (i + 1); } AdjustCharacterOnGround(placedEnemy); // Adjust the enemy to the ground placedEnemyCharacters.Add(placedEnemy); } } }