using UnityEngine; using System.Collections; public class SimpleBow : Weapon { [Header("Bow Settings")] public GameObject arrowPrefab; // Assign arrow prefab in inspector public WeaponItem weaponItemData; // Reference to the ScriptableObject data public SimpleBow() : base() { weaponName = "Simple Bow"; description = "A basic bow for shooting arrows."; attackSpeed = 2.0f; // Example: 2 second between shots weaponModel = null; // Assign a 3D model if available baseWeaponModifier = weaponModifier; } public override int MinDamage => weaponItemData != null ? weaponItemData.minDamage : 1; public override int MaxDamage => weaponItemData != null ? weaponItemData.maxDamage : 6; public override int Range => weaponItemData != null ? weaponItemData.range : 100; public override int WeaponModifier { get => weaponModifier; set => weaponModifier = value; } public int closeRangePenalty = 3; // Penalty for close public int closeRange = 5; // Close range threshold in units /// /// Initialize this weapon from a WeaponItem ScriptableObject /// /// The WeaponItem data to use public void InitializeFromItem(WeaponItem itemData) { if (itemData == null) return; weaponItemData = itemData; weaponName = itemData.itemName; description = itemData.description; attackSpeed = itemData.attackSpeed; weaponModifier = itemData.weaponModifier; baseWeaponModifier = itemData.weaponModifier; weaponModel = itemData.model3D; // Set arrow prefab if provided in the item data if (itemData.arrowPrefab != null) { arrowPrefab = itemData.arrowPrefab; } Debug.Log($"SimpleBow initialized from item data: {weaponName}"); } protected override IEnumerator PerformAttackSequence(Character target) { isAttacking = true; Debug.Log($"{Wielder.CharacterName} begins attacking {target.CharacterName} with {weaponName} (attack speed: {attackSpeed}s)"); // Wait for the attack speed duration yield return new WaitForSeconds(attackSpeed); // Check if target is still in range after attack speed delay if (!IsInRange(Wielder.transform.position, target.transform.position)) { Debug.Log($"{target.CharacterName} moved out of range before {Wielder.CharacterName}'s attack completed!"); isAttacking = false; yield break; } // Check if target is still targetable (not dead) if (!target.IsTargetable) { Debug.Log($"{target.CharacterName} is no longer targetable!"); isAttacking = false; yield break; } if (Vector3.Distance(Wielder.transform.position, target.transform.position) < closeRange) { Debug.Log($"target {target.CharacterName} is too close for {Wielder.CharacterName} to use {weaponName} efficiently, getting a penalty of {closeRangePenalty} to the attack roll."); WeaponModifier = baseWeaponModifier - closeRangePenalty; } // Perform hit calculation (d20 + weapon modifier vs target armor class) int d20Roll = Random.Range(1, 21); int attackRoll = d20Roll + WeaponModifier; bool hits = attackRoll >= target.ArmorClass; int healthBefore = target.CurrentHealth; int damageDealt = hits ? GetDamage() : 0; Debug.Log($"=== BOW ATTACK DEBUG ==="); Debug.Log($"Attacker: {Wielder.CharacterName} with {weaponName}"); Debug.Log($"Target: {target.CharacterName}"); Debug.Log($"D20 Roll: {d20Roll} + Weapon Modifier: {WeaponModifier} = Attack Roll: {attackRoll}"); Debug.Log($"Target AC: {target.ArmorClass}"); Debug.Log($"Target Health Before: {healthBefore}/{target.MaxHealth}"); if (hits) { Debug.Log($"HIT! Arrow will deal {damageDealt} damage when it reaches target"); } else { Debug.Log($"MISS! {attackRoll} < {target.ArmorClass} - Arrow will miss target"); } // Spawn arrow projectile SpawnArrow(target, hits, damageDealt); isAttacking = false; } private void SpawnArrow(Character target, bool willHit, int damage) { if (arrowPrefab == null) { Debug.LogError($"Arrow prefab not assigned to {weaponName}!"); // Fallback to direct damage application if (willHit) { target.TakeDamage(damage); } return; } // Calculate arrow spawn position (slightly in front of wielder) Vector3 spawnPosition = Wielder.transform.position + Wielder.transform.forward * 0.5f + Vector3.up * 1.0f; // Instantiate arrow GameObject arrowInstance = Instantiate(arrowPrefab, spawnPosition, Quaternion.identity); // Try to get Arrow component, if not found, add it Arrow arrowScript = arrowInstance.GetComponent(); if (arrowScript == null) { Debug.LogError($"The arrow prefab '{arrowPrefab.name}' is missing the 'Arrow' script component. Destroying spawned arrow.", arrowPrefab); Destroy(arrowInstance); if (willHit) { // Fallback to direct damage application target.TakeDamage(damage); } return; } if (willHit) { // Raycast to find hit point on collider Vector3 targetPosition; Collider targetCollider = target.GetComponent(); if (targetCollider != null) { // Aim at the center of the collider Vector3 center = targetCollider.bounds.center; Vector3 extents = targetCollider.bounds.extents; // Add a small random offset within the collider's bounds for variety Vector3 randomOffset = new Vector3( Random.Range(-extents.x * 0.7f, extents.x * 0.7f), Random.Range(-extents.y * 0.7f, extents.y * 0.7f), Random.Range(-extents.z * 0.7f, extents.z * 0.7f) ); targetPosition = center + randomOffset; } else { // Fallback: use transform position targetPosition = target.transform.position + Vector3.up * 0.5f; // Aim roughly at chest height } Vector3 direction = (targetPosition - spawnPosition).normalized; RaycastHit hit; Vector3 hitPoint = targetPosition; // fallback to center/randomized if (targetCollider != null && targetCollider.Raycast(new Ray(spawnPosition, direction), out hit, 100f)) { hitPoint = hit.point; arrowScript.Initialize(spawnPosition, hitPoint, target, damage); } else { // fallback: center of target arrowScript.Initialize(spawnPosition, hitPoint, target, damage); } Debug.Log($"Arrow will hit {target.CharacterName} at {hitPoint} for {damage} damage!"); } else { // Arrow will miss - calculate miss position Vector3 missPosition = CalculateMissPosition(target.transform.position); Debug.Log($"Arrow will miss! Calculated miss position: {missPosition}"); // Arrow will miss - call InitializeMiss directly arrowScript.InitializeMiss(spawnPosition, missPosition); } } private Vector3 CalculateMissPosition(Vector3 targetPosition) { // Calculate a random miss position around the target float missDistance = Random.Range(1f, 3f); float missAngle = Random.Range(0f, 360f) * Mathf.Deg2Rad; Vector3 missOffset = new Vector3( Mathf.Cos(missAngle) * missDistance, 0f, Mathf.Sin(missAngle) * missDistance ); return targetPosition + missOffset; } }