Thursday, October 7, 2010

Let's Make Us A Game! -- Setting Up Some Obstacles

If you're just tuning in, we're designing and programming a simple adventure game using Inform 7.  In our previous installments, we came up with a general adventure game concept, mapped out a small world and implemented its rooms and locations.

Now it's time to think about making a story, and a game, out of it.  One of the driving aspects of any story is conflict -- it's what gets in the way of the protagonist's goal that makes for interesting drama.  In adventure games, such conflicts naturally take the form of puzzles and physical obstacles.  This is not to say that interactive fiction should not aspire to more interesting themes -- tales of interpersonal and inner conflict constitute much of our species' conventional literature -- but for our purposes here, some basic physical obstacles will do.

If we're following principles of coherent game design, we should endeavor to make our obstacles organic to the environment and the story.  Our project concerns a puppy named Riley and his efforts to obtain the family's turkey dinner; with that in mind, we should not suddenly introduce pirates, piranhas, paradox or PLUGH into the mix for the sake of a puzzle.  One decision we have already made concerns Riley's size -- he is a puppy, with little physical power aside from his remarkable jumping ability.  We have also mapped out an environment that includes a back yard and a tall dinner table.  So let's see what obstacles we can fit into the established map.

As of our last installment, the back yard connects to the hallway to the south directly, and Riley has been free to move between the two locations.  It seems like we ought to at least put a puppy door between the back yard and the hallway.  This we can do by adding a basic definition:

The puppy door is a door.  The puppy door is south of the Back Yard and north of the Hallway.

The Inform compiler complains after this addition, because we have previously established that the Back Yard is north of the Hallway, and we can't have two different destinations mapped as north of the Hallway.  So we have to revise the Back Yard definition we wrote earlier to treat the puppy door as an intermediate object:

The Back Yard is north of the puppy door.  

This satisfies the compiler, but doesn't do much that's interesting.  We can see the puppy door in both the Back Yard and the Hallway, described by default as You can also see a puppy door here.  And because Inform understands the concept of a door, it allows us to open and close it.  At this point, we can also move freely through the door; the default Inform behavior automatically opens the door before we try to pass through it.
So it's functional, but boring.  Let's set it up so that the player (as Riley) has to put some thought into how to go through the door, and describe its opening and closing more appropriately.  Because we are going to write nudge on the door to indicate a puppy's casual means of opening it, let's also treat a PUSH verb as an OPEN verb for this door.

Instead of opening the puppy door, say "You nudge the door gently, but it seems too heavy to move."

Instead of pushing the puppy door, try opening the puppy door.


So we have now prevented the player from simply going S from the Back Yard into the Hallway; the door will no longer freely open.  Now, of course, we need a solution for this minor obstacle, and at first blush there are two possibilities that players might reasonably try, given the hint that the door seems too heavy to move.  The player could RUN AT THE PUPPY DOOR to gain a little momentum; the player could also PUSH THE PUPPY DOOR HARD to put a little extra puppy grunt into it.  This gets a little programmer-ish, because by default adverbs qualifying an action in more detail are not handled by Inform.  So to implement this, we create a new verb called forcing, and tell the parser how to interpret a couple of alternate syntax possibilities:

Forcing is an action applying to one visible thing.  Understand "push [something] hard" as forcing.  Understand "push hard on/at [something]" as forcing.  Understand "force [something]" as forcing.  Understand "run at/through [something]" as forcing.

Instead of forcing the puppy door:
say "You hit the puppy door with all your might, and suddenly find yourself tumbling through to the other side!";
move player to the other side of the puppy door.

There's a shortcoming here -- something like RUN TO THE SOUTH FAST from the Back Yard really ought to behave like these other options, but does not, because by default Inform treats RUN as the gentler GO.  We still want GO to act like PUSH, so we can't just treat any attempt to move in that direction as a FORCE.  One further complication -- because SOUTH is a direction, not a [something], and we want RUN NORTH FAST to behave the same way in the Hallway, we need to be a little creative here.

What we'll do is create a "ramming" action that looks for doors in the direction of action and tries to force them open.  We'll need to treat this new action as an entering move if it's applied to a thing, since an energetic puppy might very well try to RAM PUDDLE.  We also ought to allow the player to RAM in any direction, whether there's a door in the way or not, and if there's no obstacle render the text as an energetic GO, while protecting the protagonist from running wildly into walls.  Inform can handle fairly complex structures, but it insists that we tab our code neatly so it can figure out what we mean.  Here's how we set this up, accounting for a number of possible expressions the player might use:

Ramming is an action applying to one visible thing.  Understand "ram [direction]" as ramming.  Understand "ram [something]" as entering.  Understand "run [direction] fast" and "run to [direction] fast" and "run fast [direction]" and "run fast to [direction]" and "run to [direction] fast" as ramming.

Carry out ramming:
    let the target item be the door noun from the location;
    let the target room be the room noun from the location;
    if the target room is a room and the target item is not a door:
        say "You travel [noun] with full puppy energy!";
        try going noun;
    if the target item is not a door and the target room is not a room:
        say "Your enthusiasm for beating your head against things is impressive, but unproductive.";
    if the target item is a door:
        try forcing the target item.

Trying this out, one other issue presents itself -- FORCE should be treated as RAM in some situations, and should always respond with something reasonable so the user doesn't get a blank response from the parser.  So for general cases, let's treat forcing an object as trying to enter it, and forcing in a compass direction as a ram attempt, by adding:

Instead of forcing something, try entering noun.  Instead of forcing direction, try ramming noun.


Whew!  That got a little code-heavy, but should do it for the moment.  Dealing with the puppy door now presents a simple verb/action puzzle that makes sense in the context of our game world.  Playtesting will probably reveal some other reasonable cases we haven't yet thought to handle properly, but this is as good a start as I can come up with at the moment.

