Take the 2-minute tour ×
Game Development Stack Exchange is a question and answer site for professional and independent game developers. It's 100% free, no registration required.

I'm thinking of writing a small text-based adventure game, but I'm not particularly sure how I should design the world from a technical standpoint.

My first thought is to do it in XML, designed something like the following. Apologies for the huge pile of XML, but I felt it important to fully explain what I'm doing.

<level>
    <start>
        <!-- start in kitchen with empty inventory -->
        <room>Kitchen</room>
        <inventory></inventory>
    </start>
    <rooms>
        <room>
            <name>Kitchen</name>
            <description>A small kitchen that looks like it hasn't been used in a while. It has a table in the middle, and there are some cupboards. There is a door to the north, which leads to the garden.</description>
            <!-- IDs of the objects the room contains -->
            <objects>
                <object>Cupboards</object>
                <object>Knife</object>
                <object>Batteries</object>
            </objects>
            </room>
        <room>
            <name>Garden</name>
            <description>The garden is wild and full of prickly bushes. To the north there is a path, which leads into the trees. To the south there is a house.</description>
            <objects>
            </objects>
        </room>
        <room>
            <name>Woods</name>
            <description>The woods are quite dark, with little light bleeding in from the garden. It is eerily quiet.</description>
            <objects>
                <object>Trees01</object>
            </objects>
        </room>
    </rooms>
    <doors>
        <!--
            a door isn't necessarily a door.
            each door has a type, i.e. "There is a <type> leading to..."
            from and to are references the rooms that this door joins.
            direction specifies the direction (N,S,E,W,Up,Down) from <from> to <to>
        -->
        <door>
            <type>door</type>
            <direction>N</direction>
            <from>Kitchen</from>
            <to>Garden</to>
        </door>
        <door>
            <type>path</type>
            <direction>N</direction>
            <from>Garden</type>
            <to>Woods</type>
        </door>
    </doors>
    <variables>
        <!-- variables set by actions -->
        <variable name="cupboard_open">0</variable>
    </variables>
    <objects>
        <!-- definitions for objects -->
        <object>
            <name>Trees01</name>
            <displayName>Trees</displayName>
            <actions>
                <!-- any actions not defined will show the default failure message -->
                <action>
                    <command>EXAMINE</command>
                    <message>The trees are tall and thick. There aren't any low branches, so it'd be difficult to climb them.</message>
                </action>
            </actions>
        </object>
        <object>
            <name>Cupboards</name>
            <displayName>Cupboards</displayName>
            <actions>
                <action>
                    <!-- requirements make the command only work when they are met -->
                    <requirements>
                        <!-- equivilent of "if(cupboard_open == 1)" -->
                        <require operation="equal" value="1">cupboard_open</require>
                    </requirements>
                    <command>EXAMINE</command>
                    <!-- fail message is the message displayed when the requirements aren't met -->
                    <failMessage>The cupboard is closed.</failMessage>
                    <message>The cupboard contains some batteires.</message>
                </action>
                <action>
                    <requirements>
                        <require operation="equal" value="0">cupboard_open</require>
                    </requirements>
                    <command>OPEN</command>
                    <failMessage>The cupboard is already open.</failMessage>
                    <message>You open the cupboard. It contains some batteries.</message>
                    <!-- assigns is a list of operations performed on variables when the action succeeds -->
                    <assigns>
                        <assign operation="set" value="1">cupboard_open</assign>
                    </assigns>
                </action>
                <action>
                    <requirements>
                        <require operation="equal" value="1">cupboard_open</require>
                    </requirements>
                    <command>CLOSE</command>
                    <failMessage>The cupboard is already closed.</failMessage>
                    <message>You closed the cupboard./message>
                    <assigns>
                        <assign operation="set" value="0">cupboard_open</assign>
                    </assigns>
                </action>
            </actions>
        </object>
        <object>
            <name>Batteries</name>
            <displayName>Batteries</displayName>
            <!-- by setting inventory to non-zero, we can put it in our bag -->
            <inventory>1</inventory>
            <actions>
                <action>
                    <requirements>
                        <require operation="equal" value="1">cupboard_open</require>
                    </requirements>
                    <command>GET</command>
                    <!-- failMessage isn't required here, it'll just show the usual "You can't see any <blank>." message -->
                    <message>You picked up the batteries.</message>
                </action>
            </actions>
        </object>
    </objects>
