Sunday, April 7, 2019

Implementing Help in a Story

Using tables

The next few posts I'd like to cover uses for tables, that I've picked up in the last week and a half. Now this won't cover every single thing that I've recently found a use for tables, but it hopefully will inspire folks on ways they can use tables.

Today's table is a help text table. So by default if you are in a story and type the command help you get a pretty useless That's not a verb I recognise. response. I'm going to make the command a little more useful by adding a simple help command.

The idea I have for a help command is that when you simply type help by itself, it will present a list of things you can get help about. Then by typing help something you get help about the something that you are asking help about. To do this kind of help we will need a table of help topics that we can provide help on. So let's begin with that.

A help-topic is a kind of value. Some help-topics are defined by the Table of Help Topics.

Table of Help Topics
help-topic          reply
move commands       "Use cardinal directions like north/south/northwest (also can be written as n/s/nw) and up and down."
take                "This will take an item that can be picked up. [italic type]Ex: 'Take pickle'"
pick up             "Same as take command. [italic type]Ex: 'Pick up pickle'"
drop                "Will remove item from your inventory and place it in the room. [italic type]Ex: 'Drop pickle'"
put down            "Same as drop command. [italic type]Ex: 'Put down pickle'"
look at             "This will look at a particular item you indicate. [italic type]Ex: 'Look at pickle'"
look inside         "Looks inside a container. [italic type]Ex: 'Look inside box'"
look                "This will just look at the room you are currently in, in general."
examine             "Same as look at command. [italic type]Ex: 'Examine the pickle'"
ask for             "This allows player to ask person for/about some item. [italic type]Ex: 'Ask Dave about the pickle'"
hit                 "Commits violence on person/item. [italic type]Ex: 'Hit Dave'"

First we define a kind of value known as help-topic. We also tie help-topic values to the Table of Help Topics. Next we define our Table of Help Topics, it has two columns. help-topic and reply.

We now can get a particular help-topic from the table and return it's particular reply that's associated with it. If you've ever been a Python developer then you might recognize this as a dictionary.

The next thing we need to do is actually create the verb for Inform to understand. We can do that with.

Understand "help [help-topic]" as asking for help about. Asking for help about is an action out of world, applying to one help-topic.
Understand "help" or "help [text]" as a mistake ("Help is available on the following topics: [help-topics list]").

Now there's two things here. The first Understand here is indicating that when the player types in help [help-topic] we are doing the action asking for help about. Inform is super literal and so creating an action called Asking for help about would require the player to literally type in exactly that, which isn't what we want the player to type instead we'd like for them to just type help [help-topic]. So internally the action is indeed called asking for help about but to the player the action is just help [help-topic].

Also notice the [help-topic] in that. That means it has to be the command help plus something in the help-topic column. If the player types help banana, well then that doesn't match anything we have in the table and thus help banana isn't the asking for help about command.

Okay moving to the next part, Asking for help about is an action out of world, applying to one help-topic. We define the action as out of world since we want asking for help to not consume time. Thus, when the player asks for help, Inform will not increment the turn counter, time will not progress, and so on. Basically, an out of world command is treated as if nothing actually happened, which is great for our help command and pretty much anything else where we need to break the forth wall. Finally we indicate that the action applies to only one help-topic.

Now, let's say the player just simple types help or types help banana. That's not asking for help about and the second line covers this. We tell Inform to understand help by itself or help with some other text that isn't in the help-topic as a mistake. A mistake is a way to indicate to Inform that the player has done something that isn't an action we've created and we're not going to attempt to process the command through the rule books. So mistakes don't get processed and thus mean they also don't take any time. It's like an out of world command, however, unlike an out of world command, we're simply just not even going to process the text.

Mistakes allow you the writer to provide a hint text to the player to maybe ask them to clarify what they were trying to do or poke them in some other direction that isn't a mistake. Sort of like if a player typed something like drink from teapot and you the writer might want to come back and say Did you mean drink from the tea cup?. In this case we tell the player Help is available on the following topics: [help-topics list]

However, we've now defined something new here say [help-topics list]. Inform doesn't know how to say [help-topics list] so we need to define that as well.

To say help-topics list:
    repeat through the Table of Help Topics:
        say "[line break] [help-topic entry]".

This defines how to say [help-topics list]. Basically we create a loop here with the repeat through the Table of Help Topics and for each entry in the table we say what's in the help-topic column.

Now finally, we're going to actually define the asking for help about command.

Carry out asking for help about:
    repeat through the Table of Help Topics:
        if the help-topic understood is the help-topic entry:
            say "[reply entry][paragraph break]";
            break.

