diff --git a/src/js/descriptionWidgets.js b/src/js/descriptionWidgets.js index 867b672af09568dcc29de76aa9e1761adfe962fc..a5a9d374238b6dfb79375c2a97bb2856d0fcdec0 100644 --- a/src/js/descriptionWidgets.js +++ b/src/js/descriptionWidgets.js @@ -692,6 +692,145 @@ App.Desc.brand = function(slave, surface) { return r; }; +/** + * @param {App.Entity.SlaveState} slave + * @returns {string} Slave's scar. Slave is the slave in question, but call the body part without modifiers. Rather than using "left breast" and "right breast" just use "breast". The function will then describe any scars on the breasts, if present, in natural language. + */ +App.Desc.scar = function(slave, surface) { + "use strict"; + let r = ``; + /* eslint-disable no-unused-vars*/ + const { + he, him, his, hers, himself, boy, He, His + } = getPronouns(slave); + /* eslint-enable */ + function describeScarInt(slave, surface) { // scars can sometimes be an int. This function generates a reasonable description. It can later be expanded to apply to different body parts, or include features of the slave such as skin tone or weight + switch (slave.scar[surface]) { + case 1: + return "light scarring"; + case 2: + return "heavy scarring"; + case 3: + return "fresh scarring"; + case 4: + return "burns"; + case 5: + return ""; + case 6: + return ""; + case 7: + if (surface === "belly") { + return "a cSection"; + } else { + return "a surgical scar"; + } + default: + return slave[surface]; + } + } + if (State.variables.showBodyMods === 1) { + if (surface === "extra") { // Make a sentence that describes all body parts that aren't explicitly described elsewhere in longSlave. If you scar a slave on her thumb, for instance. But why. + let extraMarks = App.Desc.extraMarks(slave, "scar"); + extraMarks = Object.keys(extraMarks); + let length = extraMarks.length; + if (length === 0) { + return r; + } else if (length === 1) { + r += `${He} also has a single unusual scar: `; + } else { + r += `${He} also has several unusual scars: `; + } + + // If L/R parts of this object match, they will be described in the same phrase. Length is used only to calculate punctuation, so we prepare to skip. + for (const bodyPart of extraMarks) { + if (bodyPart.startsWith("left ")) { + let right = "right " + bodyPart.replace("left ", ""); + if (slave.scar[bodyPart] && slave.scar[right]) { + length--; + } + } + } + let counter = 0; + for (const bodyPart of extraMarks) { + counter++; + surface = App.Desc.oppositeSides(bodyPart); + if (slave.scar[surface.center]) { // center defined, body part has no mirror. + r += `${describeScarInt(slave, surface.center)} scared into the flesh of ${his} ${surface.center}`; + } else { // Center not defined, body part has a mirror. + if (!slave.scar[surface.left] && !slave.scar[surface.right]) { + // no marks + } else if (bodyPart.startsWith("right ") && slave.scar[surface.left]) { + // we already described it on the left + } else if (slave.scar[surface.left] === slave.scar[surface.right]) { + // matching places and marks + // note that the slave.scar object won't have slave.scar["upper armS"] with an S defined, just the left and right, so we just use the left since we know they match. + r += `${describeScarInt(slave, surface.left)} scared into the flesh of both ${his} ${surface.both}`; + } else if (slave.scar[surface.left] && slave.scar[surface.right]) { + // matching places but different marks + r += `both ${describeScarInt(slave, surface.left)} scared into the flesh of ${his} ${surface.left}, and ${describeScarInt(slave, surface.right)} scared into ${his} ${surface.right}`; + } else if (slave.scar[surface.left]) { + // left + r += `${describeScarInt(slave, surface.left)} scared into the flesh of ${his} ${surface.left}`; + } else if (slave.scar[surface.right]) { + // right + r += `${describeScarInt(slave, surface.right)} scared into the flesh of ${his} ${surface.right}`; + } + } + if (counter === length) { + r += `. `; + } else if (counter === length - 1) { + r += `, and `; + } else if (counter < length) { + r += `, `; + } + } + } else if (surface) { /* describes a single scared body part */ + if (surface === "belly" && setup.fakeBellies.includes(bellyAccessory) && slave.scar.belly) { + r += `${His} fake belly has the same scar, ${slave.scar.belly}, as ${his} real one. `; + } else { + surface = App.Desc.oppositeSides(surface); + if (slave.scar[surface.center]) { // center defined, body part has no mirror. + r += `${He} has ${describeScarInt(slave, surface.center)} scared into the flesh of ${his} ${surface.center}. `; + } else { // Center not defined, body part has a mirror. + if (!slave.scar[surface.left] && !slave.scar[surface.right]) { + // no marks + } else if (slave.scar[surface.left] === slave.scar[surface.right]) { + // matching places and marks + // note that the slave.scar object won't have slave.scar["upper armS"] with an S defined, just the left and right, so we just use the left since we know they match. + r += `${He} has ${describeScarInt(slave, surface.left)} scared into the flesh of both ${his} ${surface.both}. `; + } else if (slave.scar[surface.left] && slave.scar[surface.right]) { + // matching places but different marks + r += `${He} has both ${describeScarInt(slave, surface.left)} scared into the flesh of ${his} ${surface.left}, and ${describeScarInt(slave, surface.right)} scared into ${his} ${surface.right}. `; + } else if (slave.scar[surface.left]) { + // left + r += `${He} has ${describeScarInt(slave, surface.left)} scared into the flesh of ${his} ${surface.left}. `; + } else if (describeScarInt(slave, surface.right)) { + // right + r += `${He} has ${describeScarInt(slave, surface.right)} scared into the flesh of ${his} ${surface.right}. `; + } + } + } + } else { /* describes all scared body parts */ + for (let [key, value] of Object.entries(slave.scar)) { + if (r === ``) { + r += `${He} has `; + } + if (key === "belly" && setup.fakeBellies.includes(bellyAccessory) && slave.scar.belly) { + r += `${value} scared on both ${his} real belly and ${his} fake one, `; + } else { + r += `${value} scared into the flesh of ${his} ${key}, `; + } + } + if (r !== ``) { + r += `marking ${him} as yours. `; + } else { + r += `${His} body is unmarked by scars. `; + } + } + } + return r; +}; + /** * @param {App.Entity.SlaveState} slave * @returns {string} Description of slave's amputation, if present diff --git a/src/js/slaveStatsChecker.js b/src/js/slaveStatsChecker.js index f7cbea30b57ce64ec066a0b44f73ef619c8cf1b7..a6c249b727049df743a0c80a2c6dd8690ce8fec0 100644 --- a/src/js/slaveStatsChecker.js +++ b/src/js/slaveStatsChecker.js @@ -24,7 +24,8 @@ window.SlaveStatsChecker = (function() { V.piercingScore = piercingScore(slave); V.tatScore = tatScore(slave); V.brandScore = brandScore(slave); - return V.tatScore + V.piercingScore + V.brandScore; + V.scarScore = scarScore(slave); + return V.tatScore + V.piercingScore + V.brandScore + V.scarScore; } /** @@ -159,6 +160,17 @@ window.SlaveStatsChecker = (function() { return score; } + /** + * helper function, not callable + * @param {App.Entity.SlaveState} slave + * @returns {number} + */ + function scarScore(slave) { + let score = 0; + score += Object.getOwnPropertyNames(slave.scar).length; + return score; + } + /** * call as SlaveStatsChecker.isModded() * @param {App.Entity.SlaveState} slave @@ -168,7 +180,8 @@ window.SlaveStatsChecker = (function() { const tattoos = tatScore(slave); const piercings = piercingScore(slave); const brands = brandScore(slave); - const mods = piercings+tattoos; + const scars = scarScore(slave); + const mods = piercings+tattoos+scars; return (mods > 15 || (piercings > 8 && tattoos > 5) || brands > 1); } @@ -182,8 +195,9 @@ window.SlaveStatsChecker = (function() { const tattoos = tatScore(slave); const piercings = piercingScore(slave); const brands = brandScore(slave); + const scars = scarScore(slave); - return (!isModded(slave) && slave.corsetPiercing === 0 && piercings < 3 && tattoos < 2 && brands === 0); + return (!isModded(slave) && slave.corsetPiercing === 0 && piercings < 3 && tattoos < 2 && brands === 0 && scars <= 1); } }()); diff --git a/src/uncategorized/bodyModification.tw b/src/uncategorized/bodyModification.tw index d61a46ad7b9b957aea2247f4e9d093e473363c8f..1a40089777698b0cd5326f0e9923778d15623458 100644 --- a/src/uncategorized/bodyModification.tw +++ b/src/uncategorized/bodyModification.tw @@ -11,7 +11,7 @@ <<= SlaveFullName($activeSlave)>> is lying strapped down on the table in your body modification studio. $He is entirely at your mercy. -<<if $brandApplied || $degradation>> +<<if $brandApplied || $degradation || $scarApplied>> <<if $activeSlave.fuckdoll == 0>> <<if canSee($activeSlave)>>There's a mirror on the ceiling, so $he can see<<else>>$He can't see, so <<if canHear($activeSlave)>>you're careful to describe<<else>>$he must, by $himself, get a feel for<</if>><</if>> $his new appearance. <</if>> @@ -20,6 +20,48 @@ <<set $activeSlave.health -= 10>> <<unset $brandApplied>> <</if>> + <<if $scarApplied>> + <<if $scarTarget.local === "entire body">> + <<switch $scarDesign.local>> + <<case 1 2 3>> /* light heavy and fresh scarring */ + The best way to apply scarring to the entire body is with a good old fashioned whip. $His body is a mess of crisscrossed lines. + <<case 4>> + Your goal wasn't to make the distinct shape of a brand, but rather to permanently mar the skin with an open flame. + <<case 7>> + <<if $PC.medical === 100>> + Your medical mastery is perfect, so creating Frankenstein's monster was a deliberate work of art. + <<elseif $PC.medical > 0>> + Your medical skills are progressing, and the Frankenstein effect reminds you of your earliest attempts. + <<else>> + You really slashed away with your knife, but were careful not to allow $her to bleed out. + <</if>> + <<default>> + <</switch>> + No matter how you chose to apply it, scarring so much of $his body has @@.red; hurt $his health.@@ + <<set $activeSlave.health -= 20>> + <<else>> + <<switch $scarDesign.local>> + <<case 1 2 3>> /* light heavy and fresh scarring */ + You had no shortage of kinky and medical tools for applying scars. $His $activeSlave.skin $his $scarTarget.local is bleedy profusely. + <<case 4>> + Your goal wasn't to make the distinct shape of a brand, but rather to permanently mar the $activeSlave.skin skin of $his $scarTarget.local with an open flame. + <<case 7>> + <<if $PC.medical === 100>> + Your medical mastery is perfect, so creating such a scar was a deliberate act of degredation. + <<elseif $PC.medical > 0>> + Your medical skills are progressing, and the sloppy scar reminds you of your earliest attempts. + <<else>> + You really slashed away at $scarTarget.local with your knife, but were careful not to allow $her to bleed out. + <</if>> + <<default>> + <</switch>> + + No matter how you chose to apply it, being scarred @@.red; hurt $his health a little.@@ + <<set $activeSlave.health -= 10>> + <</if>> + Afterwards you seal the wounds with a white medical spray. Infection is no risk to your slave thanks to your curatives, but in order to form obvious scar tissue you want to keep air out and delay normal healing as long as possible. + <<unset $scarApplied>> + <</if>> <<if $activeSlave.fetish != "mindbroken" && $activeSlave.fuckdoll == 0>> <<if $degradation > 1>> <<if $degradation > 5>> @@ -1170,7 +1212,7 @@ Scars: <</for>> <<if (jQuery.isEmptyObject($activeSlave.scar))>> <br> - $His skin is not scared. + $His skin is not r. <</if>> <br> @@ -1180,19 +1222,36 @@ Use ''_printScar'' or choose another scar: | [[Heavy scarring|Body Modification][$scarDesign.local = 2]] | [[Fresh scarring|Body Modification][$scarDesign.local = 3]] | [[Burns|Body Modification][$scarDesign.local = 4]] -| [[A menacing scar|Body Modification][$scarDesign.local = 5]] -| [[An exotic scar|Body Modification][$scarDesign.local = 6]] +| <<link "A menacing scar">> + <<set $scarDesign.local = 5>> + <<if $scarTarget.local === "entire body">> + <<set $scarTarget.local === "left cheek">> + <</if>> + <<goto "Body Modification">> +<</link>> + +| <<link "An exotic scar">> + <<set $scarDesign.local = 6>> + <<if $scarTarget.local === "entire body">> + <<set $scarTarget.local === "left cheek">> + <</if>> + <<goto "Body Modification">> +<</link>> | [[A surgical scar|Body Modification][$scarDesign.local = 7]] + <br> Or design your own: <<textbox "$scarDesign.local" $scarDesign.local "Body Modification">> <br> Choose a site for scaring: + +[[Entire body|Body Modification][$scarTarget.local = "entire body"]] + /* Sorted head to toe */ /* Head */ -<<if $activeSlave.earShape != "none">>[[Ears|Body Modification][$scarTarget.local = "ear"]]<</if>> +<<if $activeSlave.earShape != "none">>| [[Ears|Body Modification][$scarTarget.local = "ear"]]<</if>> | [[Cheeks|Body Modification][$scarTarget.local = "cheek"]] | [[Neck|Body Modification][$scarTarget.local = "neck"]] @@ -1264,10 +1323,17 @@ Or a custom site: <<textbox "$scarTarget.local" $scarTarget.local "Body Modifica $He already has _printScar on $his $scarTarget.local. <<else>> <<link "Scar">> + <<if $scarTarget.local === "entire body">> + <<set _scarArray = ["left breast", "right breast", "back", "lower back", "left thigh", "right thigh"]>> + <<else>> + <<set _scarArray = [$scarTarget.local]>> + <</if>> + <<for _i to 0; _i < _scarArray.length; _i++>> + <<set $activeSlave.scar[_scarArray[_i]] = $scarDesign.local>> + <<run cashX(forceNeg($modCost), "slaveMod", $activeSlave)>> + <<set $degradation += 10>> + <</for>> <<set $scarApplied = 1>> - <<set $activeSlave.scar[$scarTarget.local] = $scarDesign.local>> - <<run cashX(forceNeg($modCost), "slaveMod", $activeSlave)>> - <<set $degradation += 10>> <<goto "Body Modification">> <</link>> with _printScar on the $scarTarget.local<<if $activeSlave.scar[$scarTarget.local]>>, covering the diff --git a/src/utility/descriptionWidgetsTattoos.tw b/src/utility/descriptionWidgetsTattoos.tw index 1d4327535c10a6f0ad383cc84231b8a61573a570..598e6dcfe407e77c06350f062c61cbfa6289ffb4 100644 --- a/src/utility/descriptionWidgetsTattoos.tw +++ b/src/utility/descriptionWidgetsTattoos.tw @@ -137,6 +137,7 @@ <</if>> /* App.Desc.boobsBrand handles boobBrands */ /*<<= App.Desc.brand($activeSlave, "breast")>>*/ + <<= App.Desc.scar($activeSlave, "breast")>> <</if>> <</widget>>