SimpleChar Source Code

From C4 Engine Wiki
Revision as of 21:43, 12 September 2023 by Eric Lengyel (talk | contribs) (Created page with "This page contains the complete source code for the <code>SimpleChar</code> example game module. See the Simple Games page for more information about the basic examples. == SimpleChar.h == <syntaxhighlight lang="c++"> #ifndef SimpleChar_h #define SimpleChar_h #include "C4Application.h" #include "C4World.h" #include "C4Input.h" #include "C4Cameras.h" #include "C4Interface.h" #include "C4Character.h" // Every application/game module needs to declare a function ca...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

This page contains the complete source code for the SimpleChar example game module. See the Simple Games page for more information about the basic examples.

SimpleChar.h

#ifndef SimpleChar_h
#define SimpleChar_h


#include "C4Application.h"
#include "C4World.h"
#include "C4Input.h"
#include "C4Cameras.h"
#include "C4Interface.h"
#include "C4Character.h"


// Every application/game module needs to declare a function called CreateApplication()
// exactly as follows. (It must be declared extern "C", and it must include the tag
// C4_MODULE_EXPORT.) The engine looks for this function in the DLL and calls
// it to create an instance of the subclass of the Application class that the
// application/game module defines.

extern "C"
{
	C4_MODULE_EXPORT C4::Application *CreateApplication(void);
}


namespace SimpleChar
{
	using namespace C4;


	// These are action types used to define action bindings in the
	// Input Manager. If the four-character code for an action is
	// 'abcd', then any input control (there can be more than one)
	// bound to %abcd triggers the associated action.

	enum : ActionType
	{
		kActionForward			= 'frwd',
		kActionBackward			= 'bkwd',
		kActionLeft				= 'left',
		kActionRight			= 'rght',
		kActionUp				= 'jump',
		kActionDown				= 'down',
		kActionUse				= 'fire'
	};


	// These are movement flags used by the soldier controller. They are set or cleared
	// by the Engage() and Disengage() functions in the MovementAction class.

	enum : uint32
	{
		kMovementForward		= 1 << 0,
		kMovementBackward		= 1 << 1,
		kMovementLeft			= 1 << 2,
		kMovementRight			= 1 << 3,
		kMovementUp				= 1 << 4,
		kMovementDown			= 1 << 5,
		kMovementPlanarMask		= 15
	};


	// Model types are associated with a model resource using the ModelRegistration
	// class. Models are registered with the engine in the Game constructor.

	enum : ModelType
	{
		kModelSoldier			= 'sold'
	};


	// New controller types are registered with the engine in the Game constructor.

	enum : ControllerType
	{
		kControllerSoldier		= 'sold'
	};


	// New locator types are registered with the engine in the Game constructor.
	// The 'spwn' locator is used to specify where the player should be positioned
	// when a world is loaded.

	enum : LocatorType
	{
		kLocatorSpawn			= 'spwn'
	};


	class SoldierController;


	// An Action object represents an input action that can be triggered by some
	// input control, such as a key on the keyboard or a button on a gamepad.
	// The HandleEngage() and HandleDisengage() methods are called when the button
	// is pressed and released, respectively. Actions are registered with the Input
	// Manager when the Game class is constructed.

	class MovementAction : public Action
	{
		private:

			uint32		movementFlag;

		public:

			MovementAction(ActionType type, uint32 flag);
			~MovementAction();

			void HandleEngage(void) override;
			void HandleDisengage(void) override;
	};


	class UseAction : public Action
	{
		public:

			UseAction();
			~UseAction();

			void HandleEngage(void) override;
			void HandleDisengage(void) override;
	};


	// The Interactor class is used to track player interactions with objects in the scene.

	class SoldierInteractor : public Interactor
	{
		private:

			SoldierController	*soldierController;

		public:

			SoldierInteractor(SoldierController *controller);
			~SoldierInteractor();

			void HandleInteractionEvent(EventType type, Node *node, const InteractionProperty *property, const Point3D *position) override;
	};


	// Controllers are used to control anything that moves in the world.
	// New types of controllers defined by the application/game module are
	// registered with the engine when the Game class is constructed.
	//
	// The SoldierController is used to animate the soldier. It uses the
	// built-in character controller as a base class so that the engine's
	// native physics can be used to move the character.

	class SoldierController final : public CharacterController
	{
		private:

			// These are motion states that are used to keep track
			// of which animation should be played.

			enum
			{
				kMotionNone,
				kMotionStand,
				kMotionForward,
				kMotionBackward
			};

			// The movement flags tell how the user is trying to move the player.

			uint32				movementFlags;

			// The soldier motion keeps track of what animation is currently playing.

			int32				soldierMotion;

			// The azimuth and altitude represent the direction the player is looking
			// by using the mouse.

			float				modelAzimuth;
			float				modelAltitude;

			// The frame animator controls playback of an animation resource.

			FrameAnimator		frameAnimator;

			// The previous center of mass stores the center point of the character on the
			// previous frame. This is used with the new center point to activate triggers.

			Point3D				previousCenterOfMass;

			// We keep an interactor object here in the controller.

			SoldierInteractor	soldierInteractor;

			SoldierController(const SoldierController& soldierController);

			Controller *Replicate(void) const override;

			void SetSoldierMotion(int32 motion);

		public:

			SoldierController(float azimuth);
			~SoldierController();

			Model *GetTargetNode(void) const
			{
				return (static_cast<Model *>(Controller::GetTargetNode()));
			}

			uint32 GetMovementFlags(void) const
			{
				return (movementFlags);
			}

			void SetMovementFlags(uint32 flags)
			{
				movementFlags = flags;
			}

			float GetModelAzimuth(void) const
			{
				return (modelAzimuth);
			}

			float GetModelAltitude(void) const
			{
				return (modelAltitude);
			}

			SoldierInteractor *GetSoldierInteractor(void)
			{
				return (&soldierInteractor);
			}

			void PreprocessController(void) override;
			void MoveController(void) override;
	};


	// The ChaseCamera class represents a camera that will track the player's movement.

	class ChaseCamera : public FrustumCamera
	{
		private:

			Model		*targetModel;

		public:

			ChaseCamera();
			~ChaseCamera();

			Model *GetTargetModel(void) const
			{
				return (targetModel);
			}

			void SetTargetModel(Model *model)
			{
				targetModel = model;
			}

			void MoveCamera(void) override;
	};


	// The application/game module will usually define a subclass of the World
	// class so that extra information can be associated with the current world.
	// In this case, a pointer to a spawn locator and an instance of the ChaseCamera
	// class is included with the world. A new instance of this World subclass should
	// be returned when the Game::CreateWorld() function is called (see below).

	class GameWorld : public World
	{
		private:

			const LocatorMarker		*spawnLocator;

			ChaseCamera				chaseCamera;

			static const LocatorMarker *FindSpawnLocator(const Zone *zone);

		public:

			GameWorld(const char *name);
			~GameWorld();

			const LocatorMarker *GetSpawnLocator(void) const
			{
				return (spawnLocator);
			}

			ChaseCamera *GetChaseCamera(void)
			{
				return (&chaseCamera);
			}

			ResourceResult PreprocessWorld(void) override;

			void HandlePhysicsSpaceExit(RigidBodyController *rigidBody) override;

			void DetectInteractions(void) override;
			void RenderWorld(void) override;
	};


	// Every application/game module needs to define a subclass of the Application
	// class to serve as the primary interface with the engine. This subclass is
	// created and returned to the engine in the CreateApplication() function.
	// There should be only one instance of this class, and a pointer to it is
	// declared below.

	class Game : public Application, public Global<Game>
	{
		private:

			ModelRegistration				soldierModelReg;
			LocatorRegistration				locatorReg;

			InputMgr::KeyCallback			*prevEscapeCallback;
			void							*prevEscapeCookie;

			MovementAction					*forwardAction;
			MovementAction					*backwardAction;
			MovementAction					*leftAction;
			MovementAction					*rightAction;
			MovementAction					*upAction;
			MovementAction					*downAction;
			UseAction						*useAction;

			SoldierController				*soldierController;

			static World *CreateWorld(const char *name, void *cookie);

			static void EscapeCallback(void *cookie);

		public:

			Game();
			~Game();

			SoldierController *GetSoldierController(void) const
			{
				return (soldierController);
			}

			EngineResult LoadWorld(const char *name) override;
			void UnloadWorld(void) override;
	};


	// This is a pointer to the one instance of the Game class through which
	// any other part of the application/game module can access it.

	extern Game *TheGame;
}


#endif

SimpleChar.cpp

#include "SimpleChar.h"


using namespace SimpleChar;


// This is the definition of the pointer to the Game class global.
// It should be initialized to nullptr, and its value will be set by
// the Game class constructor.

Game *SimpleChar::TheGame = nullptr;


C4::Application *CreateApplication(void)
{
	// This function should simply return a pointer to a new instance of
	// the Application class. Normally, the application/game module will
	// define a subclass of the Application class (in this case, the
	// Game class) and return a pointer to a new instance of that type.

	// This function is called exactly one time right after the
	// application/game module DLL is loaded by the engine. The returned
	// class is destroyed via the virtual destructor of the Application
	// class right before the application/game module DLL is unloaded.

	return (new Game);
}


MovementAction::MovementAction(ActionType type, uint32 flag) : Action(type)
{
	// Each instance of the MovementAction class represents a movement
	// in a single direction, as indicated by the flag parameter.
	// All of the MovementAction instances are constructed in the
	// Game class constructor.

	movementFlag = flag;
}

MovementAction::~MovementAction()
{
}

void MovementAction::HandleEngage(void)
{
	// This function is called when the input control associated with this
	// particular action is engaged (e.g., a key was pressed). We respond to
	// such an event by setting a movement flag in the soldier controller.

	SoldierController *controller = TheGame->GetSoldierController();
	if (controller)
	{
		controller->SetMovementFlags(controller->GetMovementFlags() | movementFlag);
	}
}

void MovementAction::HandleDisengage(void)
{
	// This function is called when the input control associated with this
	// particular action is disengaged (e.g., a key was released). We respond to
	// such an event by clearing a movement flag in the soldier controller.

	SoldierController *controller = TheGame->GetSoldierController();
	if (controller)
	{
		controller->SetMovementFlags(controller->GetMovementFlags() & ~movementFlag);
	}
}


UseAction::UseAction() : Action(kActionUse)
{
}

UseAction::~UseAction()
{
}

void UseAction::HandleEngage(void)
{
	// The player has pressed the fire/use button. If we are currently interacting with
	// a node in the scene and that node has a controller, then we send an activate event
	// to that controller to let it know that the player is doing something with it.

	SoldierController *controller = TheGame->GetSoldierController();
	if (controller)
	{
		const SoldierInteractor *interactor = controller->GetSoldierInteractor();
		const Node *interactionNode = interactor->GetInteractionNode();
		if (interactionNode)
		{
			Controller *interactionController = interactionNode->GetController();
			if (interactionController)
			{
				interactionController->HandleInteractionEvent(kEventInteractionActivate, &interactor->GetInteractionPosition(), controller->GetTargetNode());
			}
		}
	}
}

void UseAction::HandleDisengage(void)
{
	// The player has released the fire/use button. Let the node with which we are interacting
	// know that we are done with it by sending its controller a deactivate event.

	SoldierController *controller = TheGame->GetSoldierController();
	if (controller)
	{
		const SoldierInteractor *interactor = controller->GetSoldierInteractor();
		const Node *interactionNode = interactor->GetInteractionNode();
		if (interactionNode)
		{
			Controller *interactionController = interactionNode->GetController();
			if (interactionController)
			{
				interactionController->HandleInteractionEvent(kEventInteractionDeactivate, &interactor->GetInteractionPosition(), controller->GetTargetNode());
			}
		}
	}
}


SoldierInteractor::SoldierInteractor(SoldierController *controller)
{
	soldierController = controller;
}

SoldierInteractor::~SoldierInteractor()
{
}

void SoldierInteractor::HandleInteractionEvent(EventType type, Node *node, const InteractionProperty *property, const Point3D *position)
{
	// Always call the base class counterpart.

	Interactor::HandleInteractionEvent(type, node, property, position);

	// If the node with which we are interacting has a controller,
	// then pass the event through to that controller.

	Controller *controller = node->GetController();
	if (controller)
	{
		controller->HandleInteractionEvent(type, position);
	}
}


SoldierController::SoldierController(float azimuth) :
		CharacterController(kControllerSoldier),
		soldierInteractor(this)
{
	soldierMotion = kMotionNone;
	movementFlags = 0;

	modelAzimuth = azimuth;
	modelAltitude = 0.0F;
}

SoldierController::SoldierController(const SoldierController& soldierController) :
		CharacterController(soldierController),
		soldierInteractor(this)
{
	soldierMotion = kMotionNone;
	movementFlags = 0;

	modelAzimuth = 0.0F;
	modelAltitude = 0.0F;
}

SoldierController::~SoldierController()
{
}

Controller *SoldierController::Replicate(void) const
{
	return (new SoldierController(*this));
}

void SoldierController::PreprocessController(void)
{
	// This function is called once before the target node is ever
	// rendered or moved. The base class PreprocessController() function
	// should always be called first, and then the subclass can do whatever
	// preprocessing it needs to do.

	CharacterController::PreprocessController();

	SetRigidBodyFlags(kRigidBodyKeepAwake | kRigidBodyFixedOrientation);
	SetFrictionCoefficient(0.0F);

	// We use a frame animator to play animation resources
	// for the soldier model.

	Model *soldier = GetTargetNode();
	frameAnimator.SetTargetModel(soldier);
	soldier->SetRootAnimator(&frameAnimator);

	// Initialize the previous center of mass to the current center of mass
	// so that this doesn't contain garbage the first time we call ActivateTriggers().

	previousCenterOfMass = GetWorldCenterOfMass();

	// Register our interactor with the world.

	soldier->GetWorld()->AddInteractor(&soldierInteractor);
}

void SoldierController::MoveController(void)
{
	// This function is called once per frame to allow the controller to
	// move its target node.

	// The movementIndexTable is a 16-entry table that maps all combinations of
	// the forward, backward, left, and right movement flags to one of eight directions.
	// The direction codes are as follows:
	//
	// 0 - forward
	// 1 - backward
	// 2 - left
	// 3 - right
	// 4 - forward and left
	// 5 - forward and right
	// 6 - backward and left
	// 7 - backward and right
	//
	// The number 8 in the table means no movement, and it appears where either no
	// movement buttons are being pressed or two opposing buttons are the only ones pressed
	// (e.g., left and right pressed simultaneously cancel each other out).

	static const uint8 movementIndexTable[16] =
	{
		8, 0, 1, 8,
		2, 4, 6, 2,
		3, 5, 7, 3,
		8, 0, 1, 8
	};

	// First, we grab the mouse deltas from the Input Manager.
	// We use these to change the angles representing the direction in
	// which the player is looking/moving.

	float azm = modelAzimuth + TheInputMgr->GetMouseDeltaX();
	if (azm < -Math::tau_over_2)
	{
		azm += Math::tau;
	}
	else if (azm > Math::tau_over_2)
	{
		azm -= Math::tau;
	}

	float alt = Clamp(modelAltitude + TheInputMgr->GetMouseDeltaY(), -1.45F, 1.45F);

	modelAzimuth = azm;
	modelAltitude = alt;

	// Now, we determine whether the player is attempting to move, and
	// we play the appropriate animation on the soldier model.

	int32 motion = kMotionStand;
	Vector2D propel(0.0F, 0.0F);

	int32 index = movementIndexTable[movementFlags & kMovementPlanarMask];
	if (index < 8)
	{
		// The movementDirectionTable maps each direction code looked up in the
		// movementIndexTable to an angle measured counterclockwise from the straight
		// ahead direction in units of tau/8.

		static const float movementDirectionTable[8] =
		{
			0.0F, 4.0F, 2.0F, -2.0F, 1.0F, -1.0F, 3.0F, -3.0F
		};

		float direction = movementDirectionTable[index] * Math::tau_over_8 + modelAzimuth;

		// Set the propulsive force based on the direction of movement.

		Vector2D cs = CosSin(direction);
		propel.Set(cs.x * 100.0F, cs.y * 100.0F);

		// Determine whether we should play the forward or backward running animation.

		motion = ((index == 1) || (index >= 6)) ? kMotionBackward : kMotionForward;
	}

	// Update the external force for the rigid body representing the character.
	// The GetGroundContact() function is a member of the CharacterController base class.

	if (GetGroundContact())
	{
		SetExternalLinearResistance(Vector2D(10.0F, 10.0F));
		SetExternalForce(propel);
	}
	else
	{
		// If the soldier is not on the ground, reduce the propulsive force down to 2%.
		// This controls how well the player is able to control his movement while
		// falling through the air.

		SetExternalLinearResistance(Vector2D::zero);
		SetExternalForce(propel * 0.02F);
	}

	// Change the soldier's orientation based on horizontal mouse movement.
	// The SetCharacterOrientation() function is a member of the CharacterController base class.

	SetCharacterOrientation(modelAzimuth);

	// If the animation needs to be changed, do it.

	if (motion != soldierMotion)
	{
		SetSoldierMotion(motion);
	}

	// Activate triggers along the line connecting to the current center of mass
	// from the center of mass in the previous frame.

	Model *model = GetTargetNode();
	model->GetWorld()->ActivateTriggers(previousCenterOfMass, GetWorldCenterOfMass(), 0.25F, model);
	previousCenterOfMass = GetWorldCenterOfMass();

	// Call the Model::AnimateModel() function to update the animation playing for the model.

	GetTargetNode()->AnimateModel();
}

void SoldierController::SetSoldierMotion(int32 motion)
{
	// This function sets the animation resource corresponding to
	// the current type of motion assigned to the soldier.

	Interpolator *interpolator = frameAnimator.GetFrameInterpolator();

	if (motion == kMotionStand)
	{
		frameAnimator.SetAnimation("soldier/Stand");
		interpolator->SetMode(kInterpolatorForward | kInterpolatorLoop);
	}
	else if (motion == kMotionForward)
	{
		frameAnimator.SetAnimation("soldier/Run");
		interpolator->SetMode(kInterpolatorForward | kInterpolatorLoop);
	}
	else if (motion == kMotionBackward)
	{
		frameAnimator.SetAnimation("soldier/Backward");
		interpolator->SetMode(kInterpolatorForward | kInterpolatorLoop);
	}

	soldierMotion = motion;
}


ChaseCamera::ChaseCamera() : FrustumCamera(2.67F, 1.0F)
{
	targetModel = nullptr;
}

ChaseCamera::~ChaseCamera()
{
}

void ChaseCamera::MoveCamera(void)
{
	Model *model = GetTargetModel();
	if (model)
	{
		CollisionData	data;

		SoldierController *controller = static_cast<SoldierController *>(model->GetController());

		// Here, we calculate the local coordinate frame for the chase camera
		// based on the direction that the player is looking.

		Vector2D t = CosSin(controller->GetModelAzimuth());
		Vector2D u = CosSin(controller->GetModelAltitude());

		Vector3D view(t.x * u.x, t.y * u.x, u.y);
		Vector3D right(t.y, -t.x, 0.0F);
		Vector3D down = Cross(view, right);

		// We are going to place the camera behind the player, but we don't
		// want the camera to go through any geometry, so we'll do a quick
		// check for a collision.

		const Point3D& position = model->GetWorldPosition();
		Point3D p1(position.x, position.y, position.z + 1.5F);
		Point3D p2 = p1 - view * 4.0F;

		if (GetWorld()->DetectCollision(p1, p2, 0.3F, kCollisionCamera, &data))
		{
			// There's something in the way, so move the camera in closer
			// to the player.

			float s = data.param;
			p2 = p1 * (1.0F - s) + p2 * s;
		}

		// Set the camera's position and orientation.

		SetNodeTransform(right, down, view, p2);
	}
}


GameWorld::GameWorld(const char *name) : World(name)
{
	// This constructor is called when the Game::CreateWorld() function is
	// called to create a new world class. The world hasn't actually been loaded
	// from disk yet when we get here.

	spawnLocator = nullptr;
}

GameWorld::~GameWorld()
{
}

const LocatorMarker *GameWorld::FindSpawnLocator(const Zone *zone)
{
	// Iterate through all of the markers in the zone.

	const Marker *marker = zone->GetFirstMarker();
	while (marker)
	{
		if (marker->NodeEnabled())
		{
			MarkerType type = marker->GetMarkerType();
			if (type == kMarkerLocator)
			{
				const LocatorMarker *locator = static_cast<const LocatorMarker *>(marker);
				if (locator->GetLocatorType() == kLocatorSpawn)
				{
					return (locator);
				}
			}
		}

		// Get the next marker in the list.

		marker = marker->GetNextListElement();
	}

	// Look in all of the subzones.

	const Zone *subzone = zone->GetFirstSubzone();
	while (subzone)
	{
		const LocatorMarker *locator = FindSpawnLocator(subzone);
		if (locator)
		{
			return (locator);
		}

		subzone = subzone->ListElement<Zone>::GetNextListElement();
	}

	return (nullptr);
}

WorldResult GameWorld::PreprocessWorld(void)
{
	// The PreprocessWorld() function is called after the world has been constructed.
	// We must always call the base class PreprocessWorld() function first. If it
	// returns an error, then we just return the same result code.

	WorldResult result = World::PreprocessWorld();
	if (result != kWorldOkay)
	{
		return (result);
	}

	// The world is now completely loaded. We search for a locator node that represents the
	// player's spawn position. It has a locator type of kLocatorSpawn.

	spawnLocator = FindSpawnLocator(GetRootNode());
	return (kWorldOkay);
}

void GameWorld::HandlePhysicsSpaceExit(RigidBodyController *rigidBody)
{
	// Do nothing because this code can't handle it.
}

void GameWorld::DetectInteractions(void)
{
	// The DetectInteractions() function is called once per frame. Before calling the base
	// class's DetectInteractions() function, we set up the interaction probe to be a line
	// segment extending two meters from the players head in the direction that the
	// camera is looking.

	SoldierController *controller = TheGame->GetSoldierController();
	if (controller)
	{
		const Point3D& p = controller->GetTargetNode()->GetWorldPosition();
		Point3D position(p.x, p.y, p.z + 1.5F);

		const Vector3D& direction = chaseCamera.GetWorldTransform()[2];
		controller->GetSoldierInteractor()->SetInteractionProbe(position, position + direction * 2.0F);
	}

	// Always call the base class counterpart.

	World::DetectInteractions();
}

void GameWorld::RenderWorld(void)
{
	// This function is called once per frame to render the world.
	// The subclass may do whatever it needs to before or after rendering,
	// but at some point must call World::RenderWorld().

	World::RenderWorld();
}


Game::Game() :

		// This is the constructor for the main application/game module class.
		// This class is created by the CreateApplication() function, which is
		// called right after the application/game DLL is loaded by the engine.
		// We initialize the global pointer to the current game instance first.

		Global<Game>(TheGame),

		// A model registration represents a model that can be instanced.
		// This particular declaration associates the kModelSoldier type with the
		// model named "soldier/Soldier.mdl". The fourth parameter tells the engine
		// to precache the model resource and not to display the model in the
		// World Editor. The last parameter specifies the default controller
		// type to assign to models of type kModelSoldier.

		soldierModelReg(kModelSoldier, nullptr, "soldier/Soldier", kModelPrecache | kModelPrivate, kControllerSoldier),

		// Locator markers are registered so that the World Editor
		// can display their names in the Markers page.

		locatorReg(kLocatorSpawn, "Spawn Location")
{
	// This sets the function that is called when the user hits the
	// escape key during gameplay. We save the old function so that
	// it can be restored when the game DLL is unloaded.

	prevEscapeCallback = TheInputMgr->GetEscapeCallback();
	prevEscapeCookie = TheInputMgr->GetEscapeCookie();
	TheInputMgr->SetEscapeCallback(&EscapeCallback, this);

	// This registers our world class constructor with the World Manager.
	// We only need to do this if we have defined a subclass of the World
	// class that holds extra information.

	TheWorldMgr->SetWorldCreator(&CreateWorld);

	// These create the movement actions that are used to
	// move the player around and interact with objects.

	forwardAction = new MovementAction(kActionForward, kMovementForward);
	backwardAction = new MovementAction(kActionBackward, kMovementBackward);
	leftAction = new MovementAction(kActionLeft, kMovementLeft);
	rightAction = new MovementAction(kActionRight, kMovementRight);
	upAction = new MovementAction(kActionUp, kMovementUp);
	downAction = new MovementAction(kActionDown, kMovementDown);
	useAction = new UseAction;

	// These register our new actions with the Input Manager.

	TheInputMgr->AddAction(forwardAction);
	TheInputMgr->AddAction(backwardAction);
	TheInputMgr->AddAction(leftAction);
	TheInputMgr->AddAction(rightAction);
	TheInputMgr->AddAction(upAction);
	TheInputMgr->AddAction(downAction);
	TheInputMgr->AddAction(useAction);

	// Let the Interface Manager determine when to change input devices to gameplay mode.

	TheInterfaceMgr->SetInputManagementMode(kInputManagementAutomatic);

	soldierController = nullptr;
}

Game::~Game()
{
	// When the game DLL is about to be unloaded, this destructor is called.

	TheWorldMgr->UnloadWorld();
	TheWorldMgr->SetWorldCreator(nullptr);

	delete useAction;
	delete downAction;
	delete upAction;
	delete rightAction;
	delete leftAction;
	delete backwardAction;
	delete forwardAction;

	// Restore the previous escape key handling function.

	TheInputMgr->SetEscapeCallback(prevEscapeCallback, prevEscapeCookie);
}

World *Game::CreateWorld(const char *name, void *cookie)
{
	// This function is called when a new world is being loaded. It should
	// return a pointer to a newly constructed subclass of the World class.

	return (new GameWorld(name));
}

void Game::EscapeCallback(void *cookie)
{
	// This function is called when the user hits the escape key in gameplay
	// mode because we registered it using the InputMgr::SetEscapeCallback() function.
}

EngineResult Game::LoadWorld(const char *name)
{
	// Attempt to load the world.

	WorldResult result = TheWorldMgr->LoadWorld(name);
	if (result == kWorldOkay)
	{
		GameWorld *world = static_cast<GameWorld *>(TheWorldMgr->GetWorld());
		const LocatorMarker *locator = world->GetSpawnLocator();
		if (locator)
		{
			// If a spawn locator was found in the world, put a soldier character there.

			// The BeginSinglePlayerGame() function puts the Message Manager in single player mode.

			TheMessageMgr->BeginSinglePlayerGame();

			// Calculate the angle corresponding to the direction the character is initially facing.

			const Vector3D direction = locator->GetWorldTransform()[0];
			float azimuth = Arctan(direction.y, direction.x);

			// Load a soldier model and attach a controller to it.

			Model *model = Model::GetModel(kModelSoldier);
			SoldierController *controller = new SoldierController(azimuth);
			model->SetController(controller);
			TheGame->soldierController = controller;

			// Put the model in the world at the locator's position.

			model->SetNodePosition(locator->GetWorldPosition());
			locator->GetWorld()->AddNewNode(model);

			// Set the world's current camera to be our chase camera.
			// The world will not render without a camera being set.

			ChaseCamera *camera = world->GetChaseCamera();
			camera->SetTargetModel(model);
			world->SetWorldCamera(camera);
		}
	}

	return (result);
}

void Game::UnloadWorld(void)
{
	TheWorldMgr->UnloadWorld();

	TheMessageMgr->EndGame();

	TheGame->soldierController = nullptr;
}

See Also