</level>

Obviously there'd need to be more to it than this. Interaction with people and enemies as well as death and completion are necessary additions. Since the XML is quite difficult to work with, I'd probably create some sort of world editor.

I'd like to know if this method has any downfalls, and if there's a "better" or more standard way of doing it.

share|improve this question
3  
Personally I wouldn't treat XML as anything more than a serialization format. If you abstract out the "somehow I'm going to read and write this to disk" question (using something like XML, JSON, protocol buffers, custom binary format, whatever), then the question becomes "what data do I need to store", which is something only you can really answer depending on what your game requirements are. –  Tetrad Nov 24 '11 at 17:34
    
Good point. However, I've seen games use styles like this before and they've turned out to be really restrictive. In this case, though, the game flow and logic is quite simple, so it might work well and save me from implementing a scripting engine. I'm primarily interested in whether such a fixed structure (separate rooms, doors, objects, variables in a definition file somewhere) is viable or not. –  Polynomial Nov 24 '11 at 17:39
    
Trying not to echo Tetrad but if you're planning on making a world editor (which I would suggest unless the game is going to be very short) then your file format doesn't make any difference since you'll be working with it in the editor, versus hard coding the rooms. –  Mike C Nov 24 '11 at 18:44

1 Answer 1

up vote 12 down vote accepted

If you're not completely wedded to C#, then the "more standard" way of doing this is to use one of the many text adventure creation tools which already exist to help people make exactly this kind of game. These tools give you an already-functioning parser, handling for death, save/restore/undo, character interaction, and other similar standard bits of text adventure functionality. Right now, the most popular authoring systems are Inform, and TADS (though there are a half dozen others available as well)

Inform can compile down into most of the Z Machine virtual machine instruction sets used by Infocom games, or into the more recent glulx virtual machine instruction sets. TADS, on the other hand, compiles down into its own virtual machine code.

Either type of binary can be run by most modern interactive fiction interpreters (in the old days, you often needed separate interpreters for TADS games from ZMachine games from glulx games. But thankfully, those days are basically over now.) Interpreters are available for just about any platform you'd want; Mac/PC/Linux/BSD/iOS/Android/Kindle/browser/etc. So you've already got cross-platform well and truly taken care of.

For most platforms, the currently-recommended interpreter is Gargoyle, but there are plenty of others, so do feel free to experiment.

Coding in Inform (especially the latest version) takes a little bit to get used to, since it's marketing itself more toward authors than toward engineers, and so its syntax looks weird and almost conversational. In Inform 7's syntax, your example would look like this:

"My Game" by Polynomial

Kitchen is a room. "A small kitchen that looks like it hasn't been used in a 
while. It has a table in the middle, and there are some cupboards. There is a 
door to the north, which leads to the garden."

In the Kitchen is a knife and some cupboards.  The cupboards are fixed in 
place and closed and openable.  In the cupboards are some batteries.

Garden is north of Kitchen. "The garden is wild and full of prickly bushes. 
To the north there is a path, which leads into the trees. To the south there 
is a house."

Woods is north of Garden.  "The woods are quite dark, with little light bleeding 
in from the garden. It is eerily quiet."  

Trees are scenery in the Woods.  "The trees are tall and thick. There aren't any 
low branches, so it'd be difficult to climb them."

Whereas TADS looks more like a traditional programming language, and the same game in TADS looks like this:

