A question about doing something with menus.

I’ve got a decent grasp of Inform 7 thus far, but this is a bit too complex for me.

Basically, what I want to see a menu system for exploration, resource gathering etc. The player could, through a menu-based interface
A: Use an item to open the menu.
B: Choose a location from the menu.
C: Choose what to do in the location: explore, gather resources, rescue a person within the area, something along those lines.
D: Another submenu would open or something might happen, depending on what the player chose to do exactly.

This all is very doable with Menus by Emily Short. But there’s one problem I’m having:

Most locations, resources within locations, etc would be hidden initially, until found by exploring or through some other way. How would I go about doing that? Is there some way to hide some things from the menu initially? Add rows to the tables later on? Is Menus the best extension for this, or is there something more suitable?

Yes, you could just move rows from some backup table to the table shown to the player, whenever a new action or location becomes available. I would do this by (a) choosing the right row from the backup table and reading its entries into some global variables; (b) choosing a blank row in the table shown to the player; © writing the global variables to the entries in this blank row.

(If the available locations and actions change often, and disappear as well as appear, you could create a routine that custom-builds the table shown to the player whenever it is opened. You’d start by blanking out the Table of Shown Options; then repeat through the Table of Potential Options, and carry out steps a-c for any row that should be copied.)

But there are many other possibilities. For instance, you could change the Menus extension so that menus have another column, “shown”, the value of which makes the row appear or not appear. Or you could decide to use a multiple choice conversation system like Simple Chat instead.

Ah, alright. That makes sense, thanks.

Here’s what I have as of now:

[code]“Locations test” by pashaimeru

Volume 1 - Engine stuff

Book 1 - Prep

Include Menus by Emily Short.
Use no scoring.
The printed name of yourself is “you”.
A thing can be usable or not usable. A thing is usually not usable.

Book 2 - Locations

Section - Using

The crystal ball is a thing. It is fixed in place. It is usable. Crystal ball can be operated or not operated. Crystal ball is not operated.

Using is an action applying to one thing. Understand “use [thing]” as using.

Check using:
If noun is not usable, say “You can’t use that.”.

Carry out using:
if noun is crystal ball:
now the current menu is the Table of Crystalball;
carry out the displaying activity;
clear the screen;
try looking.

	Table of Crystalball
	title	subtable	description	toggle
	"Location"	Table of Locations	--	--

		Table of Locations
		title	subtable	description	toggle
		"Forest"	Table of Location_forest	--	--
		["Mountains" Table of Location_mountains	--	--]
		with 5 blank rows
		
			Table of Location_forest
			title	subtable	description	toggle
			"Explore"	--	--	explore forest rule
			
			Table of Location_mountains
			title	subtable	description	toggle
			"Explore"	--	--	explore mountains rule

Part - Exploring

A rubber ball is a thing.

This is the explore forest rule:
choose a blank row in Table of Locations;
now title entry is “Mountains”;
now subtable entry is Table of Location_mountains;
decrease the menu depth by 3;
say “Mountains found etc.”;

This is the explore mountains rule:
decrease the menu depth by 3;
now player is carrying a rubber ball;
say “You found a rubber ball somehow.”;

Volume 2 - The world

Test room is a room. The player is in test room. The crystal ball is here.[/code]

Primitive as hell right now, but I’m trying to get the structure down. Two problems with this:

  1. Making a different exploration rule for every location seems a bit uneloquent. Is there a way around that? I’ll end up with a hundred different rules at this rate.

  2. The says don’t appear at all. How do I give the player some feedback for his actions?

EDIT: Rephrased the question a bit~

Right, you’d need to find a way to generalise it. In order to give you more specific advice, I think it would be useful to have a little more of an idea of what you want to do with these tables – what kind of entries should appear in them, and under what circumstance should new entries appear?

Ah – I don’t think you can say things while you are in a menu. (Perhaps they get printed to the main screen but the player will never see this?) If you want to use a menu to do in-game things, and print in-game information to the screen, I think you might be better off using something like Simple Chat. (To get an idea of how this would work, you could check my game Fate: use the commands “open cabinet”, “take dagger”, “s”, “open drawer”, “take key”, “e”, “unlock cage”, “open cage”, “cut wings” for an example of an in0game menu containing actions. Or simply “s”, “talk to charles” to see it for a menu containing conversation topics. The difference is cosmetic. Incidentally, that game also starts out with a crystal ball.)

