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) { state.world = story["world"]; initRoomState(state); } function initRoomState(state) { state.player.rooms = {}; Object.entries(state.world).forEach(([key, val]) => { state.player.rooms[key] = {}; }); } function resetControls(state) { 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("disabled"); button.setAttribute("disabled", "true"); button.textContent = dirs[dir]; moveHolder.appendChild(button); moveListeners[dir] = undefined; }); const actionHolder = document.querySelector("#actions"); actionHolder.innerHTML = ""; actionButtons = []; if (state.player.location) { if (state.world[state.player.location].actions) { state.world[state.player.location].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, state) { 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, state)) { return; } } } if (room.hooks) { for (let hook of room.hooks) { if (!hook(room, state)) { return; } } } if (exit.move) exit.move(from, state); if (from && from.exit) from.exit(from, state); if (room.move) room.move(room, state); if (room.enter) room.enter(room, state); state.player.location = dest; resetControls(state); refresh(); } function goToRoom(dest, state) { const from = state.world[state.player.location]; const room = state.world[dest]; if (room.hooks) { for (let hook of room.hooks) { if (!hook(room, state)) { return; } } } if (from && from.exit) from.exit(from, state); if (room.enter) room.enter(state.world[dest], state); state.player.location = dest; resetControls(state); refresh(); } function updateRoom(state) { 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); button.classList.add("disabled"); button.setAttribute("disabled", "true"); 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, state))) { return; } } button.textContent = dest.name; // if any condition fails, don't enable/add a listener if (exit.conditions) { if (!exit.conditions.every(cond => cond(room,state))) { return; } } button.classList.remove("disabled"); button.removeAttribute("disabled"); if (moveListeners[dir]) { button.removeEventListener("click", moveListeners[dir]); moveListeners[dir] = undefined; } moveFunc = () => { moveToRoom(room, exit, exit.target, state); }; button.addEventListener("click", moveFunc); moveListeners[dir] = moveFunc; button.addEventListener("mouseenter", () => { showActionDescription(exit.desc); }); button.addEventListener("mouseleave", () => { removeActionDescription(); }); }); } 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", () => { action.execute(room, state); refresh(); }); button.addEventListener("mouseenter", () => { showActionDescription(action.desc); }); button.addEventListener("mouseleave", () => { removeActionDescription(); }); } if (action.show) { if (!action.show.every(cond => cond(room, state))) { continue; } } keptButtons.push(actionButtons[index]); if (action.conditions) { if (!action.conditions.every(cond => cond(room, state))) { button.classList.add("disabled"); button.setAttribute("disabled", "true"); } } } 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)); } }