Nil Object Reference Error with NPC

Hi! I’ve got a problem that it seems a lot of people here have, but none of those threads help.

My game so far is basic - Four roads meeting to one point, a guy called Michael in that point. He’s supposed to ask you something that you have to reply yes or no to. I get a nil object reference error with a yellow arrow pointing to the line inConvState.handleConversation(otherActor, topic, convType); in the actor.t file.

The comment above it said that “it wasn’t a greeting”. I tried to figure it out by saying “> hello” as a command. It returned the same error but with the yellow arrow at curState.suggestTopicsFor(actor, explicit);. I’m stuck. What am I doing wrong?

This is Michael’s code, by the way.

Michael: Person 'tall young man*men/guy/friend/person*people' 'Michael'
    location = tIntersectionMiddle
    desc = "He's your friend."
    isHim = true
    isProperName = true
;

+ blackTShirt: Wearable 'black tshirt/t-shirt/shirt' 'black t-shirt'
    "It's just a black t-shirt."
    isWornBy = Michael
;

+ blueJeans: Wearable 'blue denim jeans/trousers/pants' 'jeans'
    "It's a bit worse for wear but he's fine with it."
    isWornBy = Michael
;

+ michaelWaiting: ConversationReadyState
    isInitState = true
    
    afterTravel(traveller, connector) {
        inherited(traveller, connector);
        
        if (traveller == me) {
            "Michael turns to face you. \b <q>Did you get it?</q> he whispers to you.";
            Michael.initiateConversation(nil, 'got-it');
        }
    }
;

+ ConvNode 'got-it'
;

++ YesTopic, SuggestedYesTopic
    "You nod. <q>Of course.</q> you say. \b You see a glint in Michael's eye. <q>Okay. Follow me.</q>"
;

++ NoTopic, StopEventList
    [
    '<q>Nope. I didn\'t.</q> you tell him. \b Michael frowns. <q>No, I\'m pretty sure you got it.<q>',
        '<q>No, seriously, I don\'t have it.</q> Now Michael\'s annoyed. <q>Look, I know you have it, alright? I just need you to confirm.</q>',
        'You don\'t want to lie anymore. You actually do have it in your bag.'
    ]
    
;

Not sure if you’ll need the stack, but here it is anyway.

Michael.suggestTopicsFor(me, nil) + 0xF
conversationManager.showOrScheduleTopicInventory(Michael, me) + 0x3A
michaelWaiting.handleConversation(me, helloTopicObj, helloConvType) + 0x26
me.sayToActor(Michael, helloTopicObj, helloConvType) + 0x53
me.sayHello(me) + 0x19
HelloAction [3687].execAction() + 0x1D
Action [3687].doActionOnce() + 0xB5
IAction [3687].doActionMain() + 0xD
Action [3687].doAction(me, me, nil, nil) + 0x7A
executeAction(me, nil, me, nil, obj#3687 (predicate(Hello))) + 0x10A
func#2c9b5() + 0x30
withCommandTranscript(CommandTranscript, obj#367e (AnonFuncPtr)) + 0x25
executeCommand(me, me, [['hello',tokWord,'hello']], true) + 0x2C2
PendingCommandToks [3691].executePending(me) + 0x1D
me.executeActorTurn() + 0xD4
func#260c4() + 0x17
senseContext.withSenseContext(nil, sight, obj#3942 (AnonFuncPtr)) + 0x1D
callWithSenseContext(nil, sight, obj#3942 (AnonFuncPtr)) + 0x15
func#26105() + 0x3F
withCommandTranscript(CommandTranscript, obj#3947 (AnonFuncPtr)) + 0x49
withActionEnv(EventAction, me, obj#3947 (AnonFuncPtr)) + 0x3C
me.executeTurn() + 0x30
runScheduler() + 0xB6
runGame(true) + 0x2C
gameMain.newGame() + 0x1B
mainCommon(&newGame) + 0x4D
main(['debug\\A Thief\'s Night.t3']) + 0x1B
flexcall(main, ['debug\\A Thief\'s Night.t3']) + 0x59
_mainCommon(['debug\\A Thief\'s Night.t3'], nil) + 0x53
_main(['debug\\A Thief\'s Night.t3']) + 0x12
1 Like

Hi!

The nil object reference happens because the library assumes the InConversationState is defined and tries to invoke the handleConversation on such an object.

Reading up a little in the manual it seems like the ConversationReadyState is dependent on InConversationState, namely that they should go be seen as pair since ConversationReadyState will do the switch to InConversationNode and back again when going in and out of conversation mode.

This relation can be defined implicitly by nestling the ConversationReadyState as a child to the InConversationState.

e.g:

+ michaelTalking: InConversationState;
	
++ michaelWaiting: ConversationReadyState
    isInitState = true

    afterTravel(traveller, connector) {
        inherited(traveller, connector);
        
        if (traveller == me) {
			"Michael turns to face you. \b <q>Did you get it?</q> he whispers to you.";
			Michael.initiateConversation(nil, 'got-it');	

        }
    }
;

See 14.6 Hello and Goodbye – Greeting Protocols in Learning Tads 3 for more information.

3 Likes

(The manual also suggests that it is the safest to run an initiateConversation from an ConvAgendaItem object. It didn’t help in this case though.)

(See 14.9 Making NPCs Initiate Conversation)

1 Like

Ah, perfect, thanks! I feel a bit bad about that, I skimmed through that part of the manual but read everything else. Thanks for taking the time to help.

It was actually a good question. It wasn’t obvious and I’m sure others have missed it too.

I think it would be more useful if the library threw a meaningful exception here instead, saying one need to set the property InConvState with an InConversationState rather than assume it is there, and as consequence throw a nullpointer. I find it too easy as a user to assume that the different ActorStates are all independent.

1 Like

I thought so too. All the actor states outlined in Learning T3 are written beside each other, to be used as parallel states. I just assumed that the ConversationReadyState and InConversationState states are supposed to be parallel too. I wonder why TADS is designed that way.

Perhaps just a case where even Homer nods :slight_smile: Not sure if that particular design is prevalent in the library? I think both the vm and the library is a piece of art in general but I’ve also been mostly studying advLite lately so haven’t been subjected to the std library for a while. I guess a narrow user base and few reports about it is part of it. Not sure anyone works with maintenance any longer?

I wonder if it would be a good idea to have a user “community edition” of the library on a github repo somewhere where things like this could be (carefully) addressed? I believe that needs consent though due to the license but not sure.

Otherwise perhaps a helper extension could be useful. Something that scans the current code and warns the user during compilation or right at the start of the game if it finds issues like this. That would actually be really helpful. A system like that is somewhat there already in adv3Lite where it warns about inconsistent quotation marks and missing &otherSide on doors etc…

2 Likes