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;
}
}