Forum begins after the advertisement:


[Part 27.5] Important bugfixes for Part 27.5

Home Forums Video Game Tutorial Series Creating a Rogue-like Shoot-em Up in Unity [Part 27.5] Important bugfixes for Part 27.5

Viewing 4 posts - 1 through 4 (of 4 total)
  • Author
    Posts
  • #18770
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    1
    ::

    This post will be a summary of bugs that have been found with Part 27 of our video. I will eventually be releasing a Part 27.5 video to address this, but for now, this post will serve as a place for me to collate all the bugs I’ve found:

    1. Error after updating PlayerInventory script

    @Grim Rubbish has pointed out that after updating the PlayerInventory script in Part 27, he gets this error:

    Argument “1”: Conversion from “int” to “ItemData.Evolution” not possible.

    This error is caused by an undocumented update to the CanEvolve() function in the Item script. The yellow highlights are the additions to the function:

    // Call this function to get all the evolutions that the weapon
    // can currently evolve to.
    public virtual ItemData.Evolution[] CanEvolve(int levelUpAmount = 1)
    {
        List<ItemData.Evolution> possibleEvolutions = new List<ItemData.Evolution>();
    
        // Check each listed evolution and whether it is in
        // the inventory.
        foreach (ItemData.Evolution e in evolutionData)
        {
            if (CanEvolve(e, levelUpAmount)) possibleEvolutions.Add(e);
        }
    
    
        return possibleEvolutions.ToArray();
    }

    The reason this update was made is to support CanEvolve(0) in the new PlayerInventory and TreasureChest script. Previously, the CanEvolve() could not take any arguments, and in our older codebase, CanEvolve() always added a level to the item before checking if it could evolve, which isn’t something that we want here. We wanted, in this part, to also check if the item can evolve at its current level — hence the update to the CanEvolve() function.

      1 anonymous person
    has upvoted this post.
    #18772
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    1
    ::

    2. Undocumented updates to GameManager

    There were also a couple of changes to Part 27’s project files that were not documented:

    • Instead of having the Done button do the SaveCoinsToStash() (as we did in Part 26), we instead have the GameManager call SaveCoinsToStash() for every player it whenever GameOver() is triggered. This makes the action much easier to find, which makes debugging easier; and it prepares our series for multiplayer implementation in future (since it saves coins for every player).
    • Since the game time and clock speed is now determined by the level data that we key into the level select scene, we’ve also added a default value for both game time and clock speed that the GameManager defaults to.
    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.UI;
    using TMPro;
    
    public class GameManager : MonoBehaviour
    {
        public static GameManager instance;
    
        // Define the different states of the game
        public enum GameState
        {
            Gameplay,
            Paused,
            GameOver,
            LevelUp,
            TreasureChest
        }
    
        // Store the current state of the game
        public GameState currentState;
    
        // Store the previous state of the game before it was paused
        public GameState previousState;
    
        [Header("Damage Text Settings")]
        public Canvas damageTextCanvas;
        public float textFontSize = 20;
        public TMP_FontAsset textFont;
        public Camera referenceCamera;
    
        [Header("Screens")]
        public GameObject pauseScreen;
        public GameObject resultsScreen;
        public GameObject levelUpScreen;
        int stackedLevelUps = 0; // If we try to StartLevelUp() multiple times.
    
        [Header("Results Screen Displays")]
        public Image chosenCharacterImage;
        public TMP_Text chosenCharacterName;
        public TMP_Text levelReachedDisplay;
        public TMP_Text timeSurvivedDisplay;
    
        const float DEFAULT_TIME_LIMIT = 1800f;
        const float DEFAULT_CLOCK_SPEED = 1f;
        float ClockSpeed => UILevelSelector.currentLevel?.clockSpeed ?? DEFAULT_CLOCK_SPEED;
        float TimeLimit => UILevelSelector.currentLevel?.timeLimit ?? DEFAULT_TIME_LIMIT;
    
        [Header("Stopwatch")]
        public float timeLimit; // The time limit in seconds
        float stopwatchTime; // The current time elapsed since the stopwatch started
        public TMP_Text stopwatchDisplay;
    
        bool levelEnded = false; // Has the time limit been reached?
        public GameObject reaperPrefab; // Spawns after time limit has been reached;
    
        PlayerStats[] players; // Tracks all players.
    
        // Getters for parity with older scripts.
        public bool isGameOver { get { return currentState == GameState.Paused; } }
        public bool choosingUpgrade { get { return currentState == GameState.LevelUp; } }
    
        // Gives us the time since the level has started.
        public float GetElapsedTime() { return stopwatchTime; }
    
        // Sums up the curse stat of all players and returns the value.
        public static float GetCumulativeCurse()
        {
            if (!instance) return 1;
    
            float totalCurse = 0;
            foreach (PlayerStats p in instance.players)
            {
                totalCurse += p.Actual.curse;
            }
            return Mathf.Max(1, totalCurse);
        }
    
        // Sum up the levels of all players and returns the value.
        public static int GetCumulativeLevels()
        {
            if (!instance) return 1;
    
            int totalLevel = 0;
            foreach (PlayerStats p in instance.players)
            {
                totalLevel += p.level;
            }
            return Mathf.Max(1, totalLevel);
        }
    
        void Awake()
        {
            players = FindObjectsOfType<PlayerStats>();
    
            //Set the level's Time Limit
            timeLimit = UILevelSelector.currentLevel.timeLimitTimeLimit;
    
            //Warning check to see if there is another singleton of this kind already in the game
            if (instance == null)
            {
                instance = this;
            }
            else
            {
                Debug.LogWarning("EXTRA " + this + " DELETED");
                Destroy(gameObject);
            }
    
            DisableScreens();
        }
    
        void Update()
        {
            switch (currentState)
            {
                case GameState.Gameplay:
                    // Code for the gameplay state
                    Time.timeScale = 1;
                    CheckForPauseAndResume();
                    UpdateStopwatch();
                    break;
                case GameState.Paused:
                    // Code for the paused state
                    CheckForPauseAndResume();
                    break;
                case GameState.GameOver:
                case GameState.TreasureChest:
                    Time.timeScale = 0;
                    break;
                case GameState.LevelUp:
                    break;
                default:
                    Debug.LogWarning("STATE DOES NOT EXIST");
                    break;
            }
        }
    
        IEnumerator GenerateFloatingTextCoroutine(string text, Transform target, float duration = 1f, float speed = 50f)
        {
            // Start generating the floating text.
            GameObject textObj = new GameObject("Damage Floating Text");
            RectTransform rect = textObj.AddComponent<RectTransform>();
            TextMeshProUGUI tmPro = textObj.AddComponent<TextMeshProUGUI>();
            tmPro.text = text;
            tmPro.horizontalAlignment = HorizontalAlignmentOptions.Center;
            tmPro.verticalAlignment = VerticalAlignmentOptions.Middle;
            tmPro.fontSize = textFontSize;
            if (textFont) tmPro.font = textFont;
            rect.position = referenceCamera.WorldToScreenPoint(target.position);
    
            // Makes sure this is destroyed after the duration finishes.
            Destroy(textObj, duration);
    
            // Parent the generated text object to the canvas.
            textObj.transform.SetParent(instance.damageTextCanvas.transform);
            textObj.transform.SetSiblingIndex(0);
    
            // Pan the text upwards and fade it away over time.
            WaitForEndOfFrame w = new WaitForEndOfFrame();
            float t = 0;
            float yOffset = 0;
            Vector3 lastKnownPosition = target.position;
            while (t < duration)
            {
                // If the RectTransform is missing for whatever reason, end this loop.
                if (!rect) break;
    
                // Fade the text to the right alpha value.
                tmPro.color = new Color(tmPro.color.r, tmPro.color.g, tmPro.color.b, 1 - t / duration);
    
                // Update the enemy's position if it is still around.
                if (target) lastKnownPosition = target.position;
    
                // Pan the text upwards.
                yOffset += speed * Time.deltaTime;
                rect.position = referenceCamera.WorldToScreenPoint(lastKnownPosition + new Vector3(0, yOffset));
    
                // Wait for a frame and update the time.
                yield return w;
                t += Time.deltaTime;
            }
        }
    
        public static void GenerateFloatingText(string text, Transform target, float duration = 1f, float speed = 1f)
        {
            // If the canvas is not set, end the function so we don't
            // generate any floating text.
            if (!instance.damageTextCanvas) return;
    
            // Find a relevant camera that we can use to convert the world
            // position to a screen position.
            if (!instance.referenceCamera) instance.referenceCamera = Camera.main;
    
            instance.StartCoroutine(instance.GenerateFloatingTextCoroutine(
                text, target, duration, speed
            ));
        }
    
        // Define the method to change the state of the game
        public void ChangeState(GameState newState)
        {
            previousState = currentState;
            currentState = newState;
        }
    
        public void PauseGame()
        {
            if (currentState != GameState.Paused)
            {
                ChangeState(GameState.Paused);
                Time.timeScale = 0f; // Stop the game
                pauseScreen.SetActive(true); // Enable the pause screen
            }
        }
    
        public void ResumeGame()
        {
            if (currentState == GameState.Paused)
            {
                ChangeState(previousState);
                Time.timeScale = 1f; // Resume the game
                pauseScreen.SetActive(false); //Disable the pause screen
            }
        }
    
        // Define the method to check for pause and resume input
        void CheckForPauseAndResume()
        {
            if (Input.GetKeyDown(KeyCode.Escape))
            {
                if (currentState == GameState.Paused)
                {
                    ResumeGame();
                }
                else
                {
                    PauseGame();
                }
            }
        }
    
        void DisableScreens()
        {
            pauseScreen.SetActive(false);
            resultsScreen.SetActive(false);
            levelUpScreen.SetActive(false);
        }
    
        public void GameOver()
        {
            timeSurvivedDisplay.text = stopwatchDisplay.text;
    
            // Set the Game Over variables here.
            ChangeState(GameState.GameOver);
            Time.timeScale = 0f; //Stop the game entirely
            DisplayResults();
    
            // Save all the coins of all the players to the save file.
            foreach (PlayerStats p in players)
            {
                p.GetComponentInChildren<PlayerCollector>().SaveCoinsToStash();
            }
    
            // Add all players' coins to their save file, since the game has ended.
            foreach(PlayerStats p in players)
            {
                if(p.TryGetComponent(out PlayerCollector c))
                {
                    c.SaveCoinsToStash();
                }
            }
        }
    
        void DisplayResults()
        {
            resultsScreen.SetActive(true);
        }
    
        public void AssignChosenCharacterUI(CharacterData chosenCharacterData)
        {
            chosenCharacterImage.sprite = chosenCharacterData.Icon;
            chosenCharacterName.text = chosenCharacterData.Name;
        }
    
        public void AssignLevelReachedUI(int levelReachedData)
        {
            levelReachedDisplay.text = levelReachedData.ToString();
        }
    
        public Vector2 GetRandomPlayerLocation()
        {
            int chosenPlayer = Random.Range(0, players.Length);
            return new Vector2(players[chosenPlayer].transform.position.x, players[chosenPlayer].transform.position.y);
        }
    
        void UpdateStopwatch()
        {
            stopwatchTime += Time.deltaTime * ClockSpeedUILevelSelector.currentLevel.clockSpeed;
            UpdateStopwatchDisplay();
    
            if (stopwatchTime >= timeLimit && !levelEnded)
            {
                levelEnded = true;
    
                // Set the enemy/event spawner GameObject inactive to stop enemies from spawning and kill the remaining enemies onscreen.
                FindObjectOfType<SpawnManager>().gameObject.SetActive(false);
                foreach (EnemyStats e in FindObjectsOfType<EnemyStats>())
                    e.SendMessage("Kill");
    
                // Spawn the Reaper off-camera
                Vector2 reaperOffset = Random.insideUnitCircle * 50f;
                Vector2 spawnPosition = GetRandomPlayerLocation() + reaperOffset;
                Instantiate(reaperPrefab, spawnPosition, Quaternion.identity);
    
    
            }
        }
    
        void UpdateStopwatchDisplay()
        {
            // Calculate the number of minutes and seconds that have elapsed
            int minutes = Mathf.FloorToInt(stopwatchTime / 60);
            int seconds = Mathf.FloorToInt(stopwatchTime % 60);
    
            // Update the stopwatch text to display the elapsed time
            stopwatchDisplay.text = string.Format("{0:00}:{1:00}", minutes, seconds);
        }
    
        public void StartLevelUp()
        {
            ChangeState(GameState.LevelUp);
    
            // If the level up screen is already active, record it.
            if (levelUpScreen.activeSelf) stackedLevelUps++;
            else
            {
                levelUpScreen.SetActive(true);
                Time.timeScale = 0f; //Pause the game for now
    
                foreach (PlayerStats p in players)
                    p.SendMessage("RemoveAndApplyUpgrades");
            }
        }
    
        public void EndLevelUp()
        {
            Time.timeScale = 1f;    //Resume the game
            levelUpScreen.SetActive(false);
            ChangeState(GameState.Gameplay);
    
            if (stackedLevelUps > 0)
            {
                stackedLevelUps--;
                StartLevelUp();
            }
        }
    }
      1 anonymous person
    has upvoted this post.
    #18773
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    1
    ::

    3. UI update glitch

    We also had a glitch with the UI update. Specifically, the items would be added to the inventory before the animation finished: https://blog.terresquall.com/wp-content/uploads/2025/08/part-27-rewards-ui-glitch.webm

    This was fixed by adding an optional updateUI parameter to some of our functions in PlayerInventory (highlighted below), so that we can add or level up items without updating the UI:

    // Finds an empty slot and adds a weapon of a certain type, returns
    // the slot number that the item was put in.
    public int Add(WeaponData data, bool updateUI = true)
    {
        int slotNum = -1;
    
        // Try to find an empty slot.
        for (int i = 0; i < weaponSlots.Capacity; i++)
        {
            if (weaponSlots[i].IsEmpty())
            {
                slotNum = i;
                break;
            }
        }
    
        // If there is no empty slot, exit.
        if (slotNum < 0) return slotNum;
    
        // Otherwise create the weapon in the slot.
        // Get the type of the weapon we want to spawn.
        Type weaponType = Type.GetType(data.behaviour);
    
        if (weaponType != null)
        {
            // Spawn the weapon GameObject.
            GameObject go = new GameObject(data.baseStats.name + " Controller");
            Weapon spawnedWeapon = (Weapon)go.AddComponent(weaponType);
            spawnedWeapon.transform.SetParent(transform); //Set the weapon to be a child of the player
            spawnedWeapon.transform.localPosition = Vector2.zero;
            spawnedWeapon.Initialise(data);
            spawnedWeapon.OnEquip();
    
            // Assign the weapon to the slot.
            weaponSlots[slotNum].Assign(spawnedWeapon);
            if(updateUI) weaponUI.Refresh();
    
            // Close the level up UI if it is on.
            if (GameManager.instance != null && GameManager.instance.choosingUpgrade)
                GameManager.instance.EndLevelUp();
    
            return slotNum;
        }
        else
        {
            Debug.LogWarning(string.Format(
                "Invalid weapon type specified for {0}.",
                data.name
            ));
        }
    
        return -1;
    }
    
    // Finds an empty slot and adds a passive of a certain type, returns
    // the slot number that the item was put in.
    public int Add(PassiveData data, bool updateUI = true)
    {
        int slotNum = -1;
    
        // Try to find an empty slot.
        for (int i = 0; i < passiveSlots.Capacity; i++)
        {
            if (passiveSlots[i].IsEmpty())
            {
                slotNum = i;
                break;
            }
        }
    
        // If there is no empty slot, exit.
        if (slotNum < 0) return slotNum;
    
        // Otherwise create the passive in the slot.
        // Get the type of the passive we want to spawn.
        GameObject go = new GameObject(data.baseStats.name + " Passive");
        Passive p = go.AddComponent<Passive>();
        p.Initialise(data);
        p.transform.SetParent(transform); //Set the weapon to be a child of the player
        p.transform.localPosition = Vector2.zero;
    
        // Assign the passive to the slot.
        passiveSlots[slotNum].Assign(p);
        if(updateUI) passiveUI.Refresh();
    
        if (GameManager.instance != null && GameManager.instance.choosingUpgrade)
        {
            GameManager.instance.EndLevelUp();
        }
        player.RecalculateStats();
    
        return slotNum;
    }
    
    // If we don't know what item is being added, this function will determine that.
    public int Add(ItemData data, bool updateUI = true)
    {
        if (data is WeaponData) return Add(data as WeaponData, updateUI);
        else if (data is PassiveData) return Add(data as PassiveData, updateUI);
        return -1;
    }
    
    // Overload so that we can use both ItemData or Item to level up an
    // item in the inventory.
    public bool LevelUp(ItemData data, bool updateUI = true)
    {
        Item item = Get(data);
        if (item) return LevelUp(item, updateUI);
        return false;
    }
    
    // Levels up a selected weapon in the player inventory.
    public bool LevelUp(Item item, bool updateUI = true)
    {
        // Tries to level up the item.
        if (!item.DoLevelUp())
        {
            Debug.LogWarning(string.Format(
                "Failed to level up {0}.",
                 item.name
            ));
            return false;
        }
    
        // Update the UI after the weapon has levelled up.
        if(updateUI)
        {
            weaponUI.Refresh();
            passiveUI.Refresh();
        }
    
        // Close the level up screen afterwards.
        if (GameManager.instance != null && GameManager.instance.choosingUpgrade)
        {
            GameManager.instance.EndLevelUp();
        }
    
        // If it is a passive, recalculate player stats.
        if (item is Passive) player.RecalculateStats();
        return true;
    }

    On top of that though, we’ll also need to add the parameter to the following functions in the Item script as well:

    // AttemptEvolution will spawn a new weapon for the character, and remove all
    // the weapons that are supposed to be consumed.
    public virtual bool AttemptEvolution(ItemData.Evolution evolutionData, int levelUpAmount = 1, bool updateUI = true)
    {
        if (!CanEvolve(evolutionData, levelUpAmount))
            return false;
    
        // Should we consume passives / weapons?
        bool consumePassives = (evolutionData.consumes & ItemData.Evolution.Consumption.passives) > 0;
        bool consumeWeapons = (evolutionData.consumes & ItemData.Evolution.Consumption.weapons) > 0;
    
        // Loop through all the catalysts and check if we should consume them.
        foreach (ItemData.Evolution.Config c in evolutionData.catalysts)
        {
            if (c.itemType is PassiveData && consumePassives) inventory.Remove(c.itemType, true);
            if (c.itemType is WeaponData && consumeWeapons) inventory.Remove(c.itemType, true);
        }
    
        // Should we consume ourselves as well?
        if (this is Passive && consumePassives) inventory.Remove((this as Passive).data, true);
        else if (this is Weapon && consumeWeapons) inventory.Remove((this as Weapon).data, true);
    
        // Add the new weapon onto our inventory.
        inventory.Add(evolutionData.outcome.itemType, updateUI);
    
        return true;
    }
    
    // Whenever an item levels up, attempt to make it evolve.
    public virtual bool DoLevelUp(bool updateUI = true)
    {
        if (evolutionData == null) return true;
    
        // Tries to evolve into every listed evolution of this weapon,
        // if the weapon's evolution condition is levelling up.
        foreach (ItemData.Evolution e in evolutionData)
        {
            if (e.condition == ItemData.Evolution.Condition.auto)
                AttemptEvolution(e, 1, updateUI);
        }
        return true;
    }

    This, in turn, affects the overriding of the DoLevelUp() function in Weapon, Passive, AuraWeapon, and any other Item subclass in your project:

    // Levels up the weapon by 1, and calculates the corresponding stats.
    public override bool DoLevelUp(bool updateUI = true)
    {
        base.DoLevelUp(updateUI);
    
        ...
    }

    Once all that is done, you will also need to add a new NotifyComplete() function in TreasureChest that can be called to refresh the inventory UI when the animation is done, as well as make the following changes to add the updateUI parameter:

    // Function for UITreasureChest to call when animation is complete.
    public void NotifyComplete() {
        recipient.weaponUI.Refresh();
        recipient.passiveUI.Refresh();
    }
    
    // Continue down the list until one returns.
    void Open(PlayerInventory inventory)
    {
        if (inventory == null) return;
    
        if (possibleDrops.HasFlag(DropType.Evolution) && TryEvolve<Weapon>(inventory, false)) return;
        if (possibleDrops.HasFlag(DropType.UpgradeWeapon) && TryUpgrade<Weapon>(inventory, false)) return;
        if (possibleDrops.HasFlag(DropType.UpgradePassive) && TryUpgrade<Passive>(inventory, false)) return;
        if (possibleDrops.HasFlag(DropType.NewWeapon) && TryGive<WeaponData>(inventory, false)) return;
        if (possibleDrops.HasFlag(DropType.NewPassive)) TryGive<PassiveData>(inventory, false);
    }
    
    // Try to evolve a random item in the inventory.
    T TryEvolve<T>(PlayerInventory inventory, bool updateUI = true) where T : Item
    {
        // Loop through every evolvable item.
        T[] evolvables = inventory.GetEvolvables<T>();
        foreach (Item i in evolvables)
        {
            // Get all the evolutions that are possible.
            ItemData.Evolution[] possibleEvolutions = i.CanEvolve(0);
            foreach (ItemData.Evolution e in possibleEvolutions)
            {
                // Attempt the evolution and notify the UI if successful.
                if (i.AttemptEvolution(e, 0, updateUI))
                {
                    UITreasureChest.NotifyItemReceived(e.outcome.itemType.icon);
                    return i as T;
                }
            }
        }
        return null;
    }
    
    // Try to upgrade a random item in the inventory.
    T TryUpgrade<T>(PlayerInventory inventory, bool updateUI = true) where T : Item
    {
        // Gets all weapons in the inventory that can still level up.
        T[] upgradables = inventory.GetUpgradables<T>();
        if (upgradables.Length == 0) return null; // Terminate if no weapons.
    
        // Do the level up, and tell the treasure chest which item is levelled.
        T t = upgradables[Random.Range(0, upgradables.Length)];
        inventory.LevelUp(t, updateUI);
        UITreasureChest.NotifyItemReceived(t.data.icon);
        return t;
    }
    
    // Try to give a new item to the inventory.
    T TryGive<T>(PlayerInventory inventory, bool updateUI = true) where T : ItemData
    {
        // Get all new item possibilities.
        T[] possibilities = inventory.GetUnowned<T>();
        if (possibilities.Length == 0) return null;
    
    
        // Add a random possibility.
        T t = possibilities[Random.Range(0, possibilities.Length)];
        inventory.Add(t, updateUI);
        UITreasureChest.NotifyItemReceived(t.icon);
        return t;
    } 

    All the changes above make it so that the UI does not update when the TreasureChest awards the player its rewards immediately upon touching it. However, because the UI does not update, we now need to manually trigger the update when UITreasureChest is done:

    public void CloseUI()
    {
        //Display Coins earned
        collector.AddCoins(coins);
    
        // Reset UI & VFX to initial state
        chestCover.SetActive(true);
        chestButton.SetActive(true);
        icons.Clear();
        beamVFX.SetActive(false);
        coinText.gameObject.SetActive(false);
        gameObject.SetActive(false);
        doneButton.SetActive(false);
        fireworks.SetActive(false);
        curvedBeams.SetActive(false);
        ResetDisplay();
    
        //reset audio
        audiosource.clip = pickUpSound;
        audiosource.time = 0f;
        audiosource.Play();
    
        isAnimating = false;
    
        GameManager.instance.ChangeState(GameManager.GameState.Gameplay);
        currentChest.NotifyComplete();
    }
      1 anonymous person
    has upvoted this post.
    #18774
    Terence
    Level 31
    Keymaster
    Helpful?
    Up
    1
    ::

    4. Prevent issuing upgrades for max level weapons

    To prevent the PlayerInventory from issuing weapons that we already have maxed out, we need to remove a single character from the ApplyUpgradeOptions() function:

    // Determines what upgrade options should appear.
    void ApplyUpgradeOptions()
    {
        // <availableUpgrades> is an empty list that will be filtered from
        // <allUpgrades>, which is the list of ALL upgrades in PlayerInventory.
        // Not all upgrades can be applied, as some may have already been
        // maxed out the player, or the player may not have enough inventory slots.
        List<ItemData> availableUpgrades = new List<ItemData>();
        List<ItemData> allUpgrades = new List<ItemData>(availableWeapons);
        allUpgrades.AddRange(availablePassives);
    
        // We need to know how many weapon / passive slots are left.
        int weaponSlotsLeft = GetSlotsLeft(weaponSlots);
        int passiveSlotsLeft = GetSlotsLeft(passiveSlots);
    
        // Filters through the available weapons and passives and add those
        // that can possibly be an option.
        foreach (ItemData data in allUpgrades)
        {
            // If a weapon of this type exists, allow for the upgrade if the
            // level of the weapon is not already maxed out.
            Item obj = Get(data);
            if (obj)
            {
                if (obj.currentLevel <= data.maxLevel) availableUpgrades.Add(data);
            }
            else
            {
                // If we don't have this item in the inventory yet, check if
                // we still have enough slots to take new items.
                if (data is WeaponData && weaponSlotsLeft > 0) availableUpgrades.Add(data);
                else if (data is PassiveData && passiveSlotsLeft > 0) availableUpgrades.Add(data);
            }
        }
    
        // Show the UI upgrade window if we still have available upgrades left.
        int availUpgradeCount = availableUpgrades.Count;
        if (availUpgradeCount > 0)
        {
            bool getExtraItem = 1f - 1f / player.Stats.luck > UnityEngine.Random.value;
            if (getExtraItem || availUpgradeCount < 4) upgradeWindow.SetUpgrades(this, availableUpgrades, 4);
            else upgradeWindow.SetUpgrades(this, availableUpgrades, 3, "Increase your Luck stat for a chance to get 4 items!");
        }
        else if (GameManager.instance != null && GameManager.instance.choosingUpgrade)
        {
            GameManager.instance.EndLevelUp();
        }
    }

    Specifically, this is the line:

    if (obj.currentLevel <= data.maxLevel) availableUpgrades.Add(data);

    Previously, when considering whether a weapon is a legitimate option, we would check to see if it was less than or equal to the max level, but if an item is equal to the max level, it should never be upgradable.

      1 anonymous person
    has upvoted this post.
Viewing 4 posts - 1 through 4 (of 4 total)
  • You must be logged in to reply to this topic.

Go to Login Page →


Advertisement below: