Differences between player and other persons

I’m encountering some weird differences between the player and NPCs when trying to create a sort of factory pattern for generating patients that all share the basic anatomy but have different characteristics. What works for NPCs doesn’t for the player even though the player presumably is also a regular person. Or is it? Here’s simplified code:

Lab is a room. "Experiments happen here."

A body_part_state is a kind of value. 
Body_part_states are initial, case.
Body_part_states are usually initial.

A body_part is a kind of thing. 
A body_part has a table name called body_part_descriptions. 

[To handle parts that come in twos like ears where we'd want to deal with one, the other, or both]
A bilateral_body_part is a kind of body_part.
A bilateral_body_part has a list of body_parts called subparts.

The ears are a kind of bilateral_body_part.
The ears is a part of every person.

The right_ear is a kind of body_part.
The printed name of right_ear is "right ear".
Understand "right ear" as right_ear.
A right_ear is part of every ears.

The left_ear is a kind of body_part.
The printed name of left_ear is "left ear".
Understand "left ear" as left_ear.
A left_ear is part of every ears.

Jill is a woman in the Lab.
The body_part_descriptions of Jill's right_ear is table of jills_right_ear_descriptions.
The body_part_descriptions of Jill's left_ear is table of jills_left_ear_descriptions.

table of jills_left_ear_descriptions
state	txt
initial	"pointed left"
case	"even more pointed left"

table of jills_right_ear_descriptions
state	txt
initial	"pointed right"
case	"even more pointed right"

The right_ear of player has a table name called body_part_descriptions.  [won't compile without this]
The body_part_descriptions of right_ear of player is table of players_right_ear_descriptions.
The left_ear of player has a table name called body_part_descriptions. [won't compile without this]
The body_part_descriptions of left_ear of player is table of players_left_ear_descriptions.

table of players_right_ear_descriptions
state	txt
initial	"hairy"
case	"hairier"

table of players_left_ear_descriptions
state	txt
initial	"ugly"
case	"uglier"

So two strange problems:

Even though this successfully creates the ears->left/right ear hierarchy for both the player and Jill, only Jill’s tables of ear descriptions get assigned. Here’s the some output from the above code:

Lab
Experiments happen here.

You can see Jill here.

>showme Jill's right ear
right ear - right_ear
location: part of Jill's ears , which is part of Jill in Lab
singular-named, proper-named; unlit, inedible, portable
list grouping key: none
printed name: "right ear"
printed plural name: "right_ears"
indefinite article: none
description: none
initial appearance: none
body_part_descriptions: table of jills_right_ear_descriptions

>showme my right ear
right ear - right_ear
location: part of your ears , which is part of yourself in Lab
singular-named, proper-named; unlit, inedible, portable
list grouping key: none
printed name: "right ear"
printed plural name: "right_ears"
indefinite article: none
description: none
initial appearance: none
body_part_descriptions: (the empty table)

>

Why does the player’s right ear have only “the empty table” as its body_part_descriptions value? What is “the empty table” anyway?

And why will this code not even compile without the restatement of this?

The right_ear of player has a table name called body_part_descriptions. 
The left_ear of player has a table name called body_part_descriptions. 

The “Report on Translation: Failed” message is this:

Problem. You wrote ‘The body_part_descriptions of right_ear of player is table of players_right_ear_descriptions’ : but the property body_part_descriptions for the right_ear of player is not allowed to exist, because you haven’t said it is. What properties something can have depends on what kind of thing it is: see the Index for details.
See the manual: 3.7 > 3.7. Properties depend on kind

Problem. You wrote ‘The body_part_descriptions of left_ear of player is table of players_left_ear_descriptions’ : but the property body_part_descriptions for the left_ear of player is not allowed to exist.

Why does the

A body_part has a table name called body_part_descriptions. 

not work for the player such that it must be repeated specifically for the player just to compile?

And even that repetition, why does the expected assignment of the table name not happen for the player when the exact same assignment statement does work for the NPC?

I’ve tried a workaround to limit the anatomy to NPCs – and not the player – but this sort of definition doesn’t help:

Definition: a thing is other if it is not the player.
The ears is a part of every other person.

since this won’t compile either. Seems I’m missing something pretty basic here.

Many thanks in advance for any pointers!

player is not actually an object, it’s a variable (because the player can change during the course of play in some stories). Usually that doesn’t matter, but sometimes it does.

If you have a look at Index -> World, what you’re actually doing there is creating new objects called right_ear of player and left_ear of player.

If you want to refer to the object that is the default player, use yourself.

Having said that, you’d think that this would work:

The body_part_descriptions of yourself's right_ear is table of players_right_ear_descriptions.

But it doesn’t. The correct construct is actually:

The body_part_descriptions of your right_ear is table of players_right_ear_descriptions.

I guess Inform is helpfully conjugating for you. (There’s a hint about this in the World Index too.)

Incidentally, if you’re going to stick with that naming convention then you’ll probably also want:

A bilateral_body_part is usually plural-named.

Well spotted!

Just to clarify, it’s the line for example

The right_ear of player has a table name called body_part_descriptions.  [won't compile without this]

which is interpreted by Inform as referring to an object whose name it doesn’t recognise, and therefore must be newly created by this line of code, called ‘the right_ear of player’, which despite its name is entirely unconnected to the player object and sits ‘nowhere’ in the World Map and whose properties (such as the subsequently initialised table name) are therefore also unconnected to those of the actual ear of the player object.

>showme right_ear of player
right_ear of player - thing
location: out of play
singular-named, improper-named; unlit, inedible, portable
list grouping key: none
printed name: "right_ear of player"
printed plural name: none
indefinite article: none
description: none
initial appearance: none
body_part_descriptions: table of players_right_ear_descriptions

Incidentally, the compiler’s error message when the preceding line is commented out and therefore no ‘right_ear of player’ object has been created:

Problem. You wrote ‘The body_part_descriptions of right_ear of player is table of players_right_ear_descriptions’ : but the property body_part_descriptions for the right_ear of player is not allowed to exist, because you haven’t said it is. What properties something can have depends on what kind of thing it is: see the Index for details.

is misleading in suggesting that the problem is the lack of a declared property for an existing object when, in this instance at least, the rather more fundamental problem is that the object itself (right_ear of player) does not exist.

Should you ever need this sort of declaration when creating or assembling things, the way Inform likes you to do it is to make a new kind to apply ‘every’ to: since kinds and their memberships are immutable in play this is the only way Inform can be sure that the meaning of ‘every’ in this sort of declaration will never change from the meaning it had at compilation. As an aside, Inform also prefers phrases including ‘every’ to begin with the ‘every’ part of the phrase and sometimes complains if you try to do it the other way round. so:

An attitude is a kind of thing.
A NPC is a kind of person.
Every NPC incorporates an attitude.

at the beginning of your code allows you to then declare any person not the player not as a generic person but as an NPC with an attitude!

Note that ‘incorporates’ is the reverse way of expressing ‘is a part of’.

Note also that if you want to have NPCs who are men, women or animals you will need to declare an attitude for each of these kinds too, since they are no longer inheriting an attitude from the generic ‘person’ kind, e.g.

Every man incorporates an attitude.
Every woman incorporates an attitude.
Every animal incorporates an attitude.

In fact, if all your non-player characters will be declared specifically as men, women or animals you don’t need the NPC kind at all- just the declarations above, since the player will then (unless you declare otherwise) be the only person not specified to be one of these 3 kinds and, therefore, without an attitude. If however you really need your player to be a woman without an attitude, you’ll need a her to be a woman distinct from all other women:

The player is a woman.
A WNPC is a kind of woman.
Every WNPC incorporates an attitude.

Another way entirely to deal with exceptions like this is to disassemble them after play begins:

When play begins:
	Now your attitude is nowhere.

which means that there is still a thing called ‘your attitude’ in the universe (but ‘nowhere’ in the game world), which can be referred to and moved or manipulated or even reincorporated into you (or, perhaps renamed, into somebody else) later, but it is for the moment no longer part of you.

Many thanks, Gavin and Peter! Most excellent help!

The fact that “player” is not a person object but rather a pointer variable that defaults to the “yourself” person object was what I didn’t grok. But as you point out, the Index does make this clear. Thanks for providing not only the explanation but also a guide how better to work problems like this out, since plenty more will no doubt arise.

For what it’s worth, here’s more of what I’m trying to accomplish:

Lab is a room. "Experiments happen here."

A body_part_state is a kind of value. 
Body_part_states are initial, case.
Body_part_states are usually initial.

A body_part is a kind of thing. 
A body_part has a table name called body_part_descriptions. 
A body_part has a body_part_state.

A bilateral_body_part is a kind of body_part.
A bilateral_body_part is usually plural-named.
A bilateral_body_part has a list of body_parts called subparts.

The ears are a kind of bilateral_body_part.
The ears is a part of every person.

The right_ear is a kind of body_part.
The printed name of right_ear is "right ear".
Understand "right ear" as right_ear.
A right_ear is part of every ears.

The left_ear is a kind of body_part.
The printed name of left_ear is "left ear".
Understand "left ear" as left_ear.
A left_ear is part of every ears.

Jill is a woman in the Lab. 
The body_part_descriptions of Jill's right_ear is table of jills_right_ear_descriptions.
The body_part_descriptions of Jill's left_ear is table of jills_left_ear_descriptions.

table of jills_left_ear_descriptions
state	txt
initial	"pointed left"
case	"even more pointed left"

table of jills_right_ear_descriptions
state	txt
initial	"pointed right"
case	"even more pointed right"

The body_part_descriptions of your right_ear is table of your_right_ear_descriptions.
The body_part_descriptions of your left_ear is table of your_left_ear_descriptions.

table of your_right_ear_descriptions
state	txt
initial	"hairy"
case	"hairier"

table of your_left_ear_descriptions
state	txt
initial	"ugly"
case	"uglier"

Instead of examining a body_part:
	let bilat be the holder of the noun;
	let txt be report_body_part_description of the noun;
	say "You carefully examine [holder of bilat]'s [noun] and find it [txt]."
				
Instead of examining a bilateral_body_part:
	let txt be "You carefully inspect [noun].";
	let lst be the list of body_parts that are part of the noun;
	repeat with pt running through lst:
		let txt be "[txt] You find the [pt] [report_body_part_description of pt].";
	say "[txt][line break]"

To decide what text is report_body_part_description of (pt - a body_part):
	let cur_state be the body_part_state of pt;
	let tbl be body_part_descriptions of pt;
	choose the row with state of cur_state from tbl;
	decide on txt entry.
	
test me with "x ears / x my ears / x my left ear / x my right ear / x jill's ears / x jill's left ear / x jill's right ear".

This code produces:

Lab
Experiments happen here.

You can see Jill here.

>test me
(Testing.)

>[1] x ears
Which do you mean, your ears or Jill's ears?

>[2] x my ears
You carefully inspect your ears. You find the right ear hairy. You find the left ear ugly.

>[3] x my left ear
You carefully examine yourself's left ear and find it ugly.

>[4] x my right ear
You carefully examine yourself's right ear and find it hairy.

>[5] x jill's ears
You carefully inspect Jill's ears. You find the right ear pointed right. You find the left ear pointed left.

>[6] x jill's left ear
You carefully examine Jill's left ear and find it pointed left.

>[7] x jill's right ear
You carefully examine Jill's right ear and find it pointed right.

>

The goal is to provide as detailed a simulation of a person as possible. This may very well drown in the Combinatorial Swamp, but that’s what I’m trying to do with this project.

Thanks again! What a great community!

Yes and no. Inform has detected that you are trying to assign values to an object that doesn’t exist, so it first created it (as it does in most other cases, such as declaring exits to rooms before you’ve defined the rooms themselves).

But then having done so, it doesn’t want to assign that specific property to the new object because it wasn’t created as a kind that has that property. That’s what the error is actually stating. This means that either the creation of the object was in error (as is the case here), or that you did intend to create the object but forgot to set it to the right kind, or did want to explicitly give it that property, and Inform isn’t sure which is the case.

Ah, yes. That insight does make more sense of the error message :+1:t2:

To avoid the jarring ‘You carefully examine yourself’s…’ you might want to try

Instead of examining a body_part:
	let bilat be the holder of the noun;
	let txt be report_body_part_description of the noun;
	say "You carefully examine [regarding the possessor of the noun][their] [noun] and find it [txt].".

To decide which person is the possessor of (T - a thing):
	let P be the holder of T;
	while P is not nothing:
		if P is a person:
			decide on P;
		now P is the holder of P;
	decide on P.
say "You carefully examine [regarding holder of bilat][possessive] [noun] and find it [txt]."

This will automatically transform to “Jill’s” or “your” as appropriate.

Or you can use [their] rather than [possessive] to get “his”, “her”, or “your” if you prefer.

FWIW, you might want to have a look at some existing work in this field for other ideas. I recall reading that there was a fairly detailed extension tracking body parts written for AIF at some point, but I never looked into it. But there’s also Slotted Wearing and Wielding by Nels Olsen, which is mostly concerned with wearing clothing rather than the parts themselves, but does define a set of body parts for those clothes to be worn on. I suspect there’s probably a few others that I’m not aware of.

1 Like

That’s a good tip. I didn’t know about or more probably had forgotten about that expansion.

Thanks for all the additional tips. Much appreciated!

Nels’ extension is pretty well restricted to anatomy involved in medieval sword battles, while the AIF stuff I’ve seen (notably Fictitious Frode’s extensions https://github.com/FictitiousFrode/AIF) is also pretty limited to its, ahem, relevant anatomy. That’s why I’m giving this a stab.