Bound and gagged! Immobilizing PC and NPCs

Just went through a clumsy, drawn out exercise to implement immobilizing a character. Since it was a verb-by-verb slog, figured I’d ask if anyone else found a more elegant way than brute force. Or if not, offer it up to posterity to save time for someone later. The scenario is being tied up, and maybe gagged. Here is my code:

modify Actor
    gagged = nil
    immobilized = nil
    reachableWhileBound = []  // can override on instantiation or set programmatically
    cantMoveMsg = '{You/he} {are} unable to move and cannot.  '
    cantTalkMsg = '{You/he} {are} unable to speak and cannot.  '

    beforeAction() {
        if ((gActor == self) && immobilized) failCheckImmobilized();  
        if ((gActor == self) && gagged) failCheckGagged();
        inherited;
    }

    failCheckImmobilized() {
        //  these actions are ok, especially system actions
        //  see below for >EXAMINE
        //
        if ((!gActionIn(Again, Inventory, ListenTo, Look, Sleep, Smell, Wait, System, SenseImplicit))

        // conversation ok if only bound
        //
            && (!gActionIn(AskVague, TalkTo, TellVague, Yell, ConvTopicT, ConvI))

       // uncomment if want to be able to feel bindings or whatever
       // could add 'Untie' or other actions here also
       //
       //     && (!gActionIn(Feel) || (reachableWhileBound.indexOf(gDobj) == nil))

       // assuming inventory still on actor, and restraints prevent self examine
       // would need to make more elaborate if environment is complex
       //
            && (!gActionIs(Examine) || gDobj.isIn(self)))
                 failCheck(cantMoveMsg);
    }

    // prevent conversation if gagged
    //
    failCheckGagged() {
        if (gActionIn(AskVague, TalkTo, TellVague, Yell, ConvTopicT, ConvI))
             failCheck(cantTalkMsg);
    }

Obviously, would need to inventory game-specific verbs to add to lists as appropriate. This has been tested let’s say a medium amount, so hoping I’ve wrung out the corner cases.

6 Likes

Mine is minimal, but it’s the same idea teaming up beforeAction with gActionIn
My only thought about streamlining would be, if you were starting a game from scratch and planning on making lots of new verbs, you could tag those new Actions with a property failIfBound = true or something, so that the beforeAction routine can also check || gAction.failifBound, etc. rather than having to have a comprehensive list of verbs. But it’s probably not saving any coding, just maybe a little clearer to manage on a larger scale.
2¢ tossed…

2 Likes

Just what the doctor asks after the IFComp work…

my major WIP involves also, let’s call those “the sweetest of bounds and/or gagging”, and the full implementation of one of those sweet scene is at the top of the TODO list. JJ’s idea of catch-all routine filtering all action implying movement or talking during those sweet moments coupled with Zieg’s idea of a new action property (and modifies to library actions…) seems the right answer to my unusual coding problem.

Because I use a3Lite, the sweet moments are scenes, things can be even easier…

congrats to both:

[You have gained a place in the CREDITS]

Best regards from Italy,
dott. Piergiorgio.

5 Likes

I am delighted that within 12hrs of posting some work I did, SOMEONE MADE PORN OF IT. Right on pace, internet!

Lol, happy it was useful. It was verb-by-verb drudgery, glad my pain helped someone else save the time!

3 Likes

Tomorrow I’ll reply to you.

2 Likes

As promised, my reply.

I don’t like the caps part of the comment, but I recognise that the wording I have used can be construed as reference to a genre of erotica I’m absolutely not interested in it, hence the oversight in choice of words.

to put in explicit terms, what I refer is the bounds of a deep hug and the gag of a french kiss: Nothing porn, I think even in US perspective, IMVHO.

I’m still thinking how to explain the scene w/o spoilers, but for now let’s point that the entire narration is from an intimistic perspective in a context of a very unique situation whose require gradual knowledge and understanding of said situation. A situation in where, trust me, the lock in deep hugging & kissing makes sense, and IS a major puzzle.

Best regards from Italy,
dott. Piergiorgio.

5 Likes

Will reiterate my apology publicly. No offense intended. My wisecracks are SO much faster than my common sense. Your work is probably ALREADY more mature than me. :grimacing:

4 Likes

The way I’ve been approaching this general class of problem (situationally making large-scale changes to the allowed actions) is via a general “scene” system. Short version:

  • Create a global singleton as the scene controller, let’s call it sceneController
  • Modify the constructor for Action via something like:
     modify Action
             construct() {
                     inherited();
                     addBeforeAfterObj(sceneController);
             }
     ;
  • The scene controller gets a method for letting objects subscribe to notifications. When the controller’s beforeAction() and afterAction() methods are called, the controller goes through the subscriber list, calling a method (I use globalBeforeAction() and globalAfterAction) on the subscriber
  • I provide a class (BeforeAfterThing) whose instances get auto-subscribed at pre-init
  • I also have a scene class, with BeforeAfterThing as a mixin, whose globalBeforeAction() and globalAfterAction() methods first call a isActive() on the scene object, immediately returning if it’s not true. If it is true, then it calls sceneBeforeAction() or sceneAfterAction() as appropriate.

