| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- 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
- /// <summary>
- /// Initialize this weapon from a WeaponItem ScriptableObject
- /// </summary>
- /// <param name="itemData">The WeaponItem data to use</param>
- 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<Arrow>();
- 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<Collider>();
- 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;
- }
- }
|