Example #1
0
        public void ItemBreaks(DaggerfallEntity owner)
        {
            // Classic does not have the plural version of this string, and uses the short name rather than the long one.
            // Also the classic string says "is" instead of "has"
            string itemBroke = "";

            if (TemplateIndex == (int)Armor.Boots || TemplateIndex == (int)Armor.Gauntlets || TemplateIndex == (int)Armor.Greaves)
            {
                itemBroke = UserInterfaceWindows.HardStrings.itemHasBrokenPlural;
            }
            else
            {
                itemBroke = UserInterfaceWindows.HardStrings.itemHasBroken;
            }
            itemBroke = itemBroke.Replace("%s", LongName);
            DaggerfallUI.Instance.PopupMessage(itemBroke);
            EquipSlots slot = owner.ItemEquipTable.GetEquipSlot(this);

            if (owner.ItemEquipTable.GetItem(slot) == this)
            {
                owner.ItemEquipTable.UnequipItem(slot);
            }
        }
Example #2
0
        private void PlayerEntity_OnDeath(DaggerfallEntity entity)
        {
            if (deathInProgress || !mainCamera)
            {
                return;
            }

            // Start the death process and pause player input
            deathInProgress = true;
            timeOfDeath     = Time.fixedTime;
            InputManager.Instance.IsPaused = true;

            // Start camera falling and fading to black
            startCameraHeight   = mainCamera.transform.localPosition.y;
            targetCameraHeight  = playerController.height - (playerController.height * 1.25f);
            currentCameraHeight = startCameraHeight;
            DaggerfallUI.Instance.FadeHUDToBlack(FadeDuration);

            if (OnPlayerDeath != null)
            {
                OnPlayerDeath(this, null);
            }
        }
Example #3
0
        private static int AdjustWeaponAttackDamage(DaggerfallEntity attacker, DaggerfallEntity target, int damage, int weaponAnimTime, DaggerfallUnityItem weapon)
        {
            if (weaponAnimTime > 0 && (weapon.TemplateIndex == (int)Weapons.Short_Bow || weapon.TemplateIndex == (int)Weapons.Long_Bow))
            {
                double adjustedDamage = damage;
                if (weaponAnimTime < 800)
                {
                    adjustedDamage *= (double)weaponAnimTime / 800;
                }
                else if (weaponAnimTime < 5000)
                {
                    adjustedDamage = damage;
                }
                else if (weaponAnimTime < 6000)
                {
                    adjustedDamage *= 0.85;
                }
                else if (weaponAnimTime < 8000)
                {
                    adjustedDamage *= 0.75;
                }
                else if (weaponAnimTime < 9000)
                {
                    adjustedDamage *= 0.5;
                }
                else if (weaponAnimTime >= 9000)
                {
                    adjustedDamage *= 0.25;
                }

#if UNITY_EDITOR
                Debug.LogFormat("Adjusted Weapon Damage for bow drawing from {0} to {1} (t={2}ms)", damage, (int)adjustedDamage, weaponAnimTime);
#endif
                return((int)adjustedDamage);
            }
            return(damage);
        }
Example #4
0
        private static int AdjustWeaponHitChanceMod(DaggerfallEntity attacker, DaggerfallEntity target, int hitChanceMod, int weaponAnimTime, DaggerfallUnityItem weapon)
        {
            if (weaponAnimTime > 0 && (weapon.TemplateIndex == (int)Weapons.Short_Bow || weapon.TemplateIndex == (int)Weapons.Long_Bow))
            {
                int adjustedHitChanceMod = hitChanceMod;
                if (weaponAnimTime < 200)
                {
                    adjustedHitChanceMod -= 40;
                }
                else if (weaponAnimTime < 500)
                {
                    adjustedHitChanceMod -= 10;
                }
                else if (weaponAnimTime < 1000)
                {
                    adjustedHitChanceMod = hitChanceMod;
                }
                else if (weaponAnimTime < 2000)
                {
                    adjustedHitChanceMod += 10;
                }
                else if (weaponAnimTime > 5000)
                {
                    adjustedHitChanceMod -= 10;
                }
                else if (weaponAnimTime > 8000)
                {
                    adjustedHitChanceMod -= 20;
                }

#if UNITY_EDITOR
                Debug.LogFormat("Adjusted Weapon HitChanceMod for bow drawing from {0} to {1} (t={2}ms)", hitChanceMod, adjustedHitChanceMod, weaponAnimTime);
#endif
                return(adjustedHitChanceMod);
            }
            return(hitChanceMod);
        }
 public override void OnWeaponHitEntity(PlayerEntity playerEntity, DaggerfallEntity targetEntity = null)
 {
     // Player just needs to strike enemy with any weapon (including melee) to register a feeding strike
     UpdateSatiation();
 }
Example #6
0
        private static bool ApplyConditionDamageThroughPhysicalHit(DaggerfallUnityItem item, DaggerfallEntity owner, int damage)
        {
            if (item.ItemGroup == ItemGroups.Armor)
            {
                int amount = damage * 5;
                item.LowerCondition(amount, owner);
#if UNITY_EDITOR
                if (owner == GameManager.Instance.PlayerEntity)
                {
                    Debug.LogFormat("Damaged {0} by {1} from dmg {3}, cond={2}", item.ItemName, amount, item.currentCondition, damage);
                }
#endif
                return(true);
            }
            return(false);
        }
 bool TryEffectBasedAbsorption(IEntityEffect effect, SpellAbsorption absorbEffect, DaggerfallEntity casterEntity)
 {
     return(RollAbsorptionChance(absorbEffect, casterEntity));
 }
 private void Entity_OnDeath(DaggerfallEntity entity)
 {
     clearBundles = true;
     entityBehaviour.Entity.OnDeath -= Entity_OnDeath;
     //Debug.LogFormat("Cleared all effect bundles after death of {0}", entity.Name);
 }
