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"); }