0

For example, I am trying to build an enemy class for a simple game. Each enemy that spawns has a type which affects it stats which are fields in the enemy class.

class Enemy:
    #Base Stats for all enemies
    name = "foo"
    current_health = 4
    max_health = 4
    attack = 0
    defense = 0
    armor = 0
    initiative = 0
    initMod = 0
    alive = True

Should each type be a subclass of enemy like so..

class goblin(Enemy):
    name = goblin;
    current_health = 8
    max_health = 8
    attack = 3
    defense = 2
    armor = 0

    def attack(){
        //goblin-specific attack
    }

But this method means that I would have to build a class for each individual type (which would be 80+ classes), or is there a better way to do it? This enemy is going to be randomized, so I was thinking the types could also be put into a dictionary which uses the names of the types as keywords. Although I'm not entirely sure how I could implement that.

Czurch
  • 33
  • 7
  • With that many potential subclasses, you're probably not modelling the problem well. For one thing it sounds like you're confusing "is-a" with "has-a" relationships—and possibly classes and instances of classes. Most of the attributes listed some probably be initialized in the class or subclass's constructor (`__init__()`). Also, all definitions of class methods automatically receive a first argument usually called `self` which is the instance of the class. So you would need to have `def attack(self):`. Also, Python doesn't use `{` and `}` brackets in that manner. – martineau Aug 10 '16 at 01:40

3 Answers3

0

If you want to go the dictionary route you could do something like this with a tuple being returned by the key:

enemy_types = {"goblin": ("goblin", 8, 8, 3, 2, 0, goblin_attack)}

def goblin_attack(enemy):
    do_stuff()

Though you may want to use a named_tuple

https://stackoverflow.com/a/13700868/2489837

to make sure you don't mix up the fields in the tuple

Community
  • 1
  • 1
north.mister
  • 500
  • 1
  • 7
  • 24
  • I think this is the route I'm going to persue. To clarify though, every enemy follows the same guidelines for combat. They all can attack or defend, but what they do during their attack may vary based on their special ability. Would I need to create a separate function for every different type of enemy in my dictionary? And would those functions reside in my enemy class file? – Czurch Aug 11 '16 at 21:33
  • That will have to be up to you. All my solution does is return a function pointer for whatever function. Depending on what their special attacks are (ie, can you just pass a parameter for how much damage it does, or does it have more special stuff associated with it) you may need a unique function for each one. Either way you could still take this dictionary route and return a tuple/object or what have you. – north.mister Aug 13 '16 at 07:12
0

If the set of attributes and possible actions for each enemy type is mostly common across types, I don't see a reason to do anything more complicated than defining an 80 item enumeration for the type, and using a single class called Enemy.

If you do need a variety of actions and such, perhaps you could group your 80 types into various collections that would then have common attributes (e.g. FlyingEnemy, UndeadEnemy, etc). Those would then become your sub-classes.

Jud
  • 1,158
  • 1
  • 8
  • 17
  • I really like this idea, could it also work with user2489837's idea to use a dictionary for different enemies? This way I could have a list of FlyingEnemies or UndeadEnemies. – Czurch Aug 11 '16 at 21:34
0

The goblin should probably be an instance of Enemy. However, in your example you added a method called attack. I would say the best way to do this is like this:

class Enemy:
   #Base Stats for all enemies
  def __init__ (self, **kwargs):
   self.name = "foo"
   self.current_health = 4
   self.max_health = 4
   self.attack = 0
   self.defense = 0
   self.armor = 0
   self.initiative = 0
   self.initMod = 0
   self.alive = True
   # Use this to specify any other parameters you may need
   for key in kwargs:
       exec ("self.%s = %s" % (key, kwargs[key])

Now you can create an instance of Enemy called goblin.

To answer your initial question of when to use a subclass, I would say that it is great to use sub-classes but may over complicate things in your case. Since you only have one method in your subclass you can easily just make a module and then have an argument in the __init__ that specifies the attack function. This will simplify your code more.

Note: If you are going to use subclasses make sure that in the __init__ of your subclass you call the __init__ of your baseclass with super().

Andrei Tumbar
  • 490
  • 6
  • 15
  • The entire `for` loop at the end could be replaced with `self.__dict__.update(kwargs)`. Generally one wants to avoid using `exec` whenever possible, and it usual is. Your indentation is also inconsistent (and not 4 spaces, see [PEP 8 - Style Guide for Python Code](https://www.python.org/dev/peps/pep-0008/)). – martineau Aug 10 '16 at 01:28
  • I apologize for my lack of knowledge on dictionaries but I think I understand what you're doing here. So I would have defined a dictionary with all the different enemy types. When I create a new instance of enemy, I need to enter the key for one of those enemies. That key is used to reference a type in the dictionary, which initializes the enemy fields to the values specified in the dictionary. p.s. Thanks for taking the time to answer – Czurch Aug 11 '16 at 21:44