static int GetBonusOrPenaltyByEnemyType(Entity.DaggerfallEntity attacker, Entity.EnemyEntity AITarget) { if (attacker == null || AITarget == null) { return(0); } int damage = 0; // Apply bonus or penalty by opponent type. // In classic this is broken and only works if the attack is done with a weapon that has the maximum number of enchantments. if (AITarget.GetEnemyGroup() == DFCareer.EnemyGroups.Undead) { if (((int)attacker.Career.UndeadAttackModifier & (int)DFCareer.AttackModifier.Bonus) != 0) { damage += attacker.Level; } if (((int)attacker.Career.UndeadAttackModifier & (int)DFCareer.AttackModifier.Phobia) != 0) { damage -= attacker.Level; } } else if (AITarget.GetEnemyGroup() == DFCareer.EnemyGroups.Daedra) { if (((int)attacker.Career.DaedraAttackModifier & (int)DFCareer.AttackModifier.Bonus) != 0) { damage += attacker.Level; } if (((int)attacker.Career.DaedraAttackModifier & (int)DFCareer.AttackModifier.Phobia) != 0) { damage -= attacker.Level; } } else if (AITarget.GetEnemyGroup() == DFCareer.EnemyGroups.Humanoid) { if (((int)attacker.Career.HumanoidAttackModifier & (int)DFCareer.AttackModifier.Bonus) != 0) { damage += attacker.Level; } if (((int)attacker.Career.HumanoidAttackModifier & (int)DFCareer.AttackModifier.Phobia) != 0) { damage -= attacker.Level; } } else if (AITarget.GetEnemyGroup() == DFCareer.EnemyGroups.Animals) { if (((int)attacker.Career.AnimalsAttackModifier & (int)DFCareer.AttackModifier.Bonus) != 0) { damage += attacker.Level; } if (((int)attacker.Career.AnimalsAttackModifier & (int)DFCareer.AttackModifier.Phobia) != 0) { damage -= attacker.Level; } } return(damage); }
public static int CalculateWeaponDamage(Entity.DaggerfallEntity attacker, Entity.DaggerfallEntity target, FPSWeapon onScreenWeapon) { // In classic, hand-to-hand damage is not affected by the strength modifier, by the type of swing or by hand-to-hand proficiency. // Both the game manual and strength attribute description say that the strength modifier applies to hand-to-hand damage, // and hand-to-hand proficiency would have no effect if it didn't do something for damage and chance to hit. int damage_low = 0; int damage_high = 0; int damage_result = 0; // TODO: Damage from AI characters. if (attacker == GameManager.Instance.PlayerEntity) { Items.DaggerfallUnityItem weapon; if (GameManager.Instance.WeaponManager.UsingRightHand) { weapon = attacker.ItemEquipTable.GetItem(Items.EquipSlots.RightHand); } else { weapon = attacker.ItemEquipTable.GetItem(Items.EquipSlots.LeftHand); } if (weapon == null) { damage_low = CalculateHandToHandMinDamage(attacker.Skills.HandToHand); damage_high = CalculateHandToHandMaxDamage(attacker.Skills.HandToHand); } else { damage_low = weapon.GetBaseDamageMin(); damage_high = weapon.GetBaseDamageMax(); } damage_result = UnityEngine.Random.Range(damage_low, damage_high + 1); if (onScreenWeapon != null) { // Apply swing modifier. if (onScreenWeapon.WeaponState == WeaponStates.StrikeUp) { damage_result += -2; } if (onScreenWeapon.WeaponState == WeaponStates.StrikeDownLeft || onScreenWeapon.WeaponState == WeaponStates.StrikeDownRight) { damage_result += 1; } if (onScreenWeapon.WeaponState == WeaponStates.StrikeDown) { damage_result += 3; } // Apply weapon expertise modifier if (weapon != null && ((int)attacker.Career.ExpertProficiencies & weapon.GetWeaponSkillUsed()) != 0) { damage_result += ((attacker.Level / 3) + 1); } // Apply hand-to-hand expertise modifier else if (weapon == null && ((int)attacker.Career.ExpertProficiencies & (int)(DaggerfallConnect.DFCareer.ProficiencyFlags.HandToHand)) != 0) { damage_result += ((attacker.Level / 3) + 1); } } // Apply the strength modifier. damage_result += DamageModifier(attacker.Stats.Strength); // Apply the material modifier if (weapon != null) { damage_result += weapon.GetMaterialDamageModifier(); } // 0 damage is possible. Plays no hit sound or blood splash. damage_result = Mathf.Max(0, damage_result); } return(damage_result); }
public static bool CalculateSuccessfulHit(Entity.DaggerfallEntity attacker, Entity.DaggerfallEntity target, int chanceToHitMod, Items.DaggerfallUnityItem weapon, int struckBodyPart) { if (attacker == null || target == null) { return(false); } int chanceToHit = chanceToHitMod; Entity.PlayerEntity player = GameManager.Instance.PlayerEntity; Entity.EnemyEntity AITarget = target as Entity.EnemyEntity; int armorValue = 0; // Apply hit mod from character biography if (target == player) { chanceToHit -= player.BiographyAvoidHitMod; } // Get armor value for struck body part if (struckBodyPart <= target.ArmorValues.Length) { armorValue = target.ArmorValues[struckBodyPart]; } chanceToHit += armorValue; // Apply adrenaline rush modifiers. if (attacker.Career.AdrenalineRush && attacker.CurrentHealth < (attacker.MaxHealth / 8)) { chanceToHit += 5; } if (target.Career.AdrenalineRush && target.CurrentHealth < (target.MaxHealth / 8)) { chanceToHit -= 5; } // Apply luck modifier. chanceToHit += ((attacker.Stats.Luck - target.Stats.Luck) / 10); // Apply agility modifier. chanceToHit += ((attacker.Stats.Agility - target.Stats.Agility) / 10); // Apply weapon material modifier. if (weapon != null) { chanceToHit += (weapon.GetWeaponMaterialModifier() * 10); } // Apply dodging modifier. // This modifier is bugged in classic and the attacker's dodging skill is used rather than the target's. // DF Chronicles says the dodging calculation is (dodging / 10), but it actually seems to be (dodging / 4). chanceToHit -= (target.Skills.Dodging / 4); // Apply critical strike modifier. if (UnityEngine.Random.Range(0, 100 + 1) < attacker.Skills.CriticalStrike) { chanceToHit += (attacker.Skills.CriticalStrike / 10); } // Apply monster modifier. if ((target != player) && (AITarget.EntityType == EntityTypes.EnemyMonster)) { chanceToHit += 40; } // DF Chronicles says -60 is applied at the end, but it actually seems to be -50. chanceToHit -= 50; Mathf.Clamp(chanceToHit, 3, 97); int roll = UnityEngine.Random.Range(0, 100 + 1); if (roll <= chanceToHit) { return(true); } else { return(false); } }
public static int CalculateWeaponDamage(Entity.DaggerfallEntity attacker, Entity.DaggerfallEntity target, FPSWeapon onscreenWeapon) { if (attacker == null || target == null) { return(0); } int damageLow = 0; int damageHigh = 0; int damageModifiers = 0; int baseDamage = 0; int damageResult = 0; int chanceToHitMod = 0; Items.DaggerfallUnityItem weapon = null; Entity.PlayerEntity player = GameManager.Instance.PlayerEntity; Entity.EnemyEntity AIAttacker = null; Entity.EnemyEntity AITarget = null; if (attacker != player) { AIAttacker = attacker as Entity.EnemyEntity; weapon = attacker.ItemEquipTable.GetItem(Items.EquipSlots.RightHand); if (weapon == null) { weapon = attacker.ItemEquipTable.GetItem(Items.EquipSlots.LeftHand); } } else { if (GameManager.Instance.WeaponManager.UsingRightHand) { weapon = attacker.ItemEquipTable.GetItem(Items.EquipSlots.RightHand); } } if (target != player) { AITarget = target as Entity.EnemyEntity; } if (weapon != null) { // If the attacker has a weapon equipped, check if the material is high enough to damage the target if (target.MinMetalToHit > (Items.WeaponMaterialTypes)weapon.NativeMaterialValue) { if (attacker == player) { DaggerfallUI.Instance.PopupMessage(UserInterfaceWindows.HardStrings.materialIneffective); } return(0); } // If the attacker has a weapon equipped, get the weapon's damage damageLow = weapon.GetBaseDamageMin(); damageHigh = weapon.GetBaseDamageMax(); short skillID = weapon.GetWeaponSkillIDAsShort(); chanceToHitMod = attacker.Skills.GetSkillValue(skillID); } else if (attacker == player) { // If the player is attacking with no weapon equipped, use hand-to-hand skill for damage damageLow = CalculateHandToHandMinDamage(attacker.Skills.HandToHand); damageHigh = CalculateHandToHandMaxDamage(attacker.Skills.HandToHand); chanceToHitMod = attacker.Skills.HandToHand; } if (AIAttacker != null) { // Note: In classic, for enemies that have weapons, the damage values in the enemy // definitions are overridden by the weapon stats. This is fine for enemy classes, // who have non-weapon damage values of 0, but for the monsters that use weapons, // they may have a better attack if they don't use a weapon. // In DF Unity, enemies are using whichever is more damaging, the weapon or non-weapon attack. int weaponAverage = ((damageLow + damageHigh) / 2); int noWeaponAverage = ((AIAttacker.MobileEnemy.MinDamage + AIAttacker.MobileEnemy.MaxDamage) / 2); if (noWeaponAverage > weaponAverage) { damageLow = AIAttacker.MobileEnemy.MinDamage; damageHigh = AIAttacker.MobileEnemy.MaxDamage; chanceToHitMod = attacker.Skills.HandToHand; } } baseDamage = UnityEngine.Random.Range(damageLow, damageHigh + 1); if (onscreenWeapon != null && (attacker == player)) { // Apply swing modifiers for player. // The Daggerfall manual groups diagonal slashes to the left and right as if they are the same, but they are different. // Classic does not apply swing modifiers to hand-to-hand. if (onscreenWeapon.WeaponState == WeaponStates.StrikeUp) { damageModifiers += -4; chanceToHitMod += 10; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDownRight) { damageModifiers += -2; chanceToHitMod += 5; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDownLeft) { damageModifiers += 2; chanceToHitMod += -5; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDown) { damageModifiers += 4; chanceToHitMod += -10; } } // Apply weapon proficiency modifiers for player. if ((attacker == player) && weapon != null && ((int)attacker.Career.ExpertProficiencies & weapon.GetWeaponSkillUsed()) != 0) { damageModifiers += ((attacker.Level / 3) + 1); chanceToHitMod += attacker.Level; } // Apply hand-to-hand proficiency modifiers for player. Hand-to-hand proficiencty is not applied in classic. else if ((attacker == player) && weapon == null && ((int)attacker.Career.ExpertProficiencies & (int)(DaggerfallConnect.DFCareer.ProficiencyFlags.HandToHand)) != 0) { damageModifiers += ((attacker.Level / 3) + 1); chanceToHitMod += attacker.Level; } // Apply modifiers for Skeletal Warrior. // In classic these appear to be applied after the swing and weapon proficiency modifiers but before all other // damage modifiers. Doing the same here. // DF Chronicles just says "Edged weapons inflict 1/2 damage" if (weapon != null && (target != player) && AITarget.CareerIndex == (int)Entity.MonsterCareers.SkeletalWarrior) { if (weapon.NativeMaterialValue == (int)Items.WeaponMaterialTypes.Silver) { baseDamage *= 2; damageModifiers *= 2; } if (weapon.GetWeaponSkillUsed() != (int)DaggerfallConnect.DFCareer.ProficiencyFlags.BluntWeapons) { baseDamage /= 2; damageModifiers /= 2; } } // Apply bonus or penalty by opponent type. // In classic this is broken and only works if the attack is done with a weapon that has the maximum number of enchantments. if ((target != player) && (AITarget.GetEnemyGroup() == DaggerfallConnect.DFCareer.EnemyGroups.Undead)) { if (((int)attacker.Career.UndeadAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Bonus) != 0) { damageModifiers += attacker.Level; } if (((int)attacker.Career.UndeadAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Phobia) != 0) { damageModifiers -= attacker.Level; } } else if ((target != player) && (AITarget.GetEnemyGroup() == DaggerfallConnect.DFCareer.EnemyGroups.Daedra)) { if (((int)attacker.Career.DaedraAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Bonus) != 0) { damageModifiers += attacker.Level; } if (((int)attacker.Career.DaedraAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Phobia) != 0) { damageModifiers -= attacker.Level; } } else if ((target != player) && (AITarget.GetEnemyGroup() == DaggerfallConnect.DFCareer.EnemyGroups.Humanoid)) { if (((int)attacker.Career.HumanoidAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Bonus) != 0) { damageModifiers += attacker.Level; } if (((int)attacker.Career.HumanoidAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Phobia) != 0) { damageModifiers -= attacker.Level; } } else if ((target != player) && (AITarget.GetEnemyGroup() == DaggerfallConnect.DFCareer.EnemyGroups.Animals)) { if (((int)attacker.Career.AnimalsAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Bonus) != 0) { damageModifiers += attacker.Level; } if (((int)attacker.Career.AnimalsAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Phobia) != 0) { damageModifiers -= attacker.Level; } } // Apply racial modifiers for player. if ((attacker == player) && weapon != null) { if (player.RaceTemplate.ID == (int)Entity.Races.DarkElf) { damageModifiers += (attacker.Level / 4); chanceToHitMod += (attacker.Level / 4); } else if (weapon.GetWeaponSkillUsed() == (int)DaggerfallConnect.DFCareer.ProficiencyFlags.MissileWeapons) { if (player.RaceTemplate.ID == (int)Entity.Races.WoodElf) { damageModifiers += (attacker.Level / 3); chanceToHitMod += (attacker.Level / 3); } } else if (player.RaceTemplate.ID == (int)Entity.Races.Redguard) { damageModifiers += (attacker.Level / 3); chanceToHitMod += (attacker.Level / 3); } } // Apply strength modifier for player or for AI characters using weapons. // The in-game display of the strength modifier in Daggerfall is incorrect. It is actually ((STR - 50) / 5). if ((attacker == player) || (weapon != null)) { damageModifiers += DamageModifier(attacker.Stats.Strength); } // Apply material modifier. // The in-game display in Daggerfall of weapon damages with material modifiers is incorrect. The material modifier is half of what the display suggests. if (weapon != null) { damageModifiers += weapon.GetWeaponMaterialModifier(); } // Choose struck body part int[] bodyParts = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6 }; int struckBodyPart = bodyParts[UnityEngine.Random.Range(0, bodyParts.Length)]; // Check for a successful hit. if (CalculateSuccessfulHit(attacker, target, chanceToHitMod, weapon, struckBodyPart) == true) { // 0 damage is possible. Creates no blood splash. damageResult = Mathf.Max(0, (baseDamage + damageModifiers)); } // If damage was done by a weapon, damage condition of weapon and armor of hit body part // In classic, shields are never damaged because the item in the equip slot of the hit // body part is all that is handled. // Here, if an equipped shield covered the hit body part, it takes damage instead. if (weapon != null && damageResult > 0) { weapon.DamageThroughPhysicalHit(damageResult, attacker); Items.DaggerfallUnityItem shield = target.ItemEquipTable.GetItem(Items.EquipSlots.LeftHand); bool shieldTakesDamage = false; if (shield != null) { Items.BodyParts[] protectedBodyParts = shield.GetShieldProtectedBodyParts(); for (int i = 0; (i < protectedBodyParts.Length) && !shieldTakesDamage; i++) { if (protectedBodyParts[i] == (Items.BodyParts)struckBodyPart) { shieldTakesDamage = true; } } } if (shieldTakesDamage) { shield.DamageThroughPhysicalHit(damageResult, target); } else { Items.EquipSlots hitSlot = Items.DaggerfallUnityItem.GetEquipSlotForBodyPart((Items.BodyParts)struckBodyPart); Items.DaggerfallUnityItem armor = target.ItemEquipTable.GetItem(hitSlot); if (armor != null) { armor.DamageThroughPhysicalHit(damageResult, target); } } } // If attack was by player or weapon-based, end here if ((attacker == player) || (weapon != null)) { return(damageResult); } // Handle multiple attacks by AI characters. else { if (AIAttacker.MobileEnemy.MaxDamage2 != 0 && (CalculateSuccessfulHit(attacker, target, chanceToHitMod, weapon, struckBodyPart) == true)) { baseDamage = UnityEngine.Random.Range(AIAttacker.MobileEnemy.MinDamage2, AIAttacker.MobileEnemy.MaxDamage2 + 1); damageResult += Mathf.Max(0, (baseDamage + damageModifiers)); } if (AIAttacker.MobileEnemy.MaxDamage3 != 0 && (CalculateSuccessfulHit(attacker, target, chanceToHitMod, weapon, struckBodyPart) == true)) { baseDamage = UnityEngine.Random.Range(AIAttacker.MobileEnemy.MinDamage3, AIAttacker.MobileEnemy.MaxDamage3 + 1); damageResult += Mathf.Max(0, (baseDamage + damageModifiers)); } return(damageResult); } }
public static int CalculateWeaponDamage(Entity.DaggerfallEntity attacker, Entity.DaggerfallEntity target, FPSWeapon onscreenWeapon) { if (attacker == null || target == null) { return(0); } int damageLow = 0; int damageHigh = 0; int damageModifiers = 0; int baseDamage = 0; int damageResult = 0; int chanceToHitMod = 0; Items.DaggerfallUnityItem weapon = null; Entity.PlayerEntity player = GameManager.Instance.PlayerEntity; Entity.EnemyEntity AIAttacker = null; Entity.EnemyEntity AITarget = null; if (attacker != player) { AIAttacker = attacker as Entity.EnemyEntity; } if (target != player) { AITarget = target as Entity.EnemyEntity; } // TODO: Get weapons of enemy classes and monsters. if (attacker == player) { if (GameManager.Instance.WeaponManager.UsingRightHand) { weapon = attacker.ItemEquipTable.GetItem(Items.EquipSlots.RightHand); } else { weapon = attacker.ItemEquipTable.GetItem(Items.EquipSlots.LeftHand); } } // If the player is attacking with no weapon equipped, use hand-to-hand skill for damage if (weapon == null && attacker == player) { damageLow = CalculateHandToHandMinDamage(attacker.Skills.HandToHand); damageHigh = CalculateHandToHandMaxDamage(attacker.Skills.HandToHand); chanceToHitMod = attacker.Skills.HandToHand; } // If a monster is attacking, use damage values from enemy definitions else if (weapon == null && AIAttacker != null) { damageLow = AIAttacker.MobileEnemy.MinDamage; damageHigh = AIAttacker.MobileEnemy.MaxDamage; chanceToHitMod = attacker.Skills.HandToHand; } // If the player is attacking with a weapon equipped, use the weapon's damage else if (attacker == player) { damageLow = weapon.GetBaseDamageMin(); damageHigh = weapon.GetBaseDamageMax(); short skillID = weapon.GetWeaponSkillID(); chanceToHitMod = attacker.Skills.GetSkillValue(skillID); } baseDamage = UnityEngine.Random.Range(damageLow, damageHigh + 1); if (onscreenWeapon != null && (attacker == player)) { // Apply swing modifiers for player. // The Daggerfall manual groups diagonal slashes to the left and right as if they are the same, but they are different. // Classic does not apply swing modifiers to hand-to-hand. if (onscreenWeapon.WeaponState == WeaponStates.StrikeUp) { damageModifiers += -4; chanceToHitMod += 10; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDownRight) { damageModifiers += -2; chanceToHitMod += 5; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDownLeft) { damageModifiers += 2; chanceToHitMod += -5; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDown) { damageModifiers += 4; chanceToHitMod += -10; } } // Apply weapon proficiency modifiers for player. if ((attacker == player) && weapon != null && ((int)attacker.Career.ExpertProficiencies & weapon.GetWeaponSkillUsed()) != 0) { damageModifiers += ((attacker.Level / 3) + 1); chanceToHitMod += attacker.Level; } // Apply hand-to-hand proficiency modifiers for player. Hand-to-hand proficiencty is not applied in classic. else if ((attacker == player) && weapon == null && ((int)attacker.Career.ExpertProficiencies & (int)(DaggerfallConnect.DFCareer.ProficiencyFlags.HandToHand)) != 0) { damageModifiers += ((attacker.Level / 3) + 1); chanceToHitMod += attacker.Level; } // Apply modifiers for Skeletal Warrior. // In classic these appear to be applied after the swing and weapon proficiency modifiers but before all other // damage modifiers. Doing the same here. // DF Chronicles just says "Edged weapons inflict 1/2 damage" if (weapon != null && (target != player) && AITarget.CareerIndex == (int)Entity.MonsterCareers.SkeletalWarrior) { if (weapon.NativeMaterialValue == (int)Items.WeaponMaterialTypes.Silver) { baseDamage *= 2; damageModifiers *= 2; } if (weapon.GetWeaponSkillUsed() != (int)DaggerfallConnect.DFCareer.ProficiencyFlags.BluntWeapons) { baseDamage /= 2; damageModifiers /= 2; } } // Apply bonus or penalty by opponent type. // In classic this is broken and only works if the attack is done with a weapon that has the maximum number of enchantments. if ((target != player) && (AITarget.GetEnemyGroup() == DaggerfallConnect.DFCareer.EnemyGroups.Undead)) { if (((int)attacker.Career.UndeadAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Bonus) != 0) { damageModifiers += attacker.Level; } if (((int)attacker.Career.UndeadAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Phobia) != 0) { damageModifiers -= attacker.Level; } } else if ((target != player) && (AITarget.GetEnemyGroup() == DaggerfallConnect.DFCareer.EnemyGroups.Daedra)) { if (((int)attacker.Career.DaedraAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Bonus) != 0) { damageModifiers += attacker.Level; } if (((int)attacker.Career.DaedraAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Phobia) != 0) { damageModifiers -= attacker.Level; } } else if ((target != player) && (AITarget.GetEnemyGroup() == DaggerfallConnect.DFCareer.EnemyGroups.Humanoid)) { if (((int)attacker.Career.HumanoidAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Bonus) != 0) { damageModifiers += attacker.Level; } if (((int)attacker.Career.HumanoidAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Phobia) != 0) { damageModifiers -= attacker.Level; } } else if ((target != player) && (AITarget.GetEnemyGroup() == DaggerfallConnect.DFCareer.EnemyGroups.Animals)) { if (((int)attacker.Career.AnimalsAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Bonus) != 0) { damageModifiers += attacker.Level; } if (((int)attacker.Career.AnimalsAttackModifier & (int)DaggerfallConnect.DFCareer.AttackModifier.Phobia) != 0) { damageModifiers -= attacker.Level; } } // Apply racial modifiers for player. if ((attacker == player) && weapon != null) { if (player.RaceTemplate.ID == (int)Entity.Races.DarkElf) { damageModifiers += (attacker.Level / 4); chanceToHitMod += (attacker.Level / 4); } else if (weapon.GetWeaponSkillUsed() == (int)DaggerfallConnect.DFCareer.ProficiencyFlags.MissileWeapons) { if (player.RaceTemplate.ID == (int)Entity.Races.WoodElf) { damageModifiers += (attacker.Level / 3); chanceToHitMod += (attacker.Level / 3); } } else if (player.RaceTemplate.ID == (int)Entity.Races.Redguard) { damageModifiers += (attacker.Level / 3); chanceToHitMod += (attacker.Level / 3); } } // Apply strength modifier for player or for AI characters using weapons. // The in-game display of the strength modifier in Daggerfall is incorrect. It is actually ((STR - 50) / 5). if ((attacker == player) || (weapon != null)) { damageModifiers += DamageModifier(attacker.Stats.Strength); } // Apply material modifier. // The in-game display in Daggerfall of weapon damages with material modifiers is incorrect. The material modifier is half of what the display suggests. if (weapon != null) { damageModifiers += weapon.GetWeaponMaterialModifier(); } // Check for a successful hit. if (CalculateSuccessfulHit(attacker, target, chanceToHitMod, weapon) == true) { // 0 damage is possible. Creates no blood splash. damageResult = Mathf.Max(0, (baseDamage + damageModifiers)); } // If attack was by player or weapon-based, end here if ((attacker == player) || (weapon != null)) { return(damageResult); } // Handle multiple attacks by AI characters. else { if (AIAttacker.MobileEnemy.MaxDamage2 != 0 && (CalculateSuccessfulHit(attacker, target, chanceToHitMod, weapon) == true)) { baseDamage = UnityEngine.Random.Range(AIAttacker.MobileEnemy.MinDamage2, AIAttacker.MobileEnemy.MaxDamage2 + 1); damageResult += Mathf.Max(0, (baseDamage + damageModifiers)); } if (AIAttacker.MobileEnemy.MaxDamage3 != 0 && (CalculateSuccessfulHit(attacker, target, chanceToHitMod, weapon) == true)) { baseDamage = UnityEngine.Random.Range(AIAttacker.MobileEnemy.MinDamage3, AIAttacker.MobileEnemy.MaxDamage3 + 1); damageResult += Mathf.Max(0, (baseDamage + damageModifiers)); } return(damageResult); } }
public static int CalculateAttackDamage(Entity.DaggerfallEntity attacker, Entity.DaggerfallEntity target, int weaponEquipSlot, int enemyAnimStateRecord) { if (attacker == null || target == null) { return(0); } int minBaseDamage = 0; int maxBaseDamage = 0; int damageModifiers = 0; int damage = 0; int chanceToHitMod = 0; int backstabbingLevel = 0; Entity.PlayerEntity player = GameManager.Instance.PlayerEntity; Items.DaggerfallUnityItem weapon = attacker.ItemEquipTable.GetItem((Items.EquipSlots)weaponEquipSlot); short skillID = 0; // Choose whether weapon-wielding enemies use their weapons or weaponless attacks. // In classic, weapon-wielding enemies use the damage values of their weapons // instead of their weaponless values. // For some enemies this gives lower damage than similar-tier monsters // and the weaponless values seems more appropriate, so here // enemies will choose to use their weaponless attack if it is more damaging. Entity.EnemyEntity AIAttacker = attacker as Entity.EnemyEntity; if (AIAttacker != null && weapon != null) { int weaponAverage = ((minBaseDamage + maxBaseDamage) / 2); int noWeaponAverage = ((AIAttacker.MobileEnemy.MinDamage + AIAttacker.MobileEnemy.MaxDamage) / 2); if (noWeaponAverage > weaponAverage) { // Use hand-to-hand weapon = null; } } if (weapon != null) { // If the attacker is using a weapon, check if the material is high enough to damage the target if (target.MinMetalToHit > (Items.WeaponMaterialTypes)weapon.NativeMaterialValue) { if (attacker == player) { DaggerfallUI.Instance.PopupMessage(UserInterfaceWindows.HardStrings.materialIneffective); } return(0); } // Get weapon skill used skillID = weapon.GetWeaponSkillIDAsShort(); } else { skillID = (short)DFCareer.Skills.HandToHand; } chanceToHitMod = attacker.Skills.GetLiveSkillValue(skillID); Entity.EnemyEntity AITarget = null; if (target != player) { AITarget = target as Entity.EnemyEntity; } if (attacker == player) { // Apply swing modifiers. Not applied to hand-to-hand in classic. FPSWeapon onscreenWeapon = GameManager.Instance.WeaponManager.ScreenWeapon; if (onscreenWeapon != null) { // The Daggerfall manual groups diagonal slashes to the left and right as if they are the same, but they are different. // Classic does not apply swing modifiers to hand-to-hand. if (onscreenWeapon.WeaponState == WeaponStates.StrikeUp) { damageModifiers += -4; chanceToHitMod += 10; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDownRight) { damageModifiers += -2; chanceToHitMod += 5; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDownLeft) { damageModifiers += 2; chanceToHitMod += -5; } if (onscreenWeapon.WeaponState == WeaponStates.StrikeDown) { damageModifiers += 4; chanceToHitMod += -10; } } if (weapon != null) { // Apply weapon proficiency if (((int)attacker.Career.ExpertProficiencies & weapon.GetWeaponSkillUsed()) != 0) { damageModifiers += ((attacker.Level / 3) + 1); chanceToHitMod += attacker.Level; } } // Apply hand-to-hand proficiency. Hand-to-hand proficiency is not applied in classic. else if (((int)attacker.Career.ExpertProficiencies & (int)(DFCareer.ProficiencyFlags.HandToHand)) != 0) { damageModifiers += ((attacker.Level / 3) + 1); chanceToHitMod += attacker.Level; } // Apply racial bonuses if (weapon != null) { if (player.RaceTemplate.ID == (int)Entity.Races.DarkElf) { damageModifiers += (attacker.Level / 4); chanceToHitMod += (attacker.Level / 4); } else if (skillID == (short)DFCareer.Skills.Archery) { if (player.RaceTemplate.ID == (int)Entity.Races.WoodElf) { damageModifiers += (attacker.Level / 3); chanceToHitMod += (attacker.Level / 3); } } else if (player.RaceTemplate.ID == (int)Entity.Races.Redguard) { damageModifiers += (attacker.Level / 3); chanceToHitMod += (attacker.Level / 3); } } // Apply backstabbing if (enemyAnimStateRecord % 5 > 2) // Facing away from player { chanceToHitMod += attacker.Skills.GetLiveSkillValue(DFCareer.Skills.Backstabbing); attacker.TallySkill(DFCareer.Skills.Backstabbing, 1); // backstabbing backstabbingLevel = attacker.Skills.GetLiveSkillValue(DFCareer.Skills.Backstabbing); } } // Choose struck body part int[] bodyParts = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6 }; int struckBodyPart = bodyParts[UnityEngine.Random.Range(0, bodyParts.Length)]; // Get damage for weaponless attacks if (skillID == (short)DFCareer.Skills.HandToHand) { if (attacker == player) { if (CalculateSuccessfulHit(attacker, target, chanceToHitMod, null, struckBodyPart)) { minBaseDamage = CalculateHandToHandMinDamage(attacker.Skills.GetLiveSkillValue(DFCareer.Skills.HandToHand)); maxBaseDamage = CalculateHandToHandMaxDamage(attacker.Skills.GetLiveSkillValue(DFCareer.Skills.HandToHand)); damage = UnityEngine.Random.Range(minBaseDamage, maxBaseDamage + 1); // Apply damage modifiers. damage += damageModifiers; // Apply strength modifier. It is not applied in classic despite what the in-game description for the Strength attribute says. damage += DamageModifier(attacker.Stats.LiveStrength); // Handle backstabbing if (backstabbingLevel > 0 && UnityEngine.Random.Range(1, 101) <= backstabbingLevel) { damage *= 3; string backstabMessage = UserInterfaceWindows.HardStrings.successfulBackstab; DaggerfallUI.Instance.PopupMessage(backstabMessage); } } } else // attacker is monster { // Handle multiple attacks by AI int attackNumber = 0; while (attackNumber < 3) // Classic supports up to 5 attacks but no monster has more than 3 { if (attackNumber == 0) { minBaseDamage = AIAttacker.MobileEnemy.MinDamage; maxBaseDamage = AIAttacker.MobileEnemy.MaxDamage; } else if (attackNumber == 1) { minBaseDamage = AIAttacker.MobileEnemy.MinDamage2; maxBaseDamage = AIAttacker.MobileEnemy.MaxDamage2; } else if (attackNumber == 2) { minBaseDamage = AIAttacker.MobileEnemy.MinDamage3; maxBaseDamage = AIAttacker.MobileEnemy.MaxDamage3; } if (DFRandom.rand() % 100 < 50 && minBaseDamage > 0 && CalculateSuccessfulHit(attacker, target, chanceToHitMod, null, struckBodyPart)) { damage += UnityEngine.Random.Range(minBaseDamage, maxBaseDamage + 1); } ++attackNumber; } } } // Handle weapon attacks else { if (CalculateSuccessfulHit(attacker, target, chanceToHitMod, weapon, struckBodyPart)) { damage = UnityEngine.Random.Range(weapon.GetBaseDamageMin(), weapon.GetBaseDamageMax() + 1); damage += damageModifiers; // Apply modifiers for Skeletal Warrior. if (AITarget != null && AITarget.CareerIndex == (int)Entity.MonsterCareers.SkeletalWarrior) { if (weapon.NativeMaterialValue == (int)Items.WeaponMaterialTypes.Silver) { damage *= 2; } if ((weapon.flags & 0x10) == 0) // not a blunt weapon { damage /= 2; } } // Apply strength modifier damage += DamageModifier(attacker.Stats.LiveStrength); // Apply material modifier. // The in-game display in Daggerfall of weapon damages with material modifiers is incorrect. The material modifier is half of what the display suggests. damage += weapon.GetWeaponMaterialModifier(); if (damage < 1) { damage = 0; } damage += GetBonusOrPenaltyByEnemyType(attacker, AITarget); if (backstabbingLevel > 1 && UnityEngine.Random.Range(1, 100 + 1) <= backstabbingLevel) { damage *= 3; string backstabMessage = UserInterfaceWindows.HardStrings.successfulBackstab; DaggerfallUI.Instance.PopupMessage(backstabMessage); } } } damage = Mathf.Max(0, damage); // If damage was done by a weapon, damage the weapon and armor of the hit body part. // In classic, shields are never damaged, only armor specific to the hitbody part is. // Here, if an equipped shield covers the hit body part, it takes damage instead. if (weapon != null && damage > 0) { weapon.DamageThroughPhysicalHit(damage, attacker); Items.DaggerfallUnityItem shield = target.ItemEquipTable.GetItem(Items.EquipSlots.LeftHand); bool shieldTakesDamage = false; if (shield != null) { Items.BodyParts[] protectedBodyParts = shield.GetShieldProtectedBodyParts(); for (int i = 0; (i < protectedBodyParts.Length) && !shieldTakesDamage; i++) { if (protectedBodyParts[i] == (Items.BodyParts)struckBodyPart) { shieldTakesDamage = true; } } } if (shieldTakesDamage) { shield.DamageThroughPhysicalHit(damage, target); } else { Items.EquipSlots hitSlot = Items.DaggerfallUnityItem.GetEquipSlotForBodyPart((Items.BodyParts)struckBodyPart); Items.DaggerfallUnityItem armor = target.ItemEquipTable.GetItem(hitSlot); if (armor != null) { armor.DamageThroughPhysicalHit(damage, target); } } } return(damage); }