I'm sure there is a better way to implement this, but when this is doing is setting up a loop and going through each entry in the table. When the help-topic the player typed in matches the help-topic entry we're currently on in the loop, we say the text that is in the reply column and then exit the loop with a break.

And there we go that's everything, so all of it in a one piece.

A help-topic is a kind of value. Some help-topics are defined by the Table of Help Topics.

Table of Help Topics
help-topic          reply
move commands       "Use cardinal directions like north/south/northwest (also can be written as n/s/nw) and up and down."
take                "This will take an item that can be picked up. [italic type]Ex: 'Take pickle'"
pick up             "Same as take command. [italic type]Ex: 'Pick up pickle'"
drop                "Will remove item from your inventory and place it in the room. [italic type]Ex: 'Drop pickle'"
put down            "Same as drop command. [italic type]Ex: 'Put down pickle'"
look at             "This will look at a particular item you indicate. [italic type]Ex: 'Look at pickle'"
look inside         "Looks inside a container. [italic type]Ex: 'Look inside box'"
look                "This will just look at the room you are currently in, in general."
examine             "Same as look at command. [italic type]Ex: 'Examine the pickle'"
ask for             "This allows player to ask person for/about some item. [italic type]Ex: 'Ask Dave about the pickle'"
hit                 "Commits violence on person/item. [italic type]Ex: 'Hit Dave'"

Understand "help [help-topic]" as asking for help about. Asking for help about is an action out of world, applying to one help-topic.
Understand "help" or "help [text]" as a mistake ("Help is available on the following topics: [help-topics list]").

To say help-topics list:
    repeat through the Table of Help Topics:
        say "[line break] [help-topic entry]".

Carry out asking for help about:
    repeat through the Table of Help Topics:
        if the help-topic understood is the help-topic entry:
            say "[reply entry][paragraph break]";
            break.

Saturday, March 23, 2019

Changing the exits in and out of a room.

Changing exits to a room.

I wanted to post this as I found it interesting. I wanted a room that was a secret room that only became available as a room that you after some task was complete. Here's the code I used.

After examining the snack wall for more than the first time :
    if the player does not have the backroom pass:
        say "Yeap, still nothing here that you want.";
    otherwise:
        if The Shop's Backroom is not adjacent to The Magazine Shop:
            say "Yeap, still nothing... Hold the phone!  You notice that the pegboard has moved a bit from the wall.  You reach out to touch the pegboard and notice that it is actually a door!";
            change the north exit of The Magazine Shop to The Shop's Backroom;
            change the south exit of The Shop's Backroom to The Magazine Shop;
            increase the score by 1;
        otherwise:
            say "This is the snack wall, it appears to act as a door to some back room of sorts.  You look over the snacks.  There's still nothing that really stands out as something you would want."

In this we have three possibilities. One where we don't have an item called backroom pass, in this case, pretty much we want to ignore any other possibility. The other two is if you are "finding" the room for the first time or not.

From this I'm sure that you can extract several other possibilities to changing the exits to and from a room. The thing to remember is that doing only one direction makes the new passage one way. So if you want to connect rooms together in the normal two way passage kind of way, then you must connect the rooms with two connections.

Tuesday, March 19, 2019

Simplifying our story

Simplifying our story.

Now that we have the new rule in that applies to every room we move in, let's go ahead and get rid of the direction section. While it's nice to see the direction section for our own purposes for understanding the map, we can just use Inform's Index function to build our map if we need an overview.

So to get rid of this, we will provide directions at room declaration like so.

The Employee Closet is west of the Employee Break Area. "You enter the c...

The Employee Closet automatically becomes a room when it is a direction from an already established room. Which does mean that the very first room you define still needs to use the:

The Employee Break Area is a room

The next thing is the second line after each room that explicitly sets the concealment, we can make that nicer looking by just referring to the last object we create using it, like so:

The Employee Closet is west of the Employee Break Area. "You enter the closet.  It's small and dusty, but you feel that you might be able to hide in here.  You notice the floor is somewhat uneven as you back up slowly into the closet and close the door."  It has concealment fairly.

Notice how we've used It has concealment fairly. right after the room description. It becomes a short hand for whatever object we just defined, so we can use that instead of typing out the entire room name.

However, our initial room we can do even one better.

The Employee Break Area is a room with concealment none. "A break room that is clearly for employees of some company.  The walls are littered with information about minimum wage, HR regulations, and so on.[if unvisited]  You aren't exactly sure how you have come to get here, but you hear voices...  Not good voices, coming from the distance and growing closer.[otherwise]  Those voices continue to get closer.[end if]  To the east and west you see a door."

