diff --git a/devTools/types/FC/facilities.d.ts b/devTools/types/FC/facilities.d.ts index 36c3bbd4d028d28fa1659b11fdff2175f13ed95b..0d3b8f87f52618dbd50148a081545a1f487634d9 100644 --- a/devTools/types/FC/facilities.d.ts +++ b/devTools/types/FC/facilities.d.ts @@ -182,40 +182,49 @@ declare namespace FC { trainingIDs: number[]; // Pit section - /** The animal fighting a slave if not null. */ - animal: string | "random"; + /** + * If there is a fight event at the end of the week. + */ + active: boolean /** The type of audience the Pit has. */ audience: "none" | "free" | "paid"; - /** Whether or not the bodyguard is fighting this week. */ - bodyguardFights: boolean; /** The type of decoration the Pit is using. */ decoration: FC.FutureSocietyDeco; - /** - * Who is fighting this week. - * - * | Value | Description | - * |------:|:---------------------------| - * | 0 | Two random slaves | - * | 1 | Random slave and bodyguard | - * | 2 | Random slave and animal | - * | 3 | True random | - * | 4 | Two specific slaves | - */ - fighters: number; /** An array of the IDs of slaves assigned to the Pit. */ fighterIDs: number[]; /** Whether or not a fight has taken place during the week. */ fought: boolean; - /** Whether or not the fights in the Pit are lethal. */ - lethal: boolean; - /** The ID of the slave fighting the bodyguard for their life. */ - slaveFightingBodyguard: number; - /** The ID of the slave fighting one of your beasts for their life. */ - slaveFightingAnimal: number; - /** The IDs of the two slaves chosen to fight this week. */ - slavesFighting: number[]; - /** The virginities of the loser not allowed to be taken. */ - virginities: "neither" | "vaginal" | "anal" | "all" + /** + * Base for the max number of fights that can be held each week. + * fightNum = 3 + 2 * fightsBase + */ + fightsBase: 0 | 1 | 2 + /** + * How many can watch the fight. Influences rep/cash generation + */ + seats: 0 | 1 | 2 + /** + * * 0: no lethal fights allowed + * * 1: lethal and nonlethal fights allowed + * * 2: only lethal fights allowed + */ + lethal: 0 | 1 | 2 + /** + * Which virginities will be respected + */ + virginities: "none" | "vaginal" | "anal" | "all" + /** + * Whether slaves fighting must be in somewhat fair health and generally able to do combat. + */ + minimumHealth: boolean + /** + * Schedule a slave to fight the BG + */ + slaveFightingBodyguard: number + /** + * Schedule two slaves to fight each other + */ + slavesFighting: [number, number] } } } diff --git a/js/002-config/fc-js-init.js b/js/002-config/fc-js-init.js index bd683646b919f39d86776a6b1b84f46c2012c15a..f5bb39f1186c8e06fbc8a25c533b19ce7425af5f 100644 --- a/js/002-config/fc-js-init.js +++ b/js/002-config/fc-js-init.js @@ -52,6 +52,7 @@ App.Facilities.Incubator = {}; App.Facilities.MasterSuite = {}; App.Facilities.Nursery = {}; App.Facilities.Pit = {}; +App.Facilities.Pit.Fights = {}; App.Facilities.Schoolroom = {}; App.Facilities.ServantsQuarters = {}; App.Facilities.Spa = {}; diff --git a/src/002-config/fc-version.js b/src/002-config/fc-version.js index 5e322c743b7c1ed3499e6d0791b1d98647f9af49..86917d4b34807cea99329f3261a336f25142af07 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.23", commitHash: null, - release: 1186, // When getting close to 2000, please remove the check located within the onLoad() function defined at line five of src/js/eventHandlers.js. + release: 1187, // 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/004-base/basePitFight.js b/src/004-base/basePitFight.js new file mode 100644 index 0000000000000000000000000000000000000000..19a44d5ba2a8aa3be812900e7e7c0ca99a65561e --- /dev/null +++ b/src/004-base/basePitFight.js @@ -0,0 +1,206 @@ +App.Facilities.Pit.Fights.BaseFight = class BaseFight { + /** + * build a new fight + */ + constructor() { + /** @member {Array<number>} actors - a list of IDs for the actors participating in this fight. */ + this.actors = []; + /** @member {object} params - a set of parameters to pass to the fight. */ + this.params = {}; + } + + /** A unique key, so we can queue the fight. + * @returns {string} + */ + get key() { + return "base fight"; + } + + /** + * Whether slaves training at the arena are allowed in this fight. If yes, they will be preferred. + * @returns {boolean} + */ + get allowTrainees() { + return false; + } + + /** + * At least one slave is expected to die. + * @returns {boolean} + */ + get lethal() { + return false; + } + + /** + * How high the impact of this fight on the total event is. A flat multiplier. Used in descriptions. + * 1 is a nonlethal 1-vs-1 fight. May not be negative + * @returns {number} + */ + get impact() { + return 1; + } + + /** Get a short description to show when selecting the fight during the event. + * Assumes fight can be run and actors have been cast already + * @returns {DocumentFragment} + */ + fightDescription() { + return new DocumentFragment(); + } + + /** Fight predicates determine whether the fight can be shown/executed + * @callback pitFightPredicate + * @returns {boolean} + */ + /** generate an array of zero or more predicates which must all return true in order for the fight to be valid. + * lambda predicates may add properties to {@link App.Facilities.Pit.Fights.BaseFight#params the params member} in order to pass information on to the fight. + * child classes should implement this. + * @returns {Array<pitFightPredicate>} + */ + fightPrerequisites() { + return [ + () => + (V.pit.lethal === 0 && !this.lethal) || + (V.pit.lethal === 1) || + (V.pit.lethal === 2 && this.lethal) + ]; + } + + /** + * Actors that are forced to be a specific slave + * @returns {Array<number>} + */ + forcedActors() { + return []; + } + + /** generate an array of zero or more arrays, each corresponding to an actor in the fight, which contain zero or more predicates which must be satisfied by the actor. + * child classes should implement this, unless they are overriding castActors. + * @returns {Array<Array<actorPredicate>>} + */ + actorPrerequisites() { + return []; + } + + /** run the fight and attach DOM output to the pit fight passage. + * child classes must implement this. + * @param {ParentNode} node - Document fragment which fight output should be attached to + * @param {App.Facilities.Pit.Fights.FighterMap} fighterMap + * @returns {number} - How successful the fight was for prestige/cash. + * The expected value should be between -1 and 1 inclusive. May go more extreme for unexpected outcomes. + * At least one maximum/minimum value should be used, and then scaled to other events with the + * {@link App.Facilities.Pit.Fights.BaseFight#impact impact property}. + */ + execute(node, fighterMap) { + return 0; + } + + /** build the actual list of actors that will be involved in this fight. + * default implementation should suffice for child classes with a fixed number of actors; may be overridden for fights with variable actor count. + * @returns {boolean} - return false if sufficient qualified actors could not be found (cancel the fight) + */ + castActors() { + const prereqs = this.actorPrerequisites(); + + this.actors = [...this.forcedActors()]; + + for (let i = 0; i < prereqs.length; ++i) { + if (this.allowTrainees) { + if (this._selectActor(prereqs[i], ...App.Entity.facilities.pit.job("trainee").employeesIDs())) { + continue; + } + } + if (!this._selectActor(prereqs[i], ...App.Entity.facilities.pit.job("fighter").employeesIDs())) { + return false; + } + } + + return true; // all actors cast + } + + /** + * @param {Array<actorPredicate>} prereqs + * @param {...number} ids + * @returns {boolean} False, if no actor could be selected + * @private + */ + _selectActor(prereqs, ...ids) { + const qualified = ids + .filter(si => !this.actors.includes(si) && prereqs.every(p => p(getSlave(si))) && this._validActor(si)); + if (qualified.length === 0) { + return false; // a required actor was not found + } + this.actors.push(qualified.pluck()); + return true; + } + + /** + * @param {number} si - Slave ID + * @returns {boolean} True, if the slave is allowed to fight + * @private + */ + _validActor(si) { + if (!V.pit.minimumHealth) { + return true; + } + const slave = getSlave(si); + return canWalk(slave) && slave.health.condition >= -20; + } +}; + +/** This is a trivial fight for use as an example. */ +App.Facilities.Pit.Fights.TestFight = class extends App.Facilities.Pit.Fights.BaseFight { + get key() { + return "test"; + } + + actorPrerequisites() { + return [ + [], // actor one, no requirements + [] // actor two, no requirements + ]; + } + + execute(node) { + let [slave1, slave2] = this.actors.map(a => getSlave(a)); // mapped deconstruction of actors into local slave variables + node.appendChild(document.createTextNode(`This test fight for ${slave1.slaveName} and ${slave2.slaveName} was successful.`)); + return 0; + } +}; + +App.Facilities.Pit.Fights.FighterMap = class { + constructor() { + /** + * @type {Map<number, number>} + */ + this.map = new Map(); + } + + fightCount(slaveId) { + const count = this.map.get(slaveId); + if (count === undefined) { + return 0; + } else { + return count; + } + } + + addFight(slaveId) { + const oldCount = this.fightCount(slaveId); + this.map.set(slaveId, oldCount + 1); + } + + fighterCount() { + return this.map.size; + } + + fightsCount() { + let count = 0; + this.map.forEach((v, k) => { + count += k; + }); + return count; + } +}; + diff --git a/src/005-passages/interactPassages.js b/src/005-passages/interactPassages.js index 3861c266e58c02a64ffc9a6671ba57d6677f94b3..794728b07e33c50723fb34c5db909920b682f8cc 100644 --- a/src/005-passages/interactPassages.js +++ b/src/005-passages/interactPassages.js @@ -351,7 +351,7 @@ new App.DomPassage("Surrogacy Workaround", new App.DomPassage("Pit Workaround", () => { - V.nextButton = V.pit.slavesFighting.length > 1 ? "Finish" : "Cancel"; + V.nextButton = V.pit.slavesFighting === null || V.pit.slavesFighting.length !== 2 ? "Cancel" : "Finish"; V.nextLink = "Pit"; return App.Facilities.Pit.workaround(); } diff --git a/src/data/backwardsCompatibility/pitBC.js b/src/data/backwardsCompatibility/pitBC.js index 877af4c22c58c2c02ba5e4ea194acf3dfc35b5b4..c6303e044a4b96987a78935695d36c7a2079e7c9 100644 --- a/src/data/backwardsCompatibility/pitBC.js +++ b/src/data/backwardsCompatibility/pitBC.js @@ -6,39 +6,63 @@ App.Facilities.Pit.BC = function() { } if (V.pit) { - V.pit.name = V.pit.name || V.pitName || "the Pit"; - V.pit.virginities = V.pit.virginities || V.pitVirginities || "neither"; + if (V.releaseID < 1186) { + V.pit.name = V.pit.name || V.pitName || "the Pit"; + V.pit.virginities = V.pit.virginities || V.pitVirginities || "neither"; - if (typeof V.pit.virginities !== "string") { - const virginities = ["neither", "vaginal", "anal", "all"]; + if (typeof V.pit.virginities !== "string") { + const virginities = ["neither", "vaginal", "anal", "all"]; - V.pit.virginities = virginities[V.pit.virginities]; - } + V.pit.virginities = virginities[V.pit.virginities]; + } - V.pit.bodyguardFights = V.pit.bodyguardFights || V.pitBG || false; - V.pit.fighterIDs = V.pit.fighterIDs || V.fighterIDs || []; - V.pit.fighters = V.pit.fighters || 0; + V.pit.bodyguardFights = V.pit.bodyguardFights || V.pitBG || false; + V.pit.fighterIDs = V.pit.fighterIDs || V.fighterIDs || []; + V.pit.fighters = V.pit.fighters || 0; - if (V.pit.bodyguardFights && V.pit.fighterIDs.includes(V.BodyguardID)) { - V.pit.fighterIDs.delete(V.BodyguardID); - } + if (V.pit.bodyguardFights && V.pit.fighterIDs.includes(V.BodyguardID)) { + V.pit.fighterIDs.delete(V.BodyguardID); + } + + if (V.farmyard) { + V.pit.animal = V.pit.animal || V.pitAnimalType || null; + } + + V.pit.trainingIDs = V.pit.trainingIDs || []; + + V.pit.audience = V.pit.audience || V.pitAudience || "none"; + V.pit.lethal = V.pit.lethal || V.pitLethal || false; + V.pit.fought = V.pit.fought || V.pitFought || false; + + V.pit.slavesFighting = V.pit.slavesFighting || []; + V.pit.decoration = V.pit.decoration || "standard"; - if (V.farmyard) { - V.pit.animal = V.pit.animal || V.pitAnimalType || null; + if (V.slaveFightingBG) { + V.pit.slaveFightingBodyguard = V.slaveFightingBG; + } } - V.pit.trainingIDs = V.pit.trainingIDs || []; + V.pit.activeFights = V.pit.activeFights || []; - V.pit.audience = V.pit.audience || V.pitAudience || "none"; - V.pit.lethal = V.pit.lethal || V.pitLethal || false; - V.pit.fought = V.pit.fought || V.pitFought || false; + V.pit.lethal = V.pit.lethal === true ? 1 : V.pit.lethal === false ? 0 : V.pit.lethal; - V.pit.slavesFighting = V.pit.slavesFighting || []; - V.pit.decoration = V.pit.decoration || "standard"; - } + delete V.pit.animal; + delete V.pit.bodyguardFights; + delete V.pit.slaveFightingAnimal; + + if (V.pit.virginities === "neither") { + V.pit.virginities = "none"; + } - if (V.slaveFightingBG && V.pit) { - V.pit.slaveFightingBodyguard = V.slaveFightingBG; + V.pit.active = V.pit.active || false; + V.pit.fightsBase = V.pit.fightsBase || 0; + V.pit.seats = V.pit.seats || 0; + if (V.pit.minimumHealth !== true && V.pit.minimumHealth !== false) { + V.pit.minimumHealth = true; + } + if (V.pit.slavesFighting !== null && V.pit.slavesFighting.length !== 2) { + V.pit.slavesFighting = null; + } } if (V.pit && V.pit.trainingIDs) { diff --git a/src/events/PE/pePitFightInvite.js b/src/events/PE/pePitFightInvite.js index bb87accef41e052dd06cf78f1a9f4d2a3ee794dd..fac0557e5a499a5cc2efd0f044898ae60d156250 100644 --- a/src/events/PE/pePitFightInvite.js +++ b/src/events/PE/pePitFightInvite.js @@ -14,7 +14,7 @@ App.Events.PEPitFightInvite = class PEPitFightInvite extends App.Events.BaseEven App.Events.addParagraph(node, r); r = []; if (V.pit) { - r.push(`Of course, ${capFirstChar(V.pit.name)} in ${V.arcologies[0].name} sees regular fights${(V.pit.lethal) ? " to the death" : ""}, but there's something extra special about attending these outside fights${(V.pit.lethal) ? ", especially with the very real risk of violent death" : ""}.`); + r.push(`Of course, ${capFirstChar(V.pit.name)} in ${V.arcologies[0].name} sees regular fights${(V.pit.lethal > 0) ? " to the death" : ""}, but there's something extra special about attending these outside fights${(V.pit.lethal > 0) ? ", especially with the very real risk of violent death" : ""}.`); } App.Events.addParagraph(node, r); diff --git a/src/events/scheduled/pitFight.js b/src/events/scheduled/pitFight.js index e8675c43d9a40a1230886706be4df1913461466c..56e29514dce1d7e9fb274f63acfb864a3a7ff995 100644 --- a/src/events/scheduled/pitFight.js +++ b/src/events/scheduled/pitFight.js @@ -2,122 +2,339 @@ App.Events.SEPitFight = class SEPitFight extends App.Events.BaseEvent { eventPrerequisites() { return [ () => !!V.pit, + () => V.pit.active, () => !V.pit.fought, ]; } - castActors() { - const available = [...new Set(V.pit.fighterIDs)]; + /** @param {DocumentFragment} node */ + execute(node) { + V.pit.fought = true; + V.nextButton = " "; + + node.append(intro()); + + const maxFights = 3 + (V.pit.fightsBase * 2); + let completedFights = 0; + let totalSuccess = 0; + let lethalFights = 0; + + const fighterMap = new App.Facilities.Pit.Fights.FighterMap(); + + const interactionSpan = document.createElement("span"); + interactionSpan.append(selectFight()); + node.append(interactionSpan); + + function selectFight() { + const f = new DocumentFragment(); + + App.Events.addParagraph(f, [`Select a fight. You have ${maxFights - completedFights} left.`]); - if (available.length > 0) { - this.actors = getFighters(V.pit.fighters).filter(f => !!f); + const availableFights = App.Facilities.Pit.getFights() + .filter(f => f.fightPrerequisites().every(p => p()) && f.castActors()); - return this.actors.length > 1 || !!V.pit.slaveFightingAnimal; + const choices = []; + for (const fight of availableFights) { + App.UI.DOM.appendNewElement("div", f, fight.fightDescription()); + const div = document.createElement("div"); + div.classList.add("indent"); + div.append(App.UI.DOM.link("Fight!", () => runFight(fight))); + div.append(" Impact: ", expectedImpact(fight.impact), " "); + if (fight.lethal) { + App.UI.DOM.appendNewElement("span", div, "Lethal", "warning"); + } else { + App.UI.DOM.appendNewElement("span", div, "Safe", "green"); + } + for (const si of fight.actors) { + const count = fighterMap.fightCount(si); + if (count > 0) { + div.append(" "); + const span = document.createElement("span"); + span.classList.add("orange"); + + span.append(getSlave(si).slaveName, " already fought "); + if (count > 1) { + span.append(count + " times today."); + } else { + span.append("once today."); + } + + div.append(span); + } + } + f.append(div); + } + if (availableFights.length === 0) { + App.UI.DOM.appendNewElement("div", f, "No fights available"); + } + App.UI.DOM.appendNewElement("div", f, App.UI.DOM.link("Select different fighters", () => refreshInteractionSpan(selectFight()))); + App.UI.DOM.appendNewElement("div", f, App.UI.DOM.link("Head back to the penthouse and cancel all the remaining fights", () => refreshInteractionSpan(finishEvent()))); + App.Events.addResponses(f, choices); + + return f; } - return false; // couldn't cast second fighter + /** + * @param {DocumentFragment|HTMLElement} content + */ + function refreshInteractionSpan(content) { + $(interactionSpan).empty().append(content); + } /** - * @param {number} setting - * @returns {Array<number>} slave IDs + * @param {number} impact + * @returns {string} */ - function getFighters(setting) { - if (V.pit.slaveFightingAnimal || V.pit.slaveFightingBodyguard) { - return getScheduledFight(); + function expectedImpact(impact) { + if (impact === 0) { + return "None"; + } else if (impact < 0.8) { + return "Small"; + } else if (impact < 1.5) { + return "Average"; + } else if (impact < 8) { + return "High"; + } else { + return "Very High"; } - if (setting === 4) { - return getSpecificFight(); + } + + /** + * @param {App.Facilities.Pit.Fights.BaseFight} fight + */ + function runFight(fight) { + const f = new DocumentFragment(); + App.UI.DOM.appendNewElement("span", f, `The ${ordinalSuffixWords(completedFights + 1)} fight this day:`, "bold"); + const success = fight.execute(f, fighterMap); + for (const si of fight.actors) { + fighterMap.addFight(si); } - if (setting === 3) { - return getRandomFight(); + const impact = success * fight.impact; + const p = document.createElement("p"); + p.append(fightSuccess(success), " ", fightImpact(impact)); + f.append(p); + totalSuccess += impact; + completedFights++; + if (fight.lethal) { + lethalFights++; } - if (setting === 2) { - return getAnimalFight(); + + if (completedFights < maxFights) { + f.append(selectFight()); + } else { + f.append(finishEvent()); } - if (setting === 1) { - return getBodyguardFight(); + + // automatically scroll to the top of the fight. + setTimeout(() => window.scrollTo(0, 0), 0); + + refreshInteractionSpan(f); + } + + /** + * @param {number} success + * @returns {DocumentFragment} + */ + function fightSuccess(success) { + const f = new DocumentFragment(); + + if (success < 0) { + f.append("The fight was an embarrassment."); + } else if (success === 0) { + f.append("The fight had no success."); + } else if (success < 0.2) { + f.append("The fight had little success."); + } else if (success < 0.8) { + f.append("The fight had normal success."); + } else if (success <= 1) { + f.append("The fight had good success."); + } else { + f.append("The fight had unexpectedly high success."); + } + + return f; + } + + /** + * @param {number} impact + * @returns {DocumentFragment} + */ + function fightImpact(impact) { + const f = new DocumentFragment(); + + impact = Math.abs(impact); + + if (impact === 0) { + f.append("The fight had no impact on the total event."); + } else if (impact < 0.2) { + f.append("The fight had little impact on the total event."); + } else if (impact < 0.8) { + f.append("The fight had normal impact on the total event."); + } else if (impact < 2) { + f.append("The fight had high impact on the total event."); + } else { + f.append("The fight had extreme impact on the total event."); } - if (setting === 0) { - return getSlavesFight(); + + return f; + } + + /** + * @param {number} success + * @returns {string} + */ + function eventSuccess(success) { + if (success < 0) { + return "The event was an embarrassment."; + } else if (success === 0) { + return "The event had no success."; + } else if (success < 0.2) { + return "The event had little success."; + } else if (success < 0.8) { + return "The event had normal success."; + } else if (success < 1.1) { + return "The event had high success."; + } else { + return "The event had extremely high success."; } + } - return []; + function intro() { + let r = []; + if (V.pit.audience === "none") { + r.push(`You are alone above the pit, left to watch them square off in private.`); + return r[0]; + } else if (V.pit.audience === "free") { + r.push(`Your guests line the rim of the pit, joking and betting.`); + } else { + r.push(`The attendees line the rim of the pit, betting and arguing.`); + } - function getScheduledFight() { - if (V.pit.slaveFightingAnimal) { - return [V.pit.slaveFightingAnimal]; - } - if (V.pit.slaveFightingBodyguard) { - return [V.pit.slaveFightingBodyguard, S.Bodyguard.ID]; - } + if (V.pit.lethal === 1) { + r.push("They expect to see a mix of lethal and nonlethal fights."); } - function getSpecificFight() { - if (V.pit.slavesFighting.length > 1 && - V.pit.slavesFighting.every(a => available.includes(a) && canFight(getSlave(a)))) { - return V.pit.slavesFighting.slice(0, 2); // cut the array off at 2 items in case it was somehow longer - } - return []; + if (V.arcologies[0].FSRomanRevivalist !== "unset") { + r.push(`They `, App.UI.DOM.makeElement("span", 'strongly approve', ["reputation", + "inc"]), ` of your hosting combat between slaves; this advances ideas from antiquity about what public events should be.`); + + repX(10 * V.FSSingleSlaveRep * (V.arcologies[0].FSRomanRevivalist / V.FSLockinLevel), "pit"); + V.arcologies[0].FSRomanRevivalist += (0.2 * V.FSSingleSlaveRep); } - function getRandomFight() { - const fightDelegates = []; + if (V.pit.minimumHealth) { + if (V.arcologies[0].FSPaternalist !== "unset") { + r.push(`They `, App.UI.DOM.makeElement("span", 'strongly approve', ["reputation", + "inc"]), ` of restricting fights to healthy slaves; this advances ideas about slave well-being.`); - if (V.active.canine || V.active.hooved || V.active.feline) { - fightDelegates.push(getAnimalFight); - } - if (S.Bodyguard) { - fightDelegates.push(getBodyguardFight); + repX(10 * V.FSSingleSlaveRep * (V.arcologies[0].FSPaternalist / V.FSLockinLevel), "pit"); + V.arcologies[0].FSPaternalist += (0.2 * V.FSSingleSlaveRep); + } else if (V.arcologies[0].FSDegradationist !== "unset") { + r.push(`They `, App.UI.DOM.makeElement("span", 'strongly disapprove', ["reputation", + "dec"]), ` of restricting fights to healthy slaves; this hampers ideas about slave degradation.`); + + repX(-10 * V.FSSingleSlaveRep * (V.arcologies[0].FSDegradationist / V.FSLockinLevel), "pit"); + V.arcologies[0].FSDegradationist -= (0.2 * V.FSSingleSlaveRep); } - if (available.length > 1) { - fightDelegates.push(getSlavesFight); + } else { + if (V.arcologies[0].FSPaternalist !== "unset") { + r.push(`They `, App.UI.DOM.makeElement("span", 'strongly disapprove', ["reputation", + "dec"]), ` of allowing ill slaves to fight; this hampers ideas about slave well-being.`); + + repX(-10 * V.FSSingleSlaveRep * (V.arcologies[0].FSPaternalist / V.FSLockinLevel), "pit"); + V.arcologies[0].FSPaternalist -= (0.2 * V.FSSingleSlaveRep); + } else if (V.arcologies[0].FSDegradationist !== "unset") { + r.push(`They `, App.UI.DOM.makeElement("span", 'strongly approve', ["reputation", + "inc"]), ` of allowing ill slaves to fight; this advances ideas about slave degradation.`); + + repX(10 * V.FSSingleSlaveRep * (V.arcologies[0].FSDegradationist / V.FSLockinLevel), "pit"); + V.arcologies[0].FSDegradationist += (0.2 * V.FSSingleSlaveRep); } + } + + const f = new DocumentFragment(); + App.Events.addParagraph(f, r); + return f; + } + + function finishEvent() { + const r = []; + V.nextButton = "Continue"; - return fightDelegates.length > 0 ? (fightDelegates.random())() : []; + if (V.pit.audience === "none") { + return new DocumentFragment(); } - function getAnimalFight() { - if ((!V.active.canine && !V.active.hooved && !V.active.feline) || - ((!V.active.canine && !V.active.hooved && V.active.feline && getAnimal(V.active.feline).species === "cat"))) { - return getSlavesFight(); - } - const fighter = available.pluck(); - V.pit.slaveFightingAnimal = fighter; + if (completedFights === 0) { + r.push("Opening the arena without hosting fights <span class='reputation dec'>hurt your reputation.</span>"); + repX(-100 * maxFights, "pit"); + const f = new DocumentFragment(); + App.Events.addParagraph(f, r); + return f; + } - return [fighter]; + if (completedFights < maxFights) { + r.push("The audience is disappointed in the few fights this week."); + totalSuccess -= (maxFights - completedFights) * 0.2; } - function getBodyguardFight() { - if (!S.Bodyguard) { return getSlavesFight(); } - if (available.includes(S.Bodyguard.ID)) { - available.delete(S.Bodyguard.ID); + if (V.pit.lethal === 1) { + if (lethalFights === 0) { + r.push("The audience expected to see blood and did not get any."); + totalSuccess -= 0.1 * Math.abs(totalSuccess); + } else if (lethalFights === completedFights) { + r.push("The audience only saw blood today and is missing some other outcomes."); + totalSuccess -= 0.1 * Math.abs(totalSuccess); } - return [available.pluck(), S.Bodyguard.ID]; } - function getSlavesFight() { - return [available.pluck(), available.pluck()]; + const fighterRatio = fighterMap.fighterCount() / fighterMap.fightsCount(); + if (fighterRatio > 0.95) { + r.push("The audience is <span class='green'>happy</span> seeing a lot of different fighters."); + totalSuccess *= 1.1; + } else if (fighterRatio > 0.8) { + // no reaction + } else { + if (fighterRatio > 0.5) { + r.push("The audience is <span class='red'>unhappy</span> seeing a lot the same fighters."); + } else { + r.push("Almost every fight had repeat fighters, <span class='red'>boring</span> the audience."); + } + totalSuccess *= 0.5 + 0.5 * (fighterRatio + 0.1); } - /** @param {App.Entity.SlaveState} slave */ - function canFight(slave) { - if (!canWalk(slave)) { - return false; - } + totalSuccess *= 0.5 + 0.5 * V.pit.seats; + if (V.pit.seats === 0) { + r.push("The small visitors section <span class='red'>limited</span> the impact today's fights had."); + } else if (V.pit.seats === 2) { + r.push("The large visitors section <span class='green'>greatly enhanced</span> the impact today's fights had."); + } + + const averageSuccess = totalSuccess / maxFights; + r.push(eventSuccess(averageSuccess)); - return true; + r.push("At the and of the day you"); + + const repGain = 100 * totalSuccess; + const cashGain = 2000 * totalSuccess; + const direction = totalSuccess > 0 ? "gained" : "lost"; + const cssDir = totalSuccess > 0 ? "inc" : "dec"; + if (V.pit.audience === "free") { + r.push(`<span class='reputation ${cssDir}'>${direction} reputation.</span>`); + repX(repGain, "pit"); + } else if (V.pit.audience === "paid") { + r.push(`<span class='cash ${cssDir}'>${direction} credits.</span>`); + cashX(cashGain, "pit"); } - } - } - /** @param {DocumentFragment} node */ - execute(node) { - V.pit.fought = true; + console.log("PIT:"); + console.log("total success:", totalSuccess, "avg:", averageSuccess); + console.log("possible rep gain: ", repGain); + console.log("possible cash gain:", cashGain); - if (V.pit.lethal) { - node.append(App.Facilities.Pit.lethalFight(this.actors)); - } else { - node.append(App.Facilities.Pit.nonlethalFight(this.actors)); + const f = new DocumentFragment(); + App.Events.addParagraph(f, r); + return f; } } }; diff --git a/src/events/scheduled/pitFightLethal.js b/src/events/scheduled/pitFightLethal.js deleted file mode 100644 index 7646a0e98ab5ee3b9757d6f75264fdfbe500a181..0000000000000000000000000000000000000000 --- a/src/events/scheduled/pitFightLethal.js +++ /dev/null @@ -1,862 +0,0 @@ -// TODO: add devotion and trust effects to animal variant -App.Facilities.Pit.lethalFight = function(fighters) { - const frag = new DocumentFragment(); - - const animals = []; - /** @type {FC.AnimalState} */ - let animal = null; - - if (V.active.canine) { - animals.push(V.active.canine); - } - if (V.active.hooved) { - animals.push(V.active.hooved); - } - if (V.active.feline) { - animals.push(V.active.feline); - } - - if (V.pit.fighters === 2 || V.pit.slaveFightingAnimal) { - animal = V.pit.animal === 'random' - ? getAnimal(animals.random()) - : getAnimal(V.pit.animal); - } - - let winner; - let loser; - - if (animal) { - winner = getWinner() ? getSlave(fighters[0]) : animal; - loser = winner.hasOwnProperty('species') ? getSlave(fighters[0]) : animal; - } else { - winner = getWinner() ? getSlave(fighters[0]) : getSlave(fighters[1]); - loser = winner.ID === fighters[0] ? getSlave(fighters[1]) : getSlave(fighters[0]); - } - - if (V.debugMode) { - console.log(winner, loser, animal); - } - - intro(frag); - fighterDeadliness(frag, getSlave(fighters[0])); - if (!animal) { - fighterDeadliness(frag, getSlave(fighters[1])); - } - fight(frag); - postFight(frag); - - V.pit.slaveFightingBodyguard = null; - V.pit.slaveFightingAnimal = null; - V.pit.slavesFighting = []; - - return frag; - - /** @param {DocumentFragment} parent */ - function intro(parent) { - const fighterOne = getSlave(fighters[0]); - const fighterTwo = getSlave(fighters[1]) || null; - - if (V.debugMode) { - console.log(fighterOne, fighterTwo); - } - - const r = []; - - App.Events.drawEventArt(parent, fighters.map(id => getSlave(id)), "no clothing"); - - const anAnimal = animal ? `${animal.articleAn} ${animal.name}.` : null; - - r.push(`This week's fight is between`, - App.UI.DOM.slaveDescriptionDialog(fighterOne), `and`, - animal - ? anAnimal - : App.UI.DOM.combineNodes(contextualIntro(fighterOne, fighterTwo, true), ".")); - - if (V.pit.audience === "none") { - r.push(`You are alone above the pit, left to watch them kill and die in private.`); - } else if (V.pit.audience === "free") { - r.push(`Your guests line the rim of the pit, joking and betting, nervous and excited by the close prospect of watching a slave die.`); - } else { - r.push(`The attendees line the rim of the pit, betting and arguing, nervous and excited by the close prospect of watching a slave die.`); - } - - if (V.arcologies[0].FSRomanRevivalist !== "unset") { - r.push(`They `, App.UI.DOM.makeElement("span", `passionately approve`, ['reputation', 'inc']), ` of your hosting lethal combat between slaves; this advances ideas from antiquity about what public events should be.`); - - repX(20 * V.FSSingleSlaveRep * (V.arcologies[0].FSRomanRevivalist / V.FSLockinLevel), "pit"); - V.arcologies[0].FSRomanRevivalist += (0.4 * V.FSSingleSlaveRep); - } else if (V.arcologies[0].FSAztecRevivalist !== "unset") { - r.push(`They `, App.UI.DOM.makeElement("span", `approve`, ['reputation', 'inc']), ` of your hosting lethal combat; this advances ideas that blood must flow as it did in ancient times.`); - - repX(10 * V.FSSingleSlaveRep * (V.arcologies[0].FSAztecRevivalist / V.FSLockinLevel), "pit"); - V.arcologies[0].FSAztecRevivalist += (0.2 * V.FSSingleSlaveRep); - } - - if (animal) { - const slave = getSlave(fighters[0]); - const {he, him, his} = getPronouns(slave); - - r.push(`You review the rules — the slave will use ${his} choice of swords, and the fight will be to the death. If ${he} wins, ${he} will earn two complete days of rest, not to mention continued life. You take a moment to look over ${fighterOne.slaveName} one more time before giving the word. After all, it might be the last time you'll see ${him} alive.`); - } else { - r.push(`You review the rules — the combatants will use their choice of swords, and the fight will be to the death, with both fighters executed if no decision is reached. The winner will earn two complete days of rest, not to mention continued life. You take a moment to look over your fighters one more time before giving the word. After all, this is likely the last time you'll see one of them alive.`); - } - - App.Events.addParagraph(parent, r); - } - - /** - * @param {DocumentFragment} parent - * @param {App.Entity.SlaveState} fighter - */ - function fighterDeadliness(parent, fighter) { - const {he, his, him, himself, He, His} = getPronouns(fighter); - const fighterDeadliness = deadliness(fighter).value; - - const r = []; - - r.push( - confidence(), - willingness(), - skill(), - age(), - muscles(), - height(), - health(), - weight(), - tired(), - pregnancy(), - labor(), - bellyFluid(), - sight(), - hearing(), - prosthetics(), - ); - - App.Events.addParagraph(parent, r); - - function confidence() { - if (fighter.fetish === Fetish.MINDBROKEN) { - return `${fighter.slaveName} is too broken to care about whether ${he} lives or dies;`; - } else if (fighterDeadliness > 5) { - return `${fighter.slaveName} seems very confident;`; - } else if (fighterDeadliness > 3) { - return `${fighter.slaveName} seems nervous, but steels ${himself};`; - } else if (fighterDeadliness > 1) { - return `${fighter.slaveName} seems hesitant and unsure;`; - } else { - return `${fighter.slaveName} is obviously terrified, and might flee if there were a way out of the pit;`; - } - } - - function willingness() { - if (fighter.fetish === Fetish.MINDBROKEN) { - return `${he} is indifferent to the prospect of killing, as well.`; - } else if (fighter.devotion > 95) { - return `${he} is clearly willing to do ${his} best to kill for you.`; - } else if (fighter.fetish === "sadist" && fighter.fetishKnown && fighter.fetishStrength > 60) { - return `the prospect of killing does not seem to concern ${him}.`; - } else if (fighter.devotion > 50) { - return `${he} obviously does not want to kill, but will do as you order.`; - } else if (fighter.devotion > -20) { - return `${he} is clearly unhappy at the prospect of killing, but knows that the alternative is death.`; - } else { - return `${he} knows that it's kill or be killed, and puts aside ${his} hatred of you in an effort to live.`; - } - } - - function skill() { - if (fighter.skill.combat > 30) { - return `${His} grip on ${his} sword is sure and easy.`; - } - } - - function age() { - if (V.AgePenalty !== 0) { - if (fighter.physicalAge >= 100) { - return `${He} seems prepared for death, in a way.`; - } else if (fighter.physicalAge >= 85) { - return `${He} tries not to waste ${his} strength before the fight, knowing that ${his} extreme age won't allow ${him} a second wind.`; - } else if (fighter.physicalAge >= 70) { - return `${He} steadies ${himself} as well as ${he} can in ${his} advanced age.`; - } - } - } - - function muscles() { - if (fighter.muscles > 95) { - return `${He} is wielding a massive two-handed blade few others could even heft.`; - } else if (fighter.muscles > 30) { - return `${He} is strong enough to handle a bastard sword.`; - } else if (fighter.muscles > 5) { - return `${He} has selected a longsword suited to ${his} strength.`; - } else if (fighter.muscles < -95) { - return `${He} has selected a meager dagger; even then ${he} can barely wield it.`; - } else if (fighter.muscles < -30) { - return `${He} has selected a dagger, the heaviest weapon ${he} can manage.`; - } else if (fighter.muscles < -5) { - return `${He} has selected a short sword, despite being able to barely lift it.`; - } else { - return `${He} has selected a short sword, the heaviest weapon ${he} can manage.`; - } - } - - function height() { - if (fighter.height > 170) { - return `${His} height gives ${him} a reach advantage.`; - } - } - - function health() { - if (fighter.health.condition > 50) { - return `${His} shining health makes ${him} a better fighter.`; - } else if (fighter.health.condition) { - return `${His} poor health makes ${him} a weaker combatant.`; - } - } - - function weight() { - if (fighter.weight > 190) { - return `${His} extreme weight nearly immobilizes ${him}. ${He} struggles to move let alone fight.`; - } else if (fighter.weight > 160) { - return `${His} extreme weight limits ${his} mobility and range of motion, making ${him} an easy target.`; - } else if (fighter.weight > 130) { - return `${His} extreme weight holds ${him} back as a pit fighter.`; - } else if (fighter.weight > 30) { - return `${His} heavy weight is an impediment as a pit fighter.`; - } else if (fighter.weight < -10) { - return `${His} light weight is an impediment as a pit fighter.`; - } - } - - function tired() { - if (fighter.health.tired > 90) { - return `${He} is exhausted and can barely stay awake; ${he} won't put up a fight.`; - } else if (fighter.health.tired > 60) { - return `${He} is fatigued, sapping the strength ${he}'ll need to strike true.`; - } else if (fighter.health.tired > 30) { - return `${He} is tired and more likely to take a hit than to give one.`; - } - } - - function pregnancy() { - if (fighter.pregKnown || fighter.bellyPreg > 1500) { - if (fighter.bellyPreg > 750000) { - return `${His} monolithic pregnancy guarantees ${his} and ${his} many, many children's deaths; not only is ${he} on the verge of splitting open, but it is an unmissable, indefensible target. ${He} has no hope of attacking around the straining mass, let alone stopping ${his} opponent. ${He} is damned.`; - } else if (fighter.bellyPreg > 600000) { - return `${His} titanic pregnancy is practically a death sentence; not only does ${he} risk bursting, but it is an unmissable, indefensible target. ${He} can barely keep it together while thinking about the lives of ${his} brood.`; - } else if (fighter.bellyPreg > 450000) { - return `${His} gigantic pregnancy practically damns ${him}; it presents an unmissable, indefensible target for ${his} adversary. ${He} can barely keep it together while thinking about the lives of ${his} brood.`; - } else if (fighter.bellyPreg > 300000) { - return `${His} massive pregnancy obstructs ${his} movement and greatly hinders ${him}. ${He} struggles to think of how ${he} could even begin to defend it from harm.`; - } else if (fighter.bellyPreg > 150000) { - return `${His} giant pregnancy obstructs ${his} movement and greatly slows ${him} down. ${He} tries not to think of how many lives are depending on ${him}.`; - } else if (fighter.bellyPreg > 100000) { - return `${His} giant belly gets in ${his} way and weighs ${him} down. ${He} is terrified for the lives of ${his} many children.`; - } else if (fighter.bellyPreg > 10000) { - return `${His} huge belly gets in ${his} way and weighs ${him} down. ${He} is terrified for the ${fighter.pregType > 1 ? `lives of ${his} children` : `life of ${his} child`}.`; - } else if (fighter.bellyPreg > 5000) { - return `${His} advanced pregnancy makes ${him} much less effective, not to mention terrified for ${his} child${fighter.pregType > 1 ? `ren` : ``}.`; - } else if (fighter.bellyPreg > 1500) { - return `${His} growing pregnancy distracts ${him} with concern over the life growing within ${him}.`; - } else { - return `The life just beginning to grow inside ${him} distracts ${him} from the fight.`; - } - } else if (fighter.bellyImplant > 1500) { - if (fighter.bellyImplant > 750000) { - return `${His} monolithic, ${fighter.bellyImplant}cc implant-filled belly guarantees ${his} death; not only is ${he} on the verge of splitting open, but it is an unmissable, indefensible target that threatens to drag ${him} to the ground. ${He} has no hope of attacking around the straining mass, let alone stopping ${his} opponent.`; - } else if (fighter.bellyImplant > 600000) { - return `${His} titanic, ${fighter.bellyImplant}cc implant-filled belly is practically a guaranteed death; ${he} can barely stand let alone fight. Not only is it cripplingly heavy, unwieldy and an easy target, but ${he} can feel it straining to hold the sheer amount of filler forced into it.`; - } else if (fighter.bellyImplant > 450000) { - return `${His} gigantic, ${fighter.bellyImplant}cc implant-filled belly is nearly a guaranteed death; it presents an unmissable, indefensible target for ${his} adversary.`; - } else if (fighter.bellyImplant > 300000) { - return `${His} massive, ${fighter.bellyImplant}cc implant-filled belly is extremely heavy, unwieldy and an easy target, practically damning ${him} in combat.`; - } else if (fighter.bellyImplant > 150000) { - return `${His} giant, ${fighter.bellyImplant}cc implant-filled belly obstructs ${his} movement and greatly slows ${him} down.`; - } else if (fighter.bellyImplant > 100000) { - return `${His} giant, ${fighter.bellyImplant}cc implant-filled belly is very heavy and unwieldy, throwing off ${his} weight and making ${him} far less effective.`; - } else if (fighter.bellyImplant > 10000) { - return `${His} huge, ${fighter.bellyImplant}cc implant-filled belly is very heavy and unwieldy, throwing off ${his} weight and making ${him} far less effective.`; - } else if (fighter.bellyImplant > 5000) { - return `${His} large, ${fighter.bellyImplant}cc implant-filled belly is heavy and unwieldy, rendering ${him} less effective.`; - } else if (fighter.bellyImplant > 1500) { - return `${His} swollen, ${fighter.bellyImplant}cc implant-filled belly is heavy and makes ${him} less effective.`; - } - } - } - - function labor() { - if (isInLabor(fighter)) { - return `${He}'s feeling labor pains. ${His} ${fighter.pregType > 1 ? `children are` : `child is`} ready to be born, oblivious to the fact that it will mean the death of ${fighter.pregType > 1 ? `their` : `its`} mother.`; - } else if (fighter.preg > fighter.pregData.normalBirth && fighter.pregControl !== "labor suppressors") { - return `${He}'ll be going into labor any time now and ${he} knows it. ${He}'s terrified of the thought of ${his} water breaking during the fight.`; - } - } - - function bellyFluid() { - if (fighter.bellyFluid > 10000) { - return `${His} hugely bloated, ${fighter.inflationType}-filled belly is taut and painful, hindering ${his} ability to fight.`; - } else if (fighter.bellyFluid > 5000) { - return `${His} bloated, ${fighter.inflationType}-stuffed belly is constantly jiggling and moving, distracting ${him} and throwing off ${his} weight.`; - } else if (fighter.bellyFluid > 2000) { - return `${His} distended, ${fighter.inflationType}-belly is uncomfortable and heavy, distracting ${him}.`; - } - } - - function sight() { - if (!canSee(fighter)) { - return `${His} lack of eyesight is certain death.`; - } else if (!canSeePerfectly(fighter)) { - return `${His} poor eyesight makes ${him} a weaker combatant.`; - } - } - - function hearing() { - if (!canHear(fighter)) { - return `${His} lack of hearing is a major detriment.`; - } else if ((fighter.hears === -1 && fighter.earwear !== "hearing aids") || (fighter.hears === 0 && fighter.earwear === "muffling ear plugs")) { // TODO: replace with canHearPerfectly - return `${His} poor hearing is a minor detriment.`; - } - } - - function prosthetics() { - if (hasAnyProstheticLimbs(fighter) && !hasAnyQuadrupedLimbs(fighter)) { - const r = []; - - r.push(`The pit lights gleam on ${his} P-Limbs.`); - - if (getLimbCount(fighter, 6) > 0) { - r.push(`The limits on ${his} cybernetic limbs have been lifted, turning them deadly.`); - } else if (getLimbCount(fighter, 5) > 0) { - r.push(`Their integral weapons are fully uncaged.`); - } - - return r.join(' '); - } - if (hasAnyProstheticLimbs(fighter) && hasAnyQuadrupedLimbs(fighter)) { - const r = []; - - r.push(`The pit lights gleam on ${his} quadrupedal limbs. The retractable claws of ${his} paws have been fully extended, ready to rip and tear.`); - return r.join(' '); - } - } - } - - /** @param {DocumentFragment} parent */ - function fight(parent) { - const r = []; - - r.push(`In any case, ${animal ? `the slave and the ${animal.name} have both` : `both of the slaves`} been dosed with aggression enhancers and inhibition blockers; they'll fight to kill.`); - - if (animal) { - if (winner.hasOwnProperty('slaveName')) { - const {he, his, him, himself, girl, He} = getPronouns(winner); - - if (!canSee(winner)) { - r.push(`${winner.slaveName} centers ${himself} and hones in on the ${animal.name}'s rapid breath. ${He} readies ${himself}, knowing ${he} has a single chance to live. With heavy steps, the ${animal.species} rushes at ${him}, fully prepared to slaughter the helpless combatant. ${winner.slaveName} gambles everything on a single high slash, a slash that cuts the ${animal.species}'s undefended neck wide open. ${winner.slaveName} collapses to the ground, fully aware of the ${animal.species}'s final gurgles and the `, App.UI.DOM.makeElement('span', `deep scratch in ${his} side.`, ['health', 'dec']), ` ${He} lived this time, barely, but ${he} doubts ${he} can do it again.`); - - healthDamage(winner, 75); - - if (V.pit.audience === 'paid') { - r.push(`The audience is awestruck by the blind ${girl}'s triumph, and your`, App.UI.DOM.makeElement('span', `cut of the betting`, ['cash', 'inc']), ` is enormous.`); - - cashX(10000, "pit", winner); - } else if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `awestruck`, ['reputation', 'inc']), `by the blind ${girl}'s triumph.`); - - repX(1000, "pit", winner); - } - } else if (winner.muscles) { - r.push(`${winner.slaveName} is so massively muscular that ${he}'s actually impeded by ${his} lack of speed and flexibility. The ${animal.species} is suitably cautious, and it does its best to avoid ${his} sword. The two go back and forth for a while – the slave slashing and swinging, the animal leaping out of the way. ${winner.slaveName} finally manages to get the better of the ${animal.species}, though, and manages to catch the tip of ${his} sword on the animal's leg. With a loud ${animal.type === 'canine' ? `yelp` : animal.type === 'hooved' ? `whinny` : `howl`}, the ${animal.species} stumbles back, giving the slave a chance to bring ${his} sword down in an overhead swing, nearly removing the ${animal.species}'s head.`); - - if (V.pit.audience === 'paid') { - r.push(`The audience is awestruck by the blind ${girl}'s triumph, and your`, App.UI.DOM.makeElement('span', `cut of the betting`, ['cash', 'inc']), ` is enormous.`); - - cashX(10000, "pit", winner); - } else if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `awestruck`, ['reputation', 'inc']), `by the blind ${girl}'s triumph.`); - - repX(1000, "pit", winner); - } - } else { - if (V.pit.audience === 'paid') { - r.push(`The audience is awestruck by the blind ${girl}'s triumph, and your`, App.UI.DOM.makeElement('span', `cut of the betting`, ['cash', 'inc']), ` is enormous.`); - - cashX(10000, "pit", winner); - } else if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `awestruck`, ['reputation', 'inc']), `by the blind ${girl}'s triumph.`); - - repX(1000, "pit", winner); - } - } - } else { - const {he, his, him, He} = getPronouns(loser); - - if (!canSee(loser)) { - r.push(`The ${animal.species} isn't aware that its opponent is blind, and either way, it wouldn't have cared. It slowly paces around the flailing ${loser.slaveName}, looking for an opening. Seeing one, the ${animal.species} ${animal.type === "hooved" ? `rushes` : `lunges`} at ${him}, ending ${his} life in one fell swoop.`); - - if (V.pit.audience === "paid") { - r.push(`The audience found the fight embarrassing, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is pitiful.`); - - cashX(40, "pit", loser); - } else if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `annoyed`, ["reputation", "dec"]), ` by this waste of a fight.`); - - repX(-20, "pit", loser); - } - } else if (animal.deadliness < deadliness(loser).value + 3) { - if (loser.skill.combat > 30) { - r.push(`${loser.slaveName} is fairly confident in ${his} fighting abilities, and ${he} knows that this fight is fairly evenly matched.`); - } else { - r.push(`${loser.slaveName} doesn't know how to handle a sword, but ${he} feels fairly confident in ${his} chances all the same.`); - } - - r.push(`${He} doesn't know how to go about attacking an animal, though, so ${he} decides to play it safe and keep the ${animal.species} at sword's length. The ${animal.species} make a few false lunges at the slave, all the while keeping out of reach. After a few minutes of this, though, it's evident that ${loser.slaveName} is beginning to tire: ${his} sword is beginning to swing slower and slower, and ${his} stance isn't as straight. The animal seems to sense this, and, spotting an opening, makes a final lunge. Its ${animal.type === "hooved" ? `hooves connect with ${his} skull` : `teeth sink into ${his} throat`}, ending ${his} life almost immediately.`); - } else if (loser.belly > 300000) { - r.push(`${loser.slaveName}'s belly is too big to possibly defend, so ${he} can't help but ${canSee(loser) ? `watch` : `cringe`} in horror as the ${animal.species} lunges at ${him}, ${animal.type === "hooved" ? `headfirst` : `fangs and claws outstretched`}. ${loser.slaveName}'s belly ruptures like a popped water balloon, showering the animal with`); - - if (loser.pregType > 0) { - r.push(`blood. ${loser.slaveName} collapses into the pile of organs and babies released from ${his} body.`); - } else if (loser.bellyImplant > 0) { - r.push(`blood and filler. ${loser.slaveName} collapses into the pool of organs and fluid released from ${his} body.`); - } else { - r.push(`blood and ${loser.inflationType}. ${loser.slaveName} collapses into the pool of organs and fluid released from ${his} body.`); - } - - r.push(`With a ${animal.type === "hooved" ? `growl` : `snort`}, the ${animal.species} quickly finishes ${him} off${animal.type === "hooved" ? ` with a swift kick to the head` : ``}.`); - - if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy kill, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is also unimpressive.`); - - cashX(2000, "pit", loser); - } else if (V.pit.audience === "free") { - r.push(`the audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), ` by this easy kill.`); - - repX(100, "pit", loser); - } - } else if (loser.boobs > 1200) { - r.push(`${loser.slaveName}'s tits are too big to possibly defend, so ${he} can't help but ${canSee(loser) ? `watch` : `cringe`} in horror in horror as the ${animal.species} lunges at ${him}, ${animal.type === "hooved" ? `headfirst` : `fangs and claws outstretched`}. ${loser.slaveName}'s reflexively drops ${his} sword to clasp ${his} ${hasBothArms(loser) ? `hands` : `hand`} over ${his} ruined breasts, gushing blood${loser.boobsImplant > 400 ? ` and implant fluid` : ``}. The ${animal.species} follows up with a ${animal.type === "hooved" - ? `fierce bite to ${hasBothLegs(loser) ? `one of ${his} legs` : hasAnyLegs(loser) ? `${his} leg` : `${his} rear`}, causing ${him} to collapse. A second later, than animal's fangs have finished ${him} off.` - : `swift kick to the chest, causing ${him} to drop like a rock. Another blow to the head finishes ${him} off.`}`); - - if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy kill, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is also unimpressive.`); - - cashX(2000, "pit", loser); - } else if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), ` by this easy kill.`); - - repX(100, "pit", loser); - } - } else { - if (loser.skill.combat > 30) { - r.push(`${loser.slaveName} is reasonably confident in ${his} fighting abilities, but ${he} isn't sure how to go about attacking the ${animal.species}. ${He} decides to open with a series of wide horizontal slashes, but the beast manages to stay out of range of ${his} sword. Realizing this isn't working, ${he} lunges at the animal, leaving ${his} side exposed. The ${animal.species} sees this and ${animal.type === "hooved" - ? `headbutts ${him} as hard as it can, sending ${him} flying into the side of ${V.pit.name}. Battered` - : `leaves some ${App.UI.DOM.makeElement("span", `deep claw marks in ${his} side.`, ['health', 'dec'])} Bleeding`} and unable to stand, ${loser.slaveName} can do little to stop the ${animal.species} from finishing ${him} off with a ${animal.type === "canine" ? `fierce bite of its fangs to ${his} throat.` : animal.type === "hooved" ? `swift kick to the head.` : `ferocious swipe of its claws to ${his} throat.`}`); - - if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the blood, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is reasonable.`); - - cashX(5000, "pit", loser); - } else if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), ` by the blood.`); - - repX(250, "pit", loser); - } - } else { - r.push(animal.type === "hooved" - ? `The ${animal.species} paws at the ground for a few seconds before launching itself at ${loser.slaveName}. ${He} just barely manages to get out of the way, and the ${animal.species} has to backpedal to avoid smashing into the wall. ${loser.slaveName} isn't out of danger just yet, though — ${he} is still standing directly behind the ${animal.species}. ${He} realizes this too late, as both of the its hooves connect with ${his} jaw. With a sickening ${App.UI.DOM.makeElement("span", `crunch`, ["note"])}, ${he} flies backwards, ${his} body slamming into the other wall of ${V.pit.name} before crumpling in a heap on the ground.` - : `${loser.slaveName} doesn't stand a chance, and ${he} knows it. ${He} comes in with a furious overhead slash, which the ${animal.species} dodges with ease. It also dodges the next few slashes before coming to a standstill. With a furious growl, it runs around ${him}, just out of reach of ${his} sword, before jumping at the wall of ${V.pit.name} and launching itself off. Its ${animal.type === "canine" ? `teeth` : `claws`} connect with ${loser.slaveName}'s throat, completely severing ${his} windpipe. ${He} falls to ${his} knees, eyes wide and clutching ${his} throat, before completely collapsing.`, - ); - - if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the violence, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is also reasonable.`); - - cashX(5000, "pit", loser); - } else if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), ` by the violence.`); - - repX(250, "pit", loser); - } - } - } - } - } else { - const winnerDeadliness = deadliness(winner).value; - const loserDeadliness = deadliness(loser).value; - - const {he, his, him, himself, girl, He} = getPronouns(winner); - const {He: He2, he: he2, his: his2, him: him2, girl: girl2} = getPronouns(loser); - - if (!canSee(winner) && !canSee(loser)) { - r.push(`${winner.slaveName} and ${loser.slaveName} are both blind, making the fight a staredown. Neither slave wants to make the first move, especially with the drowning cacophony coming from the jeering crowd. Slowly, ${winner.slaveName} moves forward, waving ${his} sword side to side in the hopes of finding ${his} opponent before ${he} ${himself} gets found. ${loser.slaveName}'s sword clips the tip of ${winner.slaveName}'s setting off a surprisingly brutal series of slashes. The two slaves violently lay into each other, suffering more and more strikes as the struggle goes on. Less than a minute from first contact, both slaves are lying in their combined blood. As you begin to call it a draw, you notice ${winner.slaveName} start to stir. ${He} staggers to ${his} feet, coated in ${his} and ${loser.slaveName}'s blood, before dropping back to ${his} knees. It seems ${he} lived through this fight; odds aren't likely ${he}'ll do it again.`); - - healthDamage(winner, 50); - - if (V.pit.audience === "free") { - r.push(`The audience is surprised by the sudden violence after so much standing around, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is modest.`); - - cashX(5000, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `surprised`, ['reputation', 'inc']), ` by the sudden violence after so much standing around.`); - - repX(250, "pit", winner); - } - } else if (winnerDeadliness > (loserDeadliness + 1) && getArmCount(winner, 6) > 1) { - r.push(`Upon your word the two combatants approach each other. ${winner.slaveName} does not carry a sword, raising only ${his} hands, but these are terrifying, modern weapons themselves. ${loser.slaveName} tries a probing sweep, but ${winner.slaveName} catches ${his2} blade in one hand, slides it down to the hilt with a shriek of metal on metal, and snaps the blade cleanly. ${loser.slaveName} ${canSee(loser) ? `watches this` : `freezes in place`}, dumbstruck, before regaining composure and taking a fighting stance again. ${winner.slaveName} manages to touch ${his} opponent's bare skin with ${his} own cybernetics, incapacitating ${loser.slaveName} with an electric shock. While ${loser.slaveName} is temporarily stunned, ${winner.slaveName} strikes ${loser.slaveName} on the head with deadly force, crushing ${his} opponent's skull. Though the fight was short,`); - - if (V.pit.audience === "free") { - r.push(`the audience is `, App.UI.DOM.makeElement("span", `very impressed`, ['reputation', 'inc']), ` by the display.`); - - repX(500, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`the audience is quite impressed by the display, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is quite good.`); - - cashX(10000, "pit", winner); - } - } else if (winnerDeadliness > (loserDeadliness + 1) && getArmCount(winner, 5) > 1) { - r.push(`Upon your word the two combatants approach each other. ${winner.slaveName} drops ${his} sword as ${he} comes, raising only ${his} hands, but these are terrifying, modern weapons themselves. ${loser.slaveName} tries a probing sweep, but ${winner.slaveName} catches ${his2} blade in one hand, slides it down to the hilt with a shriek of metal on metal, and snaps the blade cleanly. As ${loser.slaveName} watches this, dumbstruck, ${winner.slaveName} places a gentle hand under ${his2} chin and then extends that arm's integral blade up and through ${his} opponent's skull. Though the fight was short,`); - - if (V.pit.audience === "free") { - r.push(`the audience is `, App.UI.DOM.makeElement("span", `very impressed`, ['reputation', 'inc']), ` by the display.`); - - repX(500, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`the audience is quite impressed by the display, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is quite good.`); - - cashX(10000, "pit", winner); - } - } else if (!canSee(winner)) { - r.push(`${winner.slaveName} centers ${himself} and hones in on ${loser.slaveName}'s rapid breath. ${He} readies ${himself}, knowing ${he} has a single chance to live. With heavy steps, ${loser.slaveName} rushes ${him}, fully prepared to slaughter the helpless combatant. ${winner.slaveName} gambles everything on a single high slash, a slash that cut ${loser.slaveName}'s undefended neck wide open. ${winner.slaveName} collapses to the ground, fully aware of ${loser.slaveName}'s final gurgles, and the sword `, App.UI.DOM.makeElement("span", `firmly planted in ${his} side.`, ['health', 'dec']), ` ${He} lived this time, barely, but ${he} doubts ${he} can do it again.`); - - healthDamage(winner, 80); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `awestruck`, ['reputation', 'inc']), ` by the blind ${girl}'s triumph.`); - - repX(2000, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is awestruck by the blind ${girl}'s triumph, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is enormous.`); - - cashX(40000, "pit", winner); - } - } else if (!canSee(loser)) { - r.push(`${winner.slaveName} sighs at ${loser.slaveName}'s random slashing and calmly struts around the panicking slave. In one quick swoop, ${he} buries ${his} blade in ${loser.slaveName}'s back, ending the poor ${girl2}'s flailing.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `annoyed`, ["reputation", "dec"]), ` by this waste of a fight.`); - - repX(-20, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience found the fight embarrassing, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is pitiful.`); - - cashX(40, "pit", winner); - } - } else if (winnerDeadliness > (loserDeadliness + 3)) { - if (winner.skill.combat > 30) { - r.push(`${winner.slaveName} wants to win, and ${he} opens the fight with a furious attack. ${loser.slaveName} manages to get ${his2} blade up, blocking a strike with a ringing clash and a few sparks, but by doing so leaves ${his2} abdomen open and obscures ${his2} vision enough that ${he2} is unprepared for the following horizontal slash, which opens ${his2} belly wide. The stricken ${girl2} collapses, feebly trying to push ${his2} viscera back into place. Whether out of mercy or a desire to get it over with, ${winner.slaveName} quickly removes the dying ${girl2}'s head.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), ` by the gore.`); - - repX(200, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the gore, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is reasonable.`); - - cashX(4000, "pit", winner); - } - } else { - r.push(`${winner.slaveName} wants to win and is confident ${he} will, but ${he} isn't particularly sure about how to do so. ${He} fights cautiously, swinging ${his} sword in powerful but inaccurate strokes. It is only a matter of time before one of these strikes gets through; it's telling that rather than hitting what ${he} aimed at, ${winner.slaveName} accidentally opens a massive gash down ${loser.slaveName}'s thigh. Realizing ${he2} has to do something, ${loser.slaveName} makes a desperate counterattack, pouring blood as ${he2} goes. ${winner.slaveName} panics and fails to parry one of the last counterstrikes before loss of blood ends the attack, suffering a `, App.UI.DOM.makeElement("span", `terrible cut`, ['health', 'dec']), ` to ${his} shoulder. Down to one arm, ${winner.slaveName} is forced to make a long, loud butchery of ending the fight.`); - - healthDamage(winner, 20); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), ` by the blood.`); - - repX(200, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the blood, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is reasonable.`); - - cashX(4000, "pit", winner); - } - } - } else if (winner.skill.combat > 30 && loser.skill.combat > 30) { - r.push(`Upon your word the two combatants approach each other warily, both knowing the other is reasonably competent. Before long they are trading thrust and parry, swing and block. ${winner.slaveName} is slowly pressed back, so ${he} decides to change the nature of the fight. After three tries ${he} manages to force ${loser.slaveName} to close, suffering a `, App.UI.DOM.makeElement("span", `nearly severed ear`, ['health', 'dec']), ` as ${he} does. ${loser.slaveName} realizes ${he2} only retains an advantage at long range but cannot back up fast enough to avoid close combat. ${loser.slaveName} is forced back fast enough that ${he2} trips; ${he2}'s barely fallen on ${his2} back before ${he2} grunts with shock and pain, dying with a look of surprise as ${he2} stares at the sword growing out of ${his2} chest.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `very impressed`, ['reputation', 'inc']), ` by the expert fight.`); - - repX(500, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is quite impressed by the expert fight, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is quite good.`); - - cashX(10000, "pit", winner); - } - } else if (winner.height - loser.height < -10) { - r.push(`${winner.slaveName} realizes that ${loser.slaveName}'s wingspan gives ${him2} a huge reach advantage. ${He} bores straight in, taking `, App.UI.DOM.makeElement("span", `a glancing scalp wound`, ['health', 'dec']), ` but coming on regardless. ${loser.slaveName} understands ${his2} opponent's intention and backs off, but the pit is small and there isn't much room to retreat. When ${his2} back hits a padded wall, ${winner.slaveName} aims a gutting cut that ${loser.slaveName} struggles to block. ${He2} manages it, but the wall catches ${his2} point, so the block is with ${his2} wrist, not ${his2} sword. The sharp blade cuts almost all the way through the joint, leaving ${him2} in agony and totally incapable of defense. ${winner.slaveName} pushes ${his2} head back against the wall and cuts ${his2} throat down to the spine.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), ` by the blood.`); - - repX(200, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the blood, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is reasonable.`); - - cashX(4000, "pit", winner); - } - } else if (winner.muscles > 30) { - r.push(`${winner.slaveName} is so massively muscular that ${he}'s actually impeded by ${his} lack of speed and flexibility. ${loser.slaveName} is properly afraid of ${his2} strength, though, so ${he2} tries to stay away as much as ${he2} can. The few times their blades clash reinforces this approach, since ${winner.slaveName} is able to beat ${his} opponent's blocks out of the way with contemptuous ease. The fight takes a long, long time, but it takes more out of ${loser.slaveName} to survive than it takes out of ${winner.slaveName} to keep swinging. Eventually the gasping, weeping ${loser.slaveName} trips and does not struggle to ${his2} feet in time. It takes ${his2} tired opponent several overhead butcher's cleaves to end it.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), ` by the show of strength.`); - - repX(50, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the show of strength, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is reasonable.`); - - cashX(1000, "pit", winner); - } - } else if (loser.belly > 300000) { - r.push(`${winner.slaveName} wants to live badly enough that ${he} takes an extremely brutal shortcut to victory. The instant the fight starts, ${he} quickly slices right across ${loser.slaveName}'s massive belly, which is far too large to possibly defend. ${loser.slaveName}'s belly ruptures like a popped water balloon, showering ${winner.slaveName} with`); - - if (loser.pregType > 0) { - r.push(`blood. ${loser.slaveName} collapses into the pile of organs and babies released from ${his2} body.`); - } else if (loser.bellyImplant > 0) { - r.push(`blood and filler. ${loser.slaveName} collapses into the pool of organs and fluid released from ${his2} body.`); - } else { - r.push(`blood and ${loser.inflationType}. ${loser.slaveName} collapses into the pool of organs and fluid released from ${his2} body.`); - } - - r.push(`${winner.slaveName} walks over to the bleeding out slave and quickly cuts ${his2} throat.`); - - if (V.pit.audience === "free") { - r.push(`the audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), ` by this easy kill.`); - - repX(100, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy kill, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is unimpressive.`); - - cashX(2000, "pit", winner); - } - } else if (loser.boobs > 1200) { - r.push(`${winner.slaveName} takes an extremely simple shortcut to victory. The instant the fight starts, ${he} slices ${loser.slaveName} right across ${his2} huge tits, which are so large they cannot properly be defended. ${loser.slaveName} reflexively drops ${his2} sword to clasp ${his2} ${hasBothArms(loser) ? `hands` : `hand`} over ${his2} ruined breasts, gushing blood${loser.boobsImplant > 400 ? ` and implant fluid` : ``}. ${winner.slaveName}'s follow-up is neither artful nor particularly well planned, but it is effective. ${He} hits the distracted ${girl2}'s neck from the side, almost but not quite separating ${his2} head from ${his2} body.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), ` by this easy kill.`); - - repX(100, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy kill, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is unimpressive.`); - - cashX(2000, "pit", winner); - } - } else if (loser.dick > 0) { - r.push(`${winner.slaveName} wants to live badly enough that ${he} takes an extremely brutal shortcut to victory. The instant the fight starts, ${he} furiously swings for ${loser.slaveName}'s face. ${loser.slaveName} reflexively raises ${his2} sword to block, at which point ${winner.slaveName} simply kicks ${him2} in the dick. ${loser.slaveName} goes down like a marionette with cut strings, ${his2} mouth soundlessly opening and closing and tears leaking from ${his2} closed eyes. ${winner.slaveName} walks over to the prostrate slave and cuts ${his2} throat without much trouble.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), ` by this easy kill.`); - - repX(100, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy kill, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is unimpressive.`); - - cashX(2000, "pit", winner); - } - } else { - r.push(`${winner.slaveName} wants to live badly enough that ${he} takes an extremely brutal shortcut to victory. The instant the fight starts, ${he} furiously swings for ${loser.slaveName}'s face. ${loser.slaveName} reflexively raises ${his2} sword to block, at which point ${winner.slaveName} simply kicks ${him2} in the cunt. ${loser.slaveName} goes down like a marionette with cut strings, ${his2} mouth soundlessly opening and closing and tears leaking from ${his2} closed eyes. ${winner.slaveName} walks over to the prostrate slave and cuts ${his2} throat without much trouble.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), ` by this easy kill.`); - - repX(100, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy kill, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), ` is unimpressive.`); - - cashX(2000, "pit", winner); - } - } - } - - App.Events.addParagraph(parent, r); - } - - /** @param {DocumentFragment} parent */ - function postFight(parent) { - const r = []; - - if (animal) { - if (winner.hasOwnProperty('slaveName')) { - const {he, his, himself, hers, He} = getPronouns(winner); - - r.push(`You let the winner ${winner.slaveName}, shaking as ${he} comes off the adrenaline, drugs, and fear, exit the pit.`); - - if (winner.fetish === Fetish.MINDBROKEN) { - r.push(`${He} was already so broken before today that ${he} will not be seriously affected by having killed another creature with ${his} own ${hasBothArms ? `hands` : `hand`}.`); - } else if (winner.devotion > 50) { - r.push(`${He} is so devoted that ${he} doesn't see anything wrong with killing another creature.`); - } else { - r.push(`${He} isn't happy about having to have taken another creature's life, and tells ${himself} that it was the animal's life or ${hers}.`); - } - } else { - r.push(`Another week, another slave killed by your animals.`); - } - } else { - const {he, his, him, himself, He} = getPronouns(winner); - const {sister: sister2} = getPronouns(loser); - const mindbrokenSpan = App.UI.DOM.makeElement("span", `no longer capable`, ["red"]); - - r.push(`You let the winner ${winner.slaveName}, shaking as ${he} comes off the adrenaline, drugs, and fear, exit the pit.`); - - if (winner.fetish === Fetish.MINDBROKEN) { - r.push(`${He} was already so broken before today that ${he} will not be seriously affected by having killed with ${his} own ${hasBothArms(winner) ? `hands` : `hand`}.`); - } else if (winner.devotion > 95) { - r.push(`${He} is so worshipful of you that ${he} does not see killing ${loser.slaveName} as special in ${his} own internal listing of things that ${he} has willingly done and would willingly do again on your behalf.`); - } else if (winner.devotion > 80) { - r.push(`${He} will never forget having killed ${loser.slaveName} with ${his} own ${hasBothArms(winner) ? `hands` : `hand`}. The only way ${he} will be able to live with ${himself} is to `, App.UI.DOM.makeElement("span", `subsume ${his} will to yours,`, ["devotion", "inc"]), ` telling ${himself} that the killing was to serve and obey you.`); - - winner.devotion += 10; - } else if (winner.fetish === "sadist" && winner.fetishKnown && winner.fetishStrength > 60) { - r.push(`${He} has sunk even deeper into cruelty, callousness, and a total disregard of the value of other slaves' lives. Going forward, ${he}'s even `, App.UI.DOM.makeElement("span", `less likely to question`, ["devotion", "inc"]), ` even the most abominable commands.`); - - winner.devotion += 10; - } else if (winner.devotion > 50) { - r.push(`${He} will never forget having killed ${loser.slaveName} with ${his} own ${hasBothArms(winner) ? `hands` : `hand`}. The only way ${he} will be able to live with ${himself} is to tell ${himself} that the killing was done on your orders.`); - } else { - r.push(`${He} will never forget having killed ${loser.slaveName} with ${his} own ${hasBothArms(winner) ? `hands` : `hand`}. The only way ${he} will be able to live with ${himself} is to `, App.UI.DOM.makeElement("span", `blame you,`, ["devotion", "dec"]), ` telling ${himself} that the killing was the only choice you gave ${him} if ${he} wanted to live.`); - - winner.devotion -= 10; - } - - if (winner.fetish !== "sadist") { - if (random(1, 100) > 50) { - r.push(`Cruelty and callousness seeps its way into ${his} sexuality; ${he} has become a `, App.UI.DOM.makeElement("span", `bloody sadist.`, ["fetish", "gain"])); - - winner.fetish = "sadist"; - winner.fetishKnown = 1; - winner.fetishStrength = 65; - } - } - - if (winner.rivalry && loser.ID === winner.rivalryTarget) { - if (winner.devotion > 75) { - r.push(`${He} is so accepting of the low value of slave life that ${he} `, App.UI.DOM.makeElement("span", `is pleased`, ["devotion", "inc"]), ` to have killed ${his} rival ${loser.slaveName}.`); - - winner.devotion += 4; - } - } else if (winner.relationship && loser.ID === winner.relationshipTarget) { - if (winner.devotion > 95) { - r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} only friend at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", "inc"]), ` end to their doomed slave relationship.`); - - winner.devotion += 4; - } else { - r.push(`${He} shows little reaction to the death of ${his} only friend at ${his} own hand. In the coming days, it becomes clear that this is because ${he} is `, mindbrokenSpan, ` of reacting to anything on an emotional level. Ever again.`); - - applyMindbroken(winner); - winner.fetishKnown = 1; - } - } else if (isParentP(winner, loser) || isParentP(loser, winner)) { - if (winner.devotion > 95) { - r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", "inc"]), ` end to their doomed family.`); - - winner.devotion += 4; - } else { - r.push(`${He} shows little reaction to the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand. In the coming days, it becomes clear that this is because ${he} is `, mindbrokenSpan, ` of reacting to anything on an emotional level. Ever again.`); - - applyMindbroken(winner); - winner.fetishKnown = 1; - } - } else if (winner.sisters > 0) { - switch (areSisters(winner, loser)) { - case 1: - if (winner.devotion > 95) { - r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", "inc"]), ` end to their doomed family.`); - - winner.devotion += 4; - } else { - r.push(`${He} shows little reaction to the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand. In the coming days, it becomes clear that this is because ${he} is `, mindbrokenSpan, ` of reacting to anything on an emotional level. Ever again.`); - - applyMindbroken(winner); - winner.fetishKnown = 1; - } - break; - case 2: - if (winner.devotion > 90) { - r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", "inc"]), ` end to their doomed family.`); - - winner.devotion += 4; - } else { - r.push(`${He} shows little reaction to the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand. In the coming days, it becomes clear that this is because ${he} is `, mindbrokenSpan, ` of reacting to anything on an emotional level. Ever again.`); - - applyMindbroken(winner); - winner.fetishKnown = 1; - } - break; - case 3: - if (winner.devotion > 85) { - r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} half-${sister2} at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", "inc"]), ` end to their doomed family.`); - - winner.devotion += 4; - } else { - r.push(`${He} is `, App.UI.DOM.makeElement("span", `utterly devastated`, ["devotion", "dec"]), ` at being forced to take the life of ${his} half-${sister2}.`); - - winner.devotion -= 50; - } - break; - } - } - - V.pit.slaveFightingBodyguard = null; - - if (winner.skill.combat < 60) { - const experienceSpan = App.UI.DOM.makeElement("span", `improved ${his} combat skills.`, ["improvement"]); - - winner.skill.combat += 5 + Math.floor(0.5 * (winner.intelligence + winner.intelligenceImplant) / 32); - r.push(`With lethal experience in ${V.pit.name}, ${winner.slaveName} has `, experienceSpan); - } - - winner.counter.pitKills++; - winner.counter.pitWins++; - } - - V.pitKillsTotal++; - V.pitFightsTotal++; - - if (loser.hasOwnProperty('slaveName')) { - V.pit.fighterIDs.delete(loser.ID); - - if (V.pit.slavesFighting.length > 0) { - V.pit.slavesFighting = []; - } - - removeSlave(loser); - } - - App.Events.addParagraph(parent, r); - } - - // Helper Functions - - /** @returns {boolean} Returns true if fighters[0] won */ - function getWinner() { - if (animal) { - if (deadliness(getSlave(fighters[0])).value > animal.deadliness) { - return random(1, 100) > 20; // 80% chance of winning - } else if (deadliness(getSlave(fighters[0])).value < animal.deadliness) { - return random(1, 100) > 80; // 20% chance of winning - } else if (random(1, 100) > 50) { // 50/50 - return true; - } - - return false; - } - - if (deadliness(getSlave(fighters[0])).value > deadliness(getSlave(fighters[1])).value) { - return random(1, 100) > 20; // 80% chance of winning - } else if (deadliness(getSlave(fighters[0])).value < deadliness(getSlave(fighters[1])).value) { - return random(1, 100) > 80; // 20% chance of winning - } else if (random(1, 100) > 50) { // 50/50 - return true; - } - - return false; - } -}; diff --git a/src/events/scheduled/pitFightNonlethal.js b/src/events/scheduled/pitFightNonlethal.js deleted file mode 100644 index 0c7605ee61c1a46db44a9f09fbd4e06f1f4e3b28..0000000000000000000000000000000000000000 --- a/src/events/scheduled/pitFightNonlethal.js +++ /dev/null @@ -1,1271 +0,0 @@ -// TODO: add devotion and trust effects to animal variant -App.Facilities.Pit.nonlethalFight = function(fighters) { - const frag = new DocumentFragment(); - - const animals = []; - /** @type {FC.AnimalState} */ - let animal = null; - - if (V.active.canine) { - animals.push(V.active.canine); - } - if (V.active.hooved) { - animals.push(V.active.hooved); - } - if (V.active.feline) { - animals.push(V.active.feline); - } - - if ((V.pit.fighters === 2 || V.pit.slaveFightingAnimal) && animals.length > 0) { - animal = V.pit.animal === 'random' - ? getAnimal(animals.random()) - : getAnimal(V.pit.animal); - } - - /** @param {App.Entity.SlaveState} slave */ - const canRun = slave => slave.muscles > -30 && - slave.weight < 95 && - slave.health.condition > -50 && - slave.bellyPreg < 5000 && - slave.bellyImplant < 4000 && - slave.bellyFluid < 2000 && - !isInLabor(slave); - - let winner; - let loser; - - if (animal) { - winner = getWinner() ? getSlave(fighters[0]) : animal; - loser = winner.hasOwnProperty('species') ? getSlave(fighters[0]) : animal; - } else { - winner = getWinner() ? getSlave(fighters[0]) : getSlave(fighters[1]); - loser = winner.ID === fighters[0] ? getSlave(fighters[1]) : getSlave(fighters[0]); - } - - if (V.debugMode) { - console.log(winner, loser); - } - - intro(frag); - fighterDeadliness(frag, getSlave(fighters[0])); - if (!animal) { - fighterDeadliness(frag, getSlave(fighters[1])); - } - fight(frag); - postFight(frag); - - V.pit.slaveFightingBodyguard = null; - V.pit.slaveFightingAnimal = null; - V.pit.slavesFighting = []; - - return frag; - - /** - * TODO: update to use "run" if animal - * - * @param {DocumentFragment} parent - */ - function intro(parent) { - const fighterOne = getSlave(fighters[0]); - const fighterTwo = getSlave(fighters[1]) || null; - - if (V.debugMode) { - console.log(fighterOne, fighterTwo); - } - - const r = []; - - App.Events.drawEventArt(frag, fighters.map(id => getSlave(id)), "no clothing"); - - const anAnimal = animal ? `${animal.articleAn} ${animal.name}` : null; - - if (animal) { - r.push(`This week ${anAnimal} is going to try to use ${fighterOne.slaveName}.`); - } else { - r.push(`This week's fight is between`, App.UI.DOM.slaveDescriptionDialog(fighterOne), `and`, - App.UI.DOM.combineNodes(contextualIntro(fighterOne, fighterTwo, true), ".")); - } - - if (animal) { - const slave = getSlave(fighters[0]); - - if (V.pit.audience === "none") { - r.push(`You are alone above the pit, left to watch the animal try use your slave in private.`); - } else if (V.pit.audience === "free") { - r.push(`Your guests line the rim of the pit, joking and laughing.`); - } else { - r.push(`The attendees line the rim of the pit, making bets on how long ${slave.slaveName} will last.`); - } - } else { - if (V.pit.audience === "none") { - r.push(`You are alone above the pit, left to watch them square off in private.`); - } else if (V.pit.audience === "free") { - r.push(`Your guests line the rim of the pit, joking and betting.`); - } else { - r.push(`The attendees line the rim of the pit, betting and arguing.`); - } - } - - if (V.arcologies[0].FSRomanRevivalist !== "unset") { - r.push(`They `, App.UI.DOM.makeElement("span", 'strongly approve', ["reputation", "inc"]), ` of your hosting combat between slaves; this advances ideas from antiquity about what public events should be.`); - - repX(10 * V.FSSingleSlaveRep * (V.arcologies[0].FSRomanRevivalist / V.FSLockinLevel), "pit"); - V.arcologies[0].FSRomanRevivalist += (0.2 * V.FSSingleSlaveRep); - } - - if (animal) { - const slave = getSlave(fighters[0]); - const {he, his} = getPronouns(slave); - - const approves = slave.devotion > 50 || (slave.devotion > 20 && (slave.fetish === "masochist" || slave.fetish === "humiliation" || slave.sexualQuirk === "perverted" || slave.behavioralQuirk === "sinful")); - - r.push(`${slave.slaveName} and the ${animal.name} enter ${V.pit.name} from opposite sides. ${slave.slaveName} is naked except for a ring gag, ${his} wrists have been bound, and ${he} has already been given ${his} instructions: ${he} is to try to avoid being caught and ${approves ? `fucked` : `raped`} by the animal for five minutes, and if ${he} succeeds, ${he} wins two complete days of rest. The ${animal.name} has been given a large dose of aphrodisiacs, and its lust is apparent – its ${animal.dick.desc} cock is clearly visible, even from where you're seated.`); - } else { - r.push(`You review the rules — the combatants are wearing light gloves, and the fight will be nonlethal, with you as the judge. The winner will have the right to do anything they wish to the loser,`); - - switch (V.pit.virginities) { - case "all": - r.push(`except take their virginity,`); - break; - case "anal": - r.push(`except take their anal virginity,`); - break; - case "vaginal": - r.push(`except take virginities,`); - break; - case "neither": - r.push(`even take virginities,`); - break; - default: - throw new Error(`Unexpected V.pit.virginities value of '${V.pit.virginities}' found`); - } - - r.push(`and earn two complete days of rest. You take a moment to look over your fighters before giving the word.`); - } - - App.Events.addParagraph(parent, r); - } - - /** - * @param {DocumentFragment} parent - * @param {App.Entity.SlaveState} fighter - */ - function fighterDeadliness(parent, fighter) { - const {he, his, him, himself, He, His} = getPronouns(fighter); - const fighterDeadliness = deadliness(fighter).value; - - const r = []; - - r.push( - confidence(), - skill(), - age(), - muscles(), - height(), - health(), - weight(), - tired(), - pregnancy(), - labor(), - bellyFluid(), - sight(), - hearing(), - prosthetics(), - willingness(), - ); - - App.Events.addParagraph(parent, r); - - function confidence() { - if (fighter.fetish === Fetish.MINDBROKEN) { - return `${fighter.slaveName} is too broken to care what happens to ${him}.`; - } else if (fighterDeadliness > 5) { - return `${fighter.slaveName} seems very confident, even eager to win a break.`; - } else if (fighterDeadliness > 3) { - return `${fighter.slaveName} seems nervous, but steels ${himself} to fight for time off.`; - } else if (fighterDeadliness > 1) { - return `${fighter.slaveName} seems hesitant and unsure.`; - } else { - return `${fighter.slaveName} is obviously terrified, and might flee if there were a way out of the pit.`; - } - } - - function willingness() { - if (fighter.devotion < 20 && fighter.trust < -20) { - return `${He} is unwilling to fight, but ${he} knows the punishment for refusing to do so will be even worse.`; - } - } - - function skill() { - if (fighter.skill.combat > 30) { - return `${His} stance is obviously well-practiced.`; - } - } - - function age() { - if (V.AgePenalty !== 0) { - if (fighter.physicalAge >= 100) { - return `${He} seems preoccupied, which is unsurprising given ${his} age and resulting fragility.`; - } else if (fighter.physicalAge >= 85) { - return `${He} tries not to waste ${his} strength before the fight, knowing that ${his} extreme age won't allow ${him} a second wind.`; - } else if (fighter.physicalAge >= 70) { - return `${He} steadies ${himself} as well as ${he} can in ${his} advanced age.`; - } - } - } - - function muscles() { - if (fighter.muscles > 95 && fighter.height > 185) { - return `${His} huge muscles are an intimidating sight and, despite their massive size, ${he} is tall enough to not be hindered by them.`; - } else if (fighter.muscles > 95) { - return `${His} huge muscles are an intimidating sight, but may hinder ${his} flexibility.`; - } else if (fighter.muscles > 30) { - return `${His} muscles are a trim and powerful sight.`; - } else if (fighter.muscles < -95) { - return `${He} can barely stand, let alone defend ${himself}.`; - } else if (fighter.muscles < -30) { - return `${He} is very weak; a single punch will likely floor ${him}.`; - } else if (fighter.muscles < -5) { - return `${He} is rather unfit; ${he} will likely be outmatched by near any real opponent.`; - } else { - return `${He} is only somewhat fit and will likely not be able to win through pure strength.`; - } - } - - function height() { - if (fighter.height > 170) { - return `${His} height gives ${him} a reach advantage with ${his} fists and feet.`; - } - } - - function health() { - if (fighter.health.condition > 50) { - return `${His} shining health makes ${him} a better fighter.`; - } else if (fighter.health.condition) { - return `${His} poor health makes ${him} a weaker combatant.`; - } - } - - function weight() { - if (fighter.weight > 190) { - return `${His} extreme weight nearly immobilizes ${him}. ${He} is essentially a fleshy punching bag.`; - } else if (fighter.weight > 160) { - return `${His} extreme weight limits ${his} mobility and range of motion even if ${he} can take punches like nothing.`; - } else if (fighter.weight > 130) { - return `${His} extreme weight holds ${him} back as a pit fighter.`; - } else if (fighter.weight > 30) { - return `${His} heavy weight is an impediment as a pit fighter.`; - } else if (fighter.weight < -10) { - return `${His} light weight is an impediment as a pit fighter.`; - } - } - - function tired() { - if (fighter.health.tired > 90) { - return `${He} is exhausted and can barely stay awake; ${he} won't put up a fight.`; - } else if (fighter.health.tired > 60) { - return `${He} is fatigued, sapping the strength ${he}'ll need in ${his} strikes.`; - } else if (fighter.health.tired > 30) { - return `${He} is tired and more likely to take a hit than to give one.`; - } - } - - function pregnancy() { - if (fighter.pregKnown || fighter.bellyPreg > 1500) { - if (fighter.bellyPreg > 750000) { - return `${His} monolithic pregnancy guarantees ${his} loss; not only is ${he} on the verge of splitting open, but it is an unmissable, indefensible target that threatens to drag ${him} to the ground. ${He} has no hope of attacking around the straining mass, let alone stopping ${his} opponent. The fear of what would happen should ${his} adversary land a hit on ${his} belly also weighs upon ${his} mind.`; - } else if (fighter.bellyPreg > 600000) { - return `${His} titanic pregnancy is practically a guaranteed loss; ${he} can barely stand let alone fight. The worry of a solid hit striking ${his} life-swollen womb also weighs on ${his} mind.`; - } else if (fighter.bellyPreg > 450000) { - return `${His} gigantic pregnancy is nearly a guaranteed loss; it presents an unmissable, indefensible target for ${his} adversary.`; - } else if (fighter.bellyPreg > 300000) { - return `${His} massive pregnancy obstructs ${his} movement and greatly hinders ${him}. ${He} struggles to think of how ${he} could even begin to defend ${his} bulk.`; - } else if (fighter.bellyPreg > 150000) { - return `${His} giant pregnancy obstructs ${his} movement and greatly slows ${him} down.`; - } else if (fighter.bellyPreg > 100000) { - return `${His} giant belly gets in ${his} way and weighs ${him} down.`; - } else if (fighter.bellyPreg > 10000) { - return `${His} huge belly is unwieldy and hinders ${his} efforts.`; - } else if (fighter.bellyPreg > 5000) { - return `${His} advanced pregnancy makes ${him} much less effective.`; - } else if (fighter.bellyPreg > 1500) { - return `${His} growing pregnancy distracts ${him} from the fight.`; - } else { - return `The life just beginning to grow inside ${him} distracts ${him} from the fight.`; - } - } else if (fighter.bellyImplant > 1500) { - if (fighter.bellyImplant > 750000) { - return `${His} monolithic, ${fighter.bellyImplant}cc implant-filled belly guarantees ${his} defeat; not only is ${he} on the verge of splitting open, but it is an unmissable, indefensible target that threatens to drag ${him} to the ground. ${He} has no hope of attacking around the straining mass, let alone stopping ${his} opponent.`; - } else if (fighter.bellyImplant > 600000) { - return `${His} titanic, ${fighter.bellyImplant}cc implant-filled belly is practically a guaranteed defeat; ${he} can barely stand let alone fight. Not only is it cripplingly heavy, unwieldy and an easy target, but ${he} can feel it straining to hold the sheer amount of filler forced into it.`; - } else if (fighter.bellyImplant > 450000) { - return `${His} gigantic, ${fighter.bellyImplant}cc implant-filled belly is nearly a guaranteed defeat; it presents an unmissable, indefensible target for ${his} adversary.`; - } else if (fighter.bellyImplant > 300000) { - return `${His} massive, ${fighter.bellyImplant}cc implant-filled belly is extremely heavy, unwieldy and an easy target, practically damning ${him} in combat.`; - } else if (fighter.bellyImplant > 150000) { - return `${His} giant, ${fighter.bellyImplant}cc implant-filled belly obstructs ${his} movement and greatly slows ${him} down.`; - } else if (fighter.bellyImplant > 100000) { - return `${His} giant, ${fighter.bellyImplant}cc implant-filled belly is very heavy and unwieldy, throwing off ${his} weight and making ${him} far less effective.`; - } else if (fighter.bellyImplant > 10000) { - return `${His} huge, ${fighter.bellyImplant}cc implant-filled belly is very heavy and unwieldy, throwing off ${his} weight and making ${him} far less effective.`; - } else if (fighter.bellyImplant > 5000) { - return `${His} large, ${fighter.bellyImplant}cc implant-filled belly is heavy and unwieldy, rendering ${him} less effective.`; - } else if (fighter.bellyImplant > 1500) { - return `${His} swollen, ${fighter.bellyImplant}cc implant-filled belly is heavy and makes ${him} less effective.`; - } - } - } - - function labor() { - if (isInLabor(fighter)) { - return `${He}'s feeling labor pains. ${His} ${fighter.pregType > 1 ? `children are` : `child is`} ready to be born, oblivious to the fact that it will put ${fighter.pregType > 1 ? `their` : `its`} mother at the mercy of ${his} opponent.`; - } else if (fighter.preg > fighter.pregData.normalBirth && fighter.pregControl !== "labor suppressors") { - return `${He}'ll be going into labor any time now and ${he} knows it. ${He}'s terrified of the thought of ${his} water breaking during the fight.`; - } - } - - function bellyFluid() { - if (fighter.bellyFluid > 10000) { - return `${His} hugely bloated, ${fighter.inflationType}-filled belly is taut and painful, hindering ${his} ability to fight.`; - } else if (fighter.bellyFluid > 5000) { - return `${His} bloated, ${fighter.inflationType}-stuffed belly is constantly jiggling and moving, distracting ${him} and throwing off ${his} weight.`; - } else if (fighter.bellyFluid > 2000) { - return `${His} distended, ${fighter.inflationType}-belly is uncomfortable and heavy, distracting ${him}.`; - } - } - - function sight() { - if (!canSee(fighter)) { - return `${His} lack of eyesight means certain defeat.`; - } else if (!canSeePerfectly(fighter)) { - return `${His} poor eyesight makes ${him} a weaker fighter.`; - } - } - - function hearing() { - if (!canHear(fighter)) { - return `${His} lack of hearing is a major detriment.`; - } else if ((fighter.hears === -1 && fighter.earwear !== "hearing aids") || (fighter.hears === 0 && fighter.earwear === "muffling ear plugs")) { // TODO: replace with canHearPerfectly - return `${His} poor hearing is a minor detriment.`; - } - } - - function prosthetics() { - if (hasAnyProstheticLimbs(fighter) && !hasAnyQuadrupedLimbs(fighter)) { - const r = []; - - r.push(`The pit lights gleam on ${his} P-Limbs.`); - - if (getLimbCount(fighter, 6) > 0) { - r.push(`${His} advanced cybernetic limbs are faster than natural limbs, and their force is amplified, so that they can become potent weapons.`); - } else if (getLimbCount(fighter, 5) > 0) { - r.push(`Though their integral weapons are disabled, ${his} upgraded prosthetics are almost as fast as natural limbs, and they can hit much, much harder.`); - } - - return r.join(' '); - } - } - if (hasAnyProstheticLimbs(fighter) && hasAnyQuadrupedLimbs(fighter)) { - const r = []; - - r.push(`The pit lights gleam on ${his} prosthetic limbs. They have the advantage of being quadrupedal, keeping ${him} low to the ground and providing better mobility.`); - return r.join(' '); - } - } - - /** - * @param {DocumentFragment} parent - */ - function fight(parent) { - const r = []; - - if (animal) { - const slave = getSlave(fighters[0]); - const {he, him, his, He} = getPronouns(slave); - - const approves = slave.devotion > 50 || (slave.devotion > 20 && (slave.fetish === "masochist" || slave.fetish === "humiliation" || slave.sexualQuirk === "perverted" || slave.behavioralQuirk === "sinful")); - - // TODO: expand this with more fight variants - r.push(`The ${animal.name} isn't in a hurry and slowly saunters towards the ${approves ? `determined` : `terrified`} slave. After what appears to be a moment of deliberation, ${he} finally makes ${his} move. ${He} starts ${canRun ? `running` : `moving as quickly as ${he} can`} in the opposite direction. The ${animal.species} follows suit and breaks out into a full sprint towards ${him}.`); - - // TODO: expand this to rely more on slave's ability to run - if (winner.hasOwnProperty('slaveName')) { - r.push(`${slave.slaveName} is quick, though, and manages to avoid the animal for the full five minutes. ${He} drops to ${his} knees in relief and exhaustion once the bell sounds and the animal is leashed and pulled back into its pen.`); - - if (V.pit.audience === "paid") { - r.push(`The audience is fairly impressed with this impressive display of sportsmanship, and your `, App.UI.DOM.makeElement('span', `cut of the betting`, ["cash", "inc"]), ` is equally impressive.`); - - cashX(2500, "pit", slave); - } else if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement('span', `fairly impressed`, ["reputation", "inc"]), ` with this impressive display of sportsmanship.`); - - repX(250, "pit", slave); - } - } else { - const minutesLasted = random(2, 5); - const secondsLasted = random(15, 45); - - if (canRun) { - r.push(`${slave.slaveName} is quick, but not quick enough. ${He} manages to last almost ${num(minutesLasted)} full minutes before the ${animal.name} finally manages to catch ${him}.`); - } else { - r.push(`${slave.slaveName} isn't quick enough to avoid the beast, and only manages to last a pitiful ${num(secondsLasted)} seconds before the ${animal.name} catches ${him}.`); - } - - if (V.pit.audience === "paid") { - r.push(`Your audience is not very impressed with this mediocre display, and your `, App.UI.DOM.makeElement('span', `cut of the betting`, ["cash", "inc"]), ` is equally unimpressive.`); - - cashX(500, "pit", slave); - } else if (V.pit.audience === "free") { - r.push(`Your audience is `, App.UI.DOM.makeElement('span', `not very impressed`, ["reputation", "inc"]), ` with this mediocre display.`); - - repX(25, "pit", slave); - } - } - } else { - const winnerDeadliness = deadliness(winner).value; - const loserDeadliness = deadliness(loser).value; - - const {he, his, him, himself, girl, He} = getPronouns(winner); - const { - he: he2, - his: his2, - him: him2, - himself: himself2, - girl: girl2, - He: He2, - His: His2, - } = getPronouns(loser); - - if (!canSee(winner) && !canSee(loser)) { - r.push(`${winner.slaveName} and ${loser.slaveName} are both blind, making the fight a staredown. Neither slave wants to make the first move, especially with the drowning cacophony coming from the jeering crowd. Slowly, ${winner.slaveName} moves forward, waving feeling for ${his} opponent before ${he} ${himself} gets found. ${loser.slaveName}'s hand meets ${winner.slaveName}'s and the two move to grab each other in a headlock. The two slaves violently thrash against each other, suffering more and more strikes as the struggle goes on. Eventually, ${loser.slaveName} can take no more and releases ${his2} grip on ${winner.slaveName} neck. It takes a moment for ${winner.slaveName} to stop wrestling the submitting ${loser.slaveName} and accept ${his} victory.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `surprised`, ["reputation", "inc"]), ` by the impromptu wrestling match.`); - - repX(50, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is surprised by the sudden wrestling match, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is modest.`); - - cashX(500, "pit", winner); - } - } else if (winnerDeadliness > (loserDeadliness + 1) && getArmCount(winner, 6) > 1) { - r.push(`Upon your word the two combatants approach each other. ${winner.slaveName} keeps ${his} advanced cybernetic limbs up in a protective position. ${loser.slaveName} probes ${him} with some light blows, puzzled by this ironclad defense. Gradually, ${he2} hits harder and harder, ${his2} opponent grunting under the impacts but holding steady. Finally, ${loser.slaveName} tires, gets off balance, and ${winner.slaveName} manages to grab ${his2} forearm. ${winner.slaveName}'s limbs emit an electric shock that temporarily incapacitates ${his} opponent. ${winner.slaveName} uses ${his} grip to pull ${his} stunned opponent in and grab ${his2} neck with the other hand, using it to exert just the right amount of pressure to choke ${him2} out harmlessly. Though the fight was short,`); - - if (V.pit.audience === "free") { - r.push(`the audience is `, App.UI.DOM.makeElement("span", `very impressed`, ["reputation", "inc"]), ` by the display.`); - - repX(100, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`the audience is quite impressed by the display, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is quite good.`); - - cashX(2000, "pit", winner); - } - } else if (winnerDeadliness > (loserDeadliness + 1) && getArmCount(winner, 5) > 1) { - r.push(`Upon your word the two combatants approach each other. ${winner.slaveName} keeps ${his} artificial limbs up in a protective position. ${loser.slaveName} probes ${him} with some light blows, puzzled by this ironclad defense. Gradually, ${he2} hits harder and harder, ${his2} opponent grunting under the impacts but holding steady. Finally, ${loser.slaveName} overcommits to a body blow, and ${winner.slaveName} grips ${his2} forearm. That is the end. The augmented grip is effectively unbreakable, and ${winner.slaveName} uses it to pull ${his} opponent in and grab ${his2} neck with the other hand, using it to exert just the right amount of pressure to choke ${him2} out harmlessly. Though the fight was short,`); - - if (V.pit.audience === "free") { - r.push(`the audience is `, App.UI.DOM.makeElement("span", `very impressed`, ["reputation", "inc"]), ` by the display.`); - - repX(100, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`the audience is quite impressed by the display, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is quite good.`); - - cashX(2000, "pit", winner); - } - } else if (!canSee(winner)) { - r.push(`${winner.slaveName} centers ${himself} and hones in on ${loser.slaveName}'s rapid breath. ${He} readies ${himself}, knowing ${he} has a single chance to win. With heavy steps, ${loser.slaveName} rushes ${him}, fully prepared to tackle the helpless combatant. ${winner.slaveName} gambles everything on a single high punch, a strike that slams ${loser.slaveName}'s undefended neck. ${loser.slaveName} falls to the ground before the panicking ${winner.slaveName}, who quickly pins the coughing loser.`); - - healthDamage(winner, 80); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `awestruck`, ["reputation", "inc"]), ` by the blind ${girl}'s triumph.`); - - repX(200, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is awestruck by the blind ${girl}'s triumph, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is enormous.`); - - cashX(4000, "pit", winner); - } - } else if (!canSee(loser)) { - r.push(`${winner.slaveName} grins at ${loser.slaveName}'s random swings and poor form. ${He} centers in on ${his} favorite part of ${loser.slaveName}'s body before rushing between ${his2} strikes and tackling ${him2} to the ground. ${loser.slaveName} lays there, helpless to stop ${winner.slaveName} from molesting ${him2}.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `annoyed`, ["reputation", "dec"]), ` by this lack of a fight.`); - - repX(-20, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience found the fight embarrassing, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is pitiful.`); - - cashX(40, "pit", winner); - } - } else if (winnerDeadliness > (loserDeadliness + 3)) { - if (winner.skill.combat > 30) { - r.push(`${winner.slaveName} wants to win, and ${he} opens the fight with a furious attack. ${loser.slaveName} manages to get ${his2} ${hasBothArms(loser) ? `forearms` : `forearm`} up, blocking a few blows, but by doing so leaves ${his2} abdomen open and obscures ${his2} vision enough that ${he2} is unprepared for the following kidney strike, which brings ${him2} gasping to one knee. ${winner.slaveName} lets ${him2} rise just a little before delivering a calculated hard right that sends ${loser.slaveName} to the mat.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), ` by this execution.`); - - repX(25, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this execution, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is unimpressive.`); - - cashX(500, "pit", winner); - } - } else { - r.push(`${winner.slaveName} wants to win and is confident ${he} will, but ${he} isn't particularly sure about how to do so. ${He} fights cautiously, mostly hitting ${loser.slaveName} from afar. Slowly, the rain of blows begins to tell, opening ${loser.slaveName}'s lip, giving ${him2} a bloody nose, and bruising ${his2} face. Realizing ${he2} has to do something, ${loser.slaveName} makes a desperate counterattack, `, App.UI.DOM.makeElement("span", `dripping blood`, ["health", "dec"]), ` as ${he2} goes. As ${he2} does ${he2} manages to get ${his2} face in the way of another of ${winner.slaveName}'s inexpert strikes and goes down hard. ${He2} makes to rise, but ${winner.slaveName} decides the fight by the simple expedient of kicking ${him2} in the crotch.`); - - healthDamage(winner, 20); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ["reputation", "inc"]), ` by the beating.`); - - repX(50, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the beating, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is reasonable.`); - - cashX(100, "pit", winner); - } - } - } else if (winner.belly > 60000 && loser.belly > 60000) { - r.push(`${winner.slaveName} and ${loser.slaveName} stare each other down and both come to a realization. Neither can reach the other around their massive bellies. Instead, they choose to ram their bulk into each other in hopes of toppling the weaker. After a drawn out struggle, both slaves' middles are `, App.UI.DOM.makeElement("span", `dark red and shuddering,`, ["health", "dec"]), ` ready to burst open. Rather than continue, ${loser.slaveName} lets the next strike down ${him2} hoping that the outcome of this fight isn't fatal.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `very impressed`, ["reputation", "dec"]), ` by the showdown.`); - - repX(75, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is very impressed by the showdown, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is good.`); - - cashX(1500, "pit", winner); - } - } else if (winner.belly > 60000 && loser.belly < 30000) { - r.push(`${loser.slaveName} spies an easy win against ${his2} massively bloated opponent and rushes in to topple ${winner.slaveName}. In an effort to defend ${himself}, ${winner.slaveName} hoists ${his} belly and turns suddenly, accidentally impacting ${loser.slaveName} with ${his} massive middle and knocking ${him2} to the ground. Seeing an opportunity, ${winner.slaveName} releases ${his} grip and slams ${his} weighty womb down on ${loser.slaveName}, bashing the wind out of ${him2}. ${loser.slaveName} struggles to slip out from under the mass, but the weight is too great and ${he2} passes out.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `impressed`, ["reputation", "dec"]), ` by this absurd win.`); - - repX(50, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is impressed by this absurd win, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is reasonably.`); - - cashX(1000, "pit", winner); - } - } else if (winner.skill.combat > 30 && loser.skill.combat > 30) { - const healthSpans = [App.UI.DOM.makeElement("span", `broken nose`, ["health", "dec"]), App.UI.DOM.makeElement("span", `to the point of damage`, ["health", "dec"])]; - - r.push(`Upon your word the two combatants approach each other warily, both knowing the other is reasonably competent. Before long they are trading expert blows. ${winner.slaveName} is getting the worst of it, so ${he} decides to change the nature of the fight. After three tries ${he} manages to bring ${loser.slaveName} to the ground, suffering a `, healthSpans[0], ` as ${he} does. ${loser.slaveName} tries to break the imperfect hold but only earns ${himself2} an elbow to the face. ${He2}'s furious and ${winner.slaveName} is obliged to wrench ${his2} arm `, healthSpans[1], ` before ${he2} allows ${himself2} to go limp.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `very impressed`, ["reputation", "inc"]), ` by the expert fight.`); - - repX(100, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is quite impressed by the expert fight, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is quite good.`); - - cashX(2000, "pit", winner); - } - } else if (winner.height - loser.height < -10) { - r.push(`${winner.slaveName} realizes that ${loser.slaveName}'s wingspan gives ${him2} a huge reach advantage. ${He} bores straight in, taking a hit or two but coming on regardless. ${loser.slaveName} understands ${his2} opponent's intention and backs off, but the pit is small and there isn't much room to retreat. When ${his2} back hits a padded wall, ${winner.slaveName} manages to land a light hit to ${his2} stomach that leaves ${loser.slaveName} winded enough that a hard kick to the side of ${his2} knee goes undefended. It causes `, App.UI.DOM.makeElement("span", `considerable damage,`, ["health", "dec"]), ` dropping ${him2} and ending the fight.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ["reputation", "inc"]), ` by the takedown.`); - - repX(50, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the takedown, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is reasonable.`); - - cashX(1000, "pit", winner); - } - } else if (loser.piercing.eyebrow.weight > 0) { - r.push(`The fight starts slowly, with the two trading jabs. Just as the spectators are getting bored, ${loser.slaveName} takes a glancing blow to the eyebrow. ${His2} piercing catches on ${winner.slaveName}'s glove and tears out. ${loser.slaveName} goes after ${his2} tormentor in fury, streaming blood, the piercing forgotten on the mat. Any tendency ${winner.slaveName} might have had to feel badly about this is extinguished by the assault, and soon ${winner.slaveName} is even willing to follow up on the success by targeting pierced body parts. The fight ends with poor ${loser.slaveName} writhing in pain on the mat, `, App.UI.DOM.makeElement("span", `leaking blood`, ["health", "dec"]), ` from several terribly shredded areas.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ["reputation", "inc"]), ` by the gory spectacle.`); - - repX(50, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the gory spectacle, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is reasonable.`); - - cashX(1000, "pit", winner); - } - } else if (winner.muscles > 30) { - r.push(`${winner.slaveName} is so massively muscular that ${he}'s actually impeded by ${his} size. ${loser.slaveName} is properly afraid of ${his} strength, though, so ${he2} tries to stay away as much as ${he2} can. The pit isn't large, however, and eventually ${winner.slaveName} manages to lay a hand on ${him2}. ${He} pulls ${him2} down, and then it's all over but the beating. ${loser.slaveName} rains blows on ${his2} huge oppressor, but all ${winner.slaveName} has to do is hold on with one arm and deliver damage with the other. By the time ${he2} gives up and goes limp, ${loser.slaveName} has collected `, App.UI.DOM.makeElement("span", `many minor injuries.`, ["health", "dec"])); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `reasonably impressed`, ["reputation", "inc"]), ` by the show of strength.`); - - repX(50, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is reasonably impressed by the show of strength, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is reasonable.`); - - cashX(1000, "pit", winner); - } - } else if (loser.belly > 300000) { - r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely brutal shortcut to victory. The instant the fight starts, ${he} quickly knees ${loser.slaveName} in the stomach. The massively swollen ${loser.slaveName} goes down with a loud thud and plenty of jiggling. ${winner.slaveName} gloats over the struggling ${loser.slaveName} watching as ${he2} is unable to pull ${his2} bloated form off the ground.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), ` by this easy win.`); - - repX(50, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy win, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is unimpressive.`); - - cashX(500, "pit", winner); - } - } else if (loser.boobs > 1200) { - r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely simple shortcut to victory. The instant the fight starts, ${he} hits ${loser.slaveName} right in ${his2} huge tits, as hard as ${he} can. This is a sucker punch of the worst kind; ${loser.slaveName}'s boobs are so big that ${he2} has no real chance of defending them. ${He2} gasps with pain${hasAnyArms(loser) ? ` and wraps ${his2} ${hasBothArms(loser) ? `arms` : `arm`} around ${his2} aching bosom` : ``}, giving ${winner.slaveName} a clear opening to deliver a free and easy blow to the jaw that sends the poor top-heavy slave to the mat. Any chance of ${loser.slaveName} rising is extinguished by ${his2} breasts; it takes ${him2} so long to muster an attempt to get up that ${winner.slaveName} can rain hits on ${him2} while ${he2} does.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), ` by this easy win.`); - - repX(25, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy win, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is unimpressive.`); - - cashX(500, "pit", winner); - } - } else if (loser.dick > 0) { - r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely unpleasant shortcut to victory. The instant the fight starts, ${he} furiously goes for ${loser.slaveName}'s eyes${hasBothArms(winner) ? `, hands forming claws` : hasAnyArms(winner) ? `, ${his} hand forming a claw` : ``}. ${loser.slaveName} ${hasAnyArms(loser) ? `defends ${himself2} with ${his2} ${hasBothArms(loser) ? `arms` : `arm`}` : `tries to defend ${himself} as best ${he} can`}, at which point ${winner.slaveName} delivers a mighty cunt punt. ${loser.slaveName} goes straight down, ${his2} mouth soundlessly opening and closing and tears leaking from ${his2} closed eyes${hasAnyArms(loser) - ? ` while ${his} ${hasBothArms(loser) - ? `hands desperately shield` - : `hand desperately shields`} ${his2} outraged pussy` - : ``}. ${winner.slaveName} follows ${him2} down and puts the unresisting ${girl2}'s head in a simple lock.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), ` by this easy win.`); - - repX(25, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy win, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is unimpressive.`); - - cashX(500, "pit", winner); - } - } else if (loser.vagina > 0) { - r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely unpleasant shortcut to victory. The instant the fight starts, ${he} furiously goes for ${loser.slaveName}'s eyes${hasBothArms(winner) ? `, hands forming claws` : hasAnyArms(winner) ? `, ${his} hand forming a claw` : ``}. ${loser.slaveName} ${hasAnyArms(loser) ? `defends ${himself2} with ${his2} ${hasBothArms(loser) ? `arms` : `arm`}` : `tries to defend ${himself} as best ${he} can`}, at which point ${winner.slaveName} delivers a mighty cunt punt. ${loser.slaveName} goes straight down, ${his2} mouth soundlessly opening and closing and tears leaking from ${his2} closed eyes${hasAnyArms(loser) ? ` while ${his} ${hasBothArms(loser) ? `hands` : `hand`} desperately shield${!hasBothArms(loser) ? `s` : ``} ${his2} outraged pussy` : ``}. ${winner.slaveName} follows ${him2} down and puts the unresisting ${girl2}'s head in a simple lock.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), ` by this easy win.`); - - repX(25, "pit", winner); - } else if (V.pit.audience === "paid") { - r.push(`The audience is not very impressed by this easy win, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), ` is unimpressive.`); - - cashX(500, "pit", winner); - } - } else { - r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely unpleasant shortcut to victory. The instant the fight starts, ${he} furiously goes for ${loser.slaveName}'s eyes, hands forming claws. ${loser.slaveName} defends ${himself2} with ${his2} arms, at which point ${winner.slaveName} delivers a clenched fist to ${loser.slaveName}'s throat. ${loser.slaveName} staggers back, wheezing for breath with tears leaking from ${his2} closed eyes. ${winner.slaveName} takes advantage of ${loser.slaveName}'s vulnerability to quickly trip and pin the coughing loser.`); - - if (V.pit.audience === "free") { - r.push(`The audience is `, App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), ` by this easy win.`); - - repX(25, "pit", winner); - } else if (V.pit.audience === "paid") { - const cashSpan = App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]); - - r.push(`The audience is not very impressed by this easy win, and your `, cashSpan, ` is unimpressive.`); - - cashX(500, "pit", winner); - } - } - } - - App.Events.addParagraph(parent, r); - } - - /** @param {DocumentFragment} parent */ - function postFight(parent) { - let r = []; - - const anus = "anus"; - const cunt = "cunt"; - const oral = "oral"; - const vaginal = "vaginal"; - const anal = "anal"; - - if (animal) { - if (winner.hasOwnProperty('species')) { - // TODO: add effects (gain masochist, etc) - const slave = getSlave(fighters[0]); - const {him, his} = getPronouns(slave); - - const approves = slave.devotion > 50 || (slave.devotion > 20 && (slave.fetish === "masochist" || slave.fetish === "humiliation" || slave.sexualQuirk === "perverted" || slave.behavioralQuirk === "sinful")); - const isVirgin = (canDoVaginal(slave) && slave.vagina === 0) || (canDoAnal(slave) && slave.anus === 0); - - const pussy = ['pussy', 'cunt', 'slit']; - const asshole = ['asshole', 'rear hole', 'anus']; - const mouth = ['mouth', 'throat']; - - const orifice = () => canDoVaginal(slave) ? either(pussy) : canDoAnal(slave) ? either(asshole) : either(mouth); - const consummation = App.UI.DOM.makeElement('span', `breaks in`, ["virginity", "loss"]); - - // TODO: redo this to better account for oral - r.push(`It ${animal.type === "hooved" ? `headbutts ${him}` : `swipes at ${his} ${hasAnyLegs(slave) ? hasBothLegs(slave) ? `legs` : `leg` : `lower body`}`}, causing ${him} to go down hard. The ${animal.name} doesn't waste a moment, and mounts ${him} quicker than you thought was possible${animal.type === "hooved" ? ` for ${animal.articleAn} ${animal.name}` : ``}. It takes a few tries, but it finally manages to sink its ${animal.dick.desc} cock into ${slave.slaveName}'s ${orifice()}, causing${V.pit.audience !== 'none' ? ` the crowd to go wild and` : ``} the slave to give a long, loud, drawn-out ${approves ? `moan` : `scream`} as its ${animal.dick.desc} dick `, isVirgin ? consummation : `fills`, `${his} ${orifice()}. Without pausing for even a second, it begins to steadily thrust, pounding ${him} harder and harder as it gets closer and closer to climax. After several minutes, you see the animal ${animal.type === "canine" ? `push its knot into ${slave.slaveName}'s ${orifice()} and` : ``} finally stop thrusting while the barely-there slave gives a loud ${approves ? `moan` : `groan`}. ${V.pit.audience !== "none" ? `The crowd gives a loud cheer as the` : `The`} animal pulls out, leaving the thoroughly fucked-out ${slave.slaveName} lying there, ${animal.species} cum streaming out of ${his} ${orifice()}.`); - - if (canDoVaginal(slave)) { - seX(slave, 'vaginal', 'animal'); - slave.vagina = slave.vagina < animal.dick.size ? animal.dick.size : slave.vagina; - } else if (canDoAnal(slave)) { - seX(slave, 'anal', 'animal'); - // @ts-ignore - slave.anus = Math.max(slave.anus, stretchedAnusSize(animal.dick.size)); - } else { - seX(slave, 'oral', 'animal'); - } - } - } else { - const {he, his, him, He} = getPronouns(winner); - const {his: his2, him: him2} = getPronouns(loser); - - const facefuck = `${He} considers ${his} options briefly, then hauls the loser to ${his2} knees for a facefuck.`; - const winnerPenetrates = orifice => `${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`}, and penetrates the loser's ${orifice}.`; - const loserProtest = `${canTalk(loser) - ? `${loser.slaveName} starts to scream a protest to stop ${winner.slaveName} raping ${him2} pregnant, but ${winner.slaveName} grinds ${his2} face into the mat to shut ${him2} up` - : `${loser.slaveName} tries to gesture a protest before ${winner.slaveName} fills ${his2} fertile ${loser.mpreg && V.pit.virginities !== anal ? `asspussy` : `pussy`} with cum, but ${winner.slaveName} grabs ${his2} ${hasBothArms(loser) ? `hands and pins them` : hasAnyArms(loser) ? `hand and pins it` : `neck firmly`} to keep ${him2} from complaining`}.`; - - const virginitySpan = App.UI.DOM.makeElement("span", ``, ["virginity", "loss"]); - - r.push(`You throw the victor's strap-on down to ${winner.slaveName}.`); - - if (canPenetrate(winner)) { - r.push(`${He} has no need of it, only taking a moment to pump ${his} dick a few times to get it to rock hardness.`); - } else if (winner.clit > 4) { - r.push(`${He} has no need of it, since ${his} clit is big enough to use instead.`); - } else if (winner.dick > 6 && !canAchieveErection(winner)) { - r.push(`${He} needs it, since ${his} enormous dick can't get hard any longer (not like it would fit in ${loser.slaveName} anyway).`); - } else if (winner.dick > 0) { - r.push(`${He} needs it, since ${his} soft dick won't be raping anything.`); - } - - if (V.pit.virginities === "all") { - if (loser.vagina === 0 && canDoVaginal(loser) && loser.anus === 0 && canDoAnal(loser)) { - r.push(`${He} respects ${loser.slaveName}'s virgin holes, and hauls the loser to ${his2} knees for a facefuck.`); - - actX(loser, oral); - } else if (loser.vagina === 0 && canDoVaginal(loser) && canDoAnal(loser)) { - r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); - - if (winner.fetish === "pregnancy") { - r.push(`and, after ${canSee(winner) ? `eyeing` : `feeling up`} ${his2} virgin vagina with desire, penetrates the loser's anus.`); - } else { - r.push(`and respects the rules by penetrating the loser's anus.`); - } - - if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { - r.push(loserProtest); - - knockMeUp(loser, 50, 1, winner.ID); - } - - actX(loser, anal); - } else if (loser.anus === 0 && canDoVaginal(loser) && canDoAnal(loser)) { - r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); - - if (winner.fetish === "buttslut" || (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg && winner.fetish === "pregnancy")) { - r.push(`and, after ${canSee(winner) ? `eyeing` : `feeling up`} ${his2} virgin anus with desire, penetrates the loser's cunt.`); - } else { - r.push(`and respects the rules by penetrating the loser's cunt.`); - } - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - actX(loser, vaginal); - } else if (canDoVaginal(loser)) { - r.push(winnerPenetrates(cunt)); - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - actX(loser, vaginal); - } else if (canDoAnal(loser)) { - r.push(winnerPenetrates(anus)); - - if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { - r.push(loserProtest); - - knockMeUp(loser, 50, 1, winner.ID); - } - - actX(loser, anal); - } else { - r.push(facefuck); - - actX(loser, oral); - } - } else if (V.pit.virginities === "anal") { - if (loser.vagina === 0 && canDoVaginal(loser)) { - r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); - - if (winner.fetish === "pregnancy") { - virginitySpan.append(`take ${his2} virginity.`); - r.push(`and gleefully takes advantage of your rules to `, virginitySpan); - } else { - virginitySpan.append(`breaks in ${his2} virgin vagina.`); - r.push(`and `, virginitySpan); - } - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - loser.vagina++; - actX(loser, vaginal); - } else if (loser.anus === 0 && canDoAnal(loser)) { - r.push(`${canTalk(loser) - ? `${loser.slaveName} starts to scream a protest to stop ${winner.slaveName} raping ${him2} pregnant, but ${winner.slaveName} grinds ${his2} face into the mat to shut ${him2} up` - : `${loser.slaveName} tries to gesture a protest before ${winner.slaveName} fills ${his2} fertile pussy with cum, but ${winner.slaveName} grabs ${his2} ${hasBothArms(loser) ? `hands and pins them` : hasAnyArms(loser) ? `hand and pins it` : `neck firmly`} to keep ${him2} from complaining`}.`); - - if (winner.fetish === "buttslut" || (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg && winner.fetish === "pregnancy")) { - if (canDoVaginal(loser)) { - r.push(`and penetrates the loser's cunt.`); - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - loser.counter.vaginal++; - V.vaginalTotal++; - } else { - r.push(`and finds only a pristine butthole waiting for ${him}. Respecting ${his2} anal virginity, ${he} hauls the loser onto ${his2} knees for a facefuck.`); - - actX(loser, oral); - } - } - } else if (canDoVaginal(loser)) { - r.push(winnerPenetrates(cunt)); - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - actX(loser, vaginal); - } else if (canDoAnal(loser)) { - r.push(winnerPenetrates(anus)); - - if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { - r.push(loserProtest); - - knockMeUp(loser, 50, 1, winner.ID); - } - - actX(loser, anal); - } else { - r.push(facefuck); - - actX(loser, oral); - } - } else if (V.pit.virginities === "vaginal") { - if (loser.vagina === 0 && canDoVaginal(loser)) { - r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); - - if (winner.fetish === "pregnancy") { - if (canDoAnal(loser)) { - r.push(`and hungrily eyes ${his2} pristine vagina before penetrating the loser's ass.`); - - if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { - r.push(loserProtest); - - knockMeUp(loser, 50, 1, winner.ID); - } - - actX(loser, anal); - } else { - r.push(`and hungrily eyes ${his2} pristine vagina before hauling the loser onto ${his2} knees for a facefuck.`); - - actX(loser, oral); - } - } else { - if (canDoAnal(loser)) { - r.push(`and penetrates the loser's ass.`); - - if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { - r.push(loserProtest); - - knockMeUp(loser, 50, 1, winner.ID); - } - - actX(loser, anal); - } else { - r.push(`and finds only a pristine butthole waiting for ${him}. Respecting ${his2} anal virginity, ${he} hauls the loser onto ${his2} knees for a facefuck.`); - - actX(loser, oral); - } - } - } else if (loser.anus === 0 && canDoAnal(loser)) { - r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); - - if (winner.fetish === "buttslut" || (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg && winner.fetish === "pregnancy")) { - virginitySpan.append(`take ${his2} anal virginity.`); - r.push(`and gleefully takes advantage of your rules to `, virginitySpan); - } else { - virginitySpan.append(`breaks in ${his2} virgin anus.`); - r.push(`and `, virginitySpan); - } - - if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { - r.push(loserProtest); - - knockMeUp(loser, 50, 1, winner.ID); - } - - loser.anus++; - actX(loser, anal); - } else if (canDoVaginal(loser)) { - r.push(winnerPenetrates(cunt)); - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - actX(loser, vaginal); - } else if (canDoAnal(loser)) { - r.push(winnerPenetrates(anus)); - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - actX(loser, anal); - } else { - r.push(facefuck); - - actX(loser, oral); - } - } else { - if (loser.vagina === 0 && canDoVaginal(loser)) { - r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); - - if (winner.fetish === "pregnancy") { - virginitySpan.append(`take ${his2} virginity.`); - r.push(`and gleefully takes advantage of your rules to `, virginitySpan); - } else { - virginitySpan.append(`breaks in ${his2} virgin vagina.`); - r.push(`and `, virginitySpan); - } - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - loser.vagina++; - actX(loser, vaginal); - } else if (loser.anus === 0 && canDoAnal(loser)) { - r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); - - if (winner.fetish === "buttslut" || (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg && winner.fetish === "pregnancy")) { - virginitySpan.append(`take ${his2} anal virginity.`); - r.push(`and gleefully takes advantage of your rules to `, virginitySpan); - } else { - virginitySpan.append(`breaks in ${his2} virgin anus.`); - r.push(`and `, virginitySpan); - } - - if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { - r.push(loserProtest); - - knockMeUp(loser, 50, 1, winner.ID); - } - - loser.anus++; - actX(loser, anal); - } else if (canDoVaginal(loser)) { - r.push(winnerPenetrates(cunt)); - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 0, winner.ID); - } - - actX(loser, vaginal); - } else if (canDoAnal(loser)) { - r.push(winnerPenetrates(anus)); - - if (canPenetrate(winner) && canImpreg(loser, winner)) { - r.push(loserProtest); - - knockMeUp(loser, 50, 1, winner.ID); - } - - actX(loser, anal); - } else { - r.push(facefuck); - - actX(loser, oral); - } - } - - App.Events.addParagraph(parent, r); - - App.Events.addParagraph(parent, [...rape(), ...rapeEffects()]); - - r = []; - if (winner.skill.combat < 60) { - const experienceSpan = App.UI.DOM.makeElement("span", `improved ${his} combat skills.`, ["improvement"]); - - winner.skill.combat += 5 + Math.floor(0.5 * (winner.intelligence + winner.intelligenceImplant) / 32); - r.push(`With experience in ${V.pit.name}, ${winner.slaveName} has `, experienceSpan); - } - - loser.counter.pitLosses++; - actX(winner, "penetrative"); - - winner.counter.pitWins++; - } - - V.pitFightsTotal++; - - App.Events.addParagraph(parent, r); - - function rape() { - const repSpan = App.UI.DOM.makeElement("span", ``, ["reputation", "inc"]); - - const r = []; - - const {he, his, He} = getPronouns(winner); - const {he: he2, his: his2, him: him2, girl: girl2} = getPronouns(loser); - - if (winner.sexualFlaw === "malicious") { - r.push(`${winner.slaveName} lives to torment and violate slaves. ${He} uses ${loser.slaveName} for ${his} `, App.UI.DOM.makeElement("span", `pleasure`, ["devotion", "inc"]), ` slapping ${him2} viciously, pinching ${him2} until ${he} draws blood, and showing off for the spectators.`); - - if (V.pit.audience !== "none") { - repSpan.append(`greatly enjoys`); - r.push(`The audience `, repSpan, ` the depraved spectacle.`); - - repX(50, "pit", winner); - } - - winner.devotion++; - } else if (winner.sexualFlaw === "abusive") { - r.push(`${winner.slaveName} lives to abuse and violate slaves. ${He} uses ${loser.slaveName} for ${his} `, App.UI.DOM.makeElement("span", `pleasure`, ["devotion", "inc"]), ` raping ${him2} mercilessly, striking ${him2} until ${he2} turns a deep reddish-purple, and showing off for the spectators.`); - - if (V.pit.audience !== "none") { - repSpan.append(`greatly enjoys`); - r.push(`The audience `, repSpan, ` the depraved spectacle.`); - - repX(50, "pit", winner); - } - - winner.devotion++; - } else if (winner.energy > 95) { - r.push(`${winner.slaveName} doesn't have to feign willingness to rape a slave ${girl2}. ${He} uses ${loser.slaveName} for ${his} `, App.UI.DOM.makeElement("span", `pleasure,`, ["devotion", "inc"]), ` groping ${him2}, kissing ${his2} unwilling mouth, and showing off for the spectators.`); - - if (V.pit.audience !== "none") { - repSpan.append(`greatly enjoys`); - r.push(`The audience `, repSpan, ` the depraved spectacle.`); - - repX(50, "pit", winner); - } - - winner.devotion++; - } else if (winner.fetish === "sadist" && winner.fetishStrength > 60) { - r.push(`${winner.slaveName} doesn't have to feign willingness abuse and violate a slave ${girl2}. ${He} uses ${loser.slaveName} for ${his} `, App.UI.DOM.makeElement("span", `pleasure`, ["devotion", "inc"]), ` slapping ${him2} cruelly, pinching ${him2} until ${he} draws blood, and showing off for the spectators.`); - - if (V.pit.audience !== "none") { - repSpan.append(`greatly enjoys`); - r.push(`The audience `, repSpan, ` the depraved spectacle.`); - - repX(50, "pit", winner); - } - - winner.devotion++; - } else if (winner.devotion > 75) { - r.push(`${winner.slaveName} is so broken to slavery that ${he} `, App.UI.DOM.makeElement("span", `enjoys`, ["devotion", "inc"]), ` winning the fight and raping ${loser.slaveName}.`); - - if (V.pit.audience !== "none") { - repSpan.append(`greatly enjoys`); - r.push(`The audience `, repSpan, ` the depraved spectacle.`); - - repX(50, "pit", winner); - } - - winner.devotion++; - } else if (winner.devotion > 20) { - r.push(`${winner.slaveName} understands ${his} role and thoroughly rapes ${loser.slaveName}.`); - - if (V.pit.audience !== "none") { - repSpan.append(`enjoys`); - r.push(`The audience `, repSpan, ` the spectacle.`); - - repX(25, "pit", winner); - } - } else { - r.push(`${winner.slaveName} is unenthusiastic and just thrusts mechanically, to avoid being punished. ${He} `, App.UI.DOM.makeElement("span", `resents`, ["devotion", "dec"]), ` having to fight and fuck.`); - - if (V.pit.audience !== "none") { - r.push(`The audience barely pays attention.`); - } - - winner.devotion -= 2; - } - - return r; - } - - function rapeEffects() { - const r = []; - - const {he, his, him, himself, He} = getPronouns(winner); - const {he: he2, him: him2, He: He2} = getPronouns(loser); - - r.push(...winnerEffects(), ...loserEffects()); - - if (loser.fetish !== "masochist" && loser.fetish !== "humiliation" && loser.sexualFlaw !== "self hating" && loser.relationship && loser.relationship < 5 && winner.ID === loser.relationshipTarget) { - r.push(`Fighting and rape have `, App.UI.DOM.makeElement("span", `damaged`, ["relationship", "dec"]), ` the relationship between the slaves.`); - } - - return r; - - function winnerEffects() { - const r = []; - - if (winner.rivalry && loser.ID === winner.rivalryTarget) { - r.push(`${He} `, App.UI.DOM.makeElement("span", `relishes`, ["devotion", "inc"]), ` the chance to abuse ${loser.slaveName}, whom ${he} dislikes.`); - - winner.devotion += 5; - } else if (winner.relationship && loser.ID === winner.relationshipTarget) { - if (winner.devotion > 20) { - r.push(`${He} accepts having to abuse ${loser.slaveName}, and plans to make it up to ${him2} later.`); - } else { - r.push(`${He} `, App.UI.DOM.makeElement("span", `hates`, ["devotion", "dec"]), ` having to abuse ${loser.slaveName}.`); - - winner.devotion -= 10; - } - } else if (areRelated(winner, loser)) { - if (winner.devotion > 20) { - r.push(`${He} accepts having to abuse ${his} ${relativeTerm(winner, loser)}, ${loser.slaveName}, and plans to make it up to ${him2} later.`); - } else { - r.push(`${He} `, App.UI.DOM.makeElement("span", `hates`, ["devotion", "dec"]), ` having to abuse ${his} ${relativeTerm(winner, loser)}, ${loser.slaveName}.`); - - winner.devotion -= 10; - } - } - - if (winner.fetish === "sadist" && winner.fetishStrength > 90 && winner.sexualFlaw !== "malicious" && winner.devotion > 20) { - r.push(`${He} noticed something while ${he} was raping ${loser.slaveName}; watching the way ${he2} writhed in pain was strangely satisfying, as ${he} was making ${him2} suffer. ${winner.slaveName} cums powerfully at the mere thought; ${he} has become `, App.UI.DOM.makeElement("span", `sexually addicted to inflicting pain and anguish.`, ["noteworthy"])); - - winner.sexualFlaw = "malicious"; - } else if (winner.fetish === "masochist" && winner.fetishStrength > 90 && winner.sexualFlaw !== "self hating" && winner.devotion < 20) { - r.push(`${He} feels horrible after forcing ${himself} on ${loser.slaveName}; ${he} is the one that should suffer, not ${him2}. ${winner.slaveName} has `, App.UI.DOM.makeElement("span", `descended into true self hatred.`, ["noteworthy"])); - - winner.sexualFlaw = "self hating"; - } else if (winner.fetish === "dom" && winner.fetishStrength > 90 && winner.sexualFlaw !== "abusive" && winner.devotion > 20) { - r.push(`${He} noticed something while ${he} was raping ${loser.slaveName}; watching the way ${he2} cowered before ${hasAnyArms(winner) ? `${his} raised palm` : `${him}`} was strangely satisfying, as were the painful moans that accompanied every forceful thrust. ${winner.slaveName} cums powerfully at the mere thought; ${he} has become `, App.UI.DOM.makeElement("span", `sexually abusive, getting off on the thrill of forcing ${himself} on other slaves.`, ["noteworthy"])); - - winner.sexualFlaw = "abusive"; - } else if (winner.behavioralFlaw === "none" && random(1, 100) < 5 && winner.devotion < 20) { - r.push(`${He} begins to act `, App.UI.DOM.makeElement("span", `oddly`, ["flaw", "gain"]), ` after the fight; having to beat down and rape another slave may have affected ${him} mentally.`); - - winner.behavioralFlaw = "odd"; - } - - return r; - } - - function loserEffects() { - const trustSpan = App.UI.DOM.makeElement("span", `fears`, ["trust", "dec"]); - - const r = []; - - if (loser.sexualFlaw === "self hating") { - r.push(`${loser.slaveName} feels ${he2} `, App.UI.DOM.makeElement("span", `deserved this.`, ["devotion", "inc"])); - - loser.devotion += 5; - } else if (loser.fetish === "masochist" && loser.fetishStrength > 60 && loser.fetishKnown) { - r.push(`${loser.slaveName} `, App.UI.DOM.makeElement("span", `gets off`, ["devotion", "inc"]), ` on being beaten and molested.`); - - loser.devotion += 5; - } else if (loser.fetish === "humiliation" && loser.fetishStrength > 60 && loser.fetishKnown) { - r.push(`${loser.slaveName} `, App.UI.DOM.makeElement("span", `enjoys`, ["devotion", "inc"]), ` the humiliation of being openly beaten and raped.`); - - loser.devotion += 5; - } else if (loser.devotion < 50) { - r.push(`${loser.slaveName} `, App.UI.DOM.makeElement("span", `resents`, ["devotion", "dec"]), ` being beaten and molested and `, trustSpan, ` that it will happen again.`); - - loser.devotion -= 10; - loser.trust -= 10; - } - - if (loser.rivalry && winner.ID === loser.rivalryTarget) { - r.push(`${He2} is `, App.UI.DOM.makeElement("span", `embarrassed`, ["devotion", "dec"]), ` by losing to and being raped by ${winner.slaveName}, whom ${he2} dislikes, and `, trustSpan, ` that it will happen again.`); - - loser.devotion -= 10; - loser.trust -= 10; - } else if (loser.relationship && winner.ID === loser.relationshipTarget) { - if (loser.devotion > 20) { - r.push(`${He2} accepts ${winner.slaveName} having to rape ${him2}.`); - } else { - r.push(`${He2} `, App.UI.DOM.makeElement("span", `hates`, ["devotion", "dec"]), ` having to accept rape from ${winner.slaveName}, and `, trustSpan, ` that it will happen again.`); - - loser.devotion -= 10; - loser.trust -= 10; - } - } else if (areRelated(loser, winner)) { - if (loser.devotion > 20) { - r.push(`${He2} accepts ${his} ${relativeTerm(loser, winner)}, ${winner.slaveName}, having to rape ${him2}, but ${he2} `, trustSpan, ` that it will happen again.`); - - loser.trust -= 10; - } else { - r.push(`${He2} `, App.UI.DOM.makeElement("span", `hates`, ["devotion", "dec"]), ` having to accept rape from ${his} ${relativeTerm(loser, winner)}, ${winner.slaveName}, and `, trustSpan, ` that it will happen again.`); - - loser.devotion -= 10; - loser.trust -= 10; - } - } - - if (loser.fetish === "masochist" && loser.fetishStrength > 90 && loser.sexualFlaw !== "self hating") { - r.push(`${He2} feels strangely content after being abused and violated; ${he2} is the one that should suffer, after all. ${loser.slaveName} has `, App.UI.DOM.makeElement("span", `descended into true self hatred.`, ["flaw", "gain"])); - - loser.sexualFlaw = "self hating"; - } else if (loser.behavioralFlaw === "none" && random(1, 100) < 5 && loser.devotion < 20) { - r.push(`${He2} begins to act `, App.UI.DOM.makeElement("span", `oddly`, ["flaw", "gain"]), ` after the fight; losing and getting raped may have affected ${him2} mentally.`); - - loser.behavioralFlaw = "odd"; - } - - return r; - } - } - } - - // Helper Functions - - /** @returns {boolean} Returns true if fighters[0] won */ - function getWinner() { - if (animal) { - if (canRun(getSlave(fighters[0]))) { - return random(1, 100) > 20; // 80% chance of winning - } else { - return random(1, 100) > 80; // 20% chance of winning - } - } - - if (deadliness(getSlave(fighters[0])).value > deadliness(getSlave(fighters[1])).value) { - return random(1, 100) > 20; // 80% chance of winning - } else if (deadliness(getSlave(fighters[0])).value < deadliness(getSlave(fighters[1])).value) { - return random(1, 100) > 80; // 20% chance of winning - } else if (random(1, 100) > 50) { // 50/50 - return true; - } - - return false; - } -}; diff --git a/src/facilities/farmyard/animals/Animal.js b/src/facilities/farmyard/animals/Animal.js index dde095cc057da57576306689fbf220a389973a07..7f30b113735a6ede33dcb794fadf518b94a438b2 100644 --- a/src/facilities/farmyard/animals/Animal.js +++ b/src/facilities/farmyard/animals/Animal.js @@ -38,11 +38,11 @@ App.Entity.Animal = class Animal { /** @returns {this} */ purchase() { V.animals[this.type].push(this.name); - + /* if (V.pit && !V.pit.animal) { V.pit.animal = this.name; } - + */ return this; } @@ -51,11 +51,11 @@ App.Entity.Animal = class Animal { if (this.isActive) { V.active[this.type] = V.animals[this.type].random() || null; } - + /* if (V.pit && V.pit.animal === this.name) { V.pit.animal = null; } - + */ V.animals[this.type] = V.animals[this.type].filter(animal => animal !== this.name); return this; diff --git a/src/facilities/farmyard/farmyard.js b/src/facilities/farmyard/farmyard.js index d7b88f3578079493340bf3bd1e1004967d06b765..5f6f2c008d8f3ce68a4aeebd0455627959e176c6 100644 --- a/src/facilities/farmyard/farmyard.js +++ b/src/facilities/farmyard/farmyard.js @@ -21,11 +21,11 @@ App.Facilities.Farmyard.farmyard = class Farmyard extends App.Facilities.Facilit V.farmyardKennels = 0; V.farmyardStables = 0; V.farmyardCages = 0; - + /* if (V.pit) { V.pit.animal = null; } - + */ V.farmyardUpgrades = { pump: 0, fertilizer: 0, @@ -717,10 +717,11 @@ App.Facilities.Farmyard.farmyard = class Farmyard extends App.Facilities.Facilit hooved: null, feline: null, }; - + /* if (V.pit) { V.pit.animal = null; } + */ V.farmyardShows = 0; V.farmyardBreeding = 0; diff --git a/src/facilities/pit/fights/0_lethalRandom.js b/src/facilities/pit/fights/0_lethalRandom.js new file mode 100644 index 0000000000000000000000000000000000000000..f9c7f49fe3884dda10ce80f67cdfdc1d0638e640 --- /dev/null +++ b/src/facilities/pit/fights/0_lethalRandom.js @@ -0,0 +1,704 @@ +/** Lethal 1v1 between random slaves. */ +App.Facilities.Pit.Fights.LR1v1 = class extends App.Facilities.Pit.Fights.BaseFight { + fightDescription() { + const f = new DocumentFragment(); + f.append("1-vs-1 fight between ", App.UI.DOM.slaveDescriptionDialog(getSlave(this.actors[0])), ` and `, + contextualIntro(getSlave(this.actors[0]), getSlave(this.actors[1]), true), "."); + return f; + } + + get lethal() { + return true; + } + + get impact() { + return 5; + } + + actorPrerequisites() { + return [ + [], + [] + ]; + } + + execute(node, fighterMap) { + const that = this; // Thanks, JS! + const [slave1, slave2] = this.actors.map(a => getSlave(a)); + + const winner = getWinner() ? slave1 : slave2; + const loser = winner === slave1 ? slave2 : slave1; + + let success = 0; + + intro(node); + fighterDeadliness(node, slave1); + fighterDeadliness(node, slave2); + fight(node); + postFight(node); + + return success; + + /** @param {DocumentFragment} parent */ + function intro(parent) { + const r = []; + + App.Events.drawEventArt(node, [slave1, slave2], "no clothing"); + + r.push(...that.introCombatants(slave1, slave2)); + + if (V.pit.audience === "none") { + r.push(`You are alone above the pit, left to watch them kill and die in private.`); + } else if (V.pit.audience === "free") { + r.push(`Your guests line the rim of the pit, joking and betting, nervous and excited by the close prospect of watching a slave die.`); + } else { + r.push(`The attendees line the rim of the pit, betting and arguing, nervous and excited by the close prospect of watching a slave die.`); + } + + if (V.arcologies[0].FSRomanRevivalist !== "unset") { + r.push(`They `, App.UI.DOM.makeElement("span", `passionately approve`, ['reputation', + 'inc']), ` of your hosting lethal combat between slaves; this advances ideas from antiquity about what public events should be.`); + + repX(20 * V.FSSingleSlaveRep * (V.arcologies[0].FSRomanRevivalist / V.FSLockinLevel), "pit"); + V.arcologies[0].FSRomanRevivalist += (0.4 * V.FSSingleSlaveRep); + } else if (V.arcologies[0].FSAztecRevivalist !== "unset") { + r.push(`They `, App.UI.DOM.makeElement("span", `approve`, ['reputation', + 'inc']), ` of your hosting lethal combat; this advances ideas that blood must flow as it did in ancient times.`); + + repX(10 * V.FSSingleSlaveRep * (V.arcologies[0].FSAztecRevivalist / V.FSLockinLevel), "pit"); + V.arcologies[0].FSAztecRevivalist += (0.2 * V.FSSingleSlaveRep); + } + + r.push(`You review the rules — the combatants will use their choice of swords, and the fight will be to the death, with both fighters executed if no decision is reached. The winner will earn two complete days of rest, not to mention continued life. You take a moment to look over your fighters one more time before giving the word. After all, this is likely the last time you'll see one of them alive.`); + + + App.Events.addParagraph(parent, r); + } + + /** + * @param {DocumentFragment} parent + * @param {App.Entity.SlaveState} fighter + */ + function fighterDeadliness(parent, fighter) { + const {he, his, him, himself, He, His} = getPronouns(fighter); + const fighterDeadliness = deadliness(fighter).value; + + const r = []; + + r.push( + confidence(), + willingness(), + skill(), + age(), + muscles(), + height(), + health(), + weight(), + tired(), + pregnancy(), + labor(), + bellyFluid(), + sight(), + hearing(), + prosthetics(), + ); + + App.Events.addParagraph(parent, r); + + function confidence() { + if (fighter.fetish === Fetish.MINDBROKEN) { + return `${fighter.slaveName} is too broken to care about whether ${he} lives or dies;`; + } else if (fighterDeadliness > 5) { + return `${fighter.slaveName} seems very confident;`; + } else if (fighterDeadliness > 3) { + return `${fighter.slaveName} seems nervous, but steels ${himself};`; + } else if (fighterDeadliness > 1) { + return `${fighter.slaveName} seems hesitant and unsure;`; + } else { + return `${fighter.slaveName} is obviously terrified, and might flee if there were a way out of the pit;`; + } + } + + function willingness() { + if (fighter.fetish === Fetish.MINDBROKEN) { + return `${he} is indifferent to the prospect of killing, as well.`; + } else if (fighter.devotion > 95) { + return `${he} is clearly willing to do ${his} best to kill for you.`; + } else if (fighter.fetish === "sadist" && fighter.fetishKnown && fighter.fetishStrength > 60) { + return `the prospect of killing does not seem to concern ${him}.`; + } else if (fighter.devotion > 50) { + return `${he} obviously does not want to kill, but will do as you order.`; + } else if (fighter.devotion > -20) { + return `${he} is clearly unhappy at the prospect of killing, but knows that the alternative is death.`; + } else { + return `${he} knows that it's kill or be killed, and puts aside ${his} hatred of you in an effort to live.`; + } + } + + function skill() { + if (fighter.skill.combat > 30) { + return `${His} grip on ${his} sword is sure and easy.`; + } + } + + function age() { + if (V.AgePenalty !== 0) { + if (fighter.physicalAge >= 100) { + return `${He} seems prepared for death, in a way.`; + } else if (fighter.physicalAge >= 85) { + return `${He} tries not to waste ${his} strength before the fight, knowing that ${his} extreme age won't allow ${him} a second wind.`; + } else if (fighter.physicalAge >= 70) { + return `${He} steadies ${himself} as well as ${he} can in ${his} advanced age.`; + } + } + } + + function muscles() { + if (fighter.muscles > 95) { + return `${He} is wielding a massive two-handed blade few others could even heft.`; + } else if (fighter.muscles > 30) { + return `${He} is strong enough to handle a bastard sword.`; + } else if (fighter.muscles > 5) { + return `${He} has selected a longsword suited to ${his} strength.`; + } else if (fighter.muscles < -95) { + return `${He} has selected a meager dagger; even then ${he} can barely wield it.`; + } else if (fighter.muscles < -30) { + return `${He} has selected a dagger, the heaviest weapon ${he} can manage.`; + } else if (fighter.muscles < -5) { + return `${He} has selected a short sword, despite being able to barely lift it.`; + } else { + return `${He} has selected a short sword, the heaviest weapon ${he} can manage.`; + } + } + + function height() { + if (fighter.height > 170) { + return `${His} height gives ${him} a reach advantage.`; + } + } + + function health() { + if (fighter.health.condition > 50) { + return `${His} shining health makes ${him} a better fighter.`; + } else if (fighter.health.condition) { + return `${His} poor health makes ${him} a weaker combatant.`; + } + } + + function weight() { + if (fighter.weight > 190) { + return `${His} extreme weight nearly immobilizes ${him}. ${He} struggles to move let alone fight.`; + } else if (fighter.weight > 160) { + return `${His} extreme weight limits ${his} mobility and range of motion, making ${him} an easy target.`; + } else if (fighter.weight > 130) { + return `${His} extreme weight holds ${him} back as a pit fighter.`; + } else if (fighter.weight > 30) { + return `${His} heavy weight is an impediment as a pit fighter.`; + } else if (fighter.weight < -10) { + return `${His} light weight is an impediment as a pit fighter.`; + } + } + + function tired() { + if (fighter.health.tired > 90) { + return `${He} is exhausted and can barely stay awake; ${he} won't put up a fight.`; + } else if (fighter.health.tired > 60) { + return `${He} is fatigued, sapping the strength ${he}'ll need to strike true.`; + } else if (fighter.health.tired > 30) { + return `${He} is tired and more likely to take a hit than to give one.`; + } + } + + function pregnancy() { + if (fighter.pregKnown || fighter.bellyPreg > 1500) { + if (fighter.bellyPreg > 750000) { + return `${His} monolithic pregnancy guarantees ${his} and ${his} many, many children's deaths; not only is ${he} on the verge of splitting open, but it is an unmissable, indefensible target. ${He} has no hope of attacking around the straining mass, let alone stopping ${his} opponent. ${He} is damned.`; + } else if (fighter.bellyPreg > 600000) { + return `${His} titanic pregnancy is practically a death sentence; not only does ${he} risk bursting, but it is an unmissable, indefensible target. ${He} can barely keep it together while thinking about the lives of ${his} brood.`; + } else if (fighter.bellyPreg > 450000) { + return `${His} gigantic pregnancy practically damns ${him}; it presents an unmissable, indefensible target for ${his} adversary. ${He} can barely keep it together while thinking about the lives of ${his} brood.`; + } else if (fighter.bellyPreg > 300000) { + return `${His} massive pregnancy obstructs ${his} movement and greatly hinders ${him}. ${He} struggles to think of how ${he} could even begin to defend it from harm.`; + } else if (fighter.bellyPreg > 150000) { + return `${His} giant pregnancy obstructs ${his} movement and greatly slows ${him} down. ${He} tries not to think of how many lives are depending on ${him}.`; + } else if (fighter.bellyPreg > 100000) { + return `${His} giant belly gets in ${his} way and weighs ${him} down. ${He} is terrified for the lives of ${his} many children.`; + } else if (fighter.bellyPreg > 10000) { + return `${His} huge belly gets in ${his} way and weighs ${him} down. ${He} is terrified for the ${fighter.pregType > 1 ? `lives of ${his} children` : `life of ${his} child`}.`; + } else if (fighter.bellyPreg > 5000) { + return `${His} advanced pregnancy makes ${him} much less effective, not to mention terrified for ${his} child${fighter.pregType > 1 ? `ren` : ``}.`; + } else if (fighter.bellyPreg > 1500) { + return `${His} growing pregnancy distracts ${him} with concern over the life growing within ${him}.`; + } else { + return `The life just beginning to grow inside ${him} distracts ${him} from the fight.`; + } + } else if (fighter.bellyImplant > 1500) { + if (fighter.bellyImplant > 750000) { + return `${His} monolithic, ${fighter.bellyImplant}cc implant-filled belly guarantees ${his} death; not only is ${he} on the verge of splitting open, but it is an unmissable, indefensible target that threatens to drag ${him} to the ground. ${He} has no hope of attacking around the straining mass, let alone stopping ${his} opponent.`; + } else if (fighter.bellyImplant > 600000) { + return `${His} titanic, ${fighter.bellyImplant}cc implant-filled belly is practically a guaranteed death; ${he} can barely stand let alone fight. Not only is it cripplingly heavy, unwieldy and an easy target, but ${he} can feel it straining to hold the sheer amount of filler forced into it.`; + } else if (fighter.bellyImplant > 450000) { + return `${His} gigantic, ${fighter.bellyImplant}cc implant-filled belly is nearly a guaranteed death; it presents an unmissable, indefensible target for ${his} adversary.`; + } else if (fighter.bellyImplant > 300000) { + return `${His} massive, ${fighter.bellyImplant}cc implant-filled belly is extremely heavy, unwieldy and an easy target, practically damning ${him} in combat.`; + } else if (fighter.bellyImplant > 150000) { + return `${His} giant, ${fighter.bellyImplant}cc implant-filled belly obstructs ${his} movement and greatly slows ${him} down.`; + } else if (fighter.bellyImplant > 100000) { + return `${His} giant, ${fighter.bellyImplant}cc implant-filled belly is very heavy and unwieldy, throwing off ${his} weight and making ${him} far less effective.`; + } else if (fighter.bellyImplant > 10000) { + return `${His} huge, ${fighter.bellyImplant}cc implant-filled belly is very heavy and unwieldy, throwing off ${his} weight and making ${him} far less effective.`; + } else if (fighter.bellyImplant > 5000) { + return `${His} large, ${fighter.bellyImplant}cc implant-filled belly is heavy and unwieldy, rendering ${him} less effective.`; + } else if (fighter.bellyImplant > 1500) { + return `${His} swollen, ${fighter.bellyImplant}cc implant-filled belly is heavy and makes ${him} less effective.`; + } + } + } + + function labor() { + if (isInLabor(fighter)) { + return `${He}'s feeling labor pains. ${His} ${fighter.pregType > 1 ? `children are` : `child is`} ready to be born, oblivious to the fact that it will mean the death of ${fighter.pregType > 1 ? `their` : `its`} mother.`; + } else if (fighter.preg > fighter.pregData.normalBirth && fighter.pregControl !== "labor suppressors") { + return `${He}'ll be going into labor any time now and ${he} knows it. ${He}'s terrified of the thought of ${his} water breaking during the fight.`; + } + } + + function bellyFluid() { + if (fighter.bellyFluid > 10000) { + return `${His} hugely bloated, ${fighter.inflationType}-filled belly is taut and painful, hindering ${his} ability to fight.`; + } else if (fighter.bellyFluid > 5000) { + return `${His} bloated, ${fighter.inflationType}-stuffed belly is constantly jiggling and moving, distracting ${him} and throwing off ${his} weight.`; + } else if (fighter.bellyFluid > 2000) { + return `${His} distended, ${fighter.inflationType}-belly is uncomfortable and heavy, distracting ${him}.`; + } + } + + function sight() { + if (!canSee(fighter)) { + return `${His} lack of eyesight is certain death.`; + } else if (!canSeePerfectly(fighter)) { + return `${His} poor eyesight makes ${him} a weaker combatant.`; + } + } + + function hearing() { + if (!canHear(fighter)) { + return `${His} lack of hearing is a major detriment.`; + } else if ((fighter.hears === -1 && fighter.earwear !== "hearing aids") || (fighter.hears === 0 && fighter.earwear === "muffling ear plugs")) { // TODO: replace with canHearPerfectly + return `${His} poor hearing is a minor detriment.`; + } + } + + function prosthetics() { + if (hasAnyProstheticLimbs(fighter) && !hasAnyQuadrupedLimbs(fighter)) { + const r = []; + + r.push(`The pit lights gleam on ${his} P-Limbs.`); + + if (getLimbCount(fighter, 6) > 0) { + r.push(`The limits on ${his} cybernetic limbs have been lifted, turning them deadly.`); + } else if (getLimbCount(fighter, 5) > 0) { + r.push(`Their integral weapons are fully uncaged.`); + } + + return r.join(' '); + } + if (hasAnyProstheticLimbs(fighter) && hasAnyQuadrupedLimbs(fighter)) { + const r = []; + + r.push(`The pit lights gleam on ${his} quadrupedal limbs. The retractable claws of ${his} paws have been fully extended, ready to rip and tear.`); + return r.join(' '); + } + } + } + + /** @param {DocumentFragment} parent */ + function fight(parent) { + const r = []; + + r.push(`In any case, both of the slaves been dosed with aggression enhancers and inhibition blockers; they'll fight to kill.`); + + const winnerDeadliness = deadliness(winner).value; + const loserDeadliness = deadliness(loser).value; + + const {he, his, him, himself, girl, He} = getPronouns(winner); + const {He: He2, he: he2, his: his2, him: him2, girl: girl2} = getPronouns(loser); + + if (!canSee(winner) && !canSee(loser)) { + r.push(`${winner.slaveName} and ${loser.slaveName} are both blind, making the fight a staredown. Neither slave wants to make the first move, especially with the drowning cacophony coming from the jeering crowd. Slowly, ${winner.slaveName} moves forward, waving ${his} sword side to side in the hopes of finding ${his} opponent before ${he} ${himself} gets found. ${loser.slaveName}'s sword clips the tip of ${winner.slaveName}'s setting off a surprisingly brutal series of slashes. The two slaves violently lay into each other, suffering more and more strikes as the struggle goes on. Less than a minute from first contact, both slaves are lying in their combined blood. As you begin to call it a draw, you notice ${winner.slaveName} start to stir. ${He} staggers to ${his} feet, coated in ${his} and ${loser.slaveName}'s blood, before dropping back to ${his} knees. It seems ${he} lived through this fight; odds aren't likely ${he}'ll do it again.`); + + healthDamage(winner, 50); + success = 0.25; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `surprised`, ['reputation', 'inc']), + ` by the sudden violence after so much standing around.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is surprised by the sudden violence after so much standing around, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is modest.`); + } + } else if (winnerDeadliness > (loserDeadliness + 1) && getArmCount(winner, 6) > 1) { + r.push(`Upon your word the two combatants approach each other. ${winner.slaveName} does not carry a sword, raising only ${his} hands, but these are terrifying, modern weapons themselves. ${loser.slaveName} tries a probing sweep, but ${winner.slaveName} catches ${his2} blade in one hand, slides it down to the hilt with a shriek of metal on metal, and snaps the blade cleanly. ${loser.slaveName} ${canSee(loser) ? `watches this` : `freezes in place`}, dumbstruck, before regaining composure and taking a fighting stance again. ${winner.slaveName} manages to touch ${his} opponent's bare skin with ${his} own cybernetics, incapacitating ${loser.slaveName} with an electric shock. While ${loser.slaveName} is temporarily stunned, ${winner.slaveName} strikes ${loser.slaveName} on the head with deadly force, crushing ${his} opponent's skull. Though the fight was short,`); + + success = 0.5; + + if (V.pit.audience === "free") { + r.push(`the audience is `, + App.UI.DOM.makeElement("span", `very impressed`, ['reputation', 'inc']), + ` by the display.`); + } else if (V.pit.audience === "paid") { + r.push(`the audience is quite impressed by the display, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is quite good.`); + } + } else if (winnerDeadliness > (loserDeadliness + 1) && getArmCount(winner, 5) > 1) { + r.push(`Upon your word the two combatants approach each other. ${winner.slaveName} drops ${his} sword as ${he} comes, raising only ${his} hands, but these are terrifying, modern weapons themselves. ${loser.slaveName} tries a probing sweep, but ${winner.slaveName} catches ${his2} blade in one hand, slides it down to the hilt with a shriek of metal on metal, and snaps the blade cleanly. As ${loser.slaveName} watches this, dumbstruck, ${winner.slaveName} places a gentle hand under ${his2} chin and then extends that arm's integral blade up and through ${his} opponent's skull. Though the fight was short,`); + + success = 0.5; + + if (V.pit.audience === "free") { + r.push(`the audience is `, + App.UI.DOM.makeElement("span", `very impressed`, ['reputation', 'inc']), + ` by the display.`); + } else if (V.pit.audience === "paid") { + r.push(`the audience is quite impressed by the display, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is quite good.`); + } + } else if (!canSee(winner)) { + r.push(`${winner.slaveName} centers ${himself} and hones in on ${loser.slaveName}'s rapid breath. ${He} readies ${himself}, knowing ${he} has a single chance to live. With heavy steps, ${loser.slaveName} rushes ${him}, fully prepared to slaughter the helpless combatant. ${winner.slaveName} gambles everything on a single high slash, a slash that cut ${loser.slaveName}'s undefended neck wide open. ${winner.slaveName} collapses to the ground, fully aware of ${loser.slaveName}'s final gurgles, and the sword `, + App.UI.DOM.makeElement("span", `firmly planted in ${his} side.`, ['health', 'dec']), + ` ${He} lived this time, barely, but ${he} doubts ${he} can do it again.`); + + healthDamage(winner, 80); + success = 2; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `awestruck`, ['reputation', 'inc']), + ` by the blind ${girl}'s triumph.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is awestruck by the blind ${girl}'s triumph, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is enormous.`); + } + } else if (!canSee(loser)) { + r.push(`${winner.slaveName} sighs at ${loser.slaveName}'s random slashing and calmly struts around the panicking slave. In one quick swoop, ${he} buries ${his} blade in ${loser.slaveName}'s back, ending the poor ${girl2}'s flailing.`); + + success = -0.1; + + if (V.pit.audience === "free") { + r.push(`The audience is `, App.UI.DOM.makeElement("span", `annoyed`, ["reputation", + "dec"]), ` by this waste of a fight.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience found the fight embarrassing, and your `, App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', + 'dec']), ` is pitiful.`); + } + } else if (winnerDeadliness > (loserDeadliness + 3)) { + if (winner.skill.combat > 30) { + r.push(`${winner.slaveName} wants to win, and ${he} opens the fight with a furious attack. ${loser.slaveName} manages to get ${his2} blade up, blocking a strike with a ringing clash and a few sparks, but by doing so leaves ${his2} abdomen open and obscures ${his2} vision enough that ${he2} is unprepared for the following horizontal slash, which opens ${his2} belly wide. The stricken ${girl2} collapses, feebly trying to push ${his2} viscera back into place. Whether out of mercy or a desire to get it over with, ${winner.slaveName} quickly removes the dying ${girl2}'s head.`); + + success = 0.2; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), + ` by the gore.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is reasonably impressed by the gore, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is reasonable.`); + } + } else { + r.push(`${winner.slaveName} wants to win and is confident ${he} will, but ${he} isn't particularly sure about how to do so. ${He} fights cautiously, swinging ${his} sword in powerful but inaccurate strokes. It is only a matter of time before one of these strikes gets through; it's telling that rather than hitting what ${he} aimed at, ${winner.slaveName} accidentally opens a massive gash down ${loser.slaveName}'s thigh. Realizing ${he2} has to do something, ${loser.slaveName} makes a desperate counterattack, pouring blood as ${he2} goes. ${winner.slaveName} panics and fails to parry one of the last counterstrikes before loss of blood ends the attack, suffering a `, App.UI.DOM.makeElement("span", `terrible cut`, ['health', + 'dec']), ` to ${his} shoulder. Down to one arm, ${winner.slaveName} is forced to make a long, loud butchery of ending the fight.`); + + healthDamage(winner, 20); + success = 0.2; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), + ` by the blood.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is reasonably impressed by the blood, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is reasonable.`); + } + } + } else if (winner.skill.combat > 30 && loser.skill.combat > 30) { + r.push(`Upon your word the two combatants approach each other warily, both knowing the other is reasonably competent. Before long they are trading thrust and parry, swing and block. ${winner.slaveName} is slowly pressed back, so ${he} decides to change the nature of the fight. After three tries ${he} manages to force ${loser.slaveName} to close, suffering a `, App.UI.DOM.makeElement("span", `nearly severed ear`, ['health', + 'dec']), ` as ${he} does. ${loser.slaveName} realizes ${he2} only retains an advantage at long range but cannot back up fast enough to avoid close combat. ${loser.slaveName} is forced back fast enough that ${he2} trips; ${he2}'s barely fallen on ${his2} back before ${he2} grunts with shock and pain, dying with a look of surprise as ${he2} stares at the sword growing out of ${his2} chest.`); + + success = 0.5; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `very impressed`, ['reputation', 'inc']), + ` by the expert fight.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is quite impressed by the expert fight, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is quite good.`); + } + } else if (winner.height - loser.height < -10) { + r.push(`${winner.slaveName} realizes that ${loser.slaveName}'s wingspan gives ${him2} a huge reach advantage. ${He} bores straight in, taking `, App.UI.DOM.makeElement("span", `a glancing scalp wound`, ['health', + 'dec']), ` but coming on regardless. ${loser.slaveName} understands ${his2} opponent's intention and backs off, but the pit is small and there isn't much room to retreat. When ${his2} back hits a padded wall, ${winner.slaveName} aims a gutting cut that ${loser.slaveName} struggles to block. ${He2} manages it, but the wall catches ${his2} point, so the block is with ${his2} wrist, not ${his2} sword. The sharp blade cuts almost all the way through the joint, leaving ${him2} in agony and totally incapable of defense. ${winner.slaveName} pushes ${his2} head back against the wall and cuts ${his2} throat down to the spine.`); + + success = 0.2; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), + ` by the blood.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is reasonably impressed by the blood, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is reasonable.`); + } + } else if (winner.muscles > 30) { + r.push(`${winner.slaveName} is so massively muscular that ${he}'s actually impeded by ${his} lack of speed and flexibility. ${loser.slaveName} is properly afraid of ${his2} strength, though, so ${he2} tries to stay away as much as ${he2} can. The few times their blades clash reinforces this approach, since ${winner.slaveName} is able to beat ${his} opponent's blocks out of the way with contemptuous ease. The fight takes a long, long time, but it takes more out of ${loser.slaveName} to survive than it takes out of ${winner.slaveName} to keep swinging. Eventually the gasping, weeping ${loser.slaveName} trips and does not struggle to ${his2} feet in time. It takes ${his2} tired opponent several overhead butcher's cleaves to end it.`); + + success = 0.05; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `reasonably impressed`, ['reputation', 'inc']), + ` by the show of strength.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is reasonably impressed by the show of strength, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is reasonable.`); + } + } else if (loser.belly > 300000) { + r.push(`${winner.slaveName} wants to live badly enough that ${he} takes an extremely brutal shortcut to victory. The instant the fight starts, ${he} quickly slices right across ${loser.slaveName}'s massive belly, which is far too large to possibly defend. ${loser.slaveName}'s belly ruptures like a popped water balloon, showering ${winner.slaveName} with`); + + if (loser.pregType > 0) { + r.push(`blood. ${loser.slaveName} collapses into the pile of organs and babies released from ${his2} body.`); + } else if (loser.bellyImplant > 0) { + r.push(`blood and filler. ${loser.slaveName} collapses into the pool of organs and fluid released from ${his2} body.`); + } else { + r.push(`blood and ${loser.inflationType}. ${loser.slaveName} collapses into the pool of organs and fluid released from ${his2} body.`); + } + + r.push(`${winner.slaveName} walks over to the bleeding out slave and quickly cuts ${his2} throat.`); + + success = 0.1; + + if (V.pit.audience === "free") { + r.push(`the audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), + ` by this easy kill.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy kill, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is unimpressive.`); + } + } else if (loser.boobs > 1200) { + r.push(`${winner.slaveName} takes an extremely simple shortcut to victory. The instant the fight starts, ${he} slices ${loser.slaveName} right across ${his2} huge tits, which are so large they cannot properly be defended. ${loser.slaveName} reflexively drops ${his2} sword to clasp ${his2} ${hasBothArms(loser) ? `hands` : `hand`} over ${his2} ruined breasts, gushing blood${loser.boobsImplant > 400 ? ` and implant fluid` : ``}. ${winner.slaveName}'s follow-up is neither artful nor particularly well planned, but it is effective. ${He} hits the distracted ${girl2}'s neck from the side, almost but not quite separating ${his2} head from ${his2} body.`); + + success = 0.1; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), + ` by this easy kill.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy kill, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is unimpressive.`); + } + } else if (loser.dick > 0) { + r.push(`${winner.slaveName} wants to live badly enough that ${he} takes an extremely brutal shortcut to victory. The instant the fight starts, ${he} furiously swings for ${loser.slaveName}'s face. ${loser.slaveName} reflexively raises ${his2} sword to block, at which point ${winner.slaveName} simply kicks ${him2} in the dick. ${loser.slaveName} goes down like a marionette with cut strings, ${his2} mouth soundlessly opening and closing and tears leaking from ${his2} closed eyes. ${winner.slaveName} walks over to the prostrate slave and cuts ${his2} throat without much trouble.`); + + success = 0.1; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), + ` by this easy kill.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy kill, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is unimpressive.`); + } + } else { + r.push(`${winner.slaveName} wants to live badly enough that ${he} takes an extremely brutal shortcut to victory. The instant the fight starts, ${he} furiously swings for ${loser.slaveName}'s face. ${loser.slaveName} reflexively raises ${his2} sword to block, at which point ${winner.slaveName} simply kicks ${him2} in the cunt. ${loser.slaveName} goes down like a marionette with cut strings, ${his2} mouth soundlessly opening and closing and tears leaking from ${his2} closed eyes. ${winner.slaveName} walks over to the prostrate slave and cuts ${his2} throat without much trouble.`); + + success = 0.1; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ['reputation', 'inc']), + ` by this easy kill.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy kill, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ['cash', 'inc']), + ` is unimpressive.`); + } + } + + App.Events.addParagraph(parent, r); + } + + /** @param {DocumentFragment} parent */ + function postFight(parent) { + const r = []; + + const {he, his, him, himself, He} = getPronouns(winner); + const {sister: sister2} = getPronouns(loser); + const mindbrokenSpan = App.UI.DOM.makeElement("span", `no longer capable`, ["red"]); + + r.push(`You let the winner ${winner.slaveName}, shaking as ${he} comes off the adrenaline, drugs, and fear, exit the pit.`); + + if (winner.fetish === Fetish.MINDBROKEN) { + r.push(`${He} was already so broken before today that ${he} will not be seriously affected by having killed with ${his} own ${hasBothArms(winner) ? `hands` : `hand`}.`); + } else if (winner.devotion > 95) { + r.push(`${He} is so worshipful of you that ${he} does not see killing ${loser.slaveName} as special in ${his} own internal listing of things that ${he} has willingly done and would willingly do again on your behalf.`); + } else if (winner.devotion > 80) { + r.push(`${He} will never forget having killed ${loser.slaveName} with ${his} own ${hasBothArms(winner) ? `hands` : `hand`}. The only way ${he} will be able to live with ${himself} is to `, App.UI.DOM.makeElement("span", `subsume ${his} will to yours,`, ["devotion", + "inc"]), ` telling ${himself} that the killing was to serve and obey you.`); + + winner.devotion += 10; + } else if (winner.fetish === "sadist" && winner.fetishKnown && winner.fetishStrength > 60) { + r.push(`${He} has sunk even deeper into cruelty, callousness, and a total disregard of the value of other slaves' lives. Going forward, ${he}'s even `, App.UI.DOM.makeElement("span", `less likely to question`, ["devotion", + "inc"]), ` even the most abominable commands.`); + + winner.devotion += 10; + } else if (winner.devotion > 50) { + r.push(`${He} will never forget having killed ${loser.slaveName} with ${his} own ${hasBothArms(winner) ? `hands` : `hand`}. The only way ${he} will be able to live with ${himself} is to tell ${himself} that the killing was done on your orders.`); + } else { + r.push(`${He} will never forget having killed ${loser.slaveName} with ${his} own ${hasBothArms(winner) ? `hands` : `hand`}. The only way ${he} will be able to live with ${himself} is to `, App.UI.DOM.makeElement("span", `blame you,`, ["devotion", + "dec"]), ` telling ${himself} that the killing was the only choice you gave ${him} if ${he} wanted to live.`); + + winner.devotion -= 10; + } + + if (winner.fetish !== "sadist") { + if (random(1, 100) > 50) { + r.push(`Cruelty and callousness seeps its way into ${his} sexuality; ${he} has become a `, App.UI.DOM.makeElement("span", `bloody sadist.`, ["fetish", + "gain"])); + + winner.fetish = "sadist"; + winner.fetishKnown = 1; + winner.fetishStrength = 65; + } + } + + if (winner.rivalry && loser.ID === winner.rivalryTarget) { + if (winner.devotion > 75) { + r.push(`${He} is so accepting of the low value of slave life that ${he} `, App.UI.DOM.makeElement("span", `is pleased`, ["devotion", + "inc"]), ` to have killed ${his} rival ${loser.slaveName}.`); + + winner.devotion += 4; + } + } else if (winner.relationship && loser.ID === winner.relationshipTarget) { + if (winner.devotion > 95) { + r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} only friend at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", + "inc"]), ` end to their doomed slave relationship.`); + + winner.devotion += 4; + } else { + r.push(`${He} shows little reaction to the death of ${his} only friend at ${his} own hand. In the coming days, it becomes clear that this is because ${he} is `, mindbrokenSpan, ` of reacting to anything on an emotional level. Ever again.`); + + applyMindbroken(winner); + winner.fetishKnown = 1; + } + } else if (isParentP(winner, loser) || isParentP(loser, winner)) { + if (winner.devotion > 95) { + r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", + "inc"]), ` end to their doomed family.`); + + winner.devotion += 4; + } else { + r.push(`${He} shows little reaction to the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand. In the coming days, it becomes clear that this is because ${he} is `, mindbrokenSpan, ` of reacting to anything on an emotional level. Ever again.`); + + applyMindbroken(winner); + winner.fetishKnown = 1; + } + } else if (winner.sisters > 0) { + switch (areSisters(winner, loser)) { + case 1: + if (winner.devotion > 95) { + r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", + "inc"]), ` end to their doomed family.`); + + winner.devotion += 4; + } else { + r.push(`${He} shows little reaction to the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand. In the coming days, it becomes clear that this is because ${he} is `, mindbrokenSpan, ` of reacting to anything on an emotional level. Ever again.`); + + applyMindbroken(winner); + winner.fetishKnown = 1; + } + break; + case 2: + if (winner.devotion > 90) { + r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", + "inc"]), ` end to their doomed family.`); + + winner.devotion += 4; + } else { + r.push(`${He} shows little reaction to the death of ${his} ${relativeTerm(winner, loser)} at ${his} own hand. In the coming days, it becomes clear that this is because ${he} is `, mindbrokenSpan, ` of reacting to anything on an emotional level. Ever again.`); + + applyMindbroken(winner); + winner.fetishKnown = 1; + } + break; + case 3: + if (winner.devotion > 85) { + r.push(`${He} is so worshipful of you that ${he} sees the death of ${his} half-${sister2} at ${his} own hand as an `, App.UI.DOM.makeElement("span", `honorable`, ["devotion", + "inc"]), ` end to their doomed family.`); + + winner.devotion += 4; + } else { + r.push(`${He} is `, App.UI.DOM.makeElement("span", `utterly devastated`, ["devotion", + "dec"]), ` at being forced to take the life of ${his} half-${sister2}.`); + + winner.devotion -= 50; + } + break; + } + } + + if (winner.skill.combat < 60) { + const experienceSpan = App.UI.DOM.makeElement("span", `improved ${his} combat skills.`, ["improvement"]); + + winner.skill.combat += 5 + Math.floor(0.5 * (winner.intelligence + winner.intelligenceImplant) / 32); + r.push(`With lethal experience in ${V.pit.name}, ${winner.slaveName} has `, experienceSpan); + } + + winner.counter.pitKills++; + winner.counter.pitWins++; + + + V.pitKillsTotal++; + V.pitFightsTotal++; + + removeSlave(loser); + + App.Events.addParagraph(parent, r); + } + + // Helper Functions + + /** @returns {boolean} Returns true if slave1 won */ + function getWinner() { + if (deadliness(slave1).value > deadliness(slave2).value) { + return random(1, 100) > 20; // 80% chance of winning + } else if (deadliness(slave1).value < deadliness(slave2).value) { + return random(1, 100) > 80; // 20% chance of winning + } else if (random(1, 100) > 50) { // 50/50 + return true; + } + + return false; + } + } + + introCombatants(slave1, slave2) { + return [`This fight is between`, App.UI.DOM.slaveDescriptionDialog(slave1), `and`, + App.UI.DOM.combineNodes(contextualIntro(slave1, slave2, true), ".")]; + } +}; diff --git a/src/facilities/pit/fights/0_nonLethalRandom.js b/src/facilities/pit/fights/0_nonLethalRandom.js new file mode 100644 index 0000000000000000000000000000000000000000..a6d47235d16315e95a72b87e2f0e0bca14f72cf1 --- /dev/null +++ b/src/facilities/pit/fights/0_nonLethalRandom.js @@ -0,0 +1,1173 @@ +/** Nonlethal 1v1 between random slaves. */ +App.Facilities.Pit.Fights.NlR1v1 = class extends App.Facilities.Pit.Fights.BaseFight { + fightDescription() { + const f = new DocumentFragment(); + f.append("1-vs-1 fight between ", App.UI.DOM.slaveDescriptionDialog(getSlave(this.actors[0])), ` and `, + contextualIntro(getSlave(this.actors[0]), getSlave(this.actors[1]), true), "."); + return f; + } + + actorPrerequisites() { + return [ + [], + [] + ]; + } + + execute(node, fighterMap) { + const that = this; // Thanks, JS! + const [slave1, slave2] = this.actors.map(a => getSlave(a)); + + const winner = getWinner() ? slave1 : slave2; + const loser = winner === slave1 ? slave2 : slave1; + + let success = 0; + + intro(node); + fighterDeadliness(node, slave1); + fighterDeadliness(node, slave2); + + fight(node); + postFight(node); + + return success; + + /** + * @param {DocumentFragment} parent + */ + function intro(parent) { + const r = []; + + App.Events.drawEventArt(node, [slave1, slave2], "no clothing"); + + r.push(...that.introCombatants(slave1, slave2)); + + r.push(`You review the rules — the combatants are wearing light gloves, and the fight will be nonlethal, with you as the judge. The winner will have the right to do anything they wish to the loser,`); + + switch (V.pit.virginities) { + case "all": + r.push(`except take their virginity,`); + break; + case "anal": + r.push(`except take their anal virginity,`); + break; + case "vaginal": + r.push(`except take virginities,`); + break; + case "none": + r.push(`even take virginities,`); + break; + default: + throw new Error(`Unexpected V.pit.virginities value of '${that.params.virginities}' found`); + } + + r.push(`and earn two complete days of rest. You take a moment to look over your fighters before giving the word.`); + + App.Events.addParagraph(parent, r); + } + + /** + * @param {DocumentFragment} parent + * @param {App.Entity.SlaveState} fighter + */ + function fighterDeadliness(parent, fighter) { + const {he, his, him, himself, He, His} = getPronouns(fighter); + const fighterDeadliness = deadliness(fighter).value; + + const r = []; + + r.push( + confidence(), + skill(), + age(), + muscles(), + height(), + health(), + weight(), + tired(), + pregnancy(), + labor(), + bellyFluid(), + sight(), + hearing(), + prosthetics(), + willingness(), + ); + + App.Events.addParagraph(parent, r); + + function confidence() { + if (fighter.fetish === Fetish.MINDBROKEN) { + return `${fighter.slaveName} is too broken to care what happens to ${him}.`; + } else if (fighterDeadliness > 5) { + return `${fighter.slaveName} seems very confident, even eager to win a break.`; + } else if (fighterDeadliness > 3) { + return `${fighter.slaveName} seems nervous, but steels ${himself} to fight for time off.`; + } else if (fighterDeadliness > 1) { + return `${fighter.slaveName} seems hesitant and unsure.`; + } else { + return `${fighter.slaveName} is obviously terrified, and might flee if there were a way out of the pit.`; + } + } + + function willingness() { + if (fighter.devotion < 20 && fighter.trust < -20) { + return `${He} is unwilling to fight, but ${he} knows the punishment for refusing to do so will be even worse.`; + } + } + + function skill() { + if (fighter.skill.combat > 30) { + return `${His} stance is obviously well-practiced.`; + } + } + + function age() { + if (V.AgePenalty !== 0) { + if (fighter.physicalAge >= 100) { + return `${He} seems preoccupied, which is unsurprising given ${his} age and resulting fragility.`; + } else if (fighter.physicalAge >= 85) { + return `${He} tries not to waste ${his} strength before the fight, knowing that ${his} extreme age won't allow ${him} a second wind.`; + } else if (fighter.physicalAge >= 70) { + return `${He} steadies ${himself} as well as ${he} can in ${his} advanced age.`; + } + } + } + + function muscles() { + if (fighter.muscles > 95 && fighter.height > 185) { + return `${His} huge muscles are an intimidating sight and, despite their massive size, ${he} is tall enough to not be hindered by them.`; + } else if (fighter.muscles > 95) { + return `${His} huge muscles are an intimidating sight, but may hinder ${his} flexibility.`; + } else if (fighter.muscles > 30) { + return `${His} muscles are a trim and powerful sight.`; + } else if (fighter.muscles < -95) { + return `${He} can barely stand, let alone defend ${himself}.`; + } else if (fighter.muscles < -30) { + return `${He} is very weak; a single punch will likely floor ${him}.`; + } else if (fighter.muscles < -5) { + return `${He} is rather unfit; ${he} will likely be outmatched by near any real opponent.`; + } else { + return `${He} is only somewhat fit and will likely not be able to win through pure strength.`; + } + } + + function height() { + if (fighter.height > 170) { + return `${His} height gives ${him} a reach advantage with ${his} fists and feet.`; + } + } + + function health() { + if (fighter.health.condition > 50) { + return `${His} shining health makes ${him} a better fighter.`; + } else if (fighter.health.condition) { + return `${His} poor health makes ${him} a weaker combatant.`; + } + } + + function weight() { + if (fighter.weight > 190) { + return `${His} extreme weight nearly immobilizes ${him}. ${He} is essentially a fleshy punching bag.`; + } else if (fighter.weight > 160) { + return `${His} extreme weight limits ${his} mobility and range of motion even if ${he} can take punches like nothing.`; + } else if (fighter.weight > 130) { + return `${His} extreme weight holds ${him} back as a pit fighter.`; + } else if (fighter.weight > 30) { + return `${His} heavy weight is an impediment as a pit fighter.`; + } else if (fighter.weight < -10) { + return `${His} light weight is an impediment as a pit fighter.`; + } + } + + function tired() { + if (fighter.health.tired > 90) { + return `${He} is exhausted and can barely stay awake; ${he} won't put up a fight.`; + } else if (fighter.health.tired > 60) { + return `${He} is fatigued, sapping the strength ${he}'ll need in ${his} strikes.`; + } else if (fighter.health.tired > 30) { + return `${He} is tired and more likely to take a hit than to give one.`; + } + } + + function pregnancy() { + if (fighter.pregKnown || fighter.bellyPreg > 1500) { + if (fighter.bellyPreg > 750000) { + return `${His} monolithic pregnancy guarantees ${his} loss; not only is ${he} on the verge of splitting open, but it is an unmissable, indefensible target that threatens to drag ${him} to the ground. ${He} has no hope of attacking around the straining mass, let alone stopping ${his} opponent. The fear of what would happen should ${his} adversary land a hit on ${his} belly also weighs upon ${his} mind.`; + } else if (fighter.bellyPreg > 600000) { + return `${His} titanic pregnancy is practically a guaranteed loss; ${he} can barely stand let alone fight. The worry of a solid hit striking ${his} life-swollen womb also weighs on ${his} mind.`; + } else if (fighter.bellyPreg > 450000) { + return `${His} gigantic pregnancy is nearly a guaranteed loss; it presents an unmissable, indefensible target for ${his} adversary.`; + } else if (fighter.bellyPreg > 300000) { + return `${His} massive pregnancy obstructs ${his} movement and greatly hinders ${him}. ${He} struggles to think of how ${he} could even begin to defend ${his} bulk.`; + } else if (fighter.bellyPreg > 150000) { + return `${His} giant pregnancy obstructs ${his} movement and greatly slows ${him} down.`; + } else if (fighter.bellyPreg > 100000) { + return `${His} giant belly gets in ${his} way and weighs ${him} down.`; + } else if (fighter.bellyPreg > 10000) { + return `${His} huge belly is unwieldy and hinders ${his} efforts.`; + } else if (fighter.bellyPreg > 5000) { + return `${His} advanced pregnancy makes ${him} much less effective.`; + } else if (fighter.bellyPreg > 1500) { + return `${His} growing pregnancy distracts ${him} from the fight.`; + } else { + return `The life just beginning to grow inside ${him} distracts ${him} from the fight.`; + } + } else if (fighter.bellyImplant > 1500) { + if (fighter.bellyImplant > 750000) { + return `${His} monolithic, ${fighter.bellyImplant}cc implant-filled belly guarantees ${his} defeat; not only is ${he} on the verge of splitting open, but it is an unmissable, indefensible target that threatens to drag ${him} to the ground. ${He} has no hope of attacking around the straining mass, let alone stopping ${his} opponent.`; + } else if (fighter.bellyImplant > 600000) { + return `${His} titanic, ${fighter.bellyImplant}cc implant-filled belly is practically a guaranteed defeat; ${he} can barely stand let alone fight. Not only is it cripplingly heavy, unwieldy and an easy target, but ${he} can feel it straining to hold the sheer amount of filler forced into it.`; + } else if (fighter.bellyImplant > 450000) { + return `${His} gigantic, ${fighter.bellyImplant}cc implant-filled belly is nearly a guaranteed defeat; it presents an unmissable, indefensible target for ${his} adversary.`; + } else if (fighter.bellyImplant > 300000) { + return `${His} massive, ${fighter.bellyImplant}cc implant-filled belly is extremely heavy, unwieldy and an easy target, practically damning ${him} in combat.`; + } else if (fighter.bellyImplant > 150000) { + return `${His} giant, ${fighter.bellyImplant}cc implant-filled belly obstructs ${his} movement and greatly slows ${him} down.`; + } else if (fighter.bellyImplant > 100000) { + return `${His} giant, ${fighter.bellyImplant}cc implant-filled belly is very heavy and unwieldy, throwing off ${his} weight and making ${him} far less effective.`; + } else if (fighter.bellyImplant > 10000) { + return `${His} huge, ${fighter.bellyImplant}cc implant-filled belly is very heavy and unwieldy, throwing off ${his} weight and making ${him} far less effective.`; + } else if (fighter.bellyImplant > 5000) { + return `${His} large, ${fighter.bellyImplant}cc implant-filled belly is heavy and unwieldy, rendering ${him} less effective.`; + } else if (fighter.bellyImplant > 1500) { + return `${His} swollen, ${fighter.bellyImplant}cc implant-filled belly is heavy and makes ${him} less effective.`; + } + } + } + + function labor() { + if (isInLabor(fighter)) { + return `${He}'s feeling labor pains. ${His} ${fighter.pregType > 1 ? `children are` : `child is`} ready to be born, oblivious to the fact that it will put ${fighter.pregType > 1 ? `their` : `its`} mother at the mercy of ${his} opponent.`; + } else if (fighter.preg > fighter.pregData.normalBirth && fighter.pregControl !== "labor suppressors") { + return `${He}'ll be going into labor any time now and ${he} knows it. ${He}'s terrified of the thought of ${his} water breaking during the fight.`; + } + } + + function bellyFluid() { + if (fighter.bellyFluid > 10000) { + return `${His} hugely bloated, ${fighter.inflationType}-filled belly is taut and painful, hindering ${his} ability to fight.`; + } else if (fighter.bellyFluid > 5000) { + return `${His} bloated, ${fighter.inflationType}-stuffed belly is constantly jiggling and moving, distracting ${him} and throwing off ${his} weight.`; + } else if (fighter.bellyFluid > 2000) { + return `${His} distended, ${fighter.inflationType}-belly is uncomfortable and heavy, distracting ${him}.`; + } + } + + function sight() { + if (!canSee(fighter)) { + return `${His} lack of eyesight means certain defeat.`; + } else if (!canSeePerfectly(fighter)) { + return `${His} poor eyesight makes ${him} a weaker fighter.`; + } + } + + function hearing() { + if (!canHear(fighter)) { + return `${His} lack of hearing is a major detriment.`; + } else if ((fighter.hears === -1 && fighter.earwear !== "hearing aids") || (fighter.hears === 0 && fighter.earwear === "muffling ear plugs")) { // TODO: replace with canHearPerfectly + return `${His} poor hearing is a minor detriment.`; + } + } + + function prosthetics() { + if (hasAnyProstheticLimbs(fighter) && !hasAnyQuadrupedLimbs(fighter)) { + const r = []; + + r.push(`The pit lights gleam on ${his} P-Limbs.`); + + if (getLimbCount(fighter, 6) > 0) { + r.push(`${His} advanced cybernetic limbs are faster than natural limbs, and their force is amplified, so that they can become potent weapons.`); + } else if (getLimbCount(fighter, 5) > 0) { + r.push(`Though their integral weapons are disabled, ${his} upgraded prosthetics are almost as fast as natural limbs, and they can hit much, much harder.`); + } + + return r.join(' '); + } + } + + if (hasAnyProstheticLimbs(fighter) && hasAnyQuadrupedLimbs(fighter)) { + const r = []; + + r.push(`The pit lights gleam on ${his} prosthetic limbs. They have the advantage of being quadrupedal, keeping ${him} low to the ground and providing better mobility.`); + return r.join(' '); + } + } + + /** + * @param {DocumentFragment} parent + */ + function fight(parent) { + const r = []; + + const winnerDeadliness = deadliness(winner).value; + const loserDeadliness = deadliness(loser).value; + + const {he, his, him, himself, girl, He} = getPronouns(winner); + const { + he: he2, + his: his2, + him: him2, + himself: himself2, + girl: girl2, + He: He2, + His: His2, + } = getPronouns(loser); + + if (!canSee(winner) && !canSee(loser)) { + r.push(`${winner.slaveName} and ${loser.slaveName} are both blind, making the fight a stare-down. Neither slave wants to make the first move, especially with the drowning cacophony coming from the jeering crowd. Slowly, ${winner.slaveName} moves forward, waving feeling for ${his} opponent before ${he} ${himself} gets found. ${loser.slaveName}'s hand meets ${winner.slaveName}'s and the two move to grab each other in a headlock. The two slaves violently thrash against each other, suffering more and more strikes as the struggle goes on. Eventually, ${loser.slaveName} can take no more and releases ${his2} grip on ${winner.slaveName} neck. It takes a moment for ${winner.slaveName} to stop wrestling the submitting ${loser.slaveName} and accept ${his} victory.`); + + success = 0.25; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `surprised`, ["reputation", "inc"]), + ` by the impromptu wrestling match.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is surprised by the sudden wrestling match, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is modest.`); + } + } else if (winnerDeadliness > (loserDeadliness + 1) && getArmCount(winner, 6) > 1) { + r.push(`Upon your word the two combatants approach each other. ${winner.slaveName} keeps ${his} advanced cybernetic limbs up in a protective position. ${loser.slaveName} probes ${him} with some light blows, puzzled by this ironclad defense. Gradually, ${he2} hits harder and harder, ${his2} opponent grunting under the impacts but holding steady. Finally, ${loser.slaveName} tires, gets off balance, and ${winner.slaveName} manages to grab ${his2} forearm. ${winner.slaveName}'s limbs emit an electric shock that temporarily incapacitates ${his} opponent. ${winner.slaveName} uses ${his} grip to pull ${his} stunned opponent in and grab ${his2} neck with the other hand, using it to exert just the right amount of pressure to choke ${him2} out harmlessly. Though the fight was short,`); + + success = 0.5; + + if (V.pit.audience === "free") { + r.push(`the audience is `, + App.UI.DOM.makeElement("span", `very impressed`, ["reputation", "inc"]), + ` by the display.`); + } else if (V.pit.audience === "paid") { + r.push(`the audience is quite impressed by the display, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is quite good.`); + } + } else if (winnerDeadliness > (loserDeadliness + 1) && getArmCount(winner, 5) > 1) { + r.push(`Upon your word the two combatants approach each other. ${winner.slaveName} keeps ${his} artificial limbs up in a protective position. ${loser.slaveName} probes ${him} with some light blows, puzzled by this ironclad defense. Gradually, ${he2} hits harder and harder, ${his2} opponent grunting under the impacts but holding steady. Finally, ${loser.slaveName} overcommits to a body blow, and ${winner.slaveName} grips ${his2} forearm. That is the end. The augmented grip is effectively unbreakable, and ${winner.slaveName} uses it to pull ${his} opponent in and grab ${his2} neck with the other hand, using it to exert just the right amount of pressure to choke ${him2} out harmlessly. Though the fight was short,`); + + success = 0.5; + + if (V.pit.audience === "free") { + r.push(`the audience is `, + App.UI.DOM.makeElement("span", `very impressed`, ["reputation", "inc"]), + ` by the display.`); + } else if (V.pit.audience === "paid") { + r.push(`the audience is quite impressed by the display, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is quite good.`); + } + } else if (!canSee(winner)) { + r.push(`${winner.slaveName} centers ${himself} and hones in on ${loser.slaveName}'s rapid breath. ${He} readies ${himself}, knowing ${he} has a single chance to win. With heavy steps, ${loser.slaveName} rushes ${him}, fully prepared to tackle the helpless combatant. ${winner.slaveName} gambles everything on a single high punch, a strike that slams ${loser.slaveName}'s undefended neck. ${loser.slaveName} falls to the ground before the panicking ${winner.slaveName}, who quickly pins the coughing loser.`); + + healthDamage(winner, 80); + success = 1; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `awestruck`, ["reputation", "inc"]), + ` by the blind ${girl}'s triumph.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is awestruck by the blind ${girl}'s triumph, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is enormous.`); + } + } else if (!canSee(loser)) { + r.push(`${winner.slaveName} grins at ${loser.slaveName}'s random swings and poor form. ${He} centers in on ${his} favorite part of ${loser.slaveName}'s body before rushing between ${his2} strikes and tackling ${him2} to the ground. ${loser.slaveName} lays there, helpless to stop ${winner.slaveName} from molesting ${him2}.`); + + success = -0.1; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `annoyed`, ["reputation", "dec"]), + ` by this lack of a fight.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience found the fight embarrassing, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "dec"]), + ` is pitiful.`); + } + } else if (winnerDeadliness > (loserDeadliness + 3)) { + if (winner.skill.combat > 30) { + r.push(`${winner.slaveName} wants to win, and ${he} opens the fight with a furious attack. ${loser.slaveName} manages to get ${his2} ${hasBothArms(loser) ? `forearms` : `forearm`} up, blocking a few blows, but by doing so leaves ${his2} abdomen open and obscures ${his2} vision enough that ${he2} is unprepared for the following kidney strike, which brings ${him2} gasping to one knee. ${winner.slaveName} lets ${him2} rise just a little before delivering a calculated hard right that sends ${loser.slaveName} to the mat.`); + + success = 0.125; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), + ` by this execution.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this execution, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is unimpressive.`); + } + } else { + r.push(`${winner.slaveName} wants to win and is confident ${he} will, but ${he} isn't particularly sure about how to do so. ${He} fights cautiously, mostly hitting ${loser.slaveName} from afar. Slowly, the rain of blows begins to tell, opening ${loser.slaveName}'s lip, giving ${him2} a bloody nose, and bruising ${his2} face. Realizing ${he2} has to do something, ${loser.slaveName} makes a desperate counterattack, `, App.UI.DOM.makeElement("span", `dripping blood`, ["health", + "dec"]), ` as ${he2} goes. As ${he2} does ${he2} manages to get ${his2} face in the way of another of ${winner.slaveName}'s inexpert strikes and goes down hard. ${He2} makes to rise, but ${winner.slaveName} decides the fight by the simple expedient of kicking ${him2} in the crotch.`); + + healthDamage(winner, 20); + success = 0.25; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `reasonably impressed`, ["reputation", "inc"]), + ` by the beating.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is reasonably impressed by the beating, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is reasonable.`); + } + } + } else if (winner.belly > 60000 && loser.belly > 60000) { + r.push(`${winner.slaveName} and ${loser.slaveName} stare each other down and both come to a realization. Neither can reach the other around their massive bellies. Instead, they choose to ram their bulk into each other in hopes of toppling the weaker. After a drawn out struggle, both slaves' middles are `, + App.UI.DOM.makeElement("span", `dark red and shuddering,`, ["health", "dec"]), + ` ready to burst open. Rather than continue, ${loser.slaveName} lets the next strike down ${him2} hoping that the outcome of this fight isn't fatal.`); + + success = 0.375; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `very impressed`, ["reputation", "dec"]), + ` by the showdown.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is very impressed by the showdown, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is good.`); + } + } else if (winner.belly > 60000 && loser.belly < 30000) { + r.push(`${loser.slaveName} spies an easy win against ${his2} massively bloated opponent and rushes in to topple ${winner.slaveName}. In an effort to defend ${himself}, ${winner.slaveName} hoists ${his} belly and turns suddenly, accidentally impacting ${loser.slaveName} with ${his} massive middle and knocking ${him2} to the ground. Seeing an opportunity, ${winner.slaveName} releases ${his} grip and slams ${his} weighty womb down on ${loser.slaveName}, bashing the wind out of ${him2}. ${loser.slaveName} struggles to slip out from under the mass, but the weight is too great and ${he2} passes out.`); + + success = 0.25; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `impressed`, ["reputation", "dec"]), + ` by this absurd win.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is impressed by this absurd win, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is reasonably.`); + } + } else if (winner.skill.combat > 30 && loser.skill.combat > 30) { + const healthSpans = [App.UI.DOM.makeElement("span", `broken nose`, ["health", "dec"]), + App.UI.DOM.makeElement("span", `to the point of damage`, ["health", "dec"])]; + + r.push(`Upon your word the two combatants approach each other warily, both knowing the other is reasonably competent. Before long they are trading expert blows. ${winner.slaveName} is getting the worst of it, so ${he} decides to change the nature of the fight. After three tries ${he} manages to bring ${loser.slaveName} to the ground, suffering a `, healthSpans[0], ` as ${he} does. ${loser.slaveName} tries to break the imperfect hold but only earns ${himself2} an elbow to the face. ${He2}'s furious and ${winner.slaveName} is obliged to wrench ${his2} arm `, healthSpans[1], ` before ${he2} allows ${himself2} to go limp.`); + + success = 0.5; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `very impressed`, ["reputation", "inc"]), + ` by the expert fight.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is quite impressed by the expert fight, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is quite good.`); + } + } else if (winner.height - loser.height < -10) { + r.push(`${winner.slaveName} realizes that ${loser.slaveName}'s wingspan gives ${him2} a huge reach advantage. ${He} bores straight in, taking a hit or two but coming on regardless. ${loser.slaveName} understands ${his2} opponent's intention and backs off, but the pit is small and there isn't much room to retreat. When ${his2} back hits a padded wall, ${winner.slaveName} manages to land a light hit to ${his2} stomach that leaves ${loser.slaveName} winded enough that a hard kick to the side of ${his2} knee goes undefended. It causes `, App.UI.DOM.makeElement("span", `considerable damage,`, ["health", + "dec"]), ` dropping ${him2} and ending the fight.`); + + success = 0.25; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `reasonably impressed`, ["reputation", "inc"]), + ` by the take-down.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is reasonably impressed by the take-down, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is reasonable.`); + } + } else if (loser.piercing.eyebrow.weight > 0) { + r.push(`The fight starts slowly, with the two trading jabs. Just as the spectators are getting bored, ${loser.slaveName} takes a glancing blow to the eyebrow. ${His2} piercing catches on ${winner.slaveName}'s glove and tears out. ${loser.slaveName} goes after ${his2} tormentor in fury, streaming blood, the piercing forgotten on the mat. Any tendency ${winner.slaveName} might have had to feel badly about this is extinguished by the assault, and soon ${winner.slaveName} is even willing to follow up on the success by targeting pierced body parts. The fight ends with poor ${loser.slaveName} writhing in pain on the mat, `, App.UI.DOM.makeElement("span", `leaking blood`, ["health", + "dec"]), ` from several terribly shredded areas.`); + + success = 0.25; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `reasonably impressed`, ["reputation", "inc"]), + ` by the gory spectacle.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is reasonably impressed by the gory spectacle, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is reasonable.`); + } + } else if (winner.muscles > 30) { + r.push(`${winner.slaveName} is so massively muscular that ${he}'s actually impeded by ${his} size. ${loser.slaveName} is properly afraid of ${his} strength, though, so ${he2} tries to stay away as much as ${he2} can. The pit isn't large, however, and eventually ${winner.slaveName} manages to lay a hand on ${him2}. ${He} pulls ${him2} down, and then it's all over but the beating. ${loser.slaveName} rains blows on ${his2} huge oppressor, but all ${winner.slaveName} has to do is hold on with one arm and deliver damage with the other. By the time ${he2} gives up and goes limp, ${loser.slaveName} has collected `, App.UI.DOM.makeElement("span", `many minor injuries.`, ["health", + "dec"])); + + success = 0.25; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `reasonably impressed`, ["reputation", "inc"]), + ` by the show of strength.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is reasonably impressed by the show of strength, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is reasonable.`); + } + } else if (loser.belly > 300000) { + r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely brutal shortcut to victory. The instant the fight starts, ${he} quickly knees ${loser.slaveName} in the stomach. The massively swollen ${loser.slaveName} goes down with a loud thud and plenty of jiggling. ${winner.slaveName} gloats over the struggling ${loser.slaveName} watching as ${he2} is unable to pull ${his2} bloated form off the ground.`); + + success = 0.25; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), + ` by this easy win.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy win, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is unimpressive.`); + } + } else if (loser.boobs > 1200) { + r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely simple shortcut to victory. The instant the fight starts, ${he} hits ${loser.slaveName} right in ${his2} huge tits, as hard as ${he} can. This is a sucker punch of the worst kind; ${loser.slaveName}'s boobs are so big that ${he2} has no real chance of defending them. ${He2} gasps with pain${hasAnyArms(loser) ? ` and wraps ${his2} ${hasBothArms(loser) ? `arms` : `arm`} around ${his2} aching bosom` : ``}, giving ${winner.slaveName} a clear opening to deliver a free and easy blow to the jaw that sends the poor top-heavy slave to the mat. Any chance of ${loser.slaveName} rising is extinguished by ${his2} breasts; it takes ${him2} so long to muster an attempt to get up that ${winner.slaveName} can rain hits on ${him2} while ${he2} does.`); + + success = 0.125; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), + ` by this easy win.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy win, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is unimpressive.`); + } + } else if (loser.dick > 0) { + r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely unpleasant shortcut to victory. The instant the fight starts, ${he} furiously goes for ${loser.slaveName}'s eyes${hasBothArms(winner) ? `, hands forming claws` : hasAnyArms(winner) ? `, ${his} hand forming a claw` : ``}. ${loser.slaveName} ${hasAnyArms(loser) ? `defends ${himself2} with ${his2} ${hasBothArms(loser) ? `arms` : `arm`}` : `tries to defend ${himself} as best ${he} can`}, at which point ${winner.slaveName} delivers a mighty cunt punt. ${loser.slaveName} goes straight down, ${his2} mouth soundlessly opening and closing and tears leaking from ${his2} closed eyes${hasAnyArms(loser) + ? ` while ${his} ${hasBothArms(loser) + ? `hands desperately shield` + : `hand desperately shields`} ${his2} outraged pussy` + : ``}. ${winner.slaveName} follows ${him2} down and puts the unresisting ${girl2}'s head in a simple lock.`); + + success = 0.125; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), + ` by this easy win.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy win, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is unimpressive.`); + } + } else if (loser.vagina > 0) { + r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely unpleasant shortcut to victory. The instant the fight starts, ${he} furiously goes for ${loser.slaveName}'s eyes${hasBothArms(winner) ? `, hands forming claws` : hasAnyArms(winner) ? `, ${his} hand forming a claw` : ``}. ${loser.slaveName} ${hasAnyArms(loser) ? `defends ${himself2} with ${his2} ${hasBothArms(loser) ? `arms` : `arm`}` : `tries to defend ${himself} as best ${he} can`}, at which point ${winner.slaveName} delivers a mighty cunt punt. ${loser.slaveName} goes straight down, ${his2} mouth soundlessly opening and closing and tears leaking from ${his2} closed eyes${hasAnyArms(loser) ? ` while ${his} ${hasBothArms(loser) ? `hands` : `hand`} desperately shield${!hasBothArms(loser) ? `s` : ``} ${his2} outraged pussy` : ``}. ${winner.slaveName} follows ${him2} down and puts the unresisting ${girl2}'s head in a simple lock.`); + + success = 0.125; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), + ` by this easy win.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy win, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is unimpressive.`); + } + } else { + r.push(`${winner.slaveName} wants to win badly enough that ${he} takes an extremely unpleasant shortcut to victory. The instant the fight starts, ${he} furiously goes for ${loser.slaveName}'s eyes, hands forming claws. ${loser.slaveName} defends ${himself2} with ${his2} arms, at which point ${winner.slaveName} delivers a clenched fist to ${loser.slaveName}'s throat. ${loser.slaveName} staggers back, wheezing for breath with tears leaking from ${his2} closed eyes. ${winner.slaveName} takes advantage of ${loser.slaveName}'s vulnerability to quickly trip and pin the coughing loser.`); + + success = 0.125; + + if (V.pit.audience === "free") { + r.push(`The audience is `, + App.UI.DOM.makeElement("span", `not very impressed`, ["reputation", "inc"]), + ` by this easy win.`); + } else if (V.pit.audience === "paid") { + r.push(`The audience is not very impressed by this easy win, and your `, + App.UI.DOM.makeElement("span", `cut of the betting`, ["cash", "inc"]), + ` is unimpressive.`); + } + } + + App.Events.addParagraph(parent, r); + } + + /** @param {DocumentFragment} parent */ + function postFight(parent) { + let r = []; + + const anus = "anus"; + const cunt = "cunt"; + const oral = "oral"; + const vaginal = "vaginal"; + const anal = "anal"; + + + const {he, his, him, He} = getPronouns(winner); + const {his: his2, him: him2} = getPronouns(loser); + + const facefuck = `${He} considers ${his} options briefly, then hauls the loser to ${his2} knees for a facefuck.`; + const winnerPenetrates = orifice => `${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`}, and penetrates the loser's ${orifice}.`; + const loserProtest = `${canTalk(loser) + ? `${loser.slaveName} starts to scream a protest to stop ${winner.slaveName} raping ${him2} pregnant, but ${winner.slaveName} grinds ${his2} face into the mat to shut ${him2} up` + : `${loser.slaveName} tries to gesture a protest before ${winner.slaveName} fills ${his2} fertile ${loser.mpreg && that.params.virginities !== anal ? `asspussy` : `pussy`} with cum, but ${winner.slaveName} grabs ${his2} ${hasBothArms(loser) ? `hands and pins them` : hasAnyArms(loser) ? `hand and pins it` : `neck firmly`} to keep ${him2} from complaining`}.`; + + const virginitySpan = App.UI.DOM.makeElement("span", ``, ["virginity", "loss"]); + + r.push(`You throw the victor's strap-on down to ${winner.slaveName}.`); + + if (canPenetrate(winner)) { + r.push(`${He} has no need of it, only taking a moment to pump ${his} dick a few times to get it to rock hardness.`); + } else if (winner.clit > 4) { + r.push(`${He} has no need of it, since ${his} clit is big enough to use instead.`); + } else if (winner.dick > 6 && !canAchieveErection(winner)) { + r.push(`${He} needs it, since ${his} enormous dick can't get hard any longer (not like it would fit in ${loser.slaveName} anyway).`); + } else if (winner.dick > 0) { + r.push(`${He} needs it, since ${his} soft dick won't be raping anything.`); + } + + if (V.pit.virginities === "all") { + if (loser.vagina === 0 && canDoVaginal(loser) && loser.anus === 0 && canDoAnal(loser)) { + r.push(`${He} respects ${loser.slaveName}'s virgin holes, and hauls the loser to ${his2} knees for a facefuck.`); + + actX(loser, oral); + } else if (loser.vagina === 0 && canDoVaginal(loser) && canDoAnal(loser)) { + r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); + + if (winner.fetish === "pregnancy") { + r.push(`and, after ${canSee(winner) ? `eyeing` : `feeling up`} ${his2} virgin vagina with desire, penetrates the loser's anus.`); + } else { + r.push(`and respects the rules by penetrating the loser's anus.`); + } + + if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { + r.push(loserProtest); + + knockMeUp(loser, 50, 1, winner.ID); + } + + actX(loser, anal); + } else if (loser.anus === 0 && canDoVaginal(loser) && canDoAnal(loser)) { + r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); + + if (winner.fetish === "buttslut" || (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg && winner.fetish === "pregnancy")) { + r.push(`and, after ${canSee(winner) ? `eyeing` : `feeling up`} ${his2} virgin anus with desire, penetrates the loser's cunt.`); + } else { + r.push(`and respects the rules by penetrating the loser's cunt.`); + } + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + actX(loser, vaginal); + } else if (canDoVaginal(loser)) { + r.push(winnerPenetrates(cunt)); + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + actX(loser, vaginal); + } else if (canDoAnal(loser)) { + r.push(winnerPenetrates(anus)); + + if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { + r.push(loserProtest); + + knockMeUp(loser, 50, 1, winner.ID); + } + + actX(loser, anal); + } else { + r.push(facefuck); + + actX(loser, oral); + } + } else if (V.pit.virginities === "anal") { + if (loser.vagina === 0 && canDoVaginal(loser)) { + r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); + + if (winner.fetish === "pregnancy") { + virginitySpan.append(`take ${his2} virginity.`); + r.push(`and gleefully takes advantage of your rules to `, virginitySpan); + } else { + virginitySpan.append(`breaks in ${his2} virgin vagina.`); + r.push(`and `, virginitySpan); + } + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + loser.vagina++; + actX(loser, vaginal); + } else if (loser.anus === 0 && canDoAnal(loser)) { + r.push(`${canTalk(loser) + ? `${loser.slaveName} starts to scream a protest to stop ${winner.slaveName} raping ${him2} pregnant, but ${winner.slaveName} grinds ${his2} face into the mat to shut ${him2} up` + : `${loser.slaveName} tries to gesture a protest before ${winner.slaveName} fills ${his2} fertile pussy with cum, but ${winner.slaveName} grabs ${his2} ${hasBothArms(loser) ? `hands and pins them` : hasAnyArms(loser) ? `hand and pins it` : `neck firmly`} to keep ${him2} from complaining`}.`); + + if (winner.fetish === "buttslut" || (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg && winner.fetish === "pregnancy")) { + if (canDoVaginal(loser)) { + r.push(`and penetrates the loser's cunt.`); + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + loser.counter.vaginal++; + V.vaginalTotal++; + } else { + r.push(`and finds only a pristine butthole waiting for ${him}. Respecting ${his2} anal virginity, ${he} hauls the loser onto ${his2} knees for a facefuck.`); + + actX(loser, oral); + } + } + } else if (canDoVaginal(loser)) { + r.push(winnerPenetrates(cunt)); + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + actX(loser, vaginal); + } else if (canDoAnal(loser)) { + r.push(winnerPenetrates(anus)); + + if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { + r.push(loserProtest); + + knockMeUp(loser, 50, 1, winner.ID); + } + + actX(loser, anal); + } else { + r.push(facefuck); + + actX(loser, oral); + } + } else if (V.pit.virginities === "vaginal") { + if (loser.vagina === 0 && canDoVaginal(loser)) { + r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); + + if (winner.fetish === "pregnancy") { + if (canDoAnal(loser)) { + r.push(`and hungrily eyes ${his2} pristine vagina before penetrating the loser's ass.`); + + if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { + r.push(loserProtest); + + knockMeUp(loser, 50, 1, winner.ID); + } + + actX(loser, anal); + } else { + r.push(`and hungrily eyes ${his2} pristine vagina before hauling the loser onto ${his2} knees for a facefuck.`); + + actX(loser, oral); + } + } else { + if (canDoAnal(loser)) { + r.push(`and penetrates the loser's ass.`); + + if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { + r.push(loserProtest); + + knockMeUp(loser, 50, 1, winner.ID); + } + + actX(loser, anal); + } else { + r.push(`and finds only a pristine butthole waiting for ${him}. Respecting ${his2} anal virginity, ${he} hauls the loser onto ${his2} knees for a facefuck.`); + + actX(loser, oral); + } + } + } else if (loser.anus === 0 && canDoAnal(loser)) { + r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); + + if (winner.fetish === "buttslut" || (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg && winner.fetish === "pregnancy")) { + virginitySpan.append(`take ${his2} anal virginity.`); + r.push(`and gleefully takes advantage of your rules to `, virginitySpan); + } else { + virginitySpan.append(`breaks in ${his2} virgin anus.`); + r.push(`and `, virginitySpan); + } + + if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { + r.push(loserProtest); + + knockMeUp(loser, 50, 1, winner.ID); + } + + loser.anus++; + actX(loser, anal); + } else if (canDoVaginal(loser)) { + r.push(winnerPenetrates(cunt)); + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + actX(loser, vaginal); + } else if (canDoAnal(loser)) { + r.push(winnerPenetrates(anus)); + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + actX(loser, anal); + } else { + r.push(facefuck); + + actX(loser, oral); + } + } else { + if (loser.vagina === 0 && canDoVaginal(loser)) { + r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); + + if (winner.fetish === "pregnancy") { + virginitySpan.append(`take ${his2} virginity.`); + r.push(`and gleefully takes advantage of your rules to `, virginitySpan); + } else { + virginitySpan.append(`breaks in ${his2} virgin vagina.`); + r.push(`and `, virginitySpan); + } + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + loser.vagina++; + actX(loser, vaginal); + } else if (loser.anus === 0 && canDoAnal(loser)) { + r.push(`${He} pushes ${loser.slaveName}'s back down onto the mat, forces ${his2} ${hasBothLegs(loser) ? `legs apart` : !hasAnyLegs(loser) ? `hips steady` : `leg aside`},`); + + if (winner.fetish === "buttslut" || (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg && winner.fetish === "pregnancy")) { + virginitySpan.append(`take ${his2} anal virginity.`); + r.push(`and gleefully takes advantage of your rules to `, virginitySpan); + } else { + virginitySpan.append(`breaks in ${his2} virgin anus.`); + r.push(`and `, virginitySpan); + } + + if (canPenetrate(winner) && canImpreg(loser, winner) && loser.mpreg) { + r.push(loserProtest); + + knockMeUp(loser, 50, 1, winner.ID); + } + + loser.anus++; + actX(loser, anal); + } else if (canDoVaginal(loser)) { + r.push(winnerPenetrates(cunt)); + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 0, winner.ID); + } + + actX(loser, vaginal); + } else if (canDoAnal(loser)) { + r.push(winnerPenetrates(anus)); + + if (canPenetrate(winner) && canImpreg(loser, winner)) { + r.push(loserProtest); + + knockMeUp(loser, 50, 1, winner.ID); + } + + actX(loser, anal); + } else { + r.push(facefuck); + + actX(loser, oral); + } + } + + App.Events.addParagraph(parent, r); + + App.Events.addParagraph(parent, [...rape(), ...rapeEffects()]); + + r = []; + if (winner.skill.combat < 60) { + const experienceSpan = App.UI.DOM.makeElement("span", `improved ${his} combat skills.`, ["improvement"]); + + winner.skill.combat += 5 + Math.floor(0.5 * (winner.intelligence + winner.intelligenceImplant) / 32); + r.push(`With experience in ${V.pit.name}, ${winner.slaveName} has `, experienceSpan); + } + + loser.counter.pitLosses++; + actX(winner, "penetrative"); + + winner.counter.pitWins++; + + + V.pitFightsTotal++; + + App.Events.addParagraph(parent, r); + + function rape() { + const repSpan = App.UI.DOM.makeElement("span", ``, ["reputation", "inc"]); + + const r = []; + + const {he, his, He} = getPronouns(winner); + const {he: he2, his: his2, him: him2, girl: girl2} = getPronouns(loser); + + if (winner.sexualFlaw === "malicious") { + r.push(`${winner.slaveName} lives to torment and violate slaves. ${He} uses ${loser.slaveName} for ${his} `, App.UI.DOM.makeElement("span", `pleasure`, ["devotion", + "inc"]), ` slapping ${him2} viciously, pinching ${him2} until ${he} draws blood, and showing off for the spectators.`); + + if (V.pit.audience !== "none") { + repSpan.append(`greatly enjoys`); + r.push(`The audience `, repSpan, ` the depraved spectacle.`); + + repX(50, "pit", winner); + } + + winner.devotion++; + } else if (winner.sexualFlaw === "abusive") { + r.push(`${winner.slaveName} lives to abuse and violate slaves. ${He} uses ${loser.slaveName} for ${his} `, App.UI.DOM.makeElement("span", `pleasure`, ["devotion", + "inc"]), ` raping ${him2} mercilessly, striking ${him2} until ${he2} turns a deep reddish-purple, and showing off for the spectators.`); + + if (V.pit.audience !== "none") { + repSpan.append(`greatly enjoys`); + r.push(`The audience `, repSpan, ` the depraved spectacle.`); + + repX(50, "pit", winner); + } + + winner.devotion++; + } else if (winner.energy > 95) { + r.push(`${winner.slaveName} doesn't have to feign willingness to rape a slave ${girl2}. ${He} uses ${loser.slaveName} for ${his} `, App.UI.DOM.makeElement("span", `pleasure,`, ["devotion", + "inc"]), ` groping ${him2}, kissing ${his2} unwilling mouth, and showing off for the spectators.`); + + if (V.pit.audience !== "none") { + repSpan.append(`greatly enjoys`); + r.push(`The audience `, repSpan, ` the depraved spectacle.`); + + repX(50, "pit", winner); + } + + winner.devotion++; + } else if (winner.fetish === "sadist" && winner.fetishStrength > 60) { + r.push(`${winner.slaveName} doesn't have to feign willingness abuse and violate a slave ${girl2}. ${He} uses ${loser.slaveName} for ${his} `, App.UI.DOM.makeElement("span", `pleasure`, ["devotion", + "inc"]), ` slapping ${him2} cruelly, pinching ${him2} until ${he} draws blood, and showing off for the spectators.`); + + if (V.pit.audience !== "none") { + repSpan.append(`greatly enjoys`); + r.push(`The audience `, repSpan, ` the depraved spectacle.`); + + repX(50, "pit", winner); + } + + winner.devotion++; + } else if (winner.devotion > 75) { + r.push(`${winner.slaveName} is so broken to slavery that ${he} `, App.UI.DOM.makeElement("span", `enjoys`, ["devotion", + "inc"]), ` winning the fight and raping ${loser.slaveName}.`); + + if (V.pit.audience !== "none") { + repSpan.append(`greatly enjoys`); + r.push(`The audience `, repSpan, ` the depraved spectacle.`); + + repX(50, "pit", winner); + } + + winner.devotion++; + } else if (winner.devotion > 20) { + r.push(`${winner.slaveName} understands ${his} role and thoroughly rapes ${loser.slaveName}.`); + + if (V.pit.audience !== "none") { + repSpan.append(`enjoys`); + r.push(`The audience `, repSpan, ` the spectacle.`); + + repX(25, "pit", winner); + } + } else { + r.push(`${winner.slaveName} is unenthusiastic and just thrusts mechanically, to avoid being punished. ${He} `, App.UI.DOM.makeElement("span", `resents`, ["devotion", + "dec"]), ` having to fight and fuck.`); + + if (V.pit.audience !== "none") { + r.push(`The audience barely pays attention.`); + } + + winner.devotion -= 2; + } + + return r; + } + + function rapeEffects() { + const r = []; + + const {he, his, him, himself, He} = getPronouns(winner); + const {he: he2, him: him2, He: He2} = getPronouns(loser); + + r.push(...winnerEffects(), ...loserEffects()); + + if (loser.fetish !== "masochist" && loser.fetish !== "humiliation" && loser.sexualFlaw !== "self hating" && loser.relationship && loser.relationship < 5 && winner.ID === loser.relationshipTarget) { + r.push(`Fighting and rape have `, App.UI.DOM.makeElement("span", `damaged`, ["relationship", + "dec"]), ` the relationship between the slaves.`); + } + + return r; + + function winnerEffects() { + const r = []; + + if (winner.rivalry && loser.ID === winner.rivalryTarget) { + r.push(`${He} `, App.UI.DOM.makeElement("span", `relishes`, ["devotion", + "inc"]), ` the chance to abuse ${loser.slaveName}, whom ${he} dislikes.`); + + winner.devotion += 5; + } else if (winner.relationship && loser.ID === winner.relationshipTarget) { + if (winner.devotion > 20) { + r.push(`${He} accepts having to abuse ${loser.slaveName}, and plans to make it up to ${him2} later.`); + } else { + r.push(`${He} `, App.UI.DOM.makeElement("span", `hates`, ["devotion", + "dec"]), ` having to abuse ${loser.slaveName}.`); + + winner.devotion -= 10; + } + } else if (areRelated(winner, loser)) { + if (winner.devotion > 20) { + r.push(`${He} accepts having to abuse ${his} ${relativeTerm(winner, loser)}, ${loser.slaveName}, and plans to make it up to ${him2} later.`); + } else { + r.push(`${He} `, App.UI.DOM.makeElement("span", `hates`, ["devotion", + "dec"]), ` having to abuse ${his} ${relativeTerm(winner, loser)}, ${loser.slaveName}.`); + + winner.devotion -= 10; + } + } + + if (winner.fetish === "sadist" && winner.fetishStrength > 90 && winner.sexualFlaw !== "malicious" && winner.devotion > 20) { + r.push(`${He} noticed something while ${he} was raping ${loser.slaveName}; watching the way ${he2} writhed in pain was strangely satisfying, as ${he} was making ${him2} suffer. ${winner.slaveName} cums powerfully at the mere thought; ${he} has become `, App.UI.DOM.makeElement("span", `sexually addicted to inflicting pain and anguish.`, ["noteworthy"])); + + winner.sexualFlaw = "malicious"; + } else if (winner.fetish === "masochist" && winner.fetishStrength > 90 && winner.sexualFlaw !== "self hating" && winner.devotion < 20) { + r.push(`${He} feels horrible after forcing ${himself} on ${loser.slaveName}; ${he} is the one that should suffer, not ${him2}. ${winner.slaveName} has `, App.UI.DOM.makeElement("span", `descended into true self hatred.`, ["noteworthy"])); + + winner.sexualFlaw = "self hating"; + } else if (winner.fetish === "dom" && winner.fetishStrength > 90 && winner.sexualFlaw !== "abusive" && winner.devotion > 20) { + r.push(`${He} noticed something while ${he} was raping ${loser.slaveName}; watching the way ${he2} cowered before ${hasAnyArms(winner) ? `${his} raised palm` : `${him}`} was strangely satisfying, as were the painful moans that accompanied every forceful thrust. ${winner.slaveName} cums powerfully at the mere thought; ${he} has become `, App.UI.DOM.makeElement("span", `sexually abusive, getting off on the thrill of forcing ${himself} on other slaves.`, ["noteworthy"])); + + winner.sexualFlaw = "abusive"; + } else if (winner.behavioralFlaw === "none" && random(1, 100) < 5 && winner.devotion < 20) { + r.push(`${He} begins to act `, App.UI.DOM.makeElement("span", `oddly`, ["flaw", + "gain"]), ` after the fight; having to beat down and rape another slave may have affected ${him} mentally.`); + + winner.behavioralFlaw = "odd"; + } + + return r; + } + + function loserEffects() { + const trustSpan = App.UI.DOM.makeElement("span", `fears`, ["trust", "dec"]); + + const r = []; + + if (loser.sexualFlaw === "self hating") { + r.push(`${loser.slaveName} feels ${he2} `, App.UI.DOM.makeElement("span", `deserved this.`, ["devotion", + "inc"])); + + loser.devotion += 5; + } else if (loser.fetish === "masochist" && loser.fetishStrength > 60 && loser.fetishKnown) { + r.push(`${loser.slaveName} `, App.UI.DOM.makeElement("span", `gets off`, ["devotion", + "inc"]), ` on being beaten and molested.`); + + loser.devotion += 5; + } else if (loser.fetish === "humiliation" && loser.fetishStrength > 60 && loser.fetishKnown) { + r.push(`${loser.slaveName} `, App.UI.DOM.makeElement("span", `enjoys`, ["devotion", + "inc"]), ` the humiliation of being openly beaten and raped.`); + + loser.devotion += 5; + } else if (loser.devotion < 50) { + r.push(`${loser.slaveName} `, App.UI.DOM.makeElement("span", `resents`, ["devotion", + "dec"]), ` being beaten and molested and `, trustSpan, ` that it will happen again.`); + + loser.devotion -= 10; + loser.trust -= 10; + } + + if (loser.rivalry && winner.ID === loser.rivalryTarget) { + r.push(`${He2} is `, App.UI.DOM.makeElement("span", `embarrassed`, ["devotion", + "dec"]), ` by losing to and being raped by ${winner.slaveName}, whom ${he2} dislikes, and `, trustSpan, ` that it will happen again.`); + + loser.devotion -= 10; + loser.trust -= 10; + } else if (loser.relationship && winner.ID === loser.relationshipTarget) { + if (loser.devotion > 20) { + r.push(`${He2} accepts ${winner.slaveName} having to rape ${him2}.`); + } else { + r.push(`${He2} `, App.UI.DOM.makeElement("span", `hates`, ["devotion", + "dec"]), ` having to accept rape from ${winner.slaveName}, and `, trustSpan, ` that it will happen again.`); + + loser.devotion -= 10; + loser.trust -= 10; + } + } else if (areRelated(loser, winner)) { + if (loser.devotion > 20) { + r.push(`${He2} accepts ${his} ${relativeTerm(loser, winner)}, ${winner.slaveName}, having to rape ${him2}, but ${he2} `, trustSpan, ` that it will happen again.`); + + loser.trust -= 10; + } else { + r.push(`${He2} `, App.UI.DOM.makeElement("span", `hates`, ["devotion", + "dec"]), ` having to accept rape from ${his} ${relativeTerm(loser, winner)}, ${winner.slaveName}, and `, trustSpan, ` that it will happen again.`); + + loser.devotion -= 10; + loser.trust -= 10; + } + } + + if (loser.fetish === "masochist" && loser.fetishStrength > 90 && loser.sexualFlaw !== "self hating") { + r.push(`${He2} feels strangely content after being abused and violated; ${he2} is the one that should suffer, after all. ${loser.slaveName} has `, App.UI.DOM.makeElement("span", `descended into true self hatred.`, ["flaw", + "gain"])); + loser.sexualFlaw = "self hating"; + } else if (loser.behavioralFlaw === "none" && random(1, 100) < 5 && loser.devotion < 20) { + r.push(`${He2} begins to act `, App.UI.DOM.makeElement("span", `oddly`, ["flaw", + "gain"]), ` after the fight; losing and getting raped may have affected ${him2} mentally.`); + + loser.behavioralFlaw = "odd"; + } + + return r; + } + } + } + + // Helper Functions + /** @returns {boolean} Returns true if slave1 won */ + function getWinner() { + if (deadliness(slave1).value > deadliness(slave2).value) { + return random(1, 100) > 20; // 80% chance of winning + } else if (deadliness(slave1).value < deadliness(slave2).value) { + return random(1, 100) > 80; // 20% chance of winning + } else if (random(1, 100) > 50) { // 50/50 + return true; + } + + return false; + } + } + + introCombatants(slave1, slave2) { + return [`This fight is between`, App.UI.DOM.slaveDescriptionDialog(slave1), `and`, + App.UI.DOM.combineNodes(contextualIntro(slave1, slave2, true), ".")]; + } +}; diff --git a/src/facilities/pit/fights/1_lethalBodyguard.js b/src/facilities/pit/fights/1_lethalBodyguard.js new file mode 100644 index 0000000000000000000000000000000000000000..a1eba8b9225e9d4dc0880cd20150e619c67f8d56 --- /dev/null +++ b/src/facilities/pit/fights/1_lethalBodyguard.js @@ -0,0 +1,34 @@ +/** Lethal 1v1 between the BG and a random slave. */ +App.Facilities.Pit.Fights.LBg1v1 = class extends App.Facilities.Pit.Fights.LR1v1 { + fightDescription() { + const f = new DocumentFragment(); + f.append("1-vs-1 fight between your bodyguard ", App.UI.DOM.slaveDescriptionDialog(getSlave(this.actors[0])), ` and `, + contextualIntro(getSlave(this.actors[0]), getSlave(this.actors[1]), true), "."); + return f; + } + + get impact() { + return super.impact * 1.1; + } + + fightPrerequisites() { + return [...super.fightPrerequisites(), () => !!S.Bodyguard]; + } + + forcedActors() { + return [S.Bodyguard.ID]; + } + + actorPrerequisites() { + return [ + [] + ]; + } + + introCombatants(slave1, slave2) { + const {his1} = getPronouns(slave1).appendSuffix("1"); + return [`In this fight your bodyguard`, App.UI.DOM.slaveDescriptionDialog(slave1), + `will demonstrate ${his1} combat prowess on`, + App.UI.DOM.combineNodes(contextualIntro(slave1, slave2, true), ".")]; + } +}; diff --git a/src/facilities/pit/fights/1_lethalScheduled.js b/src/facilities/pit/fights/1_lethalScheduled.js new file mode 100644 index 0000000000000000000000000000000000000000..aece15f06fc6b3cbeef425253c7c4265ed037402 --- /dev/null +++ b/src/facilities/pit/fights/1_lethalScheduled.js @@ -0,0 +1,26 @@ +/** Lethal 1v1 between the BG and a random slave. */ +App.Facilities.Pit.Fights.LSch1v1 = class extends App.Facilities.Pit.Fights.LR1v1 { + fightDescription() { + const f = new DocumentFragment(); + f.append("Scheduled: 1-vs-1 fight between ", App.UI.DOM.slaveDescriptionDialog(getSlave(this.actors[0])), ` and `, + contextualIntro(getSlave(this.actors[0]), getSlave(this.actors[1]), true), "."); + return f; + } + + fightPrerequisites() { + return [...super.fightPrerequisites(), () => V.pit.slavesFighting !== null]; + } + + forcedActors() { + return [V.pit.slavesFighting[0], V.pit.slavesFighting[1]]; + } + + actorPrerequisites() { + return []; + } + + execute(node) { + V.pit.slavesFighting = null; + return super.execute(node); + } +}; diff --git a/src/facilities/pit/fights/1_nonLethalBodyguard.js b/src/facilities/pit/fights/1_nonLethalBodyguard.js new file mode 100644 index 0000000000000000000000000000000000000000..f6cc33d05456974a3d379de32b50532c3cdc52df --- /dev/null +++ b/src/facilities/pit/fights/1_nonLethalBodyguard.js @@ -0,0 +1,34 @@ +/** Nonlethal 1v1 between the BG and a random slave. */ +App.Facilities.Pit.Fights.NlBg1v1 = class extends App.Facilities.Pit.Fights.NlR1v1 { + fightDescription() { + const f = new DocumentFragment(); + f.append("1-vs-1 fight between your bodyguard ", App.UI.DOM.slaveDescriptionDialog(getSlave(this.actors[0])), ` and `, + contextualIntro(getSlave(this.actors[0]), getSlave(this.actors[1]), true), "."); + return f; + } + + get impact() { + return super.impact * 1.1; + } + + fightPrerequisites() { + return [...super.fightPrerequisites(), () => !!S.Bodyguard]; + } + + forcedActors() { + return [S.Bodyguard.ID]; + } + + actorPrerequisites() { + return [ + [] + ]; + } + + introCombatants(slave1, slave2) { + const {his1} = getPronouns(slave1).appendSuffix("1"); + return [`In this fight your bodyguard`, App.UI.DOM.slaveDescriptionDialog(slave1), + `will demonstrate ${his1} combat prowess on`, + App.UI.DOM.combineNodes(contextualIntro(slave1, slave2, true), ".")]; + } +}; diff --git a/src/facilities/pit/fights/1_nonLethalScheduled.js b/src/facilities/pit/fights/1_nonLethalScheduled.js new file mode 100644 index 0000000000000000000000000000000000000000..dc53667286d8cd97f824b7670bb13f8a31c6c217 --- /dev/null +++ b/src/facilities/pit/fights/1_nonLethalScheduled.js @@ -0,0 +1,25 @@ +/** Nonlethal 1v1 between the BG and a random slave. */ +App.Facilities.Pit.Fights.NlSch1v1 = class extends App.Facilities.Pit.Fights.NlR1v1 { + fightDescription() { + const f = new DocumentFragment(); + f.append("Scheduled: 1-vs-1 fight between ", App.UI.DOM.slaveDescriptionDialog(getSlave(this.actors[0])), ` and `, + contextualIntro(getSlave(this.actors[0]), getSlave(this.actors[1]), true), "."); + return f; + } + + fightPrerequisites() { + return [...super.fightPrerequisites(), () => V.pit.slavesFighting !== null]; + } + + forcedActors() { + return [V.pit.slavesFighting[0], V.pit.slavesFighting[1]]; + } + + actorPrerequisites() { + return []; + } + execute(node) { + V.pit.slavesFighting = null; + return super.execute(node); + } +}; diff --git a/src/facilities/pit/fights/2_lethalScheduledBG.js b/src/facilities/pit/fights/2_lethalScheduledBG.js new file mode 100644 index 0000000000000000000000000000000000000000..514589c5563e7fc6980459a84bff9efabfa6581b --- /dev/null +++ b/src/facilities/pit/fights/2_lethalScheduledBG.js @@ -0,0 +1,33 @@ +/** Lethal 1v1 between the BG and a random slave. */ +App.Facilities.Pit.Fights.LSchBg1v1 = class extends App.Facilities.Pit.Fights.LBg1v1 { + fightDescription() { + const f = new DocumentFragment(); + f.append("Scheduled: 1-vs-1 fight between your bodyguard ", App.UI.DOM.slaveDescriptionDialog(getSlave(this.actors[0])), ` and `, + contextualIntro(getSlave(this.actors[0]), getSlave(this.actors[1]), true), "."); + return f; + } + + fightPrerequisites() { + return [() => V.pit.slaveFightingBodyguard !== null, ...super.fightPrerequisites()]; + } + + forcedActors() { + return [...super.forcedActors(), V.pit.slaveFightingBodyguard]; + } + + actorPrerequisites() { + return []; + } + + execute(node) { + V.pit.slaveFightingBodyguard = null; + return super.execute(node); + } + + introCombatants(slave1, slave2) { + const {his1} = getPronouns(slave1).appendSuffix("1"); + return [`In this scheduled fight your bodyguard`, App.UI.DOM.slaveDescriptionDialog(slave1), + `will demonstrate ${his1} combat prowess on`, + App.UI.DOM.combineNodes(contextualIntro(slave1, slave2, true), ".")]; + } +}; diff --git a/src/facilities/pit/pit.js b/src/facilities/pit/pit.js index a7f7cadce30943c5f931670b3c05eb81fefbb7f6..922dc56562a0a2cb6a26c718d2839c0e5cec7282 100644 --- a/src/facilities/pit/pit.js +++ b/src/facilities/pit/pit.js @@ -5,11 +5,6 @@ App.Facilities.Pit.pit = function() { const pit = App.Entity.facilities.pit; - if (V.pit.slavesFighting.length !== 2) { - V.pit.slavesFighting = []; - V.pit.fighters = V.pit.fighters !== 4 ? V.pit.fighters : 0; - } - const container = document.createElement("div"); container.append(assemble()); return container; @@ -30,10 +25,15 @@ App.Facilities.Pit.pit = function() { const fightsFrag = new DocumentFragment(); App.UI.DOM.appendNewElement("div", fightsFrag, pitDescription(), ['margin-bottom']); - App.UI.DOM.appendNewElement("div", fightsFrag, pitRules(), ['margin-bottom']); - App.UI.DOM.appendNewElement("div", fightsFrag, pitScheduled(), ['margin-bottom']); App.UI.DOM.appendNewElement("div", fightsFrag, pitSlaves(), ['margin-bottom']); - tabs.addTab("Fights", "fights", fightsFrag); + tabs.addTab("Fighters", "fights", fightsFrag); + + const endWeekFrag = new DocumentFragment(); + App.UI.DOM.appendNewElement("div", endWeekFrag, arenaUpgrades(), ['margin-bottom']); + App.UI.DOM.appendNewElement("div", endWeekFrag, arenaRules(), ['margin-bottom']); + App.UI.DOM.appendNewElement("div", endWeekFrag, arenaScheduledBG(), ['margin-bottom']); + App.UI.DOM.appendNewElement("div", endWeekFrag, arenaScheduledFight(), ['margin-bottom']); + tabs.addTab("Fights", "ew_fights", endWeekFrag); frag.append(tabs.render()); @@ -45,6 +45,46 @@ App.Facilities.Pit.pit = function() { App.UI.DOM.replace(container, assemble()); } + + function arenaScheduledBG() { + const el = document.createDocumentFragment(); + + if (V.pit.slaveFightingBodyguard !== null) { + const bodyguard = V.pit.slaveFightingBodyguard; + + el.append(`You have scheduled ${getSlave(bodyguard).slaveName} to fight your bodyguard to the death this week.`); + + App.UI.DOM.appendNewElement("div", el, App.UI.DOM.link(`Cancel it`, () => { + V.pit.slaveFightingBodyguard = null; + + App.UI.reload(); + }), ['indent']); + } + return el; + } + + function arenaScheduledFight() { + const el = document.createDocumentFragment(); + + if (V.pit.slavesFighting !== null) { + const [slave1, slave2] = V.pit.slavesFighting; + + el.append(`You have scheduled `, App.UI.DOM.slaveDescriptionDialog(getSlave(slave1)), ` and `, + contextualIntro(getSlave(slave1), getSlave(slave2), true), " to fight this week."); + + App.UI.DOM.appendNewElement("div", el, App.UI.DOM.link(`Cancel it`, () => { + V.pit.slavesFighting = null; + + App.UI.reload(); + }), ['indent']); + } else { + el.append(App.UI.DOM.passageLink("Schedule two slaves to fight each other.", "Pit Workaround")); + } + + return el; + } + + function intro() { const el = document.createDocumentFragment(); @@ -185,176 +225,84 @@ App.Facilities.Pit.pit = function() { return el; } - function pitRules() { + function arenaRules() { const el = document.createDocumentFragment(); - const animal = V.active.canine || V.active.hooved || V.active.feline; - /** @type {FC.Facilities.Rule[]} */ - const rules = !V.pit.slaveFightingAnimal && !V.pit.slaveFightingBodyguard// only display if a fight has not been scheduled - ? [{ - property: "audience", prereqs: [], options: [{ - get text() { - return `Fights here are strictly private.`; - }, link: `Closed`, value: 'none', - }, { - get text() { - return `Fights here are free and open to the public.`; - }, link: `Free`, value: 'free', - }, { - get text() { - return `Admission is charged to the fights here.`; - }, link: `Paid`, value: 'paid', - }, ], object: V.pit, - }, { - property: "fighters", prereqs: [], options: [{ - get text() { - return `Two fighters will be selected from the pool at random.`; - }, link: `Slaves`, value: 0, - }, { - get text() { - return `Your bodyguard ${S.Bodyguard.slaveName} will fight a slave selected from the pool at random.`; - }, link: `Bodyguard`, value: 1, prereqs: [!!S.Bodyguard, ], - }, { - get text() { - return `A random slave will fight an animal.`; - }, link: `Animal`, value: 2, prereqs: [!!animal, ], handler: () => { - if (!V.pit.animal) { - V.pit.animal = animal; - } - }, - }, { - get text() { - return `A random slave will fight another slave${S.Bodyguard && !animal ? ` or your bodyguard` : S.Bodyguard ? `, your bodyguard` : ``}${animal ? ` or an animal` : ``}.`; - }, link: `Random`, value: 3, prereqs: [!!S.Bodyguard || !!animal, ], - }, { - get text() { - return `${V.pit.slavesFighting.length > 1 ? `${SlaveFullName(getSlave(V.pit.slavesFighting[0]))} will fight ${SlaveFullName(getSlave(V.pit.slavesFighting[1]))} this week.` : `Two chosen slaves will fight.`}`; - }, link: `Choose slaves`, value: 4, handler: () => { - Engine.play("Pit Workaround"); - - V.pit.slavesFighting = []; - }, - }, ], object: V.pit, - }, { - property: "animal", prereqs: [V.pit.fighters === 2 || V.pit.fighters === 3, !!animal, ], options: [{ - get text() { - return `Your slave will fight ${getAnimal(V.active.canine).articleAn} ${V.active.canine}.`; - }, - get link() { - return capFirstChar(V.active.canine); - }, - value: V.active.canine, - prereqs: [!!V.active.canine, - !["beagle", "French bulldog", "poodle", "Yorkshire terrier"].includes(V.active.canine), ], - }, { - get text() { - return `Your slave will fight ${getAnimal(V.active.hooved).articleAn} ${V.active.hooved}.`; - }, get link() { - return capFirstChar(V.active.hooved); - }, value: V.active.hooved, prereqs: [!!V.active.hooved, ], - }, { - get text() { - return `Your slave will fight ${getAnimal(V.active.feline).articleAn} ${V.active.feline}.`; - }, - get link() { - return capFirstChar(V.active.feline); - }, - value: V.active.feline, - prereqs: [!!V.active.feline, getAnimal(V.active.feline)?.species !== "cat", ], - }, { - get text() { - return `Your slave will fight one of your animals at random.`; - }, - link: `Random`, - value: 'random', - prereqs: [(!!V.active.canine && !!V.active.hooved) || (!!V.active.canine && !!V.active.feline) || (!!V.active.hooved && !!V.active.feline), ], - }, ], object: V.pit, - }, { - property: "lethal", prereqs: [], options: [{ - get text() { - return V.pit.fighters === 2 ? `The fighter will be armed with a sword and will fight to the death.` : `Fighters will be armed with swords, and fights will be to the death.`; - }, link: `Lethal`, value: true, - }, { - get text() { - return V.pit.fighters === 2 ? `The slave will be restrained and will try to avoid becoming the animal's plaything.` : `Fighters will use their fists and feet, and fights will be to submission.`; - }, link: `Nonlethal`, value: false, - }, ], object: V.pit, - }, { - property: "virginities", prereqs: [V.pit.fighters !== 2, !V.pit.lethal, ], options: [{ - get text() { - return `No virginities of the loser will be respected.`; - }, link: `Neither`, value: 'neither', - }, { - get text() { - return `Vaginal virginity of the loser will be respected.`; - }, link: `Vaginal`, value: 'vaginal', - }, { - get text() { - return `Anal virginity of the loser will be respected.`; - }, link: `Anal`, value: 'anal', - }, { - get text() { - return `All virginities of the loser will be respected.`; - }, link: `All`, value: 'all', - }, ], object: V.pit, - }, ] : []; - - if (rules.length > 0 && rules.some(rule => rule.prereqs.every(prereq => prereq === true))) { - App.UI.DOM.appendNewElement("h2", el, `Rules`); - - rules.forEach(rule => { - if (rule.prereqs.every(prereq => prereq === true)) { - const options = new App.UI.OptionsGroup(); - const option = options.addOption(null, rule.property, rule.object || V); - - rule.options.forEach(o => { - if (!o.prereqs || o.prereqs.every(prereq => prereq === true)) { - option.addValue(o.link, o.value); - if (o.handler) { - option.addCallback(o.handler); - } - if (o.note) { - option.addComment(o.note); - } - - if ((rule.object && _.isEqual(rule.object[rule.property], o.value)) || _.isEqual(V[rule.property], o.value)) { - App.UI.DOM.appendNewElement("div", el, o.text); - } - } - }); - - App.UI.DOM.appendNewElement("div", el, options.render(), ['margin-bottom']); - } - - if (rule.nodes) { - App.Events.addNode(el, rule.nodes); - } - }); + const options = new App.UI.OptionsGroup(); + + options.addOption("Host fights once a week", "active", V.pit) + .addValue("Yes", true).on() + .addValue("No", false).off(); + + let option = options.addOption("Audience", "audience", V.pit) + .addValue("Closed", "none") + .addValue("Free", "free") + .addValue("Paid", "paid"); + if (V.pit.audience === "none") { + option.addComment(`Fights here are strictly private.`); + } else if (V.pit.audience === "free") { + option.addComment(`Fights here are free and open to the public.`); + } else if (V.pit.audience === "paid") { + option.addComment(`Admission is charged to the fights here.`); } - return el; - } - - function pitScheduled() { - const el = document.createDocumentFragment(); - - if (V.pit.slaveFightingAnimal || V.pit.slaveFightingBodyguard) { - const animal = V.pit.slaveFightingAnimal; - const bodyguard = V.pit.slaveFightingBodyguard; - - el.append(`You have scheduled ${getSlave(animal || bodyguard).slaveName} to fight ${V.pit.slaveFightingAnimal ? `an animal` : `your bodyguard`} to the death this week.`); + options.addOption("Lethal fights", "lethal", V.pit) + .addValue("Forbidden", 0).addValue("Allowed", 1).addValue("Always", 2); - App.UI.DOM.appendNewElement("div", el, App.UI.DOM.link(`Cancel it`, () => { - V.pit.slaveFightingAnimal = null; - V.pit.slaveFightingBodyguard = null; + options.addOption("Respect virginities", "virginities", V.pit) + .addValue("All", "all").addValue("Vaginal", "vaginal") + .addValue("Anal", "anal").addValue("None", "none"); - App.UI.reload(); - }), ['indent']); - } + App.UI.DOM.appendNewElement("div", el, options.render(), ['margin-bottom']); return el; } + function arenaUpgrades() { + const f = new DocumentFragment(); + f.append((new App.Upgrade("seats", [ + { + value: 0, + upgraded: 1, + text: "Around the pit is an small space to watch the fights from.", + link: "Add some proper stands.", + cost: 5000 * V.upgradeMultiplierArcology + }, + { + value: 1, + upgraded: 2, + text: "Surrounding the pit are small stands to watch the fights from.", + link: "Replace the stands with big, permanent ones.", + cost: 20000 * V.upgradeMultiplierArcology + }, + { + value: 2, + text: "The pit is encased by large stands offering a large space to watch the fights from.", + } + ], V.pit)).render()); + f.append((new App.Upgrade("fightsBase", [ + { + value: 0, + upgraded: 1, + text: "Next to the pit is a small room for your slaves to store their equipment in.", + link: "Add a room for the slaves to wait in between fights.", + cost: 5000 * V.upgradeMultiplierArcology + }, + { + value: 1, + upgraded: 2, + text: "Around the arena are some rooms for your slaves to wait and store their equipment in between fights", + link: "Improve the equipment and add spaces for the slaves to prepare for their fights.", + cost: 20000 * V.upgradeMultiplierArcology + }, + { + value: 2, + text: "Around the pit are various facilities for your slaves to rest and prepare in between fights. High quality equipment is available in multiple store rooms", + } + ], V.pit)).render()); + return f; + } + function arenaSlaves() { const el = document.createDocumentFragment(); diff --git a/src/facilities/pit/pitFightList.js b/src/facilities/pit/pitFightList.js new file mode 100644 index 0000000000000000000000000000000000000000..32d2acb4e78576b3238a695c1aaaea1467500731 --- /dev/null +++ b/src/facilities/pit/pitFightList.js @@ -0,0 +1,27 @@ +/** + * Gives all pit fights + * @returns {Array<App.Facilities.Pit.Fights.BaseFight>} + */ +App.Facilities.Pit.getFights = function() { + return [ + // new App.Facilities.Pit.Fights.TestFight(), + new App.Facilities.Pit.Fights.NlR1v1(), + new App.Facilities.Pit.Fights.NlBg1v1(), + new App.Facilities.Pit.Fights.LR1v1(), + new App.Facilities.Pit.Fights.LBg1v1(), + new App.Facilities.Pit.Fights.LSchBg1v1(), + new App.Facilities.Pit.Fights.LSch1v1(), + new App.Facilities.Pit.Fights.NlSch1v1(), + ]; +}; +/** + * Gives all pit fights as a Mmap + * @returns {Map<string, App.Facilities.Pit.Fights.BaseFight>} + */ +App.Facilities.Pit.getFightsMap = function() { + const m = new Map(); + for (const f of App.Facilities.Pit.getFights()) { + m.set(f.key, f); + } + return m; +}; diff --git a/src/facilities/pit/pitUtils.js b/src/facilities/pit/pitUtils.js index fc3fe4d9d4707abb2423c300a23a7921781ae0c7..b42689e232e204aa180958fb371b2a441044d719 100644 --- a/src/facilities/pit/pitUtils.js +++ b/src/facilities/pit/pitUtils.js @@ -6,17 +6,17 @@ App.Facilities.Pit.init = function() { trainingIDs: [], // Pit section - animal: null, + active:false, audience: "free", - bodyguardFights: false, decoration: "standard", - fighters: 0, fighterIDs: [], fought: false, - lethal: false, - slaveFightingAnimal: null, + fightsBase: 0, + seats: 0, + lethal: 0, + virginities: "all", + minimumHealth: true, slaveFightingBodyguard: null, - slavesFighting: [], - virginities: "neither", + slavesFighting: null }; }; diff --git a/src/facilities/pit/pitWorkaround.js b/src/facilities/pit/pitWorkaround.js index f50a29b5f2ade0979473dba1abfa4cc9e9af5445..f0a40dcb0a08232190beb1e9967bc8f2239dbecd 100644 --- a/src/facilities/pit/pitWorkaround.js +++ b/src/facilities/pit/pitWorkaround.js @@ -1,32 +1,51 @@ App.Facilities.Pit.workaround = function() { - const frag = new DocumentFragment(); + const f = new DocumentFragment(); - const slaveOne = getSlave(V.pit.slavesFighting[0]); - const slaveTwo = getSlave(V.pit.slavesFighting[1]); + // Warning: + // In this passage the V.pit.slavesFighting array is constructed. This means that during this passage the length of + // the array cannot be assumed to be 2. Once leaving this passage it has to be ensured that V.pit.slavesFighting is + // in a valid state first. + if (V.pit.slavesFighting === null) { + // @ts-ignore + V.pit.slavesFighting = []; + } + + V.passageSwitchHandler = passageHandler; + + const slave1 = getSlave(V.pit.slavesFighting[0]); + const slave2 = getSlave(V.pit.slavesFighting[1]); - frag.append( + f.append( App.UI.DOM.makeElement("h1", `Slaves Fighting`), App.UI.DOM.makeElement("div", V.pit.slavesFighting.length > 1 - ? `${slaveOne.slaveName} is fighting ${slaveTwo.slaveName} this week.` + ? `${slave1.slaveName} is fighting ${slave2.slaveName} this week.` : `Choose two slaves to fight.`), App.UI.DOM.makeElement("h2", `Choose slaves`, ['margin-top']), ); - for (const slave of V.pit.fighterIDs) { - if (V.pit.slavesFighting.includes(slave)) { - App.UI.DOM.appendNewElement("div", frag, SlaveFullName(getSlave(slave)), ['indent']); + for (const slave of V.slaves) { + if (V.pit.slavesFighting.includes(slave.ID)) { + App.UI.DOM.appendNewElement("div", f, SlaveFullName(slave), ['indent']); } else { - App.UI.DOM.appendNewElement("div", frag, App.UI.DOM.link(SlaveFullName(getSlave(slave)), () => { + App.UI.DOM.appendNewElement("div", f, App.UI.DOM.link(SlaveFullName(slave), () => { if (V.pit.slavesFighting.length > 1) { V.pit.slavesFighting.shift(); } - V.pit.slavesFighting.push(slave); + V.pit.slavesFighting.push(slave.ID); + V.passageSwitchHandler = () => { }; App.UI.reload(); + V.passageSwitchHandler = passageHandler; }), ['indent']); } } - return frag; + function passageHandler() { + if (V.pit.slavesFighting.length !== 2) { + V.pit.slavesFighting = null; + } + } + + return f; }; diff --git a/src/js/assignJS.js b/src/js/assignJS.js index 4f1409dcbd4d084bc9c4c0509a6be2e9eebfbd86..12d17a3cfb0bfdbd589c26488492e803dcf4b243 100644 --- a/src/js/assignJS.js +++ b/src/js/assignJS.js @@ -384,7 +384,9 @@ globalThis.assignJob = function(slave, job) { if (V.dojo > 1) { slave.rules.living = LivingRule.LUXURIOUS; } - if (V.pit && V.pit.bodyguardFights && V.pit.fighterIDs.includes(slave.ID)) { V.pit.fighterIDs.delete(slave.ID); } + if (V.pit) { + V.pit.fighterIDs.delete(slave.ID); + } break; case Job.AGENT.toLowerCase(): diff --git a/src/npc/interaction/killSlave.js b/src/npc/interaction/killSlave.js index a891216f4af44f6beb64e3b105295cf662f1afcd..d703c7a983b19b1948d2589721c7c59dbf771f87 100644 --- a/src/npc/interaction/killSlave.js +++ b/src/npc/interaction/killSlave.js @@ -773,90 +773,19 @@ App.UI.SlaveInteract.killSlave = function(slave) { if (slave.skill.combat > 30) { reactionText = `${He} nods ${his} head and straightens up, as though mentally preparing ${himself} for the fight for ${his} life.`; } - - if (V.animals.canine.length || V.animals.hooved.length || V.animals.feline.length) { - combatDiv.append(animals()); - } else { - combatDiv.append(bodyguard()); - } + combatDiv.append(bodyguard()); return combatDiv; - function animals() { - const subDiv = document.createElement("div"); - const linksDiv = App.UI.DOM.makeElement("div", null, ['kill-slave-options']); - - const links = []; - - const moreThanOneAnimal = V.active.canine && V.active.hooved || - V.active.canine && V.active.feline || - V.active.hooved && V.active.feline; - - let activeAnimal; - - if (!moreThanOneAnimal) { - if (V.active.canine) { - activeAnimal = V.active.canine; - } else if (V.active.hooved) { - activeAnimal = V.active.hooved; - } else { - activeAnimal = V.active.feline; - } - } - - subDiv.append(`You tell ${him} you'll give ${him} the chance to win ${his} life in combat – if ${he} wants to live, ${he} can either fight your bodyguard or one of your beasts.`); - - links.push(App.UI.DOM.link(`Have ${him} fight ${S.Bodyguard.slaveName}`, () => { - V.pit.slaveFightingBodyguard = slave.ID; - V.pit.slaveFightingAnimal = null; - V.pit.lethal = true; - V.pit.animal = null; - - App.UI.DOM.replace(linksDiv, `It's decided. ${He} will fight your bodyguard, ${S.Bodyguard.slaveName}. ${reactionText}`); - })); - - if (V.active.canine && getAnimal(V.active.canine).species === "dog" && !V.active.hooved && !V.active.feline) { - links.push(App.UI.DOM.disabledLink(`Have ${him} fight one of your animals`, [`A dog isn't a proper challenge.`])); - } else if (V.active.feline && getAnimal(V.active.feline).species === "cat" && !V.active.hooved && !V.active.canine) { - links.push(App.UI.DOM.disabledLink(`Have ${him} fight one of your animals`, [`Housecats are much too small to fight.`])); - } else { - links.push(App.UI.DOM.link(`Have ${him} fight one of your animals`, () => { - const animalOptions = []; - - if (V.active.canine && getAnimal(V.active.canine).species !== "dog") { - animalOptions.push(V.active.canine); - } - if (V.active.hooved) { - animalOptions.push(V.active.hooved); - } - if (V.active.feline && getAnimal(V.active.feline).species !== "cat") { - animalOptions.push(V.active.feline); - } - - V.pit.slaveFightingAnimal = slave.ID; - V.pit.slaveFightingBodyguard = null; - V.pit.lethal = true; - V.pit.animal = animalOptions.random(); - - App.UI.DOM.replace(linksDiv, `It's decided. ${He} will fight ${moreThanOneAnimal ? ` one of your animals.` : ` your ${activeAnimal.species}.`} ${reactionText}`); - })); - } - - App.UI.DOM.appendNewElement("div", linksDiv, App.UI.DOM.generateLinksStrip(links)); - - subDiv.append(linksDiv); - - return subDiv; - } - function bodyguard() { const subDiv = document.createElement("div"); subDiv.append(`You tell ${him} you'll let your bodyguard decide ${his} fate — if ${he} wants to live, ${he}'ll have to beat ${S.Bodyguard.slaveName} in hand-to-hand combat in ${V.pit.name}. `, reactionText); V.pit.slaveFightingBodyguard = slave.ID; - V.pit.lethal = true; - V.pit.animal = null; + if (V.pit.lethal === 0) { + V.pit.lethal = 1; + } return subDiv; }