public void SetEnemyEquipment(int[] traits) { // Assign the enemies starting equipment. AssignEnemyEquipment(GameManager.Instance.PlayerEntity, this, traits); // Initialize armor values to 100 (no armor) for (int i = 0; i < ArmorValues.Length; i++) { ArmorValues[i] = 100; } // Calculate armor values from equipment for (int i = (int)Game.Items.EquipSlots.Head; i < (int)Game.Items.EquipSlots.Feet; i++) { Items.DaggerfallUnityItem item = ItemEquipTable.GetItem((Items.EquipSlots)i); if (item != null && item.ItemGroup == Game.Items.ItemGroups.Armor) { UpdateEquippedArmorValues(item, true); } } if (entityType == EntityTypes.EnemyClass) { // Clamp to maximum armor value of 60. In classic this also applies for monsters. // Note: Classic sets the value to 60 if it is > 50, which seems like an oversight. for (int i = 0; i < ArmorValues.Length; i++) { if (ArmorValues[i] > 60) { ArmorValues[i] = 60; } } } else { // Note: In classic, the above applies for equipment-using monsters as well as enemy classes. // The resulting armor values are often 60. Due to the +40 to hit against monsters this makes // monsters with equipment very easy to hit, and 60 is a worse value than any value monsters // have in their definition. To avoid this, in DF Unity the equipment values are only used if // they are better than the value in the definition. for (int i = 0; i < ArmorValues.Length; i++) { if (ArmorValues[i] > (sbyte)(mobileEnemy.ArmorValue * 5)) { ArmorValues[i] = (sbyte)(mobileEnemy.ArmorValue * 5); } } } }
public void SetEnemyEquipment(int variant) { PlayerEntity player = GameManager.Instance.PlayerEntity; int itemLevel = player.Level; Genders gender = player.Gender; Races race = player.Race; int chance = 0; if (variant == 0) { // right-hand weapon int item = UnityEngine.Random.Range((int)Game.Items.Weapons.Broadsword, (int)(Game.Items.Weapons.Longsword) + 1); Items.DaggerfallUnityItem weapon = Game.Items.ItemBuilder.CreateWeapon((Items.Weapons)item, Game.Items.ItemBuilder.RandomMaterial(itemLevel)); ItemEquipTable.EquipItem(weapon, true, false); items.AddItem(weapon); chance = 50; // left-hand shield item = UnityEngine.Random.Range((int)Game.Items.Armor.Buckler, (int)(Game.Items.Armor.Round_Shield) + 1); if (UnityEngine.Random.Range(1, 101) <= chance) { Items.DaggerfallUnityItem armor = Game.Items.ItemBuilder.CreateArmor(gender, race, (Items.Armor)item, Game.Items.ItemBuilder.RandomArmorMaterial(itemLevel)); ItemEquipTable.EquipItem(armor, true, false); items.AddItem(armor); } // left-hand weapon else if (UnityEngine.Random.Range(1, 101) <= chance) { item = UnityEngine.Random.Range((int)Game.Items.Weapons.Dagger, (int)(Game.Items.Weapons.Shortsword) + 1); weapon = Game.Items.ItemBuilder.CreateWeapon((Items.Weapons)item, Game.Items.ItemBuilder.RandomMaterial(itemLevel)); ItemEquipTable.EquipItem(weapon, true, false); items.AddItem(weapon); } } else { // right-hand weapon int item = UnityEngine.Random.Range((int)Game.Items.Weapons.Claymore, (int)(Game.Items.Weapons.Battle_Axe) + 1); Items.DaggerfallUnityItem weapon = Game.Items.ItemBuilder.CreateWeapon((Items.Weapons)item, Game.Items.ItemBuilder.RandomMaterial(itemLevel)); ItemEquipTable.EquipItem(weapon, true, false); items.AddItem(weapon); if (variant == 1) { chance = 75; } else if (variant == 2) { chance = 90; } } // helm if (UnityEngine.Random.Range(1, 101) <= chance) { Items.DaggerfallUnityItem armor = Game.Items.ItemBuilder.CreateArmor(gender, race, Game.Items.Armor.Helm, Game.Items.ItemBuilder.RandomArmorMaterial(itemLevel)); ItemEquipTable.EquipItem(armor, true, false); items.AddItem(armor); } // right pauldron if (UnityEngine.Random.Range(1, 101) <= chance) { Items.DaggerfallUnityItem armor = Game.Items.ItemBuilder.CreateArmor(gender, race, Game.Items.Armor.Right_Pauldron, Game.Items.ItemBuilder.RandomArmorMaterial(itemLevel)); ItemEquipTable.EquipItem(armor, true, false); items.AddItem(armor); } // left pauldron if (UnityEngine.Random.Range(1, 101) <= chance) { Items.DaggerfallUnityItem armor = Game.Items.ItemBuilder.CreateArmor(gender, race, Game.Items.Armor.Left_Pauldron, Game.Items.ItemBuilder.RandomArmorMaterial(itemLevel)); ItemEquipTable.EquipItem(armor, true, false); items.AddItem(armor); } // cuirass if (UnityEngine.Random.Range(1, 101) <= chance) { Items.DaggerfallUnityItem armor = Game.Items.ItemBuilder.CreateArmor(gender, race, Game.Items.Armor.Cuirass, Game.Items.ItemBuilder.RandomArmorMaterial(itemLevel)); ItemEquipTable.EquipItem(armor, true, false); items.AddItem(armor); } // greaves if (UnityEngine.Random.Range(1, 101) <= chance) { Items.DaggerfallUnityItem armor = Game.Items.ItemBuilder.CreateArmor(gender, race, Game.Items.Armor.Greaves, Game.Items.ItemBuilder.RandomArmorMaterial(itemLevel)); ItemEquipTable.EquipItem(armor, true, false); items.AddItem(armor); } // boots if (UnityEngine.Random.Range(1, 101) <= chance) { Items.DaggerfallUnityItem armor = Game.Items.ItemBuilder.CreateArmor(gender, race, Game.Items.Armor.Boots, Game.Items.ItemBuilder.RandomArmorMaterial(itemLevel)); ItemEquipTable.EquipItem(armor, true, false); items.AddItem(armor); } // Initialize armor values to 100 (no armor) for (int i = 0; i < ArmorValues.Length; i++) { ArmorValues[i] = 100; } // Calculate armor values from equipment for (int i = (int)Game.Items.EquipSlots.Head; i < (int)Game.Items.EquipSlots.Feet; i++) { Items.DaggerfallUnityItem item = ItemEquipTable.GetItem((Items.EquipSlots)i); if (item != null && item.ItemGroup == Game.Items.ItemGroups.Armor) { UpdateEquippedArmorValues(item, true); } } if (entityType == EntityTypes.EnemyClass) { // Clamp to maximum armor value of 60. In classic this also applies for monsters. // Note: Classic sets the value to 60 if it is > 50, which seems like an oversight. for (int i = 0; i < ArmorValues.Length; i++) { if (ArmorValues[i] > 60) { ArmorValues[i] = 60; } } } else { // Note: In classic, the above applies for equipment-using monsters as well as enemy classes. // The resulting armor values are often 60. Due to the +40 to hit against monsters this makes // monsters with equipment very easy to hit, and 60 is a worse value than any value monsters // have in their definition. To avoid this, in DF Unity the equipment values are only used if // they are better than the value in the definition. for (int i = 0; i < ArmorValues.Length; i++) { if (ArmorValues[i] > (sbyte)(mobileEnemy.ArmorValue * 5)) { ArmorValues[i] = (sbyte)(mobileEnemy.ArmorValue * 5); } } } }
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); }
/// <summary> /// Attempt to trap a soul. /// </summary> /// <returns>True if entity is allowed to die after trap attempt.</returns> bool AttemptSoulTrap() { // Must have a peered DaggerfallEntityBehaviour and EntityEffectManager EntityEffectManager manager = (EntityBehaviour) ? EntityBehaviour.GetComponent <EntityEffectManager>() : null; if (!manager) { return(true); } // Find the soul trap incumbent SoulTrap soulTrapEffect = (SoulTrap)manager.FindIncumbentEffect <SoulTrap>(); if (soulTrapEffect == null) { return(true); } // Roll chance for trap, or always succeed if Azura's Star is equipped. // If trap fails then entity should die as normal without trapping a soul // If trap succeeds and player has a free soul gem then entity should die after storing soul // If trap succeeds and player has no free soul gems then entity will not die until effect expires or fails bool azurasStarEquipped = false; Items.DaggerfallUnityItem azurasStar = GameManager.Instance.PlayerEntity.ItemEquipTable.GetItem(Game.Items.EquipSlots.Amulet0); if (azurasStar != null && azurasStar.Enchantments[0].type == DaggerfallConnect.FallExe.EnchantmentTypes.SpecialArtifactEffect && azurasStar.Enchantments[0].param == 9) { azurasStarEquipped = true; } else { azurasStar = GameManager.Instance.PlayerEntity.ItemEquipTable.GetItem(Game.Items.EquipSlots.Amulet1); if (azurasStar != null && azurasStar.Enchantments[0].type == DaggerfallConnect.FallExe.EnchantmentTypes.SpecialArtifactEffect && azurasStar.Enchantments[0].param == 9) { azurasStarEquipped = true; } } if (azurasStarEquipped || soulTrapEffect.RollTrapChance()) { // Attempt to fill an empty soul trap if (soulTrapEffect.FillEmptyTrapItem((MobileTypes)mobileEnemy.ID)) { // Trap filled, allow entity to die normally DaggerfallUI.AddHUDText(TextManager.Instance.GetText("ClassicEffects", "trapSuccess"), 1.5f); return(true); } else { // No empty gems, keep entity tethered to life - player is alerted so they know what's happening currentHealth = 1; DaggerfallUI.AddHUDText(TextManager.Instance.GetText("ClassicEffects", "trapNoneEmpty")); return(false); } } else { // Trap failed DaggerfallUI.AddHUDText(TextManager.Instance.GetText("ClassicEffects", "trapFail"), 1.5f); return(true); } }