The individual scene instances can then implement whatever action-preempting behavior you need. For convenience I use two base classes: one that allows all actions except for those defined in a list, and one that blocks all actions except for those defined in a list. I’ve also got some generic action “classes” that can be checked for (sense actions, movement actions, and that kind of thing).

4 Likes

after the little divagation, jbg’s idea is somewhat similar to my ideas for a “dungeon master” discreetly steering the PC (and player) toward the “right spot”. That is, a concept similar to bounding and gagging the PC, but allowing more freedom of action along the path toward the right spot, somewhat replacing the GuidedTour NPC mechanism, missing in a3Lite.

Let’s say that the PC accrues knowledge until he realise a major, shocking, realisation and needs the classical “fresh air on a balcony”, where the real turning point start. Problem, the threshold can be fired in multiple locations, so using barriers is not only clumsy to code, but can lead to serious issues, up to and including the dreaded “unwinnable state”.

So the interesting problem is that not only that the PC is bound on a path with multiple entry points, also the player should be encouraged to follow it, not forced into it. (hence my calling “Dungeon Master”), tentatively an invisible NPC follower, with its appropriate reactBefore/reactAfter along the path.

of course, the “dungeon master” can handle bounding & gagging, but honestly, in the “sweet case” of hugging & kissing, I feel that I’m coding an interloper, not precisely an happy coding…

BTW, if bounding & gagging, whatever type, is a work tailored for ALAN 3 and library 220ß, whose not only has this:



EVENT tied_up
  "Suddenly you're interrupted. A couple of crooks enter the room,
   grab hold of you, push you into a chair, gag you and tie you
   into it tightly. You cannot move your arms or legs."
  MAKE my_game NOT attack.
  MAKE my_game NOT attack_with.
  MAKE my_game NOT bite.
  MAKE my_game NOT break.
  MAKE my_game NOT burn.
  MAKE my_game NOT burn_with.
  ...
END EVENT.

and has also even a very nifty “restricted level”, by far the best restricting actions of all IF language, bounding and gagging are implemented with exactly
one instruction:



EVENT tied_up
  "Suddenly your investigations are interrupted.
   A couple of crooks enter the room, grab hold of you, push
   you sitting on a chair and tie you into it tightly.
   You cannot move your arms or legs."
  SET restricted_level OF my_game TO 2. -- all action verbs will be disabled
  MAKE my_game rub.                     -- but 'rub' will work
END EVENT.

(cfr. Alan Standard Library v2.2.0 User’s Manual, ch. 18.3)

so, the ideal should be a contrib library implementing this restricted_level, or better, adding this to the optionals of adv3Lite…

Now, and JJ is allowed to wisecrack :wink: , if someone want really badly to code a BDSM AIF, s/he now know what IF language to use :smiley:

Best regrds from Italy,
dott. Piergiorgio.

2 Likes

I don’t know how relevant it is to everything that contributors to this thread might want to achieve, but it may be worth pointing out that adv3Lite defines an actorAction() method on the actor (normally the Player Character) which could be used to veto certain actions, for example:

actorAction()
{
        if(gActionIs(Jump))
        {
            "You are rooted to the spot." ;
            exit;
        }
 }
3 Likes

Adv3 also defines actorAction, which is in fact where I put my version of the code, rather than beforeAction… Off the top of my head I can’t think of a strong reason why they differ, other than that actorAction isn’t called till all scope objects run their beforeAction, in case that matters.

In my case I ended up using a beforeAction() and afterAction() approach because Action provides the addBeforeAfterObj() mechanism for adding objects to the action’s notification list independent of scope. And I use it to add a singleton (instead of adding individual actors directly) beause Action doesn’t provide a way of removing objects after they’ve been added via addBeforeAfterObj().

I considered just adding an Action.removeBeforeAfterObj() method (internally Action just keeps track of them in a Vector and there’s no fancy bookeeping). But I think I prefer breaking the elaborate situational logic out into its own thing. Mostly because most of the scene-like things I have are triggerable by either the player or NPCs. So baking it all into a method on Actor (or on a subclass) feels like something that would get out of hand.

2 Likes

I’m back again at tackling the issue of the long list of verb to gag or bound :wink: that is, for the far shorter case of gagging:

//Alan lib 3.20 restriction lev. 1: can't talk (as in being french-kissed..)
class lists: object //lists container
l1rest = [AskAbout, AskAboutImplicit, AskFor, AskForImplicit, Query, 
QueryAbout, QueryVague, SayAction, SayTo, ShowTo, ShowToImplicit, TalkAbout, 
TalkAboutImplicit, TalkTo, TellAbout, TellAboutImplicit, TellTo, Yell]
l2rest = [Attach, AttachTo, Attack, AttackWith, Board, Break, Burn, BurnWith, 
Clean, CleanWith] // partial 4 check
;
[in the test room code]

