|  | dirs = {
  "up-left": "Northwest",
  "up": "North",
  "up-right": "Northeast",
  "left": "West",
  "right": "East",
  "down-left": "Southwest",
  "down": "South",
  "down-right": "Southeast",
  "ascend": "Up",
  "descend": "Down"
}
moveListeners = {
}
actionButtons = [
]
function initWorld(story) {
  state.world = story["world"];
  initRoomState(state);
}
function initRoomState() {
  state.player.rooms = {};
  Object.entries(state.world).forEach(([key, val]) => {
    state.player.rooms[key] = {};
  });
}
function resetControls() {
  const moveHolder = document.querySelector("#move-holder");
  moveHolder.innerHTML = "";
  Object.entries(dirs).forEach(([dir, name]) => {
    const button = document.createElement("button");
    button.classList.add("move-button")
    button.id = "move-" + dir;
    button.classList.add("missing");
    disableButton(button);
    button.textContent = dirs[dir];
    moveHolder.appendChild(button);
    moveListeners[dir] = undefined;
  });
  const actionHolder = document.querySelector("#actions");
  actionHolder.innerHTML = "";
  actionButtons = [];
  const actions = state.world[state.player.location].actions;
  if (actions) {
    actions.forEach(action => {
      actionButtons.push(undefined);
    });
  }
}
function showActionDescription(desc) {
  const descHolder = document.querySelector("#desc");
  descHolder.textContent = desc;
}
function removeActionDescription() {
  const descHolder = document.querySelector("#desc");
  descHolder.textContent = "";
}
function moveToRoom(src, exit, dest) {
  const from = state.world[state.player.location];
  const room = state.world[dest];
  if (exit.hooks) {
    for (let hook of exit.hooks) {
      if (!hook(room, exit)) {
        return;
      }
    }
  }
  if (room.hooks) {
    for (let hook of room.hooks) {
      if (!hook(room)) {
        return;
      }
    }
  }
  if (exit.move)
    exit.move(from);
  if (from && from.exit)
    from.exit(from);
  if (room.move)
    room.move(room);
  if (room.enter)
    room.enter(room);
  state.player.location = dest;
  resetControls(state);
  createStatDisplays(room.data.stats, "area");
  refresh();
}
function goToRoom(dest) {
  const from = state.world[state.player.location];
  const room = state.world[dest];
  if (room.hooks) {
    for (let hook of room.hooks) {
      if (!hook(room)) {
        return;
      }
    }
  }
  if (from && from.exit)
    from.exit(from);
  if (room.enter)
    room.enter(state.world[dest]);
  state.player.location = dest;
  resetControls(state);
  createStatDisplays(room.data.stats, "area");
  refresh();
}
function updateRoom() {
  const name = state.player.location;
  const room = state.world[name];
  if (!state.player.rooms[room.id]) {
    state.player.rooms[room.id] = {};
  }
  const areaName = document.querySelector("#area-name");
  const areaDesc = document.querySelector("#area-desc");
  areaName.textContent = room.name;
  areaDesc.textContent = room.desc;
  Object.entries(dirs).forEach(([dir, name]) => {
    const button = document.querySelector("#move-" + dir);
    disableButton(button);
    button.textContent = dirs[dir];
  });
  if (room.exits) {
    Object.entries(room.exits).forEach(([dir, exit]) => {
      const button = document.querySelector("#move-" + dir);
      const dest = state.world[exit.target];
      // don't even show an exit if this fails!
      if (exit.show) {
        if (!exit.show.every(cond => cond(room))) {
          return;
        }
      }
      button.classList.remove("missing");
      disableButton(button);
      button.textContent = dest.name;
      button.addEventListener("mouseenter", () => {
        showActionDescription(exit.desc);
      });
      button.addEventListener("mouseleave", () => {
        removeActionDescription();
      });
      // if any condition fails, don't turn it on and allow clicks
      if (exit.conditions) {
        if (!exit.conditions.every(cond => cond(room,state))) {
          return;
        }
      }
      enableButton(button);
      if (moveListeners[dir]) {
        button.removeEventListener("click", moveListeners[dir]);
        moveListeners[dir] = undefined;
      }
      moveFunc = () => {
        moveToRoom(room, exit, exit.target);
      };
      button.addEventListener("click", moveFunc);
      moveListeners[dir] = moveFunc;
    });
  }
  const actionHolder = document.querySelector("#actions");
  const existingButtons = Array.from(document.querySelectorAll("#actions > button"));
  const keptButtons = [];
  if (room.actions) {
    for (index in room.actions) {
      const action = room.actions[index];
      let button;
      if (actionButtons[index]) {
        button = actionButtons[index];
      }
      else {
        button = document.createElement("button");
        button.classList.add("action-button");
        actionButtons[index] = button;
        button.textContent = action.name;
        button.addEventListener("click", () => {
          if (!button.classList.contains("disabled")) {
            action.execute(room);
            refresh();
          }
        });
        button.addEventListener("mouseenter", () => {
          showActionDescription(action.desc);
        });
        button.addEventListener("mouseleave", () => {
          removeActionDescription();
        });
      }
      if (action.show) {
        if (!action.show.every(cond => cond(room))) {
          continue;
        }
      }
      keptButtons.push(actionButtons[index]);
      if (action.conditions) {
        if (!action.conditions.every(cond => cond(room))) {
          disableButton(button);
        } else {
          enableButton(button);
        }
      }
    }
    const removed = existingButtons.filter(button => {
      return !keptButtons.includes(button);
    });
    removed.forEach(button => actionHolder.removeChild(button));
    const added = actionButtons.filter(button => {
      return keptButtons.includes(button) && !existingButtons.includes(button);
    });
    added.forEach(button => actionHolder.appendChild(button));
  }
}
function disableButton(button) {
  button.setAttribute("tabindex", "-1");
  button.classList.add("disabled");
}
function enableButton(button) {
  button.removeAttribute("tabindex");
  button.classList.remove("disabled");
}
 |