Cracking Open Combat

Discuss modding questions and implementation details.
l3lessed
Posts: 85
Joined: Mon Aug 12, 2019 4:32 pm

Re: Cracking Open Combat

Post by l3lessed » Fri Aug 16, 2019 6:36 pm

So, I think i found out why they where apply the range they were. It doesn't have to do with raycasting at all. It instead, has to do with how the weapons manager tracks what weapons are equiped and which ones are selected for actual combat. In order to make it easy for the system to update range no matter what is equipped in what hand, they just have it run every frame. It's a simple solution, but not super efficient coding and cpu cycle wise. I'm going to see if I can streamline this a little, while I work through this project. The more cpu cycles/ frames I can free up, the more I can use those freed cycles for other combat related computations. I honestly think they were planning on doing something more complicated with range and stamina and other combat mechanics, but ended early because of developer creep and bugs. Original release of the game was pushed out and bug ridden, despite its scale and greatness.

I can update this, but it will probably required me adding some type of pass through method just for setting range when weapons are switched and equipped,

Some more good news. I was able to ram in a simple raycasting debug line. Now, when in the unity engine, I can see where attacks are casting and how far they are going out. It draws a bright green line in the scene window for 600 seconds where the spherecast is. However, since it is a debug feature, it will not show up, on the fly, in the game. To do that, the raycast code itself would need modified, and I don't even want to dump the time into that yet, if ever.

Also, the game is using spherecasts, which are large spheres that are project outward instead of a small line to blow up the hit trace box and make it more likely melee weapons will hit on the side of the screen. However, this explains why weapons feel odd and janky a lot of times. The on screen animation has nothing to do with what is being detected for hits. There is a single sphere being projected outward from the center of the screen/mouse position. If an enemy is on the side of your screen, and the weapon animation seems like it should hit, it never will.

This was an issue faced in Age of Chivalry, when it was a HL2 MOD, and I was working on it heavily with the modding team. HL2 was built for gun based play, so all attacks where raycast lines to simulate bullets. To mimic melee weapons, we built a raycast projector routine/method that read a simple text file, and then cast a series of rays, instead of a single one, to try and mimic the animation of the weapon as close as possible. It was not perfect, but it may be a good solution here for the age of the engine and the more simplified mechanics and visuals.

l3lessed
Posts: 85
Joined: Mon Aug 12, 2019 4:32 pm

Re: Cracking Open Combat

Post by l3lessed » Fri Aug 16, 2019 10:06 pm

I have began an official mod tracking thread. Please refer to it for any update news. However, lease keep in depth discussion in this thread, so my tracking thread doesn't get swamped with posts. I have put up descriptions of features and where they are at, bug and coding changes, and some screenshots for proof of concept.

Also, fist issue and missing range issue has been debugged. There is now a if - then, that will check and assign a default classic value of 2.25f to fists.

viewtopic.php?f=14&t=2533&p=29528#p29528

l3lessed
Posts: 85
Joined: Mon Aug 12, 2019 4:32 pm

Re: Cracking Open Combat

Post by l3lessed » Sat Aug 17, 2019 6:30 am

Interesting discovery about how armor is used.

So, armor value is merely added the the hit chance value, which contrary to though, the higher it is, the less likely you will be hit. There is no damage reduction from armor in any form.

However, an interesting note, the engine will randomly pick from a set of 6 numbers, each one representing a body part. Based on the body part/number picked, it will pull the armor value from the specific body part and add it's value to the hit chance calculator. Also, interesting bonus feature added by the df unity team. Shields block your arms and hand and take damage for them instead of your armor.

However, shields, according to the unofficial wiki, provide minimal armor addition, which is fine if a blocking mechanic can be added. I can't find the specific code for the shield adding modifiers.
https://en.uesp.net/wiki/Daggerfall:Armor

User avatar
mikeprichard
Posts: 709
Joined: Sun Feb 19, 2017 6:49 pm

Re: Cracking Open Combat

Post by mikeprichard » Sat Aug 17, 2019 6:49 am

