From 65bf3a776dc3f4f269c353ced2a2fc2e32a98432 Mon Sep 17 00:00:00 2001 From: Pregmodder <pregmodder@gmail.com> Date: Sun, 20 Dec 2020 23:14:39 -0500 Subject: [PATCH] Managed options.js collision in the worst/best way possible. --- Changelog.txt | 7 + .../{options.js => optionsPassage.js} | 0 src/gui/options/options.js | 1410 ++++++++++++----- src/gui/options/optionsGroup.js | 465 ++++++ src/gui/options/optionsPassage.js | 1061 ------------- 5 files changed, 1475 insertions(+), 1468 deletions(-) rename src/005-passages/{options.js => optionsPassage.js} (100%) create mode 100644 src/gui/options/optionsGroup.js delete mode 100644 src/gui/options/optionsPassage.js diff --git a/Changelog.txt b/Changelog.txt index 2c325881265..0829a6ba3d2 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -2,6 +2,13 @@ Pregmod 0.10.7.1-3.8.x + -learned how to add vectors + -fixed nicea event chain + -fixed issues with the TFS + -clothing data overhaul + -description options converted to JS + -fixes + 12/13/2020 3 diff --git a/src/005-passages/options.js b/src/005-passages/optionsPassage.js similarity index 100% rename from src/005-passages/options.js rename to src/005-passages/optionsPassage.js diff --git a/src/gui/options/options.js b/src/gui/options/options.js index a6ffd817607..74cbea915f9 100644 --- a/src/gui/options/options.js +++ b/src/gui/options/options.js @@ -1,465 +1,1061 @@ -App.UI.OptionsGroup = (function() { - class Row { - /** - * @param {HTMLDivElement} container - */ - render(container) {} // jshint ignore:line - } +App.UI.optionsPassage = function() { + const el = new DocumentFragment(); + V.passageSwitchHandler = App.EventHandlers.optionsChanged; + el.append(intro()); + + // Results + const results = document.createElement("div"); + results.id = "results"; + el.append(results); + + App.UI.tabBar.handlePreSelectedTab(V.tabChoice.Options); + // TODO: move me /** - * @typedef value - * @property {*} value - * @property {string} [name] - * @property {string} mode - * @property {number} [compareValue] - * @property {string} [descAppend] can be SC markup - * @property {boolean} [on] - * @property {boolean} [off] - * @property {boolean} [neutral] - * @property {Function} [callback] + * + * @param {string} id + * @param {Node} element + * @returns {HTMLSpanElement} */ + function makeSpanIded(id, element) { + const span = document.createElement("span"); + span.id = id; + span.append(element); + return span; + } - class Option extends Row { - /** - * @param {string} description can be SC markup - * @param {string} property - * @param {object} [object=V] - */ - constructor(description, property, object = V) { - super(); - this.description = description; - this.property = property; - this.object = object; - /** - * @type {Array<value>} - */ - this.valuePairs = []; - } + const tabCaptions = { + "display": 'Display', + "contentFlavor": 'Content & flavour', + "mods": 'Mods', + "debugCheating": 'Debug & cheating', + "experimental": 'Experimental' + }; - /** - * @param {*} name - * @param {*} [value=name] - * @param {Function} [callback] - * @returns {Option} - */ - addValue(name, value = name, callback = undefined) { - this.valuePairs.push({ - name: name, value: value, mode: "=", callback: callback - }); - return this; - } + const tabBar = App.UI.DOM.appendNewElement("div", el, '', "tab-bar"); + tabBar.append( + App.UI.tabBar.tabButton('display', tabCaptions.display), + App.UI.tabBar.tabButton('content-flavor', tabCaptions.contentFlavor), + App.UI.tabBar.tabButton('mods', tabCaptions.mods), + App.UI.tabBar.tabButton('debug-cheating', tabCaptions.debugCheating), + App.UI.tabBar.tabButton('experimental', tabCaptions.experimental), + ); - /** - * @param {Array<*|Array>} values - * @returns {Option} - */ - addValueList(values) { - for (const value of values) { - if (Array.isArray(value)) { - this.addValue(value[0], value[1]); - } else { - this.addValue(value); - } - } - return this; - } + el.append(App.UI.tabBar.makeTab('display', makeSpanIded("content-display", display()))); + el.append(App.UI.tabBar.makeTab('content-flavor', makeSpanIded("content-content-flavor", contentFlavor()))); + el.append(App.UI.tabBar.makeTab('mods', makeSpanIded("content-mods", mods()))); + el.append(App.UI.tabBar.makeTab('debug-cheating', makeSpanIded("content-debug-cheating", debugCheating()))); + el.append(App.UI.tabBar.makeTab('experimental', makeSpanIded("content-experimental", experimental()))); - /** - * @param {Map} values - * @returns {Option} - */ - addValueMap(values) { - for (const [key, value] of values) { - this.addValue(key, value); - } - return this; - } + return el; - /** - * @param {*} value - * @param {number} compareValue - * @param {string} mode on of: "<", "<=", ">", ">=" - * @param {string} [name=value] - */ - addRange(value, compareValue, mode, name = value) { - this.valuePairs.push({ - name: name, value: value, mode: mode, compareValue: compareValue - }); - return this; - } + function intro() { + let links; + let options; + let r; + const el = new DocumentFragment(); - /** - * @param {Object} [params] - * @param {string} [params.unit] - * @param {boolean} [params.large=false] - * @returns {Option} - */ - showTextBox({unit, large = false} = {}) { - this.textbox = {unit: unit, large: large}; - return this; - } + options = new App.UI.OptionsGroup(); + options.addOption("End of week autosaving is currently", "autosave") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + el.append(options.render()); - /** - * @param {string} comment can be SC markup - * @returns {Option} - */ - addComment(comment) { - this.comment = comment; - return this; - } + App.UI.DOM.appendNewElement("div", el, `This save was created using FC version ${V.ver} build ${V.releaseID}. You are currently playing version: ${App.Version.base}, mod version: ${App.Version.pmod}, build: ${App.Version.release}${App.Version.commitHash ? `, commit: ${App.Version.commitHash}` : ``}`); - /** - * Adds a button that executes the callback when clicked AND reloads the passage - * - * @param {string} name - * @param {Function} callback - * @param {string} passage - */ - customButton(name, callback, passage) { - this.valuePairs.push({ - name: name, value: passage, callback: callback, mode: "custom" - }); - return this; - } + links = []; + links.push(App.UI.DOM.passageLink("Apply Backwards Compatibility Update", "Backwards Compatibility")); - /** - * @param {Node} node - * @returns {Option} - */ - addCustomDOM(node) { - this.valuePairs.push({ - value: node, mode: "DOM" - }); - return this; - } + links.push( + App.UI.DOM.link( + `Reset extended family mode controllers`, + () => { + resetFamilyCounters(); + const span = document.createElement("span"); + span.classList.add("note"); + App.UI.DOM.appendNewElement("span", span, "Done: ", "lightgreen"); + span.append("all family relations flushed and rebuilt."); + jQuery("#results").empty().append(span); + }, + [], + "", + "Clears and rebuilds .sister and .daughter tracking." + ) + ); - /* modify last added option */ - /** - * Added to the description if last added value is selected. - * example use: addValue(...).customDescription(...).addValue(...).customDescription(...) - * @param {string} description can be SC markup - */ - customDescription(description) { - this.valuePairs.last().descAppend = description; - return this; + if (isNaN(V.rep)) { + links.push( + App.UI.DOM.link( + `Reset Reputation (${V.rep})`, + () => { + V.rep = 0; + jQuery("#results").empty().append(`Reputation reset to ${V.rep}`); + }, + [], + "Options" + ) + ); } - /** - * @param {Function} callback gets executed on every button click. Selected value is given as argument. - */ - addCallback(callback) { - this.valuePairs.last().callback = callback; - return this; + if (isNaN(V.rep)) { + links.push( + App.UI.DOM.link( + `Reset Money (${V.cash})`, + () => { + V.cash = 500; + jQuery("#results").empty().append(`Cash reset to ${V.cash}`); + }, + [], + "Options" + ) + ); } - /** - * @param {Function} callback gets executed on every button click. Selected value is given as argument. - */ - addCallbackToEach(callback) { - this.valuePairs.forEach(pair => pair.callback = callback); - return this; + if (V.releaseID === 1057) { + links.push( + App.UI.DOM.link( + `Free male anatomy removal due to accidentally flawed updater`, + () => { + V.PC.dick = 0; + V.PC.balls = 0; + V.PC.prostate = 0; + V.PC.scrotum = 0; + V.PC.ballsImplant = 0; + jQuery("#results").empty().append(`Cash reset to ${V.cash}`); + }, + [], + "Options", + "Use this if your female PC picked up a few extra parts during the conversion process.", + ) + ); } - /** - * Mark option as on to style differently. - * @returns {Option} - */ - on() { - this.valuePairs.last().on = true; - return this; - } + App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links)); - /** - * Mark option as off to style differently. - * @returns {Option} - */ - off() { - this.valuePairs.last().off = true; - return this; + if ((V.releaseID >= 1000) || V.ver.startsWith("0.9") || V.ver.startsWith("0.8") || V.ver.startsWith("0.7") || V.ver.startsWith("0.6")) { + App.UI.DOM.appendNewElement("h3", el, `NEW GAME PLUS`); + r = []; + r.push(`You can begin a new game with up to five (or more) of your current slaves, although starting resources other than these slaves will be reduced. New Game Plus`); + r.push(App.UI.DOM.makeElement("span", "MAY", "yellow")); + r.push(`work across versions. To attempt to migrate a save across versions:`); + App.Events.addNode(el, r, "div", "note"); + + const ngpInstructions = document.createElement("ol"); + App.UI.DOM.appendNewElement("li", ngpInstructions, "Save on this screen", "note"); + App.UI.DOM.appendNewElement("li", ngpInstructions, "Re-open the .html in a new tab then load the above save.", "note"); + App.UI.DOM.appendNewElement( + "li", + ngpInstructions, + App.UI.DOM.link( + "Activate New Game Plus.", + () => { + V.ui = "start"; + }, + [], + "New Game Plus" + ), + "note" + ); + el.append(ngpInstructions); + } else { + App.UI.DOM.appendNewElement("div", el, `New Game Plus is not available because this game was not started with a compatible version.`, "note"); } + return el; + } - /** - * Mark option as neutral to style differently. - * @returns {Option} - */ - neutral() { - this.valuePairs.last().neutral = true; - return this; + function display() { + const el = new DocumentFragment(); + let options; + let r; + + App.UI.DOM.appendNewElement("h2", el, "Reports"); + + options = new App.UI.OptionsGroup(); + + options.addOption("End week report descriptive details are", "showEWD") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("End week report performance details are", "showEWM") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Master Suite report details such as slave changes are", "verboseDescriptions") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("End week societal effects from slaves are", "compressSocialEffects", V.UI) + .addValue("Expanded", 0).on().addValue("Compacted", 1).off(); + + options.addOption("Accordion on week end defaults to", "useAccordion") + .addValue("Open", 0).on().addValue("Collapsed", 1).off(); + + options.addOption("Economic Tabs on weekly reports are", "useTabs") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Economic detail sheets for facilities are", "showEconomicDetails") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Economic report neighbor details such as trade impacts on culture are", "showNeighborDetails") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Numeric formatting is currently", "formatNumbers") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("This will comma-format numbers in some areas."); + + el.append(options.render()); + + App.UI.DOM.appendNewElement("h2", el, "General"); + + options = new App.UI.OptionsGroup(); + + options.addOption("Main menu leadership controls displayed", "positionMainLinks") + .addValueList([["Above", 1], ["Above and below", 0], ["Below", -1]]); + + options.addOption("New Model UI", "newModelUI") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Penthouse Facility Display", "verticalizeArcologyLinks") + .addValueList([["Triple column", 3], ["Double Column", 2], ["Single Column", 1], ["Collapsed", 0]]); + + options.addOption("Main menu arcology description", "seeArcology") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Main menu desk description", "seeDesk") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Main menu newsfeed", "seeFCNN") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Tips from the Encyclopedia are", "showTipsFromEncy") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Help tooltips are", "tooltipsEnabled") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment(`This is mostly for new players. <span class='exampleTooltip noteworthy'>Colored text</span> can have tooltips.`); + + options.addOption("Main menu slave tabs are", "useSlaveSummaryTabs") + .addValue("Enabled", 1).on().addValue("CardStyle", 2).on().addValue("Disabled", 0).off(); + + options.addOption("The slave Quick list in-page scroll-to is", "useSlaveListInPageJSNavigation") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Condense special slaves into their own tab", "useSlaveSummaryOverviewTab") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Interactions with your fucktoys are", "fucktoyInteractionsPosition") + .addValueList([["next to them", 1], ["at page bottom", 0]]); + + options.addOption("Hide tabs in Slave Interact", "slaveInteractLongForm") + .addValue("Enabled", true).on().addValue("Disabled", false).off(); + + options.addOption("Line separations are", "lineSeparations") + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + el.append(options.render()); + + r = []; + r.push(`UI theme selector. Allows to select a single CSS file to be loaded.`); + r.push(App.UI.DOM.makeElement("span", `The file has to be located in the same directory as the HTML file otherwise it will simply not load at all.`, "red")); + r.push(App.UI.Theme.selector()); + App.Events.addParagraph(el, r); + + App.UI.DOM.appendNewElement("h2", el, "Sidebar"); + + options = new App.UI.OptionsGroup(); + + options.addOption("Cash is", "Cash", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("Upkeep is", "Upkeep", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("Sex slave count is", "SexSlaveCount", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("Room population is", "roomPop", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("GSP is", "GSP", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("Rep is", "Rep", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("Confirmation before ending a week is", "confirmWeekEnd", V.sideBarOptions) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Enabling this will open a dialog box to confirm you meant to end a week."); + + if (V.secExpEnabled > 0) { + options.addOption("Authority is", "Authority", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("Security is", "Security", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("Crime is", "Crime", V.sideBarOptions) + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); } - /** - * Puts the options in side a pulldown if there are at least 6. - * Not counting text boxes or comments. - * @returns {Option} - */ - pulldown() { - this.enablePulldown = true; - return this; + el.append(options.render()); + + + App.UI.DOM.appendNewElement("h2", el, "Images"); + el.append(App.UI.artOptions()); + + return el; + } + + function contentFlavor() { + const el = new DocumentFragment(); + let r; + let options; + + App.UI.DOM.appendNewElement("h2", el, "Content"); + + r = []; + r.push("More granular control of what appears is in"); + r.push(App.UI.DOM.passageLink("Description Options", "Description Options")); + App.Events.addNode(el, r, "div", "note"); + + options = new App.UI.OptionsGroup(); + + options.addOption("The difficulty setting is currently set to", "baseDifficulty") + .addValueList([["Very easy", 1], ["Easy", 2], ["Default", 3], ["Hard", 4], ["Very hard", 5]]); + + options.addOption("Slaves falling ill is currently", "seeIllness") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Will not affect existing ill slaves already in-game."); + + options.addOption("Extreme content like amputation is currently", "seeExtreme") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Will not affect extreme surgeries already applied in-game."); + + options.addOption("Bestiality related content is currently", "seeBestiality") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Watersports related content is currently", "seePee") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Incest content is currently", "seeIncest") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Pregnancy related content is currently", "seePreg") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Will not affect existing pregnancies already in-game."); + + options.addOption("Child gender to be generated based off dick content settings", "seeDicksAffectsPregnancy") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment(`${(V.seeDicksAffectsPregnancy === 1) ? `Currently ${V.seeDicks}% of children will be born male. ` : ``}Will not affect existing pregnancies already in-game.`); + + if (V.seeDicksAffectsPregnancy === 0) { + options.addOption("XX slaves only father daughters", "adamPrinciple") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Will not affect existing pregnancies already in-game."); } - /** - * @param {HTMLDivElement} container - */ - render(container) { - /* left side */ - const desc = document.createElement("div"); - desc.className = "description"; - $(desc).wiki(this.description); - container.append(desc); - - /* right side */ - const currentValue = this.object[this.property]; - let anySelected = false; - - const buttonGroup = document.createElement("div"); - buttonGroup.classList.add("button-group"); - if (!this.enablePulldown || this.valuePairs.length < 6) { - for (const value of this.valuePairs) { - if (value.mode === "DOM") { - /* insert DOM and go to next element */ - buttonGroup.append(value.value); - continue; - } - const button = document.createElement("button"); - button.append(value.name); - if (value.on) { - button.classList.add("on"); - } else if (value.off) { - button.classList.add("off"); - } else if (value.neutral) { - button.classList.add("neutral"); - } - if (value.mode === "custom") { - button.onclick = () => { - value.callback(); - if (value.value) { - Engine.play(value.value); - } else { - App.UI.reload(); - } - }; - } else { - if (value.mode === "=" && currentValue === value.value) { - button.classList.add("selected", "disabled"); - anySelected = true; - if (value.descAppend !== undefined) { - desc.append(" "); - $(desc).wiki(value.descAppend); - } - } else if (!anySelected && inRange(value.mode, value.compareValue, currentValue)) { - button.classList.add("selected"); - // disable the button if clicking it won't change the variable value - if (currentValue === value.value) { - button.classList.add("disabled"); - } - anySelected = true; - if (value.descAppend !== undefined) { - desc.append(" "); - $(desc).wiki(value.descAppend); - } - } - button.onclick = () => { - this.object[this.property] = value.value; - if (value.callback) { - value.callback(); - } - App.UI.reload(); - }; - } - buttonGroup.append(button); - } - } else { - let matchFound = false; - let select = document.createElement("select"); - - for (const value of this.valuePairs) { - let el = document.createElement("option"); - el.textContent = value.name; - el.value = value.value; - if (this.object[this.property] === value.value) { - el.selected = true; - matchFound = true; - } - select.appendChild(el); - } - if (!matchFound) { - select.selectedIndex = -1; - } - select.onchange = () => { - const O = select.options[select.selectedIndex]; - if (isNaN(Number(O.value))) { - this.object[this.property] = O.value; - } else { - this.object[this.property] = Number(O.value); - } - const originalObj = this.valuePairs.find(obj => obj.name === O.textContent); - if (originalObj && typeof originalObj.callback === "function") { - originalObj.callback(); - } - App.UI.reload(); - }; - buttonGroup.append(select); + options.addOption("Extreme pregnancy content like broodmothers is currently", "seeHyperPreg") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Will not affect existing hyperpregnancies already in-game."); + + options.addOption("Pregnancy complications due to multiples and body size are currently", "dangerousPregnancy") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption(`Precocious puberty (pregnancy younger than ${V.fertilityAge})`, "precociousPuberty") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Will not affect existing precocious puberty cases already in-game."); + + options.addOption("Slaves with fat lips or heavy oral piercings may lisp", "disableLisping") + .addValue("Yes", 0).on().addValue("No", 1).off(); + + options.addOption("Disables the long term damage mechanic. //Temp option//", "disableLongDamage") + .addValue("Enabled", 0).on().addValue("Disabled", 1).off(); + + options.addOption("Experimental male pronouns are currently", "diversePronouns") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Apply Backwards Compatibility after changing to update slave's pronouns. Not all scenes support male pronouns and this is not yet incorporated into the lore or mechanics."); + + options.addOption("Male slave names are currently", "allowMaleSlaveNames") + .addValue("Enabled", true).on().addValue("Disabled", false).off() + .addComment("This only affects slave generation and not your ability to name your slaves."); + + options.addOption("Missing slave names are currently", "showMissingSlaves") + .addValue("Enabled", true).on().addValue("Disabled", false).off(); + + el.append(options.render()); + + App.UI.DOM.appendNewElement("h2", el, `Intersecting mechanics`); + + options = new App.UI.OptionsGroup(); + + options.addOption("Slave assets affected by weight is", "weightAffectsAssets") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Diet will still affect asset size."); + + options.addOption("Curative side effects are", "curativeSideEffects") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("If enabled, curatives have a chance to give slaves harmful side effects."); + + el.append(options.render()); + + App.UI.DOM.appendNewElement("h2", el, `Flavour`); + + options = new App.UI.OptionsGroup(); + + options.addOption("Slave reactions to facility assignments are", "showAssignToScenes") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Post sex clean up", "postSexCleanUp") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Appraisal miniscenes on slave sale are", "showAppraisal") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Assignment performance vignettes on the end week report are", "showVignettes") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Slaves can have alternate titles", "newDescriptions") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Family titles for relatives", "allowFamilyTitles") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Limit family growth", "limitFamilies") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Restricts acquisition of additional relatives, by means other than birth, for slaves with families."); + + options.addOption("Distant relatives such as aunts, nieces and cousins are", "showDistantRelatives") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + el.append(options.render()); + return el; + } + + function mods() { + const el = new DocumentFragment(); + let options; + + options = new App.UI.OptionsGroup(); + + options.addOption("The Special Force Mod is currently", "Toggle", V.SF) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("<div>This mod is triggered after week 72. It is non-canon where it conflicts with canonical updates to the base game.</div>"); + + options.addOption("The Security Expansion mod is", "secExpEnabled") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("<div>The mod can be activated in any moment, but it may result in unbalanced gameplay if activated very late in the game.</div>"); + + el.append(options.render()); + + if (V.secExpEnabled > 0) { + if (Object.values(V.SecExp).length === 0) { + App.SecExp.generalBC(); + Engine.play("Options"); } + App.UI.DOM.appendNewElement("h2", el, `Security Expansion mod options`); + if (V.terrain === "oceanic") { + App.UI.DOM.appendNewElement("div", el, `Oceanic arcologies are not by default subject to external attacks. You can however allow them to happen anyway. If you choose to do so please keep in mind that descriptions and mechanics are not intended for naval combat but land combat.`); + } + options = new App.UI.OptionsGroup(); - if (this.textbox) { - const isNumber = typeof currentValue === "number"; - const textbox = App.UI.DOM.makeTextBox(currentValue, input => { - this.object[this.property] = input; - App.UI.reload(); - }, isNumber); - if (isNumber) { - textbox.classList.add("number"); - } - if (this.textbox.large) { - textbox.classList.add("full-width"); - } - buttonGroup.append(textbox); - if (this.textbox.unit) { - buttonGroup.append(" ", this.textbox.unit); - } + if (V.SecExp.settings.battle.enabled > 0 || V.SecExp.settings.rebellion.enabled > 0) { + options.addOption("Detailed battle statistics are", "showStats", V.SecExp.settings) + .addValue("Shown", 1).on().addValue("Hidden", 0).off() + .addComment("Visibility of detailed statistics and battle turns."); + + options.addOption("Difficulty is", "difficulty", V.SecExp.settings) + .addValueList([["Extremely hard", 2], ["Very hard", 1.5], ["Hard", 1.25], ["Normal", 1], ["Easy", 0.75], ["Very easy", 0.5]]); + + options.addOption("Unit descriptions are", "unitDescriptions", V.SecExp.settings) + .addValue("Abbreviated", 1).addValue("Summarized", 0); } - if (this.comment) { - const comment = document.createElement("span"); - comment.classList.add("comment"); - $(comment).wiki(this.comment); - buttonGroup.append(comment); + options.addOption("Battles are", "enabled", V.SecExp.settings.battle) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Rebellions are", "enabled", V.SecExp.settings.rebellion) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + if (V.SecExp.settings.battle.enabled > 0) { + options.addOption("Battle frequency", "frequency", V.SecExp.settings.battle) + .addValueList([["Extremely high", 2], ["Very high", 1.5], ["High", 1.25], ["Normal", 1], ["Low", 0.75], ["Very low", 0.5]]); } - container.append(buttonGroup); - - function inRange(mode, compareValue, value) { - if (mode === "<") { - return value < compareValue; - } else if (mode === "<=") { - return value <= compareValue; - } else if (mode === ">") { - return value > compareValue; - } else if (mode === ">=") { - return value >= compareValue; - } - return false; + + if (V.SecExp.settings.rebellion.enabled > 0) { + options.addOption("Rebellion buildup", "speed", V.SecExp.settings.rebellion) + .addValueList([["Extremely fast", 2], ["Very fast", 1.5], ["Fast", 1.25], ["Normal", 1], ["Slow", 0.75], ["Very slow", 0.5]]); } - } - } - class Comment extends Row { - /** - * @param {string} comment can be SC markup - */ - constructor(comment) { - super(); - this.comment = comment; - this.long = false; - } - /** - * @param {HTMLDivElement} container - */ - render(container) { - /* left */ - container.append(document.createElement("div")); - - /* right */ - const comment = document.createElement("div"); - comment.classList.add("comment"); - $(comment).wiki(this.comment); - container.append(comment); - } - } + if (V.SecExp.settings.battle.enabled > 0) { + options.addOption("Commanders gain a prestige rank every 10 victories", "allowSlavePrestige", V.SecExp.settings.battle) + .addValue("Yes", 1).on().addValue("No", 0).off(); + } - class CustomRow extends Row { - /** - * @param {HTMLElement|string} element - */ - constructor(element) { - super(); - this.element = element; + if (V.SecExp.settings.battle.enabled > 0) { + options.addOption("Force battles", "force", V.SecExp.settings.battle) + .addValue("Yes", 1).on().addValue("No", 0).off(); + } + if (V.SecExp.settings.rebellion.enabled > 0) { + options.addOption("Force rebellions", "force", V.SecExp.settings.rebellion) + .addValue("Yes", 1).on().addValue("No", 0).off() + .addComment("Rebellions take precedence over Battles."); + } + + if (V.SecExp.settings.battle.enabled > 0) { + options.addOption("Late game major battles are", "enabled", V.SecExp.settings.battle.major) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + } + + if (V.SecExp.settings.battle.enabled > 0 && V.SecExp.settings.battle.major.enabled > 0) { + options.addOption("Multiplier is", "mult", V.SecExp.settings.battle.major) + .addValueList([["Extremely high", 2], ["Very high", 1.5], ["High", 1.25], ["Normal", 1], ["Low", 0.75], ["Very low", 0.5]]); + + options.addOption("This week a major battle is", "force", V.SecExp.settings.battle.major) + .addValue("Guaranteed", 1).on().addValue("Not guaranteed", 0).off(); + } + + if (V.SecExp.settings.battle.enabled > 0 && V.SecExp.settings.battle.major.enabled > 0) { + options.addOption("Gameover on battle loss", "gameOver", V.SecExp.settings.battle.major) + .addValue("Yes", 1).on().addValue("No", 0).off(); + } + + if (V.SecExp.settings.rebellion.enabled > 0) { + options.addOption("Gameover on rebellion loss", "gameOver", V.SecExp.settings.rebellion) + .addValue("Yes", 1).on().addValue("No", 0).off(); + } + + el.append(options.render()); + + const subHeading = document.createElement("div"); + subHeading.classList.add("subHeading"); + + if (V.debugMode || V.cheatMode || V.cheatModeM) { // TODO make me a fucking function + App.UI.DOM.appendNewElement("div", subHeading, "Debug/cheat", "bold"); + let td; + let links; + const table = document.createElement("table"); + table.classList.add("invisible"); + el.append(table); + + let tr = document.createElement("tr"); + tr.style.textAlign = "center"; + + td = createTd(); + links = []; + links.push( + App.UI.DOM.link( + "Set loyalty high", + () => { + changeLoyalty("high"); + }, + [], + "Options" + ) + ); + links.push( + App.UI.DOM.link( + "Set loyalty average", + () => { + changeLoyalty("average"); + }, + [], + "Options" + ) + ); + links.push( + App.UI.DOM.link( + "Set loyalty low", + () => { + changeLoyalty("low"); + }, + [], + "Options" + ) + ); + links.push( + App.UI.DOM.link( + "Randomize loyalty", + () => { + changeLoyalty("random"); + }, + [], + "Options" + ) + ); + + td.append(App.UI.DOM.generateLinksStrip(links)); + tr.append(td); + table.append(tr); + + tr = document.createElement("tr"); + tr.style.textAlign = "center"; + td = createTd(); + links = []; + links.push(App.UI.DOM.link( + "Give Authority", + () => { + V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 1000, 0, 20000); + }, + [], + "Options" + )); + links.push(App.UI.DOM.link( + "Remove Authority", + () => { + V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - 1000, 0, 20000); + }, + [], + "Options" + )); + td.append(App.UI.DOM.generateLinksStrip(links)); + tr.append(td); + table.append(tr); + + + tr = document.createElement("tr"); + td = document.createElement("td"); + td.style.textAlign = "right"; + links = []; + links.push(App.UI.DOM.link( + "Raise security", + () => { + V.SecExp.core.security = Math.clamp(V.SecExp.core.security + 5, 0, 100); + }, + [], + "Options" + )); + links.push(App.UI.DOM.link( + "Lower security", + () => { + V.SecExp.core.security = Math.clamp(V.SecExp.core.security - 5, 0, 100); + }, + [], + "Options" + )); + tr.append(App.UI.DOM.generateLinksStrip(links)); + tr.append(td); + + td = document.createElement("td"); + td.style.textAlign = "left"; + links = []; + links.push(App.UI.DOM.link( + "Raise crime", + () => { + V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + 5, 0, 100); + }, + [], + "Options" + )); + links.push(App.UI.DOM.link( + "Lower crime", + () => { + V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow - 5, 0, 100); + }, + [], + "Options" + )); + tr.append(App.UI.DOM.generateLinksStrip(links)); + tr.append(td); + table.append(tr); + + tr = document.createElement("tr"); + td = document.createElement("td"); + td.style.textAlign = "right"; + links = []; + links.push(App.UI.DOM.link( + "Give militia manpower", + () => { + V.SecExp.units.militia.free += 30; + }, + [], + "Options" + )); + links.push(App.UI.DOM.link( + "Remove militia manpower", + () => { + V.SecExp.units.militia.free = Math.max(V.SecExp.units.militia.free - 30, 0); + }, + [], + "Options" + )); + tr.append(App.UI.DOM.generateLinksStrip(links)); + tr.append(td); + + td = document.createElement("td"); + td.style.textAlign = "left"; + links = []; + links.push(App.UI.DOM.link( + "Give mercs manpower", + () => { + V.SecExp.units.mercs.free += 30; + }, + [], + "Options" + )); + links.push(App.UI.DOM.link( + "Remove mercs manpower", + () => { + V.SecExp.units.mercs.free = Math.max(V.SecExp.units.mercs.free - 30, 0); + }, + [], + "Options" + )); + tr.append(App.UI.DOM.generateLinksStrip(links)); + tr.append(td); + table.append(tr); + subHeading.append(table); + el.append(subHeading); + } /* closes cheatmode check */ + } /* closes SecExp check*/ + return el; + + function createTd() { + const td = document.createElement("td"); + td.style.columnSpan = "2"; + return td; } /** - * @param {HTMLDivElement} container + * + * @param {"high"|"average"|"low"|"random"} level */ - render(container) { - /** @type {HTMLDivElement} */ - const div = App.UI.DOM.makeElement("div", this.element, "custom-row"); - container.append(div); + function changeLoyalty(level) { + const numberMap = new Map([ + ["high", [80, 100]], + ["average", [40, 60]], + ["low", [20]], + ["random", [100]], + ]); + + for (const squad of App.SecExp.unit.humanSquads()) { + squad.loyalty = numberGenerator(); + } + + function numberGenerator() { + const range = numberMap.get(level); + if (range[1]) { + return random(range[0], range[1]); + } else { + return random(range[0]); + } + } } } - return class { - constructor() { - /** - * @type {Array<Row>} - */ - this.rows = []; - this.doubleColumn = false; + function debugCheating() { + const el = new DocumentFragment(); + let options; + let option; + let links; + let r; + const popCap = menialPopCap(); + + App.UI.DOM.appendNewElement("h2", el, `Debug`); + + options = new App.UI.OptionsGroup(); + + options.addOption("DebugMode is", "debugMode") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("This will add a Display Variables and Bug Report passage to the sidebar."); + + if (V.debugMode > 0) { + options.addOption("The custom function part of debug mode is", "debugModeCustomFunction") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); } - /** - * @returns {App.UI.OptionsGroup} - */ - enableDoubleColumn() { - this.doubleColumn = true; - return this; + option = options.addOption("Genetics array") + .customButton("Run test", () => { }, "test genetics"); + if (V.cheatMode === 1) { + option.customButton("Edit Genetics", () => { }, "Edit Genetics"); + } else { + option.addComment("Enable cheat mode to edit genetics."); } - /** - * @template {Row} T - * @param {T} row - * @returns {T} - * @private - */ - _addRow(row) { - this.rows.push(row); - return row; + options.addOption("Rules Assistant").customButton("Reset Rules", () => { initRules(); }, "Rules Assistant"); + + options.addOption("Passage Profiler is", "profiler") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("Outputs performance data at the bottom of every passage."); + + el.append(options.render()); + + App.UI.DOM.appendNewElement("h2", el, `Cheating`); + + options = new App.UI.OptionsGroup(); + + options.addOption("CheatMode is", "cheatMode") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("This will allow manual selection of events and unlock some options that would usually be restricted by progress."); + + if (V.cheatMode === 0) { + el.append(options.render()); + } else { + options.addOption("Sidebar Cheats are currently", "cheatModeM") + .addValue("Shown", 1).on().addValue("Hidden", 0).off(); + + options.addOption("Slave aging", "seeAge") + .addValue("Enabled", 1).on().addValue("Celebrate birthdays, but don't age.", 2).neutral().addValue("Disabled", 0).off(); + + el.append(options.render()); + + links = []; + + links.push( + App.UI.DOM.link( + `Add ${commaNum(100000)} money`, + () => { + V.cheater = 1; + cashX(100000, "cheating"); + }, + [], + "Options" + ) + ); + + links.push( + App.UI.DOM.link( + `Add ${commaNum(10000)} rep`, + () => { + V.cheater = 1; + repX(10000, "cheating"); + }, + [], + "Options" + ) + ); + + r = []; + r.push(App.UI.DOM.generateLinksStrip(links)); + r.push(App.UI.DOM.makeElement("span", "Cheating will be flagged in your save", "note")); + App.Events.addNode(el, r, "div", "scLink2"); + + + SectorCounts(); + + links = []; + links.push( + App.UI.DOM.link( + "Raise prosperity cap", + () => { + V.AProsperityCapModified += 10; + }, + [], + "Options" + ) + ); + + links.push( + App.UI.DOM.link( + "Lower prosperity cap", + () => { + V.AProsperityCapModified -= 10; + }, + [], + "Options" + ) + ); + App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); + + + links = []; + links.push( + App.UI.DOM.link( + "Raise prosperity", + () => { + V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity + 10, 0, V.AProsperityCap); + }, + [], + "Options" + ) + ); + + links.push( + App.UI.DOM.link( + "Lower prosperity", + () => { + V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity - 10, 0, V.AProsperityCap); + }, + [], + "Options" + ) + ); + App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); + + links = []; + links.push( + App.UI.DOM.link( + "Give menial slaves", + () => { + V.menials = Math.clamp(V.menials + 30, 0, popCap.value); + }, + [], + "Options" + ) + ); + + links.push( + App.UI.DOM.link( + "Remove menial slaves", + () => { + V.menials = Math.clamp(V.menials - 30, 0, popCap.value); + }, + [], + "Options" + ) + ); + App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); + + links = []; + + // Will no longer work as intended due to population changes + links.push( + App.UI.DOM.link( + "Add citizens", + () => { + V.lowerClass = Math.max(V.lowerClass + 200, 0); + }, + [], + "Options" + ) + ); + + // also no longer properly functional + links.push( + App.UI.DOM.link( + "Remove citizens", + () => { + V.lowerClass = Math.max(V.lowerClass - 200, 0); + }, + [], + "Options" + ) + ); + App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); + + links = []; + // Will work to a limited degree, minimums and maximums for slaves are set through population + links.push( + App.UI.DOM.link( + "Add slaves", + () => { + V.NPCSlaves = Math.max(V.NPCSlaves + 200, 0); + }, + [], + "Options" + ) + ); + + // Will work to a limited degree + links.push( + App.UI.DOM.link( + "Remove slaves", + () => { + V.NPCSlaves = Math.max(V.NPCSlaves - 200, 0); + }, + [], + "Options" + ) + ); + App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); } + return (el); + } - /** - * @param {string} name - * @param {string} property - * @param {object} [object=V] - * @returns {Option} - */ - addOption(name, property, object = V) { - const option = new Option(name, property, object); - return this._addRow(option); + function experimental() { + const el = new DocumentFragment(); + let options; + let r; + + r = []; + r.push(`Experimental means just that: experimental. Options below are likely to be in an`); + r.push(App.UI.DOM.makeElement("span", `even more incomplete or broken state than usual.`, "yellow")); + r.push(App.UI.DOM.makeElement("span", `THEY MAY NOT WORK AT ALL.`, "red")); + r.push(`Make sure you back up your save before enabling any of these, and if you are that interested, consider helping to improve them.`); + App.Events.addNode(el, r, "div", "bold"); + + options = new App.UI.OptionsGroup(); + + if (V.seePreg !== 0) { + options.addOption("Nursery is", "nursery", V.experimental) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("This will enable the experimental nursery, which allows players to interact with growing slave children. An alternative to the incubator."); } - /** - * @param {string} comment may contain SC markup - * @returns {Comment} - */ - addComment(comment) { - const c = new Comment(comment); - return this._addRow(c); + options.addOption("Food is", "food", V.experimental) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("This will enable the experimental food supply and demand system, as well as a new farmyard building and assignments."); + + if (V.seeExtreme === 1 && V.seeBestiality === 1) { + options.addOption("Animal Ovaries are", "animalOvaries", V.experimental) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("This will allow slaves to be impregnated by animals."); } - /** - * Adds a custom element taking up both rows - * - * @param {HTMLElement|string} element - * @returns {CustomRow} - */ - addCustom(element) { - return this._addRow(new CustomRow(element)); + if (V.seeExtreme === 1) { + options.addOption("Dinner party", "dinnerParty", V.experimental) + .addValue("Enabled", 1).on().addValue("Disabled", 0).off() + .addComment("This will enable a controversial but very broken event. Warning: Snuff, cannibalism."); } - /** - * @returns {HTMLDivElement} - */ - render() { - const container = document.createElement("div"); - container.className = "options-group"; - if (this.doubleColumn) { - container.classList.add("double"); - } + options.addOption("New event", "tempEventToggle") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - for (/** @type {Row} */ const row of this.rows) { - row.render(container, this.doubleColumn); - } + el.append(options.render()); + return el; + } +}; - return container; +App.UI.artOptions = function() { + const el = new DocumentFragment(); + let options = new App.UI.OptionsGroup(); + + if (V.seeImages > 0) { + App.Events.drawEventArt(el, BaseSlave()); + } + + options.addOption("Images are", "seeImages") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + if (V.seeImages > 0) { + options.addOption("Image style is", "imageChoice") + .addValueList([["Revamped embedded vector art", 3], ["Non-embedded vector art", 2], ["NoX/Deepmurk's vector art", 1], ["Shokushu's rendered imagepack", 0]]); + + if (V.imageChoice === 1) { + options.addComment('<span class="warning">Git compiled only, no exceptions.</span>'); + + options.addOption("Face artwork is", "seeFaces") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Highlights on shiny clothing are", "seeVectorArtHighlights") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Height scaling", "seeHeight") + .addValue("All images", 2).on().addValue("Small images", 1).neutral().addValue("Disabled", 0).off(); + + options.addOption("Clothing erection bulges are", "showClothingErection") + .addValue("Enabled", true).on().addValue("Disabled", false).off(); + } else if (V.imageChoice === 0) { + options.addComment(`You need """to""" + <a href="https://mega.nz/#!upoAlBaZ!EbZ5wCixxZxBhMN_ireJTXt0SIPOywO2JW9XzTIPhe0">download the image pack</a> + """and""" put the 'renders' folder into the resources/ folder where this html file is.` + ); + + options.addOption("Slave summary fetish images are", "seeMainFetishes") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + } else if (V.imageChoice === 3) { + options.addComment('<span class="warning">Git compiled only, no exceptions.</span>'); + + options.addOption("Clothing erection bulges are", "showClothingErection") + .addValue("Enabled", true).on().addValue("Disabled", false).off(); } - }; -})(); + + options.addOption("PA avatar art is", "seeAvatar") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Slave images in lists are", "seeSummaryImages") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + + options.addOption("Slave images in the weekly report are", "seeReportImages") + .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); + } + el.append(options.render()); + return el; +}; diff --git a/src/gui/options/optionsGroup.js b/src/gui/options/optionsGroup.js new file mode 100644 index 00000000000..a6ffd817607 --- /dev/null +++ b/src/gui/options/optionsGroup.js @@ -0,0 +1,465 @@ +App.UI.OptionsGroup = (function() { + class Row { + /** + * @param {HTMLDivElement} container + */ + render(container) {} // jshint ignore:line + } + + /** + * @typedef value + * @property {*} value + * @property {string} [name] + * @property {string} mode + * @property {number} [compareValue] + * @property {string} [descAppend] can be SC markup + * @property {boolean} [on] + * @property {boolean} [off] + * @property {boolean} [neutral] + * @property {Function} [callback] + */ + + class Option extends Row { + /** + * @param {string} description can be SC markup + * @param {string} property + * @param {object} [object=V] + */ + constructor(description, property, object = V) { + super(); + this.description = description; + this.property = property; + this.object = object; + /** + * @type {Array<value>} + */ + this.valuePairs = []; + } + + /** + * @param {*} name + * @param {*} [value=name] + * @param {Function} [callback] + * @returns {Option} + */ + addValue(name, value = name, callback = undefined) { + this.valuePairs.push({ + name: name, value: value, mode: "=", callback: callback + }); + return this; + } + + /** + * @param {Array<*|Array>} values + * @returns {Option} + */ + addValueList(values) { + for (const value of values) { + if (Array.isArray(value)) { + this.addValue(value[0], value[1]); + } else { + this.addValue(value); + } + } + return this; + } + + /** + * @param {Map} values + * @returns {Option} + */ + addValueMap(values) { + for (const [key, value] of values) { + this.addValue(key, value); + } + return this; + } + + /** + * @param {*} value + * @param {number} compareValue + * @param {string} mode on of: "<", "<=", ">", ">=" + * @param {string} [name=value] + */ + addRange(value, compareValue, mode, name = value) { + this.valuePairs.push({ + name: name, value: value, mode: mode, compareValue: compareValue + }); + return this; + } + + /** + * @param {Object} [params] + * @param {string} [params.unit] + * @param {boolean} [params.large=false] + * @returns {Option} + */ + showTextBox({unit, large = false} = {}) { + this.textbox = {unit: unit, large: large}; + return this; + } + + /** + * @param {string} comment can be SC markup + * @returns {Option} + */ + addComment(comment) { + this.comment = comment; + return this; + } + + /** + * Adds a button that executes the callback when clicked AND reloads the passage + * + * @param {string} name + * @param {Function} callback + * @param {string} passage + */ + customButton(name, callback, passage) { + this.valuePairs.push({ + name: name, value: passage, callback: callback, mode: "custom" + }); + return this; + } + + /** + * @param {Node} node + * @returns {Option} + */ + addCustomDOM(node) { + this.valuePairs.push({ + value: node, mode: "DOM" + }); + return this; + } + + /* modify last added option */ + + /** + * Added to the description if last added value is selected. + * example use: addValue(...).customDescription(...).addValue(...).customDescription(...) + * @param {string} description can be SC markup + */ + customDescription(description) { + this.valuePairs.last().descAppend = description; + return this; + } + + /** + * @param {Function} callback gets executed on every button click. Selected value is given as argument. + */ + addCallback(callback) { + this.valuePairs.last().callback = callback; + return this; + } + + /** + * @param {Function} callback gets executed on every button click. Selected value is given as argument. + */ + addCallbackToEach(callback) { + this.valuePairs.forEach(pair => pair.callback = callback); + return this; + } + + /** + * Mark option as on to style differently. + * @returns {Option} + */ + on() { + this.valuePairs.last().on = true; + return this; + } + + /** + * Mark option as off to style differently. + * @returns {Option} + */ + off() { + this.valuePairs.last().off = true; + return this; + } + + /** + * Mark option as neutral to style differently. + * @returns {Option} + */ + neutral() { + this.valuePairs.last().neutral = true; + return this; + } + + /** + * Puts the options in side a pulldown if there are at least 6. + * Not counting text boxes or comments. + * @returns {Option} + */ + pulldown() { + this.enablePulldown = true; + return this; + } + + /** + * @param {HTMLDivElement} container + */ + render(container) { + /* left side */ + const desc = document.createElement("div"); + desc.className = "description"; + $(desc).wiki(this.description); + container.append(desc); + + /* right side */ + const currentValue = this.object[this.property]; + let anySelected = false; + + const buttonGroup = document.createElement("div"); + buttonGroup.classList.add("button-group"); + if (!this.enablePulldown || this.valuePairs.length < 6) { + for (const value of this.valuePairs) { + if (value.mode === "DOM") { + /* insert DOM and go to next element */ + buttonGroup.append(value.value); + continue; + } + const button = document.createElement("button"); + button.append(value.name); + if (value.on) { + button.classList.add("on"); + } else if (value.off) { + button.classList.add("off"); + } else if (value.neutral) { + button.classList.add("neutral"); + } + if (value.mode === "custom") { + button.onclick = () => { + value.callback(); + if (value.value) { + Engine.play(value.value); + } else { + App.UI.reload(); + } + }; + } else { + if (value.mode === "=" && currentValue === value.value) { + button.classList.add("selected", "disabled"); + anySelected = true; + if (value.descAppend !== undefined) { + desc.append(" "); + $(desc).wiki(value.descAppend); + } + } else if (!anySelected && inRange(value.mode, value.compareValue, currentValue)) { + button.classList.add("selected"); + // disable the button if clicking it won't change the variable value + if (currentValue === value.value) { + button.classList.add("disabled"); + } + anySelected = true; + if (value.descAppend !== undefined) { + desc.append(" "); + $(desc).wiki(value.descAppend); + } + } + button.onclick = () => { + this.object[this.property] = value.value; + if (value.callback) { + value.callback(); + } + App.UI.reload(); + }; + } + buttonGroup.append(button); + } + } else { + let matchFound = false; + let select = document.createElement("select"); + + for (const value of this.valuePairs) { + let el = document.createElement("option"); + el.textContent = value.name; + el.value = value.value; + if (this.object[this.property] === value.value) { + el.selected = true; + matchFound = true; + } + select.appendChild(el); + } + if (!matchFound) { + select.selectedIndex = -1; + } + select.onchange = () => { + const O = select.options[select.selectedIndex]; + if (isNaN(Number(O.value))) { + this.object[this.property] = O.value; + } else { + this.object[this.property] = Number(O.value); + } + const originalObj = this.valuePairs.find(obj => obj.name === O.textContent); + if (originalObj && typeof originalObj.callback === "function") { + originalObj.callback(); + } + App.UI.reload(); + }; + buttonGroup.append(select); + } + + if (this.textbox) { + const isNumber = typeof currentValue === "number"; + const textbox = App.UI.DOM.makeTextBox(currentValue, input => { + this.object[this.property] = input; + App.UI.reload(); + }, isNumber); + if (isNumber) { + textbox.classList.add("number"); + } + if (this.textbox.large) { + textbox.classList.add("full-width"); + } + buttonGroup.append(textbox); + if (this.textbox.unit) { + buttonGroup.append(" ", this.textbox.unit); + } + } + + if (this.comment) { + const comment = document.createElement("span"); + comment.classList.add("comment"); + $(comment).wiki(this.comment); + buttonGroup.append(comment); + } + container.append(buttonGroup); + + function inRange(mode, compareValue, value) { + if (mode === "<") { + return value < compareValue; + } else if (mode === "<=") { + return value <= compareValue; + } else if (mode === ">") { + return value > compareValue; + } else if (mode === ">=") { + return value >= compareValue; + } + return false; + } + } + } + + class Comment extends Row { + /** + * @param {string} comment can be SC markup + */ + constructor(comment) { + super(); + this.comment = comment; + this.long = false; + } + + /** + * @param {HTMLDivElement} container + */ + render(container) { + /* left */ + container.append(document.createElement("div")); + + /* right */ + const comment = document.createElement("div"); + comment.classList.add("comment"); + $(comment).wiki(this.comment); + container.append(comment); + } + } + + class CustomRow extends Row { + /** + * @param {HTMLElement|string} element + */ + constructor(element) { + super(); + this.element = element; + } + + /** + * @param {HTMLDivElement} container + */ + render(container) { + /** @type {HTMLDivElement} */ + const div = App.UI.DOM.makeElement("div", this.element, "custom-row"); + container.append(div); + } + } + + return class { + constructor() { + /** + * @type {Array<Row>} + */ + this.rows = []; + this.doubleColumn = false; + } + + /** + * @returns {App.UI.OptionsGroup} + */ + enableDoubleColumn() { + this.doubleColumn = true; + return this; + } + + /** + * @template {Row} T + * @param {T} row + * @returns {T} + * @private + */ + _addRow(row) { + this.rows.push(row); + return row; + } + + /** + * @param {string} name + * @param {string} property + * @param {object} [object=V] + * @returns {Option} + */ + addOption(name, property, object = V) { + const option = new Option(name, property, object); + return this._addRow(option); + } + + /** + * @param {string} comment may contain SC markup + * @returns {Comment} + */ + addComment(comment) { + const c = new Comment(comment); + return this._addRow(c); + } + + /** + * Adds a custom element taking up both rows + * + * @param {HTMLElement|string} element + * @returns {CustomRow} + */ + addCustom(element) { + return this._addRow(new CustomRow(element)); + } + + /** + * @returns {HTMLDivElement} + */ + render() { + const container = document.createElement("div"); + container.className = "options-group"; + if (this.doubleColumn) { + container.classList.add("double"); + } + + for (/** @type {Row} */ const row of this.rows) { + row.render(container, this.doubleColumn); + } + + return container; + } + }; +})(); diff --git a/src/gui/options/optionsPassage.js b/src/gui/options/optionsPassage.js deleted file mode 100644 index 74cbea915f9..00000000000 --- a/src/gui/options/optionsPassage.js +++ /dev/null @@ -1,1061 +0,0 @@ -App.UI.optionsPassage = function() { - const el = new DocumentFragment(); - V.passageSwitchHandler = App.EventHandlers.optionsChanged; - el.append(intro()); - - // Results - const results = document.createElement("div"); - results.id = "results"; - el.append(results); - - App.UI.tabBar.handlePreSelectedTab(V.tabChoice.Options); - - // TODO: move me - /** - * - * @param {string} id - * @param {Node} element - * @returns {HTMLSpanElement} - */ - function makeSpanIded(id, element) { - const span = document.createElement("span"); - span.id = id; - span.append(element); - return span; - } - - const tabCaptions = { - "display": 'Display', - "contentFlavor": 'Content & flavour', - "mods": 'Mods', - "debugCheating": 'Debug & cheating', - "experimental": 'Experimental' - }; - - const tabBar = App.UI.DOM.appendNewElement("div", el, '', "tab-bar"); - tabBar.append( - App.UI.tabBar.tabButton('display', tabCaptions.display), - App.UI.tabBar.tabButton('content-flavor', tabCaptions.contentFlavor), - App.UI.tabBar.tabButton('mods', tabCaptions.mods), - App.UI.tabBar.tabButton('debug-cheating', tabCaptions.debugCheating), - App.UI.tabBar.tabButton('experimental', tabCaptions.experimental), - ); - - el.append(App.UI.tabBar.makeTab('display', makeSpanIded("content-display", display()))); - el.append(App.UI.tabBar.makeTab('content-flavor', makeSpanIded("content-content-flavor", contentFlavor()))); - el.append(App.UI.tabBar.makeTab('mods', makeSpanIded("content-mods", mods()))); - el.append(App.UI.tabBar.makeTab('debug-cheating', makeSpanIded("content-debug-cheating", debugCheating()))); - el.append(App.UI.tabBar.makeTab('experimental', makeSpanIded("content-experimental", experimental()))); - - return el; - - function intro() { - let links; - let options; - let r; - const el = new DocumentFragment(); - - options = new App.UI.OptionsGroup(); - options.addOption("End of week autosaving is currently", "autosave") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - el.append(options.render()); - - App.UI.DOM.appendNewElement("div", el, `This save was created using FC version ${V.ver} build ${V.releaseID}. You are currently playing version: ${App.Version.base}, mod version: ${App.Version.pmod}, build: ${App.Version.release}${App.Version.commitHash ? `, commit: ${App.Version.commitHash}` : ``}`); - - links = []; - links.push(App.UI.DOM.passageLink("Apply Backwards Compatibility Update", "Backwards Compatibility")); - - links.push( - App.UI.DOM.link( - `Reset extended family mode controllers`, - () => { - resetFamilyCounters(); - const span = document.createElement("span"); - span.classList.add("note"); - App.UI.DOM.appendNewElement("span", span, "Done: ", "lightgreen"); - span.append("all family relations flushed and rebuilt."); - jQuery("#results").empty().append(span); - }, - [], - "", - "Clears and rebuilds .sister and .daughter tracking." - ) - ); - - - if (isNaN(V.rep)) { - links.push( - App.UI.DOM.link( - `Reset Reputation (${V.rep})`, - () => { - V.rep = 0; - jQuery("#results").empty().append(`Reputation reset to ${V.rep}`); - }, - [], - "Options" - ) - ); - } - - if (isNaN(V.rep)) { - links.push( - App.UI.DOM.link( - `Reset Money (${V.cash})`, - () => { - V.cash = 500; - jQuery("#results").empty().append(`Cash reset to ${V.cash}`); - }, - [], - "Options" - ) - ); - } - - if (V.releaseID === 1057) { - links.push( - App.UI.DOM.link( - `Free male anatomy removal due to accidentally flawed updater`, - () => { - V.PC.dick = 0; - V.PC.balls = 0; - V.PC.prostate = 0; - V.PC.scrotum = 0; - V.PC.ballsImplant = 0; - jQuery("#results").empty().append(`Cash reset to ${V.cash}`); - }, - [], - "Options", - "Use this if your female PC picked up a few extra parts during the conversion process.", - ) - ); - } - - App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links)); - - if ((V.releaseID >= 1000) || V.ver.startsWith("0.9") || V.ver.startsWith("0.8") || V.ver.startsWith("0.7") || V.ver.startsWith("0.6")) { - App.UI.DOM.appendNewElement("h3", el, `NEW GAME PLUS`); - r = []; - r.push(`You can begin a new game with up to five (or more) of your current slaves, although starting resources other than these slaves will be reduced. New Game Plus`); - r.push(App.UI.DOM.makeElement("span", "MAY", "yellow")); - r.push(`work across versions. To attempt to migrate a save across versions:`); - App.Events.addNode(el, r, "div", "note"); - - const ngpInstructions = document.createElement("ol"); - App.UI.DOM.appendNewElement("li", ngpInstructions, "Save on this screen", "note"); - App.UI.DOM.appendNewElement("li", ngpInstructions, "Re-open the .html in a new tab then load the above save.", "note"); - App.UI.DOM.appendNewElement( - "li", - ngpInstructions, - App.UI.DOM.link( - "Activate New Game Plus.", - () => { - V.ui = "start"; - }, - [], - "New Game Plus" - ), - "note" - ); - el.append(ngpInstructions); - } else { - App.UI.DOM.appendNewElement("div", el, `New Game Plus is not available because this game was not started with a compatible version.`, "note"); - } - return el; - } - - function display() { - const el = new DocumentFragment(); - let options; - let r; - - App.UI.DOM.appendNewElement("h2", el, "Reports"); - - options = new App.UI.OptionsGroup(); - - options.addOption("End week report descriptive details are", "showEWD") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("End week report performance details are", "showEWM") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Master Suite report details such as slave changes are", "verboseDescriptions") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("End week societal effects from slaves are", "compressSocialEffects", V.UI) - .addValue("Expanded", 0).on().addValue("Compacted", 1).off(); - - options.addOption("Accordion on week end defaults to", "useAccordion") - .addValue("Open", 0).on().addValue("Collapsed", 1).off(); - - options.addOption("Economic Tabs on weekly reports are", "useTabs") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Economic detail sheets for facilities are", "showEconomicDetails") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Economic report neighbor details such as trade impacts on culture are", "showNeighborDetails") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Numeric formatting is currently", "formatNumbers") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("This will comma-format numbers in some areas."); - - el.append(options.render()); - - App.UI.DOM.appendNewElement("h2", el, "General"); - - options = new App.UI.OptionsGroup(); - - options.addOption("Main menu leadership controls displayed", "positionMainLinks") - .addValueList([["Above", 1], ["Above and below", 0], ["Below", -1]]); - - options.addOption("New Model UI", "newModelUI") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Penthouse Facility Display", "verticalizeArcologyLinks") - .addValueList([["Triple column", 3], ["Double Column", 2], ["Single Column", 1], ["Collapsed", 0]]); - - options.addOption("Main menu arcology description", "seeArcology") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Main menu desk description", "seeDesk") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Main menu newsfeed", "seeFCNN") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Tips from the Encyclopedia are", "showTipsFromEncy") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Help tooltips are", "tooltipsEnabled") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment(`This is mostly for new players. <span class='exampleTooltip noteworthy'>Colored text</span> can have tooltips.`); - - options.addOption("Main menu slave tabs are", "useSlaveSummaryTabs") - .addValue("Enabled", 1).on().addValue("CardStyle", 2).on().addValue("Disabled", 0).off(); - - options.addOption("The slave Quick list in-page scroll-to is", "useSlaveListInPageJSNavigation") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Condense special slaves into their own tab", "useSlaveSummaryOverviewTab") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Interactions with your fucktoys are", "fucktoyInteractionsPosition") - .addValueList([["next to them", 1], ["at page bottom", 0]]); - - options.addOption("Hide tabs in Slave Interact", "slaveInteractLongForm") - .addValue("Enabled", true).on().addValue("Disabled", false).off(); - - options.addOption("Line separations are", "lineSeparations") - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - el.append(options.render()); - - r = []; - r.push(`UI theme selector. Allows to select a single CSS file to be loaded.`); - r.push(App.UI.DOM.makeElement("span", `The file has to be located in the same directory as the HTML file otherwise it will simply not load at all.`, "red")); - r.push(App.UI.Theme.selector()); - App.Events.addParagraph(el, r); - - App.UI.DOM.appendNewElement("h2", el, "Sidebar"); - - options = new App.UI.OptionsGroup(); - - options.addOption("Cash is", "Cash", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("Upkeep is", "Upkeep", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("Sex slave count is", "SexSlaveCount", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("Room population is", "roomPop", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("GSP is", "GSP", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("Rep is", "Rep", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("Confirmation before ending a week is", "confirmWeekEnd", V.sideBarOptions) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Enabling this will open a dialog box to confirm you meant to end a week."); - - if (V.secExpEnabled > 0) { - options.addOption("Authority is", "Authority", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("Security is", "Security", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("Crime is", "Crime", V.sideBarOptions) - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - } - - el.append(options.render()); - - - App.UI.DOM.appendNewElement("h2", el, "Images"); - el.append(App.UI.artOptions()); - - return el; - } - - function contentFlavor() { - const el = new DocumentFragment(); - let r; - let options; - - App.UI.DOM.appendNewElement("h2", el, "Content"); - - r = []; - r.push("More granular control of what appears is in"); - r.push(App.UI.DOM.passageLink("Description Options", "Description Options")); - App.Events.addNode(el, r, "div", "note"); - - options = new App.UI.OptionsGroup(); - - options.addOption("The difficulty setting is currently set to", "baseDifficulty") - .addValueList([["Very easy", 1], ["Easy", 2], ["Default", 3], ["Hard", 4], ["Very hard", 5]]); - - options.addOption("Slaves falling ill is currently", "seeIllness") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Will not affect existing ill slaves already in-game."); - - options.addOption("Extreme content like amputation is currently", "seeExtreme") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Will not affect extreme surgeries already applied in-game."); - - options.addOption("Bestiality related content is currently", "seeBestiality") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Watersports related content is currently", "seePee") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Incest content is currently", "seeIncest") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Pregnancy related content is currently", "seePreg") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Will not affect existing pregnancies already in-game."); - - options.addOption("Child gender to be generated based off dick content settings", "seeDicksAffectsPregnancy") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment(`${(V.seeDicksAffectsPregnancy === 1) ? `Currently ${V.seeDicks}% of children will be born male. ` : ``}Will not affect existing pregnancies already in-game.`); - - if (V.seeDicksAffectsPregnancy === 0) { - options.addOption("XX slaves only father daughters", "adamPrinciple") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Will not affect existing pregnancies already in-game."); - } - - options.addOption("Extreme pregnancy content like broodmothers is currently", "seeHyperPreg") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Will not affect existing hyperpregnancies already in-game."); - - options.addOption("Pregnancy complications due to multiples and body size are currently", "dangerousPregnancy") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption(`Precocious puberty (pregnancy younger than ${V.fertilityAge})`, "precociousPuberty") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Will not affect existing precocious puberty cases already in-game."); - - options.addOption("Slaves with fat lips or heavy oral piercings may lisp", "disableLisping") - .addValue("Yes", 0).on().addValue("No", 1).off(); - - options.addOption("Disables the long term damage mechanic. //Temp option//", "disableLongDamage") - .addValue("Enabled", 0).on().addValue("Disabled", 1).off(); - - options.addOption("Experimental male pronouns are currently", "diversePronouns") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Apply Backwards Compatibility after changing to update slave's pronouns. Not all scenes support male pronouns and this is not yet incorporated into the lore or mechanics."); - - options.addOption("Male slave names are currently", "allowMaleSlaveNames") - .addValue("Enabled", true).on().addValue("Disabled", false).off() - .addComment("This only affects slave generation and not your ability to name your slaves."); - - options.addOption("Missing slave names are currently", "showMissingSlaves") - .addValue("Enabled", true).on().addValue("Disabled", false).off(); - - el.append(options.render()); - - App.UI.DOM.appendNewElement("h2", el, `Intersecting mechanics`); - - options = new App.UI.OptionsGroup(); - - options.addOption("Slave assets affected by weight is", "weightAffectsAssets") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Diet will still affect asset size."); - - options.addOption("Curative side effects are", "curativeSideEffects") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("If enabled, curatives have a chance to give slaves harmful side effects."); - - el.append(options.render()); - - App.UI.DOM.appendNewElement("h2", el, `Flavour`); - - options = new App.UI.OptionsGroup(); - - options.addOption("Slave reactions to facility assignments are", "showAssignToScenes") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Post sex clean up", "postSexCleanUp") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Appraisal miniscenes on slave sale are", "showAppraisal") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Assignment performance vignettes on the end week report are", "showVignettes") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Slaves can have alternate titles", "newDescriptions") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Family titles for relatives", "allowFamilyTitles") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Limit family growth", "limitFamilies") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Restricts acquisition of additional relatives, by means other than birth, for slaves with families."); - - options.addOption("Distant relatives such as aunts, nieces and cousins are", "showDistantRelatives") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - el.append(options.render()); - return el; - } - - function mods() { - const el = new DocumentFragment(); - let options; - - options = new App.UI.OptionsGroup(); - - options.addOption("The Special Force Mod is currently", "Toggle", V.SF) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("<div>This mod is triggered after week 72. It is non-canon where it conflicts with canonical updates to the base game.</div>"); - - options.addOption("The Security Expansion mod is", "secExpEnabled") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("<div>The mod can be activated in any moment, but it may result in unbalanced gameplay if activated very late in the game.</div>"); - - el.append(options.render()); - - if (V.secExpEnabled > 0) { - if (Object.values(V.SecExp).length === 0) { - App.SecExp.generalBC(); - Engine.play("Options"); - } - App.UI.DOM.appendNewElement("h2", el, `Security Expansion mod options`); - if (V.terrain === "oceanic") { - App.UI.DOM.appendNewElement("div", el, `Oceanic arcologies are not by default subject to external attacks. You can however allow them to happen anyway. If you choose to do so please keep in mind that descriptions and mechanics are not intended for naval combat but land combat.`); - } - options = new App.UI.OptionsGroup(); - - if (V.SecExp.settings.battle.enabled > 0 || V.SecExp.settings.rebellion.enabled > 0) { - options.addOption("Detailed battle statistics are", "showStats", V.SecExp.settings) - .addValue("Shown", 1).on().addValue("Hidden", 0).off() - .addComment("Visibility of detailed statistics and battle turns."); - - options.addOption("Difficulty is", "difficulty", V.SecExp.settings) - .addValueList([["Extremely hard", 2], ["Very hard", 1.5], ["Hard", 1.25], ["Normal", 1], ["Easy", 0.75], ["Very easy", 0.5]]); - - options.addOption("Unit descriptions are", "unitDescriptions", V.SecExp.settings) - .addValue("Abbreviated", 1).addValue("Summarized", 0); - } - - options.addOption("Battles are", "enabled", V.SecExp.settings.battle) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Rebellions are", "enabled", V.SecExp.settings.rebellion) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - if (V.SecExp.settings.battle.enabled > 0) { - options.addOption("Battle frequency", "frequency", V.SecExp.settings.battle) - .addValueList([["Extremely high", 2], ["Very high", 1.5], ["High", 1.25], ["Normal", 1], ["Low", 0.75], ["Very low", 0.5]]); - } - - if (V.SecExp.settings.rebellion.enabled > 0) { - options.addOption("Rebellion buildup", "speed", V.SecExp.settings.rebellion) - .addValueList([["Extremely fast", 2], ["Very fast", 1.5], ["Fast", 1.25], ["Normal", 1], ["Slow", 0.75], ["Very slow", 0.5]]); - } - - - if (V.SecExp.settings.battle.enabled > 0) { - options.addOption("Commanders gain a prestige rank every 10 victories", "allowSlavePrestige", V.SecExp.settings.battle) - .addValue("Yes", 1).on().addValue("No", 0).off(); - } - - if (V.SecExp.settings.battle.enabled > 0) { - options.addOption("Force battles", "force", V.SecExp.settings.battle) - .addValue("Yes", 1).on().addValue("No", 0).off(); - } - if (V.SecExp.settings.rebellion.enabled > 0) { - options.addOption("Force rebellions", "force", V.SecExp.settings.rebellion) - .addValue("Yes", 1).on().addValue("No", 0).off() - .addComment("Rebellions take precedence over Battles."); - } - - if (V.SecExp.settings.battle.enabled > 0) { - options.addOption("Late game major battles are", "enabled", V.SecExp.settings.battle.major) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - } - - if (V.SecExp.settings.battle.enabled > 0 && V.SecExp.settings.battle.major.enabled > 0) { - options.addOption("Multiplier is", "mult", V.SecExp.settings.battle.major) - .addValueList([["Extremely high", 2], ["Very high", 1.5], ["High", 1.25], ["Normal", 1], ["Low", 0.75], ["Very low", 0.5]]); - - options.addOption("This week a major battle is", "force", V.SecExp.settings.battle.major) - .addValue("Guaranteed", 1).on().addValue("Not guaranteed", 0).off(); - } - - if (V.SecExp.settings.battle.enabled > 0 && V.SecExp.settings.battle.major.enabled > 0) { - options.addOption("Gameover on battle loss", "gameOver", V.SecExp.settings.battle.major) - .addValue("Yes", 1).on().addValue("No", 0).off(); - } - - if (V.SecExp.settings.rebellion.enabled > 0) { - options.addOption("Gameover on rebellion loss", "gameOver", V.SecExp.settings.rebellion) - .addValue("Yes", 1).on().addValue("No", 0).off(); - } - - el.append(options.render()); - - const subHeading = document.createElement("div"); - subHeading.classList.add("subHeading"); - - if (V.debugMode || V.cheatMode || V.cheatModeM) { // TODO make me a fucking function - App.UI.DOM.appendNewElement("div", subHeading, "Debug/cheat", "bold"); - let td; - let links; - const table = document.createElement("table"); - table.classList.add("invisible"); - el.append(table); - - let tr = document.createElement("tr"); - tr.style.textAlign = "center"; - - td = createTd(); - links = []; - links.push( - App.UI.DOM.link( - "Set loyalty high", - () => { - changeLoyalty("high"); - }, - [], - "Options" - ) - ); - links.push( - App.UI.DOM.link( - "Set loyalty average", - () => { - changeLoyalty("average"); - }, - [], - "Options" - ) - ); - links.push( - App.UI.DOM.link( - "Set loyalty low", - () => { - changeLoyalty("low"); - }, - [], - "Options" - ) - ); - links.push( - App.UI.DOM.link( - "Randomize loyalty", - () => { - changeLoyalty("random"); - }, - [], - "Options" - ) - ); - - td.append(App.UI.DOM.generateLinksStrip(links)); - tr.append(td); - table.append(tr); - - tr = document.createElement("tr"); - tr.style.textAlign = "center"; - td = createTd(); - links = []; - links.push(App.UI.DOM.link( - "Give Authority", - () => { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 1000, 0, 20000); - }, - [], - "Options" - )); - links.push(App.UI.DOM.link( - "Remove Authority", - () => { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - 1000, 0, 20000); - }, - [], - "Options" - )); - td.append(App.UI.DOM.generateLinksStrip(links)); - tr.append(td); - table.append(tr); - - - tr = document.createElement("tr"); - td = document.createElement("td"); - td.style.textAlign = "right"; - links = []; - links.push(App.UI.DOM.link( - "Raise security", - () => { - V.SecExp.core.security = Math.clamp(V.SecExp.core.security + 5, 0, 100); - }, - [], - "Options" - )); - links.push(App.UI.DOM.link( - "Lower security", - () => { - V.SecExp.core.security = Math.clamp(V.SecExp.core.security - 5, 0, 100); - }, - [], - "Options" - )); - tr.append(App.UI.DOM.generateLinksStrip(links)); - tr.append(td); - - td = document.createElement("td"); - td.style.textAlign = "left"; - links = []; - links.push(App.UI.DOM.link( - "Raise crime", - () => { - V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + 5, 0, 100); - }, - [], - "Options" - )); - links.push(App.UI.DOM.link( - "Lower crime", - () => { - V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow - 5, 0, 100); - }, - [], - "Options" - )); - tr.append(App.UI.DOM.generateLinksStrip(links)); - tr.append(td); - table.append(tr); - - tr = document.createElement("tr"); - td = document.createElement("td"); - td.style.textAlign = "right"; - links = []; - links.push(App.UI.DOM.link( - "Give militia manpower", - () => { - V.SecExp.units.militia.free += 30; - }, - [], - "Options" - )); - links.push(App.UI.DOM.link( - "Remove militia manpower", - () => { - V.SecExp.units.militia.free = Math.max(V.SecExp.units.militia.free - 30, 0); - }, - [], - "Options" - )); - tr.append(App.UI.DOM.generateLinksStrip(links)); - tr.append(td); - - td = document.createElement("td"); - td.style.textAlign = "left"; - links = []; - links.push(App.UI.DOM.link( - "Give mercs manpower", - () => { - V.SecExp.units.mercs.free += 30; - }, - [], - "Options" - )); - links.push(App.UI.DOM.link( - "Remove mercs manpower", - () => { - V.SecExp.units.mercs.free = Math.max(V.SecExp.units.mercs.free - 30, 0); - }, - [], - "Options" - )); - tr.append(App.UI.DOM.generateLinksStrip(links)); - tr.append(td); - table.append(tr); - subHeading.append(table); - el.append(subHeading); - } /* closes cheatmode check */ - } /* closes SecExp check*/ - return el; - - function createTd() { - const td = document.createElement("td"); - td.style.columnSpan = "2"; - return td; - } - - /** - * - * @param {"high"|"average"|"low"|"random"} level - */ - function changeLoyalty(level) { - const numberMap = new Map([ - ["high", [80, 100]], - ["average", [40, 60]], - ["low", [20]], - ["random", [100]], - ]); - - for (const squad of App.SecExp.unit.humanSquads()) { - squad.loyalty = numberGenerator(); - } - - function numberGenerator() { - const range = numberMap.get(level); - if (range[1]) { - return random(range[0], range[1]); - } else { - return random(range[0]); - } - } - } - } - - function debugCheating() { - const el = new DocumentFragment(); - let options; - let option; - let links; - let r; - const popCap = menialPopCap(); - - App.UI.DOM.appendNewElement("h2", el, `Debug`); - - options = new App.UI.OptionsGroup(); - - options.addOption("DebugMode is", "debugMode") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("This will add a Display Variables and Bug Report passage to the sidebar."); - - if (V.debugMode > 0) { - options.addOption("The custom function part of debug mode is", "debugModeCustomFunction") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - } - - option = options.addOption("Genetics array") - .customButton("Run test", () => { }, "test genetics"); - if (V.cheatMode === 1) { - option.customButton("Edit Genetics", () => { }, "Edit Genetics"); - } else { - option.addComment("Enable cheat mode to edit genetics."); - } - - options.addOption("Rules Assistant").customButton("Reset Rules", () => { initRules(); }, "Rules Assistant"); - - options.addOption("Passage Profiler is", "profiler") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("Outputs performance data at the bottom of every passage."); - - el.append(options.render()); - - App.UI.DOM.appendNewElement("h2", el, `Cheating`); - - options = new App.UI.OptionsGroup(); - - options.addOption("CheatMode is", "cheatMode") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("This will allow manual selection of events and unlock some options that would usually be restricted by progress."); - - if (V.cheatMode === 0) { - el.append(options.render()); - } else { - options.addOption("Sidebar Cheats are currently", "cheatModeM") - .addValue("Shown", 1).on().addValue("Hidden", 0).off(); - - options.addOption("Slave aging", "seeAge") - .addValue("Enabled", 1).on().addValue("Celebrate birthdays, but don't age.", 2).neutral().addValue("Disabled", 0).off(); - - el.append(options.render()); - - links = []; - - links.push( - App.UI.DOM.link( - `Add ${commaNum(100000)} money`, - () => { - V.cheater = 1; - cashX(100000, "cheating"); - }, - [], - "Options" - ) - ); - - links.push( - App.UI.DOM.link( - `Add ${commaNum(10000)} rep`, - () => { - V.cheater = 1; - repX(10000, "cheating"); - }, - [], - "Options" - ) - ); - - r = []; - r.push(App.UI.DOM.generateLinksStrip(links)); - r.push(App.UI.DOM.makeElement("span", "Cheating will be flagged in your save", "note")); - App.Events.addNode(el, r, "div", "scLink2"); - - - SectorCounts(); - - links = []; - links.push( - App.UI.DOM.link( - "Raise prosperity cap", - () => { - V.AProsperityCapModified += 10; - }, - [], - "Options" - ) - ); - - links.push( - App.UI.DOM.link( - "Lower prosperity cap", - () => { - V.AProsperityCapModified -= 10; - }, - [], - "Options" - ) - ); - App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); - - - links = []; - links.push( - App.UI.DOM.link( - "Raise prosperity", - () => { - V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity + 10, 0, V.AProsperityCap); - }, - [], - "Options" - ) - ); - - links.push( - App.UI.DOM.link( - "Lower prosperity", - () => { - V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity - 10, 0, V.AProsperityCap); - }, - [], - "Options" - ) - ); - App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); - - links = []; - links.push( - App.UI.DOM.link( - "Give menial slaves", - () => { - V.menials = Math.clamp(V.menials + 30, 0, popCap.value); - }, - [], - "Options" - ) - ); - - links.push( - App.UI.DOM.link( - "Remove menial slaves", - () => { - V.menials = Math.clamp(V.menials - 30, 0, popCap.value); - }, - [], - "Options" - ) - ); - App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); - - links = []; - - // Will no longer work as intended due to population changes - links.push( - App.UI.DOM.link( - "Add citizens", - () => { - V.lowerClass = Math.max(V.lowerClass + 200, 0); - }, - [], - "Options" - ) - ); - - // also no longer properly functional - links.push( - App.UI.DOM.link( - "Remove citizens", - () => { - V.lowerClass = Math.max(V.lowerClass - 200, 0); - }, - [], - "Options" - ) - ); - App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); - - links = []; - // Will work to a limited degree, minimums and maximums for slaves are set through population - links.push( - App.UI.DOM.link( - "Add slaves", - () => { - V.NPCSlaves = Math.max(V.NPCSlaves + 200, 0); - }, - [], - "Options" - ) - ); - - // Will work to a limited degree - links.push( - App.UI.DOM.link( - "Remove slaves", - () => { - V.NPCSlaves = Math.max(V.NPCSlaves - 200, 0); - }, - [], - "Options" - ) - ); - App.UI.DOM.appendNewElement("div", el, App.UI.DOM.generateLinksStrip(links), "scLink2"); - } - return (el); - } - - function experimental() { - const el = new DocumentFragment(); - let options; - let r; - - r = []; - r.push(`Experimental means just that: experimental. Options below are likely to be in an`); - r.push(App.UI.DOM.makeElement("span", `even more incomplete or broken state than usual.`, "yellow")); - r.push(App.UI.DOM.makeElement("span", `THEY MAY NOT WORK AT ALL.`, "red")); - r.push(`Make sure you back up your save before enabling any of these, and if you are that interested, consider helping to improve them.`); - App.Events.addNode(el, r, "div", "bold"); - - options = new App.UI.OptionsGroup(); - - if (V.seePreg !== 0) { - options.addOption("Nursery is", "nursery", V.experimental) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("This will enable the experimental nursery, which allows players to interact with growing slave children. An alternative to the incubator."); - } - - options.addOption("Food is", "food", V.experimental) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("This will enable the experimental food supply and demand system, as well as a new farmyard building and assignments."); - - if (V.seeExtreme === 1 && V.seeBestiality === 1) { - options.addOption("Animal Ovaries are", "animalOvaries", V.experimental) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("This will allow slaves to be impregnated by animals."); - } - - if (V.seeExtreme === 1) { - options.addOption("Dinner party", "dinnerParty", V.experimental) - .addValue("Enabled", 1).on().addValue("Disabled", 0).off() - .addComment("This will enable a controversial but very broken event. Warning: Snuff, cannibalism."); - } - - options.addOption("New event", "tempEventToggle") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - el.append(options.render()); - return el; - } -}; - -App.UI.artOptions = function() { - const el = new DocumentFragment(); - let options = new App.UI.OptionsGroup(); - - if (V.seeImages > 0) { - App.Events.drawEventArt(el, BaseSlave()); - } - - options.addOption("Images are", "seeImages") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - if (V.seeImages > 0) { - options.addOption("Image style is", "imageChoice") - .addValueList([["Revamped embedded vector art", 3], ["Non-embedded vector art", 2], ["NoX/Deepmurk's vector art", 1], ["Shokushu's rendered imagepack", 0]]); - - if (V.imageChoice === 1) { - options.addComment('<span class="warning">Git compiled only, no exceptions.</span>'); - - options.addOption("Face artwork is", "seeFaces") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Highlights on shiny clothing are", "seeVectorArtHighlights") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Height scaling", "seeHeight") - .addValue("All images", 2).on().addValue("Small images", 1).neutral().addValue("Disabled", 0).off(); - - options.addOption("Clothing erection bulges are", "showClothingErection") - .addValue("Enabled", true).on().addValue("Disabled", false).off(); - } else if (V.imageChoice === 0) { - options.addComment(`You need """to""" - <a href="https://mega.nz/#!upoAlBaZ!EbZ5wCixxZxBhMN_ireJTXt0SIPOywO2JW9XzTIPhe0">download the image pack</a> - """and""" put the 'renders' folder into the resources/ folder where this html file is.` - ); - - options.addOption("Slave summary fetish images are", "seeMainFetishes") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - } else if (V.imageChoice === 3) { - options.addComment('<span class="warning">Git compiled only, no exceptions.</span>'); - - options.addOption("Clothing erection bulges are", "showClothingErection") - .addValue("Enabled", true).on().addValue("Disabled", false).off(); - } - - options.addOption("PA avatar art is", "seeAvatar") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Slave images in lists are", "seeSummaryImages") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - - options.addOption("Slave images in the weekly report are", "seeReportImages") - .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); - } - el.append(options.render()); - return el; -}; -- GitLab