code:
/*
* <<htag>> macro
* A simple macro which allows to create wrapping html elements with dynamic IDs.
*	idea blatantly robbed from the spanMacroJS.tw but expanded to a more generic
*	case, allowing <div>, <button> or whatever you want. elements, default is for
*	the div though. In addition, you can pass an object in as the first argument
*	instead of an id, and each of the object's attributes will become attributes
*	of the generate tag.
*
* Usage: <<htag id>>...<</htag>>
* Usage: <<htag id tag>>...<</htag>>
* Usage: <<htag attributes>>...<</htag>>
* Usage: <<htag attributes tag>>...<</htag>>
*/
Macro.add('htag', {
	tags: null,
	handler() {
		const payload = this.payload[0].contents.replace(/(^\n+|\n+$)/, '');
		let htag = 'div';
		let attributes;

		function munge(val, key) {
			return `${key}="${val}"`;
		}

		if (this.args.length === 0) { return this.error('invalid syntax, format: <<htag [id [ tag ] | attributes [ tag ] >>'); }
		if (this.args.length > 1) { htag = String(this.args[1]).trim(); }
		if (typeof this.args[0] === "object") {
			attributes = $.map(this.args[0], munge).join(" ");
		} else {
			attributes = `id="${String(this.args[0]).trim()}"`;
		}
		if (Config.debug) {
			this.debugView.modes({
				block: true
			});
		}

		jQuery(`<${htag} ${attributes} />`)
			.wiki(payload)
			.appendTo(this.output);
	}
});


code:
/**
 * checks for illness and reduces the effectiveness of the slave accordingly -- 10, 25, 50, 85, 95% reduced
 * also checks for tiredness and reduces effectiveness from that as well -- 10% reduction at 80+ and 5% at 50-80
 * @param {App.Entity.SlaveState} slave
 * @returns {number}
 */
window.healthPenalty = function healthPenalty(slave) {
	const H = slave.health;
	let penalty = 100;
	if (H.illness > 0) {
		penalty *= 1 - (100 - Math.min(Math.pow(H.illness, 2) * 5 + 5, 95)) / 100;
	}
	if (H.tired > 90) {
		penalty *= 0.9;
	} else if (H.tired > 60) {
		penalty *= 0.95;
	}
	penalty = Math.trunc(penalty) / 100;
	return penalty;
};

/**
 * All things hurting a slave go through this to update short term damage and slave health.
 * @param {App.Entity.SlaveState} slave
 * @param {number} damage
 * @returns {void}
 */
window.healthDamage = function healthDamage(slave, damage) {
	const H = slave.health;
	damage = Math.max(Math.trunc(damage), 0);
	H.shortDamage += damage;
	H.condition -= damage;
	updateHealth(slave);
};

/**
 * All things directly curing wounds on a slave go through this to update short term damage and slave health.
 * Use sparingly and for direct medical treatment by qualified professionals only; improveCondition should be used instead for drugs, natural healing, etc.
 * @param {App.Entity.SlaveState} slave
 * @param {number} cure
 * @returns {void}
 */
window.healthCure = function healthCure(slave, cure) {
	const H = slave.health;
	cure = Math.max(Math.trunc(cure), 0);
	if (cure > H.shortDamage) {
		cure = H.shortDamage;
	}
	H.shortDamage -= cure;
	H.condition += cure;
	updateHealth(slave);
};

/**
 * Surgical procedures also depend on the PC's medicine skill
 * @param {App.Entity.SlaveState} slave
 * @param {number} damage
 * @returns {void}
 */
window.surgeryDamage = function surgeryDamage(slave, damage) {
	const playerSkillFactor = 1 + Math.clamp(Math.pow(State.variables.PC.skill.medicine / 100, 2), 0, 1);
	healthDamage(slave, Math.trunc(damage / playerSkillFactor));
};

/**
 * All things improving a slave's condition go through this to update condition and slave health.
 * @param {App.Entity.SlaveState} slave
 * @param {number} condition
 * @returns {void}
 */
window.improveCondition = function improveCondition(slave, condition) {
	const H = slave.health;
	H.condition += Math.max(Math.trunc(condition), 0);
	updateHealth(slave);
};

/**
 * Updates slave.health.health
 * @param {App.Entity.SlaveState} slave
 * @returns {void}
 */
window.updateHealth = function updateHealth(slave) {
	const H = slave.health;
	const condition = H.condition;

	// Converting the other variables to better fit the same scale as .condition
	const damage = (40 - H.shortDamage - H.longDamage) * 2.5; // 100 / -inf
	const tired = (40 - H.tired) * 2.5; // 100 / -150
	const illness = (5 - Math.pow(H.illness, 2)) * 20; // 100 / -400

	// Assigning weights to the different components and aggregating
	H.health = condition * 0.6 + damage * 0.2 + tired * 0.1 - illness * 0.1;
};

/**
 * A function for setting valid health values for new slaves, comes with a set of defaults.
 * Not meant for use with existing slaves (consider healthDamage(), surgeryDamage() and improveCondition() first), but sometimes useful still.
 * @param {App.Entity.SlaveState} slave
 * @param {number} condition
 * @param {number} [shortDamage]
 * @param {number} [longDamage]
 * @param {number} [illness]
 * @param {number} [tired]
 * @returns {void}
 */
window.setHealth = function setHealth(slave, condition = -101, shortDamage = -1, longDamage = -1, illness = -1, tired = -1) {
	const H = slave.health;

	// Making sure all values get set to either a default or their desired value
	if (condition < -100) {
		H.condition = jsRandom(-50, 50); // Default value
	} else {
		H.condition = condition;
	}
	if (shortDamage < 0) {
		H.shortDamage = Math.max(normalRandInt(0, 3), 0);
	} else {
		H.shortDamage = shortDamage;
	}
	if (longDamage < 0) {
		H.longDamage = Math.max(normalRandInt(0, 3), 0);
	} else {
		H.longDamage = longDamage;
	}
	if (illness < 0 || illness > 5) {
		H.illness = Math.max(normalRandInt(0, 0.5), 0);
	} else {
		H.illness = illness;
	}
	if (tired < 0 || tired > 100) {
		H.tired = jsRandom(20, 50);
	} else {
		H.tired = tired;
	}

	// Adjusting long term damage for age
	if (slave.physicalAge > 29) {
		for (let i = 1; i < (slave.physicalAge - 29); i++) {
			H.longDamage += Math.trunc((i + 4 + jsRandom(1, 15)) / 20);
		}
	}

	// Making sure their overall health doesn't go too low (and would lead to a very likely death)
	if (H.condition - H.shortDamage - H.longDamage < -100) {
		let excess = -100 + H.condition + H.shortDamage + H.longDamage;
		if (H.shortDamage + H.longDamage < 50) { // As long as the damage doesn't get too crazy we'll compensate condition for it
			H.condition += excess;
		} else if (H.longDamage < 50) { // As long as long damage doesn't get too high we'll compensate by improving shortDamage and condition
			if (H.shortDamage - Math.ceil(excess / 2) > 0) {
				H.shortDamage -= Math.ceil(excess / 2);
				H.condition += Math.ceil(excess / 2);
			} else {
				excess -= H.shortDamage;
				H.shortDamage = 0;
				H.condition += excess;
			}
		} else { // If such high longDamage was intentional, they really should have done better basic math to ensure these slaves wouldn't just die immediately
			if (H.shortDamage - Math.ceil(excess / 3) > 0 && H.longDamage - Math.ceil(excess / 3) > 0) {
				H.longDamage -= Math.ceil(excess / 3);
				H.shortDamage -= Math.ceil(excess / 3);
				H.condition += Math.ceil(excess / 3);
			} else {
				excess -= H.shortDamage;
				H.shortDamage = 0;
				if (H.longDamage - Math.ceil(excess / 2) > 0) {
					H.longDamage -= Math.ceil(excess / 2);
					H.condition += Math.ceil(excess / 2);
				} else {
					excess -= H.longDamage;
					H.longDamage = 0;
					H.condition += excess;
				}
			}
		}
	}

	// Finally giving slaves their overall health value
	H.health = H.condition - H.shortDamage - H.longDamage;
};

