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/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..a29a101338a5672d8a6a63d8c1e43713be1ab4b2 100644 --- a/src/art/genAI/prompts/arousalPromptPart.js +++ b/src/art/genAI/prompts/arousalPromptPart.js @@ -4,7 +4,17 @@ 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 (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..8de58d9d188a192dd22b357c0c757fdf2aefc5c3 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`; + prompt = `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`; - } else if (this.slave.boobs < 1400) { - return `huge breasts, large breasts`; + prompt = `medium breasts`; + } else if (this.slave.boobs < 800 || (this.slave.visualAge < 7 && V.aiAgeFilter)) { + prompt = `large breasts`; + } else if (this.slave.boobs < 1000 || (this.slave.visualAge < 13 && V.aiAgeFilter)) { + prompt = `huge breasts`; + } else if (this.slave.boobs < 1400 || (this.slave.visualAge < 18 && V.aiAgeFilter)) { + 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 ? ", (nipples:1.1), areola" : ""}`; } else if (this.slave.boobs < 650) { - return; + return this.slave.visualAge < 18 && V.aiAgeFilter ? "(nipples:1.1), areola" : undefined; } else { - return `small breasts, flat chest`; + return `small breasts, flat chest${this.slave.visualAge < 18 && V.aiAgeFilter ? ", (nipples:1.3), areola, bare breasts" : ""}`; } } }; diff --git a/src/art/genAI/prompts/clothesPromptPart.js b/src/art/genAI/prompts/clothesPromptPart.js index 37f67e53d8e91b5633ea7b400a5a2a27aeea9154..8676665372aa4d387e67c6ba9c02cd0609814296 100644 --- a/src/art/genAI/prompts/clothesPromptPart.js +++ b/src/art/genAI/prompts/clothesPromptPart.js @@ -5,6 +5,7 @@ const clothesPrompts = { "positive": "(completely nude:1.1), pussy, nipples", "negative": "clothes, jeans, underwear, pants, shorts, skirt, panties", }, + "a Fuckdoll suit": { // NG good gen requires LoRA, but below will work without LoRA as well "positive": "black latex bodysuit, long sleeves, <lora:xxmaskedxx_lora_v01:0.8> xxmaskedxx", "negative": "bare shoulders, exposed skin, exposed legs, exposed arms, short sleeves, nude, pussy, nipples", @@ -440,6 +441,321 @@ const clothesPrompts = { } }; +const clothesPromptsAgeControl = { + "no clothing": { + "positive": "strapless tube top, visible shoulders", + "negative": "", + }, + "chains": { + "positive": "metal chains collar, chainmail tube top, visible shoulders", + "negative": "", + }, + "body oil": { + "positive": "(shiny oiled skin:1.2), strapless tube top, visible shoulders", + "negative": "", + }, + "a slutty qipao": { + "positive": "qipao, chinese clothing", + "negative": "", + }, + "spats and a tank top": { + "positive": "bike shorts, tank top", + "negative": "bike", + }, + "uncomfortable straps": { + "positive": "leather straps top, visible shoulders", + "negative": "", + }, + "shibari ropes": { + "positive": "macrame tube top, ropes", + "negative": "", + }, + "restrictive latex": { + "positive": "latex bodysuit, long sleeves", + "negative": "bare shoulders, exposed skin, exposed legs, exposed arms, short sleeves", + }, + "a latex catsuit": { + "positive": "latex bodysuit, long sleeves", + "negative": "bare shoulders, exposed skin, exposed legs, exposed arms, short sleeves", + }, + "attractive lingerie": { + "positive": "strapless swimsuit, visible shoulders", + "negative": "jeans", + }, + "attractive lingerie for a pregnant woman": { + "positive": "strapless swimsuit, visible shoulders", + "negative": "", + }, + "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": "loose dress", + "negative": "jeans", + }, + "a succubus outfit": { + "positive": "demon costume, red leather top, black demon horns", + "negative": "jeans, nude, pussy, nipples", + }, + "a fallen nuns habit": { + "positive": "(latex nun habit:1.1)", + "negative": "jeans", + }, + "a penitent nuns habit": { + "positive": "(latex nun habit:1.1)", + "negative": "jeans", + }, + "a chattel habit": { + "positive": "(white latex nun habit:1.1), sleveless, plunging, deep V-neck, visible shoulders", + "negative": "", + }, + "a string bikini": { + "positive": "strapless swimsuit", + "negative": "jeans,", + }, + "a scalemail bikini": { + "positive": "scalemail swimsuit", + "negative": "jeans", + }, + "striped panties": { + "positive": "strapless striped swimsuit", + "negative": "jeans", + }, + "a cheerleader outfit": { + "positive": "(cheerleader outfit:1.1), crop top, midriff", + "negative": "jeans", + }, + "clubslut netting": { + "colors": ["light blue", "pink", "lime green"], + "positive": "rave clothing, mesh clothing, $color bodysuit, choker", + "negative": "jeans, pants, corset", + }, + "slutty business attire": { // Doesn't work well + "positive": "suit jacket, plunging, deep V-neck", + "negative": "jeans" + }, + "nice business attire": { + "positive": "suit jacket, collared shirt", + "negative": "jeans", + }, + "a ball gown": { + "positive": "ballgown, long dress, luxurious dress", + "negative": "jeans", + }, + "a slave gown": { + "positive": "ballgown, long dress, luxurious dress, plunging, deep V-neck, straps", + "negative": "jeans", + }, + "a halter top dress": { + "positive": "(halterneck:1.1), long dress, luxurious dress, backless", + "negative": "jeans", + }, + "an evening dress": { + "positive": "evening gown, long dress, luxurious dress", + "negative": "jeans", + }, + "a mini dress": { + "positive": "short dress, tight dress, strapless, plunging, deep V-neck", + "negative": "jeans", + }, + "a comfortable bodysuit": { + "positive": "latex bodysuit, long sleeves", + "negative": "bare shoulders, exposed skin, exposed legs, exposed arms, short sleeves", + }, + "a leotard": { + "positive": "leotard", + "negative": "jeans", + }, + "a monokini": { + "positive": "swimsuit", + "negative": "jeans", + }, + "an apron": { + "positive": "apron swimsuit", + "negative": "", + }, + "overalls": { + "positive": "overalls, visible shoulders, sleeveless", + "negative": "shirt, pants, shorts, topless", + }, + "a bunny outfit": { + "positive": "magazine bunny costume, leotard", + "negative": "jeans, nude, rabbit ears", + }, + "a slutty maid outfit": { + "positive": "maid, minidress, apron, white shirt, plunging, deep V-neck", + "negative": "jeans", + }, + "a nice maid outfit": { + "positive": "maid, dress, apron, white shirt", + "negative": "jeans", + }, + "a slutty nurse outfit": { + "positive": "nurse, white jacket, plunging, deep V-neck", + "negative": "jeans, shirt", + }, + "a gothic lolita dress": { + "positive": "gothic, short dress", + "negative": "jeans", + }, + "a slutty pony outfit": { // Not sure about what a pony outfit is + "positive": "latex bodysuit, long sleeves, plunging, deep V-neck", + "negative": "nude", + }, + "a button-up shirt and panties": { + "positive": "collared shirt, oversized clothes", + "negative": "", + }, + "a button-up shirt": { + "positive": "collared shirt, oversized clothes", + "negative": "", + }, + "a sweater": { + "positive": "only sweater, oversized clothes", + "negative": "", + }, + "a t-shirt": { + "positive": "only t-shirt", + "negative": "", + }, + "a tank-top": { + "positive": "only tank top, visible shoulders", + "negative": "", + }, + "a tube top": { + "positive": "only tube top, visible shoulders", + "negative": "", + }, + "an oversized t-shirt": { + "positive": "only t-shirt, oversized clothes", + "negative": "", + }, + "a bra": { + "positive": "white swimsuit top", + "negative": "", + }, + "a sports bra": { + "positive": "sports swimsuit top", + "negative": "", + }, + "a striped bra": { + "positive": "striped swimsuit top", + "negative": "", + }, + "pasties": { + "positive": "strapless tube top, visible shoulders", + "negative": "", + }, + "a tube top and thong": { + "positive": "tube top, visible shoulders", + "negative": "", + }, + "a sweater and panties": { + "positive": "sweater, oversized clothes", + "negative": "", + }, + "a tank-top and panties": { + "positive": "tank top, visible shoulders", + "negative": "", + }, + "a t-shirt and thong": { + "positive": "t-shirt", + "negative": "", + }, + "an oversized t-shirt and boyshorts": { + "positive": "t-shirt, oversized clothes", + "negative": "", + }, + "sport shorts and a t-shirt": { + "positive": "sports t-shirt", + "negative": "", + }, + "sport shorts and a sports bra": { + "positive": "sports swimsuit top", + "negative": "", + }, + "a t-shirt and panties": { + "positive": "t-shirt", + "negative": "", + }, + "striped underwear": { + "positive": "striped swimsuit top", + "negative": "", + }, + "a thong": { + "positive": "tube top, visible shoulders", + "negative": "", + }, + "a skimpy loincloth": { + "positive": "strapless tube top", + "negative": "", + }, + "boyshorts": { + "positive": "swimsuit top", + "negative": "", + }, + "panties": { + "positive": "swimsuit top", + "negative": "", + }, + "panties and pasties": { + "positive": "swimsuit top", + "negative": "", + }, + "cutoffs": { + "positive": "strapless tube top, visible shoulders", + "negative": "", + }, + "sport shorts": { + "positive": "sports swimsuit top", + "negative": "", + }, + "a sweater and cutoffs": { + "positive": "sweater", + "negative": "", + }, + "leather pants and a tube top": { + "positive": "tube top, visible shoulders", + "negative": "", + }, + "a t-shirt and jeans": { + "positive": "t-shirt", + "negative": "", + }, + "leather pants and pasties": { + "positive": "swimsuit top", + "negative": "", + }, + "leather pants": { + "positive": "swimsuit top", + "negative": "", + }, + "jeans": { + "positive": "swimsuit top", + "negative": "", + }, + "harem gauze": { + "positive": "harem outfit, loose dress", + "negative": "", + }, + "slutty jewelry": { + "positive": "jewelry, gem, gold chains, armlet, visible shoulders", + "negative": "" + }, + "a Santa dress": { + "positive": "santa costume, santa dress", + "negative": "" + }, + "a bimbo outfit": { + "positive": "(pink tube top:1.1), plunging, deep V-neck", + "negative": "", + }, + "a slutty outfit": { + "positive": "(pink crop top:1.1), plunging, deep V-neck", + "negative": "", + }, +}; App.Art.GenAI.ClothesPromptPart = class ClothesPromptPart extends App.Art.GenAI.PromptPart { /** * @returns {string} @@ -469,7 +785,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 +831,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", "nipples", "nude", "scrotum", "clitoris"]; + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + toAdd.forEach(w => { + if (!negPrompt.includes(w)) { + negPrompt += `, ${w}`; + } + }); + } + return negPrompt + } + /** * @override */ @@ -520,7 +856,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 +872,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.addNegativeControl(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/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..714b780bbf555f638bf86235d749e1b63da21b7d 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; + } + 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..1c6d40900f2f626b68468f24db83ff1c365f74b1 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: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:1.3), 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: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 "full body portrait, full body, medium shot, " + V.aiCustomStyleNeg; + } else { return V.aiCustomStyleNeg; + } case 1: // photorealistic + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return "greyscale, monochrome, cg, render, unreal engine, full body portrait, medium shot"; + } else { return "greyscale, monochrome, cg, render, unreal engine, closeup, medium shot"; + } case 2: // anime/hentai + if (this.slave.visualAge < 18 && V.aiAgeFilter) { + return "greyscale, monochrome, photography, 3d render, text, speech bubble, full body portrait, medium shot"; + } 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/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..2f2833c2b6c14568b370b194c9eeec81fcc18ddf 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(`Some images of characters that appear to be minors may be questionable in some countries, even if generated by AI. This option tries to generate SFW images for them. <span class="warning">Disable it at your own risk.</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.");