Simple Game Mode Tutorial

From Hero of Allacrost Wiki
Jump to: navigation, search

Who this tutorial is for[edit]

This tutorial is for developers new to Allacrost, or anyone who doesn't know how to create an Allacrost game mode. It requires that you know how to program in C++, and that you know terms such derived, and class, and public function. It requires that you have the Allacrost source code (preferably latest svn) and that you be able to compile Allacrost. It does not require that you know how to use the Allacrost video or audio engines, although knowing how to use allacrost engines will help in future tutorials. These tutorials do not require that you know any lua.

Tutorial[edit]

This tutorial teaches you how to make a simple Allacrost "mode". Example modes include boot mode, map mode, and battle mode. Modes help us to modularize Allacrost's code. At any given time during Allacrost's execution, a certain mode is active.

Modes are defined in C++ classes. Each mode is a derived class of GameMode. Every mode must implement the public functions Update(), Draw(), and Reset(). Update() updates the mode, and Draw() draws to the screen. Each of these functions are called once per frame. Reset() sets the mode to an initial, sane state. It is only called by the mode manager once, when it is first created. If you need to reset your mode after it is already initialized and running, you can call Reset() again from within your mode.

In this tutorial, you will create a dummy mode. It is only a few dozen lines of code, and you will make some minor modifications to mode_manager.h, boot.cpp and Makefile.am. This mode will not display anything. It will not do anything except exit to boot mode when you press a key. You will activate it by pressing "New Game" at the boot menu.

First, here is the code for the dummy mode. Place this in your allacrost source directory, in src/modes/mode_tutorial1.cpp.

///////////////////////////////////////////////////////////////////////////////
//            Copyright (C) 2004-2007 by The Allacrost Project
//                         All Rights Reserved
//
// This code is licensed under the GNU GPL version 2. It is free software
// and you may modify it and/or redistribute it under the terms of this license.
// See http://www.gnu.org/copyleft/gpl.html for details.
///////////////////////////////////////////////////////////////////////////////

/*!****************************************************************************
 * \file    mode_tutorial1.cpp
 * \author  Winter Knight, winterknight@nerdshack.com
 * \brief   Source file for Tutorial1Mode interface
 *****************************************************************************/


#include "mode_tutorial1.h"

#include "utils.h"				// For debugging info
#include "input.h"				// To catch keypresses
#include "boot.h"				// To create a new mode when this one expires

using namespace hoa_mode_manager;
using namespace hoa_video;
using namespace hoa_input;
using namespace hoa_system;
using namespace hoa_boot;

namespace hoa_tutorial1 {

Tutorial1Mode::Tutorial1Mode() {
	IF_PRINT_DEBUG(MODE_MANAGER_DEBUG) << "Tutorial1Mode constructor invoked." << std::endl;
}

Tutorial1Mode::~Tutorial1Mode() {
	IF_PRINT_DEBUG(MODE_MANAGER_DEBUG) << "Tutorial1Mode destructor invoked." << std::endl;
}

void Tutorial1Mode::Reset() {
	// Good place to reset
}

void Tutorial1Mode::Update() {
	if (InputManager->ConfirmPress() || InputManager->CancelPress()) {
		ModeManager->Pop();
		BootMode * mode = new BootMode();
		ModeManager->Push(mode);
	}
}

void Tutorial1Mode::Draw() {
	// Drawing code goes here.
}

} // namespace hoa_tutorial1

And here is the code for mode_tutorial1.h. Place it in the same directory as mode_tutorial1.cpp

////////////////////////////////////////////////////////////////////////////////
//            Copyright (C) 2004-2007 by The Allacrost Project
//                         All Rights Reserved
//
// This code is licensed under the GNU GPL version 2. It is free software 
// and you may modify it and/or redistribute it under the terms of this license.
// See http://www.gnu.org/copyleft/gpl.html for details.
////////////////////////////////////////////////////////////////////////////////

/** ****************************************************************************
*** \file    mode_tutorial1.h
*** \author  Winter Knight, winterknight@nerdshack.com
*** \brief   Header file for Tutorial1Mode interface.
***
*** This is a dummy mode. It doesn't do much.
***
*** \note Enter extra notes here.
*** ***************************************************************************/

#ifndef __MODE_TUTORIAL1_HEADER__
#define __MODE_TUTORIAL1_HEADER__

#include "mode_manager.h"


