3
\$\begingroup\$

I'm developing a RPG-game. After searching about various ways to save item information in RPG-game, I determined to save information in JSON files. The problem is that in my game, some equipment's stat depends on the character condition.

class Equipment {
    String name;
    double strength;
    double agility;
    
    Equipment(String name) {
        this.name = name;
    }

    checkCondition(Character character) {
        switch (this.name) {
            case "itemA" -> {
                strength = 20;
                      //This is what I mean by "condition dependent item"
                agility = character.strength > 30 ? 20 : 10;
            }

        
            case "itemB" -> strength = 40;
        }
    }
}

As you can see, "itemA"'s stat is dependent to characters' strength.

{
    "name": "itemB",
    "strength": 40,
    "agility": 0
}

This is how I planned to save the items, but "itemA" doesn't seem to fit in this form. Please let me know you have an idea to solve this problem.

\$\endgroup\$
10
  • \$\begingroup\$ Are you rolling your item stats or do you have an fixed amount of items you can identify by id? \$\endgroup\$
    – Zibelas
    Commented Dec 18, 2023 at 12:24
  • \$\begingroup\$ @Zibelas I'm not sure what you mean by 'rolling item stats', but what I'm trying to do is to store information of fixed amount of items(itemA, itemB, ... , itemN) \$\endgroup\$
    – evol1102
    Commented Dec 18, 2023 at 13:23
  • 2
    \$\begingroup\$ I've tagged your question effect-modifier which is our tag for systems like this where an item applies modifications to another item, like a character, possibly subject to special conditions. A lot of similar considerations come up, whether those modifiers are buffs, potions, equipment, cards, etc. So you may want to take a read through other Q&A with that tag to get some inspirations. What's a way to implement a flexible buff/debuff system? is the most extensive so far, so it's a particularly good one to look at. \$\endgroup\$
    – DMGregory
    Commented Dec 18, 2023 at 14:18
  • \$\begingroup\$ Your first mistake was to store the stat as a stateful field, rather than calculating it on the fly whenever it is requested. This mistake was already present before you even considered persisting the information to an external source. \$\endgroup\$
    – Flater
    Commented Dec 19, 2023 at 0:35
  • 1
    \$\begingroup\$ @evol1102: If I write down the current date on a piece of paper, that's problematic. Because now I have to worry if the piece of paper is still up to date, and I have to make sure to update the piece of paper at the right time because otherwise I'm going to end up doing the wrong thing because an older date is still written down. Instead of writing it down, it would be better if I looked it up from a reliable source instead (which for your example means doing that calculation whenever you want to access the value, instead of doing it pre-emptively and storing it in a field). \$\endgroup\$
    – Flater
    Commented Dec 19, 2023 at 8:12

1 Answer 1

6
\$\begingroup\$

I wouldn't do this based on item name. Instead, I'd implement a system of modifiers to describe what an item does, something like...

{
    name: "itemA",
    displayName: "Slippers of Mighty Grace",
    effects: [
        {
            type: "statCheck",
            stat: "strength",
            threshold: 31,
            effectsIfMet: [
                {
                    type: "statBoost",
                    stat: "agility",
                    value: 20
                }
            ],
            effectsIfNotMet: [
                {
                    type: "statBoost",
                    stat: "agility",
                    value: 10
                }
            ]
        }
    ]
}

When you load this item from JSON, you string together a matching pattern of polymorphic effect nodes, each with their own Apply method, something like this:

public class StatCheckEffect implements ItemEffect {
    public Stat stat;
    public int threshold;
    
    public ItemEffect[] effectsIfMet;
    public ItemEffect[] effectsIfNotMet;

    public void Apply(Character char) {
        if (char.GetStat(stat) >= threshold) {
            for(ItemEffect effect : effectsIfMet)
                effect.Apply(char);
        } else {
            for(ItemEffect effect : effectsIfNotMet)
                effect.Apply(char);
        }
    }
}

public class StatBoostEffect implements ItemEffect {
    public Stat stat;
    public int value;

    public void Apply(Character char) {
        char.ModifyStat(stat, value);
    }
}

When the item is equipped, you run the Apply() methods on all its top-level effects. When the character's status changes in a way that might affect equipment properties, you reset the character's stats to base and re-run the Apply() methods of all equipped gear to find the new net result. (More sophisticated caching to avoid redundant processing is possible, but do it the simple way first to check if you really need the added complexity)

\$\endgroup\$
5
  • \$\begingroup\$ This is exactly what I was looking for! But, I have question about "you reset the character's stats to base and re-run the Apply()". When I reset the character's stats and re-run the Apply() methods, isn't the results always same?(Since char.GetStat(stat) would be the initialization value of a character) \$\endgroup\$
    – evol1102
    Commented Dec 18, 2023 at 15:24
  • \$\begingroup\$ Here I'm assuming this is being done in response to a change in the character's base stats, like if they've leveled up and their strength is 1 point higher now, or a change to other character properties that might be used to trigger conditions on items. \$\endgroup\$
    – DMGregory
    Commented Dec 18, 2023 at 15:27
  • \$\begingroup\$ Oh, now I understand. Thank you very much for you kindness :D \$\endgroup\$
    – evol1102
    Commented Dec 18, 2023 at 15:34
  • \$\begingroup\$ The only problem with rerunning the apply could be if there are cursed items that lower stats if you are over a threshold. Curse lowers the attributes and Bonus falls off, no bonus removes the curse and that lets you apply the Bonus again. \$\endgroup\$
    – Zibelas
    Commented Dec 18, 2023 at 15:53
  • 2
    \$\begingroup\$ If you have issues like that, I'd recommend separating "base stat" and "modified stat" and have conditions only ever trigger off of "base stat", but that's a whole extra set of design problems that merits a separate question, I'd say. \$\endgroup\$
    – DMGregory
    Commented Dec 18, 2023 at 17:37

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .