[SOLVED] Hide inactive map layers

By default, the map shows inactive layers (i.e. the ones the player isn't on on the z-axis) as a faded but still present render. I'm trying to find a way to hide these inactive layers altogether without the game "forgetting" what rooms the player already explored when a layer is left and returned to. I did manage to find a way to do this by modifying the CoreGrid library's Grid_Redraw function to only draw rooms on the active layer (see below) and clearing/redrawing the grid whenever the player changes levels.

  <function name="Grid_Redraw">
    foreach (object, AllObjects()) {
      if (Grid_GetRoomBooleanForPlayer(game.pov, object, "grid_isdrawn") and (Grid_GetGridCoordinateForPlayer (game.pov, player.parent, "z").toString() = DictionaryItem(Grid_GetPlayerCoordinatesForRoom (game.pov, object),"z").toString())) {
        Grid_DrawRoom (object, true, game.pov)
      }
    }
  </function>

(Also, here's the code I use to actually redraw the grid on layer changes, just in case there's something screwy here:)

          JS.Grid_ClearAllLayers()
          Grid_Redraw()
          Grid_DrawPlayerInRoom(player.parent)

However, this seems to degrade performance over time, which I assume is due to the constant redrawing. Would there be another, more elegant way to hide these inactive map layers?


Many of these functions are defined in grid.js as well, and are underlying JS functions. I'm still not 100% how the architecture of Quest works, but I know that if I was able to change the activateLayer function, which is where opacity of inactive layers is defined (as a static value of 0.2), I'd be able to do what I want far more easily (essentially just by changing that static opacity to 0). I'm just not sure how to do this, or if it's even possible, given that overriding JS functions in Quest doesn't seem to be readily doable. Or am I missing something?

Here's the function:

function activateLayer(index) {
    showCustomLayer(false);
    layers[getLayerIndex(index)].activate();
    layers[getLayerIndex(index)].opacity = 1;
    if (currentLayer != index) {
        layers[getLayerIndex(currentLayer)].opacity = 0.2;
        currentLayer = index;
    }
}

Overriding javascript functions in Quest is mostly easy, because javascript treats functions like any other property or variable.

However, the grid functions are an exception. Many of them are protected inside a closure, meaning that they aren't accessible to the main code. Their status variables, things such as currentLayer, are also stored as local variables rather than properties, meaning that even if you could replace the function, there's no way to get access to those variables unless you can find a function containing an eval somewhere inside the closure.

However, there are ways around this. The functions which are called from Quest are accessible, because they are properties of an interface object, gridApi. You can replace these functions, but your new versions can't use any of the local variables that they have access to.

My first guess would be to try replacing gridApi.drawPlayer, which will probably be called every time the player changes layer. It already calls activateLayer, so you can assume that any layers with opacity 0.2 aren't the current one. In this case, I prefer to check for fractional opacity, because comparisons of decimals may be subject to rounding errors.

So you'd end up with pseudocode that looks like:

$(function () {// This is a javascript closure; it keeps our local variables local
  var original_drawplayer = gridApi.drawPlayer;// make a copy of the original function

  gridApi.drawPlayer = function(x, y, z, r, b, w, f) {// and define a replacement function
    original_drawplayer(x, y, z, r, b, w, f)// Call the original function

    foreach (layer in layers) {// Need to find the layers
      if (layer.opacity < 0.5) layer.opacity = 0;
    }
  };
});

This code should do what you want, I think. But first we need to find the array of layers. The variable layers isn't accessible because it's a local variable - so we need to get it from somewhere else. Thankfully, Paperscript doesn't use a closure to hide its data, so we can access the array in there… it'll just take a little time to find it.

OK, here we go… this seems to work:

$(function () {
  var original_drawplayer = gridApi.drawPlayer;

  gridApi.drawPlayer = function(x, y, z, r, b, w, f) {
    original_drawplayer(x, y, z, r, b, w, f)

    $.each(paper.project.layers, function () {
      this.setVisible(this.getOpacity() > 0.5);
    });
  };
});

To include that from Quest, you could either put it in a javascript file (if you're using the desktop editor, which I'm not familiar with), or minify it so that you can include it in your UI Initialisation script (on the game's "Advanced Scripts" tab). A version suitable for including in Quest would be:

JS.eval("$(function(){var a=gridApi.drawPlayer;gridApi.drawPlayer=function(){a.apply(gridApi,arguments),$.each(paper.project.layers,function(){this.setVisible(.5<this.getOpacity())})}});")

Does that work for you? I haven't actually got a game with multiple map layers to test it on, so am writing code off the top of my head here. But it looks about right if I poke around with bits of the code in the JS console; so I'm moderately confident.


That works perfectly, thanks a ton!
Thanks as well for the very thorough explanation as to the approach to the problem, it's very helpful.


Log in to post a reply.

Support

Forums