l3lessed wrote:
Sat Aug 17, 2019 6:30 am
Interesting discovery about how armor is used.

So, armor value is merely added the the hit chance value, which contrary to though, the higher it is, the less likely you will be hit. There is no damage reduction from armor in any form.

However, an interesting note, the engine will randomly pick from a set of 6 numbers, each one representing a body part. Based on the body part/number picked, it will pull the armor value from the specific body part and add it's value to the hit chance calculator. Also, interesting bonus feature added by the df unity team. Shields block your arms and hand and take damage for them instead of your armor.

However, shields, according to the unofficial wiki, provide minimal armor addition, which is fine if a blocking mechanic can be added. I can't find the specific code for the shield adding modifiers.
https://en.uesp.net/wiki/Daggerfall:Armor
This is a helpful start for my plans to update that wiki page as to how exactly armor works - will keep an eye out here for more! Not sure how the 6 random numbers exactly relate to the 7 different body parts covered by armor (pauldrons having left and right pieces separately), or how shields factor in, etc., but this is encouraging.

Firebrand
Posts: 150
Joined: Thu Jul 18, 2019 6:07 pm

Re: Cracking Open Combat

Post by Firebrand » Sat Aug 17, 2019 7:54 am

I've never understood why shields didn't get material modifiers to their armor rating, like all other pieces of armor. Is there a reason, or it's just an oversight from the developers of classic Daggerfall? :?

Ommamar
Posts: 334
Joined: Thu Jul 18, 2019 3:08 am

Re: Cracking Open Combat

Post by Ommamar » Sat Aug 17, 2019 3:46 pm

Firebrand wrote:
Sat Aug 17, 2019 7:54 am
I've never understood why shields didn't get material modifiers to their armor rating, like all other pieces of armor. Is there a reason, or it's just an oversight from the developers of classic Daggerfall? :?
I don't know if it was an intent or a crap where out of time and need to ship this game reason. Here is a thread with some ideas and discussion about it viewtopic.php?f=22&t=2451

l3lessed
Posts: 85
Joined: Mon Aug 12, 2019 4:32 pm

Re: Cracking Open Combat

Post by l3lessed » Sat Aug 17, 2019 11:38 pm

Here is what the body parts mean by number directly from code.

Code: Select all

    public enum BodyParts
    {
        None = -1,
        Head = 0,
        RightArm = 1,
        LeftArm = 2,
        Chest = 3,
        Hands = 4,
        Legs = 5,
        Feet = 6,
    }
Here is how the engine decides what is hit by code, including if the shield or hands/arms need to take the damage.

First is the random chance roller to see what is hit out of the above list, with feet and head having the lowest chance and chest having the highest.

Code: Select all

        private static int CalculateStruckBodyPart()
        {
            Formula_1i del;
            if (formula_1i.TryGetValue("CalculateStruckBodyPart", out del))
                return del(0);

            int[] bodyParts = { 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6 };
            return bodyParts[UnityEngine.Random.Range(0, bodyParts.Length)];
        }
Once it randomly chooses one of those numbers from the method, it then figures out if a shield is equipped or not, then assigns damage to the proper equipped item.

Code: Select all

weapon.DamageThroughPhysicalHit(damage, attacker);

                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)
                    shield.DamageThroughPhysicalHit(damage, target);
                else
                {
                    EquipSlots hitSlot = DaggerfallUnityItem.GetEquipSlotForBodyPart((BodyParts)struckBodyPart);
                    DaggerfallUnityItem armor = target.ItemEquipTable.GetItem(hitSlot);
                    if (armor != null)
                        armor.DamageThroughPhysicalHit(damage, target);
                }
Let me know if you need any of this explained.

Ommamar
Posts: 334
Joined: Thu Jul 18, 2019 3:08 am

Re: Cracking Open Combat

Post by Ommamar » Sun Aug 18, 2019 12:19 am

