Advice for storing different attacks in an RPG

Godot Version

3.5

Question

Hi! I’m trying to make a simple-ish turn-based RPG, and I’m trying to figure out a nice way to store all the possible Attacks. In my head, I feel like it should be:

  1. Make a skeleton class for the attack object
  2. Make a bunch of subclasses that override the variables and methods.
  3. When the player selects an attack, we just load the corresponding subclass

But then I learned that you can’t define multiple classes in a single file (I’d prefer not to make like 50 files, if I can help it). And even if I could, I’m not totally sure how I would even select the right subclass, once the player makes the choice (can you store subclass names in a variable and then create them?). This would also extend to storing different kinds of enemies, or status effects, too.

A workaround, or just other ideas, would be nice.

I’d suggest making your class inherit from Resource, and making the base attack class fairly broad. A Resource is a kind of object with instances that can be saved to disk, and those instances can then be dragged into script variables in the inspector.

Example

Maybe your base attack class looks something like this:

class Attack: Resource {
    [Export] public int Damage = 0;
    [Export] public Element ElementalAffinity = Element.None;  // enum of elements in the game
    [Export] public StatType AttackStat = StatType.Strength;  // enum of kinds of stats

    public virtual void OnAttack(Character source, Character target) {

        target.hp -= Damage + source.Stats[AttackStat];

        if (target.IsWeakTo(ElementalAffinity)) {
            target.hp -= Damage;
        }
    }
}

Here, most attack definitions would be instances of your base attack class, rather than subclasses. Every time you needed a new attack, you could make a new instance of your Attack resource, save it to disk, and then plop it into the UI or Character or whatever stored available attack definitions.

Here’s what a few attack resources might look like, written in pseudocode (with Resources, you would input these values in the inspector):

Bash:
   Damage: 10
   ElementType: None
   AttackStat: Strength

Poisoned Dagger:
   Damage: 5
   ElementType: Poison
   AttackStat: Dexterity

If you needed an attack to do special behavior, you could override OnAttack. For example, maybe some attacks should cost mana.

class Spell: Attack {
    [Export] public int ManaCost = 0;
    
    public override void OnAttack(Character source, Character target) {
        if (source.mp >= ManaCost) {
            base.OnAttack(source, target);
            source.mp -= ManaCost;
        }
    }
}

And your new resources could look like this:

Magic Missile:
   Damage: 5
   ElementType: None
   AttackStat: Intelligence
   ManaCost: 5

Fireball:
   Damage: 20
   ElementType: Fire
   AttackStat: Intelligence
   ManaCost: 20

Of course, it’s up to you what you include in the base class vs in its children, and there’s a lot more behavior you could add to either (attacks that heal the source or target, attacks that hit multiple times, etc).

I’ve kind of rambled on a bit here. TLDR:

  • Make a base attack Resource
  • Define new attacks as instances of that Resource whenever possible.
  • If you need new functionality, add it to the base class or create a new subclass of your attack Resource

This is great, definitely going to try it! I’m assuming this translates over to gdscript?

It should, yeah. I don’t know much about GDScript, but nothing I’ve done here is very specific to C Sharp.