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 < 1) { 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:
/**
 * @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]
					});
				}
			}
		}
	}
	const newSlave = clone(baseHeroSlave);
	deepAssign(newSlave, heroSlave);
	generatePuberty(newSlave);
	return newSlave;
};

repo_name: Raou/fc


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) {
	const V = State.variables;

	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 "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 "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 "cat tail":
		case "fox tail":
			return (V.toysBoughtButtPlugTails === 1);
		default:
			return true;
	}
};


code:
App.Facilities.Nursery.ChildSummary = function(child) {
	const V = State.variables;
	let r = ``;
	r += `${child} will stay in ${V.nurseryName} for another ${child.weeksLeft} weeks. `;
	r += SlaveSummary(child);
	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);
		}
	}
});