license: mit


code:
/**
 * @param {App.Entity.SlaveState} heroSlave
 * @param {App.Entity.SlaveState} baseHeroSlave
 * @returns {App.Entity.SlaveState}
 */
App.Utils.getHeroSlave = function(heroSlave, baseHeroSlave) {
	function isObject(o) {
		return (o !== undefined && typeof o === 'object' && !Array.isArray(o));
	}

	function deepAssign(target, source) {
		if (isObject(target) && isObject(source)) {
			for (const key in source) {
				if (!source.hasOwnProperty(key)) { continue; }
				if (isObject(source[key])) {
					if (!target.hasOwnProperty(key)) { target[key] = {}; }
					deepAssign(target[key], source[key]);
				} else {
					Object.assign(target, {
						[key]: source[key]
					});
				}
			}
		}
	}

	function repairLimbs(slave) {
		if (slave.hasOwnProperty("removedLimbs")) {
			if (slave.removedLimbs[0] === 1) {
				removeLimbs(slave, "left arm");
			}
			if (slave.removedLimbs[1] === 1) {
				removeLimbs(slave, "right arm");
			}
			if (slave.removedLimbs[2] === 1) {
				removeLimbs(slave, "left leg");
			}
			if (slave.removedLimbs[3] === 1) {
				removeLimbs(slave, "right leg");
			}
			delete slave.removedLimbs;
		}
	}

	const newSlave = clone(baseHeroSlave);
	let albinismOverride = null;
	if (heroSlave.albinismOverride !== null) {
		albinismOverride = heroSlave.albinismOverride;
		delete heroSlave.albinismOverride;
	}
	deepAssign(newSlave, heroSlave);
	newSlave.albinismOverride = albinismOverride;
	repairLimbs(newSlave);
	generatePuberty(newSlave);
	return newSlave;
};

/**
 * Marks limbs to be removed when going trough App.Utils.getHeroSlave.
 * Does not actually remove limbs, only use on slaves that go through App.Utils.getHeroSlave!!
 * @param {{}}hero
 * @param {string} limb
 */
App.Utils.removeHeroLimbs = function(hero, limb = "all") {
	if (!hero.hasOwnProperty("removedLimbs")) {
		hero.removedLimbs = [0, 0, 0, 0];
	}

	switch (limb) {
		case "all":
			hero.removedLimbs = [1, 1, 1, 1];
			break;
		case "left arm":
			hero.removedLimbs[0] = 1;
			break;
		case "right arm":
			hero.removedLimbs[1] = 1;
			break;
		case "left leg":
			hero.removedLimbs[2] = 1;
			break;
		case "right leg":
			hero.removedLimbs[3] = 1;
			break;
	}
};


code:
/* intended to condense the clothing/toy/etc availability checks into something less asinine */

/**
 * Checks whether item is accessible
 * @param {string} string Name of wearable item
 * @returns {boolean}
 */
