modified OnEnterRoom

Hi!
Kind of dreading the response now, sure there'll be someone telling me it's easier to use the built-in one, and suggesting bodges to make it work as I intended. But, I want to share just in case anyone is interested in this.

I've modified the OnEnterRoom script (called from game.pov.changedparent, it runs all the onexit/onenter/ShowRoomDescription/etc scripts). My version is arranged differently, but should do exactly the same things as the core one in most circumstances.

Where it's different:

  • If beforeexit, onexit, beforefirstenter, or beforeenter scripts move the player back to the room they came from, it stops. It doesn't run the onenter scripts for that room, which seems rational to me because they never really left.
  • If any of those scripts moves the player somewhere else, it continues running the enter-scripts for the new destination (but only runs them once)
  • If description, firstenter, enter, or roomenter scripts move the player somewhere else, it will abort running the remaining scripts, and continue with the beforeexit script for the original destination. Scripts will not be run twice (I thought about having it complete the scripts anyway; but figured that in most cases I wouldn't want it to).
  • ShowRoomDescription and game.roomenter are not run if you do RemoveObject (player). I would assume that any script that removes the active player object is going to put them back before they get a turn, so we can rely on a description being output when they're returned to normal player space.

The code is pretty ugly right now, and may be a bit difficult to understand. But… would anyone else find this useful or interesting?


I surely would!


What happens when you remove the active player?


That does sound good. We do get people having issues when they try to move the player in an onenter script - which seems quite a reasonable thing to do. Anything to make these things just work would be good.


What happens when you remove the active player?

My experiments with other games suggest that setting the player's parent to null (which is what RemoveObject does) sometimes calls exit scripts for the current room and sometimes doesn't, and I hadn't been able to figure out what made the difference; then sometimes generates an error from within OnEnterRoom because it tries to check if game.pov.parent has script attributes without checking if it's null.

I don't know if changedparent is supposed to be called when the value is set to null; but it seems to be inconsistent. So my thought was that the best option is to make an explicit check and do nothing. That way, you could use:

RemoveObject (player)
MoveObject (player, roomA)

as a quick way to bypass the current room's exit script(s).

Actually, there's a better way to do that. I'm still fiddling with the code since I posted last, and I think I'm going to modify 2 lines so that calling it directly will work.
(So you could do mod_OnEnterRoom(roomA, roomB) and it will move the player to roomB, calling all the exit scripts for roomA and the entry scripts for roomB, even if roomA isn't actually the room they were in previously)

My original intention was just duplicating the OnEnterRoom script to call my mod_ShowRoomDescription(room), which doesn't add blank lines if an empty section has a line break both before and after, and also allows me to easily add extra sections to the description.


I thought I had a finished version, then started changing things.

Here's my modified function mod_OnEnterRoom(oldRoom, newRoom) as it stands; not fully tested yet, but feedback welcome.

// The following line copied from the core version of this function
// I'm assuming this may be set to true in the editor, and is set false here rather than needing to check somehow if this is the first room we're entering
game.displayroomdescriptiononstart = false
//
if (not HasString(game.pov, "onenter_state")) {
  // Not currently entering a room, so we set up state
  game.pov.onenter_state = "beforeexit"
  game.pov.onenter_entering = newRoom
  if (not IsDefined("oldRoom")) {
    oldRoom = null
  }
  if (oldRoom = null) {
    game.pov.onenter_leaving = null
  }
  else {
    if (game.clearscreenonroomenter) {
      ClearScreen
      if (not game.currentexitmessage = null) {
        msg (game.currentexitmessage)
        game.currentexitmessage = null
      }
    }
    game.pov.onenter_leaving = oldRoom
  }
}
state = game.pov.onenter_state
wasstate = state     // So we can check if any of those enter/exit scripts wants to mess around with our progress.
// The following variable is used in exactly one place; but web editor falls over if asked to parse `if((A = B) and (X = Y))` when they've got dots in, and fails to save correctly
//
entering = game.pov.onenter_entering
if ((state = "finish") or (not entering = newRoom)) {
  // we've been called by game.pov.changedparent from an onenter/etc script.
  // Just change the variables ready for next time
  // OR
  // The only way we can get here when state is "finish" is if this script called `game.pov.parent = newRoom`
  //   so we need to do nothing
  //
  // The frame that called that script will notice the changes after we return
  game.pov.onenter_entering = newRoom
  game.pov.onenter_leaving = oldRoom
}
else {
  // We've been called by a previous iteration of ourselves
  switch (state) {
    case ("beforeexit") {
      if (oldRoom = null) {
        // If oldRoom is null, we skip straight to entering the new room
        state = "beforefirstenter"
      }
      else {
        if (HasScript(oldRoom, "beforeexit")) {
          do (oldRoom, "beforeexit")
        }
        // If 'beforeexit' changes the target room, we never reached the original target room,
        // so just change the target and then proceed with 'onexit'
        // unless it resets us to the original room, in which case stop
        if (game.pov.onenter_entering = oldRoom) {
          state = "finish"
        }
        else {
          newRoom = game.pov.onenter_entering
          state = "onexit"
        }
      }
    }
    case ("onexit") {
      if (HasScript(oldRoom, "onexit")) {
        do (oldRoom, "onexit")
      }
      // If 'onexit' changes the target room, we never reached the original target room, so just change the target and then proceed with 'onexit'
      // unless it resets us to the original room, in which case stop
      if (game.pov.onenter_entering = oldRoom) {
        state = "finish"
      }
      else {
        newRoom = game.pov.onenter_entering
        state = "beforefirstenter"
      }
    }
    case ("beforefirstenter") {
      if (newRoom = null) {
        // If the player is being moved to 'null', we can only assume a script is doing something weird.
        // In this case, there's no need to check for scripts or even show a description.
        state = "finish"
      }
      else {
        if (not GetBoolean(newRoom, "visited")) {
          if (HasScript(newRoom, "beforefirstenter")) {
            do (newRoom, "beforefirstenter")
          }
        }
        // If 'beforefirstenter' changes the target room, we never reached the target room,
        // so run this script for the new target instead
        if (game.pov.onenter_entering = newRoom) {
          state = "beforeenter"
        }
        else {
          newRoom = game.pov.onenter_entering
        }
      }
    }
    case ("beforeenter") {
      if (HasScript(newRoom, "beforeenter")) {
        do (newRoom, "beforeenter")
      }
      // If 'beforeenter' changes the target room, we never reached the target room,
      // so go back to 'beforefirstenter' for the new target instead
      if (game.pov.onenter_entering = newRoom) {
        state = "description"
      }
      else {
        state = "beforefirstenter"
        newRoom = game.pov.onenter_entering
      }
    }
    case ("description") {
      // following code copied wholesale from the original OnEnterRoom
      // hope I've not broken it somehow
      if (game.gridmap) {
        Grid_CalculateMapCoordinates (newRoom, game.pov)
        Grid_DrawPlayerInRoom (newRoom)
      }
      if (IsDefined("oldRoom")) {
        if (oldRoom <> null and game.changeroom_newline and not game.command_newline) {
          msg ("")
        }
      }
      request (UpdateLocation, CapFirst(GetDisplayName(newRoom)))
      roomFrameExists = false
      if (HasString(newRoom, "picture")) {
        if (LengthOf(newRoom.picture) > 0) {
          roomFrameExists = true
          SetFramePicture (newRoom.picture)
        }
      }
      if (game.clearframe and not roomFrameExists) {
        ClearFramePicture
      }
      if (game.showdescriptiononenter) {
        ShowRoomDescription
      }
      // If ShowRoomDescription evicts us from a room (why would you do that‽ WHY?!)
      // then we 'have entered'; so we have to run this room's exit scripts
      if (newRoom = game.pov.onenter_entering) {
        state = "roomenter"
      }
      else {
        oldRoom = game.pov.onenter_leaving
        newRoom = game.pov.onenter_entering
        state = "beforeexit"
        newRoom.visited = true
      }
    }
    case ("roomenter") {
      if (HasScript(game, "roomenter")) {
        do (game, "roomenter")
      }
      // If any 'enter' script changes the target room, we reached the target room,
      // so go back to the beginning of the process
      if (newRoom = game.pov.onenter_entering) {
        if (GetBoolean(newRoom, "visited")) {
          state = "enter"
        }
        else {
          state = "firstenter"
        }
      }
      else {
        oldRoom = game.pov.onenter_leaving
        newRoom = game.pov.onenter_entering
        state = "beforeexit"
      }
      newRoom.visited = true
    }
    case ("firstenter") {
      if (HasScript(newRoom, "firstenter")) {
        do (newRoom, "firstenter")
      }
      // If any 'enter' script changes the target room, we reached the target room,
      // so go back to the beginning of the process
      if (newRoom = game.pov.onenter_entering) {
        state = "enter"
      }
      else {
        oldRoom = game.pov.onenter_leaving
        newRoom = game.pov.onenter_entering
        state = "beforeexit"
      }
    }
    case ("enter") {
      if (HasScript(newRoom, "enter")) {
        do (newRoom, "enter")
      }
      // If any 'enter' script changes the target room, we reached the target room,
      // so go back to the beginning of the process
      if (newRoom = game.pov.onenter_entering) {
        state = "finish"
      }
      else {
        oldRoom = game.pov.onenter_leaving
        newRoom = game.pov.onenter_entering
        state = "beforeexit"
      }
    }
    default {
      msg ("{b:ERROR!} Unexpected state in mod_OnEnterRoom:"+state)
      state = "finish"
    }
  }
  if (not wasstate = game.pov.onenter_state) {
    // If an enter/exit script manually changes `game.pov.onenter_state`, we let
    //   it do whatever it wants with the next step, overriding the logic above.
    state = game.pov.onenter_state
    oldRoom = game.pov.onenter_leaving
    newRoom = game.pov.onenter_entering
  }
  if (state = "finish") {
    // Finished!
    game.pov.parent = newRoom
    game.pov.onenter_state = null
    game.pov.onenter_entering = null
    game.pov.onenter_leaving = null
    if (game.gridmap) {
      MergePOVCoordinates
    }
  }
  else {
    // I know tail recursion is inefficient, but it seems better than many, many nested 'on ready's
    on ready {
      game.pov.onenter_state = state
      mod_OnEnterRoom (oldRoom, newRoom)
    }
  }
}

And the player.changedparent script to call it:

  if (game.pov = this) {
    if (IsDefined("oldvalue")) {
      mod_OnEnterRoom (oldvalue, this.parent)
    }
    else {
      mod_OnEnterRoom (null, this.parent)
    }
  }
  this.hasbeenmoved = true

Editing in progress; so there may be oddities and bits in there that are no longer relevant. But hopefully you can see what I was aiming for.


If you'd rather see it in action, here's a test game with these functions.

http://textadventures.co.uk/games/view/luz7uwwsuuqfmk-pmtnx8w/disposable-game

Would love to see if other people can find any bugs I'm not working on yet.


Very, very nice!


This is the only thing I noticed (I assume this is precisely the sort of thing you're looking for. ):

You can see two fishs and a stone.


Yeah, I saw that but I can't figure out…

Ugh. The "beforefirstenter" script for the first room is getting run before the initialisation scripts for the objects in other rooms.
I realised that the stock ShowRoomDescription would get run once on startup, so I disabled room description on startup and set the player object to run mod_OnRoomEnter at the end of it's initialisation.

Which means that the "onenter" script for the start room runs, and clones any fish in the starting room before the initialisation script that sets their pluralalias attributes.

So how do I fix that?

Edit: I'm an idiot.
To fix that, I move the player object to the very end of the object list, and put mod_OnRoomEnter(null, startroom) in its initialisation script. Putting it at the end of the list ensures that it's the last initialise script to run.


I'm an idiot.

I don't think it counts as idiocy when you catch your own mistake. (Ha! I hope that's true, anyway! Or I'm screwed!)


Log in to post a reply.

Support

Forums