1

I am designing an RPG and would like to have the ability to attach classes to each other. What I'm looking to do is have say an Item class. The weapon class would inherit from it. A sword would be an instance of the weapon class. I want to then be able to attach properties to the sword. These properties would be other classes. For example I could attach the container class to it and the sword (only that instance of the sword) would become a container. I could also maybe attach something like an enchantment to that sword.

For a bonus it would be nice to be able to combine instances as well. So instead of having to have a fire_enchantment class I could just make it an instance of Enchantment and attach it to the sword instance.

I've googled around and haven't been able to find a design pattern that fits this. I recall studying one but can't remember what it was called (Was a few years ago)

I'm at a loss of of which design pattern allowed this. The combining of multiple classes dynamically.

jmarkmurphy
  • 11,030
  • 31
  • 59
xLilCasper
  • 93
  • 1
  • 10
  • The term is probably polymorphism. but I don't know how to do it. :( – Aiden Zhao Sep 17 '19 at 22:51
  • Thanks for the comment. Polymorphism would be allowing me to create a sword instance and pass it in where an Item class type was needed. It's the reason I structured the classes that way but I don't see a way for it to allow dynamic/run time changes to a class. – xLilCasper Sep 17 '19 at 23:21
  • no idea :(, seems like you are trying to implement something like how Path of Exile gems is are working? anyways I wouldn't be able to help you. >. – Aiden Zhao Sep 17 '19 at 23:25
  • Haven't played Path of Exiles but most "gem" systems would have a similar idea. Adding a gem to a sword would be like attaching a class to it. Adding functionality at run time. – xLilCasper Sep 17 '19 at 23:58
  • Polymorphism would also allow you to create an Enhancement class and add it to `attributes[]` array of type Property, you can also have Container, and Magic inheriting from Property that can also be added as an attribute of the sword. And the sword is also a subtype of Item which would allow it to be added to the Player's `items[]` array along with Bread, Shield and Flute – slebetman Sep 18 '19 at 01:01

1 Answers1

1

I think you seem to understand the idea of inheritance in python (e.g. class Subclass(Superclass): ) so I won't cover that here.

The classes you want to 'attach' can be treated as any other variable within the Weapon class.

class Enchantment(object):
    def __init__(self, name, type):
        self.name = name
        self.type = type
        # can define more member variables here, and set with setter methods

    # more Enchantment methods here...

class Weapon(object):
    def __init__(self, name, type)
        self.name = name
        self.type = type
        self.enchantments = []
        # more Weapon member variables here

    def add_enchantment(self, enchantment):
        # any logic you need to check when adding an enchantment
        self.enchantments.append(enchantment)

Then in wherever your game code is running you could do

sword = Weapon('My sword', 'sword')

fire_enchantment = Enchantment('Fireball', 'fire')
sword.add_enchantment(fire_enchantment)

You can then add methods on the Weapon class to do things with the enchantments/add certain logic.

The enchantment is still an instance of an object, so if you access it in the list (maybe by identifying it by its name, or looping through the list) all its methods and variables are accessible. You just need to build an interface to it via the Weapon class e.g. get_enchantment(self, name), or have other methods in the Weapon class interact with it (e.g. when you attack you might loop through the enchantments and see if they add any extra damage).

There's obviously design considerations about how you design your classes (the above was thrown together for example and doesn't include inheritance). For example you might only allow one enchantment per weapon, in which case you shouldn't use a list in the weapon object, but could just set self.enchantment = None in the constructor, and set self.enchantment = enchantment in the add_enchantment method.

The point I'm making is you can treat instances of Enchantment or other 'attachable' classes as variables. Just make sure you create an instance of the class e.g. fire_enchantment = Enchantment('Fireball', 'fire').

There's plenty of reading out there in terms of inheritance and OOP in general. Hope this helps!

Additional answer from OP

I think the Mixin pattern is what I was looking for. After digging around more I found this post which has an answer for dynamic mixin's.

elembie
  • 600
  • 2
  • 8
  • I think this fulfills the bonus requirement but I don't see how I can make something a container. My thought recent thoughts on this is that the container class would have something along the lines of open_container function. When I "attach" this to an item, it would be come a container. So the sword item could have the container class attached to it and it would gain this method. Same thing could be for a spawner class. Attach it to a chest and the chest is now a spawner. More research yielded the type function, could something like this be used to combine classes in the manner I want? – xLilCasper Sep 18 '19 at 22:11
  • How about you have a base class in which you declare all the potential 'attachments' as member variables e.g. in the `__init__` method of the `Base(object)` you can have `self.container = None` , `self.spawner = None` etc. In the base class you can define methods to `get` and `set` for each of the potential attaching classes. Your `Weapon` class can inherit from `Base` and at run time you can do `sword.set_container(container)` or `chest.set_spawner(spawner)`, in which both chest and sword are instances of objects that extend the `Base` class. Add error handling for when they're not set. – elembie Sep 18 '19 at 23:47
  • In the above your `Container` or `Spawner` classes will be defined separately, and you pass instances of those to the `Weapon` or `Chest` classes, which inherit the `set_container` or `set_spawner` from `Base`. You'll want to add error handling to check they're instances of the correct objects when passed (maybe using `type` or `isinstance` - [here](https://stackoverflow.com/questions/1549801/what-are-the-differences-between-type-and-isinstance) for explanation). – elembie Sep 18 '19 at 23:52
  • 1
    I think the Mixin pattern is what I was looking for. After digging around more I found https://stackoverflow.com/questions/8544983/dynamically-mixin-a-base-class-to-an-instance-in-python which has an answer for dynamic mixin's. This should allow me to attach the container class to any other class at run time by using it as a mixin. sword = sword.__class__ = type(sword.__class__.__name__,(Container,Weapon),{}). I can use the extend_instance function to make this a bit neater. Maybe make that a method of my GameObject class. Thanks for the ideas, Accepting this answer as it lead me to mine. – xLilCasper Sep 19 '19 at 00:37
  • Well there you go - I've learned something too! However IMO it does seem potentially a little over engineered, and the code may become quite confusing to understand if you're adding multiple mixins of different types, to many different base objects ([see discussion](https://softwareengineering.stackexchange.com/questions/312339/are-python-mixins-an-anti-pattern)). Anyway - best of luck with the game - I'll add your answer to mine :) – elembie Sep 19 '19 at 00:58