Example #9
0
        /// Allocate any equipment damage from a strike, and reduce item condition.
        private static bool DamageEquipment(DaggerfallEntity attacker, DaggerfallEntity target, int damage, DaggerfallUnityItem weapon, int struckBodyPart)
        {
            int   atkStrength    = attacker.Stats.LiveStrength;
            int   tarMatMod      = 0;
            int   matDifference  = 0;
            bool  bluntWep       = false;
            bool  shtbladeWep    = false;
            bool  missileWep     = false;
            int   wepEqualize    = 1;
            int   wepWeight      = 1;
            float wepDamResist   = 1f;
            float armorDamResist = 1f;

            // If damage was done by a weapon, damage the weapon and armor of the hit body part.
            if (weapon != null && damage > 0)
            {
                int atkMatMod = weapon.GetWeaponMaterialModifier() + 2;
                int wepDam    = damage;
                wepEqualize = EqualizeMaterialConditions(weapon);
                wepDam     *= wepEqualize;

                if (weapon.GetWeaponSkillIDAsShort() == 32)                 // Checks if the weapon being used is in the Blunt Weapon category, then sets a bool value to true.
                {
                    wepDam      += (atkStrength / 10);
                    wepDamResist = (wepEqualize * .20f) + 1;
                    wepDam       = (int)Mathf.Ceil(wepDam / wepDamResist);
                    bluntWep     = true;
                    wepWeight    = (int)Mathf.Ceil(weapon.EffectiveUnitWeightInKg());

                    ApplyConditionDamageThroughWeaponDamage(weapon, attacker, wepDam, bluntWep, shtbladeWep, missileWep, wepEqualize); // Does condition damage to the attackers weapon.
                }
                else if (weapon.GetWeaponSkillIDAsShort() == 28)                                                                       // Checks if the weapon being used is in the Short Blade category, then sets a bool value to true.
                {
                    if (weapon.TemplateIndex == (int)Weapons.Dagger || weapon.TemplateIndex == (int)Weapons.Tanto)
                    {
                        wepDam      += (atkStrength / 30);
                        wepDamResist = (wepEqualize * .90f) + 1;
                        wepDam       = (int)Mathf.Ceil(wepDam / wepDamResist);
                        shtbladeWep  = true;
                    }
                    else
                    {
                        wepDam      += (atkStrength / 30);
                        wepDamResist = (wepEqualize * .30f) + 1;
                        wepDam       = (int)Mathf.Ceil(wepDam / wepDamResist);
                        shtbladeWep  = true;
                    }

                    ApplyConditionDamageThroughWeaponDamage(weapon, attacker, wepDam, bluntWep, shtbladeWep, missileWep, wepEqualize); // Does condition damage to the attackers weapon.
                }
                else if (weapon.GetWeaponSkillIDAsShort() == 33)                                                                       // Checks if the weapon being used is in the Missile Weapon category, then sets a bool value to true.
                {
                    missileWep = true;

                    ApplyConditionDamageThroughWeaponDamage(weapon, attacker, wepDam, bluntWep, shtbladeWep, missileWep, wepEqualize); // Does condition damage to the attackers weapon.
                }
                else                                                                                                                   // If all other weapons categories have not been found, it defaults to this, which currently includes long blades and axes.
                {
                    wepDam      += (atkStrength / 10);
                    wepDamResist = (wepEqualize * .20f) + 1;
                    wepDam       = (int)Mathf.Ceil(wepDam / wepDamResist);

                    ApplyConditionDamageThroughWeaponDamage(weapon, attacker, wepDam, bluntWep, shtbladeWep, missileWep, wepEqualize);                     // Does condition damage to the attackers weapon.
                }

                if (attacker == GameManager.Instance.PlayerEntity)
                {
                    WarningMessagePlayerEquipmentCondition(weapon);
                }

                DaggerfallUnityItem shield = target.ItemEquipTable.GetItem(EquipSlots.LeftHand);                 // Checks if character is using a shield or not.
                bool shieldTakesDamage     = false;
                if (shield != null)
                {
                    BodyParts[] protectedBodyParts = shield.GetShieldProtectedBodyParts();

                    for (int i = 0; (i < protectedBodyParts.Length) && !shieldTakesDamage; i++)
                    {
                        if (protectedBodyParts[i] == (BodyParts)struckBodyPart)
                        {
                            shieldTakesDamage = true;
                        }
                    }
                }

                if (shieldTakesDamage)
                {
                    int shieldEqualize = EqualizeMaterialConditions(shield);
                    damage       *= shieldEqualize;
                    tarMatMod     = ArmorMaterialModifierFinder(shield);
                    matDifference = tarMatMod - atkMatMod;
                    damage        = MaterialDifferenceDamageCalculation(shield, matDifference, atkStrength, damage, bluntWep, wepWeight, shieldTakesDamage);

                    ApplyConditionDamageThroughWeaponDamage(shield, target, damage, bluntWep, shtbladeWep, missileWep, wepEqualize);

                    if (target == GameManager.Instance.PlayerEntity)
                    {
                        WarningMessagePlayerEquipmentCondition(shield);
                    }
                }
                else
                {
                    EquipSlots          hitSlot = DaggerfallUnityItem.GetEquipSlotForBodyPart((BodyParts)struckBodyPart);
                    DaggerfallUnityItem armor   = target.ItemEquipTable.GetItem(hitSlot);
                    if (armor != null)
                    {
                        int armorEqualize = EqualizeMaterialConditions(armor);
                        damage       *= armorEqualize;
                        tarMatMod     = ArmorMaterialModifierFinder(armor);
                        matDifference = tarMatMod - atkMatMod;
                        damage        = MaterialDifferenceDamageCalculation(armor, matDifference, atkStrength, damage, bluntWep, wepWeight, shieldTakesDamage);

                        ApplyConditionDamageThroughWeaponDamage(armor, target, damage, bluntWep, shtbladeWep, missileWep, wepEqualize);

                        if (target == GameManager.Instance.PlayerEntity)
                        {
                            WarningMessagePlayerEquipmentCondition(armor);
                        }
                    }
                }
                return(false);
            }
            else if (weapon == null && damage > 0)             // Handles Unarmed attacks.
            {
                DaggerfallUnityItem shield = target.ItemEquipTable.GetItem(EquipSlots.LeftHand);
                bool shieldTakesDamage     = false;
                if (shield != null)
                {
                    BodyParts[] protectedBodyParts = shield.GetShieldProtectedBodyParts();

                    for (int i = 0; (i < protectedBodyParts.Length) && !shieldTakesDamage; i++)
                    {
                        if (protectedBodyParts[i] == (BodyParts)struckBodyPart)
                        {
                            shieldTakesDamage = true;
                        }
                    }
                }

                if (shieldTakesDamage)
                {
                    int shieldEqualize = EqualizeMaterialConditions(shield);
                    damage        *= shieldEqualize;
                    tarMatMod      = ArmorMaterialModifierFinder(shield);
                    atkStrength   /= 5;
                    armorDamResist = (tarMatMod * .35f) + 1;
                    damage         = (int)Mathf.Ceil((damage + atkStrength) / armorDamResist);

                    ApplyConditionDamageThroughUnarmedDamage(shield, target, damage);

                    if (target == GameManager.Instance.PlayerEntity)
                    {
                        WarningMessagePlayerEquipmentCondition(shield);
                    }
                }
                else
                {
                    EquipSlots          hitSlot = DaggerfallUnityItem.GetEquipSlotForBodyPart((BodyParts)struckBodyPart);
                    DaggerfallUnityItem armor   = target.ItemEquipTable.GetItem(hitSlot);
                    if (armor != null)
                    {
                        int armorEqualize = EqualizeMaterialConditions(armor);
                        damage        *= armorEqualize;
                        tarMatMod      = ArmorMaterialModifierFinder(armor);
                        atkStrength   /= 5;
                        armorDamResist = (tarMatMod * .20f) + 1;
                        damage         = (int)Mathf.Ceil((damage + atkStrength) / armorDamResist);

                        ApplyConditionDamageThroughUnarmedDamage(armor, target, damage);

                        if (target == GameManager.Instance.PlayerEntity)
                        {
                            WarningMessagePlayerEquipmentCondition(armor);
                        }
                    }
                }
                return(false);
            }
            return(false);
        }
Example #10
0
        /// Applies condition damage to an item based on physical hit damage. Specifically for unarmed attacks.
        private static void ApplyConditionDamageThroughUnarmedDamage(DaggerfallUnityItem item, DaggerfallEntity owner, int damage)
        {
            //Debug.LogFormat("Item Group Index is {0}", item.GroupIndex);
            //Debug.LogFormat("Item Template Index is {0}", item.TemplateIndex);

            if (item.ItemGroup == ItemGroups.Armor)             // Target gets their armor/shield condition damaged.
            {
                damage /= 100;
                int amount = item.IsShield ? damage: damage * 2;
                item.LowerCondition(amount, owner);

                /*int percentChange = 100 * amount / item.maxCondition;
                 * if (owner == GameManager.Instance.PlayerEntity){
                 * Debug.LogFormat("Target Had {0} Damaged by {1}, cond={2}", item.LongName, amount, item.currentCondition);
                 *      Debug.LogFormat("Had {0} Damaged by {1}%, of Total Maximum. There Remains {2}% of Max Cond.", item.LongName, percentChange, item.ConditionPercentage);} // Percentage Change */
            }
        }
