Skip to content
Snippets Groups Projects
slaveSummaryHelpers.js 46.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • // WARNING This file defines objects referenced in slaveSummaryWidgets.js.
    // Either keep this file above the slaveSummaryWidgets.js in the name ordered list
    // (tweego process them in this order), or rework the references.
    
    /* eslint-disable camelcase */
    
    App.UI.SlaveSummaryImpl = function() {
    
    	const data = App.Data.SlaveSummary;
    
    
    	const helpers = function() {
    		/**
    		 * @param {HTMLElement} element
    		 * @param {string|string[]} [classNames]
    		 */
    		function _addClassNames(element, classNames) {
    			if (classNames != undefined) { /* eslint-disable-line eqeqeq */
    				if (Array.isArray(classNames)) {
    					element.classList.add(...classNames);
    				} else {
    					element.classList.add(classNames);
    				}
    			}
    		}
    
    		/**
    		 * @param {Node} container
    		 * @param {string} text
    		 * @param {string|string[]} [classNames]
    		 * @param {boolean} [stdDecor=false]
    		 * @param {number} [value]
    		 */
    		function makeSpan(container, text, classNames, stdDecor = false, value) {
    			let r = document.createElement("span");
    			_addClassNames(r, classNames);
    			if (value != undefined && V.summaryStats) { /* eslint-disable-line eqeqeq */
    				text += `[${value}]`;
    			}
    
    			r.textContent = stdDecor ? `${capFirstChar(text)}. ` : text + ' ';
    
    			if (container) {
    				container.appendChild(r);
    			}
    			return r;
    		}
    
    		/**
    		 * @param {Node} container
    		 * @param {string} text
    		 * @returns {Text}
    		 */
    		function addText(container, text) {
    			const r = document.createTextNode(text);
    			if (container) {
    				container.appendChild(r);
    			}
    			return r;
    		}
    
    		/**
    		 * @param {Node} [container]
    		 * @param {string|string[]} [classNames]
    		 */
    		function makeBlock(container, classNames) {
    			let r = document.createElement("span");
    			r.classList.add("ssb");
    			_addClassNames(r, classNames);
    			if (container) {
    				container.appendChild(r);
    			}
    			return r;
    		}
    
    		/**
    		 * @param {Node} container
    		 * @param {string|string[]} [classNames]
    		 * @returns {HTMLParagraphElement}
    		 */
    		function makeParagraph(container, classNames) {
    			let r = document.createElement("p");
    			r.classList.add("si");
    			_addClassNames(r, classNames);
    			if (container) {
    				container.appendChild(r);
    			}
    			return r;
    		}
    
    		/**
    		 * @param {object} dict
    		 * @param {*} value
    		 * @param {*} [defaultValue]
    		 * @returns {*|null}
    		 */
    		function getExactRating(dict, value, defaultValue = null) {
    			const res = dict[value];
    			return res ? res : defaultValue;
    		}
    
    		/**
    		 * @param {object} ratings
    		 * @param {number} value
    		 * @returns {*|null}
    		 */
    		function getNumericRating(ratings, value) {
    
    			for (const key in ratings) {
    				if (parseInt(key) >= value) {
    					return ratings[key];
    
    			return null;
    		}
    
    		/**
    		 * @param {object} ratings
    		 * @param {number[]} values
    		 * @returns {*|null}
    		 */
    		function getMultiNumericRating(ratings, values) {
    			const firstRating = getNumericRating(ratings, values[0]);
    			if (firstRating === null || typeof firstRating === "string" || firstRating.hasOwnProperty("desc")) {
    				return firstRating;
    			}
    			return getMultiNumericRating(firstRating, values.slice(1));
    
    		/**
    		 * @typedef {object} StyledDesc
    		 * @property {string} desc
    		 * @property {string|string[]} [style]
    		 */
    
    		/** @typedef {Object.<string, StyledDesc>} StyledRatings */
    		/**
    		 * @param {Node} container
    		 * @param {StyledRatings} ratings
    		 * @param {number} [value]
    		 * @param {number} [offset] value offset in the ratings dictionary (to eliminate negative values)
    		 * @param {boolean} [stdDecor=false]
    		 */
    		function makeRatedStyledSpan(container, ratings, value, offset = 0, stdDecor = false) {
    			/** @type {StyledDesc} */
    			const d = getNumericRating(ratings, value + offset);
    
    			if (d) {
    				makeSpan(container, d.desc, d.style, stdDecor, value);
    			}
    		}
    
    		/**
    		 * @param {Node} container
    		 * @param {StyledDesc} styledDesc
    		 * @param {number} [value]
    		 * @param {boolean} [stdDecor]
    		 */
    		function makeStyledSpan(container, styledDesc, value, stdDecor = false) {
    			if (styledDesc) {
    				makeSpan(container, styledDesc.desc, styledDesc.style, stdDecor, value);
    			}
    
    		}
    
    		/**
    		 * @param {Node} container
    		 * @param {StyledRatings} ratings
    		 * @param {string|number} value
    		 */
    
    		function makeMappedStyledSpan(container, ratings, value) {
    
    			const d = ratings[value];
    			if (d) {
    				makeSpan(container, d.desc, d.style);
    			}
    		}
    
    		/**
    		 * @param {Node} container
    		 * @param {Object.<string, string>} ratings
    		 * @param {string|number} value
    		 * @param {string|string[]} [classNames]
    		 */
    		function makeMappedSpan(container, ratings, value, classNames) {
    			const d = ratings[value];
    			if (d) {
    				makeSpan(container, d, classNames);
    			}
    		}
    
    		/**
    		 * Returns first three string characters with the first one uppercased (string -> Str)
    		 * @param {string} s
    		 * @returns {string}
    		 */
    		function firstThreeUc(s) {
    			return s.charAt(0).toUpperCase() + s.charAt(1) + s.charAt(2);
    		}
    
    
    		/**
    		 * @param {FC.ArcologyState} arcology
    		 */
    
    		function syncFSData(arcology) {
    			arcology = arcology || V.arcologies[0];
    			for (const fsp of App.Data.misc.FutureSocieties) {
    
    				/** @type {FC.FSPolicyValue} */
    
    				const policy = arcology[fsp];
    				const p = fsp.slice(2);
    				FSData.policy[p] = {
    					active: policy === "unset" ? 0 : 1,
    					strength: Math.trunc(policy === "unset" ? 0: policy / 10)
    				};
    			}
    
    			const dislikeBigButts = (FSData.policy.TransformationFetishist.strength < 2) && (FSData.policy.HedonisticDecadence.strength < 2) && (FSData.policy.AssetExpansionist.strength < 2) && (arcology.FSIntellectualDependencyLawBeauty === 0);
    			FSData.bigButts = dislikeBigButts ? -1 : 0;
    
    		/**
    		 * @typedef {Object} FSDatum
    		 * @property {number} active FS policy is active (0,1)
    		 * @property {number} strength FS decoration level divided by 10
    		 */
    		const FSData = {
    			/** @type {Object.<string, FSDatum>} */
    			policy: {},
    			bigButts: 0,
    		};
    
    		/**
    		 *
    		 * @param {ParentNode} container
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {string} text
    		 * @returns {HTMLSpanElement}
    		 */
    		function referenceSlaveWithPreview(container, slave, text) {
    			const res = App.UI.DOM.appendNewElement("span", container, text, "textWithTooltip");
    			const tooltip = App.UI.DOM.appendNewElement("span", res, undefined, "tooltip");
    			tooltip.append(App.UI.DOM.generateLinksStrip([
    				App.UI.DOM.slaveDescriptionDialog(slave, "Pop-up", {eventDescription: false, noArt: true}),
    				App.UI.SlaveList.SlaveInteract.stdInteract(slave, "Go to")
    			]));
    			return res;
    		}
    
    		const longFamilyBits = {
    			and: " and ",
    			makeBit: s => s + '.',
    			daughters10: "Has tons of daughters.",
    			daughters5: "Has many daughters.",
    			daughters1: "Has several daughters.",
    			sisters10: "One of many sisters.",
    			sisters5:  "Has many sisters.",
    			sisters1: "Has several sisters.",
    			emotionBind: "Emotionally bonded to you.",
    			emotionSlut: "Emotional slut."
    		};
    
    		const shortFamilyBits = {
    			and: " & ",
    			makeBit: s => s,
    			daughters10: "tons of daughters",
    			daughters5: "many daughters",
    			daughters1: "has daughters",
    			sisters10: "One of many sisters.",
    			sisters5:  "Has many sisters.",
    			sisters1: "Has several sisters.",
    			emotionBind: "E Bonded",
    			emotionSlut: "E Slut"
    		};
    
    		/**
    		 * @param {Node} container
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {boolean} short
    		 */
    		function renderFamily(container, slave, short) {
    			let handled = 0;
    			const bits = short ? shortFamilyBits : longFamilyBits;
    			const block = makeBlock();
    			const cssClassName = "lightgreen";
    			if (slave.mother > 0) {
    				const _ssj = V.slaves.find(s => s.ID === slave.mother);
    				if (_ssj) {
    					helpers.referenceSlaveWithPreview(block, _ssj, SlaveFullName(_ssj));
    					addText(block, "'s ");
    					let spanText = getPronouns(slave).daughter;
    					if (slave.relationshipTarget === _ssj.ID) {
    						spanText += `${bits.and}${relationshipTerm(slave)}`;
    						handled = 1;
    					}
    					makeSpan(block, bits.makeBit(spanText), cssClassName);
    				}
    			} else if (slave.mother === -1) {
    				addText(block, `Your `);
    				if (slave.relationship < -1) {
    					makeSpan(block, bits.makeBit(`${getPronouns(slave).daughter}${bits.and}${PCrelationshipTerm(slave)}`), cssClassName);
    					handled = 1;
    				} else {
    					makeSpan(block, bits.makeBit(getPronouns(slave).daughter), cssClassName);
    				}
    			} else if (slave.mother in V.missingTable && V.showMissingSlavesSD && V.showMissingSlaves) {
    				addText(block, `${V.missingTable[slave.mother].fullName}'s `);
    				makeSpan(block, bits.makeBit(getPronouns(slave).daughter), cssClassName);
    			}
    			if (slave.father > 0 && slave.father !== slave.mother) {
    				const _ssj = V.slaves.find(s => s.ID === slave.father);
    				if (_ssj) {
    					helpers.referenceSlaveWithPreview(block, _ssj, SlaveFullName(_ssj));
    					addText(block, "'s ");
    					let spanText = getPronouns(slave).daughter;
    					if (slave.relationshipTarget === _ssj.ID) {
    						spanText += `${bits.and}${relationshipTerm(slave)}`;
    						handled = 1;
    					}
    					makeSpan(block, bits.makeBit(spanText), cssClassName);
    				}
    			} else if (slave.father === -1 && slave.father !== slave.mother) {
    				addText(block, `Your `);
    				if (slave.relationship < -1) {
    					makeSpan(block, bits.makeBit(`${getPronouns(slave).daughter}${bits.and}${PCrelationshipTerm(slave)}`), cssClassName);
    					handled = 1;
    				} else {
    					makeSpan(block, bits.makeBit(getPronouns(slave).daughter), cssClassName);
    				}
    			} else if (slave.father in V.missingTable && slave.father !== slave.mother && V.showMissingSlavesSD && V.showMissingSlaves) {
    				addText(block, `${V.missingTable[slave.father].fullName}'s `);
    				makeSpan(block, bits.makeBit(getPronouns(slave).daughter), cssClassName);
    			}
    			if (areSisters(V.PC, slave) > 0) {
    				addText(block, `Your `);
    				if (slave.relationship < -1) {
    					makeSpan(block, bits.makeBit(`${relativeTerm(V.PC, slave)}${bits.and}${PCrelationshipTerm(slave)}`), cssClassName);
    					handled = 1;
    				} else {
    					makeSpan(block, bits.makeBit(relativeTerm(V.PC, slave)), cssClassName);
    				}
    			}
    			if (slave.daughters === 1) {
    				const _ssj = V.slaves.find(s => s.mother === slave.ID || s.father === slave.ID);
    				if (_ssj) {
    					helpers.referenceSlaveWithPreview(block, _ssj, SlaveFullName(_ssj));
    					addText(block, "'s ");
    					let spanText = relativeTerm(_ssj, slave);
    					if (slave.relationshipTarget === _ssj.ID) {
    						spanText += `${bits.and}${relationshipTerm(slave)}`;
    						handled = 1;
    					}
    					makeSpan(block, bits.makeBit(spanText), cssClassName);
    				}
    			} else if (slave.daughters > 1) {
    				if (slave.daughters > 10) {
    					makeSpan(block, bits.daughtersGt10, cssClassName);
    				} else if (slave.daughters > 5) {
    					makeSpan(block, bits.daughtersGt5, cssClassName);
    				} else {
    					makeSpan(block, bits.daughtersGt1, cssClassName);
    				}
    			}
    			if (slave.sisters === 1) {
    				const _ssj = V.slaves.find(s => areSisters(s, slave) > 0);
    				if (_ssj) {
    					helpers.referenceSlaveWithPreview(block, _ssj, SlaveFullName(_ssj));
    					addText(block, "'s ");
    					let spanText = getPronouns(slave).sister;
    					if (slave.relationshipTarget === _ssj.ID) {
    						spanText += `${bits.and}${relationshipTerm(slave)}`;
    						handled = 1;
    					}
    					makeSpan(block, bits.makeBit(spanText), cssClassName);
    				}
    			} else if (slave.sisters > 1) {
    				if (slave.sisters > 10) {
    					makeSpan(block, bits.sisters10, cssClassName);
    				} else if (slave.sisters > 5) {
    					makeSpan(block, bits.sisters5, cssClassName);
    				} else {
    					makeSpan(block, bits.sisters1, cssClassName);
    				}
    			}
    			if (slave.relationship > 0 && handled !== 1) {
    				const _ssj = V.slaves.find(s => s.ID === slave.relationshipTarget);
    				if (_ssj) {
    					helpers.referenceSlaveWithPreview(block, _ssj, SlaveFullName(_ssj));
    					addText(block, "'s ");
    					makeSpan(block, bits.makeBit(relationshipTerm(slave)), cssClassName);
    				}
    			} else if (slave.relationship === -3 && !areRelated(V.PC, slave)) {
    				makeSpan(block, bits.makeBit(`Your ${getPronouns(slave).wife}`), cssClassName);
    			} else if (slave.relationship === -2) {
    				makeSpan(block, bits.emotionBind, cssClassName);
    			} else if (slave.relationship === -1) {
    				makeSpan(block, bits.emotionSlut, cssClassName);
    			}
    
    			if (block.textContent.length > 0) {
    				container.appendChild(block);
    			}
    		}
    
    
    		return {
    
    			addText,
    			makeSpan,
    			makeBlock,
    			makeParagraph,
    			getExactRating,
    			getNumericRating,
    			getMultiNumericRating,
    			makeStyledSpan,
    			makeRatedStyledSpan,
    			makeMappedStyledSpan,
    			makeMappedSpan,
    			firstThreeUc,
    			syncFSData,
    			FSData,
    			referenceSlaveWithPreview,
    			renderFamily
    
    		};
    	}();
    
    	const bits = function() {
    		const addText = helpers.addText;
    		const makeSpan = helpers.makeSpan;
    		const makeBlock = helpers.makeBlock;
    
    		const makeRatedStyledSpan = helpers.makeRatedStyledSpan;
    
    
    		/**
    		 * Returns index in the hips-ass rating table
    		 * @param {App.Entity.SlaveState} slave
    		 * @returns {number} 0 if butt is considered too small, 1 is too big, -1 otherwise.
    		 */
    		function hipsAssRating(slave) {
    			const FSData = helpers.FSData;
    			if (slave.hips < -1) {
    				if (slave.butt > 2 && FSData.bigButts <= 0) {
    					return 1;
    				}
    			} else if (slave.hips < 0) {
    				if (slave.butt > 4 && FSData.bigButts <= 0) {
    					return 1;
    				}
    			} else if (slave.hips > 2) {
    				if (slave.butt <= 8) {
    					return 0;
    				}
    			} else if (slave.hips > 1) {
    				if (slave.butt <= 3 && (FSData.policy.SlimnessEnthusiast.active === 0 || (slave.boobs >= 500))) {
    					return 0;
    				}
    			} else if (slave.hips > 0) {
    				if (slave.butt > 8) {
    					if (FSData.bigButts <= 0) {
    						return 1;
    					}
    				} else if (slave.butt <= 2 && ((FSData.policy.SlimnessEnthusiast.active === 0) || (slave.boobs >= 500))) {
    					return 0;
    				}
    			} else {
    				if (slave.butt > 6) {
    					if (FSData.bigButts <= 0) {
    						return 1;
    					}
    				} else if (slave.butt <= 1 && (FSData.policy.SlimnessEnthusiast.active === 0 || (slave.boobs >= 500))) {
    					return 0;
    				}
    			}
    			return -1;
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    
    		 * @param {FC.Data.SlaveSummary.SmartPiercing} spData
    
    		 * @returns {string}
    		 */
    		function smartFetishStr(slave, spData) {
    			if (slave.fetishKnown === 1) {
    				if (slave.clitSetting === "off") {
    					return spData.setting.off;
    				} else if ((slave.energy <= 95) && (slave.clitSetting === "all")) {
    					return spData.setting.all;
    				} else if ((slave.energy > 5) && (slave.clitSetting === "none")) {
    					return spData.setting.none;
    				} else if (((slave.fetish !== "none") && (slave.clitSetting === "vanilla"))) {
    					return spData.setting.vanilla;
    				} else if (slave.fetishStrength <= 95 || slave.fetish !== slave.clitSetting) {
    					const s = spData.setting[slave.clitSetting];
    					if (s) {
    						return s;
    					}
    				}
    				if (!["anti-men", "anti-women", "men", "women"].includes(slave.clitSetting)) {
    					return spData.setting.monitoring;
    				}
    			} else {
    
    svornost's avatar
    svornost committed
    				return spData.setting[slave.clitSetting];
    
    			}
    			return null;
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    
    		 * @param {FC.Data.SlaveSummary.SmartPiercing} spData
    
    		 * @returns {string}
    		 */
    		function smartAttractionStr(slave, spData) {
    			const sps = spData.setting;
    			const cs = slave.clitSetting;
    			if (slave.attrKnown === 1) {
    				switch (cs) {
    					case "women":
    						if (slave.attrXX < 95) {
    							return sps.women;
    						} else {
    							return sps.monitoring;
    						}
    
    svornost's avatar
    svornost committed
    					case "men":
    						if (slave.attrXY < 95) {
    							return sps.men;
    						} else {
    							return sps.monitoring;
    						}
    					case "anti-women":
    						if (slave.attrXX > 0) {
    							return sps["anti-women"];
    						} else {
    							return sps.monitoring;
    						}
    					case "anti-men":
    						if (slave.attrXY > 0) {
    							return sps["anti-men"];
    						} else {
    							return sps.monitoring;
    						}
    
    				}
    			} else {
    				switch (cs){
    					// fall-through
    					case "women":
    					case "men":
    					case "anti-women":
    					case "anti-men":
    						return sps[cs];
    				}
    			}
    			return null;
    		}
    
    
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_health(slave, c) {
    			if (slave.health.health < -20) {
    				makeSpan(c, "H", ["red", "strong"], true, slave.health.health);
    			} else if (slave.health.health <= 20) {
    				makeSpan(c, "H", ["yellow", "strong"], true, slave.health.health);
    			} else if (slave.health.health > 20) {
    				makeSpan(c, "H", ["green", "strong"], true, slave.health.health);
    			}
    			if (passage() === "Clinic" && V.clinicUpgradeScanner) {
    				if (slave.chem > 15) {
    					makeSpan(c, `C${Math.ceil(slave.chem / 10)}`, ["cyan", "strong"]);
    
    				} else if (slave.chem <= 15 && slave.assignment === Job.CLINIC) {
    
    					makeSpan(c, `CSafe`, ["green", "strong"]);
    				}
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_illness(slave, c) {
    
    			if (slave.health.illness > 2) {
    
    				makeSpan(c, `Ill${slave.health.illness}`, ["red", "strong"], true, slave.health.illness);
    			} else if (slave.health.illness > 0) {
    				makeSpan(c, `Ill${slave.health.illness}`, ["yellow", "strong"], true, slave.health.illness);
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_tired(slave, c) {
    
    			helpers.makeRatedStyledSpan(c, data.short.health.tiredness, slave.health.tired, 0, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_health(slave, c) {
    
    			helpers.makeRatedStyledSpan(c, data.long.health.health, slave.health.health, 100, true);
    
    			if (passage() === "Clinic" && V.clinicUpgradeScanner) {
    				if (slave.chem > 15) {
    					makeSpan(c, `Carcinogen buildup: ${Math.ceil(slave.chem / 10)}.`, "cyan");
    
    				} else if (slave.chem <= 15 && slave.assignment === Job.CLINIC) {
    
    					makeSpan(c, `Safe chem levels.`, "green");
    				}
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_illness(slave, c) {
    
    			makeRatedStyledSpan(c, data.long.health.illness, slave.health.illness, 0, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_tired(slave, c) {
    
    			makeRatedStyledSpan(c, data.long.health.tiredness, slave.health.tired, 0, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_age(slave, c) {
    
    			const style = "pink";
    			makeSpan(c, V.showAgeDetail ? `Age ${slave.actualAge}` : helpers.getNumericRating(data.long.body.age, slave.actualAge), style, true);
    
    			/*
    			 ** No NCS, then do the standard, However because of the wrinkles of Incubators, as long as visual age is greater
    			 ** than or equal to physical age, we do the old physical body/Looks for fresh out of the can NCS slaves.
    			 */
    			if (((slave.geneMods.NCS === 0) || (slave.visualAge >= slave.physicalAge))) {
    				if (slave.actualAge !== slave.physicalAge) {
    
    					makeSpan(c, `${slave.physicalAge} year old body`, style, true);
    
    				}
    				if (slave.visualAge !== slave.physicalAge) {
    
    					makeSpan(c, `Looks ${slave.visualAge}`, style, true);
    
    				}
    			} else {
    				/*
    				 ** Now the rub. The use of physical Age for the year old body above, basically conflicts with the changes
    				 ** that NCS introduces, so here to *distinguish* the changes, we use visual age with the 'year old body'
    				 ** and appears, for example: Slave release from incubator at age 10, Her summary would show, 'Age 0. 10
    				 ** year old body.' But if she's given NCS a few weeks after release, while she's still before her first
    				 ** birthday, it'll appear the same. But once her birthday fires, if we ran with the above code it would
    				 ** say: 'Age 1. 11 year old body.' -- this conflicts with the way NCS works though, because she hasn't
    				 ** visually aged, so our change here makes it say 'Age 1. Appears to have a 10 year old body.'
    				 */
    
    				makeSpan(c, `Appears to have a ${slave.visualAge} year old body`, style, true);
    
    			}
    			if (slave.geneMods.NCS === 1) {
    
    				makeSpan(c, "NCS", "orange");
    
    			if (slave.geneMods.immortality === 1) {
    				makeSpan(c, "Immortal", "orange");
    			}
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_face(slave, c) {
    
    			const r = helpers.getNumericRating(data.long.body.face, slave.face + 100);
    
    			makeSpan(c, `${r.desc} ${slave.faceShape} face`, r.style, true, slave.face);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_eyes(slave, c) {
    			if (!canSee(slave)) {
    				makeSpan(c, "Blind.", "red");
    			} else if (!canSeePerfectly(slave)) {
    				makeSpan(c, "Nearsighted.", "yellow");
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_ears(slave, c) {
    			if (slave.hears <= -2) {
    				makeSpan(c, "Deaf.", "red");
    			} else if ((slave.hears === -1) && (slave.earwear !== "hearing aids")) {
    				makeSpan(c, "Hard of hearing.", "yellow");
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_lips(slave, c) {
    
    			makeRatedStyledSpan(c, data.long.body.lips, slave.lips, 0, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_teeth(slave, c) {
    
    			helpers.makeMappedStyledSpan(c, data.long.body.teeth, slave.teeth);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_muscles(slave, c) {
    
    			const h = helpers;
    
    svornost's avatar
    svornost committed
    			h.makeStyledSpan(c, h.getMultiNumericRating(data.long.body.muscles, [slave.muscles + 100, h.FSData.policy.PhysicalIdealist.active]), slave.muscles, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_voice(slave, c) {
    			if (slave.voice === 0) {
    				makeSpan(c, "Mute.", "red");
    			} else {
    
    				helpers.makeMappedStyledSpan(c, data.long.accent, slave.accent);
    
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_tits_ass(slave, c) {
    
    			const h = helpers;
    			h.makeStyledSpan(c,
    				h.getMultiNumericRating(data.long.body.titsAss,
    
    					[slave.boobs, slave.butt, h.FSData.policy.AssetExpansionist.active, slave.weight + 100, slave.muscles + 100]));
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_hips(slave, c) {
    
    			const di = hipsAssRating(slave);
    			if (di >= 0) {
    				helpers.makeMappedStyledSpan(c, data.long.body.hipsAss, di);
    
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_waist(slave, c) {
    
    			makeRatedStyledSpan(c, data.long.body.waist, slave.waist, 100, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_implants(slave, c) {
    			const styles = "pink";
    			if ((slave.boobsImplant !== 0) || (slave.buttImplant !== 0) || (slave.lipsImplant !== 0) || (slave.bellyImplant !== -1)) {
    				makeSpan(c, "Implants.", styles);
    			} else if ((slave.faceImplant >= 30) || (slave.waist < -95)) {
    				makeSpan(c, "Surgery enhanced.", styles);
    			} else {
    				makeSpan(c, "All natural.", styles);
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_lactation(slave, c) {
    			if (slave.lactation === 1) {
    				makeSpan(c, "Lactating naturally.", "pink");
    			} else if (slave.lactation === 2) {
    				makeSpan(c, "Heavy lactation.", "pink");
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function long_mods(slave, c) {
    
    			const modScore = SlaveStatsChecker.modScore(slave);
    			if (slave.corsetPiercing === 0 && modScore.piercing < 3 && modScore.tat < 2) {
    
    				return;
    
    			} else if (modScore.total > 15 || (modScore.piercing > 8 && modScore.tat > 5)) {
    
    				makeSpan(c, "Extensive body mods.");
    
    			} else if (modScore.total > 7) {
    
    				makeSpan(c, "Noticeable body mods.");
    			} else {
    				makeSpan(c, "Light body mods.");
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_age(slave, c) {
    			let r = makeSpan(c, "", "pink");
    			if (V.showAgeDetail === 1) {
    				r.textContent += slave.actualAge.toString();
    			} else if (slave.actualAge >= 18) {
    
    				r.textContent += helpers.getNumericRating(data.short.body.age, slave.actualAge);
    
    			if (slave.actualAge !== slave.physicalAge) {
    				r.textContent += ` w ${slave.physicalAge}y-bdy`;
    			}
    			if (slave.visualAge !== slave.physicalAge) {
    				r.textContent += ` Lks${slave.visualAge}`;
    			}
    
    			r.textContent += " ";
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_face(slave, c) {
    
    			makeRatedStyledSpan(c, data.short.body.face, slave.face, 100, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_eyes(slave, c) {
    			if (!canSee(slave)) {
    				makeSpan(c, "Blind", "red");
    			} else if (!canSeePerfectly(slave)) {
    				makeSpan(c, "Sight-", "yellow");
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_ears(slave, c) {
    			if (slave.hears === -2) {
    				makeSpan(c, "Deaf", "red");
    			} else if ((slave.hears === -1) && (slave.earwear !== "hearing aids")) {
    				makeSpan(c, "Hearing-", "yellow");
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_lips(slave, c) {
    
    			makeRatedStyledSpan(c, data.short.body.lips, slave.lips, 0, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_teeth(slave, c) {
    
    			helpers.makeMappedStyledSpan(c, data.short.body.teeth, slave.teeth);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_muscles(slave, c) {
    
    			const h = helpers;
    
    Pregmodder's avatar
    Pregmodder committed
    			h.makeStyledSpan(c, h.getMultiNumericRating(data.short.body.muscles, [slave.muscles + 100, h.FSData.policy.PhysicalIdealist.active]), slave.muscles, true);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_voice(slave, c) {
    			if (slave.voice === 0) {
    				makeSpan(c, "Mute", "red");
    			} else {
    
    				helpers.makeMappedStyledSpan(c, data.short.accent, slave.accent);
    
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_tits_ass(slave, c) {
    
    			const h = helpers;
    			h.makeStyledSpan(c,
    				h.getMultiNumericRating(data.short.body.titsAss,
    
    					[slave.boobs, slave.butt, h.FSData.policy.AssetExpansionist.active, slave.weight + 100, slave.muscles + 100]));
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_hips(slave, c) {
    
    			const di = hipsAssRating(slave);
    			if (di >= 0) {
    				helpers.makeMappedStyledSpan(c, data.short.body.hipsAss, di);
    
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_waist(slave, c) {
    
    			makeRatedStyledSpan(c, data.short.body.waist, slave.waist, 100, false);
    
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_implants(slave, c) {
    			if ((slave.boobsImplant === 0) && (slave.buttImplant === 0) && (slave.waist >= -95) && (slave.lipsImplant === 0) && (slave.faceImplant <= 5) && (slave.bellyImplant === -1)) {
    				makeSpan(c, "Natr", "pink");
    			} else {
    				makeSpan(c, "Impl", "pink");
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_lactation(slave, c) {
    			if (slave.lactation === 1) {
    				makeSpan(c, "Lact", "pink");
    			} else if (slave.lactation === 2) {
    				makeSpan(c, "Lact", "pink");
    			}
    		}
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_mods(slave, c) {
    
    			const modScore = SlaveStatsChecker.modScore(slave);
    			if (slave.corsetPiercing === 0 && modScore.piercing < 3 && modScore.tat < 2) {
    
    				return;
    
    			} else if (modScore.total > 15 || (modScore.piercing > 8 && modScore.tat > 5)) {
    
    				makeSpan(c, "Mods++");
    
    			} else if (modScore.total > 7) {
    
    				makeSpan(c, "Mods+");
    			} else {
    				makeSpan(c, "Mods");
    			}
    			if (!jQuery.isEmptyObject(slave.brand)) {
    				makeSpan(c, "Br");
    			}
    		}
    
    
    		/**
    		 * @param {App.Entity.SlaveState} slave
    		 * @param {Node} c
    		 * @returns {void}
    		 */
    		function short_intelligence(slave, c) {