Example #1
0
        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);
            }
        }
Example #6
0
        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);
        }