1
\$\begingroup\$

I've been working on a full RPG skeleton for a while, and I've just now reached a point on the module dealing with items where I feel comfortable sharing. My intent was to create a structure for the items, and then a CLI tool for creating/editing/saving JSON files which will be read as data.

This is going to be quite a bit of code, so I understand if people don't particularly care to read it. I know there are some odd things already. I went a little too crazy with name mangling and my documentation isn't always on point. Everything is still set up for creating a text based game, but I figured once I get that knocked out adding additional attributes for images wouldn't bee all that difficult. I'm not sure if that's the wrong approach, but I guess I'll find out when I get there.

I've left out one file because it doesn't really pertain to anything else here. It's shop_dict.py and just contains a ShopDict class and methods for updating stock. Not entirely important for this and maybe doesn't specifically belong in this module. I know that structure and naming are a couple of my big issues as well, and I'm working to understand a little better.

All of these files, as well as my progress with the other modules, can be found here at my Gitlab.

item.py

#!/usr/bin/env python3
from pkg_resources import resource_string
import json
"""
File used to define item class, weapon class, armor class, and consumable class
"""

class Item:
    """
    Parameters and methods for the base Item class
    """

    def __init__(self, id = '1', name = 'test', desc = 'test desc', item_type = None):
        self._id = id
        self._name = name
        self._desc = desc
        self.item_type = item_type

    def __repr__(self):
        return 'Id:{}\n Name:{}\n Desc:{}\n Item Type:{}'.format(self._id, self._name, self._desc, self.item_type)


class Weapon(Item):
    """
    Parameters and methods for the Weapon subclass
    """

    def __init__(self, id = 8, name = 'test weapon', desc = 'test desc', item_type = 'Weapon', damage = 0, weapon_type = None, two_handed = False):
        super().__init__(id, name, desc, item_type)
        self.damage = damage
        self.weapon_type = weapon_type
        self.two_handed = two_handed
        self.equipped = False


class Armor(Item):
    """
    Parameters and methods for the Armor subclass
    """

    def __init__(self, id = '10', name = 'test armor', desc = 'test desc', item_type = 'Armor', defense = 0, slot = None):
        super().__init__(id, name, desc, item_type)
        self.defense = defense
        self._slot = slot
        self.equipped = False


class Consumable(Item):
    """
    Parameters and methods for the Consumable subclass
    """

    def __init__(self, id = '12', name = 'test consumable', desc = 'test desc', item_type = 'Consumable', first_effect = None, second_effect = None):
        super().__init__(id, name, desc, item_type)
        self.item_type = 'Consumable'
        self.first_effect = first_effect
        self.second_effect = second_effect

    def _check_stat(self, effect):
        """
        Checks which stat to modify
        """
        stat = effect.keys()
        return stat

    def _check_mod(self, effect):
        """
        Checks the value for the effect dict
        """
        mod_amount = effect.values()
        return mod_amount

item_dict.py

#!/usr/bin/env python3

from pkg_resources import resource_string
from gim import item as I
import json

"""
File used to define ItemDict class and it's methods
"""

class ItemDict():
    def __init__(self, id = 1, num_of_items = 5):
        # Dictionary containing reference to all items
        self.all_items = {}
        # Dictionary containing reference to only weapons
        self.weapons = {}
        # Dictionary containing reference to only armor
        self.armor = {}
        # Dictionary containing reference to only consumables
        self.consumables = {}

        self._id = id
        self._num_of_items = num_of_items
        self._update_main_dict()
        self._update_weapons()
        self._update_armor()
        self._update_consumables()

    def __iter__(self):
        return iter(self.all_items.items())

    def __len__(self):
        return len(self.all_items)

    def __contains__(self, item):
        return item.id in self.all_items

    def __getitem__(self, item):
        return self.all_items[item.id]

    def _build_item(self, id):
        """
        Gathers item data from json file and returns instance object based on
        item type.
        """
        jsontext = resource_string(__name__, 'data/item{}.json'.format(id))
        d = json.loads(jsontext.decode('utf-8'))
        d['id'] = id
        if d['item_type'] == 'Weapon':
            item = I.Weapon(**d)
        elif d['item_type'] == 'Armor':
            item = I.Armor(**d)
        elif d['item_type'] == 'Consumable':
            item = I.Consumable(**d)
        return item

    def _update_main_dict(self):
        d = {}
        for i in range(self._id, self._num_of_items + 1):
            try:
                d[i] = self._build_item(i)
            except FileNotFoundError:
                print('File not found.  Please check to make sure it exists')

        self.all_items = d

    def _update_weapons(self):
        d = {}
        for i in self.all_items.values():
            if i.item_type == 'Weapon':
                d[i._id] = i
        self.weapons = d

    def _update_armor(self):
        d = {}
        for i in self.all_items.values():
            if i.item_type == 'Armor':
                d[i._id] = i
        self.armor = d

    def _update_consumables(self):
        d = {}
        for i in self.all_items.values():
            if i.item_type == 'Consumable':
                d[i._id] = i
        self.consumables = d

