1. Cookbook

Warning
Please note this section is NOT for children or beginners. Please make sure you are very familiar with the basic aspects of adventuron before reading onwards.

This section contains various recipes for common types of problems that are encountered.

1.1. Inventory Limits

By default, Adventuron supports holding 10 items in the inventory, and 200 weight units (more on that later).

If you wish to change this, then you must setup an inventory limit in the game settings (not to be confused with the theme settings).

Upon executing the snippet below, type GET ALL at the prompt.

start_at = village

locations {
   village  : location "You are in the village" ;
}

objects {
   lamp  : object "a lamp"  at = "village" ;
   pen   : object "a pen"   at = "village" ;
   sword : object "a sword" at = "village" ;
   rock  : object "a rock"  at = "village" ;
}

integers {
   inventory_limit : integer "3" ;
}

game_settings {
   // By referencing an integer, we can dynamically change this limit in-game
   // using : set_integer, or using a dynamic integer.
   inventory_items_limit_var = inventory_limit
}

1.2. Vehicles

The following example shows how to create a vehicle (a boat), which can be moved between two location (update the code to allow it to travel to many more locations, possibly using a string to track its location rather than a boolean).

The boat is both an object, and a location. The boat object and the boat location cannot have the same identifier.

If the player enters the boat from the same location as the boat can be in, then the player is sent to the "inside_boat" location. From there rowing will move the location of the boat between two locations (lakeside, and island_beach) - it will create the boat in one of two location, the opposite one of the current location.

This code sample also demostrates a "repair boat" puzzle, but without a pre-requisite. You can repair the boat just by typing "repair boat".

Exiting the boat will take the player to the location containing the boat.

######################################
#  Adventuron                        #
######################################

start_at                 = lakeside

######################################
#  Booleans                          #
######################################

booleans {
   is_boat_fixed       : boolean "false" ;
   is_boat_at_lakeside : boolean "true" ;
}

######################################
#  Locations                         #
######################################

locations {
   lakeside     : location  "You are at the lakeside" ;
   inside_boat  : location  "You are a boat." ;
   island_beach : location  "You are at a beach on an island" ;
}

on_describe {

   : if (is_at "inside_boat") {
      : if (is_boat_fixed == false) {
         : print "There is a hole in the boat." ;
      }
   }
}

######################################
#  On Command                        #
######################################

on_command {

   : match "enter boat;climb boat"  {
      : if (is_at "lakeside" || is_at "island_beach") {
         : goto "inside_boat" ;
         : print "You climb inside the boat" ;
         : press_any_key ;
         : redescribe;

      }
   }

   : match "leave boat;exit _"  {
      : if (is_at "inside_boat") {
         : goto "old_boat" ;
         : print "You climb out of the boat" ;
         : press_any_key ;
         : redescribe;

      }

   }

   : match "repair hole;repair boat"  {
      : if (is_at "inside_boat" && is_boat_fixed == false) {
         : print "You repair the hole in the boat" ;
         : set_true "is_boat_fixed" ;
         : press_any_key ;
         : redescribe;
      }
   }

   : match "row boat"  {
      : if (is_at "inside_boat") {
         : if (is_boat_fixed) {
            : if (is_boat_at_lakeside) {
               : print "You row the boat to the island." ;
               : set_false "is_boat_at_lakeside" ;
               : create "old_boat" target = "island_beach" ;

            }
            : else {
               : print "You row the boat to the lakeside." ;
               : set_true "is_boat_at_lakeside" ;
               : create "old_boat" target = "lakeside" ;
            }
         }
         : else {
            : print "You can't row the boat with the hole in it." ;
         }
      }
   }
}

objects {
   old_boat : object "an old boat" at = "lakeside" ;
}

themes {
   my_theme : theme {
      lister_exits {
         is_list_enter                        = false
         is_list_exit                         = false
      }
   }
}

1.3. Custom get / drop messages

To take or drop an object without the system message being printed (if you want to print a custom message for example), then you can use the get command, but the quiet version of the command, as shown below.

The is_carried check can be used to test that the get was successful. A failed get will always print the failure message (your hands are full, you can’t take that, etc, - messages can be configured in the theme).

start_at = village

locations {
   village  : location "You are in a village." ;
}

objects {
   lamp : object "a lamp" at = "village" ;
}

on_command {
   : match "get lamp"  {
      : if (is_beside "lamp") {
         : get "lamp" quiet = "true" ;
         : if (is_carried "lamp") {
            : print "You pick up the dusty lamp" ;
            : press_any_key ;
            : redescribe;
         }
      }
   }
}

A simpler method exists too. This message will only be displayed if the object is taken.

start_at = village

locations {
   village  : location "You are in a village." ;
}

objects {
   lamp : object "a lamp" at = "village" get_message = "You pick up the dusty lamp";
}

1.4. Block Access To A Location

A barrier can be used to block access to a location. A block can be created in the "barriers" section.

In this example, a lamp is required to enter forest_1.

There is an issue here that the player may drop the lamp in forest_1, then leave forest_1, then permanently block the path, but that can be taken place with in other parts of the code.

Blocks can also have different conditions (block_when_carried, block_when, block_when_not, block_when_exists, block_when_not_exists, block_when_worn, block_when_not_worn).

start_at = village

locations {
   village  : location "You are in the village" ;
   forest_1 : location "You are in a forest" ;
   forest_2 : location "You are in a spooky forest" ;
}
connections {
   from, direction, to = [
      village, east, forest_1,
      forest_1, east, forest_2,
   ]
}

barriers {
   block_forest_path : block {
      location               = forest_1
      block_when_not_carried = lamp
      message                = You need a light source to go into the dark forest.
   }
}
objects {
   lamp : object "a magical glowing lamp" at = "village" ;
}

themes {
   my_theme : theme {
      theme_settings {
         font = clairsys_10
      }
   }
}

1.5. Block Exits From A Location

A location agnostic block is a block that blocks all exits from the current location, no matter what the location is (you can narrow this down with the block condition itself).

By default it has a higher priority than other types of block.

NOTE

This type of block is not compatible with Adventuron 2 PAW / Adventuron 2 DAAD (currently).

start_at        = stream
redescribe = auto_beta
locations {
   stream   : location "You are by the side of a lovely stream.\nType <WEAR CLOAK<12>> to enable block, type <REMOVE CLOAK<12>> to disable block.";
   forest   : location "You are in a grand forest." ;
   old_hut  : location "You are in an old hut." ;
}
connections {
   from, direction, to = [
      stream, north, forest,
      forest, east, old_hut,
   ]
}
barriers {
   block_disorientate : block {
      block_when_worn = cloak
      message         = You are so confused, you can't seem to move anywhere.
   }
}
objects {
   cloak : object "the cloak of disorientation" wearable = "true" at = "inventory" ;
}

1.6. Goto the location of an object

The goto command will let you travel to a location or the location of an object. If the object is non existent, then the goto command does nothing when called.

start_at = village

locations {
   village : location "You are in the village. Type TELEPORT to go to the location of the lamp." ;
   forest  : location "You are in a forest. Type TELEPORT to go to the location of the lamp." ;
}

connections {
   from, direction, to = [
      village, east, forest,
   ]
}

on_command {
   : match "teleport _"  {
      : print "You goto the location of the lamp" ;
      : goto "lamp" ;
      : press_any_key ;
      : redescribe;
   }
}

objects {
   lamp : object "a lamp" at = "forest" ;
}

1.7. One-time Object Creation

It’s very common operation for wanting to create an object when examining something for the first time. If an object is created one time, even if it is destroy subsequently, then has_not_created will return false.

start_at = village

locations {
   village : location "You are in the village." ;
}
objects {
   letter : object "a letter" ;
   postbox : scenery "a postbox" at = "village" ;
}

on_command {
   : if_examine "postbox"  {
      : if (has_not_created "letter") {
         : create "letter" ;
         : print "You notice a letter stuck in the postbox" ;
         : press_any_key ;
         : redescribe;
      }
   }
}

1.8. Dynamic Text

Printing the contents of a variable is possible using the expression form, which is a contained by these brackets as shown {( )}.

start_at = village

locations {
   village  : location "You are in the village. Type SCORE to see your score." ;
}

integers {
   // Set the default score to zero here
   score : integer "0" ;
}

on_command {
   : match "score _"  {
      // Note that CONTROL + SPACE only works on blank lines in these blocks
      : print {(
         "Your score is " +
         score +
         "."
      )}
   }
}

You can also use the {} form in regular text to import the value of a string variable.

start_at = village

strings {
   day_of_week : string "Wednesday" ;
}
locations {
   village  : location "You are in the village. It is {day_of_week}." ;
}
booleans {
   is_wednesday : boolean "false" ;
}

You can also use the {boolean ? if_true_string_var : if_false_string_var} form in regular text to import the value of a string variable.

start_at = village

strings {
   wednesday : string "Wednesday" ;
   not_wednesday : string "Not Wednesday" ;
}
locations {
   village  : location "You are in the village. It is {is_wednesday ? wednesday : not_wednesday}.\nType LOOK to refresh the dynamic location description." ;
}
booleans {
   is_wednesday : boolean "false" ;
}
on_tick {
   : if (is_wednesday) {
      : set_false "is_wednesday" ;
   }
   : else {
      : set_true "is_wednesday" ;
   }
}

Here is another variation, but without the requirement for a string variable to reference

start_at = village

locations {
   village  : location "You are in the village. It is {is_wednesday ? `wednesday` : `not wednesday`}.\nType LOOK to refresh the dynamic location description." ;
}
booleans {
   is_wednesday : boolean "false" ;
}
on_tick {
   : if (is_wednesday) {
      : set_false "is_wednesday" ;
   }
   : else {
      : set_true "is_wednesday" ;
   }
}

Here is yet another variation, but in this variation, we only print something if it is a Wednesday, and we use inline text.

start_at = village

locations {
   village  : location "You are in the village. {is_wednesday ? `It is wednesday.`}\nType LOOK to refresh the dynamic location description." ;
}
booleans {
   is_wednesday : boolean "true" ;
}
on_tick {
   : if (is_wednesday) {
      : set_false "is_wednesday" ;
   }
   : else {
      : set_true "is_wednesday" ;
   }
}

