I am in the process of creating a text-based RPG for the purpose of learning more about OOP in Python. Right now things are going well, but I'm facing a problem that I don't exactly can find a good answer: Skill implementation. I also think that I'm going to face similar problem with the Items.At this moment I have 3 classes(professions): Fighter, Rogue, and Mage. Each of these classes should have their own skills, but, skills have similar things(name, description, MP usage), so I created a BasicSkill class, and 3 subclasses: PassiveSkill, ActiveSkill and ChanelledSkill.PassiveSkill are skills that doesn't require MP to use, are always active, and in most of cases, give the player buffs, like dual-wielding, more resistance, etc. ActiveSkill are skills that require MP to be used, and, in majority, cause damage or a instant buff. Finally, ChanelledSkill are skills that require more than one turn to be used, cost more MP than the others, and can be interruptedBut, when comes to the skill creation, I can only think in something like this:def hack_and_slash(self):        This is a Fighter skill, it's melee and has no cool down.        from skills import ActiveSkill        hack_and_slash = ActiveSkill(Hack n' Slash, A powerful double attack that has a chance of causing bleeding.,                                     8, 3)        self.mp -= hack_and_slash.        # more about the skill goes here, like what it does in terms of damaging, buffing, etc.Which I think is not exactly what I'm aiming for, since I have to instantiate it every time I use, or something in the same lines.I want to create the skills in a way that would require less instantiation, and also make more possible to cast. Also, all characters(player, npc, and enemies) can cast skills.There's a bunch of files in this project, so I will post the ones I think are more relevant here, and, if you want, you can check out the project here.This is my Player class:from character import Character# TODO implementation of player basic attacks and abilities# TODO implementation of a D&D style skill system(diplomacy, stealth, streetwise, etc)class Player(Character):    LEVEL_UP = 100  # initial XP to lvl up    COMMANDS = {'attack': 'a', 'character': 'c', 'inventory': 'i'}  # commands available for the player    # in future this skills will be used inside and outside combat situations, for like take out an enemy in stealth,    # or convince a guard to let you in a closed door    SKILLS = {'persuasion': 0, 'intimidation': 0, 'stealth': 0, 'perception': 0}    DAILY_SELF_HEALS = 4  # each ingame day player can use 4 self heals, it resets after getting the rested status    ATTRIBUTES = {'strenght': 0, 'constitution': 0, 'dexterity': 0, 'intelligence': 0, 'wisdom': 0, 'charisma': 0}    def __init__(self, armor_class):        super().__init__(input(Tell us your name, hero:\n>), 20, 10, {'gold': 10, 'torch': 1}, armor_class, 1)        self.exp = 0        self.max_hp = self.hp  # max HP of the player        self.max_mp = self.mp  # max MP of the player        self._strength = 0  # should I change it to dictionary with the attributes instead?        self._constitution = 0        self._dexterity = 0        self._intelligence = 0        self._wisdom = 0        self._charisma = 0    def level_up(self):        if self.exp >= self.LEVEL_UP:            self.lvl += 1            self.LEVEL_UP *= 1.25            self.LEVEL_UP = int(self.LEVEL_UP)            self.exp = 0            stat_choice = input(                You have 2 points to spend in your attributes!\nType which attributes you want to raise up: )            stat_choice = stat_choice.split(',')            self.ATTRIBUTES[stat_choice[0]] += 1            self.ATTRIBUTES[stat_choice[1]] += 1            self.max_hp = int(self.max_hp * 1.1)            self.max_mp = int(self.max_mp * 1.1)            return True        else:            return False    def gain_exp(self, exp):        self.exp += exp        print(You gained %d XP % exp)        if self.level_up():            print(Congratulations, you gained a level!\nYour current level is %d\n % self.lvl)        else:            print(Your current XP is %d/%d\n % (self.exp, self.LEVEL_UP))    def get_loot(self, enemy):        # buggy right now, problem with remove inventory from dead enemy        for item in enemy.inventory:            while True:                get_item = input(You found %s! do you want to get it?(Y=yes/N=no/A=all items) % item)                if get_item.lower() == a:                    self.inventory.update(enemy.inventory)                    enemy.inventory = {}                    return                elif get_item.lower() == y:                    self.inventory[item] = enemy.inventory[item]                    enemy.inventory.pop(item, None)                    break                elif get_item.lower() == n:                    break                else:                    print(Unfortunately, you don't have this choice hero, take a look again...)    def combat(self, enemy):                the combat between two entities(player and enemy)        :param enemy: the enemy of current character        :return:                if super().combat(enemy):            self.gain_exp(10)            self.get_loot(enemy)        else:            print(\t\t[GAME OVER...]\t\t)    def self_heal(self):        if self.DAILY_SELF_HEALS > 0:            self.DAILY_SELF_HEALS -= 1            self.hp += int(self.max_hp / 4)    def rest(self):        rest_time = input(\tHow much time would you like to rest? )        if rest_time >= 6:            self.STATUS['rested'] = True    def __str__(self):        str_info = \tName: [%s]\tLEVEL: %d\n\tHP: %2d\t\tMP: %2d % (self.name, self.lvl, self.hp, self.mp)        str_stats = \t\tSTR: %2d\n\t\tCON: %2d\n\t\tDEX: %2d\n\t\tINT: %2d\n\t\tWIS: %2d\n\t\tCHA: %2d\n % (            self._strength, self._constitution, self._dexterity, self._intelligence, self._wisdom,            self._charisma)        return |%s|\n%s % (str_info, str_stats)This, my Character class:# all character, being Player, NPC, or Enemy content should be implemented herefrom utils import Dice    class Character:                Main character class, all living entities derivate from it                # a dictionary of possible status of player, the idea is that this status change gameplay/combat in the future.        # By default all characters are rested.        STATUS = {'rested': True, 'hunger': False, 'poisoned': False, 'bleeding': False, 'blind': False, 'frozen': False,                  'paralyzed': False, 'dead': False}        # Right now there are only four equipment slot, which one should receive an armor object, not yet implemented        EQUIPMENT_SLOTS = {'head': None, 'chest': None, 'legs': None, 'boots': None}        def __init__(self, name, hp, mp, inventory, armor_class, lvl):                        Constructor for a character            :param name: name for the character            :type name: str            :param hp: hit points(health) for the character            :type hp: int            :param mp: magic points(mana) for the character            :type mp: int            :param inventory: inventory of character(gold, items, equips)            :type inventory: dict            :param armor_class: main defense            :type armor_class: int            :param lvl: level of character            :type lvl: int            :return: Character object                        self.name = name            self.hp = hp            self.mp = mp            self.inventory = inventory            self.armor_class = armor_class            self.lvl = lvl            self.atk_bonus = 1            self.atk_dmg = 4            self.movement_speed = 6        def take_dmg(self, dmg):            self.hp -= dmg            if self.hp <= 0:                print(\n[%s has died!] % self.name)                self.STATUS['dead'] = True            else:                print(\n[%s has %d health left % (self.name, self.hp))        def attack(self, enemy):                        All characters can attack, so this is the main attack(physical) method            :param enemy: Enemy that is in combat with character            :return:                        d20 = Dice(20)            dmg = self.atk_dmg            if d20.roll() + self.atk_bonus >= enemy.armor_class:                if d20 == 20:                    print(CRITICAL HIT!)                    dmg *= 2                else:                    dice_dmg = Dice(self.atk_dmg)                    dmg = dice_dmg.roll()                print(\n[%s hit %s for %d damage] % (self.name, enemy.name, dmg))                enemy.take_dmg(dmg)                if enemy.STATUS['dead']:                    return            else:                print(\n[%s missed] % self.name)        def combat(self, enemy):            while True:                self.attack(enemy)                if enemy.STATUS['dead']:                    return True                enemy.attack(self)                if self.STATUS['dead']:                    break            return False        def equip_armor(self, armor_piece):                        Equip a piece of armor in the correspondent slot            :param armor_piece: armor piece to equip            :type armor_piece: object            :return:                        replace_equip = input(                Would you like to replace %s with %s?(Y/N) % (self.EQUIPMENT_SLOTS['head'], armor_piece.name))            if replace_equip.lower() == y:                self.EQUIPMENT_SLOTS['head'] = armor_piece                return            else:                return        def list_inventory(self):                        list a character's inventory            :return:                        print(\t\t[%s] INVENTORY:\t\t\n % self.name)            for item in self.inventory:                print(%s:\t%d % (item, self.inventory[item]))The Skill class:# All basic skill implementation should be here    class BasicSkill:        SKILL_TYPE = {'passive': False, 'active': False, 'channeled': False}        def __init__(self, name, description, skill_type, skill_dmg=0, skill_range=0):                        Basic Skill class            :param name: skill name            :type name: string            :param description: description            :type description: string            :param skill_type: passive, active or channeled            :type skill_type: string            :param skill_dmg: default=0, meaning it's passive            :type skill_dmg: int            :param skill_range: default=0, meaning it's personal            :type skill_range: int            :return:                        self._name = name            self._description = description            self.SKILL_TYPE[skill_type] = True            self.skill_dmg = skill_dmg            self.skill_range = skill_range    class PassiveSkill(BasicSkill):        def __init__(self, name, description, skill_type='passive'):            super().__init__(name, description, skill_type)    class ActiveSkill(BasicSkill):        def __init__(self, name, description, skill_dmg, mp_use, cooldown=1, skill_type='active', skill_range=1):            super().__init__(name, description, skill_type, skill_dmg, skill_range)            self.mp_use = mp_use            self.cooldown = cooldown    class ChanneledSkill(BasicSkill):        def __init__(self, name, description, skill_dmg, channeling_time, cooldown=2, skill_type='chanelled',                     skill_range=1):            super().__init__(name, description, skill_type, skill_dmg, skill_range)            self.channeling_time = channeling_time            self.cooldown = cooldownAs an example, the Fighter class (all the classes/professions are implemented in similar way):from player import Player# TODO implement fighter stats and attacksclass Fighter(Player):        Fighter class, a strong warrior that uses meele attacks to face his opponents        def __init__(self):        super().__init__(armor_class=10)        print(\nYou have chosen the path of strength!)        self._strength += 2        self._constitution += 2        self.atk_bonus += 1        self.atk_dmg += 3    def hack_and_slash(self):        from skills import ActiveSkill        hack_and_slash = ActiveSkill(Hack n' Slash, A powerful double attack that has a chance of causing bleeding.,                                     8, 3)        self.mp -= hack_and_slash.mp_use    def __str__(self):        super().__str__()Any ideas are welcome, as well as code improvement tips. I tried to figure out a good way to make this skill implementation system, but I couldn't exactly find one. The only alternative I tough was a dictionary of skills, but I don't know how would it work.I'm using python 3, in a Ubuntu 14.04LTS, using PyCharm as my IDE.
Skill creation in a Python text-based RPG
python;object oriented;game;python 3.x
Aside from Pimgd's comments, I'd like to add that from an architecture point of view, you might want to reconsider your abilities.  To make them easier to maintain, you should design all (or nearly all) abilities as just a dictionary of pure data.  Figure out all the possible interactions and make key-value pairs for them.For example:{   'name': 'Hack and Slash',  'description': 'This is a...',  'type': 'ActiveSkill',  'affects': 'target',  'damage': 30,  'damage-type': ['melee', 'iron'],  'cooldown': 0,  'mp-cost': 0}Or{  'name': 'Dual Wield'  'description': 'Use two weapons!'  'type': 'PassiveSkill'  'tags': ['dual-wield']}Or{  'name': 'Health Potion',  'description': 'Regain Health',  'type': 'Item',  'tags': ['destroy-on-use'],  'target': 'self',  'damage': -30,   # negative damage is health gain  'inventory-space': 1}By making your code generic (rather than one function per skill/ability) and making all skills and items just data it will make your project easier to debug and items and skills easier to modify or add to.