Hat not adding bonuses

So i feel like I’m going crazy (but most likely just missing a syntax i dont see)

I created a hat of health (similar to Sam) but when i equip it nothing happens. My health stays at 100. I have the " Should use modifiers" box checked on my player’s base stats in the the scene and in the prefab and also removed the old equipment.cs from my player and added the stats equipment. My scripts look basically identical at this point so not sure where im missing something.

Stats Equipment

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameDevTV.Inventories;
using RPG.Stats;

namespace RPG.Inventories
{
    public class StatsEquipment : Equipment, IModifierProvider
    {
        IEnumerable<float> IModifierProvider.GetAdditiveModifier(Stat stat)
        {
            foreach (var slot in GetAllPopulatedSlots())
            {
                var item = GetItemInSlot(slot) as IModifierProvider;
                if (item == null) continue;

                foreach (float modifier in item.GetAdditiveModifier(stat))
                {
                    yield return modifier;
                }
            }
        }

        IEnumerable<float> IModifierProvider.GetPercentageModifiers(Stat stat)
        {
            foreach (var slot in GetAllPopulatedSlots())
            {
                var item = GetItemInSlot(slot) as IModifierProvider;
                if (item == null) continue;

                foreach (float modifier in item.GetPercentageModifiers(stat))
                {
                    yield return modifier;
                }
            }
        }
    }

}

StatsEquipableItem

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GameDevTV.Inventories;
using RPG.Stats;

namespace RPG.Inventories
{
    [CreateAssetMenu(menuName = ("RPG/Inventory/Equipable Item"))]
    public class StatsEquipableItem : EquipableItem
    {
        [SerializeField] Modifier[] additiveModifiers;
        [SerializeField] Modifier[] percentageModifiers;

        [System.Serializable]
        struct Modifier
        {
            public Stat stat;
            public float value;
        }

       public IEnumerable<float> GetAdditiveModifier(Stat stat)
        {
            foreach (var modifier in additiveModifiers)
            {
                if (modifier.stat == stat)
                {
                    yield return modifier.value;
                }
            }
        }

       public IEnumerable<float> GetPercentageModifiers(Stat stat)
        {
            foreach (var modifier in percentageModifiers)
            {
                if (modifier.stat == stat)
                {
                    yield return modifier.value;
                }
            }
        }
    }
}

Base Stats

using System;
using System.Collections;
using System.Collections.Generic;
using GameDevTV.Utils;
using UnityEngine;

namespace RPG.Stats
{
    public class BaseStats : MonoBehaviour
    {
        [Range(1, 99)]
        [SerializeField] int startingLevel = 1;
        [SerializeField] CharacterClass characterClass;
        [SerializeField] Progression progression = null;
        [SerializeField] GameObject levelUpFX = null;
        [SerializeField] bool shouldUseModifiers = false;

        public event Action onLevelUp;

        LazyValue<int> currentLevel;

        Experience experience;

        private void Awake()
        {
            experience = GetComponent<Experience>();
            currentLevel = new LazyValue<int>(CalculateLevel);
        }

        private void Start()
        {
            currentLevel.ForceInit();
        }

        private void OnEnable()
        {
            if (experience != null)
            {
                //adding method to list onExperienceGained
                experience.onExperienceGained += UpdateLevel;
            }
        }

        private void OnDisable()
        {
            if (experience != null)
            {
                //adding method to list onExperienceGained
                experience.onExperienceGained -= UpdateLevel;
            }
        }

        private void UpdateLevel()
        {
            //if (currentLevel >= progression.MaxLevel(characterClass)) return;
            int newLevel = CalculateLevel();
            if(newLevel > currentLevel.value)
            {
                currentLevel.value = newLevel;
                LevelUpEffect();
                onLevelUp();
            }
        }

        private void LevelUpEffect()
        {
            Instantiate(levelUpFX, transform);
        }

        private float GetPercentageModifier(Stat stat)
        {
            if (!shouldUseModifiers) return 0;
            float total = 0;
            foreach (IModifierProvider provider in GetComponents<IModifierProvider>())
            {
                foreach (float modifier in provider.GetPercentageModifiers(stat))
                {
                    total += modifier;
                }
            }
            return total;
        }

        private float GetBaseStat(Stat stat)
        {
            return progression.GetStat(stat, characterClass, GetLevel());
        }

        private float GetAdditiveModifier(Stat stat)
        {
            if (!shouldUseModifiers) return 0;
            float total = 0;
            foreach(IModifierProvider provider in GetComponents<IModifierProvider>())
            {
                foreach(float modifier in provider.GetAdditiveModifier(stat))
                {
                    total += modifier;
                }
            }
            return total;
        }

        public float GetStat(Stat stat)
        {
            return (GetBaseStat(stat) + GetAdditiveModifier(stat)) * (1 + GetPercentageModifier(stat) / 100);
        }

        public int GetLevel()
        {
            return currentLevel.value;
        }

        public int CalculateLevel()
        {
            Experience experience = GetComponent<Experience>();
            if (experience == null) return startingLevel;

            float currentXP =  experience.GetPoints();
            int penultimateLevel = progression.GetLevels(Stat.ExperienceToLevelUp, characterClass);

          for (int level = 1; level < penultimateLevel; level ++)
            {
               float XPToLevelUp = progression.GetStat(Stat.ExperienceToLevelUp, characterClass, level);
                if(XPToLevelUp > currentXP)
                {
                    return level;
                }
            }

            return penultimateLevel + 1;
        }

    }

}

So everything looks right.

One thing to check for, it’s possible that the Max Health is rising, but the Current health is not. The Health.Stat affects the Max Health, and doesn’t actually change the current health unless the character is healed/levels up, etc. If you’re using our rather simple HUD, you might notice that the health changes from 100/100 to 100/110 to reflect the Healthy Hat’s effect on the max health.