Hey thanks for the code very helpful. To me it really shows how weak shields are in DFU, if I have the system right the negation is based on the number of the armor piece that is hit which is determined by material type. So even if you have iron plate you are better of not carrying a shield as the most damage a shield can negate in the current system is 4 points where every iron piece gives you a +7 negation factor. At this time it is better for your armor worn on the body to be hit as it has more damage negation. It would be great if we could get some material negation into shields as the only reason to use them would be item enchantments. I think this is an important point as once you implement a block with shield (or weapon?) you will put more focus on the shield taking the damage. If it is limited to 4 points of negation at the maximum makes it a very unattractive choice.

User avatar
mikeprichard
Posts: 709
Joined: Sun Feb 19, 2017 6:49 pm

Re: Cracking Open Combat

Post by mikeprichard » Sun Aug 18, 2019 2:30 am

l3lessed, this is really helpful; I think even I am starting to understand! Apologies in advance for the wall of text, but there are a few points from your above post I'd like to clarify:

1) If I'm reading your quoted code correctly, the game essentially rolls a twenty-sided die each time you're attacked by a physical weapon, and assigns the resulting hit to one of the seven body parts as follows:

Head (helm) = 2/20 chance
Right Arm (right pauldron) = 3/20 chance
Left Arm (left pauldron) = 3/20 chance
Chest (cuirass) = 4/20 chance
Hands (gauntlets) = 4/20 chance
Legs (greaves) = 3/20 chance
Feet (boots) = 1/20 chance

The displayed armor "modifier" (which would more accurately be referred to as the total armor "rating" - e.g. 21 for daedric armor pieces) for the selected armor piece is then applied as a negative value to reduce the enemy's final to-hit chance against the player. First, is this all correct so far? Second, if so, how exactly does the "None = -1" line from your first code snippet apply here, if at all?

2) In my very limited testing (viewtopic.php?f=8&t=2514#p29304), it seemed I got hit a LOT less than only 21% less often when wearing full Daedric vs. when completely naked. I'm the first to say that my in-game testing is not to be relied upon and only the code can tell us what's really going on, but since my results seemed so far off the above analysis, are you certain that the armor "modifier"/"rating" (again, 21 for daedric pieces in this example) is merely another modifier flatly subtracted from the enemy's chance to hit the player? Or am I completely missing something here?

3) Finally, re: how shields work, you mentioned earlier that at least in DFU, "Shields block your arms and hand and take damage for them instead of your armor". However, in the inventory paper doll UI, four of the seven displayed body part armor ratings change when I equip a shield (e.g. go from "21" to "25" when equipping a tower shield over full daedric armor): the helm, left pauldron, gauntlet, and greaves slots; this is identical to classic Daggerfall's UI behavior. (I can't figure out how to grab a screenshot, but you should be able to see quickly what I mean in your own game.) Does this indicate a UI bug, or is something else going on here? Moreover, as I'm interested in this primarily for classic Daggerfall purposes to update the UESP page, do you have any idea yet how exactly shields affect armor rating(s) in classic DF? (Though I realize you can only look at the DFU code, not the classic code, so... I guess not!)

Thanks so much for your time and help to me and the community!

l3lessed
Posts: 85
Joined: Mon Aug 12, 2019 4:32 pm

Re: Cracking Open Combat

Post by l3lessed » Sun Aug 18, 2019 6:28 am

First off, your d20 ratios are correct. That is exactly how it is right now. I have dreamed about one day adding multiple hitboxes to the player to remove the random chance from body location hits.

Also, I imagine -1/none is used for missed hits in some way. Not 100% what for, unless I dig into it, but nothing that concerns armor or hit chances or things your asking about.

Heres the hit chance code.

