diff --git a/devTools/FC.ts b/devTools/FC.ts index a3125ee3a5ec7815ea39d6a05b111eb68da4d2dc..0e0e0d1055519b5908e49ffc52af9029f75d87bf 100644 --- a/devTools/FC.ts +++ b/devTools/FC.ts @@ -256,6 +256,7 @@ namespace App { declare function appendNewElement<K extends keyof HTMLElementTagNameMap>(tag: K, parent: ParentNode, content?: string | Node, classNames?: string | string[]): HTMLElementTagNameMap[K]; } namespace View { } + namespace Navigation { } namespace SlaveSummary { type AppendRenderer = (slave: FC.SlaveState, parentNode: Node) => void; diff --git a/src/002-config/mousetrapConfig.js b/src/002-config/mousetrapConfig.js index df31dc5fb398961e29312bfaca764e0dab3ac096..720f0514fb2222e6d05c8f86013068f04352ce68 100644 --- a/src/002-config/mousetrapConfig.js +++ b/src/002-config/mousetrapConfig.js @@ -1,29 +1,137 @@ -/* eslint-disable */ -Mousetrap.bind("enter", function() { - $("#story-caption #endWeekButton a.macro-link").trigger("click"); +/** + * Expand mousetrap with multiple binds per key, passage dependent bindings and a menu for custom rebinding. + */ + +App.UI.Navigation = (function() { + /** + * @typedef action + * @property {Function} callback + * @property {Array<string>} combinations 0 <= length <= 2 + * @property {Array<string>} [passages] not existing means everywhere + * @property {string|function(): string} [uiName] allow different name in hotkey settings + */ + + /** + * The key is used in the hotkey settings to describe the action + * @type {object.<string, action>} + */ + const actions = {}; + + /** + * References a key combination to a set of actions + * @type {object.<string, Array<string>>} + */ + const bindings = {}; + + /** + * @param {string} name used as key + * @param {action} action + */ + function addDefault(name, action) { + actions[name] = action; + for (const binding of action.combinations) { + addBinding(name, binding); + } + } + + /** + * @param {string} action + * @param {string} combination + */ + function addBinding(action, combination) { + if (bindings[combination]) { + bindings[combination].push(action); + } else { + bindings[combination] = [action]; + Mousetrap.bind(combination, () => { + for (const binding of bindings[combination]) { + const action = actions[binding]; + // only activate callback if we are on the right passage + if (!action.passages || action.passages.includes(State.passage)) { + action.callback(); + } + } + }); + } + } + + /** + * @param {string} combination + */ + function removeBinding(combination) { + if (bindings[combination]) { + const index = bindings[combination].indexOf(category); + if (index > -1) { + bindings[combination].splice(index, 1); + } + if (bindings[combination].length === 0) { + delete bindings[combination]; + Mousetrap.unbind(combination); + } + } + } + + function hotkeysForAction(name) { + if (!actions[name]) { + return ""; + } + const c =actions[name].combinations + if (c.length === 0) { + return ""; + } + // TODO make key combinations look nicer + if (c.length === 1) { + return `[${c[0]}]`; + } + return `[${c[0]},${c[1]}]`; + } + + return { + add: addDefault, + hotkeys: hotkeysForAction, + //init: loadFromStorage, + //settings: settingsMenu, + }; +})(); + +// add hotkeys +App.UI.Navigation.add("Advance week", { + callback: function() { + $("#story-caption #endWeekButton a.macro-link").trigger("click"); + }, combinations: ["enter"] }); -Mousetrap.bind("space", function() { - $("#story-caption #nextButton a.macro-link").trigger("click"); +App.UI.Navigation.add("Continue/Back", { + callback: function() { + $("#story-caption #nextButton a.macro-link").trigger("click"); + }, combinations: ["space"] }); -Mousetrap.bind("left", function() { - $("#prevSlave a.macro-link").trigger("click"); - $("#prevChild a.macro-link").trigger("click"); +App.UI.Navigation.add("Previous Slave", { + callback: function() { + $("#prevSlave a.macro-link").trigger("click"); + }, combinations: ["left", "q"] }); -Mousetrap.bind("q", function() { - $("#prevSlave a.macro-link").trigger("click"); - $("#prevChild a.macro-link").trigger("click"); +App.UI.Navigation.add("Next Slave", { + callback: function() { + $("#nextSlave a.macro-link").trigger("click"); + }, combinations: ["right", "e"] }); -Mousetrap.bind("right", function() { - $("#nextSlave a.macro-link").trigger("click"); - $("#nextChild a.macro-link").trigger("click"); +App.UI.Navigation.add("Previous Child", { + callback: function() { + $("#prevChild a.macro-link").trigger("click"); + }, combinations: ["left", "q"] }); -Mousetrap.bind("e", function() { - $("#nextSlave a.macro-link").trigger("click"); - $("#nextChild a.macro-link").trigger("click"); +App.UI.Navigation.add("Next Child", { + callback: function() { + $("#nextChild a.macro-link").trigger("click"); + }, combinations: ["right", "e"] }); -Mousetrap.bind("f", function() { - $("#walkpast a.macro-link").trigger("click"); +App.UI.Navigation.add("walkpast", { + callback: function() { + $("#walkpast a.macro-link").trigger("click"); + }, combinations: ["f"] }); -Mousetrap.bind("h", function() { - $("#manageHG a").trigger("click"); +App.UI.Navigation.add("Manage HG", { + callback: function() { + $("#manageHG a").trigger("click"); + }, combinations: ["h"] }); diff --git a/src/gui/quicklinks.js b/src/gui/quicklinks.js index 5fda6b661f6c7dc1a85bb4b9f96c3142d5a7b9de..0f23b17fdde43112042ec3e460f8f492739d0eb1 100644 --- a/src/gui/quicklinks.js +++ b/src/gui/quicklinks.js @@ -177,49 +177,6 @@ App.UI.quickMenu = (function() { "Manage Corporation": () => V.corp.SpecToken > 0 && V.corp.SpecTimer === 0, }); - // setup hotkeys list, upper/lower case is important! - // Due to limitation to the key capture library keys cannot be used when they are already used in - // src/002-config/mousetrapConfig.js - const hotkeys = cleanPassageMapping({ - "BG Select": "b", - "Buy Slaves": "s", - edicts: "E", - Firebase: "z", - "Future Society": "f", - Main: "m", - "Manage Arcology": "c", - "Manage Corporation":"C", - "Manage Penthouse": "p", - "Manage Personal Affairs": "x", - "Neighbor Interact": "d", - Options: "o", - "Personal assistant options": "t", - "Personal Attention Select": "a", - Policies: "y", - propagandaHub: "H", - "Recruiter Select": "u", - riotControlCenter: "R", - "Rules Assistant": "r", - secBarracks: "A", - securityHQ: "S", - "Universal Rules": "v", - // Facilities - Brothel: "1", - Club: "2", - Arcade: "3", - Dairy: "4", - Farmyard: "5", - "Servants' Quarters": "6", - "Master Suite": "7", - Schoolroom: "8", - Spa: "9", - Nursery: "0", - Clinic: "shift+1", - Cellblock: "shift+2", - Incubator: "shift+3", - Pit: "shift+4", - }); - /** * The DOM element of name of the currently played passage or any of it's parents. Used during generation to * uncollapse the category with the current passage. @@ -235,14 +192,73 @@ App.UI.quickMenu = (function() { let hotkeysEnabled = false; // register hotkeys - for (const passage in hotkeys) { - Mousetrap.bind(hotkeys[passage], () => { - if (hotkeysEnabled - // the passage is accessible - && !(hiddenPassages[passage] && hiddenPassages[passage]())) { - Engine.play(passage); - } + // this is in it's own scope as we can forget the hotkeys object immediately afterwards + { + // setup hotkeys list, upper/lower case is important! + const hotkeys = cleanPassageMapping({ + "BG Select": "b", + "Buy Slaves": "s", + edicts: "E", + Firebase: "z", + "Future Society": "f", + Main: "m", + "Manage Arcology": "c", + "Manage Corporation": "C", + "Manage Penthouse": "p", + "Manage Personal Affairs": "x", + "Neighbor Interact": "d", + Options: "o", + "Personal assistant options": "t", + "Personal Attention Select": "a", + Policies: "y", + propagandaHub: "H", + "Recruiter Select": "u", + riotControlCenter: "R", + "Rules Assistant": "r", + secBarracks: "A", + securityHQ: "S", + "Universal Rules": "v", + // Facilities + Brothel: "1", + Club: "2", + Arcade: "3", + Dairy: "4", + Farmyard: "5", + "Servants' Quarters": "6", + "Master Suite": "7", + Schoolroom: "8", + Spa: "9", + Nursery: "0", + Clinic: "shift+1", + Cellblock: "shift+2", + Incubator: "shift+3", + Pit: "shift+4", }); + + // register + for (const passage of jumpTo) { + if (!hidden.includes(passage)) { + const action = { + callback: () => { + if (hotkeysEnabled + // the passage is accessible + && !(hiddenPassages[passage] && hiddenPassages[passage]())) { + Engine.play(passage); + } + }, + combinations: [], + }; + // add hotkey if there is one + if (hotkeys[passage]) { + action.combinations.push(hotkeys[passage]); + } + // custom ui text + if (uiNames[passage]) { + action.uiName = uiNames[passage]; + } + App.UI.Navigation.add(passage, action); + } + } } // setup history @@ -268,10 +284,7 @@ App.UI.quickMenu = (function() { history.shift(); } }); - Mousetrap.bind("backspace", () => { - // jump back in history - goBack(); - }); + App.UI.Navigation.add("Back in history", {callback: goBack, combinations: ["backspace"]}); /** * Goes back in history if possible. @@ -447,8 +460,9 @@ App.UI.quickMenu = (function() { Engine.play(passage); }; div.prepend(a); - if (hotkeys[passage]) { - div.append(" ", App.UI.DOM.makeElement("span", `[${hotkeys[passage]}]`, "hotkey")); + const hotkeyString = App.UI.Navigation.hotkeys(passage); + if (hotkeyString !== "") { + div.append(" ", App.UI.DOM.makeElement("span", hotkeyString, "hotkey")); } }