With an existing item system, how can I save and load data if it was modified? (2 Questions)

Hi everyone.

I have 2 questions, so here’s the situation…
I have an item system, which means I also have a Data table that contains each of the items information.

Let me make this short and say we only have DT_Weapons.
Which has several rowname of:
Assault_Rifle
Shotgun
Sniper
Bow
Pistol


During basic gameplay, when going to buy an item, I call the DT_Weapons to spawn these items.
I buy any item I want, let’s say Sniper.
The sniper has 20 damage as an example.

Now, here’s the hard part…
Let’s say I saved the game.
But, it seems that I had to balance the Sniper’s damage a bit, because it’s overpowered.
so inside of the DT_Weapons, I change the damage for the Sniper to 15.

But also, maybe I want to rename the Sniper to Operator, so I do that too.

Now the problem is… when I load the game again, the old values are still there.
I still have a weapon named “Sniper” with “20 Damage”
Instead of “Operator” with “15 Damage”.

Now, there is something very important to keep in mind, a weapon can have a rarity, and that rarity might impact its damage…
so I can’t just SET the damage to 15, instead I need to say that the new damage is 20 - 5 = 15
If we assumed the sniper was legendary, and it s 30 damage, it needs to go down to 25 damage, because the base default stat was decreased by 5.

You see, I know I hadn’t made any modifications to the saved data, and that is the entirety of my question.
So… my first question is…
Is there some official or efficient way/method to automatically Patch up the saved data before loading it?
Or is this something I have to figure out on my own?
AKA actually make THE automated system that does this?
So it’d go for all weapons, check which one was changed, and update its values.



Another question that’s related to this…
Let’s assume I can access the DT_Weapons from another DT, say DT_Unit as an example.
In this DT_Unit I can specify for each unit type/class which weapons it has.

Item selection example:

As it stands right now, if I select a weapon, then go to DT_Weapons and change that weapon’s name to something else, it doesn’t get updated in DT_Unit.
Which of course would be tedious to fix for each unit type/class I have.

Example: Unit Scout has Sniper
Sniper is changed to Operator.
Unit Scout is still referencing “Sniper” but it no longer exists…
Therefore, an error would occur.

So, is there a way to avoid this?
While also being able to change the weapon name?


That’s all.

A system for updating saves would be extremely tedious.
You would have to permanently store the ways to update between versions and you wouldn’t be able to change the system without updating every single old update.
I highly recommend not doing this.

The easiest way to prevent your issue would be to get the data every time rather than saving it.
Don’t save a “Sniper, 40 damage, 200 critical-multiplier” etc.
Instead, save “Sniper” and use that as a key to check what the actual name (the displayed name) and stats are when you load. Just treat the display name as another stat, and keep the row name the same

This would also solve your second problem. The backend name would never change, so you would never have to rename anything.

1 Like

Adding to what rokenrock said above, for weapon rarity, you could have a second table which stores the multipliers for each tier.

Then for your pawn’s inventory variable, make it a struct with the weapon and modifier row names. Then when you load game or pick up a new weapon, break the struct and use the names to look up the info in both tables.

Doing it like that would avoid having multiple versions of the same gun in the weapon table, and would make it easier if you decide to add an upgrade system at some point.

Hi, thanks a lot for the reply!
I think I was suppose to be more specific about the rarity…

You see, the rarity can modify several things, Damage/HitRate/CritRate/Ammo.
and each of them gets randomly modified whenever the item first gains its rarity type. (so you could gain 5 damage and 3 hitrate, or the opposite, it’s random)
So even if I were to follow through with your suggestion, if instead I want to modify the stats gained from the legendary rarity, I’d still need to go over some process to decrease or increase the number of stats gained on existing (saved) weapons.

So… with this specific situation, I’d still have a problem.
(I don’t want to randomize the rarity effect each time I call the weapon)

However, your 2nd part of your suggestion makes sense, only changing the display name, but maintaining the rowname, sounds great, and we might actually do this.
Again, thanks a lot for the reply though <3

Ah- my bad. I somehow completely missed your rarity comment…

I would suggest something along these lines:

For the WeaponEntry struct, I’ve made something basic that just includes damage. It includes the base damage as well as an array of the minimum and maximum modification from your rarity.


