help fixing an error and help making cat behaviour?

https://snipboard.io/JxAfmL.jpg
Look above at the link.

(PS: I solved the error, just need cat script.)


The cat should be a fairly simple turnscript. My usual approach is to work from the top down; taking the most general case first.

I'll do this step by step rather than throwing a block of code at you, because hopefully that makes it easier to understand for the purpose of fixing problems.

So…

and not move to other rooms while being held by the player

You probably don't need to worry about this. Most NPC movement scripts just look for exits in the current room. If the cat's current room is the inventory, there aren't any exits there, so it won't move.

i need it to walk around the ship too

This is simple enough. First, find an exit from the room the cat is currently in:

exit = PickOneUnlockedExit (cat.parent)

Then we need to check if the exit is valid. If the player is holding the cat, or if all the exits are locked, then exit will be null. We need to check that before moving the cat:

exit = PickOneUnlockedExit (cat.parent)
if (not exit = null and HasObject (exit, "to")) {
  MoveObject (cat, exit.to)
}

The exit's to attribute points to the room on the other side. I check that to exists before moving, because it's possible to have an exit that doesn't go anywhere and runs a script instead, and you don't want the cat to follow these.

However, at this point I have a thought. In my experience, cats tend to spend a lot of time sitting in front of closed doors, waiting for someone to open them. So maybe it would be better to have the cat pick an exit at random, and then if that exit is closed, don't move. That's an easy change to make:

exit = PickOneExit (cat.parent)
if (not exit = null and HasObject (exit, "to") and not GetBoolean (exit, "locked")) {
  MoveObject (cat, exit.to)
}

If you want the cat to only go through certain doors, or you want to make the cat only move every other turn or something, you could add an extra and expression to that line

With most NPCs, you would also want to tell the player if a someone has moved into or out of the room they're in. Cats can be quiet, so you might not want this (the cat will just appear in the sidebar), but I'll assume you do.

So, you want to check if the cat is in the player's room, and if the destination is the player's room. Something like:

exit = PickOneExit (cat.parent)
if (not exit = null and HasObject (exit, "to") and not GetBoolean (exit, "locked")) {
  if (cat.parent = game.pov.parent) {
    msg ("The cat wanders off to the " + GetDisplayAlias (exit.to) + ".")
  }
  if (exit.to = game.pov.parent) {
    msg ("Your {object:cat} walks in from the " + GetDisplayAlias (cat.parent) + ".")
  }
  MoveObject (cat, exit.to)
}

