Designing Games with Xconq - A Tutorial Example

Go to the first, previous, next, last section, table of contents.

## A Tutorial Example

Before delving into the depths of the language, let's look at an example. Suppose you just finished watching a Godzilla movie, complete with roaring monsters, panic-stricken mobs, fire trucks putting out flames, and so forth, and were inspired to design a game around this theme.

- Basic Definitions
- Adding Movement
- Buildings and Rubble Piles
- Human Units
- The Scenario

### Basic Definitions

Start by opening up a file, calling it something like `g-vs-t.g`, or some other name appropriate for your type of machine, and then type this into it:

    (game-module "g-vs-t"
      (title "Godzilla vs Tokyo")
      (blurb "Godzilla stomps on Tokyo")
    )

This is a GDL form. It declares the name of the game to be `"g-vs-t"`, gives it a title that prospective players will see in menus, plus a short description or blurb. The blurb should tell prospective players what the game is all about, perhaps whether it is simple or complex, or whether it is one-player or multi-player. Both title and blurb are examples of properties, which are like slots in structures.

The `game-module` form is optional but recommended; some interfaces use it to add the game to a list of games that players can choose from.

The general syntax of `game-module` form is similar to that used by nearly all GDL forms; it amounts to a definition of an "object" (such as a game module or a unit type) with properties (such as name, description, speed, etc). Some properties are required, and appear at fixed positions, while others are optional and can be specified in any order, so they are introduced by name. The general format, then, looks like

    (<object> ... <required properties> ...
      ...
      (<property name> <property value>)
      ...
      )

There are very few exceptions to this general syntax rule.

(People often have trouble with parentheses in Lisp, but if you follow the same kinds of indentation rules that you always use in C or Pascal, then you will encounter no additional trouble. Also, many editors such as Emacs are intelligent enough to indicate when parentheses match, and automatically do proper indentation.)

Now the first thing you'll need is a monster. In Xconq, each unit has a type, and you define the characteristics attached to the type.

    (unit-type monster)

This declares a new unit type named `monster`, but says nothing else about it. Let's use this more interesting form instead:

    (unit-type monster
      (image-name "monster")
      (help "crushes, crumbles, and chomps")
      (start-with 1)
    )

This shows the usual way of describing the monster. In this case, `image-name` is a property that specifies the name of the icon that will be used to display a monster. (Xconq comes with a library of over 700 icons, of which "monster"; it looks a little like Godzilla.) The property `start-with` says that each side should start out with one monster. This isn't quite right, because there should only be one side with a monster, and this will give each side a monster to start out with, but we'll see how to fix that later on.

We also need at least one type of terrain for the world:

    (terrain-type street (image-name "gray"))

These two forms are actually sufficient by themselves to start up a game. (Go ahead and try it.) However, you'll notice that the game is not very interesting. Although each player gets a monster, and the world consists of all-street terrain, nobody can actually do anything, and turns just whiz by, since the defaults basically turn off all possible actions.

### Adding Movement

OK, let's give the monsters the ability to act by putting this form into the file:

    (add monster acp-per-turn 4)

The `add` form is very useful; it says to modify the existing type named `monster`, setting the property `acp-per-turn` to 4, overwriting whatever value might have been there previously. The `acp-per-turn` property gives the monster the ability to act, up to 4 actions in each turn. By default, the ability to act is 1-1 with the speed of the unit, so the monster can also move into a new cell 4 times each turn. If you run the game now, you will find that your monster can now get around just fine.

Why 4? Actually, at this point the exact value doesn't matter, since nothing else is happening. If the speed is 1, then the turns go faster; if the speed is 10, then they go slower and more action happens in a single turn. In a complete design however, the exact speed of each unit can be a critical design parameter, and for this game, I figured that a speed of 4 allowed a monster to cover several cells in a hurry while not being able to get too far. Also, I'm planning to make panic-stricken mobs have a speed of 1, which is the slowest possible. Making actions 1-1 with speed is usually the right thing to do, since then a player will get to move 4 times each turn (later on we will see reasons for other combinations of values).

The `add` form works on most types of objects. It has the general syntax

    (add <type(s)/object(s)> <property name> <value(s)>)

The type or object may be a list, in which the value is either given to all members of the list, or if it is a list itself, then the list of values is matched up with the list of types.

### Buildings and Rubble Piles

