0

I'm designing a simple role-playing game (similar to World of Warcraft, League of Legends, etc.) where you basically have a hero or a champion who fights with wild creatures and other heroes/champions. This hero can then level up by gaining experience points from fights, and each level grants him a skill point, which allows him to level up one of his skills. Each skill has its own purpose and does its own thing, an example of a skill would be:

class DamageAura(Skill):
    """
    This skill increases damage dealt by your hero,
    as well as deals damage back to the ones who attack your hero.
    """

    name = 'Damage Aura'
    description = '...'

    # These methods are called when the actual event happens in game

    def unit_attack(self, target, damage):  
        target._health -= (self.level / 100) * damage

    def unit_defend(self, attacker, damage):
        attacker.deal_damage((self.level / 100) * damage)

Now I would like the players of the game to be able to make their own heroes, and I need it to be simple. The problem is, I don't want to do "bad" code simply to make creating heroes easier. Here's what a hero class would ideally look like:

class Warrior(Hero):
    name = 'Warrior'
    description = 'Ancient Warrior is thirsty for blood.'
    maximum_level = 30
    agility = 20
    intelligence = 10
    strength = 30

    class DamageAura(Skill):
        name = 'Damage Aura'
        description = '...'

        def unit_attack(...):
            ...

    class StrongerWeapons(Skill):
        name = 'Stronger Weapons'
        ...

Now the problem is, I'd like the name variable to be an attribute of the actual hero (f.e.Warrior) instance, not a class variable. Is there an easy, yet safe way to merge these two? Basically the class variables would define the default values for a hero, but if I wanted to create a Warrior instance with a custom name, I could do so by simply doing warrior1 = Warrior(name='Warrior God', maximum_level=40)

This is what the class would look like if I didn't have to worry about subclasses or ease-of-creation:

class Hero(object):
    def __init__(self, name, description='', maximum_level=50, skill_set=None, ...):
        self.name = name
        self.description = description
        self.maximum_level = maximum_level
        self.skill_set = skill_set or []
        ...

EDIT: I forgot to mention; each player could be playing the exact same hero, Warrior in this case, so that's why Warrior is a class, not just an instance of Hero. They would all have the same skill set, but unique levels, etc.

EDIT 2: I'm having no problem with Python classes or anything, normally I would NOT make the skills nested classes, or anything similar, but in this case I must invest a lot on ease of creating classes, so even the "noobs" can do it without knowing rarely any python at all.

Markus Meskanen
  • 19,939
  • 18
  • 80
  • 119
  • Not sure if you were implying this, but `__init__` is not "bad code". It's quite commonly used. – Kevin Dec 15 '14 at 15:00
  • So, you intend to let players create custom Hero classes by letting them write code, is that correct? If so, why? – Pieter Witvoet Dec 15 '14 at 15:01
  • Note that nested classes are very rarely a good idea in Python. – Daniel Roseman Dec 15 '14 at 15:01
  • @Kevin Yes, it's what *the class would look like if I didn't have to worry about subclasses or ease-of-creation*. However, it completely discards the possibility of using the "ease-of-creation" version where I define the variables on the class-level (first piece of code in my original question). – Markus Meskanen Dec 15 '14 at 15:02
  • @PieterWitvoet Well, technically not players, but the server hosts. I want each server to be able to decide what their own heroes look like, this way there could be thousands of different heroes with different skill sets. – Markus Meskanen Dec 15 '14 at 15:03
  • Given your requirement that *"even the "noobs" can do it without knowing rarely any python at all"*, I would strongly suggest you look at parsing the definitions from a defined file format, rather than letting them near the actual code! That's a much easier way to provide *"ease of creating classes"* than abusing OOP. – jonrsharpe Dec 15 '14 at 15:11

1 Answers1

2

You could allow the end users to create their Hero definitions as a dictionary:

my_hero = dict(name='Warrior'.
               description='Ancient Warrior is thirsty for blood.',
               maximum_level=30,
               ...)

Then you can easily create an instance like:

hero = Hero(**my_hero)

(If the syntax is unfamiliar, see What does ** (double star) and * (star) do for parameters?)

For both DamageAura and Warrior, it seems more appropriate for them to be instances than subclasses of Skill and Hero respectively.


Alternatively, consider letting the user create their characters outside of your code (e.g. in JSON, XML, YAML, ...), then parse the files and create appropriate instances from the input data.

Community
  • 1
  • 1
jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
  • Read my edit, I'm pretty sure the classes should be classes, not instances. Sorry, forgot to mention it in the first place. How would I implement the skills using your method? They'd need to be linked to the hero, and preferrably hero would be defined before skills. – Markus Meskanen Dec 15 '14 at 15:08
  • @MarkusMeskanen but there is no reason each player couldn't have their own `Hero` instance *without* subclassing - just create four `Hero` instances from the `Warrior` specification. – jonrsharpe Dec 15 '14 at 15:10
  • Ahh I see it now... Thanks, I think I'll use your approach! – Markus Meskanen Dec 15 '14 at 15:16