With how it’s set up, rarity -1 is base stats so even the worst weapon will still have some variation, but you can change this by adding +1 to the rarity calculations.

With the way I’ve set up the following functions, this is additive.
IE if you had a setup like the following:


You could end up with 40 (base) → 34 (rarity 0, -20/+20) → 168 (rarity 1 -300/+400)
Of course you probably wouldn’t ever want to let the player get a -280, but that’s up to you- I just added really drastic numbers to showcase it.



This is all set up with just damage. You definitely want to make this a bit more generic. I’d recommend using a map with an enum key of the stats (Damage, Crit Chance, Bullet Multiplier, etc) with a value of a custom struct that holds a float array since you can’t directly put an array in a map.
That would end up looking something like this


You can then just loop through them when saving/loading
image



For the actual implementation, I’d recommend making a function to obscure it (This is just for damage implementation, but again you’ll probably want it more generic). I’ve gone ahead and made something simple separated into two parts.

The first part goes through to make sure that the array is populated enough. If you’re at rarity 4 but only have 2 lerps decided, you’ll want to have those filled out.
You may also want to make a custom pure function to replace RandomFloat. Maybe you’ll have some lucky modifier on the player or something and want to lean higher towards 1 during that specific upgrade.
image

The second part simply iterates through the rarities, checks what our lerp happens to be for each one, calculates the added damage using the min/max values for each rarity, and tallies them up combined with base damage.



The saving and loading is extremely simple:

EDIT [Still haven’t made this generic, but I’m going with the assumption that I have]:
I just realized you don’t necessarily want every stat to be upgraded every time. You may want to add a system to randomly weigh them. Maybe something like this:


And then right before updating the stats, do this (X is what the total will be- like 1 could end up with a 0.4 lerp for ammo and a 0.6 lerp for crit chance. With how I have it set up now, it’s also possible that it would choose the same thing to upgrade twice):

Replacing the RandomFloat node with this:

1 Like

Hi again @rokenrock Thanks a lot for the reply.
I think the most thing that I was looking for is the save and load method.
This one:
image

Now… either I’m missing something… or this does in fact have a flaw.
I’ll explain the main problem through a simple example:

Let’s say we are in V1 of the game.
Sniper stats is: 20/75/30/15 → Damage/HitChance/CritRate/Ammo
And let’s say that the legendary trait gives us 10 upgrade points which are distributed randomly onto these stats.
Essentially we could end up with +10 to damage
Or +5 to damage and hitchance
Or +3 to damage and hitchance and ammo and +1 to critrate.
So… purely random.

Now, if I use your method, that means every time I load up the weapon, I will need to fire upgrade points distributer, which could end up giving me a different set of upgrades every time I load the game.

And that of course doesn’t make sense… so this is one of the problems I have with the suggestion.


Another problem is what if I patch the game from V1 to V1.5
With this patch, I’ve changed the default damage of the sniper rifle only, from 20 → 15
Your second part of your suggestion regarding the DT helps with this, so all good on that front.

However, if I also said I want to change the number of points that a legendary rarity gives… from 10 → 7
Now we’ve got another problem.
This means I need to somehow save-up where did each point get assigned to, and then randomly remove 3 of them from each stat that was upgraded by the rarity.
(If instead I change it from 10 to 15, I’d need to add 5 more randomly to each stat)

Essentially I’m basically circling back to my main problem, where I’d need to create some sort of “Save Game Mender”, which first checks for any changes (Such as the game version), if it is different then it will run this “Mender”.
Though I don’t need to change the save-game file, but rather make sure that after the game is loaded, all weapons that each unit has in the whole game is checked. (Which would be done ONCE)

To put it in perspective, we may have around 100~200 or so units that can exist at a time.
the number may increase in the future, but right now we’re still building a test version of the game, so we don’t worry too much about that.

Each unit can have up to 10 weapons in its inventory, or only 1~3
But I’m not only trying to check for weapons, but also for Armor/Helmet/Accessories.
Anyhow, if I can figure out how to do it for one of those things, I can make it work with the others.

Edit: Oh and thanks again for your suggestion!

