Most reliable and best way to move an actor to a random, adjacent, room?

I’ve been looking for hours but nothing on dynamically selecting a room. Thought I’d try again here, but is this possible?

1 Like

I am so sorry, I missed that this was TADS and not Inform 7. My bad!

This should be an easy one. I think if you’re going to spend time on TADS you really should read Learning TADS 3 though!
It will be something along the lines of

//
foreach (local dir in Direction.allDirections)
   local conn = npc.location.getTravelConnector(dir, npc);
  //add conns to a vector;

after you've built the list of possible adjacent connectors/rooms, you can do a nested travelVia on one of these connectors  via a random selection from the vector, or you could build a list of adjacent locations (not connectors, perhaps using getDestination() instead) in some similar manner and use scriptedTravelTo(rand(destinationList))

Also worth reading NPC Travel in the Tech Manual…
I know all that code was incomplete and kind of vague, but if you can’t work it out from there I can try to get more specific. I just don’t expect it’ll be too hard for you to get the details, especially if you read a couple of pertinent sections…

So what you’re saying is that I can’t just abuse the search bar…and intfiction
:smile: Alright yeah I’ll spend these next few days reading up on stuff. Thanks again!

Okay, I’ve taken a read through now, multiple guides and I have a good understanding of what I had to do…
I have this, and (I’ve read through and they all say (it seems) to maybe use reportAfter or beforeAction) they seem to move bob, yet the reports report bob’s position of where he WAS, not where he went to. This includes his specialDesc and automatically created ‘bob is in the east plaza’ due to sense connectors.

    afterAction() {
        // Random movement code.
        local conns = new Vector(Direction.allDirections.length()), conn = nil, dest = nil;
        foreach (local dir in Direction.allDirections) { // Loop through every direction.
            conn = self.location.getTravelConnector(dir, self); // Grab out connector
            if (conn != nil // There is an exit in this direction?
                && conn.isConnectorVisibleInDark(self.location, self)) // Can we see it?
            {
                dest = conn.getDestination(self.location, self); //Add the desitnation if it is.
                conns.append(dest);
            }
        }
        moveIntoForTravel(conns[rand(conns.length()) + 1]); // Move to a random one of these destinations.
    }

Again I’ve probably made a rookie mistake, it’s a bit late… but read through the entirety of making actors in Technical Manual and System Manual, but they only teach me how to position reports and such, not movement.

Anybody have ideas?

wait executeTurn might work, but it seems not to be picking up any connectors, resulting in an empty list and TADS crash…

Alright it was local scope issues with the for block… but I don’t know how to make variables global for that code block.

Okay, this is odd… does append not add to the Vector? Because that’s what locals are showing and it says index out of range, also does this with lists, using conns += dest as well.

executeTurn() {
        // Random movement code.
        local conns = new Vector(Direction.allDirections.length()), conn, dest;
        foreach (local dir in Direction.allDirections) { // Loop through every direction.
            conn = self.location.getTravelConnector(dir, self); 
            dest = conn.getApparentDestination(self.location, self); // Grab our connector, destination
            if(dest != nil && conn.isConnectorVisibleInDark(self.location, self))
            {
                conns.append(dest);
            }
        }
        moveIntoForTravel(conns[rand(conns.length())]); // Move to a random one of these destinations.
    }

So from this debugging, I found that the foreach method isn’t even running! I have ZERO idea why it would run everything BUT the foreach, and … yeah I need more help. I don’t know where to begin looking, and I don’t want to bug you but I’m genuinely stumped. This is fact highly similar to the exit lister code, yet it doesn’t want to work.

    idleTurn() {
        // Random movement code.
        local conns = new Vector(Direction.allDirections.length()), conn, dest;
        tadsSay('DEBUG!');
        foreach (local dir in Direction.allDirections) { // Loop through every direction.
            conn = self.location.getTravelConnector(dir, self); 
            dest = conn.getApparentDestination(self.location, self); // Grab our connector, destination
            tadsSay(dest);
            if(dest != nil && conn.isConnectorVisibleInDark(self.location, self))
            {
                conns.append(dest);
                tadsSay(dest);
            }
        }
        moveIntoForTravel(conns[rand(conns.length())]); // Move to a random one of these destinations.
    }