//! All calls to tutorial1 mode are wrapped in this namespace.
namespace hoa_tutorial1 {

//! public consts related to this mode go here.

//! An internal namespace to be used only within the tutorial1 code. Don't use this namespace anywhere else!
namespace private_tutorial1 {
	
//! Private tutorial1 specific consts and such.
}

class Tutorial1Mode : public hoa_mode_manager::GameMode {
public:
	Tutorial1Mode();
	~Tutorial1Mode();

	void Reset();
	void Update();
	void Draw();
};

} // namespace hoa_tutorial1

#endif

In order to get this code to compile when you run "make", you need to add these files to the "modes_SOURCES" list in Makefile.am. The tail of this list should look something like this:

	$(MODES_DIR)/scene.cpp \
	$(MODES_DIR)/scene.h \
	$(MODES_DIR)/shop/shop.h \
	$(MODES_DIR)/shop/shop.cpp \
	$(MODES_DIR)/shop/shop_windows.h \
	$(MODES_DIR)/shop/shop_windows.cpp \
	$(MODES_DIR)/mode_tutorial1.cpp \
	$(MODES_DIR)/mode_tutorial1.h

Now, add our mode to the list of modes in mode_manager.h. The complete list, after you add our dummy mode, looks like this:

const uint8 MODE_MANAGER_DUMMY_MODE  = 0;
const uint8 MODE_MANAGER_BOOT_MODE   = 1;
const uint8 MODE_MANAGER_MAP_MODE    = 2;
const uint8 MODE_MANAGER_BATTLE_MODE = 3;
const uint8 MODE_MANAGER_MENU_MODE   = 4;
const uint8 MODE_MANAGER_SHOP_MODE   = 5;
const uint8 MODE_MANAGER_PAUSE_MODE  = 6;
const uint8 MODE_MANAGER_QUIT_MODE   = 7;
const uint8 MODE_MANAGER_SCENE_MODE  = 8;
const uint8 MODE_MANAGER_WORLD_MODE  = 9;
const uint8 MODE_MANAGER_TUTORIAL1_MODE = 99;

