diff --git a/src/Mods/SpecialForce/SpecialForce.js b/src/Mods/SpecialForce/SpecialForce.js index ba69c9cc2cde609b346a4fcd84cd0b3132d175e1..dc53b2c38a1a24b3924d8d15b79f41dc01696d13 100644 --- a/src/Mods/SpecialForce/SpecialForce.js +++ b/src/Mods/SpecialForce/SpecialForce.js @@ -47,7 +47,7 @@ App.SF.unlocked = (function() { function garage(mode = 'standard') { if (V.SF.Squad.Firebase >= 1 && V.terrain !== "oceanic") { - if (V.SF.Squad.AV + V.SF.Squad.TV + V.SF.Squad.PGT > 0 || mode === 'cheat') { + if (V.SF.Squad.AV + V.SF.Squad.TV + V.SF.Squad.PGT >= 0 || mode === 'cheat') { return true; } } @@ -56,7 +56,7 @@ App.SF.unlocked = (function() { function hangar(mode = 'standard') { if (V.SF.Squad.Firebase >= 4) { - if (V.SF.Squad.AA + V.SF.Squad.TA + V.SF.Squad.SpacePlane + V.SF.Squad.GunS > 0 || mode === 'cheat') { + if (V.SF.Squad.AA + V.SF.Squad.TA + V.SF.Squad.SpacePlane + V.SF.Squad.GunS >= 0 || mode === 'cheat') { return true; } } @@ -65,7 +65,7 @@ App.SF.unlocked = (function() { function launchBay(mode = 'standard') { if (secondTier()) { - if (V.SF.Squad.Satellite + V.SF.Squad.GiantRobot + V.SF.Squad.MissileSilo > 0 || mode === 'cheat') { + if (V.SF.Squad.Satellite + V.SF.Squad.GiantRobot + V.SF.Squad.MissileSilo >= 0 || mode === 'cheat') { return true; } } @@ -74,7 +74,7 @@ App.SF.unlocked = (function() { function navalYard(mode = 'standard') { if (secondTier() && (V.terrain === "oceanic" || V.terrain === "marine")) { - if (V.SF.Squad.AircraftCarrier + V.SF.Squad.Sub + V.SF.Squad.HAT > 0 || mode === 'cheat') { + if (V.SF.Squad.AircraftCarrier + V.SF.Squad.Sub + V.SF.Squad.HAT >= 0 || mode === 'cheat') { return true; } } diff --git a/src/Mods/SpecialForce/editSF.tw b/src/Mods/SpecialForce/editSF.tw index 14a4c0b768707548323d54c143d1b4822ef7e976..b0797d7bbfa8f1046d9b382c2fa209f18afbb000 100644 --- a/src/Mods/SpecialForce/editSF.tw +++ b/src/Mods/SpecialForce/editSF.tw @@ -2,7 +2,10 @@ <<script>> for (let i in V.SF.Squad) { - V.SF.Squad[i] = Math.clamp(V.SF.Squad[i], 0, App.SF.upgrades.currentUnitMax('V.SF.Squad[i]')); + const v = String([i]); + if (V.SF.Squad[i] > App.SF.upgrades.currentUnitMax(v)) { + V.SF.Squad[i] = App.SF.upgrades.currentUnitMax(v); + } } <</script>> @@ -10,47 +13,47 @@ for (let i in V.SF.Squad) { $nextButton = "Back to $SF.Lower's Firebase", $nextLink = "Firebase", _options = new App.UI.OptionsGroup()>> __Upgrades__: _size/_max(<<= (_size/_max).toFixed(2)>>%) -<<run _options.addOption("''Firebase: ''", "Firebase", V.SF.Squad).showTextBox()>> -<<run _options.addOption("''Armoury: ''", "Armoury", V.SF.Squad).showTextBox()>> -<<run _options.addOption("''Drugs: ''", "Drugs", V.SF.Squad).showTextBox()>> +<<run _options.addOption("''Firebase: '' (current max <<= App.SF.upgrades.currentUnitMax('Firebase')>>)", "Firebase", V.SF.Squad).showTextBox()>> +<<run _options.addOption("''Armoury: '' (current max <<= App.SF.upgrades.currentUnitMax('Armoury')>>)", "Armoury", V.SF.Squad).showTextBox()>> +<<run _options.addOption("''Drugs: '' (current max <<= App.SF.upgrades.currentUnitMax('Drugs')>>)", "Drugs", V.SF.Squad).showTextBox()>> <<if $SF.Squad.Firebase >= 2>> - <<run _options.addOption("''Drones: ''", "Drones", V.SF.Squad).showTextBox()>> + <<run _options.addOption("''Drones: '' (current max <<= App.SF.upgrades.currentUnitMax('Drones')>>)", "Drones", V.SF.Squad).showTextBox()>> <</if>> <<if App.SF.unlocked.garage('cheat')>> - <<run _options.addOption("''Garage:''<br> ''Attack Vehicles: ''", "AV", V.SF.Squad).showTextBox()>> - <<run _options.addOption(" ''Transport Vehicles: ''", "TV", V.SF.Squad).showTextBox()>> + <<run _options.addOption("''Garage:''<br> ''Attack Vehicles: '' (current max <<= App.SF.upgrades.currentUnitMax('AV')>>)", "AV", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Transport Vehicles: '' (current max <<= App.SF.upgrades.currentUnitMax('TV')>>)", "TV", V.SF.Squad).showTextBox()>> <<if _T1>> - <<run _options.addOption(" ''Prototype Goliath Tank: ''", "PGT", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Prototype Goliath Tank: '' (current max <<= App.SF.upgrades.currentUnitMax('PGT')>>)", "PGT", V.SF.Squad).showTextBox()>> <</if>> <</if>> <<if App.SF.unlocked.hangar('cheat')>> - <<run _options.addOption("''Hangar:''<br> ''Attack Planes: ''", "AA", V.SF.Squad).showTextBox()>> - <<run _options.addOption(" ''Transport Planes: ''", "TA", V.SF.Squad).showTextBox()>> + <<run _options.addOption("''Hangar:''<br> ''Attack Planes: '' (current max <<= App.SF.upgrades.currentUnitMax('AA')>>)", "AA", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Transport Planes: '' (current max <<= App.SF.upgrades.currentUnitMax('TA')>>)", "TA", V.SF.Squad).showTextBox()>> <<if _T1>> - <<run _options.addOption(" ''Spaceplane: ''", "SpacePlane", V.SF.Squad).showTextBox()>> - <<run _options.addOption(" ''Gunship: ''", "GunS", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Spaceplane: '' (current max <<= App.SF.upgrades.currentUnitMax('SpacePlane')>>)", "SpacePlane", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Gunship: '' (current max <<= App.SF.upgrades.currentUnitMax('GunS')>>)", "GunS", V.SF.Squad).showTextBox()>> <</if>> <</if>> <<if App.SF.unlocked.launchBay('cheat')>> - <<run _options.addOption("''Launch Bay:''<br> ''Satellite: ''", "Satellite", V.SF.Squad).showTextBox()>> + <<run _options.addOption("''Launch Bay:''<br> ''Satellite: '' (current max <<= App.SF.upgrades.currentUnitMax('Satellite')>>)", "Satellite", V.SF.Squad).showTextBox()>> <<if $terrain != "oceanic">> - <<run _options.addOption(" ''Giant Robot: ''", "GiantRobot", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Giant Robot: '' (current max <<= App.SF.upgrades.currentUnitMax('GiantRobot')>>)", "GiantRobot", V.SF.Squad).showTextBox()>> <</if>> - <<run _options.addOption(" ''Cruise Missile: ''", "MissileSilo", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Cruise Missile: '' (current max <<= App.SF.upgrades.currentUnitMax('MissileSilo')>>)", "MissileSilo", V.SF.Squad).showTextBox()>> <</if>> <<if App.SF.unlocked.navalYard('cheat')>> - <<run _options.addOption("<br><br>''Naval Yard:''<br> ''Aircraft Carrier: ''", "AircraftCarrier", V.SF.Squad).showTextBox()>> - <<run _options.addOption(" ''Submarine: ''", "Sub", V.SF.Squad).showTextBox()>> - <<run _options.addOption(" ''Amphibious Transport: ''", "HAT", V.SF.Squad).showTextBox()>> + <<run _options.addOption("<br><br>''Naval Yard:''<br> ''Aircraft Carrier: '' (current max <<= App.SF.upgrades.currentUnitMax('AircraftCarrier')>>)", "AircraftCarrier", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Submarine: '' (current max <<= App.SF.upgrades.currentUnitMax('Sub')>>)", "Sub", V.SF.Squad).showTextBox()>> + <<run _options.addOption(" ''Amphibious Transport: '' (current max <<= App.SF.upgrades.currentUnitMax('HAT')>>)", "HAT", V.SF.Squad).showTextBox()>> <</if>> <<includeDOM _options.render()>> <<if $SF.FS.Tension !== -1>> <br><br>The Colonel's current Tension: <<textbox "$SF.FS.Tension" $SF.FS.Tension "editSF">> -<</if>> \ No newline at end of file +<</if>> diff --git a/src/data/backwardsCompatibility/datatypeCleanup.js b/src/data/backwardsCompatibility/datatypeCleanup.js index ce7c3899fabe8b1ad49dc3f5ed4c8c0cda68fc9d..dc624ea183f2e307e7e86eadab8890c6924a7cfc 100644 --- a/src/data/backwardsCompatibility/datatypeCleanup.js +++ b/src/data/backwardsCompatibility/datatypeCleanup.js @@ -1778,8 +1778,7 @@ App.Entity.Utils.GenePoolRecordCleanup = (function() { "counter", "custom", "porn", "prestige", "prestigeDesc", "subTarget", "relationship", "relationshipTarget", "rivalry", "rivalryTarget", - "pronoun", "possessive", "possessivePronoun", "objectReflexive", "object", "noun", - "weekAcquired", "HGExclude", + "weekAcquired", "HGExclude", "StudExclude", "daughters", "sisters", "origin", "canRecruit", "choosesOwnAssignment", "assignment", @@ -1790,6 +1789,7 @@ App.Entity.Utils.GenePoolRecordCleanup = (function() { "eyewear", "earwear", "preg", "pregSource", "pregType", "pregAdaptation", "labor", "bellyAccessory", + "breedingMark", "clitSetting", "rules", "useRulesAssistant", @@ -1799,11 +1799,11 @@ App.Entity.Utils.GenePoolRecordCleanup = (function() { "makeup", "nails", "vaginalAccessory", "vaginalAttachment", "dickAccessory", "nipplesAccessory", "armAccessory", "legAccessory", "buttplug", "buttplugAttachment", - "fetishKnown", + "fetishKnown", "attrKnown", "rudeTitle", "currentRules", "induce", - "induceLactation", "boobsMilk", + "induceLactation", "boobsMilk", "lactation", "lactationAdaptation", "lactationDuration", "mpreg", "inflation", "inflationType", "inflationMethod", "milkSource", "cumSource", "burst", @@ -1814,7 +1814,7 @@ App.Entity.Utils.GenePoolRecordCleanup = (function() { "choosesOwnChastity", "pregControl", "death", - "onDiet", + "onDiet", "weightDirection", "prematureBirth", "slaveCost", "NCSyouthening", diff --git a/src/data/backwardsCompatibility/updateSlaveObject.js b/src/data/backwardsCompatibility/updateSlaveObject.js index 0e63c24a3c0498114559e63f64870f2c5a23a9ba..aecfcbe7fa6955dd6bcd935feeccb640287555e0 100644 --- a/src/data/backwardsCompatibility/updateSlaveObject.js +++ b/src/data/backwardsCompatibility/updateSlaveObject.js @@ -61,7 +61,7 @@ App.Update.Slave = function(slave, genepool = false) { slave.pubertyXY = 0; } } - if (slave.genetics === undefined) { slave.genetics = {}; } + if (slave.genetics !== undefined) { delete slave.genetics; } if (slave.geneMods === undefined) { slave.geneMods = {NCS: 0, rapidCellGrowth: 0}; } if (slave.inducedNCS !== undefined) { slave.geneMods.NCS = slave.inducedNCS; diff --git a/src/events/RETS/reSiblingTussle.js b/src/events/RETS/reSiblingTussle.js new file mode 100644 index 0000000000000000000000000000000000000000..4c7e8301aa4c3920df3ad8b437da658ad5eb9fc8 --- /dev/null +++ b/src/events/RETS/reSiblingTussle.js @@ -0,0 +1,237 @@ +App.Events.RETSSiblingTussle = class RETSSiblingTussle extends App.Events.BaseEvent { + eventPrerequisites() { + return [ + () => V.minimumSlaveAge < 18 + ]; + } + + actorPrerequisites() { + return [ + [ // event slave + s => s.fetish !== "mindbroken", + hasAnyArms, + hasBothLegs, + s => s.actualAge < 18, + s => s.sisters > 0 + ], + [ // and her sibling + s => s.fetish !== "mindbroken", + hasAnyArms, + hasBothLegs, + isSlaveAvailable, + s => s.actualAge < 18, + s => areSisters(getSlave(this.actors[0]), s) > 0, // sibling check + s => App.Utils.jobForAssignment(getSlave(this.actors[0]).assignment).facility.employeesIDs().has(s.ID), // lives in the same facility + ] + ]; + } + + execute(node) { + /** @type {Array<App.Entity.SlaveState>} */ + let [sib1, sib2] = this.actors.map(a => getSlave(a)); + const { + he, his, him + } = getPronouns(sib1); + const { + He2, he2, his2, him2 + } = getPronouns(sib2).appendSuffix("2"); + const hostFacility = App.Utils.jobForAssignment(getSlave(this.actors[0]).assignment).facility; + + V.nextLink = "Next Week"; + + App.Events.drawEventArt(node, [sib1, sib2], "no clothing"); + + let t = []; + t.push(`As you're conducting your morning rounds, you hear a commotion coming from the shower and dressing area of ${hostFacility.name}.`); + App.Events.addParagraph(node, t); + + t = []; + t.push(`Like most siblings who haven't yet developed mature conflict-resolution skills,`); + t.push(App.UI.DOM.slaveDescriptionDialog(sib1)); + t.push(`and`); + t.push(contextualIntro(sib1, sib2, "DOM")); + t.push(`sometimes get physical when they annoy one another. Today a tussle has broken out over some minor slight.`); + App.Events.addParagraph(node, t); + + t = []; + t.push(`The other slaves around them are mostly ignoring them in their haste to prepare for their workday, but the still wet and naked ${sib1.slaveName} and ${sib2.slaveName} sure manage to get your attention as they hit, kick, and wrestle with each other. You doubt they'll do any lasting damage, and maybe getting some of that hostile energy out will make them more compliant as the day goes on, but you also consider whether imposing some discipline might be a good idea.`); + App.Events.addParagraph(node, t); + + App.Events.addResponses(node, [ + new App.Events.Result("Make them kiss and make up", kissAndMakeUp), // don't think there's a need for an incest guard here, they'll break off early if necessary + new App.Events.Result("Punish them together", punish), + new App.Events.Result("Just watch and see what happens", watch) + ]); + + function kissAndMakeUp() { + const frag = document.createDocumentFragment(); + + t = []; + t.push(`You walk into the dressing area and clear your throat loudly. The other slaves pause what they're doing and turn to look at you, either from devotion or fear.`); + t.push(`After a few seconds, ${sib2.slaveName} realizes that things have gone strangely quiet and scrambles to stand and pay attention to you, and ${sib1.slaveName} follows suit when ${he} realizes that the nature of their fight has changed.`); + App.Events.addParagraph(frag, t); + + t = []; + t.push(`You tell the rest of the slaves to go back to what they were doing while you talk to ${sib1.slaveName} and ${sib2.slaveName}. You explain to the two of them that they have responsibilities to attend to, and that doesn't include fighting with each other, and order them to apologize to one another and kiss. When ${sib2.slaveName} tries to pin the blame for the fight on ${sib1.slaveName}, you cut ${him2} off and tell ${him2} that you're not interested in who started it, just how they're going to resolve it, and that ${he2} won't like what happens if you think ${he2}'s backtalking you.`); + App.Events.addParagraph(frag, t); + + t = []; + t.push(`${He2} gets the idea, and apologizes (with somewhat questionable sincerity) to ${sib1.slaveName} for ${his2} part in things. You turn to ${sib1.slaveName} expectantly, and ${he} also apologizes to ${his} ${relativeTerm(sib1, sib2)}, and then leans in for a ${V.seeIncest === 0 ? "chaste" : ""} kiss, as directed.`); + App.Events.addParagraph(frag, t); + + if (V.seeIncest > 0 && (sib1.energy > 60 || sib2.energy > 60)) { + if (App.Utils.sexAllowed(sib1, sib2)) { + t = []; + t.push(`The kiss begins to get a little more heated and they glance at you for permission to continue instead of getting back to work immediately. While you know you should really tell them to get back to work, the rules you've set allow them to go further, and they're at least getting along. And it's pretty hot, so you're keen to watch.`); + App.Events.addParagraph(frag, t); + + t = []; + t.push(`As soon as they realize you aren't going to stop them, the kiss gets deeper and the groping starts in earnest.`); + if (sib1.dick > 0) { + t.push(`${sib2.slaveName} immediately reaches for ${his2} ${relativeTerm(sib2, sib1)}'s dick, fondling and tugging on it to make sure it's hard for what they're about to do.`); + } else if (sib1.vagina >= 0) { + t.push(`${sib2.slaveName} immediately reaches for ${his2} ${relativeTerm(sib2, sib1)}'s soft slit, rubbing and massaging it to make sure ${he}'s good and wet.`); + } else if (sib1.boobs >= 300) { + t.push(`${sib2.slaveName} moves ${his2}'s hands to ${his2} ${relativeTerm(sib2, sib1)}'s tits, pawing at them and playing with ${his} ${sib1.nipples} nipples.`); + } else { // null with no tits? c'mon... + t.push(`${sib2.slaveName} slides ${his2} hand between ${his2} ${relativeTerm(sib2, sib1)}'s thighs, massaging the soft sensitive skin of ${his} crotch.`); + } + // reverse order and add a couple extra options to try to avoid similar text + t.push(`${sib1.slaveName}, meanwhile,`); + if (sib2.boobs >= 300) { + t.push(`moves ${his}'s hands to ${his} ${relativeTerm(sib1, sib2)}'s tits, pawing at them and playing with ${his2} ${sib1.nipples} nipples.`); + } else if (sib2.balls > 0 && sib1.dick > 0) { // avoid double dick groping by going for the balls + t.push(`grabs ${his} ${relativeTerm(sib1, sib2)}'s balls, fondling them to make sure ${he2}'s going to make plenty of cum.`); + } else if (sib2.dick > 0) { + t.push(`grabs ${his} ${relativeTerm(sib1, sib2)}'s dick, fondling and tugging on it to make sure it's hard for what they're about to do.`); + } else if (sib2.clit > 0 && sib1.vagina >= 0) { // avoid double vagina groping by going for the clit + t.push(`reaches for ${his} ${relativeTerm(sib1, sib2)}'s clit, rubbing and flicking it to make sure ${he2}'s ready for action.`); + } else if (sib2.vagina >= 0) { + t.push(`reaches for ${his} ${relativeTerm(sib1, sib2)}'s soft slit, rubbing and massaging it to make sure ${he2}'s good and wet.`); + } else { + t.push(`slides ${his} hand between ${his} ${relativeTerm(sib1, sib2)}'s thighs, massaging the soft sensitive skin of ${his2} crotch.`); + } + t.push(`Once they're both good and ready, they get on a dressing bench in a classic 69 position, eagerly bringing each other to orgasm orally while you watch.`); + seX(sib1, "oral", sib2, "oral"); + App.Events.addParagraph(frag, t); + } else { + t = []; + t.push(`The kiss begins to get a little more heated and you clearly see the lust growing in ${sib1.energy > sib2.energy ? sib1.slaveName : sib2.slaveName}'s eyes, but they know the rules and pull apart before they go too far.`); + App.Events.addParagraph(frag, t); + } + } + + t = []; + t.push(`They feel better, and they've both learned to <span class="trust inc">trust</span> and <span class="devotion inc">rely on</span> your judgement.`); + App.Events.addParagraph(frag, t); + + sib1.trust += 2; + sib1.devotion += 2; + sib2.trust += 2; + sib2.devotion += 2; + + return frag; + } + + function punish() { + const frag = document.createDocumentFragment(); + + t = []; + t.push(`You step between the two siblings and take a few weak blows before they realize you're there; you see the fear grow on their faces immediately when they realize how much trouble they're in.`); + t.push(`You grab ${sib2.slaveName} by the throat and throw ${him2} to the ground, ordering ${him2} to stay there until you're ready for ${him2}.`); + t.push(`Then you push ${sib1.slaveName} into a kneeling position and force your ${V.PC.dick > 0 ? "dick" : "strap-on"} down ${his} throat, facefucking ${him} brutally as you explain that both of them are your property and that every time they fight, they are risking damage to your property.`); + App.Events.addParagraph(frag, t); + + t = []; + t.push(`Once you think your message has gotten through, you pull ${sib1.slaveName} off your ${V.PC.dick > 0 ? "dick" : "strap-on"} with a pop, pull ${sib2.slaveName} to ${his2} knees and repeat your chosen punishment on ${him2}.`); + t.push(`${sib1.slaveName} kneels obediently, crying quietly as you abuse ${his} ${relativeTerm(sib1, sib2)}.`); + t.push(`When you've finished, you're sure both siblings are more likely to <span class="trust dec">respect the rules</span>, out of fear if nothing else.`); + App.Events.addParagraph(frag, t); + + sib1.trust -= 5; + sib2.trust -= 5; + + return frag; + } + + function watch() { + const dead1 = Deadliness(sib1); + const dead2 = Deadliness(sib2); + if (Math.abs(dead1 - dead2) <= 1) { + // fight between equals + t = []; + t.push(`The two siblings are fairly closely matched in size and skill, and the nude brawl carries on for a few minutes as the other slaves finish preparing for their day. Eventually, ${sib1.slaveName} notices that they're going to be late to work, and they agree to a truce.`); + t.push(`As they dress quickly, they see you standing at the door, and a quick flash of <span class="trust dec">fear</span> passes over their faces as they realize you've probably just seen the whole thing.`); + t.push(`The fear is replaced with relief when they realize that if you'd wanted to punish them, you probably would have done so already, but they'll leave with the lasting impression that you're always watching.`); + + sib1.trust -= 2; + sib2.trust -= 2; + + return t; + } else { + // there's a clear winner + const winner = dead1 > dead2 ? sib1 : sib2; + const loser = dead1 > dead2 ? sib2 : sib1; + const { + HeW, HisW, heW, himW, hisW, himselfW + } = getPronouns(winner).appendSuffix("W"); + const { + heL, himL, hisL + } = getPronouns(loser).appendSuffix("L"); + + t = []; + t.push(`${winner.slaveName} has a clear advantage over ${hisW} ${relativeTerm(winner, loser)} and quickly subdues ${himL}.`); + if (V.seeIncest && App.Utils.sexAllowed(winner, loser) && (winner.energy > 40 || winner.fetish === "dom")) { + t.push(`Excited by ${hisW} dominance of ${hisW} weaker ${relativeTerm(winner, loser)}, ${heW} quickly begins to take advantage of ${himL}.`); + let sexType = "oral"; + if (canPenetrate(winner)) { + // no taking of virginities here + if (canDoVaginal(loser) && loser.vagina > 0) { + t.push(`${HeW} flips ${himL} onto ${hisL} back, spreads ${hisL} legs and positions ${himselfW} to penetrate ${himL}.`); + sexType = "vaginal"; + } else if (canDoAnal(loser) && loser.anus > 0) { + t.push(`${HeW} flips ${himL} onto ${hisL} belly, spreads ${hisL} buttocks and positions ${himselfW} to penetrate ${hisL} ass.`); + sexType = "anal"; + } + } + if (sexType === "oral") { + t.push(`${HeW} moves over ${loser.slaveName}'s prone body until ${heW}'s above ${hisL} mouth, and orders ${hisW} ${relativeTerm(winner, loser)} to orally service ${himW}.`); + } + if (loser.fetish === "submissive") { + t.push(`${loser.slaveName} enjoys being dominated anyway, so this situation is good for ${himL} too.`); + } else if (V.universalRulesConsent === 1) { + t.push(`${loser.slaveName} knows he's lost; technically ${heL} could reject ${winner.slaveName}'s advances, but ${heL} isn't exactly opposed to this anyway.`); + } else { + t.push(`Whether ${loser.slaveName} is OK with this or just accepts that it's happening to ${himL} is unclear, but it doesn't really matter.`); + } + seX(winner, "penetrative", loser, sexType); + if (sexType === "vaginal") { + t.push(`${winner.slaveName} slides ${hisW} ${winner.dick > 3 ? "stiff prick" : "hard little dick"} into ${hisW} ${relativeTerm(winner, loser)}'s ${loser.vagina < 2 ? "tight slit" : "cunt"} and starts pounding away.`); + } else if (sexType === "anal") { + t.push(`${winner.slaveName} slides ${hisW} ${winner.dick > 3 ? "stiff prick" : "hard little dick"} into ${hisW} ${relativeTerm(winner, loser)}'s ${loser.anus < 2 ? "tight ass" : "well-used ass"} and starts pounding away.`); + } else { + t.push(`${winner.slaveName} encourages ${loser.slaveName} to ${winner.dick > 0 ? `suck ${himW} off` : `eat ${himW} out`} quickly, knowing that they both still have to get to work.`); + } + if (loser.fetish !== "submissive") { + t.push(`It's not long before ${heW} comes, and the two slaves separate to finish getting ready for work. ${winner.slaveName} had fun this morning, but is still clearly <span class="improvement">up for more</span>.`); + if (fetishChangeChance(loser) > jsRandom(0, 100)) { + loser.fetishKnown = 1; + loser.fetish = "submissive"; + loser.fetishStrength = 20; + t.push(`${loser.slaveName}, meanwhile, really enjoyed the feeling of being dominated by ${hisL} ${relativeTerm(loser, winner)}, and has become <span class="fetish gain">submissive</span>.`); + } + winner.energy += 4; + } else { + t.push(`${HisW} ${relativeTerm(winner, loser)} really gets off on ${hisL} submissive position and comes before ${heW} does, and when they're finished they both leave satisfied, <span class="improvement">heightening their sex drives</span>.`); + sib1.energy += 4; + sib2.energy += 4; + } + } else { + t.push(`${HeW} quickly extracts a promise from ${loser.slaveName} to help ${himW} with ${hisW} work before letting him up, and they resume dressing for work without further incident.`); + // no stat changes + } + return t; + } + } + } +}; diff --git a/src/events/debugEvent.js b/src/events/debugEvent.js index 8af32bffca568150ba04abb0c00402a6e0740b21..4bac7ec3a28b400e8af5f9f53155f261c2c80af7 100644 --- a/src/events/debugEvent.js +++ b/src/events/debugEvent.js @@ -24,9 +24,32 @@ App.Events.debugEvent = function(eventName) { return cast; } + function makeActorList() { + const actors = document.createDocumentFragment(); + const actorReqs = event.actorPrerequisites(); + for (let i = 0; i < actorReqs.length; ++i) { + let tab = App.UI.DOM.appendNewElement("div", actors); // TODO: put these in tabs? + for (const slave of V.slaves) { + let slaveDiv = App.UI.DOM.appendNewElement("div", tab, App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name")); + let slaveFails = false; + for (const p of actorReqs[i]) { + let passed = testPredicate(slaveDiv, p, slave); + slaveFails = slaveFails || !passed; + App.UI.DOM.appendNewElement("div", slaveDiv, p.name || p.toString(), [passed ? "green" : "red", "indent"]); + } + if (!slaveFails) { + App.UI.DOM.appendNewElement("div", slaveDiv, App.UI.DOM.link("Choose this slave", castSlave, [slave, i]), "indent"); + } + App.UI.DOM.appendNewElement("hr", tab); + } + } + return actors; + } + function castSlave(slave, index) { event.actors[index] = slave.ID; $('#castList').empty().append(makeCastList()); + $('#actorList').empty().append(makeActorList()); } function testPredicate(outDiv, p, ...args) { @@ -54,23 +77,8 @@ App.Events.debugEvent = function(eventName) { const castList = App.UI.DOM.appendNewElement("div", frag, makeCastList()); castList.id = "castList"; App.UI.DOM.appendNewElement("hr", frag); - const actorReqs = event.actorPrerequisites(); - for (let i = 0; i < actorReqs.length; ++i) { - let tab = App.UI.DOM.appendNewElement("div", frag); // TODO: put these in tabs? - for (const slave of V.slaves) { - let slaveDiv = App.UI.DOM.appendNewElement("div", tab, App.UI.DOM.makeElement("span", SlaveFullName(slave), "slave-name")); - let slaveFails = false; - for (const p of actorReqs[i]) { - let passed = testPredicate(slaveDiv, p, slave); - slaveFails = slaveFails || !passed; - App.UI.DOM.appendNewElement("div", slaveDiv, p.name || p.toString(), [passed ? "green" : "red", "indent"]); - } - if (!slaveFails) { - App.UI.DOM.appendNewElement("div", slaveDiv, App.UI.DOM.link("Choose this slave", castSlave, [slave, i]), "indent"); - } - App.UI.DOM.appendNewElement("hr", tab); - } - } + const actorList = App.UI.DOM.appendNewElement("div", frag, makeActorList()); + actorList.id = "actorList"; } } else { App.UI.DOM.appendNewElement("div", prereqs, "Specified name does not resolve to an event.", "major-warning"); diff --git a/src/events/randomEvent.js b/src/events/randomEvent.js index 929702590a8a3403a14a66a75118f2b335963854..bbdeff3c4451ae3c0e810eae64cc46216dd9bbd4 100644 --- a/src/events/randomEvent.js +++ b/src/events/randomEvent.js @@ -17,6 +17,7 @@ App.Events.getIndividualEvents = function() { new App.Events.RESSWaistlineWoes(), new App.Events.RESSAssFitting(), new App.Events.RESSRetchingCum(), + new App.Events.RETSSiblingTussle(), new App.Events.RECIButthole(), new App.Events.RECIFuta(), new App.Events.RECIOrientation(), diff --git a/src/events/reRelativeRecruiter.js b/src/events/reRelativeRecruiter.js index a323f7988d442e49cce7fe00daa0190a1e8f2158..18469448a5525b9578299418306adae2412efdc8 100644 --- a/src/events/reRelativeRecruiter.js +++ b/src/events/reRelativeRecruiter.js @@ -561,7 +561,8 @@ App.Events.RERelativeRecruiter = class RERelativeRecruiter extends App.Events.Ba } const origSlave = BaseSlave(); - Object.assign(origSlave, V.genePool.find(s => s.ID === getSlave(_this.actors[0]).ID)); + const genepoolRec = App.Entity.Utils.GenePoolRecordCleanup(V.genePool.find(s => s.ID === _this.actors[0])); + Object.assign(origSlave, genepoolRec); const newSlave = generateRelatedSlave(origSlave, _this.params.relative); clearMods(newSlave); diff --git a/src/js/relationshipChecks.js b/src/js/relationshipChecks.js index c5d8f849c14f054d5a9429968a3a5aa0c6ed73df..fddc8c28a34ed4e88318375faa2b0c88fa22cba7 100644 --- a/src/js/relationshipChecks.js +++ b/src/js/relationshipChecks.js @@ -68,9 +68,9 @@ globalThis.PCrelationshipTerm = function(id) { * even though it's not going to have any mechanical impact on the scene. * @param {App.Entity.SlaveState|App.Entity.PlayerState} context * @param {App.Entity.SlaveState|App.Entity.PlayerState} actor - * @param {boolean} [asLink=false] - when true, instead of using the slave's first name, use their full name with a (SC Markup) link to the slave description dialog. + * @param {boolean|string} [asLink=false] - when true, instead of using the slave's first name, use their full name with a (SC Markup) link to the slave description dialog. when "DOM", generate a DOM link and return a DocumentFragment. * @param {boolean} [insertComma=false] - when true, if a relationship is found, it will be separated from the actor's name by a comma ("her father, Dave" instead of "her father Dave") - * @returns {string} + * @returns {string|DocumentFragment} */ globalThis.contextualIntro = function(context, actor, asLink=false, insertComma=false) { let first = true; @@ -100,7 +100,15 @@ globalThis.contextualIntro = function(context, actor, asLink=false, insertComma= if (r !== ``) { r += (insertComma || actor === V.PC) ? ", " : " "; } - const namePart = asLink ? App.UI.slaveDescriptionDialog(actor) : actor.slaveName; - r += actor === V.PC ? "you" : namePart; - return r; + + if (asLink !== "DOM" || actor === V.PC) { + const namePart = asLink ? App.UI.slaveDescriptionDialog(actor) : actor.slaveName; + r += actor === V.PC ? "you" : namePart; + return r; + } else { + const frag = document.createDocumentFragment(); + $(frag).append(r); + frag.append(App.UI.DOM.slaveDescriptionDialog(actor)); + return frag; + } }; diff --git a/src/npc/interaction/fFeet.tw b/src/npc/interaction/fFeet.tw index 9e4085ccf539145177d813a72b459630a36485ed..ed12652f7c9999442742ac852b8b960841a1965e 100644 --- a/src/npc/interaction/fFeet.tw +++ b/src/npc/interaction/fFeet.tw @@ -131,7 +131,7 @@ <<set _boobs = "flat">> <</if>> <<if _boobsa != "skip">> - <<set _boobs = "_boobsa getSlave($AS).boobShape">> + <<set _boobs = _boobsa + " " + getSlave($AS).boobShape>> <</if>> <<if getSlave($AS).balls == 1>> diff --git a/src/npc/interaction/passage/matchmaking.tw b/src/npc/interaction/passage/matchmaking.tw index ab1a90294aac1f154e31d152893f570d5c080220..aa3ddabef18811f7f779d6810194757e6430ac9b 100644 --- a/src/npc/interaction/passage/matchmaking.tw +++ b/src/npc/interaction/passage/matchmaking.tw @@ -183,7 +183,7 @@ Being ordered into a relationship would be difficult for anyone, but they're so <</if>> <<set $subSlave.relationship = 4>> -<<set $subSlave.relationshipTarget = getSlave($activeSlave.ID).ID>> +<<set $subSlave.relationshipTarget = $activeSlave.ID>> <<set $subSlave.devotion -= 20>> <<set getSlave($activeSlave.ID).relationship = 4>> <<set getSlave($activeSlave.ID).relationshipTarget = $subSlave.ID>> @@ -191,8 +191,6 @@ Being ordered into a relationship would be difficult for anyone, but they're so <<set $slaves[$slaveIndices[$subSlave.ID]] = $subSlave>> -<<set $activeSlave = getSlave($activeSlave.ID)>> - <</if>> <<set $activeSlave = getSlave($activeSlave.ID)>> /* this should be harmless, keyword should */ diff --git a/src/npc/startingGirls/startingGirls.tw b/src/npc/startingGirls/startingGirls.tw index bf95040f8994d935fa15783d1e7fbf66fc4d6945..e264cac9de0d7eced671a266a69b51f3baff3aea 100644 --- a/src/npc/startingGirls/startingGirls.tw +++ b/src/npc/startingGirls/startingGirls.tw @@ -894,8 +894,8 @@ V.activeSlave.fetish = either("boobs", "buttslut", "cumslut", "dom", "humiliation", "masochist", "pregnancy", "sadist", "submissive", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none"); V.activeSlave.fetishKnown = 0 - }).addValueList([["None", "none"], ["Sub", "submissive"], ["Dom", "dom"], ["Cumslut", "cumslut"], ["Humilation", "humilation"], - ["Buttslut", "buttslut"], ["Breasts", "breasts"], ["Pregnancy", "pregnancy"], ["Sadism", "sadism"], ["Masochism", "masochism"]])>> + }).addValueList([["None", "none"], ["Sub", "submissive"], ["Dom", "dom"], ["Cumslut", "cumslut"], ["Humiliation", "humiliation"], + ["Buttslut", "buttslut"], ["Breasts", "boobs"], ["Pregnancy", "pregnancy"], ["Sadism", "sadist"], ["Masochism", "masochist"]])>> <<if $seeExtreme === 1>> <<run _option.addValue("Mindbroken", "mindbroken", () => { V.activeSlave.fetishStrength = 10; diff --git a/src/uncategorized/bodyModification.js b/src/uncategorized/bodyModification.js index 2e136955d81e23e6027b79dc30db8aa8ba143586..0839bdf7582dc3ddf8e1fa82df37c0f1dee5c796 100644 --- a/src/uncategorized/bodyModification.js +++ b/src/uncategorized/bodyModification.js @@ -239,10 +239,10 @@ App.Medicine.Modification.Select.brand = function(slave, cheat = false) { } function symbolOptions(brandList) { - const frag = new DocumentFragment(); const list = App.Medicine.Modification.Brands[brandList]; - let length = Object.keys(list).length; + const array = []; for (const brand in list) { + const frag = new DocumentFragment(); if (!cheat && list[brand].hasOwnProperty("requirements")) { if (!list[brand].requirements()) { continue; @@ -260,12 +260,9 @@ App.Medicine.Modification.Select.brand = function(slave, cheat = false) { } ) ); - length--; - if (length > 0) { - frag.append(" | "); - } + array.push(frag); } - return frag; + return App.UI.DOM.generateLinksStrip(array); } function slaveBody() { @@ -321,9 +318,9 @@ App.Medicine.Modification.Select.brand = function(slave, cheat = false) { const div = document.createElement("div"); div.classList.add("double-choices"); div.style.textIndent = 0; - let length = Object.keys(bodyPartObj).length; + const array = []; for (const bp in bodyPartObj) { - div.append( + array.push( App.UI.DOM.link( bodyPartObj[bp], () => { @@ -332,11 +329,8 @@ App.Medicine.Modification.Select.brand = function(slave, cheat = false) { } ) ); - length--; - if (length > 0) { - div.append(" | "); - } } + div.append(App.UI.DOM.generateLinksStrip(array)); return div; } diff --git a/src/uncategorized/pePitFight.tw b/src/uncategorized/pePitFight.tw index b6c4cc595637a76be3107b55485a9df854c37f1e..962dd2d72ce2aa089ae56d3e9db4dcff92243785 100644 --- a/src/uncategorized/pePitFight.tw +++ b/src/uncategorized/pePitFight.tw @@ -202,7 +202,7 @@ The umpire announces gravely that the fight is to the death and rings a bell. The combat is long and exhausting. $activeSlave.slaveName and $his opponent are closely matched in terms of skill, so neither takes foolish risks and a protracted, bloody fight results as both slaves take horrible but non-life-threatening cuts. Finally, $activeSlave.slaveName's superior physical condition wins out and $his opponent falls from exhaustion and blood loss. $activeSlave.slaveName stumbles over to open _hisU throat. This victory has @@.green;won you some renown@@ and @@.yellowgreen;a sum of money@@ from each of the spectators, though $activeSlave.slaveName is @@.red;badly hurt.@@ <<run repX(500, "pit", $activeSlave)>> <<run cashX(5000, "pit", $activeSlave)>> - <<set $activeSlave.health.condition = -50>> + <<run healthDamage($activeSlave, 50)>> <<set $activeSlave.counter.pitWins += 1>> <<set $activeSlave.counter.pitKills += 1>> <<set $pitKillsTotal += 1>> diff --git a/src/uncategorized/reBoomerang.tw b/src/uncategorized/reBoomerang.tw index 9b0f9fb1140078791ac32c37700e386f86855353..739c5934a5bd48660807b2e2515378c7fc7b29a1 100644 --- a/src/uncategorized/reBoomerang.tw +++ b/src/uncategorized/reBoomerang.tw @@ -326,7 +326,7 @@ brings up the relevant feeds. There's a naked body crumpled pathetically against <<if $seePreg != 0>><<if $activeSlave.ovaries>><<set $activeSlave.preg = random(5,_pregWeeks-1), $activeSlave.pregType = 1, $activeSlave.pregSource = 0, $activeSlave.pregWeek = $activeSlave.preg, $activeSlave.pregKnown = 1, SetBellySize($activeSlave)>><</if>><</if>> <<case "monster movie">> You sold $him to a film studio to act as a double for a famous actress where $he was used installed with a massive fake pregnancy for the ending. "It'<<s>> too big... Can't breath right... Wa<<s>> going to be <<s>>old... <<S>>in<<c>>e I wa<<s>> a di<<s>>po<<s>>able prop... I'd rather be your prop." $He returns to wheezing against the solid mass bulging from $his middle. - <<set $activeSlave.health = random(-70, -60)>> + <<run healthDamage($activeSlave, random(60, 70))>> <<if $seeHyperPreg != 1>> <<set $activeSlave.bellyImplant = 120000, $activeSlave.belly = 120000>> <<else>>