diff --git a/.gitignore b/.gitignore index 637f049bf560872d04b147cc4d77c75ed445e4f7..e5083cd695a31a8edcfb48652e4b9dcd1f67cebb 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,8 @@ pnpm-lock.yaml # Visual Studio Code .vscode/settings.json .vscode/tasks.json +# .vscode/bookmarks.json is created by the amazing https://marketplace.visualstudio.com/items?itemName=alefragnani.Bookmarks VSCode extension +.vscode/bookmarks.json *.code-workspace # WebStorm diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js index 182213c657e25c393998e021d5c8f70231098c0a..f124864282419f0f688176beb7a0d6b00b42508a 100644 --- a/js/003-data/gameVariableData.js +++ b/js/003-data/gameVariableData.js @@ -331,6 +331,15 @@ App.Data.defaultGameStateVariables = { processedSlaves: [], }, + /** @type {number} FC.HumanState.ID */ + donatrix: 0, + /** @type {number} FC.HumanState.ID */ + receptrix: 0, + /** @type {number} FC.HumanState.ID */ + impregnatrix: 0, + /** @type {App.Entity.Fetus[]} */ + transplantFetuses: [], + // Mods mods: { /** @type {FC.Mods.Food} */ diff --git a/src/002-config/fc-version.js b/src/002-config/fc-version.js index cd3725834bd12eda3f9f2c477e088d83f868abba..c7c616859186f03cf80881bec90829106b6902f5 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: "4.0.0-alpha.31", commitHash: null, - release: 1234, // When getting close to 2000, please remove the check located within the onLoad() function defined at line five of src/js/eventHandlers.js. + release: 1235, // 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/interactPassages.js b/src/005-passages/interactPassages.js index 35ab9d640ef8fddb3b85dee3faec16f754ec7c34..96b7d42faa661ee9eeef460f3c5ad80a645ce01a 100644 --- a/src/005-passages/interactPassages.js +++ b/src/005-passages/interactPassages.js @@ -381,20 +381,6 @@ new App.DomPassage("Cloning Workaround", } ); -new App.DomPassage("Ova Transplant Workaround", - () => { - V.nextButton = "Cancel"; - return App.UI.ovaTransplantWorkaround(); - } -); - -new App.DomPassage("Bulk Ova Transplant Workaround", - () => { - V.nextButton = "Cancel"; - return App.UI.ovaTransplantWorkaround(true); - } -); - new App.DomPassage("Subordinate Targeting", () => { V.nextButton = "Back"; diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js index ef598d43e4064d3d0e072c57ce402bccd48a043f..8400380fe89da276467741fb4d927947b9451a73 100644 --- a/src/data/backwardsCompatibility/backwardsCompatibility.js +++ b/src/data/backwardsCompatibility/backwardsCompatibility.js @@ -279,6 +279,32 @@ App.Update.globalVariables = function(node) { V.pregnancyNotice.renderFetus = V.pregnancyNotice.renderFetus || true; V.pregnancyNotice.processedSlaves = V.pregnancyNotice.processedSlaves || []; + V.donatrix = V.donatrix || 0; + if (typeof V.donatrix !== "number") { + if ("ID" in V.donatrix) { + V.donatrix = V.donatrix.ID; + } else { + V.donatrix = 0; + } + } + V.receptrix = V.receptrix || 0; + if (typeof V.receptrix !== "number") { + if ("ID" in V.receptrix) { + V.receptrix = V.receptrix.ID; + } else { + V.receptrix = 0; + } + } + V.impregnatrix = V.impregnatrix || 0; + if (typeof V.impregnatrix !== "number") { + if ("ID" in V.impregnatrix) { + V.impregnatrix = V.impregnatrix.ID; + } else { + V.impregnatrix = 0; + } + } + V.transplantFetuses = V.transplantFetuses || []; + if (Array.isArray(V.nationalities)) { V.nationalities = weightedArray2HashMap(V.nationalities); } @@ -1671,7 +1697,6 @@ App.Update.slaveRecords = function(node) { return (fetus.reserve !== "") ? fetus.reserve : "nothing"; } })(); - fetus.noticeData.transplantReceptrix = fetus.noticeData.transplantReceptrix || 0; fetus.noticeData.cheatAccordionCollapsed = fetus.noticeData.cheatAccordionCollapsed || true; fetus.noticeData.child = fetus.noticeData.child || undefined; }); diff --git a/src/events/nonRandom/pregnancyNotice.js b/src/events/nonRandom/pregnancyNotice.js index 5f82b38d64ac657bbdbf17ecd5071d4d8f20d3db..9211d7c1dc8072c2b2f0c385d058df457bc7b744 100644 --- a/src/events/nonRandom/pregnancyNotice.js +++ b/src/events/nonRandom/pregnancyNotice.js @@ -141,12 +141,19 @@ App.Events.PregnancyNotice.Event = (node, mother) => { // @ts-expect-error weekAcquired doesn't exist on PlayerState const motherIsNew = (mother.ID === -1) ? false : (mother.weekAcquired === V.week); const totalBabyCount = mother.womb.length; - const fetuses = mother.womb.filter((fetus) => ( - fetus.age === 2 || - (fetus.age === 6 && ["wait", "undecided"].includes(fetus.noticeData.fate)) || - // @ts-expect-error weekAcquired doesn't exist on PlayerState - (mother.ID !== -1 && mother.weekAcquired === V.week && fetus.age > 6) - )); + /** + * @param {App.Entity.Fetus} fetus + * @returns {boolean} + */ + const filterFetus = (fetus) => { + return ( + fetus.age === 2 || + (fetus.age === 6 && ["wait", "undecided"].includes(fetus.noticeData.fate)) || + // @ts-expect-error weekAcquired doesn't exist on PlayerState + (mother.ID !== -1 && mother.weekAcquired === V.week && fetus.age > 6) + ); + }; + const fetuses = mother.womb.filter((fetus) => filterFetus(fetus)); const hasIncubator = (V.incubator.capacity > 0); const nurseryName = App.Entity.facilities.nursery.name.replace(/^the /i, "").trim(); const incubatorName = App.Entity.facilities.incubator.name.replace(/^the /i, "").trim(); @@ -169,7 +176,7 @@ App.Events.PregnancyNotice.Event = (node, mother) => { let motherDiv = App.UI.DOM.makeElement("div"); if (!motherIsPC) { // @ts-expect-error FC.HumanState is not accepted by drawEventArt - App.Events.drawEventArt(motherDiv, mother); + App.Events.drawEventArt(motherDiv, mother); // TODO: smaller image } // @ts-expect-error saSlaveName will not accept FC.HumanState motherDiv.append(App.SlaveAssignment.saSlaveName(mother)); @@ -195,17 +202,22 @@ App.Events.PregnancyNotice.Event = (node, mother) => { // TODO:@franklygeorge mother's (If not PC) info (Same as main screen). // TODO:@franklygeorge list out each group of babies and how old they are (See how the slave description does it for superfetation) + // TODO:@franklygeorge options: collapse all, expand all, collapse processed, expand processed + // TODO:@franklygeorge: auto collapse processed option + let unprocessedDiv = App.UI.DOM.makeElement("div"); function updateProcessed() { // clear unprocessedDiv's contents unprocessedDiv.innerHTML = ""; - let unprocessedFetuses = fetuses.filter((fetus) => ( + let unprocessedFetuses = mother.womb.filter((fetus) => ( + filterFetus(fetus) && ( fetus.noticeData.fate === "undecided" || - (fetus.age >= 6 && fetus.noticeData.fate === "wait") - ) && - mother.womb.indexOf(fetus) !== -1 // transplanted fetuses are processed + (fetus.age >= 6 && fetus.noticeData.fate === "wait") || + fetus.noticeData.fate === "terminate" || + fetus.noticeData.fate === "transplant" + ) )); if (unprocessedFetuses.length === 0) { unprocessedDiv.append(`There are no unprocessed children.`); @@ -213,16 +225,17 @@ App.Events.PregnancyNotice.Event = (node, mother) => { App.Utils.updateUserButton(); } else { let unprocessedFetusNames = unprocessedFetuses.map((fetus) => (fetus.genetics.name)); + let terminatableFetuses = unprocessedFetuses.filter((fetus) => (mother.womb.indexOf(fetus) !== -1) && canTerminateFetus(mother, fetus)); + let transplantableFetuses = unprocessedFetuses.filter((fetus) => canTransplantFetus(mother, fetus) === 1); unprocessedDiv.append(`${toSentence(unprocessedFetusNames, ", ", " and ")} ${(unprocessedFetuses.length === 1) ? "has": "have"} not been processed yet.`); V.nextButton = " "; App.Utils.updateUserButton(); let bulkOptions = new App.UI.OptionsGroup; let bulkChoice = { - /** @type {"incubator"|"nursery"|"nothing"|"wait"|"undecided"} */ + /** @type {"incubator"|"nursery"|"nothing"|"wait"|"undecided"|"terminate"|"transplant"} */ choice: "undecided" }; - // TODO:@franklygeorge option to show only unprocessed (collapse all processed and expand all unprocessed accordions); bulkOptions.customRefresh(() => { unprocessedFetuses.forEach((fetus) => { fetus.noticeData.fate = bulkChoice.choice; @@ -233,6 +246,15 @@ App.Events.PregnancyNotice.Event = (node, mother) => { let noteSpan = $(`#${fetus.ID}-note-span`)[0]; fetusInfo(fetus, noteSpan); }); + if (bulkChoice.choice === "transplant") { + let bulkTransplantDiv = App.UI.DOM.makeElement("div"); + unprocessedDiv.append(bulkTransplantDiv); + transplantingTool(mother, unprocessedFetuses, bulkTransplantDiv, transplantableFetuses.length, updateProcessed, undefined, true); + } else if (bulkChoice.choice === "terminate") { + let bulkTerminateDiv = App.UI.DOM.makeElement("div"); + unprocessedDiv.append(bulkTerminateDiv); + BulkTerminateTool(mother, unprocessedFetuses, bulkTerminateDiv, terminatableFetuses.length, updateProcessed); + } }); let bulkOption = bulkOptions.addOption("Change all unprocessed to: ", "choice", bulkChoice); if (hasIncubator && unreservedTanks() >= unprocessedFetuses.length) { @@ -244,6 +266,12 @@ App.Events.PregnancyNotice.Event = (node, mother) => { if (unprocessedFetuses.filter((fetus) => fetus.age === 2).length !== 0) { bulkOption.addValue(`Ask me again in ${num(4)} more weeks.`, "wait"); } + if (transplantableFetuses.length !== 0) { + bulkOption.addValue("Transplant fetuses", "transplant"); + } + if (terminatableFetuses.length !== 0) { + bulkOption.addValue(`Terminate fetuses`, "terminate"); + } bulkOption.addValue("Do nothing", "nothing"); unprocessedDiv.append(bulkOptions.render()); @@ -263,6 +291,7 @@ App.Events.PregnancyNotice.Event = (node, mother) => { fetusesDiv.prepend(fetusAccordion.accordion); }); + // TODO:@franklygeorge also show unprocessedDiv here (above fetus info) node.append(fetusesDiv); node.append(unprocessedDiv); @@ -282,7 +311,7 @@ App.Events.PregnancyNotice.Event = (node, mother) => { const fatherIsPC = (typeof father !== "string" && father.ID === -1); const fatherName = (typeof father === "string") ? father: SlaveFullName(father); - const canTerminate = canTerminateFetus(mother, fetus); + const canTerminate = (mother.womb.indexOf(fetus) !== -1) && canTerminateFetus(mother, fetus); const canTransplant = canTransplantFetus(mother, fetus); const twins = getFetusTwins(fetus); @@ -311,7 +340,7 @@ App.Events.PregnancyNotice.Event = (node, mother) => { // add child image to intro if (V.pregnancyNotice.renderFetus === true && (V.geneticMappingUpgrade >= 1 || cheating)) { let childArtDiv = App.UI.DOM.makeElement("div"); - App.Events.drawEventArt(childArtDiv, fakeChild, "no clothing"); + App.Events.drawEventArt(childArtDiv, fakeChild, "no clothing"); // TODO: smaller image intro.push(childArtDiv); } @@ -545,6 +574,9 @@ App.Events.PregnancyNotice.Event = (node, mother) => { fetus.reserve = ""; }); } + fetusOption.addValue("Undecided", "undecided", () => { + fetus.reserve = ""; + }); // create div let fetusDiv = App.UI.DOM.makeElement("div"); @@ -611,7 +643,7 @@ App.Events.PregnancyNotice.Event = (node, mother) => { if (fetus.noticeData.fate === "nursery") { fetusDiv.append(App.UI.DOM.makeElement( "div", - `If they are sent to the nursery then the description above will not match when born is born.`, + `If they are sent to the nursery then the description above will not match when the child is born.`, ["note"] // TODO:@franklygeorge fix this. Probably need to make InfantState an extension of SlaveState or just convert it to SlaveState instead )); @@ -621,12 +653,10 @@ App.Events.PregnancyNotice.Event = (node, mother) => { if (fetus.noticeData.fate === "terminate") { const options = new App.UI.OptionsGroup(); options.customRefresh(() => { - WombRemoveFetus(mother, mother.womb.indexOf(fetus)); - if (mother.preg === 0) { - mother.pregWeek = -1; - } + terminateFetus(mother, fetus); let jDiv = $(`#fetus-id-${fetus.ID}`); jDiv.empty().append("Fetus terminated"); + updateProcessed(); }); options.addOption("", "do", {do: false}) .addValue("Terminate", true); @@ -642,93 +672,9 @@ App.Events.PregnancyNotice.Event = (node, mother) => { App.UI.DOM.appendNewElement("span", fetusDiv, `Fetus cannot be transplanted because it has already been transplanted. The original mother was `); App.UI.DOM.appendNewElement("span", fetusDiv, popup); } else { - let transplantingCost = V.surgeryCost * 2; - // Slave selector - let eligibility = 0; - let options = new App.UI.OptionsGroup(); - options.customRefresh(() => { - fetusInfo(fetus, noteSpan); - }); - let option = options.addOption(`Select a host`, "transplantReceptrix", fetus.noticeData); - - App.UI.DOM.appendNewElement("h4", fetusDiv, "Slave details"); - for (const slave of V.slaves) { - if ((mother.ID !== slave.ID && slave.ovaries > 0 || slave.mpreg > 0) && - isSlaveAvailable(slave) && slave.preg >= 0 && slave.preg < slave.pregData.normalBirth / 10 && - slave.pregWeek >= 0 && slave.pubertyXX === 1 && slave.pregType < 12 && - slave.bellyImplant === -1 && slave.broodmother === 0 && slave.inflation <= 2 && slave.physicalAge < 70 - ) { - const slaveView = App.UI.DOM.appendNewElement("div", fetusDiv, App.SlaveAssignment.saSlaveName(slave)); - if (slave.pregType === 0) { - App.UI.DOM.appendNewElement("span", slaveView, " Their womb is empty", ["note", "green"]); - } else if (slave.pregType >= 4) { - App.UI.DOM.appendNewElement("span", slaveView, ` Using a slave carrying multiples is inadvisable; Their womb already has ${num(slave.pregType)} fetuses in it`, ["note", "red"]); - } else { - App.UI.DOM.appendNewElement("span", slaveView, ` Their womb already has ${num(slave.pregType)} fetuses in it`, ["note", "yellow"]); - } - option.addValue(SlaveFullName(slave), slave.ID); - eligibility = 1; - } - } - if (eligibility === 0) { - App.UI.DOM.appendNewElement("div", fetusDiv, "You have no slaves capable of acting as a surrogate."); - } - - if (V.PC.vagina !== -1 && mother.ID !== -1 && V.PC.preg >= 0 && V.PC.preg < 4 && V.PC.pregType < 8 && V.PC.physicalAge < 70) { - if (V.PC.womb.length === 0) { - App.UI.DOM.appendNewElement("h4", fetusDiv, "Your womb is empty.", ["green"]); - } else if (V.PC.pregType >= 4) { - App.UI.DOM.appendNewElement("span", fetusDiv, `Putting another child in your womb is inadvisable; Your womb already has ${num(V.PC.pregType)} fetuses in it.`, ["red"]); - } else { - App.UI.DOM.appendNewElement("h4", fetusDiv, "Your womb has enough room for another child.", ["yellow"]); - } - option.addValue("Use your own womb", -1); - } - option.addValue("Undecided", 0); - fetusDiv.append(options.render()); - - // finalize button - if (fetus.noticeData.transplantReceptrix !== 0) { - let transplantCommitButton = new App.UI.OptionsGroup(); - transplantCommitButton.customRefresh(() => { - // Set fate to "undecided" so that the event will come up for the new mother. Also stops an infinite recursion loop, so don't remove it unless you know what you are doing - fetus.noticeData.fate = "undecided"; - // If new mother is in the list of already processed slaves remove them from the list - V.pregnancyNotice.processedSlaves = V.pregnancyNotice.processedSlaves.filter((id) => { - return (id !== fetus.noticeData.transplantReceptrix); - }); - // @ts-expect-error this is not defined in GameVariables - V.donatrix = mother; - // @ts-expect-error this is not defined in GameVariables - V.receptrix = (fetus.noticeData.transplantReceptrix === -1) ? V.PC : getSlave(fetus.noticeData.transplantReceptrix); - // @ts-expect-error this is not defined in GameVariables - V.wombIndex = mother.womb.indexOf(fetus); - cashX(forceNeg(transplantingCost), (fetus.noticeData.transplantReceptrix === -1) ? "PCmedical": "slaveSurgery"); - V.surgeryType = "transplant"; - Dialog.setup("Transplant Fetus"); - Dialog.append(App.UI.surrogacy()); - Dialog.open(); - - let jDiv = $(`#fetus-id-${fetus.ID}`); - - // change the id of div - // this fixes a bug where the old div is still on the dom but not visible anymore (SugarCube's history functionality) - // and so it gets the new contents that we don't want it to get - jDiv.attr('id', `#fetus-id-${fetus.ID}-transplanted`); - - // notify the user that the transplanting was successful - jDiv.empty().append( - "Fetus has been transplanted into ", - (fetus.noticeData.transplantReceptrix === -1) ? "your": App.SlaveAssignment.saSlaveName(getSlave(fetus.noticeData.transplantReceptrix)), - (fetus.noticeData.transplantReceptrix === -1) ? "": "'s", - " womb." - ); - }); - transplantCommitButton.addOption("", "do", {do: false}) - .addValue(`Transplant fetus into ${(fetus.noticeData.transplantReceptrix === -1) ? "your womb" : SlaveFullName(getSlave(fetus.noticeData.transplantReceptrix))}`, true); - transplantCommitButton.addComment(`This will cost ${cashFormat(transplantingCost)}`); - fetusDiv.append(transplantCommitButton.render()); - } + let transplantDiv = App.UI.DOM.makeElement("div"); + fetusDiv.append(transplantDiv); + transplantingTool(mother, fetus, transplantDiv, 1, fetusInfo, [fetus, noteSpan], true); } } diff --git a/src/facilities/geneLab.js b/src/facilities/geneLab.js index 7bca2229b8c9cfc747109d003c40ee425e192a6c..372571afb5d43dcc3fd24f99cbdfdbfb851a7abb 100644 --- a/src/facilities/geneLab.js +++ b/src/facilities/geneLab.js @@ -183,8 +183,8 @@ App.UI.geneLab = function() { App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( "Make a clone", () => { - V.donatrix = "undecided"; - V.receptrix = "undecided"; + V.donatrix = 0; + V.receptrix = 0; }, [], "Cloning Workaround", )); } diff --git a/src/facilities/surgery/analyzePlayerPregnancy.js b/src/facilities/surgery/analyzePlayerPregnancy.js index 01805f21e286a16cd75ff66dfb26a6447f0e6e0e..28d0949a402b0e5b77781472d45daa942b699e94 100644 --- a/src/facilities/surgery/analyzePlayerPregnancy.js +++ b/src/facilities/surgery/analyzePlayerPregnancy.js @@ -89,17 +89,38 @@ App.UI.analyzePCPregnancy = function() { } App.UI.DOM.appendNewElement("h2", node, "Deep scan"); App.UI.DOM.appendNewElement("p", node, analyzePregnancies(V.PC, false)); - if (V.surgeryUpgrade === 1) { - if (V.PC.womb.filter(fetus => (fetus.age < 4 && (!FutureSocieties.isActive('FSRestart') || V.eugenicsFullControl === 1 || V.propOutcome === 0 || (fetus.fatherID !== -1 && fetus.fatherID !== -6)))).length > 1) { - linkArray.push(App.UI.DOM.link( - "Offload all your ova into an empty womb", - () => { - V.donatrix = V.PC; - V.nextLink = passage(); - }, - [], - "Bulk Ova Transplant Workaround" - )); + if ((V.pregnancyMonitoringUpgrade && V.surgeryUpgrade === 1) || V.cheatMode !== 0) { + const terminatable = V.PC.womb.filter((fetus) => canTerminateFetus(V.PC, fetus) || V.cheatMode !== 0); + if (terminatable.length > 0) { + const terminateButton = new App.UI.OptionsGroup(); + terminateButton.customRefresh(() => { + terminateFetus(V.PC, terminatable); + // reload + Engine.play(passage()); + }); + let terminateOption = terminateButton.addOption("", "do", {do: false}); + if (terminatable.length === V.PC.womb.length) { + terminateOption.addValue(`Terminate all your fetuses`, true); + } else { + terminateOption.addValue(`Terminate ${num(terminatable.length)} of your terminatable fetuses`, true); + } + node.append(terminateButton.render()); + } + const transplantable = V.PC.womb.filter((fetus) => canTransplantFetus(V.PC, fetus) === 1 || V.cheatMode !== 0); + if (transplantable.length > 0) { + let transplantDiv = App.UI.DOM.makeElement("div"); + const transplantButton = new App.UI.OptionsGroup(); + transplantButton.customRefresh(() => { + transplantingTool(V.PC, transplantable, transplantDiv, transplantable.length, undefined, undefined, false); + }); + let transplantOption = transplantButton.addOption("", "do", {do: false}); + if (transplantable.length === V.PC.womb.length) { + transplantOption.addValue(`Offload all of your fetuses`, true); + } else { + transplantOption.addValue(`Offload ${num(transplantable.length)} of your transplantable fetuses`, true); + } + node.append(transplantButton.render()); + node.append(transplantDiv); } } } diff --git a/src/facilities/surgery/analyzePregnancy.js b/src/facilities/surgery/analyzePregnancy.js index a7e61453585edd36bb77d55ed48cbdf3f09dc9d4..ff2fc9a6976a6c243e8937e1da4e515ca4d1b96a 100644 --- a/src/facilities/surgery/analyzePregnancy.js +++ b/src/facilities/surgery/analyzePregnancy.js @@ -258,31 +258,28 @@ globalThis.analyzePregnancies = function(mother, cheat) { */ function ovumSurgery() { if (canTerminate || canTransplant === 1) { - const row = options.addCustomOption(`Surgical options`); + el.append(App.UI.DOM.makeElement("h2", "Surgical Options")); if (canTerminate) { - row.addButton( - `Terminate ovum`, - () => { - WombRemoveFetus(mother, i); - if (mother.preg === 0) { - mother.pregWeek = -1; - } - }, - passage() - ); + const terminateButton = new App.UI.OptionsGroup(); + terminateButton.customRefresh(() => { + terminateFetus(mother, fetus); + // reload + Engine.play(passage()); + }); + terminateButton.addOption("", "do", {do: false}) + .addValue(`Terminate fetus`, true); + el.append(terminateButton.render()); } if (canTransplant === 1) { - row.addButton( - "Transplant ovum", - () => { - // @ts-expect-error donatrix doesn't exist in V - V.donatrix = mother; - // @ts-expect-error wombIndex doesn't exist in V - V.wombIndex = i; - V.nextLink = passage(); - }, - "Ova Transplant Workaround" - ); + let transplantDiv = App.UI.DOM.makeElement("div"); + const transplantButton = new App.UI.OptionsGroup(); + transplantButton.customRefresh(() => { + transplantingTool(getSlave(fetus.motherID), [fetus], transplantDiv, 1, undefined, undefined, false); + }); + transplantButton.addOption("", "do", {do: false}) + .addValue(`Transplant fetus`, true); + el.append(transplantButton.render()); + el.append(transplantDiv); } } } @@ -391,17 +388,38 @@ App.UI.analyzePregnancy = function() { } App.UI.DOM.appendNewElement("h2", node, "Deep scan"); App.UI.DOM.appendNewElement("p", node, analyzePregnancies(slave, false)); - if (V.surgeryUpgrade === 1) { - if (slave.womb.filter(fetus => (fetus.age < 4 && (!FutureSocieties.isActive('FSRestart') || V.eugenicsFullControl === 1 || slave.breedingMark === 0 || V.propOutcome === 0 || (fetus.fatherID !== -1 && fetus.fatherID !== -6)))).length > 1) { - App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( - "Transplant all ova", - () => { - V.donatrix = slave; - V.nextLink = passage(); - }, - [], - "Bulk Ova Transplant Workaround" - )); + if ((V.pregnancyMonitoringUpgrade && V.surgeryUpgrade === 1) || V.cheatMode !== 0) { + const terminatable = slave.womb.filter((fetus) => canTerminateFetus(slave, fetus) || V.cheatMode !== 0); + if (terminatable.length > 0) { + const terminateButton = new App.UI.OptionsGroup(); + terminateButton.customRefresh(() => { + terminateFetus(slave, terminatable); + // reload + Engine.play(passage()); + }); + let terminateOption = terminateButton.addOption("", "do", {do: false}); + if (terminatable.length === slave.womb.length) { + terminateOption.addValue(`Terminate all fetuses`, true); + } else { + terminateOption.addValue(`Terminate ${num(terminatable.length)} terminatable fetuses`, true); + } + node.append(terminateButton.render()); + } + const transplantable = slave.womb.filter((fetus) => canTransplantFetus(slave, fetus) === 1 || V.cheatMode !== 0); + if (transplantable.length > 0) { + let transplantDiv = App.UI.DOM.makeElement("div"); + const transplantButton = new App.UI.OptionsGroup(); + transplantButton.customRefresh(() => { + transplantingTool(slave, transplantable, transplantDiv, transplantable.length, undefined, undefined, false); + }); + let transplantOption = transplantButton.addOption("", "do", {do: false}); + if (transplantable.length === slave.womb.length) { + transplantOption.addValue(`Transplant all fetuses`, true); + } else { + transplantOption.addValue(`Transplant ${num(transplantable.length)} transplantable fetuses`, true); + } + node.append(transplantButton.render()); + node.append(transplantDiv); } } } else if (slave.preg === -3) { // special states diff --git a/src/js/wombJS.js b/src/js/wombJS.js index 67ccfa2c8f2f18dd320f925eb64560c0a13a7d2d..3e9bcea9c7d8403e709c048bc7416946940af87e 100644 --- a/src/js/wombJS.js +++ b/src/js/wombJS.js @@ -57,36 +57,36 @@ globalThis.WombInit = function(actor) { // backward compatibility setup. Fully accurate for normal pregnancy only. if (actor.womb.length > 0 && actor.womb[0].genetics === undefined) { i = 0; - actor.womb.forEach(function(ft) { - if (typeof ft.reserve !== 'string') { - ft.reserve = ""; + actor.womb.forEach(function(fetus) { + if (typeof fetus.reserve !== 'string') { + fetus.reserve = ""; } - if (typeof ft.motherID !== 'number') { // setting missing biological mother ID for fetus. - ft.motherID = actor.ID; + if (typeof fetus.motherID !== 'number') { // setting missing biological mother ID for fetus. + fetus.motherID = actor.ID; } - if (ft.ID === undefined) { - ft.ID = generateNewID(); + if (fetus.ID === undefined) { + fetus.ID = generateNewID(); } - if (typeof ft.realAge !== 'number') { // setting missing chronological age - ft.realAge = ft.age; + if (typeof fetus.realAge !== 'number') { // setting missing chronological age + fetus.realAge = fetus.age; } - ft.genetics = generateGenetics(actor, actor.pregSource, i); + fetus.genetics = generateGenetics(actor, actor.pregSource, i); i++; }); } else if (actor.womb.length === 0 && actor.pregType > 0 && actor.broodmother === 0) { WombImpregnate(actor, actor.pregType, actor.pregSource, actor.preg); } else if (actor.womb.length === 0 && actor.pregType > 0 && actor.broodmother > 0 && actor.broodmotherOnHold < 1) { // sorry but for already present broodmothers it's impossible to calculate fully, approximation used. - let pw = actor.preg; - if (pw > actor.pregData.normalBirth) { pw = actor.pregData.normalBirth; } // to avoid disaster. - const bCount = Math.floor(actor.pregType / pw); - const bLeft = actor.pregType - (bCount * pw); - if (pw > actor.pregType) { - pw = actor.pregType; // low children count broodmothers not supported here. It's emergency/backward compatibility code, and they not in game anyway. So minimum is 1 fetus in week. - actor.preg = pw; // fixing initial pregnancy week. - } - for (i = 0; i < pw; i++) { + let pregWeek = actor.preg; + if (pregWeek > actor.pregData.normalBirth) { pregWeek = actor.pregData.normalBirth; } // to avoid disaster. + const bCount = Math.floor(actor.pregType / pregWeek); + const bLeft = actor.pregType - (bCount * pregWeek); + if (pregWeek > actor.pregType) { + pregWeek = actor.pregType; // low children count broodmothers not supported here. It's emergency/backward compatibility code, and they not in game anyway. So minimum is 1 fetus in week. + actor.preg = pregWeek; // fixing initial pregnancy week. + } + for (i = 0; i < pregWeek; i++) { WombImpregnate(actor, bCount, actor.pregSource, i); // setting fetuses for every week, up to 40 week at max. } @@ -95,23 +95,23 @@ globalThis.WombInit = function(actor) { } } - actor.womb.forEach(f => { - if (!jsDef(f.genetics.inbreedingCoeff)) { - f.genetics.inbreedingCoeff = ibc.coeff( - {ID: null, mother: f.genetics.mother, father: f.genetics.father} + actor.womb.forEach(fetus => { + if (!jsDef(fetus.genetics.inbreedingCoeff)) { + fetus.genetics.inbreedingCoeff = ibc.coeff( + {ID: null, mother: fetus.genetics.mother, father: fetus.genetics.father} ); } - if (!jsDef(f.genetics.artSeed)) { + if (!jsDef(fetus.genetics.artSeed)) { // probably could detect and fix clones/twins here too, but I'm not bothering - f.genetics.artSeed = jsRandom(0, 10 ** 14); + fetus.genetics.artSeed = jsRandom(0, 10 ** 14); } - if (!jsDef(f.genetics.adultHeight)) { - f.genetics.adultHeight = Height.randomAdult({ - nationality: f.genetics.nationality, race: f.genetics.race, genes: f.genetics.gender, physicalAge: 20, birthWeek: 0 + if (!jsDef(fetus.genetics.adultHeight)) { + fetus.genetics.adultHeight = Height.randomAdult({ + nationality: fetus.genetics.nationality, race: fetus.genetics.race, genes: fetus.genetics.gender, physicalAge: 20, birthWeek: 0 }); } - if (!jsDef(f.genetics.boobPotential)) { - f.genetics.boobPotential = adjustBreastSize(f.genetics); + if (!jsDef(fetus.genetics.boobPotential)) { + fetus.genetics.boobPotential = adjustBreastSize(fetus.genetics); } }); }; @@ -137,8 +137,6 @@ App.Entity.Fetus = class { this.noticeData = { /** @type {("undecided"|"nothing"|"terminate"|"transplant"|"incubator"|"nursery"|"wait")} */ fate: "undecided", - /** @type {number} The id of the human that will receive the fetus during transplanting or 0 */ - transplantReceptrix: 0, /** If true then the fetus' cheat menu accordion is collapsed */ cheatAccordionCollapsed: true, /** @type {App.Entity.SlaveState} This is used by the descriptors and is used by generateChild() when the slave is born */ @@ -511,75 +509,78 @@ globalThis.WombMaxPreg = function(actor) { } }; -globalThis.WombNormalizePreg = function(actor) { - WombInit(actor); +/** + * @param {FC.HumanState} mother + */ +globalThis.WombNormalizePreg = function(mother) { + WombInit(mother); // this is broodmother on hold. - if (actor.womb.length === 0 && actor.broodmother >= 1) { - actor.pregType = 0; - actor.pregKnown = 0; + if (mother.womb.length === 0 && mother.broodmother >= 1) { + mother.pregType = 0; + mother.pregKnown = 0; // to avoid legacy code conflicts - broodmother on hold // can't be impregnated, but she is not on normal contraceptives. // So we set this for special case. - if (actor.preg >= 0) { - actor.preg = 0.1; + if (mother.preg >= 0) { + mother.preg = 0.1; } - if (actor.pregSource !== 0) { - actor.pregSource = 0; + if (mother.pregSource !== 0) { + mother.pregSource = 0; } - if (actor.pregWeek > 0) { - actor.pregWeek = 0; + if (mother.pregWeek > 0) { + mother.pregWeek = 0; } - actor.broodmotherCountDown = 0; + mother.broodmotherCountDown = 0; } - if (actor.womb.length > 0) { - let max = WombMaxPreg(actor); + if (mother.womb.length > 0) { + let max = WombMaxPreg(mother); // console.log("max: " + max); // console.log(".preg: "+ actor.preg); - if (actor.pregWeek < 1) { - actor.pregWeek = 1; + if (mother.pregWeek < 1) { + mother.pregWeek = 1; } - if (max < actor.preg) { - WombProgress(actor, actor.preg - max, actor.preg - max); + if (max < mother.preg) { + WombProgress(mother, mother.preg - max, mother.preg - max); // console.log("progress in womb"); - } else if (max > actor.preg) { - actor.preg = max; + } else if (max > mother.preg) { + mother.preg = max; // console.log("advancing .preg"); } - if (actor.womb[0].age >= 10 && actor.pregKnown === 0) { - actor.pregKnown = 1; + if (mother.womb[0].age >= 10 && mother.pregKnown === 0) { + mother.pregKnown = 1; } - actor.pregType = actor.womb.length; - actor.pregSource = actor.womb[0].fatherID; - } else if (actor.womb.length === 0 && actor.broodmother < 1) { + mother.pregType = mother.womb.length; + mother.pregSource = mother.womb[0].fatherID; + } else if (mother.womb.length === 0 && mother.broodmother < 1) { // not broodmother // console.log("preg fixing"); - actor.pregType = 0; - actor.pregKnown = 0; + mother.pregType = 0; + mother.pregKnown = 0; - if (actor.preg > 0) { - actor.preg = 0; + if (mother.preg > 0) { + mother.preg = 0; } - if (actor.pregSource !== 0) { - actor.pregSource = 0; + if (mother.pregSource !== 0) { + mother.pregSource = 0; } // We can't properly set postpartum here, // but can normalize obvious error with forgotten property. - if (actor.pregWeek > 0) { - actor.pregWeek = 0; + if (mother.pregWeek > 0) { + mother.pregWeek = 0; } } - actor.bellyPreg = WombGetVolume(actor); + mother.bellyPreg = WombGetVolume(mother); }; globalThis.WombChangeID = function(actor, fromID, toID) { @@ -695,14 +696,19 @@ globalThis.WombGetFetus = function(actor, fetusNum) { } }; -// give reference to fetus object, and remove it form the womb. -globalThis.WombRemoveFetus = function(actor, fetusNum) { - WombInit(actor); - if (actor.womb.length >= fetusNum) { - let ft = actor.womb[fetusNum]; - actor.womb.splice(fetusNum, 1); - WombUpdatePregVars(actor); - return ft; +/** + * give reference to fetus object, and remove it from the womb. + * @param {FC.HumanState} mother + * @param {number} fetusIndex the index number of the fetus + * @returns {App.Entity.Fetus|null} + */ +globalThis.WombRemoveFetus = function(mother, fetusIndex) { + WombInit(mother); + if (mother.womb.length - 1 >= fetusIndex) { + let fetus = mother.womb[fetusIndex]; + mother.womb.splice(fetusIndex, 1); + WombUpdatePregVars(mother); + return fetus; } else { return null; } @@ -761,14 +767,77 @@ globalThis.canTerminateFetus = (mother, fetus) => { ); }; +/** + * @param {FC.HumanState} mother + * @param {App.Entity.Fetus[]|App.Entity.Fetus} fetuses fetus to terminate or array of fetuses to terminate + */ +globalThis.terminateFetus = (mother, fetuses) => { + if (!Array.isArray(fetuses)) { + fetuses = [fetuses]; + } + + fetuses.reverse().forEach((fetus) => { + const fetusIndex = mother.womb.indexOf(fetus); + if (fetusIndex === -1) { return; } + WombRemoveFetus(mother, fetusIndex); + }); + if (mother.preg === 0) { + mother.pregWeek = -1; + } +}; + +/** + * @param {FC.HumanState} mother + * @param {App.Entity.Fetus[]} fetuses + * @param {HTMLDivElement} bulkTerminateDiv + * @param {number} terminatingCount + * @param {Function} terminatedCallback + */ +globalThis.BulkTerminateTool = (mother, fetuses, bulkTerminateDiv, terminatingCount, terminatedCallback) => { + bulkTerminateDiv.innerHTML = ""; + let terminatableFetuses = fetuses.filter((fetus) => canTerminateFetus(mother, fetus)); + let r = new SpacedTextAccumulator(); + + r.push("Terminating"); + r.push(App.UI.DOM.makeTextBox( + terminatingCount, + (value) => { + if (value > terminatableFetuses.length) { value = terminatableFetuses.length; } + if (value < 1) { value = 1; } + // refresh + BulkTerminateTool(mother, fetuses, bulkTerminateDiv, value, terminatedCallback); + }, + true + )); + r.push(`out of ${terminatableFetuses.length} fetuses`); + r.toParagraph(); + bulkTerminateDiv.append(r.container()); + const options = new App.UI.OptionsGroup(); + options.customRefresh(() => { + // take terminatingCount fetuses out of terminatableFetuses + let processing = terminatableFetuses.slice(0, terminatingCount); + // remove contents for each terminated fetus + processing.forEach(fetus => { + let jDiv = $(`#fetus-id-${fetus.ID}`); + if (jDiv && jDiv.length !== 0) { + jDiv.empty().append("Fetus terminated"); + } + }); + // terminate fetuses + terminateFetus(mother, processing); + terminatedCallback(); + }); + options.addOption("", "do", {do: false}) + .addValue(`Terminate ${num(terminatingCount)} fetuses`, true); + bulkTerminateDiv.append(options.render()); +}; + /** * @param {FC.HumanState} mother the mother of the fetus * @param {App.Entity.Fetus} fetus the fetus to check * @returns {-1|0|1} -1 = has already been transpanted, 0 = cannot be transpanted, 1 = can be transplanted */ globalThis.canTransplantFetus = (mother, fetus) => { - if (mother.womb.indexOf(fetus) === -1) { throw new Error("mother.womb must contain fetus"); } - if (fetus.motherID !== mother.ID) { return -1; } else if ( @@ -790,6 +859,198 @@ globalThis.canTransplantFetus = (mother, fetus) => { return 0; }; +/** + * Sets everything up and then calls App.UI.surrogacy() + * @param {FC.HumanState} mother + * @param {FC.HumanState} receptrix + * @param {App.Entity.Fetus[]|App.Entity.Fetus} fetuses fetus to transplant or array of fetuses to transplant + * @param {number} transplantingCost + * @param {boolean} [dialogInsteadOfPassageTraversal=false] if true use a dialog box, otherwise traverse to App.UI.surrogacy() + */ +globalThis.transplantFetus = (mother, receptrix, fetuses, transplantingCost, dialogInsteadOfPassageTraversal = false) => { + if (!Array.isArray(fetuses)) { + fetuses = [fetuses]; + } + if (fetuses.length === 0) { return; } + if (V.donatrix !== mother.ID) { + V.donatrix = mother.ID; + } + if (V.receptrix !== receptrix.ID) { + V.receptrix = receptrix.ID; + } + if (V.transplantFetuses !== fetuses) { + V.transplantFetuses = fetuses; + } + cashX(forceNeg(transplantingCost), (receptrix.ID === -1) ? "PCmedical": "slaveSurgery"); + V.surgeryType = "transplant"; + if (dialogInsteadOfPassageTraversal) { + Dialog.setup("Transplant Fetus"); + Dialog.append(App.UI.surrogacy()); + Dialog.open(); + } else { + V.nextLink = passage(); + // traverse to App.UI.surrogacy() + Engine.play("Surrogacy"); + } +}; + +/** + * @param {FC.HumanState} mother + * @param {App.Entity.Fetus[]|App.Entity.Fetus} fetuses + * @param {HTMLDivElement} transplantDiv + * @param {number} transplantingCount + * @param {Function} [transplantedCallback=undefined] + * @param {any[]} [transplantedCallbackVariables=undefined] variables to pass to transplantedCallback + * @param {boolean} [dialogInsteadOfPassageTraversal=false] passed to transplantFetus() + */ +globalThis.transplantingTool = ( + mother, fetuses, + transplantDiv, transplantingCount, + transplantedCallback = undefined, transplantedCallbackVariables = undefined, + dialogInsteadOfPassageTraversal = false +) => { + if (!Array.isArray(fetuses)) { fetuses = [fetuses]; } + + transplantDiv.innerHTML = ""; + let transplantableFetuses = fetuses.filter((fetus) => canTransplantFetus(mother, fetus) === 1); + let r = new SpacedTextAccumulator(); + + let transplantingCost = V.surgeryCost * 2; + if (fetuses.length > 1) { + // bulk transplanting cost more per each fetus transplanted, but is cheaper than transplanting them one at a time + transplantingCost += Math.floor(Math.max(1, V.surgeryCost/10)) * transplantingCount - 1; + } + + r.push("Transplanting"); + if (transplantableFetuses.length > 1) { + r.push(App.UI.DOM.makeTextBox( + transplantingCount, + (value) => { + if (value > transplantableFetuses.length) { value = transplantableFetuses.length; } + if (value < 1) { value = 1; } + // refresh + transplantingTool( + mother, fetuses, + transplantDiv, value, + transplantedCallback, transplantedCallbackVariables, + dialogInsteadOfPassageTraversal + ); + }, + true + )); + r.push(`out of ${transplantableFetuses.length}`); + } else { + transplantingCount = 1; + r.push(num(transplantingCount)); + } + r.push((transplantingCount === 1) ? "fetus.": "fetuses."); + r.toParagraph(); + transplantDiv.append(r.container()); + + // Slave selector + let eligibility = 0; + let options = new App.UI.OptionsGroup(); + options.customRefresh(() => { + // refresh + transplantingTool( + mother, fetuses, + transplantDiv, transplantingCount, + transplantedCallback, transplantedCallbackVariables, + dialogInsteadOfPassageTraversal + ); + }); + let option = options.addOption(`Select a host`, "receptrix"); + + App.UI.DOM.appendNewElement("h4", transplantDiv, "Slave details"); + for (const slave of V.slaves) { + if ((mother.ID !== slave.ID && slave.ovaries > 0 || slave.mpreg > 0) && + isSlaveAvailable(slave) && slave.preg >= 0 && slave.preg < slave.pregData.normalBirth / 10 && + slave.pregWeek >= 0 && slave.pubertyXX === 1 && slave.pregType < 12 && + slave.bellyImplant === -1 && slave.broodmother === 0 && slave.inflation <= 2 && slave.physicalAge < 70 + ) { + const slaveView = App.UI.DOM.appendNewElement("div", transplantDiv, App.SlaveAssignment.saSlaveName(slave)); + if (slave.pregType === 0) { // TODO:@franklygeorge adaptive to the amount of fetuses selected for transplanting + App.UI.DOM.appendNewElement("span", slaveView, " Their womb is empty", ["note", "green"]); + } else if (slave.pregType >= 4) { // TODO:@franklygeorge adaptive to the amount of fetuses selected for transplanting + App.UI.DOM.appendNewElement("span", slaveView, ` Using a slave carrying multiples is inadvisable; Their womb already has ${num(slave.pregType)} fetuses in it`, ["note", "red"]); + } else { // TODO:@franklygeorge adaptive to the amount of fetuses selected for transplanting + App.UI.DOM.appendNewElement("span", slaveView, ` Their womb already has ${num(slave.pregType)} fetuses in it`, ["note", "yellow"]); + } + option.addValue(SlaveFullName(slave), slave.ID); + eligibility = 1; + } + } + if (eligibility === 0) { + App.UI.DOM.appendNewElement("div", transplantDiv, "You have no slaves capable of acting as a surrogate."); + } + + if (V.PC.vagina !== -1 && mother.ID !== -1 && V.PC.preg >= 0 && V.PC.preg < 4 && V.PC.pregType < 8 && V.PC.physicalAge < 70) { + if (V.PC.womb.length === 0) { // TODO:@franklygeorge adaptive to the amount of fetuses selected for transplanting + App.UI.DOM.appendNewElement("h4", transplantDiv, "Your womb is empty.", ["green"]); + } else if (V.PC.pregType >= 4) { // TODO:@franklygeorge adaptive to the amount of fetuses selected for transplanting + App.UI.DOM.appendNewElement("span", transplantDiv, `Putting another child in your womb is inadvisable; Your womb already has ${num(V.PC.pregType)} fetuses in it.`, ["red"]); + } else { // TODO:@franklygeorge adaptive to the amount of fetuses selected for transplanting + App.UI.DOM.appendNewElement("h4", transplantDiv, `Your womb has enough room for ${(transplantingCount === 1) ? "another child": "more children"}.`, ["yellow"]); + } + option.addValue("Use your own womb", -1); + } + option.addValue("Undecided", 0); + transplantDiv.append(options.render()); + + // finalize button + if (V.receptrix !== 0) { + let transplantCommitButton = new App.UI.OptionsGroup(); + transplantCommitButton.customRefresh(() => { + // take transplantingCount fetuses out of transplantableFetuses + let processing = transplantableFetuses.slice(0, transplantingCount); + // remove contents for each transplanted fetus + processing.forEach(fetus => { + let jDiv = $(`#fetus-id-${fetus.ID}`); + if (jDiv && jDiv.length !== 0) { + // change the id of div + // this fixes a bug where the old div is still on the dom but not visible anymore (SugarCube's history functionality) + // and so it gets the new contents that we don't want it to get + jDiv.attr('id', `#fetus-id-${fetus.ID}-transplanted`); + + // notify the user that the transplanting was successful + jDiv.empty().append( + "Fetus has been transplanted into ", + (V.receptrix === -1) ? "your": App.SlaveAssignment.saSlaveName(getSlave(V.receptrix)), + (V.receptrix === -1) ? "": "'s", + " womb." + ); + } + // Set fate to "undecided" so that the event will come up for the new mother. Also stops an infinite recursion loop, so don't remove it unless you know what you are doing + fetus.noticeData.fate = "undecided"; + }); + + // If new mother is in the list of already processed slaves remove them from the list + V.pregnancyNotice.processedSlaves = V.pregnancyNotice.processedSlaves.filter((id) => { + return (id !== V.receptrix); + }); + + // transplant fetuses + transplantFetus(mother, (V.receptrix === -1) ? V.PC : getSlave(V.receptrix), processing, transplantingCost, dialogInsteadOfPassageTraversal); + + if (transplantedCallback) { + if (Array.isArray(transplantedCallbackVariables)) { + transplantedCallback(...transplantedCallbackVariables); + } else { + transplantedCallback(); + } + } + }); + let transplantCommitOption = transplantCommitButton.addOption("", "do", {do: false}); + if (transplantingCount === 1) { + transplantCommitOption.addValue(`Transplant fetus into ${(V.receptrix === -1) ? "your womb" : SlaveFullName(getSlave(V.receptrix)) + "'s womb"}`, true); + } else { + transplantCommitOption.addValue(`Transplant ${num(transplantingCount)} fetuses into ${(V.receptrix === -1) ? "your womb" : SlaveFullName(getSlave(V.receptrix)) + "'s womb"}`, true); + } + transplantCommitButton.addComment(`This will cost ${cashFormat(transplantingCost)}`); + transplantDiv.append(transplantCommitButton.render()); + } +}; + // change genetic property of all fetuses based on race globalThis.WombFatherRace = function(actor, raceName) { let skinColor = randomRaceSkin(raceName); diff --git a/src/npc/surgery/cloningWorkaround.js b/src/npc/surgery/cloningWorkaround.js index 2231b3cbe93e80fa0b972f04b055713fe576040c..907c283cd094aafcd79e351d3b6d3e317ce85d37 100644 --- a/src/npc/surgery/cloningWorkaround.js +++ b/src/npc/surgery/cloningWorkaround.js @@ -5,22 +5,18 @@ App.UI.cloningWorkaround = function() { const donatrix = V.donatrix; const receptrix = V.receptrix; - let impreg; - if (donatrix !== "undecided" && donatrix.ID === -1) { + let impreg = "undecided"; + if (donatrix === -1) { impreg = PlayerName(); - } else if (donatrix !== "undecided") { - impreg = SlaveFullName(donatrix); - } else { - impreg = donatrix; + } else if (donatrix !== 0) { + impreg = SlaveFullName(getSlave(donatrix)); } - let receive; - if (receptrix !== "undecided" && receptrix.ID === -1) { + let receive = "undecided"; + if (receptrix === -1) { receive = PlayerName(); - } else if (receptrix !== "undecided") { - receive = SlaveFullName(receptrix); - } else { - receive = receptrix; + } else if (receptrix !== 0) { + receive = SlaveFullName(getSlave(receptrix)); } App.UI.DOM.appendNewElement("h2", node, `Genetic Source`); @@ -30,19 +26,19 @@ App.UI.cloningWorkaround = function() { App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( "Yourself", () => { - V.donatrix = V.PC; + V.donatrix = V.PC.ID; App.UI.reload(); } )); for (const slave of V.slaves) { const div = App.UI.DOM.appendNewElement("div", node, App.UI.DOM.referenceSlaveWithPreview(slave, SlaveFullName(slave))); - if (donatrix !== "undecided" && donatrix.ID === slave.ID) { + if (donatrix === slave.ID) { div.classList.add("note"); } else { div.append(" ", App.UI.DOM.link( "Select", () => { - V.donatrix = slave; + V.donatrix = slave.ID; App.UI.reload(); } )); @@ -54,13 +50,13 @@ App.UI.cloningWorkaround = function() { for (const slave of V.slaves) { if (canBeReceptrix(slave)) { const div = App.UI.DOM.appendNewElement("div", node, App.UI.DOM.referenceSlaveWithPreview(slave, SlaveFullName(slave))); - if (receptrix !== "undecided" && receptrix.ID === slave.ID) { + if (receptrix === slave.ID) { div.classList.add("note"); } else { div.append(" ", App.UI.DOM.link( "Select", () => { - V.receptrix = slave; + V.receptrix = slave.ID; App.UI.reload(); } )); @@ -76,13 +72,13 @@ App.UI.cloningWorkaround = function() { } if (V.PC.vagina !== -1 && V.PC.preg >= 0 && V.PC.preg < 4 && V.PC.pregType < 8 && V.PC.physicalAge < 70) { - if (receptrix !== "undecided" && receptrix.ID === V.PC.ID) { + if (receptrix === V.PC.ID) { App.UI.DOM.appendNewElement("div", node, `Yourself`, "note"); } else { App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( "Use your own womb", () => { - V.receptrix = V.PC; + V.receptrix = V.PC.ID; App.UI.reload(); } )); diff --git a/src/npc/surgery/ovaTransplantWorkaround.js b/src/npc/surgery/ovaTransplantWorkaround.js deleted file mode 100644 index c0f73ed1054942ec607b6d7af7a5320d167b48a4..0000000000000000000000000000000000000000 --- a/src/npc/surgery/ovaTransplantWorkaround.js +++ /dev/null @@ -1,53 +0,0 @@ -App.UI.ovaTransplantWorkaround = function(bulk = false) { - const node = new DocumentFragment(); - V.receptrix = 0; - let eligibility = 0; - let surgeryType = bulk ? "transplant all" : "transplant"; - - if (bulk) { - App.UI.DOM.appendNewElement("p", node, "You've decided to transplant all the fertilized ova; now you must select whose fertile womb will be harboring them.", "scene-intro"); - } else { - App.UI.DOM.appendNewElement("p", node, "You've decided which fertilized ovum is to be transplanted; now you must select whose womb will be its new home.", "scene-intro"); - } - - App.UI.DOM.appendNewElement("h2", node, "Select a slave to serve as the host"); - - for (const slave of V.slaves) { - if ((V.donatrix.ID !== slave.ID && slave.ovaries > 0 || slave.mpreg > 0) && - isSlaveAvailable(slave) && slave.preg >= 0 && slave.preg < slave.pregData.normalBirth / 10 && - slave.pregWeek >= 0 && slave.pubertyXX === 1 && (bulk ? slave.pregType === 0 : slave.pregType < 12) && - slave.bellyImplant === -1 && slave.broodmother === 0 && slave.inflation <= 2 && slave.physicalAge < 70 - ) { - const div = App.UI.DOM.appendNewElement("div", node, App.UI.DOM.referenceSlaveWithPreview(slave, SlaveFullName(slave))); - div.append(" ", App.UI.DOM.passageLink( - "Select", "Surrogacy", - () => { - V.receptrix = slave; - cashX(forceNeg(V.surgeryCost * 2), "slaveSurgery"); - V.surgeryType = surgeryType; - } - )); - if (slave.pregType >= 4 || (bulk && slave.pregType > 1)) { - App.UI.DOM.appendNewElement("span", div, `Using a slave carrying multiples is inadvisable`, ["note"]); - } - eligibility = 1; - } - } - if (eligibility === 0) { - App.UI.DOM.appendNewElement("div", node, "You have no slaves capable of acting as a surrogate."); - } - - if (V.PC.vagina !== -1 && V.donatrix.ID !== -1 && V.PC.preg >= 0 && V.PC.preg < 4 && (bulk ? V.PC.pregType === 0 : V.PC.pregType < 8) && V.PC.physicalAge < 70) { - App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( - `Use your own womb`, - () => { - V.receptrix = V.PC; - cashX(forceNeg(V.surgeryCost * 2), "PCmedical"); - V.surgeryType = surgeryType; - }, - [], - "Surrogacy" - )); - } - return node; -}; diff --git a/src/npc/surgery/surrogacyWorkaround.js b/src/npc/surgery/surrogacyWorkaround.js index 1b87c8a98f32059b12ee0b7330356319fb5205ed..9a8b957ed89da56af41471f9bf0ccb981be6fa85 100644 --- a/src/npc/surgery/surrogacyWorkaround.js +++ b/src/npc/surgery/surrogacyWorkaround.js @@ -8,27 +8,22 @@ App.UI.surrogacyWorkaround = function() { let eligibility = 0; let eligibilityI = 0; let eligibility2 = 0; - const donatrixID = (donatrix.ID === V.PC.ID) ? -1 : 0; - let impreg; - if (impregnatrix !== "undecided" && impregnatrix.ID === -1) { + let impreg = "undecided"; + if (impregnatrix === -1) { impreg = PlayerName(); - } else if (impregnatrix !== "undecided") { - impreg = SlaveFullName(impregnatrix); - } else { - impreg = impregnatrix; + } else if (impregnatrix !== 0) { + impreg = SlaveFullName(getSlave(impregnatrix)); } - let receive; - if (receptrix !== "undecided" && receptrix.ID === -1) { + let receive = "undecided"; + if (receptrix === -1) { receive = PlayerName(); - } else if (receptrix !== "undecided") { - receive = SlaveFullName(receptrix); - } else { - receive = receptrix; + } else if (receptrix !== 0) { + receive = SlaveFullName(getSlave(receptrix)); } - App.UI.DOM.appendNewElement("p", node, `${(donatrixID === -1) ? `You've prepared yourself to have an egg taken from your ovaries;` : `${getSlave(V.AS).slaveName} is prepped to have an egg harvested from ${getPronouns(getSlave(V.AS)).possessive} ovaries;`} now you must select a target to fertilize it and who will carry it to term.`, "scene-intro"); + App.UI.DOM.appendNewElement("p", node, `${(donatrix === -1) ? `You've prepared yourself to have an egg taken from your ovaries;` : `${getSlave(V.AS).slaveName} is prepped to have an egg harvested from ${getPronouns(getSlave(V.AS)).possessive} ovaries;`} now you must select a target to fertilize it and who will carry it to term.`, "scene-intro"); if (impreg !== "undecided" || receive !== "undecided") { const bearers = []; @@ -56,12 +51,12 @@ App.UI.surrogacyWorkaround = function() { App.UI.DOM.appendNewElement("h2", node, `Semen donatrix: ${impreg}`); for (const slave of V.slaves) { - if (slave.balls > 0 && slave.pubertyXY === 1 && isSlaveAvailable(slave) && canBreed(donatrix, slave)) { + if (slave.balls > 0 && slave.pubertyXY === 1 && isSlaveAvailable(slave) && canBreed(getSlave(donatrix), slave)) { const div = App.UI.DOM.appendNewElement("div", node, App.UI.DOM.referenceSlaveWithPreview(slave, SlaveFullName(slave))); div.append(" ", App.UI.DOM.link( "Select", () => { - V.impregnatrix = slave; + V.impregnatrix = slave.ID; App.UI.reload(); } )); @@ -74,7 +69,7 @@ App.UI.surrogacyWorkaround = function() { if (V.incubator.tanks.length > 0 && V.incubator.upgrade.reproduction === 1) { for (const tank of V.incubator.tanks) { - if (tank.balls > 0 && tank.dick > 0 && tank.incubatorSettings.reproduction === 2 && canBreed(donatrix, tank)) { + if (tank.balls > 0 && tank.dick > 0 && tank.incubatorSettings.reproduction === 2 && canBreed(getSlave(donatrix), tank)) { if (eligibilityI === 0) { App.UI.DOM.appendNewElement("h2", node, `Incubator settings are resulting in large-scale fluid secretion. Select an eligible incubatee to milk for semen:`); eligibilityI = 1; @@ -82,7 +77,7 @@ App.UI.surrogacyWorkaround = function() { App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( tank.slaveName, () => { - V.impregnatrix = tank; + V.impregnatrix = tank.ID; App.UI.reload(); } )); @@ -97,7 +92,7 @@ App.UI.surrogacyWorkaround = function() { App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( "Use your own", () => { - V.impregnatrix = V.PC; + V.impregnatrix = V.PC.ID; App.UI.reload(); } )); @@ -105,7 +100,7 @@ App.UI.surrogacyWorkaround = function() { App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( "Use a vial of your own", () => { - V.impregnatrix = V.PC; + V.impregnatrix = V.PC.ID; V.PC.counter.storedCum--; App.UI.reload(); } @@ -120,7 +115,7 @@ App.UI.surrogacyWorkaround = function() { div.append(" ", App.UI.DOM.link( "Select", () => { - V.receptrix = slave; + V.receptrix = slave.ID; App.UI.reload(); }, [], "", (slave.pregType >= 4) ? `Using a slave carrying multiples is inadvisable` : `` @@ -136,7 +131,7 @@ App.UI.surrogacyWorkaround = function() { App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( "Use your own womb", () => { - V.receptrix = V.PC; + V.receptrix = V.PC.ID; App.UI.reload(); } )); diff --git a/src/player/managePersonalAffairs.js b/src/player/managePersonalAffairs.js index 6ecdd94493b72dd9693c02377bf4aede6b2774a9..997159d8e116a435217449111de36d3fb5b0200a 100644 --- a/src/player/managePersonalAffairs.js +++ b/src/player/managePersonalAffairs.js @@ -186,12 +186,9 @@ App.UI.managePersonalAffairs = function() { if (PC.preg >= 0 && PC.ovaries && PC.ovaryAge < 47) { links.push(App.UI.DOM.passageLink(`Harvest and implant an egg`, 'Surrogacy Workaround', () => { - // @ts-ignore - V.donatrix = V.PC; - // @ts-ignore - V.impregnatrix = "undecided"; - // @ts-ignore - V.receptrix = "undecided"; + V.donatrix = V.PC.ID; + V.impregnatrix = 0; + V.receptrix = 0; V.nextLink = 'Manage Personal Affairs'; })); @@ -399,35 +396,54 @@ App.UI.managePersonalAffairs = function() { } } else if (PC.preg > 0 && PC.pregKnown) { text.push(`You have a bun baking in the oven.`); - - if (FutureSocieties.isActive('FSRestart', arcology) || V.eugenicsFullControl === 1 || (V.PC.pregSource !== -1 && V.PC.pregSource !== -6)) { - links.push(App.UI.DOM.link(`Pop some morning after pills`, () => { - WombFlush(V.PC); - - App.UI.DOM.replace(appearanceDiv, appearance); - App.UI.DOM.replace(reputationDiv, reputation); - App.UI.DOM.replace(pregnancyDiv, pregnancy); - })); - if (V.pregnancyMonitoringUpgrade && V.surgeryUpgrade === 1) { - const whereTo = PC.pregType > 1 ? "Bulk Ova Transplant Workaround" : "Ova Transplant Workaround"; - links.push(App.UI.DOM.passageLink(`Make it someone else's problem`, whereTo, () => { - V.donatrix = V.PC; - V.nextLink = passage(); - })); + let terminatable = V.PC.womb.filter((fetus) => canTerminateFetus(V.PC, fetus) || V.cheatMode !== 0); + if (terminatable.length > 0) { + const terminateButton = new App.UI.OptionsGroup(); + terminateButton.customRefresh(() => { + terminateFetus(V.PC, terminatable); + // reload + Engine.play(passage()); + }); + let terminateOption = terminateButton.addOption("", "do", {do: false}); + if (terminatable.length === V.PC.womb.length) { + terminateOption.addValue(`Pop some morning after pills`, true); + } else { + terminateOption.addValue(`Terminate ${num(terminatable.length)} of your terminatable fetuses`, true); + } + text.push(terminateButton.render()); + } + let transplantable = V.PC.womb.filter((fetus) => canTransplantFetus(V.PC, fetus) === 1 || V.cheatMode !== 0); + if (transplantable.length > 0) { + if ((V.pregnancyMonitoringUpgrade && V.surgeryUpgrade === 1) || V.cheatMode !== 0) { + let transplantDiv = App.UI.DOM.makeElement("div"); + text.push(transplantDiv); + const transplantButton = new App.UI.OptionsGroup(); + transplantButton.customRefresh(() => { + transplantingTool(V.PC, transplantable, transplantDiv, transplantable.length, undefined, undefined, false); + }); + let transplantOption = transplantButton.addOption("", "do", {do: false}); + if (transplantable.length === V.PC.womb.length) { + transplantOption.addValue(`Make it someone else's problem`, true); + } else { + transplantOption.addValue(`Offload ${num(transplantable.length)} of your transplantable fetuses`, true); + } + text.push(transplantButton.render()); } text.push(App.UI.DOM.generateLinksStrip(links)); } } else if (PC.preg > 0) { text.push(`Your fertile ${PC.mpreg === 1 ? "ass" : ""}pussy has been thoroughly seeded; there is a chance you are pregnant.`); - - if (!FutureSocieties.isActive('FSRestart', arcology) || V.eugenicsFullControl === 1 || (V.PC.pregSource !== -1 && V.PC.pregSource !== -6)) { - text.push(App.UI.DOM.link(`Pop some morning after pills`, () => { - WombFlush(V.PC); - - App.UI.DOM.replace(appearanceDiv, appearance); - App.UI.DOM.replace(reputationDiv, reputation); - App.UI.DOM.replace(pregnancyDiv, pregnancy); - })); + let terminatable = V.PC.womb.filter((fetus) => canTerminateFetus(V.PC, fetus) || V.cheatMode !== 0); + if (terminatable.length > 0) { + const terminateButton = new App.UI.OptionsGroup(); + terminateButton.customRefresh(() => { + terminateFetus(V.PC, terminatable); + // reload + Engine.play(passage()); + }); + terminateButton.addOption("", "do", {do: false}) + .addValue(`Pop some morning after pills`, true); + text.push(terminateButton.render()); } } else if (PC.pregWeek < 0) { text.push(`You're still recovering from your recent pregnancy.`); diff --git a/src/pregmod/surrogacy.js b/src/pregmod/surrogacy.js index b08d89fd63dd8f86780997b0d3171dcb1bd6dd77..98fcd8faea21e719c14da5562bce63fb7e3ba272 100644 --- a/src/pregmod/surrogacy.js +++ b/src/pregmod/surrogacy.js @@ -1,44 +1,24 @@ +/** + * This defines the passage contents for surrogacy, transplanting, and cloning surgeries + * @returns {DocumentFragment} + */ App.UI.surrogacy = function() { const node = new DocumentFragment(); - const receptrix = getReceptrix(); - const donatrix = getDonatrix(); - const impregnatrix = getImpregnatrix(); - const wombIndex = V.wombIndex; - const slave = getSlave(V.AS) || V.surgeryType.startsWith("transplant") ? donatrix: undefined; let r = []; - function getReceptrix() { - if (V.receptrix && V.receptrix.ID) { - return V.receptrix.ID === -1 ? V.PC : getSlave(V.receptrix.ID); - } - } - - function getDonatrix() { - if (V.donatrix && V.donatrix.ID) { - return V.donatrix.ID === -1 ? V.PC : getSlave(V.donatrix.ID); - } - } - - function getImpregnatrix() { - if (V.impregnatrix && V.impregnatrix.ID) { - if (V.impregnatrix.ID === -1) { - return V.PC; - } else { - return findFather(V.impregnatrix.ID); - } - } - } - switch (V.surgeryType) { case "surrogacy": - if (receptrix.ID === -1) { + if (V.receptrix === -1) { + const donatrix = (V.donatrix === -1) ? V.PC : getSlave(V.donatrix); r.push(`Since the surgery required only a local anesthetic, you remain fully aware of the procedure as the autosurgery carries it out. You slowly rise to your feet, a hand to your lower belly, appreciating the new life growing within you.`); V.PC.pregKnown = 1; - WombSurrogate(V.PC, 1, donatrix, impregnatrix.ID, 1); + WombSurrogate(V.PC, 1, donatrix, V.impregnatrix, 1); WombNormalizePreg(V.PC); } else { + const receptrix = getSlave(V.receptrix); + const donatrix = (V.donatrix === -1) ? V.PC : getSlave(V.donatrix); receptrix.pregKnown = 1; - WombSurrogate(receptrix, 1, donatrix, impregnatrix.ID, 1); + WombSurrogate(receptrix, 1, donatrix, V.impregnatrix, 1); WombNormalizePreg(receptrix); const { He, @@ -86,13 +66,12 @@ App.UI.surrogacy = function() { V.impregnatrix = 0; V.donatrix = 0; break; - case "transplant": - case "transplant all": { - const bulk = V.surgeryType === "transplant all"; + case "transplant": { + const bulk = V.transplantFetuses.length > 1; const child = bulk ? "children" : "child"; - let fetus; - if (receptrix.ID === -1) { - r.push(`Since the surgery required only a local anesthetic, you are very aware that you are now carrying ${slave.slaveName}'s ${child}. You slowly`); + const donatrix = (V.donatrix === -1) ? V.PC : getSlave(V.donatrix); + if (V.receptrix === -1) { + r.push(`Since the surgery required only a local anesthetic, you are very aware that you are now carrying ${donatrix.slaveName}'s ${child}. You slowly`); if (canWalk(V.PC)) { r.push(`rise to your feet, a hand to`); } else if (canMove(V.PC)) { @@ -101,44 +80,8 @@ App.UI.surrogacy = function() { r.push(`run a hand across`); } r.push(`your lower belly, appreciating the new ${bulk ? "lives" : "life"} growing within you.`); - if (bulk) { - for (let i = 0; i < slave.womb.length; i++) { - if (slave.womb[i].age < 4 && (!FutureSocieties.isActive('FSRestart') || V.eugenicsFullControl === 1 || mother.breedingMark === 0 || V.propOutcome === 0 || (slave.womb[i].fatherID !== -1 && slave.womb[i].fatherID !== -6))) { - fetus = WombRemoveFetus(slave, i); - WombAddFetus(V.PC, fetus); - i--; - } - } - } else { - fetus = WombRemoveFetus(slave, wombIndex); - WombAddFetus(V.PC, fetus); - } - V.PC.pregKnown = 1; - V.PC.preg = WombMaxPreg(V.PC); - slave.preg = WombMaxPreg(slave); - WombNormalizePreg(V.PC); - WombNormalizePreg(slave); } else { - if (bulk) { - for (let i = 0; i < donatrix.womb.length; i++) { - if (donatrix.womb[i].age < 4 && (!FutureSocieties.isActive('FSRestart') || V.eugenicsFullControl === 1 || mother.breedingMark === 0 || V.propOutcome === 0 || (donatrix.womb[i].fatherID !== -1 && donatrix.womb[i].fatherID !== -6))) { - fetus = WombRemoveFetus(donatrix, i); - WombAddFetus(receptrix, fetus); - i--; - } - } - } else { - fetus = WombRemoveFetus(donatrix, wombIndex); - WombAddFetus(receptrix, fetus); - } - receptrix.pregKnown = 1; - receptrix.preg = WombMaxPreg(receptrix); - donatrix.preg = WombMaxPreg(donatrix); - WombNormalizePreg(receptrix); - WombNormalizePreg(donatrix); - if (donatrix.ID === -1) { - V.PC = donatrix; - } + const receptrix = getSlave(V.receptrix); const { He, he, his, him @@ -198,18 +141,33 @@ App.UI.surrogacy = function() { receptrix.devotion -= 15; } } + const receptrix = (V.receptrix === -1) ? V.PC : getSlave(V.receptrix); + V.transplantFetuses.reverse().forEach(fetus => { + WombRemoveFetus(donatrix, donatrix.womb.indexOf(fetus)); + WombAddFetus(receptrix, fetus); + }); + receptrix.pregKnown = 1; + receptrix.preg = WombMaxPreg(receptrix); + donatrix.preg = WombMaxPreg(donatrix); + WombNormalizePreg(receptrix); + WombNormalizePreg(donatrix); + if (donatrix.preg === 0) { + donatrix.pregWeek = -1; + } V.receptrix = 0; V.donatrix = 0; - V.wombIndex = 0; + V.transplantFetuses = []; break; } - case "clone": - if (receptrix.ID === -1) { + case "clone": { + const donatrix = (V.donatrix === -1) ? V.PC : getSlave(V.donatrix); + if (V.receptrix === -1) { r.push(`Since the surgery required only a local anesthetic, you remain fully aware of the procedure as the autosurgery carries it out. You slowly rise to your feet, a hand to your lower belly, appreciating the clone growing within you.`); V.PC.pregKnown = 1; WombImpregnateClone(V.PC, 1, donatrix, 1); WombNormalizePreg(V.PC); } else { + const receptrix = getSlave(V.receptrix); receptrix.pregKnown = 1; WombImpregnateClone(receptrix, 1, donatrix, 1); WombNormalizePreg(receptrix); @@ -274,6 +232,7 @@ App.UI.surrogacy = function() { } V.receptrix = 0; V.donatrix = 0; + } } App.Events.addParagraph(node, r); return node;