Handling NPC fidgets

Most of the time the scripting for an NPC probably comes from “inside” the NPC themself, via something like AgendaItem.invokeItem(). And done this way, sense context nonsense takes care of itself automatically. For example:

+ alice:        Person 'alice' 'Alice'
        "She looks like the first person you'd turn to in a problem."
        isHer = true
        isProperName = true
;
++ aliceAgenda: AgendaItem
        initiallyActive = true
        isReady = true
        invokeItem() {
               "Alice clears her throat. ";
        }
;

…means that every turn the player will see “Alice clears her throat.” if they’re in the same room as Alice, and they won’t see it (or anything else related to the agenda) if they’re not in the same location/sense context as Alice.

This is slightly more complicated if you want to implement fidgets like this that can be triggered from elsewhere (that is, not from within the NPC’s own turn/actions). But it’s still relatively straightforward; you can do something like:

modify Actor
        fidget(msg, sense?) {
                callWithSenseContext(self, (sense ? sense : sight),
                        {: "<<msg>> " });
        }

So now alice.fidget('Alice clears her throat', sound); will behave the same way the example invokeItem() output did: Show the fidget message to the player if they’re in the same sense context as Alice, show them nothing otherwise.

Okay. With that all being said, is there a simpler way of doing this:

        fidgetNearAndFar(v0, v1, sense?) {
                if(!sense)
                        sense = sight;

                if(gPlayerChar.senseObj(sense, self).trans != opaque)
                        callWithSenseContext(self, sense, {: "<<v0>> " });
                else
                        callWithSenseContext(nil, nil, {: "<<v1>> "});
        }

That is, allow something like alice.fidgetNearAndFar('Alice sets off a firecracker.', 'In the distance you hear someone set off a firecracker.', sound);, which will output one message if the player is in the given sense context and a different one if they’re not in the sense context.

Technically this could be reduced to:

        fidgetNearAndFar(v0, v1, sense?) {
                if(gPlayerChar.senseObj((sense ? sense : sight), self).trans
                        != opaque)
                        "<<v0>> ";
                else
                        "<<v1>> ";
        }

…assuming that gPlayerChar.senseObj() will evaluate the context in exactly the same way as callWithSenseContext(), so doing the latter is probably redundant.

But more broadly I’m wondering if I’m missing some method(s) for causing objects to emit sense information in an ad-hoc manner (that is, as opposed to using something like a declared Noise instance or whatever).

Other than tinkering with SoundEvent and SoundObserver, I don’t know that Inhave anything to add.
But are you aware that you can give default values to function arguments like:
someFunction(arg1,sense=sight)
?
Then if they only supply 1, sense is left as sight, otherwise they can supply a second arg…

Yes, but I’m doing a lot of subclassing, and it’s easier to “lose” the defaults that way. E.g.:

modify Thing
        foo(v='bar') { return(v); }
;
class FooThing: Thing
        foo(v?) { return(inherited(v)); }
;

In this case FooThing.foo() will return nil; you’d have to re-set the default in the subclass if you wanted to preserve the behavior. On the other hand:

modify Thing
        foo(v?) { return(v ? v : 'bar'); }
;
class FooThing: Thing
        foo(v?) { return(inherited(v)); }
;

FooThing.foo() will return “bar”, same as Thing.foo().

1 Like

Gotcha, makes sense…