diff --git a/js/003-data/gameVariableData.js b/js/003-data/gameVariableData.js index eb454a556895f595aa5c88e658319f8043548e24..85f94bc7fd4f82564d95bf74b638caf46f106288 100644 --- a/js/003-data/gameVariableData.js +++ b/js/003-data/gameVariableData.js @@ -244,6 +244,7 @@ App.Data.defaultGameStateVariables = { aiUpscaleScale: 1.75, aiUpscaler: "SwinIR_4x", aiWidth: 512, + aiAgeFilter: true, customClothesPrompts: {}, showAgeDetail: 1, diff --git a/src/002-config/fc-version.js b/src/002-config/fc-version.js index 1e4661de995c0d4a36d83225542f2a4402488488..ca6d7ad7f69408c39fef43c55792ef290f72b3ea 100644 --- a/src/002-config/fc-version.js +++ b/src/002-config/fc-version.js @@ -2,5 +2,5 @@ App.Version = { base: "0.10.7.1", // The vanilla version the mod is based off of, this should never be changed. pmod: "4.0.0-alpha.31", commitHash: null, - release: 1252, // When getting close to 2000, please remove the check located within the onLoad() function defined at line five of src/js/eventHandlers.js. + release: 1253, // 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/art/genAI/prompts/agePromptPart.js b/src/art/genAI/prompts/agePromptPart.js index 24636f7480aad824fd8c00480a162fe2342f6ef5..0cb18e4db57808f0aa152c8e938f3c7e08ff8ee3 100644 --- a/src/art/genAI/prompts/agePromptPart.js +++ b/src/art/genAI/prompts/agePromptPart.js @@ -19,8 +19,17 @@ App.Art.GenAI.AgePromptPart = class AgePromptPart extends App.Art.GenAI.PromptPa } else { ageTags = `elderly`; } - - return `${ageTags}, ${this.slave.visualAge} year old`; + let grade = ""; + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + if (this.slave.visualAge < 5) { + grade = "preschooler"; + } else if (this.slave.visualAge < 7) { + grade = "kindergartner"; + } else { + grade = ordinalSuffixWords(this.slave.visualAge - 6) + " grader"; + } + } + return `${ageTags}${this.slave.visualAge < 18 && V.aiAgeFilter ? `, ${grade}` : `, ${this.slave.visualAge} year old`}`; } /** @@ -28,7 +37,7 @@ App.Art.GenAI.AgePromptPart = class AgePromptPart extends App.Art.GenAI.PromptPa */ negative() { if (this.slave.visualAge < 20) { - return `elderly, adult, 30 year old, 40 year old`; + return `${this.slave.visualAge < 18 && V.aiAgeFilter ? `school, class, 20 year old, ` : ""}elderly, adult, 30 year old, 40 year old`; } else if (this.slave.visualAge < 30) { /* empty */ } else if (this.slave.visualAge < 40) { diff --git a/src/art/genAI/prompts/androidPromptPart.js b/src/art/genAI/prompts/androidPromptPart.js index 96f9d02aa4ebe8639d1493d3882c7402440ee50e..3e7f248b857d1284ae7c2fb9d07676641d3d9ddf 100644 --- a/src/art/genAI/prompts/androidPromptPart.js +++ b/src/art/genAI/prompts/androidPromptPart.js @@ -9,7 +9,7 @@ App.Art.GenAI.AndroidPromptPart = class AndroidPromptPart extends App.Art.GenAI. // limbs covered by fuckdoll suit } else if (App.Art.GenAI.sdClient.hasLora("hololive_roboco-san-10")) { - if (hasBothProstheticArms(this.slave) && hasBothProstheticLegs(this.slave)) { + if (hasBothProstheticArms(this.slave) && hasBothProstheticLegs(this.slave) && !(this.slave.visualAge < 18 && V.aiAgeFilter)) { parts.push(`<lora:hololive_roboco-san-10:1>, android, mechanical arms, mechanical legs`); } else if (hasBothProstheticArms(this.slave)) { parts.push(`<lora:hololive_roboco-san-10:1>, android, mechanical arms`); @@ -36,7 +36,7 @@ App.Art.GenAI.AndroidPromptPart = class AndroidPromptPart extends App.Art.GenAI. return; // space for negative prompt if needed NG } else if (hasBothProstheticArms(this.slave)) { return `mechanical legs`; - } else if (hasBothProstheticLegs(this.slave)) { + } else if (hasBothProstheticLegs(this.slave) && !(this.slave.visualAge < 18 && V.aiAgeFilter)) { return `mechanical arms`; } } diff --git a/src/art/genAI/prompts/arousalPromptPart.js b/src/art/genAI/prompts/arousalPromptPart.js index 7b133238092353b518bbae021f492db66ce4c569..b7945789378f9e617486995f5b85d0b1648384b6 100644 --- a/src/art/genAI/prompts/arousalPromptPart.js +++ b/src/art/genAI/prompts/arousalPromptPart.js @@ -4,7 +4,20 @@ App.Art.GenAI.ArousalPromptPart = class ArousalPromptPart extends App.Art.GenAI. */ positive() { let prompt = {terms: [], weight: 1}; - if (asSlave(this.slave)?.fuckdoll > 0) { + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + if (asSlave(this.slave)?.fuckdoll > 0) { + return undefined; + } + if (this.slave.energy > 60) { + prompt.terms.push("blush"); + } + if (this.slave.energy > 80) { + prompt.terms.push("sweat", "heavy breathing"); + } + if (this.slave.energy > 95) { + prompt.weight = 1.1; + } + } else if (asSlave(this.slave)?.fuckdoll > 0) { // fuckdolls are kept in a state of permanent arousal, with genitals exposed if (this.slave.vagina >= 0) { prompt.terms.push("pussy juice"); diff --git a/src/art/genAI/prompts/breastsPromptPart.js b/src/art/genAI/prompts/breastsPromptPart.js index c5a25b737272eab89307c355e1829f47c31f1305..e77b5ebdb565cada6b53409d8cc88f36ddfe1262 100644 --- a/src/art/genAI/prompts/breastsPromptPart.js +++ b/src/art/genAI/prompts/breastsPromptPart.js @@ -3,27 +3,29 @@ App.Art.GenAI.BreastsPromptPart = class BreastsPromptPart extends App.Art.GenAI. * @override */ positive() { + let prompt; if (this.slave.boobs < 300) { - return `flat chest`; + prompt = `flat chest`; } else if (this.slave.boobs < 400) { - return `small breasts, flat chest`; + prompt = `small breasts, flat chest`; } else if (this.slave.boobs < 500) { - return `small breasts`; - } else if (this.slave.boobs < 650) { - return `medium breasts`; - } else if (this.slave.boobs < 800) { - return `large breasts`; - } else if (this.slave.boobs < 1000) { - return `huge breasts`; + prompt = `small breasts`; + } else if (this.slave.boobs < 650 || (this.slave.visualAge < 6 && V.aiAgeFilter)) { + prompt = `medium breasts`; + } else if (this.slave.boobs < 800 || (this.slave.visualAge < 10 && V.aiAgeFilter)) { + prompt = `large breasts`; + } else if (this.slave.boobs < 1000 || (this.slave.visualAge < 18 && V.aiAgeFilter)) { + prompt = `huge breasts`; } else if (this.slave.boobs < 1400) { - return `huge breasts, large breasts`; + prompt = `huge breasts, large breasts`; } else { // bigger than H cup: best to use the LoRA if we can if (App.Art.GenAI.sdClient.hasLora("BEReaction")) { - return `<lora:BEReaction:1>, bereaction, breast expansion, (gigantic breasts:1.2)`; + prompt = `<lora:BEReaction:1>, bereaction, breast expansion, (gigantic breasts:1.2)`; } else { - return `(huge breasts:1.2), large breasts`; + prompt = `(huge breasts:1.2), large breasts`; } } + return this.slave.visualAge < 18 && V.aiAgeFilter ? prompt.replaceAll("breasts", "bosom") : prompt; } /** @@ -31,11 +33,11 @@ App.Art.GenAI.BreastsPromptPart = class BreastsPromptPart extends App.Art.GenAI. */ negative() { if (this.slave.boobs < 300) { - return `medium breasts, large breasts, huge breasts`; + return `medium breasts, large breasts, huge breasts${this.slave.visualAge < 18 && V.aiAgeFilter ? ", bare breasts, (nipples:1.1), areola, exposed chest" : ""}`; } else if (this.slave.boobs < 650) { - return; + return this.slave.visualAge < 18 && V.aiAgeFilter ? "bare breasts, (nipples:1.1), areola, exposed chest" : undefined; } else { - return `small breasts, flat chest`; + return `small breasts, flat chest${this.slave.visualAge < 18 && V.aiAgeFilter ? ", bare breasts, (nipples:1.3), areola, exposed chest" : ""}`; } } }; diff --git a/src/art/genAI/prompts/clothesPromptPart.js b/src/art/genAI/prompts/clothesPromptPart.js index 37f67e53d8e91b5633ea7b400a5a2a27aeea9154..52360dbdd76b092c7cf933908ce39e256a7be927 100644 --- a/src/art/genAI/prompts/clothesPromptPart.js +++ b/src/art/genAI/prompts/clothesPromptPart.js @@ -440,6 +440,253 @@ const clothesPrompts = { } }; +const clothesPromptsAgeControl = { + "no clothing": { + "positive": "strapless tube top, visible shoulders", + "negative": "", + }, + "chains": { + "positive": "metal chains collar, chainmail tube top, visible shoulders, chain belt, chainmail skirt", + "negative": "jeans, pants, skirt", + }, + "body oil": { + "positive": "(shiny skin, glistening skin, body oil:1.1), strapless swimsuit, visible shoulders", + "negative": "jeans", + }, + "a slutty qipao": { + "positive": "qipao, chinese clothing", + "negative": "jeans, nude, pussy, nipples", + }, + "spats and a tank top": { + "positive": "bike shorts, tank top", + "negative": "bike, jeans, nude, pussy, nipples", + }, + "uncomfortable straps": { + "positive": "leather straps top, visible shoulders, leather belt, leather straps skirt", + "negative": "jeans, pants, shorts", + }, + "shibari ropes": { + "positive": "macrame tube top, ropes, rope belt, macrame skirt", + "negative": "jeans, pants, shorts", + }, + "attractive lingerie": { + "positive": "strapless swimsuit, visible shoulders", + "negative": "jeans, pants", + }, + "attractive lingerie for a pregnant woman": { + "positive": "strapless swimsuit, visible shoulders", + "negative": "jeans, pants", + }, + "kitty lingerie": { // Broken for photorealistic models, probably works for anime models + "positive": "strapless hello kitty swimsuit, visible shoulders", + "negative": "cat ears, jeans", + }, + "a maternity dress": { + "positive": "wide dress, loose dress", + "negative": "jeans, nude, pussy, nipples", + }, + "a succubus outfit": { + "positive": "demon costume, red leather top, red leather miniskirt, black demon horns", + "negative": "jeans, nude, pussy, nipples", + }, + "a penitent nuns habit": { + "positive": "(latex nun habit:1.1), ropes", + "negative": "jeans", + }, + "a chattel habit": { + "positive": "(white latex nun habit:1.1), gold belt, sleveless, cleavage, visible shoulders", + "negative": "", + }, + "a string bikini": { + "positive": "strapless swimsuit", + "negative": "jeans,", + }, + "a scalemail bikini": { + "positive": "chainmail swimsuit", + "negative": "jeans", + }, + "striped panties": { + "positive": "strapless blue striped swimsuit", + "negative": "jeans", + }, + "clubslut netting": { + "colors": ["light blue", "pink", "lime green"], + "positive": "rave clothing, fishnet clothing, $color bodysuit, choker", + "negative": "jeans, pants, corset", + }, + "a slave gown": { + "positive": "ballgown, long dress, luxurious dress, cleavage, slave straps", + "negative": "jeans", + }, + "a halter top dress": { + "positive": "(halterneck:1.1), long dress, luxurious dress, backless dress", + "negative": "jeans", + }, + "a leotard": { + "positive": "leotard", + "negative": "jeans", + }, + "a monokini": { + "positive": "swimsuit", + "negative": "jeans", + }, + "an apron": { + "positive": "apron swimsuit", + "negative": "t-shirt, shirt, pants, shorts", + }, + "overalls": { + "positive": "overalls, visible shoulders, sleeveless", + "negative": "t-shirt, shirt, pants, shorts, topless", + }, + "a bunny outfit": { + "positive": "magazine bunny costume, backless leotard", + "negative": "jeans, nude, rabbit ears", + }, + "a gothic lolita dress": { + "positive": "gothic dress, short dress, thighhighs", + "negative": "jeans", + }, + "a button-up shirt and panties": { + "positive": "collared shirt, oversized clothes, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "a button-up shirt": { + "positive": "collared shirt, oversized clothes, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "a sweater": { + "positive": "only sweater, oversized clothes, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "a t-shirt": { + "positive": "only t-shirt, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "a tank-top": { + "positive": "only tank top, visible shoulders", + "negative": "jeans", + }, + "a tube top": { + "positive": "only tube top, visible shoulders", + "negative": "jeans", + }, + "an oversized t-shirt": { + "positive": "only t-shirt, oversized clothes, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "a bra": { + "positive": "white swimsuit top", + "negative": "jeans, pants, skirt, shorts", + }, + "a sports bra": { + "positive": "sports swimsuit top", + "negative": "jeans, pants, skirt, shorts", + }, + "a striped bra": { + "positive": "striped swimsuit top", + "negative": "jeans, pants, skirt, shorts", + }, + "pasties": { + "positive": "strapless tube top, visible shoulders", + "negative": "", + }, + "a tube top and thong": { + "positive": "tube top, visible shoulders", + "negative": "jeans", + }, + "a sweater and panties": { + "positive": "sweater, oversized clothes, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "a tank-top and panties": { + "positive": "tank top, visible shoulders", + "negative": "jeans", + }, + "a t-shirt and thong": { + "positive": "t-shirt, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "an oversized t-shirt and boyshorts": { + "positive": "t-shirt, oversized clothes, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "sport shorts and a sports bra": { + "positive": "sports swimsuit top", + "negative": "jeans, pants, skirt", + }, + "a t-shirt and panties": { + "positive": "t-shirt, swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "striped underwear": { + "positive": "striped swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "a thong": { + "positive": "tube top, visible shoulders", + "negative": "jeans", + }, + "a skimpy loincloth": { + "positive": "leather straples swimsuit", + "negative": "jeans, pants, skirt, shorts", + }, + "boyshorts": { + "positive": "swimsuit top", + "negative": "jeans", + }, + "panties": { + "positive": "swimsuit top", + "negative": "jeans", + }, + "panties and pasties": { + "positive": "swimsuit top", + "negative": "jeans", + }, + "cutoffs": { + "positive": "jean shorts, strapless tube top, visible shoulders", + "negative": "", + }, + "sport shorts": { + "positive": "sports swimsuit top, sport shorts", + "negative": "jeans, pants, skirt", + }, + "leather pants and a tube top": { + "positive": "leather pants, tube top, visible shoulders", + "negative": "jeans, skirt, shorts", + }, + "leather pants and pasties": { + "positive": "leather pants, swimsuit top", + "negative": "jeans, skirt, shorts", + }, + "leather pants": { + "positive": "leather pants, swimsuit top", + "negative": "jeans, skirt, shorts", + }, + "jeans": { + "positive": "jeans, swimsuit top", + "negative": "", + }, + "harem gauze": { + "positive": "harem outfit, loose dress", + "negative": "jeans, shorts", + }, + "slutty jewelry": { + "positive": "jewelry, gem, gold chains, armlet, visible shoulders", + "negative": "jeans, pants, shorts" + }, + "a bimbo outfit": { + "positive": "(pink tube top:1.1), cleavage", + "negative": "", + }, + "a slutty outfit": { + "positive": "(pink crop top:1.1), cleavage", + "negative": "", + }, + "a courtesan dress": { // Corset was messing stuff up, so I removed it + "positive": "(luxurious flowing dress:1.1), exposed shoulders, long sleeves, detached sleeves", + "negative": "jeans, nude, pussy, nipples", + }, +}; App.Art.GenAI.ClothesPromptPart = class ClothesPromptPart extends App.Art.GenAI.PromptPart { /** * @returns {string} @@ -469,7 +716,10 @@ App.Art.GenAI.ClothesPromptPart = class ClothesPromptPart extends App.Art.GenAI. * @returns {string} */ bodyPartReplacer(prompt) { // NG add penis, and penis size, and LoRA ties using this.slave.dick (size=/=inches, 3 is "Normal") and confirm hormone balance, add Null - if (this.slave.dick === 0 && this.slave.vagina === -1) { // Null slave + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return prompt; + } + if (this.slave.dick === 0 && this.slave.vagina === -1) { // Null slave if (App.Art.GenAI.sdClient.hasLora("nopussy_v1")) { return prompt.replace(/( *)pussy(,)*/g, " <lora:nopussy_v1:1>,"); // Removes pussy or penis for null slaves } else { @@ -512,6 +762,23 @@ App.Art.GenAI.ClothesPromptPart = class ClothesPromptPart extends App.Art.GenAI. return prompt; } + /** + * Adds missing words to the negative prompt is aiAgeControl is active + * @param {string} negPrompt + * @returns {string} + */ + addNegativeControl(negPrompt) { + const toAdd = ["penis", "pussy", "nude", "scrotum", "clitoris", "topless"]; + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + toAdd.forEach(w => { + if (!negPrompt.includes(w)) { + negPrompt += `${negPrompt.length > 0 ? ", " : ""}${w}`; + } + }); + } + return negPrompt + } + /** * @override */ @@ -520,7 +787,11 @@ App.Art.GenAI.ClothesPromptPart = class ClothesPromptPart extends App.Art.GenAI. if (V.customClothesPrompts.hasOwnProperty(this.getClothes()) && V.customClothesPrompts[this.getClothes()].positive !== '') { basePrompt = V.customClothesPrompts[this.getClothes()]; } else { - basePrompt = clothesPrompts[this.getClothes()]; + if (this.slave.visualAge < 18 && V.aiAgeFilter){ + basePrompt = clothesPromptsAgeControl[this.getClothes()] ?? clothesPrompts[this.getClothes()]; + } else { + basePrompt = clothesPrompts[this.getClothes()]; + } } const coloredPrompt = this.colorReplacer(basePrompt.positive, basePrompt.colors); @@ -532,9 +803,9 @@ App.Art.GenAI.ClothesPromptPart = class ClothesPromptPart extends App.Art.GenAI. */ negative() { if (V.customClothesPrompts.hasOwnProperty(this.getClothes()) && V.customClothesPrompts[this.getClothes()].negative !== '') { - return V.customClothesPrompts[this.getClothes()].negative; + return this.addNegativeControl(V.customClothesPrompts[this.getClothes()].negative + (this.slave.visualAge < 18 && V.aiAgeFilter) ? ", (nude:1.3), (nipples:1.1), areola" : ""); } else { - return clothesPrompts[this.getClothes()].negative; + return this.slave.visualAge < 18 && V.aiAgeFilter ? this.addNegativeControl(clothesPromptsAgeControl[this.getClothes()]?.negative ?? clothesPrompts[this.getClothes()].negative) : clothesPrompts[this.getClothes()].negative; } } }; diff --git a/src/art/genAI/prompts/collarPromptPart.js b/src/art/genAI/prompts/collarPromptPart.js index 787fdbf6e32f42a382f757f09bd3833ebb5ff9f1..097022fbd5082e9c980dcd270356c1cbdfc47fb0 100644 --- a/src/art/genAI/prompts/collarPromptPart.js +++ b/src/art/genAI/prompts/collarPromptPart.js @@ -20,6 +20,9 @@ App.Art.GenAI.CollarPromptPart = class CollarPromptPart extends App.Art.GenAI.Pr } else if (this.slave.collar === "satin choker") { return "satin choker"; } else if (this.slave.collar !== "none") { + if (this.slave.visualAge < 18 && V.aiAgeFilter && this.slave.collar.includes("counter")) { // Doesn't work, but removes "pregnancy" from the prompt + return "electronic display on neck"; + } return `${this.slave.collar} collar`; } } diff --git a/src/art/genAI/prompts/fakeBoobsPromptPart.js b/src/art/genAI/prompts/fakeBoobsPromptPart.js index 1420cb23d018de58637d4e0350d52ec0c0b699ce..360479ce407e44197e1fb5c45784671a141e5751 100644 --- a/src/art/genAI/prompts/fakeBoobsPromptPart.js +++ b/src/art/genAI/prompts/fakeBoobsPromptPart.js @@ -3,6 +3,9 @@ App.Art.GenAI.FakeBoobsPromptPart = class FakeBoobsPromptPart extends App.Art.Ge * @override */ positive() { + if (this.slave.visualAge < 18 && V.aiAgeFilter){ + return undefined; + } if (App.Art.GenAI.sdClient.hasLora("hugefaketits1")) { if (this.slave.boobsImplant >= 1000) { return `fake tits, <lora:hugefaketits1:1>`; diff --git a/src/art/genAI/prompts/genderPromptPart.js b/src/art/genAI/prompts/genderPromptPart.js index ac88f55005bf177f5eced16b220224e0bbeca902..ba5f3fdc472803168111f9879be7b4578b3635cf 100644 --- a/src/art/genAI/prompts/genderPromptPart.js +++ b/src/art/genAI/prompts/genderPromptPart.js @@ -31,29 +31,34 @@ App.Art.GenAI.GenderPromptPart = class GenderPromptPart extends App.Art.GenAI.Pr * @override */ positive() { + let prompt = undefined; if (this.isFeminine) { if (this.slave.race === "catgirl") { - return "catgirl, catperson <lora:CatgirlLoraV7:0.8>"; + prompt = "catgirl, catperson <lora:CatgirlLoraV7:0.8>"; } else if (this.slave.visualAge >= 20) { - return "woman"; + prompt = "woman"; } else { - return "girl"; + prompt = "girl"; } } else if (this.isMasculine) { if (this.slave.race === "catgirl") { - return "catboy, catperson <lora:CatgirlLoraV7:0.8>"; + prompt = "catboy, catperson <lora:CatgirlLoraV7:0.8>"; } else if (this.slave.visualAge >= 20) { - return "man"; + prompt = "man"; } else { - return "boy"; + prompt = "boy"; } } else { if (this.slave.race === "catgirl") { - return "catperson <lora:CatgirlLoraV7:0.8>"; + prompt = "catperson <lora:CatgirlLoraV7:0.8>"; } else { - return undefined; + prompt = undefined; } } + if (this.slave.visualAge < 18 && V.aiAgeFilter && typeof prompt !== "undefined") { + prompt = `${this.slave.visualAge} year old ${prompt}`; + } + return prompt; } /** diff --git a/src/art/genAI/prompts/hipsPromptPart.js b/src/art/genAI/prompts/hipsPromptPart.js index 28dfd4abd62fc2d82d0824989a50739446a6d732..3605b9a484f0b32a662135bdb9111ef20aee024f 100644 --- a/src/art/genAI/prompts/hipsPromptPart.js +++ b/src/art/genAI/prompts/hipsPromptPart.js @@ -3,6 +3,9 @@ App.Art.GenAI.HipsPromptPart = class HipsPromptPart extends App.Art.GenAI.Prompt * @override */ positive() { + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return undefined; + } if (this.slave.hips <= -2) { return `(narrow hips:1.1)`; } else if (this.slave.hips === -1) { diff --git a/src/art/genAI/prompts/piercingsPromptPart.js b/src/art/genAI/prompts/piercingsPromptPart.js index cbe0d13c567c2f29f28292cc167b3392b237bbbb..113de44e7044a30f6f144cf5af8e350057b5717a 100644 --- a/src/art/genAI/prompts/piercingsPromptPart.js +++ b/src/art/genAI/prompts/piercingsPromptPart.js @@ -4,9 +4,10 @@ App.Art.GenAI.PiercingsPromptPart = class PiercingsPromptPart extends App.Art.Ge */ positive() { const isFuckdoll = asSlave(this.slave)?.fuckdoll !== 0; + const skipIt = this.slave.visualAge < 18 && V.aiAgeFilter; let piercingParts = []; - if (this.slave.piercing.areola.weight > 0) { + if (this.slave.piercing.areola.weight > 0 && !skipIt) { if (!isFuckdoll || this.slave.race === "catgirl") { // TODO: needs exposure check let desc = this.slave.piercing.areola.desc ? (pronounsForSlaveProp(this.slave, this.slave.piercing.areola.desc) + ` `) : ``; piercingParts.push(`${desc}areola piercing`); @@ -28,13 +29,13 @@ App.Art.GenAI.PiercingsPromptPart = class PiercingsPromptPart extends App.Art.Ge let desc = this.slave.piercing.lips.desc ? (pronounsForSlaveProp(this.slave, this.slave.piercing.lips.desc) + ` `) : ``; piercingParts.push(`${desc}lip piercing`); } - if (this.slave.piercing.navel.weight > 0) { + if (this.slave.piercing.navel.weight > 0 && !skipIt) { if (!isFuckdoll || this.slave.race === "catgirl") { // covered by fuckdoll suit or fur let desc = this.slave.piercing.navel.desc ? (pronounsForSlaveProp(this.slave, this.slave.piercing.navel.desc) + ` `) : ``; piercingParts.push(`${desc}navel piercing`); } } - if (this.slave.piercing.nipple.weight > 0) { + if (this.slave.piercing.nipple.weight > 0 && !skipIt) { if (!isFuckdoll) { // TODO: needs exposure check let desc = this.slave.piercing.nipple.desc ? (pronounsForSlaveProp(this.slave, this.slave.piercing.nipple.desc) + ` `) : ``; piercingParts.push(`${desc}nipple piercing`); @@ -50,7 +51,7 @@ App.Art.GenAI.PiercingsPromptPart = class PiercingsPromptPart extends App.Art.Ge let desc = this.slave.piercing.tongue.desc ? (pronounsForSlaveProp(this.slave, this.slave.piercing.tongue.desc) + ` `) : ``; piercingParts.push(`${desc}tongue piercing`); } - if (this.slave.piercing.vagina.weight > 0 && this.slave.dick <= 0) { + if (this.slave.piercing.vagina.weight > 0 && this.slave.dick <= 0 && !skipIt) { let desc = this.slave.piercing.vagina.desc ? (pronounsForSlaveProp(this.slave, this.slave.piercing.vagina.desc) + ` `) : ``; piercingParts.push(`${desc}labia piercing`); } diff --git a/src/art/genAI/prompts/pregPromptPart.js b/src/art/genAI/prompts/pregPromptPart.js index 7c7a6daba374342f24fac2936b12c217ab047f61..5168c4cb79ccb61fb863104587c5466a2411b17c 100644 --- a/src/art/genAI/prompts/pregPromptPart.js +++ b/src/art/genAI/prompts/pregPromptPart.js @@ -3,6 +3,10 @@ App.Art.GenAI.PregPromptPart = class PregPromptPart extends App.Art.GenAI.Prompt * @override */ positive() { + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return undefined; + } + if (this.slave.belly >= 10000) { return "pregnant, full term"; } else if (this.slave.belly >= 5000) { diff --git a/src/art/genAI/prompts/pubicHairPromptPart.js b/src/art/genAI/prompts/pubicHairPromptPart.js index 9603f2190bb0d2b9fd9c2eaf916ffc034742b974..33695d7f28867930036ad5d05e80c7cc0fe3be12 100644 --- a/src/art/genAI/prompts/pubicHairPromptPart.js +++ b/src/art/genAI/prompts/pubicHairPromptPart.js @@ -3,6 +3,10 @@ App.Art.GenAI.PubicHairPromptPart = class PubicHairPromptPart extends App.Art.Ge * @override */ positive() { + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return undefined; + } + if (this.slave.pubicHStyle === "waxed" || this.slave.pubicHStyle === "bald" || this.slave.pubicHStyle === "hairless" || this.slave.physicalAge < Math.min(this.slave.pubertyAgeXX, this.slave.pubertyAgeXY)) { return; } diff --git a/src/art/genAI/prompts/stylePromptPart.js b/src/art/genAI/prompts/stylePromptPart.js index 2d68b65a50c2f1782fdd6063646dd8f059baeb18..e66c9e23d49cebfb4110f36d21e03a7cbefe9e4f 100644 --- a/src/art/genAI/prompts/stylePromptPart.js +++ b/src/art/genAI/prompts/stylePromptPart.js @@ -5,11 +5,23 @@ App.Art.GenAI.StylePromptPart = class StylePromptPart extends App.Art.GenAI.Prom positive() { switch (V.aiStyle) { case 0: // custom + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return "front-up portrait, (tight medium shot:1.3), " + V.aiCustomStylePos; // custom may break the control + } else { return V.aiCustomStylePos; + } case 1: // photorealistic - return "<lora:LowRA:0.5> full body portrait, photorealistic, dark theme, black background"; + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return "<lora:LowRA:0.5> front-up portrait, (tight medium shot:1.2), (focus on face:1.1), photorealistic, dark theme, black background"; + } else { + return "<lora:LowRA:0.5> full body portrait, photorealistic, dark theme, black background"; + } case 2: // anime/hentai + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return "front-up portrait, (tight medium shot:1.1), (focus on face:1.1), 2d, anime, hentai, dark theme, black background"; + } else { return "full body portrait, 2d, anime, hentai, dark theme, black background"; + } } } @@ -19,11 +31,23 @@ App.Art.GenAI.StylePromptPart = class StylePromptPart extends App.Art.GenAI.Prom negative() { switch (V.aiStyle) { case 0: // custom + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return "NSFW, full shot, medium full shot, full body portrait, waist, hips, bottom, navel, legs, " + V.aiCustomStyleNeg; + } else { return V.aiCustomStyleNeg; + } case 1: // photorealistic + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return "NSFW, greyscale, monochrome, cg, render, unreal engine, full shot, medium full shot, full body portrait, waist, hips, navel, bottom, legs, (head out of frame:1.1), (eye out of frame:1.2)"; + } else { return "greyscale, monochrome, cg, render, unreal engine, closeup, medium shot"; + } case 2: // anime/hentai + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return "NSFW, greyscale, monochrome, photography, 3d render, text, speech bubble, (head out of frame), full shot, medium full shot, full body portrait, waist, hips, navel, bottom, legs, head out of frame, eye out of frame"; + } else { return "greyscale, monochrome, photography, 3d render, text, speech bubble, closeup, medium shot"; + } } } }; diff --git a/src/art/genAI/prompts/tattoosPromptPart.js b/src/art/genAI/prompts/tattoosPromptPart.js index 925c0401d5bd795fa3e6d8fbfb8b890158ff7bf0..93db774024b7ec8083e16920571edf91fe1da1d0 100644 --- a/src/art/genAI/prompts/tattoosPromptPart.js +++ b/src/art/genAI/prompts/tattoosPromptPart.js @@ -11,14 +11,15 @@ App.Art.GenAI.TattoosPromptPart = class TattoosPromptPart extends App.Art.GenAI. if (this.slave.armsTat) { tattooParts.push(`${this.slave.armsTat} arm tattoo`); } - if (this.slave.legsTat) { + + if (this.slave.legsTat && !(this.slave.visualAge < 18 && V.aiAgeFilter)) { tattooParts.push(`${this.slave.legsTat} leg tattoo`); } if (this.slave.bellyTat) { tattooParts.push(`${this.slave.bellyTat} belly tattoo`); } if (this.slave.boobsTat) { // TODO: needs exposure check - tattooParts.push(`${this.slave.boobsTat} breast tattoo`); + tattooParts.push(`${this.slave.boobsTat} ${this.slave.visualAge < 18 && V.aiAgeFilter ? "chest" : "breast"} tattoo`); } if (tattooParts.length > 0) { diff --git a/src/art/genAI/prompts/waistPromptPart.js b/src/art/genAI/prompts/waistPromptPart.js index a5dbfe711c62601faaca5d340d9fb01e2418128e..fa68f8f768d67a9d4069c0f115f6db4d85489bf4 100644 --- a/src/art/genAI/prompts/waistPromptPart.js +++ b/src/art/genAI/prompts/waistPromptPart.js @@ -3,6 +3,9 @@ App.Art.GenAI.WaistPromptPart = class WaistPromptPart extends App.Art.GenAI.Prom * @override */ positive() { + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return undefined; + } if (this.slave.waist > 95) { return `very wide waist`; } else if (this.slave.waist > 10) { diff --git a/src/data/backwardsCompatibility/backwardsCompatibility.js b/src/data/backwardsCompatibility/backwardsCompatibility.js index 85464a77b02d8ecc79edbfecf67aa99315fb735e..02c4d17fd83d9e52292c03b4ce39c8129b5a3d13 100644 --- a/src/data/backwardsCompatibility/backwardsCompatibility.js +++ b/src/data/backwardsCompatibility/backwardsCompatibility.js @@ -2858,6 +2858,9 @@ App.Update.oldVersions = function(node) { } } } + if (V.releaseID < 1253) { + V.aiAgeFilter = true; + } node.append(`Done!`); }; diff --git a/src/gui/options/options.js b/src/gui/options/options.js index 7e75a275d9a44cc556c89970cafaeba974455bb9..6dc1f9bfb2b435c30dc08407abd8c0dfb8a5614c 100644 --- a/src/gui/options/options.js +++ b/src/gui/options/options.js @@ -1621,6 +1621,10 @@ App.UI.artOptions = function() { options.addOption("API URL", "aiApiUrl").showTextBox().addComment("The URL of the Automatic 1111 Stable Diffusion API."); App.UI.aiPromptingOptions(options); + options.addOption("Visual age filter", 'aiAgeFilter') + .addValue("Enabled", true).on().addValue("Disabled", false).off() + .addComment(`Creating images of characters that <U>appear to be</U> minors may be questionable in some countries, especially if they are generated by AI. Realistic images are even riskier due to their easy confusion with real ones. This option attempts to generate SFW images for them. <span class="warning">You may want to check you local laws before disabling this option.</span>`); + options.addOption("Caching Strategy", 'aiCachingStrategy') .addValue("Reactive", 'reactive').addValue("Static", 'static') .addComment("Caching behavior for AI images. Reactive pictures always reflect the state of the slave at the current time. Static refreshes every set amount of weeks, or manually. Images will not be brought across different strategies, but if the model is the same the generated images will be the same as well.");