But again, it is hard to know whether this is good advice without a little more information about what you’ll be wanting to do with these menus.

Hmm, I solved the saying problem. But yeah, I better explain myself a bit better here.

Very simply put, I want something like the following with the thing:

  1. The player uses the crystal ball (or whatever thingamajing needed to activate the menu)
  2. The player chooses a location (in this example, plains). Keep in mind that the locations aren’t actual rooms or anything the player could physically enter, they only exist in this menu thing.
  3. The player chooses an action to do at the location (in this example, exploration).
  4. A random number is generated. Depending on the result, something happens: perhaps he finds an item. Perhaps he finds a new location, like mountains or lake or whatever. Maybe he finds a node of resources. Maybe something else happens: there’s gonna be a variety of events possible.

So let’s say the player explores the plains a bit. He finds mountains, another location. Then he starts exploring mountains. He finds a node of iron there. Then, he…

  1. Uses the crystal ball again
  2. Chooses a location (mountains)
  3. Chooses an action to do at the location (resource gathering).
  4. Chooses a resource to gather from the location (iron).
  5. A random amount of iron is gathered.

So basically just that. The way I see it, Menus by Emily Short would basically force me to make a ton of rules: a rule for exploring mountains, a rule for exploring plains, a rule for gathering iron from plains, a rule for gathering wood from plains, a rule for gathering iron from mountains, etc etc. It seems like a pretty absurd way of actually doing it. I might end up needing hundreds of them, and they’d be a nightmare to manage.

In a yet one more additional question: a random result is generated every time the player explores an area. I usually wouldn’t want the same event to be able to happen more than once (the player can only discover the mountains one time, for example). Do I have to make a variable for every single event, or is there some neat way of doing it?

Welcome to Inform. You’ll find that there are many aspects of Inform’s language and underlying mechanics that rely on you doing relatively absurd things. This facet of Inform is hidden until you really want to do some serious work with it.

I think we can work something out with lists and/or tables. The basic idea would be this: you build a list or table of ‘locations’. You then make an explore rule, which chooses a random element from this list and adds it to the Table of Available Locations (or whatever you call your table of locations). You would need only one list and one rule.

Let me know whether that sounds reasonable, and then we can work on whatever would be the next step. :slight_smile:

I think I might count as someone who has done serious work with Inform (including programming-heavy work like The Art of Fugue, ATTACK, Mid the Sagebrush and the Cactus and a random dungeon creator I am working on right now), and I have never yet done any absurd Inform coding.

Since I have no idea what your other programming skills are or how well versed you are in effective programmatic design or design patterns in a variety of system and scripting languages (as opposed to a DSL like Inform), this statement doesn’t mean as much as you seem to think it does. (When Inform 7 can be put on a resume and not laughed at, let me know. Then my opinion might change.) I’d also say that what you think of as “absurd” may differ from what others do. Even if you personally have never done anything that you personally consider absurd, that doesn’t count as a data point against what I said. Go through many of the threads in this forum and you will see people using words that are synonymous with absurd, either in actuality or conceptually. So, again, even if you haven’t apparently many others have.

As just one example, some people would consider any approach that utilizes a table-like structure as Inform implements them absurd. Others would consider the fact that you have to completely a remove an action and treat it as something new to be absurd. Others would consider that a language that forces you to worry about line breaks as much as Inform does and use “conditional break” and “run paragraph on” is absurd. I guess it just depends.

“Menus” doesn’t seem absolutely suitable for what you want to do – as the extension documentation says, it’s designed to let you say things in response to a choice, or to do things in response to a choice, but not both. (And it’s mostly designed for out-of-world actions like hints, I think.) It might be the best out-of-the-box solution for what you want to do, though (OTOH on Victor’s recommendation you might want to look at “Simple Chat,” which I’ve never used).

But it looks like you might be able to do a workaround by using the capacity inform has to use a “To say” phrase to do stuff other than printing. So if you define the mountains as a thing or a kind of a value, you can drop “[explore mountains]” into something that you’re printing, and then use a phrase starting with “To say explore (L - a place)” (or something like that) to do what you want with the exploring. Here’s how I modified your code to do this:

[code]“Locations test” by pashaimeru

Volume 1 - Engine stuff

Book 1 - Prep

Include Menus by Emily Short.
Use no scoring.
The printed name of yourself is “you”.
A thing can be usable or not usable. A thing is usually not usable.