When we define a room we get to make one relationship on the same line, for every other room we've used that one relationship to be the direction, hence the reason why we need the second line after the description. However, our initial room doesn't need a relationship to anything else since nothing else exists in our world at the moment.

So with all of that said and done, our final story is now.

[CONCEALMENT]

Concealment is a kind of value.
Concealment are none, fairly, mostly, and completely.
Concealment is usually none.
A person has a concealment.
A room has a concealment.

After going to a room (called P):
    Now concealment of the player is concealment of P;
    Continue the action.

When play begins:
    Cover is blown in eight turns from now;
    Now concealment of the player is none.

[ROOMS]

The Employee Break Area is a room with concealment none. "A break room that is clearly for employees of some company.  The walls are littered with information about minimum wage, HR regulations, and so on.[if unvisited]  You aren't exactly sure how you have come to get here, but you hear voices...  Not good voices, coming from the distance and growing closer.[otherwise]  Those voices continue to get closer.[end if]  To the east and west you see a door."

The Employee Closet is west of the Employee Break Area. "You enter the closet.  It's small and dusty, but you feel that you might be able to hide in here.  You notice the floor is somewhat uneven as you back up slowly into the closet and close the door."  It has concealment fairly.

The Employee Bathroom is east of the Employee Break Area. "You enter the bathroom.  It's quite roomy and you can see a lock on the door.  Clearly you could hide out in here, but for how long?  You feel a breeze coming from a half a foot high vent that is at floor level."  It has concealment mostly.

The Hiding Area is below the Employee Closet. "You creep down into the floor and into the open area underneath the closet.[if unvisited]  You feel around and feel small bars of metal, but are unable to properly identify what the metal is.  You attempt to pick up one of the small bars and notice that it has immense weight.[end if]  You slide the boarding back over the hole."  It has concealment mostly.

The Vent Shaft is east of the Employee Bathroom.  "You crawl into the venting area.  The pipe narrows too much to go any further than maybe eight feet.  However, you turn around crawl backwards into the venting and pull the grate back over the vent."  It has concealment completely.

[EVENTS]
At the time when cover is blown:
    If concealment of the player is none:
        End the story saying "No effort was made to hide from the voices.  Instead you thought you might be able to fight them off.  Turns out you were wrong.";
    If concealment of the player is fairly:
        End the story saying "You at least tried...  However, now you realize you could have done way better.";
    If concealment of the player is mostly:
        If the player is in The Hiding Area:
            End the story saying "Foolish, you hid yourself in the same place they we're hiding their gold.";
        Otherwise:
            End the story saying "A good effort to hide from the voices, but in the end it was not enough.";
    If concealment of the player is completely:
        End the story finally saying "Great job you hid form them!!".


Test me with "e/e/z/z/z/z/z/z/z".

I've move the rule around so it is at top now, since it does not require any other room to be defined before defining it, since it is generically calling any room "P".

As you can see this story uses fewer lines and is less error prone since everything that describes the room is right there with the room being created.

Saturday, March 16, 2019

Rules for Every Room.

Now making a rule that applies to every room.

One final time, let's look at this bit of code.

After going to The Employee Break Area:
    Now concealment of the player is none;
    Continue the action.

Here the rule has to specifically be applied to the room we are entering. This can get really troublesome because as we add more rooms, we will need to add more rules like this for changing the value specific to that room. For example, say we create a new room to the south of the Employee Break Area.

[...  See other post for everything before...]
[ROOMS]

The Employee Break Area is a room. "A break room that is clearly for employees of some company.  The walls are littered with information about minimum wage, HR regulations, and so on.[if unvisited]  You aren't exactly sure how you have come to get here, but you hear voices...  Not good voices, coming from the distance and growing closer.[otherwise]  Those voices continue to get closer.[end if]  To the east and west you see a door."

Some Random New Room is a room. "You enter a technicolor room of wonderment.  You think to yourself, 'How on Earth did I get here?!'"
[..and so on...]

Some Random New Room is south of The Employee Break Area.

The problem here is that the players concealment is set to the value of none when entering the Employee Break Area. When moving into Some Random New Room, that value is never updated since there is no after rule that updates it. Adding a new after for each room can get really tiresome. Instead we can just create a single rule that updates the players concealment to either the default of none or whatever concealment we've given to the room.

First let's change our value concealment to not only apply to people but to rooms as well.

Concealment is a kind of value.
Concealment are none, fairly, mostly, and completely.
Concealment is usually none.
A person has a concealment.
A room has a concealment.

The new line here is A room has a concealment. This allows rooms to also have a concealment value. Since we have the Concealment is usually none. Any new room or person we create and not set their concealment explicitly, will default to having a concealment of none, which is great as opposed to "whatever value the room you just left had."