#charset "us-ascii"
#include <adv3.h>
gameMain: GameMainDef
    initialPlayerChar = me
;
versionInfo: GameID
    name = 'My Game'
    byline = 'by Polynomial'
;
startroom: Room                  /* we could call this anything we liked */ 
    roomName = 'Kitchen'         /* the displayed "name" of the room */ 
    desc = "A small kitchen that looks like it hasn't been used 
            in a while. It has a table in the middle, and there 
            are some cupboards. There is a door to the north, 
            which leads to the garden." 
    north = garden         /* where 'north' will take us */ 
; 

+me: Actor
; 

cupboards: OpenableContainer
    vocabWords = 'cupboard/cupboards' 
    name = 'cupboards' 
    isPlural = true
    location = startroom 
; 
battery: Thing
    name = 'battery'
    location = cupboards
;
knife: Thing
    name = 'knife'
    location = startroom
;
garden: Room
    roomName = 'Garden'
    desc = "The garden is wild and full of prickly bushes. To the 
            north there is a path, which leads into the trees. To 
            the south there is a house." 
    north = woods
    south = startroom
; 
woods: Room
    roomName = 'Woods'
    desc = "The woods are quite dark, with little light bleeding 
            in from the garden. It is eerily quiet."
    south = garden
;
trees: Decoration
    desc = "The trees are tall and thick. There aren't any low 
            branches, so it'd be difficult to climb them."
    location = woods
;

Both systems are freely available, very frequently used, and have copious amounts of tutorial documentation (available from the links I gave above), so it's worth checking out both of them and picking the one you prefer.

Note that the two systems do have subtly different standard behaviours (although both may be modified). Here's a screenshot of the game being played, as compiled from the Inform source:

Inform screenshot

And here's one from the game being played (inside a terminal – typography can be a lot nicer than this), as compiled from the Tads source:

TADS3 screenshot

Interesting points to note: TADS gives you a 'score' display in the top right by default (but you can turn it off), while Inform doesn't (but you can turn it on). Inform will by default tell you open/closed states of items in the room description, Tads won't. Tads tends to automatically take actions for you in order to carry out player commands (unless you tell it not to), where Inform tends not to (unless you tell it to).

Either one can be used to make any sort of game (as they're both highly configurable), but Inform is more structured toward producing modern-style interactive fiction (often with minimal puzzles and more narrative in style), where TADS is more structured toward producing old-style text adventures (often strongly focused on puzzles and rigorously defining the mechanics of the game's world model).

share|improve this answer
    
this is very cool and informative, but imo doesn't answer the question. I was going to ask basically this exact same question. I'd like to know more about whether or not this XML is a valid approach or if there are any pitfalls or weaknesses it would have. –  DLeh Dec 1 at 4:55
    
@DLeh The question was "I'd like to know if this method has any downfalls, and if there's a "better" or more standard way of doing it" This answer provides the better-and-more-standard-way-of-doing-it. –  Trevor Powell Dec 1 at 23:13
    
But since you asked about "pitfalls and weaknesses": The Inform implementation is 19 lines long. The TADS example is 40 lines long. The XML implementation requires 126 lines (and would be even longer if it was word-wrapped at 80 columns and contained whitespace for legibility, the way that the Inform and TADS implementations do). –  Trevor Powell Dec 2 at 0:15
    
In addition to being much shorter, the Inform and TADS examples also support more features. For example, in both of them you can put the knife into the cupboards, which isn't supported at all in the XML version. –  Trevor Powell Dec 2 at 0:17
    
It's also worth noting that the XML version is baking the cupboards' contents into the cupboards' description. That is, there's a hardcoded message for what to print when opening or looking at the (open) cupboards, which tells you that there are batteries inside. But what if the player has already taken the batteries? The XML version will tell you that there are batteries inside (because that's the only string it has available to display), while the Inform and TADS versions will tell you that the cupboards are empty. –  Trevor Powell Dec 2 at 0:18

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.