SimpleChar Source Code: Difference between revisions

From C4 Engine Wiki
Jump to navigation Jump to search
(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...")
 
(No difference)

Latest revision as of 21:43, 12 September 2023

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