What will this break? | movingNpc type: changedparent script

K.V.

 <type name="movingNpc">
    <changedparent type="script">
      if (this.parent = game.pov.parent) {
        if (IsDefined("oldvalue")) {
          x = GetExitByLink(this.parent, oldvalue)
          if (not x = null) {
            x = GetObject(x)
          }
          article = ""
          if (not x = null) {
            switch (x.alias) {
              case ("north", "south", "east", "west", "northeast", "southeast", "northwest", "southwest") {
                article = "the"
              }
              case ("up") {
                article = ""
                x.alias = "above"
              }
              case ("down") {
                article = ""
                x.alias = "below"
              }
              case ("in") {
                article = ""
                x.alias = "inside"
              }
              case ("out") {
                article = ""
                x.alias = "outside"
              }
              default {
                article = ""
              }
            }
          }
          if (not x = null) {
            msg (CapFirst(GetDisplayName(this)) + " arrives from " + article + " " + x.alias + ".")
          }
        }
      }
      else if (oldvalue = game.pov.parent) {
        x = GetExitByLink(game.pov.parent, this.parent)
        if (not x = null) {
          x = GetObject(x)
        }
        if (not x = null) {
          msg (CapFirst(GetDisplayName(this)) + " went " + x.alias + ".")
        }
      }
      this.hasbeenmoved = true
    </changedparent>
  </type>

As long as the player can't become the NPC, could this hurt anything?


> z
You wait 1 minute.
A bear went inside.

> z
You wait 1 minute.
A bear arrives from inside.

> z
You wait 1 minute.
A bear went inside.

> z
You wait 1 minute.
A bear arrives from inside.


One thing that catches my attention straight away is you're changing the alias of the exit. So does that mean that after it says "Joe Somebody arrives from above", you can then type "above" to go up? (If the exit doesn't have alts set, typing "go up" will no longer work, and it could mess up the compass, but I've not looked into how the sidebars work so I could be wrong)

Apart from that, it's pretty much the same as my attempt (though I also had a parent-of-parent check to display "You hear someone moving about elsewhere in the house"), so I think it should be fine.

In a more complex world, it might be convenient to have the option to stick an "OnNPCMoved" script (expecting an NPC as a parameter) on an exit, that overrides the other notifications. That way if you've got a big heavy door, you could stick a script on it that lets the player know they heard the door open and slam even if they're not nearby. Or if you have an enemy moving through a secret passage it could say "One of the bookcases suddenly swings open and Jack Bobson steps out!" rather than "Jack Bobson arrives from the north".


K.V.

you're changing the alias of the exit

I don't think I am.

It should just be a variable (x) that prints in my message.

You've got me curious now. I'm going to check.


> inside
I didn't quite catch that.
A bear went inside.


Nah. Sounded plausible, though! (I tried it after it said the bear went inside. I only copy & pasted the one turn.)


K.V.

In a more complex world, it might be convenient to have the option to stick an "OnNPCMoved" script (expecting an NPC as a parameter) on an exit, that overrides the other notifications.

True, true.

That could be easily done. Just don't give the NPC the movingNpc type, since it would be a specific message to print anyway.