That wouldn’t happen. That’s the entire reason the Rarity Lerp exists and is saved in the first place.
The part below is responsible for deciding the random value, the part I’m assuming you’re talking about.


Notice that the purpose of the section is to fill out unfilled rarity lerps. Let’s take damage for example- say you have rarity 3 (which is 4 upgrades from base) with a rarity lerp arr of {0.5, 0, 1}. This part will see that you still need one more lerp, and will fix that- so {0.5, 0, 1, 0.7}.

It doesn’t run if you already have enough lerps- which are saved. Technically the upgrade points distributor runs every time you load the game, but it runs 0 times so long as you haven’t changed the rarity between the load call and this.


Distribute X can recreate that, but you may want to just create a different method that isn’t normalized.


I don’t think you would ever do this, though (Also the system as it is isn’t built for giving multiple points per stat, you’re just supposed to modify the arr containing min/max values).
The main reason you’d downgrade that from 10 → 7 would be due to some stats being overpowered- in which case you wouldn’t decrease the number of them, you would just decrease that stat in the max array for legendary.


I just really don’t like having backwards compatibility code. If this system really doesn’t work, I can whip up something.

1 Like

Okay I see, seems like this is what I in fact missed.
So… we DO modify the data… well I guess we aren’t modifying the saved data, but instead modifying how it is loaded, so I think that makes sense.

I was under the impression that we are not suppose to do that because we’d be doing this vvv (Unless I’m misunderstanding something… again)


Also

Sorry, the method your using is exactly what I wanted, I didn’t mean that I wanted an alternative, I was just trying to explain the situation.
In fact I’m using a very similar method to this, so all good regarding this.

I don’t particularly agree with this.
because although yes it could happen that a stat is overpowered…
but it also could be that the number of stats was a tad much, so I’d want to decrease them.

so instead of having 4 points that are distributed between 4 stats (With a 0.25 chance for each to gain 1 point)
perhaps I want to dial it down to 3 points for the 4 stats. (So only 3 will gain bonuses, with a 0.33 chance for each to gain a bonus)
Meaning 1 stat is guaranteed to NOT get upgraded, which is a big difference.

Though, I guess since you’re going through the trouble of saving the {0.5, 0, 1, 0.7} values… it makes me think, why not instead just save the number of points.
and for each point I’d give the stats a flat value, instead of a randomized one.
I could probably make it a MAP of (String : Int) where it can be like (Damage : 4)
Meaning damage has 4 points assigned to it.

So… I guess that can work… hmmm

Well, this is very new to me, so I’m also pretty spooked regarding that :sweat_smile:
Just fear of the unknown, maybe I do something now that I regret later.
But no matter how I look at it, it seems I need to make some code that can work with patches… (I’ve never worked with those :skull:)
My imagination just isn’t helping me much on how to build a robust way to do this.
(Maybe I have and I don’t realize it? idk)


Anyhow, thanks so much for the reply!

The reason we don’t just have a number associated with the stats is because then it makes it really hard to change stuff- which is the reason you’re using something that isn’t just a single float.

You still can’t change the number of stats given retroactively with that kind of system (just as the other method, you can only change the stats) without the backwards compatibility code. Especially if the number of points is random.


I’ll leave it to you to make the backwards compatibility system itself, but I’d recommend using a structure like so:

  • You have an Actor that acts as a Compatibility manager
    In the actor, store a list of versions. You only really need to store the ones that have changes. You should also store the last version of the save and the current game version.

  • Create an interface with an “Upgrade” function that takes two version identifiers (It should be a number in some way- or multiple numbers).

  • This function should call itself for every version change. Meaning if have version 1.25 and the current version is 1.33, you’ll call Upgrade(1.25, 1.33) from the manager to start it, and that will then call Upgrade(1.25, 1.26), Upgrade(1.26, 1.27), Upgrade(1.27, 1.28), Upgrade(1.28, 1.29) etc. You can check if this is the initial call by checking the distances. If the distance is 1, check the upgrade-to term and switch off it. You only need to implement the branches that actually need a change- maybe only 1.29 was actually changed.

  • In the manager, on load game, use the get all actors with interface node for those with the previously created interface. After that, call the upgrade function on them. Once all have been upgraded, save the game over the previously saved game- of course updating the version before doing so.

1 Like