How can I code A Following Person Based on Van Helsing but with a pause?

Hi everyone.

In my game the NPC Mrs. Whip is in a room with an item that the player needs. Once they take the item Mrs. Whip chases and harangues them. If they don’t go to a new room each turn she wrestles the item away.

What I want to be able to code is some kind of actual inform7 version of

if the player is in the same location as mrs whip for greater than 1 turn:
     say "Mrs Whip grabs the sticky tack out of your hand saying 'That's my sticky tack. go get your own!" and storms back to her room";
     move the sticky tack to the suite;
     move Mrs Whip to the suite.
.

at which point I’m stuck. I don’t know how I would code that bit, or how to even search for it. I know about time but not counting it as part of a rule in this context.

I’ve used “Van Helsing” as the template for the chasing part (just in case it is not familiar to everyone) it goes like this:

Every turn:
    if the location of Mrs. Whip is not the location of the player:
        let the way be the best route from the location of Mrs. Whip to the location of the player, using doors;
        try Mrs. Whip going the way;
    otherwise:
        say "'Give me that back.' says Mrs. Whip."

Ultimately, once the player learns where to successfully run away they will reach a location where the player can “lose” her.

Thank you in advance

3 Likes

A somewhat simplified and quick example:

"Don't Linger"

Kitchen is  room.

Dining Room is east of Kitchen.

Alice is a woman in Kitchen.

To decide whether (T1 - thing) and (T2 - thing) are co-located:
	if the holder of T1 is the holder of T2, decide yes;
	decide no.

Every turn when not the player and Alice are co-located:
	now Alice is in the location;
	say "Alice pursues you."

The player carries a prize.

Every turn when the player was in the location and the player and Alice are co-located for at least two turns:
	now Alice carries the prize;
	say "Alice swipes the prize."

Test me with "e / w / e / w / z".

Improvements can be made to the above; it’s just to illustrate the basic idea. See WWI 9.13 The past and perfect tenses for details.

2 Likes

Thank you for that @otistdog. Will that let them be co-located for one turn before the object gets taken back?

In my head it will go:

you are in the hall.

Mrs. whip is here looking angry and she says “give me back my sticky tack”.

Then if the player does anything except leave she will take the sticky tack back.

Maybe I am misreading it, but it looks like she will grab it straight away once they are in the same place.

This looks like it should work for me – note that the key rule says “every turn when the player was in the location”, meaning that they were in the same location last turn as they were this turn.

If you want there to be a round of demanding from the NPC, then changing the rules will do it:

Every turn when the player was in the location and the player and Alice are co-located for exactly two turns:
	say "Alice demands the prize."

Every turn when the player was in the location and the player and Alice are co-located for at least three turns:
	now Alice carries the prize;
	say "Alice swipes the prize."

Test me with "z / e / w / z / e / w / z / z".

Yes. I see that now. I’ve got something to do but I’ll give it a whirl and reply back later.

Thank you @otistdog and @DeusIrae.

Actually, the logic is incorrect for the desired test. It slipped my mind that global variables (like player and location) are evaluated on an as-they-are-now basis when storing data for past-tense comparisons. For example, the player was in the location test turns into the following I6 condition (part of the TestPastSingleState routine):

(((real_location == ContainerOf(player))));

which translates as “the player is ‘directly in’ (i.e. standing on the ground) of their current location” – something that will be true both before and after a change of rooms. Also, the startup logic of the past-tense tracking subsystem stores the initial conditions of the game as the immediately previous condition, so the timing is off in the example scenario because Alice and the player start in the same room (thus they both were and are co-located on the first turn).

That’s a very detailed answer @otistdog. I’m not sure I have the wherewithal to make the most of it.

As a consequence, of what I think you’re saying, does your second version of the code compensate for this additional co-location and create the desired outcome? (Sorry, I’m not back to my PC yet).

No, the second version uses the same faulty player was in the location condition.

Here are some refinements to try to capture what I think is your intended scenario (one turn of warning, then the player must run continuously):

