0
\$\begingroup\$

So I'm creating a rather generic strategy game similar to Warcraft or League of Legends, which needs to have:

  • a Player class for representing the users playing the game
  • a Character class for the character the user is controlling (champions in LoL, heroes in Warcraft)
  • a Spell class for the spells each character has

Also, each player can own multiple characters, but only one can be active at a time (for now at least...)

So the design issue I'm having is with defining the characters and spells:

I want there to be "presets", similar to how Warcraft and LoL have predefined sets of heroes/champions + spells, i.e. you don't get to choose which spells you want for your character, but you choose a character and it has its own unique spells. And multiple people can pick the same character, in which case they have the same spells too, just on different levels.

First I thought to just create Character and Spell classes, but it's not that simple, since I want it to be easy to define new characters, and defining a character isn't as simple as just creating a new object like this:

char = Character(name='Warrior', spells=[...])

Since I want every player who wants to play "Warrior" to have the same set of spells (every warrior should have FireBall spell while mages have FrostBolt, for example) and same name (Warrior), but different instances.

How should I define something like this? I'm using Python 3.5, but any generic design pattern to solve this is more than welcome.

\$\endgroup\$
  • \$\begingroup\$ "Warcraft or League of Legends" those are two very different games. \$\endgroup\$ – Charanor Apr 12 '17 at 0:03
  • \$\begingroup\$ @Charanor With two very similar character/spell/item systems \$\endgroup\$ – Markus Meskanen Apr 12 '17 at 4:13
  • \$\begingroup\$ Actually they're very similar in every aspect, LoL is just a copy of DotA (the original Dota 1), a custom game mode for Warcraft 3. This further proves they have exactly the same interactions between heroes/champions and their spells and items, because DotA (1) just uses Warcraft's engine and system \$\endgroup\$ – Markus Meskanen Apr 12 '17 at 4:15
3
\$\begingroup\$

This looks like a job for the Flyweight Pattern! (Closely related to the Type Object pattern)

Here you separate the concept of a character archetype from the instance of the character itself.

You might for instance have a CharacterArchetype class that stores anything that doesn't change from one instance of a given character to another, things like:

  • Character name
  • Base stats
  • Spell list
  • Animation set
  • Sound set
  • List of visual skins
  • etc.

Your Character class then holds a reference to its archetype, along with anything that can vary between instances:

  • Controlling player (if it needs to know this)
  • Current HP / energy meter(s)
  • Modified stats (eg. if under the effect of a buff, debuff, or status effect)
  • Spell cooldowns
  • Movement state
  • Visual representation (using a particular skin)
  • etc.

You can spawn many character instances referencing the same read-only archetype data, without redundantly repeating the definition of "Warrior" or "Mage" every time you need a new instance.

Your archetypes would likely be defined in one or more data files so they're easy to edit, especially if you're working in a team with character/combat designers who might not be best-suited to working in code directly.

\$\endgroup\$
  • \$\begingroup\$ This seems like exactly what I need! Do you suggest creating a separate archetype class for spells too (since each Warrior is gonna have a separate instance of the same spell), or should I just provide the skill classes to the CharacterArchetype and instantiate those upon character initialization? \$\endgroup\$ – Markus Meskanen Apr 11 '17 at 18:27
  • \$\begingroup\$ It depends how much instance data is in the spell. If you just need cooldowns on each ability slot, you don't need to instantiate the whole spell for that. In order to cast Fireball, I don't necessarily need my own copy of the Fireball spell with its own mana cost etc — I can look that up from a rulebook I share with all the other mages. Instead, we could look at Fireball.Cast(int level) as a factory method that produces a suitably configured "ball of fire" object for the actual in-world instance of the spell effect, which may be a composition of components like Projectile and DamageOnContact. \$\endgroup\$ – DMGregory Apr 13 '17 at 9:02
  • \$\begingroup\$ Yeah I ended up creating separate archetypes for the abilities too, it's now easy to create character and spell archetypes with nothing but JSON! :) Thank you very much for the help, this answer cleared up a lot. \$\endgroup\$ – Markus Meskanen Apr 13 '17 at 11:40
  • \$\begingroup\$ Why did you suggest the Flyweight Pattern as opposed to Type Object pattern though? I believe this is more of a Type Object pattern, right? I mean, they're the same thing, just different name for different purpose; but my purpose isn't to save space, but to manage same "types" of characters \$\endgroup\$ – Markus Meskanen Apr 13 '17 at 11:42
  • \$\begingroup\$ It's basically a difference in emphasis. If the characteristics we're working with are already encoded in instance data, but redundantly so, then I'd call it "flyweight" when we sweep all that redundant data into shared instances. If instead we're dealing with a proliferation of classes, where our type system is getting cluttered with a lot of similar & rigid types, then I'd call it a "type object" when we hoist the differences between those classes out of the type system and into instance data, so we can iterate quickly & flexibly in a data-driven way. Same result, different root problems. \$\endgroup\$ – DMGregory Apr 13 '17 at 13:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.