Humerus - A Skeleton Game built with Albow

This is the outer framework for a game based on the Albow widget library. It is designed for a certain style of game, one made up of levels that are played through in sequence. It provides a main menu, a level editor menu, stubs for playing and editing screens, and housekeeping code for managing, reading and writing saved game files and level files.

Humerus is meant to be used by making a copy and modifying it to customise it and fill in the details. This document explains the overall structure of Humerus and points out the places where customisation and extension will be needed.

Structure of Humerus

Humerus is structured as outlined in the following diagram.



HumerusShell is a subclass of the Abow Shell class. It serves as a container for the various Screen classes and contains most of the top-level code for implementing the commands in the main menu and the editor menu.

At the centre of everything is the Game object. There is exactly one instance of this class for the life of the application, and it acts as an intermediary between the user interface objects and the currently active Level object. All references between widgets and levels go through the Game object.

There is one Level object in existence at any given time, created when the level is loaded. When a new level is loaded, the old Level object is discarded and a new one created.

The Game object contains a substantial amount of code for keeping track of the current Level object and its filename and the current directories for standard and custom levels, opening, reading and writing saved game files and level files, and various other housekeeping tasks.

HumerusShell

The HumerusShell class contains a large chunk of framework code. The following methods may be useful:
show_menu()
Switches to the MenuScreen.
show_story()
Same as the About menu item.
start_tutorial()
Same as the Tutorial menu item.
new_game()
Same as the New Game menu item.
start_level(testing = False)
Starts playing the currently loaded level. If testing = true, the level is started in testing mode (see Level).
prev_level()
Loads and starts the previous level.
next_level()
Loads and starts the next level.
start_level_file(pathname)
Loads the specified level file and starts playing it.
enter_editor()
Aborts the current level and switches to the EditorMenuScreen.
show_editor_menu()
Switches to the EditorMenuScreen.
edit_new_level():
Same as the New Level editor menu command.
quit()
Exits the application.
load_file(pathname)
Loads either a saved game file or a level file, depending on the filename extension.
save_game()
Same as the Save Game menu item.
load_level()
Same as the Load Level editor menu command.
save_level_as()
Same as the Save Level As editor menu command.
save_level()
Same as the Save Level As editor menu command.
ask_save_game()
If there is unsaved game progress, asks the user whether to save it. Returns true if the user chose to save or discard, false if he cancelled.
choose_level()
Same as the Choose Level menu command.
play_std_levels()
Same as the Play Standard Levels menu command.
play_custom_levels()
Same as the Play Custom Levels menu command.
new_level_set()
Same as the New Level Set editor menu command.

MenuScreen

The MenuScreen class provides the following menu items. You may want to modify its constructor to customise the appearance of the menu and add or remove items.

AboutSwitches to the StoryScreen.
TutorialLoads the first tutorial level (see Level Naming) and starts playing it.
New GameLoads the first non-tutorial level and starts playing it. Also clears any recorded game progress and saved game filename.
Load GamePrompts for a saved game file to load and restores the current level directory and game progress from it. Loads the next uncompleted level from the level directory and starts playing it.
Save GamePrompts for a filename and saves the current level directory and game progress in it.
QuitQuits the application.
Start LevelStarts playing the currently loaded level from the beginning. Only enabled when a level is not yet being played.
Resume LevelResumes playing a level that has been suspended. Only enabled when a level is being played.
Abort LevelAborts a suspended level and enables the Start Level command.
Choose LevelSwitches to the ChooseLevelScreen.
Play Standard LevelsChanges the current level directory to the standard level directory (Resources/levels).
Play Custom LevelsPrompts for a level file or a level set directory. If a level file is chosen, it is loaded and started. If a level set directory is chosen, it is made the current level directory and the firstnon-tutorial level in it is loaded and started.
Level EditorSwitches to the EditorMenuScreen.

ChooseLevelScreen

This screen displays a list of accessible levels from the current level directory to choose from. The list includes all tutorial levels, all levels recorded as completed in the current game, and the next uncompleted level. Choosing a level from the list loads and starts it.

LevelScreen

This is the screen where playing of levels occurs. You will need to fill in its implementation with code appropriate to your game.

EditorMenuScreen

This screen displays a menu with the following options.

New Level SetPrompts the user to create a directory for holding a set of levels. The directory name is given the level_set_suffix extension.
New LevelCreates a new, empty level and switches to the EditorLevelScreen.
Edit LevelSwitches to the EditorLevelScreen.
Load LevelPrompts for a level file to load, and switches to the EditorLevelScreen.
Save LevelSaves the current level under its existing filename.
Save Level AsPrompts for a new filename under which to save the current level.
Exit EditorReturns to the MenuScreen.


EditorLevelScreen

This is the screen used for editing levels. You will have to fill it in with code to implement an editor for your levels.

As well as the EditorLevelScreen stub, editor.py also exports a widget class called ChangeIndicator. If you put one of these somewhere on your editing screen, it will show a red dot when there are unsaved changes to the level.

