diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js index b859b9718de84ccb55cad8f0f9e0a02895929dcc..15d295d1db54e7e4953cf219bf72dd1c859cec38 100644 --- a/js/003-data/gameVariableData.js +++ b/js/003-data/gameVariableData.js @@ -163,7 +163,7 @@ App.Data.defaultGameStateVariables = { summaryStats: 0, surnameOrder: 0, /** @type {Object.<string, string>} */ - tabChoice: {Main: "all"}, + tabChoice: { Main: "all" }, universalRulesAssignsSelfFacility: 0, universalRulesBirthing: 0, universalRulesCSec: 0, @@ -342,7 +342,7 @@ App.Data.resetOnNGPlus = { }, /** @type {assistant} */ assistant: {}, - targetArcology: {fs: "New"}, + targetArcology: { fs: "New" }, readySlaves: 0, plot: 1, @@ -782,6 +782,11 @@ App.Data.resetOnNGPlus = { smartVaginalAttachments: 0, }, }, + customItem: { + /** @type {Map<string, slaveButtplugs>} */ + buttPlugs: new Map([]), + dildos: new Map([]), + }, dairyPiping: 0, milkPipeline: 0, cumPipeline: 0, @@ -911,11 +916,11 @@ App.Data.resetOnNGPlus = { ui: "start", tooltipsEnabled: 0, - brandTarget: {primary: "left buttock", secondary: "left buttock", local: "left buttock"}, - brandDesign: {primary: "your initials", official: "your initials", local: "your initials"}, + brandTarget: { primary: "left buttock", secondary: "left buttock", local: "left buttock" }, + brandDesign: { primary: "your initials", official: "your initials", local: "your initials" }, - scarTarget: {primary: "left cheek", secondary: "left cheek", local: "left cheek"}, - scarDesign: {primary: "generic", local: "generic"}, + scarTarget: { primary: "left cheek", secondary: "left cheek", local: "left cheek" }, + scarDesign: { primary: "generic", local: "generic" }, oralTotal: 0, vaginalTotal: 0, @@ -1000,6 +1005,7 @@ App.Data.resetOnNGPlus = { animalTesticles: 0, /* {pigTestes: 0, dogTestes: 0, horseTestes: 0, cowTestes: 0} currently unused*/ animalMpreg: 0, /* {pigMpreg: 0, dogMpreg: 0, horseMpreg: 0, cowMpreg: 0} currently unused*/ geneticMappingUpgrade: 0, + toyShop: false, pregnancyMonitoringUpgrade: 0, cloningSystem: 0, geneticFlawLibrary: 0, diff --git a/js/003-data/slaveWearData.js b/js/003-data/slaveWearData.js index 737c020d0284e3fede3ea03c02cb5a32d06bd353..8cab5e39f1d0c3014ffd3718bef01027589d2894 100644 --- a/js/003-data/slaveWearData.js +++ b/js/003-data/slaveWearData.js @@ -1148,13 +1148,12 @@ App.Data.shoes = new Map([ // TODO: add lift property * @property {string} name * @property {FC.FutureSociety} [fs] Automatically unlocked with this FS. * @property {boolean} [requirements] - * @property {boolean} [harsh] - * @property {0|1|2|3} width height in cm. Adds to heel height. - * @property {0|1|2} length height in cm. Over 4cm they may totter. 21cm and over (8 inch heels) will be painful/extreme + * @property {0|1|2|3} width + * @property {0|1|2} length */ /** - * @type {Map<string, slaveButtplugs>} slaveShoesCategory + * @type {Map<string, slaveButtplugs>} */ App.Data.buttplugs = new Map([ ["none", diff --git a/src/002-config/fc-version.js b/src/002-config/fc-version.js index c0159ceb1e40363fe3d02c90f290ad8926aefd46..393d11da8a111d1fbbbecb3174255714e1d76ba8 100644 --- a/src/002-config/fc-version.js +++ b/src/002-config/fc-version.js @@ -2,5 +2,5 @@ App.Version = { base: "0.10.7.1", // The vanilla version the mod is based off of, this should never be changed. pmod: "3.8.4", commitHash: null, - release: 1112 // When getting close to 2000, please remove the check located within the onLoad() function defined at line five of src/js/eventHandlers.js. + release: 1113 // When getting close to 2000, please remove the check located within the onLoad() function defined at line five of src/js/eventHandlers.js. }; diff --git a/src/005-passages/facilitiesPassages.js b/src/005-passages/facilitiesPassages.js index 0a372a4f922ae23e0d70ba36c8b93d31a79ab590..0fcb73572a5b57709a5a37271459a350aeddc86b 100644 --- a/src/005-passages/facilitiesPassages.js +++ b/src/005-passages/facilitiesPassages.js @@ -68,3 +68,12 @@ new App.DomPassage("Rules Assistant", return div; }, ["jump-to-safe", "jump-from-safe"] ); + +new App.DomPassage("Toy Shop", + () => { + V.nextButton = "Back"; + V.nextLink = "Manage Penthouse"; + + return App.UI.toyShop(); + }, ["jump-to-safe", "jump-from-safe"] +); diff --git a/src/facilities/toyShop/toyShop.js b/src/facilities/toyShop/toyShop.js new file mode 100644 index 0000000000000000000000000000000000000000..84ac5b700889cf72295d9af2100f280f12df2f16 --- /dev/null +++ b/src/facilities/toyShop/toyShop.js @@ -0,0 +1,241 @@ +/** + * UI for the Body Modification system/studio. Refreshes without refreshing the passage. + */ +App.UI.toyShop = function() { + const container = document.createElement("span"); + let buttPlugName; + let buttPlugData; + let selectedPlug; + init(); + + container.append(createPage()); + return container; + + function createPage() { + const el = new DocumentFragment(); + el.append(intro()); + el.append(buttPlugs()); + return el; + } + + function init() { + buttPlugName = ""; + buttPlugData = { + name: "", + width: 1, + length: 1 + }; + } + + function intro() { + const el = new DocumentFragment(); + App.UI.DOM.appendNewElement("h1", el, "Toy Shop"); + App.UI.DOM.appendNewElement("div", el, `The room is filled with the smell of rubber, latex, and various synthetic materials and solvents. A series of screens allows you to design toys of various shapes and sizes, and then produce them at scale. A bin of defects sits in the corner, glistening a bit under a layer of lubrication.`, "scene-intro"); + return el; + } + + + function buttPlugs() { + const el = new DocumentFragment(); + let linkArray; + App.UI.DOM.appendNewElement("h2", el, "Buttplugs"); + const select = App.UI.DOM.appendNewElement("div", el, App.UI.DOM.link("Start a new design", () => { + init(); + refresh(); + })); + if (V.customItem.buttPlugs.size > 0) { + select.append(selectDesign()); + } + el.append(create()); + return el; + + function create() { + const el = new DocumentFragment(); + const existingDesign = V.customItem.buttPlugs.get(buttPlugName); + if (existingDesign) { + el.append(descLocked()); + } else { + el.append(desc()); + } + if (buttPlugData.name) { + el.append(title()); + } + el.append( + width(), + length(), + ); + + if (existingDesign) { + const build = App.UI.DOM.appendNewElement("div", el, `Send updated design to production and make sure all appropriate slaves are updated `); + const linkArray = []; + linkArray.push( + App.UI.DOM.link( + "Update the mold", + () => { buildPlug(); } + ) + ); + + linkArray.push( + App.UI.DOM.link( + `Recall "${buttPlugName}"`, + () => { deletePlug(); } + ) + ); + build.append(App.UI.DOM.generateLinksStrip(linkArray)); + } else if (buttPlugName && buttPlugData.name){ + el.append(apply()); + } + + return el; + + function descLocked() { + return App.UI.DOM.makeElement("div", `Description has already been selected for this model: "${buttPlugName}"`); + } + } + function selectDesign() { + const el = new DocumentFragment(); + const choice = App.UI.DOM.appendNewElement("span", el, ` or choose an existing design to edit `); + const select = App.UI.DOM.appendNewElement("select", choice); + let matchFound = false; + for (const [key, values] of V.customItem.buttPlugs) { + const option = App.UI.DOM.appendNewElement("option", select, values.name); + option.value = key; + if (option.value === buttPlugName) { + option.selected = true; + matchFound = true; + } + } + if (!matchFound) { + select.selectedIndex = -1; + } + select.onchange = () => { + const O = select.options[select.selectedIndex]; + selectedPlug = O.value; + buttPlugName = selectedPlug; + buttPlugData = V.customItem.buttPlugs.get(selectedPlug); + refresh(); + }; + return el; + } + + function desc() { + const value = App.UI.DOM.makeElement("div", `Enter shape here as it will appear in descriptions `); + value.append(App.UI.DOM.makeTextBox( + buttPlugName, + v => { + buttPlugName = v; + buttPlugData.name = capFirstChar(v); + refresh(); + } + )); + App.UI.DOM.appendNewElement("span", value, ` Your slave has a standard ${buttPlugName ? buttPlugName : `spade-shaped plug`} wedged firmly in their asshole.`, "note"); + return value; + } + + function title() { + const title = App.UI.DOM.makeElement("div", `Enter title as it will appear in lists of choices `); + title.append(App.UI.DOM.makeTextBox( + buttPlugData.name, + v => { + buttPlugData.name = capFirstChar(v); + refresh(); + } + )); + return title; + } + + function width() { + const widthOptions = new Map([ + ["standard", 1], + ["large", 2], + ["huge", 3], + ]); + const width = App.UI.DOM.makeElement("div", `Select width `); + linkArray = []; + for (const [key, value] of widthOptions) { + if (buttPlugData.width === value) { + linkArray.push( + App.UI.DOM.disabledLink( + key, + ["Currently selected"] + ) + ); + } else { + linkArray.push( + App.UI.DOM.link( + key, + () => { + buttPlugData.width = value; + refresh(); + } + ) + ); + } + } + width.append(App.UI.DOM.generateLinksStrip(linkArray)); + return width; + } + + function length() { + const lengthOptions = new Map([ + ["standard", 1], + ["long", 2], + ]); + const length = App.UI.DOM.makeElement("div", `Select length `); + linkArray = []; + for (const [key, value] of lengthOptions) { + if (buttPlugData.length === value) { + linkArray.push( + App.UI.DOM.disabledLink( + key, + ["Currently selected"] + ) + ); + } else { + linkArray.push( + App.UI.DOM.link( + key, + () => { + buttPlugData.length = value; + refresh(); + } + ) + ); + } + } + length.append(App.UI.DOM.generateLinksStrip(linkArray)); + return length; + } + + function buildPlug() { + V.customItem.buttPlugs.set(buttPlugName, buttPlugData); + init(); + refresh(); + } + + function apply() { + const build = App.UI.DOM.appendNewElement("div", el, `Send design to production and make available for all slaves `); + build.append( + App.UI.DOM.link( + "Start the mold", + () => { buildPlug(); } + ) + ); + return build; + } + + function deletePlug() { + V.customItem.buttPlugs.delete(buttPlugName); + for (const slave of V.slaves) { + if (slave.buttplug === buttPlugName) { + slave.buttplug = "none"; + } + } + refresh(); + } + } + + function refresh() { + jQuery(container).empty().append(createPage()); + } +}; diff --git a/src/gui/quicklinks.js b/src/gui/quicklinks.js index c7a5dbb9347e69d586d7466daa550ebb0a43e461..f993e46a2038ab8f40f373312f0e156ea1212795 100644 --- a/src/gui/quicklinks.js +++ b/src/gui/quicklinks.js @@ -65,6 +65,7 @@ App.UI.quickMenu = (function() { "Implant Manufactory": true, "Prosthetic Lab": true, "Wardrobe": true, + "Toy Shop": true, "The Black Market": true, }, Tools: { @@ -96,6 +97,7 @@ App.UI.quickMenu = (function() { "Firebase": () => !V.SF.Toggle || V.SF.Toggle < 1 || V.SF.Active < 1, "Future Society": () => !V.FSAnnounced, "Gene Lab": () => !V.geneticMappingUpgrade, + "Toy Shop": () => !V.toyShop, "Head Girl Suite": () => !V.HGSuite, "Implant Manufactory": () => !V.ImplantProductionUpgrade, "Incubator": () => !V.incubator, diff --git a/src/interaction/siWardrobe.js b/src/interaction/siWardrobe.js index 41862a1791f9738bf707c76fd7f30657003b01a2..f9f984bee5b3fc05d008e910dcf08c644f297471 100644 --- a/src/interaction/siWardrobe.js +++ b/src/interaction/siWardrobe.js @@ -519,6 +519,19 @@ App.UI.SlaveInteract.wardrobe = function(slave) { App.UI.DOM.appendNewElement("div", el, generateRows(normalArray, "buttplug", true), "choices"); App.UI.DOM.appendNewElement("div", el, generateRows(longArray, "buttplug", true), "choices"); + if (V.customItem.buttPlugs.size > 0) { + let customArray = []; + for (const [key, object] of V.customItem.buttPlugs) { + const reshapedItem = { + text: object.name, + updateSlave: {buttplug: key}, + FS: object.fs, + }; + customArray.push(reshapedItem); + } + App.UI.DOM.appendNewElement("div", el, generateRows(customArray, "buttplug", false), "choices"); + } + return el; } diff --git a/src/js/utilsAssessSlave.js b/src/js/utilsAssessSlave.js index acd47c0bf2b31895bb19c12194c6ace86138ab20..7f4c6e6166fa67241b313151361f87f328f836c6 100644 --- a/src/js/utilsAssessSlave.js +++ b/src/js/utilsAssessSlave.js @@ -274,7 +274,8 @@ globalThis.shoeHeight = function(slave) { * @returns {0|1|2|3} */ globalThis.plugWidth = function(slave) { - return App.Data.buttplugs.get(slave.buttplug) ? App.Data.buttplugs.get(slave.buttplug).width : 0; + const plug = App.Data.buttplugs.get(slave.buttplug) || V.customItem.buttPlugs.get(slave.buttplug); + return plug.width || 0; }; /** @@ -282,7 +283,8 @@ globalThis.plugWidth = function(slave) { * @returns {0|1|2|3} */ globalThis.plugLength = function(slave) { - return App.Data.buttplugs.get(slave.buttplug) ? App.Data.buttplugs.get(slave.buttplug).length : 0; + const plug = App.Data.buttplugs.get(slave.buttplug) || V.customItem.buttPlugs.get(slave.buttplug); + return plug.length || 0; }; /** diff --git a/src/npc/descriptions/butt/buttplug.js b/src/npc/descriptions/butt/buttplug.js index bf8bfd502dff87d6cd2b0935e35a0b1b88928ed4..eda8f53815e9c790dc38fef555aca17a43de2340 100644 --- a/src/npc/descriptions/butt/buttplug.js +++ b/src/npc/descriptions/butt/buttplug.js @@ -5,7 +5,7 @@ * @param {boolean} [params.eventDescription] * @returns {string} */ -App.Desc.buttplug = function(slave, {market, eventDescription} = {}) { +App.Desc.buttplug = function(slave, { market, eventDescription } = {}) { const r = []; const { he, him, his, He, His @@ -612,73 +612,76 @@ App.Desc.buttplug = function(slave, {market, eventDescription} = {}) { } } } - - switch (slave.buttplug) { - case "plug": - r.push(`It's filled by a standard`); + const buttplug = App.Data.buttplugs.get(slave.buttplug) || V.customItem.buttPlugs.get(slave.buttplug); + if (buttplug.width === 1) { + if (buttplug.length === 1) { + r.push(`It's filled by a standard sized`); if (slave.anus > 2) { - r.push(`buttplug, which is on the verge of falling out.`); + r.push(`${slave.buttplug}, which is on the verge of falling out.`); } else { - r.push(`buttplug.`); - } - if (slave.buttplugAttachment === "tail") { - r.push(`A tail protrudes from the back of the plug and dangles from ${his} rear.`); - } else if (slave.buttplugAttachment === "fox tail") { - r.push(`A bushy ${slave.hColor} tail with a white tip protrudes from the back of the plug and dangles from ${his} rear.`); - } else if (slave.buttplugAttachment === "cow tail") { - r.push(`A slim, spotted tail with a cute tuft at its tip protrudes from the back of the plug and dangles from ${his} rear.`); - } else if (slave.buttplugAttachment === "cat tail") { - r.push(`A tail protrudes from the back of the plug and springs upwards from ${his} rear.`); + r.push(`${slave.buttplug}.`); } - break; - case "long plug": + } else { r.push(`It's filled by a standard sized, overly long`); if (slave.anus > 2) { - r.push(`buttplug, which is on the verge of sliding out ${his} rear.`); + r.push(`${slave.buttplug}, which is on the verge of sliding out ${his} rear.`); } else { - r.push(`buttplug.`); + r.push(`${slave.buttplug}.`); } r.push(`It causes a noticeable bulge in ${his} belly.`); - if (slave.buttplugAttachment === "tail") { - r.push(`A tail protrudes from the back of the plug and dangles from ${his} rear.`); - } else if (slave.buttplugAttachment === "fox tail") { - r.push(`A bushy ${slave.hColor} tail with a white tip protrudes from the back of the plug and dangles from ${his} rear.`); - } else if (slave.buttplugAttachment === "cow tail") { - r.push(`A slim, spotted tail with a cute tuft at its tip protrudes from the back of the plug and dangles from ${his} rear.`); - } else if (slave.buttplugAttachment === "cat tail") { - r.push(`A tail protrudes from the back of the plug and springs upwards from ${his} rear.`); + } + } else if (buttplug.width === 2) { + r.push(`It's`); + if (slave.anus < 2) { + r.push(`agonizingly stretched`); + } else if (slave.anus < 3) { + r.push(`uncomfortably stretched`); + } else { + r.push(`comfortably stretched`); + } + if (buttplug.length === 1) { + r.push(`by a large ${slave.buttplug}.`); + } else { + r.push(`by a large and long ${slave.buttplug}. It causes a noticeable bulge in ${his} belly.`); + } + } else if (buttplug.width === 3) { + if (buttplug.length === 1) { + if (slave.anus < 4) { + r.push(`It's agonizingly stretched by a ${slave.buttplug} so huge ${his} anus is probably being stretched into a permanent gape.`); + } else { + r.push(`Its ridiculous gape is comfortably filled by a huge ${slave.buttplug}.`); } - break; - case "large plug": - r.push(`It's`); - if (slave.anus < 2) { - r.push(`agonizingly stretched`); - } else if (slave.anus < 3) { - r.push(`uncomfortably stretched`); + } else { + if (slave.anus < 4) { + r.push(`It's agonizingly stretched by a ${slave.buttplug} so huge it causes ${his} belly to bulge and is likely stretching ${his} anus into a permanent gape.`); } else { - r.push(`comfortably stretched`); + r.push(`Its ridiculous gape is comfortably filled by a wide and long ${slave.buttplug}. It causes a noticeable bulge in ${his} belly.`); } - r.push(`by a large buttplug.`); + } + if (slave.anus < 4) { + if (slave.fuckdoll === 0) { + if (slave.fetish === "masochist" && slave.fetishKnown === 1 && slave.fetishStrength > 60) { + r.push(`${He}'s frequently in tears from the pain, which is so sharp it often tips ${him} over into spontaneous orgasm.`); + } else { + r.push(`${He} spends much of ${his} time sobbing with anal pain and fear at having ${his} hole ruined.`); + } + } + } + } + + switch (buttplug.width) { + case 3: if (slave.buttplugAttachment === "tail") { - r.push(`${He} swings the tail from side to side with every shift of ${his} rear.`); + r.push(`${He} sways the tail back and forth with every ginger movement of ${his} rear.`); } else if (slave.buttplugAttachment === "fox tail") { - r.push(`${He} swings the bushy ${slave.hColor} tail from side to side with every shift of ${his} rear.`); + r.push(`${He} sways the bushy ${slave.hColor} tail back and forth with every ginger movement of ${his} rear.`); } else if (slave.buttplugAttachment === "cow tail") { - r.push(`${He} swings the spotted tail from side to side with every shift of ${his} rear.`); + r.push(`${He} sways the spotted tail back and forth with every ginger movement of ${his} rear.`); } else if (slave.buttplugAttachment === "cat tail") { - r.push(`${He} bounces the tail from side to side with every shift of ${his} rear.`); + r.push(`${He} jiggles the tail back and forth with every ginger movement of ${his} rear.`); } break; - case "long, large plug": - r.push(`It's`); - if (slave.anus < 2) { - r.push(`agonizingly stretched`); - } else if (slave.anus < 3) { - r.push(`uncomfortably stretched`); - } else { - r.push(`comfortably stretched`); - } - r.push(`by a large and long buttplug. It causes a noticeable bulge in ${his} belly.`); + case 2: if (slave.buttplugAttachment === "tail") { r.push(`${He} swings the tail from side to side with every shift of ${his} rear.`); } else if (slave.buttplugAttachment === "fox tail") { @@ -689,51 +692,17 @@ App.Desc.buttplug = function(slave, {market, eventDescription} = {}) { r.push(`${He} bounces the tail from side to side with every shift of ${his} rear.`); } break; - case "huge plug": - if (slave.anus < 4) { - r.push(`It's agonizingly stretched by a plug so huge ${his} anus is probably being stretched into a permanent gape.`); - if (slave.fuckdoll === 0) { - if (slave.fetish === "masochist" && slave.fetishKnown === 1 && slave.fetishStrength > 60) { - r.push(`${He}'s frequently in tears from the pain, which is so sharp it often tips ${him} over into spontaneous orgasm.`); - } else { - r.push(`${He} spends much of ${his} time sobbing with anal pain and fear at having ${his} hole ruined.`); - } - } - } else { - r.push(`Its ridiculous gape is comfortably filled by a huge buttplug.`); - } - if (slave.buttplugAttachment === "tail") { - r.push(`${He} sways the tail back and forth with every ginger movement of ${his} rear.`); - } else if (slave.buttplugAttachment === "fox tail") { - r.push(`${He} sways the bushy ${slave.hColor} tail back and forth with every ginger movement of ${his} rear.`); - } else if (slave.buttplugAttachment === "cow tail") { - r.push(`${He} sways the spotted tail back and forth with every ginger movement of ${his} rear.`); - } else if (slave.buttplugAttachment === "cat tail") { - r.push(`${He} jiggles the tail back and forth with every ginger movement of ${his} rear.`); - } - break; - case "long, huge plug": - if (slave.anus < 4) { - r.push(`It's agonizingly stretched by a plug so huge it causes ${his} belly to bulge and is likely stretching ${his} anus into a permanent gape.`); - if (slave.fuckdoll === 0) { - if (slave.fetish === "masochist" && slave.fetishKnown === 1 && slave.fetishStrength > 60) { - r.push(`${He}'s frequently in tears from the pain, which is so sharp it often tips ${him} over into spontaneous orgasm.`); - } else { - r.push(`${He} spends much of ${his} time sobbing with anal pain and fear at having ${his} hole ruined.`); - } - } - } else { - r.push(`Its ridiculous gape is comfortably filled by a wide and long buttplug. It causes a noticeable bulge in ${his} belly.`); - } + case 1: if (slave.buttplugAttachment === "tail") { - r.push(`${He} sways the tail back and forth with every ginger movement of ${his} rear.`); + r.push(`A tail protrudes from the back of the plug and dangles from ${his} rear.`); } else if (slave.buttplugAttachment === "fox tail") { - r.push(`${He} sways the bushy ${slave.hColor} tail back and forth with every ginger movement of ${his} rear.`); + r.push(`A bushy ${slave.hColor} tail with a white tip protrudes from the back of the plug and dangles from ${his} rear.`); } else if (slave.buttplugAttachment === "cow tail") { - r.push(`${He} sways the spotted tail back and forth with every ginger movement of ${his} rear.`); + r.push(`A slim, spotted tail with a cute tuft at its tip protrudes from the back of the plug and dangles from ${his} rear.`); } else if (slave.buttplugAttachment === "cat tail") { - r.push(`${He} jiggles the tail back and forth with every ginger movement of ${his} rear.`); + r.push(`A tail protrudes from the back of the plug and springs upwards from ${his} rear.`); } } + return r.join(" "); }; diff --git a/src/uncategorized/managePenthouse.tw b/src/uncategorized/managePenthouse.tw index 34163138a6feb591ba32f7e89e4aeb2102fea304..7133d8f82669f540bda3db6de7e871ff67bd9d65 100644 --- a/src/uncategorized/managePenthouse.tw +++ b/src/uncategorized/managePenthouse.tw @@ -279,6 +279,15 @@ </div> <</if>> + <div> + <<if !$toyShop>> + [[Install a workshop for making custom toys|Manage Penthouse][cashX(-10000, "capEx"), $toyShop = true, $PC.skill.engineering += .1]] + <span class="detail">Costs <<print cashFormat(10000)>></span> + <<else>> + There is a [[workshop|Toy Shop]] for making custom toys. + <</if>> + </div> + <div> <<if $studio == 0>> [[Install a media hub to convert slave video feeds into pornography|Manage Penthouse][cashX(forceNeg(Math.trunc(10000*$upgradeMultiplierArcology)), "capEx"), $studio = 1, $PC.skill.engineering += 1]]