diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 413112e667a6018674c27f10c8b7fe65f84d9c51..ff968cd5905f3d966ad7b01bd1737a342168ea58 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,10 +19,16 @@ To effectively work on the project the following tools are required: ### Setting everything up 0. Clone the project from GitGud.io ([Detailed Git setup and work cycle](devNotes/gitSetup.md)) -1. Open a terminal (Linux) / cmd window (Windows) and navigate to the `fc-pregmod` root directory. +1. Open a terminal (GNU/Linux) / cmd/Powershell window (Windows) and then navigate to the `fc-pregmod` + root directory via `cd PATH`. e.g. + Windows: `cd C:\path\to\project\fc-pregmod` + GNU/Linux: `cd /path/to/project/fc-pregmod/` + 2. Run `npm install` 3. Open the directory in your preferred IDE 4. Configure your IDE to use ESLint and the type checking capabilities of the TypeScript compiler. +5. It might also be worth installing the following extensions if using VS Code: + [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) and [GitLens — Git supercharged](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens). ## Compiling diff --git a/devNotes/gitSetup.md b/devNotes/gitSetup.md index 022c3049b5e2fa2df676d78c8fa99e9e1bdc3c42..bebe7eeab083de08fa7e925d11dce8138b4f8a43 100644 --- a/devNotes/gitSetup.md +++ b/devNotes/gitSetup.md @@ -2,58 +2,52 @@ ## First time setup -1. [Install Git for terminal](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) or a Git GUI of your +0. [Install Git for terminal](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) or a Git GUI of your choice. -2. Create an account on gitgud if you don't have a usable one. +1. Create an account on gitgud if you don't have a usable one. * (optional) Add an SSH key to your account for easier pushing. This allows you to connect to gitgud through SHH, which doesn't require your credentials every time. -5. Fork the main repository through gitgud interface. +2. Fork the main repository through gitgud interface. * (optional) Delete all branches other than pregmod-master, so you don't get them locally when fetching. -3. Clone the repo - * Via terminal: `git clone --single-branch https://gitgud.io/pregmodfan/fc-pregmod.git` +3. Clone the repo and then change to the new directory + * Clicking on the dropdown arrow on the far right blue clone button in the gitgud interface on your fork gives you the relevant URLs. + * Via terminal: `git clone --depth 1 --single-branch <url-to-your-fork> && cd fc-pregmod` -4. Get updates - * Via terminal: `git pull` - -6. Setup your fork as a remote - * Rename the main repository to upstream - * Via terminal: `git remote rename origin upstream` - * Add your repo as remote - * Via terminal: `git remote add origin <url-to-your-fork>` - * The big clone button in the gitgud interface on your fork gives you the relevant URLs. - -7. Checkout `pregmod-master` - * Via terminal: `git checkout pregmod-master` - -8. Make sure `fc-pregmod` tracks `upstream/pregmod-master` - * Via terminal: `git branch -u upstream/pregmod-master` +4. Add the main repository as a remote target + * Via terminal: `git remote add upstream https://gitgud.io/pregmodfan/fc-pregmod.git` ## Typical cycle with Git: -1. Get changes from upstream - * Via terminal: `git pull` - * If you don't get new commits from `upstream` repeat steps 7 & 8 of the setup. +0. Switch to the pregmod-master branch, then check for and merge any upstream updates + before updating your remote if any updates were avabile + * Via terminal: `git checkout -q pregmod-master && git fetch upstream && git merge upstream/pregmod-master && git push -q` -2. Checkout a new branch for your work +1. Checkout a new branch for your work * Via terminal: `git checkout -b <branch-name>` -3. Make your changes as you like -4. Commit your changes - * Via terminal: `git commit <files>` - * Make the commit message useful (`Fix X`, `Add Y`, etc.) +2. Make desired changes +3. Add them to be committed + * Via terminal: `git add *` +4. Commit + * Make the commit message useful (`Fix X`, `Add Y`, etc.) + * Via terminal: `git commit -m "MESSAGE"` 5. (optional, but recommended) Run sanityCheck before final push to catch any errors you missed. * You can ignore errors that already existed 6. Push result into your forked repository * Via terminal: - * Initially `git push -u origin <branch-name>` + * Initially: git push -u origin `git rev-parse --abbrev-ref HEAD` * Afterwards `git push` will suffice. 7. Create merge request through gitgud interface. + * Suggestion: Tick "Delete source branch when merge request is accepted." + to help automatically tidy up your fork. 8. Checkout `pregmod-master` in preparation of next change. - * Via terminal: `git checkout pregmod-master` + * Via terminal: `git checkout -q pregmod-master` 9. Once the merge request was accepted, delete your local branch. - * Via terminal: `git branch -d <branch-name>` + * Via terminal: `git branch -dq <branch-name>` + +To get a list of all current branches run `git branch --list` via terminal. diff --git a/devNotes/jsEventCreationGuide.md b/devNotes/jsEventCreationGuide.md index ab0c75671727b6d1d94cd00d0c83b1c737b19357..647d395070f8156ff48927b128824b66924fcf0b 100644 --- a/devNotes/jsEventCreationGuide.md +++ b/devNotes/jsEventCreationGuide.md @@ -90,8 +90,6 @@ App.Events.MyEvent = class MyEvent extends App.Events.BaseEvent { ``` # Dealing with lisping -`Enunciate($activeSlave)/getWrittenTitle($activeSlave)/<<say>>s/that'<<s>>/<<Master>>` -Can be converted to `const {say, title: Master} = getEnunciation(eventSlave);` Master can just be template literalled into spoken() and it'll convert it itself. diff --git a/devNotes/usefulJSFunctionDocumentation.md b/devNotes/usefulJSFunctionDocumentation.md index 2b10b33cdd48ae236b7203a6621b746d6bf499a4..716e8ee6d6e98fcb8beae3aa506a410e13994b8b 100644 --- a/devNotes/usefulJSFunctionDocumentation.md +++ b/devNotes/usefulJSFunctionDocumentation.md @@ -162,8 +162,6 @@ properMaster() // Returns the slave's title for Master when WrittenMaster() is i WrittenMaster(slave) // Returns a slave's title for the player and sets lisping. Returns $activeSlave if not given an argument. -Enunciate(slave) // Syncs lisp widgets with slave. - Spoken(slave, speech) // Returns speech with lisp if slave lisps. Replaces `<<say>>s` with Spoken(slave, "says"). SlaveFullName(slave) // Returns the slave's full name. diff --git a/devTools/types/FC/SecExp.d.ts b/devTools/types/FC/SecExp.d.ts index b9e445560220f3e15374692c89189d701f0cde72..e71d7056ebbe021db5f67fe334cc2ba6035f0346 100644 --- a/devTools/types/FC/SecExp.d.ts +++ b/devTools/types/FC/SecExp.d.ts @@ -10,7 +10,6 @@ declare namespace FC { interface PlayerUnitData extends UnitData { active: number, ID: number, - isDeployed: number } interface PlayerHumanUnitData extends PlayerUnitData { diff --git a/devTools/types/FC/gameState.d.ts b/devTools/types/FC/gameState.d.ts index 4037fdb6bf69ae510f614a2dd30413eba51910dd..e86bae7234875eaf1d86484f1bd6d770582587c0 100644 --- a/devTools/types/FC/gameState.d.ts +++ b/devTools/types/FC/gameState.d.ts @@ -10,27 +10,6 @@ declare namespace FC { interface Enunciation { title: string; say: string; - s: string; - S: string; - ss: string; - c: string; - C: string; - cc: string; - z: string; - Z: string; - zz: string; - ch: string; - Ch: string; - ps: string; - Ps: string; - sh: string; - Sh: string; - sc: string; - Sc: string; - sch: string; - Sch: string; - x: string; - X: string; } interface PeacekeepersState { @@ -90,7 +69,6 @@ declare namespace FC { */ interface TemporaryVariablesInTheGameState { gameover?: string; - enunciate?: Enunciation; sortQuickList?: string; slaveAfterRA?: SlaveState; /** @deprecated */ diff --git a/devTools/types/FC/human.d.ts b/devTools/types/FC/human.d.ts index b7fdcf8e73f9e11ae975c6761a44f4350c5a167d..fe3d6fccb4ae98573d3c13627bc43b0f30f23a9a 100644 --- a/devTools/types/FC/human.d.ts +++ b/devTools/types/FC/human.d.ts @@ -257,8 +257,8 @@ declare global { "breast redistributors" | "butt redistributors" | "sag-B-gone" | "growth stimulants" | "stimulants"; type EarWear = WithNone<"hearing aids" | "muffling ear plugs" | "deafening ear plugs">; - type EarShape = WithNone<"damaged" | "normal" | "pointy" | "elven" | "ushi" | "robot">; - type EarTypeKemonomimi = WithNone<"normal" | "neko" | "inu" | "kit" | "tanuki" | "usagi">; + type EarShape = WithNone<"damaged" | "normal" | "pointy" | "elven" | "cow" | "robot" | "orcish" | "sheep" | "deer" | "gazelle" | "bird" | "dragon">; + type EarTypeKemonomimi = WithNone<"normal" | "cat" | "leopard" | "tiger" | "dog" | "wolf" | "jackal" | "fox" | "tanuki" | "raccoon" | "rabbit" | "squirrel"| "horse">; type EyebrowStyle = "bald" | "curved" | "elongated" | "high-arched" | "natural" | "rounded" | "shaved" | "shortened" | "slanted inwards" | "slanted outwards" | "straight"; type EyebrowThickness = "pencil-thin" | "thin" | "threaded" | "natural" | "tapered" | "thick" | "bushy"; @@ -283,7 +283,7 @@ declare global { type InflationLiquid = WithNone<"water" | "cum" | "milk" | "food" | "aphrodisiac" | "curative" | "tightener" | "urine" | "stimulant">; type TailType = WithNone<"mod" | "combat" | "sex">; type Markings = WithNone<"beauty mark" | "birthmark" | "freckles" | "heavily freckled">; - type TailShape = WithNone<"neko" | "inu" | "kit" | "kitsune" | "tanuki" | "ushi" | "usagi" | "risu" | "uma">; + type TailShape = WithNone<"cat" | "leopard" | "tiger" | "dog" | "jaguar" | "lion" | "wolf" | "jackal" | "fox" | "kitsune" | "tanuki" | "raccoon" | "rabbit" | "squirrel" | "horse" | "bird" | "phoenix" | "peacock" | "raven" | "swan" | "sheep" | "cow" | "gazelle" | "deer" | "succubus" | "dragon" >; type ToyHole = "all her holes" | "mouth" | "boobs" | "pussy" | "ass" | "dick"; type OvaryImplantType = 0 | "fertility" | "sympathy" | "asexual"; type NippleShape = "huge" | "puffy" | "inverted" | "tiny" | "cute" | "partially inverted" | "fuckable" | "flat"; diff --git a/js/003-data/dictionary.js b/js/003-data/dictionary.js new file mode 100644 index 0000000000000000000000000000000000000000..7ab039156b9b329f9827be8c5e46d86183100afb --- /dev/null +++ b/js/003-data/dictionary.js @@ -0,0 +1,23 @@ +App.Data.dictionary = { + "cat": { + Japanese: "neko" + }, + "dog": { + Japanese: "inu" + }, + "fox": { + Japanese: "kit" + }, + "cow": { + Japanese: "ushi" + }, + "rabbit": { + Japanese: "usagi" + }, + "squirrel": { + Japanese: "risu" + }, + "horse": { + Japanese: "uma" + }, +}; diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js index 420b4115ed640e813a92dc0ee2bb34bed91fa507..c286bceb66684f121ee2764a69f8fd6ef3e48567 100644 --- a/js/003-data/gameVariableData.js +++ b/js/003-data/gameVariableData.js @@ -1315,66 +1315,3 @@ App.Data.resetOnNGPlus = { choosesOwnAssignmentText: {}, favorites: [] }; - -App.Data.ignoreGameStateVariables = [ - // pronouns - "Mothers", - "mothers", - "Fathers", - "Husbands", - "Husband", - "Brother", - "Son", - "Shota", - "Men", - "Man", - "fathers", - "husbands", - "husband", - "brother", - "son", - "shota", - "men", - "man", - "Mother", - "Wives", - "Wife", - "Sister", - "Daughter", - "Loli", - "Women", - "Woman", - "mother", - "wives", - "wife", - "sister", - "daughter", - "loli", - "women", - "woman", - "Girl", - "Herself", - "Hers", - "Her", - "She", - "girl", - "herself", - "hers", - "her", - "she", - "Father", - "father", - "Boy", - "Himself", - "His", - "Him", - "He", - "boy", - "himself", - "his", - "him", - "he", - - // Enunciate - "enunciate" -]; diff --git a/js/003-data/slaveProstheticsData.js b/js/003-data/slaveProstheticsData.js index 80b89c1a22a502648eaed8ba15b60c75a3faf90c..da6ed9f59d761a89e0737907f8ed1d8ce6f0d3ca 100644 --- a/js/003-data/slaveProstheticsData.js +++ b/js/003-data/slaveProstheticsData.js @@ -149,15 +149,32 @@ App.Data.prosthetics = { * @type {Map<FC.TailShape, {animal: string, desc: string}>} */ App.Data.modTails = new Map([ - ["neko", {animal: "Cat", desc: "a long, slender cat tail"}], - ["inu", {animal: "Dog", desc: "a bushy dog tail"}], - ["kit", {animal: "Fox", desc: "a soft, fluffy fox tail"}], - ["kitsune", {animal: "Kitsune", desc: "three incredibly soft, fluffy fox tails"}], + ["cat", {animal: "Cat", desc: "a long, slender cat tail"}], + ["leopard", {animal: "Leopard", desc: "a long, fluffy leopard tail"}], + ["tiger", {animal: "Tiger", desc: "a long, fluffy tiger tail"}], + ["jaguar", {animal: "Jaguar", desc: "a long, fluffy jaguar tail"}], + ["lion", {animal: "Lion", desc: "a long, slender lion tail"}], + ["dog", {animal: "Dog", desc: "a bushy dog tail"}], + ["wolf", {animal: "Wolf", desc: "a long and fluffy wolf tail"}], + ["jackal", {animal: "Jackal", desc: "a bushy jackal tail"}], + ["fox", {animal: "Fox", desc: "a soft, fluffy fox tail"}], + ["kitsune", {animal: "Kitsune", desc: "nine incredibly soft, fluffy fox tails"}], ["tanuki", {animal: "Tanuki", desc: "a long, fluffy tanuki tail"}], - ["ushi", {animal: "Cow", desc: "a long cow tail"}], - ["usagi", {animal: "Rabbit", desc: "a short rabbit tail"}], - ["risu", {animal: "Squirrel", desc: "a large squirrel tail"}], - ["uma", {animal: "Horse", desc: "a long horse tail"}] + ["raccoon", {animal: "Raccoon", desc: "a long, fluffy raccoon tail"}], + ["rabbit", {animal: "Rabbit", desc: "a short rabbit tail"}], + ["squirrel", {animal: "Squirrel", desc: "a large squirrel tail"}], + ["horse", {animal: "Horse", desc: "a long horse tail"}], + ["bird", {animal: "Bird", desc: "a bundle of tail feathers"}], + ["phoenix", {animal: "Phoenix", desc: "a magnificent bundle of luminescent tail feathers"}], + ["peacock", {animal: "Peacock", desc: "a gorgeous bundle of peacock tail feathers"}], + ["raven", {animal: "Raven", desc: "a bundle of crow tail feathers"}], + ["swan", {animal: "Swan", desc: "a small bundle of short swan tail feathers"}], + ["sheep", {animal: "Sheep", desc: "a short, woolly sheep's tail"}], + ["cow", {animal: "Cow", desc: "a long cow tail"}], + ["gazelle", {animal: "Gazelle", desc: "a short, silky gazelle tail"}], + ["deer", {animal: "Deer", desc: "a short deer tail"}], + ["succubus", {animal: "Succubus", desc: "a long, slender succubus tail"}], + ["dragon", {animal: "Dragon", desc: "a long, thick dragon tail"}] ]); /** diff --git a/js/medicine/surgery/ears/earMajor.js b/js/medicine/surgery/ears/earMajor.js index b4bfe5dcefac98d9149ab04c75295cc22045dd15..600a306762597df262f51ae8783b00b42bb72076 100644 --- a/js/medicine/surgery/ears/earMajor.js +++ b/js/medicine/surgery/ears/earMajor.js @@ -77,17 +77,15 @@ App.Medicine.Surgery.Procedures.EarMajorReshape = class extends App.Medicine.Sur App.Medicine.Surgery.Procedures.TopEarReshape = class extends App.Medicine.Surgery.Procedure { /** * @param {App.Entity.SlaveState} slave - * @param {string} shapeName * @param {FC.EarTypeKemonomimi} newShape */ - constructor(slave, shapeName, newShape) { + constructor(slave, newShape) { super(slave); - this.shapeName = shapeName; - this.targetShape = newShape; + this.newShape = newShape; } get name() { - return `Reshape into ${this.shapeName} ears`; + return `Reshape into ${this.newShape} ears`; } get healthCost() { @@ -95,7 +93,7 @@ App.Medicine.Surgery.Procedures.TopEarReshape = class extends App.Medicine.Surge } apply(cheat) { - this._slave.earT = this.targetShape; + this._slave.earT = this.newShape; return this._assemble(new App.Medicine.Surgery.Reactions.EarMajor()); } }; diff --git a/src/002-config/fc-version.js b/src/002-config/fc-version.js index 8e55bd9e56d091c2fbdcb481290a732eea118dd3..4e33ea4c9dbc5ec7f461dd0ddd8bfe5f87b5d7b3 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.8", commitHash: null, - release: 1139 // When getting close to 2000, please remove the check located within the onLoad() function defined at line five of src/js/eventHandlers.js. + release: 1141 // 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/Mods/Catmod/events/CMREFS/refsTotallyLegitCatgirls.js b/src/Mods/Catmod/events/CMREFS/refsTotallyLegitCatgirls.js index a981b6c91b82f25746b3d9192eda7e668cdf2914..d505ca800d872d8a3e7f7f6a45ec0120cfdcda61 100644 --- a/src/Mods/Catmod/events/CMREFS/refsTotallyLegitCatgirls.js +++ b/src/Mods/Catmod/events/CMREFS/refsTotallyLegitCatgirls.js @@ -8,7 +8,7 @@ App.Events.refsTotallyLegitCatgirls = class refsTotallyLegitCatgirls extends App execute(node) { const slave = GenerateNewSlave("XX", {minAge: 16, maxAge: 24}); slave.origin = "You purchased $him from a reputable street merchant selling you completely legitimate catgirls."; - slave.tailShape = "neko"; + slave.tailShape = "cat"; slave.tailColor = slave.hColor; slave.collar = "uncomfortable leather"; slave.clothes = "kitty lingerie"; diff --git a/src/Mods/Catmod/events/nonRandom/projectNComplete.js b/src/Mods/Catmod/events/nonRandom/projectNComplete.js index f8047469b776c712062ce17964868b3882d268ff..c695bdc02fbdeb1cc8483fc40cec141e40b119df 100644 --- a/src/Mods/Catmod/events/nonRandom/projectNComplete.js +++ b/src/Mods/Catmod/events/nonRandom/projectNComplete.js @@ -32,10 +32,10 @@ App.Events.SEProjectNComplete = class SEProjectNComplete extends App.Events.Base slave.trust = 30; slave.boobs = 300; slave.earShape = "none"; - slave.earT = "neko"; + slave.earT = "cat"; slave.earTColor = slave.hColor; slave.earImplant = 1; - slave.tailShape = "neko"; + slave.tailShape = "cat"; slave.tailColor = slave.hColor; slave.eye.left.pupil = "catlike"; slave.eye.right.pupil = "catlike"; diff --git a/src/Mods/Catmod/events/reRecruit/punkFemcat.js b/src/Mods/Catmod/events/reRecruit/punkFemcat.js index 2a12b1cc7bf086af27e14fca9e968fb841f33b75..ae701669bd0167977aad83810addf22af32bbd71 100644 --- a/src/Mods/Catmod/events/reRecruit/punkFemcat.js +++ b/src/Mods/Catmod/events/reRecruit/punkFemcat.js @@ -78,10 +78,10 @@ App.Events.recPunkFemcat = class recPunkFemcat extends App.Events.BaseEvent { slave.devotion = random(-55, -40); slave.trust = random(-20, 10); slave.earShape = "none"; - slave.earT = "neko"; + slave.earT = "cat"; slave.earTColor = slave.hColor; slave.earImplant = 1; - slave.tailShape = "neko"; + slave.tailShape = "cat"; slave.tailColor = slave.hColor; slave.eye.left.pupil = "catlike"; slave.eye.right.pupil = "catlike"; diff --git a/src/Mods/Catmod/events/reRecruit/punkSissycat.js b/src/Mods/Catmod/events/reRecruit/punkSissycat.js index be2f278af0e8c720150abe01e95b468f2c8a0b4a..23dbc235c0d0d729db9b6679ee9e7f84f69d4f3f 100644 --- a/src/Mods/Catmod/events/reRecruit/punkSissycat.js +++ b/src/Mods/Catmod/events/reRecruit/punkSissycat.js @@ -78,10 +78,10 @@ App.Events.recPunkSissycat = class recPunkSissycat extends App.Events.BaseEvent slave.devotion = random(-55, -40); slave.trust = random(-20, 10); slave.earShape = "none"; - slave.earT = "neko"; + slave.earT = "cat"; slave.earTColor = slave.hColor; slave.earImplant = 1; - slave.tailShape = "neko"; + slave.tailShape = "cat"; slave.tailColor = slave.hColor; slave.eye.left.pupil = "catlike"; slave.eye.right.pupil = "catlike"; diff --git a/src/Mods/Catmod/events/reRecruit/runawayCat.js b/src/Mods/Catmod/events/reRecruit/runawayCat.js index 1526605e6eac5f34d4b15fe467901d630740cd85..9b887667d5271742192cd624232f37f19cb7bb37 100644 --- a/src/Mods/Catmod/events/reRecruit/runawayCat.js +++ b/src/Mods/Catmod/events/reRecruit/runawayCat.js @@ -81,10 +81,10 @@ App.Events.recRunawayCat = class recRunawayCat extends App.Events.BaseEvent { slave.devotion = random(-25, -10); slave.trust = random(-25, -10); slave.earShape = "none"; - slave.earT = "neko"; + slave.earT = "cat"; slave.earTColor = slave.hColor; slave.earImplant = 1; - slave.tailShape = "neko"; + slave.tailShape = "cat"; slave.tailColor = slave.hColor; slave.eye.left.pupil = "catlike"; slave.eye.right.pupil = "catlike"; diff --git a/src/Mods/Catmod/events/scheduled/vatcatboy.js b/src/Mods/Catmod/events/scheduled/vatcatboy.js index b9fde211ffb82f82d94532c1cd902dd722035460..8f68775b6a28ed1b0b4c14a2c548ee4d1c9f14e3 100644 --- a/src/Mods/Catmod/events/scheduled/vatcatboy.js +++ b/src/Mods/Catmod/events/scheduled/vatcatboy.js @@ -23,10 +23,10 @@ App.Events.SEVatCatBoy = class SEVatCatBoy extends App.Events.BaseEvent { slave.devotion = 20; slave.trust = 30; slave.earShape = "none"; - slave.earT = "neko"; + slave.earT = "cat"; slave.earTColor = slave.hColor; slave.earImplant = 1; - slave.tailShape = "neko"; + slave.tailShape = "cat"; slave.tailColor = slave.hColor; slave.eye.left.pupil = "catlike"; slave.eye.right.pupil = "catlike"; diff --git a/src/Mods/Catmod/events/scheduled/vatcatgirl.js b/src/Mods/Catmod/events/scheduled/vatcatgirl.js index 48d6e2e281a3790f565e6e9e220585a28a762179..97127918aeccc8550f9dd198e6a45e91f5376fd6 100644 --- a/src/Mods/Catmod/events/scheduled/vatcatgirl.js +++ b/src/Mods/Catmod/events/scheduled/vatcatgirl.js @@ -23,10 +23,10 @@ App.Events.SEVatCatGirl = class SEVatCatGirl extends App.Events.BaseEvent { slave.trust = 30; slave.earShape = "none"; slave.teeth = "fangs"; - slave.earT = "neko"; + slave.earT = "cat"; slave.earTColor = slave.hColor; slave.earImplant = 1; - slave.tailShape = "neko"; + slave.tailShape = "cat"; slave.tailColor = slave.hColor; slave.eye.left.pupil = "catlike"; slave.eye.right.pupil = "catlike"; diff --git a/src/Mods/SecExp/buildings/riotControlCenter.js b/src/Mods/SecExp/buildings/riotControlCenter.js index de1c0990ddccbe122f2742a91086920e4a007a0f..e2fe0621a04208241bf132e73005c1b9c90b371a 100644 --- a/src/Mods/SecExp/buildings/riotControlCenter.js +++ b/src/Mods/SecExp/buildings/riotControlCenter.js @@ -23,7 +23,7 @@ App.SecExp.riotCenter = (function() { }; } - function GUI() { + function GUI() { const node = new DocumentFragment(); V.nextButton = "Back"; V.nextLink = "Main"; @@ -39,7 +39,7 @@ App.SecExp.riotCenter = (function() { } if (V.SecExp.buildings.riotCenter.upgrades.freeMedia === 0) { - App.UI.DOM.appendNewElement("div", node, + App.UI.DOM.appendNewElement("div", node, makeLink( "Provide free media access in all the arcology", 5000, @@ -50,7 +50,7 @@ App.SecExp.riotCenter = (function() { } else { App.UI.DOM.appendNewElement("div", node, "You are providing free access to many mass media in the arcology."); if (V.SecExp.buildings.riotCenter.upgrades.freeMedia < 5) { - App.UI.DOM.appendNewElement("div", node, + App.UI.DOM.appendNewElement("div", node, makeLink( "Invest more resources in the free media project to increase its effectiveness", 5000, @@ -68,9 +68,9 @@ App.SecExp.riotCenter = (function() { node.append(progressReport("slave")); App.UI.DOM.appendNewElement("div", node, progressReport("citizen")); App.Events.addParagraph(node, []); - + if (V.SecExp.buildings.riotCenter.upgrades.rapidUnit === 0) { - node.append( + node.append( makeLink( "Create rapid deployment riot units", 7500, @@ -81,7 +81,7 @@ App.SecExp.riotCenter = (function() { } else { node.append("You created a rapid deployment riot unit."); if (V.SecExp.buildings.riotCenter.upgrades.rapidUnit < 5) { - App.UI.DOM.appendNewElement("div", node, + App.UI.DOM.appendNewElement("div", node, makeLink( "Invest more resources in the rapid deployment unit", 5000, @@ -94,7 +94,7 @@ App.SecExp.riotCenter = (function() { } if (V.SecExp.buildings.riotCenter.upgrades.rapidUnitSpeed < 2) { - App.UI.DOM.appendNewElement("div", node, + App.UI.DOM.appendNewElement("div", node, makeLink( "Enhance the internal informants network", 5000, @@ -216,7 +216,6 @@ App.SecExp.riotCenter = (function() { App.UI.DOM.appendNewElement("div", node, `You have given the riot unit access to the combat armor suits of ${V.SF.Lower}.`); } } - return node; function deployUnit() { @@ -241,14 +240,10 @@ App.SecExp.riotCenter = (function() { App.UI.DOM.appendNewElement("div", text, App.UI.DOM.link( `Deploy the unit against ${type} rebel leaders`, () => { - if (cost === "rep") { - V.SecExp.core.authority -= 1000 + 50 * V.SecExp.buildings.riotCenter.upgrades.rapidUnit; - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority, 0, 20000); - } else { - repX(forceNeg(1000 + 50 * V.SecExp.buildings.riotCenter.upgrades.rapidUnit), "war"); - } + const price = forceNeg(1000 + 50 * V.SecExp.buildings.riotCenter.upgrades.rapidUnit); + (function() { cost === "reputation" ? repX(price, "war") : App.SecExp.authorityX(-price) })(); const change = random(15) + random(1, 2) * V.SecExp.buildings.riotCenter.upgrades.rapidUnit; - V.SecExp.rebellions[type + "Progress"] = Math.clamp(V.SecExp.rebellions[type + "Progress"] - change,0 , 100); + V.SecExp.rebellions[type + "Progress"] = Math.clamp(V.SecExp.rebellions[type + "Progress"] - change, 0, 100); V.SecExp.buildings.riotCenter.sentUnitCooldown = 3 - V.SecExp.buildings.riotCenter.upgrades.rapidUnitSpeed; App.UI.DOM.replace(text, `${capFirstChar(type)} rebellion progress set back by ${change}%. The unit will be able to deployed again in ${V.SecExp.buildings.riotCenter.sentUnitCooldown} weeks.`); } @@ -289,7 +284,7 @@ App.SecExp.riotCenter = (function() { case "brainImplantProject": case "advancedRiotEquip": V.SecExp.buildings.riotCenter[type]++; - break; + break; case "reactor": case "waterway": case "assistant": @@ -316,5 +311,5 @@ App.SecExp.riotCenter = (function() { } return `Unrest is extremely high between ${type + 's'}. The chances of a rebellion happening in the near future are extremely high.`; } - } + } })(); diff --git a/src/Mods/SecExp/buildings/securityHQ.js b/src/Mods/SecExp/buildings/securityHQ.js index 0a2efc0d22ed767c7895026154ae83efb8dba69c..07ab9f3f8b662397d50c8eaa6888b3d56adc4f70 100644 --- a/src/Mods/SecExp/buildings/securityHQ.js +++ b/src/Mods/SecExp/buildings/securityHQ.js @@ -250,16 +250,16 @@ App.SecExp.secHub = (function() { } } - App.Events.addParagraph(z, [App.UI.DOM.makeElement("h2", "Crime")]); + App.UI.DOM.appendNewElement("h2", z, `Crime`); if (V.SecExp.buildings.secHub.upgrades.crime.advForensic === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Install advanced forensic equipment", 5000, "Will bring down the crime level cap by 10 points, but will require 10 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", "advForensic") ); } else { - App.UI.DOM.appendNewElement("span", z, "You have installed advanced forensic equipment, able to extract every bit of precious information from any clue."); + App.UI.DOM.appendNewElement("div", z, "You have installed advanced forensic equipment, able to extract every bit of precious information from any clue."); } if (V.SecExp.buildings.secHub.upgrades.crime.autoArchive === 0) { App.UI.DOM.appendNewElement("div", z, @@ -269,30 +269,30 @@ App.SecExp.secHub = (function() { "autoArchive") ); } else { - App.UI.DOM.appendNewElement("span", z, "You have installed auto-curating archiver software, which will update in real time your data archives with any new relevant information on criminals residing in your arcology."); + App.UI.DOM.appendNewElement("div", z, "You have installed auto-curating archiver software, which will update in real time your data archives with any new relevant information on criminals residing in your arcology."); } if (V.SecExp.core.authority > 10000) { if (V.SecExp.buildings.secHub.upgrades.crime.autoTrial === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Install automated trials software", 10000, "Will bring down the crime level cap by 15 points, but will require 5 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", "autoTrial") ); } else { - App.UI.DOM.appendNewElement("span", z, "You have installed advanced legal algorithms that allows the handling of legal matters much quicker and much more accurately."); + App.UI.DOM.appendNewElement("div", z, "You have installed advanced legal algorithms that allows the handling of legal matters much quicker and much more accurately."); } if (V.SecExp.buildings.secHub.upgrades.crime.worldProfiler === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Install worldwide profilers", 15000, "Will bring down the crime level cap by 15 points, but will require 10 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", "worldProfiler") ); } else { - App.UI.DOM.appendNewElement("span", z, "You have installed advanced profiler software, which will constantly scour every known data archive on the globe (legally or not) to gather as much information as possible on dangerous criminals."); + App.UI.DOM.appendNewElement("div", z, "You have installed advanced profiler software, which will constantly scour every known data archive on the globe (legally or not) to gather as much information as possible on dangerous criminals."); } } return z; @@ -301,17 +301,17 @@ App.SecExp.secHub = (function() { const z = new DocumentFragment(); App.UI.DOM.appendNewElement("h2", z, `Reconnaissance`); if (V.SecExp.buildings.secHub.upgrades.intel.sensors === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Install perimeter sensors", 5000, "Will increase recon capabilities, but will require 5 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", "sensors") ); } else { - App.UI.DOM.appendNewElement("span", z, "You have installed perimeter seismic sensors able to detect movement with high accuracy."); + App.UI.DOM.appendNewElement("div", z, "You have installed perimeter seismic sensors able to detect movement with high accuracy."); } if (V.SecExp.buildings.secHub.upgrades.intel.signalIntercept === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Create signal interception hub", 10000, "Will increase recon capabilities, but will require 5 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", @@ -323,7 +323,7 @@ App.SecExp.secHub = (function() { if (V.SecExp.core.authority > 10000) { if (V.SecExp.buildings.secHub.upgrades.intel.radar === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Install advanced radar equipment", 15000, "Will increase recon capabilities, but will require 10 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", @@ -334,19 +334,19 @@ App.SecExp.secHub = (function() { } } - App.Events.addParagraph(z, [App.UI.DOM.makeElement("h2", "Readiness")]); + App.UI.DOM.appendNewElement("h2", z, `Readiness`); if (V.SecExp.buildings.secHub.upgrades.readiness.pathways === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Build specialized pathways in the arcology", 5000, "Will increase readiness by 1, but will require 5 extra slaves in the headquarters and increase upkeep. The remaining slaves will be more efficient in dealing with crime.", "pathways") ); } else { - App.UI.DOM.appendNewElement("span", z, "You have built specialized pathways inside the arcology to quickly move troops around the structure."); + App.UI.DOM.appendNewElement("div", z, "You have built specialized pathways inside the arcology to quickly move troops around the structure."); } if (V.SecExp.buildings.secHub.upgrades.readiness.rapidVehicles === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Buy rapid armored transport vehicles", 7500, "Will increase readiness by 2, but will require 5 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", @@ -358,7 +358,7 @@ App.SecExp.secHub = (function() { if (V.SecExp.core.authority > 10000) { if (V.SecExp.buildings.secHub.upgrades.readiness.rapidPlatforms === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Build rapid deployment platforms", 10000, "Will increase readiness by 2, but will require 10 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", @@ -369,7 +369,7 @@ App.SecExp.secHub = (function() { App.UI.DOM.appendNewElement("div", z, "You have built rapid deployment platforms able to equip and deploy units within very limited time windows."); } if (V.SecExp.buildings.secHub.upgrades.readiness.earlyWarn === 0) { - App.UI.DOM.appendNewElement("span", z, + App.UI.DOM.appendNewElement("div", z, makeLink("Institute early warning systems", 15000, "Will increase readiness by 2, but will require 10 extra slaves in the headquarters and increases upkeep. The remaining slaves will be more efficient in dealing with crime.", @@ -405,7 +405,7 @@ App.SecExp.secHub = (function() { } text.push("Considering the current upgrades the"); if (isSecurity) { - text.push(`resting level for security is ${App.SecExp.Check.secRestPoint() * (Math.clamp(V.SecExp.buildings.secHub.menials, 0, App.SecExp.Check.reqMenials()) / App.SecExp.Check.reqMenials())}, `); + text.push(`resting level for security is ${Math.trunc(App.SecExp.Check.secRestPoint() * (Math.clamp(V.SecExp.buildings.secHub.menials, 0, App.SecExp.Check.reqMenials()) / App.SecExp.Check.reqMenials()))}, `); text.push(`while the effective maximum level is ${Math.trunc(App.SecExp.Check.reqMenials() * (Math.clamp(V.SecExp.buildings.secHub.menials, 0, App.SecExp.Check.reqMenials()) / App.SecExp.Check.reqMenials()))}.`); } else { text.push(`maximum level of crime is ${App.SecExp.Check.crimeCap()}, `); @@ -438,24 +438,24 @@ App.SecExp.secHub = (function() { case "cyberBots": case "eyeScan": case "cryptoAnalyzer": - V.SecExp.buildings.secHub.security[type] = 1; + V.SecExp.buildings.secHub.upgrades.security[type] = 1; break; case "advForensic": case "autoArchive": case "autoTrial": case "worldProfiler": - V.SecExp.buildings.secHub.crime[type] = 1; + V.SecExp.buildings.secHub.upgrades.crime[type] = 1; break; case "sensors": case "signalIntercept": case "radar": - V.SecExp.buildings.secHub.intel[type] = 1; + V.SecExp.buildings.secHub.upgrades.intel[type] = 1; break; case "pathways": case "rapidVehicles": case "rapidPlatforms": case "earlyWarn": - V.SecExp.buildings.secHub.readiness[type] = 1; + V.SecExp.buildings.secHub.upgrades.readiness[type] = 1; break; } App.UI.reload(); diff --git a/src/Mods/SecExp/events/attackOptions.js b/src/Mods/SecExp/events/attackOptions.js deleted file mode 100644 index c5e37fe07a6fdb38498d7d58ca3ddb194b0dbf72..0000000000000000000000000000000000000000 --- a/src/Mods/SecExp/events/attackOptions.js +++ /dev/null @@ -1,391 +0,0 @@ -App.Events.attackOptions = class attackOptions extends App.Events.BaseEvent { - eventPrerequisites() { - return [ - () => V.secExpEnabled > 0, - () => V.SecExp.war.foughtThisWeek === 0, - () => V.SecExp.war.type.includes("Attack") - ]; - } - - execute(node) { - /** Display's the deploy menu for the unit. - * @param {FC.SecExp.PlayerHumanUnitData} [input] the unit to be checked. - * @param {FC.SecExp.PlayerHumanUnitTypeMod} [type] the class of unit to be checked. - * @returns {HTMLDivElement} - */ - const deployMenu = function(input, type) { - let el = document.createElement("div"); - if (input.active === 1 && input.troops > 0) { - App.UI.DOM.appendNewElement("div", el, App.SecExp.unit.describe(input, type, true)); - } - return el; - }; - if (typeof V.SecExp.settings.unitDescriptions === "undefined") { - V.SecExp.settings.unitDescriptions = 0; - } - V.nextButton = " "; - V.encyclopedia = "Battles"; - let r = []; - - let options; - let option; - - App.UI.DOM.appendNewElement("h1", node, `${V.SecExp.war.type} Imminent`, ["monospace", "red"]); - if (!V.SecExp.war.type.includes("Major")) { - if (V.SecExp.battles.victories + V.SecExp.battles.losses > 0) { - r.push(`The ominous message dominates the screens of your office, and ${V.assistant.name} quickly gathers all information available to prepare for battle.`); - if (V.SecExp.war.attacker.type === "raiders") { - if (App.SecExp.battle.recon() >= 1) { - r.push(`A disorganized horde of raiders is coming to your city. To such jackals your arcology surely looks like an appetizing morsel.`); - r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); - } else { - r.push(`Some of your citizens saw the disorganized horde of raiders coming towards the city and quickly reported it. To such jackals your arcology surely looks like an appetizing morsel.`); - } - const div = document.createElement("div"); - App.UI.DOM.makeElement("span", `Raiders`, "strong"); - App.UI.DOM.makeElement("span", `are roaming gangs of bandits, preying on the vulnerable supply lines of Free Cities and old world nations. They are rarely equipped with decent armaments and even more rarely have any formal military training, but they make up for that with high mobility and numbers.`); - r.push(div); - } else if (V.SecExp.war.attacker.type === "free city") { - if (App.SecExp.battle.recon() >= 1) { - r.push(`A menacing column of slavers and hired mercenaries is coming to your city. Another free city is ready to use their best tools to hit a dangerous competitor where it hurts.`); - r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); - } else { - r.push(`Some of your citizens saw the menacing column of slavers and hired mercenaries and rushed to your office to bring the grim news. Another free city is ready to use their best tools to bring down a dangerous competitor.`); - } - const div = document.createElement("div"); - App.UI.DOM.makeElement("span", `Free City expeditions`, "strong"); - App.UI.DOM.makeElement("span", `are usually composed of mercenaries hired to take down sensible supplies or infrastructure in order to damage the enemies of their contractor. They have on average good equipment and training, together with decent mobility, making them a formidable force. Their biggest weakness however is their relatively low numbers.`); - r.push(div); - } else if (V.SecExp.war.attacker.type === "freedom fighters") { - if (App.SecExp.battle.recon() >= 1) { - r.push(`A dangerous looking army of guerrillas is gathering just outside the arcology. Fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire.`); - r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); - } else { - r.push(`Some of your citizens saw the dangerous looking army of guerrillas is gathering just outside the arcology. Fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire.`); - } - const div = document.createElement("div"); - App.UI.DOM.makeElement("span", `Freedom Fighters`, "strong"); - App.UI.DOM.makeElement("span", `are groups of individuals fighting to rid the planet of "evils" such as the Free Cities and their way of life. Lacking the strength to assault one directly they fight guerrilla style slowly starving to death their enemies. They are rarely well equipped, but with good training and mobility they are not a threat that can be taken lightly.`); - r.push(div); - } else if (V.SecExp.war.attacker.type === "old world") { - if (App.SecExp.battle.recon() >= 1) { - r.push(`A disciplined yet dusty, scruffy old world army is approaching the confines of your arcology. There's nothing better than a good war to unite the electorate and your arcology is just the perfect target.`); - r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); - } else { - r.push(`Some of your citizens saw the disciplined yet dusty, scruffy old world army is approaching the confines of your arcology. There's nothing better than a good war to unite the electorate and your arcology is just the perfect target.`); - } - const div = document.createElement("div"); - App.UI.DOM.makeElement("span", `Old world expeditions`, "strong"); - App.UI.DOM.makeElement("span", `are usually sent to secure resources and trade routes for their nation or, more often, to provide their citizens with a bogeyman to be scared of. They are usually decently equipped and trained, which together with their generous numbers make them a tough nut to crack. However, they often lack in mobility.`); - r.push(div); - } - } else { - r.push(`Your assistant interrupted your rest to bring the grim news. You quickly rush to your console, where you can see one of the convoys supplying your arcology has been attacked and looted. It seems a group of desperate looking bandits decided it was a good idea to steal from you.`); - r.push(`Due to their great wealth, Free Cities inevitably become tasty morsels for anyone able to field armed men. Considering the particular needs of arcologies their supply lines tend to be delicate lifelines, often preyed upon by those who stand to gain from the free city downfall.`); - } - } else { - if (V.SecExp.battles.major > 0) { - r.push(`The ominous message dominates the screens of your office, and ${V.assistant.name} quickly gathers all information available to prepare for the major battle ahead.`); - } else { - r.push(`Your assistant interrupted your rest to bring the grim news. You quickly rush to your console, where you can see the satellite images coming in of the force about to crash against your arcology. It's not the first time your armies fought for the survival of your empire, but this time it seems it will be a fight for life or death.`); - } - if (V.SecExp.war.attacker.type === "raiders") { - if (App.SecExp.battle.recon() >= 1) { - r.push(`A massive, disorganized horde of raiders is coming to your city. It seems a warlord of the wastelands amassed enough men to try and obtain a slice of territory of his own; if he's not defeated there won't be a tomorrow for the arcology.`); - r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); - } else { - r.push(`Some of your citizens saw the massive, disorganized horde of raiders coming towards the city and quickly reported it. It seems a warlord of the wastelands amassed enough men to try and obtain a slice of territory of his own; if he's not defeated there won't be a tomorrow for the arcology.`); - } - r.push(App.UI.DOM.makeElement("div", `Raiders are roaming gangs of bandits, preying on the vulnerable supply lines of Free Cities and old world nations. They are rarely equipped with decent armaments and even more rarely have any formal military training, but they make up for that with high mobility and numbers.`)); - } else if (V.SecExp.war.attacker.type === "free city") { - if (App.SecExp.battle.recon() >= 1) { - r.push(`A massive, menacing column of slavers and hired mercenaries is coming to your city. The quantity of money invested in this assault is staggering; it seems you made some very powerful enemies. If they're not defeated your story will end this day.`); - r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); - } else { - r.push(`Some of your citizens saw the massive, menacing column of slavers and hired mercenaries and rushed to your office to bring the grim news. The quantity of money invested in this assault is staggering; it seems you made some very powerful enemies. If they're not defeated your story will end this day.`); - } - r.push(App.UI.DOM.makeElement("div", `Free City expeditions are usually composed of mercenaries hired to take down sensible supplies or infrastructure in order to damage the enemies of their contractor. They have, on average, good equipment and training, together with decent mobility, making them a formidable force. Their biggest weakness, however, is their relatively low numbers.`)); - } else if (V.SecExp.war.attacker.type === "freedom fighters") { - if (App.SecExp.battle.recon() >= 1) { - r.push(`A massive, dangerous army of guerrillas is gathering just outside the arcology. A huge ocean of fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire once and for all. And this time they won't stop until your body is burnt to a crisp.`); - r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); - } else { - r.push(`Some of your citizens saw the massive, dangerous army of guerrillas is gathering just outside the arcology. A huge ocean of fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire once and for all. And this time they won't stop until your body is burnt to a crisp.`); - } - r.push(App.UI.DOM.makeElement("div", `Freedom Fighters are groups of individuals fighting to rid the planet of "evils" such as the Free Cities and their way of life. Lacking the strength to assault one directly, they fight guerrilla style, slowly starving to death their enemies. They are rarely well equipped, but with good training and mobility they are not a threat that can be taken lightly.`)); - } else if (V.SecExp.war.attacker.type === "old world") { - if (App.SecExp.battle.recon() >= 1) { - r.push(`A massive, disciplined old world army is approaching the confines of your arcology. It seems one of the nations of the old world is determined to put your arcology to rest once and for all or die trying.`); - r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); - } else { - r.push(`Some of your citizens saw the massive, disciplined old world army is approaching the confines of your arcology. It seems one of the nations of the old world is determined to put your arcology to rest once and for all or die trying.`); - } - r.push(App.UI.DOM.makeElement("div", `Old world expeditions are usually sent to secure resources and trade routes for their nation or, more often, to provide their citizens with a bogeyman to be scared of. They are usually decently equipped and trained, which together with their generous numbers make them a tough nut to crack. However, they often lack in mobility.`)); - } - } - App.Events.addParagraph(node, r); - r = []; - App.UI.DOM.appendNewElement("h3", node, `Recon: (AO: ${V.terrain})`); - const estimatedMen = V.SecExp.war.estimatedMen; - const expectedEquip = V.SecExp.war.expectedEquip; - const isOceanic = V.terrain === "oceanic"; - r.push(`It seems your troops and your adversary will fight`); - if (V.SecExp.war.terrain === "rural") { - r.push(`in`, App.UI.DOM.makeElement("span", `the rural land`, "strong"), `surrounding the free city.`); - } else if (V.SecExp.war.terrain === "urban") { - r.push(`in the old`, App.UI.DOM.makeElement("span", `abandoned city`, "strong"), `surrounding the free city.`); - } else if (V.SecExp.war.terrain === "hills") { - r.push(`on`, App.UI.DOM.makeElement("span", `the hills`, "strong"), `around the free city.`); - } else if (V.SecExp.war.terrain === "coast") { - r.push(`along`, App.UI.DOM.makeElement("span", `the coast`, "strong"), `just outside the free city.`); - } else if (V.SecExp.war.terrain === "outskirts") { - r.push(`right against`, App.UI.DOM.makeElement("span", `the walls of the arcology.`, "strong")); - } else if (V.SecExp.war.terrain === "mountains") { - r.push(`in`, App.UI.DOM.makeElement("span", `the mountains`, "strong"), `overlooking the arcology.`); - } else if (V.SecExp.war.terrain === "wasteland") { - r.push(`in`, App.UI.DOM.makeElement("span", `the wastelands`, "strong"), `outside the free city territory.`); - } else if (V.SecExp.war.terrain === "international waters") { - r.push(`in`, App.UI.DOM.makeElement("span", `the water surrounding`, "strong"), `the free city.`); - } else if (["a sunken ship", "an underwater cave"].includes(V.SecExp.war.terrain)) { - r.push(`in`, App.UI.DOM.makeElement("span", `${V.SecExp.war.terrain}`, "strong"), `near the free city.`); - } else if (V.SecExp.war.terrain === "error") { - r.push(App.UI.DOM.makeElement("span", `Error: failed to assign terrain.`, "red"), `${V.SecExp.war.terrain} reads: ${V.SecExp.war.terrain}.`); - } else { - r.push(App.UI.DOM.makeElement("span", `Error: failed to read terrain.`, "red"), `${V.SecExp.war.terrain} reads: ${V.SecExp.war.terrain}.`); - } - if (App.SecExp.battle.recon() === 3) { - r.push(`Your recon capabilities are top notch. The information collected will be most likely correct or very close to be so:`); - } else if (App.SecExp.battle.recon() === 2) { - r.push(`Your recon capabilities are decent. The information collected will be mostly close to the truth:`); - } else if (App.SecExp.battle.recon() === 1) { - r.push(`Your recon capabilities are fairly low. The information collected will be quite inaccurate:`); - } else { - r.push(`Your recon capabilities are almost non-existent. The information collected will be wild guesses at best:`); - } - r.push(`approximately`); - r.push(App.UI.DOM.makeElement("span", `${estimatedMen} men`, "strong")); - r.push(`are coming, they seem to be`); - if (expectedEquip <= 0) { - r.push(App.UI.DOM.makeElement("span", `poorly armed.`, "strong")); - r.push(`Old rusty small arms are the norm with just a few barely working civilian ${isOceanic ? 'boats' : 'vehicles'}.`); - } else if (expectedEquip === 1) { - r.push(App.UI.DOM.makeElement("span", `lightly armed,`, "strong")); - r.push(`mostly with small arms and some repurposed civilian ${isOceanic ? 'boats' : 'vehicles'} with scattered machine gun support. There's no sign of heavy ${isOceanic ? 'boats' : 'vehicles'}, ${isOceanic ? 'submarines' : 'artillery'} or aircraft.`); - } else if (expectedEquip === 2) { - r.push(App.UI.DOM.makeElement("span", `decently armed`, "strong")); - r.push(`with good quality small arms, machine guns and a few mortars. There appear to be some heavy military ${isOceanic ? 'boats' : 'vehicles'} coming as well.`); - } else if (expectedEquip === 3) { - r.push(App.UI.DOM.makeElement("span", `well armed`, "strong")); - r.push(`with high quality small arms, ${isOceanic ? 'spear men' : 'snipers'}, demolitions teams, heavy duty machine guns and mortars. Heavy military ${isOceanic ? 'boats' : 'vehicles'} are numerous and a few ${isOceanic ? 'submarines' : 'artillery pieces'} are accompanying the detachment.`); - } else if (expectedEquip >= 4) { - r.push(App.UI.DOM.makeElement("span", `extremely well armed`, "strong")); - r.push(`with excellent small arms and specialized teams with heavy duty infantry support weapons. Heavy presence of armored military ${isOceanic ? 'boats' : 'vehicles'}, ${isOceanic ? 'submarines' : 'artillery pieces'} and even some attack helicopters.`); - } - App.Events.addParagraph(node, r); - r = []; - App.UI.DOM.appendNewElement("h2", node, `Battle plan`); - if (V.SecExp.war.commander === "bodyguard" && V.BodyguardID === 0 || V.SecExp.war.commander === "headGirl" && V.HeadGirlID === 0) { - App.UI.DOM.makeElement("span", `Chosen leader ${V.SecExp.war.commander} cannot be found, please select another.`, "warning"); - V.SecExp.war.commander = "PC"; - } - - options = new App.UI.OptionsGroup(); // leader assignment - option = options.addOption("Leader of the troops", "commander", V.SecExp.war) - .addValueList([["You", "PC"], [V.assistant.name, "assistant"]]); - - if (V.BodyguardID !== 0 && V.SecExp.edicts.defense.slavesOfficers === 1) { - option.addValue("Bodyguard", "bodyguard"); - } - if (V.HeadGirlID !== 0 && V.SecExp.edicts.defense.slavesOfficers === 1) { - option.addValue("Head Girl", "headGirl"); - } - if (V.SecExp.edicts.defense.militia >= 1) { - option.addValue("Citizens' militia officers", "citizen"); - } - if (V.mercenaries > 0) { - option.addValue("Mercenary officers", "mercenary"); - } - if (V.SF.Toggle && V.SF.Active >= 1 && V.SF.MercCon.CanAttend === -2) { - option.addValue("Colonel", "colonel"); - } - node.append(options.render()); - - options = new App.UI.OptionsGroup(); - const tacticsDesc = new Map([ - ["Bait and Bleed", `Combines bait and switch tactics with guerrilla style assaults, with the objective of slowly bleed the enemy.`], - [`Guerrilla`, `Involves using terrain knowledge and small fast attacks to hinder and weaken the enemy.`], - [`Choke Points`, `Involves using terrain knowledge and strong fortifications in order to stop the enemy on its track`], - [`Interior Lines`, `Involves exploiting a defender's shorter logistics lines and redeployment times in order to keep the enemy pressured.`], - [`Pincer Maneuver`, `Involves letting the enemy push back the center in order to envelop their formation.`], - [`Defense In Depth`, `Involves letting the enemy gain terrain to gain tactical superiority by alternating between delaying actions and small counterattacks.`], - [`Blitzkrieg`, `Involves breaking the front of the enemy with a fast armored force concentrated into a small area.`], - [`Human Wave`, `Involves assaulting the enemy with large numbers of infantry to overwhelm their lines.`], - ]); - App.UI.DOM.appendNewElement("h3", node, `Tactics`); - const tactics = App.Data.SecExp.TerrainAndTactics.get(V.SecExp.war.terrain); - for (const tactic in tactics) { - const option = options.addOption(tactic, "chosenTactic", V.SecExp.war).addValue("Select", tactic); - const comment = document.createElement("span"); - if (tactics[tactic].atkMod > 0.1) { - App.UI.DOM.appendNewElement("span", comment, "Atk+, ", "green"); - } else if (tactics[tactic].atkMod < 0.1) { - App.UI.DOM.appendNewElement("span", comment, "Atk-, ", "red"); - } - if (tactics[tactic].defMod > 0.1) { - App.UI.DOM.appendNewElement("span", comment, "Def+, ", "green"); - } else if (tactics[tactic].defMod < 0.1) { - App.UI.DOM.appendNewElement("span", comment, "Def-, ", "red"); - } - comment.append(tacticsDesc.get(tactic)); - option.addComment(comment); - } - node.append(options.render()); - - App.UI.DOM.appendNewElement("h3", node, `Troops`); - if (V.SF.Toggle && V.SF.Active >= 1 && V.SecExp.war.type.includes("Major")) { - options = new App.UI.OptionsGroup(); - options.addOption("The incoming attack's scale warrants deploying the special force.", "deploySF", V.SecExp.war) - .addValue("Green light", 1).on().addValue("Red light", 0).off() - .addComment("Some upgrades will be able to support your troops even if the special force is not deployed in the fight."); - node.append(options.render()); - } - - let linkArray = []; - if (V.SecExp.battles.lastSelection.length > 0) { - linkArray.push( - App.UI.DOM.link( - `Restore saved`, - () => { - for (let i = 0; i < V.SecExp.battles.lastSelection.length; i++) { - for (const squad of App.SecExp.unit.squads()) { - if (V.SecExp.battles.lastSelection[i] === squad.ID) { - squad.isDeployed = 1; - break; - } - } - } - V.SecExp.war.saveValid = 1; - V.SecExp.war.commander = V.SecExp.battles.saved.commander; - V.SecExp.war.deploySF = V.SecExp.battles.saved.sfSupport; - }, - [], - passage() - ) - ); - } else { - r.push(`Restore saved`); - } - if (V.SecExp.war.saveValid !== 1) { - linkArray.push( - App.UI.DOM.link( - `Save current`, - () => { - if (App.SecExp.battle.deployedUnits('bots')) { - V.SecExp.battles.lastSelection.push(-1); - } - for (const squad of App.SecExp.unit.squads("human")) { - if (squad.isDeployed === 1) { - V.SecExp.battles.lastSelection.push(squad.ID); - } - } - V.SecExp.war.saveValid = 1; - V.SecExp.battles.saved.commander = V.SecExp.war.commander; - V.SecExp.battles.saved.sfSupport = V.SecExp.war.deploySF; - }, - [], - passage() - ) - ); - } else { - r.push(`Save current`); - } - if (App.SecExp.battle.deployedUnits() > 0) { - linkArray.push( - App.UI.DOM.link( - `Clear current`, - () => { - App.SecExp.unit.squads().forEach(s => s.isDeployed = 0); - V.SecExp.war.saveValid = 0; - }, - [], - passage() - ) - ); - } else { - r.push(`Clear current`); - } - if (V.SecExp.battles.lastSelection.length > 0) { - linkArray.push( - App.UI.DOM.link( - `Clear saved`, - () => { - V.SecExp.battles.lastSelection = []; - V.SecExp.war.saveValid = 0; - }, - [], - passage() - ) - ); - } else { - r.push(`Clear saved`); - } - node.append("Roster: ", App.UI.DOM.generateLinksStrip(linkArray)); - - // troop deployment - if (App.SecExp.battle.deployableUnits() > 0) { - r.push(`With your current readiness level you can send an additional`); - r.push(App.UI.DOM.makeElement("span", String(App.SecExp.battle.deployableUnits()), "strong")); - r.push(`units.`); - } - node.append(App.SecExp.unit.replenishAll()); - if (App.SecExp.battle.deployableUnits() === 0) { - App.UI.DOM.appendNewElement("div", node, `Unit roster full.`, "strong"); - } - - const tabBar = new App.UI.Tabs.TabBar("SecExpAttackOptions"); - for (const [u] of App.SecExp.unit.list()) { - if (V.SecExp.units[u].squads.length > 0) { - tabBar.addTab(capFirstChar(u), u, unitTab(u)); - } - } - node.append(tabBar.render()); - - options = new App.UI.OptionsGroup(); - option = options.addCustomOption("Send your orders"); - if (App.SecExp.battle.deployedUnits() > 0) { - option.addButton(`Deploy troops`, () => { - V.SecExp.war.result = 4; // Sets to a value outside accepted range (-3,3) to avoid evaluation problems - V.SecExp.war.foughtThisWeek = 1; - }, `conflictHandler`); - } else { - App.UI.DOM.appendNewElement("div", node, `You need at least a unit in your roster to proceed to battle.`, "red"); - } - option.addButton(`Attempt to bribe (approximately ${cashFormat(Math.round(App.SecExp.battle.bribeCost() * (1 + either(-1, 1) * random(2) * 0.1)))})`, () => { - V.SecExp.war.result = 1; - V.SecExp.war.foughtThisWeek = 1; - }, `conflictHandler`); - option.addButton(`Surrender`, () => { - V.SecExp.war.result = -1; - V.SecExp.war.foughtThisWeek = 1; - }, "conflictReport"); - node.append(options.render()); - - return node; - - function unitTab(type) { - const frag = new DocumentFragment(); - frag.append(toggleLongDesc()); - V.SecExp.units[type].squads.forEach(s => frag.append(deployMenu(s))); - return frag; - } - - function toggleLongDesc() { - const el = new DocumentFragment(); - const options = new App.UI.OptionsGroup(); - options.addOption("Unit descriptions are", "unitDescriptions", V.SecExp.settings) - .addValueList([["Abbreviated", 1], ["Summarized", 0]]); - el.append(options.render()); - return el; - } - } -}; diff --git a/src/Mods/SecExp/events/conflictOptions.js b/src/Mods/SecExp/events/conflictOptions.js new file mode 100644 index 0000000000000000000000000000000000000000..73a32eb0287ca140ca1396252fdd14595466e7e8 --- /dev/null +++ b/src/Mods/SecExp/events/conflictOptions.js @@ -0,0 +1,488 @@ +App.Events.conflictOptions = class conflictOptions extends App.Events.BaseEvent { + eventPrerequisites() { + return [ + () => V.secExpEnabled > 0, + () => V.SecExp.war.foughtThisWeek === 0, + () => V.SecExp.war.type !== "" + ]; + } + + execute(node) { + V.nextButton = " "; + V.encyclopedia = "Battles"; + const isBattle = V.SecExp.war.type.includes("Attack"); + let r = []; + let options; + let option; + + App.UI.DOM.appendNewElement("h1", node, `${V.SecExp.war.type}${isBattle ? 'Imminent' : '!'}`, ["monospace", "red"]); + if (isBattle) { + if (typeof V.SecExp.settings.unitDescriptions === "undefined") { + V.SecExp.settings.unitDescriptions = 0; + } + if (!V.SecExp.war.type.includes("Major")) { + if (V.SecExp.battles.victories + V.SecExp.battles.losses > 0) { + r.push(`The ominous message dominates the screens of your office, and ${V.assistant.name} quickly gathers all information available to prepare for battle.`); + if (V.SecExp.war.attacker.type === "raiders") { + if (App.SecExp.battle.recon() >= 1) { + r.push(`A disorganized horde of raiders is coming to your city. To such jackals your arcology surely looks like an appetizing morsel.`); + r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); + } else { + r.push(`Some of your citizens saw the disorganized horde of raiders coming towards the city and quickly reported it. To such jackals your arcology surely looks like an appetizing morsel.`); + } + const div = document.createElement("div"); + App.UI.DOM.makeElement("span", `Raiders`, "strong"); + App.UI.DOM.makeElement("span", `are roaming gangs of bandits, preying on the vulnerable supply lines of Free Cities and old world nations. They are rarely equipped with decent armaments and even more rarely have any formal military training, but they make up for that with high mobility and numbers.`); + r.push(div); + } else if (V.SecExp.war.attacker.type === "free city") { + if (App.SecExp.battle.recon() >= 1) { + r.push(`A menacing column of slavers and hired mercenaries is coming to your city. Another free city is ready to use their best tools to hit a dangerous competitor where it hurts.`); + r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); + } else { + r.push(`Some of your citizens saw the menacing column of slavers and hired mercenaries and rushed to your office to bring the grim news. Another free city is ready to use their best tools to bring down a dangerous competitor.`); + } + const div = document.createElement("div"); + App.UI.DOM.makeElement("span", `Free City expeditions`, "strong"); + App.UI.DOM.makeElement("span", `are usually composed of mercenaries hired to take down sensible supplies or infrastructure in order to damage the enemies of their contractor. They have on average good equipment and training, together with decent mobility, making them a formidable force. Their biggest weakness however is their relatively low numbers.`); + r.push(div); + } else if (V.SecExp.war.attacker.type === "freedom fighters") { + if (App.SecExp.battle.recon() >= 1) { + r.push(`A dangerous looking army of guerrillas is gathering just outside the arcology. Fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire.`); + r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); + } else { + r.push(`Some of your citizens saw the dangerous looking army of guerrillas is gathering just outside the arcology. Fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire.`); + } + const div = document.createElement("div"); + App.UI.DOM.makeElement("span", `Freedom Fighters`, "strong"); + App.UI.DOM.makeElement("span", `are groups of individuals fighting to rid the planet of "evils" such as the Free Cities and their way of life. Lacking the strength to assault one directly they fight guerrilla style slowly starving to death their enemies. They are rarely well equipped, but with good training and mobility they are not a threat that can be taken lightly.`); + r.push(div); + } else if (V.SecExp.war.attacker.type === "old world") { + if (App.SecExp.battle.recon() >= 1) { + r.push(`A disciplined yet dusty, scruffy old world army is approaching the confines of your arcology. There's nothing better than a good war to unite the electorate and your arcology is just the perfect target.`); + r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); + } else { + r.push(`Some of your citizens saw the disciplined yet dusty, scruffy old world army is approaching the confines of your arcology. There's nothing better than a good war to unite the electorate and your arcology is just the perfect target.`); + } + const div = document.createElement("div"); + App.UI.DOM.makeElement("span", `Old world expeditions`, "strong"); + App.UI.DOM.makeElement("span", `are usually sent to secure resources and trade routes for their nation or, more often, to provide their citizens with a bogeyman to be scared of. They are usually decently equipped and trained, which together with their generous numbers make them a tough nut to crack. However, they often lack in mobility.`); + r.push(div); + } + } else { + r.push(`Your assistant interrupted your rest to bring the grim news. You quickly rush to your console, where you can see one of the convoys supplying your arcology has been attacked and looted. It seems a group of desperate looking bandits decided it was a good idea to steal from you.`); + r.push(`Due to their great wealth, Free Cities inevitably become tasty morsels for anyone able to field armed men. Considering the particular needs of arcologies their supply lines tend to be delicate lifelines, often preyed upon by those who stand to gain from the free city downfall.`); + } + } else { + if (V.SecExp.battles.major > 0) { + r.push(`The ominous message dominates the screens of your office, and ${V.assistant.name} quickly gathers all information available to prepare for the major battle ahead.`); + } else { + r.push(`Your assistant interrupted your rest to bring the grim news. You quickly rush to your console, where you can see the satellite images coming in of the force about to crash against your arcology. It's not the first time your armies fought for the survival of your empire, but this time it seems it will be a fight for life or death.`); + } + if (V.SecExp.war.attacker.type === "raiders") { + if (App.SecExp.battle.recon() >= 1) { + r.push(`A massive, disorganized horde of raiders is coming to your city. It seems a warlord of the wastelands amassed enough men to try and obtain a slice of territory of his own; if he's not defeated there won't be a tomorrow for the arcology.`); + r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); + } else { + r.push(`Some of your citizens saw the massive, disorganized horde of raiders coming towards the city and quickly reported it. It seems a warlord of the wastelands amassed enough men to try and obtain a slice of territory of his own; if he's not defeated there won't be a tomorrow for the arcology.`); + } + r.push(App.UI.DOM.makeElement("div", `Raiders are roaming gangs of bandits, preying on the vulnerable supply lines of Free Cities and old world nations. They are rarely equipped with decent armaments and even more rarely have any formal military training, but they make up for that with high mobility and numbers.`)); + } else if (V.SecExp.war.attacker.type === "free city") { + if (App.SecExp.battle.recon() >= 1) { + r.push(`A massive, menacing column of slavers and hired mercenaries is coming to your city. The quantity of money invested in this assault is staggering; it seems you made some very powerful enemies. If they're not defeated your story will end this day.`); + r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); + } else { + r.push(`Some of your citizens saw the massive, menacing column of slavers and hired mercenaries and rushed to your office to bring the grim news. The quantity of money invested in this assault is staggering; it seems you made some very powerful enemies. If they're not defeated your story will end this day.`); + } + r.push(App.UI.DOM.makeElement("div", `Free City expeditions are usually composed of mercenaries hired to take down sensible supplies or infrastructure in order to damage the enemies of their contractor. They have, on average, good equipment and training, together with decent mobility, making them a formidable force. Their biggest weakness, however, is their relatively low numbers.`)); + } else if (V.SecExp.war.attacker.type === "freedom fighters") { + if (App.SecExp.battle.recon() >= 1) { + r.push(`A massive, dangerous army of guerrillas is gathering just outside the arcology. A huge ocean of fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire once and for all. And this time they won't stop until your body is burnt to a crisp.`); + r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); + } else { + r.push(`Some of your citizens saw the massive, dangerous army of guerrillas is gathering just outside the arcology. A huge ocean of fanatics and idealists armed with dead men's words and hope, set on erasing your fledgling empire once and for all. And this time they won't stop until your body is burnt to a crisp.`); + } + r.push(App.UI.DOM.makeElement("div", `Freedom Fighters are groups of individuals fighting to rid the planet of "evils" such as the Free Cities and their way of life. Lacking the strength to assault one directly, they fight guerrilla style, slowly starving to death their enemies. They are rarely well equipped, but with good training and mobility they are not a threat that can be taken lightly.`)); + } else if (V.SecExp.war.attacker.type === "old world") { + if (App.SecExp.battle.recon() >= 1) { + r.push(`A massive, disciplined old world army is approaching the confines of your arcology. It seems one of the nations of the old world is determined to put your arcology to rest once and for all or die trying.`); + r.push(`Fortunately you knew of their coming, thanks to your recon systems.`); + } else { + r.push(`Some of your citizens saw the massive, disciplined old world army is approaching the confines of your arcology. It seems one of the nations of the old world is determined to put your arcology to rest once and for all or die trying.`); + } + r.push(App.UI.DOM.makeElement("div", `Old world expeditions are usually sent to secure resources and trade routes for their nation or, more often, to provide their citizens with a bogeyman to be scared of. They are usually decently equipped and trained, which together with their generous numbers make them a tough nut to crack. However, they often lack in mobility.`)); + } + } + App.Events.addParagraph(node, r); + r = []; + + App.UI.DOM.appendNewElement("h3", node, `Recon: (AO: ${V.terrain})`); + const estimatedMen = V.SecExp.war.estimatedMen; + const expectedEquip = V.SecExp.war.expectedEquip; + const isOceanic = V.terrain === "oceanic"; + r.push(`It seems your troops and your adversary will fight`); + if (V.SecExp.war.terrain === "rural") { + r.push(`in`, App.UI.DOM.makeElement("span", `the rural land`, "strong"), `surrounding the free city.`); + } else if (V.SecExp.war.terrain === "urban") { + r.push(`in the old`, App.UI.DOM.makeElement("span", `abandoned city`, "strong"), `surrounding the free city.`); + } else if (V.SecExp.war.terrain === "hills") { + r.push(`on`, App.UI.DOM.makeElement("span", `the hills`, "strong"), `around the free city.`); + } else if (V.SecExp.war.terrain === "coast") { + r.push(`along`, App.UI.DOM.makeElement("span", `the coast`, "strong"), `just outside the free city.`); + } else if (V.SecExp.war.terrain === "outskirts") { + r.push(`right against`, App.UI.DOM.makeElement("span", `the walls of the arcology.`, "strong")); + } else if (V.SecExp.war.terrain === "mountains") { + r.push(`in`, App.UI.DOM.makeElement("span", `the mountains`, "strong"), `overlooking the arcology.`); + } else if (V.SecExp.war.terrain === "wasteland") { + r.push(`in`, App.UI.DOM.makeElement("span", `the wastelands`, "strong"), `outside the free city territory.`); + } else if (V.SecExp.war.terrain === "international waters") { + r.push(`in`, App.UI.DOM.makeElement("span", `the water surrounding`, "strong"), `the free city.`); + } else if (["a sunken ship", "an underwater cave"].includes(V.SecExp.war.terrain)) { + r.push(`in`, App.UI.DOM.makeElement("span", `${V.SecExp.war.terrain}`, "strong"), `near the free city.`); + } else if (V.SecExp.war.terrain === "error") { + r.push(App.UI.DOM.makeElement("span", `Error: failed to assign terrain.`, "red"), `${V.SecExp.war.terrain} reads: ${V.SecExp.war.terrain}.`); + } else { + r.push(App.UI.DOM.makeElement("span", `Error: failed to read terrain.`, "red"), `${V.SecExp.war.terrain} reads: ${V.SecExp.war.terrain}.`); + } + if (App.SecExp.battle.recon() === 3) { + r.push(`Your recon capabilities are top notch. The information collected will be most likely correct or very close to be so:`); + } else if (App.SecExp.battle.recon() === 2) { + r.push(`Your recon capabilities are decent. The information collected will be mostly close to the truth:`); + } else if (App.SecExp.battle.recon() === 1) { + r.push(`Your recon capabilities are fairly low. The information collected will be quite inaccurate:`); + } else { + r.push(`Your recon capabilities are almost non-existent. The information collected will be wild guesses at best:`); + } + r.push(`approximately`); + r.push(App.UI.DOM.makeElement("span", `${estimatedMen} men`, "strong")); + r.push(`are coming, they seem to be`); + if (expectedEquip <= 0) { + r.push(App.UI.DOM.makeElement("span", `poorly armed.`, "strong")); + r.push(`Old rusty small arms are the norm with just a few barely working civilian ${isOceanic ? 'boats' : 'vehicles'}.`); + } else if (expectedEquip === 1) { + r.push(App.UI.DOM.makeElement("span", `lightly armed,`, "strong")); + r.push(`mostly with small arms and some repurposed civilian ${isOceanic ? 'boats' : 'vehicles'} with scattered machine gun support. There's no sign of heavy ${isOceanic ? 'boats' : 'vehicles'}, ${isOceanic ? 'submarines' : 'artillery'} or aircraft.`); + } else if (expectedEquip === 2) { + r.push(App.UI.DOM.makeElement("span", `decently armed`, "strong")); + r.push(`with good quality small arms, machine guns and a few mortars. There appear to be some heavy military ${isOceanic ? 'boats' : 'vehicles'} coming as well.`); + } else if (expectedEquip === 3) { + r.push(App.UI.DOM.makeElement("span", `well armed`, "strong")); + r.push(`with high quality small arms, ${isOceanic ? 'spear men' : 'snipers'}, demolitions teams, heavy duty machine guns and mortars. Heavy military ${isOceanic ? 'boats' : 'vehicles'} are numerous and a few ${isOceanic ? 'submarines' : 'artillery pieces'} are accompanying the detachment.`); + } else if (expectedEquip >= 4) { + r.push(App.UI.DOM.makeElement("span", `extremely well armed`, "strong")); + r.push(`with excellent small arms and specialized teams with heavy duty infantry support weapons. Heavy presence of armored military ${isOceanic ? 'boats' : 'vehicles'}, ${isOceanic ? 'submarines' : 'artillery pieces'} and even some attack helicopters.`); + } + App.Events.addParagraph(node, r); + r = []; + + App.UI.DOM.appendNewElement("h2", node, `Battle plan`); + if (V.SecExp.war.commander === "bodyguard" && V.BodyguardID === 0 || V.SecExp.war.commander === "headGirl" && V.HeadGirlID === 0) { + App.UI.DOM.makeElement("span", `Chosen leader ${V.SecExp.war.commander} cannot be found, please select another.`, "warning"); + V.SecExp.war.commander = "PC"; + } + options = new App.UI.OptionsGroup(); // leader assignment + option = options.addOption("Leader of the troops", "commander", V.SecExp.war) + .addValueList([["You", "PC"], [V.assistant.name, "assistant"]]); + + if (V.BodyguardID !== 0 && V.SecExp.edicts.defense.slavesOfficers === 1) { + option.addValue("Bodyguard", "bodyguard"); + } + if (V.HeadGirlID !== 0 && V.SecExp.edicts.defense.slavesOfficers === 1) { + option.addValue("Head Girl", "headGirl"); + } + if (V.SecExp.edicts.defense.militia >= 1) { + option.addValue("Citizens' militia officers", "citizen"); + } + if (V.mercenaries > 0) { + option.addValue("Mercenary officers", "mercenary"); + } + if (V.SF.Toggle && V.SF.Active >= 1 && V.SF.MercCon.CanAttend === -2) { + option.addValue("Colonel", "colonel"); + } + node.append(options.render()); + + options = new App.UI.OptionsGroup(); + const tacticsDesc = new Map([ + ["Bait and Bleed", `Combines bait and switch tactics with guerrilla style assaults, with the objective of slowly bleed the enemy.`], + [`Guerrilla`, `Involves using terrain knowledge and small fast attacks to hinder and weaken the enemy.`], + [`Choke Points`, `Involves using terrain knowledge and strong fortifications in order to stop the enemy on its track`], + [`Interior Lines`, `Involves exploiting a defender's shorter logistics lines and redeployment times in order to keep the enemy pressured.`], + [`Pincer Maneuver`, `Involves letting the enemy push back the center in order to envelop their formation.`], + [`Defense In Depth`, `Involves letting the enemy gain terrain to gain tactical superiority by alternating between delaying actions and small counterattacks.`], + [`Blitzkrieg`, `Involves breaking the front of the enemy with a fast armored force concentrated into a small area.`], + [`Human Wave`, `Involves assaulting the enemy with large numbers of infantry to overwhelm their lines.`], + ]); + + App.UI.DOM.appendNewElement("h3", node, `Tactics`); + const tactics = App.Data.SecExp.TerrainAndTactics.get(V.SecExp.war.terrain); + for (const tactic in tactics) { + option = options.addOption(tactic, "chosenTactic", V.SecExp.war).addValue("Select", tactic); + const comment = document.createElement("span"); + if (tactics[tactic].atkMod > 0.1) { + App.UI.DOM.appendNewElement("span", comment, "Atk+, ", "green"); + } else if (tactics[tactic].atkMod < 0.1) { + App.UI.DOM.appendNewElement("span", comment, "Atk-, ", "red"); + } + if (tactics[tactic].defMod > 0.1) { + App.UI.DOM.appendNewElement("span", comment, "Def+, ", "green"); + } else if (tactics[tactic].defMod < 0.1) { + App.UI.DOM.appendNewElement("span", comment, "Def-, ", "red"); + } + comment.append(tacticsDesc.get(tactic)); + option.addComment(comment); + } + node.append(options.render()); + + App.UI.DOM.appendNewElement("h3", node, `Troops`); + if (V.SF.Toggle && V.SF.Active >= 1 && V.SecExp.war.type.includes("Major")) { + options = new App.UI.OptionsGroup(); + options.addOption("The incoming attack's scale warrants deploying the special force.", "deploySF", V.SecExp.war) + .addValue("Green light", 1).on().addValue("Red light", 0).off() + .addComment("Some upgrades will be able to support your troops even if the special force is not deployed in the fight."); + node.append(options.render()); + } + + let linkArray = []; + if (V.SecExp.battles.lastSelection.length > 0) { + linkArray.push( + App.UI.DOM.link( + `Restore saved`, + () => { + V.SecExp.war.deployed = V.SecExp.battles.lastSelection; + V.SecExp.war.saveValid = 1; + V.SecExp.war.commander = V.SecExp.battles.saved.commander; + V.SecExp.war.deploySF = V.SecExp.battles.saved.sfSupport; + App.UI.reload(); + } + ) + ); + } else { + r.push(`Restore saved`); + } + if (V.SecExp.war.saveValid !== 1) { + linkArray.push( + App.UI.DOM.link( + `Save current`, + () => { + V.SecExp.battles.lastSelection = V.SecExp.war.deployed; + V.SecExp.war.saveValid = 1; + V.SecExp.battles.saved.commander = V.SecExp.war.commander; + V.SecExp.battles.saved.sfSupport = V.SecExp.war.deploySF; + App.UI.reload(); + } + ) + ); + } else { + r.push(`Save current`); + } + if (App.SecExp.battle.deployedUnits() > 0) { + linkArray.push( + App.UI.DOM.link( + `Clear current`, + () => { + V.SecExp.war.deployed = []; + V.SecExp.war.saveValid = 0; + App.UI.reload(); + } + ) + ); + } else { + r.push(`Clear current`); + } + if (V.SecExp.battles.lastSelection.length > 0) { + linkArray.push( + App.UI.DOM.link( + `Clear saved`, + () => { + V.SecExp.battles.lastSelection = []; + V.SecExp.war.saveValid = 0; + App.UI.reload(); + } + ) + ); + } else { + r.push(`Clear saved`); + } + node.append("Roster: ", App.UI.DOM.generateLinksStrip(linkArray)); + + // troop deployment + if (App.SecExp.battle.deployableUnits() > 0) { + r.push(`With your current readiness level you can send an additional`); + r.push(App.UI.DOM.makeElement("span", String(App.SecExp.battle.deployableUnits()), "strong")); + r.push(`units.`); + } + } else { + const isSlaveRebellion = V.SecExp.war.type.includes("Slave"); + r.push(`In the end it happened, the ${isSlaveRebellion ? "slaves" : "citizens"}`); + r.push(`of your arcology dared took up arms and rose up against their betters. Your penthouse is flooded with reports from all over the arcology of small skirmishes between the rioting slaves and the security forces. It appears <strong>${num(Math.trunc(V.SecExp.war.attacker.troops))}</strong> rebels are in the streets right now, building barricades and`); + if (isSlaveRebellion) { + r.push(`freeing their peers.`); + } else { + r.push(`destroying your property.`); + } + if (V.SecExp.war.attacker.equip <= 0) { + r.push(`They are <strong>poorly armed</strong>.`); + } else if (V.SecExp.war.attacker.equip === 1) { + r.push(`They are <strong>lightly armed</strong>.`); + } else if (V.SecExp.war.attacker.equip === 2) { + r.push(`They are <strong>decently armed</strong>.`); + } else if (V.SecExp.war.attacker.equip === 3) { + r.push(`They are <strong>well armed</strong>.`); + } else if (V.SecExp.war.attacker.equip >= 4) { + r.push(`They are <strong>extremely well armed</strong>.`); + } + if (V.SecExp.war.irregulars > 0) { + r.push(`${num(Math.trunc(V.SecExp.war.irregulars))} of your citizens took up arms to defend their arcology owner.`); + } + if (V.SecExp.war.rebellingID.length > 0) { + App.Events.addParagraph(node, r); + r = []; + let rebelling = []; + for (const squad of App.SecExp.unit.squads("human")) { + if (squad.active === 1 && (V.SecExp.war.rebellingID.includes(squad.ID))) { + rebelling.push(squad.platoonName); + } + } + r.push(`${toSentence(rebelling)} betrayed you and joined the insurrection.`); + } + let defending = []; + if (V.arcologyUpgrade.drones === 1) { + defending.push(`Your security drones`); + } + for (const squad of App.SecExp.unit.squads("human")) { + if (squad.active === 1 && (!V.SecExp.war.rebellingID.includes(squad.ID))) { + defending.push(squad.platoonName); + } + } + if (V.SF.Toggle && V.SF.Active >= 1) { + let SFname = defending.length > 0 ? V.SF.Lower : capFirstChar(V.SF.Lower); + defending.push(`${SFname}, ${num(V.SF.ArmySize)} strong`); + } + if (defending.length > 0) { + App.Events.addParagraph(node, r); + r = []; + r.push(`${toSentence(defending)} are called to defend the arcology from this menace.`); + } + App.Events.addParagraph(node, r); + r = []; + r.push(`The confined spaces of the arcology and the number of vital yet delicate systems within its walls do not allow a lot of tactical flexibility. This will be a long and strenuous fight, street after street, barricade after barricade. In order to preserve the structural integrity of the building and the lives of our civilians, we will have to limit our firepower.`); + App.Events.addParagraph(node, r); + + let text; + if (V.SecExp.war.engageRule === 0) { + text = `Your troops will use only nonlethal weapons or light firearms to limit to the maximum the collateral damage. This will however weaken our troops considerably.`; + } else if (V.SecExp.war.engageRule === 1) { + text = `Your troops will limit the use of explosives and heavy weapons to limit considerably the collateral damage. This will however weaken our troops.`; + } else if (V.SecExp.war.engageRule === 2) { + text = `Your troops will not limit their arsenal. This will put the structure and your citizens at risk, but our troops will be at full capacity.`; + } else if (V.SecExp.war.engageRule === 3) { + text = `Your troops will make use of the special weaponry, equipment and infrastructure developed by the riot control center to surgically eliminate rebels and dissidents with little to no collateral damage.`; + } + if (text) { + App.UI.DOM.appendNewElement("div", node, text, "note"); + } + + const engageRules = new Map([ + [0, `Only light firearms and nonlethal weapons`], + [1, `No heavy ordnance`], + [2, `Normal engagement rules`], + ]); + if (V.SecExp.buildings.riotCenter && V.SecExp.buildings.riotCenter.advancedRiotEquip === 1) { + engageRules.set(3, `Advanced riot protocol`); + } + + for (const [value, text] of engageRules) { + App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( + text, + () => { + V.SecExp.war.engageRule = value; + App.UI.reload(); + } + )); + } + + App.Events.addParagraph(node, [`We can dedicate some of our forces to the protection of the vital parts of the arcology, doing so will prevent the failure of said systems, but will also take away strength from our assault.`]); + const locations = new Map([ + ["penthouseDefense", `penthouse`], + ["reactorDefense", `reactors`], + ["assistantDefense", `assistant's central CPU`], + ["waterwayDefense", `waterways`], + ]); + const activeDefenses = Array.from(locations.keys()).filter(loc => V.SecExp.war[loc] === 1); + if (activeDefenses.length > 0) { + App.UI.DOM.appendNewElement("div", node, `Your troops will garrison the ${toSentence(activeDefenses.map(loc => locations.get(loc)))}.`, "note"); + } + for (const [loc, text] of locations) { + const choices = []; + choices.push(App.UI.DOM.link( + `Garrison the ${text}`, + () => { + V.SecExp.war[loc] = 1; + App.UI.reload(); + } + )); + choices.push(App.UI.DOM.link( + `Discard the order`, + () => { + delete V.SecExp.war[loc]; + App.UI.reload(); + } + )); + App.UI.DOM.appendNewElement("div", node, App.UI.DOM.generateLinksStrip(choices)); + } + } + + node.append(App.SecExp.unit.replenishAll()); + if (isBattle) { + if (App.SecExp.battle.deployableUnits() === 0) { + App.UI.DOM.appendNewElement("div", node, `Unit roster full.`, "strong"); + } + options = new App.UI.OptionsGroup(); + options.addOption("Unit descriptions are", "unitDescriptions", V.SecExp.settings) + .addValueList([["Abbreviated", 1], ["Summarized", 0]]); + node.append(options.render()); + + const tabBar = new App.UI.Tabs.TabBar("SecExpAttackOptions"); + for (const [u] of App.SecExp.unit.list()) { + if (V.SecExp.units[u].squads.length > 0) { + tabBar.addTab(capFirstChar(u), u, unitTab(u)); + } + } + node.append(tabBar.render()); + } + + options = new App.UI.OptionsGroup(); + option = options.addCustomOption(isBattle ? "Send your orders" : "Actions"); + if (isBattle && App.SecExp.battle.deployedUnits() > 0 || !isBattle) { + option.addButton(isBattle ? `Deploy troops` : `Proceed`, () => { + V.SecExp.war.result = 4; // Sets to a value outside accepted range (-3, 3) to avoid evaluation problems + V.SecExp.war.foughtThisWeek = 1; + }, `conflictHandler`); + } else if (isBattle && App.SecExp.battle.deployedUnits() === 0) { + App.UI.DOM.appendNewElement("div", node, `You need at least a unit in your roster to proceed to battle.`, "red"); + } + if (isBattle) { + option.addButton(`Attempt to bribe (approximately ${cashFormat(Math.round(App.SecExp.battle.bribeCost() * (1 + either(-1, 1) * random(2) * 0.1)))})`, () => { + V.SecExp.war.result = 1; + V.SecExp.war.foughtThisWeek = 1; + }, `conflictHandler`); + } + option.addButton(`Surrender`, () => { + V.SecExp.war.result = -1; + V.SecExp.war.foughtThisWeek = 1; + }, "conflictReport"); + node.append(options.render()); + return node; + + // Battles + /** Generates the deploy menu for the unit. + * @param {FC.SecExp.PlayerHumanUnitTypeMod} [type] the class of unit to be checked. + */ + function unitTab(type) { + const frag = new DocumentFragment(); + for (const u of V.SecExp.units[type].squads.filter(s => s.active === 1 && s.troops > 0)) { + App.UI.DOM.appendNewElement("div", frag, App.SecExp.unit.describe(u, true)); + } + return frag; + }; + }; +}; diff --git a/src/Mods/SecExp/events/conflictReport.js b/src/Mods/SecExp/events/conflictReport.js index 9c16504f3e362774ac9be82cecf902c4c91926f7..e346eb941deaa673aa49d6ae1bb5c6295de97e39 100644 --- a/src/Mods/SecExp/events/conflictReport.js +++ b/src/Mods/SecExp/events/conflictReport.js @@ -512,7 +512,7 @@ App.Events.conflictReport = function() { V.lowerClass -= random(10, 30); } repX((result === 3 ? random(800, 1000) : random(600, 180)), "war"); - V.SecExp.core.authority += (result === 3 ? random(800, 1000) : random(600, 800)); + App.SecExp.authorityX(result === 3 ? random(800, 1000) : random(600, 800)); } else { if (result === 3) { r.push("You were also able to capture"); @@ -538,16 +538,16 @@ App.Events.conflictReport = function() { } if (V.SecExp.war.attacker.type === "raiders") { repX((result === 3 ? 4000 : 1000) * majorBattleMod, "war"); - V.SecExp.core.authority += (result === 3 ? 800 : 200) * majorBattleMod; + App.SecExp.authorityX((result === 3 ? 800 : 200) * majorBattleMod); } else if (V.SecExp.war.attacker.type === "free city") { repX((result === 3 ? 6000 : 1500) * majorBattleMod, "war"); - V.SecExp.core.authority += (result === 3 ? 1200 : 300) * majorBattleMod; + App.SecExp.authorityX((result === 3 ? 1200 : 300) * majorBattleMod); } else if (V.SecExp.war.attacker.type === "freedom fighters") { repX((result === 3 ? 7500 : 2000) * majorBattleMod, "war"); - V.SecExp.core.authority += (result === 3 ? 1500 : 450) * majorBattleMod; + App.SecExp.authorityX((result === 3 ? 1500 : 450) * majorBattleMod); } else if (V.SecExp.war.attacker.type === "old world") { repX((result === 3 ? 8000 : 2100) * majorBattleMod, "war"); - V.SecExp.core.authority += (result === 3 ? 1600 : 500) * majorBattleMod; + App.SecExp.authorityX((result === 3 ? 1600 : 500) * majorBattleMod); } r.push(`which once sold produced`); if (V.SecExp.war.attacker.equip === 0) { @@ -608,7 +608,7 @@ App.Events.conflictReport = function() { V.lowerClass -= Math.trunc((V.SecExp.war.attacker.troops - V.SecExp.war.attacker.losses) * 0.6); } repX((result === -3 ? random(-800, -1000) : random(-600, -800)), "war"); - V.SecExp.core.authority -= (result === -3 ? random(800, 1000) : random(600, 800)); + App.SecExp.authorityX(result === -3 ? random(-800, -1000) : random(-600, -800)); } else { if (result === -3) { App.UI.DOM.appendNewElement("div", node, " Obviously your troops were not able to capture anyone or anything."); @@ -631,16 +631,16 @@ App.Events.conflictReport = function() { r = []; if (V.SecExp.war.attacker.type === "raiders") { repX(forceNeg((result === -3 ? 400 : 40) * majorBattleMod), "war"); - V.SecExp.core.authority -= (result === -3 ? 400 : 40) * majorBattleMod; + App.SecExp.authorityX((result === -3 ? -400 : -40) * majorBattleMod); } else if (V.SecExp.war.attacker.type === "free city") { repX(forceNeg((result === -3 ? 600 : 60) * majorBattleMod), "war"); - V.SecExp.core.authority -= (result === -3 ? 600 : 60) * majorBattleMod; + App.SecExp.authorityX((result === -3 ? -600 : -60) * majorBattleMod); } else if (V.SecExp.war.attacker.type === "freedom fighters") { repX(forceNeg((result === -3 ? 750 : 75) * majorBattleMod), "war"); - V.SecExp.core.authority -= (result === -3 ? 750 : 75) * majorBattleMod; + App.SecExp.authorityX((result === -3 ? -750 : -75) * majorBattleMod); } else if (V.SecExp.war.attacker.type === "old world") { repX(forceNeg((result === -3 ? 800 : 80) * majorBattleMod), "war"); - V.SecExp.core.authority -= (result === -3 ? 800 : 80) * majorBattleMod; + App.SecExp.authorityX((result === -3 ? -800 : -80) * majorBattleMod); } cashX(forceNeg((result === -3 ? 5000 : 3000) * majorBattleMod), "war"); @@ -669,7 +669,6 @@ App.Events.conflictReport = function() { } if (inRebellion) { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority, 0, 20000); if (V.SecExp.war.engageRule === 0) { r.push(`Since you ordered your troops to limit their weaponry to low caliber or nonlethal, the arcology reported only`); r.push(`<span class="red">minor damage.</span>`); @@ -1324,7 +1323,7 @@ App.Events.conflictReport = function() { } cashX(-1000, "war"); repX(random(-1000, -1200), "war"); - V.SecExp.core.authority -= random(1000, 1200); + App.SecExp.authorityX(random(-1000, -1200)); if (V.week <= 30) { arcologyEffects(100, 150, 5); } else if (V.week <= 60) { @@ -1343,16 +1342,16 @@ App.Events.conflictReport = function() { r.push(`Rather than waste the lives of your men you decided to surrender, hoping your enemy will cause less damage if you indulge them, this is however a big hit to your status. Your <span class="red">reputation</span> and <span class="red">authority</span> are significantly impacted.`); if (V.SecExp.war.attacker.type === "raiders") { repX(forceNeg(600 * majorBattleMod), "war"); - V.SecExp.core.authority -= 600 * majorBattleMod; + App.SecExp.authorityX(-600 * majorBattleMod); } else if (V.SecExp.war.attacker.type === "free city") { repX(forceNeg(800 * majorBattleMod), "war"); - V.SecExp.core.authority -= 800 * majorBattleMod; + App.SecExp.authorityX(-800 * majorBattleMod); } else if (V.SecExp.war.attacker.type === "freedom fighters") { repX(forceNeg(1000 * majorBattleMod), "war"); - V.SecExp.core.authority -= 1000 * majorBattleMod; + App.SecExp.authorityX(-1000 * majorBattleMod); } else if (V.SecExp.war.attacker.type === "old world") { repX(forceNeg(1200 * majorBattleMod), "war"); - V.SecExp.core.authority -= 1200 * majorBattleMod; + App.SecExp.authorityX(-1200 * majorBattleMod); } App.Events.addParagraph(node, r); r = []; @@ -1391,18 +1390,17 @@ App.Events.conflictReport = function() { r.push(`There was no time to organize a defense and so the enemy walked into the arcology as it was his. Your reputation and authority suffer a hit.`); if (V.SecExp.war.attacker.type === "raiders") { repX(forceNeg(400 * majorBattleMod), "war"); - V.SecExp.core.authority -= 400 * majorBattleMod; + App.SecExp.authorityX(-400 * majorBattleMod); } else if (V.SecExp.war.attacker.type === "free city") { repX(forceNeg(600 * majorBattleMod), "war"); - V.SecExp.core.authority -= 600 * majorBattleMod; + App.SecExp.authorityX(-600 * majorBattleMod); } else if (V.SecExp.war.attacker.type === "freedom fighters") { repX(forceNeg(750 * majorBattleMod), "war"); - V.SecExp.core.authority -= 750 * majorBattleMod; + App.SecExp.authorityX(-750 * majorBattleMod); } else if (V.SecExp.war.attacker.type === "old world") { repX(forceNeg(800 * majorBattleMod), "war"); - V.SecExp.core.authority -= 800 * majorBattleMod; + App.SecExp.authorityX(-800 * majorBattleMod); } - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority, 0, 20000); App.Events.addParagraph(node, r); r = []; r.push(`Fortunately the arcology survives <span class="yellow">mostly intact,</span> however reports of <span class="red">mass looting and killing of citizens</span> flood your office for a few days.`); @@ -1446,9 +1444,7 @@ App.Events.conflictReport = function() { } App.Events.addParagraph(node, r); - if (inBattle) { - App.SecExp.unit.squads().forEach(s => s.isDeployed = 0); - } else { + if (inRebellion) { V.SecExp.rebellions[V.SecExp.war.type.toLowerCase().replace(' rebellion', '') + 'Progress'] = 0; V.SecExp.rebellions.tension = Math.clamp(V.SecExp.rebellions.tension - random(50, 100), 0, 100); if (slaveRebellion) { diff --git a/src/Mods/SecExp/events/rebellionOptions.js b/src/Mods/SecExp/events/rebellionOptions.js deleted file mode 100644 index 855b373e3b8e497bcb893ff87cd0a88e85faf9e4..0000000000000000000000000000000000000000 --- a/src/Mods/SecExp/events/rebellionOptions.js +++ /dev/null @@ -1,159 +0,0 @@ -App.Events.rebellionOptions = class rebellionOptions extends App.Events.BaseEvent { - eventPrerequisites() { - return [ - () => V.secExpEnabled > 0, - () => V.SecExp.war.foughtThisWeek === 0, - () => V.SecExp.war.type.includes("Rebellion") - ]; - } - - execute(node) { - let r = []; - V.nextButton = " "; - V.encyclopedia = "Battles"; - const isSlaveRebellion = V.SecExp.war.type.includes("Slave"); - - App.UI.DOM.appendNewElement("h2", node, `${V.SecExp.war.type}!`); - r.push(`In the end it happened, the ${isSlaveRebellion ? "slaves" : "citizens"}`); - r.push(`of your arcology dared took up arms and rose up against their betters. Your penthouse is flooded with reports from all over the arcology of small skirmishes between the rioting slaves and the security forces. It appears <strong>${num(Math.trunc(V.SecExp.war.attacker.troops))}</strong> rebels are in the streets right now, building barricades and`); - if (isSlaveRebellion) { - r.push(`freeing their peers.`); - } else { - r.push(`destroying your property.`); - } - if (V.SecExp.war.attacker.equip <= 0) { - r.push(`They are <strong>poorly armed</strong>.`); - } else if (V.SecExp.war.attacker.equip === 1) { - r.push(`They are <strong>lightly armed</strong>.`); - } else if (V.SecExp.war.attacker.equip === 2) { - r.push(`They are <strong>decently armed</strong>.`); - } else if (V.SecExp.war.attacker.equip === 3) { - r.push(`They are <strong>well armed</strong>.`); - } else if (V.SecExp.war.attacker.equip >= 4) { - r.push(`They are <strong>extremely well armed</strong>.`); - } - if (V.SecExp.war.irregulars > 0) { - r.push(`${num(Math.trunc(V.SecExp.war.irregulars))} of your citizens took up arms to defend their arcology owner.`); - } - if (V.SecExp.war.rebellingID.length > 0) { - App.Events.addParagraph(node, r); - r = []; - let rebelling = []; - for (const squad of App.SecExp.unit.squads("human")) { - if (squad.active === 1 && (V.SecExp.war.rebellingID.includes(squad.ID))) { - rebelling.push(squad.platoonName); - } - } - r.push(`${toSentence(rebelling)} betrayed you and joined the insurrection.`); - } - let defending = []; - if (V.arcologyUpgrade.drones === 1) { - defending.push(`Your security drones`); - } - for (const squad of App.SecExp.unit.squads("human")) { - if (squad.active === 1 && (!V.SecExp.war.rebellingID.includes(squad.ID))) { - defending.push(squad.platoonName); - } - } - if (V.SF.Toggle && V.SF.Active >= 1) { - let SFname = defending.length > 0 ? V.SF.Lower : capFirstChar(V.SF.Lower); - defending.push(`${SFname}, ${num(V.SF.ArmySize)} strong`); - } - if (defending.length > 0) { - App.Events.addParagraph(node, r); - r = []; - r.push(`${toSentence(defending)} are called to defend the arcology from this menace.`); - } - App.Events.addParagraph(node, r); - r = []; - r.push(`The confined spaces of the arcology and the number of vital yet delicate systems within its walls do not allow a lot of tactical flexibility. This will be a long and strenuous fight, street after street, barricade after barricade. In order to preserve the structural integrity of the building and the lives of our civilians, we will have to limit our firepower.`); - App.Events.addParagraph(node, r); - - let text; - if (V.SecExp.war.engageRule === 0) { - text = `Your troops will use only nonlethal weapons or light firearms to limit to the maximum the collateral damage. This will however weaken our troops considerably.`; - } else if (V.SecExp.war.engageRule === 1) { - text = `Your troops will limit the use of explosives and heavy weapons to limit considerably the collateral damage. This will however weaken our troops.`; - } else if (V.SecExp.war.engageRule === 2) { - text = `Your troops will not limit their arsenal. This will put the structure and your citizens at risk, but our troops will be at full capacity.`; - } else if (V.SecExp.war.engageRule === 3) { - text = `Your troops will make use of the special weaponry, equipment and infrastructure developed by the riot control center to surgically eliminate rebels and dissidents with little to no collateral damage.`; - } - if (text) { - App.UI.DOM.appendNewElement("div", node, text, "note"); - } - - const engageRules = new Map([ - [0, `Only light firearms and nonlethal weapons`], - [1, `No heavy ordnance`], - [2, `Normal engagement rules`], - ]); - if (V.SecExp.buildings.riotCenter && V.SecExp.buildings.riotCenter.advancedRiotEquip === 1) { - engageRules.set(3, `Advanced riot protocol`); - } - - for (const [value, text] of engageRules) { - App.UI.DOM.appendNewElement("div", node, App.UI.DOM.link( - text, - () => { - V.SecExp.war.engageRule = value; - reload(); - } - )); - } - - App.Events.addParagraph(node, [`We can dedicate some of our forces to the protection of the vital parts of the arcology, doing so will prevent the failure of said systems, but will also take away strength from our assault.`]); - - const locations = new Map([ - ["penthouseDefense", `penthouse`], - ["reactorDefense", `reactors`], - ["assistantDefense", `assistant's central CPU`], - ["waterwayDefense", `waterways`], - ]); - const activeDefenses = Array.from(locations.keys()).filter(loc => V.SecExp.war[loc] === 1); - if (activeDefenses.length > 0) { - App.UI.DOM.appendNewElement("div", node, `Your troops will garrison the ${toSentence(activeDefenses.map(loc => locations.get(loc)))}.`, "note"); - } - for (const [loc, text] of locations) { - const choices = []; - choices.push(App.UI.DOM.link( - `Garrison the ${text}`, - () => { - V.SecExp.war[loc] = 1; - reload(); - } - )); - choices.push(App.UI.DOM.link( - `Discard the order`, - () => { - delete V.SecExp.war[loc]; - reload(); - } - )); - App.UI.DOM.appendNewElement("div", node, App.UI.DOM.generateLinksStrip(choices)); - } - - node.append(App.SecExp.unit.replenishAll()); - App.UI.DOM.appendNewElement("div", node, App.UI.DOM.passageLink( - "Proceed", - "conflictHandler", - () => { - V.SecExp.war.result = 4; // Sets to a value outside accepted range (-3,3) to avoid evaluation problems - V.SecExp.war.foughtThisWeek = 1; - } - )); - App.UI.DOM.appendNewElement("div", node, App.UI.DOM.passageLink( - "Surrender", - "conflictReport", - () => { - V.SecExp.war.result = -1; - V.SecExp.war.foughtThisWeek = 1; - } - )); - - function reload() { - $(node).empty(); - new App.Events.rebellionOptions().execute(node); - } - } -}; diff --git a/src/Mods/SecExp/js/Unit.js b/src/Mods/SecExp/js/Unit.js index f57948fd59ce17798507a9e115499bb997072547..06ed89666f93c20bc68d048ac251f0c0c7659341 100644 --- a/src/Mods/SecExp/js/Unit.js +++ b/src/Mods/SecExp/js/Unit.js @@ -80,7 +80,7 @@ App.SecExp.unit = (function() { } /** Calculate the unit type via passed unitID. - * @returns {FC.SecExp.PlayerHumanUnitType} + * @returns {FC.SecExp.PlayerHumanUnitTypeMod} */ function checkID(unitID) { for (const [u, d] of list()) { @@ -125,19 +125,18 @@ App.SecExp.unit = (function() { let newUnit = { equip: 0, active: 1, maxTroops: 30, troops: 30, - ID: -1, isDeployed: 0 + platoonName: `${ordinalSuffix(V.SecExp.units[type].created)} ${V.SecExp.units[type].defaultName}` }; if (type !== "bots") { Object.assign(newUnit, { training: 0, cyber: 0, medics: 0, SF: 0, commissars: 0, battlesFought: 0, loyalty: jsRandom(40, 60), - ID: genID(), }); newUnit.troops = Math.min(newUnit.maxTroops, unitFree(type).print()); unitFree(type).remove(newUnit.troops); } - newUnit.platoonName = `${ordinalSuffix(V.SecExp.units[type].created)} ${V.SecExp.units[type].defaultName}`; + genID(newUnit, type); V.SecExp.units[type].squads.push(newUnit); } } @@ -152,7 +151,7 @@ App.SecExp.unit = (function() { let linkArray = []; linkArray.push("Default unit name:", App.UI.DOM.makeTextBox(V.SecExp.units[type].defaultName, str => { V.SecExp.units[type].defaultName = str; App.UI.reload(); })); - if (type !== "bots" && unitFree(type).canUpgrade() && App.SecExp.battle.activeUnits() < App.SecExp.battle.maxUnits()) { + if (unitFree(type).canUpgrade() && App.SecExp.battle.activeUnits() < App.SecExp.battle.maxUnits()) { linkArray.push(App.UI.DOM.link(`Form a new unit`, () => { generate(type, true); if (type === "bots") { @@ -176,20 +175,18 @@ App.SecExp.unit = (function() { App.UI.DOM.appendNewElement("div", unitDetail, describe(unit, type)); linkArray.push(App.UI.DOM.makeTextBox(unit.platoonName, str => { unit.platoonName = str; App.UI.reload(); })); - if (type !== "bots") { - linkArray.push(App.UI.DOM.link(`Disband the unit`, () => { - unitFree(type).add(unit.troops); - V.SecExp.units[type].squads.deleteAt(i); - V.SecExp.battles.lastSelection.deleteAt(i); - App.UI.reload(); - } - )); + linkArray.push(App.UI.DOM.link(`Disband the unit`, () => { + unitFree(type).add(unit.troops); + V.SecExp.units[type].squads.deleteAt(i); + V.SecExp.battles.lastSelection.deleteAt(i); + App.UI.reload(); } + )); if (unit.active === 1) { if (unit.troops < unit.maxTroops && unitFree(unit).canUpgrade()) { linkArray.push(App.UI.DOM.link(`Replenish unit`, () => { - replenish(unit, type); + replenish(unit); App.UI.reload(); } )); @@ -379,17 +376,16 @@ App.SecExp.unit = (function() { function replenishAll() { let el = document.createElement("div"); let woundedUnit = new Map([]); - for (const unit of Array.from(list().keys())) { - for (const squad of V.SecExp.units[unit].squads) { - if (squad.troops < squad.maxTroops && unitFree(unit).canUpgrade()) { - woundedUnit.set(unit); - } + for (const squad of App.SecExp.unit.squads()) { + const unit = checkID(squad.ID); + if (squad.troops < squad.maxTroops && unitFree(unit).canUpgrade()) { + woundedUnit.set(unit); } } if (woundedUnit.size > 0) { el.append(App.UI.DOM.link("Replenish all units", () => { for (const [u] of woundedUnit) { - V.SecExp.units[u].squads.forEach(s => replenish(s, u)); + V.SecExp.units[u].squads.forEach(s => replenish(s)); } App.UI.reload(); } @@ -404,28 +400,34 @@ App.SecExp.unit = (function() { * @returns {boolean} */ function isDeployed(unit) { - return (V.SecExp.war.type.includes("Attack") && unit.isDeployed === 1) || (V.SecExp.war.type.includes("Rebellion") && unit.active === 1 && !V.SecExp.war.rebellingID.includes(unit.ID)); + return V.SecExp.war && (V.SecExp.war.type.includes("Attack") && V.SecExp.war.deployed.includes(unit.ID)) || (V.SecExp.war.type.includes("Rebellion") && unit.active === 1 && !V.SecExp.war.rebellingID.includes(unit.ID)); } /** * @param {FC.SecExp.PlayerHumanUnitData} input - * @param {FC.SecExp.PlayerHumanUnitTypeMod} unitType * @param {boolean} inBattle - if true appends a deploy/recall link to the description, allowing for [input] to be deployed/recalled. * @returns {HTMLDivElement} */ - function describe(input, unitType, inBattle = false) { + function describe(input, inBattle = false) { const brief = V.SecExp.settings.unitDescriptions; + const unitType = App.SecExp.unit.checkID(input.ID); let el = new DocumentFragment(); if (inBattle) { const canDeploy = !isDeployed(input) && App.SecExp.battle.deployableUnits() > 0; el.append(App.UI.DOM.link(`(${canDeploy ? 'Deploy' : 'Recall'}) `, () => { - input.isDeployed = canDeploy ? 1 : 0; + if (canDeploy) { + V.SecExp.war.deployed.push(input.ID); + } else { + V.SecExp.war.deployed.deleteAt(s => s === input.ID); + } V.SecExp.war.saveValid = 0; App.UI.reload(); } )); } + App.UI.DOM.appendNewElement("span", el, `${input.platoonName}`, "bold"); + App.UI.DOM.appendNewElement("span", el, `${!brief ? ``:`. `} `); App.UI.DOM.appendNewElement("span", el, `${input.platoonName}`, "bold"); App.UI.DOM.appendNewElement("span", el, `${!brief ? ``:`. `} `); @@ -593,24 +595,40 @@ App.SecExp.unit = (function() { function squads(type = '') { let array = Object.values(V.SecExp.units).map(s => s.squads).flatten(); switch(type) { - case "human": - array = []; - for (const unit of Array.from(list().keys()).slice(1)) { - for (const squad of V.SecExp.units[unit].squads) { - array.push(squad); - } - } + case "human": return array.filter(s => checkID(s.ID) !== "bots"); } return array; } /** Generate a unit ID for a new unit - * @returns {number} + * @param {FC.SecExp.PlayerHumanUnitData} squad + * @param {FC.SecExp.PlayerHumanUnitTypeMod} unit + * @returns {void} */ - function genID() { - return Math.max( - squads("human").map(u => u.ID).reduce((acc, cur) => Math.max(acc, cur), 0) - ) + 1; + function genID(squad, unit) { + if (V.SecExp.units[unit].squads.filter(s => !s.ID.isBetween(list().get(unit).baseID, list().get(unit).maxID, true)).length) { + const oldID = squad.ID; + let location; + const newID = list().get(unit).baseID + (unit === 'bots' ? 0 : oldID); + if (V.SecExp.battles.lastSelection && V.SecExp.battles.lastSelection.includes(oldID) || V.lastSelection && V.lastSelection.includes(oldID)) { + if (V.SecExp.battles.lastSelection) { + location = V.SecExp.battles.lastSelection.indexOf(oldID); + } else { + location = V.lastSelection.indexOf(oldID); + V.SecExp.battles.lastSelection = []; + } + V.SecExp.battles.lastSelection[location] = newID; + } + squad.ID = newID; + } else if (!jsDef(squad.ID)) { + if (V.SecExp.units[unit].squads.length === 0) { + squad.ID = list().get(unit).baseID; + } else { + squad.ID = Math.max( + V.SecExp.units[unit].squads.map(u => u.ID).reduce((acc, cur) => Math.max(acc, cur), 0) + ) + 1; + } + } } /** performs operations on a unit @@ -692,10 +710,10 @@ App.SecExp.unit = (function() { /** Replenishes a unit if needed * helper function, not callable externally * @param {FC.SecExp.PlayerHumanUnitData} squad - * @param {FC.SecExp.PlayerHumanUnitTypeMod} type */ - function replenish(squad, type) { + function replenish(squad) { const oldTroops = squad.troops; + const type = checkID(squad.ID); if (type !== "bots") { if (unitFree(type).print() >= squad.maxTroops - squad.troops) { @@ -718,7 +736,7 @@ App.SecExp.unit = (function() { * @param {object} [unit] the unit to be checked * @param {string} [type] the type of unit to be checked */ - function bulkUpgrade(unit, type) { + function bulkUpgrade(unit, type) { unit = Array.isArray(unit) ? unit : [unit]; let el = document.createElement("a"); @@ -800,11 +818,11 @@ App.SecExp.getUnit = function(type, index) { case "bots": return new App.SecExp.DroneUnit(V.SecExp.units.bots.squads[index], App.SecExp.BaseDroneUnit); case "militia": - return new App.SecExp.HumanUnit(V.SecExp.units.militia.squads[index], App.SecExp.BaseMilitiaUnit, type); + return new App.SecExp.HumanUnit(V.SecExp.units.militia.squads[index], App.SecExp.BaseMilitiaUnit); case "slaves": - return new App.SecExp.HumanUnit(V.SecExp.units.slaves.squads[index], App.SecExp.BaseSlaveUnit, type); + return new App.SecExp.HumanUnit(V.SecExp.units.slaves.squads[index], App.SecExp.BaseSlaveUnit); case "mercs": - return new App.SecExp.HumanUnit(V.SecExp.units.mercs.squads[index], App.SecExp.BaseMercUnit, type); + return new App.SecExp.HumanUnit(V.SecExp.units.mercs.squads[index], App.SecExp.BaseMercUnit); default: throw Error(`Unknown unit type: ${type}`); } @@ -1184,7 +1202,7 @@ App.SecExp.Unit = class SecExpUnit { } /** @abstract - * @returns {string} */ + * @returns {DocumentFragment} */ printStats() { throw Error("derive me"); } @@ -1227,12 +1245,10 @@ App.SecExp.DroneUnit = class SecExpDroneUnit extends App.SecExp.Unit { App.SecExp.HumanUnit = class SecExpHumanUnit extends App.SecExp.Unit { /** @param {FC.SecExp.PlayerHumanUnitData} data * @param {BaseUnit} baseUnit - * @param {string} descriptionType */ - constructor(data, baseUnit, descriptionType) { + constructor(data, baseUnit) { super(data, baseUnit); this._data = data; // duplicate assignment, just for TypeScript - this._descType = descriptionType; } get attack() { @@ -1259,6 +1275,7 @@ App.SecExp.HumanUnit = class SecExpHumanUnit extends App.SecExp.Unit { printStats() { let r = new DocumentFragment(); + this._descType = App.SecExp.unit.checkID(this._data.ID); App.UI.DOM.appendNewElement("div", r, `${this._descType} base attack: ${this._baseUnit.attack} (After modifiers: ${Math.trunc(this.attack)})`); App.UI.DOM.appendNewElement("div", r, `${this._descType} base defense: ${this._baseUnit.defense} (After modifiers: ${Math.trunc(this.defense)})`); if (this._data.equip > 0) { diff --git a/src/Mods/SecExp/js/authorityReport.js b/src/Mods/SecExp/js/authorityReport.js index f22585f97a7f4d06223da43e25cd1c3d2b828983..4a8b8f87d4a88b91262111408b4bdc3126a20ada 100644 --- a/src/Mods/SecExp/js/authorityReport.js +++ b/src/Mods/SecExp/js/authorityReport.js @@ -159,7 +159,7 @@ App.SecExp.authorityReport = function() { } else { r.push(`This week <span class="red">authority has decreased.</span>`); } - V.SecExp.core.authority = Math.trunc(Math.clamp(V.SecExp.core.authority + authGrowth, 0, 20000)); + App.SecExp.authorityX(authGrowth); if (V.SecExp.settings.rebellion.enabled === 1) { const authorityEffects = function(group) { @@ -305,7 +305,8 @@ App.SecExp.authorityReport = function() { } } - Math.clamp(slave, 0, 95); Math.clamp(citizen, 0, 95); // rolls to see if event happens - there's always a min 5% chance nothing happens + slave = Math.clamp(slave, 0, 95); + citizen = Math.clamp(citizen, 0, 95); // rolls to see if event happens - there's always a min 5% chance nothing happens const roll = random(1, slave + citizen); slave = Math.trunc(slave * V.SecExp.settings.rebellion.speed); citizen = Math.trunc(citizen * V.SecExp.settings.rebellion.speed); if (V.SecExp.buildings.riotCenter && V.SecExp.buildings.riotCenter.brainImplant === 106) { diff --git a/src/Mods/SecExp/js/edicts.js b/src/Mods/SecExp/js/edicts.js index 3d93fd00d993adf1189ff29dbf3edba65ce8c679..09c5b0d60c912d332170d65cfc6d086f0c1be99f 100644 --- a/src/Mods/SecExp/js/edicts.js +++ b/src/Mods/SecExp/js/edicts.js @@ -23,7 +23,7 @@ App.SecExp.edicts = function() { () => { if (detail.implement[i].conditional) { cashX(-5000, "edicts"); - V.SecExp.core.authority -= 1000; + App.SecExp.authorityX(-1000); } current.set(); App.UI.reload(); @@ -962,7 +962,6 @@ App.SecExp.edicts = function() { if (count("battles") > 0 || count("rebellions") > 0 || V.mercenaries > 0) { tabBar.addTab("Military", "Military", Military()); } - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority, 0, 20000); if (V.SecExp.core.authority < 1000) { App.UI.DOM.appendNewElement("div", node, "Not enough Authority.", ["red", "note"]); } diff --git a/src/Mods/SecExp/js/secExp.js b/src/Mods/SecExp/js/secExp.js index b7ab03653c9b313cb5a7300977484dadbe726609..521cceb7cd373de38d86033e3730176235e6f582 100644 --- a/src/Mods/SecExp/js/secExp.js +++ b/src/Mods/SecExp/js/secExp.js @@ -1,3 +1,10 @@ +/** Takes the passed input and returns a clamped V.SecExp.core.authority + * @param {number} input defaults to zero + */ +App.SecExp.authorityX = function(input = 0) { + V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + input, 0, 20000); +}; + App.SecExp.generator = (function() { return { attack, diff --git a/src/Mods/SecExp/js/secExpBC.js b/src/Mods/SecExp/js/secExpBC.js index 8a869cbb91bc64a63fe41f3f7dbd86d0cfe35dc5..4ac09e38c24dfa6842b63a18581684b184dacaa6 100644 --- a/src/Mods/SecExp/js/secExpBC.js +++ b/src/Mods/SecExp/js/secExpBC.js @@ -111,12 +111,15 @@ App.SecExp.generalBC = function() { if (V.SF.Active < 1) { u.SF = 0; } + } - if (unit !== 'bots' && u.platoonName.contains('undefined')) { - u.platoonName = u.platoonName.replace('undefined', data.defaultName); - } - u.equip = u.equip || 0; + if (unit !== 'bots' && u.platoonName.contains('undefined')) { + u.platoonName = u.platoonName.replace('undefined', data.defaultName); } + + App.SecExp.unit.genID(u, unit); + u.equip = u.equip || 0; + delete u.isDeployed; }); } diff --git a/src/Mods/SecExp/js/securityReport.js b/src/Mods/SecExp/js/securityReport.js index c683497f166d2d42bffbc6357a5242532856ad65..ca7f03a1bf6ee6470128ba22424032631f932802 100644 --- a/src/Mods/SecExp/js/securityReport.js +++ b/src/Mods/SecExp/js/securityReport.js @@ -454,10 +454,8 @@ App.SecExp.securityReport = function() { } if (activeUnits > 0) { // loyalty and training - for (const [unit] of Array.from(App.SecExp.unit.list()).slice(1)) { - for (const squad of V.SecExp.units[unit].squads) { - r.push(App.SecExp.humanLoyaltyChanges(squad, unit)); - } + for (const squad of App.SecExp.unit.squads("human")) { + r.push(App.SecExp.humanLoyaltyChanges(squad)); } } } diff --git a/src/Mods/SecExp/potentialToDo.txt b/src/Mods/SecExp/potentialToDo.txt index 115cabfcfd0894eb133db51d6e2453a60f33cd5b..869d8e71ab6d3cda28dd801c8df7bf6258b92a32 100644 --- a/src/Mods/SecExp/potentialToDo.txt +++ b/src/Mods/SecExp/potentialToDo.txt @@ -3,10 +3,6 @@ Hexall90's last merged commit: 52dde0b3 - Can other Arcologies assist with the attacks from your Arcology? Like if you're being attacked by raiders they send some rapid-deployment forces and help you but when it's the Old world they wouldn't in fear of being criticize or something - Or sending your troops to help other arcologies to earn reputation, POW as menials and captured military assets for cash, to boost cultural exchange and economic development. Or choosing not to send troops (if rival) to lower its value and to preverse your cultural independance. -- Remove unit.isDeployed as it is only used in battles and add the IDs to an array. - This would require that units have unique IDs (e.g bots: -1 -> 99, militia: 100 -> 199, slaves: 200 -> 299, mercs: 300 - 399, etc). - It would also allow for _*RebelledID to potentially be removed. - - While at it something for barracks(general? commissar?) and riot center([Insert player title there]'s Will?? Big Sister? ) too would be nice - While at it decoupling of propaganda slave and recruiter when? - Would it be possible to add option for assigning hacker to security HQ that would work similarly to giving office for recruiter in propaganda hub? Preferably with smiling man slave giving extra bonuses there @@ -34,7 +30,6 @@ Hexall90's last merged commit: 52dde0b3 - Suggestion - Arcology Conquest - https://gitgud.io/pregmodfan/fc-pregmod/issues/760 - Does forcing every citizen to be in military raise appeal of slaves that know how to fight? - And if I am a master tactician, maybe I could get an estimate how effective the various tactics could be? -- Ability to create more drone squads. - Increase base maximum units to 18, 20 with SF. - Suggestion - Gradual Battle Frequency - https://gitgud.io/pregmodfan/fc-pregmod/issues/1245#note_82504 - Option to send slaves into military unit to gain some nice scars and experience wold be nice (with retrieval). diff --git a/src/Mods/SpecialForce/SpecialForce.js b/src/Mods/SpecialForce/SpecialForce.js index dec0588b4e56f2f5783b16984504f59eb3a38aed..35061476c362896691b1ba68b9b98d585d6110f3 100644 --- a/src/Mods/SpecialForce/SpecialForce.js +++ b/src/Mods/SpecialForce/SpecialForce.js @@ -228,10 +228,10 @@ App.SF.fsIntegration = (function() { let perimeter; let roleplaying; let colonel; + const hyperPreg0 = V.seeHyperPreg > 0 ? 'hyper-' : ''; + const hyperPreg1 = V.seeHyperPreg > 0 ? 'hyper' : ''; switch(x) { case 'Repopulation': - const hyperPreg0 = V.seeHyperPreg > 0 ? 'hyper-' : ''; - const hyperPreg1 = V.seeHyperPreg > 0 ? 'hyper' : ''; dec = `Repopulation efforts: focus on mass breeding in order to repopulate the future world.`; gift = `To ease The Colonel's hostility towards Repopulationism, you have gifted her with a very large and unusually shaped armored Command Vehicle; it is designed to fully support The Colonel herself and all of her Command & Communications needs in the field. Given its one-occupant design, it comes well supported with its global-uplink communications array, onboard super computer, shock and blast resistant interior foam padding, adjustable body harness, extended life support & CBRN protection suites, advanced waste removal systems, and its in-built pregnancy-support systems. Should she ever decide to take to the battlefield whilst ${hyperPreg1} pregnant herself, there is no vehicle in the world she would prefer over this one. Despite not yet being ${hyperPreg0}pregnant, she is impressed by how roomy, comfortable, and capable her new Command Vehicle is, despite its large profile. You see her using it quite a bit too, despite her body still being much smaller than the unit was designed for.`; foods = `The troops enjoy their Repopulationist food, which tastes better than the old food, while being completely unaware that they increase semen production and promote ovulation.`; @@ -1233,9 +1233,7 @@ App.SF.AAR = function(endWeekCall = 1) { Multiplier.troop += V.SF.ArmySize / 200; upkeep += V.SF.ArmySize * 33 * 1/N0; if (V.secExpEnabled > 0 && endWeekCall > 0) { - V.SecExp.core.authority += 25 * (Math.ceil(V.SF.ArmySize / 200)); - V.SecExp.core.authority += size * 10; - Math.clamp(V.SecExp.core.authority, 0, 20000); + App.SecExp.authorityX((25 * (Math.ceil(V.SF.ArmySize / 200))) + size * 10); } } @@ -1582,7 +1580,7 @@ App.SF.progress = function(x, max) { let z; let i; if (max === undefined) { - Math.clamp(x, 0, 10); + x = Math.clamp(x, 0, 10); if (App.SF.unlocked.secondTier() === false) { z = 5 - x; for (i = 0; i < x; i++) { @@ -1604,7 +1602,7 @@ App.SF.progress = function(x, max) { } } } else { - Math.clamp(x, 0, max); + x = Math.clamp(x, 0, max); x = Math.floor(10 * x / max); z = 10 - x; for (i = 0; i < x; i++) { diff --git a/src/art/vector/VectorArtJS.js b/src/art/vector/VectorArtJS.js index 507ed00a944e734503190c94e0e5c5bbe7b50e32..972d206f27b63d1c798f8ae116fa7d52212b5dda 100644 --- a/src/art/vector/VectorArtJS.js +++ b/src/art/vector/VectorArtJS.js @@ -2042,10 +2042,10 @@ App.Art.vectorArtElement = (function() { break; } switch (slave.earT) { - case "neko": + case "cat": svgQueue.add("Art_Vector_Cat_Ear_Back"); break; - case "kit": + case "fox": svgQueue.add("Art_Vector_Fox_Ear_Back"); break; } diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js index 6c33dc2bad1a0f1b4388e0dba9409b4b087bb80e..7234c01f9bf3dbebad6836909339601994b16488 100644 --- a/src/data/backwardsCompatibility/backwardsCompatibility.js +++ b/src/data/backwardsCompatibility/backwardsCompatibility.js @@ -6,23 +6,15 @@ App.Update.autoshred = function(node) { Object.getOwnPropertyNames(App.Data.resetOnNGPlus) ) ); - let extraCount = 0; for (const v in V) { if (!set.has(v)) { if (V.debugMode) { - if (!App.Data.ignoreGameStateVariables.includes(v)) { - console.log("Not on whitelist, removed:", "V." + v + ":", V[v]); - } else { - extraCount++; - } + console.log("Not on whitelist, removed:", "V." + v + ":", V[v]); } delete V[v]; } } - if (extraCount !== 0) { - console.log(`and ${extraCount} more`); - } node.append(`Done!`); }; diff --git a/src/data/backwardsCompatibility/updateSlaveObject.js b/src/data/backwardsCompatibility/updateSlaveObject.js index 468e070aafe771687413089cda7e443890742383..a970e9b4f597f281e5069e2fb645fbb3ecb19a23 100644 --- a/src/data/backwardsCompatibility/updateSlaveObject.js +++ b/src/data/backwardsCompatibility/updateSlaveObject.js @@ -1020,14 +1020,39 @@ App.Update.Slave = function(slave, genepool = false) { } else { WombInit(slave); } - if (V.releaseID < 1036) { - for (let pmw = 0; pmw < slave.womb.length; pmw++) { - if (slave.womb[pmw].genetics.mother !== slave.womb[pmw].motherID || slave.womb[pmw].genetics.father !== slave.womb[pmw].fatherID) { - slave.womb[pmw].genetics = generateGenetics(slave, slave.womb[pmw].fatherID, pmw); + if (V.releaseID <= 1141) { + const animalMap = new Map([ + ["neko", "cat"], + ["inu", "dog"], + ["kit", "fox"], + ["ushi", "cow"], + ["usagi", "rabbit"], + ["risu", "squirrel"], + ["uma", "horse"], + ]); + const tail = animalMap.get(slave.tailShape); + if (tail) { + slave.tailShape = tail; + } + const earShape = animalMap.get(slave.earShape); + if (earShape) { + slave.earShape = earShape; + } + const earT = animalMap.get(slave.earT); + if (earT) { + slave.earT = earT; + } + + if (V.releaseID < 1036) { + for (let pmw = 0; pmw < slave.womb.length; pmw++) { + if (slave.womb[pmw].genetics.mother !== slave.womb[pmw].motherID || slave.womb[pmw].genetics.father !== slave.womb[pmw].fatherID) { + slave.womb[pmw].genetics = generateGenetics(slave, slave.womb[pmw].fatherID, pmw); + } } } } + if (!jsDef(slave.inbreedingCoeff)) { slave.inbreedingCoeff = ibc.coeff(slave); slave.womb.forEach(f => { diff --git a/src/endWeek/economics/neighborsDevelopment.js b/src/endWeek/economics/neighborsDevelopment.js index ea9b60c11841df8f52ea0e3f1aaf8c52160ce126..1d801ee963c1c82f25e754267b301b905ba5d9e8 100644 --- a/src/endWeek/economics/neighborsDevelopment.js +++ b/src/endWeek/economics/neighborsDevelopment.js @@ -330,7 +330,7 @@ App.EndWeek.neighborsDevelopment = function() { redHanded = 1; repX(forceNeg(random(100, 200)), "war"); if (V.secExpEnabled > 0) { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - random(100, 500) * V.arcologies[0].CyberEconomic, 0, 20000); + App.SecExp.authorityX(random(-100, -500) * V.arcologies[0].CyberEconomic); V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + random(10, 25), 0, 100); } V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity, 1, V.AProsperityCap); @@ -377,7 +377,7 @@ App.EndWeek.neighborsDevelopment = function() { redHanded = 1; repX(forceNeg(random(100, 200)), "war"); if (V.secExpEnabled > 0) { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - random(100, 500) * V.arcologies[0].CyberReputation, 0, 20000); + App.SecExp.authorityX(random(-100, -500) * V.arcologies[0].CyberReputation); V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + random(10, 25), 0, 100); } V.arcologies[0].prosperity = Math.clamp(V.arcologies[0].prosperity, 1, 300); diff --git a/src/endWeek/economics/persBusiness.js b/src/endWeek/economics/persBusiness.js index 9f5fea1e3302625fe15c5c9e464e738d4214610f..51628d2873e541d01a610dd7ca2deed0c7de7040 100644 --- a/src/endWeek/economics/persBusiness.js +++ b/src/endWeek/economics/persBusiness.js @@ -514,7 +514,7 @@ App.EndWeek.personalBusiness = function() { V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow - 25, 0, 100); } if (V.SecExp.proclamation.currency === "authority") { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - 2000, 0, 20000); + App.SecExp.authorityX(-2000); } else if (V.SecExp.proclamation.currency === "reputation") { repX(Math.clamp(V.rep - 2000, 0, 20000), "personalBusiness"); } else { @@ -785,7 +785,7 @@ App.EndWeek.personalBusiness = function() { if (V.secExpEnabled > 0) { X = 1; r.push(`<span class="red">authority,</span>`); - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - random(100, 500), 0, 20000); + App.SecExp.authorityX(random(-100, -500)); r.push(`<span class="red">crime rate</span>`); V.SecExp.core.crimeLow = Math.clamp(V.SecExp.core.crimeLow + random(10, 25), 0, 100); r.push(`and`); @@ -877,7 +877,7 @@ App.EndWeek.personalBusiness = function() { r.push(`You are selling the data collected by your security department, which earns a discreet sum of <span class="yellowgreen">${cashFormat(dataGain)}.</span>`); cashX(dataGain, "personalBusiness"); r.push(`Many of your citizens are not enthusiastic of this however, <span class="red">damaging your authority.</span>`); - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - 50, 0, 20000); + App.SecExp.authorityX(-50); } } diff --git a/src/endWeek/reports/clinicReport.js b/src/endWeek/reports/clinicReport.js index 797ebee9b36f9f8f0a999cb9bd0b53a21a472a53..707c7c35d0caa4273f11a0b1957d3bc592cc08f9 100644 --- a/src/endWeek/reports/clinicReport.js +++ b/src/endWeek/reports/clinicReport.js @@ -144,7 +144,7 @@ App.EndWeek.clinicReport = function() { r.push(`catch a little of it in a cup for further "analysis",`); } r.push(`<span class="fetish inc">further feeding ${his} cum fetishism.</span>`); - Math.clamp(S.Nurse.fetishStrength += 4, 0, 100); + S.Nurse.fetishStrength = Math.clamp(S.Nurse.fetishStrength + 4, 0, 100); } } } @@ -156,11 +156,11 @@ App.EndWeek.clinicReport = function() { slave.lactationDuration = 2; if (jsRandom(1, 100) > 65 && canTaste(S.Nurse)) { if (S.Nurse.fetish === "none") { - r.push(`Those lovely globes are just too tantilizing for ${him} to resist, <span class="fetish gain">${he} can't help but take a drink ${himself}.</span>`); + r.push(`Those lovely globes are just too tantalizing for ${him} to resist, <span class="fetish gain">${he} can't help but take a drink ${himself}.</span>`); S.Nurse.fetish = "boobs"; } else if (S.Nurse.fetish === "boobs") { r.push(`${He} takes the opportunity to <span class="fetish gain">feed ${his} breast fetishism</span> by getting up close and personal with ${his2} prized udders.`); - Math.clamp(S.Nurse.fetishStrength += 4, 0, 100); + S.Nurse.fetishStrength = Math.clamp(S.Nurse.fetishStrength + 4, 0, 100); } } } diff --git a/src/endWeek/saAgent.js b/src/endWeek/saAgent.js index 48d4ef3b22e30244452157276693d2a29de6a1cd..168cfe4699bb279de0a8b137c70bd6a8128a5da9 100644 --- a/src/endWeek/saAgent.js +++ b/src/endWeek/saAgent.js @@ -183,8 +183,7 @@ App.SlaveAssignment.agent = function(slave) { } else if (slave.pregSource > 0) { const babyDaddy = findFather(slave.pregSource); if (babyDaddy) { - const adjust = babyDaddy.counter.slavesKnockedUp++; - adjustFatherProperty(babyDaddy, "slavesKnockedUp", adjust); + babyDaddy.counter.slavesKnockedUp++; } } } diff --git a/src/endWeek/saDiet.js b/src/endWeek/saDiet.js index 99b03b77733fa62fcf2f37c6975a68854f4d05de..36004ec460259b5e839477137f77fd62a236f84f 100644 --- a/src/endWeek/saDiet.js +++ b/src/endWeek/saDiet.js @@ -1465,12 +1465,12 @@ App.SlaveAssignment.diet = (function() { if (V.geneticMappingUpgrade >= 1) { r.push(`${His} body <span class="lime">passively loses muscle mass</span> due to ${his} myotonic dystrophy.`); } - Math.clamp(slave.muscles -= 3, -100, 100); + slave.muscles = Math.clamp(slave.muscles - 3, -100, 100); } else if (slave.geneticQuirks.mGain === 2 && slave.muscles < 100 && slave.weight >= -95) { if (V.geneticMappingUpgrade >= 1) { r.push(`${His} body <span class="lime">passively builds muscle mass</span> due to ${his} myotonic hypertrophy.`); } - Math.clamp(slave.muscles += 3, -100, 100); + slave.muscles = Math.clamp(slave.muscles + 3, -100, 100); } } if (!["fattening", "restricted", "slimming"].includes(slave.diet)) { @@ -1483,7 +1483,7 @@ App.SlaveAssignment.diet = (function() { r.push(`hypoleptinemia.`); } } - Math.clamp(slave.weight -= 3, -100, 200); + slave.weight = Math.clamp(slave.weight - 3, -100, 200); } else if (slave.weightDirection === 1 && slave.weight < 200) { if (V.geneticMappingUpgrade >= 1) { r.push(`${His} body <span class="lime">aggressively stores fat</span> due to ${his}`); @@ -1493,7 +1493,7 @@ App.SlaveAssignment.diet = (function() { r.push(`hyperleptinemia.`); } } - Math.clamp(slave.weight += 3, -100, 200); + slave.weight = Math.clamp(slave.weight + 3, -100, 200); } } } @@ -1541,11 +1541,11 @@ App.SlaveAssignment.diet = (function() { if (V.cockFeeder !== 1) { if (slave.dietCum === 2) { r.push(`${He} has trouble keeping ${his} disgusting food down; ${he} loses weight.`); - Math.clamp(slave.weight -= 5, -100, 200); + slave.weight = Math.clamp(slave.weight - 5, -100, 200); } else if (slave.dietCum === 1) { if (random(1, 3) === 3) { r.push(`${He} has trouble keeping ${his} disgusting food down; ${he} loses weight.`); - Math.clamp(slave.weight -= 5, -100, 200); + slave.weight = Math.clamp(slave.weight - 5, -100, 200); if (boobSize >= 200 * gigantomastiaMod && gigantomastiaMod !== 3) { r.push(`<span class="change negative">${His} breasts get a little smaller.</span>`); slave.boobs -= 50; diff --git a/src/endWeek/saPregnancy.js b/src/endWeek/saPregnancy.js index cf16e2fe56a2ede423e9983ac9fd08ac4d873bd3..ff59538e4f944fc12e47d72c011f43d255036708 100644 --- a/src/endWeek/saPregnancy.js +++ b/src/endWeek/saPregnancy.js @@ -84,8 +84,7 @@ App.SlaveAssignment.pregnancy = (function() { } else if (slave.pregSource > 0) { let babyDaddy = findFather(slave.pregSource); if (babyDaddy) { - let adjust = babyDaddy.counter.slavesKnockedUp++; - adjustFatherProperty(babyDaddy, "slavesKnockedUp", adjust); + babyDaddy.counter.slavesKnockedUp++; } } } diff --git a/src/endWeek/saRelationships.js b/src/endWeek/saRelationships.js index 25ede0cc13fb0cd90708b64b0d63faf9ddea46f2..7d24c7ffbfc9126490ed082148cf5b30cec67b5a 100644 --- a/src/endWeek/saRelationships.js +++ b/src/endWeek/saRelationships.js @@ -1193,11 +1193,13 @@ App.SlaveAssignment.relationships = (function() { /** @param {Map<string, Array<App.Entity.SlaveState>>} map * @param {App.Entity.SlaveState} relative */ function addToRelativeMap(map, relative) { - const term = relativeTerm(slave, relative); - if (!map.has(term)) { - map.set(term, [relative]); - } else { - map.get(term).push(relative); + const terms = relativeTerms(slave, relative); + for (const term in terms) { + if (!map.has(term)) { + map.set(term, [relative]); + } else { + map.get(term).push(relative); + } } } diff --git a/src/endWeek/saSocialEffects.js b/src/endWeek/saSocialEffects.js index 0235db59779ce8d1d35923340e96106107ee21f9..ea35e24658217fc9ae99823c6827f7848592b92c 100644 --- a/src/endWeek/saSocialEffects.js +++ b/src/endWeek/saSocialEffects.js @@ -708,7 +708,7 @@ App.SlaveAssignment.saSocialEffects = function(slave) { `Society <span class="green">approves</span> of ${slave.slaveName}'s graceful, girlish form; ${he} furthers the fashion for slaves like ${him}.`)); } let unslim = []; - if (slave.boobs > 400) { + if (slave.boobs > 650) { unslim.push("tits"); } if (slave.butt > 4) { diff --git a/src/events/PETS/petsStewardessBeating.js b/src/events/PETS/petsStewardessBeating.js index 950c73ca40823af73c14aa6565d5bab1b5404816..fb4129fca51bc25dc0d7d69621472aa2fe5fcbe5 100644 --- a/src/events/PETS/petsStewardessBeating.js +++ b/src/events/PETS/petsStewardessBeating.js @@ -8,7 +8,7 @@ App.Events.petsStewardessBeating = class petsStewardessBeating extends App.Event actorPrerequisites() { return [[ - s => s.assignment === Job.HOUSE + s => [Job.HOUSE, Job.QUARTER].includes(s.assignment) ]]; } @@ -51,7 +51,7 @@ App.Events.petsStewardessBeating = class petsStewardessBeating extends App.Event App.Events.addResponses(node, [ new App.Events.Result(`Fuck the servant's mouth as the stewardess beats ${him2}`, fuck), - new App.Events.Result(`Make sure your stewardess remembers ${his} place`, remembers) + new App.Events.Result(`Make sure your stewardess remembers ${his} place`, remembers, S.Stewardess.anus === 0 ? `This option will take ${S.Stewardess.slaveName}'s anal virginity` : ``) ]); function fuck() { @@ -63,11 +63,10 @@ App.Events.petsStewardessBeating = class petsStewardessBeating extends App.Event } else { r.push(`down ${his2} throat.`); } - r.push(`${He2} gags reflexively, jerking back, only to jerk forward again in automatic pain avoidance when ${S.Stewardess.slaveName} hits ${his2} already-sore buttocks yet again. ${He2}'s broken enough to understand that ${he2} needs to relax and let ${himself2} be abused, but ${his2} body's reflexive responses deny ${him2} the relief that might be given. The sadistic stewardess <span class="hotpink">comes twice</span> before you do, a deliciously aggressive expression on ${his} face. Poor ${subSlave.slaveName} staggers off coughing, promising to <span class="gold">never offend</span> again.`); + r.push(`${He2} gags reflexively, jerking back, only to jerk forward again in automatic pain avoidance when ${S.Stewardess.slaveName} hits ${his2} already-sore buttocks yet again. ${He2}'s broken enough to understand that ${he2} needs to relax and let ${himself2} be abused, but ${his2} body's reflexive responses deny ${him2} the relief that might be given. The sadistic stewardess <span class="devotion inc">comes twice</span> before you do, a deliciously aggressive expression on ${his} face. Poor ${subSlave.slaveName} staggers off coughing, promising to <span class="trust dec">never offend</span> again.`); S.Stewardess.devotion += 4; subSlave.trust -= 5; seX(subSlave, "oral", V.PC, "penetrative"); - V.slaves[V.slaveIndices[subSlave.ID]] = subSlave; App.Events.addParagraph(frag, r); return frag; } @@ -103,7 +102,7 @@ App.Events.petsStewardessBeating = class petsStewardessBeating extends App.Event } else { r.push(`${His} ass is so tight that fucking it standing is punishment enough.`); } - r.push(`${He} submissively takes ${his} buttfuck, and begs your forgiveness when ${he} feels you`); + r.push(`${He} submissively takes ${his} buttfuck, and <span class="devotion inc">begs your forgiveness</span> when ${he} feels you`); if (V.PC.dick === 0) { r.push(`shoot your load up ${his} ass.`); } else { diff --git a/src/events/RE/reArcologyInspection.js b/src/events/RE/reArcologyInspection.js index c5a55c83414f577891f3fc874b645bf014956477..d405fe6091652bba73e89bb2f4d2a43b2c8385f8 100644 --- a/src/events/RE/reArcologyInspection.js +++ b/src/events/RE/reArcologyInspection.js @@ -459,7 +459,7 @@ App.Events.REArcologyInspection = class REArcologyInspection extends App.Events. t.push(`Every arcology has its share of dissidents; it's one of the inevitable results of the semi-anarchic nature of the Free Cities. You have an opportunity to build your own <span class="reputation inc">reputation</span> and also help secure ${agent ? `${agent.slaveName}'s` : `your indirect`} <span class="darkviolet">authority</span> over ${arcology.name}, and it would be a shame to let it pass unheeded. You spend the rest of the day helping pass judgement, and even administer a few whippings and assfuckings yourself, just for good measure.`); repX(100, "event"); if (V.secExpEnabled) { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 250, 0, 20000); + App.SecExp.authorityX(250); } return t; } diff --git a/src/events/RETS/reSiblingTussle.js b/src/events/RETS/reSiblingTussle.js index 4840d7fab1e9b376d1c2fb6b5426876fd813131e..d28711bb6f1f85eccef9a0a922cb95355cdf5fc4 100644 --- a/src/events/RETS/reSiblingTussle.js +++ b/src/events/RETS/reSiblingTussle.js @@ -76,7 +76,7 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv App.Events.addParagraph(frag, t); t = []; - t.push(`${He2} gets the idea, and apologizes (with somewhat questionable sincerity) to ${sib1.slaveName} for ${his2} part in things. You turn to ${sib1.slaveName} expectantly, and ${he} also apologizes to ${his} ${relativeTerm(sib1, sib2)}, and then leans in for a ${V.seeIncest === 0 ? "chaste" : ""} kiss, as directed.`); + t.push(`${He2} gets the idea, and apologizes (with somewhat questionable sincerity) to ${sib1.slaveName} for ${his2} part in things. You turn to ${sib1.slaveName} expectantly, and ${he} also apologizes to ${his} ${siblingTerm(sib1, sib2)}, and then leans in for a ${V.seeIncest === 0 ? "chaste" : ""} kiss, as directed.`); App.Events.addParagraph(frag, t); if (V.seeIncest > 0 && (sib1.energy > 60 || sib2.energy > 60)) { @@ -88,28 +88,28 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv t = []; t.push(`As soon as they realize you aren't going to stop them, the kiss gets deeper and the groping starts in earnest.`); if (sib1.dick > 0) { - t.push(`${sib2.slaveName} immediately reaches for ${his2} ${relativeTerm(sib2, sib1)}'s dick, fondling and tugging on it to make sure it's ${canAchieveErection(sib1) ? `hard` : `as half-hard as it can be`} for what they're about to do.`); + t.push(`${sib2.slaveName} immediately reaches for ${his2} ${siblingTerm(sib2, sib1)}'s dick, fondling and tugging on it to make sure it's ${canAchieveErection(sib1) ? `hard` : `as half-hard as it can be`} for what they're about to do.`); } else if (sib1.vagina >= 0) { - t.push(`${sib2.slaveName} immediately reaches for ${his2} ${relativeTerm(sib2, sib1)}'s soft slit, rubbing and massaging it to make sure ${he}'s good and wet.`); + t.push(`${sib2.slaveName} immediately reaches for ${his2} ${siblingTerm(sib2, sib1)}'s soft slit, rubbing and massaging it to make sure ${he}'s good and wet.`); } else if (sib1.boobs >= 300) { - t.push(`${sib2.slaveName} moves ${his2}'s hands to ${his2} ${relativeTerm(sib2, sib1)}'s tits, pawing at them and playing with ${his} ${sib1.nipples} nipples.`); + t.push(`${sib2.slaveName} moves ${his2}'s hands to ${his2} ${siblingTerm(sib2, sib1)}'s tits, pawing at them and playing with ${his} ${sib1.nipples} nipples.`); } else { // null with no tits? c'mon... - t.push(`${sib2.slaveName} slides ${his2} hand between ${his2} ${relativeTerm(sib2, sib1)}'s thighs, massaging the soft sensitive skin of ${his} crotch.`); + t.push(`${sib2.slaveName} slides ${his2} hand between ${his2} ${siblingTerm(sib2, sib1)}'s thighs, massaging the soft sensitive skin of ${his} crotch.`); } // reverse order and add a couple extra options to try to avoid similar text t.push(`${sib1.slaveName}, meanwhile,`); if (sib2.boobs >= 300) { - t.push(`moves ${his}'s hands to ${his} ${relativeTerm(sib1, sib2)}'s tits, pawing at them and playing with ${his2} ${sib1.nipples} nipples.`); + t.push(`moves ${his}'s hands to ${his} ${siblingTerm(sib1, sib2)}'s tits, pawing at them and playing with ${his2} ${sib1.nipples} nipples.`); } else if (sib2.balls > 0 && sib1.dick > 0) { // avoid double dick groping by going for the balls - t.push(`grabs ${his} ${relativeTerm(sib1, sib2)}'s balls, fondling them to make sure ${he2}'s going to make plenty of cum.`); + t.push(`grabs ${his} ${siblingTerm(sib1, sib2)}'s balls, fondling them to make sure ${he2}'s going to make plenty of cum.`); } else if (sib2.dick > 0) { - t.push(`grabs ${his} ${relativeTerm(sib1, sib2)}'s dick, fondling and tugging on it to make sure it's ${canAchieveErection(sib2) ? `hard` : `at least semi-hard`} for what they're about to do.`); + t.push(`grabs ${his} ${siblingTerm(sib1, sib2)}'s dick, fondling and tugging on it to make sure it's ${canAchieveErection(sib2) ? `hard` : `at least semi-hard`} for what they're about to do.`); } else if (sib2.clit > 0 && sib1.vagina >= 0) { // avoid double vagina groping by going for the clit - t.push(`reaches for ${his} ${relativeTerm(sib1, sib2)}'s clit, rubbing and flicking it to make sure ${he2}'s ready for action.`); + t.push(`reaches for ${his} ${siblingTerm(sib1, sib2)}'s clit, rubbing and flicking it to make sure ${he2}'s ready for action.`); } else if (sib2.vagina >= 0) { - t.push(`reaches for ${his} ${relativeTerm(sib1, sib2)}'s soft slit, rubbing and massaging it to make sure ${he2}'s good and wet.`); + t.push(`reaches for ${his} ${siblingTerm(sib1, sib2)}'s soft slit, rubbing and massaging it to make sure ${he2}'s good and wet.`); } else { - t.push(`slides ${his} hand between ${his} ${relativeTerm(sib1, sib2)}'s thighs, massaging the soft sensitive skin of ${his2} crotch.`); + t.push(`slides ${his} hand between ${his} ${siblingTerm(sib1, sib2)}'s thighs, massaging the soft sensitive skin of ${his2} crotch.`); } t.push(`Once they're both good and ready, they get on a dressing bench in a classic 69 position, eagerly bringing each other to orgasm orally while you watch.`); seX(sib1, "oral", sib2, "oral"); @@ -144,7 +144,7 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv t = []; t.push(`Once you think your message has gotten through, you pull ${sib1.slaveName} off your ${V.PC.dick > 0 ? "dick" : "strap-on"} with a pop, pull ${sib2.slaveName} to ${his2} knees and repeat your chosen punishment on ${him2}.`); - t.push(`${sib1.slaveName} kneels obediently, crying quietly as you abuse ${his} ${relativeTerm(sib1, sib2)}.`); + t.push(`${sib1.slaveName} kneels obediently, crying quietly as you abuse ${his} ${siblingTerm(sib1, sib2)}.`); t.push(`When you've finished, you're sure both siblings are more likely to <span class="trust dec">respect the rules,</span> out of fear if nothing else.`); App.Events.addParagraph(frag, t); @@ -180,9 +180,9 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv } = getPronouns(loser).appendSuffix("L"); t = []; - t.push(`${winner.slaveName} has a clear advantage over ${hisW} ${relativeTerm(winner, loser)} and quickly subdues ${himL}.`); + t.push(`${winner.slaveName} has a clear advantage over ${hisW} ${siblingTerm(winner, loser)} and quickly subdues ${himL}.`); if (V.seeIncest && App.Utils.sexAllowed(winner, loser) && (winner.energy > 40 || winner.fetish === "dom")) { - t.push(`Excited by ${hisW} dominance of ${hisW} weaker ${relativeTerm(winner, loser)}, ${heW} quickly begins to take advantage of ${himL}.`); + t.push(`Excited by ${hisW} dominance of ${hisW} weaker ${siblingTerm(winner, loser)}, ${heW} quickly begins to take advantage of ${himL}.`); /** @type {FC.SlaveActs} */ let sexType = "oral"; if (canPenetrate(winner)) { @@ -196,7 +196,7 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv } } if (sexType === "oral") { - t.push(`${HeW} moves over ${loser.slaveName}'s prone body until ${heW}'s above ${hisL} mouth, and orders ${hisW} ${relativeTerm(winner, loser)} to orally service ${himW}.`); + t.push(`${HeW} moves over ${loser.slaveName}'s prone body until ${heW}'s above ${hisL} mouth, and orders ${hisW} ${siblingTerm(winner, loser)} to orally service ${himW}.`); } if (loser.fetish === "submissive") { t.push(`${loser.slaveName} enjoys being dominated anyway, so this situation is good for ${himL} too.`); @@ -207,12 +207,12 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv } seX(winner, "penetrative", loser, sexType); if (sexType === "vaginal") { - t.push(`${winner.slaveName} slides ${hisW} ${winner.dick > 3 ? "stiff prick" : "hard little dick"} into ${hisW} ${relativeTerm(winner, loser)}'s ${loser.vagina < 2 ? "tight slit" : "cunt"} and starts pounding away.`); + t.push(`${winner.slaveName} slides ${hisW} ${winner.dick > 3 ? "stiff prick" : "hard little dick"} into ${hisW} ${siblingTerm(winner, loser)}'s ${loser.vagina < 2 ? "tight slit" : "cunt"} and starts pounding away.`); if (canImpreg(loser, winner)) { knockMeUp(loser, 10, 0, winner.ID); } } else if (sexType === "anal") { - t.push(`${winner.slaveName} slides ${hisW} ${winner.dick > 3 ? "stiff prick" : "hard little dick"} into ${hisW} ${relativeTerm(winner, loser)}'s ${loser.anus < 2 ? "tight ass" : "well-used ass"} and starts pounding away.`); + t.push(`${winner.slaveName} slides ${hisW} ${winner.dick > 3 ? "stiff prick" : "hard little dick"} into ${hisW} ${siblingTerm(winner, loser)}'s ${loser.anus < 2 ? "tight ass" : "well-used ass"} and starts pounding away.`); if (canImpreg(loser, winner)) { knockMeUp(loser, 10, 1, winner.ID); } @@ -225,11 +225,11 @@ App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEv loser.fetishKnown = 1; loser.fetish = "submissive"; loser.fetishStrength = 20; - t.push(`${loser.slaveName}, meanwhile, really enjoyed the feeling of being dominated by ${hisL} ${relativeTerm(loser, winner)}, and has become <span class="fetish gain">submissive</span>.`); + t.push(`${loser.slaveName}, meanwhile, really enjoyed the feeling of being dominated by ${hisL} ${siblingTerm(loser, winner)}, and has become <span class="fetish gain">submissive</span>.`); } winner.energy += 4; } else { - t.push(`${HisW} ${relativeTerm(winner, loser)} really gets off on ${hisL} submissive position and comes before ${heW} does, and when they're finished they both leave satisfied, <span class="libido inc">heightening their sex drives</span>.`); + t.push(`${HisW} ${siblingTerm(winner, loser)} really gets off on ${hisL} submissive position and comes before ${heW} does, and when they're finished they both leave satisfied, <span class="libido inc">heightening their sex drives</span>.`); sib1.energy += 4; sib2.energy += 4; } diff --git a/src/events/intro/pcAppearance.js b/src/events/intro/pcAppearance.js index dffb91634035498e09b4961c29816bd7e1d510f4..b336a1531745a7538909aee07ffc09297cff494b 100644 --- a/src/events/intro/pcAppearance.js +++ b/src/events/intro/pcAppearance.js @@ -24,7 +24,7 @@ App.UI.Player.appearance = function(options, summary = false) { .addRange(165, 170, "<", "Average") .addRange(180, 185, "<", "Tall") .addRange(190, 185, ">=", "Very tall") - .addComment(`Average height for a ${V.PC.actualAge} year old is ${heightToEitherUnit(Height.mean(V.PC))}`); + .addComment(`Average height for a ${V.PC.physicalAge} year old is ${heightToEitherUnit(Height.mean(V.PC))}`); option = options.addCustomOption() .addButton( "Make average", @@ -487,6 +487,7 @@ App.UI.Player.design = function() { V.PC.physicalAge = V.PC.actualAge; V.PC.visualAge = V.PC.actualAge; + V.PC.ovaryAge = V.PC.actualAge; if (V.cheatMode) { options.addOption("Actual Age", "actualAge", V.PC).showTextBox(); options.addOption("Physical Age", "physicalAge", V.PC).showTextBox(); diff --git a/src/events/nonRandomEvent.js b/src/events/nonRandomEvent.js index 3b49dc3aed58b99db6eed37e7fafd359e72e7bda..58c3d83945f4355d9672f7fb0e573f8311fe19db 100644 --- a/src/events/nonRandomEvent.js +++ b/src/events/nonRandomEvent.js @@ -9,8 +9,7 @@ App.Events.getNonrandomEvents = function() { // instantiate all possible scheduled/nonrandom events here // ORDER MATTERS - if multiple events from this list trigger in a single week, they are executed in this order - new App.Events.rebellionOptions(), - new App.Events.attackOptions(), + new App.Events.conflictOptions(), new App.Events.SEPlayerBirth(), new App.Events.SEpcBirthday(), new App.Events.SEIndependenceDay(), @@ -194,7 +193,7 @@ App.Events.playNonrandomEvent = function() { // skip nonindividual event, go straight to individual event // week 0: 50% chance to skip with plot disabled, 62.5% with plot enabled. // reaches 0% chance at week 50 with plot disabled, or week 75 with plot enabled. - setTimeout(() => Engine.play("RIE Eligibility Check"), Engine.minDomActionDelay); + setTimeout(() => Engine.play("Random Individual Event"), Engine.minDomActionDelay); } else { setTimeout(() => Engine.play("Random Nonindividual Event"), Engine.minDomActionDelay); } diff --git a/src/events/randomEvent.js b/src/events/randomEvent.js index 252db36e07eb455148a522a414aea6d325fecd22..44e6aa566941e90b138a11767646600c16278ab0 100644 --- a/src/events/randomEvent.js +++ b/src/events/randomEvent.js @@ -242,13 +242,9 @@ App.Events.getNonindividualEvents = function() { new App.Events.RENickname().setType("RNIE"), - new App.Events.REBusyMasterSuite(), new App.Events.REMaleCitizenHookup(), new App.Events.RECitizenHookup(), new App.Events.REMaleArcologyOwner(), - new App.Events.RESEndowment(), - new App.Events.RESMove(), - new App.Events.RESSale(), new App.Events.REBoomerang(), new App.Events.REMilfTourist(), new App.Events.REAWOL(), @@ -259,6 +255,11 @@ App.Events.getNonindividualEvents = function() { new App.Events.REBrothelFunction(), new App.Events.RERebels(), + // schools + new App.Events.RESEndowment(), + new App.Events.RESMove(), + new App.Events.RESSale(), + // legendary new App.Events.RELegendaryFormerAbolitionist(), new App.Events.RELegendaryCow(), @@ -272,6 +273,7 @@ App.Events.getNonindividualEvents = function() { new App.Events.REBusyDairy(), new App.Events.REBusyBrothel(), new App.Events.REBusyServantsQuarters(), + new App.Events.REBusyMasterSuite(), // refs new App.Events.refsBaronDemand(), @@ -427,6 +429,11 @@ App.Events.playRandomIndividualEvent = function() { const d = document.createElement("div"); const clearEvent = () => { V.event = null; }; + const makeNoEvent = (/** @type {number} */ id) => { + const noEvent = new App.Events.RENoEvent(); + noEvent.actors = [id]; + return noEvent; + }; if (V.event instanceof App.Events.BaseEvent) { // we've deserialized a saved game with an event active, or a player has picked one, so just play it immediately @@ -465,7 +472,7 @@ App.Events.playRandomIndividualEvent = function() { $(linkList).empty(); const events = App.Events.getValidEvents(App.Events.getIndividualEvents(), eventSlave); if (events.length === 0) { - events.push(new App.Events.RENoEvent()); + events.push(makeNoEvent(eventSlave.ID)); } for (const event of events) { App.UI.DOM.appendNewElement("div", linkList, App.UI.DOM.passageLink(event.eventName, passage(), () => { V.event = event; })); @@ -482,7 +489,7 @@ App.Events.playRandomIndividualEvent = function() { // pick a random individual event for that slave. Use RE No Event if there are none she's eligible for. const events = App.Events.getValidEvents(App.Events.getIndividualEvents(), eventSlave); - const event = events.random() || new App.Events.RENoEvent(); + const event = events.random() || makeNoEvent(eventSlave.ID); // record the chosen event in 'current' (pre-play!) history as well as current state so that it will serialize out correctly if saved from this passage // WARNING: THIS IS ***NOT*** THE ACTIVE STATE PAGE! diff --git a/src/events/scheduled/assholeKnight.js b/src/events/scheduled/assholeKnight.js index f601f17b94009f3bec94cdcc942dd56ca8cf9cd4..9c5a195f978ebd339b869434825c509d5bbefea0 100644 --- a/src/events/scheduled/assholeKnight.js +++ b/src/events/scheduled/assholeKnight.js @@ -41,7 +41,7 @@ App.Events.SEAssholeKnight = class SEAssholeKnight extends App.Events.BaseEvent assholeKnight.devotion = random(-80, -60); assholeKnight.muscles = random(50, 75); assholeKnight.skill.combat = 1; - assholeKnight.behavioralFlaw = "malicious"; + assholeKnight.sexualFlaw = "malicious"; assholeKnight.behavioralQuirk = "none"; assholeKnight.trust = random(-30, -20); assholeKnight.boobs = 150; diff --git a/src/events/scheduled/seNicaeaAnnouncement.js b/src/events/scheduled/seNicaeaAnnouncement.js index c1a9c22b39df9e31dbb1c122c8b1c509f2510a3e..f94b6348cf30b35e340c5345439a20d6e2663b9b 100644 --- a/src/events/scheduled/seNicaeaAnnouncement.js +++ b/src/events/scheduled/seNicaeaAnnouncement.js @@ -53,7 +53,6 @@ App.Events.SENicaeaAnnouncement = class SENicaeaAnnouncement extends App.Events. } r.push(`comes to the point. "We need someone to call a council to lay down a strong foundation for the faith. We need a creed that can act as a uniting requisite to be a faithful slaveowner. You're one of the world's most prominent Chattel Religionist arcology owners. There's certainly no one who's ahead of you. I've spoken with our coreligionists, and those who aren't addicted to anarchy deputed me to ask you to convene it and host a council to establish the creed. If you're not willing, we'll find someone else."`); App.Events.addParagraph(node, r); - r = []; App.Events.addParagraph(node, [`Hosting a new religion's first synod will be expensive and time-consuming, but would give you considerable power to influence its outcome. However, if you decline, it will be held elsewhere without your input and will probably fracture the developing faith. Vocal opposition would likely rob it of force, leaving Chattel Religionism much as it is now, and prejudice the faithful against another attempt for many years.`]); diff --git a/src/events/scheduled/seNicaeaPreperation.js b/src/events/scheduled/seNicaeaPreperation.js index 453008f94ab3e2ed7cf97400102496f8de19a952..ee5f163ede5ed4e20d301f1124c824bccffd8b7c 100644 --- a/src/events/scheduled/seNicaeaPreperation.js +++ b/src/events/scheduled/seNicaeaPreperation.js @@ -35,7 +35,6 @@ App.Events.SENicaeaPreparation = class SENicaeaPreparation extends App.Events.Ba r.push(`Of course, ${S.Concubine.slaveName} is always there to help you relax when you need it.`); } App.Events.addParagraph(node, r); - r = []; App.Events.addParagraph(node, [`The first major choice you have to make concerns the seats on the Council. There are some Chattel Religionists you simply have to invite; they're so prominent that if you don't, the Council's decisions will lack any weight. But that doesn't mean you don't have room to manipulate the list of people invited in order to enhance your influence in the council. It wouldn't be difficult to selectively invite Chattel Religionist slaveowners that have the reputation to support attendance, who you know to be philosophically aligned with your thoughts about the faith. On the other hand, managing the seats on the Council with perfect impartiality would definitely help acceptance of any creed the Council agrees upon.`]); diff --git a/src/events/scheduled/sePCBirthday.js b/src/events/scheduled/sePCBirthday.js index 859be70ffc2d466faee3307255d638132bd1e466..5cfd6526af3b6e3307be5daa4690178314f91b5f 100644 --- a/src/events/scheduled/sePCBirthday.js +++ b/src/events/scheduled/sePCBirthday.js @@ -243,13 +243,13 @@ App.Events.pcBirthday = (function(events) { case "formal": repX(100, "event"); if (V.secExpEnabled) { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 300, 0, 20000); + App.SecExp.authorityX(300); } break; case "casual": repX(300, "event"); if (V.secExpEnabled) { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 100, 0, 20000); + App.SecExp.authorityX(100); } break; } diff --git a/src/events/scheduled/seRecruiterSuccess.js b/src/events/scheduled/seRecruiterSuccess.js index 44469990a03c46046f2d6a0568547c0095148a31..e7c5ac22785b5c0b1764c170b7d1260c23753604 100644 --- a/src/events/scheduled/seRecruiterSuccess.js +++ b/src/events/scheduled/seRecruiterSuccess.js @@ -53,7 +53,7 @@ App.Events.SERecruiterSuccess = class SERecruiterSuccess extends App.Events.Base slave.tonguePiercing = pierceMe(); slave.earPiercing = pierceMe(); slave.nosePiercing = pierceMe(); - slave.sexualFlaw = "hates women"; + slave.behavioralFlaw = "hates women"; } else if (V.recruiterTarget === "dissolute sissies") { slave = GenerateNewSlave("XY", {disableDisability: 1}); generateSalonModifications(slave); @@ -77,7 +77,7 @@ App.Events.SERecruiterSuccess = class SERecruiterSuccess extends App.Events.Base slave.navelPiercing = pierceMe(); slave.nipplesPiercing = pierceMe(); slave.clitPiercing = pierceMe(); - slave.sexualFlaw = "hates women"; + slave.behavioralFlaw = "hates women"; } else if (V.recruiterTarget === "expectant mothers") { slave = GenerateNewSlave("XX", {minAge: Math.max(V.fertilityAge, V.minimumSlaveAge), ageOverridesPedoMode: 1, disableDisability: 1}); generateSalonModifications(slave); diff --git a/src/facilities/nursery/utils/nurseryUtils.js b/src/facilities/nursery/utils/nurseryUtils.js index 616f13542dd39c5faecb42931acff435d3d1157c..249c1cf68b187eb24a13c1ad782d6117596658f8 100644 --- a/src/facilities/nursery/utils/nurseryUtils.js +++ b/src/facilities/nursery/utils/nurseryUtils.js @@ -178,7 +178,7 @@ App.Facilities.Nursery.infantToChild = function infantToChild(child) { if (child.race === "catgirl") { child.earImplant = 1; child.earShape = "none"; - child.earT = "neko"; + child.earT = "cat"; child.earTColor = child.hColor; } child.earPiercing = 0; @@ -344,7 +344,7 @@ App.Facilities.Nursery.infantToChild = function infantToChild(child) { child.tailShape = "none"; } else { child.tailColor = child.hColor; - child.tailShape = "neko"; + child.tailShape = "cat"; } child.tastes = 0; child.teeth = "baby"; diff --git a/src/facilities/surgery/surgeryPassageFaceAndHair.js b/src/facilities/surgery/surgeryPassageFaceAndHair.js index 5e1c69ab709dea8c2cf4d29cd79b2e29f481b748..94a72735b55ed9d95fd7fa373081f5f07e6747ee 100644 --- a/src/facilities/surgery/surgeryPassageFaceAndHair.js +++ b/src/facilities/surgery/surgeryPassageFaceAndHair.js @@ -397,16 +397,16 @@ App.UI.surgeryPassageHairAndFace = function(slave, refreshParent, cheat = false) } else { r.push(`a pair of ears grafted to the top of ${his} head.`); } - } else if (slave.earT === "neko") { - r.push(`a pair of cat ears adorning ${his} head.`); - } else if (slave.earT === "inu") { - r.push(`a pair of dog ears adorning ${his} head.`); - } else if (slave.earT === "kit") { - r.push(`a pair of fox ears adorning ${his} head.`); + } else if (slave.earT === "cat") { + r.push(`a pair of ${App.Utils.translate("cat")} ears adorning ${his} head.`); + } else if (slave.earT === "dog") { + r.push(`a pair of ${App.Utils.translate("dog")} ears adorning ${his} head.`); + } else if (slave.earT === "fox") { + r.push(`a pair of ${App.Utils.translate("fox")} ears adorning ${his} head.`); } else if (slave.earT === "tanuki") { r.push(`a pair of tanuki ears adorning ${his} head.`); - } else if (slave.earT === "usagi") { - r.push(`a pair of rabbit ears adorning ${his} head.`); + } else if (slave.earT === "rabbit") { + r.push(`a pair of ${App.Utils.translate("rabbit")} ears adorning ${his} head.`); } else { r.push(`You done goofed.`); r.push(`<span class="note">Report This</span>`); @@ -420,29 +420,29 @@ App.UI.surgeryPassageHairAndFace = function(slave, refreshParent, cheat = false) new App.Medicine.Surgery.Procedures.RemoveTopEars(slave), refresh, cheat)); } - if (slave.earT !== "neko") { + if (slave.earT !== "cat") { linkArray.push(App.Medicine.Surgery.makeLink( - new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "cat", "neko"), + new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "cat"), refresh, cheat)); } - if (slave.earT !== "inu") { + if (slave.earT !== "dog") { linkArray.push(App.Medicine.Surgery.makeLink( - new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "dog", "inu"), + new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "dog"), refresh, cheat)); } - if (slave.earT !== "kit") { + if (slave.earT !== "fox") { linkArray.push(App.Medicine.Surgery.makeLink( - new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "fox", "kit"), + new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "fox"), refresh, cheat)); } if (slave.earT !== "tanuki") { linkArray.push(App.Medicine.Surgery.makeLink( - new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "tanuki", "tanuki"), + new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "tanuki"), refresh, cheat)); } - if (slave.earT !== "usagi") { + if (slave.earT !== "rabbit") { linkArray.push(App.Medicine.Surgery.makeLink( - new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "rabbit", "usagi"), + new App.Medicine.Surgery.Procedures.TopEarReshape(slave, "rabbit"), refresh, cheat)); } if (slave.earTColor === "hairless") { diff --git a/src/gui/options/options.js b/src/gui/options/options.js index 43403dd282a35f7cf7c403cd561c5a47261d3d8c..017e3d3369e0658644230ec9f1b1f2f3a3a2404e 100644 --- a/src/gui/options/options.js +++ b/src/gui/options/options.js @@ -31,7 +31,6 @@ App.UI.optionsPassage = function() { function intro() { let links; let options; - let r; const el = new DocumentFragment(); options = new App.UI.OptionsGroup(); @@ -49,7 +48,7 @@ App.UI.optionsPassage = function() { ]); fillRow([ `This save was created using:`, - App.UI.DOM.makeElement("span", `FC version: ${V.ver},`, (V.ver !== App.Version.base)? "yellow": null), + App.UI.DOM.makeElement("span", `FC version: ${V.ver},`, (V.ver !== App.Version.base) ? "yellow" : null), ``, App.UI.DOM.makeElement("span", `build: ${V.releaseID}.`, (V.releaseID !== App.Version.release) ? "red" : null) ]); @@ -312,7 +311,7 @@ App.UI.optionsPassage = function() { links.push(App.UI.DOM.link( "Give Authority", () => { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority + 1000, 0, 20000); + App.SecExp.authorityX(1000); }, [], "Options" @@ -320,7 +319,7 @@ App.UI.optionsPassage = function() { links.push(App.UI.DOM.link( "Remove Authority", () => { - V.SecExp.core.authority = Math.clamp(V.SecExp.core.authority - 1000, 0, 20000); + App.SecExp.authorityX(-1000); }, [], "Options" @@ -947,7 +946,7 @@ App.Intro.contentAndFlavor = function(isIntro) { const descOptions = App.UI.DOM.makeElement("div"); r = []; r.push("More granular control of what appears is in"); - r.push(App.UI.DOM.link("Description Options", ()=> { + r.push(App.UI.DOM.link("Description Options", () => { jQuery(descOptions).empty().append(App.UI.descriptionOptions()); })); @@ -1088,23 +1087,31 @@ App.UI.artOptions = function() { const el = new DocumentFragment(); let options = new App.UI.OptionsGroup(); - const art = App.UI.DOM.appendNewElement("div", el, App.Art.SlaveArtElement(BaseSlave(), 2, 0), ["imageRef", "medImg"]); - art.style.float = "none"; - art.style.display = "block"; - art.style.marginLeft = "auto"; - art.style.marginRight = "auto"; + if (V.seeImages) { + const art = App.UI.DOM.appendNewElement("div", el, App.Art.SlaveArtElement(BaseSlave(), 2, 0), ["imageRef", "medImg"]); + art.style.float = "none"; + art.style.display = "block"; + art.style.marginLeft = "auto"; + art.style.marginRight = "auto"; + } options.addOption("Images are", "seeImages") .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); /* REMOVE THIS WARNING ONCE ART DEVELOPMENT RESUMES */ - options.addComment('<span class="warning">All image packs are currently out of date; some outfits will not be displayed.</span>'); + options.addComment('<span class="warning">All image packs are currently incomplete; some outfits will not be displayed.</span>'); 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 image pack", 0], ["Elohiem's interactive WebGL", 4]]); - + const option = options.addOption("Image style is", "imageChoice") + .addValueList([ // Keeping the most up to date options as the first in line + ["Elohiem's interactive WebGL", 4], + ["NoX/Deepmurk's vector art", 1], + ["Revamped embedded vector art", 3], + ["Non-embedded vector art", 2], + ["Shokushu's rendered image pack", 0], + ]); if (V.imageChoice === 1) { + options.addComment("The only 2D art in somewhat recent development. Contains many outfits."); options.addOption("Face artwork is", "seeFaces") .addValue("Enabled", 1).on().addValue("Disabled", 0).off(); @@ -1117,17 +1124,18 @@ App.UI.artOptions = function() { 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" + + options.addComment("Dead since long before the end of vanilla. You need to" + " <a href='https://mega.nz/#!upoAlBaZ!EbZ5wCixxZxBhMN_ireJTXt0SIPOywO2JW9XzTIPhe0' target='_blank'>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("This art development is dead."); options.addOption("Clothing erection bulges are", "showClothingErection") .addValue("Enabled", true).on().addValue("Disabled", false).off(); } else if (V.imageChoice === 4) { - options.addComment(`<a href='https://mega.nz/folder/HgZ0WTxB#o3mwURAAQfN5WZzutQvbXA' target='_blank'> Download the WebGL art assets</a> and place the 'webgl' folder into the resources/ folder where this HTML file is. + options.addComment(`This art is currently (9/6/21) the most actively developed. Real time 3D models. <a href='https://mega.nz/folder/HgZ0WTxB#o3mwURAAQfN5WZzutQvbXA' target='_blank'> Download the WebGL art assets</a> and place the 'webgl' folder into the resources/ folder where this HTML file is. Then <b>refresh</b> the page. Create the resources folder if it does not exist. <span class="warning">(Android/MacOS not supported)</span>`); @@ -1147,7 +1155,9 @@ App.UI.artOptions = function() { .addValue("0.25", 0.25).off().addValue("0.5", 0.5).off().addValue("1", 1).on().addValue("2", 2).off().addValue("4", 4).off(); options.addOption("Face culling", "setFaceCulling") .addValue("Enabled", true).off().addValue("Disabled", false).on() - .addComment("Wether to draw the backside of the model, affects transparent surfaces such as hair. Enabling is recommended for low-end GPU's."); + .addComment("Whether to draw the backside of the model; affects transparent surfaces such as hair. Enabling is recommended for low-end GPU's."); + } else if (V.imageChoice === 2) { + option.addComment("This art development is dead since vanilla. Since it is not embedded, requires a separate art pack to be downloaded."); } options.addOption("PA avatar art is", "seeAvatar") diff --git a/src/gui/storyCaption.js b/src/gui/storyCaption.js index 3b7faf0c43d6d5cbb9bbfb8a0a3cffd28f40581f..340a32dbd27759faff05803d2872ec0cfbf972ee 100644 --- a/src/gui/storyCaption.js +++ b/src/gui/storyCaption.js @@ -339,7 +339,7 @@ App.UI.storyCaption = function() { div.append(" | "); const span = document.createElement("span"); span.className = "darkviolet"; - V.SecExp.core.authority = Math.clamp(Math.trunc(V.SecExp.core.authority), 0, 20000); + App.SecExp.authorityX(); if (V.SecExp.core.authority > 19500) { span.append("divine will"); } else if (V.SecExp.core.authority > 19000) { diff --git a/src/interaction/prostheticConfig.js b/src/interaction/prostheticConfig.js index 2515fba7e7edf4564e865f8663918f590caaa307..f30399b22fab2778a4ec79ec1d64fe4b85bed78d 100644 --- a/src/interaction/prostheticConfig.js +++ b/src/interaction/prostheticConfig.js @@ -561,23 +561,23 @@ App.UI.prostheticsConfigPassage = function() { V.nextLink = "Prosthetics Configuration"; App.UI.DOM.appendNewElement("div", node, `Attaching ${his} tail is a simple procedure, you simply push the connector into a socket, right where ${his} tailbone ends, until the lock engages.`); r.push(`When you are done, ${he} looks back and`); - if (slave.tailShape === "neko") { + if (slave.tailShape === "cat") { r.push(`sways ${his} tail side to side enigmatically.`); - } else if (slave.tailShape === "inu") { + } else if (slave.tailShape === "dog") { r.push(`wags ${his} tail side to side energetically.`); - } else if (slave.tailShape === "kit") { + } else if (slave.tailShape === "fox") { r.push(`slowly sways ${his} tail feeling the soft fur brush against ${his} skin.`); } else if (slave.tailShape === "kitsune") { r.push(`slowly sways ${his} tails luxuriating in the incredibly soft, fluffy fur brushing against ${his} skin.`); } else if (slave.tailShape === "tanuki") { r.push(`admires ${his} long, thick fluffy tail.`); - } else if (slave.tailShape === "ushi") { + } else if (slave.tailShape === "cow") { r.push(`swats ${himself} playfully.`); - } else if (slave.tailShape === "usagi") { + } else if (slave.tailShape === "rabbit") { r.push(`wiggles ${his} little tail a bit.`); - } else if (slave.tailShape === "risu") { + } else if (slave.tailShape === "squirrel") { r.push(`admires ${his} the size of ${his} fluffy tail.`); - } else if (slave.tailShape === "uma") { + } else if (slave.tailShape === "horse") { r.push(`sways ${his} tail back and forth.`); } else if (slave.tail === "combat") { r.push(`experimentally whips the long tail side to side then takes aim at a prepared fruit, lashes out with blinding speed and smiles as it explodes into chunks.`); diff --git a/src/js/SlaveState.js b/src/js/SlaveState.js index 49427dc95f0210cbe70ba6b88113e90c16db3901..82683585a5636ad5b3e6b1ce9b8a6d4d6ac360ce 100644 --- a/src/js/SlaveState.js +++ b/src/js/SlaveState.js @@ -729,7 +729,7 @@ App.Entity.SlaveState = class SlaveState { * @type {FC.EarShape} */ this.earShape = "normal"; /** type of kemonomimi ears if any - * "neko", "inu", "kit", "tanuki", "usagi" + * "cat", "dog", "fox", "tanuki", "cow" * @type {FC.EarTypeKemonomimi}*/ this.earT = "none"; /** kemonomimi ear color @@ -759,7 +759,7 @@ App.Entity.SlaveState = class SlaveState { */ this.PTail = 0; /** the current shape of their modular tail - * "none", "neko", "inu", "kit", "kitsune", "tanuki", "ushi", "usagi", "risu", "uma" + * "none", "cat", "dog", "fox", "kitsune", "tanuki", "cow", "rabbit", "squirrel", "horse" * @type {FC.TailShape} */ this.tailShape = "none"; /** tail color */ diff --git a/src/js/birth/birth.js b/src/js/birth/birth.js index 25c61a51ca0fc18d82f50c47f59a34fe8b77767d..7127d4eb496e0f8357024afc76b956a3f5d437d0 100644 --- a/src/js/birth/birth.js +++ b/src/js/birth/birth.js @@ -328,8 +328,7 @@ globalThis.birth = function(slave, {birthStorm = false, cSection = false, artRen } else if (baby.fatherID > 0) { const babyDaddy = findFather(baby.fatherID); if (babyDaddy) { - const adjust = babyDaddy.counter.slavesFathered++; - adjustFatherProperty(babyDaddy, "slavesFathered", adjust); + babyDaddy.counter.slavesFathered++; } } } diff --git a/src/js/extendedFamilyModeJS.js b/src/js/extendedFamilyModeJS.js index 55955af1b9fbea45e2df5188563e6f9b67210464..4b619c13c4457391e77c699c53157df1a7068551 100644 --- a/src/js/extendedFamilyModeJS.js +++ b/src/js/extendedFamilyModeJS.js @@ -412,71 +412,121 @@ globalThis.totalPlayerRelatives = function(pc) { return relatives; }; -/** - * Returns the term for slave2's relationship to slave1 (i.e. "daughter" if slave2 is slave1's daughter). - * Performs distant relative checking if enabled. +/** Returns the term used for slave2's sibling relationship to slave1, or null if there is no sibling relationship * @param {Relative} slave1 * @param {Relative} slave2 - * @returns {string|null} - returns null if the slaves are not related, even distantly. + * @returns {string} */ -globalThis.relativeTerm = function(slave1, slave2) { +globalThis.siblingTerm = function(slave1, slave2) { const useMaleTerms = (V.diversePronouns || slave2.ID === -1); - if (slave2.mother === slave1.ID || slave2.father === slave1.ID) { - if (slave2.genes === "XY" && useMaleTerms) { - return "son"; - } else { - return "daughter"; - } - } else if (slave1.mother === slave2.ID && slave1.father === slave2.ID) { + switch (areSisters(slave2, slave1)) { + case 1: + if (slave2.genes === "XY" && useMaleTerms) { + return "twin brother"; + } else { + return "twin sister"; + } + case 2: + if (slave2.genes === "XY" && useMaleTerms) { + return "brother"; + } else { + return "sister"; + } + case 3: + if (slave2.genes === "XY" && useMaleTerms) { + return "half-brother"; + } else { + return "half-sister"; + } + } + return null; +}; + +/** Returns the term used for slave2's parental relationship to slave1, or null if there is no parental relationship + * @param {Relative} slave1 + * @param {Relative} slave2 + * @returns {string} + */ +globalThis.parentTerm = function(slave1, slave2) { + if (slave1.mother === slave2.ID && slave1.father === slave2.ID) { return "sole parent"; } else if (slave1.mother === slave2.ID) { return "mother"; } else if (slave1.father === slave2.ID) { return "father"; - } else if (areSisters(slave2, slave1) === 1) { - if (slave2.genes === "XY" && useMaleTerms) { - return "twin brother"; - } else { - return "twin sister"; - } - } else if (areSisters(slave2, slave1) === 2) { - if (slave2.genes === "XY" && useMaleTerms) { - return "brother"; - } else { - return "sister"; - } - } else if (areSisters(slave2, slave1) === 3) { + } + return null; +}; + +/** + * Returns an array of any terms for slave2's relationship to slave1 (i.e. ["daughter"] if slave2 is slave1's daughter, or ["daughter", "niece"] if slave2 is both slave1's daughter and her niece). + * Performs distant relative checking if enabled. + * @param {Relative} slave1 + * @param {Relative} slave2 + * @returns {string[]} + */ +globalThis.relativeTerms = function(slave1, slave2) { + let relations = []; + const useMaleTerms = (V.diversePronouns || slave2.ID === -1); + const parent = parentTerm(slave1, slave2); + if (parent) { + relations.push(parent); + } else if (slave2.mother === slave1.ID || slave2.father === slave1.ID) { if (slave2.genes === "XY" && useMaleTerms) { - return "half-brother"; + relations.push("son"); } else { - return "half-sister"; + relations.push("daughter"); } - } else if (isAunt(slave1, slave2)) { + } + const sibling = siblingTerm(slave1, slave2); + if (sibling) { + relations.push(sibling); + } + if (isAunt(slave1, slave2)) { if (slave2.genes === "XY" && useMaleTerms) { - return "uncle"; + relations.push("uncle"); } else { - return "aunt"; + relations.push("aunt"); } - } else if (isAunt(slave2, slave1)) { + } + if (isAunt(slave2, slave1)) { if (slave2.genes === "XY" && useMaleTerms) { - return "nephew"; + relations.push("nephew"); } else { - return "niece"; + relations.push("niece"); } - } else if (areCousins(slave2, slave1)) { - return "cousin"; - } else if (isGrandfatherP(slave1, slave2)) { - return "grandfather"; + } + if (areCousins(slave2, slave1)) { + relations.push("cousin"); + } + if (isGrandfatherP(slave1, slave2)) { + relations.push("grandfather"); } else if (isGrandmotherP(slave1, slave2)) { - return "grandmother"; + relations.push("grandmother"); } else if (isGrandparentP(slave2, slave1)) { if (slave2.genes === "XY" && useMaleTerms) { - return "grandson"; + relations.push("grandson"); } else { - return "granddaughter"; + relations.push("granddaughter"); } } + return relations; +}; + +/** + * Returns the term for slave2's relationship to slave1 (i.e. "daughter" if slave2 is slave1's daughter). + * If multiple relationships exist, a compound noun will be returned (i.e. "daughter, granddaughter and niece") + * Performs distant relative checking if enabled. + * @param {Relative} slave1 + * @param {Relative} slave2 + * @returns {string|null} - returns null if the slaves are not related, even distantly. + */ +globalThis.relativeTerm = function(slave1, slave2) { + const relations = relativeTerms(slave1, slave2); + if (relations.length > 0) { + return toSentence(relations); + } return null; }; diff --git a/src/js/relationshipChecks.js b/src/js/relationshipChecks.js index e453a510965224c9fec89d8fca260f813c11015c..56eaf1f964da850a0105022265175e1b280070f2 100644 --- a/src/js/relationshipChecks.js +++ b/src/js/relationshipChecks.js @@ -73,30 +73,26 @@ globalThis.PCrelationshipTerm = function(id) { * @returns {string|DocumentFragment} */ globalThis.contextualIntro = function(context, actor, asLink = false, insertComma = false) { - let first = true; let r = ``; - const firstPreamble = (context === V.PC) ? "your" : getPronouns(context).possessive; - let preamble = () => { - const s = first ? `${firstPreamble} ` : ` and `; - first = false; - return s; - }; + const preamble = (context === V.PC) ? "your" : getPronouns(context).possessive; + let terms = []; const contextSlave = asSlave(context); const actorSlave = asSlave(actor); if (contextSlave && contextSlave.relationship > 0 && contextSlave.relationshipTarget === actor.ID) { - r += preamble() + relationshipTerm(contextSlave); + terms.push(relationshipTerm(contextSlave)); } else if (context === V.PC && actorSlave && actorSlave.relationship < -1) { - r += preamble() + PCrelationshipTerm(actorSlave); + terms.push(PCrelationshipTerm(actorSlave)); } else if (actor === V.PC && contextSlave && contextSlave.relationship < -1) { - r += preamble() + PCrelationshipTerm(contextSlave); + terms.push(PCrelationshipTerm(contextSlave)); } else if (contextSlave && contextSlave.rivalry > 0 && contextSlave.rivalryTarget === actor.ID) { - r += preamble() + rivalryTerm(contextSlave); + terms.push(rivalryTerm(contextSlave)); } - const relative = relativeTerm(context, actor); - if (relative) { - r += preamble() + relative; + terms = terms.concat(relativeTerms(context, actor)); + + if (terms.length > 0) { + r = preamble + " " + toSentence(terms); } if (r !== ``) { diff --git a/src/js/slaveSummaryHelpers.js b/src/js/slaveSummaryHelpers.js index db6bfbd4cda085cd1d3bcc3f0c97d058d914b628..00b68a44578b2d3119ab7bad57477a887e707437 100644 --- a/src/js/slaveSummaryHelpers.js +++ b/src/js/slaveSummaryHelpers.js @@ -320,10 +320,10 @@ App.UI.SlaveSummaryImpl = function() { if (areSisters(V.PC, slave) > 0) { addText(block, `Your `); if (slave.relationship < -1) { - makeSpan(block, bits.makeBit(`${relativeTerm(V.PC, slave)}${bits.and}${PCrelationshipTerm(slave)}`), cssClassName); + makeSpan(block, bits.makeBit(`${siblingTerm(V.PC, slave)}${bits.and}${PCrelationshipTerm(slave)}`), cssClassName); handled = 1; } else { - makeSpan(block, bits.makeBit(relativeTerm(V.PC, slave)), cssClassName); + makeSpan(block, bits.makeBit(siblingTerm(V.PC, slave)), cssClassName); } } if (slave.daughters === 1) { @@ -331,7 +331,7 @@ App.UI.SlaveSummaryImpl = function() { if (ssj) { helpers.referenceSlaveWithPreview(block, ssj, SlaveFullName(ssj)); addText(block, "'s "); - let spanText = relativeTerm(ssj, slave); + let spanText = parentTerm(ssj, slave); if (slave.relationshipTarget === ssj.ID) { spanText += `${bits.and}${relationshipTerm(slave)}`; handled = 1; diff --git a/src/js/speech.js b/src/js/speech.js index 1995b7a06ac023c9bc4d4b1d622e3b9a09660de6..b0e0ac499961403e7313487db85a31349a8789e1 100644 --- a/src/js/speech.js +++ b/src/js/speech.js @@ -63,27 +63,6 @@ globalThis.getEnunciation = function(slave) { } } ret.say = "lisp"; - ret.s = "th"; - ret.S = "Th"; - ret.ss = "th"; - ret.c = "th"; - ret.C = "Th"; - ret.cc = "kth"; - ret.z = "th"; - ret.Z = "Th"; - ret.zz = "th"; - ret.ch = "th"; - ret.Ch = "Th"; - ret.ps = "th"; - ret.Ps = "Th"; - ret.sh = "th"; - ret.Sh = "Th"; - ret.sc = "th"; - ret.Sc = "Th"; - ret.sch = "th"; - ret.Sch = "Th"; - ret.x = "kth"; - ret.X = "Th"; } else { if (V.PC.customTitle !== undefined) { ret.title = V.PC.customTitle; @@ -142,39 +121,11 @@ globalThis.getEnunciation = function(slave) { } } ret.say = "say"; - ret.s = "s"; - ret.S = "S"; - ret.ss = "ss"; - ret.c = "c"; - ret.C = "C"; - ret.cc = "cc"; - ret.z = "z"; - ret.Z = "Z"; - ret.zz = "zz"; - ret.ch = "ch"; - ret.Ch = "Ch"; - ret.ps = "ps"; - ret.Ps = "Ps"; - ret.sh = "sh"; - ret.Sh = "Sh"; - ret.sc = "sc"; - ret.Sc = "Sc"; - ret.sch = "sch"; - ret.Sch = "Sch"; - ret.x = "x"; - ret.X = "X"; } return ret; }; -/** Write all of the enunciations used by a slave into global state data for use by SugarCube macros. - * @param {App.Entity.SlaveState} slave - */ -globalThis.Enunciate = function(slave) { - V.enunciate = getEnunciation(slave); -}; - /** * Returns speech with lisp if slave lisps * @param {App.Entity.SlaveState} slave diff --git a/src/js/utilsMisc.js b/src/js/utilsMisc.js index 0163e91779ada368b3fd2c2266e13afe92324029..785c43193716c604a335a3edbca188d13b828ce5 100644 --- a/src/js/utilsMisc.js +++ b/src/js/utilsMisc.js @@ -169,3 +169,13 @@ globalThis.asSlave = function(human) { } return /** @type {App.Entity.SlaveState} */(human); }; + +/** + * @param {string} englishWord + * @returns {string} + */ +App.Utils.translate = function(englishWord) { + return App.Data.dictionary[englishWord] && App.Data.dictionary[englishWord].hasOwnProperty(V.language) + ? App.Data.dictionary[englishWord][V.language] + : englishWord; +}; diff --git a/src/npc/descriptions/butt/butt.js b/src/npc/descriptions/butt/butt.js index a2d34410c752eaf8fa965f8f876a69ed9c1efe6a..837a26cb1e7879c6655dac712b86b6e3b5f27a10 100644 --- a/src/npc/descriptions/butt/butt.js +++ b/src/npc/descriptions/butt/butt.js @@ -1270,24 +1270,24 @@ App.Desc.butt = function(slave, {market, eventDescription} = {}) { } } - if (slave.tailShape === "neko") { - r.push(`${He} has a long, slender, ${slave.tailColor} cat tail which tends to sway side to side when ${he} is focused on a task.`); - } else if (slave.tailShape === "inu") { - r.push(`${He} has a bushy, ${slave.tailColor} dog tail which tends to wag energetically when ${he} gets exited.`); - } else if (slave.tailShape === "kit") { - r.push(`${He} has a soft, fluffy, ${slave.tailColor} fox tail.`); + if (slave.tailShape === "cat") { + r.push(`${He} has a long, slender, ${slave.tailColor} ${App.Utils.translate("cat")} tail which tends to sway side to side when ${he} is focused on a task.`); + } else if (slave.tailShape === "dog") { + r.push(`${He} has a bushy, ${slave.tailColor} ${App.Utils.translate("dog")} tail which tends to wag energetically when ${he} gets exited.`); + } else if (slave.tailShape === "fox") { + r.push(`${He} has a soft, fluffy, ${slave.tailColor} ${App.Utils.translate("fox")} tail.`); } else if (slave.tailShape === "kitsune") { r.push(`${He} has three incredibly soft, fluffy, ${slave.tailColor} fox tails; they feel heavenly to the touch.`); } else if (slave.tailShape === "tanuki") { r.push(`${He} has a long, fluffy, ${slave.tailColor} tanuki tail with a dark stripe running down the middle.`); - } else if (slave.tailShape === "ushi") { - r.push(`${He} has a long, ${slave.tailColor} cow tail; it has a small tuft of hair at the end and tends to swat at things absentmindedly.`); - } else if (slave.tailShape === "usagi") { - r.push(`${He} has a short and fluffy ${slave.tailColor} rabbit tail.`); - } else if (slave.tailShape === "risu") { - r.push(`${He} has a big and bushy ${slave.tailColor} squirrel tail.`); - } else if (slave.tailShape === "uma") { - r.push(`${He} has a long horse tail consisting of consisting of ${slave.tailColor} hair.`); + } else if (slave.tailShape === "cow") { + r.push(`${He} has a long, ${slave.tailColor} ${App.Utils.translate("cow")} tail; it has a small tuft of hair at the end and tends to swat at things absentmindedly.`); + } else if (slave.tailShape === "rabbit") { + r.push(`${He} has a short and fluffy ${slave.tailColor} ${App.Utils.translate("rabbit")} tail.`); + } else if (slave.tailShape === "squirrel") { + r.push(`${He} has a big and bushy ${slave.tailColor} ${App.Utils.translate("squirrel")} tail.`); + } else if (slave.tailShape === "horse") { + r.push(`${He} has a long ${App.Utils.translate("horse")} tail consisting of consisting of ${slave.tailColor} hair.`); } else if (slave.tail === "combat") { r.push(`${He} has a very long ${slave.tailColor} metallic tail that can lash out, constrict, and deploy razor sharp spikes on command.`); } else if (slave.tail === "sex") { diff --git a/src/npc/descriptions/ears.js b/src/npc/descriptions/ears.js index d0a1d0a3096aba94291624706288ff83d0e69162..6a0cbb48b44147019d164ae6ec6b97fc1613ae96 100644 --- a/src/npc/descriptions/ears.js +++ b/src/npc/descriptions/ears.js @@ -38,12 +38,12 @@ App.Desc.ears = function(slave) { r.push(`${His} small, ${either("elfin", "leaf-shaped", "pointed")} ears are quite cute and give ${him} an exotic appearance.`); } else if (slave.earShape === "elven") { r.push(`${He} has long, thin elven ears that ${either(`tend to droop when ${he} is relaxed or sad`, `tend to waggle up and down when ${he} is excited`, `twitch at the slightest touch`)}.`); - } else if (slave.earShape === "ushi") { + } else if (slave.earShape === "cow") { r.push(`${He} has long, floppy cow ears.`); // that ${either(`tend to droop when ${he} is relaxed or sad`, `tend waggle up and down when ${he} is excited`, `twitch at the slightest touch`)}. These don't make sense for the most part. } - if (slave.earT === "neko") { - r.push(`${He} has cute, ${slave.earTColor} cat ears on ${his} head; they`); + if (slave.earT === "cat") { + r.push(`${He} has cute, ${slave.earTColor} ${App.Utils.translate("cat")} ears on ${his} head; they`); if (slave.earImplant === 1) { r.push(`perk up at`); if (slave.devotion > 20) { @@ -53,8 +53,8 @@ App.Desc.ears = function(slave) { } } r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`); - } else if (slave.earT === "inu") { - r.push(`${He} has cute, ${slave.earTColor} dog ears on ${his} head; they`); + } else if (slave.earT === "dog") { + r.push(`${He} has cute, ${slave.earTColor} ${App.Utils.translate("dog")} ears on ${his} head; they`); if (slave.earImplant === 1) { r.push(`perk up at`); if (slave.devotion > 50) { @@ -64,8 +64,8 @@ App.Desc.ears = function(slave) { } } r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`); - } else if (slave.earT === "kit") { - r.push(`${He} has elegant, ${slave.earTColor} fox ears on ${his} head; they`); + } else if (slave.earT === "fox") { + r.push(`${He} has elegant, ${slave.earTColor} ${App.Utils.translate("fox")} ears on ${his} head; they`); if (slave.earImplant === 1) { r.push(`perk up at`); if (slave.devotion > 50) { @@ -86,8 +86,8 @@ App.Desc.ears = function(slave) { } } r.push(`${either(`tend to droop when ${he} is relaxed or sad`, `twitch at the slightest touch`)}.`); - } else if (slave.earT === "usagi") { - r.push(`${He} has long, ${slave.earTColor}, fluffy rabbit ears on ${his} head; they`); + } else if (slave.earT === "rabbit") { + r.push(`${He} has long, ${slave.earTColor}, fluffy ${App.Utils.translate("rabbit")} ears on ${his} head; they`); if (slave.earImplant === 1) { r.push(`perk up at`); if (slave.devotion > 50) { diff --git a/src/npc/generate/generateGenetics.js b/src/npc/generate/generateGenetics.js index 5479ccf07f51610dc39d18abf8fd06bddf6a7032..d4d9417c87ee3760c4a05327b97f2a1d794c4140 100644 --- a/src/npc/generate/generateGenetics.js +++ b/src/npc/generate/generateGenetics.js @@ -1084,9 +1084,9 @@ globalThis.generateChild = function(mother, ovum, incubator = false) { } else { child.earImplant = 1; child.earShape = "none"; - child.earT = "neko"; + child.earT = "cat"; child.earTColor = child.hColor; - child.tailShape = "neko"; + child.tailShape = "cat"; child.tailColor = child.hColor; } if (genes.faceShape !== undefined) { @@ -1153,9 +1153,9 @@ globalThis.generateChild = function(mother, ovum, incubator = false) { } else { child.earImplant = 1; child.earShape = "none"; - child.earT = "neko"; + child.earT = "cat"; child.earTColor = child.hColor; - child.tailShape = "neko"; + child.tailShape = "cat"; child.tailColor = child.hColor; } child.spermY = genes.spermY; diff --git a/src/npc/generate/generateNewSlaveJS.js b/src/npc/generate/generateNewSlaveJS.js index 1d57f5dd45f5c97d8dd32fc963f0438a36479a94..9a3c477f74ac669a608036124b06e0da6aca005e 100644 --- a/src/npc/generate/generateNewSlaveJS.js +++ b/src/npc/generate/generateNewSlaveJS.js @@ -1559,9 +1559,9 @@ globalThis.GenerateNewSlave = (function() { slave.faceShape = "feline"; eyeColor(["light grey", "blue", "blue", "brown", "brown", "brown", "green"]); // nonfunctional // - slave.earT = "neko"; + slave.earT = "cat"; slave.earImplant = 1; - slave.tailShape = "neko"; + slave.tailShape = "cat"; slave.tailColor = slave.hColor; slave.eye.right.pupil = "catlike"; slave.eye.left.pupil = "catlike"; diff --git a/src/npc/generate/lawCompliance.js b/src/npc/generate/lawCompliance.js index 4188dcf278aa99acf7f7b315383b9be09655f3e8..64cc2396c7e51003a166b040de03d077189ec09d 100644 --- a/src/npc/generate/lawCompliance.js +++ b/src/npc/generate/lawCompliance.js @@ -297,7 +297,7 @@ App.Desc.lawCompliance = function(slave, market = 0) { function FSDegradationistSMR() { slave.trust -= 10; - Math.clamp(slave.health.tired += 30, 0, 100); + slave.health.tired = Math.clamp(slave.health.tired + 30, 0, 100); return `${His} current owners <span class="gold">brutalized ${him}</span> before putting ${him} on sale, using clever methods to produce agony without seriously damaging ${his} health.`; } @@ -581,7 +581,7 @@ App.Desc.lawCompliance = function(slave, market = 0) { function healthInspectionSMR() { if (slave.health.condition < -10) { improveCondition(slave, 10); - Math.clamp(slave.health.tired -= 30, 0, 100); + slave.health.tired = Math.clamp(slave.health.tired - 30, 0, 100); return `${His} current owners held ${him} for basic medical care before putting ${him} on sale, resolving some of ${his} health issues.`; } } @@ -795,10 +795,11 @@ App.Desc.lawCompliance = function(slave, market = 0) { r.push(`Furthermore, ${his}`); if (slave.preg > 0) { r.push(`pregnancy was terminated and afterwards ${his}`); + TerminatePregnancy(slave); + actX(slave, "abortions"); } r.push(`tubes were promptly tied to prevent ${him} from passing on ${his} inferior genes, <span class="mediumorchid">saddening</span> and <span class="gold">frightening</span> ${him}.`); - TerminatePregnancy(slave); - actX(slave, "abortions"); + slave.preg = -3; slave.devotion -= 30; slave.trust -= 30; } else if (slave.balls > 0) { @@ -814,14 +815,15 @@ App.Desc.lawCompliance = function(slave, market = 0) { r.push(`to prevent ${him} from passing on ${his} inferior genes, <span class="mediumorchid">infuriating</span> and <span class="gold">terrifying</span> ${him}.`); slave.devotion -= 20; slave.trust -= 20; - } else if ((isFertile(slave) || slave.preg > 0)) { + } else if (isFertile(slave) || slave.preg > 0) { r.push(`${His}`); if (slave.preg > 0) { r.push(`pregnancy was terminated and afterwards ${his}`); + TerminatePregnancy(slave); + actX(slave, "abortions"); } r.push(`tubes were promptly tied to prevent ${him} from passing on ${his} inferior genes, <span class="mediumorchid">saddening</span> and <span class="gold">frightening</span> ${him}.`); - TerminatePregnancy(slave); - actX(slave, "abortions"); + slave.preg = -3; slave.devotion -= 10; slave.trust -= 10; } else { diff --git a/src/npc/generate/newChildIntro.js b/src/npc/generate/newChildIntro.js index 763ce2783be107b570fb188e4be0232a8b94408f..b537b8e214fcd29fa4aa8920c97378ba0a94f58d 100644 --- a/src/npc/generate/newChildIntro.js +++ b/src/npc/generate/newChildIntro.js @@ -983,7 +983,7 @@ App.UI.newChildIntro = function(slave) { "div", el, App.UI.DOM.link( - `Permit ${his} devoted ${relativeTerm(slave, parent)}, ${parent.slaveName}, to name ${his2} ${daughter}`, + `Permit ${his} devoted ${parentTerm(slave, parent)}, ${parent.slaveName}, to name ${his2} ${daughter}`, () => { parentNames(parent, slave); slave.birthName = slave.slaveName; diff --git a/src/npc/startingGirls/startingGirls.js b/src/npc/startingGirls/startingGirls.js index 0204197f5bfe70792939b3865d1b59d0716056e6..389d06e49d30dc0b9de0211c29e31217505ae903 100644 --- a/src/npc/startingGirls/startingGirls.js +++ b/src/npc/startingGirls/startingGirls.js @@ -100,7 +100,7 @@ App.StartingGirls.cleanup = function(slave) { if (slave.pubertyXX < 1) { slave.counter.birthsTotal = 0; } - slave.counter.birthsTotal = Math.clamp(slave.counter.birthsTotal, 0, ((slave.actualAge - slave.pubertyAgeXX) * 50)); + slave.counter.birthsTotal = Math.clamp(slave.counter.birthsTotal, 0, ((slave.physicalAge - slave.pubertyAgeXX) * 50)); } if ((slave.anus > 2 && slave.skill.anal <= 10) || (slave.anus === 0 && slave.skill.anal > 30)) { @@ -727,7 +727,7 @@ App.StartingGirls.physical = function(slave, cheat = false) { .addRange(165, 170, "<", "Average") .addRange(180, 185, "<", "Tall") .addRange(190, 185, ">=", "Very tall"); - option = options.addCustomOption(`Average height for a ${slave.actualAge} year old is ${heightToEitherUnit(Height.mean(slave))}`) + option = options.addCustomOption(`Average height for a ${slave.physicalAge} year old is ${heightToEitherUnit(Height.mean(slave))}`) .addButton( "Make average", () => resyncSlaveHight(slave), @@ -826,15 +826,15 @@ App.StartingGirls.physical = function(slave, cheat = false) { options.addOption("Tail shape", "tailShape", slave) .addValueList([ ["None", "none"], - ["Cat", "neko"], - ["Dog", "inu"], - ["Fox", "kit"], + ["Cat", "cat"], + ["Dog", "dog"], + ["Fox", "fox"], ["3 Tailed fox", "kitsune"], ["Tanuki", "tanuki"], - ["Bovine", "ushi"], - ["Rabbit", "usagi"], - ["Squirrel", "risu"], - ["Horse", "uma"], + ["Bovine", "cow"], + ["Rabbit", "rabbit"], + ["Squirrel", "squirrel"], + ["Horse", "horse"], ]); } } @@ -1659,7 +1659,7 @@ App.StartingGirls.profile = function(slave, cheat = false) { slave.clit = 0; slave.pubertyXY = 0; slave.pubertyAgeXY = V.potencyAge; - slave.pubertyXX = (slave.pubertyAgeXX < slave.actualAge ? 1 : 0); + slave.pubertyXX = (slave.pubertyAgeXX < slave.physicalAge ? 1 : 0); slave.vagina = Math.max(0, slave.vagina); slave.boobs = Math.max(500, slave.boobs); slave.shoulders = either(-2, -1, 0); @@ -1670,7 +1670,7 @@ App.StartingGirls.profile = function(slave, cheat = false) { WombFlush(slave); slave.belly = 0; slave.bellyPreg = 0; - slave.pubertyXY = (slave.pubertyAgeXY < slave.actualAge ? 1 : 0); + slave.pubertyXY = (slave.pubertyAgeXY < slave.physicalAge ? 1 : 0); slave.pubertyXX = 0; slave.pubertyAgeXX = V.fertilityAge; slave.ovaries = 0; diff --git a/src/player/desc/pNotesBelly.js b/src/player/desc/pNotesBelly.js index ce543eac26c09ef9b2cb6042081eec316199c071..951dd7154f7c602408796cd0ac9703bfbaf9ea80 100644 --- a/src/player/desc/pNotesBelly.js +++ b/src/player/desc/pNotesBelly.js @@ -156,8 +156,7 @@ App.Desc.Player.pNotesBelly = function(PC = V.PC) { } else if (PC.preg === 8 && PC.pregSource > 0) { const babyDaddy = findFather(PC.pregSource); if (babyDaddy) { - adjust = babyDaddy.counter.PCKnockedUp++; - adjustFatherProperty(babyDaddy, "PCKnockedUp", adjust); + babyDaddy.counter.PCKnockedUp++; } if (V.slaveIndices[PC.pregSource]) { const {him} = getPronouns(babyDaddy); @@ -297,8 +296,7 @@ App.Desc.Player.pNotesBelly = function(PC = V.PC) { } else if (PC.preg === 8 && PC.pregSource > 0) { const babyDaddy = findFather(PC.pregSource); if (babyDaddy) { - adjust = babyDaddy.counter.PCKnockedUp++; - adjustFatherProperty(babyDaddy, "PCKnockedUp", adjust); + babyDaddy.counter.PCKnockedUp++; } if (V.slaveIndices[PC.pregSource]) { const {him} = getPronouns(babyDaddy); @@ -416,8 +414,7 @@ App.Desc.Player.pNotesBelly = function(PC = V.PC) { } else if (PC.preg === 8 && PC.pregSource > 0) { const babyDaddy = findFather(PC.pregSource); if (babyDaddy) { - adjust = babyDaddy.counter.PCKnockedUp++; - adjustFatherProperty(babyDaddy, "PCKnockedUp", adjust); + babyDaddy.counter.PCKnockedUp++; } if (V.slaveIndices[PC.pregSource]) { const {him} = getPronouns(babyDaddy); diff --git a/src/player/desc/playerBelly.js b/src/player/desc/playerBelly.js index 4ae76b84930d27ab39c4eadbf68e5a6731af9b2a..3986048c461525ca94cd69b96b8c4a7c463e4ee6 100644 --- a/src/player/desc/playerBelly.js +++ b/src/player/desc/playerBelly.js @@ -217,8 +217,7 @@ App.Desc.Player.belly = function() { } else if (V.PC.preg === 8 && V.PC.pregSource > 0) { const babyDaddy = findFather(V.PC.pregSource); if (babyDaddy) { - adjust = babyDaddy.counter.PCKnockedUp++; - adjustFatherProperty(babyDaddy, "PCKnockedUp", adjust); + babyDaddy.counter.PCKnockedUp++; } if (V.slaveIndices[V.PC.pregSource]) { const {him} = getPronouns(babyDaddy); @@ -358,8 +357,7 @@ App.Desc.Player.belly = function() { } else if (V.PC.preg === 8 && V.PC.pregSource > 0) { const babyDaddy = findFather(V.PC.pregSource); if (babyDaddy) { - adjust = babyDaddy.counter.PCKnockedUp++; - adjustFatherProperty(babyDaddy, "PCKnockedUp", adjust); + babyDaddy.counter.PCKnockedUp++; } if (V.slaveIndices[V.PC.pregSource]) { const {him} = getPronouns(babyDaddy); @@ -477,8 +475,7 @@ App.Desc.Player.belly = function() { } else if (V.PC.preg === 8 && V.PC.pregSource > 0) { const babyDaddy = findFather(V.PC.pregSource); if (babyDaddy) { - adjust = babyDaddy.counter.PCKnockedUp++; - adjustFatherProperty(babyDaddy, "PCKnockedUp", adjust); + babyDaddy.counter.PCKnockedUp++; } if (V.slaveIndices[V.PC.pregSource]) { const {him} = getPronouns(babyDaddy); diff --git a/src/player/js/PlayerState.js b/src/player/js/PlayerState.js index aad6102cbb8c1368de648799396ed559ca5bc7e0..3720a7b32fcd50aa1f151e3184cff83c745906aa 100644 --- a/src/player/js/PlayerState.js +++ b/src/player/js/PlayerState.js @@ -428,7 +428,7 @@ App.Entity.PlayerState = class PlayerState { * "none", "damaged", "normal", "pointy", "elven", "ushi" */ this.earShape = "normal"; /** type of kemonomimi ears if any - * "neko", "inu", "kit", "tanuki", "usagi" */ + * "cat", "dog", "fox", "tanuki", "cow" */ this.earT = "none"; /** kemonomimi ear color * "hairless" */ @@ -454,7 +454,7 @@ App.Entity.PlayerState = class PlayerState { */ this.PTail = 0; /** the current shape of your modular tail - * "none", "neko", "inu", "kit", "kitsune", "tanuki", "ushi", "usagi", "risu", "uma" */ + * "none", "cat", "dog", "fox", "kitsune", "tanuki", "cow", "rabbit", "squirrel", "horse" this.tailShape = "none"; /** tail color */ this.tailColor = "none"; diff --git a/src/utility/enunciateWidgets.tw b/src/utility/enunciateWidgets.tw deleted file mode 100644 index 4aea9a754c1958b84c95f0260bde7f3b0bd710de..0000000000000000000000000000000000000000 --- a/src/utility/enunciateWidgets.tw +++ /dev/null @@ -1,87 +0,0 @@ -:: enunciate widgets [nobr widget] - -<<widget "Master">><<if def $args[0]>><<run Enunciate($args[0])>><<elseif ndef $enunciate>><<run Enunciate($activeSlave)>><</if>>$enunciate.title<</widget>> -<<widget "say">><<if (def $args[0]) && $enunciate.say != "lisp">>$args[0]<<else>>$enunciate.say<</if>><</widget>> -<<widget "s">>$enunciate.s<</widget>> -<<widget "S">>$enunciate.S<</widget>> -<<widget "ss">>$enunciate.ss<</widget>> -<<widget "c">>$enunciate.c<</widget>> -<<widget "C">>$enunciate.C<</widget>> -<<widget "cc">>$enunciate.cc<</widget>> -<<widget "z">>$enunciate.z<</widget>> -<<widget "Z">>$enunciate.Z<</widget>> -<<widget "zz">>$enunciate.zz<</widget>> -<<widget "ch">>$enunciate.ch<</widget>> -<<widget "Ch">>$enunciate.Ch<</widget>> -<<widget "ps">>$enunciate.ps<</widget>> -<<widget "Ps">>$enunciate.Ps<</widget>> -<<widget "sh">>$enunciate.sh<</widget>> -<<widget "Sh">>$enunciate.Sh<</widget>> -<<widget "sc">>$enunciate.sc<</widget>> -<<widget "Sc">>$enunciate.Sc<</widget>> -<<widget "sch">>$enunciate.sch<</widget>> -<<widget "Sch">>$enunciate.Sch<</widget>> -<<widget "x">>$enunciate.x<</widget>> -<<widget "X">>$enunciate.X<</widget>> - -/* - If $args[0] is defined, it is assumed the primary slave is speaking about secondary slave. Else, the secondary slave is speaking about primary slave. - This means that <<he>> will print _heLisp or $he, and <<he 2>> will print _he2Lisp or _he2. - Pronouns must be set for both slaves, and <<setSpokenLocalPronouns primarySlave secondarySlave>> must be previously called. -*/ -<<widget "he">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_heLisp<<else>>$he<</if>><<else>><<if _primarySlaveLisp>>_he2Lisp<<else>>_he2<</if>><</if>><</widget>> -<<widget "his">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_hisLisp<<else>>$his<</if>><<else>><<if _primarySlaveLisp>>_his2Lisp<<else>>_his2<</if>><</if>><</widget>> -<<widget "hers">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_hersLisp<<else>>$hers<</if>><<else>><<if _primarySlaveLisp>>_hers2Lisp<<else>>_hers2<</if>><</if>><</widget>> -<<widget "himself">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_himselfLisp<<else>>$himself<</if>><<else>><<if _primarySlaveLisp>>_himself2Lisp<<else>>_himself2<</if>><</if>><</widget>> -<<widget "daughter">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_daughterLisp<<else>>$daughter<</if>><<else>><<if _primarySlaveLisp>>_daughter2Lisp<<else>>_daughter2<</if>><</if>><</widget>> -<<widget "sister">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_sisterLisp<<else>>$sister<</if>><<else>><<if _primarySlaveLisp>>_sister2Lisp<<else>>_sister2<</if>><</if>><</widget>> -<<widget "loli">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_loliLisp<<else>>$loli<</if>><<else>><<if _primarySlaveLisp>>_loli2Lisp<<else>>_loli2<</if>><</if>><</widget>> -<<widget "wife">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_wifeLisp<<else>>$wife<</if>><<else>><<if _primarySlaveLisp>>_wife2Lisp<<else>>_wife2<</if>><</if>><</widget>> -<<widget "wives">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_wivesLisp<<else>>$wives<</if>><<else>><<if _primarySlaveLisp>>_wives2Lisp<<else>>_wives2<</if>><</if>><</widget>> -<<widget "He">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_HeLisp<<else>>$He<</if>><<else>><<if _primarySlaveLisp>>_He2Lisp<<else>>_He2<</if>><</if>><</widget>> -<<widget "His">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_HisLisp<<else>>$His<</if>><<else>><<if _primarySlaveLisp>>_His2Lisp<<else>>_His2<</if>><</if>><</widget>> -<<widget "Hers">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_HersLisp<<else>>$Hers<</if>><<else>><<if _primarySlaveLisp>>_Hers2Lisp<<else>>_Hers2<</if>><</if>><</widget>> -<<widget "Himself">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_HimselfLisp<<else>>$Himself<</if>><<else>><<if _primarySlaveLisp>>_Himself2Lisp<<else>>_Himself2<</if>><</if>><</widget>> -<<widget "Daughter">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_DaughterLisp<<else>>$Daughter<</if>><<else>><<if _primarySlaveLisp>>_Daughter2Lisp<<else>>_Daughter2<</if>><</if>><</widget>> -<<widget "Sister">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_SisterLisp<<else>>$Sister<</if>><<else>><<if _primarySlaveLisp>>_Sister2Lisp<<else>>_Sister2<</if>><</if>><</widget>> -<<widget "Loli">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_LoliLisp<<else>>$Loli<</if>><<else>><<if _primarySlaveLisp>>_Loli2Lisp<<else>>_Loli2<</if>><</if>><</widget>> -<<widget "Wife">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_WifeLisp<<else>>$Wife<</if>><<else>><<if _primarySlaveLisp>>_Wife2Lisp<<else>>_Wife2<</if>><</if>><</widget>> -<<widget "Wives">><<if ndef $args[0]>><<if _secondarySlaveLisp>>_WivesLisp<<else>>$Wives<</if>><<else>><<if _primarySlaveLisp>>_Wives2Lisp<<else>>_Wives2<</if>><</if>><</widget>> - -<<widget "heP">><<if _playerSlaveLisp>>_hePLisp<<else>>_heP<</if>><</widget>> -<<widget "hisP">><<if _playerSlaveLisp>>_hisPLisp<<else>>_hisP<</if>><</widget>> -<<widget "hersP">><<if _playerSlaveLisp>>_hersPLisp<<else>>_hersP<</if>><</widget>> -<<widget "himselfP">><<if _playerSlaveLisp>>_himselfPLisp<<else>>_himselfP<</if>><</widget>> -<<widget "daughterP">><<if _playerSlaveLisp>>_daughterPLisp<<else>>_daughterP<</if>><</widget>> -<<widget "sisterP">><<if _playerSlaveLisp>>_sisterPLisp<<else>>_sisterP<</if>><</widget>> -<<widget "loliP">><<if _playerSlaveLisp>>_loliPLisp<<else>>_loliP<</if>><</widget>> -<<widget "wifeP">><<if _playerSlaveLisp>>_wifePLisp<<else>>_wifeP<</if>><</widget>> -<<widget "wivesP">><<if _playerSlaveLisp>>_wivesPLisp<<else>>_wivesP<</if>><</widget>> -<<widget "HeP">><<if _playerSlaveLisp>>_HePLisp<<else>>_HeP<</if>><</widget>> -<<widget "HisP">><<if _playerSlaveLisp>>_HisPLisp<<else>>_HisP<</if>><</widget>> -<<widget "HersP">><<if _playerSlaveLisp>>_HersPLisp<<else>>_HersP<</if>><</widget>> -<<widget "HimselfP">><<if _playerSlaveLisp>>_HimselfPLisp<<else>>_HimselfP<</if>><</widget>> -<<widget "DaughterP">><<if _playerSlaveLisp>>_DaughterPLisp<<else>>_DaughterP<</if>><</widget>> -<<widget "SisterP">><<if _playerSlaveLisp>>_SisterPLisp<<else>>_SisterP<</if>><</widget>> -<<widget "LoliP">><<if _playerSlaveLisp>>_LoliPLisp<<else>>_LoliP<</if>><</widget>> -<<widget "WifeP">><<if _playerSlaveLisp>>_WifePLisp<<else>>_WifeP<</if>><</widget>> -<<widget "WivesP">><<if _playerSlaveLisp>>_WivesPLisp<<else>>_WivesP<</if>><</widget>> - -<<widget "heA">><<if _assistantSlaveLisp>>_heALisp<<else>>_heA<</if>><</widget>> -<<widget "hisA">><<if _assistantSlaveLisp>>_hisALisp<<else>>_hisA<</if>><</widget>> -<<widget "hersA">><<if _assistantSlaveLisp>>_hersALisp<<else>>_hersA<</if>><</widget>> -<<widget "himselfA">><<if _assistantSlaveLisp>>_himselfALisp<<else>>_himselfA<</if>><</widget>> -<<widget "daughterA">><<if _assistantSlaveLisp>>_daughterALisp<<else>>_daughterA<</if>><</widget>> -<<widget "sisterA">><<if _assistantSlaveLisp>>_sisterALisp<<else>>_sisterA<</if>><</widget>> -<<widget "loliA">><<if _assistantSlaveLisp>>_loliALisp<<else>>_loliA<</if>><</widget>> -<<widget "wifeA">><<if _assistantSlaveLisp>>_wifeALisp<<else>>_wifeA<</if>><</widget>> -<<widget "wivesA">><<if _assistantSlaveLisp>>_wivesALisp<<else>>_wivesA<</if>><</widget>> -<<widget "HeA">><<if _assistantSlaveLisp>>_HeALisp<<else>>_HeA<</if>><</widget>> -<<widget "HisA">><<if _assistantSlaveLisp>>_HisALisp<<else>>_HisA<</if>><</widget>> -<<widget "HersA">><<if _assistantSlaveLisp>>_HersALisp<<else>>_HersA<</if>><</widget>> -<<widget "HimselfA">><<if _assistantSlaveLisp>>_HimselfALisp<<else>>_HimselfA<</if>><</widget>> -<<widget "DaughterA">><<if _assistantSlaveLisp>>_DaughterALisp<<else>>_DaughterA<</if>><</widget>> -<<widget "SisterA">><<if _assistantSlaveLisp>>_SisterALisp<<else>>_SisterA<</if>><</widget>> -<<widget "LoliA">><<if _assistantSlaveLisp>>_LoliALisp<<else>>_LoliA<</if>><</widget>> -<<widget "WifeA">><<if _assistantSlaveLisp>>_WifeALisp<<else>>_WifeA<</if>><</widget>> -<<widget "WivesA">><<if _assistantSlaveLisp>>_WivesALisp<<else>>_WivesA<</if>><</widget>>