Ineffective Progression

OK I really hate opening topics up and being everywhere, but this one is quite a bug, because sometimes it works, and sometimes it doesn’t, and the only way for me to figure it out is by deactivating the damage randomizer from time to time (I made up my mind I’m not re-activating it until release date, since clearly it can be quite the mess behind the scenes, and I want to catch it before it causes chaos…)

In my progression System, I have introduced stats for my player to progress through, and this includes Attack, Ranged, Defence and Mage, as part of me replacing the current system with a skilling system. The problem is, it’s… not working as expected. The way it currently works is that it first levels up normally for one level, and then it never levels up again. The weapons are effective, as per the formula in the code below, but the stats of the player himself just never improve, as his hits are not affected by his stats, just his weapon, hence making his combat stats, quite literally, ineffective. Here is the code that I suspect is responsible for this:


    public int GetDamage() {
        if (currentWeaponConfig.GetSkill() == Skill.Attack) {
                return (int) (GetComponent<BaseStats>().GetStat(Stat.Attack) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus() / 100)));
                /* int attackDamage = (int)(GetComponent<BaseStats>().GetStat(Stat.Attack) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus() / 100)));
                playerDamageComponent.DealDamage(Skill.Attack, attackDamage);
                return attackDamage; */
            }
        else if (currentWeaponConfig.GetSkill() == Skill.Ranged) {
                return (int) (GetComponent<BaseStats>().GetStat(Stat.Ranged) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus()/100)));
                /* int rangedDamage = (int)(GetComponent<BaseStats>().GetStat(Stat.Ranged) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus() / 100)));
                playerDamageComponent.DealDamage(Skill.Ranged, rangedDamage);
                return rangedDamage; */
            }
        else {
                return (int) (GetComponent<BaseStats>().GetStat(Stat.Magic) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus()/100)));
                /* int magicDamage = (int)(GetComponent<BaseStats>().GetStat(Stat.Magic) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus() / 100)));
                playerDamageComponent.DealDamage(Skill.Magic, magicDamage);
                return magicDamage; */
            }

        }

        void Hit() {

        // This function is only used in the AttackBehaviour() function, and 
        //it ensures damage is slightly delayed until the player punches the enemy

        // It also checks if our current weapon has a Projectile or not (in case our player is a ranger)
        // If he does, we launch our projectile (E.g: arrows)
        // If not, just do regular melee damage (as usual)

        if(target == null) return; // ensures no errors occur if the attack animation is playing, but we can't find a target 
                                   // (like when we walk away from an incomplete attack animation: this produces an error, and this is the solution)

        // float damage = GetComponent<BaseStats>().GetStat(Stat.Damage);
        float damage = GetDamage();

        // Defending our Player from damage done by target, based on the shield or item he's wearing:
        BaseStats targetBaseStats = target.GetComponent<BaseStats>();
        
        if (targetBaseStats != null) {

                float defence = targetBaseStats.GetStat(Stat.Defence);
                
                // This formula reduces the effectiveness of weapons as the player gets stronger:
                damage /= (1 + (defence/damage));  // Damage Formula (Power = Power * (1 + defence/enemy damage))
                // damage *= UnityEngine.Random.Range(0, 1.25f);  // Randomizing the Damage
                // if (UnityEngine.Random.Range(0,100) > 95) damage *= 2.0f;   // Critical hit

        }

// Rest of the hit function...

What changes can I possibly do, to ensure the damage he deals considers his Skill level as well?

Edit: After a little more investigation, I realized the combat and defence skills are effective, but BASED ON THE COMBAT LEVEL, NOT THE INDIVIDUAL SKILLS THEMSELVES, so when the player levels his combat up, the damage from his progression system and defence become more effective too, and they all iterate to the next progression bar value (not to my visible eye, but that’s how the game system works based on try and error right now, and it’s definitely not how I want it to work), rather than doing it when the skills individually iterate, which also means that some of the untrained skills also miraculously get better when the combat level increases, although I didn’t even start training them… Where did I go so wrong?! If it helps in anyway, this is the thread that links to the Skills Based system we were developing

The BaseStats has no way of knowing exactly what the Skill is that is being tested…

Let’s add this method to BaseStats()

        public float GetStatBySpecifiedLevel(Stat stat, int level)
        {
            return (progression.GetStat(stat, characterClass, level) + GetAdditiveModifier(stat)) *
                   (1 + GetPercentageModifier(stat) / 100);
        }

Now your GetDamage() calculation will read

public int GetDamage() {
        if (currentWeaponConfig.GetSkill() == Skill.Attack) {
                return (int) (GetComponent<BaseStats>().GetStatBySpecifiedLevel(GetComponent<StatStore>().GetSkillLevel(Skill.Attack)) + (currentWeaponConfig.GetDamage()) * (1 + currentWeaponConfig.GetPercentageBonus() / 100));
            }
        else... //rest of conditions

Or… to greatly simplify this

public int GetDamage()
{
    return (int)(GetComponent<BaseStats>().GetStatBySpecifiedLevel(GetComponent<SkillStore>().GetSkillLevel(currentWeaponConfig.GetSkill()) + currentWeaponConfig.GetDamage()) * (1 + currentWeaponConfig.GetPercentageBonus()/100);
}

Hi Brian, I think the input we have when calling ‘GetStatBySpecifiedLevel()’ in ‘Fighter.cs’, from ‘BaseStats.cs’ is only for the level, but there is no corresponder to stat, and the compiler is complaining about that. I’ll have a look into how we can fix this, but if this comment isn’t edited, then I was unable to fix it :sweat_smile:

Actually, there was a Momentary Lapse of Reason (fantastic Pink Floyd album!) on my part…
You would need the if statements to convert the Skill.Attack to Stat.Attack, etc…

if(currentWeaponConfig.GetSkill() == Skill.Attack)
{
    return (int)(GetComponent<BaseStats>().GetStatBySpecifiedLevel(Stat.Attack, GetComponent<SkillStore>().GetSkillLevel(Skill.Attack)) + currentWeaponConfig.GetDamage()) * (1+currentWeaponConfig.GetPercentageBonus()/100));
}
//remainder of if statements

alls good xD, as usual thanks again for your amazing help Brian. So far it seems to be working, I’m still thoroughly testing it to make sure no more faults occur xD (I’ll edit this comment in a bit)

Ahh… there is one major flaw with this system, for the AI NPCs, and that they literally deal null (not zero, NULL, not even the weapon damage is accounted for… they play the animation, but don’t instantiate the projectiles (if any) or deal any sort of damage, not even a zero) damage with this new method, and I’m guessing it’s because they don’t have a ‘SkillStore.cs’ script attached to them. I don’t know what the consequences of attaching one may have, as I don’t want them suddenly growing in XP as well…

It is, indeed…
In fact, I would imagine this would throw a number Null Reference errors…

You might need a check… if the character has a SkillStore, use the SkillStore method, if it doesn’t use the original method you posted…

So… will it look something like this, because so far the ranged seems to rely on the attack XP, and that sounds kind of fishy tbh…

    public int GetDamage() {

        if (this.GetComponent<SkillStore>()) {

                if (currentWeaponConfig.GetSkill() == Skill.Attack)
                {
                    return (int)(GetComponent<BaseStats>().GetStatBySpecifiedLevel(Stat.Attack, GetComponent<SkillStore>().GetSkillLevel(Skill.Attack)) + currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus() / 100));
                }

                else if (currentWeaponConfig.GetSkill() == Skill.Ranged)
                {
                    return (int)(GetComponent<BaseStats>().GetStatBySpecifiedLevel(Stat.Ranged, GetComponent<SkillStore>().GetSkillLevel(Skill.Attack)) + currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus() / 100));
                }

                else return (int)(GetComponent<BaseStats>().GetStatBySpecifiedLevel(Stat.Magic, GetComponent<SkillStore>().GetSkillLevel(Skill.Magic)) + currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus() / 100));


            }

            else {

            if (currentWeaponConfig.GetSkill() == Skill.Attack) {
                    return (int) (GetComponent<BaseStats>().GetStat(Stat.Attack) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus() / 100)));
                }
            else if (currentWeaponConfig.GetSkill() == Skill.Ranged) {
                    return (int) (GetComponent<BaseStats>().GetStat(Stat.Ranged) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus()/100)));
                }
            else {
                    return (int) (GetComponent<BaseStats>().GetStat(Stat.Magic) + (currentWeaponConfig.GetDamage() * (1 + currentWeaponConfig.GetPercentageBonus()/100)));
                }

            }

        }

