SimpleBow.cs 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. using UnityEngine;
  2. using System.Collections;
  3. public class SimpleBow : Weapon
  4. {
  5. [Header("Bow Settings")]
  6. public GameObject arrowPrefab; // Assign arrow prefab in inspector
  7. public WeaponItem weaponItemData; // Reference to the ScriptableObject data
  8. public SimpleBow() : base()
  9. {
  10. weaponName = "Simple Bow";
  11. description = "A basic bow for shooting arrows.";
  12. attackSpeed = 2.0f; // Example: 2 second between shots
  13. weaponModel = null; // Assign a 3D model if available
  14. baseWeaponModifier = weaponModifier;
  15. }
  16. public override int MinDamage => weaponItemData != null ? weaponItemData.minDamage : 1;
  17. public override int MaxDamage => weaponItemData != null ? weaponItemData.maxDamage : 6;
  18. public override int Range => weaponItemData != null ? weaponItemData.range : 100;
  19. public override int WeaponModifier { get => weaponModifier; set => weaponModifier = value; }
  20. public int closeRangePenalty = 3; // Penalty for close
  21. public int closeRange = 5; // Close range threshold in units
  22. /// <summary>
  23. /// Initialize this weapon from a WeaponItem ScriptableObject
  24. /// </summary>
  25. /// <param name="itemData">The WeaponItem data to use</param>
  26. public void InitializeFromItem(WeaponItem itemData)
  27. {
  28. if (itemData == null) return;
  29. weaponItemData = itemData;
  30. weaponName = itemData.itemName;
  31. description = itemData.description;
  32. attackSpeed = itemData.attackSpeed;
  33. weaponModifier = itemData.weaponModifier;
  34. baseWeaponModifier = itemData.weaponModifier;
  35. weaponModel = itemData.model3D;
  36. // Set arrow prefab if provided in the item data
  37. if (itemData.arrowPrefab != null)
  38. {
  39. arrowPrefab = itemData.arrowPrefab;
  40. }
  41. Debug.Log($"SimpleBow initialized from item data: {weaponName}");
  42. }
  43. protected override IEnumerator PerformAttackSequence(Character target)
  44. {
  45. isAttacking = true;
  46. Debug.Log($"{Wielder.CharacterName} begins attacking {target.CharacterName} with {weaponName} (attack speed: {attackSpeed}s)");
  47. // Wait for the attack speed duration
  48. yield return new WaitForSeconds(attackSpeed);
  49. // Check if target is still in range after attack speed delay
  50. if (!IsInRange(Wielder.transform.position, target.transform.position))
  51. {
  52. Debug.Log($"{target.CharacterName} moved out of range before {Wielder.CharacterName}'s attack completed!");
  53. isAttacking = false;
  54. yield break;
  55. }
  56. // Check if target is still targetable (not dead)
  57. if (!target.IsTargetable)
  58. {
  59. Debug.Log($"{target.CharacterName} is no longer targetable!");
  60. isAttacking = false;
  61. yield break;
  62. }
  63. if (Vector3.Distance(Wielder.transform.position, target.transform.position) < closeRange)
  64. {
  65. Debug.Log($"target {target.CharacterName} is too close for {Wielder.CharacterName} to use {weaponName} efficiently, getting a penalty of {closeRangePenalty} to the attack roll.");
  66. WeaponModifier = baseWeaponModifier - closeRangePenalty;
  67. }
  68. // Perform hit calculation (d20 + weapon modifier vs target armor class)
  69. int d20Roll = Random.Range(1, 21);
  70. int attackRoll = d20Roll + WeaponModifier;
  71. bool hits = attackRoll >= target.ArmorClass;
  72. int healthBefore = target.CurrentHealth;
  73. int damageDealt = hits ? GetDamage() : 0;
  74. Debug.Log($"=== BOW ATTACK DEBUG ===");
  75. Debug.Log($"Attacker: {Wielder.CharacterName} with {weaponName}");
  76. Debug.Log($"Target: {target.CharacterName}");
  77. Debug.Log($"D20 Roll: {d20Roll} + Weapon Modifier: {WeaponModifier} = Attack Roll: {attackRoll}");
  78. Debug.Log($"Target AC: {target.ArmorClass}");
  79. Debug.Log($"Target Health Before: {healthBefore}/{target.MaxHealth}");
  80. if (hits)
  81. {
  82. Debug.Log($"HIT! Arrow will deal {damageDealt} damage when it reaches target");
  83. }
  84. else
  85. {
  86. Debug.Log($"MISS! {attackRoll} < {target.ArmorClass} - Arrow will miss target");
  87. }
  88. // Spawn arrow projectile
  89. SpawnArrow(target, hits, damageDealt);
  90. isAttacking = false;
  91. }
  92. private void SpawnArrow(Character target, bool willHit, int damage)
  93. {
  94. if (arrowPrefab == null)
  95. {
  96. Debug.LogError($"Arrow prefab not assigned to {weaponName}!");
  97. // Fallback to direct damage application
  98. if (willHit)
  99. {
  100. target.TakeDamage(damage);
  101. }
  102. return;
  103. }
  104. // Calculate arrow spawn position (slightly in front of wielder)
  105. Vector3 spawnPosition = Wielder.transform.position + Wielder.transform.forward * 0.5f + Vector3.up * 1.0f;
  106. // Instantiate arrow
  107. GameObject arrowInstance = Instantiate(arrowPrefab, spawnPosition, Quaternion.identity);
  108. // Try to get Arrow component, if not found, add it
  109. Arrow arrowScript = arrowInstance.GetComponent<Arrow>();
  110. if (arrowScript == null)
  111. {
  112. Debug.LogError($"The arrow prefab '{arrowPrefab.name}' is missing the 'Arrow' script component. Destroying spawned arrow.", arrowPrefab);
  113. Destroy(arrowInstance);
  114. if (willHit)
  115. {
  116. // Fallback to direct damage application
  117. target.TakeDamage(damage);
  118. }
  119. return;
  120. }
  121. if (willHit)
  122. {
  123. // Raycast to find hit point on collider
  124. Vector3 targetPosition;
  125. Collider targetCollider = target.GetComponent<Collider>();
  126. if (targetCollider != null)
  127. {
  128. // Aim at the center of the collider
  129. Vector3 center = targetCollider.bounds.center;
  130. Vector3 extents = targetCollider.bounds.extents;
  131. // Add a small random offset within the collider's bounds for variety
  132. Vector3 randomOffset = new Vector3(
  133. Random.Range(-extents.x * 0.7f, extents.x * 0.7f),
  134. Random.Range(-extents.y * 0.7f, extents.y * 0.7f),
  135. Random.Range(-extents.z * 0.7f, extents.z * 0.7f)
  136. );
  137. targetPosition = center + randomOffset;
  138. }
  139. else
  140. {
  141. // Fallback: use transform position
  142. targetPosition = target.transform.position + Vector3.up * 0.5f; // Aim roughly at chest height
  143. }
  144. Vector3 direction = (targetPosition - spawnPosition).normalized;
  145. RaycastHit hit;
  146. Vector3 hitPoint = targetPosition; // fallback to center/randomized
  147. if (targetCollider != null && targetCollider.Raycast(new Ray(spawnPosition, direction), out hit, 100f))
  148. {
  149. hitPoint = hit.point;
  150. arrowScript.Initialize(spawnPosition, hitPoint, target, damage);
  151. }
  152. else
  153. {
  154. // fallback: center of target
  155. arrowScript.Initialize(spawnPosition, hitPoint, target, damage);
  156. }
  157. Debug.Log($"Arrow will hit {target.CharacterName} at {hitPoint} for {damage} damage!");
  158. }
  159. else
  160. {
  161. // Arrow will miss - calculate miss position
  162. Vector3 missPosition = CalculateMissPosition(target.transform.position);
  163. Debug.Log($"Arrow will miss! Calculated miss position: {missPosition}");
  164. // Arrow will miss - call InitializeMiss directly
  165. arrowScript.InitializeMiss(spawnPosition, missPosition);
  166. }
  167. }
  168. private Vector3 CalculateMissPosition(Vector3 targetPosition)
  169. {
  170. // Calculate a random miss position around the target
  171. float missDistance = Random.Range(1f, 3f);
  172. float missAngle = Random.Range(0f, 360f) * Mathf.Deg2Rad;
  173. Vector3 missOffset = new Vector3(
  174. Mathf.Cos(missAngle) * missDistance,
  175. 0f,
  176. Mathf.Sin(missAngle) * missDistance
  177. );
  178. return targetPosition + missOffset;
  179. }
  180. }