Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Dungeon of Despair: A scripted demo game using Conscript

0.00/5 (No votes)
5 Sep 2008 2  
This article presents a demo script-driven game using Conscript, a scripting engine presented in an earlier article

Screenshot - DOD01.jpg

Screenshot - DOD11.jpg

Inset screenshots are from Lockdown [Alex Varanese, 2002, Game Scripting Mastery].

Introduction

This article presents Dungeon of Despair (DOD), a complete game application intended as a demonstration of the Conscript scripting engine presented in an earlier article. The game is developed as a borderless Windows application using GDI+ to render graphics.

Inspiration

DOD is inspired from Lockdown, a game developed by Alex Varanese as a demo for the Xtremescript scripting engine developed in his book, Game Scripting Mastery. Screenshots for Lockdown are provided inset within the above screenshot. In Lockdown, you are a droid trapped in a station of some sort and you must locate four colour-coded keys, placing them in a central control room to unlock the station and escape to safety. The player sees the station one room at a time, with each room leading to one or more rooms via doorways. Protecting the keys and control rooms are enemy droids with varying levels of intelligence, some of which can not only harm the player on touch but also fire at the player in some cases. The player can fire back and can rest to restore the droid's health meter. The game ends if the player looses all his or her health or when the station is unlocked. Lockdown illustrates the application of scripting by implementing ambient effects -- such as flickering lighting -- and enemy droid A.I. as scripts.

The gameplay mechanics for DOD are very similar to Lockdown, albeit within a fantasy setting inspired from classic 2D RPGs. In DOD, the player is a mage trapped in a magical dungeon from which escape is only possible via the activation of a magic portal. The portal in turn can only be activated by locating four elemental vials and placing them on the portal. The player must, however, face a number of enemies, the type of which determines the overall intelligence and hence threat level. The player looses health on touching enemies or being hit by enemy spells. Health is represented as a coloured health meter at the top left of the screen. Four icons on the top right of the screen represent an inventory of vials collected by the player.

Screenshot - DOD02.jpg

Screenshot - DOD07.jpg

Application of scripting

Like Lockdown, DOD is a game application complex enough to warrant the use of scripting while still being limited in scope as an illustrative example. DOD employs Conscript scripts primarily to implement enemy A.I. There is no specific scripting for ambient effects. However, the dungeon layout and its contents are defined by a script-defined data structure. The game also illustrates a simple script-driven cutscene system, whereby entry to each room triggers an optional cutscene script function to help immerse the player in the game.

Artwork

Background art was generated from a number of (heavily!) processed photographs. The character sprites and dialogue face sets were generated using the online RPG sprite generator found here.

Game Instructions

To run the game, compile the attached solution and execute the resulting binary. On execution, the game application displays a combined title/menu screen where the user can select to start a game session or exit the application. Menu item selection may be performed via the cursor keys and the space bar or return key. Once a game session is started, the user may move the player around using the cursor keys and fire spells using the space bar. During cutscenes, player controls are temporarily disabled. The user may quit the game session and return to the title screen any time by hitting the Esc key. Game Over screens may be exited by hitting the space bar or return key.

Background

Embedded scripting engines have proved to be a valuable tool in game production. They empower the game designers to build content for the game without requiring the intervention of the game programmers who, in turn, are free to concentrate on other, more technical aspects of game development. Another benefit is that production turn-around times are substantially reduced as designers can alter game content without requiring new builds of the game application. Scripting engines are also the cornerstone of "moddable" games that encourage communities to extend the longevity of games via user-created content.

Implementation

The game application follows an OO design paradigm where the game's main components are represented by objects that interact with each other. The application uses a borderless main window, rendering graphics using GDI+ and reading player input via keyboard events. The main states of the game -- that is, the title screen, play sessions, game over screen, etc. -- are implemented via game state classes deriving from a common GameState class. The application maintains a reference to the currently active state class that in turn handles its own behaviour. Each state may also request a transition to a new state, such as when starting a new game from the title screen or ending a play session and displaying the Game Over screen.

Dungeon Representation

The game world, represented by a Dungeon class, consists of a collection of interconnected rooms represented by a Room class. Each room has links to one or more other rooms via a Doors class instance that maintains room links for the four main compass points. Additional room properties provide decorative features to introduce some variety in the rooms, such as flickering torches placed around the room or a circular pedestal, typically placed in junction rooms. One room in particular contains the elemental portal, and this is represented by a room reference in the Dungeon class. Similar references are also used for the starting room and the placement of the four elemental vials around the dungeon.

Screenshot - DOD09.jpg

The dungeon data is defined in a Conscript script Dungeon.cns as a multi-level associative array with a structure similar to the Dungeon, Room and Doors instance hierarchy. The script is loaded and executed on switching to the play session state. The resulting structure is extracted from the script's variable dictionary and processed to construct the dungeon layout. The following is an excerpt from the dungeon layout script:

var s_dungeon;

function main()
{
    var rooms = {};

    // Room 00

    rooms.R00 = {};
    rooms.R00.Pedestal = true;
    rooms.R00.Doors = {};
    rooms.R00.Doors.East = "R10";
    rooms.R00.Doors.South = "R01";
    rooms.R00.TorchNE = true;
    rooms.R00.TorchSW = true;
    rooms.R00.Enemies = {};
    rooms.R00.Enemies.Enemy0 = {};
    rooms.R00.Enemies.Enemy0.Type = "Wizard";
    rooms.R00.Enemies.Enemy0.Quantity = 1;
    rooms.R00.Enemies.Enemy1 = {};
    rooms.R00.Enemies.Enemy1.Type = "Pig";
    rooms.R00.Enemies.Enemy1.Quantity = 1;
    rooms.R00.Enemies.Enemy2 = {};
    rooms.R00.Enemies.Enemy2.Type = "Lizard";
    rooms.R00.Enemies.Enemy2.Quantity = 1;

    // Room 10

    rooms.R10 = {};
    rooms.R10.Doors = {};
    rooms.R10.Doors.West = "R00";
    rooms.R10.Doors.East = "R20";
    rooms.R10.TorchNE = true;
    rooms.R10.TorchSE = true;
    rooms.R10.Enemies = {};
    rooms.R10.Enemies.Enemy0 = {};
    rooms.R10.Enemies.Enemy0.Type = "Wizard";
        rooms.R10.Enemies.Enemy0.Quantity = 1;
    rooms.R10.Enemies.Enemy1 = {};
    rooms.R10.Enemies.Enemy1.Type = "Pig";
    rooms.R10.Enemies.Enemy1.Quantity = 2;

    // more rooms here..

    // :

    // :


    // dungeon top-level array

    s_dungeon = {};

    // uncomment for infinite health

    //s_dungeon.Cheat = true;


    s_dungeon.Rooms = rooms;

    // define starting room and portal room

    s_dungeon.StartingRoom = "R34";
    s_dungeon.PortalRoom = "R30";

    // define vial rooms

    s_dungeon.RedVialRoom    = "R20";
    s_dungeon.GreenVialRoom  = "R40";
    s_dungeon.YellowVialRoom = "R44";
    s_dungeon.BlueVialRoom   = "R24";
}

Entities

The player, enemies and projectiles all derive from a common Entity class that provides basic services such as placement and movement. The player is represented by a Player class that contains properties for the player's health and flags for possession of the vials. Each enemy entity is represented by an Enemy class that includes properties for the enemy's health, a ScriptContext instance referencing an appropriate A.I. script for the enemy and a speed property for the enemy's rate of movement.

Screenshot - DOD04.jpg

Screenshot - DOD05.jpg

Each enemy script is designed to execute in parallel with the rest of the application and hence consists of a main loop in which the environment is sensed and appropriate actions are taken. In order to drive the enemy instance associated with the script context, a number of host functions are registered, with the enemy instance assigned as the function handler. The following is the list of enemy A.I. host functions:

int GetRandom(int iMin, int iMax)
returns a random integer in a given range
Move(int iX, int iY)
moves the enemy to the given coordinates
SetDirection(int iDirection)
turns the enemy towards the given direction
SetAutomaticDirection(bool bFlag)
enables/disables automatic direction based on velocity
{"X":int, "Y":int} GetPosition()
gets the enemy's coordinates
{"X":int, "Y":int} GetPlayerPosition()
gets the player's coordinates
CastSpell()
casts a spell in the direction faced by the enemy

Cutscenes

Whenever the player enters a room, the game application checks if a cutscene is available, in which case, control of the player is temporarily relinquished from the user while the cutscene is played. Cutscenes are implemented as script-defined functions in a script file Cutscenes.cns. The application locates the function on the assumption that the function's name is of the format Cutscene_RNN where RNN is the room identifier. The cutscene function consists of a sequence of host function calls that control the character interactions during the scene. The following script function defines the cutscene for the central room R32. This particular scene encourages the player to explore the North exit:

// centre cut-scene

function Cutscene_R32()
{
    // do scene only first time round

    if (s_canSeeLight != null) return;
    s_canSeeLight = true;

    // disable player control

    Player_SetActive(false);

    // move uo to room centre and talk

    Player_MoveTo(320, 160);
    while(Player_Moving()) yield;
    Player_Say("I can see strange lights coming beyond this doorway!");
    while(TextWindow_IsActive()) yield;

    // move closer to door and talk some more

    Player_MoveTo(320, 170);
    Player_Say("Maybe I should have a look...");

    // return control to player

    Player_SetActive(true);
}

Screenshot - DOD03.jpg

Screenshot - DOD06.jpg

To support scripted cutscenes, the following host functions are defined in the game application:

Player_SetActive(bool bFlag)
enables/disables player control
Player_SetPosition(int iX, int iY)
sets the player's position
Player_MoveTo(int iX, int iY)
requests the player to move to the given position
bool Player_Moving()
returns true if the player is moving
Player_Say(String strMessage)
makes the player say something in the chat window
bool Player_HasAllVials()
returns true if the player has collected all four vials
Boss_SetActive(bool bFlag)
shows/hides the enemy boss
Boss_SetPosition(int iX, int iY)
sets the boss' position
Boss_MoveTo(int iX, int iY)
requests the enemy boss to move to the given position
bool Boss_Moving()
returns true if the enemy boss is moving
Boss_Say(String strMessage)
makes the boss say something in the chat window
Boss_SpawnEnemy(String strType)
spawns an enemy of the given type
bool TextWindow_IsActive()
returns true if the chat window is still open

Points of Interest

Conceptually, DOD is a very simple game that could have easily been developed without the use of embedded scripting. However, thanks to scripting, it is possible to not only alter the dungeon layout, but also the A.I. behaviour of the enemies and the game's overall plotline. Handing more power to the scripting system increases the "moddability" of the game. In the next article, I will present a complete IDE application for Conscript that demonstrates the use of the Conscript API in a more sophisticated way, such as the provision of debugging functionality and the implementation of a host function module plug-in mechanism.

Screenshot - DOD08.jpg

Screenshot - DOD10.jpg

Related Articles

  • EezeeScript: A simple embeddable scripting language for .NET
  • Conscript: An embeddable, compiled scripting language for .NET
  • Conscript IDE: An Integrated Development Environment (IDE) implementation for the Conscript scripting language.

History

  • 20/Jul/2007 - First version released

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here