window.isItemAccessible = function(string) {
	if (V.cheatMode === 1) {
		return true;
	}
	switch (string) {
		/* no breaks needed because we always return */
		case "attractive lingerie for a pregnant woman":
			return (V.arcologies[0].FSRepopulationFocus > 0 || V.clothesBoughtMaternityLingerie === 1);
		case "a bunny outfit":
			return (V.arcologies[0].FSGenderFundamentalist > 0 || V.clothesBoughtBunny === 1);
		case "body oil":
			return (V.arcologies[0].FSPhysicalIdealist > 0 || V.clothesBoughtOil === 1);
		case "chains":
			return (V.arcologies[0].FSDegradationist > 0 || V.clothesBoughtChains === 1);
		case "a chattel habit":
			return (V.arcologies[0].FSChattelReligionist > 0 || V.clothesBoughtHabit === 1);
		case "conservative clothing":
			return (V.arcologies[0].FSPaternalist > 0 || V.clothesBoughtConservative === 1);
		case "harem gauze":
			return (V.arcologies[0].FSArabianRevivalist > 0 || V.clothesBoughtHarem === 1);
		case "a huipil":
			return (V.arcologies[0].FSAztecRevivalist > 0 || V.clothesBoughtHuipil === 1);
		case "a kimono":
			return (V.arcologies[0].FSEdoRevivalist > 0 || V.clothesBoughtKimono === 1 || V.continent === "Japan");
		case "a maternity dress":
			return (V.arcologies[0].FSRepopulationFocus > 0 || V.clothesBoughtMaternityDress === 1);
		case "a slutty qipao":
			return (V.arcologies[0].FSChineseRevivalist > 0 || V.clothesBoughtQipao === 1);
		case "a long qipao":
			return (V.arcologies[0].FSChineseRevivalist > 0 || V.clothesBoughtCultural === 1);
		case "stretch pants and a crop-top":
			return (V.arcologies[0].FSHedonisticDecadence > 0 || V.clothesBoughtLazyClothes === 1);
		case "a toga":
			return (V.arcologies[0].FSRomanRevivalist > 0 || V.clothesBoughtToga === 1);
		case "Western clothing":
			return (V.arcologies[0].FSPastoralist > 0 || V.clothesBoughtWestern === 1);
		case "a courtesan dress":
			return (V.arcologies[0].FSSlaveProfessionalism > 0 || V.clothesBoughtCourtesan === 1);
		case "a bimbo outfit":
			return (V.arcologies[0].FSIntellectualDependency > 0 || V.clothesBoughtBimbo === 1);
		case "petite admi outfit":
			return (V.arcologies[0].FSPetiteAdmiration > 0 || V.clothesBoughtPetite === 1);
		case "battlearmor":
		case "a military uniform":
		case "a red army uniform":
		case "battledress":
			return (V.clothesBoughtMilitary === 1);
		case "a biyelgee costume":
		case "a dirndl":
		case "lederhosen":
		case "a mounty outfit":
		case "a hanbok":
			return (V.clothesBoughtCultural === 1);
		case "a burqa":
		case "a niqab and abaya":
			return (V.clothesBoughtMiddleEastern === 1 || V.continent === "the Middle East");
		case "a hijab and blouse":
			return (V.clothesBoughtMiddleEastern === 1 || V.clothesBoughtConservative === 1 || V.continent === "the Middle East");
		case "a burkini":
			return (V.clothesBoughtMiddleEastern === 1 && V.clothesBoughtSwimwear === 1 || V.continent === "the Middle East");
		case "a Santa dress":
			return (V.clothesBoughtCostume === 1);
		case "a klan robe":
		case "a slutty klan robe":
		case "a schutzstaffel uniform":
		case "a slutty schutzstaffel uniform":
			return (V.clothesBoughtPol === 1);
		case "nice business attire":
		case "a nice nurse outfit":
		case "a police uniform":
			return (V.clothesBoughtCareer === 1);
		case "a nice maid outfit":
			return (V.clothesBoughtCareer === 1 || V.PC.career === "servant");
		case "a ball gown":
		case "a gothic lolita dress":
			// case 'a halter top dress':
			// case 'a mini dress':
			// case 'a slave gown':
			return (V.clothesBoughtDresses === 1);
		case "a cybersuit":
		case "a latex catsuit":
			return (V.clothesBoughtBodysuits === 1);
		case "a button-up shirt and panties":
		case "a button-up shirt":
		case "cutoffs":
		case "jeans":
		case "leather pants and a tube top":
		case "leather pants":
		case "an oversized t-shirt":
		case "a sweater and cutoffs":
		case "a sweater and panties":
		case "a sweater":
		case "a t-shirt and jeans":
		case "a t-shirt and panties":
		case "a t-shirt":
		case "a tank-top and panties":
		case "a tank-top":
		case "a tube top":
			return (V.clothesBoughtCasual === 1);
		case "boyshorts":
		case "a bra":
		case "kitty lingerie":
		case "panties and pasties":
		case "a skimpy loincloth":
		case "a thong":
		case "pasties":
			return (V.clothesBoughtUnderwear === 1);
		case "leather pants and pasties":
		case "a t-shirt and thong":
		case "a tube top and thong":
		case "an oversized t-shirt and boyshorts":
			return (V.clothesBoughtUnderwear === 1 && V.clothesBoughtCasual === 1);
		case "sport shorts and a sports bra":
		case "sport shorts":
		case "a sports bra":
			return (V.clothesBoughtSports === 1);
		case "sport shorts and a t-shirt":
			return (V.clothesBoughtSports === 1 && V.clothesBoughtCasual === 1);
		case "a nice pony outfit":
		case "a slutty pony outfit":
			return (V.clothesBoughtPony === 1);
		case "a monokini":
		case "a one-piece swimsuit":
			return (V.clothesBoughtSwimwear === 1);
		case "a striped bra":
		case "striped panties":
		case "striped underwear":
			return (V.clothesBoughtPantsu === 1 || V.continent === "Japan");
		case "platform shoes":
		case "platform heels":
		case "extreme platform heels":
			return (V.arcologies[0].FSStatuesqueGlorification > 0 || V.shoesBoughtHeels === 1);
		case "bowtie":
			return (V.arcologies[0].FSGenderFundamentalist > 0 || V.clothesBoughtBunny === 1);
		case "ancient Egyptian":
			return (V.arcologies[0].FSEgyptianRevivalist > 0 || V.clothesBoughtEgypt === 1);
		case "massive dildo gag":
			return (V.toysBoughtGags === 1);
		case "a small empathy belly":
		case "a medium empathy belly":
		case "a large empathy belly":
		case "a huge empathy belly":
			return (V.arcologies[0].FSRepopulationFocus > 0 || V.clothesBoughtBelly === 1);
		case "bullet vibrator":
		case "smart bullet vibrator":
		case "long dildo":
		case "long, large dildo":
		case "long, huge dildo":
			return (V.toysBoughtDildos === 1);
		case "vibrator":
			return (V.toysBoughtVaginalAttachments === 1);
		case "long plug":
		case "long, large plug":
		case "long, huge plug":
			return (V.toysBoughtButtPlugs === 1);
		case "tail":
		case "cow tail":
		case "cat tail":
		case "fox tail":
			return (V.toysBoughtButtPlugTails === 1);
		default:
			return true;
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {string} prosthetic
 * @returns {boolean}
 */
window.isProstheticAvailable = function(slave, prosthetic) {
	return slave.readyProsthetics.findIndex(function(p) { return p.id === prosthetic; }) !== -1;
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {string} prosthetic
 */
window.addProsthetic = function(slave, prosthetic) {
	if (!isProstheticAvailable(slave, prosthetic)) {
		let limb = prostheticToLimb(prosthetic);
		if (limb > 0) {
			let p = {
				id: prosthetic,
				arm: {left: new App.Entity.LimbState(), right: new App.Entity.LimbState()},
				leg: {left: new App.Entity.LimbState(), right: new App.Entity.LimbState()}
			};
			p.arm.left.type = limb;
			p.arm.right.type = limb;
			p.leg.left.type = limb;
			p.leg.right.type = limb;
			slave.readyProsthetics.push(p);
		} else {
			slave.readyProsthetics.push({id: prosthetic});
		}
	}
};

/**
 * @param {App.Entity.SlaveState} slave
 * @param {string} prosthetic
 * @returns {{}}
 */
window.findProsthetic = function(slave, prosthetic) {
	return slave.readyProsthetics.find(p => p.id === prosthetic);
};

/**
 * @param {string} prosthetic
 * @returns {number}
 */
window.prostheticToLimb = function(prosthetic) {
	switch (prosthetic) {
		case "basicL":
			return 2;
		case "sexL":
			return 3;
		case "beautyL":
			return 4;
		case "combatL":
			return 5;
		case "cyberneticL":
			return 6;
		default:
			return 0;
	}
};

/**
 *
 * @param {number} limb
 * @returns {string}
 */
window.limbToProsthetic = function(limb) {
	switch (limb) {
		case 2:
			return "basicL";
		case 3:
			return "sexL";
		case 4:
			return "beautyL";
		case 5:
			return "combatL";
		case 6:
			return "cyberneticL";
		default:
			return "";
	}
};


code:
App.Medicine.Modification = {};

/**
 * Basic application of scar
 * @param {App.Entity.SlaveState} slave
 * @param {string} scar
 * @param {string} design
 * @param {number} weight
 */
App.Medicine.Modification.addScar = function(slave, scar, design, weight) {
	/*
	V.scarApplied = 1;
	V.degradation += 10;
	surgeryDamage(slave, 10); // dangerous to uncomment this as sometimes many scars are applied at once.
	cashX(forceNeg(surgery.costs), "slaveSurgery", slave);
	surgeryDamage(slave, (V.PC.skill.medicine >= 100) ? Math.round(surgery.healthCosts / 2) : surgery.healthCosts);*/
	if (!weight) {
		weight = 1;
	}
	if (!slave.scar.hasOwnProperty(scar)) {
		slave.scar[scar] = new App.Entity.scarState();
	}
	if (!slave.scar[scar].hasOwnProperty(design)) {
		slave.scar[scar][design] = weight;
	} else {
		slave.scar[scar][design] += weight;
	}
};
/**
 * Basic application of scar
 * @param {App.Entity.SlaveState} slave
 * @param {string} scar
 * @param {string} design
 */
App.Medicine.Modification.removeScar = function(slave, scar, design) {
	/*
	V.scarApplied = 1;
	V.degradation += 10;
	surgeryDamage(slave, 10); //dangerous to uncomment this as sometimes many scars are applied at once.
	cashX(forceNeg(surgery.costs), "slaveSurgery", slave);
	surgeryDamage(slave, (V.PC.skill.medicine >= 100) ? Math.round(surgery.healthCosts / 2) : surgery.healthCosts);*/
	if (slave.scar.hasOwnProperty(scar)) { // if scar object exists for this body part
		if (slave.scar[scar].hasOwnProperty(design)) { // if object has this kind of scar (might be custom)
			if (["generic", "whip", "chain", "burn", "menacing", "exotic", "surgical", "c-section", "cutting"].includes(design)) {
				slave.scar[scar][design] = 0;
			} else {
				delete slave.scar[scar][design]; // scar was custom
			}
		}
		// remove the scar object entirely if no entry is scarred:
		let weights = Object.values(slave.scar[scar]);
		let total = 0;
		let i;
		for (i = 0; i < weights.length; i++) {
			total += weights[i];
		}
		if (total === 0) {
			delete slave.scar[scar];
		}
	}
};

/**
 * Slave is whipped over the entire body, and strains in manacles so much that the wrists and ankles scar if present.
 * @param {App.Entity.SlaveState} slave
 * @param {number} weight
 */
App.Medicine.Modification.addScourged = function(slave, weight) {
	let scarArray = ["left breast", "right breast", "back", "lower back", "left buttock", "right buttock"];
	let i = 0;
	// Whip
	if (getLeftArmID(slave) === 1) {
		scarArray.push("left upper arm");
	}
	if (getRightArmID(slave) === 1) {
		scarArray.push("right upper arm");
	}
	if (getLeftLegID(slave) === 1) {
		scarArray.push("left thigh");
	}
	if (getRightLegID(slave) === 1) {
		scarArray.push("right thigh");
	}

	for (i = 0; i < scarArray.length; i++) {
		App.Medicine.Modification.addScar(slave, scarArray[i], "whip", weight);
	}
	// Manacles
	scarArray = [];
	if (getLeftArmID(slave) === 1) {
		scarArray.push("left wrist");
	}
	if (getRightArmID(slave) === 1) {
		scarArray.push("right wrist");
	}
	if (getLeftLegID(slave) === 1) {
		scarArray.push("left ankle");
	}
	if (getRightLegID(slave) === 1) {
		scarArray.push("right ankle");
	}
	for (i = 0; i < scarArray.length; i++) {
		App.Medicine.Modification.addScar(slave, scarArray[i], "chain", weight);
	}
};

/**
 * Scars a slave over a large section of their body.
 * @param {App.Entity.SlaveState} slave
 * @param {string} location full, upper, lower, left or right
 * @param {string} type whip, burn, surgical, generic
 * @param {number} weight
 */
App.Medicine.Modification.addBulkScars = function(slave, location, type, weight) {
	let scarArray = [];

	/* Divide slave into quarters, and add each quarter as needed. */

	/* Top left */
	if (["left", "upper", "full"].includes(location)) {
		scarArray.push("left breast");
		if (getLeftArmID(slave) === 1) {
			scarArray.push("left upper arm", "left lower arm", "left hand");
		}
	}

	/* Top right */
	if (["right", "upper", "full"].includes(location)) {
		scarArray.push("right breast");
		if (getRightArmID(slave) === 1) {
			scarArray.push("right upper arm", "right lower arm", "right hand");
		}
	}

	/* Lower left */
	if (["left", "lower", "full"].includes(location)) {
		scarArray.push("left buttock");
		if (getLeftLegID(slave) === 1) {
			scarArray.push("left thigh", "left calf", "left foot");
		}
	}

	/* Lower Right */
	if (["right", "lower", "full"].includes(location)) {
		scarArray.push("right buttock");
		if (getRightLegID(slave) === 1) {
			scarArray.push("right thigh", "right calf", "right foot");
		}
	}

	/* Extra */
	if (["upper", "full"].includes(location)) {
		scarArray.push("back", "lower back");
	}

	let i = 0;
	for (i = 0; i < scarArray.length; i++) {
		App.Medicine.Modification.addScar(slave, scarArray[i], type, weight);
	}
};

/**
 * Adds a piercing to a slave.
 * @param {App.Entity.SlaveState} slave
 * @param {string} location
 * @param {number} weight
 * @returns {string} slave reaction
 */
App.Medicine.Modification.setPiercing = function(slave, location, weight) {
	if (weight === 3) {
		// smart piercings cost more
		cashX(forceNeg(V.SPcost), "slaveMod", slave);
	} else {
		cashX(forceNeg(V.modCost), "slaveMod", slave);
	}

	// reaction
	const {He, he, His, his, him} = getPronouns(slave);
	const delta = weight - slave[`${location}Piercing`];
	let r = ` `;
	if (location === "tongue") {
		if (slave.fetishKnown && slave.fetish === "cumslut" && slave.fetishStrength > 10) {
			r += `Since ${he}'s an oral whore, ${he}'s `;
			if (delta > 0) {
				if (weight > 1) {
					r += `<span class="devotion inc">very happy</span> you took the time to personally pierce ${his} tongue so heavily. `;
				} else {
					r += `<span class="devotion inc">happy</span> to get a tongue piercing from you personally. `;
				}
				r += `After all, ${he}'s pretty sure it's a sign there will be plenty of ${V.PC.dick > 0 ? `dick for ${him} to suck` : `pussy for ${him} to eat`} in the future. `;
				slave.devotion += 2 * delta;
			} else if (slave.devotion <= 95) {
				r += `<span class="devotion dec">saddened</span> you decided to take out ${his} tongue piercings. ${He}'s afraid this means something about ${his} future as a suck slut. `;
				slave.devotion += 2 * delta;
			} else {
				r += `disapointed to lose ${his} tongue piercings, but accepts your will for ${his} body. `;
			}
		}
		if (slave.sexualFlaw === "hates oral" && weight > 0) {
			r += `${He} has trouble thinking of ${his} mouth as a warm, wet, playful hole, so ${his} new piercings will be therapeutic. Having something to suck on at all times should force ${him} past ${his} hang-ups. `;
		}
	} else if (location === "nipples" || location === "areolae") {
		if (slave.fetishKnown && slave.fetish === "boobs" && slave.fetishStrength > 10) {
			r += `Since ${he}'s fixated on ${his} boobs, ${he}'s `;
			if (delta > 0) {
				if (weight > 1) {
					r += `<span class="devotion inc">very happy</span> you took the time to personally pierce them so heavily. `;
				} else {
					r += `<span class="devotion inc">happy</span> to get them pierced by you personally. `;
				}
				r += `As far as ${he}'s concerned, more attention on ${his} tits is always good. `;
				slave.devotion += 2 * delta;
			} else if (slave.devotion <= 95) {
				r += `<span class="devotion dec">saddened</span> you decided to take out ${his} piercings. ${He} doesn't like anything that makes ${his} tits less distinctive. `;
				slave.devotion += 2 * delta;
			} else {
				r += `disapointed to lose ${his} the piercings in ${his} ${location}, but accepts your will for ${his} body. `;
			}
		}
	} else if (location === "corset") {
		if (slave.fetishKnown && slave.fetish === "masochist" && slave.fetishStrength > 10) {
			r += `Since ${he}'s a pain whore `;
			if (delta > 0) {
				r += `${he} <span class="devotion inc">got off</span> on you giving ${him} a corset piercing personally. ${He} loved the feeling of the metal entering ${his} flesh. `;
				slave.devotion += 2 * delta;
			} else if (slave.devotion <= 95) {
				r += `<span class="devotion dec">saddened</span> you decided to take out ${his} corset piercings.  ${He}'ll miss the constant slight discomfort they gave ${him}. `;
				slave.devotion += 2 * delta;
			} else {
				r += `disapointed to lose ${his} corset piercings, but accepts your will for ${his} body. `;
			}
		}
	} else if (location === "vagina") {
		if (slave.fetishKnown && slave.fetish === "pregnancy" && slave.fetishStrength > 10) {
			r += `Since ${he}'s a whore for impregnation, ${he}'s `;
			if (delta > 0) {
				if (weight > 1) {
					r += `<span class="devotion inc">very happy</span> you took the time to personally pierce ${his} pussylips so heavily. `;
				} else {
					r += `<span class="devotion inc">happy</span> to get pussy piercings from you personally. `;
				}
				r += `Like most pregnancy fetishists, ${he}'s a little desperate for dick, and anything that points towards more bareback sex is appealing to ${him}. `;
				slave.devotion += 2 * delta;
			} else if (slave.devotion <= 95) {
				r += `saddened you decided to take out ${his} pussy piercings. Like most pregnancy fetishists, ${he}'s a little desperate for dick, and ${he}'s afraid of anything that points towards less attention focused on ${his} cunt. `;
				slave.devotion += 2 * delta;
			} else {
				r += `disapointed to lose ${his} pussy piercings, but accepts your will for ${his} body. `;
			}
		}
		if (slave.sexualFlaw === "hates penetration" && weight > 0) {
			r += `${He} has trouble thinking of ${his} vagina as a fuckhole rather than something special to protect and cherish, so ${his} new piercings will be therapeutic. The constant, inescapable stimulation of ${his} labia should force ${him} past ${his} hang-ups. `;
		}
	} else if (location === "dick") {
		if (slave.fetishKnown && slave.fetish === "pregnancy" && slave.fetishStrength > 10) {
			r += `Since ${he}'s fixated on using ${his} cock to impregnate other slaves, ${he}'s `;
			if (delta > 0) {
				if (weight > 1) {
					r += `<span class="devotion inc">very happy</span> you took the time to personally stick so much metal into ${his} dick`;
					if (slave.scrotum > 0) {
						r += ` and ballsack`;
					}
					r += `. `;
				} else {
					r += `<span class="devotion inc">happy</span> to get penis piercings from you personally. `;
				}
				r += `${He}'s extremely proud of ${his} newly decorated member. `;
				slave.devotion += 2 * delta;
			} else if (slave.devotion <= 95) {
				r += `saddened you decided to take out ${his} dick piercings. ${He} passionately loves using ${his} penis, and ${he}'s concerned you're planning to let ${him} do less of that. `;
				slave.devotion += 2 * delta;
			} else {
				r += `disapointed to lose ${his} dick piercings, but accepts your will for ${his} body. `;
			}
		}
	} else if (location === "anus") {
		if (slave.fetishKnown && slave.fetish === "buttslut" && slave.fetishStrength > 10) {
			r += `Since ${he}'s an anal slut, ${he}'s `;
			if (delta > 0) {
				if (weight > 1) {
					r += `<span class="devotion inc">very happy</span> you took the time to personally pierce the entire area around ${his} whorish asspussy. `;
				} else {
					r += `<span class="devotion inc">happy</span> to get a piercing next to ${his} favorite hole from you personally. `;
				}
				r += `${He} can't wait to find out how the metal will feel against ${his} butthole when ${he} moves around. `;
				slave.devotion += 2 * delta;
			} else if (slave.devotion <= 95) {
				r += `saddened you decided to take out ${his} backdoor piercings. ${He} will miss the constant anal titillation, and ${he}'s worried this means you're becoming less interested in ${his} butthole. `;
				slave.devotion += 2 * delta;
			} else {
				r += `disapointed to lose ${his} backdoor piercings, but accepts your will for ${his} body. `;
			}
		}
		if (slave.sexualFlaw === "hates anal" && weight > 0) {
			r += `${He} has trouble thinking of ${his} asshole as a sexy, fuckable hole, so ${his} new piercings will be therapeutic. The constant tickling back there should force ${him} past ${his} hang-ups. `;
		}
	}

	if (slave.genes === "XY" && slave.attrXY <= 35 && ["ear", "lips", "nose", "eyebrow", "navel"].contains(location)) {
		r += `${His} girly new `;
		switch (location) {
			case "ear":
				r += `pierced ears should help ${him} a little with $his issues about ${his} self-image as a fuckable slave girl. `;
				break;
			case "lips":
				r += `facial piercing, just below ${his} pretty mouth, should help ${him} get used to the idea of it being a warm, wet hole for cocks. `;
				break;
			case "nose":
				r += `nose piercing should make ${his} reflection in the mirror seem just a bit more fuckably feminine to ${him}. `;
				break;
			case "navel":
				r += `navel piercing should help ${him} see ${his} naked body in a submissively sexual way, especially as ${he} turns to hide it and present ${his} asspussy. `;
				break;
			case "eyebrow":
				r += `facial piercing should help destroy ${his} vestiges of sexual identity outside of ${his} life as a hole for cocks. `;
				break;
			default:
				r += `set of slutty facial piercings should help ${him} get used to the idea of being a girly slave meant to please dicks. `; /* impossible, but I'm leaving the text anyway */
		}
		if (slave.devotion < -20) {
			r += `Unfortunately, that positive effect will have to wait until ${he}'s a little less resistant to the idea of being a sex slave. `;
		}
	}

	slave[`${location}Piercing`] = weight;
	return r;
};

/**
 * Adds a tattoo to a slave.
 * @param {App.Entity.SlaveState} slave
 * @param {string} location
 * @param {string|number} design (0 removes)
 * @returns {string} slave reaction
 */
App.Medicine.Modification.setTattoo = function(slave, location, design) {
	cashX(forceNeg(V.modCost), "slaveMod", slave);

	// reaction
	const {He, he, His, his, him} = getPronouns(slave);
	Enunciate(slave); // TODO: it'd be nice to return a deconstructable object from Enunciate like we do from getPronouns...
	const s = V.sEnunciate;
	const ss = V.sEnunciate;
	const x = V.xEnunciate;
	const c = V.cEnunciate;
	const Master = V.titleEnunciate;
	const say = V.sayEnunciate;
	let r = ` `;
	if (location === "anus" && design !== 0) {
		if (canSee(slave) && canTalk(slave)) {
			const anus = (function(s) {
				if (s.anus > 3) {
					return "anal gape";
				} else if (s.anus === 3) {
					return "relaxed asspussy";
				} else if (s.anus === 2) {
					return "soft butthole";
				} else {
					return "tight butthole";
				}
			})(slave);

			if (design === "bleached") {
				r += `${He} knew ${he} was getting ${his} ass bleached, of course. The stinging sensation was hard to miss. Even so, ${he} cranes ${his} neck around as soon as possible to look at ${his} ${anus} in the mirror. `;
			} else {
				r += `${He} knew ${he} was getting ${his} asshole tattooed, of course. ${He} cranes ${his} neck around as soon as possible to look at ${his} ${anus} in the mirror. `;
				if (slave.butt > 5) {
					r += `${His} huge buttocks are still held apart by the spreaders used to give the tattooing actuators access to ${his} most intimate area, giving ${him} a good view. `;
				}
			}
			if (slave.fetishKnown && slave.fetish === "buttslut") {
				switch (design) {
					case "bleached":
						r += `${He} giggles at ${his} crinkled fuckhole's clean, fuckable appearance, and starts squeezing ${his} sphincter muscles experimentally. "Thank you, ${Master}," ${he} gasps, flushing.`;
						break;
					case "flowers":
					case "tribal patterns":
						r += `${He} gasps at the beautiful design and then suddenly bursts into tears. "T-thank you, ${Master}," ${he} blubbers. "I'm ${s}-${s}uch an anal whore."`;
						break;
					case "advertisements":
						r += `${He} stares, ${his} mouth working as ${he} spells out `;
						if (slave.vagina > -1) {
							r += `'Fucktoy's Favorite Fuckhole.' ${He} smiles naughtily. "${Master}, it sure i${s}, `;
						} else {
							r += `'Spit First.' ${He} smiles naughtily. "${Master}, that work${s} for me. Lube'${s} good too. Or, um, dry'${s} fine too," `;
						}
						r += `${he} giggles.`;
						break;
					case "rude words":
						r += `$He stares, $his mouth working as $he spells out `;
						if ($activeSlave.vagina > -1) {
							r += `'Fucktoy's Favorite Fuckhole.' ${He} smiles naughtily. "${Master}, it sure i${s}, `;
						} else {
							r += `'Sissy Slut's Sodomy Slit.' ${He} smiles naughtily. "${Master}, I love it when you fuck my lewd butt ${s}lit," `;
						}
						r += `${he} giggles.`;
						break;
					case "sacrilege":
						r += `${He} stares, ${his} mouth working as ${he} reads the ring of Aramaic blasphemy around ${his} hole. Getting it, ${he} smiles naughtily. "${Master}, I'm a horrible little butthole ${s}uccubu${s}. Plea${s}e, ${Master}, feed me. I need cum in my a${ss} to ${s}urvive."`;
						break;
					case "sacrament":
						r += `${He} stares, ${his} mouth working as ${he} reads the ring of Aramaic around ${his} hole. Getting it, ${he} smiles beatifically. "${Master}, I alway${s} knew butt${s}e${x} was a ${s}acrament."`;
						break;
					case "degradation":
						r += `${He} stares, ${his} mouth working as ${he} spells out '${WrittenMaster(slave)} Made Me An Anal Slut.' ${He} smiles naughtily. "Oh ${Master}, you did," ${he} ${say}s gratefully. "And I love you for it."`;
						break;
					case "counting":
						r += `${He} stares, realizing that the markings denote how many times ${he}'s been sodomized. "${Master}, thank you, and, and, I hope I'll have to come back in here a lot," ${he} ${say}s. "Um, becau${s}e I have to have that updated. Becau${s}e I'm being fucked in the butt a lot," ${he} adds, wanting to be completely clear.`;
						break;
					case "bovine patterns":
						r += `${He} stares at ${his} new cow's anus. Finally, ${he} focuses on the upsides of being permanently transformed like this. "${Master}, I hope thi${s} mean${s} I'll have to be fed up the butt a lot," ${he} ${say}s, referencing the milking systems' anal feeders.`;
						break;
					case "possessive":
						r += `${He} stares, realizing that the ring of numbers around ${his} anus give the date ${he} was enslaved by you. "Be${s}t pla${c}e for that date, ${Master}," ${he} ${say}s. "Being a butt ${s}lut i${s} my life now."`;
						break;
					case "paternalist":
						r += `${He} stares, ${his} mouth working as ${he} spells out '${WrittenMaster(slave)} Helped Me Love Anal.' "Oh ${Master}, you did," ${he} coos. "You did! Thank you ${s}o much for helping me become the anal ${s}lut I wa${s} alway${s} meant to be!"`;
						break;
					default:
						r += `${He} smiles, pleased at the attention ${his} ${anus} is getting.`;
				}
			} else if (slave.devotion > 50) {
				switch (design) {
					case "bleached":
						r += `${He} gasps at how closely the crinkled skin around ${his} anal opening matches the rest of ${his} ${slave.skin} body. "Oh, it'${s} ${s}o beautiful! Thank you, ${Master}, thank you," ${he} exclaims lovingly.`;
						break;
					case "flowers":
					case "tribal patterns":
						r += `${He} gasps at the beautiful design. "It'${s} ${s}o pretty, ${Master}," $he ${say}s. "Thank you. I love you ${s}o much!"`;
						break;
					case "advertisements":
					case "degradation":
					case "paternalist":
					case "possessive":
					case "rude words":
					case "sacrament":
					case "sacrilege":
						r += `${He} stares, ${his} mouth working as ${he} spells out the words tattooed in a ring around ${his} asshole. "Thank you, ${Master}," ${he} ${say}s devotedly. "I love you and I'll ${s}erve you with my butthole a${s} be${s}t I can."`;
						break;
					default:
						r += `${He} smiles, pleased at the mark of your attention to ${his} body. "Thank you, ${Master}," ${he} ${say}s devotedly. "I love you and I'll ${s}erve you with my butthole a${s} be${s}t I can."`;
				}
			} else if (slave.sexualFlaw === "hates anal") {
				r += `${He} stares, `;
				switch (design) {
					case "bleached":
						r += `horrified, and then shudders convulsively, but can't look away. `;
						break;
					case "flowers":
					case "tribal patterns":
						r += `torn between fascination at the intricate design and horror at having ${his} anus permanently tattooed. `;
						break;
					case "advertisements":
					case "degradation":
					case "paternalist":
					case "possessive":
					case "rude words":
					case "sacrament":
					case "sacrilege":
						r += `${his} mouth working as ${he} spells out the words tattooed in a ring around ${his} asshole. ${He} starts to cry as their meaning sinks in. `;
						break;
					default:
						r += `lower lip quivering. `;
				}
				r += `${He} hates being assraped, and knows that this means there's lots of it in store for ${him}. "${Master}, p-plea${s}e," ${he} moans halfheartedly. "I d-don't like being b-butt f-fucked."`;
			} else if (slave.devotion > 20) {
				r += `${He} stares, `;
				switch (design) {
					case "bleached":
						r += `fascinated. $He doesn't seem sure what to make of this. `;
						break;
					case "flowers":
					case "tribal patterns":
						r += `torn between admiration at the intricate design and realization that ${his} anus has been permanently tattooed. `;
						break;
					case "advertisements":
					case "degradation":
					case "paternalist":
					case "possessive":
					case "rude words":
					case "sacrament":
					case "sacrilege":
						r += `${his} mouth working as ${he} spells out the words tattooed in a ring around ${his} asshole. ${He} shudders a little as they sink in and ${he} internalizes what they say about ${him} and ${his} asshole. `;
						break;
					default:
						r += `realizing that ${his} anus has been permanently tattooed. It's yet another proof that ${he}'s permanently a sex slave, and ${his} anus is for fucking. `;
				}
				r += `"Thank you, ${Master}," ${he} ${say}s, taking refuge in propriety. "Um, d-doe${s} thi${s} mean I'm going to be more of a, um, b-butt${s}lut?" ${he} asks hesitantly, not sounding very enthusiastic.`;
			} else if (slave.trust < -20) {
				r += `${He} stares, `;
				switch (design) {
					case "bleached":
						r += `trying to process the appearance of what must seem like an unnaturally clean-looking hole. `;
						break;
					case "flowers":
					case "tribal patterns":
						r += `struggling to process the fact that one of ${his} most intimate areas has been tattooed. Beautifully tattooed, but still." `;
						break;
					case "advertisements":
					case "degradation":
					case "paternalist":
					case "possessive":
					case "rude words":
					case "sacrament":
					case "sacrilege":
						r += `${his} mouth working as ${he} spells out the words tattooed in a ring around ${his} asshole. ${He} shudders as ${he} does ${his} best to conceal ${his} feelings about such a permanent display of ${his} status as an anal slave. `;
						break;
					default:
						r += `looking sad as the meaning of a permanent display of ${his} status as an anal slave sinks in. `;
				}
				r += `After a short pause ${he} remembers ${his} duties and stammers, "T-thank you, ${Master}." After a while longer ${he} turns ${his} head away, clearly not wanting to dwell on how ${his} anus is obviously being beautified to attract sexual attention.`;
			} else {
				switch (design) {
					case "bleached":
						r += `${His} face darkens as ${he} sees ${his} crinkled fuckhole's clean, fuckable appearance. "Don't you dare fucking a${ss}rape me!" ${he} bursts out.`;
						break;
					case "flowers":
					case "tribal patterns":
						r += `${He}'s momentarily surprised by the pretty design, but then ${his} face darkens. "That'${s} permanent!" ${he} bursts out. "My a${ss}hole i${s} going to look like that forever!"`;
						break;
					case "advertisements":
						r += `${He} stares, ${his} mouth working as ${he} spells out `;
						if ($activeSlave.vagina > -1) {
							r += `'Second Pussy.' ${His} face darkens. "No it'${s} not," ${he} bursts out. "Anal is dirty."`;
						} else {
							r += `'Spit First.' ${His} face darkens. "Gro${ss}," ${he} bursts out. "Gro${ss} and wrong!"`;
						}
						break;
					case "rude words":
						r += `${He} stares, ${his} mouth working as ${he} spells out `;
						if ($activeSlave.vagina > -1) {
							r += `'Fucktoy's Favorite Fuckhole.' ${His} face darkens. "No it'${s} not," ${he} bursts out. "It'${s} my a${ss}!"`;
						} else {
							r += `'Sissy Slut's Sodomy Slit.' ${His} face darkens. "No it'${s} not," ${he} bursts out. "I'm not a ${s}i${ss}y! Or a ${s}lut! And it'${s} not a ${s}lit!" Those are debatable, but they can all change, of course.`;
						}
						break;
					case "sacrament":
					case "sacrilege":
						r += `${He} stares, ${his} mouth working as ${he} reads the ring of Aramaic around ${his} hole. ${He} doesn't understand it, but ${he}'s not happy about having stuff permanently inscribed around ${his} ass.`;
						break;
					case "degradation":
						r += `${He} stares, ${his} mouth working as ${he} spells out '${WrittenMaster(slave)} Made Me An Anal Slut.' ${His} face darkens. "No I'm not," ${he} bursts out. That remains to be seen.`;
						break;
					case "counting":
						r += `${He} stares, realizing that the markings denote how many times ${he}'s been sodomized. ${His} face darkens. "I ${s}ee there'${s} room for more," ${he} bursts out. "Fucking gro${ss}."`;
						break;
					case "bovine patterns":
						r += `${He} stares at ${his} new cow's anus. "Fucking gro${ss}," ${he} bursts out. "I'm not an animal." That remains to be seen.`;
						break;
					case "possessive":
						r += `${He} stares, realizing that the ring of numbers around ${his} anus give the date ${he} was enslaved by you. ${His} face darkens. "Wor${s}t day of my life," ${he} bursts out.`;
						break;
					case "paternalist":
						r += `${He} stares, ${his} mouth working as ${he} spells out '${WrittenMaster(slave)} Helped Me Love Anal.' "I don't," ${he} bursts out. "It'${s} fucking gro${ss}."`;
						break;
					default:
						r += `${He} stares, horrified that you have defaced ${his} body in this way.`;
				}
			}
		}
	}
	// TODO: maybe some reactions for other kinds of tattoos would be nice too?

	slave[`${location}Tat`] = design;
	return r;
};


code:
/* eslint-disable camelcase */
/* eslint-disable no-empty */
/* Use like:
	<<options $varname "New Passage (defaults to current passage)">>
	  A title
	  <<option "value_to_set_varname_to" "English text to show user" "additional variables to set when clicked" "Extra English text to show, but not as a link">>
		 Text to show if $varname matches this option
	  <<option ....>>
	  <<comment>>
		Some comment to add at the end
	<</option>>

	optionlt and optionslte lets you also specify a 'less than' or 'less than or
	equal' value, to show an option as selected if it is less than this amount
	(and not selected by a previous option)

	<<optionlt "less than value" "value_to_set_varname_to" "English text to show user" "additional variables to set when clicked" "Extra English text to show, but not as a link">>
  */
Macro.add('options', {
	skipArgs : false,
	tags	 : ['option', 'comment', 'optionlt', 'optionlte', 'optiongt', 'optiongte',
				'optiondefault', 'optionif'],
	handler : function() {
		try {
			let currentOption = this.payload[0].args[0];
			let currentOptionIsNumber = typeof currentOption === "number";
			let variable = null;
			let title = this.payload[0].contents || '';
			let passageName = this.payload[0].args[1] || passage();
			let found = false;
			let found_index = 0;
			let comment = null;
			let hasMultipleOptionsWithSameValue = false;
			let description = "";
			let hasCurrentOption = this.payload[0].args.full &&
				this.payload[0].args.full !== '""' && this.payload[0].args.full !== "''";

			/* Check if we have a first argument - if we do, it should be a
			   variable like $foo
			  */
			if (hasCurrentOption) {
				if (currentOption === undefined)
					currentOption = false;
				if (this.payload[0].args.full.startsWith("State.temporary.")) {
					variable = "_" + this.payload[0].args.full.split(' ', 1)[0].substring("State.temporary.".length);
				} else if (this.payload[0].args.full.startsWith("State.variables.")) {
					variable = "$" + this.payload[0].args.full.split(' ', 1)[0].substring("State.variables.".length);
				} else {
					// eslint-disable-next-line no-console
					console.log(this.payload[0].args.full);
					throw new Error("First parameter to 'options' must be a variable");
				}

				/* First print out the string for the current value */
				for (let i = 1, len = this.payload.length; i < len; ++i) {
					if (this.payload[i].name === 'option') {
						if (this.payload[i].args[0] === currentOption) {
							if (found) {
								hasMultipleOptionsWithSameValue = true;
							} else {
								description = this.payload[i].contents;
								found_index = i;
								found = true;
							}
						}
					} else if (this.payload[i].name === 'optionlt') {
						if (!found && this.payload[i].args[0] > currentOption) {
							description = this.payload[i].contents;
							found = true;
							found_index = i;
						}
					} else if (this.payload[i].name === 'optionlte') {
						if (!found && this.payload[i].args[0] >= currentOption) {
							description = this.payload[i].contents;
							found = true;
							found_index = i;
						}
					} else if (this.payload[i].name === 'optiongt') {
						if (!found && this.payload[i].args[0] < currentOption) {
							description = this.payload[i].contents;
							found = true;
							found_index = i;
						}
					} else if (this.payload[i].name === 'optiongte') {
						if (!found && this.payload[i].args[0] <= currentOption) {
							description = this.payload[i].contents;
							found = true;
							found_index = i;
						}
					} else if (this.payload[i].name === 'optiondefault') {
						if (!found) {
							description = this.payload[i].contents;
							found = true;
							found_index = i;
						}
					} else if (this.payload[i].name === 'comment') {
					} else if (this.payload[i].name === 'optionif') {
					} else {
						throw new Error("Only valid tag is 'option' inside 'options'");
					}
				}
			} else {
				// No variable was passed to <<options>>
				// This is valid, but then we only allow an empty <<option>> or <<comment>>
				// inside
				for (let i = 1, len = this.payload.length; i < len; ++i) {
					if (this.payload[i].name === 'option' && this.payload[i].args.length === 0) {
						// This is valid for an empty <<options>>
					} else if (this.payload[i].name === "comment") {
						// This is valid for an empty <<options>>
					} else {
						throw new Error("Missing variable to <<options>>");
					}
				}
			}

			let showSelectedOption = true; // this.payload.length !== 3 || !description;
			/* Now print out the list of options */
			let output = "";
			let optionIfIsFalse = false;
			for (let i = 1, len = this.payload.length; i < len; ++i) {
				if (this.payload[i].name === "optionif") {
					if (this.payload[i].args.length === 0) {
						optionIfIsFalse = false; /* No options means to turn off optionif */
					} else if (this.payload[i].args.length === 1) {
						// Evaluate it and see if is false
						if (typeof (this.payload[i].args[0]) !== 'boolean') {
							throw new Error("optionif requires true or false for the first (and only) parameter");
						}
						optionIfIsFalse = !this.payload[i].args[0];
					} else {
						throw new Error("Only one argument expected to 'optionif'");
					}
					continue;
				}
				if (optionIfIsFalse) {
					continue;
				} else if (this.payload[i].name.startsWith('option')) {
					let args = this.payload[i].args;
					let hasComparitor = this.payload[i].name !== "option" && this.payload[i].name !== "optiondefault";
					let argText = args[hasComparitor ? 2 : 1] || "";
					if (args.length === 0) {
						output += this.payload[i].contents.trim();
					} else {
						let extraComment = args[hasComparitor ? 4 : 3];
						extraComment = extraComment ? ' ' + extraComment : '';
						// We use a very crude heuristic for styling 'Enable'
						// and 'Disable' buttons differently.
						const isEnableOption = argText && (argText.startsWith("Enable") || argText === "Yes" || argText.startsWith("Allow"));
						const isDisableOption = argText && (argText.startsWith("Disable") || argText === "No" || argText.startsWith("Deny"));
						let className = "optionMacroOption ";
						if (isEnableOption) {
							className += "optionMacroEnable";
						} else if (isDisableOption) {
							className += "optionMacroDisable";
						}
						if (found_index !== i || hasMultipleOptionsWithSameValue) {
							let onClickChange = args[hasComparitor ? 3 : 2];
							onClickChange = onClickChange ? ', ' + onClickChange : '';
							output += `<span class="${className}">[[${argText}${extraComment}|${passageName}][${variable}=${JSON.stringify(args[hasComparitor ? 1 : 0])}${onClickChange}]]</span>`;
						} else if (showSelectedOption) {
							output += `<span class="optionMacroSelected ${className}">${argText}${extraComment}</span>`;
						}
					}
				} else if (this.payload[i].name === 'comment') {
					comment = this.payload[i].contents;
				}
			}
			jQuery(this.output).wiki(
				'' +
					'' + title + ' ' + description + "" +
					'' + output + "" +
					(comment ? '' + comment + "" : '') +
				'');
		}
		catch (ex) {
			return this.error('bad options expression: ' + ex.message);
		}
	}
});