2
\$\begingroup\$

I'm planning to make a Class for RPG game's character skill. But the problem is that the skills need to be classified by multiple criteria. Below are the criteria for skills.

  1. Independent / dependent

Some skills are usuable by itself. Howver some skills are only used (automatically) in situations where certain skills are used. For example 'Additinal FireBall' skill are only used after 'FireBall' skill.

  1. Attack / Buff

Attack skills deal damage to enemy. On the contrary buff skills do some useful effects like increasing character's movement speed.

abstract class Skill {
    String skillId;

    abstract void use(Character character, Enemy enemy);
}

abstract class IndepentdentSkill {
}

abstract class DependentSkill {
    List<String> conditionSkills; //DependentSkill are automatically used after one of conditionSkills are used
}

interface AttackSkill {
    void doDamage(Enemy enemy);
}

interface BuffSkill {
    void applyBuff(Character character);
}

class IndependentAttackSkill extends IndepentdentSkill implements AttackSkill {
    @Override
    void use(Character character, Enemy enemy) {
        doDamage(enemey);
    }

    @Override
    void doDamage(Enemy enemy) {
        enemy.takeDamage(this.damage);
    }
}

class IndependentBuffSkill extends IndepentdentSkill implements BuffSkill {
    List<BuffEffects> BuffEffects;

    @Override
    void use(Character character, Enemy enemy) {
        doDamage(enemey);
    }

    @Override
    void applyBuff(Character character) {
        character.applyBuff(this.BuffEffects);
    }
}

//class DependentAttackSkill { ... }
//class DependentBuffSkill { ... }

I tried to make class like below but there were several problems.

  1. In this example, there will be four(2 * 2) class in total(IndependentAttackSkill, ..., DependentBuffSkill). If I need one more criteria and it would be 2 * 2 * 2 = 8. Moreover, if I have to add 'Defense' classification there would be a lot of work.

  2. method Skill.use(Character, Enemy) are taking parameter more than necessary. As you can see in the concrete Class, Skill.use() only use one of them.

If you know a class design that can complement these two issues, I would really appreciate it if you could let me know

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

When I made a Tactical RPG a couple years ago, I had a class for Actionss that can be used in combat as a one-time action and StatusEffects that remain on a Combatant* and do... something.

A Skill can enable a combatant to use an Action (which the player would consider an "active skill") and/or bestow a permanent StatusEffect on them (which the player would consider a "passive skill"). Using an Action usually does damage, but can also result in bestowing temporary StatusEffects on one or more combatants (which the player would call a "buff"/"debuff") or do something entirely different.

The Action class is pretty straight-forward: It has various properties that say on what kind of targets it can be used (allies, enemies, tiles or perhaps no specific target at all), in what range, with what area of effect and at what cost it can be used. And then it gets a use(Combatant user, List<Combatant> targets) method that can be overridden by derived classes to contain custom code that actually does stuff. Like inflict damage or bestow status effects.

The StatusEffect class is basically an event handler. A class inheriting from StatusEffect can subscribe to various events while active. When such an event happens (combatant stats are recalculated, combatant turn begins, combatant uses an Action, combatant receives damage...), the game checks if any active status effects subscribed to these events and then calls their handler method, which then get an opportunity to influence the results.

* I usually don't like having separate classes for Character and Enemy. I usually have one class Combatant that represents any participants in a combat and has a property that says which team it is fighting for.

\$\endgroup\$
7
  • 1
    \$\begingroup\$ Just to emphasize, if I'm reading Philipp's answer accurately: distinctions like attack vs buff or independent vs additional do not need to be encoded into your class inheritance hierarchy. You can use a general skill class and specify its valid activation criteria, targets, and effects in data via its properties. So, for example, an action that heals allies and an action that damages enemies could be two instances of the same class, just flipping the sign on the "HP Modification" property and changing the "Valid Targets" property from "Aliies" to "Enemies" \$\endgroup\$
    – DMGregory
    Commented Mar 20 at 11:26
  • 1
    \$\begingroup\$ @DMGregory Modeling healing as attacks with negative damage seems like a good idea at first but often doesn't work out too well. For example when you also have defense. You usually only want that to apply to damage but not to healing. I often started that way and then realized that healing adds so many exceptions to my damage calculation algorithm that it should better be modeled as an entirely different mechanic with its own method. \$\endgroup\$
    – Philipp
    Commented Mar 20 at 11:30
  • 1
    \$\begingroup\$ Fair point. Replace my example with a "speed up" (ally) / "slow" (enemy) or "strengthen" (ally) / "weaken" (enemy) pair of actions that apply corresponding positive or negative status effects, specified in data, rather than needing to have a separate inheritance path for each. \$\endgroup\$
    – DMGregory
    Commented Mar 20 at 11:38
  • 1
    \$\begingroup\$ The first fireball example from OP can be solved this way as well by adding a buff on the player after casting fireball. This buff gets consumed by the next cast skill (regardless which it is). The additional fireball skill can only be cast if it detects said buff on the player. This allows multiple skills to add a buff (like free fireball), they don't need to know which skills profit from it. The same is for the additional fireball, it does not care which skills supply the buff. It just cares if it is on the player \$\endgroup\$
    – Zibelas
    Commented Mar 20 at 12:18
  • 1
    \$\begingroup\$ @evol1102 The skill enhancement "2x damage for FireBall" would be a StatusEffect that subscribes to the "deal damage" event. It would check if the Action being used to deal damage is a Fireball action, and if so multiply the damage with 2. \$\endgroup\$
    – Philipp
    Commented Mar 21 at 8:18

You must log in to answer this question.

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