This final variation, demonstrates embedded {} blocks.

  • The outer {} only includes the text between the backtick (`) characters if the boolean reference resolves to true.

  • the next {} only includes the {wed}., if it is wednesday (true in this example).

  • the most inner {} refers to the variable 'wed', which contains a value of "Wednesday".

  • Backticks can be escaped using `` in this syntax.

start_at = village

locations {
   village  : location "You are in the village. {is_wednesday ? `It is {is_wednesday ? `{wed}.`}.`}\nType LOOK to refresh the dynamic location description." ;
}


strings{
   wed : string "Wednesday";
}
booleans {
   is_wednesday : boolean "true" ;
}
on_tick {
   : if (is_wednesday) {
      : set_false "is_wednesday" ;
   }
   : else {
      : set_true "is_wednesday" ;
   }
}

1.9. Persistant Variables

You can specify the 'survivor' scope on a variable to inform Adventuron to let the variable value survive at the point of an end_game or restart occurring (the default value will be set on first game only and never be reapplied upon restart).

In this snippet, completely unrelated, but we also demonstrate the % operator, which is the modulus (or remainder) operator. game_count % 10 gives us the remainder of an integer division by 10. This number will always be in the ranger of zero to nine.

Note
Make sure you provide a full game_information {} section, with a UNIQUE UUID in order for this to work outside of the editor.
start_at                 = my_location

locations {
   my_location : location "You are in a room. This is your {gane_count_nice} game." ;
}

integers {
   // The variable will survive a game restart (or game over)
   game_count : integer "0" scope="survivor";
}

on_startup {
   : increment "game_count" ;
}

strings {
   // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
   gane_count_nice : dynamic_string {(
       game_count +
         (game_count % 10 == 1 ? "st" :
          game_count % 10 == 2 ? "nd" :
          game_count % 10 == 3 ? "rd" : "th")
   )}
}

game_information {
   game_name        = Survivor 1
   game_shortname   = S1
   written_by       = Adventuron Documentation
   year_of_original = 2020
   year_of_release  = 2020
   uuid             = GENERATE A UUID HERE BEFORE RUNNING THIS EXAMPLE - https://www.uuidgenerator.net/
   short_synopsis   = Solve the adventure
   game_version     = 1.0.0
}
start_at = my_location

locations {
   my_location : location "You are in a room." ;
}

integers {
   survivor : integer "100" scope="survivor";
}

on_describe {
   : print {("Survivor = " + survivor)}
}

on_command {

   : match "test _"  {
      : increment "survivor" ;
      : print {("Survivor = " + survivor)}
   }

}

game_information {
   game_name        = Survivor 2
   game_shortname   = S2
   written_by       = Adventuron Documentation
   year_of_original = 2020
   year_of_release  = 2020
   uuid             = GENERATE A UUID HERE BEFORE RUNNING THIS EXAMPLE - https://www.uuidgenerator.net/
   short_synopsis   = Solve the adventure
   game_version     = 1.0.0
}

1.10. Keeping Score

To keep score use an integer to keep track of the score.

To print a score, use the advanced version of print. This version of print requires {( )} brackets as shown below. A piece of text to be printed is still surrounded by double quotes, but variables can now be referenced in this form. Append parts of the text together with the "+" operator as shown.

Feel free to experiment with this sample.

start_at = village

locations {
   village  : location "You are in the village.\nType JUMP to increase your score, type SCORE to see your score." ;
}

integers {
   // Set the default score to zero here
   score : integer "0" ;
}

on_command {
   : match "jump _"  {
      : if (score < 20) {
         : increment "score" ;
         : print "You do something very impressive" ;
      }
   }
   : match "score _"  {
      : print {(
         "Your score is " + score + "."
      )}
   }
}

1.11. Picking Commands One At A Time Without Repetition (aka Cycling)

Sometimes you may wish to add some ambient messages to your game, or perhaps some other logic that occurs in cycles.

The logic of a cycle command is that one command out of the command stored inside the cycle {} block will be executed. When executed, the command will not be executed again until all other commands have been exhausted via repeated runs of the cycle command.

At the start of each round, the iteration order is determined. By default, the order is randomised, but you can set the order using predictable_order = "true|false".

Each cycle command requires a unique key as this will be used to store uniquely serialize the state of the cycle command.

The key can be anything you like but it must be unique in your gamefile per : cycle {} block.

Warning
Please do not use else blocks inside this command as the behaviour if doing this is undefined right now. If statement are fine.

1.11.1. Cycling With Predictable Order

start_at = woods

locations {
   woods : location "You are in the woods.\nType <WAIT<12>> repeatedly to cycle through the print commands." {
      on_tick {
         : cycle key = "woods_events" predictable_order = "true" skip_interval = "0"  {
            : print "You step on some twigs." ;
            : print "You hear the hoot of an owl." ;
            : print "The wind blows through your hair." ;
            : print "In the distance you hear the church bell chime." ;
            : print "You are afraid." ;
         }
      }
   }
}

1.11.2. Cycling With Randomized Order (per round)

By setting predictable_order = "false", Adventuron will jumble the commands so that they happen in a random order each round. Each round the random order will be different, but by default the same command can never be executed twice in a row (if it was randomly at the start of a new round after being at the end of the last round then it will automatically be shuffled by adventuron). This behaviour is can be switched off using allow_back_to_back="true" (false by default).

start_at = woods

locations {
   woods : location "You are in the woods.\nType <WAIT<12>> repeatedly to cycle through the print commands." {
      on_tick {
         : cycle key = "woods_events" predictable_order = "false" skip_interval = "0"  {
            : print "You step on some twigs." ;
            : print "You hear the hoot of an owl." ;
            : print "The wind blows through your hair." ;
            : print "In the distance you hear the church bell chime." ;
            : print "You are afraid." ;
         }
      }
   }
}

1.11.3. Skipping

By using skip="1" (or greater), then the cycle command will only be ignored for 1 or more ticks (depending on the integer provided) after each execution.

start_at = woods

locations {
   woods : location "You are in the woods.\nType <WAIT<12>> repeatedly to cycle through the print commands." {
      on_tick {
         : cycle key = "woods_events" predictable_order = "false" skip_interval = "0" skip="1" {
            : print "You step on some twigs." ;
            : print "You hear the hoot of an owl." ;
            : print "The wind blows through your hair." ;
            : print "In the distance you hear the church bell chime." ;
            : print "You are afraid." ;
         }
      }
   }
}

1.12. Semi Random Messages

If you want to execute one command in a mutually exclusive set at random, use the execute_one_at_random command.

This command will select one command (at random) from its direct descendents to execute and only run that command.

Warning
Please do not use else blocks inside this command as the behaviour if doing this is undefined right now. If statement are fine.
start_at = village

locations {
   village  : location "You are in the village." ;
}

on_tick {
   : execute_one_at_random {
      : print "one" ;
      : print "two" ;
      : print "three" ;
      : print "four" ;
   }
}

1.13. Asking For A String Using A Negative Confirmation Question

start_at                 = my_location
locations {
   my_location : location "Hello {name}. You are in a room." ;
}
strings {
   name : string "";
}
on_startup {
   : ask_string
      question                  = "What is your name?"
      confirm_negative_question = "You entered your name as \"{name}\".\nWould you like to change the name?"
      var                       = "name"
   ;
}

1.14. Asking For Yes / No Answer

Adventuron allows for the setting of a boolean based on asking a yes / no question.

The : ask_bool command must be used in conjunction with a boolean.

In this snippet, some text in the game is conditionally displayed using a dynamic_string too, along with
a ? : operation

BOOLEAN ? value_if_true : value_if_false

NOTE : This feature does not work with Adventuron2PAW or Adventuron2DAAD

start_at                 = my_location

locations {
   my_location : location "You are in a bright kitchen.\n{toast_thought}" ;
}

strings {
   // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
   toast_thought : dynamic_string {(
      is_liking_toast ?
      "I can't stop thinking about delicious toast." :
      "I can't stop thinking about how disgusting toast is."

   )}
}

booleans {
   is_liking_toast : boolean "false" ;
}

on_startup {
   : ask_bool {
      question   = Do you like toast?
      yes_answer = Yes, I like toast very much
      no_answer  = No, toast is not something that appeals to me at all
      var        = is_liking_toast
   }
}

1.15. Adding A Status Bar

A status bar is a screen element that sticks to the top of the screen and does not scroll out of view.

Status bars in Adventuron currently can only (currently) contain two slots, top left, and top right.

To create a status, define one in your theme. Status bars can be independently styled using 'header_bar_pen' and 'header_bar_paper' inside the colors section of the theme section.

Within the status_bar section, you can use the 'dynamic_text' item to reference a string (which will contain some piece of text that may change during the game).

You can also refer to the 'header_text' from the header_layout section, which will use the current location header text for the slot.

There is a third option, not show here, where you can specify static_text, which is text that is literal and will not change.

The existence of a status_bar section in a theme WITHOUT a specific layout will default to a layout with the status bar showing, but without the header. The absense of a status_bar section will default to a layout that prints the header as text, and may scroll away as the screen gets full.

1.15.1. Minimal Status Bar

For a minimal status bar (without styling), simply specify SB in your theme layout. SB must be the first item in the layout.

If SB is specified without any further configuration, then the location header will be listed in the status bar. Generally speaking, you do not want to do this unless you have configured header text for all locations in your game.

Type lots of inputs to see that the header text "Village" never flows off the screen.

start_at = village
locations {
   village  : location "You are in the village" header = "Village" ;
}
integers {
   score : integer "0" ;
}
objects {
   lamp : scenery "a magical glowing lamp" at = "village" ;
}
themes {
   my_theme : theme {
      theme_settings {
         layout = SB G D X O
      }

      screen {
         padding_horz             = 4
         status_bar_padding_horz  = 4
      }

      colors {
         status_bar_pen   = 15
         status_bar_paper = 9
      }
   }
}

1.15.2. Implied Status Bar Without Specifying Layout

If a layout is not specified but the status_bar{} section is provided, then the default (implied) layout will start with SB (status bar).

This is a lazy way to configure the system, and generally speaking it’s better practise to explicitly create a layout string.

Note
If the status_bar{} section is not provided then an inline (non status bar) location header piece of text will be printed (as is usual behaviour).
start_at = village
locations {
   village  : location "You are in the village" header = "Village" ;
}
integers {
   score : integer "0" ;
}

strings {
   // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
   score_topright : dynamic_string {(
      score + "!"
   )}
}

objects {
   lamp : scenery "a magical glowing lamp" at = "village" ;
}
themes {
   my_theme : theme {
      status_bar {
         : header_text;
         : dynamic_text "score_topright" ;
      }
      colors {
         status_bar_pen   = 15
         status_bar_paper = 9
      }
   }
}

In the sample below, type 'jump' at the prompt to increment the score (up to a max of 20). The header will update. The header never scrolls off screen.

start_at = village

locations {
   village  : location "You are in the village" header = "Village" ;
   forest_1 : location "You are in a forest" header = "Forest" ;
   forest_2 : location "You are in a spooky forest" header = "Spooky Forest" ;
}
connections {
   from, direction, to = [
      village, east, forest_1,
      village, west, forest_2,
   ]
}
strings {
   score_topright : dynamic_string {(
      score + " / 20"
   )}
}

######################################
#  On Command                        #
######################################

on_command {

   : match "jump _"  {
      : if (score < 20) {
         : increment "score" ;
         : print "You do something very impressive" ;
         : press_any_key ;
         : redescribe;
      }
   }
}

integers {
   score : integer "0" ;
}
objects {
   lamp : scenery "a magical glowing lamp" at = "village" ;
}
themes {
   my_theme : theme {
      theme_settings {
         // SB = status bar
         layout                = SB G D X O
         header_capitalization = original
      }
      screen {
         experimental_spacer_pre_text = 5
      }
      status_bar {
         : header_text;
         : dynamic_text "score_topright" ;
      }
      colors {
         // 15 == white (#fff)
         status_bar_pen   = 15
         // 9 = Blue (#00f)
         status_bar_paper = 9
      }
   }
}

Another example:

start_at = village

locations {
   village  : location "You are in the village" header = "Village" ;
   forest_1 : location "You are in a forest" header = "Forest" ;
   forest_2 : location "You are in a spooky forest" header = "Spooky Forest" ;
}
connections {
   from, direction, to = [
      village, east, forest_1,
      village, west, forest_2,
   ]
}
strings {

   // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
   topright : dynamic_string {(
      h() + " (" + turns() + " turns)"
   )}

   // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
   topleft : dynamic_string {(
      /* If turns less than 10 then use "Morning" OTHERWISE ....*/
      turns() < 10 ? "Morning" :
      /* If turns less than 20 then use "Lunchtime" OTHERWISE ....*/
      turns() < 20 ? "Lunchtime" :
      /* Use "Lunchtime" OTHERWISE ....*/
      "Evening"
   )}
}

######################################
#  On Command                        #
######################################

on_command {

   : match "jump _"  {
      : if (score < 20) {
         : increment "score" ;
         : print "You do something very impressive" ;
         : press_any_key ;
         : redescribe;
      }
   }
}

integers {
   score : integer "0" ;
}
objects {
   lamp : scenery "a magical glowing lamp" at = "village" ;
}
themes {
   my_theme : theme {
      theme_settings {
         // SB = status bar
         layout = SB G D X O
         header_capitalization = original
      }
      screen {
         experimental_spacer_pre_text = 5
      }
      status_bar {
         : dynamic_text "topleft" ;
         : dynamic_text "topright" ;
      }
      colors {
         // 15 == white (#fff)
         status_bar_pen   = 15
         // 9 = Blue (#00f)
         status_bar_paper = 9
      }
   }
}

Excluding the status bar on mobile.

1.16. Default layouts

Manual Redescribe Mode default layout (without status_bar defined):

H G- D X O

Manual Redescribe Mode default layout (WITH status_bar defined):

SB G- D X O

Auto Redescribe Mode default layout (with or without status_bar defined):

G D O X

1.17. Treasure Hunt Mode

Adventuron Supports a basic 'treasure hunt' mode.

To switch on treasure hunt mode, simply supply a treasure_room, and mark one or more object as a treasure (using treasure="true").

In the code show below, dropping the lamp in the treasure room will display a (system) message of "You have found all the treasures. You have won!".

The winning message can be customised using a theme, and the automatic win-game logic can be disabled in the game settings, in case, you want to script a better ending, or have a more complex set of conditions for completing the treasure hunt.

start_at      = treasure_room
treasure_room = treasure_room
locations {
   treasure_room : location "The treasure room";
   tomb          : location "A tomb" ;
}
connections {
   from, direction, to = [
      treasure_room, east, tomb,
   ]
}
objects {
   lamp : object "a lamp" at = "tomb" treasure = "true" ;
}

Treasure hunt mode with two treasures:

start_at      = treasure_room
treasure_room = treasure_room

locations {
   treasure_room : location "The treasure room";
   tomb          : location "A tomb" ;
   stream        : location "A stream" ;
}

connections {
   from, direction, to = [
      treasure_room, east, tomb,
      treasure_room, south, stream,
   ]
}

objects {
   lamp : object "<a lamp<14>>" at = "tomb"   treasure = "true" ;
   ring : object "<a ring<14>>" at = "stream" treasure = "true" ;
}

1.18. Advanced Theming

1.18.1. Changing The Font

Fonts are defined at theme level. The default font is Bamburgh, but you can change fonts here.

start_at = village

locations {
   village  : location "You are in the village" ;
   forest_1 : location "You are in a forest" ;
   forest_2 : location "You are in a spooky forest" ;
}
connections {
   from, direction, to = [
      village, east, forest_1,
      forest_1, east, forest_2,
   ]
}
objects {
   lamp : object "a magical glowing lamp" at = "village" ;
}

themes {
   my_theme : theme {
      theme_settings {
         font = clairsys_10
      }
   }
}

User imported fonts (use the MENU / IMPORT option in the Adventuron Classroom editor) can be referenced via pressing CONTROL (or ALT) + SPACE after "font = ", all user fonts are named userfont_xxxxxxxxxx (where xxxxxxxxx is the filename of the ttf font imported).

1.18.2. Setting border mode on.

If you set 'border_mode_vertical_percent = x' (where x is usually best with a value of 1), then this will reserve a MINIMUM of x percentage of the screen as a vertical border (non mobile mode only).

As the content panel of the display area generally only scales in integer amounts (pixel doubling, tripling, etc), then it will be likely that a lot more of the vertical section of the screen will be lost, in order to fit with this request without blurring the bitmap text.

If in doubt, use a value of 'border_mode_vertical_percent = 1'.

1.18.3. Setting border colour.

To set the border colour, just set border = x in the colors{} section inside a theme (where x = #000 - #fff, or a palette entry colour 0 - 15 - which corresponds to the zx spectrum palette by default).

Note
I’m using the spelling of "colour" in my description (because I’m British), but the name of the color fields in Adventuron is in American English (as is the coding convention). I’m sure Webster though he was making things easy.

1.18.4. Theme Inheritance

As border colour is tied up to the theme, then there would normally be a lot of copying and pasting of themes to be identical except for the border colour change.

This is simplified in Adventuron with theme inheritance. Simple write extends = theme_id (where theme is the name of another theme), and all the settings of that theme will be applied, before the settings of the current theme are applied.

You can extend to any depth you like, but do not specify circular inheritance (theme a extends theme b which extends theme a which extends theme b, etc).

1.18.5. Changing The Border Colour

To change the border colour, simply call the name of the theme that has the border colour (via : set_theme "theme_id";).

This feature, and theme inheritance was used in the final public-facing version of Excalibur.

Upon entering a location, in on_describe{} a theme would be set for the current location, which allowed the border colour to match the artwork and the tone of the current location.

start_at    = room
start_theme = base

locations {
   room : location "You are in a room.\n^n^   Type 'GREEN'  to set the border green.\n   Type 'YELLOW' to set the border yellow.^m^\n   (non-mobile clients only).\n" ;
}

on_command {
   : match "green _"  {
      : set_theme "green";
      : print "OK" ;
   }
   : match "yellow _"  {
      : set_theme "yellow";
      : print "OK" ;
   }
   : match "grey _"  {
      : set_theme "base";
      : print "OK" ;
   }
}

themes {
   base : theme {
      theme_settings {
         font    = clairsys_10
         columns = 50
      }
      screen {
         content_width                = 400
         border_mode_vertical_percent = 1
         paragraph_spacing_multiplier = 1
      }
      colors {
         pen   = 0
         paper = 7
      }
   }
   green : theme {
      extends = base
      colors {
         border  = 4
      }
   }
   yellow : theme {
      extends = base
      colors {
         border  = 6
      }
   }
}

1.18.6. Making sure that images are scaled correctly

Use the width = xxxx attribute inside the screen block inside a theme to make sure that the horizontal width of the play area is scaled by multiples of this figure.

Note
Mobile clients do not use this setting and always use 100% of horizontal width (high dpi means that artifacts are barely visible on mobile).
start_at = village

locations {
   village : location "You are in the village." ;
   forest  : location "You are in a forest." ;
}

connections {
   from, direction, to = [
      village, east, forest,
   ]
}

######################################
#  Themes                            #
######################################

themes {
   my_theme : theme {
      screen {
         // Setting the width to 256 will snap the screen scaling factor in integer increments of 256 pixels (meaning graphic scaling can be crisp for precise pixel art)
         content_width = 256
      }
   }
}

1.19. Music

Note
THIS IS VERY EXPERIMENTAL AND MAY NOT WORK WELL OR AT ALL

Playing music is experimentally implemented in Adventuron.

MP3s can be referenced via absolute url (starting with http:// or https:// or relative urls).

If using relative urls, then to test your game, you must compile the game and use it from your local file system, or compile it and host it on a website. The relative urls
will not work using the classroom website as the assets will not be hosted there.

If there is a desktop version of Adventuron Classroom produces, this will not be an issue.

start_at = river

locations {
   river : location "You are in the river.\n" ;
}

on_tick {
   : if (is_just_entered () ) {
      : if (is_at "river") {
         : play_music sound="song_river";
      }
      : else {
         : stop_music;
      }
   }
}

assets {
   sounds {
      // NOTE :: Make sure that only reference content you are
      //         licensed to use.

      // ALSO :: You can use relative paths here, but you can only
      //         test in compiled versions of your code (not the
      //         web editor)
      song_river : sound_sample "https://somedomainid.com/sounds/river.mp3" ;
   }
}

1.20. Darkness

Darkness is a special state where a location cannot be described.

It’s not generally a very friendly state to leave the player in as it stops object lists and exit lists from being displayed (by default) but some people might want to add this to their game.

Darkness requires:

  1. A boolean that describes if the current state is dark or not

  2. Setting up a reference to the darkness boolean in the settings{} block.

  3. (Optionally) Customizing the darkness message, and possibly the darkness text colour.

In this example we use a dynamic boolean to dynamically calculate if it is dark.

Before a location is described, the darkness expression is evaluated and if it is dark, then the darkness message is displayed in place of the regular location text.

start_at = village

locations {
   village  : location "You are in the village" ;
   forest_1 : location "You are in a forest" ;
   forest_2 : location "You are in a spooky forest" ;
}

// Zones are ways to define a group of locations. In this case
// we just choose the locations we will mark as dark.
zones {
   dark_zone : zone {
      locations = [ forest_1, forest_2 ]
   }
}
booleans {
   // A dynamic boolean is an expression that is
   // freshly calculated every time it is referred to

   // The part within the {( )} characters is just like
   // an expression you would find between the () of an :if
   // statement

   // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
   is_dark : dynamic_boolean {(
      is_at "dark_zone" &&
      is_present "lamp" == false
   )}
}
game_settings {
   dark_expression = is_dark
}

connections {
   from, direction, to = [
      village, east, forest_1,
      forest_1, east, forest_2,
   ]
}
objects {
   lamp : object "a magical glowing lamp" at = "village" ;
}

// Setting the dark message is optional here
themes {
   my_theme : theme {
      system_messages {
         it_is_dark = Darkness surrounds you !
      }
   }
}

1.21. Advanced Darkness

This more advanced demonstration of darkness uses dynamic booleans to simplify in_game logic.

Boolean dynamics are used to simlify the logic in the on_command block.

Light lamp is matched before the darkness verb blocking.

start_at                 = start_room

locations {
   start_room : location "You are in a lovely treasure room." {

      on_command {
         : match "test _"  {
            : print "Should be blocked in the dark!" ;
            : done ;
         }
      }

   }
}

objects {
   // The crown is in the same location as the player at the start, but GET is blocked
   // so cannot be taken even if the player knows it's there.
   crown : object "a gold crown" at = "start_room" ;

   // The lamp description is dynamic, based on the state of the lamp
   lamp  : object "a lamp{is_lamp_lit ? ` (lit)`}" at = "inventory" ;

}

booleans {

   is_lamp_lit : boolean "false" ;

   is_dark     : dynamic_boolean  {(
      is_at "start_room" && is_lamp_lit == false
   )}

   // Everything except [movement, quit, save, load,
   // inventory, turns, look] is banned in the dark.
   is_inaccessible_darkness_verb : dynamic_boolean {(
      (
         /* is movement command matches any system compass direction verb + all aliases */
         is_movement_command () ||
         verb_is "quit"         ||
         verb_is "save"         ||
         verb_is "load"         ||
         verb_is "inventory"    ||
         verb_is "turns"        ||
         verb_is "look"
      ) == false
   )}
}



on_pre_command {

   // This comes before the darkness action block
   // so we can perform this action in the dark.

   : match "light lamp"  {
      : if (is_carried "lamp" && is_lamp_lit == false) {
         : set_true "is_lamp_lit" ;
         : print "You light the lamp" ;
         : press_any_key ;
         : redescribe;
      }
   }

   // Blocks everything except movement, quit, save, load, inventory, turns, look
   : if (is_dark && is_inaccessible_darkness_verb) {
      : print "You fumble in the dark." ;
      : done ;
   }
}


game_settings {
   // Tells Adventuron when to override the location description with
   // the darkness message (in the theme system messages), plus will remove direction list
   // and object list from the location layout (when is_dark resolves to true)
   dark_expression = is_dark
}

themes {
   my_theme : theme {
      colors {
         // Set the darkness pen colour to dark blue.
         dark_pen = 9
      }
      system_messages {
         it_is_dark = Darkness surrounds you.
      }
   }
}

1.22. Checking if the player or an object is in a zone

The following snippet demonstrates how to check if the player (or an item / object) is inside a zone.

start_at = lakeside

locations {
   lakeside       : location "You are by the side of a lake.";
   forest         : location "You are in a forest." ;
   treetop        : location "You are in a treetop." ;
}

connections {
   from, direction, to = [
      lakeside,       east,  forest,
      forest,         up,    treetop,
   ]
}

objects {
   lamp : object "a lamp" at = "forest"  ;
}

zones {
   forest_zone : zone { locations  = [ treetop, forest ] }
   treetop_zone : zone { locations  = [ treetop ] }
}

on_tick {

   : if ( is_at "forest_zone" ) {
      : append "The player is in the foresty zone. " ;
   }

   : if ( is_within { outer = "forest_zone" inner = "lamp" } ) {
      : append "The lamp is within the foresty zone. " ;
   }

   : if ( is_within {  outer = "treetop_zone" inner = "lamp" } ) {
      : append "The lamp is within the treetop zone. " ;
   }

}

Using advanced commands like is_within, can make your source code complex to read.

Using dynamic booleans can make the source of your game much easier to read.

This version is functionally identical to the snippet shown above.

start_at = lakeside

locations {
   lakeside       : location "You are by the side of a lake.";
   forest         : location "You are in a forest." ;
   treetop        : location "You are in a treetop." ;
}

zones {
   forest_zone  : zone { locations  = [ treetop, forest ] }
   treetop_zone : zone { locations  = [ treetop ] }
}

connections {
   from, direction, to = [
      lakeside,       east,  forest,
      forest,         up,    treetop,
   ]
}

objects {
   lamp : object "a lamp" at = "forest"  ;
}

booleans {
   is_player_in_forest_zone : dynamic_boolean {( is_at "forest_zone" )}
   is_lamp_in_forest_zone   : dynamic_boolean {( is_within { outer = "forest_zone"  inner = "lamp" } )}
   is_lamp_in_treetop_zone  : dynamic_boolean {( is_within { outer = "treetop_zone" inner = "lamp" } )}
}

on_tick {
   : if ( is_player_in_forest_zone ) { : append "The player is in the foresty zone. " ;   }
   : if ( is_lamp_in_forest_zone )   { : append "The lamp is within the foresty zone. " ; }
   : if ( is_lamp_in_treetop_zone )  { : append "The lamp is within the treetop zone. " ; }
}

1.23. Auto Redescribe Mode

Adventuron by default does not redescribe the current location text when a command is entered by the player. The screen refreshes when the player changes location, types LOOK, or when the game auther scripts a REDESCRIBE command.

Adventuron can be set to change this behaviour such that the screen will automatically refresh if the object list or exit list changes as a result of an action without having to issue a REDESCRIBE command.

The system handlers for get and drop are defaulted to being silent (as getting and dropping success will be conveyed by the updated object list). You can override this if you wish in the on_command block by matching "get _" and "drop _" then doing whatever you like.

Auto Redescribe Mode also changes the default layout to something more appropriate for object centric gameplay. The layout can be manually restored to whatever you like in the theme. Auto Redescribe Mode just affects the default.

If something is printed, then Adventuron will automatically insert a press_any_key command before refreshing the screen.

There are a number of gotchas to this approach, the first of which is that an auto-refresh is currently not a full refresh. It won’t call the on_describe() method.

There are a number of ways to mitigate this issue, and more will be forthcoming.

1.23.1. Basic Demonstration

##############################################
##
## Auto Redescribe Mode will automatically
## clear down the screen when the visible objects
## or visible exits change in the current location
##
## In auto_beta Redescribe Mode, auto redescribes do not
## generate a call to on_describe() when
## redescribing. on_describe() is only called when
## a : redescribe; command is issued or when the
## player changes location.
##
## It is anticipated that full 'auto' mode will be added
## with this behaviour corrected shortly.
##
##############################################

start_at   = village
redescribe = auto_beta

locations {
   village  : location "You are in the village.";
   forest   : location "You are in a forest"  ;
}

objects {
   lamp  : object "a lamp"  at = "village" ;
   pen   : object "a pen"   at = "village" ;
   sword : object "a sword" at = "village" ;
   rock  : object "a rock"  at = "village" ;
}

connections {
   from, direction, to = [
      forest, east, village,
   ]
}

barriers {
   block_forest : block {
      location               = forest
      message                = The path is blocked by rubble
      block_when_carried     = sword
   }
}

1.23.2. Advanced Demonstration

##############################################
##
## Auto Redescribe Mode will automatically
## clear down the screen when the visible objects
## or visible exits change in the current location
##
## In Auto Redescribe Mode, auto redescribes do not
## generate a call to on_describe() when
## redescribing. on_describe() is only called when
## a : redescribe; command is issued or when the
## player changes location.
##
##############################################

start_at   = village
redescribe = auto_beta

locations {
   village  : location "You are in the village.";
   forest   : location "You are in a forest"  ;
}

objects {
   lamp  : object "a lamp"  at = "village" ;
   pen   : object "a pen"   at = "village" ;
   sword : object "a sword" at = "village" ;
   rock  : object "a rock"  at = "village" ;
}

on_startup {

   : print "^nc^<-------------------------------------\nAuto Redescribe Mode\n-------------------------------------<15>>^m^" ;

   : print "<* Type UNLOCK to unlock the exit to the forest without printing anything (instant automatic refresh).<10>>" ;
   : print "<* Type UNLOCKP (after restarting) to unlock the exit to the forest whilst printing a message (a press any key will automatically be placed after the final print message).<11>>" ;
   : print "<* GET/DROP an object to auto refresh the screen.<12>>" ;
   : print "<* GET ALL or DROP ALL to observe one screen refresh only.<13>>" ;
   : print "<* When not unlocked the forest manually (seperate restart) getting the sword will unlock the forest, dropping the sword will lock the forest (exit list automatically updates).<14>>" ;
   : print "<* Auto redescribes do not trigger on_describe() as the describe happens after the on_tick() event therefore it would be dangerous to execute out of sequence or to run another describe without a tick.<15>>" ;

   : press_any_key ;

}

connections {
   from, direction, to = [
      forest, east, village,
   ]
}

barriers {
   block_forest : block {
      location               = forest
      message                = The path is blocked by rubble
      block_when_not         = is_forest_unblocked
   }
}

booleans {
   // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
   is_forest_unblocked : dynamic_boolean {(
      is_carried "sword" || is_unlocked
   )}
   is_unlocked : boolean "false" ;
}

on_command {
   : match "unlock _"  {
      // NOTICE :: We do not need to call redescribe here ...
      // If there is no printing, then a describe is automatically
      // called (but on_describe() will not be called in this instance)
      : set_true "is_unlocked" ;
   }

   : match "unlockp _"  {
      // NOTICE :: We do not need to call redescribe here ...
      : set_true "is_unlocked" ;
      : print "You unlock access to the forest" ;
   }
}

on_describe {
   // Playing some beeps to demonstrate when on_describe is called
   // If we printed on_describe here, then it would
   : print "on-describe()" ;
}

on_tick {
   : if (is_at "village" && is_first_entered()) {
      : print "NOTE : There is a tick counting bug on GET ALL and DROP ALL at the moment, this will be fixed shortly." ;
   }
   : beep millis = "80"  pitch = "0" ;
}

1.24. Linking External Assets

Importing graphics via the import menu is not scalable to a large number of larger graphics and so an alternative mechanism exists for using images (and other types of media) with Adventuron.

Adventuron cac referencing resources (such as graphics) from external files via either a relative path, or a web url.

e.g.

graphics {
  // The png file will be read from the same path as the game html file
   blah : png "mypng.png";

  // This file should be referencable from anywhere (but only if playing
  // online).
  // Absolute paths can be convenient in development, but for packaging,
  // relative paths are better.

   blah2 : jpeg "https://somedomain.com/somepath/myjpeg.jpg";
}
Warning
The web based editor currently can’t render resources supplied with a relative path (using an explicit url is fine) so when debugging it will look like there are no graphics. To see your graphics in your game, you will need to compile your html to the folder containing your assets.

1.24.1. Pre-caching linked images

Adventuron by default does not pre-cache images for your game. This may result in "pop-in" when Adventuron displays a graphic.

Adventuron supports asynchronous pre-caching of all assets, and also granular pre-caching of assets.

The 'precache_all' option will preload all images in the following order:

  1. Preload the image for location 1 (if it is a linked media image).

  2. Preload the images for locations neighbouring location 1, via the connection table (if they are linked media). Blocks are not taken into consideration.

  3. Preload the images for remaining locations (if they are linked media).

  4. Preload other images used by the game.

Note
Preloading sound effects is not yet supported.
game_settings {
   precache_strategy = precache_all
}

1.25. Changing Default Sound Effect (Jingles)

By default, Adventuron plays no sound effects, which is generally acceptable for modern "Interactive Fiction" style games.

Adventuron also supports contextual sound effects that can be enabled in a custom theme.

Note
Using the 'two' theme, or extending the 'two' theme will enable these sound effects by default.

1.25.1. Enabling the sound effects

The sound effects are off by default and must be enabled (per category) in the theme_settings{} section of a theme. By default, all sound effects are switched off, so it is the responsibility of the author to switch them on if required.

Even if the sound effects are enabled at the theme (or source code) level, then the player must still give consent for sound effects whent he game starts. Adventuron will automatically ask for permissions it requires (to play sound or music) as long as it hasn’t asked the same permission before.

Players can type SOUND OFF and SOUND ON during the game to change their preference during gameplay (NOTE : No UI exists for toggling sound in the current version of Adventuron but this is planned for a future release0>

start_theme = my_theme
themes {
   my_theme : theme {
      theme_settings {
         losegame_jingle = on
         success_jingle  = on
         wingame_jingle  = on
         failure_jingle  = on
      }
   }
}

1.25.2. Customizing the sound effects

Subroutine Name Details

success_jingle

Plays when the : success command is executed (user should map this).

failure_jingle

Plays when the : failure command is executed or when the player attempts to perform a bad command (such as moving in an invalid compass direction).

lose_game_jingle

Plays when the : lose_game (or : end_game) command is executed.

win_game_jingle

Plays when the : win_game command is executed.

1.25.3. Full source example (default jingles)

start_at    = my_location
start_theme = my_theme

themes {
   my_theme : theme {
      theme_settings {
         losegame_jingle = on
         success_jingle  = on
         wingame_jingle  = on
         failure_jingle  = on
      }
   }
}

locations {
   my_location : location "You are in a room with no exits.\n^n^Type WINGAME to play wingame jingle.\nType LOSEGAME to play losegame jingle.\nType FAILURE (or type bad direction) to play failure jingle.\nType SUCCESS to play success jingle.^m^" ;
}

on_command {

   : match "wingame _"  {
      : win_game ;
   }

   : match "losegame _"  {
      : lose_game;
   }

   : match "success _"  {
      : success ;
   }

   : match "failure _"  {
      : failure ;
   }

}

1.25.4. Full source example (customized jingles)

Here is an example where the author may wish to customize the jingles to play particular mp3 sounds (note that you can customize using beep commands too).

start_at    = my_location
start_theme = my_theme

themes {
   my_theme : theme {
      theme_settings {
         losegame_jingle = on
         success_jingle  = on
         wingame_jingle  = on
         failure_jingle  = on
      }
   }
}

locations {
   my_location : location "You are in a room with no exits.\n^n^Type WINGAME to play wingame jingle.\nType LOSEGAME to play losegame jingle.\nType FAILURE (or type bad direction) to play failure jingle.\nType SUCCESS to play success jingle.^m^" ;
}

on_command {

   : match "wingame _"  {
      : win_game ;
   }

   : match "losegame _"  {
      : lose_game;
   }

   : match "success _"  {
      : success ;
   }

   : match "failure _"  {
      : failure ;
   }

}

subroutines {

   success_jingle : subroutine {
      : play_sound "incidental_bell";
   }
   failure_jingle : subroutine {
      : play_sound "incidental_boop";
   }
   lose_game_jingle : subroutine {
      : play_sound "incidental_wawawaaa";
   }
   win_game_jingle : subroutine {
      : play_sound "incidental_tadaa";
   }

}

assets {
   sounds {
      // Note that sound sample assets refer to assets
      // that must be available at the parallel level to the
      // compiled html file (will not be playable in the web
      // version of the Adventuron Editor -- need to compile
      // to test.)
      incidental_bell     : sound_sample "bell.mp3" ;
      incidental_boop     : sound_sample "boop.mp3" ;
      incidental_wawawaaa : sound_sample "wawawaa.mp3" ;
      incidental_tadaa    : sound_sample "tadaa.mp3" ;
   }
}

1.26. Disabling Dynamic Connection Scanning

By default, Adventuron will scan the on_command {} table for additional dynamic directions which may be available to a game.

The following source code shows a game where the field connects (bidirectionally) eastwards from the field to the forest location. A : match record has also been added that intercepts the "west" verb, and will go to the lake if the player types "west" from the field.

Adventuron detects this, and will present "west" as a direction.

start_at                 = field

locations {
   field   : location "You are in a field." ;
   forest  : location "You are in a forest." ;
   lake    : location "You are next to a lake." ;
}

connections {
   from, direction, to = [
      field, east, forest,
   ]
}

on_command {
   : match "west _"  {
      : if (is_at "field") {
         : goto "lake" ;
         : redescribe;
      }
   }
}

If an author wishes to disable connections from being scanned in the on_command {} table then, use the 'exit_list_calculation = basic' option, as shown below:

start_at                 = field

game_settings {
   # basic = only use connections table and do not use on_command table.
   exit_list_calculation = basic
}

locations {
   field   : location "You are in a field." ;
   forest  : location "You are in a forest." ;
   lake    : location "You are next to a lake." ;
}

connections {
   from, direction, to = [
      field, east, forest,
   ]
}

on_command {
   : match "west _"  {
      : if (is_at "field") {
         : goto "lake" ;
         : redescribe;
      }
   }
}

1.27. Finding An Object

Note
This snippet only checks for direct ownership, and will not snap up to the location id.

Use the < parent_of "entity_id" > function to access the id of the direct parent (or holder) of an object.

start_at = village

locations {
   village  : location "You are in the village.";
}

objects {
   sword : object "a sword"  ;
   lamp  : object "a lamp"  at = "inventory" ;
   spoon : object "a spoon" at = "village"  ;
   fork  : object "a fork"  at = "bag";
   bag   : object "a bag"   at = "inventory" container_type="bag";
}

on_tick {
   : if (parent_of "lamp" == "inventory") {
      : print "The lamp is in your inventory." ;
   }
   : if (parent_of "spoon" == current_location()) {
      : print "The spoon is in the same location as you." ;
   }
   : print {( "Location of Lamp  : " + parent_of "lamp" )}
   : print {( "Location of Sword : " + parent_of "sword" )}
   : print {( "Location of Spoon : " + parent_of "spoon" )}
   : print {( "Location of Bag   : " + parent_of "bag" )}
   : print {( "Location of Fork  : " + parent_of "fork" )}

}

1.28. Pocket When Inventory Space Is Short

The following example demonstrates that Adventuron will recover when a pocket command is executed when the player has no more inventory capacity. The gifted object is simply placed on the floor in the location where the player is next. This is very important as some in-game events cannot be repeated, and the object needs somewhere to be.

start_at = village

locations {
   village  : location "You are in the village.\nType <SWORD<12>> to attempt to place a sword in your pocket / inventory (inventory limit is 1 in this game) then it will move the player to the forest." ;

   forest : location "You are in a forest.\nType <SWORD<12>> to attempt to place a sword in your pocket / inventory (inventory limit is 1 in this game)." ;

}

objects {
   lamp  : object "a lamp"  at = "inventory" ;
   pen   : object "a pen"   at = "village" ;
   sword : object "a sword"  ;
   rock  : object "a rock"  at = "village" ;
}

integers {
   inventory_limit : integer "1" ;
}

connections {
   from, direction, to = [
      village, east, forest,
   ]
}

on_tick {
   : inventory;
}

on_command {
   : match "sword _"  {
      : print "Attempting to create and put a sword in your pocket, then moving to the forest." ;
      : pocket "sword" ;
      : goto "forest" ;
      : press_any_key ;
      : redescribe;
   }
}

game_settings {
   inventory_items_limit_var = inventory_limit
}

1.29. Eliminating Parser Delay

Adventuron comes pre-configured with a small amount of parser delay. To switch off this delay, use the following snippet.

start_at = village

locations {
   village  : location "You are in the village.";
}

themes {
   my_theme : theme {
      delays {
         // 0 milliseconds of delay
         default = 0
      }
   }
}

1.30. Implementing A Safe

Adventuron naturally tries to match inputs that the player enters against its vocabulary. There is one exception, and that is in the case of a verb and a noun entered.

In the case that one word is entered, or two words are entered (language independent), then Adventuron records the original verb and the original noun.

These will store the exact inputs the player entered. In English, the verb comes before the noun. In other languages, vice versa. The parser is aware of which logic to apply depending on the language it is dealing with.

So, we can access the original verb with experimental_parse_result_original "verb" and the original noun with experimental_parse_result_original "noun1". These are functions that return string values, empty if there was not a value provided.

When dealing with safe combinations, we want to check that the player either entered a number in the verb or noun position.

To do this we simply check that we are in the correct context (in the same location as a safe), then we check to see if either the verb or noun are numeric using the is_int() function (is integer?).

If the original verb contains an integer, then match it in the verb position using match. Match the correct combination first. After matching the good combination, we use a : done; command to stop handling items in the current event block (on_command{}).

We use a similar pattern for matching the combination in the noun position, except that we also check for "dial" as the verb.

start_at  = office
locations {
   office : location "You are in an office" ;
}
objects {
   safe : scenery "a safe" at = "office" msg="The safe has a keypad.\nType DIAL XXXX (where XXXX is a number to try to open the safe).";
}

strings {
   safe_combo : string;
}

on_startup {
   : set_string var = "safe_combo"  {(
      random (9) + "" + random (9) + "" + random (9) + "" + random (9)
   )}
}

on_describe {
   // Usually you would not print the combo, it would be part of the combo.
   : print {("The safe combo this time is : " + safe_combo)}
}

on_command {
   : if (is_present "safe") {
      : if (is_int (experimental_parse_result_original "verb")) {
         : if (safe_combo == experimental_parse_result_original "verb") {
            : print "You enter the correct safe combination" ;
            : done ; // Stops it from matching anything else
         }
         : print "Wrong Combo" ;
      }
      : match "dial _"  {
         : if (is_int(experimental_parse_result_original "noun1")) {
            : if (safe_combo == experimental_parse_result_original "noun1") {
               : print "You enter the correct safe combination" ;
               : done ; // Stops it from matching anything else
            }
            : print "Wrong Combo" ;
         }
         : else {
            : print "Please type DIAL XXXX (where XXXX is a number)." ;
         }
      }
   }
}

1.31. Treasure Hunt Customization

By specifying "treasure_room = xxxx" at the top of an Adventuron game file, the author signals to adventuron that they want to set up a 'typical' treasure hunt mode.

Setting up the treasure hunt mode will change the default layout (for any themes) but it will also set up a win_game condition (automatically).

The win game condition is essentailly prepended to the on_tick block and looks like this:

## Adventuron AUTOMATICALLY adds this : if block if the author activates treasure hunt mode :
on_tick {
   : if (treasure_deposited() == treasure_total()) {
      : print << your_theme / system_messages / all_treasures_found_win_game >> ;
      : win_game;
   }
}

If you want a bespoke end game routing (perhaps a clear screen is required, some music, perhaps a graphic or animation), then you have to use the following two blocks of code:

Snippet One - Will remove the default win game condition from the treasure hunt mode.

game_settings {
   treasure_hunt_mode = bespoke
}

Snippet Two - Place your own win-game condition in the on_tick block (tests to see if the treasure item count is the same as the treasure item in the treasure room count):

: if (treasure_deposited() == treasure_total()) {
   : print "THIS IS MY CUSTOM END GAME HANDLER";
   : win_game;
}

Full example of a bespoke treasure hunt mode …​.

Note
The 'two' theme is a theme that is built into Adventuron, and provides a minimal textual set of system response messages, as well as formats the screen similarly to the game 'two', released in 2019.
start_at      = tomb
start_theme   = two
treasure_room = treasure_room

locations {
   treasure_room : location "Treasure Room";
   tomb          : location "Tomb";
}

connections {
   from, direction, to = [
      treasure_room, east, tomb,
   ]
}

objects {
   lamp : object "a lamp" at = "tomb" treasure = "true";
}

game_settings {
   treasure_hunt_mode = bespoke
}

on_tick {
   : if (treasure_deposited() == treasure_total()) {
      : clear_screen;
      // NOTE : Win game jingle does not play when bespoke
      //        Treasure Hunt Mode Is Used (play your own jingle).
      : print "THIS IS MY CUSTOM END GAME HANDLER";
      : win_game;
   }
}

1.31.1. Coding a treasure hunt endgame

A treasure hunt endgame is some action that must be performed by the player after placing all the treasure in the treasure room.

Coding a treasure hunt endgame is very specific to the endgame you have in mind.

The first condition is that you want to have all your treasures in the treasure room. You can use the "treasure_deposited() == treasure_total()" as shown previously to check for this.

The first time that all the treasure is placed, best to signal some sort of reward (such as a : success; command plus a message that tells the player that the endgame has started).

This should take the form of a boolean variable that is set the first time that all the treasure is placed. You may want to play the success message multiple times when the treasure is placed, in which case the second boolean is not required.

Rather than say any more, study (and adapt) the following code.

start_at      = beach
start_theme   = two
treasure_room = styx
redescribe    = auto_beta

game_settings {
   treasure_hunt_mode = bespoke
}

locations {
   beach         : location "Beach";
   tomb          : location "Tomb";
   styx          : location "River Styx" ;
}

connections {
   from, direction, to = [
      beach, east, tomb,
      tomb, down, styx,
   ]
}

objects {
   coin_1   : object "a <red coin<10>>" at = "tomb" treasure = "true";
   coin_2   : object "a <blue coin<13>>" at = "beach" treasure = "true";
   ferryman : scenery "the ferryman" msg = "TWO COINS" at = "styx";
}

vocabulary {
   : verb / aliases = [examine, talk]
}

booleans {
   has_found_all_treasures_before : boolean;
   has_found_all_treasures : dynamic_boolean {(
      treasure_deposited() == treasure_total()
   )}
}

on_command {
   : match "pay ferryman; pay man"  {
      : if (is_at "styx") {
         : if (has_found_all_treasures) {
            : print "YOU WIN THE GAME!" ;
            : win_game ;
         }
         : else {
            : print "NEED TWO COINS" ;
         }
      }
   }
}

on_describe {
   : if (is_at "styx") {
      : if (has_found_all_treasures) {
         : print "\"PAY FERRYMAN\"" ;
      }
      : else {
         : print "DROP TWO COINS HERE." ;
      }
   }
}

on_tick {
   : if (is_at "styx" && has_found_all_treasures) {
      // We use the second boolean to only  show this message once
      // We use manual redescribe to force on_describe{} message to show
      : if (has_found_all_treasures_before == false) {
         : success ;
         : print "ALL COINS FOUND." ;
         : set_true "has_found_all_treasures_before" ;
         : press_any_key ;
         : redescribe;
      }
   }
}

1.32. Checking Permissions

Adventuron requires active consent for sound, blips, and music. Graphic permission is implied, but can be revoked by the player.

You may work some logic into your game to play sounds, or music effects but one problem is that the beep command is synchronous (the game must wait for the beep to play).

If yoy are going to play a lot of beep sound effects, it’s better to manually check if sound permission is on. Players can grant sound permission by typing SOUND ON, and revoke it by typing SOUND OFF. If you have sound in your game, the game will ask you when the game first starts if you want sound on or off, but it won’t ask a second time. The player needs to actively type SOUND ON to enable sound.

Anyway, the way to test for various permissions is to use the sysvar_bool() function, demonstrated below:

: if (sysvar_bool "sysvar_sound_enabled") {
   // Sound is enabled
}
: else {
   // Sound is not enabled
   // Player can type SOUND ON to switch on sound, or SOUND OFF at runtime
}

Here are a table of permission categories (which can be used as parameters to the sysvar_bool "" function):

Permission Id Details

sysvar_features_sound

True if the current game has any kind of sound (blip, ambient sound, incidental sound, or music).

sysvar_features_music

True if the current game has music.

sysvar_features_blip

True if the current game has blips.

sysvar_has_asked_blip

True if the current game has already asked for blip permission.

sysvar_has_asked_music

True if the current game has already asked for music permission.

sysvar_has_asked_sound

True if the current game has already asked for sound permission.

sysvar_blip_enabled

True if blip is enabled, false otherwise.

sysvar_sound_enabled

True if sound is enabled, false otherwise.

sysvar_music_enabled

True if music is enabled, false otherwise. If sysvar_sound_enabled is false, then music will not play.

sysvar_sfx_enabled

True if sound effects are enabled, false otherwise. If sysvar_sound_enabled is false, then sound effects will not play.

sysvar_ambient_enabled

True if ambient sound is enabled, false otherwise. If sysvar_sound_enabled is false, then ambient sound will not play.

sysvar_graphics_enabled

True if graphics are enabled, false otherwise.

If you want to simplify the use of the sysvar_bool() funtion, consider setting up a dynamic_boolean:

Snippet shown below (not full adventuron source file):

booleans {
   is_sound_enabled : dynamic_boolean {( sysvar_bool "sysvar_sound_enabled" )}
}

on_command {
      : match "play piano" {
         : if (is_present "piano") {
            : if (is_sound_enabled) {
               // Play beeps here
            }
            : else {
               : print "You play a few notes on the piano";
            }
         }
   }
}

1.33. Containers

Containers are not yet supported by Adventuron (preliminary support is in the engine).

You can check if an object is inside a location as follows:

start_at = lake

locations {
   forest : location "You are in a forest." ;
   lake   : location "You are by a lake" ;
}

objects {
   lamp  : object "a lamp" at = "forest" ;
}

connections {

   from, direction, to = [
      forest, south, lake,
   ]

}

on_tick {

   : if (is_within_direct { outer = "forest" inner = "lamp" } ) {
      : print "The lamp is in the forest (directly)." ;
   }
   : else {
      : print "The lamp is NOT in the forest (directly)." ;
   }

}

1.34. List Capitalization

The formatting of the object list, exit list, and inventory list can be overridden within the theme settings.

Note
If you only declare one theme, this will become the default theme, no matter what the identifier of the theme is.
start_at = village

locations {
   village  : location "You are in the village" ;
   forest_1 : location "You are in a forest" ;
   forest_2 : location "You are in a spooky forest" ;
}
connections {
   from, direction, to = [
      village, east, forest_1,
      forest_1, east, forest_2,
   ]
}
objects {
   lamp : object "a magical glowing lamp" at = "village" ;
}

themes {
   my_theme : theme {
      lister_objects {
         item_capitalization = original
      }
   }
}

1.35. Masking Commands

The on_command {} event handler typically contains code that will do something specific for your game.

If something cannot be matched in the on_command {} block then a default handler for the command will be executed.

If the player types something random then the engine will display the appropriate system message for the input, but there are also generic handlers for the common verbs such as get, drop, wear, remove, go east, go north, etc.

These default handlers are executed when the player input doesn’t match something in the on_command {} block, and they are not executed if something is executed in the on_command {} block.

Note
Commands such as done, if, match, else, else_if are not considered a match, but the CONTENT of an if statement is considered a match. That is, if you test for a particular condition inside your match, and the expression is evaluated to be negative, Adventuron has done nothing except the test, therefore Adventuron will still run the default handler for the entered user command (assuming nothing else matches in the on_command {} block).

99% of the time, this is correct behaviour, but sometimes you may want to do something in addition to the system event handler.

In these cases, you need to hide your handler from Adventuron so it doesn’t know you’ve executed your own code.

Anything inside the : mask {} command will hide the user handler from the system. This means that the system handler will be executed in addition to the code inisde your mask{} block.

As always, this concept is best demonstrated.

In the code below, try running it as is, then remove the : mask{} block (comment it out) and observe the behaviour difference.

start_at = field
locations {
   field      : location "You are in a field." ;
   meadow     : location "You are in a meadow." ;
   river_bank : location "You are at a river bank." ;
}
connections {
   from, direction, to = [
      field,  east, meadow,
      meadow, east, river_bank,
   ]
}

integers {
   num_times_try_go_east : integer "0" ;
}

on_command {
   : match "e _"  {
      // Anything inside a mask will not override a
      // system handler. In this case, we wish to
      // increment a counter everytime we try to
      // head east, but we don't want to block the
      // system movement handler.
      : mask {
         : increment "num_times_try_go_east" ;
      }
   }
}

on_tick {
   : print "You have tried to move east {num_times_try_go_east} time(s)." ;
}

1.36. How to see which locations are adjacent to the current location

The try_move() function will return a string containing the id of the location in the direction of the verb that the player entered. If there is no accessible location in that direction (no connection, or an active block), then a blank string will be returned.

To check to see that there is a valid location:

: if (try_move() != "") {
   // Do something
}

To check to see there is no location or a blocked location:

: if (try_move() == "") {
   // Do something
}

A larger example is here !

start_at = meadow
locations {
   field      : location "You are in a field." ;
   meadow     : location "You are in a meadow." ;
   river_bank : location "You are at a river bank." ;
}
connections {
   from, direction, to = [
      field,  east, meadow,
      meadow, east, river_bank,
   ]
}

on_command {
   : match "e _"  {
      : if (try_move() != "") {
         // Note :: This overrides the system movement handler (see the mask section for more information on this)
         : print "You can go east, but you choose not to go.";
      }
   }
}

A masked example (uses mask + try move):

start_at = field
locations {
   field      : location "You are in a field." ;
   meadow     : location "You are in a meadow." ;
   river_bank : location "You are at a river bank." ;
}
connections {
   from, direction, to = [
      field,  east, meadow,
      meadow, east, river_bank,
   ]
}

integers {
   num_times_gone_east : integer "0" ;
}

on_command {
   : match "e _"  {
      // Anything inside a mask will not override a
      // system handler. In this case, we wish to
      // increment a counter everytime we successfully
      // head east, but we don't want to block the
      // system movement handler.
      : mask {
         // Try move contains the id of the location
         // correspondin to the direction verb in
         // the logial sentence
         // If the direction leads nowhere or it
         // is blocked, then a blank string is returned.
         : if (try_move() != "") {
            : increment "num_times_gone_east" ;
         }
      }
   }
}

on_tick {
   : print "You have successfully gone east {num_times_gone_east} time(s)." ;
}

1.37. Door Interrogation

The 'is_barrier_active' and 'is_locked' boolean functions can be used to test the state of a door or a barrier.

See the example below.

start_at = lakeside

locations {
   lakeside       : location "You are by the side of a lake.";
   outside_castle : location "You are outside the castle. There is a door." ;
   castle         : location "You are inside the castle." ;
}

connections {
   from, direction, to = [
      lakeside,       north, outside_castle,
      outside_castle, north, castle,
   ]
}

objects  { key : object  "a key" at="lakeside"; }

barriers {
   my_door : door {
      from = outside_castle
      to   = castle
      key  = key
   }
}

on_tick {
   : if (is_at "outside_castle") {
      : if (is_blocking "my_door") {
         : print "The door is closed." ;
      }
      : else {
         : print "The door is open." ;
      }
      : if (is_locked "my_door") {
         : print "The door is locked." ;
      }
      : else {
         : print "The door is unlocked." ;
      }
   }
}

1.38. Copying The Value Of A Variable To Another Variable

To copy the value of a variable into another variable use set_integer (for numbers), set_string (for text) and set_boolean (for booleans).

## int_b = int_a
: set_integer var = "int_b"    {(int_a)}
## string_b = string_a
: set_string  var = "string_b" {(string_a)}
## bool_b = bool_a
: set_boolean var = "bool_b"   {(bool_a)}

A full demo of all three types of copy is included below (type COPY at the command to initial the copy.

The location text is formatted to debug the value of all the variables.

start_at = my_location

locations {
   my_location : location "^n^int_a is {int_a}\nint_b is {int_b}\nbool_a is {bool_a}\nbool_b is {bool_b}\nstring_a is {string_a}\nstring_b is {string_b}^m^\nType <COPY<12>> to copy A variables to B variables.";
}

integers {
   int_a    : integer "5" ;
   int_b    : integer "0" ;
}
booleans {
   bool_a   : boolean "true" ;
   bool_b   : boolean "false" ;
}
strings {
   string_a : string "toast";
   string_b : string "eggs";
}

on_command {
   : match "copy _"  {
      : set_integer var = "int_b"    {(int_a)}
      : set_string  var = "string_b" {(string_a)}
      : set_boolean var = "bool_b"   {(bool_a)}
      : print "Copied all A variables into B variables." ;
      : press_any_key ;
      : redescribe;
   }
}

(Advanced)

It’s also possible to include expressions on the right hand side:

# If we are running on a mobile client, then "copy the value of a and add 1 into b", else "copy the value of a into b".
: set_integer var = "int_b"    {(is_mobile() ? int_a + 1 : int_a)}

1.39. Experimental Disambiguation

Adventuron has a basic (experimental) system-level method of disambiguating the first subject, where you wish to create adventures with multiple objects/scenery with the same noun, or possibly even adjective/noun.

System level disambiguation is already in place for GET/DROP/WEAR/REMOVE/EXAMINE, but needs to be added for user verbs, as different verbs will assume objects are in a certain place. For example, the DROP command only relates to object the player is holding, and GET only relates to objects that the player is beside but not holding.

: disambiguate_s1 "carried";

If successful will place exactly one object_id into the subject 1 slot of the logical sentence - s1().

If nothing is found, then 'unknown' will be returned by s1() . 'unknown' is a reserved object id and authors are not permitted to use 'unknown' as their own object id.

Warning
Adventuron will be receiving a much larger update that will more easily resolve subject disambiguation later in 2020. Until then, a temporary command has been added. This command will be replaced with the approved method later in the year, so treat this as a temporary API for now (the full API will be a superset of this functionality, but a minor change to source will be required).
Disambiguation Category Details

carried

Assumes s1() refers to an item (worn or unworn) that the player is carrying.

beside

Assumes s1() refers to an item (worn or unworn) that the player is beside (inside the location that the player is in but not carried).

present

Assumes s1() refers to an item (worn or unworn) that the player is carrying or is beside (in the same location).

worn

Assumes s1() refers to an item (worn or unworn) that the player is carrying and is worn.

unworn

Assumes s1() refers to an item (worn or unworn) that the player is carrying and is not worn and is wearable.

start_at = my_location

locations {
   my_location : location "You are in a room." ;
}

objects {
   magenta_coin    : object "a <magenta coin<11>>" start_at = "inventory" ;
   red_coin        : object "a <red coin<10>>"     start_at = "inventory" ;
   green_coin      : object "a <green coin<12>>"   start_at = "inventory" ;
   vending_machine : scenery "a vending maching ({coins_in_vending_machine} / 3)"   start_at = "my_location" ;
   candy           : object "a candy bar" ;
}

on_describe {
   : if (carried ()  > 0) {
      : inventory;
   }
}

integers {
   coins_in_vending_machine : integer;
}

on_command {
   : match "insert coin; use coin" {


      : if (is_present "vending_machine") {

         //
         // disambiguate_s1 "carried"
         //
         // Will check that the adjective noun in the first slot matches exactly one object (or entity)
         // If not, it will present the player with a list of matches (if there are 2 or more), otherwise
         // it will set 'unknown' (if zero matches).

         : disambiguate_s1 "carried";

         // At this point, s1() will return either 'unknown' or the entity id of a single subject.

         : if (s1() == "unknown") {
            : print "No coin to insert." ;
            : done;
         }

         : if (subject1_is "red_coin" || subject1_is "magenta_coin" || subject1_is "green_coin") {
            // In this example, we don't care which coin is used first, middle or last, but at this point
            // we could isolate each coin and handle them differently if we wanted to.
            // When using s1() == "object_id", watch out for typos.
            : print "Ker-ching." ;
            : increment "coins_in_vending_machine" ;
            : destroy;
            : if (coins_in_vending_machine == 3) {
               : create "candy" ;
               : beep millis = "100"  pitch = "0" ;
               : beep millis = "100"  pitch = "2" ;
               : beep millis = "100"  pitch = "4" ;
               : beep millis = "100"  pitch = "6" ;
               : beep millis = "100"  pitch = "8" ;
            }
            : else {
               : beep millis = "100"  pitch = "0" ;
               : beep millis = "300"  pitch = "6" ;
            }
            : redescribe;
         }
      }
   }
}

1.40. Context Menus

A later version of Adventuron will support flexible context menus per object. It will be able to determine which actions are appropriate to certain objects, and the author will be able to control if all options are available from the start of the game, or if the player has to type nouns first before they appear as an option.

In the current version of Adventuron a limited version of this system is available, by right clicking with the mouse, or long pressing with touch (a finger) on a listed object, a context menu will appear, in which it is possible to select GET, DROP, or EXAMINE verbs. GET and DROP are only available where it makes sense in the context (you can’t drop an object you are not holding, you can’t get an object you are already holding or a scenery object).

There will be a very rich API for controlling context menus in a later release, but right now, you can enable these context menus in the theme settings as shown below:

theme_settings {
   experimental_contextual_menus = true
}

A more full example is shown below:

Note
The 'two' theme is a theme that is built into Adventuron, and provides a minimal textual set of system response messages, as well as formats the screen similarly to the game 'two', released in 2019.
start_at     = room
start_theme  = my_theme
redescribe   = auto_beta

locations {
   room : location "Room" ;
}

objects {
   yellow_horn  : object "a <yellow horn<14>>"  at = "room" msg="Look at the sky.";
   magenta_horn : object "a <magenta horn<11>>" at = "room" msg="Deep Purple.";
   cyan_horn    : object "a <cyan horn<13>>"    at = "room" msg="Misty.";
}

themes {
   my_theme : theme {
      extends = two
      theme_settings {
         experimental_contextual_menus = true
      }
   }
}

1.40.1. Auto Beta Mode 2

Sometimes you may want to maintain the command log, in that case, then use the auto beta mode 2.

Technically, the auto redescribe is not a redescribe at all, it simply binds the current state of the game to the layout of your theme.

e.g. When the room state changes, it does not execute an on_describe() event (same as auto beta mode 1).

This mode does not silence the get and drop commands by default (as in auto beta mode 1) and it doesn’t insert a press any key if you print something (like auto beta 1).

Note
The 'two' theme is a theme that is built into Adventuron, and provides a minimal textual set of system response messages, as well as formats the screen similarly to the game 'two', released in 2019.
start_at    = start_location
start_theme = my_theme

// Auto Beta 2 - Another experimental
//             auto redescribe test mode
//               Will redescribe and maintain
//               the entire command log.
//
//             NOTE : Subject to change...
redescribe               = auto_beta_2

strings {
   locdesc : dynamic_string {(
      (is_beside "spoon" ? "You are in a spoony place" : "You are in a place") + ", and you have taken " +
      turns() + " turns."
   )}
}

locations {
   start_location : location "{locdesc}";
}

objects {
   lamp  : object "a lamp"  at = "start_location" ;
   spoon : object "a spoon" at = "start_location" ;
}

on_describe {
   : print "Hello" ;
}

themes {
   my_theme : theme {
      extends = two
      theme_settings {
         layout  = SB G D X O SEP "adv_line_red" LOCK
         columns = 64
      }
      lister_objects {
         show_when_empty         = true
      }
      status_bar {
         : fixed_text "DEMO PROGRAM" ;
         : fixed_text "ADVENTURON" ;
      }
      system_messages {
         object_list_header   = YOU SEE:\s
         object_list_empty    = Nothing
         object_list_end_text = .
      }
   }
}

assets {

   graphics {
      blue_image  : base64_png "iVBORw0KGgoAAAANSUhEUgAAAQAAAABQAQMAAADMRTlmAAAAA1BMVEU/SMwiP9ThAAAAGUlEQVRIx+3BMQEAAADCIPunNsReYAAAEBwKUAABS3aq6QAAAABJRU5ErkJggg==";
      green_image : base64_png "iVBORw0KGgoAAAANSUhEUgAAAQAAAABQAQMAAADMRTlmAAAAA1BMVEUisUweBz0WAAAAGUlEQVRIx+3BMQEAAADCIPunNsReYAAAEBwKUAABS3aq6QAAAABJRU5ErkJggg==";
      red_image   : base64_png "iVBORw0KGgoAAAANSUhEUgAAAQAAAABQAQMAAADMRTlmAAAAA1BMVEXtHCTcNMelAAAAGUlEQVRIx+3BMQEAAADCIPunNsReYAAAEBwKUAABS3aq6QAAAABJRU5ErkJggg==";
      // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
      start_location : dynamic_graphic {(

         /* random(2) retuns value between 0 and 2 */
         /* 33% chance here to select red*/
         random (2) < 1 ? "red_image"  :

         /* random(2) retuns value between 0 and 1       */
         /* 50% chance here between two remaining values */
         /* (50% multiplied by 66% = 33%                 */
         random (1) < 1 ? "blue_image" : "green_image"
      )}
   }

}

1.41. Multiple Choice (Beta)

Adventuron has a method of presenting multiple choices.

Multiple choices are displayed using the " : choose " command, and will display options for each choice that has been added via the " : add_choice " command. Rather than setting the value of a variable, the choose command allows Adventuron to conditionall run sub commands if the choice is selected. The commands to run are contained between { } brackets.

This can be useful for an hybrid "on rails" / parser type game.

All choices are flushed every time the player selects ': choose'.

Important
This method of presenting choices is only intended for hybrid parser / choice based games. Games are only saveable when the command prompt (not a choice prompt) is displayed and the player types SAVE currently. Autosave does not (currently) work inside of : choose;
Important
Adventuron will has a 'pure' choice (gamebook) only mode that will be documented later on (currently under development). This alternate form of multiple choice will autosave correctly.
start_at = start_location

locations {
   start_location : location "Type 'choice demo' to see multiple choice demo." ;
}

on_command {

   : match "choice demo"  {

      // Adds a choice

      : add_choice "Fight Wizard" {
         : print "Fight The Wizard Routine.";
      }


      // Adds a choice

      : add_choice "Run Away" {
         : print "You run away.";
         : lose_game;
      }

      // Adds a choice

      : add_choice "Use Something" {
         : print "You look in your inventory....";
      }

      // If no choices have been added, then choose will do nothing.

      : choose "What do you want to do?";

   }

}

1.41.1. Colour cycling for multiple choice options

Later versions of Adventuron will improve theming for multiple choice options. By default, multiple choice options are coloured the same as regular story text, but you can add a colour cycler for multiple choice options (always resets for each list of choices displayed):

start_at                 = start_location
start_theme              = my_theme

locations {
   start_location : location "Type 'choice demo' to see multiple choice demo." ;
}

on_command {

   : match "choice demo"  {

      : add_choice "Fight Wizard" {
         : print "Fight The Wizard Routine.";
      }

      : add_choice "Run Away" {
         : print "You run away.";
         : lose_game;
      }

      : add_choice "Use Something" {
         : print "You look in your inventory....";
      }

      : choose "What do you want to do?";

   }

}

themes {

   my_theme : theme {
      colors {
         multiple_choice_pen = [ "#f00","#0f0", "#00f"]
      }
   }

}

Another example of gamebook mode (gamebook mode is subject to change syntax) .

NOTE : The 'option' command is an alias for 'add_choice'.

start_at  = 001_hallway
pages {
   001_hallway "You are in the hallway." {
      : option "Turn to page 2 to go to kitchen." ;
      : option "Turn to page 3 to go to library." ;
      : option "Turn to page 4 to leave the house";
   }
   002_kitchen  "You are in kitchen." {
      : option "Turn to page 1 to go to hallway." ;
      : option "Turn to page 3 to go to library." ;
   }
   003_library "You are in library." {
      : option "Turn to page 1 to go to hallway." ;
      : option "Turn to page 2 to go to kitchen." ;
   }
   004_finish "You are outside the house." ;
}

themes {
   my_theme : theme {
      lister_choices {
         gamebook_choice_list_style = list
      }
   }
}

1.42. Dynamic Graphics

Adventuron utilizes graphics by referencing them via their graphic id. Graphics that share the same id as a location, or automatically associated with that location, as their location graphic.

Location graphic ids can also be updated during a game manually via the

: set_graphic = "GRAPHIC_ID"  target = "LOCATION_ID" ;

But, there is also a more powerful way of manipulating graphics at runtime via use of a dynamic_graphic.

The example shown below will select a red, green, or blue image at random for a single location.

start_at     = start_location

locations {
   start_location : location "You are in a room (keep typing LOOK to see image randomize)." ;
}

assets {
   graphics {
      blue_image  : base64_png "iVBORw0KGgoAAAANSUhEUgAAAQAAAABQAQMAAADMRTlmAAAAA1BMVEU/SMwiP9ThAAAAGUlEQVRIx+3BMQEAAADCIPunNsReYAAAEBwKUAABS3aq6QAAAABJRU5ErkJggg==";
      green_image : base64_png "iVBORw0KGgoAAAANSUhEUgAAAQAAAABQAQMAAADMRTlmAAAAA1BMVEUisUweBz0WAAAAGUlEQVRIx+3BMQEAAADCIPunNsReYAAAEBwKUAABS3aq6QAAAABJRU5ErkJggg==";
      red_image   : base64_png "iVBORw0KGgoAAAANSUhEUgAAAQAAAABQAQMAAADMRTlmAAAAA1BMVEXtHCTcNMelAAAAGUlEQVRIx+3BMQEAAADCIPunNsReYAAAEBwKUAABS3aq6QAAAABJRU5ErkJggg==";
      // NOTE : This syntax is due to change (slightly) in later revisions of Adventuron.
      start_location : dynamic_graphic {(

         /* random(2) retuns value between 0 and 2 */
         /* 33% chance here to select red*/
         random (2) < 1 ? "red_image"  :

         /* random(2) retuns value between 0 and 1       */
         /* 50% chance here between two remaining values */
         /* (50% multiplied by 66% = 33%                 */
         random (1) < 1 ? "blue_image" : "green_image"
      )}
   }
}

The script notation from dynamic_integer, dynamic_string, and dynamic_boolean is featured here.

random(2) produces a number at random between 0 and 2 (3 distinct values).

The following notation is read as follows:

CONDITION ? if_true : if_false

These conditions can be chained together as follows

CONDITION1 ? if_true_1 :

CONDITION2 ? if_true_2 :

CONDITION3 ? if_true_3 :

CONDITION4 ? if_true_4 : if_false

In brief, it means if CONDITION1 returns a true value, then select if_true_1, otherwise if CONDITION2 returns a true value, then select if_true_2, otherwise if CONDITION3 returns a true value, then select if_true_3, otherwise if CONDITION4 returns a true value, then select if_true_4, otherwise select if_false.

As many conditions as you need can be chained toghether in this way, and you can think of it as a primitive if statement.

A requirement in adventuron is that expressions are not permitted to change the game state, so unfortunately, we can’t utilize commands here to set up a random value in a variable unfortunately, but chaining together ? : statements is a good way to achieve case() functionality.

1.43. ADVANCED TOPIC - Understanding Turns and Ticks (Beginners do not read)

Note
This is VERY advanced, and is only provided to better understand the Adventuron game lifecycle better. Beginners - DO NOT READ THIS SECTION - it’s generally not needed.

A turn represents the number of inputs received by the player.

A tick is the number of times that the on_tick {} event handler block has occured in the game (excluding the startup tick).
The first tick is tick 0, and the first turn is turn 1.

Tick 0 runs before first input has been entered by the game (upon presenting the first location). Tick 1 is the first tick that can follow turn 1 (even though it’s technically the second tick). There is no turn 0.

#############################################
# The first tick is TICK 0 (after the first
# room is described). The first tick is enabled
# by defauly but this tick may be configurable in
# a later version of Adventuron.
# -----------------------------------------
# Every submitted command increments turns()
# But some system commands (such as SAVE)
# do not increment the tick counter.
# -----------------------------------------
#
# In the below sample code, the commands ONE, TWO, FOUR
# all generate ticks, INVENTORY and THREE do not generate
# ticks. THREE purposefully stops a tick with the : stop_ticks;
# command.
#
#############################################

start_at                 = my_location

locations {
   my_location : location "You are in a room.\nType the following commands one at a time at the command line (inventory will not generate a tick):\n^n^(1) ONE \n(2) TWO\n(3) THREE\n(4) INVENTORY\n^m^(5) FOUR" ;
}

on_command {
   // Match all words
   : match "_ _"  {
      // Do not match "inventory" (let the system handle it)
      : if (verb_is "inventory" == false) {
         // If the input is "three" then skip downstream ticks
         // (this allows custom responses without the game world pproceeding)
         : if (verb_is "three") {
            : stop_tick;
         }
         // Print how many ticks and turns have elapsed so far
         : print {(
            "Ticks : " + ticks() +  " Turns : " + turns()
         )}
      }
   }
}

game_settings {
   // This is set to false by default (inventory generates a tick by default, but we can disable that here)
   // NOTE : This is an experimental setting, and may change in a future release.
   experimental_inventory_stops_tick = true
}

on_tick {
   : print {("TICK #" + ticks() )}
}

Expected results:

tick

1.43.1. More on Ticks & Turns

Here is another demonstration of turns() and ticks().

  • The turns() function will return a number that is incremented just before on_command() is executed (can think of turns as commands processed so far).

  • The ticks() function will return a number that is incremented just before on_tick() is executed (can think of ticks as ticks processed so far).

turns() and ticks() can be very much out of synch, and this is normal, as some commands are timeless.

Items in GET ALL | DROP ALL | etc. get their own turn followed by their own tick.

A general rule of thumb is only to use the ticks() function in the on_ticks{} block and only use turns() {} in the on_command block.

start_at = village

locations {
   village  : location "You are in the village.";
}

objects {
   lamp  : object "a lamp"  at = "village" ;
   spoon : object "a spoon" at = "village"  ;
}

on_startup {
   : print {("- on_startup (ticks so far:"+ticks() + ", turns so far:" +turns()+")" )}
   : press_any_key ;
}
on_command {
: if (true) {
      // Masking makes sure that a command handler doesn't override a system handler.
      : mask {
         : print {("--------------------" )}
         : print {("- on_command (ticks so far:"+ticks() + ", turns so far:" +turns()+")" )}

      }
   }
}

on_tick {
   : print {("- on_tick (ticks so far:"+ticks() + ", turns so far:" +turns()+")" )}
}

on_describe {
   : print {("- on_describe (ticks so far:"+ticks() + ", turns so far:" +turns()+")" )}
}

themes {
   my_theme : theme {
      theme_settings {
         clear_screen_enable = false
      }
   }
}

GET ALL (turn, tick, turn, tick)

tick2

GET LAMP (turn, tick)

tick3

GET LAMP, GET SPOON (turn, tick, turn, tick)

tick4

1.44. Gamebook Mode

This is a sample of the experimental gamebook mode of Adventuron (subject to change).

An on_describe {} handler in gamebook mode must do ONE of the following:

  1. Present at least one choice (contribute an add_choice {} block) …​

  2. Change the location to a different location.

  3. Execute a : lose_game ; or win_game ; command.

In this mode, there is no command entry (no parser), and Adventuron will present automatically call a ": choose ;" command at the end of the on_describe block.

At the end of a gamebook tick, the current page will be automatically redescribed.

If you print something in a gamebook tick, then please add a press_any_key afterward, or else the redescribe will overwrite its output.

After each turn a redescribe of the current location will occur automatically, and an autosave will occur just before the redescribe.

It’s still possible in this mode to ask for keyboard input via ask_string, ask_bool & ask_int, but logical sentence processing is switched off.

It is also possible to embed add_choice within existing choices, as required. At least one choice will ALWAYS be required in a location, or else a win_game, or lose_game command should be supplied. All code should be in the on_describe (per location) and the on_pre_describe block (at the top level of the source code). Andthing printed in the pre-describe section will be printed before the location is described.

In gamebook mode, it’s currently not supported to navigate via the connection table. Connections between locations are made manually via the add_choice commands.

Generally speaking inventory management and object management features must be coded into this mode (currently). Some object management and general purpose (use object with xxx) functionality may be added in the future to this mode.

NOTE : This mode does not have a way of quitting, saving or loading at present (auto save automatically works though - i.e. revisiting the game will resume where you left off).

This mode will be expanded upon in later revisions of Adventuron.

It is strongly advised not to use hyperlinks in your story text in this mode, as the behaviour is currently undefined.

start_at  = page_001
game_type = gamebook

on_startup {
   : print "This is a sample of the <GAMEBOOK<14>> mode of <ADVENTURON<#r>>.\nIn this mode, there is no command entry (no parser), and Adventuron will present automatically call a \": choose ;\" command at the end of the on_describe block.\nEvery page that does not win or lose the game must present at least one choice, or Adventuron will report an error.\nAfter each turn a redescribe of the current location will occur automatically, and an autosae will occur just before the redescribe.\nThis mode will be expanded upon in later revisions of Adventuron.\nIt is strongly advised not to use hyperlinks in your story text in this mode, as the behaviour is currently undefined." ;
   : press_any_key ;
}

objects {
   key : object "<a key<12>>" start_at = "page_003" ;
}

locations {

   page_001 : location "You are in front of three large doors.\n One door is marked <CERTAIN DEATH<10>>, one is marked <SOMETHING USEFUL<11>>, and the final door is marked <FREEDOM<12>>." {
      on_describe {
         : add_choice "Enter the door marked <CERTAIN DEATH<10>>."  {
            : goto "page_002" ;
         }

         : add_choice "Enter the door marked <SOMETHING USEFUL<11>>"  {
            : goto "page_003" ;
         }

         : add_choice "Enter the door marked <FREEDOM<12>>."  {
            : if (is_carried "key") {
               : print "Using the <KEY<12>>, you unlock and open the door." ;
               : press_any_key ;
               : goto "page_005" ;
            }
            : else {
               : goto "page_004" ;
            }
         }
      }
   }

   page_002 : location "You fell down a bottomless pit.\nTHIS IS THE END OF YOUR JOURNEY." {
      on_describe {
         : failure ;
         : lose_game;
      }
   }

   page_003 : location "You are in a dark cellar." {
      on_describe {
         : if (is_beside "key") {
            : add_choice "Get The Key"  {
               : get "key";
            }
         }
         : add_choice "Go Back." goto = "back";
      }
   }


   page_004 : location "The door to <FREEDOM<12>> is locked [[require a key]]." {
      on_describe {
         : goto {("back")}
         : press_any_key ;
      }
   }


   page_005 : location "You are free, you live happily ever after." {
      on_describe {
         : success ;
         : win_game ;
      }
   }

}

1.44.1. Auto taking objects in gamebook mode

start_at  = 001_room
game_type = gamebook

locations {
   001_room : location "You are in room 1." {
      on_describe {
         : add_choice "Turn to page 2 to go to room 2.";
         : add_choice "Turn to page 3 to go to room 3.";
      }
   }
   002_room : location "You are in room 2." {
      on_describe {
         : add_choice "Turn to page 1 to go to room 1.";
         : add_choice "Turn to page 3 to go to room 3.";
      }
   }
   003_room : location "You are in room 3." {
         on_describe {
         : add_choice "Turn to page 1 to go to room 1.";
         : add_choice "Turn to page 2 to go to room 2.";
      }
   }
}

objects {
   lamp  : object "a lamp"  at = "001_room" ;
   spoon : object "a spoon" at = "002_room" ;
   knife : object "a knife" at = "003_room" ;
}

on_describe {

   : if (is_beside "lamp") {
      : get "lamp" ;
   }
   : if (is_beside "spoon") {
      : get "spoon" ;
   }
   : if (is_beside "knife") {
      : get "knife" ;
   }

}

1.45. Going Back To The Previous Location

Adventuron doesn’t currently have an built-in way of going back to the previous location.

This code pattern will record the players route through the game and allow the player to go back to the last location (that is not the current location) that was described.

start_at = lakeside

locations {
   lakeside     : location "You are by a lakeside." ;
   forest       : location "You are in a forest." ;
   outside_cave : location "You are outside a cave." ;
}

connections {
   from, direction, to = [
      lakeside, north, forest,
      forest,   north, outside_cave,
   ]
}

strings {
   pending_last_location : string;
   came_from_location    : string;
}

on_describe {
   : gosub "maintain_last_location" ;
   : if (came_from_location != "" ) {
      : print {("Type BACK to go back to location with id : " + came_from_location)}
   }
}

on_command {
   : match "back _"  {
      : if (came_from_location != "") {
         : goto {(came_from_location)}
         : print {("You go back to " + came_from_location)}
         : press_any_key ;
         : redescribe;
      }
   }
}

subroutines {
   maintain_last_location : subroutine {
      : if (pending_last_location != current_location()) {
         : set_string var="came_from_location" {(pending_last_location)}
      }
      : set_string var="pending_last_location" {(current_location())}
   }
}

1.46. Time

Printing the time is easy by simply manipulating integers representing hours and minutes.

Time can be formatted with dynamic strings.

start_at = my_location

locations {
   my_location : location "Type <SHOW TIME<12>> to show the time (moves by one minute each game turn)." ;
}

strings {

   hhmm : dynamic_string {(
      (hour < 10 ? "0" : "") +
      hour + ":" +
      (minute < 10 ? "0" : "") +
      minute
   )}

}

integers {
   hour   : integer "8" ;
   minute : integer "0" ;
}

on_tick {
   : increment "minute" ;
   : if (minute > 59) {
      : set_integer var = "minute"  value = "0" ;
      : increment "hour" ;
   }
   : if (hour > 23) {
      : set_integer var = "hour"  value = "0" ;
   }
}

on_command {
   : match "show time"  {
      : print "The time is {hhmm}." ;
   }
}

1.47. Control of flow tests

Attached is a source code snippet which demonstrates Adventuron’s control of flow in the on_command {} block.

Run the 10 test by typing the following commands one at a time. You should see "001-PASS-FINISH" …​ "007-PASS-FINISH" printed out each time, and no text containing the word "FAIL".

If you see no text, it is also an error, it should ALWAYS end in text ending in PASS-FINISH.

  • TEST 1

  • TEST 2

  • TEST 3

  • TEST 4

  • TEST 5

  • TEST 6

  • TEST 7

  • TEST 8

  • TEST 9

  • TEST 10

start_at = test_location

locations {
   test_location "Please type TEST 1, TEST 2, TEST 3 .. TEST 13." ;
}

booleans {
   a; b; c; d;
}

on_command {

   : match "test 1"  {
      : set_true  "a" ;
      : set_true  "b" ;
      : set_true  "c" ;
      : set_false "d" ;

      : if (a) {
         : print "001-PASS-1" ;
      }
      : else_if (b) {
         : print "001-FAIL-1" ;
      }
      : else {
         : print "001-FAIL-2" ;
      }

      : print "001-PASS-2" ;

      : if (a == true && b == true) {
         : print "001-PASS-3" ;
         : print "001-PASS-FINISH (3 TIMES)" ;

         : done ;
      }
      : else {
         : print "001-FAIL-3" ;
         : done ;
      }
      : print "001-FAIL-4" ;
      : done;
   }

   : match "test 2"  {
      : set_true "a" ;
      : set_true "b" ;
      : set_true "c" ;
      : set_false "d" ;

      : if (d || a) {
         : print "002-PASS-1" ;
      }
      : else_if (b) {
         : print "002-FAIL-1" ;
      }
      : else {
         : print "002-FAIL-2" ;
      }

      : print "002-PASS-2" ;

      : if (a == true && b == false) {
         : print "002-FAIL-3" ;
         : done ;
      }
      : else {
         : print "002-PASS-3" ;
         : print "002-PASS-FINISH (3 TIMES)" ;
         : return;
      }
      : print "002-FAIL-4" ;
      : done;
   }

   : match "test 3"  {
      : set_true "a" ;
      : set_true "b" ;
      : set_true "c" ;
      : set_false "d" ;

      : if (a) {
         : print "003-PASS-1" ;
      }
      : else_if (b) {
         : print "003-FAIL-1" ;
      }
      : else {
         : print "003-FAIL-2" ;
      }

      : print "003-PASS-2" ;

      : if (a == true && b == true) {
         : print "003-PASS-3" ;

      }
      : else {
         : print "003-FAIL-3" ;
         : done ;
      }
      : print "003-PASS-4" ;
   }

   : match "test 3"  {
      : print "003-PASS-FINISH (4 TIMES)" ;
   }


   : match "test 4" {
      : set_false "a"  ;
      : set_false "b"  ;
      : set_false "c"  ;
      : set_false "d"  ;

      : if (a) {
         : print "004-FAIL-1" ;
      }
      : else {

      : print "004-PASS-1" ;
         : if (b) {
            : print "004-FAIL-2" ;
         }
         : else_if (c) {
            : print "004-FAIL-3" ;
         }
         : else_if (d) {
            : print "004-FAIL-4" ;
         }
         : else {
            : print "004-PASS-2" ;
         }
         : print "004-PASS-3" ;
      }
      : print "004-PASS-4" ;
      : print "004-PASS-FINISH (4 TIMES)" ;
   }

   : match "test 5" {
      : set_false "a"  ;
      : set_false "b"  ;
      : set_false "c"  ;
      : set_false "d"  ;

      : if (a) {
         : print "005-FAIL-1" ;
      }
      : else {
         : print "005-PASS-1" ;
         : gosub "test5_a";
         : print "005-PASS-4" ;
      }
      : print "005-PASS-5" ;
      : print "005-PASS-FINISH (5 TIMES)" ;
   }

   : match "test 6" {
      : set_false "a"  ;
      : set_false "b"  ;
      : set_false "c"  ;
      : set_false "d"  ;

      : if (a) {
         : print "006-FAIL-1" ;
      }
      : else {

         : print "006-PASS-1" ;
         : gosub "test6_a";
         : print "006-PASS-3" ;
      }
      : print "006-PASS-4" ;
      : print "006-PASS-FINISH (4 TIMES)" ;
   }

   : match "test 7" {
      : set_false "a"  ;
      : set_false "b"  ;
      : set_false "c"  ;
      : set_false "d"  ;

      : if (a) {
         : print "007-FAIL-1" ;
      }
      : else {

         : print "007-PASS-1" ;
         : gosub "test7_a";
         : print "007-FAIL-6" ;
      }
      : print "007-FAIL-7" ;
   }

   : match "test 8" {
      : set_true "a"  ;
      : set_true "b"  ;
      : set_true "c"  ;
      : set_true "d"  ;
      : print "PASS-001" ;

      : if (a) {
         : if (b) {
            : if (c) {
               : print "PASS-002";
               : gosub "test8_a" ;
            }
            : else {
               : print "FAIL-000";
            }
         }
         : else {
            : print "FAIL-001" ;
         }
      }
      : else {
         : print "FAIL-002" ;
      }

      : print "PASS-004" ;
      : print "FINISHED (4 passed)" ;
   }

   : match "test 9"  {
      : set_true "a"  ;
      : set_true "b"  ;
      : if (a && b) {
         : print "PASS-001";
      }
      : print "PASS-002";
      : print "FINISHED (2 passed)" ;
   }

   : match "test 10"  {
      : set_true "a"  ;
      : set_false "b"  ;
      : if (a && b) {
         : print "FAIL-001";
      }
      : print "PASS-001";
      : print "FINISHED (1 passed)" ;
   }


   : match "test 11"  {
      : set_true "a"  ;
      : set_false "b"  ;
      : if (a && b) {
         : print "FAIL-001";
      }
      : else {
         : print "PASS-001";
      }

      : print "PASS-002";
      : print "FINISHED (2 passed)" ;
   }

   : match "test 12"  {
      : set_true "a"  ;
      : set_false "b"  ;
      : if (a && b) {
         : print "FAIL-001";
      }
      : else_if (a == true) {
         : print "PASS-001";
      }

      : print "PASS-002";
      : print "FINISHED (2 passed)" ;
   }

   : match "test 13"  {
      : set_true "a"  ;
      : set_false "b"  ;
      : if (a && b) {
         : print "FAIL-001";
      }
      : else_if (b) {
         : print "FAIL-002";
      }
      : else {
         : print "PASS-001";
      }

      : print "PASS-002";
      : print "FINISHED (2 passed)" ;
   }

   : match "test 14"  {
      : set_true "a"  ;
      : set_false "b"  ;
      : if (a && b) {
         : print "FAIL-001";
      }
      : else_if (a == false) {
         : print "FAIL-002";
      }

      : print "PASS-001";
      : print "FINISHED (1 passed)" ;
   }

   : match "test -"  {
      : print "Please type TEST 1, TEST 2, TEST 3, ... TEST 13." ;

   }

}

subroutines {

   test5_a : subroutine {
      : if (b) {
         : print "005-FAIL-2" ;
      }
      : else_if (c) {
         : print "005-FAIL-3" ;
      }
      : else_if (d) {
         : print "005-FAIL-4" ;
      }
      : else {
         : print "005-PASS-2" ;
      }

      : print "005-PASS-3" ;
   }

   test6_a : subroutine {
      : if (b) {
         : print "006-FAIL-2" ;
      }
      : else_if (c) {
         : print "006-FAIL-3" ;
      }
      : else_if (d) {
         : print "006-FAIL-4" ;
      }
      : else {
         : print "006-PASS-2" ;
         : return;
      }
   }

   test7_a : subroutine {
      : if (b) {
         : print "007-FAIL-2" ;
      }
      : else_if (c) {
         : print "007-FAIL-3" ;
      }
      : else_if (d) {
         : print "007-FAIL-4" ;
      }
      : else {
         : print "007-PASS-2" ;
         : print "007-PASS-FINISH (2 TIMES)" ;
         : done;
      }

      : print "007-FAIL-5" ;
   }

   test8_a : subroutine {

      : if (a) {
         : if (b) {

            : print "PASS-003";
         }
         : else {
            : print "FAIL-005";
         }
      }
      : else {
         : print "FAIL-006";
      }
   }

}

1.48. TWO Theme

The TWO theme is baked into Adventuron. All you have to do is extend the theme by creating a theme and extending it.

my_theme : theme {
   extends = two
}

Here is the source code for the two theme.

two : theme {

   theme_settings {
      font                           = daad
      layout                         = SB O X SEP "adv_line_red" LOCK
      layout_mobile                  = SB O X SEP "adv_line_red" LOCK
      columns                        = 48
      columns_mobile                 = 24
      keyboard_click                 = on
      failure_jingle                 = on
      success_jingle                 = on
      losegame_jingle                = on
      capitalization                 = upper
      header_capitalization          = original
      shader                         = scanlines
      default_delay                  = 0
      hide_status_bar_on_clearscreen = true
      hide_status_bar_on_endgame     = true
   }

   screen {
      padding_horz                 = 4
      padding_horz_mobile          = 4
      status_bar_padding_top       = 3
      status_bar_padding_horz      = 4
      paragraph_spacing_multiplier = 1
      border_mode_vertical_percent = 5
   }

   status_bar {
      : location_text  ;
      : treasure_score ;
   }

   lister_inventory {
      list_type = single_line_no_article
   }

   system_messages {
      object_list_header           = ""
      exit_list_header_concise     = ""
      all_treasures_found_win_game = "YOU WIN!"
      i_cant_do_that               = "DOESN'T WORK."
      unknown_noun                 = "NOT UNDERSTOOD."
      unknown_verb                 = "NOT UNDERSTOOD."
      you_cant_go_that_direction   = "CAN'T GO."
      inventory_list_header        = "CARRYING: "
      inventory_list_empty         = "<NOTHING<10>>"
      you_see_nothing_special      = "NOTHING SPECIAL."
      on_get                       = "TAKEN."
      on_drop                      = "DROPPED."
      on_wear                      = "WORN."
      on_remove                    = "REMOVED."
      you_are_already_carrying     = "ALREADY HAVE."
      dont_have_one_of_those       = "DON'T HAVE."
      ask_quit                     = "QUIT?"
      post_quit                    = "OK"
      cant_take                    = "CAN'T TAKE."
      prompt                       = "? "
      cant_see_one_of_those        = "CAN'T SEE."
      not_present                  = "NOT HERE."
      nothing_to_get               = "NOT HERE."
      restore_from_autosave        = "ROLLBACK?"
      you_already_wear             = "ALREADY WEARING."
      must_remove_first            = "REMOVE FIRST."
      it_is_dark                   = "IT'S DARK."
      worn_suffix                  = " *"
      cannot_carry_any_more        = "HANDS FULL."
   }
}

1.49. Sudden Death and 'Rollback'

Note
This feature is not 8-bit compatible.

(Disabled by default)

Adventuron can be configured to be able to rollback the game one turn upon executing a LOSE_GAME command. This is useful where a game wants to introduce sudden death (or forewarned death) as a mechanic, with the sudden death being grossly unfair.

Sudden death is a great way to make the player feel some tension in the game, but of course, should be used appropriately in games targetting younger players (sudden death can also be just a generic game over message such as "the dog steals your wand, your adventure is over", and this type of message could be accompanied by the ': lose_game' command.

Anyway, to configure rollback, use the following code.

Rollback will ask a question if the player wants to rollback when the lose_game command is executed, and it will reload the state from the turn before the turn where the death tick took place.

Warning
This approach is only appropriate where death (lose_game) is not inevitable within one move. For example, if death is inevitable from the moment you do something, and then it takes 3 or 4 turns to actually lose_game, then rollback will only roll back to a moment where lose_game cannot be avoided.
start_at = lake

game_settings {
   rollback_enabled = true
}

locations {
   forest : location "You are in a forest." ;
   cave   : location "You are in front of a cave." ;
   lake   : location "You are in by the side of a lake." ;
}

connections {
   from, direction, to = [
      lake, north, forest,
      forest, north, cave,
   ]
}

objects {
   bush   : scenery "a bush" msg = "You find nothing else." at = "lake" ;
   helmet : object  "a bicycle helmet" wearable = "true" ;
}

on_command {
   : if_examine "bush"  {
      : print "YOU FIND SOMETHING!" ;
      : create "helmet" ;
      : press_any_key ;
      : redescribe;
   }
}

on_tick {
   : if (is_just_entered () && is_at "cave") {
      : if (is_worn "helmet") {
         : print "ROCKS SLIDE ....." ;
         : press_any_key ;
         : print "YOUR HELMET PROTECTS ....." ;
         : press_any_key ;
         : print "YOU WIN!" ;
         : win_game ;
      }
      : else {
         : print "ROCKS SLIDE ....." ;
         : press_any_key ;
         : print "NOTHING PROTECTS YOUR HEAD ....." ;
         : press_any_key ;
         : print "YOU LOSE!" ;
         : lose_game ;
      }
   }
}

Adventuron supports rolling back to not the previous turn, but rather, the previous turn in the previous location . This still doesn’t work for certain games where certain deaths can follow the player around for a certain number of moves. New rollback plans will be added (such as those that depend on virtual autosave tripwires) in future version of adventuron.

: lose_game rollback_plan = "last_location";

1.50. Rewinding

Note
This feature is not 8-bit compatible.

(Disabled by default)

Adventuron can optionally allow the player to undo commands. The amount of moves that can be rewound is dependent upon the client, but typically it is a buffer of 30 moves.

The rewind buffer is the same buffer used by rollback, but just like rollback, rewinding is enabled by default.

Rewinding and rollback can be independently switched on and off, and they do not affect each other. In both cases, internally, Adventuron will use the move buffer.

To rewind the game one move either:

  • type UNDO

  • type REWIND
    or

  • press CONTROL + SHIFT + LEFT ARROW (rewind) or CONTROL + SHIFT + RIGHT ARROW (redo)

start_at = lake

game_settings {
   rewind_enabled   = true
   rollback_enabled = true
}

locations {
   forest : location "You are in a forest." ;
   cave   : location "You are in front of a cave." ;
   lake   : location "You are in by the side of a lake." ;
}

connections {
   from, direction, to = [
      lake, north, forest,
      forest, north, cave,
   ]
}

objects {
   bush   : scenery "a bush" msg = "You find nothing else." at = "lake" ;
   helmet : object  "a bicycle helmet" wearable = "true" ;
}

on_command {
   : if_examine "bush"  {
      : print "YOU FIND SOMETHING!" ;
      : create "helmet" ;
      : press_any_key ;
      : redescribe;
   }
}

on_tick {
   : if (is_just_entered () && is_at "cave") {
      : if (is_worn "helmet") {
         : print "ROCKS SLIDE ....." ;
         : press_any_key ;
         : print "YOUR HELMET PROTECTS ....." ;
         : press_any_key ;
         : print "YOU WIN!" ;
         : win_game ;
      }
      : else {
         : print "ROCKS SLIDE ....." ;
         : press_any_key ;
         : print "NOTHING PROTECTS YOUR HEAD ....." ;
         : press_any_key ;
         : print "YOU LOSE!" ;
         : lose_game ;
      }
   }
}

1.51. Room Escape Game

Below is a small room escape game written in Adventuron.

start_at = cell

locations {
   cell : location "You are in your cell. You see a door, a bed and bland wallpaper adorns the walls." ;
}

objects {
   wallpaper : object "a strip of wallpaper" ;
   pen       : object "a pen"                ;
   key       : object "a small key"          ;
}

booleans {
   is_key_in_keyhole   : boolean "true"  ;
   is_key_on_paper     : boolean "false" ;
   is_paper_under_door : boolean "false" ;
}

vocabulary {
   : noun / aliases = [wallpaper, paper]
}

on_command {

   : match "search bed; examine bed"  {
      : if (has_not_created "pen") {
         : print "You find something" ;
         : create "pen" ;
         : press_any_key ;
         : redescribe;
      }
   }

   : match "examine wallpaper"  {
      : if (has_not_created "wallpaper") {
         : create "wallpaper" ;
         : print "A piece of the wallpaper falls away" ;
         : press_any_key ;
         : redescribe;
      }
      : else {
         : print "You think you should leave the rest of the wallpaper in place." ;
      }
   }

   : match "examine door"  {
      : print "A solid looking oak door with a keyhole." ;
      : if (is_paper_under_door) {
         : print "The wallpaper is peeking out from under the door." ;
      }
   }

   : match "examine keyhole"  {
      : if (is_key_in_keyhole) {
         : print "There appears to be a key in the keyhole on the other side of the door." ;
      }
      : else {
         : print "The key is no longer in the keyhole.\nYou can't see anything else of interest due to the darkness." ;
      }
   }


   : match "slide wallpaper;insert wallpaper;place wallpaper"  {
      : if (is_carried "wallpaper") {
         : if (noun2_is "door") {
            : if (is_paper_under_door) {
               : print "You can't slide it under the door any more." ;
            }
            : else {
               : print "You slide the wallpaper under the door." ;
               : set_true "is_paper_under_door" ;
               : destroy "wallpaper" ;
            }
         }
         : else {
            : print "Where?" ;
         }
      }
      : else {
         : print "You don't have it." ;
      }
   }

   : match "poke keyhole; poke key; insert pen; put pen"  {
      : if (is_key_in_keyhole) {
         : if (is_carried "pen" && (noun2_is "pen" || noun2_is "keyhole" )) {
            : set_false "is_key_in_keyhole" ;
            : if (is_paper_under_door) {
               : print "The key falls onto the paper." ;
               : set_true "is_key_on_paper" ;
            }
            : else {
               : print "The key falls onto the floor behind the door and bounces away." ;
               : print "More planning is perhaps required." ;
               : print "GAME OVER" ;
               : end_game ;
            }
         }
         : else {
            : print "Your finger is too big." ;
         }
      }
      : else {
         : print "The key has already fallen" ;
      }
   }

   : match "pull paper; get paper"  {
      : if (is_paper_under_door) {
         : pocket "wallpaper" ;
         : if (is_key_on_paper) {
            : print "You pull the paper back from underneath the door. You also take the key that is resting upon it." ;
            : pocket "key" ;
         }
         : else {
            : print "You pick up the wallpaper." ;
            : pocket "wallpaper" ;
         }
         : set_false "is_paper_under_door" ;
         : set_false "is_key_on_paper" ;
         : press_any_key ;
         : redescribe;
      }
   }

   : match "unlock door; open door"  {
      : if (is_carried "key") {
         : print "Using the small key, you unlock the door, open it, and continue onward to your next adventure." ;
         : press_any_key ;
         : end_game ;
      }
      : else {
         : print "The door is locked" ;
      }
   }
}

1.52. The difference between :done and :return

: done will always end the current event handler immediately, no more instructions will be executed.

: return (in a subroutine) will exit the current subroutine, but resume execution the line after the line that called the : gosub.

This will print "One".

start_at = my_location

locations {
   my_location : location "" ;
}

on_tick {
   : gosub "my_subroutine" ;
   : print "Two" ;
}

subroutines {
   my_subroutine : subroutine {
      : print "One" ;
      : done ;
   }
}

This will print "One", "Two" (on different lines)

start_at = my_location

locations {
   my_location : location "" ;
}

on_tick {
   : gosub "my_subroutine" ;
   : print "Two" ;
}

subroutines {
   my_subroutine : subroutine {
      : print "One" ;
      : return ;
      : print "Error" ;
   }
}

1.53. Phone Ringing

Playing beeps with pauses between them can make for some interesting effects.

Note
There is no interrupting this phone ring.
start_at = my_location

locations {
   my_location : location "You are in a room." ;
}

integers {
   tmp : integer "0" ;
}

on_describe {
   : gosub "phone_ring" ;
}


booleans {
   // sysvar_bool allows access to boolean system variables
   is_sound_enabled : dynamic_boolean {( sysvar_bool "sysvar_sound_enabled" )}
}

subroutines {
   phone_ring : subroutine {
      : print "The phone is ringing ..." ;

      // Without checking for sound being enabled
      // then the pauses will occur on clients without
      // sound playback
      : if (is_sound_enabled) {
         : gosub "phone_ring_solo" ;
         : pause "50" ;
         : gosub "phone_ring_solo" ;
         : pause "1500" ;
         : gosub "phone_ring_solo" ;
         : pause "50" ;
         : gosub "phone_ring_solo" ;
         : pause "1500" ;
         : gosub "phone_ring_solo" ;
         : pause "50" ;
         : gosub "phone_ring_solo" ;
      }

   }

   phone_ring_solo : subroutine {
      : set_integer var = "tmp"  value = "0" ;
      : while (tmp < 20) {
         : if ((tmp % 2) == 0) {
            : beep millis = "20" pitch = "3" ;
         }
         : else {
            : beep millis = "20" pitch = "7" ;
         }
         : increment "tmp" ;
      }
   }
}

1.54. End of Tutorial

Click here to return to the Adventuron User Guide.