/// <summary>Make sure to check if able to attack prior to calling as this routine doesn't check if we're able to attack.</summary> /// <returns>False if the attack isn't allowed or did not succeed, true if attack successful.</returns> internal override bool MeleeAttack(Mob target, bool isPrimaryHand, bool riposte) { if (!base.MeleeAttack(target, isPrimaryHand, riposte)) return false; // Are we using a weapon? Item weapon = isPrimaryHand ? this.GetEquipment(EquipableType.Primary) : this.GetEquipment(EquipableType.Secondary); if (weapon != null) { if (!weapon.IsWeapon) { _log.DebugFormat("Attack cancelled: {0} is not a weapon.", weapon.Name); return false; } } // Cool, so we're going to attack // First calculate the skill and animation to use Skill attackSkill; AttackAnimation attackAnim; GetSkillAndAnimForAttack(weapon, isPrimaryHand, out attackSkill, out attackAnim); OnAnimation(new AnimationEventArgs(attackAnim)); // Cue the animation //_log.DebugFormat("Attacking {0} with {1} in slot {2} using skill {3}", this.TargetMob.Name, weapon == null ? "Fist" : weapon.Name, // isPrimaryHand ? "Primary" : "Secondary", attackSkill.ToString("f")); // Next figure out the potential damage int potDamage = GetWeaponDamage(target, weapon); int damage = 0; //_log.DebugFormat("Potential damage calculated as {0}", potDamage); // If potential damage is more than zero we know it's POSSIBLE to hit the target if (potDamage > 0) { // TODO: Finishing blow attempt CheckForSkillUp(attackSkill, target, -5); CheckForSkillUp(Skill.Offense, target, -5); // Ok, so we know we CAN hit... let's see if we DO hit if (target.TryToHit(this, attackSkill, isPrimaryHand)) { // TODO: Augment potDamage with any Beserker damage bonuses int minHitDmg = 1; int maxHitDmg = this.GetMaxDamage(potDamage, attackSkill); // Level cap the damage if (this.Level < 10) maxHitDmg = Math.Min(maxHitDmg, 20); else if (this.Level < 20) maxHitDmg = Math.Min(maxHitDmg, 40); // TODO: apply damage bonuses (affects min and max dmg as well as hate) // TODO: adjust min damage with any applicable item or buff bonuses maxHitDmg = Math.Max(maxHitDmg, minHitDmg); // Ensure max is at least min Random rand = new Random(); damage = rand.Next(minHitDmg, maxHitDmg + 1); //_log.DebugFormat("Damage calculated to {0} (min {1}, max {2}, str {3}, skill {4}, pot dmg {5}, lvl {6})", damage, minHitDmg, // maxHitDmg, this.STR, GetSkillLevel(attackSkill), potDamage, this.Level); // With damage now calculated, see if the mob can avoid or mitigate damage = target.TryToAvoidDamage(this, damage); if (damage > 0) { damage = target.TryToMitigateDamage(this, damage, minHitDmg); // wasn't avoided, try to mitigate // TODO: apply any damage bonuses (is this the right term... bonuses?... wasn't that done by now? // TODO: try a critical hit (why are we trying this after the mitigation?) } //_log.DebugFormat("Damage after avoidance and mitigation: {0}", damage); } else { // We missed //_log.Debug("Missed."); } // TODO: riposte // TODO: strikethrough } else damage = (int)AttackAvoidanceType.Invulnerable; target.Damage(this, damage, 0, attackSkill); // Send attack damage - even zero and negative damage BreakSneakiness(); // TODO: weapon procs if (damage > 0) { // TODO: handle lifetap effects? // TODO: trigger defensive procs return true; } else return false; }
internal override bool MeleeAttack(Mob target, bool isPrimaryHand, bool riposte) { if (!base.MeleeAttack(target, isPrimaryHand, riposte)) return false; FaceMob(target); // Are we using a weapon? Item weapon = isPrimaryHand ? this.GetEquipment(EquipableType.Primary) : this.GetEquipment(EquipableType.Secondary); // Not much from the weapon is factored into the attack - just the skill type for the correct animations. if (weapon != null) { if (!isPrimaryHand && weapon.ItemType == (byte)ItemType.Shield) { _log.DebugFormat("{0} attacking with {1}({2}) in secondary - attack with shield cancelled.", this.Name, weapon.Name, weapon.ItemID); return false; } } // First calculate the skill and animation to use Skill attackSkill; AttackAnimation attackAnim; GetSkillAndAnimForAttack(weapon, isPrimaryHand, out attackSkill, out attackAnim); OnAnimation(new AnimationEventArgs(attackAnim)); // Cue the animation _log.DebugFormat("NPC attacking {0} with {1} in slot {2} using skill {3}", this.TargetMob.Name, weapon == null ? "Fist" : weapon.Name, isPrimaryHand ? "Primary" : "Secondary", attackSkill.ToString("f")); // Next figure out the potential damage int potDamage = GetWeaponDamage(target, weapon); int damage = 0; //_log.DebugFormat("Potential damage calculated as {0} for {1}", potDamage, this.Name); // If potential damage is more than zero we know it's POSSIBLE to hit the target if (potDamage > 0) { // Elemental & Bane dmg added differently than for PCs - where PCs would use the potential dmg (which has elem & bane included) // to figure max dmg, NPCs use the built-in min & max dmg values along with any elem and/or bane dmg. int elemBaneDmg = 0; if (weapon != null) { if (weapon.BaneDmgBody == (int)target.BodyType) elemBaneDmg += weapon.BaneDmgBodyAmt; if (weapon.BaneDmgRace == target.Race) elemBaneDmg += weapon.BaneDmgRaceAmt; if (weapon.ElemDmgAmt > 0) { // TODO: check for other's resist } } int minDmg = _minDmg + elemBaneDmg; int maxDmg = _maxDmg + elemBaneDmg; // Ok, so we know we CAN hit... let's see if we DO hit if (target is ZonePlayer && ((ZonePlayer)target).Sitting) { _log.DebugFormat("{0} is sitting - {1} hitting for max damage {2}", ((ZonePlayer)target).Name, this.Name, maxDmg); damage = maxDmg; } else if (target.TryToHit(this, attackSkill, isPrimaryHand)) { Random rand = new Random(); damage = rand.Next(minDmg, maxDmg + 1); //_log.DebugFormat("{4}'s damage calculated to {0} (min {1}, max {2}, pot dmg {3})", damage, minDmg, maxDmg, potDamage, this.Name); // With damage now calculated, see if the mob can avoid and mitigate damage = target.TryToAvoidDamage(this, damage); if (damage > 0) { damage = target.TryToMitigateDamage(this, damage, minDmg); // wasn't avoided, try to mitigate // TODO: apply any damage bonuses (is this the right term... bonuses?... wasn't that done by now? // TODO: try a critical hit (why are we trying this after the mitigation?) } //_log.DebugFormat("NPC damage after avoidance and mitigation: {0}", damage); } //else // _log.DebugFormat("{0} missed {1}.", this.Name, this.TargetMob.Name); // TODO: cancel a riposte of a riposte } else damage = (int)AttackAvoidanceType.Invulnerable; target.Damage(this, damage, 0, attackSkill); // Send attack damage - even zero and negative damage BreakSneakiness(); // TODO: weapon procs // TODO: check if we're getting a riposte back? if (damage > 0) { // TODO: handle lifetap effects? // TODO: trigger defensive procs return true; } else return false; }