What else can we do here?  Well, let's also establish that if Riley makes it into the hallway, but has muddy paws, he gets kicked out of the house again.  To handle this, we need to set things up so that entering the mud puddle puts mud on Riley, and check for that condition on entry to the hallway.  This is an incomplete puzzle at the moment, as the player can simply avoid going into the mud puddle, and also has no way to clean Riley's paws after doing so.  But it's a situation that fits our game world, and perhaps it will inspire some ideas as we proceed. 

We implement this by defining a player attribute called "muddiness," and changing it from "clean" to "muddied" after the player goes into the mud puddle.  We'll define the kick-out behavior, and also define a response for clean paws, as a hint to the player that something interesting or different might happen if Riley's paws are not clean.  This doesn't read as much like English as Inform 7 would like it to -- we're still writing a computer program at the end of the day -- but it gets the job done without being too cryptic:

A muddiness is a kind of value.  The muddinesses are clean and muddied.  The player has a muddiness.  The player is clean.

After entering the Mud Puddle:
    say "SPLASH!  Mud, glorious mud!  It squishes delightfully between your claws.";
    now the player is muddied.

Every turn:
    if the player is in the Hallway:
        if the player is muddied:
            say "Uh-oh!  Mom enters the hallway, sees the mud all over your paws and sends you right back outside again!";
            move player to the Back Yard;
        if the player is clean:
            say "The hallway linoleum feels cool under your clean paws."

Next time, we'll do more with Mom and her routine, which will give us a chance to explore the creation of non-player characters.

The current version of our Inform source code is below the fold.



"Riley's Adventure" by GamingAfter40

Mom's Kitchen is a room. "This is where the magic happens. Strange and mysterious scents fill the air, and the floor is always worth checking for treasure."

The turkey is here. "One scent in particular stands out -- rich and juicy and tasty."

The Hallway is north of the kitchen. "The hallway is often populated by feet and shoes coming and going, but it's quiet at the moment."

The puppy door is a door.  The puppy door is south of the Back Yard and north of the Hallway.

Instead of opening the puppy door, say "You nudge the door gently, but it seems too heavy to move."

Instead of pushing the puppy door, try opening the puppy door.

Forcing is an action applying to one visible thing.  Understand "push [something] hard" as forcing.  Understand "push hard on/at [something]" as forcing.  Understand "force [something]" as forcing.  Understand "run at/through [something]" as forcing.

Instead of forcing the puppy door:
say "You hit the puppy door with all your might, and suddenly find yourself tumbling through to the other side!";
move player to the other side of the puppy door.

Instead of forcing something, try entering noun.  Instead of forcing direction, try ramming noun.

Ramming is an action applying to one visible thing.  Understand "ram [direction]" as ramming.  Understand "ram [something]" as entering.  Understand "run [direction] fast" and "run to [direction] fast" and "run fast [direction]" and "run fast to [direction]" and "run to [direction] fast" as ramming.

Carry out ramming:
    let the target item be the door noun from the location;
    let the target room be the room noun from the location;
    if the target room is a room and the target item is not a door:
        say "You travel [noun] with full puppy energy!";
        try going noun;
    if the target item is not a door and the target room is not a room:
        say "Your enthusiasm for beating your head against things is impressive, but unproductive.";
    if the target item is a door:
        try forcing the target item.

The Back Yard is north of the puppy door.  "The back yard is full of grass and bugs and the occasional squirrel. A large tree provides shade on hot summer days."

The Mud Puddle is an enterable container.  The Mud Puddle is in the Back Yard.   "[if the player is not inside the Mud Puddle]A lovely puddle of mud remains from yesterday's rain."

A muddiness is a kind of value.  The muddinesses are clean and muddied.  The player has a muddiness.  The player is clean.

After entering the Mud Puddle:
    say "SPLASH!  Mud, glorious mud!  It squishes delightfully between your claws.";
    now the player is muddied.

Every turn:
    if the player is in the Hallway:
        if the player is muddied:
            say "Uh-oh!  Mom enters the hallway, sees the mud all over your paws and sends you right back outside again!";
            move player to the Back Yard;
        if the player is clean:
            say "The hallway linoleum feels cool under your clean paws."

After exiting from the Mud Puddle, say "You hop out of the mud puddle, dripping awesomely muddy water."

Instead of taking the Mud Puddle, say "You can probably take some mud with you, but picking the entire puddle up is beyond even your puppy powers."

Instead of examining the Mud Puddle, say "It's rich and thick and pungent, made of dirt and worms and everything else that is wonderful in this world."

The Dining Room is east of the hallway. "This is where the people eat. It is a sumptuous palace of steel and fabric, with chairs [if the player is not on the Dinner Table]rising high into the air.[otherwise]surrounding the table.  From up here you can see the whole dining room.[end if]"

The Dinner Table is an enterable supporter.  The Dinner Table can be climbed.  The Dinner Table is in the Dining Room. "[if the player is not on the Dinner Table]The dinner table dominates the room, overshadowing everything else with its promise of tasty past and future."

Instead of entering the Dinner Table when the player is not on the Dinner Table, say "Perhaps you could climb up there?"

Instead of climbing or entering the Dinner Table when the player is on the Dinner Table, say "You're already up here."

Instead of climbing the Dinner Table: say "You jump high, higher, and then clamber up onto the dinner table."; move player to the Dinner Table.

Instead of going down when the player is on the Dinner Table, try getting off the Dinner Table.

After getting off the dinner table, say "You hop off the dinner table, landing on the floor with a mighty (small) oof."

The player is in The Back Yard.

No comments:

Post a Comment