NPC random pathfinding

So I'm building a game at the moment and have a cleaner droid who moves randomly from room to room among a set range.

I'm quite deliberately moving the object rather than getting it to use exits, simulating the fact that the little fella is using service hatches that the player is too large to fit through, so that it can 'clean' (ie pick up objects under a certain volume left around and drop them off in a specific 'bin' object) rooms that are locked. Note: I'm just working on the pathfinding at the moment and will get to the cleaning scripts later.

So, the game runs this at gamestart:

r21apath = NewObjectList ()
list add = (r21apath, aviary)
list add = (r21apath, courtyard)
list add = (r21apath, dark hall)
list add = (r21apath, the cells)
list add = (r21apath, dungeon)

As well as starting an 8-second BotTimer.

Our little r21a, the cleaner, starts in the courtyard. The courtyard is the third room the player ever enters, so the droid doesn't do anything, in order to guarantee you see it.

But the first time you look at r21a, it sets the BotTimer to the following script :

n = GetRandomInt (0, 4)
if (r21a.parent = player.parent) {
	msg ("r21a leaves " + r21a.parent + "")
r21a.parent = ObjectListItem(r21apath, n) 
if (r21a.parent = player.parent) {
	msg ("r21a enters " + r21a.parent + "")

Please note I've been busy at work and have been writing this on my phone and haven't really had the chance to put the code into Quest yet.

So my questions (finally), to those coders more proficient than I, are:

  1. Does this look workable?

  2. Would there be a DRY way to implement this as a function? I'd imagine something along the lines of giving the cleaner and other pathfinders a 'pathfinder' flag, running a foreach at game-start to create a list named after each pathfinder. Can you create a list and have its name decided by a script? Could a pathfinder have its visitable rooms set as attributes and then the gamestart foreach could fetch those details to add to the list?

  3. Help?

Looks good...
And a good idea about a function...
And if the function, or functions includes a map, you could expand where the droid goes...
Like this:
(not real code)
function Droid_move()
If r21a.parent =aviary {
r21apath=("north room, east room,courtyard", ",")
if r21a.parent =Courtyard{
r21apath=("aviary,dark hall,the cells,dungeon", ",")
if r21a.parent =dark hall {
r21apath=("room1,room2,room3,room4,room5,courtyard", ",")
then do this for every room the droid can go to and it allows you to control where it goes.
(someone else will give you a ForEachExit search thingy that will do the same...)

Then you random selected room from the r21apath list

One thing to watch out for is spaces...
r21apath=("room1,room2,room3,room4,room5,courtyard", ",")
is not the same as
r21apath=("room1, room2, room3, room4, room5, courtyard", ",")
Quest sees the space in front of the room names in the list and tells you that room1 is not the same as room1...
(Took a while to find that one!!!)

r21apath needs to be attached to an object. Personally, I would give the r21a object an attribute called "path", so it is r21a.path. This answers your point 2; just check if an object has a path attribute.

You could put all the rooms r21a can visit inside another room (one the player cannot access), representing the zone. Then (supposing it is called "cleaning zone"):

r21a.path = GetDirectChildren(cleaning zone)

And you could use PickOneObject to select the next room. Also check if the room actually changed. The then could be (assuming all rooms have aliases).

newRoom = PickOneObject (r21a.path)
if (not newRoom = r21a.parent) {
  if (r21a.parent = player.parent) {
    msg ("r21a leaves " + r21a.parent.alias + ".")
  if (newRoom  = player.parent) {
    msg ("r21a enters " + r21a.parent.alias + ".")
  r21a.parent = newRoom 

You could easily put that in a function, just change "r21a" for "npc", and have "npc" as a parameter.

More advanced... If you are using the desktop version, the best way to make it DRY is using types. Have the above as a script on the "robot type", and change "r21a" for "this" ("this" indicates the thing the script is attached to), called "takeaturn". Then have all the robots inherit from the "robot type", which in tirn inherits from the "npc type". Each turn (i.e., 8 seconds), run the "takeaturn" script of all the NPCs; for the robot NPCs, that would be the script above, but other NPCs could have their own script for their own behaviour.

foreach (npc, FilterByType(AllObjects(), "npc type")) {
  do(npc, "takeaturn")

Not relevant in this case, but for completeness Quest does have PickOneExit and PickOneUnlockedExit for when you want an NPC to use exits.

Pixie, thanks!

I worked through that and made a few minor adjustments - both because of a few different gameplay elements that I had that were were interfering and also because I likely made errors in code entry (my pc has no internet connection, so I didn't have the option of copying and pasting your code).

I've edited the lookat command so that it sets a lookedat flag and coded that into the timer as if (GetBoolean(object, "lookedat")){ expression. Mainly because I want the player to encounter the NPCs in specific rooms - bit of a hassle if they wander off before you even get there.

I'll post the code when I can plug the computer in again.

As a quick side note, how would you deal with NPCs that share some but not all of their paths? Logically it seems to me that the overlapping rooms would have to both be nested inside two uber-rooms. I didn't think Quest could do that.

If they share some but not all rooms in their paths, you might have to explicitly create the path attribute as you did in your first example.

I'd probably have done this using exits anyway, but had the NPC not check if the exit is locked or visible (so you can use an invisible exit to indicate a path NPCs can take but the player can't).

Log in to post a reply.