Game

The Game class fully implements saving and loading of game state files that record which levels have been completed. The game file is a text file that begins with a line of the form
<magic_string> <file_version> <game_version>
The second line contains the pathname of the directory containing the levels, as a relative path if it is the standard level directory, or an absolute path otherwise. This is followed by the filenames of the completed levels (last pathname component only), one per line.

This assumes that the only information which needs to be saved is whether a level has been completed or not. If you want to save more information than that, you will have to modify the read_game_from_file() and write_game_to_file() methods of the Game class.

Level

The Level class provided is mostly a stub for you to fill in with your own code, but it does provide some functionality. The following instance variables are defined and maintained:
game
A reference to the Game object. for convenience.
changed
A flag indicating unsaved changes. In your level editor, if you set this to True every time the level is modified in some way, the user will be prompted to save changes before another level is loaded for any reason, or before quitting the application.
pathname
Current level file pathname.
state
The level can be in one of the following states:
RESETThe level has not yet been started.
PLAYINGThe level has been started and is currently being played.
SUCCEEDEDThe level has ended in a winning state.
FAILEDThe level has ended in a losing state.

is_tutorial
A flag that is set when the level is a tutorial level (see Level Naming).

testing
A flag to facilitate testing a level from the level editor. If you call game.start_level(testing = True), the level is started with this flag set. Then when you exit the level by pressing Escape, the shell switches back to the LevelEditorScreen.
You will need to implement the following methods of the Level class:
read_contents_from_file(self, reader)
write_contents_to_file(self, f)
See Level Files below.
begin_frame(self)
Update the state of the level at the beginning of an animation frame.

Level Files

Some framework code is provided for reading and writing level files. It is assumed that they will be text files beginning with a header of the form
<magic_string> <file_version>
followed by lines of the form
<token> [<argument>, ...]
where <token> is a string identifying the type of line, and the arguments are written as a Python list of constant Python expressions.

The framework code takes care of reading and writing the header line. The write_contents_to_file() method of the Level takes an open file object to which the header line has already been written, and you must write the rest of the data as lines with the above format.

The read_contents_from_file() method is passed a Reader instance which assists with reading the content lines. It has the following attributes and methods:
token
The token value of the current line.
eof
True when the end of the file has been reached by a call to next_line(). The token is then undefined and no other methods should be called.
next_line()
Advances to the next line of the file, sets token to its token value, and prepares to read the line's arguments.
get_arg(converter[, default])
Reads the next argument from the current line and applies the given converter function to it. If the converter raises a TypeError or ValueError, the error() method is called. If there are no more arguments and a default is specified, it is returned, otherwise error() is called.
get_tuple(converter, ...)
Gets the next argument, which must be a tuple, and applies the given converter functions to each of its elements. Calls error() if there are any type or value errors.
get_tuple_list(converter, ...)
Gets the next argument, which must be a list of tuples, and converts each of them as for get_tuple(). Calls error() if there are any type or value errors.
check(expected_token)
If the current token is not the expected_token, error() is called.
expect(expected_token)
Checks for expected_token and then advances to the next line.
looking_at(expected_token)
Returns a boolean indicating whether the current token equals expected_token. If eof is true, calls error().
error(self, message)
Raises an EnvironmentError incorporating the given message together with the file name and current line number.
When read_contents_from_file() is called, the header line has already been processed and the Reader is positioned at the first content line.

Level Naming

Levels are played in alphabetical order of their filenames. A filename starting with "+" is taken to be a tutorial level and comes before all non-tutorial levels.

The Level class has a get_name() method that returns a name suitable for displaying to the player. It is derived from the level filename by the following transformations:
For example, the file name "+03-Oscillating_Bees" becomes "Tutorial 3: Oscillating Bees".

command_line.py

This module defines a list of command line options. The values of these options become attributes of the settings.options object. The command line parsing is implemented in settings.py if you want to customise it further.

constants.py

This module defines the following constants that you will need to provide values for:

game_nameThe name of your game, as it is to be displayed to the user.
game_file_suffixFilename extension for saved game files, including the dot.
game_file_magicA distinctive string written to the beginning of a saved game file to identify it as such.
game_file_versionVersion number of the saved game file format. This number should be incremented whenever you make an incompatible change to the file format of saved games.
level_set_suffixFilename extension for directories containing level sets, including the dot.
level_file_suffixFilename extension for level files, including the dot.
level_file_magicA distinctive string written to the beginning of a level file to identify it as such.
level_file_versionVersion number of the level file format. This number should be incremented whenever you make an incompatible change to the level file format.

settings.py

The following default values are defined in settings.py. They are not currently settable from the command line, but could be made so by editing the argument parsing code.
screen_size
Size of the pygame screen.
flags
Flags to use for initialising the pygame screen.
frame_time
Time between animation frames in milliseconds.
sample_rate
Sample rate for initialising the mixer.
---