Something like that. Your Ranged is relying on Attack because you told it to

Also note that your Percentage bonus is only applying at all to the currentWeaponConfig.GetDamage() and not at all to the result of the GetStat or GetStatBySpecifiedLevel.

I mean… it doesn’t make sense to apply bonuses when you don’t have a weapon on, right?

Sorry, my mistake :sweat_smile:

Specifically what I mean is the calculation…
Suppose your value from the stat is 7, the weapon damage is 7 and the percent bonus is 10
The course calculation would be (7 + 7) * 1.10f = 14 *1.10f = 15.4f
Your calculation, based on the placement of the parenthesis works out to
7 + 7 * 1.10f = 7 + 7.7f = 14.7f

Figured I’d give the weapon the bonus, without having the player involved… I’ll fiddle with both values though, let’s see which one ends up making it till the end :stuck_out_tongue:

If you’re going to have a Weapon Damage and a Weapon Bonus, but that bonus only applies to the Weapon damage, then instead of setting the weapon damage to 7, save the calculation space and just set it to 7.7 because if it’s not transitive to the base stat, it’s a waste of calculation.

I’ll probably re-work that formula then

Well… Defence is suffering the same issue now. I tried using the same formula, but this one was ineffective :sweat_smile:

So the logic I’m trying to implement goes as follows (in Fighter.Hit()):

  1. If you have a target, and that target has a SkillStore (i.e: the player), then get his defence level, and add to it the bonuses of the armor (I think this was handled in another script, so I skipped it in the code below)
  2. If that target doesn’t have a SkillStore (i.e: Enemy) then just get the Stat.Defence off that enemy
