[insert links here]
1.1 Purpose of this
This document describes the design of OpenMortal.
OpenMortal consists of two main parts: the frontend and the backend.
- The frontend is a C++ program, responsible for multimedia
(sounds, music, graphics) and general interaction with the players
(menus, keyboard input), and the demo and intro screens.
- The backend is written in Perl, and is incorporated into the C++
program with Perl embedding.
1.2 Revision History
This table will be appended whenever this document is changed.
Description of the nature of changes will be stored here.
|Initial version. Describes the program's state
after release of version 0.3
Here are the definitions of terms used in this documentation.
|Player refers to one of the two persons
playing the game. A player chooses a fighter. The two
players are referred to as "Player 1" and "Player 2", even though the
C++ and perl code count arrays from 0.
|Fighter is one of the many available
characters. Usually there are only two fighters loaded at any
time. Fighters are static: their properties never change. Maybe
Fighter should be renamed to Character?
|One game is the part of the program in
which the players actually compete. The game consists of
a number of rounds. The player selection, and gameover screen
are not part of this definition.
|One round starts with both players
at full health, and ends either with a timeout or with a ko.
|A graphical element on the game screen that is
not the background or the characters. E.g. the "3x combo" text or a
thrown projectile are doodads.
|A tint is a methodical change in a
palette. There are many ways to tint (e.g. grayscaling, darkening, green
chroma, etc). Usually when two players choose the same fighter,
the fighter of player 2 is tinted.
|The description of a frozen moment in the course
of a game. The Backend is responsible for calculating each
consecutive scene. The number of scenes calculated per
second is constant (except for the "hurry up mode", or if the computer
is too slow).
|Frames Per Second. The FPS indicator on the
screen during a game indicates the number of scenes drawn,
not the number of scenes calculated by the backend.
1.4. C++ Coding
Unfortunately two coding conventions are mixed in OpenMortal. The older
style is somewhat Qt-esque (method and variable names starting with
lower caps), the other is a new coding style that I adapted to at work.
I plan to convert the entire codebase to the new style eventually in
small steps. Here I will describe the new conventions:
The prefixes used are:
- Class names: MixedCaps. No "C" prefix (maybe I should switch to a
- Struct names: SMixedCaps. (Sometimes the "S" is missing.. no big
- Typedef names: TSomeTypedef
- Enum names: SomeThingEnum.
- Enum values: Prefix_ENUM_VALUE
- Method names: MixedCaps.
- Variable names: <prefix>VariableName.
- Instance property names: m_<prefix>VariableName,
- Class property names (a.k.a. static class variables):
- Method argument names: a_<prefix>VariableName. If a
reference or pointer argument is "output-only:
- Global variable names: g_<prefix>VariableName.
- Array of something: a<something>
- Pointer to something: p<something>
- Reference of something: r<something>
- Basic types: Integer: i; char: c; double: d; enum: en; object
(class or struct): o; std::string: s;
SomeExampleClass: public SomeBaseClass
void SomeMethod( int& a_riOutSomething );
static enum SomeThingEnum
2. The Frontend
The frontend is a C++ program. It uses SDL for hardware access
such as screen drawing, music, sound effect and keyboard input. For
information and documentation of SDL, SDL_image, SDL_ttf
and SDL_mixer, please look at the SDL
The Frontend divides responsibilities into a number of classes:
Some utility methods and structures are imported from the SGE
library. Yet some more are in gfx.cpp and common.cpp.
There pieces are of less importance, and will not be discussed in this
- Backend: Initializes the perl backend and exposes basic
perl services. Also responsible to connect the perl game backend to the
C++ frontend by exporting the value of the perl variables which fully
describe a "scene". The single instance is g_oBackend.
- Demo: There are several subclasses of the Demo class, each
responsible for running one of the intro/demo screens.
- Game: Objects of this class are responsible for running a game,
or playing a replay.
- Menu: Shows one of the game menus. The Menu class used in
OpenMortal are not nearly as sophisticated as the menus in GUI
applications, they are just a quick hack to allow the player to modify
- MszAudio: Sound and music services. The single instance is Audio.
TODO: Rename the class to Audio and the instance to g_oAudio
- PlayerSelect: Implements services that allow interactive
or programmatical assignment of fighters to players.
Also stores the RlePack of both players. The single
instance is g_oPlayerSelect.
- RlePack: Stores an array of images, compressed with
- SState: Global program configuration and current state.
The single instance is g_oState.
2.1. State and Menus
State, despite it's name, stores global program configuration and
state. It is a singlular object, and is accessed with a global
pointer, g_oState. All other frontend modules access the state
through this object. The state is made persistent through it's
methods, Load() and Save(). Load is called on program
start, Save is called when the program exits.
The State is the way the menus communicate with the rest of the
system. For example, if the user chooses "Quit" from the menu, the m_bQuitFlag
is set to true, and the program will react accordingly.
The state's most important properties are:
- m_enGameMode: This enumerable can take the following values:
The mode changes when a game is started or the game ends (either in the
GameOver screen, or via the "Surrender Game" menu option).
- IN_DEMO: The game is currently in "demo" mode: displaying the
intro screens, etc.
- IN_SINGLE: The game is in single-player mode.
- IN_MULTI: The game is in multi-player mode.
- m_bQuitFlag: This is set if the program receives a quit
event from the operating system (e.g. KILL signal, window close event,
etc), or the user chooses "Quit" from the menu. Once the quit flag is
set, the program will abort. All main loops check the value of the quit
flag, and will break as soon as it is true.
- m_bFullScreen: Quite simply, it is true in fullscreen
mode, and false in windowed mode. The user can change this via the menu.
The State's ToggleFullscreen() method will switch between
fullscreen and windowed mode. Maybe this functionality doesn't
belong to the State? ...
- Sound properties: Mixing rate, number of
channels, volume, etc.
- m_aiPlayerKeys: A double array of each
player's keys. This is used most often in processing SDL key events: if
the event's keysym matches a value in m_aiPlayerKeys, that means that a
meaningful key was pushed or released.
The RlePack is an array of images, compressed with runlength
OpenMortal stores the character graphics (and eventually, the doodads
too) in RlePacks. The reason is simple: RlePacks give an acceptable
tradeoff between memory usage and blitting speed. Also the RlePack
allows the sprites to be draw horizontally flipped.
The sprites in the RlePack are always paletted (8 bits per
pixel). The size of the palette is between 0 and 256. The RlePack stores
two copies of its palette: one is the "base" palette, as it was
read from disk, the other is the "tinted" palette. The TintEnum
contains values that can be passed to RlePack::SetTint. This is
used for two things:
Additionally, the RlePack allows the color range to be shifted.
This is necessary: if we want to load two characters with different
palettes, both cannot occupy the same physical color range. Since all
player RlePacks use 64 colors, the first pack will be shifted use colors
112-175, and the second will use colors 176-239. (The rest of the system
- In case both players choose the same fighter, player 2's fighter
is tinted so they won't get confused.
- Some special effects (e.g. frozen) make the figther tinted as
RlePack doesn't concern itself with concepts such as "player" or
"doodad", it merely stores the palette and sprites. This part of
OpenMortal can be reused in any project with little changes.
This class implements services that allows players to select their
fighters. It also stores info about which fighter is available, and
allows other parts of the program to programmatically assign a fighter
to a player, and set fighter tints (this is used by e.g. the
aqua-colored "frozen" effect).
Most important methods are:
- GetPlayerInfo( int a_iPlayer ): Returns the info of a
given player (0 or 1). The most important piece in the info is the
player's RlePack which is used for drawing the player in the Game, the
GameOver and the FighterInfo screens.
- SetPlayer( int a_iPlayer, FighterEnum a_enFighter ):
Changes the fighter designated to the given player.
- IsFighterAvailable( FighterEnum a_enFighter ): Returns
true if the given fighter can be used. Some fighters are just not in
the data files yet.
- DoPlayerSelect(): Allows the players to interactively
select their fighters.
The Backend has two distinct functions: providing a connection to the
embedded perl interpreter, and providing the frontend with variables
that describe a scene of the game.
The backend can
The string conversion routines are used for replays and instant
playback. There are plans to add network read/write support to the
backend which would allow OpenMortal to be played networked. This is a
feature planned for version 0.5 or later.
- Read the scene from the Perl backend.
- Read the scene from a string.
- Write the scene to a string.
Most important methods of the Backend are:
The public members of Backend describe the scene which was last read.
The meaning of these should be mostly obvious and I will not detail
them here. For more information about the scene and the working
of the perl backend, see the chapter on the perl backend.
- PerlEvalF( const char* format, ... ): Formats the string
and executes it as a perl expression.
- GetPerlString( const char* a_pcScalarName ): Retrieves the
value of a perl scalar as a string.
- GetPerlInt( const char* a_pcScalarName ): Retrieves the
value of a perl scalar as an integer.
- AdvancePerl(): Makes the perl interpreted advance from the
current scene to the next one. This should be called a constant
number of time per second to make the game running seamlessly. Most
speedup or slowdown effects are accomplished by changing the frequency
with which this method is called.
- ReadFromPerl(), ReadFromString(), WriteFromString():
Perform the input/output of the scene as described above.
3. The Backend
3.7. Fighter Data
4. The Game
5. Game AI