To give the monster something to do besides walk around, add buildings as a new unit type:

    (unit-type building (image-name "city20"))

    (table independent-density (building street 500))

The `building` type uses an icon that is normally used for a 20th-century city, but it has the right look. The `independent-density` table says how many buildings will be scattered across in the world. The `table` form consists of the name of the table followed by one or several three-part lists; the two indexes into the table, and a value. In this case, one index is a unit type `building`, the other is a terrain type `street`, and the value is `500`, which means that we will get about 500 buildings placed on a 100x100 world (look up the definition of this table in the index). You need some for testing purposes, otherwise you won't see any when you start up the game.

We're going to let buildings default to not being able to do anything, since that seems like a reasonable behavior for buildings (although Baba Yaga's hut might be fun...).

By default, buildings act strictly as obstacles; monsters cannot touch them, push them out of the way, or walk over them. In real(?) life of course, monsters hit buildings, so we have to define a sort of combat.

    (table hit-chance
      (monster building 90)
      (building monster 10)
      )

    (table damage
      (monster building 1)
      (building monster 3)
      )

    (add (monster building) hp-max (100 3))

The `hit-chance` and `damage` tables are the two basic tables defining combat. The hit chance is simply the percent chance that an attack will succeed, while the damage is the number of hit points that will be lost in a successful attack. The unit property `hp-max` is the maximum number of hit points that a unit can have, and by default, that is also what units normally start with.

Note that the `add` form allows lists in addition to single types and values, in which case it just matches up the two lists. The `add` tries to be smart about this sort of thing; see its official definition for all the possibilities.

The net effect of these three forms is to say that a monster has a 90% chance of hitting a building and causing 1 hp of damage; three such hits destroy the building. A monster's knuckle might occasionally be skinned doing this; a 10% chance of 3/100 hp damage is not usually dangerous, and feels a little more realistic without complicating things for the player.

Now you can start up a game, and have your monster go over and bash on buildings. Simulated wanton destruction!

By default, a destroyed building vanishes, leaving only empty terrain behind. If you want to leave an obstacle, define a new unit type and let the destroyed building turn into it:

    (unit-type rubble-pile)

    (add building wrecked-type rubble-pile)

(Incidentally, you need to add the unit-type before any tables are defined, so that Xconq knows how big the tables need to be.)

In practice, you have to be careful to define the behavior of rubble piles. What happens when a monster hits a rubble pile? Can the rubble pile be cleared away? Does it affect movement? Try these things in a game now and see what happens; sometimes the behavior will be sensible, and sometimes not.

For instance, you will observe that the default behavior is for the rubble pile to be an impenetrable obstacle! The monster can't hit it, and can't stand on it, and in fact can't do anything at all. OK, let's fix it. Monsters are agile enough to climb over all sorts of things, so the right thing is to let the monster co-occupy the cell that the rubble pile is in. The default is to only allow one unit in a cell, but this can be changed:

    (table unit-size-in-terrain (rubble-pile t* 0))

This says that while all other units have a size of 1, rubble piles only have a size of 0. By default, each terrain type has a capacity of 1, so this allows one unit and any number of rubble piles to stack together in a cell.

If you try this out, you'll find that the monster can now cross over rubble piles, but still has to bash buildings in order to get them out of the way.

Incidentally, it can cause problems to set a unit size to zero, because it allows infinite stacking. Since buildings and rubble piles don't move, there will never be more than one in a cell, but Xconq will happily let hundreds of units share the same cell, which works, but causes no end of headaches for players confronted with overloaded displays.

### Human Units

Now you've got an "interactive experience" but no game; there's no challenge or goal. You could maybe make a two-or-more-player game where the players race to see who can flatten the mostest the fastest, but that's still not too interesting to anyone past the age of 5. Instead, we need to make some units for the people bravely (or not so bravely) resisting the monster's depredations:

    (unit-type mob (name "panic-stricken mob") (image-name "mob"))
    (unit-type |fire truck| (image-name "firetruck"))
    (unit-type |national guard| (image-name "soldiers"))

Note that a type's name may have an embedded space, but then you have to put vertical bars around the whole symbol. Things are starting to get complicated, so let's define some shorter synonyms:

    (define f |fire truck|)
    (define g |national guard|)

    (define humans (mob f g))

You can use the newly defined symbols `f` and `g` anywhere in place of the original type names. The symbol `humans` is a list of types, and will be useful in filling several propertys at once.