Book 2 - Locations

Section - Using

The crystal ball is a thing. It is fixed in place. It is usable. Crystal ball can be operated or not operated. Crystal ball is not operated.

Using is an action applying to one thing. Understand “use [thing]” as using.

A place is a kind of value. The places are the forest, the mountains, and the pointless valley.

Check using:
If noun is not usable, say “You can’t use that.”.

Carry out using:
if noun is crystal ball:
now the current menu is the Table of Crystalball;
carry out the displaying activity;
clear the screen;
try looking.

	Table of Crystalball
	title	subtable	description	toggle
	"Location"	Table of Locations	--	--

		Table of Locations
		title	subtable	description	toggle
		"Forest"	Table of Location_forest	--	--
		"Pointless Valley"	--	"[explore the pointless valley]"	--
		["Mountains" Table of Location_mountains	--	--]
		with 5 blank rows
		
			Table of Location_forest
			title	subtable	description	toggle
			"Explore"	--	"Mountains found etc. [explore forest]"	--
			
			Table of Location_mountains
			title	subtable	description	toggle
			"Explore"	--	"You found a rubber ball somehow. [explore mountains]"	--

Part - Exploring

A rubber ball is a thing.

To say explore (L - a place):
if L is the forest:
choose a blank row in Table of Locations;
now title entry is “Mountains”;
now subtable entry is Table of Location_mountains;
otherwise if L is the mountains:
now player is carrying a rubber ball;
otherwise:
say “You explore [the L] but don’t find much of anything”;
decrease the menu depth by 3.

Volume 2 - The world

Test room is a room. The player is in test room. The crystal ball is here.[/code]

As you can see I don’t have to write a separate rule for everything I want to explore, and in fact I only have to write “decrease the menu depth by 3” one time instead of once for each case. An annoyance is that it says “You explore pointless valley but don’t find much of anything”; this won’t happen if you define the locations as things rather than places, though since then “[the L]” appends a definite article.

Hope this is helpful.

BTW, you probably want to use a different word than “location” in your code, since Inform reserves “location” for the room where the player is. (If you’re wondering why my code says “L - a place,” it’s because I originally had “location” instead of “place” and it gave me compiler errors. :blush:)

Oh wow, thanks a ton. That’s actually really nice, compared to the horrible mess I was about to do.

To say explore (L - a place): if L is the forest: choose a blank row in Table of Locations; now title entry is "Mountains"; now subtable entry is Table of Location_mountains; otherwise if L is the mountains: now player is carrying a rubber ball; otherwise: say "You explore [the L] but don't find much of anything"; decrease the menu depth by 3.
About this, though. Let’s say I want fifty different things that can happen when the player explores the forest. This seems like a bit subpar way of doing it, if it gets long like that; what’d be a better one? Lists or tables as VictorGijsbers recommended?

I’m not super-informed on this, but I would go for tables. They can be more flexible – you can include additional columns to keep track of things like whether the row can be used again or should be blanked out after use, and whether the row has been used, and things like that. Also, as I learned the hard way, lists are memory hogs; they can be expanded dynamically, so Inform leaves a lot of space for them to expand.

EDIT: And, depending on how you manage the tables, you might find that this lets you streamline the to-say phrase further. Declare “Every place has a table called exploration-table. The exploration-table is usually the Table of Nothing Interesting Happening.” and then you could just have the “explore L” phrase call a random row from the exploration-table of L and do – well, whatever it is you’re planning to do with the table. You’d need to find some way of having a table row do things like adding “mountains” to the available locations; maybe this could be done by having a column of stored actions that might be called, and then defining an action on a place that adds the place to the Table of Locations?

Hrmm, how would I do that? I haven’t really played around with tables at all. It seems like a liiittle ugly way of organising it too, though it’s not too bad. Furthermore, choosing a random row from the table would actually be a kind of a bad idea: that’d give every exploration result the same chance of triggering, but I want some to be common and some to be rare.

Here’s really what I want to do. This is doing it in a really ugly way, mainly due to the horrible “exploration event” thing that I have to find a proper way around, but it’ll give you the idea:

[code]An exploration event is a kind of thing. Exploration event can be triggered or not triggered. Mountains found, foo, bar and event something are exploration events.