Even when i put the hat on my health still says 100/100. Not really sure where to look at this point. I’ve tried deleting and recreating the hat SO, I’ve gone through all my scripts that deal with stats and health but don’t really see anything missing.

Heres my Health script as well

using RPG.Core;
using System;
using GameDevTV.Utils;
using UnityEngine.Events;

namespace RPG.Attributes
{
    public class Health : MonoBehaviour, ISaveable
    {
        [SerializeField] float regenerationPercentage = 80;
        [SerializeField] UnityEvent<float> takeDamage;
        [SerializeField] UnityEvent onDie;

        LazyValue<float> healthPoints;

        private Animator animator;
        private ActionScheduler actionScheduler;

        private bool isDead = false;

        private void Awake()
        {
            actionScheduler = GetComponent<ActionScheduler>();
            animator = GetComponent<Animator>();
            healthPoints = new LazyValue<float>(GetInitialHealth);
        }

        private float GetInitialHealth()
        {
            return GetComponent<BaseStats>().GetStat(Stat.Health);
        }

        private void Start()
        {
            healthPoints.ForceInit();
        }

        private void OnEnable()
        {
            GetComponent<BaseStats>().onLevelUp += RegenerateHealth;
        }

        private void OnDisable()
        {
            GetComponent<BaseStats>().onLevelUp -= RegenerateHealth;
        }

        private void RegenerateHealth()
        {
            float regenHealthPoints = GetComponent<BaseStats>().GetStat(Stat.Health) * (regenerationPercentage / 100);
            healthPoints.value = Mathf.Max(healthPoints.value, regenHealthPoints);

            //or set to maxHealth , healthPoints = GetComponent<BaseStats>().GetStat(Stat.Health)
        }

        public void TakeDamage(GameObject instigator, float damage)
        {
            //bounded health between 0 and 100
            healthPoints.value = Mathf.Max(healthPoints.value - damage, 0);
        

            if (IsDead())
            {
                onDie.Invoke();
                Die();
                AwardExperience(instigator);
            }
            else
            {
                takeDamage.Invoke(damage);
            }
            UpdateState();
        }

        public float GetHealthPoints()
        {
            return healthPoints.value;
        }

        public float GetMaxHealthPoints()
        {
            return GetComponent<BaseStats>().GetStat(Stat.Health);
        }

        public void Heal (float healthToRestore)
        {
            healthPoints.value = Mathf.Min(healthPoints.value + healthToRestore, GetMaxHealthPoints());
            UpdateState();
        }

        private void AwardExperience(GameObject instigator)
        {
          Experience experience = instigator.GetComponent<Experience>();
            if (experience == null) return;

            experience.GainExperience(GetComponent<BaseStats>().GetStat(Stat.ExperienceReward));
        }

        private void Die()
        {
            if (isDead) return;

            isDead = true;
            animator.SetTrigger("Die");
            actionScheduler.CancelCurrentAction();
        }

        public bool IsDead()
        {
            return healthPoints.value <= 0;
            //return isDead;
        }

        public float GetPercentage()
        {
            return 100 * GetFraction();
        }

        public float GetFraction()
        {
            return healthPoints.value / GetComponent<BaseStats>().GetStat(Stat.Health);
        }

        public object CaptureState()
        {
            return healthPoints.value;
        }

        public void RestoreState(object state)
        {
            healthPoints.value = (float)state;

            if (healthPoints.value > 0)
            {
                isDead = false;
                GetComponent<Animator>().SetTrigger("load");
            }
            else Die();
        }

        private void UpdateState()
        {
            Animator animator = GetComponent<Animator>();
            if (!isDead && IsDead())
            {
                animator.SetTrigger("die");
                GetComponent<ActionScheduler>().CancelCurrentAction();
            }

            if (isDead && !IsDead())
            {
                animator.Rebind();
            }

            isDead = IsDead();
        }
    }
}

I’m not seeing anything missing either, so let’s make sure this isn’t just a UI issue:
One at a time, add these debugs and run the program:

In GetStat(), before the return:

if(shouldUseModifiers)
{
    Debug.Log($"GetStat({stat}) baseStat = {GetBaseStat(stat)} + Additive({GetAdditiveModifier(stat)}) * (1 + Percent(GetPercentageModifier(stat)})/100 = {(GetBaseStat(stat) + GetAdditiveModifier(stat)) * (1 + GetPercentageModifier(stat) / 100)}");
}

Once you’ve applied the Healthy Hat, see if the numbers change.
In BaseStats.GetAdditiveModifier() before the return statement

Debug.Log($"BaseStats GetAdditiveModifier({stat}) = {total}");

In BaseStats.GetPercentageModifier() before the return statement

Debug.Log($"BaseStats GetPercentageModifier({stat}) = {total}");

And in Health.GetMaxHealthPoints() before the return statement

Debug.Log($"GetMaxHealthPoints() = {GetComponent<BaseStats>().GetStat(Stat.Health)}";

So went through the debugs, its definitely seems more than just a UI issue


I fixed it! And of course my initial guess that it was some sort of syntax issue was the problem in my StatsEquipableItem i didnt pass in the IModifierProvider

i had

    [CreateAssetMenu(menuName = ("RPG/Inventory/Equipable Item"))]
    public class StatsEquipableItem : EquipableItem
    {

should be

    [CreateAssetMenu(menuName = ("RPG/Inventory/Equipable Item"))]
    public class StatsEquipableItem : EquipableItem, IModifierProvider
    {
2 Likes

Nice job solving your own problem!

Ack, I missed that in the earlier code you pasted. Good catch!

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

Privacy & Terms