json_handler.py

#!/usr/bin/env python3

import json
import glob
from pkg_resources import resource_string

class DirectoryFiles():
    def __init__(self, directory = 'data/', filename = '*', extension = '.json'):
        self._directory = directory
        self._filename = filename
        self._extension = extension
        self._full_path = "{}{}{}".format(self._directory, self._filename, self._extension)
        self._files = None
        self._digits = []
        self._next = None
        # Build a list of all files in the data directory
        self._update_files()
        # Isolate only the digits in the filenames
        self._isolate_digits()
        # Store digit for next file in self._next
        self._get_next()

    def __repr__(self):
        return "{}\n{}\n{}".format(self.__class__.__name__, self._full_path, self._files)

    def _update_files(self):
        self._files = glob.glob('{}'.format(self._full_path))
        self._files.sort()

    def _isolate_digits(self):
        for filename in self._files:
            digit = ''
            for c in filename:
                if c.isdigit():
                    digit += c
            try:
                self._digits.append(int(digit))
            except ValueError:
                pass

    def _get_next(self):
        self._next = str(self._digits[-1] + 1)


class ItemData():
    def __init__(self, item_type = None, filename = None):
        self._item_type = item_type
        self._filename = filename
        self._attributes = None
        self._dict = {}
        self._json_dump = None
        self._mangle_file = 'data/mangle.json'
        self._mangle_dict = None
        self._update_mangle()

    def __iter__(self):
        return iter(self._dict.items())

    def __setitem__(self, key, item):
        self._dict[key] = item

    def _update_mangle(self):
        jsontext = resource_string(__name__, self._mangle_file)
        d = json.loads(jsontext.decode('utf-8'))
        self._mangle_dict = d


    def _mangle_names(self):
        """
        Used to mangle attribute names to ensure that there are no conflicts
        when creating an instance of Item() class.
        """
        for k, v in self._mangle_dict.items():
            try:
                self._dict[v] = self._dict.pop(k)
            except (KeyError, ValueError):
                pass



    def _to_json(self):
        self._json_dump = json.dumps(self._dict)

def test():
    test = DirectoryFiles()

if __name__ == "__main__":
    test()

command.py

#!/usr/bin/env python3

import os
import json
import textwrap
import cmd2
from pkg_resources import resource_string
from gim import json_handler as jh
from gim import item as i