Example #11
0
        /// Applies condition damage to an item based on physical hit damage.
        private static void ApplyConditionDamageThroughWeaponDamage(DaggerfallUnityItem item, DaggerfallEntity owner, int damage, bool bluntWep, bool shtbladeWep, bool missileWep, int wepEqualize) // Possibly add on so that magic damage also damages worn equipment.
        {
            //Debug.LogFormat("Item Group Index is {0}", item.GroupIndex);
            //Debug.LogFormat("Item Template Index is {0}", item.TemplateIndex);

            if (item.ItemGroup == ItemGroups.Armor)             // Target gets their armor/shield condition damaged.
            {
                int amount = item.IsShield ? damage * 2: damage * 4;
                item.LowerCondition(amount, owner);

                /*int percentChange = 100 * amount / item.maxCondition;
                 * if (owner == GameManager.Instance.PlayerEntity){
                 * Debug.LogFormat("Target Had {0} Damaged by {1}, cond={2}", item.LongName, amount, item.currentCondition);
                 *      Debug.LogFormat("Had {0} Damaged by {1}%, of Total Maximum. There Remains {2}% of Max Cond.", item.LongName, percentChange, item.ConditionPercentage);} // Percentage Change */
            }
            else             // Attacker gets their weapon damaged, if they are using one, otherwise this method is not called.
            {
                int amount = (10 * damage) / 50;
                if ((amount == 0) && Dice100.SuccessRoll(40))
                {
                    amount = 1;
                }

                if (missileWep)
                {
                    amount = SpecificWeaponConditionDamage(item, amount, wepEqualize);
                }

                item.LowerCondition(amount, owner);

                /*int percentChange = 100 * amount / item.maxCondition;
                 * if (owner == GameManager.Instance.PlayerEntity){
                 *      Debug.LogFormat("Attacker Damaged {0} by {1}, cond={2}", item.LongName, amount, item.currentCondition);
                 *      Debug.LogFormat("Had {0} Damaged by {1}%, of Total Maximum. There Remains {2}% of Max Cond.", item.LongName, percentChange, item.ConditionPercentage);} // Percentage Change */
            }
        }
 public ItemEquipTable(DaggerfallEntity parentEntity)
 {
     this.parentEntity = parentEntity;
 }
Example #13
0
 /// <summary>
 /// Called by WeaponManager when player hits an entity with a weapon (includes hand-to-hand).
 /// Target entity may be null, racial overrides should handle this.
 /// </summary>
 public virtual void OnWeaponHitEntity(PlayerEntity playerEntity, DaggerfallEntity targetEntity = null)
 {
 }
Example #14
0
        /// <summary>
        /// Sets up enemy based on current settings.
        /// </summary>
        public void ApplyEnemySettings(MobileGender gender)
        {
            DaggerfallUnity dfUnity = DaggerfallUnity.Instance;
            Dictionary <int, MobileEnemy> enemyDict = GameObjectHelper.EnemyDict;
            MobileEnemy mobileEnemy = enemyDict[(int)EnemyType];

            if (AlliedToPlayer)
            {
                mobileEnemy.Team = MobileTeams.PlayerAlly;
            }

            // Find mobile unit in children
            MobileUnit dfMobile = GetMobileBillboardChild();

            if (dfMobile != null)
            {
                // Setup mobile billboard
                Vector2 size = Vector2.one;
                mobileEnemy.Gender    = gender;
                mobileEnemy.Reactions = EnemyReaction;
                dfMobile.SetEnemy(dfUnity, mobileEnemy, EnemyReaction, ClassicSpawnDistanceType);

                // Setup controller
                CharacterController controller = GetComponent <CharacterController>();
                if (controller)
                {
                    // Set base height from sprite
                    size = dfMobile.GetSize();
                    controller.height = size.y;

                    // Reduce height of flying creatures as their wing animation makes them taller than desired
                    // This helps them get through doors while aiming for player eye height
                    if (dfMobile.Enemy.Behaviour == MobileBehaviour.Flying)
                    {
                        // (in frame 0 wings are in high position, assume body is  the lower half)
                        AdjustControllerHeight(controller, controller.height / 2, ControllerJustification.BOTTOM);
                    }

                    // Limit minimum controller height
                    // Stops very short characters like rats from being walked upon
                    if (controller.height < 1.6f)
                    {
                        AdjustControllerHeight(controller, 1.6f, ControllerJustification.BOTTOM);
                    }

                    controller.gameObject.layer = LayerMask.NameToLayer("Enemies");
                }

                // Setup sounds
                EnemySounds enemySounds = GetComponent <Game.EnemySounds>();
                if (enemySounds)
                {
                    enemySounds.MoveSound   = (SoundClips)dfMobile.Enemy.MoveSound;
                    enemySounds.BarkSound   = (SoundClips)dfMobile.Enemy.BarkSound;
                    enemySounds.AttackSound = (SoundClips)dfMobile.Enemy.AttackSound;
                }

                MeshRenderer meshRenderer = dfMobile.GetComponent <MeshRenderer>();
                if (meshRenderer)
                {
                    if (dfMobile.Enemy.Behaviour == MobileBehaviour.Spectral)
                    {
                        meshRenderer.material.shader = Shader.Find(MaterialReader._DaggerfallGhostShaderName);
                        meshRenderer.material.SetFloat("_Cutoff", 0.1f);
                    }
                    if (dfMobile.Enemy.NoShadow)
                    {
                        meshRenderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
                    }
                    if (dfMobile.Enemy.GlowColor != null)
                    {
                        meshRenderer.receiveShadows = false;
                        GameObject enemyLightGameObject = Instantiate(LightAura);
                        enemyLightGameObject.transform.parent        = dfMobile.transform;
                        enemyLightGameObject.transform.localPosition = new Vector3(0, 0.3f, 0.2f);
                        Light enemyLight = enemyLightGameObject.GetComponent <Light>();
                        enemyLight.color   = (Color)dfMobile.Enemy.GlowColor;
                        enemyLight.shadows = DaggerfallUnity.Settings.DungeonLightShadows ? LightShadows.Soft : LightShadows.None;
                    }
                }

                // Setup entity
                if (entityBehaviour)
                {
                    EnemyEntity entity = new EnemyEntity(entityBehaviour);
                    entityBehaviour.Entity = entity;

                    // Enemies are initially added to same world context as player
                    entity.WorldContext = GameManager.Instance.PlayerEnterExit.WorldContext;

                    int enemyIndex = (int)EnemyType;
                    if (enemyIndex >= 0 && enemyIndex <= 42)
                    {
                        entityBehaviour.EntityType = EntityTypes.EnemyMonster;
                        entity.SetEnemyCareer(mobileEnemy, entityBehaviour.EntityType);
                    }
                    else if (enemyIndex >= 128 && enemyIndex <= 146)
                    {
                        entityBehaviour.EntityType = EntityTypes.EnemyClass;
                        entity.SetEnemyCareer(mobileEnemy, entityBehaviour.EntityType);
                    }
                    else if (DaggerfallEntity.GetCustomCareerTemplate(enemyIndex) != null)
                    {
                        // For custom enemies, we use the 7th bit to tell whether a class or monster was intended
                        // 0-127 is monster
                        // 128-255 is class
                        // 256-383 is monster again
                        // etc
                        if ((enemyIndex & 128) != 0)
                        {
                            entityBehaviour.EntityType = EntityTypes.EnemyClass;
                        }
                        else
                        {
                            entityBehaviour.EntityType = EntityTypes.EnemyMonster;
                        }
                        entity.SetEnemyCareer(mobileEnemy, entityBehaviour.EntityType);
                    }
                    else
                    {
                        entityBehaviour.EntityType = EntityTypes.None;
                    }
                }

                // Add special behaviour for Daedra Seducer mobiles
                if (dfMobile.Enemy.ID == (int)MobileTypes.DaedraSeducer)
                {
                    dfMobile.gameObject.AddComponent <DaedraSeducerMobileBehaviour>();
                }
            }
        }