Here's the way I have this same thing working when my player's followers enter the location. (They don't always come from the same room.)

Click here to see somewhat related code

Depends on PathLib

Room enter script:

foreach (o, AllObjects()) {
  if (HasAttribute(o, "shadowing")) {
    npc = o
    // msg (o)
    targetroom = player.parent
    if (npc.parent <> targetroom) {
      list = PathLib_GetPath(npc.parent, targetroom)
      if (list <> null) {
        exit = list[0]
        npc.parent = exit.to
        if (npc.parent <> targetroom) {
          msg ((CapFirst(GetDisplayName(npc))) +" is in "+(GetDisplayName(npc.parent))+", and headed your way.")
        }
        else {
          diralias = ReverseDirection(exit.alias)
          if (diralias = "out") {
            msg (GetDisplayName(npc) + " arrives from the outside.")
          }
          else if (diralias = "in") {
            msg (GetDisplayName(npc) + " arrives from the inside.")
          }
          else if (diralias = "up") {
            msg (GetDisplayName(npc) + " arrives from above.")
          }
          else if (diralias = "down") {
            msg (GetDisplayName(npc) + " arrives from below.")
          }
          else {
            msg (GetDisplayName(npc) + " arrives from the " + diralias + ".")
          }
        }
      }
    }
  }
}

http://textadventures.co.uk/forum/samples/topic/lxmex1fwpu2itl8tfidcvg/combining-followers-with-pathlib-or-a-feeble-attempt-by-richard-headkid#d39974d9-eb16-4024-b012-c9c6f94d789e

(RH is my pseudonym.)


[Edit: Remove all that. I got confused; it's changedhealth that gets messed up by the game.changedpov script; not changedparent)


That could be easily done. Just don't give the NPC the movingNpc type, since it would be a specific message to print anyway.

I think you misunderstood me. I'm thinking about situations where a particular exit (shop door with a bell on it? teleporter? Alarmed door?) displays a message when an NPC passes through it.
I was assuming that movingNPC was inherited by all types of NPC that move; whether they're followers or enemies or anything else, in which case its changedparent script would be the right place to check for such a script on the exit. If this is only one type of moving NPC, then I guess they'd all have to check.


K.V.

Yeah... I'm overriding this:

if (game.pov = this) {
  if (IsDefined("oldvalue")) {
    OnEnterRoom (oldvalue)
  }
  else {
    OnEnterRoom (null)
  }
  if (game.gridmap) {
    MergePOVCoordinates
  }
}
this.hasbeenmoved = true

...but, as long as you don't make a playable object a movingNpc type, I don't think it will hurt.

Come to think of it, I could probably just add an else on the end of the original script:

if (game.pov = this) {
  if (IsDefined("oldvalue")) {
    OnEnterRoom (oldvalue)
  }
  else {
    OnEnterRoom (null)
  }
  if (game.gridmap) {
    MergePOVCoordinates
  }
}
else if (this.parent = game.pov.parent) {
  if (IsDefined("oldvalue")) {
    x = GetExitByLink(this.parent, oldvalue)
    if (not x = null) {
      x = GetObject(x)
    }
    article = ""
    if (not x = null) {
      switch (x.alias) {
        case ("north", "south", "east", "west", "northeast", "southeast", "northwest", "southwest") {
          article = "the"
        }
        case ("up") {
          article = ""
          x.alias = "above"
        }
        case ("down") {
          article = ""
          x.alias = "below"
        }
        case ("in") {
          article = ""
          x.alias = "inside"
        }
        case ("out") {
          article = ""
          x.alias = "outside"
        }
        default {
          article = ""
        }
      }
    }
    if (not x = null) {
      msg (CapFirst(GetDisplayName(this)) + " arrives from " + article + " " + x.alias + ".")
    }
  }
}
else if (oldvalue = game.pov.parent) {
  x = GetExitByLink(game.pov.parent, this.parent)
  if (not x = null) {
    x = GetObject(x)
  }
  if (not x = null) {
    msg (CapFirst(GetDisplayName(this)) + " went " + x.alias + ".")
  }
}
this.hasbeenmoved = true

Yep.

Just tested it (halfway).

It works for the bear, anyway. I didn't try to switch the game.pov to the bear...

It should work, though. I just added an else.


K.V.

I think you misunderstood me. I'm thinking about situations where a particular exit (shop door with a bell on it? teleporter? Alarmed door?) displays a message when an NPC passes through it.

Oh!

An exit message on those exits, maybe?

Hmm...

I know that PathLib keeps them from going through locked exits. (Or I think I know that. Ha-ha!) Maybe there's something in that script that can be tweaked for exits with doors or scripts or something.


K.V.

It will add the attribute to each object you add the type to, so you could just Make and editable copy, then add something like:

if (oldvalue = room_with_bell_on_door) msg ("DING DING DING!")


You could also add a noisyExit attribute to appropriate things, and use foreach to iterate through all the objects with the movingNpc type and all rooms with the noisyExit attribute. (I think you could. I'm pretty sure it would be a fairly simple addition. The specific message for the exit is what I'm not sure about, but I think I remember Doctor Agon fooling around with exit messages easily.)


Edit3: Sorry, you can't post that here. Why not?
Edit2: Fixed a typo; also a little rearrangement to allow custom-named exits.
Edit: Made a silly mistake. D'oh.

Sorry, I was confused. InitPOV() overwrites the characters changedhealth (without checking if you already set one) so that you can't customise its behaviour; I thought it did the same with changedparent.

With regard to 'noisy exits', I was thinking you could do something like:

if (game.pov = this) {
  if (IsDefined("oldvalue")) {
    OnEnterRoom (oldvalue)
  }
  else {
    OnEnterRoom (null)
  }
  if (game.gridmap) {
    MergePOVCoordinates
  }
}
else {
  exit = GetExitByLink(oldvalue, this.parent)
  exit_has_script = false
  if (not exit = null) {
    exit = GetObject(x)
    if (HasScript (exit, "onNPCmovement")) {
      exit_has_script = true
    }
  }
  message = ""
  if (this.parent = game.pov.parent) {
    if (IsDefined("oldvalue")) {
      x = GetExitByLink(this.parent, oldvalue)
      if (not x = null) {
        x = GetObject(x)
      }
      direction = ""
      if (not x = null) {
        if (HasString (x, "descriptionfrom")) {
          direction = x.descriptionfrom
        }
        else {
          switch (x.alias) {
            case ("north", "south", "east", "west", "northeast", "southeast", "northwest", "southwest") {
              direction = "the "+x.alias
            }
            case ("up") {
              direction = "above"
            }
            case ("down") {
              direction = "below"
            }
            case ("in", "out") {
              direction = x.alias + "side"
            }
            default {
              direction = x.alias
            }
          }
        }
      }
      if (not x = null) {
        message = CapFirst(GetDisplayName(this)) + " arrives from " + direction + "."
      }
    }
  }
  else if (oldvalue = game.pov.parent) {
    x = GetExitByLink(game.pov.parent, this.parent)
    if (not x = null) {
      x = GetObject(x)
    }
    if (not x = null) {
      if (HasString (x, "descriptionto") {
        message = CapFirst(GetDisplayName(this)) + " went " + x.descriptionto + "."
      }
      else {
        message = CapFirst(GetDisplayName(this)) + " went " + x.alias + "."
      }
    }
  }
  if (exit_has_script) {
    parameters = NewDictionary()
    dictionary add (parameters, "npc", this)
    dictionary add (parameters, "defaultmessage", message)
    do (exit, "onNPCmovement", parameters)
  }
  else if (not message = "") {
    msg (message)
  }
}
this.hasbeenmoved = true

Hmm ... that could be more elegant.
But in any case, you can give an exit a script attribute "onNPCmovement" with something like:

if (not defaultmessage = "") {
  msg ("The bell on the door rings as "+defaultmessage)
}
else if (Contains (general_store_back_rooms, game.pov)) {
  msg ("You hear the jingle of the bell on the door")
}

Set up to allow extra attributes on an exit, to change the descriptions a little without requiring extra code.


(previous post edited. Yay, it let me edit!)


(Yes, I put way, way too much effort into reducing future work)


K.V.

Yes, I put way, way too much effort into reducing future work

So do I.

Work hard now. Take it easy forever after.


That noisy exit script looks good, too, by the way.


I figured if you pass it the default message, it makes it easy for the exit to modify it, or to check if the player can see the moving NPC.
I was going to add an option for onNPCmovement to be a string rather than a script… but then I realised I can think of about an equal number of cases where you'd want the message to be displayed when the player is present or when they're not. So the script is probably flexible enough.

Another (slightly silly) example for a more modern setting.

if (defaultmessage <> "") {
  msg (defaultmessage)
}
if (game.base_alertstatus > 5) {
  msg ("A siren sounds briefly, and a mechanical voice announces: “"+GetDisplayAlias(npc)+" has entered the building!”")
}

On my first game, I had a case where you can hear footsteps, but only if RandomChance(getstat("hearing")-10*GetDistance(npc.parent, game.pov.parent)).


It should just be a variable (x) that prints in my message.

I'm confused. I'm pretty sure that the variable x is still a reference to the same exit. So x = some_other_exit won't affect the exit, but x.alias = whatever will.

Several times in my randomly shifting forest, I've got code that boils down to

x = GetExitByLink(reachable, unreachable)
if (not x = null) {
  x = GetObject(x)
  x.visible = true
}

And that makes the exit visible even though x is a local variable.

When you tested it, did you test it on the first turn after the bear leaves? Because I was expecting it to change the alias of the exit when the bear arrives through it.

Wait…

In your switch/case block, you're comparing x.alias to "in" and "out". But that's only when the bear enters the player's room, not when they leave. And yet your screenshot shows "The bear went inside". So the exit's alias was "inside" to start with, and the case ("in") { block is redundant? Or your example screenshot are from a more complex version of the code that adds "inside" and "outside" when the NPC is leaving your room as well? Or am I missing something obvious.

In either case, "go inside" is still a reasonable command. I'd be more worried about that happening with "above" and "below".


K.V.

Sorry, I fell asleep.

That changes the exit to visible???

Uh-oh! You're right! (And I use this method to rename things all the time. This is a classic case of 'not enough sleep'.)


> go below

The Barn
You can see a bear, Jerry and a glitch.
You can go up.
A bear went up.

> go above
You can't go there.
A bear arrives from above.

> go above

Basement
You can go up or below.
A bear arrives from below.


K.V.

Fixed the alias changing:

if (game.pov = this) {
  if (IsDefined("oldvalue")) {
    OnEnterRoom (oldvalue)
  }
  else {
    OnEnterRoom (null)
  }
  if (game.gridmap) {
    MergePOVCoordinates
  }
}
else if (this.parent = game.pov.parent) {
  if (IsDefined("oldvalue")) {
    x = GetExitByLink(this.parent, oldvalue)
    if (not x = null) {
      x = GetObject(x)
    }
    article = ""
    if (not x = null) {
      switch (x.alias) {
        case ("north", "south", "east", "west", "northeast", "southeast", "northwest", "southwest") {
          article = "the"
          dir = x.alias
        }
        case ("up") {
          article = ""
          dir = "above"
        }
        case ("down") {
          article = ""
          dir = "below"
        }
        case ("in") {
          article = ""
          dir = "inside"
        }
        case ("out") {
          article = ""
          dir = "outside"
        }
        default {
          article = ""
        }
      }
    }
    if (not x = null) {
      msg (CapFirst(GetDisplayName(this)) + " arrives from " + article + " " + dir + ".")
      //msg (x.alias)
      //msg (dir)
    }
  }
}
else if (oldvalue = game.pov.parent) {
  x = GetExitByLink(game.pov.parent, this.parent)
  if (not x = null) {
    x = GetObject(x)
  }
  if (not x = null) {
    msg (CapFirst(GetDisplayName(this)) + " went " + x.alias + ".")

K.V.

mrangel,

I have a serious question:

Do you enjoy writing code for games more than you enjoy playing them?

Because Quest is like the biggest text adventure of them all, if you ask me!


NOTE: Had I read your most recent script after I got my eyes all the way open, I'd have just pasted it into my game.


My most recent version still has issues, I believe :p An issue which I noticed, but was distracted by an email ("Your new book has successfully been published") and then forgot.
I do believe you have corrected the same issue, though.


I definately enjoy constantly learning to code more and develop better code... than I spend on progressing my game, though there's a reason, as indeed, as you get better at coding, the faster you can code in your game and do more cool stuff for your game. The better you're at coding, the easier and faster it is to make a game. "Hard training, makes for easy fighting (Alexander V. Suvorov: 63-0 record during the Napoleonic Era of trench warfare and calvary, it's a good thing for Napoleon that they've never met, hehe)"

As game making also requires a lot of non-code work too: game design, writing/authoring (story, plot, dialogue/conversations), media (art/graphics, music, sound / sound effects, animation, etc), game mechanics / equations/formulas, game balancing, etc etc etc. All of which takes considerable work too, as much as the coding aspect. It often takes me many hours on thinking this type of stuff, whereas I can often get some coding figured out quicker, generally, anyways.


@hegemonkhan For me, it's a few days of thinking to figure out I have a typo somewhere, and several days to figure out I had an extra } in the WeaponUpdate, currently my most important function, forcing it to call a "}" function!


K.V.

Error running script: unknown function }


That's my number one error. Number two: missing )


As you code more, you learn not to make these simple mistakes as much and if you do, you can usually find and fix them quickly/easily. It just takes practice, you get used to it, eventually. Coding requires and thus forces you to become 100% grammer/spelling error free, not just a "grammer/spelling nazi", but a "SS/hitler grammer/spelling nazi", lol. Coding requires and forces you to learn to have ZERO mistakes... it'll frighten you, that you can learn to not make any mistakes, but it does happen, with enough practice.

Though I'm still a coding noob... there's still code that can take me even an entire month to get figured-out/working...sighs... it's not fun learning to code... taking long amounts of time and effort to learn it...but once you do... then the fun begins... hehe :D


My most common error is the one telling me that a variable named HasAttribute (game is not an object.


Just thinking; this feels a little unnatural or surreal in some situations.
(A version of this was the first "complex" script I tried to write on the day I signed up for this site; so I knew there was something that had annoyed me the first time I looked into it)

I think if I was using this in a real game, I'd either override the "go" command to set game.pov.lastexitused to the exit; or have a turn script record where the player was at the end of the last turn. Then substitute a message like CapFirst(GetDisplayName(this)) + " arrives close behind you." for the case where the NPC is going through the same door the player just did. Because while correct, seeing "John arrives from the north" when he went through the door right behind you… just feels weird to me.


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums