using UnityEngine; using System.Collections; public abstract class Weapon : MonoBehaviour { public string weaponName; public string description; public int damage; public float attackSpeed; public GameObject weaponModel; // Assign the 3D model for the weapon // Reference to the character wielding this weapon public Character Wielder { get; private set; } // Track if an attack is currently in progress protected bool isAttacking = false; public float lastAttackTime { get; private set; } // Abstract properties that must be implemented by derived classes public abstract int MinDamage { get; } public abstract int MaxDamage { get; } public abstract int Range { get; } public abstract int WeaponModifier { get; set; } protected int weaponModifier = 0; // Base modifier for the weapon, can be overridden by subclasses protected int baseWeaponModifier = 0; // Base modifier for the weapon, used for calculations public float AttackStarted { get; private set; } public void SetWielder(Character character) { Wielder = character; if (Wielder == null) { Debug.LogWarning($"Weapon {weaponName} - SetWielder called with null character!"); } } public virtual int GetDamage() { return UnityEngine.Random.Range(MinDamage, MaxDamage + 1); } public virtual bool IsInRange(Vector3 attackerPosition, Vector3 targetPosition) { float distance = Vector3.Distance(attackerPosition, targetPosition); return distance <= Range; } public bool IsAttacking() { return isAttacking; } // Method to perform attack - can be called by the character public virtual void Attack(GameObject target) { if (Wielder == null) { Debug.LogError($"Cannot attack - {weaponName} has no wielder!"); return; } if (isAttacking) { Debug.Log($"attack started {AttackStarted} time now {Time.time}"); ; Debug.Log($"{Wielder.CharacterName} is already attacking!"); return; } Character targetCharacter = target.GetComponent(); if (targetCharacter == null) { Debug.LogError($"Target {target.name} is not a character!"); return; } if (!targetCharacter.IsTargetable) { Debug.Log($"Cannot attack {targetCharacter.CharacterName} - target is not targetable!"); return; } if (!IsInRange(Wielder.transform.position, target.transform.position)) { Debug.Log($"{Wielder.CharacterName} is not in range to attack {targetCharacter.CharacterName} with {weaponName}"); return; } AttackStarted = Time.time; // Start the attack coroutine StartCoroutine(PerformAttackSequence(targetCharacter)); } protected virtual IEnumerator PerformAttackSequence(Character target) { isAttacking = true; lastAttackTime = Time.time; 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; } // 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; Debug.Log($"=== 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) { int damageDealt = GetDamage(); Debug.Log($"HIT! Damage Roll: {damageDealt}"); target.TakeDamage(damageDealt); Debug.Log($"Target Health After: {target.CurrentHealth}/{target.MaxHealth}"); Debug.Log($"{Wielder.CharacterName} hits {target.CharacterName} with {weaponName} for {damageDealt} damage!"); } else { Debug.Log($"MISS! {attackRoll} < {target.ArmorClass}"); Debug.Log($"Target Health After: {healthBefore}/{target.MaxHealth} (no change)"); Debug.Log($"{Wielder.CharacterName} misses {target.CharacterName} with {weaponName}!"); } Debug.Log($"=== END ATTACK ==="); isAttacking = false; } }