Example #15
0
        public static int CalculateAttackDamagePhysicalCombat(DaggerfallEntity attacker, DaggerfallEntity target, bool enemyAnimStateRecord, int weaponAnimTime, DaggerfallUnityItem weapon)
        {
            if (attacker == null || target == null)
            {
                return(0);
            }

            int          damageModifiers      = 0;
            int          damage               = 0;
            int          chanceToHitMod       = 0;
            int          backstabChance       = 0;
            PlayerEntity player               = GameManager.Instance.PlayerEntity;
            short        skillID              = 0;
            bool         unarmedAttack        = false;
            bool         weaponAttack         = false;
            bool         bluntWep             = false;
            bool         specialMonsterWeapon = false;
            bool         monsterArmorCheck    = false;
            bool         critSuccess          = false;
            float        critDamMulti         = 1f;
            int          critHitAddi          = 0;
            float        matReqDamMulti       = 1f;

            EnemyEntity AITarget = null;

            AITarget = target as EnemyEntity;

            // 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.
            EnemyEntity AIAttacker = attacker as EnemyEntity;

            if (AIAttacker != null && weapon != null)
            {
                int weaponAverage   = (weapon.GetBaseDamageMin() + weapon.GetBaseDamageMax()) / 2;
                int noWeaponAverage = (AIAttacker.MobileEnemy.MinDamage + AIAttacker.MobileEnemy.MaxDamage) / 2;
                if (noWeaponAverage > weaponAverage)
                {
                    // Use hand-to-hand
                    weapon = null;
                }
            }

            if (weapon != null)
            {
                if (PhysicalCombatArmorPatch.softMatRequireModuleCheck) // Only run if "Soft Material Requirements" module is active.
                {
                    // If the attacker is using a weapon, check if the material is high enough to damage the target
                    if (target.MinMetalToHit > (WeaponMaterialTypes)weapon.NativeMaterialValue)
                    {
                        int targetMatRequire = (int)target.MinMetalToHit;
                        int weaponMatValue   = weapon.NativeMaterialValue;
                        matReqDamMulti = targetMatRequire - weaponMatValue;

                        if (matReqDamMulti <= 0) // There is no "bonus" damage for meeting material requirements, nor for exceeding them, just normal unmodded damage.
                        {
                            matReqDamMulti = 1;
                        }
                        else // There is a damage penalty for attacking a target with below the minimum material requirements of that target, more as the difference between becomes greater.
                        {
                            matReqDamMulti = (Mathf.Min(matReqDamMulti * 0.2f, 0.9f) - 1) * -1; // Keeps the damage multiplier penalty from going above 90% reduced damage.
                        }
                        if (attacker == player)
                        {
                            Debug.LogFormat("1. matReqDamMulti = {0}", matReqDamMulti);
                        }
                    }
                    // Get weapon skill used
                    skillID = weapon.GetWeaponSkillIDAsShort();
                    if (skillID == 32) // Checks if the weapon being used is in the Blunt Weapon category, then sets a bool value to true.
                    {
                        bluntWep = true;
                    }
                }
                else
                {
                    // If the attacker is using a weapon, check if the material is high enough to damage the target
                    if (target.MinMetalToHit > (WeaponMaterialTypes)weapon.NativeMaterialValue)
                    {
                        if (attacker == player)
                        {
                            DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("materialIneffective"));
                        }
                        return(0);
                    }
                    // Get weapon skill used
                    skillID = weapon.GetWeaponSkillIDAsShort();
                    if (skillID == 32) // Checks if the weapon being used is in the Blunt Weapon category, then sets a bool value to true.
                    {
                        bluntWep = true;
                    }
                }
            }
            else
            {
                skillID = (short)DFCareer.Skills.HandToHand;
            }

            if (attacker == player)
            {
                int playerWeaponSkill = attacker.Skills.GetLiveSkillValue(skillID);
                playerWeaponSkill = (int)Mathf.Ceil(playerWeaponSkill * 1.5f); // Makes it so player weapon skill has 150% of the effect it normally would on hit chance. So now instead of 50 weapon skill adding +50 to the end, 50 will now add +75.
                chanceToHitMod    = playerWeaponSkill;
            }
            else
            {
                chanceToHitMod = attacker.Skills.GetLiveSkillValue(skillID);
            }

            if (PhysicalCombatArmorPatch.critStrikeModuleCheck)                             // Applies the 'Critical Strikes Increase Damage' module if it is enabled in the settings.
            {
                if (attacker == player)                                                     // Crit modifiers, if true, for the player.
                {
                    critSuccess = PhysicalCombatArmorPatch.CriticalStrikeHandler(attacker); // Rolls for if the attacker is sucessful with a critical strike, if yes, critSuccess is set to 'true'.

                    if (critSuccess)
                    {
                        critDamMulti = (attacker.Skills.GetLiveSkillValue(DFCareer.Skills.CriticalStrike) / 5);
                        //Debug.LogFormat("1. critDamMulti From PLAYER Skills = {0}", critDamMulti);
                        critHitAddi = (attacker.Skills.GetLiveSkillValue(DFCareer.Skills.CriticalStrike) / 4);
                        //Debug.LogFormat("2. critHitAddi From PLAYER Skills = {0}", critHitAddi);

                        critDamMulti = (critDamMulti * .05f) + 1;
                        //Debug.LogFormat("3. Final critDamMulti From PLAYER Skills = {0}", critDamMulti);

                        chanceToHitMod += critHitAddi; // Adds the critical success value to the 'chanceToHitMod'.
                    }
                }
                else // Crit modifiers, if true, for monsters/enemies.
                {
                    critSuccess = PhysicalCombatArmorPatch.CriticalStrikeHandler(attacker); // Rolls for if the attacker is sucessful with a critical strike, if yes, critSuccess is set to 'true'.

                    if (critSuccess)
                    {
                        critDamMulti = (attacker.Skills.GetLiveSkillValue(DFCareer.Skills.CriticalStrike) / 5);
                        //Debug.LogFormat("1. critDamMulti From MONSTER Skills = {0}", critDamMulti);
                        critHitAddi = (attacker.Skills.GetLiveSkillValue(DFCareer.Skills.CriticalStrike) / 10);
                        //Debug.LogFormat("2. critHitAddi From MONSTER Skills = {0}", critHitAddi);

                        critDamMulti = (critDamMulti * .025f) + 1;
                        //Debug.LogFormat("3. Final critDamMulti From MONSTER Skills = {0}", critDamMulti);

                        chanceToHitMod += critHitAddi; // Adds the critical success value to the 'chanceToHitMod'.
                    }
                }
            }

            if (attacker == player)
            {
                // Apply swing modifiers
                FormulaHelper.ToHitAndDamageMods swingMods = FormulaHelper.CalculateSwingModifiers(GameManager.Instance.WeaponManager.ScreenWeapon);
                damageModifiers += swingMods.damageMod;
                chanceToHitMod  += swingMods.toHitMod;

                // Apply proficiency modifiers
                FormulaHelper.ToHitAndDamageMods proficiencyMods = FormulaHelper.CalculateProficiencyModifiers(attacker, weapon);
                damageModifiers += proficiencyMods.damageMod;
                chanceToHitMod  += proficiencyMods.toHitMod;

                // Apply racial bonuses
                FormulaHelper.ToHitAndDamageMods racialMods = FormulaHelper.CalculateRacialModifiers(attacker, weapon, player);
                damageModifiers += racialMods.damageMod;
                chanceToHitMod  += racialMods.toHitMod;

                backstabChance  = FormulaHelper.CalculateBackstabChance(player, null, enemyAnimStateRecord);
                chanceToHitMod += backstabChance;
            }

            // Choose struck body part
            int struckBodyPart = FormulaHelper.CalculateStruckBodyPart();

            // Get damage for weaponless attacks
            if (skillID == (short)DFCareer.Skills.HandToHand)
            {
                unarmedAttack = true; // Check for later if weapon is NOT being used.

                if (attacker == player || (AIAttacker != null && AIAttacker.EntityType == EntityTypes.EnemyClass))
                {
                    if (FormulaHelper.CalculateSuccessfulHit(attacker, target, chanceToHitMod, struckBodyPart))
                    {
                        damage = FormulaHelper.CalculateHandToHandAttackDamage(attacker, target, damageModifiers, attacker == player); // Added my own, non-overriden version of this method for modification.

                        damage = FormulaHelper.CalculateBackstabDamage(damage, backstabChance);
                    }
                }
                else if (AIAttacker != null) // attacker is a monster
                {
                    specialMonsterWeapon = PhysicalCombatArmorPatch.SpecialWeaponCheckForMonsters(attacker);

                    if (specialMonsterWeapon)
                    {
                        unarmedAttack = false;
                        weaponAttack  = true;
                        weapon        = PhysicalCombatArmorPatch.MonsterWeaponAssign(attacker);
                        skillID       = weapon.GetWeaponSkillIDAsShort();
                        if (skillID == 32) // Checks if the weapon being used is in the Blunt Weapon category, then sets a bool value to true.
                        {
                            bluntWep = true;
                        }
                    }

                    // Handle multiple attacks by AI
                    int minBaseDamage = 0;
                    int maxBaseDamage = 0;
                    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;
                        }

                        int reflexesChance = 50 - (10 * ((int)player.Reflexes - 2));

                        if (DFRandom.rand() % 100 < reflexesChance && minBaseDamage > 0 && FormulaHelper.CalculateSuccessfulHit(attacker, target, chanceToHitMod, struckBodyPart))
                        {
                            int hitDamage = UnityEngine.Random.Range(minBaseDamage, maxBaseDamage + 1);
                            // Apply special monster attack effects
                            //SHIELD MODULE ADDITION: If player is not blocking with shield, allow custom hit effects to apply.\\
                            if (hitDamage > 0 && !FPSShield.isBlocking)
                            {
                                FormulaHelper.OnMonsterHit(AIAttacker, target, hitDamage);
                            }

                            damage += hitDamage;
                        }
                        ++attackNumber;
                    }
                    if (damage >= 1)
                    {
                        damage = FormulaHelper.CalculateHandToHandAttackDamage(attacker, target, damage, attacker == player); // Added my own, non-overriden version of this method for modification.
                    }
                }
            }
            // Handle weapon attacks
            else if (weapon != null)
            {
                weaponAttack = true; // Check for later on if weapon is being used.

                // Apply weapon material modifier.
                chanceToHitMod += FormulaHelper.CalculateWeaponToHit(weapon);

                // Mod hook for adjusting final hit chance mod. (is a no-op in DFU)
                if (PhysicalCombatArmorPatch.archeryModuleCheck)
                {
                    chanceToHitMod = FormulaHelper.AdjustWeaponHitChanceMod(attacker, target, chanceToHitMod, weaponAnimTime, weapon);
                }

                if (FormulaHelper.CalculateSuccessfulHit(attacker, target, chanceToHitMod, struckBodyPart))
                {
                    damage = FormulaHelper.CalculateWeaponAttackDamage(attacker, target, damageModifiers, weaponAnimTime, weapon);

                    damage = FormulaHelper.CalculateBackstabDamage(damage, backstabChance);
                }

                // Handle poisoned weapons
                //SHIELD MODULE ADDITION: If player is not blocking with shield, allow custom hit effects to apply.\\
                if (damage > 0 && weapon.poisonType != Poisons.None && !FPSShield.isBlocking)
                {
                    FormulaHelper.InflictPoison(attacker, target, weapon.poisonType, false);
                    weapon.poisonType = Poisons.None;
                }
            }

            damage = Mathf.Max(0, damage); // I think this is just here to keep damage from outputting a negative value.

            //Debug.LogFormat("4. Here is damage value before crit modifier is applied = {0}", damage);

            if (critSuccess)                                      // Since the critSuccess variable only ever becomes true inside when the module is active, this is always false when that module is disabled.
            {
                damage = (int)Mathf.Round(damage * critDamMulti); // Multiplies 'Final' damage values, before reductions, with the critical damage multiplier.
                                                                  //Debug.LogFormat("5. Here is damage value AFTER crit modifier is applied = {0}", damage);
            }

            //if (attacker == player)
            //Debug.LogFormat("2. Here is damage value BEFORE soft material requirement modifier is applied = {0}", damage);

            float damCheckBeforeMatMod = damage;

            damage = (int)Mathf.Round(damage * matReqDamMulti); // Could not find much better place to put there, so here seems fine, right after crit multiplier is taken into account.

            //if (attacker == player)
            //Debug.LogFormat("3. Here is damage value AFTER soft material requirement modifier is applied = {0}", damage);

            float damCheckAfterMatMod = damage;

            if (PhysicalCombatArmorPatch.softMatRequireModuleCheck)
            {
                if (attacker == player)
                {
                    if (damCheckBeforeMatMod > 0 && (damCheckAfterMatMod / damCheckBeforeMatMod) <= 0.45f)
                    {
                        DaggerfallUI.AddHUDText("This Weapon Is Not Very Effective Against This Creature.", 1.00f);
                    }
                }
            }

            int targetEndur = target.Stats.LiveEndurance - 50;
            int targetStren = target.Stats.LiveStrength - 50; // Every point of these does something, positive and negative between 50.
            int targetWillp = target.Stats.LiveWillpower - 50;

            float naturalDamResist = (targetEndur * .002f);

            naturalDamResist += (targetStren * .001f);
            naturalDamResist += (targetWillp * .001f);

            Mathf.Clamp(naturalDamResist, -0.2f, 0.2f);                                      // This is to keep other mods that allow over 100 attribute points from allowing damage reduction values to go over 20%. May actually remove this cap for monsters, possibly, since some of the higher level ones have over 100 attribute points.
                                                                                             //Debug.LogFormat("Natural Damage Resist = {0}", naturalDamResist);

            DaggerfallUnityItem shield = target.ItemEquipTable.GetItem(EquipSlots.LeftHand); // Checks if character is using a shield or not.
            bool shieldStrongSpot      = false;

            PhysicalCombatArmorPatch.shieldBlockSuccess = false;
            if (shield != null)
            {
                BodyParts[] protectedBodyParts = shield.GetShieldProtectedBodyParts();

                for (int i = 0; (i < protectedBodyParts.Length) && !shieldStrongSpot; i++)
                {
                    if (protectedBodyParts[i] == (BodyParts)struckBodyPart)
                    {
                        shieldStrongSpot = true;
                    }
                }
                PhysicalCombatArmorPatch.shieldBlockSuccess = PhysicalCombatArmorPatch.ShieldBlockChanceCalculation(target, shieldStrongSpot, shield);

                if (PhysicalCombatArmorPatch.shieldBlockSuccess)
                {
                    PhysicalCombatArmorPatch.shieldBlockSuccess = PhysicalCombatArmorPatch.CompareShieldToUnderArmor(target, struckBodyPart, naturalDamResist);
                }
            }

            if (PhysicalCombatArmorPatch.condBasedEffectModuleCheck) // Only runs if "Condition Based Effectiveness" module is active. As well if a weapon is even being used.
            {
                if (attacker == player && weapon != null)            // Only the player has weapon damage effected by condition value.
                {
                    damage = PhysicalCombatArmorPatch.AlterDamageBasedOnWepCondition(damage, bluntWep, weapon);
                    //Debug.LogFormat("Damage Multiplier Due To Weapon Condition = {0}", damage);
                }
            }


            //--->AMBIDEXTERITY MODULE ADDITION<---\\
            //beginning of major code additions to catch and redirect damage for the parry and shield mechanics.

            //--->PARRY ADDITION<---\\
            //beginning of parry system. Checks who is the target and attacker and activates proper parry accordingly.

            //if the player is not involved in the attack, do the following npc parry check code.....
            if (target != GameManager.Instance.PlayerEntity && attacker != GameManager.Instance.PlayerEntity)
            {
                //sets up attacker and target objects to check if they are in attack state when damage is being done.
                DaggerfallMobileUnit targetController   = target.EntityBehaviour.GetComponentInChildren <DaggerfallMobileUnit>();
                DaggerfallMobileUnit attackerController = attacker.EntityBehaviour.GetComponentInChildren <DaggerfallMobileUnit>();

                if (targetController.Summary.EnemyState == MobileStates.PrimaryAttack && attackerController.Summary.EnemyState == MobileStates.PrimaryAttack)
                {
                    //grabs attackers sense object.
                    EnemySenses attackerSenses = attacker.EntityBehaviour.GetComponent <EnemySenses>();

                    //uses attackers sense to figure out their direction to target using their position data.
                    Vector3 toTarget = attackerSenses.PredictedTargetPos - attackerSenses.transform.position;
                    toTarget.y = 0;

                    //uses attackers sense to figure out their direction to target using their position data.
                    Vector3 targetDirection2D = attackerSenses.PredictedTargetPos - attackerSenses.transform.position;
                    targetDirection2D.y = 0;

                    //if the attack angle is 35 degree angle degrees or more (player does not have them close to center screen) don't register the parry.
                    if (!(Vector3.Angle(toTarget, targetDirection2D) > 30))
                    {
                        AmbidexterityManager.AmbidexterityManagerInstance.activateNPCParry(target, attacker, damage);
                        Debug.Log("Enemy Parry!");
                        damage = 0;
                    }
                }
            }

            //--SHIELD REDIRECT CODE--\\
            //checks to see if player is blocking yet and if the target is the player. If so, assign damage to attackerDamage, enemy object to enemyEntity, and
            //0 out the damage, so player doesn't take any.
            if (FPSShield.isBlocking && target == GameManager.Instance.PlayerEntity && damage != 0)
            {
                //grabs attackers sense object.
                EnemySenses attackerSenses = attacker.EntityBehaviour.GetComponent <EnemySenses>();

                //uses attackers sense to figure out their direction to target using their position data.
                Vector3 toTarget = attackerSenses.PredictedTargetPos - attackerSenses.transform.position;
                toTarget.y = 0;

                //grabs players main camera object and sets up player direction using the camera object.
                Vector3 targetDirection2D;
                Camera  mainCamera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent <Camera>();
                targetDirection2D = -new Vector3(mainCamera.transform.forward.x, 0, mainCamera.transform.forward.z);

                //if the attack angle is shield block angle degrees or more (player does not have them on screen) don't register the block.
                if (!(Vector3.Angle(toTarget, targetDirection2D) > FPSShield.blockAngle) || !(Vector3.Angle(toTarget, targetDirection2D) > 40))
                {
                    Debug.Log("Attack parried!");
                    AmbidexterityManager.isHit          = true;
                    AmbidexterityManager.attackerDamage = damage;
                    damage = 0;
                    AmbidexterityManager.AmbidexterityManagerInstance.attackerEntity = attacker;
                }
            }

            if (damage < 1) // Cut off the execution if the damage is still not anything higher than 1 at this point in the method.
            {
                return(damage);
            }

            FormulaHelper.DamageEquipment(attacker, target, damage, weapon, struckBodyPart); // Might alter this later so that equipment damage is only calculated with the amount that was reduced, not the whole initial amount, will see.

            if (((target != player) && (AITarget.EntityType == EntityTypes.EnemyMonster)))
            {
                monsterArmorCheck = PhysicalCombatArmorPatch.ArmorStruckVerification(target, struckBodyPart); // Check for if a monster has a piece of armor/shield hit by an attack, returns true if so.

                if (!monsterArmorCheck)
                {
                    //Debug.Log("------------------------------------------------------------------------------------------");
                    //Debug.LogFormat("Here is damage value before Monster 'Natural' Damage reduction is applied = {0}", damage);

                    damage = PhysicalCombatArmorPatch.PercentageReductionCalculationForMonsters(attacker, target, damage, bluntWep, naturalDamResist);

                    //Debug.LogFormat("Here is damage value after Monster 'Natural' Damage reduction = {0}", damage);
                    //Debug.Log("------------------------------------------------------------------------------------------");
                }
                else
                {
                    if (unarmedAttack)
                    {
                        //Debug.Log("------------------------------------------------------------------------------------------");
                        //Debug.LogFormat("Here is damage value before armor reduction is applied = {0}", damage);

                        damage = PhysicalCombatArmorPatch.CalculateArmorDamageReductionWithUnarmed(attacker, target, damage, struckBodyPart, naturalDamResist); // This will be the method call for armor reduction against unarmed.

                        //Debug.LogFormat("Here is damage value after armor reduction = {0}", damage);
                        //Debug.Log("------------------------------------------------------------------------------------------");
                    }
                    else if (weaponAttack)
                    {
                        //Debug.Log("------------------------------------------------------------------------------------------");
                        //Debug.LogFormat("Here is damage value before armor reduction is applied = {0}", damage);

                        damage = PhysicalCombatArmorPatch.CalculateArmorDamageReductionWithWeapon(attacker, target, damage, weapon, struckBodyPart, naturalDamResist); // This will be the method call for armor reduction against weapons.

                        //Debug.LogFormat("Here is damage value after armor reduction = {0}", damage);
                        //Debug.Log("------------------------------------------------------------------------------------------");
                    }
                }
            }
            else
            {
                if (unarmedAttack)
                {
                    //Debug.Log("------------------------------------------------------------------------------------------");
                    //Debug.LogFormat("Here is damage value before armor reduction is applied = {0}", damage);
                    int damBefore = damage;

                    damage = PhysicalCombatArmorPatch.CalculateArmorDamageReductionWithUnarmed(attacker, target, damage, struckBodyPart, naturalDamResist); // This will be the method call for armor reduction against unarmed.

                    int damAfter = damage;
                    //Debug.LogFormat("Here is damage value after armor reduction = {0}", damage);
                    if (damBefore > 0)
                    {
                        int damReduPercent = ((100 * damAfter / damBefore) - 100) * -1;
                        //Debug.LogFormat("Here is damage reduction percent = {0}%", damReduPercent);
                    }
                    //Debug.Log("------------------------------------------------------------------------------------------");
                }
                else if (weaponAttack)
                {
                    //Debug.Log("------------------------------------------------------------------------------------------");
                    //Debug.LogFormat("Here is damage value before armor reduction is applied = {0}", damage);
                    int damBefore = damage;

                    damage = PhysicalCombatArmorPatch.CalculateArmorDamageReductionWithWeapon(attacker, target, damage, weapon, struckBodyPart, naturalDamResist); // This will be the method call for armor reduction against weapons.

                    int damAfter = damage;
                    //Debug.LogFormat("Here is damage value after armor reduction = {0}", damage);
                    if (damBefore > 0)
                    {
                        int damReduPercent = ((100 * damAfter / damBefore) - 100) * -1;
                        //Debug.LogFormat("Here is damage reduction percent = {0}%", damReduPercent);
                    }
                    //Debug.Log("------------------------------------------------------------------------------------------");
                }
            }

            // Apply Ring of Namira effect
            if (target == player)
            {
                DaggerfallUnityItem[] equippedItems = target.ItemEquipTable.EquipTable;
                DaggerfallUnityItem   item          = null;
                if (equippedItems.Length != 0)
                {
                    if (IsRingOfNamira(equippedItems[(int)EquipSlots.Ring0]) || IsRingOfNamira(equippedItems[(int)EquipSlots.Ring1]))
                    {
                        IEntityEffect effectTemplate = GameManager.Instance.EntityEffectBroker.GetEffectTemplate(RingOfNamiraEffect.EffectKey);
                        effectTemplate.EnchantmentPayloadCallback(EnchantmentPayloadFlags.None,
                                                                  targetEntity: AIAttacker.EntityBehaviour,
                                                                  sourceItem: item,
                                                                  sourceDamage: damage);
                    }
                }
            }

            //Debug.LogFormat("Damage {0} applied, animTime={1}  ({2})", damage, weaponAnimTime, GameManager.Instance.WeaponManager.ScreenWeapon.WeaponState);

            return(damage);
        }
