Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I store items and their properties in an external file and call it when needed? (When programming a Roguelike in Python)

I have just finished working through this tutorial for programming a Roguelike in Python and am now very much on my own in figuring out where to go and what to do next.

My predicament is with the messiness of the code. I would like to store elements somewhere, for example items; creatures; skills; anything where there could be a large number of them with numerous properties for themselves.

At the moment, all this code is in one single file that is getting quite large, and this is at its most basic. The function for placing items on a level looks much like this at the moment: (This function is called when the level is generated)

def place_objects(room):

    #Maximum number of items per room
    max_items = from_dungeon_level([[1, 1], [2, 4]])

    #Chance of each item (by default they have a chance of 0 at level 1, which then goes up)
    item_chances = {}
    item_chances['heal'] = 35
    item_chances['lightning'] = from_dungeon_level([[25, 4]])
    item_chances['fireball'] = from_dungeon_level([[25, 6]])
    item_chances['confuse'] = from_dungeon_level([[10, 2]])
    item_chances['sword'] = from_dungeon_level([[5, 4]])
    item_chances['shield'] = from_dungeon_level([[15, 8]])

    #Choose a random number of items
    num_items = libtcod.random_get_int(0, 0, max_items)

    for i in range(num_items):
        #Choose random spot for this item
        x = libtcod.random_get_int(0, room.x1+1, room.x2-1)
        y = libtcod.random_get_int(0, room.y1+1, room.y2-1)

        #Only place it if the tile is not blocked
        if not is_blocked(x, y):
            choice = random_choice(item_chances)
            if choice == 'heal':
                #Create a healing potion
                item_component = Item(use_function=cast_heal)
                item = Object(x, y, '~', 'Salve', libtcod.light_azure, item=item_component)
            elif choice == 'lightning':
                #Create a lightning bolt scroll
                item_component = Item(use_function=cast_lightning)
                item = Object(x, y, '#', 'Scroll of Lightning bolt', libtcod.light_yellow, item=item_component)
            elif choice == 'fireball':
                #Create a fireball scroll
                item_component = Item(use_function=cast_fireball)
                item = Object(x, y, '#', 'Scroll of Fireball', libtcod.light_yellow, item=item_component)
            elif choice == 'confuse':
                #Create a confuse scroll
                item_component = Item(use_function=cast_confuse)
                item = Object(x, y, '#', 'Scroll of Confusion', libtcod.light_yellow, item=item_component)
            elif choice == 'sword':
                #Create a sword
                equipment_component = Equipment(slot='right hand', power_bonus=3)
                item = Object(x, y, '/', 'Sword', libtcod.sky, equipment=equipment_component)
            elif choice == 'shield':
                #Create a shield
                equipment_component = Equipment(slot='left hand', defense_bonus=1)
                item = Object(x, y, '[', 'Shield', libtcod.sky, equipment=equipment_component)

            objects.append(item)
            item.send_to_back()

As I intend to add more items, this function will get seriously long, as will all the functions that then needed to be created to handle what each item does. I would really like to take this out of main.py and store it somewhere else, to make it easier to work with, but I don't know how to do this at the moment.

Here are my attempts to try and think through the problem:

Could I have a file that contains an item class with a number of properties that each item contains; (name, type, condition, enchantment, icon, weight, color, description, equip_slot, materal). Then store the functions for what the items do in that file? How would the main file know when to call this other file?

Could all the item data be stored in an external file (like an XML or something) and read from there when needed?

This is something that I could apply to more than just items obviously. This would be really useful for not having a really bloated main.py that holds all the creatures, items and other objects in the game ballooning thousands of lines of code, when what I really want is a main loop and a better structure of organization.

like image 382
sixteenmiles Avatar asked Nov 01 '22 10:11

sixteenmiles


2 Answers

If you don't need human-readable files, consider using Python's pickle library; this can serialise objects and functions, which can be written to and read back from files.

In terms of organisation, you could have an objects folder, separating out those classes from your main game loop, e.g.:

game/
    main.py
    objects/
        items.py
        equipment.py
        creatures.py

For further improvements, use inheritance to do e.g.:

class Equipment(Object):

class Sword(Equipment):

class Item(Object):

class Scroll(Item):

Then all the setup for any e.g. sword can be defined in the initialisation (e.g. which hand it's in) and only the things that vary for a specific sword (e.g. name, power bonus) need to be passed in.

like image 84
jonrsharpe Avatar answered Nov 13 '22 01:11

jonrsharpe


Could all the item data be stored in an external file (like an XML or something) and read from there when needed?

You can do this with the ConfigParser module.

You could store the properties of the items in a separate file (a normal, text file). Read this file to create your objects automatically.

I'm going to use the examples from the documentation as a guide here:

import ConfigParser

config = ConfigParser.RawConfigParser()
config.add_section('Sword')
config.set('Sword', 'strength', '15')
config.set('Sword', 'weight', '5')
config.set('Sword', 'damage', '10')

# Writing our configuration file to 'example.cfg'
with open('example.cfg', 'wb') as configfile:
    config.write(configfile)

Now, to read the file:

import ConfigParser
from gameobjects import SwordClass

config = ConfigParser.RawConfigParser()
config.read('example.cfg')

config_map = {'Sword': SwordClass}
game_items = []

for i in config.sections():
    if i in config_map:
       # We have a class to generate
       temp = config_map[i]()
       for option,value in config.items(i):
           # get all the options for this sword
           # and set them
           setattr(temp, option, value)
       game_items.append(temp)
like image 43
Burhan Khalid Avatar answered Nov 13 '22 01:11

Burhan Khalid