Next we need to assign a concealment value to the rooms we already have.

The concealment of the Employee Break Area is none.

[.. just do this for each room ..]

Finally we will write a generic rule to rule them all.

[CONCEALMENT]
After going to a room (called P):
    Now concealment of the player is concealment of P;
    Continue the action.

[EVENTS]
[...and so on...]

Here we create a after going to a room rule that will be checked every time we enter a room. By default concealment of a room will be none and so by default our player will receive a concealment of none as well.

This is way better than having an after for every room that we might enter.

Monday, March 11, 2019

Why continue?

Why continue?

So going back to my previous post, we see that the rule processing is as follows:

Rule processing diagram

Rule processing diagram

Now let's look at the after rule from my example story.

After going to The Employee Break Area:
    Now concealment of the player is none;
    Continue the action.

Here we are adding an additional rule to the built in action of going to. The going to action is a built in action that changes the room that the player is currently in.

Once the player successfully moves into a new room, the carry out rules in going to actually update the internal value that represents the players location and other variables. In addition any kind of "Actions" that happen when entering the room are triggered.

Once that's all completed any after rules are check to see if there is a match. IF there is, the after rule runs and the action is stopped. But we're already in the room, what possible action could be stopped? What is stopped is the report rule for the going to action which is where all of the say actions are located when moving into a new room. So basically you get no room description, your player has indeed moved into the room, but since we are stopping before the report rules, we'll get no room description.

Hence the reason we have Continue the action. within the after rule here. The instead and after stop the action should anything match, thus it is here we will use the Continue the action. commands the most. However, there might be times when we are in a rule that would continue but wish to stop the action from additional processing. For that we can use Stop the action. which will stop the action. Depending on how far we are in the processing, this might trigger an action to be marked as failing.

Saturday, March 9, 2019

Rule Processing

Rule Processing

In the last post I used an after rule and I wanted to spend the next few posts talking about rules since I've spent a bit of time on the subject.

Here's an example of using an after rule from the last post.

After going to The Employee Break Area:
    Now concealment of the player is none;
    Continue the action.

Rule processing follows a specific order for processing. The below graph shows the path that rule processing follows for each action.

Rule processing diagram

Rule processing diagram

From this you can see six main processing stages for each action.

  • Before - These are the rules processed before the action actually takes place. This would include things like setting up variables, checking if certain conditions exist, and so on.
  • Instead - These rules are exceptions to the case. Here you would enumerate one offs that would block the rule from succeeding. If an action matches an instead rule, the action stops being processed.
  • Check - These are the basic premise rules that constituent the actual action. In other words, the before rules sets things up, but should something in the before rules stop the action, then the action isn't ever really carried out. However the checking rules, you've committed to the action whether it succeeds or not.

Quick Example: Like let's say your action is rob a bank. If you aren't near a bank then you really can't commit to the action so there is no success or failure, but if you are near a bank but forgot your disguise, you can still commit to robbing the bank just with an unsuccessful outcome. So before rules might be are you near a bank? vs a check rule being did you bring a disguise?.

Also Note: During the check rules, it might be tempting to use something like say "Something, Something, Something". However, you must not say anything or change any values in the story during a check action. Doing so signals to Inform that the action has failed. So in order to indicate success, you must not say or change any values in a check rule.

  • Carry out - These are the rules that change all of the values within your game based on a success. Again if it was robbing a bank, then you'd want to give the bags of money to the player and setup an event that will happen in X turns when the police show up if they are still in the bank. However, you must not say anything during this phase, that is what the report rules will do.

Note: If you do use say actions within a carry out rule, you will break the try silently action. Which if you don't use try silently in your story, then well it honestly doesn't matter then.

  • After - These are actions that happen after the action has definitely happened. As you can see in my example, I update the concealment of the player after the going to another room action has already taken place. Do note that the after rules stop the action from reaching the report rules, just like the instead rules.

  • Report - This is the last phase of rule processing. Basically this is everything that will be said as the outcome of the action, UNLESS we are try silently the action. In that case, report rules will not be ran.

I will note one more thing before ending here for now. NPCs in your story have two additional phases in the rule processing that apply to them only. I'll cover that later.

See Also

For additional information about rules see Chapter 7 and Chapter 12 from the Writing Inform book.

Quick Note Before Posting

So I didn't do a post last week, I've been trying to figure out Blogger's CSS. Additionally, I started reading up about rulebooks in Inform and they're pretty involved. That said, I do plan on posting again later tonight and continuing to post regularly on Saturday's.

Implementing Help in a Story