To say explore (P - a place):
now r is a random number from 1 to 100;
if P is the forest:
if r >= 1 and r <= 5 and mountains found is not triggered:
choose a blank row in Table of Locations;
now title entry is “Mountains”;
now subtable entry is Table of Location_mountains;
now result is “Mountains found etc.”;
now mountains found is triggered;
otherwise if r >= 6 and r <= 15 and foo is not triggered:
now result is “Foo”;
now foo is triggered;
otherwise if r >= 16 and r <= 35 and bar is not triggered:
now result is “Bar”;
now bar is triggered;
otherwise if r >= 36 and r <= 55 and event something is not triggered:
now result is “Something”;
now event something is triggered;
otherwise if r >= 56 and r <= 100:
now result is “Nothing happens”;
otherwise:
now result is “You explore, but don’t find much of anything”;
otherwise if P is the mountains:
now result is “You explore the mountains but don’t find much of anything”;
otherwise:
now result is “You explore, but don’t find much of anything”;
decrease the menu depth by 3;[/code]
Are tables really the best way of implementing this properly? If so, how do I actually do it in the best way?

Well, here I’m seriously not an expert. In fact, this is kind of kicking my rear at the moment; the solution that I thought would be simple turns out to be not so. To give non-equal random weights to the rows, you could put in a column for the rank (in your example, 5 for discovering the mountains, 15 for foo, up to 100 for bar), choose a random r, and pick the row whose rank is at least r; but that turned out to be surprisingly difficult, at least for me. Here’s the start of the rule I came up with:

To say explore (L - a place):
	if L is the forest:
		let r be a random number from 1 to 25;
		let N be 0 [this is a very unlovely hack because I can't figure out how to say "choose a row with rank at least r"];
		repeat with M running from 1 to the number of rows in the Table of Forest Explorations: [can't even use "repeat through the Table of Forest Explorations" because I'm not sure how to reverse-engineer M from that]
			if N is 0:
				choose row M in the Table of Forest Explorations;
				if rank entry is at least r:
					now N is M; [and now I don't know how to just quit the loop, so we repeat through the rest of the table even though it's pointless]
		if N is 0:
			let N be the number of rows in the Table of Forest Explorations; [panic in case we never chose a row]
		choose row N in the Table of Forest Explorations;

Then there’s a question of what you want to do with the Table of Forest Explorations. My original idea was to have a column for the description you print when you hit your random entry, and a column with a stored action that would be carried out if applicable. But most of those entries would be blank, and I couldn’t get my blank entries to behave. I kludged it up by creating a dummy action that does nothing, which went wherever I really wanted a blank entry, but that’s awful.

One alternative would be to reuse the say-phrase trick; instead of having a stored action, pop “[discover mountains]” into the text that gets said in that row, and use “To say discover mountains” (or something even more generalizable) to add the mountains to the table of locations. That might even be more generalizable, since you could put as many of those say-phrases in as you want, instead of being restricted to one stored action.

Anyway, I’ll paste what I currently have below a spoiler tag. You’d want to generalize it in some ways – every place would have a table of outcomes associated with it (some of them might be a table with one row, saying “nothing happens,” and the carry out rule for discovering would also be something you’d want to generalize. Maybe someone more competent than I will explain why blank entries are the messenger of my doom and destruction.

[spoiler][code]“Locations test” by pashaimeru

Volume 1 - Engine stuff

Book 1 - Prep

Include Menus by Emily Short.
Use no scoring.
The printed name of yourself is “you”.
A thing can be usable or not usable. A thing is usually not usable.

Book 2 - Locations

Section - Using

The crystal ball is a thing. It is fixed in place. It is usable. Crystal ball can be operated or not operated. Crystal ball is not operated.

Using is an action applying to one thing. Understand “use [thing]” as using.

A place is a kind of thing. The forest is a place. The mountains is a place. The pointless valley is a place.

Check using:
If noun is not usable, say “You can’t use that.”.

Carry out using:
if noun is crystal ball:
now the current menu is the Table of Crystalball;
carry out the displaying activity;
clear the screen;
try looking.

	Table of Crystalball
	title	subtable	description	toggle
	"Location"	Table of Locations	--	--

		Table of Locations
		title	subtable	description	toggle
		"Forest"	Table of Location_forest	--	--
		"Pointless Valley"	--	"[explore the pointless valley]"	--
		["Mountains" Table of Location_mountains	--	--]
		with 5 blank rows
		
			Table of Location_forest
			title	subtable	description	toggle
			"Explore"	--	"[explore forest]"	--
			
			Table of Location_mountains
			title	subtable	description	toggle
			"Explore"	--	"You found a rubber ball somehow. [explore mountains]"	--

Part - Exploring

A rubber ball is a thing.

To say explore (L - a place):
if L is the forest:
let r be a random number from 1 to 25;
let N be 0 [this is a very unlovely hack because I can’t figure out how to say “choose a row with rank at least r”];
repeat with M running from 1 to the number of rows in the Table of Forest Explorations:
if N is 0:
choose row M in the Table of Forest Explorations;
if rank entry is at least r:
now N is M;
if N is 0:
let N be the number of rows in the Table of Forest Explorations;
choose row N in the Table of Forest Explorations;
say the description entry;
try the outcome entry; [I’d like to have this be conditional on “if there is an outcome entry:” but that never seems to trigger; even with that condition in, when it hits a blank outcome entry it produces a run-time error]
now the description entry is “Nothing happens.”;
now the outcome entry is the action of booping; [I’d like to say “blank out the outcome entry” but that doesn’t seem to do anything]
otherwise if L is the mountains:
now player is carrying a rubber ball;
otherwise:
say “You explore [the L] but don’t find much of anything”;
decrease the menu depth by 3.

Table of Forest Explorations
rank description outcome
15 “Mountains found etc.” the action of discovering the mountains
20 “foo” the action of booping [these really should be blank entries, but I can’t get that to work]
25 “Nothing happens.” the action of booping

Discovering is an action applying to one visible thing. [“visible” because it won’t be “touchable”; see 12.7 of the documentation]
Carry out discovering:
if the noun is the mountains:
choose a blank row in Table of Locations;
now title entry is “Mountains”;
now subtable entry is Table of Location_mountains;
otherwise:
stop the action.
Booping is an action applying to nothing. [and it does nothing]

Volume 2 - The world

Test room is a room. The player is in test room. The crystal ball is here.[/code][/spoiler]

Hehheh, thanks a ton. That’s pretty good, already, I’ll see if I can’t work with that.

Glad it helped. I’ll send up a couple distress flags to see if someone can help me with the parts that don’t work.

Let’s say we want the probability of choosing a row to be (rank of this row / total rank of all rows). This would ensure that a row with a rank of 20 gets chosen exactly two times as often as a row with rank 10, and so on – lovely, and we don’t need to rewrite all the numbers if we decide to take out one of the earlier rows. Here’s some (untested, so there may be small mistakes) code that would do that:

[code]Total rank is a number that varies.

To calculate total rank:
let m be 0;
repeat through the Table of Exploration:
increase m by rank entry;
now total rank is m.

To decide which number is chosen row:
calculate total rank;
let x be a random number between 1 and total rank;
repeat with n running from 1 to the number of rows in the Table of Exploration:
choose row n in the Table of Exploration;
if x is not greater than rank entry:
decide on n;
decrease x by rank entry;
decide on 1. [This should never happen.]
[/code]
And we can now use the phrase “choose row chosen row in the Table of Exploration”.

(To see how the algorithm works, let’s say that the rank entries are 5, 10, 20 and 7. That means total rank evaluates to 5 + 10 + 20 + 7 = 42. We choose a random number between 1 and 42. If it is not greater than 5 (probability 5/42), we choose the first row. If it is greater than 5, we decrease it by 5, leaving us with a random number between 1 and 37. If this is not greater than 10 (total probability 10 / 42), we choose the second row. If not, we decrease it by 10… and so on.)

That’s very nice, Victor. Another advantage of having using total rank as the denominator is that it lets you (for instance) zero out the rank after a row gets used, so you don’t have to make “nothing happens” more and more frequent.

I guess the decide-phrase is the thing that I want to interrupt the repeat-loop once I’ve hit an outcome, though I’m still somewhat grumpy that there’s no easier way to say something like “choose the first row with a rank greater than n.” (The Table of Rankings for scores must do something like that, but it seems to be hard-coded in I6. Boo for both scoring and I6 hard-coding.)

pashaimeru, the part where “if there is no outcome entry” didn’t work must have been some weird error on my part – but the inability to blank out the outcome entry looks like a real bug, so if you want to adapt my code it looks like you’ll have to use the booping kludge until that bug is fixed.