"Don't Linger"

Kitchen is a room.

Dining Room is east of Kitchen.

Alice is a woman in Dining Room. Alice can be pursuing the player.

To decide which object is the same place as (T - thing): [solely for readability]
	decide on the holder of T.

A person has an object called last place. [for testing whether the person has moved this turn]

Last Every turn (this is the update last places rule): [last so that it will register the previous turn's place]
	repeat with P running through people:
		now the last place of P is the holder of P.

To decide whether (P - person) has not moved this turn:
	if the last place of P is the holder of P, decide yes;
	decide no.

The player carries a prize.

Every turn when Alice is not pursuing the player and Alice does not carry the prize and Alice is in the same place as the player:
	now Alice is pursuing the player;
	say "Alice demands the prize."

Every turn when Alice does not carry the prize and Alice is pursuing the player and Alice is not in the same place as the player:
	now Alice is in the holder of the player; [oversimplified, perhaps]
	say "Alice pursues you."

Every turn when Alice does not carry the prize and Alice is pursuing the player and Alice has not moved this turn and Alice is in the same place as the player for at least two turns:
	now Alice carries the prize;
	now Alice is not pursuing the player;
	say "Alice swipes the prize."

Test me with "e / w / e / w / z / e / w / z / z".
1 Like

I like this version, but I think a simpler solution would also work here.

Alice can be annoyed. Alice is not annoyed.
Every turn when the player carries the prize:
    if the location of Alice is not the location of the player:
        now Alice is not annoyed;
        [movement logic here]
    otherwise if Alice is not annoyed:
        say "Alice glares at you. 'Give me that!' She looks mad.";
        now Alice is annoyed;
    otherwise:
        say "Alice snatches the prize.";
        now Alice carries the prize.

If you want her to give the player more turns of warning, you can change “annoyed” from a boolean property to a number, and have it count down to zero.

1 Like

I’ve tried @otistdog and @Draconis 's suggestions and I appreciate the effort you both took to help me.

I can fully understand @Draconis’s shorter version but I suspect there’s a lot of unexploited depth in @otistdog’s version that I’m not sufficiently competent in Inform7 coding to appreciate let alone make use of.

In any case I cobbled together something using both ideas. It still doesn’t do exactly what I’m after i.e.once the sticky tack is taken I don’t want mrs whip to react until the player leaves the, in this example, kitchen, but otherwise I think it works.

Maybe there are issues with it I can’t see though.

the kitchen is a room.

the kitchen is west of the bedroom.

the dining room is east of the lounge.

the lounge is north of the kitchen.

the bedroom is south of the dining room.

the sticky tack is in the kitchen.

mrs whip is in the kitchen.

Every turn:
	if the location of Mrs Whip is not the location of the player:
		let the way be the best route from the location of Mrs Whip to the location of the player, using doors;
		try Mrs Whip going the way;
	otherwise:
		stop the action.


mrs whip can be annoyed. mrs whip is not annoyed.
Every turn when the player carries the sticky tack:
	if the location of mrs whip is not the location of the player:
		now mrs whip is not annoyed;
		[movement logic here]
	otherwise if mrs whip is not annoyed:
		say "Mrs. Whip glares at you. 'Give me that!' She looks mad.";
		now mrs whip is annoyed;
	otherwise:
		say "mrs whip snatches the sticky tack.";
		now mrs whip carries the sticky tack.

Yes, this is a really easy trap to fall into, as conditions like ‘if the location was the location’ seem so natural, with an intended meaning of 'if (the current location) is the same as (the location as it was last turn). Unfortunately, for reasons explained below, what it actually means is- 'was it true at the end of last turn that ‘the location is the location’…

The easiest way to avoid this when thinking about the past is to remember that Inform’s inbuilt memory only stores the answers to true/false questions asked in the present tense and does not store values or objects. So, we can’t use the inbuilt system to flag to Inform that we’re interested in Elizabeth Bennet’s past dance-partners, or even her most recent dance-partner, or her dance-partner last turn, because that would require Inform to remember a list of one or more objects, like keeping a full dance-card of Miss Bennet’s partners. But we can ask Inform’s inbuilt memory to record the answer to a true/false question asked in the present tense- ‘Is Elizabeth Bennet’s dance-partner Mr Darcy?’.
At the end of each turn Inform will ask this question and keep a count of the number of consecutive turns this question has been true and also of the number of times it has switched from being false (last turn) to being true (this turn). Before updating those counts Inform also stores them as they were when it last asked the question at the end of the previous turn.

By referring to these four counts Inform can say whether the question was, has been or had been true, and for how many turns or times. In the simplest case, Elizabeth Bennet’s dance partner was Mr Darcy if the consecutive turn count recorded at the end of last turn for the question ‘Is Elizabeth Bennet’s dance partner Mr Darcy?’ is at least 1

So, when asking if something was true, it’s always useful to frame the question in the present tense and remember that we’re asking if that question in the present tense was true if asked at the end of last turn, remembering also that the question can’t mix information from the past and the present- any variables in the question will be considered with regard to the values/objects they held at the end of last turn. So, in this case ‘if the player was in the location’ refers to the player object as it was at the end of last turn and the location also as it was at the end of last turn and can be reframed as if we were asking at the end of last turn ‘is the (then current) player in the (then current) location?’ - in which case it becomes obvious that this will always be true.

When it’s necessary to retrieve objects/values from the past, such as ‘Who was Miss Bennet’s dance-partner last turn?’ or ‘What was the player’s location last turn’ so that we can compare them with present variable values or objects whose value couldn’t be definitely known at the end of last turn, we’ll have to abandon Inform’s built-in memory and build our own. If we’ll only ever need to know about last turn, we can use an ‘Every turn’ rule to store the current value in a global variable or a property, overwriting the previous value (making sure this overwriting with the current value only happens AFTER any code needing to refer to last turn’s values has run- e.g. by making it a ‘Last every turn’ rule). If we’ll need to refer back to the full dance-card, we can instead use the ‘Last every turn’ rule to add the current value to the end of a list.

3 Likes

That’s a good explanation @drpeterbatesuk of some complicated internal logic for Inform7. I appreciate you spelling it out.

Do you see any issues with the code I created above, the one refinement aside?

the kitchen is a room.

the kitchen is west of the bedroom.

the dining room is east of the lounge.

the lounge is north of the kitchen.

the bedroom is south of the dining room.

the sticky tack is in the kitchen.

mrs whip is in the kitchen.

Every turn: [*** this rule will run whether or not the player carries the sticky tack, so Mrs Whip will follow the player around throughout the whole game, regardless. Is that's what's intended? if not, move this code to replace [movement logic here] in the next rule ***]
	if the location of Mrs Whip is not the location of the player:
		let the way be the best route from the location of Mrs Whip to the location of the player, using doors;
		try Mrs Whip going the way;
	otherwise:
		stop the action. [*** this is a dangerous idea: it will stop ALL subsequent every turn rules  (i.e. ones that haven't already been run) from running, which as you add more rules is sooner or later likely to lead to disaster. Instead have when... conditions with each every turn rule so that each one always runs when it should. If whether one runs depends on the result of another, generally best to have both in one rule- otherwise you'll need to set a global flag in one rule to let the other one know if it should run or not ***]


mrs whip can be annoyed. mrs whip is not annoyed. [*** technically 'mrs whip is not annoyed.' is unnecessary, as if we write 'a can be b' by default a is not b. But some like to specify the default for the sake of clarity. It's a matter of style vs brevity of coding. ***]
Every turn when the player carries the sticky tack: [*** the player can avoid this condition firing by putting the sticky tack on or inside something else they're carrying, as 'carries' implies 'directly carried, not in or on something else'. If you want to block this as an unintended solution to throwing Mrs Whip off the trail, use 'encloses' instead of 'carries'. Also, what should happen if the player drops the sticky tack?- this condition won't be true then either... ***]
	if the location of mrs whip is not the location of the player:
		now mrs whip is not annoyed;
		[movement logic here]
	otherwise if mrs whip is not annoyed:
		say "Mrs. Whip glares at you. 'Give me that!' She looks mad.";
		now mrs whip is annoyed;
	otherwise:
		say "mrs whip snatches the sticky tack.";
		now mrs whip carries the sticky tack.
2 Likes

Thank you again @drpeterbatesuk.

You’ve identified exactly the sorts of issues I would not have been competent to address and where my lack of familiarity with the medium stymies my creativity.

In my head, if the player drops the sticky tack Mrs whip picks it up and then returns to her room where she drops it. The whole bit is to get Mrs whip out of her room and lose her so the player can circle back and get a second item from that room. As a result if she catches them the sticky tack has to go back to her room and be accessible. I don’t want the player to be able to “hide” the sticky tack so she doesn’t follow them as they have to get her out of the room and have her chase them until she falls into a “trap”.

Okay. Thank you @drpeterbatesuk.

I think I have the basic code working properly, including addressing the dropping of the sticky tack. The giving of the sticky tack I will address separately as it occurs the player may pass off the sticky tack, or may even try returning it to Mrs. Whip.

In a perfect world Mrs. Whip would return room by room to the kitchen but, that’s a level of complexity I think is probably unnecessary.

the kitchen is a room.

the kitchen is west of the bedroom.

the dining room is east of the lounge.

the lounge is north of the kitchen.

the bedroom is south of the dining room.

the sticky tack is in the kitchen.

mrs whip is in the kitchen.

mrs whip can be annoyed. mrs whip is not annoyed.

Every turn when the player encloses the sticky tack:
	if the location of mrs whip is not the location of the player:
		now mrs whip is not annoyed;	
	if the location of mrs whip is not the location of the player:
		let the way be the best route from the location of Mrs Whip to the location of the player, using doors;
		try Mrs Whip going the way;
	otherwise if mrs whip is not annoyed:
		if the location is the kitchen:
			say "Mrs. Whip starts eyeing you suspiciously.";
		otherwise:
			say "Mrs. Whip [one of]glares at you[or]fixes you with a gimlet eye[or]affixes you with a penetrating stare[or]side eyes you[or]eyes you suspiciously[at random] and says [one of][quotation mark]Give me that![quotation mark][or][quotation mark]Get your own sticky tack![quotation mark][or][quotation mark]Unhand my sticky tack you scoundrel![quotation mark][at random] She looks [one of]mad[or]furious[or]apoplectic[or]rage-filled[or]angry[or]enraged[or]indignant[or]offended[or]out of breath[or]outraged[at random].";
		now mrs whip is annoyed;
	otherwise:
		if the location is not the kitchen:
			say "Mrs. Whip snatches the sticky tack triumphantly and, turning on her heel, she storms off to her room.";
		otherwise:
			say "Mrs. Whip grabs the sticky tack and returns to hanging the posters.";
		now mrs whip is not annoyed;
		now the sticky tack is in the kitchen;
		now mrs whip is in the kitchen.
		
		
Every turn when the player does not enclose the sticky tack and mrs whip is in the location and the location is not the kitchen:
	say "[quotation mark]There's my sticky-tack![quotation mark] says Mrs. Whip grabbing the weird blue stuff triumphantly. [paragraph break]With With a HARUMPF, she turns on her heel and stalks off to her classroom.";
	move the sticky tack to the kitchen;
	now mrs whip is in the kitchen.
1 Like

As written, this rule will run when the sticky tack is not in the location with Mrs Whip and the player- she will appear to grab it from afar…

In a connected issue, I think you still have a problem if the player somehow can drop the sticky tack without Mrs Whip immediately grabbing it, in that Mrs Whip will continue to follow the player, leaving the sticky tack behind. Perhaps the logic should be that she follows the sticky tack rather than the player, then immediately grabs it if she finds it not enclosed by the player, or after 1 turn’s delay in the same location if the player encloses it.