I’ve found that “Menus” alone can provide a very versatile and robust technique for all sorts of interactivity, even if such wasn’t emshort’s purpose when she wrote the extension. “Menus” can also, under some circumstances, facilitate a fast-paced and quite entertaining offering of play.
Joel Webster sounds satisfied with the replies he’s received, which is groovy since that’s our objective here. Nonetheless, as I felt my response yesterday was far too short on particulars since I had no reference materials handy, I dug out an Inform story file I wrote some time ago that demonstrates how, using only “Menus,” to resolve each and every issue Joel initially raised in his questions. If the discussion here is already resolved to the OP’s satisfaction then consider the posting of (a surfeit of) code below as a mere appendix, germane because it directly addresses several points on how to use and modify the “Menus” extension.
Certainly, as emshort and others mentioned, one drawback of using “Menus” is that some interpreters may clear their text-record buffer each time the screen is cleared (which occurs every turn a menu is displayed using “Menus”), leading to player frustration. For example, when using Gargoyle one loses the continuous stream of text availabe by pressing “page-up” after a menu is displayed. However, not all interpreters suffer such difficulties; I (exclusively) prefer Windows Frotz and Windows Glulxe for playing any game written with Inform 6 or Inform 7, and the “scrollback” function of these two interpreters is unaffected by screen-clearings. I think the point then would be that this is something an author ought keep in mind while preparing a game for distribution to others, and might perhaps lead an author to either release a game along with an amenable interpreter or plainly recommend one program over another to potential players.
Though far more lengthy than the usual example in a forum discussion, the code below had the virtue of being already written; copy/pasting is far easier than writing something novel. Among other things, it shows how to carefully construct/display/dispense with menus as well as how to construct various status bars for use with menus, when to use subtleties like “rule succeeds” and “current menu is,” and also moves both the player and other characters about according to rules effected with menu-usage. In other words, this example seemed to me so relevant to everything that Joel mentioned in his first post that I thought it a shame to let it gather dust on my hard drive rather than record it here. The example also makes a more complex use of I7’s Scene machinery than we often see publicly posted; since questions about scene-usage come up from time to time, perhaps what follows can serve as a reference on that count as well.
(As the example is quite lengthy at ~1800 words, I’ve tucked it into a a spoiler tag:)
[spoiler][code]
[Preface:
What follows is only the barest outline of but one aspect of a simple rpg-style game featuring heavily randomized combat, which uses a menu-driven interactive system for resolving encounters. Among other things, the example below lacks any implementation at all of one of the more fundamental ideas which led me to write this code in the first place: namely, instead of having numerous actors clogging up the off-stage area (and hence our memory allocation), I wanted to use but a single placeholder or generic enemy; at the start of each encounter, this generic enemy (“Met”) would then be temporarily redefined (printed name, character statistics, etc) by reference to a grand Table of Monsters where such data would be stored for all the various types of opponents one might encounter.
I ought add that the broader notion of such a menu-driven system is hardly novel, as I’ve seen other IF games that use something similar as their basis of interaction-- though as far as I know none use Inform 7’s Scene machinery as a fundamental game mechanic.
Also note that, due to forum typography restrictions, copying/pasting the code below into an Inform story file may be rather tedious as all tab stops in both rules and tables will be converted to spaces; these spaces will have to be deleted and replaced with tabs one by one.]
Volume 0 - Preliminary Notions
Include Menus by Emily Short.
[The following modification of “Menus” is required to counter the player quitting our menu system (by pressing “Q”) during encounters, leaving the game in a very wretched state indeed]
The basic menu contents rule is not listed in any rulebook.
Rule for displaying (this is the alternate menu contents rule):
now the current menu selection is 1;
show alt-menu contents.
To show alt-menu contents:
increase the menu depth by 1;
let temporary depth be the menu depth;
let temporary menu be the current menu;
let temporary title be the current menu title;
let __x be 0;
let __index be 0;
while __index is not 1:
now the current menu is the temporary menu;
let __n be 0;
repeat through current menu:
increase __n by 1;
if title entry is current menu title, now current menu selection is __n;
now the current menu title is the temporary title;
reprint current menu;
let __x be the chosen letter;
if __x is a number listed in the Table of Menu Commands:
unless encounter is happening:
consider the effect entry;
if temporary depth > menu depth:
now __index is 1;
otherwise:
if __x is -8 or __x is 81 or __x is 113 or __x is 27:
if temporary depth > menu depth:
now __index is 1;
otherwise:
consider the effect entry;
if temporary depth > menu depth:
now __index is 1.
[Some other initialization stuff]
The return-spot is a room that varies. The return-spot is usually starthere.
The current-foe is a person that varies. The current-foe is usually Avatar.
Volume P - People
[In a more fully-developed example, these “character statistics” would likely be uniquely assigned by table entries or else derived by formula from “basic attributes” like strength, dexterity, etc]
A person has a number called hit-points. The hit-points of a person are usually 25.
A person has a number called damage. The damage of a person is usually 5.
A person has a number called hit-chance. The hit-chance of a person is usually 67.
Volume S - Scenes
Part 1 - Random Encounters
Chapter 1 - Begin the Random Encounter
Section 1 - Definition of the Encounter Scene
Start-encounter is a truth state that varies. Start-encounter is usually false.
Encounter-resolved is a truth state that varies. Encounter-resolved is usually false.
Encounter is a recurring scene. Encounter begins when start-encounter is true. Encounter ends when encounter-resolved is true.
Section 2 - Starting the Encounter Scene
[In a more fully-developed example, “When encounter begins” is the point where we would refer to an enemies table and turn generic “Met” into Hilgar the Orc, Olaf the Cyborg Terminator, etc]
When encounter begins:
now return-spot is the location of the player;
now start-encounter is false;
move met to the arena;
now the hit-points of Met is 25;
now met is alive;
move the player to the arena;
now resolve-initial-interaction is false.
[temp]
When encounter begins:
now the current-foe is Met.
Section 3 - Concluding the Encounter Scene
When encounter ends:
move the player to return-spot;
remove met from play;
now encounter-resolved is false.
[temp]
When encounter ends:
now the current-foe is Avatar.
Chapter 2 - Initial Phase of the Encounter
Section 1 - Defining the Initial Phase
Resolve-initial-interaction is a truth state that varies. Resolve-initial-interaction is usually true.
Initial-reaction is a recurring scene. Initial-reaction begins when resolve-initial-interaction is false. Initial-reaction ends when resolve-initial-interaction is true.
Section 2 - Refining the Initial Phase
Initial-reaction has a text called initial-outcome. The initial-outcome of initial-reaction is usually “”.
[I ardently wished to use a KOV here with adverbial iterations, but apparently while a KOV can be specified as applying to ALL scenes, individual scenes may not have their own discrete value assignments; hence the confusing number-system.]
Initial-reaction has a number called initial-reaction-endstate. The initial-reaction-endstate of initial-reaction is usually 0. [1=fight, 2=chat, 3=flee, 4=bribe]
Initial-reaction ends angrily when the initial-reaction-endstate of initial-reaction is 1.
Initial-reaction ends chattily when the initial-reaction-endstate of initial-reaction is 2.
Initial-reaction ends furtively when the initial-reaction-endstate of initial-reaction is 3.
Initial-reaction ends expensively when the initial-reaction-endstate of initial-reaction is 4.
Rule for constructing the status line while initial-reaction is happening:
fill status bar with Table of Initial-Phase Status;
rule succeeds.
Table of Initial-Phase Status
left central right
“” “Shall you:” “”
“” “” “”
" Up/Down arrow = Navigate" “” “ENTER = Select”
Section 3 - Commencing the Initial Phase
When initial-reaction begins:
determine outcome of initial-reaction.
To determine outcome of initial-reaction:
now the current menu is the Table of Encounter-Starting Options;
carry out the displaying activity.
Table of Encounter-Starting Options
title subtable description toggle
“Fight” – -- InitiateCombat rule
“Chat” – -- InitiateEncounterChat rule
“Flee” – -- InitiateFleeing rule
“Bribe” – -- InitiateOffering rule
Section 4 - Concluding the Initial Phase
When initial-reaction ends angrily:
say “[initial-outcome of initial-reaction]”;
say paragraph break;
now resolve-initial-interaction is true;
now the initial-outcome of initial-reaction is “”;
now the initial-reaction-endstate of initial-reaction is 0;
now in-combat is true.
[The next several rules would of course change into intermediate steps rather than conclusions if we implemented a more substantial consequence of choosing them]
When initial-reaction ends chattily:
say “[initial-outcome of initial-reaction]”;
say paragraph break;
now resolve-initial-interaction is true;
now the initial-outcome of initial-reaction is “”;
now the initial-reaction-endstate of initial-reaction is 0;
now encounter-resolved is true.
When initial-reaction ends furtively:
say “[initial-outcome of initial-reaction]”;
say paragraph break;
now resolve-initial-interaction is true;
now the initial-outcome of initial-reaction is “”;
now the initial-reaction-endstate of initial-reaction is 0;
now encounter-resolved is true.
When initial-reaction ends expensively:
say “[initial-outcome of initial-reaction]”;
say paragraph break;
now resolve-initial-interaction is true;
now the initial-outcome of initial-reaction is “”;
now the initial-reaction-endstate of initial-reaction is 0;
now encounter-resolved is true.
Section 5 - Combat may be an outcome of the Initial Phase
This is the InitiateCombat rule:
decrease the menu depth by 1;
now the initial-outcome of initial-reaction is “There shall be blood! You decide to fight [printed name of the current-foe].”;
now the initial-reaction-endstate of initial-reaction is 1;
rule succeeds.
Section 6 - Talking may be an outcome of the Initial Phase
This is the InitiateEncounterChat rule:
decrease the menu depth by 1;
now the initial-outcome of initial-reaction is “Feeling rather sociable, you decide to chat up [printed name of the current-foe].”;
now the initial-reaction-endstate of initial-reaction is 2;
rule succeeds.
Section 7 - Evasion may be an outcome of the Initial Phase
This is the InitiateFleeing rule:
decrease the menu depth by 1;
now the initial-outcome of initial-reaction is “Feeling apprehensive, you attempt to evade the attention of [printed name of the current-foe].”;
now the initial-reaction-endstate of initial-reaction is 3;
rule succeeds.
Section 8 - Bribery may be an outcome of the Initial Phase
This is the InitiateOffering rule:
decrease the menu depth by 1;
now the initial-outcome of initial-reaction is “You hope that [printed name of current-foe] may be satisfied with a bribe and allow you to be on about your business.”;
now the initial-reaction-endstate of initial-reaction is 4;
rule succeeds.
Chapter 3 - Combat Phase of the Encounter
Section 1 - Defining the Scene of Combat
In-combat is a truth state that varies. In-combat is usually false.
Combat is a recurring scene. Combat begins when in-combat is true. Combat ends when in-combat is false.
Section 2 - Effecting Combat
When combat begins:
initialize fighting.
To initialize fighting:
now the current menu is the Table of Tactical Options;
carry out the displaying activity;
clear the screen.
Every turn during combat:
initialize fighting.
[temp]
When combat ends:
now encounter-resolved is true.
Rule for constructing the status line while combat is happening:
fill status bar with Table of Combat Menu Status;
rule succeeds.
Table of Combat Menu Status
left central right
“” “Fight it out!” “”
" Your HP: [hit-points of player]" “” “[printed name of the current-foe][’]s HP: [hit-points of current-foe] "
“” “” “”
" Up/Down arrow = Navigate” “” “ENTER = Select”
Table of Tactical Options
title subtable description toggle
“Attack” – -- ResolveAttacks rule
“Use Item” – -- ItemUseInCombat rule
“Flee” – -- FleeCombat rule
“Surrender” – -- HopeForMercy rule
Section 3 - Resolving Combat
This is the ResolveAttacks rule:
say paragraph break;
say “Fisticuffs ensue!”;
let firststrike be a random number between 1 and 2;
let pcstrike be a random number between 1 and 100;
let metstrike be a random number between 1 and 100;
if firststrike is 1:
say “You obtain the initiative!”;
if the hit-chance of the player is greater than pcstrike or the hit-chance of the player is pcstrike:
say “You hit [printed name of the current-foe] for [damage of the player]!”;
decrease the hit-points of Met by damage of the player;
update salubrity for met;
if met is dead:
say “You mercilessly slay [printed name of the current-foe]!”;
now in-combat is false;
wait for any key;
decrease the menu depth by 1;
rule fails;
if the hit-chance of the player is less than pcstrike:
say “You swing at [printed name of the current-foe], but he dodges your blow.”;
if the hit-chance of met is greater than metstrike or the hit-chance of met is metstrike:
say “[Printed name of the current-foe] hits you for [damage of met]!”;
decrease the hit-points of the player by damage of met;
update salubrity for the player;
if the player is dead:
decrease the menu depth by 1;
rule fails;
if the hit-chance of met is less than metstrike:
say “[Printed name of the current-foe] swings at you, but he misses.”;
if firststrike is 2:
say “[Printed name of the current-foe] obtains the initiative!”;
if the hit-chance of met is greater than metstrike or the hit-chance of met is metstrike:
say “[Printed name of the current-foe] hits you for [damage of met]!”;
decrease the hit-points of the player by damage of met;
update salubrity for the player;
if the player is dead:
decrease the menu depth by 1;
rule fails;
if the hit-chance of met is less than metstrike:
say “[Printed name of the current-foe] swings at you, but he misses.”;
if the hit-chance of the player is greater than pcstrike or the hit-chance of the player is pcstrike:
say “You hit [printed name of the current-foe] for [damage of the player]!”;
decrease the hit-points of Met by damage of the player;
update salubrity for met;
if met is dead:
say “You mercilessly slay [printed name of the current-foe]!”;
now in-combat is false;
wait for any key;
decrease the menu depth by 1;
rule fails;
if the hit-chance of the player is less than pcstrike:
say “You swing at [printed name of the current-foe], but he dodges your blow.”;
wait for any key;
rule succeeds.
A person can be dead or alive. [In a more well-developed example, this would have non-trivial consequences]
To update salubrity for (theyem - a person):
if the hit-points of theyem is less than 1:
now theyem is dead;
if theyem is the player:
end the story saying “[printed name of current-foe] mercilessly slays you.”
Chapter 4 - Rainy Day Notions
[in a more well-developed and interesting example, the following options would of course lead to something much more substantial than a mere line of printed text:]
This is the ItemUseInCombat rule:
say paragraph break;
say “Whereby we rummage through our loot for something useful in the present circumstance.”;
wait for any key;
rule succeeds.
This is the FleeCombat rule:
say paragraph break;
say “Whereby we attempt to escape under fire from the enemy.”;
wait for any key;
rule succeeds.
This is the HopeForMercy rule:
say paragraph break;
say “Perhaps the enemy will take pity on our ineptitude.”;
wait for any key;
rule succeeds.
Volume W - World
The Starthere is a room. “Go west for encounter testing.”.
A person called Avatar is in starthere. The player is Avatar.
The Conduit is west of starthere. The description of the conduit is “We ought not reach this enigmatic place.”.
Instead of going west from starthere:
if a random chance of 1 in 2 succeeds:
now start-encounter is true;
otherwise:
say “You explore a bit, but don’t find anything interesting.”
The Arena is a room.
There is a person called Met.
[/code][/spoiler]
I hope anyone trying to use “Menus” in the future will find the above example answers far more questions than it poses.
Endosphere