Game Logic (src/game-implementation
)¶
As said, on its own the game engine doesn’t do anything but provide you a set of concepts. To put these concepts to use, you will need the following:
- Define game objects with their actions and results
- A way for the frontend to execute specific actions on specific game objects
- Convert the results of these actions into something the frontend understands
- A way to track what the user has been doing along the way
This is where the files under src/game-implementation
come into play.
One key file is the services/gameService.ts
. This file tells the game engine:
- Which game objects and actions actually exist in the game world by using
registerGameObject
andregisterAction
in the constructor. - How user progression will be stored by providing a
PlayerSession
-type.
Only one instance of the GameService
will be created whenever the backend application starts. This happens in the global.ts
file.
Next there is routes.ts
, which exposes two API endpoints: /state
and /action
. These are respectively handled by the functions handleStateRequest
and handleActionRequest
found in controllers/GameController.ts
.
From a Single Responsibility perspective, handleStateRequest
and handleActionRequest
will only extract data from the request and return a response. The actual logic is performed by the executeAction
function, which is where the actual game engine is put to work:
-
It will determine on which game object to execute the requested action. It does this by converting the alias of an game object into an actual instance. This conversion is handled by the
getGameObjectsByAliases
function found on theGameService
. If no game object is provided, it will use the room the user is currently in based on itsPlayerSession
. -
It will then use the
executeAction
function found on theGameService
to execute the requested action on the game object. To do this, theexecuteAction
will determine which Action-class is reponsible for handling the requested action, instance it and call the appropriatehandle
function. Thehandle
function will then make sure the game object actually supports the action. If it does, it will be executed and itsActionResult
returned. -
As a last step, the action result has to be converted to a
GameState
, which is used to instruct the frontend what happened after executing an action. For this, theconvertActionResultToGameState
function is used. In this function, the type ofActionResult
determines what kind ofGameState
is returned.For example: The game implementation comes with an additional
ActionResult
:SwitchPageActionResult
. ThisActionResult
can be used to instruct theRootComponent
on the frontend to switch to other “pages”. If this action result is used, it will return a different type ofGameState
.
Class Diagram¶
Just an impression
This class diagram gives an impression of how all the different classes link together, but is not a complete representation.
---
config:
class:
hideEmptyMembersBox: true
---
classDiagram
direction LR
class GameObject {
<<abstract>>
+get alias() string
+name() SyncOrAsync<string>
}
class Room {
<<abstract>>
images() SyncOrAsync<string[]>
actions() SyncOrAsync<Action[]>
objects() SyncOrAsync<GameObject[]>
}
class Item {
<<abstract>>
}
class Character {
<<abstract>>
}
class Examine {
<<interace>>
+examine() ActionResult | undefined*
}
class Talk {
<<interace>>
+talk(choiceId?: number) ActionResult | undefined*
}
class Simple {
<<interace>>
+simple(alias: string) ActionResult | undefined*
}
class ActionResult {
<<abstract>>
}
class TextActionResult {
+get text() string[]
}
class TalkActionResult {
+get character() Character
+get choices() TalkChoice[]
}
class TalkChoice {
+get id() number
+get text() string
}
class SwitchPageActionResult {
+get page() string
}
class BaseGameService~T~ {
<<abstract>>
#registerGameObject(gameObjectClass: GameObjectClass) void
#registerAction(actionClass: ActionClass) void
+createPlayerSessionMiddleware() ExpressMiddleware
+createNewPlayerSession() T*
+getPlayerSession() T
+resetPlayerSession() void
+getGameObjectsByAliases(aliases: string[]) GameObject[]
+getGameObjectByAlias(alias: string) GameObject | undefined
+executeAction(alias: string, gameObjects: GameObject[]) SyncOrAsync<ActionResult | undefined>
}
class GameService {
+getGameObjectsFromInventory() GameObject[]
}
class PlayerSession {
<<type>>
+currentRoom string
+inventory string[]
}
class Action {
<<abstract>>
+get alias() string
+name() SyncOrAsync<string>*
+get needsObject() boolean
+execute(alias: string, gameObjects: GameObject[]) ActionResult | undefined*
}
class ExamineAction {
+readonly Alias: string = "examine"$
}
class TalkAction {
+readonly Alias: string = "talk"$
}
class SimpleAction {
+execute(alias: string, gameObject: GameObject) ActionResult | undefined$
}
class GameController {
+handleStateRequest(_: Request, res: Response) Promise<void>
+handleActionRequest(req: Request, res: Response) Promise<void>
+executeAction(actionAlias: string, gameObjectAliases? string[]): Promise<GameState | undefined>
+convertActionResultToGameState(actionResult?: ActionResult) Promise<GameState | undefined>
-convertTalkChoiceToReference(action: TalkActionResult, choice: TalkChoice) ActionReference
-convertActionToReference(action: Action) Promise<ActionReference>
-convertGameObjectToReference(gameObject: GameObject) Promise<GameObjectReference>
}
class DefaultGameState {
<<type>>
+type : "default"
+roomAlias : string
+roomName : string
+roomImages : string[]
+text : string[]
+actions : ActionReference[]
+objects : GameObjectReference[]
}
class ActionReference {
<<type>>
+alias : string
+name : string
+needsObject : boolean
}
class GameObjectReference {
<<type>>
+alias : string
+name : string
}
Room--|>GameObject
Room..|>Examine
Item--|>GameObject
Character--|>GameObject
Character..|>Talk
ExamineAction--|>Action
TalkAction--|>Action
SimpleAction--|>Action
TextActionResult--|>ActionResult
TalkActionResult--|>TextActionResult
SwitchPageActionResult--|>ActionResult
GameService--|>BaseGameService
GameController--GameService
GameController--DefaultGameState
GameService--Action
GameService--PlayerSession
Room--Action
Room--GameObject
DefaultGameState--ActionReference
DefaultGameState--GameObjectReference
TalkActionResult--TalkChoice
ExamineAction--Examine
TalkAction--Talk
SimpleAction--Simple