ActorStates in Tads3

Hi guys! I’m working on my second major game in Tads3, and the actorstates are giving me nothing but grief. I have what is basically an escort quest with an NPC who reacts violently to a given substance, but the trouble is, he’s not really reacting at all. :frowning:

First it just wouldn’t leave the initial actor state, no matter what I did. Then when I omitted the initialState property, it didn’t choose any state at all! o_O

I just can’t figure out how to make the states change. I must have poured through the tutorials over and over. I tried the isActive property, agendaItems for setCurState, even both at the same time! The properties in the actor himself operate correctly, but the actorStates…they just…aAARRGGHHH!!!

Can you guys help me work this out? :cry:

Hi QueenoftheCapes!

Yep, that’s what isInitState does. Set it to true on the state you want the containing actor to use from the start of the game. :wink:

At the point you want your actor to assume a certain ActorState, you need to call setCurState(whatever) - where ‘whatever’ is the new actor state you want to use.

Try doing this for a very simple case - for example, put it into the topicResponse of a particular AskTellTopic - and you should find that it works. If it does, then I suspect the issue has more to do with how exactly you’re handling this part:

If you’re able to set actor states in the simplest case, then perhaps you can go into what you specifically want to happen here and how you’ve tried to go about it.

Ah, thank you! :slight_smile: Okay, I’m testing the basics now.

In the mean time, what I’m trying to do is cause state to change whenever he canSee(objectinquestion)

like for example:

vampire : Actor
;

+cringing : ActorState
“He looks pretty upset”
isActive = vampire.canSee(cross)
;

+standing : ActorState
“He is standing in an example question, making faces.”
isActive = !vampire.canSee(cross)
isInitState = true
;

That’s basically what I’ve been trying to do, but with no results. I’m reluctant to try placing the setCurState thingy in an action(){} for anything, because there are theoretically a million ways a cross could be hidden away or revealed.
Am I missing something here? :frowning:

I think your best bet is to check each turn to see if the vampire has seen a cross, and change the state accordingly. There are quite a few different ways to go about this. Inside the actor itself, afterTravel can run checks immediately on entering a new location (if, say, you want the vampire’s description in the room to immediately appear as something like, “The vampire looks very upset.”) and idleTurn runs every turn that the vampire doesn’t take an action (but if you override idleTurn be sure to add ‘inherited;’ to the method, or the whole TADS Workbench will crash).

Within actorStates, takeTurn seems to do the same things as idleTurn (without the crashing), while afterAction runs every turn that the actor sees an action occuring (whether that action is taken by the player, the vampire, or yet another actor).

I’ve messed about with stuff like this before (and I probably will again), so I tried a little bit of experimental coding… :wink:

gameMain: GameMainDef
    /* the initial player character is 'me' */
    initialPlayerChar = me
;

startRoom: Room 'Start Room'
    "This is the starting room. "
    north=nextRoom
;

+hidingPlace: Container 'box' 'box';

++cross: Thing, Hidden 'cross' 'cross';

+ me: Actor;

+vampire: Person 'vampire' 'vampire'
    canSeeCross=(vampire.canSee(cross) || vampire.canSee(cross2))
    crossCheck()
    {
        if(canSeeCross && vampire.curState!=vampireCringing)
        {
            "<.p>The vampire hisses. <q>That cross! Hideous thing!</q>";
            setCurState(vampireCringing);  //set vampire to cringing state
        }
        else if(!canSeeCross && vampire.curState!=vampireStanding)
        {
            "<.p>The vampire relaxes. <q>The ambience is much nicer all of a sudden.</q>";
            setCurState(vampireStanding);  //return vampire to normal state
        }            
    }
    idleTurn()  //called every turn by actor, unless it takes an action
    {
        inherited; //this does important stuff which we mustn't override
        vampire.crossCheck();
    }
;

++vampireCringing : AccompanyingState
    specialDesc="The vampire seems very upset. "
    accompanyTravel(leadActor, conn)  
    { 
        return leadActor == gPlayerChar; 
    }
    afterAction()  //called every turn someone takes an action, if the actor is in this actorState
    {
        vampire.crossCheck();
    }
;

++vampireStanding : AccompanyingState
    specialDesc="The vampire is here, elegant and darkly dressed."
    isInitState = true
    accompanyTravel(leadActor, conn)  
    { 
        return leadActor == gPlayerChar; 
    }
    afterAction()
    {
        vampire.crossCheck();
    }
;

//////////////////////NEXT ROOM//////////////////

nextRoom: Room 'Next Room'
    south=startRoom
;

+cross2: Thing 'cross' 'cross'
;

This is only one possible (probably inelegant) solution. You could break up the two checks (can I see a cross/can I NOT see a cross) into the two different actorStates, for example, and remove the idleTurn stuff entirely. Then the vampire will only notice a cross in plain sight (or the absence of a cross) on the first actual turn after you enter a location. It all depends on the messages you write.

It’d also be nice to have like a vampireRepellant mix-in class that you could add to any crosses, garlic, rosaries, wolfsbane etc that you happen to implement. For that you could probably do a check like so:

        //NB. I've not tested this code in its current form
        local seenRepellant = nil;
        
        foreach(local cur in vampire.location.contents)
        {
            if(cur.ofKind(vampireRepellant))
            {
                seenRepellant = cur;
                return seenRepellant; //return that item
            }
        }

Which would also allow you to get the name of the item the vampire was worried about and stick it into messages. Again, there’s probably a better way to do that, but unless you’re doing anything really CPU intensive with your game, it would work.

For most of this stuff, I’ve referred to the TADS 3 Tour Guide. I know there’s lots of different manuals and tutorials, but that’s the one I look at the most often.

Hope this helps. :smiley:

Thank you so much!! :smiley:
I’ll try that. Thanks, guys! :slight_smile:

Edit: It works! You people are beautiful! Thank you! =D