Example #16
0
        public static int CalculateAttackDamage(DaggerfallEntity attacker, DaggerfallEntity target, bool enemyAnimStateRecord, int weaponAnimTime, DaggerfallUnityItem weapon)
        {
            if (attacker == null || target == null)
            {
                return(0);
            }

            int          damageModifiers = 0;
            int          damage          = 0;
            int          chanceToHitMod  = 0;
            int          backstabChance  = 0;
            PlayerEntity player          = GameManager.Instance.PlayerEntity;
            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.
            EnemyEntity AIAttacker = attacker as EnemyEntity;

            if (AIAttacker != null && weapon != null)
            {
                int weaponAverage   = (weapon.GetBaseDamageMin() + weapon.GetBaseDamageMax()) / 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 > (WeaponMaterialTypes)weapon.NativeMaterialValue)
                {
                    if (attacker == player)
                    {
                        DaggerfallUI.Instance.PopupMessage(TextManager.Instance.GetLocalizedText("materialIneffective"));
                    }
                    return(0);
                }
                // Get weapon skill used
                skillID = weapon.GetWeaponSkillIDAsShort();
            }
            else
            {
                skillID = (short)DFCareer.Skills.HandToHand;
            }

            chanceToHitMod = attacker.Skills.GetLiveSkillValue(skillID);

            if (attacker == player)
            {
                // Apply swing modifiers
                FormulaHelper.ToHitAndDamageMods swingMods = FormulaHelper.CalculateSwingModifiers(GameManager.Instance.WeaponManager.ScreenWeapon);
                damageModifiers += swingMods.damageMod;
                chanceToHitMod  += swingMods.toHitMod;

                // Apply proficiency modifiers
                FormulaHelper.ToHitAndDamageMods proficiencyMods = FormulaHelper.CalculateProficiencyModifiers(attacker, weapon);
                damageModifiers += proficiencyMods.damageMod;
                chanceToHitMod  += proficiencyMods.toHitMod;

                // Apply racial bonuses
                FormulaHelper.ToHitAndDamageMods racialMods = FormulaHelper.CalculateRacialModifiers(attacker, weapon, player);
                damageModifiers += racialMods.damageMod;
                chanceToHitMod  += racialMods.toHitMod;

                backstabChance  = FormulaHelper.CalculateBackstabChance(player, null, enemyAnimStateRecord);
                chanceToHitMod += backstabChance;
            }

            // Choose struck body part
            backstabChance = FormulaHelper.CalculateBackstabChance(player, null, enemyAnimStateRecord);
            int struckBodyPart = FormulaHelper.CalculateStruckBodyPart();

            // Get damage for weaponless attacks
            if (skillID == (short)DFCareer.Skills.HandToHand)
            {
                if (attacker == player || (AIAttacker != null && AIAttacker.EntityType == DaggerfallWorkshop.EntityTypes.EnemyClass))
                {
                    if (FormulaHelper.CalculateSuccessfulHit(attacker, target, chanceToHitMod, struckBodyPart))
                    {
                        damage = FormulaHelper.CalculateHandToHandAttackDamage(attacker, target, damageModifiers, attacker == player);

                        damage = FormulaHelper.CalculateBackstabDamage(damage, backstabChance);
                    }
                }
                else if (AIAttacker != null) // attacker is a monster
                {
                    // Handle multiple attacks by AI
                    int minBaseDamage = 0;
                    int maxBaseDamage = 0;
                    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;
                        }

                        int reflexesChance = 50 - (10 * ((int)player.Reflexes - 2));

                        if (DaggerfallWorkshop.DFRandom.rand() % 100 < reflexesChance && minBaseDamage > 0 && FormulaHelper.CalculateSuccessfulHit(attacker, target, chanceToHitMod, struckBodyPart))
                        {
                            int hitDamage = UnityEngine.Random.Range(minBaseDamage, maxBaseDamage + 1);
                            // Apply special monster attack effects
                            //SHIELD MODULE ADDITION: If player is not blocking with shield, allow custom hit effects to apply.\\
                            if (hitDamage > 0 && !FPSShield.isBlocking)
                            {
                                FormulaHelper.OnMonsterHit(AIAttacker, target, hitDamage);
                            }

                            damage += hitDamage;
                        }
                        ++attackNumber;
                    }
                }
            }
            // Handle weapon attacks
            else if (weapon != null)
            {
                // Apply weapon material modifier.
                chanceToHitMod += FormulaHelper.CalculateWeaponToHit(weapon);

                // Mod hook for adjusting final hit chance mod and adding new elements to calculation. (no-op in DFU)
                chanceToHitMod = FormulaHelper.AdjustWeaponHitChanceMod(attacker, target, chanceToHitMod, weaponAnimTime, weapon);

                if (FormulaHelper.CalculateSuccessfulHit(attacker, target, chanceToHitMod, struckBodyPart))
                {
                    damage = FormulaHelper.CalculateWeaponAttackDamage(attacker, target, damageModifiers, weaponAnimTime, weapon);

                    damage = FormulaHelper.CalculateBackstabDamage(damage, backstabChance);
                }

                // Handle poisoned weapons
                if (damage > 0 && weapon.poisonType != Poisons.None)
                {
                    FormulaHelper.InflictPoison(attacker, target, weapon.poisonType, false);
                    weapon.poisonType = Poisons.None;
                }
            }

            damage = Mathf.Max(0, damage);

            //--->AMBIDEXTERITY MODULE ADDITION<---\\
            //beginning of major code additions to catch and redirect damage for the parry and shield mechanics.

            //--->PARRY ADDITION<---\\
            //beginning of parry system. Checks who is the target and attacker and activates proper parry accordingly.

            //if the player is not involved in the attack, do the following npc parry check code.....
            if (target != GameManager.Instance.PlayerEntity && attacker != GameManager.Instance.PlayerEntity)
            {
                //sets up attacker and target objects to check if they are in attack state when damage is being done.
                DaggerfallMobileUnit targetController   = target.EntityBehaviour.GetComponentInChildren <DaggerfallMobileUnit>();
                DaggerfallMobileUnit attackerController = attacker.EntityBehaviour.GetComponentInChildren <DaggerfallMobileUnit>();

                if (targetController.Summary.EnemyState == MobileStates.PrimaryAttack && attackerController.Summary.EnemyState == MobileStates.PrimaryAttack)
                {
                    //grabs attackers sense object.
                    EnemySenses attackerSenses = attacker.EntityBehaviour.GetComponent <EnemySenses>();

                    //uses attackers sense to figure out their direction to target using their position data.
                    Vector3 toTarget = attackerSenses.PredictedTargetPos - attackerSenses.transform.position;
                    toTarget.y = 0;

                    //uses attackers sense to figure out their direction to target using their position data.
                    Vector3 targetDirection2D = attackerSenses.PredictedTargetPos - attackerSenses.transform.position;
                    targetDirection2D.y = 0;

                    //if the attack angle is 35 degree angle degrees or more (player does not have them close to center screen) don't register the parry.
                    if (!(Vector3.Angle(toTarget, targetDirection2D) > 30))
                    {
                        AmbidexterityManager.AmbidexterityManagerInstance.activateNPCParry(target, attacker, damage);
                        Debug.Log("Enemy Parry!");
                        damage = 0;
                    }
                }
            }

            //if the player is the target of the attack, do the following player parry check code.....
            if (target == GameManager.Instance.PlayerEntity)
            {
                //grabs the attackers mobileunit class object to check their state below.
                DaggerfallMobileUnit attackerController = attacker.EntityBehaviour.GetComponentInChildren <DaggerfallMobileUnit>();

                //if the enemy is in their primary/melee attack state and the player is on frame 1 or 2, do ....
                if (attackerController.Summary.EnemyState == MobileStates.PrimaryAttack && AmbidexterityManager.AmbidexterityManagerInstance.AttackState != 0 && (AltFPSWeapon.currentFrame == 2 || OffHandFPSWeapon.currentFrame == 2))
                {
                    //grabs attackers sense object.
                    EnemySenses attackerSenses = attacker.EntityBehaviour.GetComponent <EnemySenses>();

                    //uses attackers sense to figure out their direction to target using their position data.
                    Vector3 toTarget = attackerSenses.PredictedTargetPos - attackerSenses.transform.position;
                    toTarget.y = 0;

                    //grabs players main camera object and sets up player direction using the camera object.
                    Vector3 targetDirection2D;
                    Camera  mainCamera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent <Camera>();
                    targetDirection2D = -new Vector3(mainCamera.transform.forward.x, 0, mainCamera.transform.forward.z);

                    //if the attack angle is 35 degree angle degrees or more (player does not have them close to center screen) don't register the parry.
                    if (!(Vector3.Angle(toTarget, targetDirection2D) > 30))
                    {
                        Debug.Log("Player Parry!");
                        AmbidexterityManager.isHit          = true;
                        AmbidexterityManager.attackerDamage = damage;
                        AmbidexterityManager.AmbidexterityManagerInstance.attackerEntity = attacker;
                        AmbidexterityManager.AmbidexterityManagerInstance.targetEntity   = target;
                        AmbidexterityManager.AmbidexterityManagerInstance.activatePlayerParry(attacker, damage);
                        damage = 0;
                    }
                }

                //if the player is the target of the attack, do the following player parry check code.....
                if (attacker == GameManager.Instance.PlayerEntity)
                {
                    //if the enemy is in their primary/melee attack state and the player is on their hit frame, do ....
                    if (attackerController.Summary.EnemyState == MobileStates.PrimaryAttack && AltFPSWeapon.currentFrame > 2)
                    {
                        Destroy(Instantiate(AmbidexterityManager.sparkParticles, attacker.EntityBehaviour.transform.position + (attacker.EntityBehaviour.transform.forward * .35f), Quaternion.identity, null), 1.0f);
                        damage = 0;
                    }
                }
            }

            //--SHIELD REDIRECT CODE--\\
            //checks to see if player is blocking yet and if the target is the player. If so, assign damage to attackerDamage, enemy object to enemyEntity, and
            //0 out the damage, so player doesn't take any.
            if ((FPSShield.isBlocking || AmbidexterityManager.AmbidexterityManagerInstance.AttackState == 7) && target == GameManager.Instance.PlayerEntity && damage != 0)
            {
                //grabs attackers sense object.
                EnemySenses attackerSenses = attacker.EntityBehaviour.GetComponent <EnemySenses>();

                //uses attackers sense to figure out their direction to target using their position data.
                Vector3 toTarget = attackerSenses.PredictedTargetPos - attackerSenses.transform.position;
                toTarget.y = 0;

                //grabs players main camera object and sets up player direction using the camera object.
                Vector3 targetDirection2D;
                Camera  mainCamera = GameObject.FindGameObjectWithTag("MainCamera").GetComponent <Camera>();
                targetDirection2D = -new Vector3(mainCamera.transform.forward.x, 0, mainCamera.transform.forward.z);

                //if the attack angle is shield block angle degrees or more (player does not have them on screen) don't register the block.
                if (!(Vector3.Angle(toTarget, targetDirection2D) > FPSShield.blockAngle) || !(Vector3.Angle(toTarget, targetDirection2D) > 40))
                {
                    Debug.Log("Attack parried!");
                    AmbidexterityManager.isHit          = true;
                    AmbidexterityManager.attackerDamage = damage;
                    damage = 0;
                    AmbidexterityManager.AmbidexterityManagerInstance.attackerEntity = attacker;
                }
            }

            FormulaHelper.DamageEquipment(attacker, target, damage, weapon, struckBodyPart);

            // Apply Ring of Namira effect
            if (target == player)
            {
                DaggerfallUnityItem[] equippedItems = target.ItemEquipTable.EquipTable;
                DaggerfallUnityItem   item          = null;
                if (equippedItems.Length != 0)
                {
                    if (IsRingOfNamira(equippedItems[(int)EquipSlots.Ring0]) || IsRingOfNamira(equippedItems[(int)EquipSlots.Ring1]))
                    {
                        IEntityEffect effectTemplate = GameManager.Instance.EntityEffectBroker.GetEffectTemplate(RingOfNamiraEffect.EffectKey);
                        effectTemplate.EnchantmentPayloadCallback(EnchantmentPayloadFlags.None,
                                                                  targetEntity: AIAttacker.EntityBehaviour,
                                                                  sourceItem: item,
                                                                  sourceDamage: damage);
                    }
                }
            }
            return(damage);
        }