Code: Select all

 public static int CalculateSuccessfulHit(DaggerfallEntity attacker, DaggerfallEntity target, int chanceToHitMod, int struckBodyPart)
        {
            if (attacker == null || target == null)
                return 0;

            Formula_2de_2i del;
            if (formula_2de_2i.TryGetValue("CalculateSuccessfulHit", out del))
                return del(attacker, target, chanceToHitMod, struckBodyPart);

            int chanceToHit = chanceToHitMod;
            PlayerEntity player = GameManager.Instance.PlayerEntity;
            EnemyEntity AITarget = target as 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] + target.IncreasedArmorValueModifier + target.DecreasedArmorValueModifier;
            }

            chanceToHit += armorValue;

            // Apply adrenaline rush modifiers.
            const int adrenalineRushModifier = 5;
            const int improvedAdrenalineRushModifier = 8;
            if (attacker.Career.AdrenalineRush && attacker.CurrentHealth < (attacker.MaxHealth / 8))
            {
                chanceToHit += (attacker.ImprovedAdrenalineRush) ? improvedAdrenalineRushModifier : adrenalineRushModifier;
            }

            if (target.Career.AdrenalineRush && target.CurrentHealth < (target.MaxHealth / 8))
            {
                chanceToHit -= (target.ImprovedAdrenalineRush) ? improvedAdrenalineRushModifier : adrenalineRushModifier;
            }

            // Apply enchantment modifier
            chanceToHit += attacker.ChanceToHitModifier;

            // Apply luck modifier.
            chanceToHit += ((attacker.Stats.LiveLuck - target.Stats.LiveLuck) / 10);

            // Apply agility modifier.
            chanceToHit += ((attacker.Stats.LiveAgility - target.Stats.LiveAgility) / 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.GetLiveSkillValue(DFCareer.Skills.Dodging) / 4);

            // Apply critical strike modifier.
            if (Dice100.SuccessRoll(attacker.Skills.GetLiveSkillValue(DFCareer.Skills.CriticalStrike)))
            {
                chanceToHit += (attacker.Skills.GetLiveSkillValue(DFCareer.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);

            if (Dice100.SuccessRoll(chanceToHit))
                return 1;
            else
                return 0;
        }
This is what you're asking about.

Code: Select all

            // Get armor value for struck body part
            if (struckBodyPart <= target.ArmorValues.Length)
            {
                armorValue = target.ArmorValues[struckBodyPart] + target.IncreasedArmorValueModifier + target.DecreasedArmorValueModifier;
            }

            chanceToHit += armorValue;
This line of code is confusing me a little, as I can't clearly understand the increasedarmorvaluemodifier. I imagine this is whatever armor modifiers are currently stacking, like enchantments. So, I think it takes the armor value of the location hit, adds and subtracts any stacked modifiers, and assigns it as the final armor value and adds it to your chance to hit, which actually increases your chance to not be hit.

As for the shields, they df unity team seems to have expanded their use slightly. They actually add a armor buff to the areas they cover, with tower shields covering the most; so tower shields provide +4 to the area they protect, kite +3, round +2, Bucker +1. Again, they also adsorb damage for the armor it covers slowing down the armors usual break down.

Code: Select all

        /// <summary>
        /// Get body parts protected by a shield.
        /// </summary>
        public BodyParts[] GetShieldProtectedBodyParts()
        {
            switch (TemplateIndex)
            {
                case (int)Armor.Buckler:
                    return new BodyParts[] { BodyParts.LeftArm, BodyParts.Hands };
                case (int)Armor.Round_Shield:
                case (int)Armor.Kite_Shield:
                    return new BodyParts[] { BodyParts.LeftArm, BodyParts.Hands, BodyParts.Legs };
                case (int)Armor.Tower_Shield:
                    return new BodyParts[] { BodyParts.Head, BodyParts.LeftArm, BodyParts.Hands, BodyParts.Legs };

                default:
                    return new BodyParts[] { };
            }
        }
Lastly, do not think of the armor value as a 1 to 1% conversion ratio. The armor system is not running on a 100% to 100 armor perfect conversion ratio. I'm unsure of the conversion ratio, but I would bet you it is not 1 to 1. In games, it barely ever is because of mechanics and other complications. So, just because you didn't see 21% reduction, doesn't mean what I think is happening from the above code is wrong.

Post Reply