Now, we need to edit boot.cpp. Normally, when you press "New Game" at the boot menu, allacrost activates map mode. We are going to change this to active our new dummy mode instead. We are going to change two lines in BootMode::Update(). Modify the try block to look like so:

			try {
//				MapMode *MM = new MapMode(MakeStandardString(GlobalManager->GetLocationName()));
				hoa_tutorial1::Tutorial1Mode *MM = new hoa_tutorial1::Tutorial1Mode();
				ModeManager->Push(MM);
			} catch (luabind::error e) {

Now, at the top of boot.cpp, replace the #include "map.h" line with #include "mode_tutorial1.h":

#include "boot.h"
// #include "map.h"
#include "mode_tutorial1.h"

That is all the code we have to add and change. To test out your new mode, go to the allacrost source root directory and run: autoreconf -i
./configure
make

and then run ./allacrost . Click on "New Game". You should see a blank screen. Now press confirm or cancel (usually "f" and "d") and you should be back at the boot menu.

Explanation[edit]

mode_tutorial1.h[edit]

////////////////////////////////////////////////////////////////////////////////
//            Copyright (C) 2004-2007 by The Allacrost Project
//                         All Rights Reserved
//
// This code is licensed under the GNU GPL version 2. It is free software 
// and you may modify it and/or redistribute it under the terms of this license.
// See http://www.gnu.org/copyleft/gpl.html for details.
////////////////////////////////////////////////////////////////////////////////

/** ****************************************************************************
*** \file    mode_tutorial1.h
*** \author  Winter Knight, winterknight@nerdshack.com
*** \brief   Header file for Tutorial1Mode interface.
***
*** This is a dummy mode. It doesn't do much.
***
*** \note Enter extra notes here.
*** ***************************************************************************/

This is the standard header that must be placed at the top of each file in Allacrost code. See Code_Standard for more info.

#ifndef __MODE_TUTORIAL1_HEADER__
#define __MODE_TUTORIAL1_HEADER__

#include "mode_manager.h"


//! All calls to tutorial1 mode are wrapped in this namespace.
namespace hoa_tutorial1 {

//! public consts related to this mode go here.

The #ifndef #define is standard for headers in c and c++. It prevents the code from this header file from being included more than once in the same object code module, thereby preventing duplicate declarations. We #include "mode_manager.h" in every mode. We have to, because that is where GameMode is defined. Every mode derives from GameMode.

Notice that this mode is wrapped in namespace, called hoa_tutorial1. Almost all of Allacrost's code is wrapped in one namespace or another. It helps to keep modules separated. All of Allacrost's namespaces begin with hoa_, except for private ones.

//! An internal namespace to be used only within the tutorial1 code. Don't use this namespace anywhere else!
namespace private_tutorial1 {
	
//! Private tutorial1 specific consts and such.
}

Here we define a private namespace, private_tutorial1. This is the place for consts that are specific to tutorial1 code, and are not used anywhere else in the entire code base. This example has no such consts, and so the private namespace is empty. The private namespace is not required. It is only included here as an example.

class Tutorial1Mode : public hoa_mode_manager::GameMode {
public:
	Tutorial1Mode();
	~Tutorial1Mode();

	void Reset();
	void Update();
	void Draw();
};

This is the definition of the Tutorial1Mode class. It derives from hoa_mode_manager::GameMode. It declares the bare minimum functions for a class that derives from GameMode.

mode_tutorial1.cpp[edit]

mode_tutorial1.cpp, like mode_tutorial1.h, includes a header that declares this file to be GPL, and a brief description. Again, see Code_Standard if you want to learn more.

#include "mode_tutorial1.h"

#include "utils.h"				// For debugging info
#include "input.h"				// To catch keypresses
#include "boot.h"				// To create a new mode when this one expires

using namespace hoa_mode_manager;
using namespace hoa_video;
using namespace hoa_input;
using namespace hoa_system;
using namespace hoa_boot;

After the initial file header, we include the header files that we are going to use in this file. We then declare the namespaces we intend to use. Note that the using namespace directive is only allowed in cpp files, it is not allowed in header files. There is no requirement for when to use "using" in a cpp file, and when to specify the namespace explicitly when you use code from that namespace. Although we include five namespaces, we do not use hoa_video or hoa_system in this example. But we will in the next.

namespace hoa_tutorial1 {

Tutorial1Mode::Tutorial1Mode() {
	IF_PRINT_DEBUG(MODE_MANAGER_DEBUG) << "Tutorial1Mode constructor invoked." << std::endl;
}

Tutorial1Mode::~Tutorial1Mode() {
	IF_PRINT_DEBUG(MODE_MANAGER_DEBUG) << "Tutorial1Mode destructor invoked." << std::endl;
}

This is a simple constructor and destructor. It uses macros from utils.h to print to the screen when this mode is created and destroyed. To see these messages, start Allacrost with the command arguments "-d mode_manager". Note that to see debugging info when other modes are created or destroyed, you have to include on the command line "-d map", or "-d boot", depending on the mode you wish to see created and destroyed.

Also note that this code is in the hoa_tutorial1 namespace.

void Tutorial1Mode::Reset() {
	// Good place to reset
}

void Tutorial1Mode::Update() {
	if (InputManager->ConfirmPress() || InputManager->CancelPress()) {
		ModeManager->Pop();
		BootMode * mode = new BootMode();
		ModeManager->Push(mode);
	}
}

void Tutorial1Mode::Draw() {
	// Drawing code goes here.
}

These are the definitions of the required functions. Reset() and Draw() are both empty. Update() is called every frame (usually several dozen times per second), and it checks to see if the player has pressed the confirm or cancel keys (via the Allacrost InputManager), and if they have, it exits from this mode and starts boot mode.

InputManager is a singleton GameInput *. GameInput is declared in input.h. See The Quick Start Guide for more info on Allacrost singletons. The InputManager, and the <key>Press() functions will be covered more thoroughly in the next tutorial.

If the player presses either the confirm or cancel keys, then this mode will exit. Specifically, we pop the tutorial mode off the stack, create a new BootMode, and then push the BootMode onto the stack.

There are a few things to note about pushing and popping modes from the stack. First, we don't have to pop the current mode in order to push a new one. It is truly a stack, and can hold as many modes as we need. It can hold more than one of the same mode. When Tutorial1 is the active mode, it is the only mode. This is because BootMode pops itself off the stack before activating Tutorial1 mode. This is to save memory. There is no reason to keep BootMode in memory while playing a long game. That is why we have to create another one when exiting this mode. If we do not create a boot mode, then the stack will be empty and Allacrost will abort.

mode_type[edit]

At the top of mode_manager.h, we included this line:

const uint8 MODE_MANAGER_TUTORIAL1_MODE = 99;

We never used it, but this is supposed to be assigned to the mode_type variable (a member of every GameMode class), so that other parts of Allacrost code can get the type of any mode by calling ModeManager->GetGameType(). It is actually not used anywhere, anymore, except in GameModeManager::DEBUG_PrintStack().

That is the end of this tutorial. The next tutorial will build on this one, and include calls to the video and audio engine, to create a small light show with music.