class Command(cmd2.Cmd):
    def __init__(self):
        super().__init__()
        # Set break_long_words to False to prevent words being split
        self.break_long_words = False
        self.prompt = '>'

        # FLAGS
        # flag for confirming creating a new file
        self._conf_new = False
        # flag for selecting item_type
        self._sel_type = False
        # flag for editting attributes
        self._edit_att = False
        # flag for confirming saving a file
        self._conf_save = False

        # current working directory
        self._cwd = None
        # extension
        self._ext = None
        # data file for cmd print messages
        self._msgs_file = 'data/cmd_msgs.json'
        # variable to hold instance of MessageHandler()
        self._msgs = None
        # data file for item attributes
        self._att_file = 'data/item_att.json'
        # variable to hold filename for creation of ItemData() instance
        self._filename = None
        # variable to hold all item attributes dict
        self._all_att = None
        # variable to hold specific item type attributes dict
        self._item_att = None
        # variable to hold string of item attributes for do_attributes method
        self._att_str = None
        # variable to hold instance of jh.DirectoryFiles()
        self._all_files = None
        # variable to hold instance of jh.ItemData()
        self._current_file = None
        # Load all print messages into a dictionary for easy access
        self._load_cmd_msgs()
        # Load item _attributes
        self._load_att()
        # Update _all_files
        self._update_dir()
        # Display startup messages
        self._startup()

    def _update_dir(self):
        """
        Create instance of jh.DirectoryFiles() using default parameters
        """

        self._all_files = jh.DirectoryFiles()

    def _load_cmd_msgs(self):
        """
        Load cmd messages from json file specified by self._msgs_file
        """

        jsontext = resource_string(__name__, self._msgs_file)
        d = json.loads(jsontext.decode('utf-8'))
        self._msgs = MsgHandler(**d)

    def _load_att(self):
        """
        Load item attribute dictionary from json file specified by self._att_file
        """

        jsontext = resource_string(__name__, self._att_file)
        d = json.loads(jsontext.decode('utf-8'))
        self._all_att = d

    def _save_file(self):
        self._current_file._to_json()
        with open(self._current_file._filename, 'w') as f:
            f.write(self._current_file._json_dump)

    def _edit_att(self, att, val):
        """
        Used to set attribute provided to value provided
        """
        self._current_file[att] = val



    def _startup(self):
        """
        Print startup messages
        """
        self._msgs._print_msg('welcome')
        self._msgs._print_msg('new_or_load')

    def _create_new(self):
        """
        Used to create a new instance of jh.ItemData()
        and change the _sel_type flag to True
        """
        self._cwd = self._all_files._directory
        self._ext = self._all_files._extension
        self._next = self._all_files._next
        filename = '{}item{}{}'.format(self._cwd, self._next, self._ext)
        self._current_file = jh.ItemData(None, filename)
        self._sel_type = True
        self._msgs._print_msg('select_type')


    def _select_type(self, type):
        self._current_file._item_type = type
        self._current_file['item_type'] = type
        self._item_att = self._all_att[type]
        self._att_str = ', '.join(self._item_att)

    def do_new(self, arg):
        """
        If current file is None, create new instance of jh.ItemData using
        parameters from self._all_files to generate a filename.
        If current file is not None ask for confirmation before creating new
        instance
        """
        if self._current_file == None:
            self._create_new()

        else:
            self._conf_new = True
            self._msgs._print_msg('current_not_none')

    def do_save(self,arg):
        if self._current_file == None:
            pass
        else:
            self._conf_save = True
            self._msgs._print_msg('save_msg')


    def do_yes(self, arg):
        if self._conf_new:
            self._create_new()
            self._conf_new = False

        elif self._conf_save:
            self._save_file()
            self._conf_save = False

        else:
            pass

    def do_no(self, arg):
        if self._conf_new:
            self._conf_new = False
        elif self._conf_save:
            self._conf_save = False
        else:
            pass


    def do_weapon(self, arg):
        """
        Set item type to Weapon when creating a new item.
        """
        if self._sel_type:
            self._select_type('Weapon')
            self._sel_type = False
            self._edit_att = True
            self._msgs._print_msg('att_msg')
        else:
            pass


    def do_armor(self, arg):
        """
        Set item type to Armor when creating a new item.
        """
        if self._sel_type:
            self._select_type('Armor')
            self._sel_type = False
            self._edit_att = True
            self._msgs._print_msg('att_msg')
        else:
            pass


    def do_consumable(self, arg):
        """
        Set item type to Consumable when creating a new item.
        """
        if self._sel_type:
            self._select_type('Consumable')
            self._sel_type = False
            self._edit_att = True
            self._msgs._print_msg('att_msg')
        else:
            pass


    def do_show(self, arg):
        print(self._current_file._dict)


    def do_edit(self, arg):
        if arg in self._item_att:
            self._msgs._print_msg(arg)
            val = input('>')
            self._current_file[arg] = val

        else:
            self._msgs._print_msg('invalid_att')


    def do_attributes(self, arg):
        self._msgs._print_msg('current_attributes')
        print(self._att_str)



class MsgHandler():
    def __init__(self, **kwargs):
        self.__dict__.update((key, value) for key, value in kwargs.items())


    def _print_msg(self, msg):
        for line in textwrap.wrap(vars(self)[msg], 80):
            print(line)


def test():
    test_inst = Command()
    #test_inst.cmdloop()
    print(test_inst._all_files._files)


if __name__ == "__main__":
    test()
\$\endgroup\$

Your Answer

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

Browse other questions tagged or ask your own question.