// testing the "gag order"
roomBeforeAction () {
  foreach (obj in lists.l1rest) {
 if (gActionIs(obj))
 { "You're alone here, but you can THINK, I think...";
   exit;
 }
  } // 1st foreach

 foreach (obj in lists.l2rest) {
 if (gActionIs(obj))
 { "I think I'm barred from attaching, attacking, boarding, breaking,  
burning and cleaning... let's see if IS true...";
   abort;
 }
} // 2nd foreach
} // roomB4Action
;

with the rather disappointing result that only SAY [something] and YELL works as expected. ALL other command output always the default answer…

some idea about this issue ? I prefer to avoid rather long (and hard to read (and debug…)) gActionIn (verb1 ,verb2, …verbN)

Best regards from Italy,
dott. Piergiorgio

1 Like

Did not look up anything Lite-related before making this answer:
Are you getting the typical default responses that are triggered by illogical in a verify routine?
Because roomBeforeAction will not be called unless a prospective action has already passed verify() and is trying to proceed through check() and action(). Perhaps a Doer would kick in before the verify responses? Or perhaps I don’t have the picture of what’s happening here.

Don’t worry about not looking up on Lite-related.

Just checked, setting the beforeRunsBeforeCheck true, the issue is above the check stage. This confirm that the issue is in verify(), and in turn illogical() preempting ALL befores. That is, a big obstacle, both in adv3 and a3Lite.

Thanks for the nudge. no haste, I have time roughly until 2025, when the (peculiar) bound/gag-related coding will matter.

Best regards from Italy,
dott. Piergiorgio.

1 Like

As John suggests, the answer here is to use a Doer. Here’s a trivial example I just tested which works as I think you want:


+ Doer 'take Thing'
    execAction(c)
    {
        "Your arm is temporarily paralysed, so you can\'t. ";
        exit;
    }    
    where = hall
;

And here’s a brief transcript from it:

Hall
From here stairs lead up to the floor above while the front door lies to the south. 
A passage leads north, and another room lies just to the east. 

You can see a wallet, a hat, an umbrella, a mobile phone, and a small table here.

On the small table you see a big red book.

>up


Landing
Stairs lead back down to the hall. An oak door leads west. 

>take stairs

The stairs are fixed in place. 

>d

Hall
From here stairs lead up to the floor above while the front door lies to the south.
A passage leads north, and another room lies just to the east. 

You can see a wallet, a hat, an umbrella, a mobile phone, and a small table here.

On the small table you see a big red book.

>take stairs

Your arm is temporarily paralysed, so you can’t. 
1 Like

hmmm… well, this can be a worthwile effort, doers seems to be the least understood feature of a3Lite, and the sheer majority of these exigencies are either during scenes or during an optional part of the story, where the player control a sort of hologram, and is limited to sense-related actions, without manipulating the environment (yes, back then I liked L9’s Scapeghost’s unusual PC and puzzles…)

Thanks for the interesting suggestion, and
Best regards from Italy,
dott. Piergiorgio.

For the next release of adv3Lite I’m adding a preAction(lst) method which will be called on the actor just prior to the execution of any Doers. This will provide an easier way to veto any action before any verify stage messages are displayed. The example I’ve just tested (and which seems to work okay) looks like this:

+ me: Player 'you'   
    isFixed = true       
    person = 2  // change to 1 for a first-person game
    contType = Carrier    
    
    preAction(lst)
    {
        local action = lst[1];
        
        if(action == Think && isTiedUp)
        {
            "Thinking sets you free!<.p>";
            isTiedUp = nil;
        }
        
        if(action.ofKind(SystemAction) || !isTiedUp)
            return;
         
        "You can't do that while you're tied up. ";
        abort;      
    }
    
    isTiedUp = true         
;

You could obviously do something more elaborate here, such as testing for the location or the occurrence of a Scene or any other condition, as well as allowing/vetoing any action you like here. but of course you’d need the other couple of tweaks to the library to make this work.

One of these would just be:

modify Thing
   preAction(lst) { }
;

The other would be:

modify Command
    execIter(lst)
    {
       
        try
        {      
            /* 
             *   Give our actor's preAction method the chance to veto this action (or maybe do
             *   something else) before it's passed to a Doer.
             */
            gActor.preAction(lst);
            
            /* carry out the default action processing */            
            execDoer(lst);
        }
        catch (ExitSignal ex)
        {
        }
        
        finally
        {
            /* 
             *   If the action isn't one this Command has executed before while
             *   iterating over its list of objects, note that we've now
             *   executed it
             */
            if(actions.indexOf(action) == nil)
                actions += action;
            
            /*  Restore the original action */
            action = originalAction;
        }
    }

``
2 Likes

no haste… that WIP’s final build is planned late in 2026 :smiley:

Anyway, there’s another pair of ideas I like to submit, but I prefer to think about prior of submitting them in private.

Best regards from Italy,
dott. Piergiorgio.

The preAction seems like a nice feature!

1 Like