[Shop Expansion] Shop Timer

OK so I know I’ve been asking a lot of questions that are out of course lately, and I truly appreciate Brian’s patience with me and my issues. I have an interesting question this time though (an expansion to the shop created in the shops and abilities course):

Based on my gameplay and testing, I noticed that there’s no way the shop can re-stock on important supplies in-game, so if they run out, they’re gone forever (which is good for hardcore Ironman or Survival games, but chill ;P). My question is, how do we implement a timer that checks every hour, or if our player dies (with a serialized, tunable time variable) if our stock is short or not, and based on that, it will either renew our supplies (once every hour, or if our player dies and drops everything) or not do anything (if stocks are maxed out to the variables we set them to)?

Here’s how I would go about it.
First, you need a DateTime that represents the last time the stock on this character was updated.

DateTime lastRefresh = DateTime.Now;

You’ll also need an int representing the time between refreshes

[SerializeField] int hoursBetweenRefresh = 1;

Then, I’d add this to the SetShopper method. This is the best time to check for restocking.

        public void SetShopper(Shopper shopper)
        {
            currentShopper = shopper;
            int totalHours = (int)Math.Floor((DateTime.Now - lastRefresh).TotalHours);
            if (totalHours > hoursBetweenRefresh)
            {
                int amountToRestock = totalHours / hoursBetweenRefresh;
                foreach (InventoryItem item in stockSold.Keys.ToList())
                {
                    stockSold[item] -= amountToRestock;
                    if (stockSold[item] <= 0) stockSold.Remove(item);
                }
                lastRefresh = DateTime.Now;
            }
        }

So here’s the logic:
Whenever we SetShopper, we check to see if the time has elapsed… I chose hours, but you could choose TotalMinutes if you prefer. If it has, then we go through each item in the Dictionary and reduce the amount sold by the number of hours / the time per refresh.
If the result is zero, then the entry is removed, effectively restocking that item completely.

To make this value save, we need to make a few small changes in the ISaveable as well:

        public object CaptureState()
        {
            Dictionary<string, int> saveObject = new Dictionary<string, int>();

            foreach (var pair in stockSold)
            {
                saveObject[pair.Key.GetItemID()] = pair.Value;
            }

            saveObject["LastRefresh"] = (int)Math.Floor((DateTime.Now - lastRefresh).TotalHours);
            return saveObject;
        }

        public void RestoreState(object state)
        {
            Dictionary<string, int> saveObject = (Dictionary<string, int>) state;
            stockSold.Clear();
            foreach (var pair in saveObject)
            {
                if (pair.Key == "LastRefresh")
                {
                    lastRefresh = DateTime.Now.AddHours(-pair.Value);
                } else
                {
                    stockSold[InventoryItem.GetFromID(pair.Key)] = pair.Value;
                }
            }
        }

OK this works beautifully, but I just noticed a new bug in my game… When I save my game and quit to the main menu and resume my game, none of my ShowHIdeUI component hot keys work. If I had icons to refer to them in-game (which is how I tested my Quest UI systems with), they would work normally, otherwise they won’t work. First time it took about 15 seconds to work, second time it didn’t work at all… Any idea why this might be the case? I apologize if I’m taking too much time today :sweat_smile:

It might be related to the bad advice I gave you in the other thread with the attempts to hide the Dialogue and ShopUI.

I’ll have a second look, just give me 5 mins please

well as it turns out… It’s not because of the other thread, something else is broken in the project, I’ll have to review what we did on this thread

This is a potential pitfall of working on multiple threads/concepts at the same time… it can be difficult to diagnose what went wrong, even with version control active.

True, which is why I don’t want to overflow my questions anymore. Best to give you a break between ideas, and not to do anything dumb myself :stuck_out_tongue_winking_eye:

Ok frankly speaking, I haven’t tested my menu <-> Interaction a lot recently, so this issue might be deeply rooted to something else, and I’m kinda lost right now :sweat_smile:. Can you suggest me a few steps to check where exactly the hotkey problem is coming from, or can I have a link to send you my project to investigate it a bit? I did a bit of further testing, and this issue seems to persist only from saving and quitting the game, followed by resuming it (Playmode doesn’t get cancelled by any means, the Saving and Quitting and Resuming all occur in the games’ menu systems)

(P.S: I don’t actually mind it that much, since I can replace it with physical UI buttons, but I haven’t worked on the UI that much recently, so for now having it as an add-on would be nice)

Given that it’s the Hot Keys… the most likely reason is that the Modal is still showing as active when it isn’t… perhaps the Pause Menu isn’t clearing it when it leaves (side effect of saving).

Possibly a Modal ShowHideUI needs to check to see if the GameObject is still Active in Update when locked == true, and if the GameObject isn’t active, then locked = false;

OK umm… isn’t that the same thing as this line we placed in ‘ShowHideUI void Update()’?:

if (locked && !modal) return;   // shuts down UI when the pause menu is clicked

That just prevents other showHideUIs from opening a window while the modal window (the pause menu) is open. The issue here is that we’re not clearing locked, which is static and therefore survives the save…
Adding this to ShowHideUI should fix the issue (since the pause menu should be destroyed on a scene reload)

        private void OnDestroy()
        {
            if (modal) locked = false;
        }

Yup, that fixed the problem. Thank you again Brian

Privacy & Terms