Gamefic

Adventure Games and Interactive Fiction

Actions

Actions are commands that can be performed by actors. In a text adventure, player input gets parsed into commands for their characters. Examples of possible commands are GO NORTH and GET LAMP.

Scripts define actions with the respond method.

Simple Commands

The simplest command form is a single verb. Example:

Mygame::Plot.script do
  respond :think do |actor|
    actor.tell "You ponder your predicament."
  end
end

The respond method’s block gets executed in response to the THINK command. The block’s first argument is always the character performing the action.

Action Targets

The respond method can take additional arguments that identify the action’s targets, e.g., the direct object of a command.

Mygame::Plot.script do
  respond :kick, Thing do |actor, thing|
    actor.tell "You kick #{the thing}."
  end
end

This action allows characters to KICK any object in their direct vicinity.

Actions identify targets by comparing the command’s direct object to the entities the player can access, e.g., other items in the room, items in the character’s inventory, items in nearby open containers, etc. The component used to perform comparisons is called a Query.

There are different types of queries that actions can use. In the standard library, the default query is available. The above example will compare the user’s input to each Thing available to the character.

Using Queries

Gamefic provides query methods for specifying which query to use for each target. The above KICK example is equivalent to specifying available:

Mygame::Plot.script do
  respond :kick, available(Thing) do |actor, thing|
    actor.tell "You kick #{the thing}."
  end
end

The available query is suitable for the majority of use cases, but the following queries are also available:

  • anywhere
  • parent
  • children
  • siblings
  • myself
  • plaintext (filters and returns text input instead of entities)
  • room (requires gamefic-standard)

The anywhere query is exceptional because it allows commands to act on any entity in the game regardless of its vicinity to the player. It can be useful for commands that don’t involve physical interaction, e.g., THINK ABOUT THE CASTLE or EMAIL BOB.

Actions with Specific Targets

The above examples specify Thing as the target. This means the action will be triggered for any Thing the query can find. Authors can specify a specific object to make a specialized version of the action.

In this example, the plot triggers a different action in response to KICK BALL.

require 'gamefic-standard'

Mygame::Plot.seed do
  @field = make Room, name: 'field'
  @ball = make Item, name: 'ball', parent: @field
  @can = make Item, name: 'can', parent: @field
end

Mygame::Plot.script do
  introduction do |actor|
    actor.parent = @field
    actor.tell "You're in a field. You see a ball and a can here."
  end

  respond :kick, Thing do |actor, thing|
    actor.tell "You kick #{the thing}."
  end

  respond :kick, @ball do |actor, thing|
    actor.tell "You're playing soccer!"
  end
end

If the command’s direct object is the ball, the second action gets triggered. For any other object, the first gets triggered.

Action Orders

The response method associates an action with a verb. In the above examples, the verbs are :think and :kick.

Multiple actions can be associated with the same verb. When a player’s command gets parsed, the game identifies all the actions that potentially match based on the verb and queries. As a general rule, the most recently defined match is the one that gets executed.

In this example, the first action will never get executed because the second action overrides it:

Mygame::Plot.script do
  respond :think do |actor|
    actor.tell "This won't happen."
  end

  respond :think do |actor|
    actor.tell "This is the first THINK action the game finds."
  end
end

Cascading through Multiple Actions with proceed

Sometimes authors want to add custom behavior for a command but still have the option to execute the previous action. The actor’s proceed method allows it to execute the previous match for the current command.

We can modify the KICK example above so the action that responds to kicking the @ball executes the previous action:

Mygame::Plot.script do
  respond :kick, @ball do |actor, thing|
    actor.proceed
    actor.tell "You're playing soccer!"
  end
end

Result:

$ rake ruby:run

You're in a field. You see a ball and a can here.

> kick can

You kick the can.

> kick ball

You kick the ball.

You're playing soccer!

Next: Creating custom scenes