TADS 3 : Silence spell. *Resolved* Thanks to bcressey

The silence spell in my game, as well as a few other spells, work on the player by giving him/her a “hidden” object. (Actually it’s an object derived from a specialized class.) Since it’s hidden and moved into the players inv automatically, it never becomes discovered and thus can’t be dropped or anything. Be that as it may…

The hidden spell stops the player from speaking by catching the various speach actions in the spell/objects “BeforeAction” … er… structure. (Method?) Whatever. Most of this works fine. It all works off of the following code snippet:

     beforeAction()
    {
        if (!(self.location == gPlayerChar)) exit; 
        if (gActionIs(Yell) || gActionIs(ConvI) || gActionIs(TopicT))
        { 
            say(cantSpeakMsg); 
            exit; 
        };
        
       
    }

The problem is this, I can’t get it to catch commands to actors. You type yell, You get the “Magic stops you from speaking.” message. You Type “Tell Bob about McGuffin.” you get the “Magic stops you from speaking.” message. But it you type “Tell bob to go North” you either get the standard “Bob refuses your request” message or off Bob goes, depending on if he’s set to accept commands or not. :cry:

The library reference lists a “CommandActor” action type. (I found the other action type references in the library reference manual.) However this action type does not respond the way the others do. I just get an error when I use it.

Any help or hints with this would be greatly appreciated.
Thanks in advance. :smiley:

  • Selva

You may get more mileage out of adjusting handleConversation() in ActorState. That would let you test the condition in one place and avoid the need to modify all of the conversation actions - ASK, TELL, HELLO, etc.

Commands to the actor get routed to handleConversation() as well, though I’m not quite sure how that process works. So this should do the trick:

modify ActorState
    handleConversation(otherActor, topic, convType)
    {
        if (gPlayerChar.silenced())
        {
            "Magic stops you from speaking. ";
            return;
        }
        inherited(otherActor, topic, convType);
    }
;

Thanks bcressey. However, while that would work, I’m really trying to keep the code encapsulated in the “spell” object/class as much as possible. Your solution, if I understand it correctly, would require entering modification into all actors in the game, something I’m trying to avoid.

Well, you can make the one modification to the core ActorState class, and every instance of it will inherit that change. From a technical standpoint, you’re correct that you’re adding the code to every actor. But from a source code file standpoint, it only has to be added once.

Another place to perform the modification would be in the player character’s canTalkTo() method. This has the virtue of not subverting the sense passing aspects of the world model, but the drawback that certain actions (like YELL) don’t respect it.

In order to do it your way, I think you would need to call the addBeforeAfterObj() on the CommandActorAction object, passing your silence object as the parameter. It looks like this initializes a persistent vector - beforeAfterObjs - so you shouldn’t need to do this more than once.

If you could give me a basic example of how to do these it would be most helpful and I would greatly appreciate it… My experience with this is… uneven.

You can create a class that inherits from ActorState and implement the desired behavior there. Then, use that new class instead of ActorState for every case you want the new behavior. This is the standard (I’d even say intended) way of doing this in object oriented languages.

I gave up on modifying the CommandActorAction - it’s not really good for anything except a placeholder. Commands from the player to NPCs are transformed into NPC actions during execution, and beforeAction() isn’t used for NPCs.

Consequently I don’t think you can do what you want with the spell object alone. I worked through a variety of approaches and came up with this one.

modify Actor
    acceptCommand(issuingActor)
    {
        if (issuingActor != self && issuingActor.silenced())
        {
            reportFailure(&cantSpeakMsg);
            return nil;
        }
        return inherited(issuingActor);
    }
    silenced()
    {
        return (silenceSpell.location == self);
    }
;

canSpeak: PreCondition
    checkPreCondition(obj, allowImplicit)
    {
        if (gActor.silenced())
        {
            reportFailure(&cantSpeakMsg);
            exit;
        }
        return nil;
    }
;

modify ConvIAction
    preCond = [canSpeak]
;

modify TopicTAction
    preCond = [canSpeak]
;

modify YellAction
    preCond = [canSpeak]
;

modify playerActionMessages
    cantSpeakMsg = 'Magic prevents {you/him} from speaking. ';
;

It creates a new precondition to check whether the actor is silenced, which depends in turn on whether the actor carries the silenceSpell object. Overriding acceptCommand() on Actor prevents the player from issuing commands while silenced.

@bcressey Thank you so much!!! That works perfectly!!! I have been pulling my hair out for a week trying to get this to work right. Thank you!