As with monsters, all these new units should be able to move:

    (add humans acp-per-turn (1 6 2))

The speeds here are adjusted so that monsters can chase and run down (and presumably trample to smithereens) mobs and guards, but fire trucks will be able to race away.

Also note the use of a three-element list that matches up with the three elements in the `humans` list. This is a very useful features of GDL, and used heavily. It can also be a problem, since if you add or remove elements from the list `humans`, every list that it is supposed to match up with also has to change. Fortunately, Xconq will tell you if any lists do not match up because they are of different lengths.

We still need to define some interaction, since monsters and humans can make faces at each other, and get in each other's way, but otherwise cannot interact.

    (table hit-chance add
      (monster humans 50)
      (humans monster (0 10 70))
      )

This time we have to say "add table" because we've already defined the `hit-chance` table and now just want to augment it. (Be sure to place this form after the first `hit-chance` table definition.)

As with the addition of properties, we can use a list in place of a single type.

Last but not least, we need a scorekeeper to say how winning and losing will happen. This is a simple(-minded?) game, so a standard type will be sufficient:

    (scorekeeper (do last-side-wins))

The `do` property of a scorekeeper may include some rather elaborate tests, but all we want to is to say that the last side left standing should be the winner, and the symbol `last-side-wins` does just that.

There might be a bit of a problem with this in practice, since in order to win, the monster has to stomp on all the humans, including fire trucks. But fire trucks can always outrun the monster, and cannot attack it directly either, which leads to a stalemate. You can fix this by zeroing the point value of fire trucks:

    (add f point-value 0)

Now, when all the mobs and guards have been stomped, the monster wins automatically, no matter how many fire trucks are left.

### The Scenario

As it now stands, your game design requires Xconq to generate all kinds of stuff randomly, such as the initial set of units, terrain, and so forth. However, we are doing a monster movie, so random combinations of monsters and people and terrain don't usually make sense. Instead of trying to define a "reasonable" random setup, we should define a scenario, either by starting a random game, modifying, and saving it, or by text editing. Since online scenario creation is hard to describe in the manual, let's do it with GDL instead.

To define a scenario, we generally need three things: sides, units, and terrain. Now the basic monster movie idea puts one monster up against a bunch of people acting together, so that suggests two sides:

    (side 1)

    (side 2 (name "Tokyo") (adjective "Japanese"))

The `1` and `2` identify the two sides uniquely, since we'll have to match units up with them in a moment. The side that plays the monster is really a convenience; players should just be aware of the one monster unit, so we don't need any sort of names. The other side has many units, which should be qualified as `"Japanese"`, and the side as a whole really represents the city of Tokyo, so use that for the side's name.

Now for the units:

    (unit monster (s 1) (n "Godzilla"))

    (unit f (s 2))
    (unit f (s 2))

    (building 9 10 2)

    (define b building)  ; abbreviate for compactness' sake

    (b 10 10 2)
    (b 11 10 2 (n "K-Mart"))
    (b 12 12 2 (n "Tokyo Hilton"))
    (b 13 12 2 (n "Hideyoshi's Rice Farm"))
    (b 14 12 2 (n "Apple Japan"))
    ;; ... need lots of buildings ...

This example shows two syntaxes for defining units: the first is introduced by the symbol `unit` and requires only a unit type (or an id, see the definition in xxx), while the second is introduced by the unit type name itself and requires a position and side. The second form is more compact and thus suitable for setting up large numbers of units, while the first form is more flexible, and can be used to modify an already-created unit. In both cases, the required data may be followed by optional properties in the usual way.

Also, since the word "building" is a little longwinded, I defined the symbol "b" to evaluate to "building". GDL has very few predefined variables, so you can use almost anything, including weird stuff like "&" and "=". Property names like `s` and `n` are NOT predefined variables, so you can use those too if you like.

At this point, you should have a basic game scenario, with one player being Godzilla, and the other trying to keep it from running amuck and flattening all of Tokyo. Have fun!

You can enhance this scenario in all kinds of ways, depending on how ambitious you want to get. Given the basic silliness of the premise, though, it would be more worthwhile to enhance the silliness and speed up the pace, rather than to add features and details. For instance, name the buildings after all the laughingstock places you know of in your own town.

To see where you could go with this, look at the library's `monster` game and its `tokyo` scenario, which include fires, different kinds of terrain, and other goodies.

Go to the first, previous, next, last section, table of contents.