Quest 6 tips : unveil the map of a region with an item


I am trying to replicate the game mechanic of displaying the complete map of a region when the player gets their hands on a map, like in early survival horror games.

Here is how I managed it.
Please note that my method uses the taking of the item, but this is also doable with the examine command and a flag in the item.

Step 1 - Your map item

createItem("b1Map", TAKEABLE(), {
  loc: "cellA",
  alias: "basement map",
  examine: "A map of the basement. It is labeled \"B1 - Detention\"",
  afterMove: function(toLoc, fromLoc) {
    msg("You note the locations.")

Step 2 - Setting preparations

settings.mapShowNotVisited = false
settings.mapGetStartingLocations = function() {
  const start1 = w.cellA
  start1.mapX = 0
  start1.mapY = 0
  start1.mapZ = 0
  start1.mapRegion = 0
  return [start1]
settings.maps = {0 : "b1Map"}

settings.maps is a dictionary with the mapRegion number as key, and the name of the map item as value.

Step 3 - Customize mapUpdate to check for the map

// Draw the map
// It collects all the SVG in five lists, which are effectively layers.
// This means all the exits appear in one layer, all the labels in another
// and so labels are always on top of exits
map.update = function() {
  // grab the current room region and level. If the room is missing either, give up now!
  const level = w[player.loc].mapZ
  const region = w[player.loc].mapRegion
  if (level === undefined || region === undefined) return
  if (w[player.loc].mapIgnore) return

  // Search for a map item corresponding to the current region.
  const mapItem = w[settings.maps[region]];
  // Stuff gets put in any of several layers, which will be displayed in this order
  const lists = {}
  for (let el of map.layers) lists[] = ['', '<g id="otherLevels-layer" ' + el.attrs + '>']

  // Loop through every room
  for (let key in w) {
    const room = w[key]
    // Do not map if in another region (if region is true, the room can handle it)
    // Only show if visited unless mapShowNotVisited or the player has the map of the region
    if (room.mapRegion !== region && room.mapRegion !== true) continue
    if ((mapItem === undefined || mapItem.loc !== && !settings.mapShowNotVisited && !room.visited) continue
    // Call mapDraw on the room if it has that, otherwise the default version
    (room.mapDraw ? room : map).mapDraw(lists, region, level, room)

  // Add it all together
  const result = settings.mapDefs ? settings.mapDefs() : []
  for (let key in lists) {
    for (let el of lists[key]) result.push(el)
  if (settings.mapExtras) result.push(...settings.mapExtras())
  result.push(settings.mapMarker ? settings.mapMarker(w[player.loc]) : map.marker(w[player.loc].mapX, w[player.loc].mapY))

  // Centre the view on the player, and draw it
  const x = w[player.loc].mapX - settings.mapWidth/2
  const y = -settings.mapHeight/2 + w[player.loc].mapY
  draw(settings.mapWidth, settings.mapHeight, result, {destination:'quest-map', x:x, y:y})

Why am I using a specific dictionary ? Because iterating over the keys of a dictionary is time-consuming, while getting a specific value with the corresponding key is near-instantaneous. So we spare ourselves from doing two loops inside the world dictionary.

Log in to post a reply.