For one thing you probably want all of this type of code in an actor’s AgendaItem or else the takeTurn method of one of its states. Also, I think (I haven’t worked in-depth on NPC travel yet) you might need to use travelTo or scriptedTravelTo to get the normal departure messages… moveIntoForTravel is kind of like a magical whisking motion, not simulated travel.
I’ll see if I can find time to look at the code closer later… but you should at least read the section of Learning TADS 3 on NPCs… at least for now you can find the parts where they talk about travelAgendaItems and such…

I remembered this bit of code in some of my source. Hopefully I didn’t forget to copy any other pertinent lines that might have been elsewhere.

gA is my macro for getActor(), Gor is getOutermostRoom(), app is macro for append, AGEN is AgendaItem; iI is invokeItem(). As you can see this was for a character that would randomly move about 1/7th of turns.

  + npcMove: AGEN
	initiallyActive = true
	isReady = (rand(7)==0)
	iI { local loc = gA.Gor; local ac = gA; local options = new Vector(8);
		foreach (local dir in Direction.allDirections)
        		{ local conn = loc.(dir.dirProp); 
                        if(conn) { local dest = conn.getDestination(loc,ac);
				if(dest && dest != loc) options.app(dest); } }
		ac.scriptedTravelTo(rand(options));
// * * * and then I had this bit tacked on to change the actor's state based on the location he went to * * //
        local newLoc = ac.Gor;
		if(newLoc.propDefined(&uarState) && newLoc.uarState)
			ac.setCurState(newLoc.uarState);
		else ac.setCurState(uarGeneral); }

All the departure messages and special descs worked normally for this character…

If you want all kinds of details on NPC travel, I highly recommend looking at the source code for “It”…

Thanks, John. I was thinking it might need to be in an AgendaItem. I referenced my code towards the exit listers… And yes, LT3 on NPCs… you keep referring code there, yet I don’t find what code you’re copying. I read that, and on NPC travel it says much the same as every other manual – not much. As for source code… I’m, uh… ‘still in school’ per say, and I don’t think I can take a look at ‘It’. But thanks. Also, do you know why the foreach doesn’t run in idle/executeTurn, before/afterAction? I’m still confused why that happened.

Also what is ‘uarGeneral’?

It’s possible the randomizing behavior I’m thinking of was something I saw in “It”. I realize the LT3 bit about travelAgendaItems performs a “route” of travels down a list of destinations. The “uarGeneral” and other stuff wasn’t important, that is just an actorState specific to one of my characters. I’ll try (again, if/when time permits) to look at your code and see what was up…
Did you try more or less copying my code, and seeing if it works?

Hey… first, as you may know by now, executeTurn is definitely not the place to be overriding stuff like this, there’s low-level library behavior going on there. idleTurn can work, but you probably would want to call inherited() first, because it deals with an NPC finishing conversations. takeTurn() on the actor’s state makes more sense, or else using an AgendaItem.
As far as I can tell your problem was using getApparentDestination instead of using getDestination. getApparentDest returns nil if your character hasn’t seen the location before, so your vector was built up of a bunch of nil values. Also, as mentioned, using moveIntoForTravel was what was suppressing your travel messages.
So you could pretty much use your code as it was, changing that, but if you put the code in takeTurn (of an ActorState) or an AgendaItem, note that you have to use getActor().location (or perhaps more safely, getActor().getOutermostRoom()) rather than self.location. Small thing, in TADS you don’t have to be explicit about self.location, you can just type location. Location will refer to the location property of whatever object is defining the method you’re working within. Also a bit simpler, rather than conns[rand(conns.length())], you can simply type rand(conns) and it will select one (if the argument is of a collection type). Finally, you don’t have to use tadsSay, you can use say(‘string’); or just “string”;
Hope that helps…

Alright. Yes, I took your code and used it to refine mine and it works! Thank you for those tips, and now I can make things a bit cleaner elsewhere as well. I want to mention as well that using getDestination() didn’t work either, for your other reasons. Using an AgendaItem seems like the safest way, and the easiest to change specific to Actors if need be. Thanks again! Now, I think I have the hardest part of what I’m trying to do out of the way…

Hmm, getDestination worked for me… this is basically your code with getApparentDestination changed, and moveIntoForTravel changed, and it worked…

idleTurn() { inherited();
        local conns = new Vector(Direction.allDirections.length()), conn, dest;
        foreach (local dir in Direction.allDirections) { 
            conn = location.getTravelConnector(dir, self); 
            dest = conn.getDestination(location, self);
            if(dest != nil && conn.isConnectorVisibleInDark(self.location, self))
            {
                conns.append(dest);
            }
        }
        scriptedTravelTo(rand(conns));
    }

But glad to hear that things are going better…