(I'm assuming you know the names of the rooms. Using GetDisplayAlias (exit) to get something like "north" would be another alternative, depending on the atmosphere you want to create)

follow the player at random

Actually following the player ends up being more complex than you think. However, there's an easier way – just give the cat a higher chance of walking into the room where the player is if they're in adjacent rooms. This could be as simple as:

exit = PickOneExit (cat.parent)
if (RandomChance (75)) {
  follow = GetExitByLink (cat.parent, game.pov.parent)
  if (TypeOf (follow) = "string") {
    exit = GetObject (follow)
  }
}
if (not exit = null and HasObject (exit, "to") and not GetBoolean (exit, "locked")) {
  if (cat.parent = game.pov.parent) {
    msg ("The cat wanders off to the " + GetDisplayAlias (exit.to) + ".")
  }
  if (exit.to = game.pov.parent) {
    msg ("Your {object:cat} walks in from the " + GetDisplayAlias (cat.parent) + ".")
  }
  MoveObject (cat, exit.to)
}

GetExitByLink finds an exit between two rooms if there is one. If the exit exists, it returns the name of the exit, which we can use GetObject on to get the actual exit. If there is no exit between those rooms, it returns null. so we check that the returned value is a string before doing anything with it.

take the items
and not take items in the same room as the player

You can put this in the same turnscript. You can put it before or after the cat moves. Or if you don't want the cat to move and pick up an object and move on the same turn, you could use an 'else' clause.

I'll use the object's take attribute, which says whether it can be picked up. If you want to have a separate attribute to determine what objects the cat can pick up, just use that instead.

if (RandomChance (35)) {
  objects = FilterByAttribute (GetDirectChildren (cat.parent), "take", true)
  list remove (objects, cat)
  obj = PickOneObject (objects)
  MoveObject (obj, cat)
  cat.hidechildren = true
}

I'd suggest making the cat a "Surface" (On the 'Container' tab), which means that the player can take objects away from it, and can see what it's holding by looking at it. In this case, hidechildren causes the object(s) in the cat to be hidden until the player looks at it.

You can change the description for an object's contents on the container tab somewhere, so that instead of "Containing:" it says "Carrying:" or "Playing with:" once the player has looked at it.

after some time it should drop the item in the room it's in

That's simple enough. If the cat can only hold one item, then I'd give it a simple percentage chance of either picking up or dropping an object (use the same dice roll for both, it's more efficient).

So it would look like:

if (RandomChance (35)) {
  objectheld = PickOneObject (GetDirectChildren(cat))
  if (objectheld = null) {
    objects = FilterByAttribute (GetDirectChildren (cat.parent), "take", true)
    if (ListCount (objects) > 1) {
      list remove (objects, cat)
      obj = PickOneObject (objects)
      MoveObject (obj, cat)
      cat.hidechildren = true
    }
  }
  else {
    MoveObject (objectheld, cat.parent)
  }
}

If you want to use different probabilities, you could just rearrange that a little:

objectheld = PickOneObject (GetDirectChildren(cat))
if (objectheld = null) {
  // chance of picking up:
  if (RandomChance (35)) {
    objects = FilterByAttribute (GetDirectChildren (cat.parent), "take", true)
    if (ListCount (objects) > 1) {
      list remove (objects, cat)
      obj = PickOneObject (objects)
      MoveObject (obj, cat)
      cat.hidechildren = true
    }
  }
}
else {
  // chance of dropping
  if (RandomChance (20)) {
    MoveObject (objectheld, cat.parent)
  }
}

not take items in the same room as the player

That's simple enough. I'll assume that you also don't want it to take items from the player if they're carrying the cat. So I use Contains (which is faster than most of the Scope functions) to check if the cat is in the player's room. This includes cases when the cat is in the room, the cat is in the inventory, or the cat is in a box in the same room, whether or not the box is closed)

if (not Contains (game.pov.parent, cat)) {
  objectheld = PickOneObject (GetDirectChildren(cat))
  if (objectheld = null) {
    // chance of picking up:
    if (RandomChance (35)) {
      objects = FilterByAttribute (GetDirectChildren (cat.parent), "take", true)
      if (ListCount (objects) > 1) {
        list remove (objects, cat)
        obj = PickOneObject (objects)
        MoveObject (obj, cat)
        cat.hidechildren = true
      }
    }
  }
  else {
    // chance of dropping
    if (RandomChance (20)) {
      MoveObject (objectheld, cat.parent)
    }
  }
}

If the cat can drop objects near the player but not pick them up, you could just move the if statement around.
But if the cat is being carried and drops an item, you probably want to let the player know, so it would become:

objectheld = PickOneObject (GetDirectChildren(cat))
if (objectheld = null) {
  // chance of picking up:
  if (not Contains (game.pov.parent, cat) and RandomChance (35)) {
    objects = FilterByAttribute (GetDirectChildren (cat.parent), "take", true)
    if (ListCount (objects) > 1) {
      list remove (objects, cat)
      obj = PickOneObject (objects)
      MoveObject (obj, cat)
      cat.hidechildren = true
    }
  }
}
else {
  // chance of dropping
  if (RandomChance (20)) {
    if (cat.parent = game.pov) {
      msg ("The cat spits out something, and you look down in time to see " + GetDisplayName(objectheld) + " hit the floor.")
      MoveObject (objectheld, game.pov.parent)
    }
    else {
      MoveObject (objectheld, cat.parent)
    }
  }
}

the player should not be able to see the object for a while until they step on it or look at their cat

Not entirely sure what you mean by 'step on it'. Do you mean that after the cat drops the object, it continues to be hidden for some length of time? I'd probably do that by making the object into scenery temporarily.

I'd add 2 lines after the cat drops an object:

objectheld.scenery = true
objectheld.lost = true

Then add a section to the beginning of the turnscript that lets the player 'find' these objects again. Something like:

randomobj = PickOneObject (GetDirectChildren (game.pov.parent))
if (not randomobj = null and GetBoolean (randomobj, "lost")) {
  msg ("You almost trip over " + GetDisplayName (randomobj) + " that your cat has left lying around.")
  randomobj.scenery = false
  randomobj.lost = false
}

This picks an object in the same room as the player, and if it's lost finds it again. It means that the more stuff is in a room, the longer it takes to find something by chance.

If you want to make it a little easier on the player, you could make it so that when they deliberately look around the room (by typing look) they find any objects the cat has been playing with. Adding something like this to the beginning of the turnscript:

if (game.pov.currentcommandpattern = look) {
  found = FilterByAttribute (ScopeVisible(), "lost", true)
  if (ListCount (found) > 0) {
    msg ("While you are looking around, you find " + FormatList (found, ", ", ", and", "") + " lying around on the floor.")
    foreach (obj, found) {
      obj.lost = false
      obj.scenery = false
    }
  }
}

(Using game.pov.currentcommandpattern is a weird way for a turnscript to find out the last command that the player used. In this case, it's a relatively easy way to do things)

You could also add a little tweak to change the message when the player takes the object from the cat. Because the cat currently won't pick up objects that run a script when they are taken, you could add one like this (modifying the 'pick up/drop' part of the cat's script):

objectheld = PickOneObject (GetDirectChildren(cat))
if (objectheld = null) {
  // chance of picking up:
  if (not Contains (game.pov.parent, cat) and RandomChance (35)) {
    objects = FilterByAttribute (GetDirectChildren (cat.parent), "take", true)
    if (ListCount (objects) > 1) {
      list remove (objects, cat)
      obj = PickOneObject (objects)
      MoveObject (obj, cat)
      cat.hidechildren = true
      obj.take => {
        this.take = true
        if (RandomChance (30)) {
          msg ("You try to take the {object:" + this.name + "}, but the {object:cat} bats it into the far corner of the room.")
          MoveObjectHere (obj)
          obj.lost = true
          obj.scenery = true
        }
        else {
          msg ("With some effort, you persuade the {object:cat} to let go of {cat.possessive} toy.")
          AddToInventory (this)
        }
      }
    }
  }
}
else {
  // chance of dropping
  if (RandomChance (20)) {
    if (cat.parent = game.pov) {
      msg ("The cat spits out something, and you look down in time to see " + GetDisplayName(objectheld) + " hit the floor.")
      MoveObject (objectheld, game.pov.parent)
    }
    else {
      MoveObject (objectheld, cat.parent)
      objectheld.lost = true
      objectheld.scenery = true
      objectheld.take = true
    }
  }
}

So with all those bits, you'd have a turnscript whose script is:

if (game.pov.currentcommandpattern = look) {
  found = FilterByAttribute (ScopeVisible(), "lost", true)
  if (ListCount (found) > 0) {
    msg ("While you are looking around, you find " + FormatList (found, ", ", ", and", "") + " lying around on the floor.")
    foreach (obj, found) {
      obj.lost = false
      obj.scenery = false
    }
  }
}
else {
  randomobj = PickOneObject (GetDirectChildren (game.pov.parent))
  if (not randomobj = null and GetBoolean (randomobj, "lost")) {
    msg ("You almost trip over " + GetDisplayName (randomobj) + " that your cat has left lying around.")
    randomobj.scenery = false
    randomobj.lost = false
  }
}

exit = PickOneExit (cat.parent)
if (RandomChance (75)) {
  follow = GetExitByLink (cat.parent, game.pov.parent)
  if (TypeOf (follow) = "string") {
    exit = GetObject (follow)
  }
}
if (not exit = null and HasObject (exit, "to") and not GetBoolean (exit, "locked")) {
  if (cat.parent = game.pov.parent) {
    msg ("The cat wanders off to the " + GetDisplayAlias (exit.to) + ".")
  }
  if (exit.to = game.pov.parent) {
    msg ("Your {object:cat} walks in from the " + GetDisplayAlias (cat.parent) + ".")
  }
  MoveObject (cat, exit.to)
}

objectheld = PickOneObject (GetDirectChildren(cat))
if (objectheld = null) {
  // chance of picking up:
  if (not Contains (game.pov.parent, cat) and RandomChance (35)) {
    objects = FilterByAttribute (GetDirectChildren (cat.parent), "take", true)
    if (ListCount (objects) > 1) {
      list remove (objects, cat)
      obj = PickOneObject (objects)
      MoveObject (obj, cat)
      cat.hidechildren = true
      obj.take => {
        this.take = true
        if (RandomChance (30)) {
          msg ("You try to take the {object:" + this.name + "}, but the {object:cat} bats it into the far corner of the room.")
          MoveObjectHere (obj)
          obj.lost = true
          obj.scenery = true
        }
        else {
          msg ("With some effort, you persuade the {object:cat} to let go of {cat.possessive} toy.")
          AddToInventory (this)
        }
      }
    }
  }
}
else {
  // chance of dropping
  if (RandomChance (20)) {
    if (cat.parent = game.pov) {
      msg ("The cat spits out something, and you look down in time to see " + GetDisplayName(objectheld) + " hit the floor.")
      MoveObject (objectheld, game.pov.parent)
    }
    else {
      MoveObject (objectheld, cat.parent)
      objectheld.lost = true
      objectheld.scenery = true
      objectheld.take = true
    }
  }
}

Edit:
I realise that using scenery like this might result in some odd behaviour. If the cat plays with an object then puts it back in the same room, then the player picks it up by typing the command (because they knew where they left it and didn't notice that it was missing from the list), they'll be able to clear the scenery flag but the lost flag will still be there.

In this case, it might be advantageous to use the take script again to clear up any confusion:

if (game.pov.currentcommandpattern = look) {
  found = FilterByAttribute (ScopeVisible(), "lost", true)
  if (ListCount (found) > 0) {
    msg ("While you are looking around, you find " + FormatList (found, ", ", ", and", "") + " lying around on the floor.")
    foreach (obj, found) {
      obj.lost = false
      obj.scenery = false
      obj.take = true
    }
  }
}
else {
  randomobj = PickOneObject (GetDirectChildren (game.pov.parent))
  if (not randomobj = null and GetBoolean (randomobj, "lost")) {
    msg ("You almost trip over " + GetDisplayName (randomobj) + " that your cat has left lying around.")
    randomobj.scenery = false
    randomobj.lost = false
    randomobj.take = true
  }
}

exit = PickOneExit (cat.parent)
if (RandomChance (75)) {
  follow = GetExitByLink (cat.parent, game.pov.parent)
  if (TypeOf (follow) = "string") {
    exit = GetObject (follow)
  }
}
if (not exit = null and HasObject (exit, "to") and not GetBoolean (exit, "locked")) {
  if (cat.parent = game.pov.parent) {
    msg ("The cat wanders off to the " + GetDisplayAlias (exit.to) + ".")
  }
  if (exit.to = game.pov.parent) {
    msg ("Your {object:cat} walks in from the " + GetDisplayAlias (cat.parent) + ".")
  }
  MoveObject (cat, exit.to)
}

objectheld = PickOneObject (GetDirectChildren(cat))
if (objectheld = null) {
  // chance of picking up:
  if (not Contains (game.pov.parent, cat) and RandomChance (35)) {
    objects = FilterByAttribute (GetDirectChildren (cat.parent), "take", true)
    if (ListCount (objects) > 1) {
      list remove (objects, cat)
      obj = PickOneObject (objects)
      MoveObject (obj, cat)
      cat.hidechildren = true
      obj.take => {
        if (RandomChance (30)) {
          msg ("You try to take the {object:" + this.name + "}, but the {object:cat} bats it into the far corner of the room.")
          MoveObjectHere (obj)
          this.lost = true
          this.scenery = true
          this.take => {
            if (RandomChance (80)) {
              msg ("You manage to find the " + GetDisplayAlias (this) + " where the cat had left it.")
              this.lost = false
              this.scenery = false
              this.take = true
              AddToInventory (this)
            }
            else {
              msg ("The " + GetDiaplayAlias (this) + " isn't anywhere to be seen. The cat must have moved it, and you might need to {command:look} around to find it.")
            }
          }
        }
        else {
          msg ("With some effort, you persuade the {object:cat} to let go of {cat.possessive} toy.")
          AddToInventory (this)
        }
      }
    }
  }
}
else {
  // chance of dropping
  if (RandomChance (20)) {
    if (cat.parent = game.pov) {
      msg ("The cat spits out something, and you look down in time to see " + GetDisplayName(objectheld) + " hit the floor.")
      MoveObject (objectheld, game.pov.parent)
    }
    else {
      MoveObject (objectheld, cat.parent)
    }
    objectheld.lost = true
    objectheld.scenery = true
    objectheld.take => {
      if (RandomChance (80)) {
        msg ("You manage to find the " + GetDisplayAlias (this) + " where the cat had left it.")
        this.lost = false
        this.scenery = false
        this.take = true
        AddToInventory (this)
      }
      else {
        msg ("The " + GetDiaplayAlias (this) + " isn't anywhere to be seen. The cat must have moved it, and you might need to {command:look} around to find it.")
      }
    }
  }
}

I've also got thoughts about cats and their tendency to climb into any open boxes they see, which would make the script a little more complex. Let me know if you'd like to try that.


I can't believe you wrote that entire thing for the developer. That's above and beyond!


Now I'm looking at the 'getting into boxes' thing.

Think it might be interesting to give some objects a 'cat behaviour' thing. Like, you can give objects a verb that the cat can use.

So the cat picks an object at random in the room it ends the turn in.

If that object has a 'catdoes' verb, run the script. If it's a message, print the message only if the player is in the room. So you can have the cat scratching at things, or sniffing things, which has no effect but gives the player a sense that the cat is a real thing.

Otherwise, if the object is an open box (or a surface) the cat gets in/on it.

Otherwise, if the object can be taken, the cat isn't carrying anything, and the player isn't present, the cat picks it up.

If you have a verb that you don't want the player to use (makes it easier in the editor, especially using the web editor), you can put something like dictionary remove (game.verbattributeslookup, "catdoes"), list remove (game.verbattributes, "catdoes"), and destroy ("catdoes") in the start script to remove that verb at the start of the game.


Here's a revised cat turnscript. A little different, but this is probably how I'd do it if it were my game.

Click here for code NOTE: This was written off the top of my head on the forum, not tested, and I haven't tried it in Quest. So there may be typos. May also be logic errors, especially as my mum walked into the room and started rearranging the furniture behind me about twenty minutes ago. If you need help making it work, let me know.

EDIT: Modified to include the part about letting the player find stuff the cat has hidden. Now, it will find any objects that the player has typed the name of in their command, anything that somehow got into the inventory without being found, as well as testing one random object in the room. Using the "look" command will still find everything.

result = ""
object = cat
params = NewDictionary()

// check if the player is messing with the cat
cat.interact = Contains (game.pov, cat)
if (HasAttribute(game.pov, "currentcommandresolvedobjects")) {
  if (ListContains (game.pov.currentcommandresolvedobjects, cat)) {
    cat.interact = true
  }
}
else {
  game.pov.currentcommandresolvedobjects = NewObjectList()
}
dictionary add (params, "interact", cat.interact)

// first, the stuff for finding objects that the cat has hidden
if (game.pov.currentcommandpattern = look) {
  found = ScopeVisible()
}
else {
  found = ListCombine (ScopeInventory(), game.pov.currentcommandresolvedobjects)
  list add(found, PickOneObject (GetDirectChildren (game.pov.parent)))
  list add(found, PickOneObject (ScopeReachableNotHeld()))
}
found  = ListCompact (found)
found  = FilterByAttribute (found, "lost", true)
showfound = NewObjectList()
if (ListCount (found) > 0) {
  foreach (obj, found) {
    obj.lost = false
    obj.scenery = false
    obj.take = true
    if (GetBoolean (obj, "visible") and not Contains (game.pov, obj)) {
      list add (showfound, obj)
    }
  }
}
if (ListCount (found) > 0) {
  msg ("While you are looking around, you find " + FormatList (found, ", ", ", and", "") + " lying around on the floor.")
}

// Only have the cat do stuff if it's awake
if (HasInt (cat, "sleeping")) {
  cat.sleeping = cat.sleeping - GetRandomInt (-3, 12)
  // cat has more chance of waking up if the player used a command on it
  if (cat.interact) {
    if (game.pov.currentcommandpattern = lookat) {
      cat.sleeping = cat.sleeping - GetRandomInt (0,5)
    }
    else {
      cat.sleeping = cat.sleeping - GetRandomInt (15,25)
      cat.startled = true
    }
  }
  else if (GetBoolean (cat, "startled") and RandomChance (20)) {
    cat.startled = false
  }
  if (cat.sleeping < 0) {
    cat.sleeping = null
    if (Contains (game.pov.parent, cat)) {
      result = cat.wakemessage
    }
  }
}
else if (HasObject (cat, "parent")) {
  // cat is awake and has not been RemoveObject'ed

  // First, we get a list of everything the cat could interact with
  objects = ListCombine (GetDirectChildren(cat.parent), ScopeExitsForRoom(cat.parent))
  list add (objects, cat.parent)
  if (HasObject (cat, "carrying")) {
    list add (objects, cat.carrying)
    list add (objects, cat.carrying)
  }
  options = NewStringList()
  foreach (obj, objects) {
    if (HasString (obj, "catactions")) {
      obj.catactions = Split (obj.catactions)
    }
    if (HasScript (obj, "catactions")) {
      list add (options, obj.name + ":catactions")
    }
    else if (HasAttribute (obj, "catactions") and GetBoolean (obj, "visible")) {
      if (not ListContains (obj.catactions, "ignore")) {
        foreach (action, obj.catactions) {
          digits = 0
          while (IsInt (Left (action, digits))) digits = digits + 1
          if (digits = 0) {
            count = 1
          }
          else {
            count = ToInt (Left (action, digits))
            action = Mid (action, digits + 1)
          }
          list add (options, obj.name + ":" + action)
        }
      }
    }
  }

  // choose an action, and split it up into action and messages
  action = PickOneString (options)
  parts = TSplit(action + ":::")
  action = Trim(ListItem (parts, 0))
  object = ListItem (parts, 0)
  list remove (parts, object)
  object = GetObject (object)
  if (action = "") {
    list remove (parts, "")
  }
  if (IsRegexpMatch("^(go|carry|message|sleep\d*)$", action) or HasScript (object, action) or HasString (object, action)) {
    list remove (parts, action)
  }
  else if (DoesInherit (object, "defaultexit") or DoesInherit (object, "container_base")) {
    action = "go"
  }
  else if (GetBoolean (object, "take") or GetBoolean (object, "drop") or object = cat.carrying) {
    action = "take"
  }
  else {
    action = "message"
  }
  mesg = ListItem (parts, 0)
  failmsg = ListItem (parts, 1)
  dictionary add (params,  "action", action)
  dictionary add (params,  "message", mesg)
  dictionary add (params,  "failmessage", failmsg)
  cat.here = Contains (cat.parent, game.pov)
  cat.seen = ListContains (ScopeVisible(), cat)
  dictionary add (params,  "here", cat.here)
  dictionary add (params,  "seen", cat.seen)
  dictionary add (params,  "carrying", cat.carrying)

  // Now we actually do the thing
  switch (action) {
    case ("go") {
      if (DoesInherit (object, "defaultexit")) {
        if (GetBoolean (object, "locked") or not HasObject (object, "to")) {
          if (Contains (game.pov.parent, cat) or game.pov.parent = object.to) {
            result = failmsg
          }
        }
        else {
          if (Contains (game.pov.parent, cat) or game.pov.parent = object.to) {
            result = mesg
          }
          cat.parent = object.to
        }
      }
      else {
        // it's a container
        if (GetBoolean (object, "isopen")) {
          if (Contains (game.pov.parent, cat)) {
            result = mesg
          }
          if (Contains (object, cat)) {
            cat.parent = object.parent
          }
          else {
            cat.parent = object
          }
        }
        else if (Contains (game.pov.parent, cat)) {
          result = failmesg
        }
      }
    }
    case ("carry") {
      if (object = cat.carrying) {
        if (cat.parent = game.pov) {
          if (ListCount (parts) > 2) {
            result = ListItem (parts, 2)
          else {
            result = mesg
          }
          MoveObject (object, game.pov.parent)
        }
        else {
          MoveObject (object, cat.parent)
          if (Contains (game.pov.parent, cat)) {
            result = mesg
          }
        }
        cat.carrying = null
        object.lost = true
        object.scenery = true
        object.take => {
          if (RandomChance (80)) {
            msg ("You manage to find the " + GetDisplayAlias (this) + " where the cat had left it.")
            this.lost = false
            this.scenery = false
            this.take = true
            AddToInventory (this)
          }
          else {
            msg ("The " + GetDiaplayAlias (this) + " isn't anywhere to be seen. The cat must have moved it, and you might need to {command:look} around to find it.")
          }
        }
      }
      else if (HasObject (cat, "carrying")) {
        // cat is carrying something else
        if (Contains (game.pov.parent, cat) and ListCount (parts) > 3) {
          result = ListItem (parts, 3)
        }
      }
      else if (ListContains (ScopeVisible(), cat)) {
        result = failmsg
      }
      else if (GetBoolean (object, "take")) {
        // picked up an item
        cat.carrying = object
      }
    }
    case ("sleep") {
      if (ListContains (ScopeVisible(), cat)) {
        result = mesg
        cat.sleeping = 80
        cat.wakemessage = failmsg
        cat.startled = false
      }
    }
    case ("message") {
      if (Contains (game.pov.parent, cat)) {
        result = mesg
      }
      else {
        result = failmsg
      }
    }
    default {
      result = GetAttribute (object, action)
    }
  }
}

// And show the result
switch (TypeOf(result)) {
  case ("string") {
    if (LengthOf (result) > 0) {
      game.text_processor_this = object
      game.text_processor_variables = params
      result = Trim (ProcessText (result))
      if (LengthOf (result) > 0) {
        msg (result)
      }
    }
  }
  case ("script") {
    dictionary add (params, "this", object)
    invoke (result, params)
  }
}

This assumes that each object the cat can interact with has an attribute catactions, which can either be a script, string, or stringlist. Default options, such as "the cat paces around the room" or "the cat has fallen asleep in the doorway" can be added to the cat itself, or the room. In the case of a string or stringlist, the format is something like: action:message:failmessage

For example, 3go:The cat wanders through the door {either here:to the kitchen:from the lounge}.:You can {either seen:see:hear} your cat scrabbling at the closed {either here:kitchen:lounge} door, eager to get through.

Actions can be go (exits and containers), carry (which the player never sees, so the message is the cat dropping the item. The failmessage is if it can't pick up while the player is watching; the 3rd and 4th messages are for if it drops an item while being carried, or if it examines an item while already carrying something else), message (display a message if the player is in the room; the second message is for if the player isn't present, so you can have the cat making noise), sleep (the fail message is for when it wakes up), or the name of a script attribute on the object. If there's a number stuck to the beginning of the action, like 6sleep, that makes the action 6 times more likely to be chosen.

If the action is omitted, we'll assume go for exits and containers, carry for moveable objects, and message otherwise.

Adding ignore to an objects action list will cause the cat to ignore that object completely.

The attribute cat.here (or the parameter here) can be used in the text processor to check if the cat was in the same room as the player at the start of its action. This will be false if the cat is trying to go through a door into the player's current room, or is trying to get out of a box.

Similarly, you can use cat.startled in the wake-up message to see if the cat woke prematurely because the player disturbed it.


Log in to post a reply.

Support

Forums