-
Notifications
You must be signed in to change notification settings - Fork 0
X‐TADS ‐ An XML Driven Text Adventure Development System
Welcome to the XML Text Adventure development system!
I’ve used a few of these in the past, early ones for the old ZX Spectrum home computer back in the 80s – I remember The Quill and Graphic Adventure Creator! I owe large debt of gratitude to these creations (and indeed the many text adventures I played on those platforms way back when) for the inspiration to write something like this.
The system is written in the Java programming language. It is my intention to make it extensible but for now I may just document a few of the key classes. I’ll relegate that to the appendices for later.
So let’s start with the basics. Are you familiar with XML? Probably not but it’s a simple system, you have HTML-like tags with attributes except with XML you can make up your own tags, so they can be pretty much anything you like, they don’t have to describe a web page.
There’s no programming in this system; everything can be described using various files with some XML. I will endeavour to show you what the system expects in terms of room descriptions, items, tasks, events and non-player characters (referred to throughout this document as NPCs).
All you need to create your adventure is an idea for a game!
This guide is not intended to be a tutorial on XML, suffice to say XML
is a flexible markup that is pretty good for defining structured data,
particularly text data. And any text adventure requires a decent amount
of data – think of all the locations you visit, objects and characters
you can interact with, events that occur and all the other things that
you can do in these immersive worlds.
All this is data so we need to be able to define this data in a sensible
and structured way.
https://aws.amazon.com/what-is/xml/ looks like a reasonable introduction.
All the game files need to follow the syntax rules briefly described here.
XML allows you to define “elements” also referred to as “tags” that may have “attributes” and use these as a simple markup. The terms ‘tag’ and ‘element’ do have specific meanings but are often used interchangeably.
Elements are just names surrounded by angle brackets, e.g. <room>. No spaces are allowed in these names so you can’t have <living room> as an element but you could have <living-room> (with a hyphen).
Elements can contain attributes. Attributes sit within the tag as a
name=”value” pair; value is always in quotes. The attribute name is
separated from the element name by a space hence why you can’t have a
space in the element name, that would be interpreted as an attribute.
For example:
<room id=”1">room is the element name and id is an attribute.
There can be as many attributes as you like, e.g.
<room id=”1” name=”Living room” purpose=”relaxing”>Every element must be closed with a closing tag so <room id=”1”> is closed with </room> or if there’s nothing else inside it (you can have elements within elements, see below) then you can use the shortcut slash at the end like this:
<room id=”1”/>An element can contain what is called character data or CDATA for short, this is just some text that lives between the open and close tag. For example:
<room id=”1” name=”Living room”>Description of room can go here.</room>Elements can be nested (elements within elements) so
<garden>
<furniture>Table</furniture>
</garden>is valid and you can have multiples of the same element inside another element e.g.
<garden>
<furniture>Table</furniture>
<furniture>Deckchair</furniture>
</garden>You can have multiple of the same element inside another element and/or multiple different elements inside one another.
In fact every element in the whole document must be nested inside a ‘root’ element. So for example in a rooms.xml definition file we have:
<rooms>
<room id=”1” name=”Living room”>… </room>
... more rooms ...
</rooms>One restriction is elements can’t have both text and other elements inside so:
<garden>Description of garden
<furniture>Table</furniture>
<furniture>Deckchair</furniture>
</garden>is not valid in this system. When we need both we just make another element containing the text, e.g. a <description> inner element.
(Technically, this kind of thing is legal in XML generally but the JAXB system I'm using to parse it doesn’t support this and to be fair, we don’t need it so it’s OK).
Lastly, you may wonder what happens if you need to use < or > in your
CDATA text or " (quotes) in in an attribute value?
The answer is something called an entity reference. Sounds mysterious and
complicated but all it is is replacing those characters with a small
alternative sequence of characters that mean the same thing. There are only
five of these as follows:
| Entity | Meaning | |
|---|---|---|
| < | < | less than |
| > | > | greater than |
| & | & | ampersand |
| ' | ' | apostrophe |
| " | " | quotation mark |
Just use for example > wherever you want want > to appear in text.
See further down when we describe some verb definitions.
So that’s pretty much all there is to it.
One last post script, you will notice every file starts with the following line:
<?xml version="1.0" encoding="UTF-8"?>Nobody knows why anymore... we just do it. Well, OK – it defines the
character encoding the XML parser uses; UTF-8 but I can’t remember ever
using anything else.
There’s no real need to go into it here but UTF-8 is a character
encoding computers understand that includes loads and loads of different
characters such as Greek and Arabic letters, mathematical symbols,
basically everything you’ve ever seen written down.
You'll never need to change the version number.
XML is not a programming language so don’t panic if you’re not a
programmer. It’s just a convenient and flexible way to describe data.
If you make a mistake when typing something in the game will show an
error when you try to start it up and often it will give you a line number
and position where the error is.
There will be plenty of examples as we go along.
There are a standard set of data files expected for your adventure, each one has a .xml suffix. All of these files can be split across multiple files of the same type. Multiple rooms_ files, items files etc.
The files are:\
game.xml – This file gives you startup conditions, the game clock
definition, wallet and currencies, the player - what items are initially being
carried, hit points, etc. Basically all your initial game state.
You can also set properties and counts here if you wish.
The last thing the game.xml file does is give you the definition of every verb the
engine understands which declares its implementation class.
The implementation class is the part of the program behind the scenes that actually
performs the action. LOOK, GET, DROP, EXAMINE, etc.
You don't need to know anything about how these classes work, just that the
verbs defined in the game.xml file use them and it's important to get the spelling
exactly right including capitalisation if you define any new verbs.
There’s nothing stopping you defining multiple game.xml files but there’s no need. If you do, you run the risk of later files overwriting information defined earlier.
A full discussion of game.xml is given in the appendices.
The rest of the files the system expects are as follows:
rooms_xxx.xml – descriptions for every room. There can be multiple of these files all starting with the prefix ‘rooms’ and ending with .xml so you can group rooms logically into areas of your map.
items.xml – all the items that can be found or interacted with. Again items can be split among multiple files with name starting items and having a .xml file type.
synonyms.xml – supplies synonyms for nouns although arguably a better way to achieve this is just to define empty items with the synonym you want as its name. Swings and roundabouts. There are advantages and disadvantages to each approach.
Then after that we have the files that essentially define puzzles and interactions.
events_xxx.xml – events that can happen either as a consequence of player actions or they are just things that happen randomly in the background. These are split into multiple files so you can group them logically, each file shout be prefixed ‘events’ and ending with extension .xml
tasks_xxx.xml – tasks are essentially the puzzles you solve. Again we can split these either logically or simply because you don’t want the file to get too big.
npc_xxx.xml – Non Player Character state machines (NPCs). Unless you have a bunch of NPCs that don’t do anything except say hello I’d definitely split these into different files as even two MPCs in the same file can get quite difficult to read through.
And that’s it! You’ll be amazed with what can be done with minimally just seven files. Actually even less if you don’t define any events or synonyms.
When developing your game these .xml files need to live in a folder
called ‘dev’ under the main folder of your install.
If this folder doesn’t exist the engine looks for a gamedata.dat
resource bundle and will run a pre-defined game from that. You release
your game by creating one of these resource bundles (using the tools
provided) and distributing that along with the engine library files (the
.jar files in the lib directory of your install).
So it will check for a dev folder first, if that doesn’t exist it will
expect a gamedata.dat file to run a game from.
If neither exist your computer will blow up.
gamedata.dat is an encrypted version of all the game xml files found in
the dev folder. Encrypted because obviously these files contain the
solutions to all your ingenious puzzles.
Of course anyone with enough tenacity and know-how will be able to
figure out how to decrypt it but it will at least keep your puzzles safe
from most prying eyes.
A sample folder is provided with a game.xml file providing verb definitions and (eventually) a skeleton game. Rename this folder to ‘dev’ to start building your own game.
XML is very readable but don't worry about giving away all your carefully crafted puzzles. When you release your game you release a gamedata.dat file instead of the folder of XML. gamedata.dat is encrypted and therefore not human readable. I'm not going to say it's crack proof but it will keep all but the most determined prying eyes away. When you SAVE your game the save files are also encrypted.
All verbs are also defined in the game.xml resource file. This defines
the verb, any prepositions or adverbs it understands, any synonyms for
the verb and it’s implementation class (as touched upon above).
You also define the help output that shows verb syntax that should be used
by the player.
You can create as many synonyms for any verb as you like, they will all automatically point to the same handler. Synonyms are defined by using the | (pipe) character to create a list.
Technically the parser will only interpret words it knows and should there be more than one verb in a sentence only the last verb read will matter. So it won’t make any difference if the player types something like GO NORTH, GO will be ignored unless you explicitly define it as a verb and map it to a handler.
Words the parser doesn’t know as verbs, adverbs, nouns, adjectives and prepositions are actually stored up and if needed it will attempt to interpret them as the name of an NPC.
As an aside:
Internally every action requires a handler, e.g. directional movements
are all handled by the DirectionHandler class.
The verb handler classes are all found in the java package
org.happysoft.games.action.handler
in the jtextadventure.jar library file for those interested.
There are three categories of verb.
The first category is for standard things you can do in any text adventure: moving around the map, picking up and dropping items, (re-)describing the current location, checking your inventory, fighting, etc. These verbs can be used in any location.
All compass directions and up and down, N, S, E, W, NE, NW, SE, SW, U, D
(plus their fully spelt out versions, NORTHEAST, etc) are actually
category one verbs mapped to the DirectionHandler class.
Other standard category one actions include:
ATTACK|KILL, attack an NPC (optionally WITH a weapon)
INVENTORY|INV, check what items you’re carrying and how much money you
have.
LOOK, re-describe the current location. Locations may change after some
actions, exits added, removed description has been changed, or NPC has
arrived or left.
EXAMINE|EXAM, take a closer look at an item.
TIME, what time is it? Game time can be disabled, if disabled this
action will print a message telling you so.
WAIT|WA, wait for a while or until a specific time.
SWAP|EXCHANGE, swap an inventory item for something an NPC is offering
(item value matters here)
BUY – Buy something from an NPC
SELL – Sell something to an NPC
GET|TAKE, pick up an item.
DROP, drop an item (leave it in the current room).
PUT, put an item in a container such as a cupboard or drawer – see below
for containers.
SCORE – show your current score..
TIME – show the current game time.
SAVE, save your game.
LOAD, reload a saved game.
QUIT – Give up and start over.
Here’s a typical verb definition found in the game.xml file:
<verb synonyms="SWAP|EXCHANGE|OFFER" handler="SwapHandler" acceptedPrepositions="FOR">
<help>
SWAP [adjective] noun FOR [adjective] noun; Ask an NPC to swap something you're carrying for something they are willing to trade.
</help>
</verb>Each of these verbs has it’s own handler (up to synonym), so SwapHandler above or LookHandler, DirectionHandler etc.
The second category is for completing tasks. These are the actions the player needs to use to accomplish any task.
Tasks can be as simple as opening a cupboard.
These verbs can only be used in particular locations or when a specific NPC is in the room or with a particular item. There is a full discussion of this class of verb in the section below on tasks.
This class of verb often makes use of a preposition so the sentence can specify an action object and a target object or an action object and an NPC.
Here’s a typical verb definition found in the game.xml file:
<verb synonyms="JEMMY|PRISE|SMASH" implementation="**DoTaskHandler**" acceptedPrepositions="WITH|USING"\>
<help>
JEMMY [adjective] noun WITH|USING [adjective] noun; Jemmy or smash something open (e.g. lock, window, door)
</help>
</verb>You can add more synonyms or more verbs as needed, the implementation is
defined by a task in one of the tasks_xxx.xml files. JEMMY, PRISE and
SMASH all do the same thing.
All of these verbs are mapped to the DoTaskHandler.
So these verbs are much more general in scope than the first category as the consequences of using them are fully scriptable and can affect the player, items the player is carrying, items in the current room or any room, the room itself (the description can be changed, exits added or removed), invoke an NPC or multiple NPCs – basically any object in the game world can be manipulated as a consequence of completing a task.
See the section on tasks for full details on how to script tasks.
The third type of verb is those that use an adverb. Adverbs can be used to make a verb context sensitive.
These verbs can be used anywhere like category one verbs or like category two verbs they can only be mapped to the DoTaskHandler or a special ItemActionHandler which maps certain classes of item (items with a particular tag) to an event.
A full discussion of this form is given later in the section on Events.
Suffice to say the event is defined per item and verb so different items can change the world in different ways.
Other adverbs may be defined as essentially further synonyms for existing verbs.
These verbs have to be defined against the AdverbHandler with an extra <adverbs></adverbs> element enclosed within the main <verb> element. The <adverbs> element define each adverb to be used and a more specific handler that that adverb invokes. e.g.:
<verb synonyms="SWITCH|TURN" implementation="AdverbHandler" itemTag="activatable">
<adverbs>
<adverb names=”ON|OFF” handler=”ItemActionHandler”/>
</adverbs>
<help>SWITCH ON|OFF [adjective] noun</help>
</verb>Here’s an example of a verb where the adverb form overrides the original use, so the verb performs a different action when it is used with the adverb to what it would usually do:
<verb synonyms="LOOK|L" handler="AdverbHandler" acceptedPrepositions="IN">
<help>
LOOK Re-describe the current location
LOOK UP [adjective] <;noun\> IN [adjective] <noun>; look something up in an index for example
</help>
<adverbs defaultHandler="LookHandler">
<adverb adverbs="UP" handler="DoTaskHandler">
</adverbs>
</verb>The adverbs enclosing tag defines the defaultHandler which is to be used when no adverb is supplied. When the adverb UP is supplied we delegate to the task handling system.
Here’s another example of a set of synonyms, this time for each
direction for use with the verb RUN.
Notice here the handler is DirectionHandler but the parameters tag
tells the handler that the time cost of moving to the new location
should be halved.
<verb synonyms="RUN" handler="AdverbHandler"\>
<help>
RUN somewhere instead of walking, this reduces the time taken to get to a destination. e.g. RUN NORTH;
</help>
<adverbs>
<adverb adverbs="N|E|S|W|NE|NW|SE|SW|NORTH|SOUTH|EAST|WEST|NORTHEAST|NORTHWEST|SOUTHEAST|SOUTHWEST|U|D|UP|DOWN" handler="DirectionHandler" **parameters**="0.5"/>
</adverbs>
</verb>Most verb handlers don’t require or expect any parameters so the use of this attribute is limited (and to be honest it feels like a hack).
There's a little more to explore about adverbs and handler selection but we'll come back to that after we've discussed items and events. We'll explain the ItemActionHandler and how to set up more varied verb interpretation rules where the mere presence of an adverb isn't quite enough to fully determine what to do.
Every adventure game needs a map for the player to explore.
I use the words room and location interchangeably. These are the locations the player character can move around among – the world.
Rooms are defined across multiple rooms_<something>.xml files. You can use as many of these files as you need to keep the definitions logically grouped.
There’s not really that much to it. You need to give each room a unique id. The name and youPrefix attributes are used by the engine when describing you are in the location or describing exits from one location to adjoining locations. Here’s an excerpt from a room description:\
You are in ‘the kitchen of your flat’
name=”the kitchen of your flat.” youPrefix=”in”)
Exits are:
NORTH to ‘the living room of your flat.’
The telescreen is blaring brassy music loudly.
Through the window to the South you can see the four Ministries rising
far above all other structures in the city.
You can see: THE PLUNGER
The XML that produces this:
<?xml version="1.0" encoding="UTF-8"?>
<rooms>
<room id="1" name="the living room of your flat." youPrefix="in" tags="home">
<description>
There is a sofa, a desk and the ubiquitous telescreen. The flat on the whole is dilapidated but not dirty or untidy.
</description>
<exits>
<exit room="2" direction="S"/>
<exit room="3" direction="N"/>
</exits>
<roomItems itemIds="2|5"/>
</room>
<room id="2" name="the kitchen in your flat." youPrefix="in" tags="home">
<description>
The telescreen is blaring brassy music loudly.
Through the window to the South you can see the four Ministries rising far above all other structures in the city.
Huge gleaming white pyramidal structures, a stark contrast to the grey Airstrip One skyline.
The Party slogans, in black lettering large enough to read even at this distance, are picked out on the Ministry of Truth:
WAR IS PEACE.
FREEDOM IS SLAVERY.
IGNORANCE IS STRENGTH.
</description>
<exits>
<exit room="1" direction="N"/>
</exits>
<roomItems itemIds="11"/>
</room>
</rooms>Each room has a list of exits which take a compass direction and the id of the room that direction takes you to.
Each room also has a pipe | separated list of items that are available in that room <roomItems> specifies the list of the items (defined in items.xml described above).
Each room can also have a list of tags (pipe | separated list of strings e.g. tags=”home|safe”). Later when we come to discuss tasks and events you’ll see how you can make use of the tags during play.
A room can optionally have an onEntered event which as you might guess runs an event when the player enters that specific location. This event (as is almost always the case) may be randomized by using a pipe | separated list of event IDs instead of a single event ID.
Exits work like you think, you declare direction=<compass_direction> and the room the exit leads to. You may also optionally declare some text to print if the exit is blocked using the exitBlockedText attribute. The presence of this tag means the exit is blocked and can't be used.
And that’s all there is to it.
All nouns and adjectives the engine understands are extracted from objects you define in the items.xml file. So the game engine doesn’t have a fixed list of nouns that it understands, they are completely determined by you, the author.
Items may be split across multiple files each called items_<something>.xml so you can split item definitions in whichever way you feel appropriate.
<item id="432" name="ASSEMBLED BOARD" price="15">
<description>
An assembled circuit board for plugging into (spoilers).
It has a connector along one side that snugly fits into a (spoilers) adaptor.
There's a bus alongside one of the chips, you wonder what that adaptor is used for.
</description>
</item>Nouns and adjectives are extracted from the name attribute, so here the noun is BOARD and adjective is ASSEMBLED. Words such as THE, OF, AN, A, SOME are ignored internally but will remain when the object name is output.
<item id="410" name="BOARD GAME" price="20" **adjectiveIsAdjunct**="true">
<description>
Square ivory tiles carved with a black ebony letter and a smaller number inlaid on each one. All the tiles are kept in a green baize cloth bag.
The board is of maple or similar hardwood and has curious coloured
squares in a star pattern proclaiming double points and triple points.
</description>
</item>In this case the NOUN in this case is GAME and the adjective is BOARD.
There is a slight complication here – because BOARD is already a noun the grammar parser needs a little help to distinguish which one you mean, so you need to set attribute adjectiveIsAdjunct=”true” to overcome this.
Along with the name (adjective and noun) goes an article. The attribute (if it’s declared separately) is article or it can be declared with the name. The article is the part of speech or when the description is output. Most of the time the article will be THE so this is the default if not specified but A, AN, SOME, YOUR, basically anything else can be used if and where appropriate. If you need multiple words, such as ‘A FEW’ then you’ll have to use the attribute. The parser will lose a word if you don’t.
So, the article for any item is ‘THE’, THE ASSEMBLED BOARD, THE DESK, THE RUSTY KEY, etc but if you set an article=”A” attribute in the <item> tag or just set the name to “A BOARD GAME” you can override it to A or anything else you need.
The article is used in some output such as when the player lists their inventory or when a trader is selling something to you.
Also article are ignored in player input so TAKE WATCH, TAKE THE WATCH are treated in exactly the same way.
Description is often just a description attribute in the item tag but for more detailed or extensive descriptions you can enclose it in a <description></description> element within the full <item> definition. If both happen to be present the (assumed to be) more detailed version in the description tag takes precedence.
One other attribute can be set, hidden=”true” (default is false). This means the item won’t be listed when the room description is output.
Sometimes because an item is listed in the location description as you enter a room that can be a clue in itself. Ah what’s that? I must have to do something with it. The hidden attribute can be used to prevent that. If you do use this attribute then the room description really should allude heavily to its existence. I’m still debating whether this is fair but it has its uses and equally you don’t have to use it.
Example:
<item id="102" name="LAMP"
description="The glass will definitely fall if struck with something. You're surprised nobody has broken it already."
fixed="true"
hidden="true"/>The room description that the lamp is in alludes to its existence and why you might want to use it in some way:
<room id="104" name="a ruined bandstand." youPrefix="by">
<description>
The remains of a bandstand rise like broken teeth, its roof long collapsed. This part of the park feels quieter, as if it's been
forgotten.
A lamppost stands next to the bandstand, paint peeling and it’s heavily rusted beneath.
It's been many years since it was last lit.
A pane of cracked glass dangles precariously from the rusted lamp housing catching the light.
It looks like a stiff breeze would blow it down.
To the South lies Freedom Square but the route is blocked by patrols.
</description>
</room>The hidden attribute also has a second use in conjunction with virtual items explained below.
All items can have a price attribute set. The price is purely numeric, the currency being defined for the game as a whole. (Yeah, there’s no international or interplanetary trade just yet).
If a price is set then the item can usually be traded with a trading enabled NPC (Non Player Character) using the BUY, SELL and SWAP verbs. Also SHOW works to get a trader to give you an offer without committing to the trade.
We cover NPCs more fully later on.
<item id="2"
name="DESK"
description="It is out of sight of the Telescreen in an alcove. This means the drawer could be used to store contraband."
fixed="true"/>The attribute fixed=”true” means it can’t be picked up (the default for fixed is false).
Some items are naturally used to store other items inside, obvious examples being a cupboard or the drawer of a desk.
<item id="6" name="THE OPEN DESK"
description="The drawer is open."
container="true" fixed="true"
prefix="In the drawer of the desk is ">
<contains itemIds="7|8"/>
</item>This item is a container item, meaning other items can be placed inside it. The item is also fixed meaning it can’t be moved (picked up).
The prefix attribute is used to make the description flow more naturally when the room is described or when the player enters the LOOK command.
So in the desk is item 7 (the DIARY); prefix is set to “in the drawer of the desk is “to list out the items it contains. If a container item contains another container item it will be listed but not any items it in turn contains. Technically this is known as a recursing – I did make it like that for a while but realised it’s a can of worms as you’d really have to have a sense of volume or weight to keep putting one object inside another and I don’t really want every item to need that amount of meta data. You can either it pick it up or you can’t.
Containers enable the player to type PUT X IN Y where Y is a container item. Basically you can put anything inside a container, it doesn’t check volume or weight or anything.
The upshot of all this is be careful defining containers, I recommend
the following:
a) make them large (in description – cupboards and such things – so the
player can’t put a sack of coffee beans inside a matchbox.
b) make them fixed so the player can’t move them and hence they
won’t be able to put one inside another – you can’t create a situation
where you can put a sack of coffee beans inside a matchbox.
Ultimately it’s up to you as the game designer which way you go.
Items may have a list of tags.
Tags are mostly used in conjunction with events, the presence of a tag may incur consequences. Groups of items can be identified by tag so multiple items can be collected together this way.
Tags are specified in the tags attribute if specified along with the item or they can be added and removed at runtime by events and tasks. If using the attribute multiple tags are specified using a pipe | delimiter, e.g. tags=”stolen|contraband”.
Items may be weapons.
You define a weapon inside the <weapon> element inside the item tag.
Weapons need to define 2 sub elements, <hit> and <miss> and may define one further element <onEmpty> for when it runs out (see consumable items below).
The <hit> tag defines the chance and some connect text to be shown if the attack is successful.
The <miss> tag just defines text for when you miss.
You may have | delimited alternatives for these text blocks and the system will choose a random one on each turn.
<item id="400" name="A CROWBAR" price="7">
<description>A solid iron jemmy, a bit rusty but definitely still useful.</description>
<weapon damage="15">
<hit chance="6" probabilitySpace="10">You swing the crowbar at :1, it connects and they stagger back.</hit>
<miss>You swing the crowbar at :1 but they nimbly dodge aside.</miss>
</weapon>
</item>You usually use a weapon from the ATTACK verb or any synonyms you define but it can be scripted from a task to support other verbs. For example you probably don't want THROW to be a full synonym for ATTACK so you can create a task to cover THROW ACID AT <npc>. When scripting an attack this way you'd use the doAttack action which hooks back directly to the ATTACK verb handler and this way the THROW ACID task becomes an attack as opposed to just THROW BALL AT NET.
See the section on combat for how to get your <npc> to retaliate to an attack.
Other items might require further items to work or to complete them, consider the torch and batteries scenario:
<item id="23" name="A TORCH"
description="A battery powered waterproof torch. It requires batteries." price="4">
<accepts itemId="24" eventId="200102"/>
</item>
<item id="24" article="SOME"
name="SOME BATTERIES"
description="Some standard sized batteries."
price="1"/>This torch declares it accepts item 24, the batteries. This enables
the player to type
PUT BATTERIES IN TORCH
without the game thinking the torch is a general
container or even allowing anything else to be put in it. So, you can’t
PUT DIARY IN TORCH because TORCH is not a container.
When the player types PUT BATTERIES IN TORCH, the engine runs event
200102, as specified by the <**accepts**> element.
Events are covered in detail in the next chapter. For now, simply think of an event as a list of actions that the engine performs in response to something changing in the world.
The game engine does not assume what adding one item to another should do in terms of your game. Different objects may power up, unlock, transform, or consume the added item, so the behaviour is defined by the event. The event just defines a list of things that happen in response to this particular change.
If the added item can later be removed again, the event must associate the two items using something called the combineItems consequence so that we can check the batteries are actually in the torch before removing them. This is covered in more detail when we talk about the events.
There’s lots that can be done with this mechanism, imagine PUT GEM IN STATUE and the statue sinks and a new door opens up behind it, or PUT KEYS IN IGNITION and now you can start the engine. Even PUT PASSWORD INTO COMPUTER and the computer unlocks allowing you to read previously secret information.
You don’t have to use PUT either, you can define a task and other verbs
against the DoTaskHandler, e.g. ATTACH, CONNECT, INSERT, whatever is
appropriate. REMOVE is a task handler verb so item removal has to
scripted.
i.e. REMOVE BATTERIES FROM TORCH
has to be scripted.
There’s one last class of item to mention, a virtual item.
Virtual items are intangible, they can’t be picked up or dropped so they
represent ideas or information such as a password.
You can only gain these items by being told them, discovering them or by
trading for them. Like any other item these items can have a price and
so can also be bought, sold or swapped.
Virtual items cannot be added to a room definition either.
You mark an item virtual by setting the attribute virtual=”true” in the <item> tag.
If a virtual item is also marked as hidden it cannot be traded either. It can still be manipulated by task consequences though – if it’s appropriate being able to change it.
Sub items are items that are part of something else. They’re used when something appears in a description so it ought to be examinable in and of its own right. For example, take the Telescreen item:
<item id="2" name="TELESCREEN" fixed="true" onTaken="200001">
<description>
It's playing brassy music. There's a slot on the side and a camera above the main screen.
It is securely fixed to the wall.
</description>
</item>The telescreen description makes mention of a slot on the side and a camera so you ought to be able to look at these objects too.
<item id="2" name="TELESCREEN" fixed="true" onTaken="200001">
<description>
It's playing brassy music. There's a slot on the side and a camera above
the main screen.
It is securely fixed to the wall.
</description>
<subItems>
<subItem name="SLOT">
<description>It looks like something can be plugged into it.</description>
</subItem>
<subItem name="CAMERA">
<description>A tiny camera mounted above the screen. You feel self-conscious looking closely at it.</description>
</subItem>
</subItems>
</item>Sub items are the same as normal items in most ways so they become part of the vocabulary that your game can handle. They have their own description and name (and therefore noun and adjective).
But there are some important differences:
- They always fixed so you can’t pick them up. You pick them up by picking up the parent item.
- They have no price. That’s part of the parent.
- They are never virtual.
- They are never containers.
- They cannot be weapons or consumable.
- They can have their own events.
Events (see below) such as onTaken and onExamined are inherited from the parent but can be overridden.
An item may be Consumable, in which case it can have a fixed number of uses before it requires replenishing. If the item doesn’t have a limited number of uses set uses to -1.
<item id=”351” name=”FOOD RATIONS” article=”SOME”>
<description>A box of basic rations.</description>
<consumable uses=”10” onEmpty=”There is nothing left.”/>
</item>An item may also be used as a weapon – this will be covered more fully in the section on combat discussed later on.
There are a lot of variations on how you can define an item but usually an item is one thing or another so a single item definition is unlikely to get too unwieldy.
The majority of items you define will look like this:
<item id="16" name="CARETAKERS KEY" description="A heavy, iron key stamped with the flats' insignia."/>A one-liner definition. Nothing more nothing less.
A few will be containers:
<item id="13" name="OPEN DESK"
description="It is strewn with bits of discarded electronics and burned out circuit boards."
container="true"
fixed="true"
prefix="In the drawer of the desk is ">
<contains itemIds="16|22"/>
</item>A few items will accept other items:
<item id="23" name="TORCH" article="A"
description="A battery powered waterproof torch. It requires batteries."
price="4"
tags="plural|activatable" onStateChanged="200103">
<accepts itemId="24" eventId="200102"/>
</item>and so on.
This next example is about as complicated as it gets - this item is both a weapon and consumable (limited number of uses):
<item id="409" name="ETCHING ACID" article="SOME" price="14" tags="dangerous">
<description>
A small jar of acid with a rubber stopper. It is very dangerous.
</description>
<consumable uses="2" onEmpty="That was the last of the acid."/>
<weapon damage="50">
<hit hitChance="3" probabilitySpace="4">
You throw the acid at :1 and he shrieks in pain as it burns through his skin.
</hit>
<miss>
You throw the acid at :1 but miss, it splashes on the floor and hisses.
</miss>
<empty>The jar is empty.</empty>
</weapon>
</item>It’s conceivable a weapon could also have sub items. Imagine a gun with six bullets. You might want to be able to examine the bullets. That makes me think of an interesting issue, can you still examine Bullets when there are none left? Well, I think yes you can at the moment – that’s probably a bug. I'll get to that.
Aside from just exploring the map and picking things up, adventure games usually require the player to solve puzzles and things happen in the world – changes occur – when the puzzles are solved.
Other things may happen in the world either randomly or according to the time of day or other triggers. This gives your world more texture.
Tasks are reactions to something the Player has typed, whereas Events are a reaction to something that has happened in the world.
Both require a set of Conditions before they can run and both result in a set of Consequences when they do run.
Events and tasks both have conditions and consequences but difference lies in what trigger them.
When developing your own adventure you can create as many tasks and events as you like.
This section describes how to script events. After this we will be in a position to show you how you can script tasks which you can use to set up the core puzzles in your game.
Tasks are very similar to events and share a lot of common XML definition so let’s look first at events.
As with other all data files, event definitions can be split across multiple .xml files each called events_xxx.xml so you can group them logically.
Events are things that occur in the game world and will happen whatever
the players current situation is.
Events occur after any player turn but not necessarily as a consequence
of that turn – although of course they may be related.
Events can be scripted to occur randomly or in a specific time interval or they can even be scheduled to happen at a point X number of game minutes in the future or at a specific time.
With both events and tasks there may be one or more conditions that have to be satisfied before that event or task can run. Multiple conditions are grouped into a <conditions> block.
Consequences are grouped into <onSuccess> and <onFailed> blocks that run only if all conditions are satisfied or any one condition fails respectively. In my experience tasks make more use of the <onFailed> block.
There are three main types of event: UNIVERSAL events, ROOM
events and events that happen as a consequence of an action, which are
labelled EVENT_TRIGGERED.
If you don't label your event or forget to it will be treated as though
it's an EVENT_TRIGGERED event.
Universal events run after every player action, i.e. after every command the player types is processed. They will also run if the player is idle for a while and a time out occurs.
Room events run, as you would imagine, when a player enters a specific location.
Triggered events can be run from other events, when a task is completed or when performing specific actions such as picking up an object or examining an object (onTaken, onExamine can trigger an event).
As events can also be scheduled to run in the future a consequence of doing something may not be seen immediately.
Here’s an example of a ROOM event that occurs randomly - Mrs Parson’s children fire a catapult at you. Notice there’s a 1 in 5 chance of it happening, you have to be in one of two rooms (either room 6 or room 7) and the checkGameState condition ensures that once it has happened it doesn’t happen again.
<event id="600" name="Mrs Parsons children fire catapult at you" roomIds="6|7">
<conditions>
<condition type=”chance” occurs=”1” probabilitySpace=”5”/>
<condition type="checkGameState" key="hasCatapultBeenFired" invert=”true”/>
</conditions>
<onSuccess>
<consequence type="message">
There's a sudden stinging blow on the back of your neck.
You spin round just in time to see one of Mrs Parsons children pocketing a catapult.
"Goldstein!" he yells as Mrs Parsons drags him away to a bedroom.
</consequence>
<consequence type="setGameState" key="hasCatapultBeenFired" value="true"/>
</onSuccess>
</event>So we see there are two conditions, one is the chance, just 1 in 5 and
the flag is not set. Only if both conditions pass will the
<onSuccess> block run.
There’s no need for an <onFailed> block, if either condition fails
nothing happens.
As mentioned if an event should run is determined by one or more
conditions declared in a <conditions></conditions> block.
Each condition is declared in a <condition></condition>
element.
All conditions are evaluated in order. If any one condition fails then execution is short-circuited, no further conditions will execute. If there is an .<onFailed> block of consequences execution passes to that.
Here’s a list of all conditions you can currently use:
| Condition |
Attributes |
|
1 |
Chance - roll a dice |
type="chance" |
| 2 | Check game state - has a certain state been set. |
type=”checkGameState” Return true if the key is set to true. |
| 3 | Check a count – check if a counter has reached a certain level. |
type=”checkCount” |
| 4 | Check if player is carrying – does the player possess a particular item? |
type=”checkCarrying” Both itemIds and tags attributes can be used, any match will result in a true. |
| 5 |
Check time |
type=”checkTime” |
| 6 | Check an NPC is distracted – Is this particular NPC distracted, can you perform the task without being seen? One of the possible consequences is to mark an NPC as distracted. |
type=”checkDistracted” |
| 7 | Check NPC alive – is the NPC still alive? Damage HP is taken automatically during combat. If HP reduces below 0 then the NPC is marked as dead. |
type=”checkAlive” |
| 8 |
Check the players balance |
type=”checkBalance” |
| 9 |
Check uses |
type=”checkUses” |
| 10 |
Check item tag |
type=”checkItemHasTag” |
| 11 |
Check room tag |
type=”checkRoomHasTag” |
| 12 |
Check room has an item |
type=”checkRoomHasItem” |
| 13 |
Check Noun |
type=”checkNoun” This should NOT be used in an event, certainly a universal event anyway as you need the current player input for it to evaluate properly. |
| 14 |
Check Adjective |
type=”checkAdjective” This should NOT be used in an event, certainly a universal event anyway as you need the current player input for it to evaluate properly. |
All conditions (one or more) are tied together in a <conditions></conditions> block. All conditions in a block have to be true for the consequences to fire.
One final point to remember is any condition can be inverted by setting
invert=”true” as an attribute.
This means you can change type=”checkCarrying” for example to a
check that the player does NOT have the specific item. Or you can
invert a checkGameState check so it would look for the key to be
false instead of true or indeed a checkCount so instead of amount
exceeds (or equal) it becomes amount (strictly) less than.
You could also check an item or room does not have a tag or is that NPC still alive? Anything if it makes sense to you.
If all the conditions pass then the event happens or the task succeeds and there will inevitably be consequences.
There can be many consequences if an event occurs or a task is successfully accomplished. All consequences are of form:
<consequence type=”...” parameters... />
As stated briefly above there may be multiple consequences and all
consequences are kept in an <onSuccess></onSuccess> block or
an optional <onFailed></onFailed> block.
Which block is executed depends on whether or not all the conditions
passed.
Further conditions may be placed within a
<gateway></gateway> block within the <onSuccess> or
<onFailed> blocks. These may be used to concatenate two different
sets of conditions together to create different circumstance – so for
example, if a <conditions> block fails we go to the
<onFailed> block but we can then optionally prevent even that from
running by imposing further more specific conditions at that point. The
same goes for <onSuccess> of course.
I’ll give an example of this later.
When we do get to the <consequences> block all consequences are executed in the order presented.
There are many possible consequences that all alter the state of the game world in some way. Each requires a specific set of parameters specified as attributes. The following table summarises all the current possible consequences.
| Consequence |
Parameters |
|
| 1 | Add an Exit to a room |
type=”addExit” |
| 2 |
Remove an Exit from a room |
type=”removeExit” |
| 3 | Add Items to an actors inventory, this may be either the player or an NPC. |
type=”addItemsToInventory” |
| 4 |
Remove items from an actors inventory. By default this will be the player but you can specify an NPC. |
type=”removeItemsFromInventory” tags: | pipe separated list of tags. Remove all
items with any of the tags specified. |
| 5 | Drop items. Remove item from an actors inventory and add them to the current room. By default the player but you can specify an NPC. |
type=”dropItems” |
| 6 |
Add new items to the room. |
type=”addItemsToRoom” |
| 7 | Remove items from a room. |
type=”removeRoomItems” |
| 8 | Add tags to an item. |
type=”addItemTags” |
| 9 |
Remove tags from an item |
type=”removeItemTags” |
| 10 | Add room tags. |
type=”addRoomTags” |
| 11 | Remove room tags. |
type=”removeIRoomTags” |
| 12 | Add items to container |
type=”addItemsToContainer” |
| 13 | Remove items from container |
type=”removeItemsToContainer” |
| 14 |
Combine Items As alluded to in the section on Holder Items. Used for when two objects are combined to make one so we keep a record of which two individual items were originally combined. 1 |
type=”combineItems” |
| 15 |
Separate Items The opposite of combine items. |
type=”separateItems” |
| 16 | Message. Output a message |
type=”message” |
| 17 |
Move an actor (player or NPC) Move an NPC from its current location to a different location |
type=”moveActor” |
| 18 | Make an NPC give items to another actor |
type=”npcGiveItems” |
| 19 | Make an NPC take things from a room |
type=”npcTakeItemsFromRoom” It’s probably more useful to use tags to specify the ‘type’ off thing to take. |
| 20 | Set a game state flag. e.g. homeDeskIsOpen = true or false |
type=”setGameState” |
| 21 | Set a new time. Unlike time cost below we can set a specific time of day. |
type=”setNewTime” |
| 22 |
Set an NPC state machine state. |
type=”setNpcState” also type=”setNextState” |
| 23 |
Run an NPC state. |
type=”runNpcState” |
| 24 |
Time cost. |
type=”timeCost” |
| 25 |
Update an item description |
type=”updateItemDescription” The description itself is set as the CDATA in the tag rather than being an attribute. |
| 26 |
Update a room description. |
type=”updateRoomDescription” The description itself is set as the CDATA in the tag rather than being an attribute. |
| 27 |
Update balance. |
type=”updateBalance” |
| 28 |
Use an item |
type=”useItem” |
| 29 | Fire an event. Trigger an event to run. |
type=”fireEvent” |
| 30 |
Cycle through a list of events. |
type=”cycleEvent” |
| 31 |
Points |
type=”points” |
| 32 |
New game. |
type=”newGame” |
| 33 |
Pause Insert a pause for dramatic purposes. Occasionally useful between two messages or when moving a player to a different location. |
type=”pause” |
| 34 |
Wait for a key press Specifically, since this is a console application it does have to Enter |
type=”waitForKey” |
| 35 |
Do Attack Used as a hook from a task into the Attack verb. See the section on combat for a full description. |
type=”doAttack” |
| 36 |
Apply Damage Apply damage points to an actor, either the player or an NPC. If the player is killed the players onDeath event will be fired. If an NPC is killed their onDeath state will be set (if there is one). |
type=”applyDamage” Using applyDamage with an NPC will also result in retaliation. See the section on NPCs and combat for a full description. If npcId is not specified the damage is applied to the player. |
-
This is required only if it’s intended for the items to be pulled apart again. A “REMOVE BATTERIES FROM TORCH” task will not work without this.↩︎
-
- When using pipe separated messages some can be empty so you don’t have to have a message appear after every turn. To set this up you just need empty pipes like so: ‘message1 | message2| | | | |‘ but you must keep a space between them otherwise they won’t be used. So in this example 4 times out of 6 there will be no message and 1/3 of the time you’ll see either message1 or message2.
↩︎ -
- A little care should be taken using the runNpcState or setNpcState consequences in an event.
You should probably NOT make an NPC jump to a state that attempts to interpret a player response as any player action response is unlikely to still be valid in the context of an event or task.\ Basically don’t jump to a state that contains a ‘response’ action (see section on NPCs).
When using it as an action from within an NPC state this is obviously fine, and in fact expected.
Also bear in mind this can be used on any NPC anywhere in your world, if the new state has any text output it will be shown even if the NPC is not present in the players location!
↩︎
Here’s an example of an event that makes use of a gateway condition:
<event id="100002" name="No telescreen" type="UNIVERSAL">
<conditions>
<condition type="checkRoomHasTag" tags="no-curfew|telescreen-hidden"/>
<condition type="checkRoomHasTag" tags="dark" invert="true"/>
</conditions>
<onSuccess>
<consequence type="message"\>You note that there's no telescreen here.</consequence>
<onSuccess>
<onFailed>
<gateway>
<condition type="checkRoomHasTag" tags="no-curfew|telescreen-hidden">
<condition type="checkRoomHasTag" tags="dark">
<condition type="checkCarrying" tags="light-source-active">
</gateway>
<consequence type="message"\>You note that there's no telescreen here.</consequence>
</onFailed>
</event>
So the upshot of this event is either:
1, If the room has a no-telescreen or telescreen-hidden tag then
by default the player sees the No telescreen here message unless it also
has the tag ‘dark’ in which case we we fall through to the
onFailed block.
Or:
2, We reach the onFailed section. We have to repeat the first check since the initial condition block can fail for one of two reasons. Now if the room is in darkness then the player won’t see the message unless they happen to be carrying an active light source, e.g. a switched on torch which overrides the darkness.
The event is marked as UNIVERSAL so it runs after every player action and therefore applies to any room with these tags.
Note that the actual tag, dark and no-telescreen used here are just part of the room definition. They don’t have any special meaning within the engine.
Now we've properly discussed events, it seems like a good time to introduce item events.
The item tag can take four more attributes that hook directly into the event system.
First off are onTaken, onDropped and onExamined. These define the id of an event which will be run when GET|TAKE, DROP or EXAMINE actions are performed. This is optional. No onTaken, onDropped or onExamined and nothing extra will happen.\
So using onTaken=”1001” in the item definition to run event 1001 when this item is picked up.
The last attribute the item tag can take is called onAction.
This attribute works with adverb definitions within the game verb in
game.xml as touched upon above.
The usual practice here is to swap an item for the ‘active’ version of it. Or for each state it can exist in. To deactivate an item you swap out the item for its inactive version using the active items onAction event.
Imagine the torch from earlier, now it has batteries it can be switched on or off. So we define two versions of the torch:
<item id="20" name="TORCH" article="A" description="A battery powered waterproof torch."
tags="activatable"
onAction="200100"/>
<item id="21" name="TORCH" article="A"
description="A battery powered waterproof torch. It is switched on."
tags="deactivatable|light-source-active"
onAction="200101"/>In the game.xml file we have the following definition for the SWITCH verb.
<verb synonyms="SWITCH|TURN" handler="AdverbHandler">
<help>
SWITCH ON|OFF [adjective] <noun>; SWITCH an object ON or OFF
</help>
<adverbs>
<adverb adverbs="ON" handler="ItemActionListener" itemTag="activatable" itemTagFailureText="Is it on already?"/>
<adverb adverbs="OFF" handler="ItemActionListener" itemTag="deactivatable" itemTagFailureText="Is it currently off?"/>
</adverbs>
</verb> The SWITCH ON and SWITCH OFF variants are mapped to the ItemActionListener.\ The itemTag defined on each adverb is activatable or deactivatable and each version of the torch item is also tagged as activatable or deactivatable.\
These tags don't mean anything to the game itself, they're just words, but they are the glue holding the two pieces together: the verb/adverb definition and the event that gets run when the player performs the specific actions.
Note the two different onAction events on the different versions of the item, these events define how to swap items 20 and 21 and back again.
So, typing SWITCH ON TORCH will run event 200100, SWITCH OFF TORCH will run event 200101 if the player is carrying the correct version of the torch (and so the tags match).
The above is great for SWITCH ON and SWITCH OFF since the verb is the same for the action and opposite action. The onAction plus the adverb definition is all you need to swap one item for the other but what if the verbs are not symmetric?
What happens if you need a different verb to perform the reverse operation, for example we could tag an item as wearable and we would need a WEAR verb to put it on and REMOVE or TAKE OFF (adverb definition on TAKE) to take it off again.
Well, you can set up <actionListeners> inside the item per verb too:
<item id="2" name="VELVET CLOAK" tags="wearable" onTaken="202">
<description>
A handsome cloak, of velvet trimmed with satin, and slightly spattered with
raindrops. Its blackness is so deep that it almost seems to suck light from the
room.
</description>
<actionListeners>
<actionListener verb="TAKE" onAction="200"/>
<actionListener verb="WEAR" onAction="201"/>
</actionListeners>
</item>
<item id="3" name="VELVET CLOAK" tags="wearable">
<description>
A handsome cloak, of velvet trimmed with satin, and slightly spattered with
raindrops. Its blackness is so deep that it almost seems to suck light from the
room.
You are wearing it.
</description>
<actionListeners>
<actionListener verb="TAKE" onAction="200"/>
<actionListener verb="WEAR" onAction="201"/>
</actionListeners>
</item>Note here we've moved the onAction attribute to the actionListener.\
So we've set up two versions of the item, not worn (item 2) and worn (item 3) and each one maps a different event to fire when the verb is TAKE or for when the verb is WEAR. These are the same events for both items but it could have been four different ones.
Let's just go over the verb definitions in game.xml so you can see how the two are linked together.
<verb synonyms="TAKE" handler="AdverbHandler" acceptedPrepositions="OUT" itemTag="wearable">
<help>
TAKE
TAKE [adjective] <noun> OUT OF <container>
TAKE OFF [adjective] <noun>; TAKE OFF an item of clothing.
</help>
<adverbs defaultHandler="TakeHandler">
<adverb adverbs="OFF" handler="ItemActionListener"/>
</adverbs>
</verb>
<verb synonyms="WEAR" handler="ItemActionListener" itemTag="wearable">
<help>
WEAR [adjective] <noun>; WEAR an item of clothing.
</help>
</verb>TAKE and TAKE OUT (TAKE x OUT OF y) are both handled natively inside the TakeHandler but TAKE also defines an adverb for when TAKE OFF is used. TAKE on its own performs the default action of picking up an object, so TAKE is mapped to the AdverbHandler, the adverb OFF is mapped to the ItemActionListener and we default to the standard TakeHandler if no adverb is supplied.
WEAR maps the verb + adverb combination so it uses the ItemActionListener instead of the default TakeHandler.\
Also note I've defined the itemTag="wearable" at the verb level rather than the adverb. This is fine, if it's at the adverb level it will override the verb level if both happen to be defined but more usefully you can provide different tags if needed per adverb. One for ON and one for OFF as shown above for SWITCH.\
If you don't need different tags per adverb then don't bother, just the verb level definition will do.
If you also define an onAction event in the item tag this event will be used a fallback if the verb form isn't explicitly defined.
Grammar parsers do not understand English (or any language you translate you game to) so they often need help understanding the shades of meaning different verbs can take on in different circumstances.
Luckily we can help the parser along by switching how a sentence is handled depending on what elements are available.
Here's an example - the various ways we can use the verb PUT:
<verb synonyms="PUT" handler="AdverbHandler" acceptedPrepositions="IN|INTO" onCompleted="OK">
<help>
PUT [adjective] <noun>; IN|INTO [adjective] <container>; PUT an item in or into a container such as a cupboard or on a chair, etc or you can use it in the more abstract sense to PUT an item IN|INTO something that uses it, like PUT BATTERIES IN TORCH or PUT PHOTO IN ALBUM
PUT [adjective] <noun> ON [adjective] <noun>; PUT something ON something else like HANG x ON y, PUT x ON table.
PUT ON [adjective] <noun> like WEAR x
See also: GET, TAKE
</help>
<adverbs defaultHandler="PutHandler">
<!-- PUT ON x (like WEAR) -->
<adverb adverbs="ON" handler="ItemActionListener" itemTag="wearable|activatable">
<rules>
<!-- PUT x ON y, maybe like HANG x ON y -->
<rule name="CheckTargetObjectNotNull" override="DoTaskHandler"/>
</rules>
</adverb>
<adverb adverbs="IN|INTO">
<rules>
<!-- PUT BATTERIES IN TORCH or fall back to PUT x IN CUPBOARD? -->
<rule name="CheckTargetAcceptsItems" override="ItemAcceptsObjectHandler"/>
</rules>
</adverb>
</adverbs>
</verb>We can use some rules to differentiate all the ways PUT can be interpreted.
We start with assuming a default PUT x IN y where y is a cupboard or some sort of container, <adverbs defaultHandler="PutHandler">. But then we start overriding that interpretation according to properties of the objects in the sentence the player typed.
We first look at the adverb ON, if there's only one object in the sentence then we must be trying to wear it (WEAR ARMOUR) or perhaps it could be interpreted as another synonym for SWITCH (SWITCH ON TV). So we allow for the item to have a few different tags, wearable or activatable.
If there is more than one item in the sentence then we look at the target object next. Using the CheckTargetAcceptItems rule we will override the default interpretation in a different way and tell the engine actually this is a required item into another item type of operation, e.g. PUT BATTERIES IN TORCH. (TORCH can't be a container because we can't put any old thing in it - it has to be just the batteries).
If neither of these fit then it falls back to the standard PUT object IN container interpretation.
The rules above are all declared in the <handlerSelectRules> element in the game.xml file.
Now we’ve covered events, we can have a look at tasks.
As mentioned above there are three categories of verb the engine understands. The second category is for performing specific tasks. Multiple synonyms are supported for any verb here too.
Internally all tasks are handled by the specialised DoTaskHandler class – this is important to know if you wish to extend the language and add tasks supported by new verbs.
Tasks can only happen in a certain location or when interacting with a particular NPC so they all specify either roomId or npcId.
All tasks consist of three parts:
A trigger: what the player needs to do to trigger the action? Basically
what do they need to type. Minimally which verb and noun are typed and
which room you have to be in or NPC you are interacting with.
You may need to specify what object you are directing the action to and
which object you are acting with (an action object and a target object).
Optionally a task can be directed towards an NPC.
One of either room or an NPC has to be defined.
Which prepositions are accepted are defined by the verb. They are
(currently) mandatory for the player to type.
A preposition implies there will be two objects involved or an object and an NPC.
So, you do something WITH something else, or do something WITH someone.
Second and third there’s the conditions and consequences blocks exactly
as with events.
A minor difference is some optional CDATA text may be included in each
condition tag which will be used, if present, as an output message if
that particular condition fails.
This feature makes sense for tasks but not so much for events.
Since events can run randomly and often run in the background unbeknownst to the player if you use condition messages for event conditions the game could well appear to show a message appearing for no particular reason.
There’s nothing stopping you using this feature of course but I’d say be careful with it.
Here’s a simple task, let’s OPEN the caretakers’ desk (the room with id = 13 is where the Caretaker NPC is and more importantly for this task where the DESK object has been added.
<task id="13000" name="Open Caretakers Desk">
<trigger action="OPEN" actionObject="DESK" actionAdjective="CARETAKERS" roomId="13"/>
<conditions>
<condition type="checkGameState" key="isCaretakersDeskOpen" value="true">It's already open.</condition>
<condition type="checkDistracted" npcId="13">The Caretaker barks at you, "Don't touch that!"</condition>
</conditions>
<onSuccess>
<consequence type="setGameState" key="isCaretakersDeskOpen" value="true"/>
<consequence type="message">You open the desk drawer.</consequence>
<consequence type="addItemToRoom" itemId="13">
<consequence type="removeItemFromRoom" itemId="12">
</onSuccess>
</task>The trigger specifies the action attribute which is the verb the player must type, the actionObject which is the item to interact with (noun) and the roomId where the interaction takes place. The actionAdjective is also specified but is optional in the player command.
So for this task the player must be in room 13 and type OPEN DESK or OPEN CARETAKERS DESK for the trigger to fire.
The conditions block checks that the desk isn’t already open (isCaretakersDeskOpen flag) and ensures you can only open it and steal its contents if you’ve managed to distract the Caretaker first.
The consequences addItemToRoom and removeItemFromRoom swap the DESK item for a different item that represents the open version of the desk. Rather than having an isOpen flag on every item or having lots of item specific flags we just define two items and swap them.
The setGameState consequence simply sets the ‘isCaretakersDeskOpen’ flag to true to say the desk is now open. It’s not especially necessary in this case, a bit nit-picky perhaps as nothing bad would happen if you open it twice but it’s good practice to do it anyway, and it provides a bit of feedback to the player.
In other situations doing something twice may have more severe or even nonsensical consequences.
There are three distinct task types which are identified in the trigger. When you specify roomId the task is room specific.
If you specify npcId then the task will apply to the NPC specified (by id not name, so it doesn't matter what room - the name will be matched with any NPCs present).
The most interesting case is when you don't specify either roomId or npcId. This becomes an item specific task - so it has to handled carefully.
It's mostly common sense - use a room level task when you're expecting the player to interact with an item in a particular room. These items can be fairly generic, e.g OPEN DESK. There's likely to be more than one desk that can be opened so script this to a particular room and then the engine can distinguish this OPEN DESK task from all the others.
Where it gets interesting is the item level tasks. When you specify neither roomId nor npcId then the task becomes generalised and the player can run it wherever they are.
Consider REMOVE BATTERIES FROM TORCH:
<task id="200104" name="Remove batteries from torch">
<trigger action="TAKE|REMOVE" actionObject="BATTERIES" targetObject="TORCH"/>
<conditions>
<condition type="checkCombinedItem" container="23" itemId="24">The torch doesn't have batteries.</condition>
</conditions>
<onSuccess>
<consequence type="fireEvent" eventIds="200104"/>
</onSuccess>
</task>Putting the batteries into the torch is handled differently because it's handled by the PUT verb and has been special cased.
Here’s a task that requires you to specify both action and target objects, let’s throw a rock at the lamp by the bandstand and smash the glass thus distracting the guards:
<task id="10400" name="Distract patrols with rock">
<trigger action="THROW" actionObject="ROCK|STONE" actionAdjective="LOOSE" targetObject="LAMP|LANTERN|LAMPPOST"
roomId="104"/>
<conditions>
<condition type="carrying" itemId="101">You need something to throw at the lamp.</condition>
<condition type="roomHasItem" itemId="102">The glass is already broken.</condition>
</conditions>
<onSuccess>
<consequence type="message">
You hurl the rock at the lantern and the glass shatters loudly, echoing across the park.
The patrols shout and rush toward the noise, leaving the southern path clear.
</consequence>
...More consequences...
</onSuccess>
</task>Any synonyms defined for lamp are all understood in place of the canonical noun. You can also define these synonyms in the synonyms.xml file and they’ll be general synonyms but if defined here the local synonyms will be accepted.
The preposition AT is defined by the THROW verb so it will be expected.
The sentence the player needs to type in this case would be THROW ROCK AT LAMP – the adjective LOOSE is optional, specificity is only needed if there two or more objects with a matching noun, maybe two different keys or something.
There are a few options in the trigger aside from verb/noun/adjective matching, you can also specify some quoted text but most importantly you need to specify either which room the task is completed in or with which NPC you are conversing with using roomId or npcId respectively.
Here’s a task that involves interacting with an NPC, note there are multiple options for the NPC name that is understood. At least one of the names should be the official NPC name as defined in an npc_xxx.xml file. Others can be shortened versions, anything natural that the player may type instead of spelling out the full name.
<task id="11000" name="Show flyer to prole">
<trigger action="SHOW" actionObject="LEAFLET" npcId="11000"/>
<conditions>
<condition type="carrying" itemId="412">The prole tuts and says, "Don't waste my time, comrade."</condition>
</conditions>
<onSuccess>
<consequence type="message">The prole says, "OK, wait a minute."/>
<consequence type="message">
You watch as he leaves by an alley beside the National Gallery North towards Charing Cross Road.
...A few minutes pass...
There's a tap on your shoulder and your blood runs cold, not the Thought Police already?
But a gaunt man with sharp eyes steps forward, "I'm Silas."
</consequence>
<consequence type="runNpcState" npcId="110" newState="8"/>
<!-- Bring Silas out of limbo and trigger his first state -->
<consequence type="moveNpc" npcId="111" roomId="110"/>
<!-- Move the Victory Square Prole into limbo -->
<consequence type="moveNpc" npcId="110" roomId="999"/>
<!-- run Silas initial state -->
<consequence type="runNpcState" npcId="111" newState="0"/>
<consequence type="timeCost" timeCost="10"/>
</onSuccess>
</task>The trigger for this task translates to the player typing SHOW LEAFLET TO [PROLE|LOITERING PROLE|PROLE LOITERER]
So, the above task is not room specific but the NPC has to be present for the player to be able to trigger it.
Here’s a task where you say a password in a location to open up a new location:
<task id="32600" name="Say Harry sent me">
<trigger action="SAY" quote="HARRY SENT ME" roomId="326"/>
<conditions>
<condition type="checkGameState" key="passwordHarrySet" value="true"\>Maybe a drink first? Check out the Victory Arms pub.
</condition>
</conditions>
<onSuccess>
<consequence type="message">
Someone looks over the top of the gate and eyes your blue overalls suspiciously.
He disappears for a minute or two but then returns and the gate is unlocked.
</consequence>
<consequence type="addExit" roomId="326" direction="NE" toRoom="355" timeCost="2"/>
</onSuccess>
</task>Quoted text must match exactly and is in quotes when the player types it – SAY “HARRY SENT ME” so it must be clear exactly what to type.
Any trigger can specify the actionObjectAdjective and targetObjectAdjective but these are only really useful if there’s more than one item with a matching noun in the players inventory or in the current location.
You can make the action item adjective non-optional by specifying a checkAdjective condition in the block of the trigger. This also has the advantage of being able to provide specific feedback to the player if the adjective they typed doesn’t match:
<condition type=”checkAdjective” adjective=”BLUE”>
You’ve cut the wrong wire! A countdown timer starts...
</condition>Non-Player Charaacters are all other characters that feature in your game.
This could people, animals or beasts that attack or even inanimate objects that
benefit from having a state machine. In the 1984 demo game, I model a lift as an NPC.
Also see the appendix for how the Quit Game function works.
NPCs can of course also be split among multiple XML definition files:
npc_*.xml
You can have as many NPCs as you like. Unless you're a masochist you'll probably want to keep all the definitions in separate files, just call the file ‘npc_[character name].xml’ and add it to the dev directory.
Each NPC has an id, a name, some optional local properties and a set of states. Each state consists of zero or more actions.
NPCs run whatever their current state is per turn after the player command has completed. If you need more than one state to run you can hook two or more states together using the runNpcState action. The states start with the initialState from the configuration and continue from there.
Only NPCs in the current room are executed. You can update the states of other NPCs using the setNpcState or runNpcState actions.
You will often want to bring an NPC into play as a consequence of some other action (a task or event) so you can configure the NPC initial room to be a limbo room (a room definition on its own, not part of the world map) and then bring them into play using the moveNpc action to move them to a particular room. See the Show flyer to prole task example above.
The general structure is as follows (properties are optional):
<npcs>
<npc id="40" name="Silas" altNames=”SILAS”>
<configuration initialRoom="999" initialState="1"/>
<properties>
<property name="loop_exit" value="false"/>
</properties>
<state id="1" evaluateUnless="loop_exit">
... various actions ...
</state>
...more states...
<!-- idle state is empty -->
<state id="7"/>
</npc>
</npcs>Every NPC definition starts off with the name and altNames attributes. The name attribute is used as the display name for room descriptions, the altNames attribute is what the grammar parser recognizes. There can be multiple names specified in altName | separated as usual.
Each state can be optionally evaluated using the evaluateUnless attribute, if the local property named is set to true the state will be skipped. This is useful to stop an NPC repeating the same action every time you perform another action in the same location but haven’t yet done what you’re required to do.
Each state has a unique id but this is per NPC so state id="1" can be used again on a different NPC – it’d be a nightmare otherwise wouldn’t it?
Each state consists of a sequence of actions. All actions are run in order.
Tip: use an XML comment to keep track of what states do, e.g.
<!-- comment goes here -->That’s a left angle bracket, exclamation mark and two dashes to start your comment and then two dashes and a right angle bracket to close it.
The NPC configuration sets up the initial values for an NPC. Which room it starts in and which state to start with too.
Most important of these is the initial room where the NPC starts off and the initial state that they start in.
It's been mentioned a few times already but I’ve found it useful to define a limbo room which is not part of the main map. This is somewhere you can place all NPCs that you don’t yet need and put NPCs that you’ve finished with. You summon them into existence or dismiss or banish them using the moveNpc action.
The <configuration> block is also where you can define if the NPC is a trader, combat parameters and patrol circuits but we'll get to that later.
Actions are analogous to consequences for Tasks and Events.
Each <state></state> block can contain zero or more actions – yes, a state may be have no actions at all and thus the NPC will remain completely inert whilst in that state.
Each action is defined as follows:
<action type=”xxx” parameters...>(Optional message)</action>within a <state></state> block.
There are several different types of action. As with events and tasks each action has a specific set of attributes.
Listed below are the specific actions that only NPCs can use do but remember that any event consequence may also be used as an action. You still use <action type=”[consequence type]”… > even if you’re reusing a consequence.
Any condition defined above may also be used as a branching action but you have to define both onSuccess and onFail parameters in addition to the existing parameters the condition already expects. These two parameters define the states to jump to should the condition pass or fail.
This does mean you can use the fireEvent consequence to run an EVENT_TRIGGERED event but there’s not much need as anything you can do there should more or less work directly as an action. It might help with code reuse of course.
<npc id="500" name="Mrs Parsons" altNames="PARSONS|MRS PARSONS" gender="she">
<configuration initialRoom="5" initialState="1">
<proximityHints>You can hear Mrs Parsons whistling a tune :1.| | | |</proximityHints>
</configuration>
<properties>
<property name="loop_exit" value="false"/>
</properties>
<state id="1" value="PASSIVE" evaluateUnless="loop_exit">
<action type="message">Mrs Parsons says, "Sorry to bother you comrade but could you help me with my sink? It's all blocked up ain't it."
</action>
<action type="choice" validResponses="1|2">1) Yes, 2) I'm sorry but I can't at the moment</action>
<action type="setNextState" nextState="4"/>
</state>
<state id="2" value="TASK ACCEPTED">
<action type="addExit" roomId="5" direction="S" toRoom="6" timeCost="0"/>
<action type="message">Mrs Parsons says, "It's through there in me kitchen."</action>
<action type="moveActor" npcId="500" roomId="7"/>
<action type="setNextState" nextState="5"/>
<action type="timeCost" timeCost="1"/>
<action type="runNpcState"/>
</state>
<state id="3" value="COMPLETE">
<action type="message">Mrs Parsons says, "Thanks for unblocking the sink, comrade."</action>
</state>
<state id="4" value="WAITING FOR ACCEPT">
<action type="response" response="1" nextState="2">Mrs Parsons says, "Thanks, comrade."</action>
<action type="response" response="2" nextState="1">
Mrs Parsons says, "I'll tell thems in the telescreen on you! Goldstein!"
</action>
<action type="setProperty" key="loop_exit" value="true"/>
<action type="timeCost" timeCost="1"/>
<action type="runNpcState"/>
</state>
<state id="5" value="WAITING IN KITCHEN"/>
</npc> So on encountering Mrs Parsons she will imediately ask you for help. This is the choice action defined.
Upon executing the choice action the game will show:
Choose: 1) Yes, 2) I’m sorry but I can’t at the moment
then jump to a state 4 to await and then interpret the player response. State 4 is not run until the player responds to the initial choice presented.
Control is returned to the player but the prompt changes to ‘Choose an option: ‘ (by default) and the player must enter 1 or 2. If the player enters anything else it is rejected and the ‘Choose an option: ‘ prompt is displayed again.
Once the player has entered an option state 4 is run to interpret it. State 1 or 2 is chosen depending on which option the player chose and that state is jumped to and executed immediately because of the runNpcState action.
If Yes is chosen state 2 is run. State 2 dynamically adds a new exit from the hallway into Mrs Parsons flat, you can't just go in without being invited. We move Mrs Parsons to her kitchen (roomId="7") and set her state to waiting.
Note that the property ‘loop_exit’ is set before jumping so if state 1 is jumped to again it will actually be skipped this time so we don’t end up in a loop of being given the same choice again and again. The player can back out or do something else.
Remember that some states will only be invoked by a task or other event. Not every state needs to lead to another.
Note on NPC Actions: NPCs do not have a separate action language. They completely reuse the existing Task and Event Consequences and Conditions defined in the system. The only difference is that when used inside an NPC state, conditions require an onSuccess and onFail attribute to cleanly branch the NPC's state machine.
So every event/task consequence above can be used as an action, instead of writing
<consequence type="xxx" ...>
or
<condition type="xxx" ...> You instead write:
<action type="xxx" ...>
or if you want a condition:
<action type="xxx" onSuccess="s1" onFail="s2"> Below is a list of all NPC specific actions - these are the actions that only NPCs can do rather than being a general task or event consequence from above.
| Action | Parameters | |
| 1 | Choice – the NPC offers the player a choice. Choices should be formatted as numbered options in the CDATA text. When a choice is presented to the player the input prompt will change from ‘What next? ’ to ‘Choose an option: ‘ and only numeric input is accepted. The player cannot enter a normal verb/noun style command (well, they can type it in but it won’t accepted). |
type=”choice” validResponses: a pipe separated list of accepted responses, e.g. “1|2|3” for three choices. prompt: The text to show before the choices are presented, by default it will be ‘Choose: ‘ |
| 2 | Response – Interpret the response from a choice action. – the player input to a choice is interpreted. Further states can be run according to that choice. The same response state can be re-used for more than one input from the choice. |
type=”response” response: the response to match the player input nextState: A pipe separated list of NPC states (this NPC only). As usual, if more than one is provided then a random one will be picked so you can randomize outcomes. |
| 3 | Run NPC State – Run the whatever NPC state is currently set (so you
need to set a different state first otherwise you could end up in an
infinite loop). This often goes hand in hand with the response handler action to auto-run the next state. |
type=”runNpcState” npcId: the id of which NPC you wish to run (optional if not set the current NPC is assumed, so this can be used to summon other NPCs and invoke them). nextState: optional, the state to run – if not set then whatever is current in the context. |
| 4 |
Set Next State. Set the next state for any NPC. |
type=”setNextState” npcId: (optional) the id of the NPC whose state you wish to update. If left out the NPC from the current context is inferred. nextState: (optional) a | pipe separated list of NPC states. As usual, if more than one is provided then a random one is chosen. If the attribute is not present whatever the current state (set by a previous action such as response or a checkXXX onSuccess/onFail) is run. |
| 5 | Display Offers – an NPC can offer you something to buy or exchange for something else. Once an item has been offered control returns to player to take up the offer with the BUY or SWAP verbs or indeed walk away. If the NPC is a trader the player may also be able to sell items to them for cash. |
type=”displayOffers” Further offers may be added from items the players sells or swaps. |
| 6 | Set a local property. – set a local property for use with the evaluateUnless attribute. |
type=”setProperty” key: the key of the property to set value: boolean true or false. |
| 7 | Set NPC Distracted |
type=”setDistracted” Use checkDistracted in an action, task or event to check this condition. |
| 8 | Set NPC Status Most NPCs will start their life in STATE_MODE which means they follow the usual state scripting but patrolling NPCs need to be AUTONOMOUS to continue their patrol. A patrolling NPC will switch to STATE_MODE automatically on encountering the player. Your script should switch them back to AUTONOMOUS when the encounter is finished. DEAD is mostly for use when an NPC is in combat mode and will be set automatically if their HP value falls to 0. HOSTILE will be used when NPCs gain the ability to respond to orders. |
type="setNpcStatus" status: must be one of (with exact spelling and capitalisation): AUTONOMOUS, DEAD, HOSTILE, STATE_MODE. |
The Quit Game verb is in fact handled as an NPC. The NPC is brought from the limbo room to the players current room and the initial state invoked. From there on the NPCs state machine handles the actual quit function. So even quit can be tailored to your game.
<?xml version="1.0" encoding="UTF-8"?>
<npcs>
<npc id="99999" name="Quit Game">
<configuration initialRoom="999" initialState="1"/>
<state id="1" value="PASSIVE">
<action type="message">Really quit?</action>
<action type="choice" validResponses="Y|N">Y) Yes N) No</action>
<action type="setNextState" nextState="2"/>
</state>
<state id="2">
<action type="response" response="Y" nextState="4"/>
<action type="response" response="N" nextState="3"/>
<action type="runNpcState" npcId="99999"/>
</state>
<state id="3">
<action type="message">OK</action>
<action type="setNextState" nextState="1"/>
<action type="moveActor" npcId="99999" roomId="999"/>
</state>
<state id="4">
<action type="message">
You look up to the telescreen and realise with sudden tears in your eyes, you love Big Brother!
</action>
<action type="setNextState" npcId="99999" nextState="1"/>
<action type="moveActor" npcId="99999" roomId="999"/>
<action type="pause" amount="2.5"/>
<action type="waitForKey">...Press [Enter] to continue...</action>
<action type="newGame"/>
</state>
</npc>
</npcs>These are now easy to implement for your game. You must first map ASK and/or TELL with their prepositions against the DoTaskHandler in game.xml All you need to implement these is a task against the either the ASK or TELL verb and the Npc you need to respond:
<task id="32600" name="ASK Gandalf about the Ring">
<trigger action="ASK" npcId="50000">
<conditions>
<condition type="checkNoun" nouns=”RING”>
</conditions>
<onSuccess>
<consequence type="runNpcState" npcId=”50000” nextState=”20”>
</onSuccess>
</task>Then the NPC state machine state 20 interprets what is asked:
<state id="20">
<action type="checkNoun" nouns=”RING” onSuccess=”21” onFail=”22”>
</state>
<state id=”21”>
<action type=”message”>Gandalf, urges you not to use it!</sction>
</state>
<state id=”22”>
... more actions....
</state>You can add checks for other nouns in state 22 and cascade through to support knowledge of as many items as you like. Finish with the default ‘I don’t know what you’re talking about’ or whatever is appropriate.
Note that the nouns attribute is plural – it can take a pipe separated list of nouns to recognize so the Npc can respond to multiple different requests in the same way.
Each noun does have to be a known Item within the items.xml file but those items can be virtual so you can ask about a password or even ME.
Note that at the moment TELL can only be used to implement
TELL <npc> ABOUT <object>
Giving an order to an NPC is not yet possible - but is planned for a future release probably using the SAY verb, SAY TO <npc> "quoted text".
When specifying an adverb enhanced verb for use in a task you only need to specify the verb part in the trigger action, e.g. the following trigger specifies LOOK as a supported verb but it is only the LOOK UP adverb enhanced form that is actually supported:
<trigger
action="COMPARE|MATCH|LOOK" actionObject="FILE|FOLDER"
targetObject="CARD|INDEX" targetObjectAdjective="REFERENCE"
roomId="546"/>Here’s the verb definition in game.xml:
<verb synonyms="LOOK" implementation="AdverbHandler" acceptedPrepositions="IN">
<help>
LOOK: Re-describe the current location or
LOOK UP [adjective] <noun> IN [adjective] <noun>; do a comparison of one object with another, look something up in an index,
etc
</help>
<adverbs defaultHandler="LookHandler">
<adverb adverbs="UP" handler="DoTaskHandler">
</adverbs>
</verb>The main handler is AdverbHandler which acts to delegate processing
to a matching subsystem. The defaultHandler specified in the
<adverbs> tag is what to do when there’s no adverb typed, i.e. a
standard LOOK is performed and the current room is re-described.
The only adverb understood for LOOK is UP and when UP is added the
action delegates to the task handling system using DoTaskHandler.
The prepositions specified in this case are only used in the augmented adverb form of the verb.
So you can use: LOOK UP FILE IN INDEX instead of or as well as COMPARE FILE WITH INDEX or even COMPARE FILE AGAINST INDEX. LOOK on its own will still re-describe the room.
Classically in old school Text Adventures you would end up somewhere in darkness, this would mean you can’t see anything so the usual room description output wouldn’t appear. All the player sees is some text saying ‘It’s dark’ or something similar.
This can be achieved by selecting the dark room renderer instead of the default renderer. In practice this simply means adding a tag to the room and mapping the dark room renderer to that tag.
game.xml again provides the definitions for the renderers.
<renderers package="org.happysoft.games.renderers">
<renderer type="DarkRoomRenderer" roomTag="dark" itemTagsNotPresent="light-source-active"/>
</renderers>The DarkRoomRenderer and the default renderer are the only renderers available at the moment.
The DarkRoomRenderer is used on any room that has the tag ‘dark’ unless you are carrying an item or there is an item in the room that has the tag ‘light-source-active’. These tags are configurable of course, you can use whatever you like.
Why not have the tag ‘light-source-active’ on the room I hear you ask? You can do it if you like, it’s up to you. You can manage all of this manually if really want to and dynamically change the room description. This approach is best if you have lots of rooms that need to be treated this way otherwise it’s a lot of scripting to keep everything up to date. Also if the light source is portable there’s no especially easy way of noticing you’ve left a room so how do you update that it’s no longer lit?
At some point you’ll be able to add further renderers of your own devising. I can’t think of many more that would be needed at the moment. Maybe you could add some flavour renderers that add extra flavour to the default description at night or in bad weather or something? You can do this via universal events already of course, I’ll provide an example in the appendix, but a renderer might be a cleaner solution as it is with ‘dark’.
We learned earlier about the <configuration> tag in an NPC definition. This is where specific types of NPC behaviour can defined
Trade is conducted through three main verbs, BUY, SELL and EXCHANGE|SWAP.
These verbs take the form:
BUY x FROM y, SELL x TO y and SWAP x FOR y.
You can also SHOW x TO y and if the NPC is a trader he will make you an offer.
Perhaps the best way to start explaining the configuration for trading NPCs is with a complete example:
<npc id="35000" name="The Market Trader" altNames="TRADER|MARKET TRADER">
<configuration initialRoom="350" initialState="2">
<proximityHints>You can hear a market trader :1.| Someone along the road :1 is shouting a sales pitch.</proximityHints>
<trading enabled="true">
<onTrade onBuy="3" onSell="4" onSwap="4">
<tag name="contraband" onSell="5" onShow="5" refuse="true">Sorry guv, I don't touch inner party stuff.</tag>
<tag name="rare" priceModifier="-1" markup="2">I thought the party detroyed all this stuff. I'll give you :1.</tag>
<tag name="damaged" priceModifier="-1" markup="1">It's seen better days, how's about :1.</tag>
<noPrice>The trader says, "Sorry guv, there's no value in that."</noPrice>
<default>The trader says, "Yeah alright, I'll give you :1 for it."</default>
</onTrade>
<offer itemId="433" price="7">
Keep this secret but I reckon someone of your calibre, you'll find a use for it.
The price is :1.
</offer>
<offer itemId="421" price="5">
You look like the type who'll find this an interesting read.
The price is :1.
</offer>
<offer itemId="20" price="3">
Let it be a light to you in dark places. For :1 of course.
</offer>
</trading>
</configuration>
<state id="1" value="INTRODUCTION">
<action type="message">"Afternoon, comrade. Buy, sell or exchange?"</action>
<action type="setNextState" nextState="2"/>
</state>
<state id="2" value="OFFER">
<action type="displayOffers"/>
<action type="setNextState" nextState="7"/>
</state>
<state id="3" value="BOUGHT">
<action type="message">Don't tell anyone you got it from me, right.</action>
<action type="setNextState" nextState="2"/>
</state>
<state id="4" value="SWAPPED OR SOLD">
<action type="message">OK done. Let's do business again!</action>
<action type="setNextState" nextState="2"/>
</state>
<state id="5" value="CHECK PASSWORD">
<action type="checkGameState" key="passwordHarrySet" onSuccess="7" onFail="6"/>
</state>
<state id="6" value="NO-SALE CONTRABAND">
<action type="message">
The trader looks around then lowers his voice and leans over to you,
"There's too many bleedin' telescreens around here", he gestures slightly towards a telescreen mounted high on a pole at the the end of the market.
"But if you go to the passageway between the tenements north and east of here and say 'Harry sent me' - they'll let you in and my mate'll take it off your hands."
</action>
<action type="setNextState" nextState="7"/>
<action type="setGameState" key="passwordHarrySet" value="true"/>
<action type="runNpcState"/>
</state>
<state id="7" value="BANTER">
<action type="message">
The trader whistles cheerfully. |
The trader hawks his wares. |
The trader watches you with interest. |
The trader promises the best prices around. |
The trader eyes your blue overalls suspiciously. |
| | |
</action>
</state>
</npc>There's lots going on here, let's break it down piece by piece. First off in the <configuration> section we have the <trading> block and set enabled to true. This enables trading for this NPC.
The second thing to notice before we get to the dynamic sales is the list of <offer> elements. These define the list of goods the trader originally has to sell to you. The displayOffers action will list the goods currently on offer as seen in state 2.
So this is where we tackle the more complex looking <onTrade> block:
<onTrade onBuy="3" onSell="4" onSwap="4">
<tag name="contraband" onSell="5" onShow="5" refuse="true">Sorry guv, I don't touch inner party stuff.</tag>
<tag name="rare" priceModifier="-1" markup="2">I thought the party detroyed all this stuff. I'll give you :1.</tag>
<tag name="damaged" priceModifier="-1" markup="1">It's seen better days, how's about :1.</tag>
<noPrice>The trader says, "Sorry guv, there's no value in that."</noPrice>
<default>The trader says, "Yeah alright, I'll give you :1 for it."</default>
</onTrade>Again everything is driven from item tags.
If you don't define at least the <noPrice> and <default> text then your trader will remain curiously silent when you show something for valuation.
(more to come, there's more to defaults and explain the tag elements).
There a few different ways to implement combat, the simplest way is to script the entire thing in NPC states. Here's a full NPC implementation that does this:
<npc id="361" name="The Drunken Prole" initialRoom="361" initialState="1" altNames="PROLE|DRUNK PROLE">
<configuration initialRoom="361" initialState="1"/>
<state id="1" value="PASSIVE">
<action type="message">
A ruddy-faced prole stares into his mug, then looks up at you blearily.
"Buy us a pint, guv?", he says, hopefully.
</action>
<action type="choice" validResponses="1|2">1) Buy him a beer. 2) Refuse.</action>
<action type="setNextState" nextState="2"/>
</state>
<state id="2" value="CHOICE">
<action type="response" response="1" nextState="6"/>
<action type="response" response="2" nextState="100">
He grumbles into his mug saying something about the bleedin' Party. You can see he's turning angry...
Suddenly he lunges at you.
</action>
<action type="setProperty" key="beer_loop_exit" value="true"/>
<action type="runNpcState"/>
</state>
<state id="3" value="HAPPY">
<action type="message">
He drains the remainder of his drink greedily and pulls up the fresh one.
"You're all right, comrade. 'ere you heard about the other market?"
"You ever need proper goods, just go to the passage between the tenements, north of here and say 'Harry sent me'."
</action>
<action type="setNextState" nextState="4"/>
<action type="timeCost" timeCost="2"/>
<action type="runNpcState"/>
</state>
<state id="4" value="DONE"/>
<state id="5" value="DESPAIR">
<action type="message">Nobody's got any bleedin' money these days. He drains the remainder of his beer and stomps out.</action>
<action type="setNextState" nextState="4"/>
<action type="timeCost" timeCost="2"/>
<action type="moveActor" npcId="361" roomId="999"/>
</state>
<state id="6">
<action type="fireEvent" eventId="36100"/>
</state>
<state id="100" value="FIGHT LOOP">
<action type="message">The prole squares up to you and takes a swing.</action>
<!-- Randomly branch into one of several outcomes -->
<action type="setNextState" nextState="101|101|102|102|103|103|104|105|106|106"/>
<action type="timeCost" timeCost="1"/>
<action type="runNpcState"/>
</state>
<!-- Outcome: Player takes damage -->
<state id="101" value="FIGHT OUTCOME">
<action type="message">He lands a heavy blow! You stagger back.</action>
<action type="choice" validResponses="1|2">1) Fight back. 2) Get out of there!</action>
<action type="setNextState" nextState="120"/>
<action type="runNpcState"/>
<action type="timeCost" timeCost="1"/>
</state>
<!-- Outcome: Opponent misses -->
<state id="102" value="FIGHT OUTCOME">
<action type="message">He swings wildly but in his drunkenness misses you completely!</action>
<action type="choice" validResponses="1|2">1) Counter-attack. 2) Slip away.</action>
<action type="setNextState" nextState="120"/>
<action type="runNpcState"/>
<action type="timeCost" timeCost="1"/>
</state>
<!-- Outcome: Opponent stumbles -->
<state id="103" value="FIGHT_OUTCOME">
<action type="message">He stumbles drunkenly, leaving himself open.</action>
<action type="choice" validResponses="1|2">1) Go for the win! 2) Escape while you can.</action>
<action type="setNextState" nextState="120"/>
<action type="runNpcState"/>
<action type="timeCost" timeCost="1"/>
</state>
<!-- Outcome: Mutual struggle -->
<state id="104">
<action type="message">You lock arms in a desperate struggle!</action>
<action type="choice" validResponses="1|2">1) Keep fighting 2) Try to flee.</action>
<action type="setNextState" nextState="120"/>
<action type="runNpcState"/>
<action type="timeCost" timeCost="1"/>
</state>
<!-- Lose -->
<state id="105" value="FIGHT LOSE">
<action type="message">
He beats you to the ground. Darkness closes in...
... Time passes ...
Finally you wake up again, feeling whoozy. Your head aches.
</action>
<action type="moveActor" roomId="391"/> <!-- hospital -->
<action type="setNextState" nextState="1"/>
<action type="timeCost" timeCost="60"/>
</state>
<!-- Win -->
<state id="106" value="FIGHT WIN">
<action type="message">With your return volley you manage to overpower him and knock him to the ground.</action>
<action type="message">
"Alright, alright. You put up a decent fight, comrade."
He picks himself up and returns to the bar.
</action>
<action type="setNextState" nextState="3"/>
<action type="runNpcState"/>
<action type="timeCost" timeCost="1"/>
</state>
<!-- RUN -->
<state id="110" value="FIGHT ESCAPE">
<action type="message">You break away and flee the scene!</action>
<action type="moveActor" roomId="360"/> <!-- outside pub -->
<action type="setNextState" nextState="1"/>
<action type="timeCost" timeCost="2"/>
</state>
<state id="120" value="INTERPRET FIGHT RESPONSE">
<action type="response" response="1" nextState="100"/>
<action type="response" response="2" nextState="110"/>
<action type="runNpcState"/>
</state>
</npc>Here, all I've done is offer continual choices and randomize the outcome until you either give up and run, win or lose and end up in Hospital. Note that you can make it so the player or NPC take damage on each round and either one could potentially die.
Probably more interesting is the ATTACK verb and using weapons but the nice thing is these system aren't mutually exclusive so you can mix traditional combat with the choice based system.
Consider the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<npcs>
<npc id="50000" name="The Security Guard" altNames="GUARD|SECURITY GUARD">
<configuration initialRoom="500" initialState="1">
<combat hp="100" onAttack="30|30|30|31" onDeath="40"/>
</configuration>
<properties>
<property name="loop_exit" value="false"/>
</properties>
<state id="1" value="PASSIVE" evaluateUnless="loop_exit">
<action type="message">A guard steps forward, gun pointed. "Where's your pass?"</action>
<action type="setNextState" nextState="1"/>
<action type="setProperty" key="loop_exit" value="true"/>
</state>
<state id="2">
<action type="message">OK, you can go through.</action>
<action type="setNextState" nextState="9"/>
<action type="addExit" roomId="500" toRoom="501" direction="W" timeCost="5"/>
</state>
<state id="9" value="DONE"/>
<state id="10">
<action type="message">The guard inspects your pass closely.</action>
<action type="checkCarrying" itemId="1" onFail="11" onSuccess="4"/>
</state>
<state id="11" value="Puts you in Hospital">
<action type="message">
No Minitrue pass? So what are you doing here?
The guard clubs you with the butt of his gun and savagely beats you to the the ground. You have no chance of fighting back.
Darkness closes in...
... Time passes ...
</action>
<action type="waitForKey">...Press [Enter] to continue...</action>
<action type="message">Finally you wake up again, feeling whoozy. Your head aches.</action>
<action type="moveActor" roomId="391"/> <!-- hospital -->
<action type="setNextState" nextState="1"/>
<action type="timeCost" timeCost="90"/>
</state>
<state id="20" value="ARRESTED">
<action type="message">
More guards arrive even though you saw no sign of anyone being summoned.
You are roughly handcuffed and transported to the windowless Ministry of Love.
</action>
<action type="moveActor" roomId="900"/> <!-- Ministry of Love -->
<action type="setNextState" nextState="1"/>
<action type="timeCost" timeCost="90"/>
</state>
<state id="30">
<action type="message">
The guard is angered by your attack and raises his iron baton to attack. |
The guard retaliates and swings for you angrily. |
The guard raises his baton and lunges at you.
</action>
<action type="chance" occurs="7" probabilitySpace="10" onSuccess="32" onFail="33"/>
</state>
<state id="31">
<action type="message">
The guard levels at you, I'm going to report this Comrade. Desist or you'll be arrested immediately.
</action>
<action type="points" key="thoughtPolice" amount="20"/>
<action type="setNextState" nextState="50"/>
</state>
<state id="32">
<action type="applyDamage" amount="25"/>
<action type="message">
His heavy baton smashes into your face; your nose might be broken, you stagger back your face swelling. |
The blow connects and you are pushed back violently with the force. Blood streams from your nose.
</action>
<action type="setNextState" nextState="50"/>
</state>
<state id="33">
<action type="message">
In his angry haste he misses! |
You jump out of the way and the guard is thrown off balance by his swing.|
You manage to dodge aside before his blow connects.
</action>
<action type="setNpcState" nextState="50"/>
</state>
<state id="40">
<action type="message">
The Guard slumps to the floor.
You look up at the Telescreen, you know you've crossed a line.
</action>
<action type="points" key="thoughtPolice" amount="101"/>
<action type="setNextState" nextState="9"/>
</state>
<state id="50">
<action type="message">
The guard snarls. |
The guard looks at you angrily. |
The guard warns you. |
The guard smmiles and taps his truncheon.
</action>
</state>
</npc>
</npcs>The only real difference is the presence of the <combat> tag:
<combat hp="100" onAttack="30|30|30|31" onDeath="40"/> We define hit points - so that the protagonist can suffer damage, and onAttack and onDeath states.
The onAttack state is a weighted random, so after an attack 3 out 4 times we'll jump to state 30
and 1 in 4 we jump to state 31.
These states deal the the Guards retaliation, either he attacks back or backs off.
State 30 deals with with the attack and you'll notice an <action type="chance"> action. This defines
a 70% chance of a successful attack - we branch again to states 32 or 33 depending on if the chance comes
up trumps.
In all cases we finish with setNextState to state 50 which defines a passive state that just prints
randomized I'm still fighting type message.
NPCs may patrol back and forth along specific routes. If they encounter the player they will move out of patrol mode to state driven mode and it’s up to the author to put them back to patrol mode once the interaction is finished.
Within the <npc><configuration> tag you need to add the <patrol> tag to define a patrol.
<configuration>
<patrol>
<rooms\>1|2|3|2</rooms>
<states>20</states>
</patrol>
<proximityHints>
You notice the sweep of a flashlight :1; the night guard is near! |
You hear footsteps :1; somebody is nearby! |
You hear the jangle of keys :1; the night guard is nearby!
</proximityHints>
</configuration>The <rooms> tag simply defines a list of rooms to proceed through, one per turn or on player timeout if they don’t make a move. The cycle is round-robin so once the end is reached it starts again from the beginning, hence the route wil be 1 → 2 → 3 → 2 then it loops back to 1 again.
If the player is encountered the NPC will automatically switch to STATE_MODE and its current or initial state will start execute.
The state machine should put the NPC back to its AUTONOMOUS mode using the setNpcStatus action when the encounter is finished.
This does mean you may need to be careful when scripting, you could leave the NPC not motionless in a room somewhere.
The <states> tag above defines a list of states to run at each stop on its route. This can check a room for items and pick things up or do whatever you need.
The proximity hints above aren’t part of the patrol configuration, they are optional and can apply to any NPC (think a market trader yelling his wares) but the engine is aware of the NPCs location in relation to the player and can generate a hint if they are in an adjacent room. The :1 placeholder is used to dynamically generate the direction where the NPC is in the hint text. So the player will something like:
You can hear a traders’ banter along the road to the WEST. You hear footsteps to the NORTHEAST; somebody is nearby!
Here’s an abridged version of the full game.xml definition file. The only thing missing is some verb definitions which you don’t really need all of. Note the game tag has a debug attribute which if set to true allows you to see extra output which may be useful while you’re developing a game.
<?xml version="1.0" encoding="UTF-8"?>
<game debug="false">
<welcome>*** Welcome to 1984: Eyes of the State****</welcome>
<player inventory="1" startRoom="1" timeout="5" skiptime="10" hp="100" onDeathEvent="500000" quitGameNpc="99999"/>
<currency major="Dollars" minor="Cents" symbol="$" initialBalance="0"/>
<gameClock type="24" startTime="13:00"/>
<renderers package="org.happysoft.games.renderers">
<renderer type="DarkRoomRenderer" roomTag="dark" itemTagsNotPresent="light-source-active"/>
</renderers>
<handlerSelectionRules package="org.happysoft.games.action.handler.rules">
<rule name="CheckTargetObjectNotNull" implementation="TargetObjectNotNullRule"/>
<rule name="CheckTargetAcceptsItems" implementation="TargetObjectAcceptsItemsRule"/>
</handlerSelectionRules>
<ignoredWords>GO|THE|A|AN|SOME|OF|YOUR|ONE|TWO|FEW| | </ignoredWords>
<pronouns>IT</pronouns>
<verbs defaultPackage="org.happysoft.games.action.handler">
<verb
synonyms="N|E|S|W|NE|NW|SE|SW|NORTH|SOUTH|EAST|WEST|NORTHEAST|NORTHWEST|SOUTHEAST|SOUTHWEST|U|D|UP|DOWN" handler="DirectionHandler" parameters="1.0">
<help>
Move from the current location in the direction specified
</help>
</verb>
<verb synonyms="LOOK" handler="AdverbHandler" acceptedPrepositions="IN">
<help>
LOOK; Re-describe the current location
LOOK UP [adjective] <noun> IN [adjective] <noun>;look something up in an index for example
</help>
<adverbs defaultHandler="LookHandler">
<adverb adverbs="UP" handler="DoTaskHandler"/>
</adverbs>
</verb>
... All the other verbs ...
</verbs>
</game>We now describe each of the elements that are valid in the game.xml file. There are two further element blocks that aren’t used but are valid which are <properties> and <counts>. These are usefult for testing and debugging puzzles whilst developing.\
Pretty much what it says on the tin – just defines some text that you see at the beginning of the game.
Defines the initial player conditions.
<player inventory="1" startRoom="1" timeout="5" skiptime="10" hp="100" onDeathEvent="500000" quitGameNpc="99999"/>inventory – pipe | separated list of items the player is initially carrying.
startRoom – the room to start the game in
timeout – amount of real world time that passes before a Time passes...
message is output when you don’t type anything.
skipTime – the amount of game time that passes when timeout occurs.
hp – the players initial hit points
onDeathEvent – should the player die in battle this event is fired.
quitGameNpc - this is an NPC that the Quit Game Verb runs to handle the 'Do you really want to quit?' question.\
Definition of the currency for your world.
<currency major="Dollars" minor="Cents" symbol="$" initialBalance="0"/>major – the main unit
minor – the minor unit
symbol – currency symbol, being a text adventure this is pretty well
restricted to usual printable characters but UTF-8 should be OK to use.
InitialBalance – the players starting wallet. Decimals are supported but only two decimal places will be shown.
The type of clock to be used in your game and the initial time when the
game first starts. Usually this will be a 12 or 24 hour clock but you
can define any number of hours for your games’ day.
If you set hours as zero then the clock is disabled. If you set hours as
12 then an am/pm suffix is used and the hours run from 1 to 12.
Otherwise hours run from 0 to hours-1.
Hours always have 60 minutes. I should make this configurable but I
haven’t got around to it yet.
Negative hours don’t mean anything.
\
<gameClock type="24" startTime="13:00"/\>
type – type of clock to use
startTime – the time at the beginning of the game.
Note even though granularity is minutes, game events are timed only to the nearest hour, so you can only specify a startHour and endHour for an interval. But actions are timed in minutes.
The room renderers as discussed in the main text.
Adverb sekection rules.\
This is a list of defined selection rules that can alter the behaviour of a verb plus adverb
combination.
There's currently only a few of these and their use is limited. But see the PUT plus adverb verb
definition for how they're used.
These are words that have no particular semantic meaning to the parser but the player is likely
to type.
These are more or less the same as the articles used for item names but include things like GO
so that the player can type GO NORTH instead of NORTH or N and the parser won't grumble.
The words we can use for IT. This word basically takes on the value of the most recent action object typed in a sentence.
A block level element defining any number of <property/> sub-elements, e.g.
<properties>
<property key=”homeDeskUnlocked” value=”true”/>
</properties>
This can be used to pre-set some game states. Mainly useful for testing
but you can use it to set any game state if you so need to.
Like properties but for game counters. It can contain any number of <count> sub-elements, e.g.
<counts>
<count key=”thoughtPoliceSuspicion” count=”90”/>
</counts>
Again, certainly useful for testing but can be used to set any counts
you use in advance.
This contains all the verb definitions with their prepositions and adverb forms as discussed throughout the text.
Following is a list of verbs the engine currently understands and the handlers used to implement them.
Any handler may be reused for a new verb or with an adverb augmented verb. Just define it in game.xml and it will be available for your game.
| Verb | Handler Class |
| N|E|S|W NE|NW|SE|SW NORTH|SOUTH|EAST|WEST NORTHEAST|NORTHWEST SOUTHEAST|SOUTHWEST U|D UP|DOWN |
DirectionHandler DirectionHandler moves the player to a new location in the given
compass direction and increments the game clock by the amount of time
indicated by the room definitions exit list. |
| LOOK | AdverbHandler where LOOK → LookHandler LOOK UP → DoTaskHandler |
| ATTACK|KILL |
AttackHandler AttackHandler instigates an attack on an NPC optionally with a weapon
(if no weapon specified it is assumed you’re attacking with your bare
hands). |
| EXAMINE|EXAM |
ExamineHandler ExamineHandler describes an object in more detail and may invoke an onExamined event if one is defined on the object. |
| INVENTORY|INV |
InventoryHandler InventoryHandler lists all items the player is carrying along with their value. |
| GET|TAKE |
TakeHandler Pick up an object and may invoke an onTaken event if one is defined on the item. |
| DROP |
DropHandler Drop the given item if the player is carrying it. |
| PUT |
PutHandler PutHandler specifically places an item in or on another item. The ‘target’ item must be a container. |
| SELL |
SellHandler SellHandler initiates a sale with a trader NPC. |
| BUY |
BuyHandler BuyHandler initiates a transaction with a trader NPC. |
| SWAP|EXCHANGE|OFFER | SwapHandler |
| WAIT|WA | WaitHandler |
| TIME | TimeHandler |
| SAVE | SaveHandler |
| LOAD | LoadHandler |
| HELP | HelpHandler |
| ATTACH | DoTaskHandler |
| DETACH | DoTaskHandler |
| CALL | DoTaskHandler |
| OPEN | DoTaskHandler |
| CLOSE | DoTaskHandler |
| COMPARE|MATCH | DoTaskHandler |
| GIVE | DoTaskHandler |
| JEMMY|PRISE|SMASH | DoTaskHandler |
| LOCK | DoTaskHandler |
| UNLOCK | DoTaskHandler |
| PAY | DoTaskHandler <NPC> |
| PICK|PICK UP | AdverbHandler (UP) where PICK UP→ TakeHandler PICK → DoTaskHandler |
| PLUG | DoTaskHandler |
| UNPLUG | DoTaskHandler |
| PRESS | DoTaskHandler |
| RUN | AdverbHandler RUN <direction> → DirectionHandler with a parameter |
| SAY |
DoTaskHandler The Hobbit did it back in 1983 so I should do it! e.g. SAY TO THORIN “CARRY ME” and Thorin picks you up so you can reach a Window. |
| SHOW | DoTaskHandler |
| SWITCH | AdverbHandler SWITCH ON|OFF <item> → ItemStateCangeHandler (with the restriction that the item must have an ‘activateable’ tag or whatever tag you define in the adverb) |
| THROW | DoTaskHandler |
| UNBLOCK | DoTaskHandler |
| UNBOLT | DoTaskHandler |
Internationalisation is built in via the messages.properties files.
Clearly you can define whatever messages you like within the xml files, so tasks will output whatever messages you wish but there are certain system level messages for when you perform (and maybe fail to perform) an action. Say you wish to TAKE BOTTLE but there’s no BOTTLE available anywhere to take, you need an error message saying that the system can’t find such an item.
This is where the messages.properties file in the distribution comes in.
By default messages.properties is used (where I’ve provided standard UK English versions of most of the system messages – some are yet to be done admittedly) but if you provide a locale specific version such as messages_en_GB.properties that will be used if the players JVM matches it so your game can run in French or German or even US English.
The naming follows the standard Java internationalisation rules, we
start with
messages.properties - then we can become language specific with
messages_en.properties - and finally we can become country specific
with
messages_en_GB.properties
You may define any locale you wish and the player JVM will pick it up.
If the players locale doesn’t match the one you define the Java runtime
will pick the best match it can find.
Following is the current set of standard system messages:
DIRECTIONS_NONE = "None."
DIRECTIONS_TO = " to "
ITEM_PRICE = "It is worth :1."
ITEM_NO_PRICE = "It is of little value."
ITEM_NOT_WEAPON = "You can't use that as a weapon."
ITEMS_CARRYING = "You are carrying: "
ITEMS_NOTHING = "Nothing."
ITEMS_NOT_CARRYING = "You are not carrying the :1."
ITEMS_YOU_CANT_TAKE_THAT = "You can't take that!"
ITEMS_MAYBE_OPEN_IT_FIRST = "It needs to be opened first."
ITEMS_YOU_CANT_SEE_THAT_ITEM = "You can't see that item here."
ITEMS_YOU_CANT_SEE_ADJECTIVE_NOUN = "You can't see :1 :2."
ITEMS_WHAT_DO_YOU_WANT_TO = "What do you want to :1?"
ITEMS_THATS_THE_WRONG = "That's the wrong :1."
ITEMS_SPECIFY_WHICH = "Specify which :1 to use."
ITEMS_YOU_DONT_HAVE_THE_CORRECT = "You don't have the correct :1."
ITEM_YOU_CANT_DO_THAT = "You can't do that with :1."
TRADE_ITEM_NOT_OFFERED = "The :1 :2 not offered for sale or trade."
TRADE_SPECIFY_WHAT_TO_TRADE = "You need to specify what you need and
what you are willing to trade for it."
TRADE_WHAT_DO_YOU_WANT_TO_TRADE = "I'm not sure what it is you want to
trade."
TRADE_INSUFFICIENT_FUNDS = "You don't have enough money to buy that."
TRADE_REJECTED = "Nah, it ain't worth it for that."
TRADE_ZERO_VALUE = "You kidding me? That's a worthless piece of junk."
TRADE_OFFER = ":1 offers you: :2."
NPCS_NPC_NOT_A_TRADER = ":1 is not interested in trading."
NPCS_NPC_NOT_HERE = ":1 is not here."
NPCS_NO_NPC_HERE = "There's nobody here."
NPCS_NO_TRADER_HERE = "There's no trader present."
NPCS_SPECIFY_WHO_YOU_ARE_INTERACTING_WITH = "Specify who you are
interacting with."
NPCS_NPC_IS_DEAD = ":1 is dead."
NPCS_NPC_NO_COMBAT = "There's no point in attacking :1."
NPCS_PRESENT_DARK = "You can sense someone is here.|You think you can
hear someone in the dark.|You can feel a presence, is there somebody
nearby?"
USAGE = "Use: :1"
TIME_PASSES = "... Time passes ..."
GAME_SAVED = "Game saved."
SAVE_GAME_FAILED = "Save game failed: :1"
LOAD_GAME_FAILED = "Load game failed: :1"
I_DONT_UNDERSTAND = "I don't understand ':1'"
I_DONT_UNDERSTAND_USE = "I don't understand that use of :1."
THERES_NO_POINT_IN_DOING_THAT = "There's no point in doing that."
NOBODY_IS_INTERESTED_IN_WHAT_YOU_SAY = "Nobody is interested in what you
say."
SPECIFY_AN_ACTION = "Specify an action with :1."
CLOCK_DISABLED = "Game clock is disabled."
YOU_CANT_GO_THAT_WAY = "You can't go that way!"
YOU_CAN_SEE = "You can see: "
IT_IS_DARK = "It is dark, you can't see anything."
YOU_ARE = "You are "
YOU_CAN_ALSO_SEE = "You can also see: "
EXITS_ARE = "Exits are: "
EXITS_PRESENT_DARK = "You can sense exits around but it is difficult to
discern anything clearly.|There must be some exits but it's too dark to
see."
MOVE_IN_DARK = "You grope your way :1.|You feel your way :1.|You
cautiously edge your way to the :1."
YOUR_CURRENT_BALANCE = "Your current balance is :1"
CURRENT_TIME = "The current time is :1"
COMBAT_NO_WEAPON = "You attack :1."
COMBAT_WITH_WEAPON = "You attack :1 with :2."
COMBAT_WITH_WEAPON_MISS = "You attack :1 with :2 but they dodge."
PRESS_ANY_KEY = "Press any key to continue..."
WHAT_NEXT = "What next? "
CHOOSE_AN_OPTION = "Choose an option: "