if (targetBaseStats != null) {

                // float defence = targetBaseStats.GetStat(Stat.Defence);
                float defence;
                
                if (targetBaseStats.GetComponent<SkillStore>()) {
                    defence = targetBaseStats.GetStatBySpecifiedLevel(Stat.Defence, GetComponent<SkillStore>().GetSkillLevel(Skill.Defence));
                }
                else defence = targetBaseStats.GetStat(Stat.Defence);

                // This formula reduces the effectiveness of weapons as the player gets stronger:
                damage /= (1 + (defence/damage));  // Damage Formula (Power = Power * (1 + defence/enemy damage))
                // damage *= UnityEngine.Random.Range(0, 1.25f);  // Randomizing the Damage
                // if (UnityEngine.Random.Range(0,100) > 95) damage *= 2.0f;   // Critical hit

        }

But… it’s not working as expected (it’s back to null errors, because of ‘targetBaseStats.GetComponent SkillStore ())’ in the nested ‘if’ statement (I didn’t know the website eliminates anything in the <> brackets))

Try it this way:

if(targetBaseStats.TryGetComponent(out SkillStore skillStore))
{
    defense = targetBaseStats.GetStatBySpecifiedLevel(Stat.Defense, skillStore.GetSkillLevel(Skill.Defense));
} else
{
     defense = targetBaseStats.GetStat(Stat.Defense);
}

Yup, that worked. Thanks again Brian :slight_smile: (I’ll thoroughly test it, should be fine though). I’ll go test the debuggers now

Swapped this out to a talk, in case I notice anything going wrong down the line (for now I’m on a short break off the project, until Brian releases his third person → RPG implementation :slight_smile:)

I gave the Shader course a look yesterday, and it seems interesting. I might try it down the line (once I’m done with the networking course, otherwise it’ll all be out-of-whack), if it works with the Unity 2021 LTS

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms