An imposter item that runs away

Hey gang. I’m still messing around creating monsters with cool characteristics just for fun. I was watching some folks play COD prop hunt, and I decided to make a creature that does something similar. It wanders around occasionally changing it’s printed name to the things around it and using this disguise to steal your stuff (carrying up to two items). That part works, but whenever I try to get the player to interact with the imposter, the player cannot do this because the player does not know the imposter’s true name. So if my imposter looks like a dresser for example, I would like “open dresser” to be met with something interesting, like the dresser running away in a random direction. The trouble is, if there isn’t an object in the room with the same name as the printed name of the imposter, I get a parser error (can’t take what isn’t there- something to that effect) and the action never executes, and if there IS such an object, then the player only ever interacts with the real object. I can’t even correct this with “instead of doing something to a dresser” because if there’s no dresser around, that action never happens.

I have two blocks of code below. The first one is the code I’m using to make the imposter, the second one is one example of a failed solution to my problem.

the imposter is a man in the bedroom.
The printed name of the imposter is "coin". The indefinite article is "a";	

To have the imposter take a turn:
	if the imposter randomly decides to act:
 [the function that does the random-acting came mostly from Gavin Lambert, who helped me out in an earlier question on this forum. This is found elsewhere in my code with a few modifications. Thanks friend!]
		if the player is in the location of the imposter:
			if the player is not carrying nothing:
				now the imposter carries a random thing carried by the player;
				say "Your pockets suddenly feel lighter.";
				if the imposter carries three things:
					try the imposter dropping a random thing;
		otherwise:
			have the imposter transform;
			have the imposter wander; [wander code also comes partly from the help y'all gave me in a previous question. Thanks guys, you rock!]

to have the imposter transform:
	let NEAR be the list of things in the location of the imposter;
	sort NEAR in random order;
	now the printed name of the imposter is "[entry 1 of NEAR]";

So all of that works. Awesome! But I have tried a bunch of methods to get the player to interact with the imposter, all of which fail. I can’t even remember most of them, but here’s just one example::

Instead of taking something while the player is in the location of the imposter:
	let N be the printed name of the imposter;    
	if the noun is N:
		try taking the imposter

[at this point, I could make some rules about what happens instead of taking the imposter]
1 Like

Ooh, neat idea! I think I’m a little fuzzy on how exactly this is meant to look from the player’s end, though – is the idea that the imposter will come into a room with a dresser, a bed, a desk, and a chair, then randomly pick one to impersonate and then when the player comes in there’ll be a dresser, a bed, two desks, and a chair, with the two desks indistinguishable? Or that the imposter somehow replaces or inhabits the desk, with the furniture returning to normal once the imposter goes back to its monster form?

Either way, one approach that might make sense is to add a mimicked/unmimicked property to all your objects, and then when the imposter chooses a shape, move it to nowhere and change the property of the chosen object to mimicked (or move a copy of the chosen object to the location from nowhere, if you want there to be two desks/chairs/whatever). And then reverse the process when its time to go walkabout again.

The advantage of doing things that way is you can write rules of the form “before doing something other than examining to an object (called the foo) when the foo is mimicked…” which should solve your issues on how the player refers to the thing, and also means the player can try to open a mimic-dresser or flip a mimic-coin, but not vice versa (though NB that “doing something other than examining to” rules don’t catch actions where the imposter is an indirect object, like TIE ROPE TO DRESSER or what have you).

Oh, and if you’re going for the approach where there are two of whatever the imposter mimics, you might also want to create a system of random properties (so a desk can be oak, or mahogany, or pine, or whatever – or if they’re meant to be physically identical, eastern or western or northern etc.) to make disambiguation a little easier.

Yes, but the imposter wanders around, so often it will be the only unique item in the room. The challenge for the player is to remember if that room “usually” has a desk in it, or if they saw a desk elsewhere already.

one approach that might make sense is to add a mimicked/unmimicked property to all your objects, and then when the imposter chooses a shape, move it to nowhere and change the property of the chosen object to mimicked (or move a copy of the chosen object to the location from nowhere, if you want there to be two desks/chairs/whatever). And then reverse the process when its time to go walkabout again.

I like this idea, and I can see that it would probably work, but I am torn, because I also like how simple my existing code is. I’m hoping to make something more elegant so that I could drop the imposter into any game I like without having to drastically rewrite the game around it. I also like how the imposter acts like a person even when it is mimicking something. Now, for example, the imposter sometimes wanders into a room with the player in it, and you get some amusing code like “the dresser arrives from the north” which I totally love.

I have some code right now which almost solves my problem, but it has some obvious drawbacks:

(by the way, the “in the vicinity” code is based on another function where I defined the vicinity as being broader than just the location, in case that part is confusing, also based partly on help I received on this forum. You guys are amazing.)

rule for printing a parser error when the latest parser error is the can't see any such thing error:
	if the imposter is in the vicinity of the player:
		say "Fearing discovery, the [the printed name of the imposter] panics!";
		have the imposter wander; [go to a random adjacent room]
		have the imposter transform;
		have the imposter evade; [go to a random adjacent room lacking the player]
	otherwise:
		continue the action;

Now if the player says “take the coin” and there is no real coin in the room, the fake coin runs away. The problem however is that the same thing happens if the player says “take the car” and there is no car in the game, or even 'take the lkjsd;lfk."

The key to making Inform understand that a particular object has a new name is exactly that: to mess with its understanding.

You can do that with a text property (in part):

The impostor has some text called the disguise.
Understand the disguise property as describing the impostor.

The main problem though is that Inform will only understand the full property text as the impostor, so if for example you had an object with printed name “TV remote” then it will only understand “TV remote” as the impostor, not “remote” or “remote control” or any other aliases you might have reasonably defined.

You can fix this with a dip into GPR tokens, although my I6 is rusty so I may have missed a detail:

Bedroom is a room.  "A very messy bedroom filled with scattered clothing.  The exit lies to the east."
Living Room is east of Bedroom.  "The central hub of the apartment, with a bedroom to the west and kitchen to the south."
Kitchen is south of Living Room.  "A strangely clean kitchenette, given the state of the rest of the apartment.  The only exit is back to the north."
A wallet is in Bedroom.  A TV remote is in Living Room.  A cake and a coin are in Kitchen.

The impostor is a animal.  The impostor has an object called the disguise.  The impostor can be startled.
Understand "[disguise]" as the impostor.
The understand token disguise translates into I6 as "DISGUISE_TOKEN".
Rule for printing the name of the impostor when the disguise of the impostor is not nothing:
	say disguise of the impostor instead.
	
Definition: a thing is non-impostor if it is not the impostor and it is not the player.

When play begins:
	now the impostor is in a random room;
	transform the impostor.

To transform the impostor:
	let D be a random non-impostor thing in the location of the impostor;
	if D is not nothing:
		now the disguise of the impostor is D;
		now the impostor is in a random room adjacent to the location of the impostor;

Every turn:
	if the impostor is startled:
		[this prevents moving a second time in one turn]
		now the impostor is not startled;
		continue the action;
	if the player cannot see the impostor and a random chance of 1 in 2 succeeds:
		transform the impostor;
		if the location of the impostor is the location, say "Was there a [disguise of the impostor] here before?  You don't recall."

[This is just for debugging; you'd remove it from the final version.]
Every turn:
	say "(impostor is disguised as [disguise of the impostor] in [location of the impostor])".
			
Instead of doing something to the impostor:
	say "The [disguise of the impostor] emits a strange shrieking noise and somehow runs out of the room.";
	now the impostor is startled;
	transform the impostor.

Include (-

[ DISGUISE_TOKEN  d;
	d = (+ disguise of the impostor +);
	if (TryGivenObject(d) > 0) {
		return (+ the impostor +);
	}
	return GPR_FAIL;
];

-)

This is almost the same, but now we’re setting the disguise to the actual object being disguised as, and getting Inform to use that object’s own parsing routines to match the impostor instead. This does now allow remote to parse as the impostor, and in theory this should work for any other complicated parsing rules given to each object too.

(You’d probably need a lot more objects to make this worthwhile.)

There’s still some caveats to this. If the player has been taking things, and the impostor takes them off the player, then there’s a decent chance that at some point it will run out of things to transform into. Perhaps it could have a fallback of transforming into something it’s carrying as well.

There would also be a problem if it transformed into something and then stayed (or was later moved) to the same room as the original item, because that would produce a parser ambiguity problem. The above avoids this because they unconditionally move after transforming (and they can’t be blocked by doors etc because they’re technically silently teleporting around), but it’s something to bear in mind if you vary the logic a bit.

1 Like

Woah. That code is super cool. I see how the disguise mechanism works for the most part, but I can tell that I’ll need to do some more learning to understand all of it.

I’m having trouble understanding the most important part- the part where you teach Inform how to recognize the disguise as the imposter when the player refers to it this way. Take this line in your code for example:

Understand "[disguise]" as the impostor.

I tried doing things like this, but inform didn’t seem to understand that the disguise (which I was calling the mimicked for a while) was something that varied. Then I tried:

The currently mimicked is a thing that varies. The currently mimicked is initially the dresser. 

After reading a command:
	if the player's command includes "[the currently mimicked]":
		replace the matched text with "the imposter";
	

But inform just wouldn’t understand that the currently mimicked would change later. With some variations of the above code I could get it to recognize the initially mimicked item, but then it would stop working once the imposter transformed.

So I’m guessing the secret to this code lies somewhere in the parts where you refer to inform 6, or it’s somewhere in here, which I completely don’t understand:

Include (-

[ DISGUISE_TOKEN  d;
	d = (+ disguise of the impostor +);
	if (TryGivenObject(d) > 0) {
		return (+ the impostor +);
	}
	return GPR_FAIL;
];

I want the code to work, but I’m equally interested in understanding why. Can you explain this piece to me, as to how you’re getting inform to recognize the disguise as being the imposter (or alternatively, and perhaps even preferably, can you point me to a hint/resource that will help me work it out myself)? I’m not afraid of learning “hard code,” if that helps at all.

(As an aside, I thoroughly enjoyed your John-Carpenter’s-the-Thing-esque interpretation of the imposter freaking out and running away).

EDIT:
Wait wait wait. Is it this line that does it?

Understand the disguise property as describing the impostor.

Do I need to understand what properties are in Inform? Should I be looking at chapters and examples that talk about this?

No, you’re mixing up two different solutions.


Solution 1:

The impostor has some text called the disguise.
Understand the disguise property as describing the impostor.

[at some point]
    now the disguise of the impostor is "lamp";
    now the disguise of the impostor is the printed name of the toaster;

This lets you use any single phrase that you like, but it will only recognise this phrase in full (in addition to the normal names, like imposter itself). So if it’s not a single word it still only works in full.


Solution 2:

Understand "[disguise]" as the impostor.
The understand token disguise translates into I6 as "DISGUISE_TOKEN".
[plus the Include DISGUISE_TOKEN block you already quoted]

[at some point]
    now the disguise of the imposter is the toaster;

This will let the imposter parse as any name that the toaster itself would be recognised as – which means that you never want them to be in the same room or it will cause an ambiguity. (Although you could probably deal with that by adding a does the player mean rule that prefers choosing the imposter, as that will lead to hilarity plus the impostor leaving so that the player can access the real object without interference.)

Technically, these solutions aren’t exclusive – you could apply both of them and they’d both work together. But you probably shouldn’t. The second one is the better one, even though it looks a bit more complicated.

2 Likes

Basically, the DISGUISE_TOKEN is a bit of I6 code that says the following:

  1. get the current “disguise” property of the impostor (the object that it’s pretending to be)
  2. try to parse the names of that object at the current position in the parsing line
  3. if that succeeded, then return the impostor, otherwise return failure.

This is what’s called a GPR, or General Parse Routine. It’s mentioned in passing in the I7 docs but not really in sufficient detail to actually write one. It gets a more complete explanation in DM4, although I still ended up writing that one based on looking at the I6 code output from the compiler rather than following any manual in particular. It’s definitely one of the dark corners of the language.

(FWIW, the return value of the routine doesn’t really seem to matter – DM4 says that for I6 it’s supposed to either return GPR_FAIL or the id of an object that it successfully parsed as (among a few less common options), but at least in this context all that I7 appears to care about is whether it’s GPR_FAIL or any other value at all.)

Anyway, the line:

The understand token disguise translates into I6 as "DISGUISE_TOKEN".

is what tells I7 to call that bit of I6 code when it’s trying to figure out what [disguise] means. This is mentioned in WI§27.23 – which is certainly a murky corner of the language, if a less dark one.

And finally, the line:

Understand "[disguise]" as the impostor.

is what tells I7 that anything that successfully parses as [disguise] is a valid name of the impostor. This is covered in WI§17.13.

2 Likes