I started a project about a year ago involving a simple terminal-based RPG with Python 3. Without really thinking about it I just jumped into it. I started with organizing multiple scripts for each.. well, function. But halfway into the project, for the end goal I'm not sure if it's easier/more efficient to just have one very large script file or multiple files.
Since I'm using the cmd
module for the terminal, I'm realizing getting the actual app running to be a looping game might be challenging with all these external files, but at the same time I have a __init__.py
file to combine all the functions for the main run script. Here's the file structure.
To clarify I'm not the greatest programmer, and I'm a novice in Python. I'm not sure of the compatibility issues yet with the cmd
module.
So my question is this; Should I keep this structure and it should work as intended? Or should I combine all those assets
scripts into one file? Or even put them apart of the start.py that uses cmd
? Here's the start function, plus some snippets of various scripts.
from assets import *
from cmd import Cmd
import pickle
from test import TestFunction
import time
import sys
import os.path
import base64
class Grimdawn(Cmd):
def do_start(self, args):
"""Start a new game with a brand new hero."""
#fill
def do_test(self, args):
"""Run a test script. Requires dev password."""
password = str(base64.b64decode("N0tRMjAxIEJSRU5ORU1BTg=="))
if len(args) == 0:
print("Please enter the password for accessing the test script.")
elif args == password:
test_args = input('> Enter test command.\n> ')
try:
TestFunction(test_args.upper())
except IndexError:
print('Enter a command.')
else:
print("Incorrect password.")
def do_quit(self, args):
"""Quits the program."""
print("Quitting.")
raise SystemExit
if __name__ == '__main__':
prompt = Grimdawn()
prompt.prompt = '> '
#ADD VERSION SCRIPT TO PULL VERSION FROM FOR PRINT
prompt.cmdloop('Joshua B - Grimdawn v0.0.3 |')
from assets import *
def TestFunction(args):
player1 = BaseCharacter()
player2 = BerserkerCharacter('Jon', 'Snow')
player3 = WarriorCharacter('John', 'Smith')
player4 = ArcherCharacter('Alexandra', 'Bobampkins')
shop = BaseShop()
item = BaseItem()
#//fix this to look neater, maybe import switch case function
if args == "BASE_OFFENSE":
print('Base Character: Offensive\n-------------------------\n{}'.format(player1.show_player_stats("offensive")))
return
elif args == "BASE_DEFENSE":
print('Base Character: Defensive\n-------------------------\n{}'.format(player1.show_player_stats("defensive")))
return
* * *
#import functions used by script
#random is a math function used for creating random integers
import random
#pickle is for saving/loading/writing/reading files
import pickle
#sys is for system-related functions, such as quitting the program
import sys
#create a class called BaseCharacter, aka an Object()
class BaseCharacter:
#define what to do when the object is created, or when you call player = BaseCharacter()
def __init__(self):
#generate all the stats. these are the default stats, not necessarily used by the final class when player starts to play.
#round(random.randint(25,215) * 2.5) creates a random number between 25 and 215, multiplies it by 2.5, then roudns it to the nearest whole number
self.gold = round(random.randint(25, 215) * 2.5)
self.currentHealth = 100
self.maxHealth = 100
self.stamina = 10
self.resil = 2
self.armor = 20
self.strength = 15
self.agility = 10
self.criticalChance = 25
self.spellPower = 15
self.intellect = 5
self.speed = 5
self.first_name = 'New'
self.last_name = 'Player'
self.desc = "Base Description"
self.class_ = None
self.equipment = [None] * 6
#define the function to update stats when the class is set
def updateStats(self, attrs, factors):
#try to do a function
try:
#iterate, or go through data
for attr, fac in zip(attrs, factors):
val = getattr(self, attr)
setattr(self, attr, val * fac)
#except an error with a value given or not existing values
except:
raise("Error updating stats.")
#print out the stats when called
#adding the category line in between the ( ) makes it require a parameter when called
def show_player_stats(self, category):
* * *
The purpose of the scripts is to show what kind of structure they have so it helps support the question of whether I should combine or not
The way you have it currently is fine, personally I much prefer a lot of files as it's a lot easier to maintain. The main issue I see is that all of your code is going under assets
, so either you'll end up with everything dumped there (defeating the point of calling it that), or you'll eventually end up with a bit of a mess of folders once you start coding other bits such as the world/levels and so on.
A quite common way of designing projects is your root would be Grimdawn
, which contians one file to call your code, then all your actual code goes in Grimdawn/grimdawn
. I would personally forget the assets
folder and instead put everything at the root of that folder, and only go deeper if some of the files get more complex or could be grouped.
I would suggest something like this (put in a couple of additions as an example):
Grimdawn/characters/Jon_Snow
Grimdawn/characters/New_Player
Grimdawn/start.py
Grimdawn/grimdawn/utils/(files containing generic functions that are not game specific)
Grimdawn/grimdawn/classes.py
Grimdawn/grimdawn/combat.py
Grimdawn/grimdawn/items.py
Grimdawn/grimdawn/mobs/generic.py
Grimdawn/grimdawn/mobs/bosses.py
Grimdawn/grimdawn/player.py
Grimdawn/grimdawn/quests/quest1.py
Grimdawn/grimdawn/quests/quest2.py
Grimdawn/grimdawn/shops.py