diff --git a/js/003-data/slaveMods.js b/js/003-data/slaveMods.js index 2f68317a67df088a7bfa9a60149b290d95a9cae8..a07bac2d4e89f34d6d5fe634d7891e17275c8016 100644 --- a/js/003-data/slaveMods.js +++ b/js/003-data/slaveMods.js @@ -479,7 +479,8 @@ App.Medicine.Modification.eyeShape = [ {value: "wide-eyed"} ]; -App.Medicine.Modification.naturalSkins = ["pure white", "ivory", "white", "extremely pale", "very pale", "pale", "extremely fair", "very fair", "fair", "light", "light olive", "tan", "olive", "bronze", "dark olive", "dark", "light beige", "beige", "dark beige", "light brown", "brown", "dark brown", "black", "ebony", "pure black", "black and white striped", "red", "yellow"]; +App.Medicine.Modification.naturalSkins = ["pure white", "ivory", "white", "extremely pale", "very pale", "pale", "extremely fair", "very fair", "fair", "light", "light olive", "tan", "olive", "bronze", "dark olive", "dark", "light beige", "beige", "dark beige", "light brown", "brown", "dark brown", "black", "ebony", "pure black"]; +App.Medicine.Modification.catgirlNaturalSkins = ["white", "brown", "black", "red", "yellow", "black and white striped"]; App.Medicine.Modification.dyedSkins = ["camouflage patterned", "dyed blue", "dyed white", "dyed gray", "dyed black", "dyed green", "dyed pink", "dyed red", "tiger striped", "dyed purple", "clown"]; App.Medicine.Modification.naturalNippleColors = ["black", "brown", "dark brown", "ebony", "ivory", "light brown", "pale pink", "pink"]; App.Medicine.Modification.eyebrowStyles = new Set(["shaved", "straight", "rounded", "natural", "slanted inwards", "slanted outwards", "high-arched", "elongated", "shortened", "curved"]); diff --git a/src/events/intro/pcAppearance.js b/src/events/intro/pcAppearance.js index 07206f540ecaa094af3377bc5e6e9acaa6f3e551..85d9523f96babf0c6f83fa72deebcd684ee8afd6 100644 --- a/src/events/intro/pcAppearance.js +++ b/src/events/intro/pcAppearance.js @@ -33,11 +33,11 @@ App.UI.Player.appearance = function(options, summary = false) { ); options.addOption("Your skin tone is", "skin", V.PC).showTextBox() - .addValueList(makeAList(App.Medicine.Modification.naturalSkins)); + .addValueList(makeAList(V.PC.race === "catgirl" ? App.Medicine.Modification.catgirlNaturalSkins : App.Medicine.Modification.naturalSkins)); if (V.cheatMode) { options.addOption("Your genetic skin tone is", "origSkin", V.PC).showTextBox() - .addValueList(makeAList(App.Medicine.Modification.naturalSkins)); + .addValueList(makeAList(V.PC.race === "catgirl" ? App.Medicine.Modification.catgirlNaturalSkins : App.Medicine.Modification.naturalSkins)); } if (V.cheatMode) { diff --git a/src/facilities/salon/salonPassage.js b/src/facilities/salon/salonPassage.js index ca637f7341ebd9e8df1f532b50824ebd8f131ba0..70974df9f6d32f10cbec07ec904bb287f42381ac 100644 --- a/src/facilities/salon/salonPassage.js +++ b/src/facilities/salon/salonPassage.js @@ -407,7 +407,8 @@ App.UI.salon = function(slave, cheat = false, startingGirls = false) { if (cheat) { option = options.addOption(`${His} natural skin color is`, "origSkin", slave).showTextBox().pulldown(); - for (const skin of App.Medicine.Modification.naturalSkins) { + const naturalSkins = slave.race === "catgirl" ? App.Medicine.Modification.catgirlNaturalSkins : App.Medicine.Modification.naturalSkins; + for (const skin of naturalSkins) { option.addValue(capFirstChar(skin), skin, () => slave.skin = slave.origSkin); } } diff --git a/src/facilities/surgery/analyzePregnancy.js b/src/facilities/surgery/analyzePregnancy.js index 0279720d92cfb4b99ca91043552ede6cc0322a25..26d217cb4ece8dc01152b916166802db56c90713 100644 --- a/src/facilities/surgery/analyzePregnancy.js +++ b/src/facilities/surgery/analyzePregnancy.js @@ -52,7 +52,7 @@ globalThis.analyzePregnancies = function(mother, cheat) { } option = options.addOption(`Skin tone: ${capFirstChar(genes.skin)}`, "skin", genes); if (cheat) { - option.showTextBox().pulldown().addValueList(App.Medicine.Modification.naturalSkins); + option.showTextBox().pulldown().addValueList(genes.race === "catgirl" ? App.Medicine.Modification.catgirlNaturalSkins : App.Medicine.Modification.naturalSkins); } option = options.addOption(`Intelligence index: ${genes.intelligence} out of 100`, "intelligence", genes); if (cheat) { diff --git a/src/js/utilsSlave.js b/src/js/utilsSlave.js index 86e0deb8154e496787504d8db84ad0fa1f9c7289..69e896443e915785143892e7081d32410e5a6381 100644 --- a/src/js/utilsSlave.js +++ b/src/js/utilsSlave.js @@ -1619,7 +1619,7 @@ globalThis.randomRaceSkin = function(raceName) { skin = jsEither(["fair", "light", "pale"]); break; case "catgirl": - skin = jsEither(["black", "white", "brown", "red", "black and white striped", "yellow"]); + skin = jsEither(App.Medicine.Modification.catgirlNaturalSkins); break; default: skin = jsEither(["dark", "light", "pale"]); diff --git a/src/npc/generate/generateGenetics.js b/src/npc/generate/generateGenetics.js index 3d0ec6b553f8c56f85c2702e77a48c4f95882a80..1b5ffaeeaff97f3673e1baac4b46fb2268a2867f 100644 --- a/src/npc/generate/generateGenetics.js +++ b/src/npc/generate/generateGenetics.js @@ -104,8 +104,8 @@ globalThis.generateGenetics = (function() { genes.inbreedingCoeff = ibc.kinship(mother, father); genes.nationality = setNationality(father, mother); genes.geneticQuirks = setGeneticQuirks(activeFather, activeMother, genes.gender); - genes.skin = setSkin(father, mother); genes.race = setRace(father, mother); + genes.skin = setSkin(father, mother, genes.race); genes.intelligence = setIntelligence(father, mother, activeMother, actor2, genes.inbreedingCoeff); genes.face = setFace(father, mother, activeMother, actor2, genes.geneticQuirks, genes.inbreedingCoeff); genes.faceShape = setFaceShape(father, mother, genes.geneticQuirks); @@ -284,8 +284,12 @@ globalThis.generateGenetics = (function() { return race; } - // skin - function setSkin(father, mother) { + /** + * @param {FC.Zeroable<FC.HumanState>} father + * @param {FC.HumanState} mother + * @param {FC.Race} race + */ + function setSkin(father, mother, race) { /** @type {FC.Zeroable<string>} */ let fatherSkin = 0; let dadSkinIndex; @@ -316,21 +320,54 @@ globalThis.generateGenetics = (function() { "ivory": 2, "pure white": 1 }; - const momSkinIndex = mother ? (skinToMelanin[mother.origSkin] || 13) : 8; - if (father !== 0) { - fatherSkin = father.origSkin; - } else if (fatherRace !== 0) { - fatherSkin = randomRaceSkin(fatherRace); - } - dadSkinIndex = fatherSkin !== 0 ? (skinToMelanin[fatherSkin] || 13) : 8; - const skinIndex = Math.round(Math.random() * (dadSkinIndex - momSkinIndex) + momSkinIndex); + const racialSkinToneBounds = { + "black": [21, 25], + "white": [4, 12], + "latina": [10, 22], + "indo-aryan": [9, 24], + "malay": [9, 24], + "pacific islander": [11, 24], + "amerindian": [11, 22], + "asian": [4, 15], + "middle eastern": [10, 21], + "semitic": [10, 21], + "southern european": [9, 15] + }; let prop = ""; - for (prop in skinToMelanin) { - if (!skinToMelanin.hasOwnProperty(prop)) { continue; } - if (skinIndex >= skinToMelanin[prop]) { return prop; } + if (race === "catgirl") { + // pick a random catgirl color, slightly preferring the parents' coloration if applicable + const catgirlColors = [...App.Medicine.Modification.catgirlNaturalSkins]; + if (mother.origSkin in catgirlColors) { + catgirlColors.push(mother.origSkin, mother.origSkin); + } + if (father && father.origSkin in catgirlColors) { + catgirlColors.push(father.origSkin, father.origSkin); + } + return catgirlColors.random(); + } else { + // blend the father's and mother's skintones + const momSkinIndex = mother ? (skinToMelanin[mother.origSkin] || 13) : 8; + if (father !== 0) { + fatherSkin = father.origSkin; + } else if (fatherRace !== 0) { + fatherSkin = randomRaceSkin(fatherRace); + } + dadSkinIndex = fatherSkin !== 0 ? (skinToMelanin[fatherSkin] || 13) : 8; + + let skinIndex = Math.round(Math.random() * (dadSkinIndex - momSkinIndex) + momSkinIndex); + if (race in racialSkinToneBounds) { + // don't exceed the skintone bounds of the already-selected race (note that "mixed race" does not have bounds) + skinIndex = Math.clamp(skinIndex, racialSkinToneBounds[race][0], racialSkinToneBounds[race][1]); + } + + // find the skin name associated with the blended skintone + for (prop in skinToMelanin) { + if (!skinToMelanin.hasOwnProperty(prop)) { continue; } + if (skinIndex >= skinToMelanin[prop]) { return prop; } + } + return prop; } - return prop; // skinIndex can be zero - now false? } /** Make sure a given eye color is a valid genetic eye color and not the result of some modification. diff --git a/src/npc/generate/generateNewSlaveJS.js b/src/npc/generate/generateNewSlaveJS.js index 7ce7bc3833a6df2ffacaceabba832784122418bc..691e0b754e72ccbf4a0d7039f7288f0488129ec1 100644 --- a/src/npc/generate/generateNewSlaveJS.js +++ b/src/npc/generate/generateNewSlaveJS.js @@ -1484,7 +1484,7 @@ globalThis.GenerateNewSlave = (function() { slave.lips = jsRandom(5, 30); slave.origSkin = jsEither(["pure black", "ebony", "black", "dark brown", "brown"]); slave.origHColor = jsEither(["jet black", "black", "black", "black", "dark brown"]); - slave.hStyle = jsEither(["crinkled", "neat"]); + slave.hStyle = jsEither(["afro", "neat"]); eyeColor(["brown"], true); break; case "white": @@ -1553,7 +1553,7 @@ globalThis.GenerateNewSlave = (function() { break; case "catgirl": slave.lips = jsRandom(5, 25); - slave.origSkin = jsEither(["white", "brown", "black", "red", "yellow", "black and white striped"]); + slave.origSkin = jsEither(App.Medicine.Modification.catgirlNaturalSkins); slave.origHColor = jsEither(["black", "white", "golden", "red", "brown"]); slave.hStyle = jsEither(["undercut", "neat"]); slave.faceShape = "feline"; diff --git a/src/player/electiveSurgery.js b/src/player/electiveSurgery.js index 55fd1e625c7099ed75a3063c5d7ed2106982131d..0332fe21ecabf2c2d2eb07404f2a0b4f81efbe56 100644 --- a/src/player/electiveSurgery.js +++ b/src/player/electiveSurgery.js @@ -168,7 +168,7 @@ App.UI.electiveSurgery = function() { if (V.PC.skin !== V.PC.origSkin) { choices.push({key: V.PC.origSkin, name: capFirstChar(`Restore natural ${V.PC.origSkin}`)}); } - for (const skin of App.Medicine.Modification.naturalSkins) { + for (const skin of [...App.Medicine.Modification.naturalSkins, ...App.Medicine.Modification.dyedSkins]) { choices.push({key: skin, name: capFirstChar(skin)}); }