How to run the same check from different actions

(Swear I posted about this yesterday, maybe I didn’t press send)

So I have a situation where I want someone to notice you and say something about your appearance, after you do one of a few things to them. If you throw something at them, talk to them, give them something, it will get their attention, and IF you look different than before, they will make a comment on it. The point is it’s the same check, but it can be triggered in different ways.

The way I’d do this in programming is I’d just have a function CheckPlayerAppearance that I’d call in all the parts of the code where something happens that should get their attention, eg. Throw, Talk, Give, etc.

In Inform 7, I gather it’s a “rules based” system, not a “function based system”, so I imagine what I’m supposed to do is, create a state for a person, then act when that state is achieved eg.

A person can be attentive. Instead of throwing something at Bob: say "Bob says: Ow that hurt."; Bob is now attentive. Instead of giving something to Bob: say "Bob says: I don't want it."; Bob is now attentive.

But I’m not sure about how to take action on that state. I imagine something like;

If Bob becomes attentive: if player wears the hat: say "Bob says: That's a cool hat!".
That syntax doesn’t work, and I’m not sure what to search for to find the answer…

I think you want to make a new phrase with the “to” command:

[code]To get Bob’s attention:
say “You now have Bob’s attention.”;

Instead of throwing something at Bob:
say “Bob says: ow”;
get Bob’s attention;[/code]

Phrases are just like functions, and you can even pass in parameters.

There’s a couple of things you can do, depending on exactly when you want things to happen. If you want the message to appear after all the action reporting and stuff, you can use an “Every Turn” rule:

Every turn when Bob is attentive and Bob was not attentive: if player wears the hat: say "Bob says: That's a cool hat!".

(For “Bob was not attentive,” see §9.13 of Writing with Inform–this is true when “Bob is not attentive” was true at the start of the most recent action.)

Another thing you can do is combine the different actions into a Kind of Action (§7.15) and write a rule that pertains to them:

[code]Giving something to Bob is provoking Bob. Throwing something at Bob is provoking Bob. Attacking Bob is provoking Bob.

After provoking Bob when the player wears the hat:
say “That’s a cool hat!”;
continue the action.[/code]

(Note that “provoking Bob” is just the name for the kind of action and Bob is not a component part of it; it’s like ProvokingBob rather than Provoking(Bob).)

However, a thing to be aware of here is that the “After” rules only run when action-processing has successfully made it through the “Carry Out” rules to the “After” rules without getting stopped by something in a Before, Instead, or Check rule. See §12.2 (also check chapters 7 and 12 of the manual in general). So if you do that, you’ll have to make sure that the rules you write to take care of the provoking Bob actions make it through the Carry Out stage. Also, by default “After” rules stop action processing, so I put in “continue the action” to tell it not to stop.

Another approach is (as craiglocke posted while I was typing!) basically the approach you’ve said–write a phrase to check the player’s appearance and call it from the appropriate parts of the code. See §11.2.

[code]To check the player’s appearance:
if player wears the hat:
say “Bob says: That’s a cool hat!”.

Instead of throwing something at Bob:
say “Bob says: Ow that hurt.”;
check the player’s appearance.
Instead of giving something to Bob:
say “Bob says: I don’t want it.”;
check the player’s appearance.[/code]

This will work basically the way you expect it to, just like the function. Most of the stuff you do in Inform happens in rules, which are usually hooked on to actions or Every Turn, but you can also write these phrases that can be called from inside rules (or other phrases).

There are many possible ways of approaching this. You could use an every turn rule, or maybe use the scene mechanics to check whether your conditions have become satisfied.

Personally I’d probably do something like this:

Already-complimented is initially false.
Bob-attentive is initially false.

Every turn when already-complimented is false and Bob-attentive is true:
      if the player wears the hat:
            say "Bob says 'Nice hat!'";
            now already-complimented is true;
      now Bob-attentive is false.

Before doing something when the current action involves Bob: now Bob-attentive is true.  

Wow, thanks everyone! All of these examples are great for me to experiment with, regardless of which one I go with. I’ll be trying them out tonight. Inform is amazing…

Quick questions for now;

  • So effectively I can see my custom actions as functions? Eg. “To check the player’s appearance:” is defining the function, and “check the player’s appearance” is calling the function?
  • In all of these examples, I assume I can substitute “Bob” with “someone” to make this a generic rule that will apply to any character? (With the exception of the bob-attentive one which looks like a global variable - which is still good to know)

You are correct about the way to define and use the functions/phrases.

To substitute a variable for Bob, you can do something like this manual example:

To admire (item - an object): say "You take a long look at [item]."

So the format for a variable input is (the variable name - the variable’s kind/type)

So (somebody - a person) should work, as long as the word ‘somebody’ isn’t used somewhere else (I usually use CurrentPerson as a variable name).

Yes, my “Bob-attentive” was a global truth state.

A person can be complimentary.
A person can be attentive. 

Definition: a person (called X) is implicated if the noun is X or the second noun is X.
Before when someone (called X) is implicated:
	now X is attentive.
	
Every turn when someone (called X) is not complimentary and X is attentive:
	if the player wears the hat:
		say "[X] says 'Nice hat!'";
                now X is complimentary;
	now X is not attentive.

Yes, except that these aren’t actually “actions”–in Inform an action is something (almost always) that the player can execute with a command, and there is a lot of machinery around an action. See chapters 7 and 12 of Writing with Inform. A function like this is called a “phrase.” But yes.

If you want to make the phrase apply to any character, you can also put a temporary variable for that in the header of the phrase and make a temporary variable for the person in the rule header:

[code]To have (the spectator - person) check the player’s appearance:
if player wears the hat:
say “[The spectator] says: That’s a cool hat!”.

Instead of throwing something at someone (called the victim):
say “[The victim] says: Ow that hurt.”;
have the victim check the player’s appearance.
Instead of giving something to someone (called the recipient):
say “[The recipient] says: I don’t want it.”;
have the recipient check the player’s appearance.[/code]

(There isn’t any real reason to have all the variables have different names, it just seemed weird to use “victim” throughout.)

Thanks guys, this was great. The simple “define function & call function” approach worked in this case, without setting a state on the character, but I’ve kept all the code snippets in this thread since it’s useful information and I’ll probably need the more involved interaction soon.

@matt w - thank you, I’m still learning about the ways I can substitute the names of things in text.
@jrb that’s pretty cool, I can see from that I can trigger something as soon as a person is mentioned in an action…