From f73644b18fc3a6f1927aa56051e1ef23c2b05af9 Mon Sep 17 00:00:00 2001 From: Svornost <11434-svornost@users.noreply.gitgud.io> Date: Thu, 30 Dec 2021 18:24:24 -0500 Subject: [PATCH 1/4] Add a juvenile detention facility to the prison circuit, when appropriate for the game settings. --- devTools/types/FC/misc.d.ts | 4 +- js/003-data/gameVariableData.js | 2 +- js/003-data/miscData.js | 3 + .../backwardsCompatibility.js | 3 + src/endWeek/nextWeek/nextWeek.js | 3 + src/init/storyInit.js | 3 + src/markets/gingering.js | 2 +- src/markets/marketUI.js | 1 + .../specificMarkets/criminalMarkets.js | 24 +++ src/markets/theMarket/marketData.js | 13 +- src/npc/generate/generateMarketSlave.js | 156 +++++++++++++++++- 11 files changed, 205 insertions(+), 9 deletions(-) diff --git a/devTools/types/FC/misc.d.ts b/devTools/types/FC/misc.d.ts index 5756b821a71..1fc19d9677d 100644 --- a/devTools/types/FC/misc.d.ts +++ b/devTools/types/FC/misc.d.ts @@ -1,7 +1,7 @@ declare namespace FC { type SlaveSchoolName = "GRI" | "HA" | "NUL" | "SCP" | "TCR" | "TFS" | "TGA" | "TSS" | "LDE" | "TUO"; - type LawlessMarkets = "generic" | "gangs and smugglers" | "heap" | "indentures" | "low tier criminals" | "military prison" | "neighbor" | - "wetware" | "white collar" | SlaveSchoolName; + type LawlessMarkets = "generic" | "gangs and smugglers" | "heap" | "indentures" | "low tier criminals" | "military prison" | "juvenile detention" | + "neighbor" | "wetware" | "white collar" | SlaveSchoolName; type OrdinaryMarkets = "kidnappers" | "trainers" | "hunters" | "raiders" | "underage raiders" | "corporate"; type SlaveMarketName = LawlessMarkets | OrdinaryMarkets; type Gingering = Zeroable<"antidepressant" | "depressant" | "stimulant" | "vasoconstrictor" | "vasodilator" | "aphrodisiac" | "ginger">; diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js index 186a86f319b..1e4f364e773 100644 --- a/js/003-data/gameVariableData.js +++ b/js/003-data/gameVariableData.js @@ -952,7 +952,7 @@ App.Data.resetOnNGPlus = { justiceEvents: ["slave deal", "slave training", "majority deal", "indenture deal", "virginity deal", "breeding deal"], /* not in setupVars because we remove events from this array as they occur */ /** @type {Array<FC.SlaveMarketName>} */ - prisonCircuit: ["low tier criminals", "gangs and smugglers", "white collar", "military prison"], + prisonCircuit: ["low tier criminals", "gangs and smugglers", "white collar", "military prison", "juvenile detention"], prisonCircuitIndex: 0, ui: "start", diff --git a/js/003-data/miscData.js b/js/003-data/miscData.js index d07b7b4df15..0702bdab943 100644 --- a/js/003-data/miscData.js +++ b/js/003-data/miscData.js @@ -1461,6 +1461,8 @@ App.Data.misc = { militaryCriminalPool: ["deserter", "gunner", "officer", "private", "sniper", "soldier", "specOps", "spy", "terrorist", "war criminal"], + juvenileCriminalPool: ["theft", "robbery", "pickpocketing", "assault", "manslaughter", "smuggling", "mule", "prostitution", "truancy", "curfew"], + fakeBellies: ["a huge empathy belly", "a large empathy belly", "a medium empathy belly", "a small empathy belly"], /* lets fake bellies be separated from other .bellyAccessory */ @@ -1870,6 +1872,7 @@ App.Data.misc.lawlessMarkets = [ "indentures", "low tier criminals", "military prison", + "juvenile detention", "neighbor", "wetware", "white collar", diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js index c5540e649d1..07a3ecdf088 100644 --- a/src/data/backwardsCompatibility/backwardsCompatibility.js +++ b/src/data/backwardsCompatibility/backwardsCompatibility.js @@ -329,6 +329,9 @@ App.Update.globalVariables = function(node) { V.prisonCircuit = ["low tier criminals", "gangs and smugglers", "white collar", "military prison"]; V.prisonCircuitIndex = random(0, V.prisonCircuit.length - 1); } + if (V.prisonCircuit.length === 4) { + V.prisonCircuit.push("juvenile detention"); + } App.Update.CustomSlaveOrder(V.huskSlave); App.Update.CustomSlaveOrder(V.customSlave); diff --git a/src/endWeek/nextWeek/nextWeek.js b/src/endWeek/nextWeek/nextWeek.js index 9196b5ab14d..03ea46a5da8 100644 --- a/src/endWeek/nextWeek/nextWeek.js +++ b/src/endWeek/nextWeek/nextWeek.js @@ -323,6 +323,9 @@ App.EndWeek.nextWeek = function() { V.thisWeeksFSWares = V.merchantFSWares.randomMany(2); V.thisWeeksIllegalWares = V.merchantIllegalWares.randomMany(1); V.prisonCircuitIndex++; + if (V.prisonCircuit[V.prisonCircuitIndex] === "juvenile detention" && (V.minimumSlaveAge > 16 || V.pedo_mode === 1)) { + V.prisonCircuitIndex++; // skip juvenile detention if juvenile slaves are not allowed, or we're in pedo mode (where all prisoners are juvenile) + } if (V.prisonCircuitIndex >= V.prisonCircuit.length) { V.prisonCircuitIndex = 0; } diff --git a/src/init/storyInit.js b/src/init/storyInit.js index e1c188bb07f..2eafb65ba8a 100644 --- a/src/init/storyInit.js +++ b/src/init/storyInit.js @@ -54,6 +54,9 @@ App.Intro.init = function() { V.weatherType = 1; V.weatherRemaining = 6; V.prisonCircuitIndex = random(0, V.prisonCircuit.length-1); + if (V.prisonCircuit[V.prisonCircuitIndex] === "juvenile detention" && (V.minimumSlaveAge > 16 || V.pedo_mode === 1)) { + V.prisonCircuitIndex = 0; // skip juvenile detention if juvenile slaves are not allowed, or we're in pedo mode (where all prisoners are juvenile) + } /* I am not a slave object! Do not treat me like one! */ V.customSlave = new App.Entity.CustomSlaveOrder(); diff --git a/src/markets/gingering.js b/src/markets/gingering.js index 11f90fbbf5a..cc25ffaed69 100644 --- a/src/markets/gingering.js +++ b/src/markets/gingering.js @@ -17,7 +17,7 @@ App.Entity.GingeringParameters = class { /* SMR prohibits gingering and is enforced for this seller - do nothing */ } else if (App.Data.misc.schools.has(market)) { /* slave schools have a reputation to maintain, and will never ginger their slaves */ - } else if (["wetware", "heap", "gangs and smugglers", "low tier criminals", "military prison", "white collar", "corporate"].includes(market)) { + } else if (["wetware", "heap", "gangs and smugglers", "low tier criminals", "military prison", "juvenile detention", "white collar", "corporate"].includes(market)) { /* these sellers see no reason to ginger their slaves */ } else if (market === "neighbor" && App.Neighbor.opinion(V.arcologies[0], V.arcologies[arcIndex]) >= 50) { /* socially-aligned neighbors will not try to cheat you */ diff --git a/src/markets/marketUI.js b/src/markets/marketUI.js index f368ab9951c..0b823840f1a 100644 --- a/src/markets/marketUI.js +++ b/src/markets/marketUI.js @@ -208,6 +208,7 @@ App.Markets.marketName = function(market, arcIndex = 1) { case "gangs and smugglers": case "white collar": case "military prison": + case "juvenile detention": return `the prisoner sale`; default: return `Someone messed up. ${market} is not known.`; diff --git a/src/markets/specificMarkets/criminalMarkets.js b/src/markets/specificMarkets/criminalMarkets.js index ce1ffb66681..7899a3b658c 100644 --- a/src/markets/specificMarkets/criminalMarkets.js +++ b/src/markets/specificMarkets/criminalMarkets.js @@ -71,3 +71,27 @@ App.Markets["military prison"] = function() { el.append(App.Markets.purchaseFramework("military prison", {sTitleSingular: "prisoner", sTitlePlural: "prisoners"})); return el; }; + +App.Markets["juvenile detention"] = function() { + const r = new SpacedTextAccumulator(); + // mixed prisoners + r.push(`You board the transport to a small brick-clad juvenile detention facility on the outskirts of the Free City. You might have confused the building for a high school, if not for the double fence topped with razor wire. Although the owners of this facility once viewed it as a rehabilitation center, a lack of funding and serious overcrowding have made them open to seeing slavery as a path to that goal.`); + if (V.PC.career === "hoodlum" || V.PC.career === "street urchin" || V.PC.career === "child prostitute") { + r.push(`You brace yourself inwardly; some of your friends from the streets ended up in places like this, and you easily could have too, if your fortunes hadn't turned.`); + } + r.toParagraph(); + r.push(`You walk into the lobby of the facility and are greeted by a stern-looking heavyset woman in a warden's uniform. "Always happy to have you visit, `); + if (V.PC.title === 1) { + r.push(`Mister`); + } else { + r.push(`Miss`); + } + r.push(`${V.PC.slaveSurname}. Your patronage is appreciated. I've arranged for a few of our detainees that need some extra discipline to be present in the courtyard, right this way."`); + r.toParagraph(); + r.push(`Entering the courtyard, you see lines painted on the asphalt for athletic activity, faded through time and wear. A pair of tired basketball hoops and patched soccer goals frame the scene, while a cluster of teens mills around in one corner. A short blast from a guard's whistle brings them to attention and the warden begins calling them forward, one at a time.`); + r.toParagraph(); + + const el = r.container(); + el.append(App.Markets.purchaseFramework("juvenile detention", {sTitleSingular: "detainee", sTitlePlural: "detainees"})); + return el; +}; diff --git a/src/markets/theMarket/marketData.js b/src/markets/theMarket/marketData.js index fb53bf18d6d..72ee489efeb 100644 --- a/src/markets/theMarket/marketData.js +++ b/src/markets/theMarket/marketData.js @@ -114,20 +114,25 @@ App.Data.Markets = { return `Slaves will tend to be medium to high quality with a variety of useful backgrounds.`; case "military prison": return `Slaves will tend to be high quality but defiant.`; + case "juvenile detention": + return `Slaves will tend to be young and healthy but defiant.`; default: return ``; } }, get sale() { + let tw = "This week "; switch (V.prisonCircuit[V.prisonCircuitIndex]) { case "low tier criminals": - return `a minor prison is selling inmates.`; + return tw + `a minor prison is selling inmates.`; case "gangs and smugglers": - return `a major prison is selling hardened criminals.`; + return tw + `a major prison is selling hardened criminals.`; case "white collar": - return `a white collar prison is selling inmates.`; + return tw + `a white collar prison is selling inmates.`; case "military prison": - return `a military prison is selling inmates.`; + return tw + `a military prison is selling inmates.`; + case "juvenile detention": + return tw + `a juvenile detention facility is selling inmates.`; default: return ``; } diff --git a/src/npc/generate/generateMarketSlave.js b/src/npc/generate/generateMarketSlave.js index 3e4c5790937..0307992e505 100644 --- a/src/npc/generate/generateMarketSlave.js +++ b/src/npc/generate/generateMarketSlave.js @@ -7,7 +7,7 @@ globalThis.generateMarketSlave = function(market = "kidnappers", numArcology = 1) { let r = ``; let slave; - let SGProp = {}; + let SGProp = new GenerateNewSlavePram(); switch (market) { case "corporate": { @@ -3176,6 +3176,160 @@ globalThis.generateMarketSlave = function(market = "kidnappers", numArcology = 1 } break; } + case "juvenile detention": { + // age of criminal responsibility/minimum age of incarceration is currently: + // 7-9 in most of the Middle East, India, Burma, Thailand, Indonesia, and Ethiopia + // 10 in most US states, Australia, New Zealand, Malaysia, the UK, Ireland, and Switzerland + // 11 in Japan, and for federal crimes in the US + // 12-14 in Canada, China, Saudi Arabia, Egypt, and most of Europe and Latin America + // that's the Old World, though; an argument could be made for just using minimum slave age here... + SGProp.minAge = Math.max(V.minimumSlaveAge, 10); + SGProp.maxAge = 17; + SGProp.disableDisability = 1; + if (random(0, 100) > 80 && (V.continent === "North America" || V.continent === "South America" || V.continent === "Australia")) { + // indigenous kids are substantially more likely to end up in juvenile detention + SGProp.race = V.continent === "Australia" ? "pacific islander" : "amerindian"; + } + slave = GenerateNewSlave(null, SGProp); + // detained kids are fed and given medical attention by the facility + setHealth(slave, jsRandom(0, 60)); + if (!slave.weight.isBetween(-60, 60)) { + slave.weight = jsRandom(-30, 15); + } + + const criminal = App.Data.misc.juvenileCriminalPool.random(); + switch (criminal) { + case "theft": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for theft."; + r += "is detained for theft."; + slave.career = either("a street urchin", "an orphan", "a juvenile delinquent"); + slave.devotion = jsRandom(-70, -20); + slave.trust = jsRandom(-100, -60); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + break; + case "robbery": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for robbery."; + r += "is detained for robbery."; + slave.career = either("a street urchin", "a juvenile delinquent"); + slave.devotion = jsRandom(-70, -20); + slave.trust = jsRandom(-100, -60); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + break; + case "pickpocketing": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for repeat pick-pocketing."; + r += "is detained for pick-pocketing."; + slave.career = "a pick-pocket"; + slave.devotion = jsRandom(-50, 0); + slave.trust = jsRandom(-100, -60); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + if (slave.weight > 10) { + slave.weight = jsRandom(-50, 0); + } + break; + case "assault": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for assault."; + r += "is detained for assault."; + slave.career = either("a bully", "a bully hunter"); + slave.devotion = jsRandom(-70, -20); + slave.trust = jsRandom(-100, -60); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + if (slave.muscles < 10) { + slave.muscles = jsRandom(10, 45); + } + slave.skill.combat = 1; + break; + case "manslaughter": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for manslaughter."; + r += "is detained for manslaughter."; + slave.devotion = jsRandom(-70, -20); + slave.trust = jsRandom(-100, -60); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + break; + case "smuggling": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for smuggling goods into the Free City."; + r += "is detained for smuggling goods."; + slave.career = "a sweatshop worker"; + slave.devotion = jsRandom(-50, 0); + slave.trust = jsRandom(-50, 0); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + break; + case "mule": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for smuggling illegal contraband inside $his body."; + r += "is detained for being a drug mule."; + slave.career = "a drug mule"; + slave.devotion = jsRandom(-20, 20); + slave.trust = jsRandom(0, 20); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + slave.anus = 4; + slave.bellySag += 5; + slave.chem = 10 * jsRandom(1, 3); + slave.addict = 100; + break; + case "prostitution": // the gem of the bunch + slave.origin = "You purchased $his life at a prison sale. $He was locked away for child prostitution."; + r += "is detained for child prostitution, ironically."; + slave.career = "a child prostitute"; + slave.devotion = jsRandom(0, 40); // likes the cut of your jib + slave.trust = jsRandom(0, 20); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + if (slave.anus === 0) { + slave.anus = jsRandom(2, 3); + } + if (slave.vagina === 0) { + slave.vagina = jsRandom(2, 3); + } + slave.skill.whoring = jsRandom(15, 30); + slave.skill.anal = jsRandom(15, 30); + slave.skill.oral = jsRandom(15, 30); + if (slave.vagina > 0) { + slave.skill.vaginal = jsRandom(15, 30); + if (slave.vaginaLube === 0) { + slave.vaginaLube = 1; + } + } + if (slave.energy < 20) { + slave.energy = jsRandom(40, 95); + } + break; + case "truancy": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for repeated truancy."; + r += "is detained for repeated truancy."; + slave.career = either("a student from a public school", "a student from a private school"); + slave.devotion = jsRandom(-70, -20); + slave.trust = jsRandom(-50, 0); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + if (slave.intelligenceImplant === 0) { + slave.intelligenceImplant = 15; + } + break; + case "curfew": + slave.origin = "You purchased $his life at a prison sale. $He was locked away for repeated curfew violations."; + r += "is detained for repeated curfew violations."; + slave.career = either("a party girl", "a juvenile delinquent"); + slave.devotion = jsRandom(-70, -20); + slave.trust = jsRandom(-50, 0); + slave.hStyle = "buzzcut"; + slave.hLength = 0; + if (slave.vagina === 0) { + slave.vagina = jsRandom(1, 3); + slave.skill.vaginal = jsRandom(5, 20); + } + if (slave.energy < 20) { + slave.energy = jsRandom(20, 80); + } + break; + } + break; + } default: { r += "Someone messed up. Market is not known."; break; -- GitLab From f65270a4220574be4db06e0a8d23091cb26bd3b6 Mon Sep 17 00:00:00 2001 From: Svornost <11434-svornost@users.noreply.gitgud.io> Date: Thu, 30 Dec 2021 18:32:05 -0500 Subject: [PATCH 2/4] strip them --- src/markets/specificMarkets/criminalMarkets.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/markets/specificMarkets/criminalMarkets.js b/src/markets/specificMarkets/criminalMarkets.js index 7899a3b658c..37fd1acbabd 100644 --- a/src/markets/specificMarkets/criminalMarkets.js +++ b/src/markets/specificMarkets/criminalMarkets.js @@ -88,7 +88,7 @@ App.Markets["juvenile detention"] = function() { } r.push(`${V.PC.slaveSurname}. Your patronage is appreciated. I've arranged for a few of our detainees that need some extra discipline to be present in the courtyard, right this way."`); r.toParagraph(); - r.push(`Entering the courtyard, you see lines painted on the asphalt for athletic activity, faded through time and wear. A pair of tired basketball hoops and patched soccer goals frame the scene, while a cluster of teens mills around in one corner. A short blast from a guard's whistle brings them to attention and the warden begins calling them forward, one at a time.`); + r.push(`Entering the courtyard, you see lines painted on the asphalt for athletic activity, faded through time and wear. A pair of tired basketball hoops and patched soccer goals frame the scene, while a cluster of teens mills around in one corner. A short blast from a guard's whistle brings them to attention and the warden begins calling them forward, one at a time, and ordering them to strip for inspection.`); r.toParagraph(); const el = r.container(); -- GitLab From 3134d7572fab28ea81458907f4dc1d7602a3ecac Mon Sep 17 00:00:00 2001 From: Svornost <11434-svornost@users.noreply.gitgud.io> Date: Thu, 30 Dec 2021 18:33:52 -0500 Subject: [PATCH 3/4] reduce overlap with existing prisons --- src/endWeek/nextWeek/nextWeek.js | 2 +- src/init/storyInit.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/endWeek/nextWeek/nextWeek.js b/src/endWeek/nextWeek/nextWeek.js index 03ea46a5da8..c22693fb2bf 100644 --- a/src/endWeek/nextWeek/nextWeek.js +++ b/src/endWeek/nextWeek/nextWeek.js @@ -323,7 +323,7 @@ App.EndWeek.nextWeek = function() { V.thisWeeksFSWares = V.merchantFSWares.randomMany(2); V.thisWeeksIllegalWares = V.merchantIllegalWares.randomMany(1); V.prisonCircuitIndex++; - if (V.prisonCircuit[V.prisonCircuitIndex] === "juvenile detention" && (V.minimumSlaveAge > 16 || V.pedo_mode === 1)) { + if (V.prisonCircuit[V.prisonCircuitIndex] === "juvenile detention" && (V.minimumSlaveAge >= 16 || V.pedo_mode === 1)) { V.prisonCircuitIndex++; // skip juvenile detention if juvenile slaves are not allowed, or we're in pedo mode (where all prisoners are juvenile) } if (V.prisonCircuitIndex >= V.prisonCircuit.length) { diff --git a/src/init/storyInit.js b/src/init/storyInit.js index 2eafb65ba8a..1245717fa59 100644 --- a/src/init/storyInit.js +++ b/src/init/storyInit.js @@ -54,7 +54,7 @@ App.Intro.init = function() { V.weatherType = 1; V.weatherRemaining = 6; V.prisonCircuitIndex = random(0, V.prisonCircuit.length-1); - if (V.prisonCircuit[V.prisonCircuitIndex] === "juvenile detention" && (V.minimumSlaveAge > 16 || V.pedo_mode === 1)) { + if (V.prisonCircuit[V.prisonCircuitIndex] === "juvenile detention" && (V.minimumSlaveAge >= 16 || V.pedo_mode === 1)) { V.prisonCircuitIndex = 0; // skip juvenile detention if juvenile slaves are not allowed, or we're in pedo mode (where all prisoners are juvenile) } -- GitLab From 326e904024b77d462276ddf0739ca4da399f930b Mon Sep 17 00:00:00 2001 From: Svornost <11434-svornost@users.noreply.gitgud.io> Date: Thu, 30 Dec 2021 18:34:20 -0500 Subject: [PATCH 4/4] reduce overlap with existing prisons --- src/npc/generate/generateMarketSlave.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/npc/generate/generateMarketSlave.js b/src/npc/generate/generateMarketSlave.js index 0307992e505..2505d59dd5c 100644 --- a/src/npc/generate/generateMarketSlave.js +++ b/src/npc/generate/generateMarketSlave.js @@ -3184,7 +3184,7 @@ globalThis.generateMarketSlave = function(market = "kidnappers", numArcology = 1 // 12-14 in Canada, China, Saudi Arabia, Egypt, and most of Europe and Latin America // that's the Old World, though; an argument could be made for just using minimum slave age here... SGProp.minAge = Math.max(V.minimumSlaveAge, 10); - SGProp.maxAge = 17; + SGProp.maxAge = 16; SGProp.disableDisability = 1; if (random(0, 100) > 80 && (V.continent === "North America" || V.continent === "South America" || V.continent === "Australia")) { // indigenous kids are substantially more likely to end up in juvenile detention -- GitLab