/* eslint-disable camelcase */
/* eslint-disable no-unused-vars */
// rewrite of the rules assistant options page in javascript
// uses an object-oriented widget pattern
// wrapped in a closure so as not to pollute the global namespace
// the widgets are generic enough to be reusable; if similar user interfaces are ported to JS, we could move the classes to the global scope

window.rulesAssistantOptions = (function() {
	"use strict";
	let V;
	/** @type {App.RA.Rule} */
	let current_rule;

	function rulesAssistantOptions(element) {
		V = State.variables;
		V.nextButton = "Back to Main";
		V.nextLink = "Main";
		V.returnTo = "Main";
		V.showEncyclopedia = 1;
		V.encyclopedia = "Personal Assistant";
		if (V.currentRule !== null) {
			const idx = V.defaultRules.findIndex(rule => rule.ID === V.currentRule);
			if (idx === -1) {
				current_rule = V.defaultRules[0];
			} else {
				current_rule = V.defaultRules[idx];
			}
		}
		const root = new Root(element);
	}

	function returnP(e) { return e.keyCode === 13; }

	function newRule(root) {
		const rule = emptyDefaultRule();
		V.defaultRules.push(rule);
		V.currentRule = rule.ID;
		reload(root);
	}

	function removeRule(root) {
		const idx = V.defaultRules.findIndex(rule => rule.ID === current_rule.ID);
		V.defaultRules.splice(idx, 1);
		if (V.defaultRules.length > 0) {
			const new_idx = idx < V.defaultRules.length ? idx : V.defaultRules.length - 1;
			V.currentRule = V.defaultRules[new_idx].ID;
		} else {
			V.currentRule = null;
		}
		reload(root);
	}

	function lowerPriority(root) {
		if (V.defaultRules.length === 1) { return; } // nothing to swap with
		const idx = V.defaultRules.findIndex(rule => rule.ID === current_rule.ID);
		if (idx === 0) { return; } // no lower rule
		arraySwap(V.defaultRules, idx, idx - 1);
		reload(root);
	}

	function higherPriority(root) {
		if (V.defaultRules.length === 1) { return; } // nothing to swap with
		const idx = V.defaultRules.findIndex(rule => rule.ID === current_rule.ID);
		if (idx === V.defaultRules.length - 1) { return; } // no higher rule
		arraySwap(V.defaultRules, idx, idx + 1);
		reload(root);
	}

	function changeName(name, root) {
		if (name === current_rule.name) { return; }
		current_rule.name = name;
		reload(root);
	}

	// reload the passage
	function reload(root) {
		const elem = root.element;
		elem.innerHTML = "";
		rulesAssistantOptions(elem);
	}

	const parse = {
		integer(string) {
			let n = parseInt(string, 10);
			return isNaN(n) ? 0 : n;
		},
		boobs(string) {
			return Math.clamp(parse.integer(string), 0, 48000);
		},
		butt(string) {
			return Math.clamp(parse.integer(string), 0, 20);
		},
		lips(string) {
			return Math.clamp(parse.integer(string), 0, 100);
		},
		dick(string) {
			return Math.clamp(parse.integer(string), 0, 30);
		},
		balls(string) {
			return Math.clamp(parse.integer(string), 0, 125);
		},
	};

	// the Element class wraps around a DOM element and adds extra functionality
	// this is safer than extending DOM objects directly
	// it also turns DOM manipulation into an implementation detail
	class Element {
		constructor(...args) {
			this.parent = null;
			this.element = this.render(...args);
			this.children = [];
		}

		appendChild(child) {
			child.parent = this;
			this.children.push(child);
			this.element.appendChild(child.element);
		}

		/**
		 * returns the first argument to simplify creation of basic container items
		 * @returns {*}
		 */
		render(...args) {
			return args[0];
		}

		remove() {
			const idx = this.parent.children.findIndex(child => child === this);
			this.parent.children.slice(idx, 1);
			this.element.remove();
		}
	}

	class Section extends Element {
		constructor(header, hidden = false) {
			super(header);
			this.hidey = this.element.querySelector("div");
			if (hidden) { this.toggle_hidey(); }
		}

		render(header) {
			const section = document.createElement("section");
			section.classList.add("rajs-section");
			const h1 = document.createElement("h1");
			h1.onclick = () => { this.toggle_hidey(); };
			h1.innerHTML = header;
			const hidey = document.createElement("div");
			section.appendChild(h1);
			section.appendChild(hidey);
			return section;
		}

		appendChild(child) {
			child.parent = this;
			this.children.push(child);
			this.hidey.appendChild(child.element);
		}

		toggle_hidey() {
			switch (this.hidey.style.display) {
				case "none":
					this.hidey.style.display = "initial";
					break;
				default:
					this.hidey.style.display = "none";
					break;
			}
		}
	}

	// list of clickable elements
	// has a short explanation (the prefix) and a value display
	// value display can optionally be an editable text input field
	// it can be "bound" to a variable by setting its "onchange" method
	class EditorWithShortcuts extends Element {
		/**
		 *
		 * @param {string} prefix
		 * @param {Array} [data]
		 * @param {boolean} [allowNullValue]
		 * @param {boolean} [editor]
		 * @param {...any} args
		 */
		constructor(prefix, data = [], allowNullValue = true, editor = false, ...args) {
			super(`${prefix}: `, editor, ...args);
			this.selectedItem = null;
			/** @protected */
			this._allowNullValue = allowNullValue;
			if (allowNullValue) {
				this.appendChild(new ListItem("No default setting", null));
			}
			data.forEach(item => this.appendChild(this._createListItem(...item)));
		}

		createEditor(...args) { return null; }

		render(prefix, editor, ...args) {
			const elem = document.createElement("div");
			const label = document.createElement("span");
			label.innerHTML = prefix;
			this.value = editor ? this.createEditor(...args) : document.createElement("strong");
			elem.appendChild(label);
			elem.appendChild(this.value);
			elem.classList.add("rajs-list");
			return elem;
		}

		inputEdited() {
			if (this.selectedItem) { this.selectedItem.deselect(); }
			this.propagateChange();
		}

		selectItem(item) {
			if (this.selectedItem) { this.selectedItem.deselect(); }
			this.selectedItem = item;
			this.setValue(item.data);
			this.propagateChange();
		}

		setValue(what) {
			const str = what === null ? "no default setting" : `${what}`;
			if (this.value.tagName === "INPUT") {
				this.value.value = str;
			} else {
				this.value.innerHTML = str;
			}
		}

		getData(what) {
			return (this.value.tagName === "INPUT" ? this.parse(this.value.value) : this.selectedItem.data);
		}

		// customizable input field parser / sanity checker
		parse(what) { return what; }

		propagateChange() {
			if (this.onchange instanceof Function) {
				this.onchange(this.getData());
			}
		}

		/**
		 * @private
		 * @param {string} display
		 * @param {any} data
		 * @returns {ListItem}
		 */
		_createListItem(display, data) {
			return new ListItem(display, data);
		}
	}

	// a clickable item of a list
	class ListItem extends Element {
		constructor(displayvalue, data) {
			super(displayvalue);
			this.data = data !== undefined ? data : displayvalue;
			this.selected = false;
		}

		render(displayvalue) {
			const elem = document.createElement("span");
			elem.classList.add("rajs-listitem");
			elem.innerHTML = displayvalue;
			elem.onclick = () => { return this.select(); };
			return elem;
		}

		select() {
			if (this.selected) { return false; }
			this.parent.selectItem(this);
			this.element.classList.add("selected");
			this.selected = true;
			return true;
		}

		deselect() {
			this.element.classList.remove("selected");
			this.selected = false;
		}
	}

	/**
	 * Automatically capitalizes shortcut labels
	 */
	class EditorWithShortcutsCapitalized extends EditorWithShortcuts {
		_createListItem(display, data) {
			return super._createListItem(capFirstChar(display), data);
		}
	}

	class List extends EditorWithShortcuts {
		constructor(prefix, data = [], allowNullValue = true, textinput = false) {
			super(prefix, data, allowNullValue, textinput);
			this.values = new Map();
			if (allowNullValue) {
				this.values.set(null, "no default setting");
			}
			data.forEach(d => {
				if (Array.isArray(d) && d.length > 1) {
					this.values.set(d[1], d[0]);
				} else {
					this.values.set(d, d);
				}
			});
			this.selectedValue = null;
		}

		createEditor() {
			let res = document.createElement("input");
			res.setAttribute("type", "text");
			res.classList.add("rajs-value"); //
			// call the variable binding when the input field is no longer being edited, and when the enter key is pressed
			res.onblur = () => {
				this.inputEdited();
			};
			res.onkeypress = (e) => {
				if (returnP(e)) { this.inputEdited(); }
			};
			return res;
		}

		getData(what) {
			return this.selectedValue;
		}

		setValue(what) {
			this.selectedValue = what;
			if (this.values.has(what)) {
				super.setValue(this.values.get(what));
			} else {
				super.setValue(what);
			}
		}
	}

	/**
	 * Automatically capitalizes shortcut labels
	 */
	class ListCapitalized extends List {
		_createListItem(display, data) {
			return super._createListItem(capFirstChar(display), data !== undefined ? data : display);
		}
	}

	class NumberRange extends EditorWithShortcutsCapitalized {
		/**
		 * @param {string} prefix
		 * @param {Array} [data=[]]
		 * @param {boolean} [allowNullValue=true]
		 * @param {number} [min=0]
		 * @param {number} [max=100]
		 * @param {boolean} [spinBox=false]
		 */
		constructor(prefix, data = [], allowNullValue = true, min = 0, max = 100, spinBox = false) {
			super(prefix, data, allowNullValue, spinBox, min, max);
		}

		createEditor(min, max) {
			function makeOp(op, ui) {
				return {op: op, ui: ui};
			}
			this.opSelector = document.createElement("select");
			for (const o of [makeOp('==', '='), makeOp('>=', "⩾"), makeOp('<=', '⩽'), makeOp('>', '>'), makeOp('<', '<')]) {
				let opt = document.createElement("option");
				opt.textContent = o.ui;
				opt.value = o.op;
				this.opSelector.appendChild(opt);
			}
			this.opSelector.classList.add("rajs-list");
			this.opSelector.onchange = () => {
				this.inputEdited();
			};

			this.numEditor = document.createElement("input");
			this.numEditor.setAttribute("type", "number");
			this.numEditor.setAttribute("min", min);
			this.numEditor.setAttribute("max", max);
			this.numEditor.classList.add("rajs-value"); //
			this.numEditor.onblur = () => {
				this.inputEdited();
			};
			this.numEditor.onkeypress = (e) => {
				if (returnP(e)) this.inputEdited();
			};

			const res = document.createElement("span");
			res.appendChild(this.opSelector);
			res.appendChild(this.numEditor);
			return res;
		}

		parse(what) {
			return what === "" ? null : parseInt(what);
		}

		setValue(what) {
			if (typeof what === 'number') { // comes from a pre-set
				this.numEditor.value = what.toString();
			} else if (what === null) {
				this.numEditor.value = null;
				this.opSelector.value = '==';
			} else if (typeof what === 'object') {
				this.numEditor.value = what.val;
				this.opSelector.value = what.cond;
			}
		}

		getData(what) {
			const v = this.parse(this.numEditor.value);
			return v === null ? null : App.RA.makeTarget(this.opSelector.value, v);
		}
	}

	// a way to organize lists with too many elements in subsections
	// children are bound to the master list
	class ListSubSection extends Element {
		constructor(parent, label, pairs) {
			super(label);
			this.parent = parent;
			pairs.forEach(item => this.appendChild(new ListItem(...item)));
		}

		render(label) {
			const elem = document.createElement("div");
			const lelem = document.createElement("em");
			lelem.innerText = `${label}: `;
			elem.appendChild(lelem);
			return elem;
		}

		appendChild(child) {
			super.appendChild(child);
			child.parent = this.parent;
			this.parent.children.push(child);
		}
	}

	// similar to list, but is just a collection of buttons
	class Options extends Element {
		constructor(elements = []) {
			super();
			elements.forEach(element => { this.appendChild(element); });
		}

		render() {
			const elem = document.createElement("div");
			elem.classList.add("rajs-list");
			return elem;
		}
	}

	// options equivalent of ListItem
	class OptionsItem extends Element {
		constructor(label, onclick) {
			super(label);
			this.label = label;
			this.onclick = onclick;
		}
		render(label, onclick) {
			const elem = document.createElement("span");
			elem.classList.add("rajs-listitem");
			elem.innerHTML = label;
			elem.onclick = () => { return this.onclick(this); };
			return elem;
		}
	}

	class ButtonList extends Element {
		render(label) {
			const elem = document.createElement("div");
			const labelel = document.createElement("span");
			labelel.innerHTML = label += ": ";
			elem.appendChild(labelel);
			return elem;
		}

		getSelection() {
			return (this.children
				.filter(child => child.selected)
				.map(child => child.setvalue)
			);
		}

		onchange() { return; }
	}

	class ButtonItem extends Element {
		constructor(label, setvalue, selected = false) {
			super(label, selected);
			this.selected = selected;
			this.setvalue = setvalue ? setvalue : label;
		}

		render(label, selected) {
			const container = document.createElement("div");
			container.classList.add("rajs-listitem");

			const labelel = document.createElement("span");
			labelel.innerHTML = label;

			const button = document.createElement("input");
			button.setAttribute("type", "checkbox");
			button.checked = selected;
			button.onchange = () => this.onchange(button.checked);
			labelel.onclick = () => button.click();

			container.appendChild(labelel);
			container.appendChild(button);

			return container;
		}

		onchange(value) {
			this.selected = value;
			this.parent.onchange(this);
		}
	}

	// rule import field
	class NewRuleField extends Element {
		constructor(root) {
			super();
			this.root = root;
		}

		render() {
			let element = document.getElementById("importfield");
			if (element !== null) {
				return element;
			}
			const container = document.createElement("div");
			container.id = "importfield";
			const textarea = document.createElement("textarea");
			textarea.placeholder = "Paste your rule here";
			container.appendChild(textarea);
			this.textarea = textarea;
			const button = document.createElement("button");
			button.name = "Load";
			button.innerHTML = "Load";
			button.onclick = () => { this.loadNewRule(); };
			container.appendChild(button);
			return container;
		}

		loadNewRule() {
			const text = this.textarea.value;
			try {
				const rule = JSON.parse(text);
				if (rule instanceof Array) {
					rule.forEach(r => {
						V.defaultRules.push(App.Entity.Utils.RARuleDatatypeCleanup(r));
					});
				} else {
					V.defaultRules.push(App.Entity.Utils.RARuleDatatypeCleanup(rule));
				}
				reload(this.root);
			} catch (e) {
				alert(`Couldn't import that rule:\n${e.message}`);
			}
		}
	}

	// the base element, parent of all elements
	class Root extends Element {
		constructor(element) {
			super(element);
			if (V.defaultRules.length === 0) {
				const paragraph = document.createElement("p");
				paragraph.innerHTML = "<strong>No rules</strong>";
				this.appendChild(new Element(paragraph));
				this.appendChild(new NoRules(this));
				return;
			}
			this.appendChild(new RuleSelector(this));
			this.appendChild(new RuleOptions(this));
			this.appendChild(new ConditionEditor(this));
			this.appendChild(new EffectEditor(this));
		}

		render(element) {
			const greeting = document.createElement("p");
			greeting.innerHTML = `<em>${properTitle()}, I will review your slaves and make changes that will have a beneficial effect. Apologies, ${properTitle()}, but this function is... not fully complete. It may have some serious limitations. Please use the 'no default setting' option to identify areas I should not address.</em>`;
			element.appendChild(greeting);
			return element;
		}
	}

	// options displayed when there are no rules
	class NoRules extends Options {
		constructor(root) {
			super();
			this.root = root;
			const newrule = new OptionsItem("Add a new rule", () => { newRule(this.root); });
			this.appendChild(newrule);
			const importrule = new OptionsItem("Import a rule", () => { this.root.appendChild(new NewRuleField(this.root)); });
			this.appendChild(importrule);
		}
	}

	// buttons for selecting the current rule
	class RuleSelector extends List {
		constructor(root) {
			super("Current rule", V.defaultRules.map(i => [i.name, i]), false);
			this.setValue(current_rule.name);
			this.onchange = function(rule) {
				V.currentRule = rule.ID;
				reload(root);
			};
		}
	}

	// buttons for doing transformations on rules
	class RuleOptions extends Options {
		constructor(root) {
			super();
			this.appendChild(new OptionsItem("New Rule", () => newRule(root)));
			this.appendChild(new OptionsItem("Remove Rule", () => removeRule(root)));
			this.appendChild(new OptionsItem("Apply rules", () => this.appendChild(new ApplicationLog())));
			this.appendChild(new OptionsItem("Lower Priority", () => lowerPriority(root)));
			this.appendChild(new OptionsItem("Higher Priority", () => higherPriority(root)));
			this.appendChild(new OptionsItem("Rename", () => this.appendChild(new RenameField(root))));
			this.appendChild(new OptionsItem("Export this rule", () => this.appendChild(new ExportField(current_rule))));
			this.appendChild(new OptionsItem("Export all rules", () => this.appendChild(new ExportField(...V.defaultRules))));
			this.appendChild(new OptionsItem("Import rule(s)", () => this.appendChild(new NewRuleField(root))));
		}
	}

	class ApplicationLog extends Element {
		render() {
			const elem = document.querySelector("#application-log") || document.createElement("div");
			elem.id = "application-log";
			clearSummaryCache();
			elem.innerHTML = V.slaves.map(slave => DefaultRules(slave)).join("");
			return elem;
		}
	}

	class RenameField extends Element {
		constructor(root) {
			super();
			this.element.onblur = () => changeName(this.element.value, root);
			this.element.onkeypress = (e) => { if (returnP(e)) { changeName(this.element.value, root); } };
		}

		render() {
			const elem = document.createElement("input");
			elem.setAttribute("type", "text");
			elem.setAttribute("value", current_rule.name);
			return elem;
		}
	}

	class ExportField extends Element {
		render(...args) {
			let element = document.getElementById("exportfield");
			if (element === null) {
				element = document.createElement("textarea");
				element.id = "exportfield";
			}
			element.value = JSON.stringify(args, null, 2);
			return element;
		}
	}

	// parent section for condition editing
	class ConditionEditor extends Section {
		constructor() {
			super("Activation Condition");
			this.appendChild(new ConditionFunction());
			this.appendChild(new AssignmentInclusion());
			this.appendChild(new SpecialInclusion());
			this.appendChild(new SpecificInclusionExclusion());
		}
	}

	class ConditionFunction extends Element {
		constructor() {
			super();
			const items = [
				["Never", false],
				["Always", true],
				["Custom", "custom"],
				["Devotion", "devotion"],
				["Trust", "trust"],
				["Health", "health"],
				["Sex", "genes"],
				["Sex drive", "energy"],
				["Height", "height"],
				["Weight", "weight"],
				["Age", "actualAge"],
				["Body Age", "physicalAge"],
				["Visible Age", "visualAge"],
				["Muscles", "muscles"],
				["Lactation", "lactation"],
				["Pregnancy", "preg"],
				["Pregnancy Multiples", "pregType"],
				["Belly Implant", "bellyImplant"],
				["Belly Size", "belly"],
				["Education", "intelligenceImplant"],
				["Intelligence", "intelligence"],
				["Fetish", "fetish"],
				["Accent", "accent"],
				["Waist", "waist"],
				["Amputation", "amp"],
			];
			this.fnlist = new List("Activation function", items);
			this.fnlist.setValue(current_rule.condition.function === "between" ? current_rule.condition.data.attribute : current_rule.condition.function);
			this.fnlist.onchange = (value) => this.fnchanged(value);
			this.appendChild(this.fnlist);
			this.fneditor = null;

			switch (current_rule.condition.function) {
				case false:
				case true:
					break;
				case "custom":
					this.show_custom_editor(CustomEditor, current_rule.condition.data);
					break;
				case "between":
					this.show_custom_editor(RangeEditor, current_rule.condition.function, current_rule.condition.data);
					break;
				case "belongs":
					this.show_custom_editor(ItemEditor, current_rule.condition.function, current_rule.condition.data);
					break;
			}
		}

		betweenP(attribute) {
			return [
				"devotion",
				"trust",
				"health",
				"energy",
				"height",
				"weight",
				"actualAge",
				"physicalAge",
				"visualAge",
				"muscles",
				"lactation",
				"preg",
				"pregType",
				"bellyImplant",
				"belly",
				"intelligenceImplant",
				"intelligence",
				"accent",
				"waist",
			].includes(attribute);
		}

		belongsP(attribute) {
			return [
				"fetish",
				"amp",
				"genes",
			].includes(attribute);
		}

		show_custom_editor(what, ...args) {
			if (this.custom_editor !== null) { this.hide_custom_editor(); }
			this.custom_editor = new what(...args);
			this.appendChild(this.custom_editor);
		}

		hide_custom_editor() {
			if (this.custom_editor) {
				this.custom_editor.remove();
				this.custom_editor = null;
			}
		}

		render() {
			const elem = document.createElement("div");
			return elem;
		}

		fnchanged(value) {
			if (this.fneditor !== null) {
				this.fneditor.element.remove();
				this.fneditor = null;
			}
			if (value === true || value === false) {
				current_rule.condition.function = value;
				current_rule.condition.data = {};
				this.hide_custom_editor();
			} else if (value === "custom") {
				current_rule.condition.function = "custom";
				current_rule.condition.data = "";
				this.show_custom_editor(CustomEditor, current_rule.condition.data);
			} else if (this.betweenP(value)) {
				current_rule.condition.function = "between";
				current_rule.condition.data = {attribute: value, value: [null, null]};
				this.show_custom_editor(RangeEditor, current_rule.condition.function, current_rule.condition.data);
			} else if (this.belongsP(value)) {
				current_rule.condition.function = "belongs";
				current_rule.condition.data = {attribute: value, value: []};
				this.show_custom_editor(ItemEditor, current_rule.condition.function, current_rule.condition.data);
			}
		}
	}

	class CustomEditor extends Element {
		constructor(data) {
			if (data.length === 0) { data = "(slave) => slave.slaveName === 'Fancy Name'"; }
			super(data);
		}

		render(data) {
			const elem = document.createElement("div");
			const textarea = document.createElement("textarea");
			textarea.innerHTML = data;
			textarea.onblur = () => current_rule.condition.data = textarea.value;
			elem.appendChild(textarea);
			const explanation = document.createElement("div");
			explanation.innerHTML = "Insert a valid <a target='_blank' class='link-external' href='https://www.w3schools.com/js/js_comparisons.asp'>JavaScript comparison and/or logical operation</a>.";
			elem.appendChild(explanation);
			return elem;
		}
	}

	class RangeEditor extends Element {
		render(fn, data) {
			const elem = document.createElement("div");

			const minlabel = document.createElement("label");
			minlabel.innerHTML = "Lower bound: ";
			elem.appendChild(minlabel);

			const min = document.createElement("input");
			min.setAttribute("type", "text");
			min.value = `${data.value[0]}`;
			min.onkeypress = e => { if (returnP(e)) { this.setmin(min.value); } };
			min.onblur = e => this.setmin(min.value);
			this.min = min;
			elem.appendChild(min);

			elem.appendChild(document.createElement("br"));

			const maxlabel = document.createElement("label");
			maxlabel.innerHTML = "Upper bound: ";
			elem.appendChild(maxlabel);

			const max = document.createElement("input");
			max.setAttribute("type", "text");
			max.value = `${data.value[1]}`;
			max.onkeypress = e => { if (returnP(e)) { this.setmax(max.value); } };
			max.onblur = e => this.setmax(max.value);
			this.max = max;
			elem.appendChild(max);

			const infobar = document.createElement("div");
			infobar.innerHTML = this.info(data.attribute);
			elem.appendChild(infobar);

			return elem;
		}

		parse(value) {
			value = value.trim();
			if (value === "null") {
				value = null;
			} else {
				value = parseInt(value);
				if (isNaN(value)) { value = null; }
			}
			return value;
		}

		setmin(value) {
			current_rule.condition.data.value[0] = this.parse(value);
			this.min.value = `${current_rule.condition.data.value[0]}`;
		}

		setmax(value) {
			current_rule.condition.data.value[1] = this.parse(value);
			this.max.value = `${current_rule.condition.data.value[1]}`;
		}

		info(attribute) {
			return ({
				"devotion": "Very Hateful: (-∞, -95), Hateful: [-95, -50), Resistant: [-50, -20), Ambivalent: [-20, 20], Accepting: (20, 50], Devoted: (50, 95], Worshipful: (95, ∞)",
				"trust": "Extremely terrified: (-∞, -95), Terrified: [-95, -50), Frightened: [-50, -20), Fearful: [-20, 20], Careful: (20, 50], Trusting: (50, 95], Total trust: (95, ∞)",
				"health": "Death: (-∞, -100), Near Death: [-100, -90), Extremely Unhealthy: [-90, -50), Unhealthy: [-50, -20), Healthy: [-20, 20], Very Healthy: (20, 50], Extremely Healthy: (50, 90], Unnaturally Healthy: (90, ∞)",
				"energy": "Frigid: (-∞, 20], Poor: (20, 40], Average: (40, 60], Powerful: (60, 80], Sex Addict: (80, 100), Nympho: 100",
				"weight": "Emaciated: (-∞, -95), Skinny: [-95, -30), Thin: [-30, -10), Average: [-10, 10], Plush: (10, 30], Fat: (30, 95], Overweight: (95, ∞)",
				"lactation": "None: 0, 1: Natural, 2: Lactation implant",
				"preg": "Barren: -2, On contraceptives: -1, Not pregnant: 0, Pregnancy weeks: [1, ∞)",
				"pregType": "Fetus count, known only after the 10th week of pregnancy",
				"bellyImplant": "Volume in CCs. None: -1",
				"belly": "Volume in CCs, any source",
				"intelligenceImplant": "Education level. 0: uneducated, 15: educated, 30: advanced education, (0, 15): incomplete education.",
				"intelligence": "From moronic to brilliant: [-100, 100]",
				"accent": "No accent: 0, Nice accent: 1, Bad accent: 2, Can't speak language: 3 and above",
				"waist": "Masculine waist: (95, ∞), Ugly waist: (40, 95], Unattractive waist: (10, 40], Average waist: [-10, 10], Feminine waist: [-40, -10), Wasp waist: [-95, -40), Absurdly narrow: (-∞, -95)",
			} [attribute] || " ");
		}
	}

	class ItemEditor extends Element {
		render(fn, data) {
			const elem = document.createElement("div");

			const input = document.createElement("input");
			input.setAttribute("type", "text");
			input.value = JSON.stringify(data.value);
			input.onkeypress = e => { if (returnP(e)) { this.setValue(input); } };
			input.onblur = e => this.setValue(input);
			this.input = input;
			elem.appendChild(input);

			const infobar = document.createElement("div");
			infobar.innerHTML = this.info(data.attribute);
			elem.appendChild(infobar);

			return elem;
		}

		info(attribute) {
			return `Insert a valid JSON array. Known values: ${{
				"fetish": "buttslut, cumslut, masochist, sadist, dom, submissive, boobs, pregnancy, none (AKA vanilla)",
				"amp": "Amputated: 1, Not amputated: 0",
				"genes": "XX, XY",
			}[attribute]}`;
		}

		setValue(input) {
			try {
				const arr = JSON.parse(input.value);
				current_rule.condition.data.value = arr;
				input.value = JSON.stringify(arr);
			} catch (e) {
				alert(e);
			}
		}
	}


	class AssignmentInclusion extends ButtonList {
		constructor() {
			super("Apply to assignments and facilities");
			const items = ["Classes", "Confined", "Fucktoy", "Gloryhole", "House Servant", "Milked", "Public Servant", "Rest", "Subordinate Slave", "Whore"];
			if (V.HGSuite > 0) {
				items.push("Head Girl Suite");
			}
			if (V.brothel > 0) {
				items.push("Brothel");
			}
			if (V.club > 0) {
				items.push("Club");
			}
			if (V.arcade > 0) {
				items.push("Arcade");
			}
			if (V.dairy > 0) {
				items.push("Dairy");
			}
			if (V.servantsQuarters > 0) {
				items.push("Servant Quarters");
			}
			if (V.masterSuite > 0) {
				items.push("Master Suite");
			}
			if (V.schoolroom > 0) {
				items.push("Schoolroom");
			}
			if (V.spa > 0) {
				items.push("Spa");
			}
			if (V.nursery > 0) {
				items.push("Nursery");
			}
			if (V.clinic > 0) {
				items.push("Clinic");
			}
			if (V.cellblock > 0) {
				items.push("Cellblock");
			}
			items.forEach(
				i => this.appendChild(new ButtonItem(i, this.getAttribute(i), current_rule.condition.assignment.includes(this.getAttribute(i)))));
		}

		onchange() {
			current_rule.condition.assignment = this.getSelection();
		}

		getAttribute(what) {
			return {
				"Rest": "rest",
				"Fucktoy": "please you",
				"Subordinate Slave": "be a subordinate slave",
				"House Servant": "be a servant",
				"Confined": "stay confined",
				"Whore": "whore",
				"Public Servant": "serve the public",
				"Classes": "take classes",
				"Milked": "get milked",
				"Gloryhole": "work a glory hole",
				"Head Girl Suite": "live with your Head Girl",
				"Brothel": "work in the brothel",
				"Club": "serve in the club",
				"Arcade": "be confined in the arcade",
				"Dairy": "work in the dairy",
				"Farmyard": "work as a farmhand",
				"Servant Quarters": "work as a servant",
				"Master Suite": "serve in the master suite",
				"Schoolroom": "learn in the schoolroom",
				"Spa": "rest in the spa",
				"Nursery": "work as a nanny",
				"Clinic": "get treatment in the clinic",
				"Cellblock": "be confined in the cellblock",
			} [what];
		}
	}

	class SpecialInclusion extends ListCapitalized {
		constructor() {
			const items = [
				["include", -1],
				["exclude", 0],
				["only", 1]
			];
			super("Special slaves", items);
			this.setValue(current_rule.condition.specialSlaves);
			this.onchange = (value) => current_rule.condition.specialSlaves = value;
		}
	}

	class SpecificInclusionExclusion extends Options {
		constructor() {
			super();
			this.appendChild(new OptionsItem("Limit to specific slaves", () => this.show_slave_selection()));
			this.appendChild(new OptionsItem("Exclude specific slaves", () => this.show_slave_exclusion()));
			this.subwidget = null;
		}

		show_slave_selection() {
			if (this.subwidget) { this.subwidget.remove(); }
			this.subwidget = new SlaveSelection();
			this.appendChild(this.subwidget);
		}

		show_slave_exclusion() {
			if (this.subwidget) { this.subwidget.remove(); }
			this.subwidget = new SlaveExclusion();
			this.appendChild(this.subwidget);
		}
	}

	class SlaveSelection extends ButtonList {
		constructor() {
			super("Include specific slaves");
			V.slaves.forEach(slave => this.appendChild(new ButtonItem(
				[slave.slaveName, slave.slaveSurname].join(" "),
				slave.ID,
				current_rule.condition.selectedSlaves.includes(slave.ID))));
		}

		onchange() {
			current_rule.condition.selectedSlaves = this.getSelection();
		}
	}

	class SlaveExclusion extends ButtonList {
		constructor() {
			super("Exclude specific slaves");
			V.slaves.forEach(slave => this.appendChild(new ButtonItem(
				[slave.slaveName, slave.slaveSurname].join(" "),
				slave.ID,
				current_rule.condition.excludedSlaves.includes(slave.ID))));
		}

		onchange() {
			current_rule.condition.excludedSlaves = this.getSelection();
		}
	}

	// parent section for effect editing
	class EffectEditor extends Element {
		constructor() {
			super();
			this.appendChild(new AppearanceSection(true));
			this.appendChild(new CosmeticSection(true));
			this.appendChild(new BodyModSection(true));
			this.appendChild(new AutosurgerySection(true));
			this.appendChild(new RegimenSection(true));
			this.appendChild(new BehaviourSection(true));
			this.appendChild(new OtherSection(true));
		}

		render() {
			const element = document.createElement("div");
			return element;
		}
	}

	class AppearanceSection extends Section {
		constructor(isCollapsed) {
			super("Appearance Settings", isCollapsed);
			this.appendChild(new ClothesList());
			this.appendChild(new CollarList());
			this.appendChild(new ShoeList());
			this.appendChild(new CorsetList());
			this.appendChild(new LeggingsList());
			this.appendChild(new VagChastityList());
			this.appendChild(new VagAccVirginsList());
			this.appendChild(new VagAccAVirginsList());
			this.appendChild(new VagAccOtherList());
			this.appendChild(new VaginalAttachmentsList());
			if (V.seeDicks !== 0 || V.makeDicks !== 0) {
				this.appendChild(new DickChastityList());
				this.appendChild(new DickAccVirginsList());
				this.appendChild(new DickAccOtherList());
			}
			this.appendChild(new AnalChastityList());
			this.appendChild(new ButtplugsVirginsList());
			this.appendChild(new ButtplugsOtherList());
			this.appendChild(new ButtplugAttachmentsList());
			this.appendChild(new ImplantVolumeList());
		}
	}

	class RegimenSection extends Section {
		constructor(isCollapsed) {
			super("Physical Regimen Settings", isCollapsed);
			if (V.arcologies[0].FSAssetExpansionistResearch === 1)
				this.appendChild(new HyperGrowthSwitch());
			this.appendChild(new IntensiveGrowthSwitch());
			this.appendChild(new GrowthList());
			this.appendChild(new CurrativesList());
			this.appendChild(new AphrodisiacList());
			this.appendChild(new ContraceptiveList());
			this.appendChild(new AbortionList());
			if (V.pregSpeedControl) {
				this.appendChild(new PregDrugsList());
			}
			this.appendChild(new FemaleHormonesList());
			this.appendChild(new ShemaleHormonesList());
			this.appendChild(new GeldingHormonesList());
			this.appendChild(new OtherDrugsList());
			if (V.enema === 1) {
				this.appendChild(new EnemaList());
			}
			this.appendChild(new DietList());
			this.appendChild(new DietGrowthList());
			this.appendChild(new DietBaseList());
			if (V.arcologies[0].FSHedonisticDecadenceResearch === 1) {
				this.appendChild(new DietSolidFoodList());
			}
			this.appendChild(new MuscleList());
			this.appendChild(new BraceList());
		}
	}

	class BehaviourSection extends Section {
		constructor(isCollapsed) {
			super("Behavior Settings", isCollapsed);
			this.appendChild(new AutomaticAssignmentList());
			this.appendChild(new LivingStandardList());
			this.appendChild(new PunishmentList());
			this.appendChild(new RewardList());
			this.appendChild(new ReleaseList());
			this.appendChild(new ToyHoleList());
			this.appendChild(new SmartFetishList());
			this.appendChild(new SmartXYAttractionList());
			this.appendChild(new SmartXXAttractionList());
			this.appendChild(new SmartEnergyList());
			this.appendChild(new SpeechList());
			this.appendChild(new RelationshipList());
			if (V.studio === 1) {
				this.appendChild(new PornBroadcastStatus());
				this.appendChild(new PornList());
			}
		}
	}

	class OtherSection extends Section {
		constructor(isCollapsed) {
			super("Other Settings", isCollapsed);
			this.appendChild(new LabelList());
			this.appendChild(new LabelRemoveList());
		}
	}

	class CosmeticSection extends Section {
		constructor(isCollapsed) {
			super("Cosmetic Settings", isCollapsed);
			this.appendChild(new EyewearList());
			this.appendChild(new LensesList());
			this.appendChild(new EarwearList());
			this.appendChild(new MakeupList());
			this.appendChild(new NailsList());
			this.appendChild(new HairLengthList());
			this.appendChild(new HaircutsList());
			this.appendChild(new HairColorList());
			this.appendChild(new HairStyleList());
			this.appendChild(new EyebrowColorList());
			this.appendChild(new EyebrowStyleList());
			this.appendChild(new EyebrowFullnessList());
			this.appendChild(new PubicHairColorList());
			this.appendChild(new PubicHairStyleList());
			this.appendChild(new ArmpitHairColorList());
			this.appendChild(new ArmpitHairStyleList());
			this.appendChild(new SkinColorList());
			this.appendChild(new MarkingsList());
		}
	}

	class BodyModSection extends Section {
		constructor(isCollapsed) {
			super("Body Mod Settings", isCollapsed);
			this.appendChild(new EarPiercingList());
			this.appendChild(new NosePiercingList());
			this.appendChild(new EyebrowPiercingList());
			this.appendChild(new NavelPiercingList());
			this.appendChild(new NipplePiercingList());
			this.appendChild(new AreolaPiercingList());
			this.appendChild(new LipPiercingList());
			this.appendChild(new TonguePiercingList());
			this.appendChild(new ClitPiercingList());
			this.appendChild(new LabiaPiercingList());
			this.appendChild(new ShaftPiercingList());
			this.appendChild(new PerineumPiercingList());
			this.appendChild(new CorsetPiercingList());

			this.appendChild(new AutoBrandingList());
			this.appendChild(new BrandingLocationList());
			this.appendChild(new BrandDesignList());

			this.appendChild(new FaceTattooList());
			this.appendChild(new ShoulderTattooList());
			this.appendChild(new ChestTattooList());
			this.appendChild(new ArmTattooList());
			this.appendChild(new UpperBackTattooList());
			this.appendChild(new LowerBackTattooList());
			this.appendChild(new AbdomenTattooList());
			if (V.seeDicks || V.makeDicks) {
				this.appendChild(new DickTattooList());
			}
			this.appendChild(new ButtockTattooList());
			this.appendChild(new AnalTattooList());
			this.appendChild(new LegTattooList());
		}
	}

	class AutosurgerySection extends Section {
		constructor() {
			super("Autosurgery Settings", true);
			this.appendChild(new AutosurgerySwitch());
			this.appendChild(new VisionSurgeryList());
			this.appendChild(new HearingSurgeryList());
			this.appendChild(new SmellSurgeryList());
			this.appendChild(new TasteSurgeryList());
			this.appendChild(new LactationSurgeryList());
			if (V.seeDicks || V.makeDicks) {
				this.appendChild(new SemenSurgeryList());
				this.appendChild(new VasectomyList());
			}
			this.appendChild(new CosmeticSurgeryList());
			this.appendChild(new LipSurgeryList());
			this.appendChild(new ButtSurgeryList());
			this.appendChild(new BreastSurgeryList());
			this.appendChild(new TighteningSurgeryList());
			this.appendChild(new TummyTuckSurgeryList());
			this.appendChild(new BodyHairSurgeryList());
			this.appendChild(new HairSurgeryList());
			if (V.bellyImplants > 0) {
				this.appendChild(new BellyImplantList());
			}
		}
	}

	class ClothesList extends List {
		constructor() {
			const items = [
				["Select her own outfit", "choosing her own clothes"]
			];
			super("Clothes", items);

			const nclothes = [
				["Apron", "an apron"],
				["Ballgown", "a ball gown"],
				["Bangles", "slutty jewelry"],
				["Bodysuit", "a comfortable bodysuit"],
				["Boyshorts", "boyshorts"],
				["Bra", "a bra"],
				["Button-up shirt and panties", "a button-up shirt and panties"],
				["Button-up shirt", "a button-up shirt"],
				["Cheerleader", "a cheerleader outfit"],
				["Clubslut netting", "clubslut netting"],
				["Cutoffs and a t-shirt", "cutoffs and a t-shirt"],
				["Cutoffs", "cutoffs"],
				["Cybersuit", "a cybersuit"],
				["Fallen nun", "a fallen nuns habit"],
				["Halter top dress", "a halter top dress"],
				["Hijab and abaya", "a hijab and abaya"],
				["Jeans", "jeans"],
				["Kitty lingerie", "kitty lingerie"],
				["Latex catsuit", "a latex catsuit"],
				["Leather pants and a tube top", "leather pants and a tube top"],
				["Leather pants and pasties", "leather pants and pasties"],
				["Leather pants", "leather pants"],
				["Leotard", "a leotard"],
				["Maid (nice)", "a nice maid outfit"],
				["Maid (slutty)", "a slutty maid outfit"],
				["Mini dress", "a mini dress"],
				["Monokini", "a monokini"],
				["Nice lingerie", "attractive lingerie"],
				["Nurse (nice)", "a nice nurse outfit"],
				["Nurse (slutty)", "a slutty nurse outfit"],
				["One-piece swimsuit", "a one-piece swimsuit"],
				["Overalls", "overalls"],
				["Over-sized t-shirt and boyshorts", "an oversized t-shirt and boyshorts"],
				["Over-sized t-shirt", "an oversized t-shirt"],
				["Panties", "panties"],
				["Pasties", "pasties"],
				["Pasties and panties", "panties and pasties"],
				["Scalemail bikini", "a scalemail bikini"],
				["Schoolgirl", "a schoolgirl outfit"],
				["Slave gown", "a slave gown"],
				["Slutty outfit", "a slutty outfit"],
				["Spats and tank top", "spats and a tank top"],
				["Sport shorts and a sports bra", "sport shorts and a sports bra"],
				["Sport shorts and a t-shirt", "sport shorts and a t-shirt"],
				["Sport shorts", "sport shorts"],
				["Sports bra", "a sports bra"],
				["String bikini", "a string bikini"],
				["Succubus costume", "a succubus outfit"],
				["Suit (nice)", "nice business attire"],
				["Suit (slutty)", "slutty business attire"],
				["Sweater and cutoffs", "a sweater and cutoffs"],
				["Sweater and panties", "a sweater and panties"],
				["Sweater", "a sweater"],
				["T-shirt and jeans", "a t-shirt and jeans"],
				["T-shirt and panties", "a t-shirt and panties"],
				["T-shirt and thong", "a t-shirt and thong"],
				["T-shirt", "a t-shirt"],
				["Tank-top and panties", "a tank-top and panties"],
				["Tank-top", "a tank-top"],
				["Thong", "a thong"],
				["Tube top and thong", "a tube top and thong"],
				["Tube top", "a tube top"]
			];
			const spclothes = [
				["Battlearmor", "battlearmor"],
				["Biyelgee costume", "a biyelgee costume"],
				["Burkini", "a burkini"],
				["Burqa", "a burqa"],
				["Dirndl", "a dirndl"],
				["Gothic Lolita Dress", "a gothic lolita dress"],
				["Hanbok", "a hanbok"],
				["Hijab and blouse", "a hijab and blouse"],
				["Ku Klux Klan Robe", "a klan robe"],
				["Ku Klux Klan Robe (slutty)", "a slutty klan robe"],
				["Lederhosen", "lederhosen"],
				["Mounty outfit", "a mounty outfit"],
				["Military uniform", "a military uniform"],
				["Niqab and abaya", "a niqab and abaya"],
				["Police Uniform", "a police uniform"],
				["Pony outfit (nice)", "a nice pony outfit"],
				["Pony outfit (slutty)", "a slutty pony outfit"],
				["Red Army uniform", "a red army uniform"],
				["Santa dress", "a Santa dress"],
				["Schutzstaffel uniform (nice)", "a schutzstaffel uniform"],
				["Schutzstaffel uniform (slutty)", "a slutty schutzstaffel uniform"],
				["Striped Bra", "a striped bra"],
				["Striped Panties", "striped panties"],
				["Striped Underwear", "striped underwear"],
				["Skimpy battledress", "battledress"],
				["Skimpy loincloth", "a skimpy loincloth"],
			];
			const fsnclothes = [
				["Body oil (FS)", "body oil"],
				["Bunny outfit (FS)", "a bunny outfit"],
				["Chattel habit (FS)", "a chattel habit"],
				["Conservative clothing (FS)", "conservative clothing"],
				["Harem gauze (FS)", "harem gauze"],
				["Huipil (FS)", "a huipil"],
				["Kimono (FS)", "a kimono"],
				["Maternity dress (FS)", "a maternity dress"],
				["Maternity lingerie (FS)", "attractive lingerie for a pregnant woman"],
				["Qipao (nice) (FS)", "a long qipao"],
				["Qipao (slutty) (FS)", "a slutty qipao"],
				["Stretch pants and a crop-top (FS)", "stretch pants and a crop-top"],
				["Toga (FS)", "a toga"],
				["Western clothing (FS)", "Western clothing"],
			];
			spclothes.forEach(pair => { if (isItemAccessible(pair[1])) { nclothes.push(pair); } });
			fsnclothes.forEach(pair => { if (isItemAccessible(pair[1])) { nclothes.push(pair); } });
			const nice = new ListSubSection(this, "Nice", nclothes);
			this.appendChild(nice);

			const hclothes = [
				["Nude", "no clothing"],
				["Penitent nun", "a penitent nuns habit"],
				["Restrictive latex", "restrictive latex"],
				["Shibari ropes", "shibari ropes"],
				["Uncomfortable straps", "uncomfortable straps"]
			];
			const fshclothes = [
				["Chains (FS)", "chains"],
			];
			fshclothes.forEach(pair => { if (isItemAccessible(pair[1])) { hclothes.push(pair); } });

			const harsh = new ListSubSection(this, "Harsh", hclothes);
			this.appendChild(harsh);

			this.setValue(current_rule.set.clothes);
			this.onchange = (value) => current_rule.set.clothes = value;
		}
	}

	class CollarList extends List {
		constructor() {
			const items = [
				["No collar", "none"],
			];
			super("Collar", items);

			const ncollars = [
				["Stylish leather", "stylish leather"],
				["Satin choker", "satin choker"],
				["Silken Ribbon", "silk ribbon"],
				["Heavy Gold", "heavy gold"],
				["Pretty jewelry", "pretty jewelry"],
				["Bell", "bell collar"],
				["Cowbell", "leather with cowbell"]
			];
			if (V.seeAge !== 0) { ncollars.push(["Nice retirement counter", "nice retirement counter"]); }
			const fsncollars = [
				["Bowtie collar", "bowtie"],
				["Ancient Egyptian", "ancient Egyptian"],
			];
			fsncollars.forEach(pair => { if (isItemAccessible(pair[1])) { ncollars.push(pair); } });
			const nice = new ListSubSection(this, "Nice", ncollars);
			this.appendChild(nice);

			const hcollars = [];
			setup.harshCollars.forEach(item => {
				if (item.fs === "seeAge" && V.seeAge === 0) {
					return;
				} else if (item.fs === "seePreg" && V.seePreg === 0) {
					return;
				} else if (item.rs === "buyGag" && V.toysBoughtGags !== 1) {
					return;
				} else {
					hcollars.push([item.name, item.value]);
				}
			});
			const harsh = new ListSubSection(this, "Harsh", hcollars);
			this.appendChild(harsh);

			this.setValue(current_rule.set.collar);
			this.onchange = (value) => current_rule.set.collar = value;
		}
	}

	class ShoeList extends List {
		constructor() {
			super("Shoes", setup.shoes.map(i => [i.name, i.value]));
			this.setValue(current_rule.set.shoes);
			this.onchange = (value) => current_rule.set.shoes = value;
		}
	}

	class CorsetList extends List {
		constructor() {
			const bellies = [];
			setup.bellyAccessories.forEach(acc => {
				if (acc.fs === undefined && acc.rs === undefined) {
					bellies.push([acc.name, acc.value]);
				} else if (acc.fs === "repopulation" && V.arcologies[0].FSRepopulationFocus !== "unset") {
					bellies.push([`${acc.name} (FS)`, acc.value]);
				} else if (acc.rs === "boughtBelly" && V.clothesBoughtBelly === 1) {
					bellies.push([`${acc.name} (Purchased)`, acc.value]);
}
			});
			super("Corsetage", bellies);
			this.setValue(current_rule.set.bellyAccessory);
			this.onchange = (value) => current_rule.set.bellyAccessory = value;
		}
	}

	class LeggingsList extends ListCapitalized {
		constructor() {
			const items = [
				["none"],
				["short stockings"],
				["long stockings"],
			];
			super("Leg accessory", items);
			this.setValue(current_rule.set.legAccessory);
			this.onchange = (value) => current_rule.set.legAccessory = value;
		}
	}

	class VagChastityList extends ListCapitalized {
		constructor() {
			const chaste = [
				["none", 0],
				["chastity", 1],
			];
			super("Vaginal chastity", chaste);
			this.setValue(current_rule.set.chastityVagina);
			this.onchange = (value) => current_rule.set.chastityVagina = value;
		}
	}

	class VagAccVirginsList extends List {
		constructor() {
			const accs = [];
			setup.vaginalAccessories.forEach(acc => {
				if (acc.fs === undefined && acc.rs === undefined) {
					accs.push([acc.name, acc.value]);
				} else if (acc.rs === "buyBigDildos" && V.toysBoughtDildos === 1) {
					accs.push([`${acc.name} (Purchased)`, acc.value]);
				}
			});
			super("Vaginal accessories for virgins", accs);
			this.setValue(current_rule.set.virginAccessory);
			this.onchange = (value) => current_rule.set.virginAccessory = value;
		}
	}

	class VagAccAVirginsList extends List {
		constructor() {
			const accs = [];
			setup.vaginalAccessories.forEach(acc => {
				if (acc.fs === undefined && acc.rs === undefined) {
					accs.push([acc.name, acc.value]);
				} else if (acc.rs === "buyBigDildos" && V.toysBoughtDildos === 1) {
					accs.push([`${acc.name} (Purchased)`, acc.value]);
				}
			});
			super("Vaginal accessories for anal virgins", accs);
			this.setValue(current_rule.set.aVirginAccessory);
			this.onchange = (value) => current_rule.set.aVirginAccessory = value;
		}
	}

	class VagAccOtherList extends List {
		constructor() {
			const accs = [];
			setup.vaginalAccessories.forEach(acc => {
				if (acc.fs === undefined && acc.rs === undefined) {
					accs.push([acc.name, acc.value]);
				} else if (acc.rs === "buyBigDildos" && V.toysBoughtDildos === 1) {
					accs.push([`${acc.name} (Purchased)`, acc.value]);
				}
			});
			super("Vaginal accessories for other slaves", accs);
			this.setValue(current_rule.set.vaginalAccessory);
			this.onchange = (value) => current_rule.set.vaginalAccessory = value;
		}
	}

	class VaginalAttachmentsList extends List {
		constructor() {
			const accs = [];
			setup.vaginalAttachments.forEach(acc => {
				if (acc.fs === undefined && acc.rs === undefined) {
					accs.push([acc.name, acc.value]);
				} else if (acc.rs === "buyVaginalAttachments" && V.toysBoughtVaginalAttachments === 1) {
					accs.push([`${acc.name} (Purchased)`, acc.value]);
				}
			});
			super("Vaginal attachments for slaves with vaginal accessories", accs);
			this.setValue(current_rule.set.vaginalAttachment);
			this.onchange = (value) => current_rule.set.vaginalAttachment = value;
		}
	}

	class DickChastityList extends ListCapitalized {
		constructor() {
			const items = [
				["none", 0],
				["chastity cage", 1],
			];
			super("Penile chastity", items);
			this.setValue(current_rule.set.chastityPenis);
			this.onchange = (value) => current_rule.set.chastityPenis = value;
		}
	}

	class DickAccVirginsList extends List {
		constructor() {
			super("Dick accessories for anal virgins", setup.dickAccessories.map(i => [i.name, i.value]));
			this.setValue(current_rule.set.aVirginDickAccessory);
			this.onchange = (value) => current_rule.set.aVirginDickAccessory = value;
		}
	}

	class DickAccOtherList extends List {
		constructor() {
			super("Dick accessories for other slaves", setup.dickAccessories.map(i => [i.name, i.value]));
			this.setValue(current_rule.set.dickAccessory);
			this.onchange = (value) => current_rule.set.dickAccessory = value;
		}
	}

	class AnalChastityList extends ListCapitalized {
		constructor() {
			const items = [
				["none", 0],
				["chastity", 1],
			];
			super("Anal chastity", items);
			this.setValue(current_rule.set.chastityAnus);
			this.onchange = (value) => current_rule.set.chastityAnus = value;
		}
	}

	class ButtplugsVirginsList extends List {
		constructor() {
			const accs = [];
			setup.buttplugs.forEach(acc => {
				if (acc.fs === undefined && acc.rs === undefined) {
					accs.push([acc.name, acc.value]);
				} else if (acc.rs === "buyBigPlugs" && V.toysBoughtButtPlugs === 1) {
					accs.push([`${acc.name} (Purchased)`, acc.value]);
}
			});
			super("Buttplugs for anal virgins", accs);
			this.setValue(current_rule.set.aVirginButtplug);
			this.onchange = (value) => current_rule.set.aVirginButtplug = value;
		}
	}

	class ButtplugsOtherList extends List {
		constructor() {
			const accs = [];
			setup.buttplugs.forEach(acc => {
				if (acc.fs === undefined && acc.rs === undefined) {
					accs.push([acc.name, acc.value]);
				} else if (acc.rs === "buyBigPlugs" && V.toysBoughtButtPlugs === 1) {
					accs.push([`${acc.name} (Purchased)`, acc.value]);
}
			});
			super("Buttplugs for other slaves", accs);
			this.setValue(current_rule.set.buttplug);
			this.onchange = (value) => current_rule.set.buttplug = value;
		}
	}

	class ButtplugAttachmentsList extends List {
		constructor() {
			const accs = [];
			setup.buttplugAttachments.forEach(acc => {
				if (acc.fs === undefined && acc.rs === undefined) {
					accs.push([acc.name, acc.value]);
				} else if (acc.rs === "buyTails" && V.toysBoughtButtPlugTails === 1) {
					accs.push([`${acc.name} (Purchased)`, acc.value]);
}
			});
			super("Buttplug attachments for slaves with buttplugs", accs);
			this.setValue(current_rule.set.buttplugAttachment);
			this.onchange = (value) => current_rule.set.buttplugAttachment = value;
		}
	}

	class ImplantVolumeList extends ListCapitalized {
		constructor() {
			const pairs = [
				["no changes", -1],
				["empty implant", 0],
				["early pregnancy", 1500],
				["second trimester pregnancy", 5000],
				["full-term pregnancy", 15000],
				["full-term with twins", 30000],
				["full-term with triplets", 45000],
				["full-term with quadruplets", 60000],
				["full-term with quintuplets", 75000],
				["full-term with sextuplets", 90000],
				["full-term with septuplets", 105000],
				["full-term with octuplets", 120000]
			];
			super("Belly implant target volume (if present)", pairs, false);
			this.setValue(current_rule.set.bellyImplantVol);
			this.onchange = (value) => current_rule.set.bellyImplantVol = value;
		}
	}

	class AutosurgerySwitch extends ListCapitalized {
		constructor() {
			const pairs = [
				["on", 1],
				["off", 0],
			];
			super("Assistant-applied implants (Autosurgery global switch)", pairs, false);
			this.setValue(current_rule.set.autoSurgery);
			this.onchange = (value) => current_rule.set.autoSurgery = value;
		}
	}

	class IntensiveGrowthSwitch extends ListCapitalized {
		constructor() {
			const pairs = [
				["no", 0],
				["yes", 1],
			];
			super("Use intensive growth drugs for healthy slaves", pairs, false);
			this.setValue(current_rule.set.growth.intensity);
			this.onchange = (value) => current_rule.set.growth.intensity = value;
		}
	}

	class HyperGrowthSwitch extends ListCapitalized {
		constructor() {
			const pairs = [
				["no", 0],
				["yes", 1],
			];
			super("Use hyper growth drugs", pairs, false);
			this.setValue(current_rule.set.hyper_drugs);
			this.onchange = (value) => current_rule.set.hyper_drugs = value;
		}
	}

	class GrowthList extends Options {
		constructor() {
			super();
			this.sublists = [];
			const pairs = [
				["No default setting", () => this.nds()],
				["Girlish figure", () => this.girlish()],
				["Stacked figure", () => this.stacked()],
				["Huge but functional", () => this.huge()],
				["Unlimited", () => this.unlimited()],
				["None", () => this.none()]
			];
			pairs.forEach(pair => this.appendChild(new OptionsItem(...pair)));

			this.breasts = new BreastGrowthList();
			this.butts = new ButtGrowthList();
			this.lips = new LipGrowthList();
			this.sublists.push(this.breasts, this.butts, this.lips);

			if (V.seeDicks > 0 || V.makeDicks > 0) {
				this.dicks = new DickGrowthList();
				this.balls = new BallGrowthList();
				this.sublists.push(this.dicks, this.balls);
			}

			this.sublists.forEach(i => this.appendChild(i));
		}

		render() {
			const elem = document.createElement("div");
			const span = document.createElement("span");
			span.innerHTML = "Growth hormone regimes for healthy slaves: ";
			elem.appendChild(span);
			return elem;
		}

		nds() {
			[this.breasts, this.butts, this.lips, this.dicks, this.balls].forEach(i => {
				i.setValue(null);
				i.propagateChange();
			});
		}

		girlish() {
			this.breasts.setValue(App.RA.makeTarget('<=', 350));
			this.butts.setValue(App.RA.makeTarget('<=', 2));
			this.lips.setValue(App.RA.makeTarget('<=', 25));
			if (this.dicks) this.dicks.setValue(App.RA.makeTarget('==', 0));
			if (this.balls) this.balls.setValue(App.RA.makeTarget('==', 0));
			this.sublists.forEach(i => i.propagateChange());
		}

		stacked() {
			this.breasts.setValue(App.RA.makeTarget('>=', 1000));
			this.butts.setValue(App.RA.makeTarget('>=', 5));
			this.lips.setValue(App.RA.makeTarget('>=', 25));
			if (this.dicks) this.dicks.setValue(App.RA.makeTarget('>=', 4));
			if (this.balls) this.balls.setValue(App.RA.makeTarget('>=', 4));
			this.sublists.forEach(i => i.propagateChange());
		}

		huge() {
			this.breasts.setValue(App.RA.makeTarget('>=', 9000));
			this.butts.setValue(App.RA.makeTarget('>=', 10));
			this.lips.setValue(App.RA.makeTarget('>=', 45));
			if (this.dicks) this.dicks.setValue(App.RA.makeTarget('>=', 6));
			if (this.balls) this.balls.setValue(App.RA.makeTarget('>=', 6));
			this.sublists.forEach(i => i.propagateChange());
		}

		unlimited() {
			this.breasts.setValue(App.RA.makeTarget('>=', 48000));
			this.butts.setValue(App.RA.makeTarget('>=', 20));
			this.lips.setValue(App.RA.makeTarget('>=', 100));
			if (this.dicks) this.dicks.setValue(App.RA.makeTarget('>=', 30));
			if (this.balls) this.balls.setValue(App.RA.makeTarget('>=', 125));
			this.sublists.forEach(i => i.propagateChange());
		}

		none() {
			this.sublists.forEach(i => {
				i.setValue(App.RA.makeTarget('==', 0));
				i.propagateChange();
			});
		}
	}

	class BreastGrowthList extends NumberRange {
		constructor() {
			const pairs = [
				["B-Cup", 350],
				["D-Cup", 1000],
				["monstrous", 9000],
				["unlimited", 48000],
				["none", 0]
			];
			super("Breasts", pairs, true, 0, 48000, true);
			this.setValue(current_rule.set.growth.boobs);
			this.onchange = (value) => current_rule.set.growth.boobs = value;
		}
	}

	class ButtGrowthList extends NumberRange {
		constructor() {
			const pairs = [
				["cute", 2],
				["big", 4],
				["huge", 6],
				["unlimited", 20],
				["none", 0]
			];
			super("Butts", pairs, true, 0, 20, true);
			this.setValue(current_rule.set.growth.butt);
			this.onchange = (value) => current_rule.set.growth.butt = value;
		}
	}

	class LipGrowthList extends NumberRange {
		constructor() {
			const pairs = [
				["plump", 25],
				["beestung", 45],
				["facepussy", 100],
				["none", 0]
			];
			super("Lips", pairs, true, 0, 100, true);
			this.setValue(current_rule.set.growth.lips);
			this.onchange = (value) => current_rule.set.growth.lips = value;
		}
	}

	class DickGrowthList extends NumberRange {
		constructor() {
			const pairs = [
				["above average", 4],
				["pornstar", 6],
				["unlimited", 30],
				["none", 0]
			];
			super("Dicks, if present", pairs, true, 0, 30, true);
			this.setValue(current_rule.set.growth.dick);
			this.onchange = (value) => current_rule.set.growth.dick = value;
		}
	}

	class BallGrowthList extends NumberRange {
		constructor() {
			const pairs = [
				["sizable", 4],
				["cumslave", 6],
				["unlimited", 125],
				["none", 0]
			];
			super("Balls, if present", pairs, true, 0, 125, true);
			this.setValue(current_rule.set.growth.balls);
			this.onchange = (value) => current_rule.set.growth.balls = value;
		}
	}

	class CurrativesList extends ListCapitalized {
		constructor() {
			const pairs = [
				["none", 0],
				["preventatives", 1],
				["curatives", 2],
			];
			super("Health drugs", pairs);
			this.setValue(current_rule.set.curatives);
			this.onchange = (value) => current_rule.set.curatives = value;
		}
	}

	class AphrodisiacList extends ListCapitalized {
		constructor() {
			const pairs = [
				["none", 0],
				["standard", 1],
				["extreme", 2],
				["anaphrodisiacs", -1]
			];
			super("Aphrodisiacs", pairs);
			this.setValue(current_rule.set.aphrodisiacs);
			this.onchange = (value) => current_rule.set.aphrodisiacs = value;
		}
	}

	class ContraceptiveList extends ListCapitalized {
		constructor() {
			const drugs = [
				["contraceptives", true],
				["fertile", false],
			];
			super("Contraceptives for fertile slaves", drugs);
			this.setValue(current_rule.set.preg);
			this.onchange = (value) => current_rule.set.preg = value;
		}
	}

	class AbortionList extends ListCapitalized {
		constructor() {
			const pairs = [
				["abort all", "all"],
			];
			if (V.pregnancyMonitoringUpgrade === 1 && V.geneticMappingUpgrade === 1) {
				pairs.push(["abort boys", "male"]);
				pairs.push(["abort girls", "female"]);
			}
			super("Pregnancy termination", pairs);
			this.setValue(current_rule.set.abortion);
			this.onchange = (value) => current_rule.set.abortion = value;
		}
	}

	class PregDrugsList extends ListCapitalized {
		constructor() {
			const pairs = [
				["none"],
				["fast gestation", "fast"],
				["slow gestation", "slow"],
				["birth suppressors", "suppress"],
				["birth stimulators", "stimulate"]
			];
			super("Pregnancy control agents for pregnant slaves", pairs);
			this.setValue(current_rule.set.pregSpeed);
			this.onchange = (value) => current_rule.set.pregSpeed = value;
		}
	}

	class FemaleHormonesList extends List {
		constructor() {
			const pairs = [
				["Intensive Female", 2],
				["Female", 1],
				["None", 0],
				["Male", -1],
				["Intensive Male", -2]
			];
			super("Hormones for female slaves", pairs);
			this.setValue(current_rule.set.XX);
			this.onchange = (value) => current_rule.set.XX = value;
		}
	}

	class GeldingHormonesList extends List {
		constructor() {
			const pairs = [
				["Intensive Female", 2],
				["Female", 1],
				["None", 0],
				["Male", -1],
				["Intensive Male", -2]
			];
			super("Hormones for geldings", pairs);
			this.setValue(current_rule.set.gelding);
			this.onchange = (value) => current_rule.set.gelding = value;
		}
	}

	class ShemaleHormonesList extends List {
		constructor() {
			const pairs = [
				["Intensive Female", 2],
				["Female", 1],
				["None", 0],
				["Male", -1],
				["Intensive Male", -2]
			];
			super("Hormones for shemales", pairs);
			this.setValue(current_rule.set.XY);
			this.onchange = (value) => current_rule.set.XY = value;
		}
	}

	class OtherDrugsList extends ListCapitalized {
		constructor() {
			const drugs = [
				["none"],
				["fertility drugs"],
				["psychosuppressants"],
				["steroids"],
				["Natural hormone enhancers", "hormone enhancers"],
				["hormone blockers"],
				["Erectile dysfunction circumvention", "priapism agents"],
				["breast injections"],
				["intensive breast injections"],
				["butt injections"],
				["intensive butt injections"],
				["lip injections"],
				["penis enhancement"],
				["intensive penis enhancement"],
				["testicle enhancement"],
				["intensive testicle enhancement"],
			];

			if (V.growthStim === 1) {
				drugs.push(["Growth Stimulants (Research)", "growth stimulants"]);
			}
			if (V.precociousPuberty === 1 && V.pubertyHormones) {
				drugs.push(["Female hormone injections (Research)", "female hormone injections"]);
				drugs.push(["Male hormone injections (Research)", "male hormone injections"]);
			}
			if (V.purchasedSagBGone === 1) {
				drugs.push(["Sag-B-gone (Product)", "sag-B-gone"]);
			}
			if (V.arcologies[0].FSSlimnessEnthusiastResearch === 1) {
				drugs.push(["Weight loss pills (FS)", "appetite suppressors"]);
				drugs.push(["breast redistributors"]);
				drugs.push(["butt redistributors"]);
				drugs.push(["nipple atrophiers"]);
				drugs.push(["lip atrophiers"]);
				drugs.push(["penis atrophiers"]);
				drugs.push(["testicle atrophiers"]);
				drugs.push(["clitoris atrophiers"]);
				drugs.push(["labia atrophiers"]);
			}
			if (V.arcologies[0].FSAssetExpansionistResearch === 1) {
				drugs.push(["hyper breast injections"]);
				drugs.push(["hyper butt injections"]);
				drugs.push(["hyper penis enhancement"]);
				drugs.push(["hyper testicle enhancement"]);
			}
			if (V.arcologies[0].FSYouthPreferentialistResearch === 1) {
				drugs.push(["Anti-aging cream (FS)", "anti-aging cream"]);
			}
			if (V.seeHyperPreg === 1 && V.superFertilityDrugs === 1) {
				drugs.push(["Super fertility drugs", "super fertility drugs"]);
			}
			super("Other drugs", drugs);
			this.setValue(current_rule.set.drug);
			this.onchange = (value) => current_rule.set.drug = value;
		}
	}

	class EnemaList extends ListCapitalized {
		constructor() {
			const enemas = [
				["none"],
				["water"]
			];
			if (V.medicalEnema === 1) {
				enemas.push(
					["aphrodisiac"],
					["curative"],
					["tightener"]
				);
			}
			super("Enemas", enemas);
			this.setValue(current_rule.set.inflationType);
			this.onchange = (value) => current_rule.set.inflationType = value;
		}
	}

	class DietList extends ListCapitalized {
		constructor() {
			const diets = [
				["Healthy diet", "healthy"],
				["fix fat and skinny slaves", "attractive"],
				["curvy", 30],
				["average", 0],
				["thin", -30]
			];
			if (V.feeder === 1) {
				diets.push(
					["feminine", "XX"],
					["masculine", "XY"]
				);
				if (V.dietXXY === 1) {
					diets.push(["futanari", "XXY"]);
				}
			}
			if (V.dietCleanse === 1) {
				diets.push(["cleansing"]);
			}
			if (V.dietFertility === 1) {
				diets.push(["fertility"]);
			}
			if (V.cumProDiet === 1) {
				diets.push(["cum production"]);
			}

			super("Slave diets", diets, true, true);
			this.setValue(current_rule.set.diet);
			this.onchange = (value) => current_rule.set.diet = value;
		}
	}

	class DietGrowthList extends ListCapitalized {
		constructor() {
			const pairs = [
				["on", 1],
				["off", 0]
			];
			super("Diet support for growth drugs", pairs, false);
			this.setValue(current_rule.set.dietGrowthSupport);
			this.onchange = (value) => current_rule.set.dietGrowthSupport = value;
		}
	}

	class DietBaseList extends List {
		constructor() {
			// TODO: better data structure?
			const pairs = [
				["No default setting", {cum: null, milk: null}],
				["Normal Diet", {cum: 0, milk: 0}],
				["Cum Added", {cum: 1, milk: 0}],
				["Milk Added", {cum: 0, milk: 1}],
				["Cum &amp; Milk Added", {cum: 1, milk: 1}],
				["Cum-Based", {cum: 2, milk: 0}],
				["Milk-Based", {cum: 0, milk: 2}],
			];
			super("Diet base", pairs, false);
			this.setValue(this.value2string(current_rule.set.dietCum, current_rule.set.dietMilk));
			this.onchange = (value) => {
				current_rule.set.dietCum = value.cum;
				current_rule.set.dietMilk = value.milk;
				this.setValue(this.value2string(current_rule.set.dietCum, current_rule.set.dietMilk));
			};
		}

		value2string(cum, milk) {
			return `cum: ${cum}, milk: ${milk}`;
		}
	}

	class DietSolidFoodList extends ListCapitalized {
		constructor() {
			const pairs = [
				["permitted", 0],
				["forbidden", 1],
			];
			super("Solid food access", pairs);
			this.setValue(current_rule.set.onDiet);
			this.onchange = (value) => current_rule.set.onDiet = value;
		}
	}

	class MuscleList extends NumberRange {
		constructor() {
			const pairs = [
				["none", 0],
				["toned", 20],
				["ripped", 50],
				["massive", 100],
				["weak", -20]
			];
			super("Muscles", pairs, true, -20, 100, true);
			this.setValue(current_rule.set.muscles);
			this.onchange = (value) => current_rule.set.muscles = value;
		}
	}

	class BraceList extends ListCapitalized {
		constructor() {
			const pairs = [
				["none"],
				["straighten"],
				["universal"]
			];
			super("Braces", pairs);
			this.setValue(current_rule.set.teeth);
			this.onchange = (value) => current_rule.set.teeth = value;
		}
	}

	class LivingStandardList extends ListCapitalized {
		constructor() {
			const pairs = [
				["luxurious"],
				["normal"],
				["spare"]
			];
			super("Living standard", pairs);
			this.setValue(current_rule.set.livingRules);
			this.onchange = (value) => current_rule.set.livingRules = value;
		}
	}

	class PunishmentList extends ListCapitalized {
		constructor() {
			const pairs = [
				["confinement"],
				["whipping"],
				["chastity"],
				["situational"]
			];
			super("Typical punishment", pairs);
			this.setValue(current_rule.set.standardPunishment);
			this.onchange = (value) => current_rule.set.standardPunishment = value;
		}
	}

	class RewardList extends ListCapitalized {
		constructor() {
			const pairs = [
				["relaxation"],
				["drugs"],
				["orgasm"],
				["situational"]
			];
			super("Typical reward", pairs);
			this.setValue(current_rule.set.standardReward);
			this.onchange = (value) => current_rule.set.standardReward = value;
		}
	}

	class ReleaseList extends ListCapitalized {
		constructor() {
			const pairs = [
				["permissive"],
				["sapphic"],
				["masturbation"],
				["restrictive"],
				["chastity"]
			];
			super("Release rules", pairs);
			this.setValue(current_rule.set.releaseRules);
			this.onchange = (value) => current_rule.set.releaseRules = value;
		}
	}

	class ToyHoleList extends ListCapitalized {
		constructor() {
			const pairs = [
				["all her holes"],
				["mouth"],
				["boobs"],
				["pussy"],
				["ass"],
				["dick"]
			];
			super("Fucktoy use preference", pairs);
			this.setValue(current_rule.set.toyHole);
			this.onchange = (value) => current_rule.set.toyHole = value;
		}
	}

	class SmartFetishList extends ListCapitalized {
		constructor() {
			const pairs = [
				["vanilla"],
				["oral"],
				["anal"],
				["boobs"],
				["Sub", "submissive"],
				["dom"],
				["humiliation"],
				["Preg", "pregnancy"],
				["Pain", "masochist"],
				["Sadism", "sadist"]
			];
			super("Smart piercing fetish target", pairs);
			this.setValue(current_rule.set.clitSetting);
			this.onchange = (value) => current_rule.set.clitSetting = value;
		}
	}

	class SmartXYAttractionList extends ListCapitalized {
		constructor() {
			const pairs = [
				["passionate", 100],
				["attracted", 75],
				["indifferent", 45],
				["none", 0]
			];
			super("Smart piercing XY attraction target", pairs);
			this.setValue(current_rule.set.clitSettingXY);
			this.onchange = (value) => current_rule.set.clitSettingXY = value;
		}
	}

	class SmartXXAttractionList extends ListCapitalized {
		constructor() {
			const pairs = [
				["passionate", 100],
				["attracted", 75],
				["indifferent", 45],
				["none", 0]
			];
			super("Smart piercing XX attraction target", pairs);
			this.setValue(current_rule.set.clitSettingXX);
			this.onchange = (value) => current_rule.set.clitSettingXX = value;
		}
	}

	class SmartEnergyList extends ListCapitalized {
		constructor() {
			const pairs = [
				["nympho", 100],
				["sex addict", 85],
				["powerful", 65],
				["healthy", 45],
				["weak", 25],
				["frigid", 0]
			];
			super("Smart piercing sex drive target", pairs);
			this.setValue(current_rule.set.clitSettingEnergy);
			this.onchange = (value) => current_rule.set.clitSettingEnergy = value;
		}
	}

	class SpeechList extends ListCapitalized {
		constructor() {
			const pairs = [
				["permissive"],
				["suppress accents", "accent elimination"],
				["restrictive"]
			];
			super("Speech rules", pairs);
			this.setValue(current_rule.set.speechRules);
			this.onchange = (value) => current_rule.set.speechRules = value;
		}
	}

	class RelationshipList extends ListCapitalized {
		constructor() {
			const pairs = [
				["permissive"],
				["just friends"],
				["restrictive"]
			];
			super("Relationship rules", pairs);
			this.setValue(current_rule.set.relationshipRules);
			this.onchange = (value) => current_rule.set.relationshipRules = value;
		}
	}

	class PornBroadcastStatus extends ListCapitalized {
		constructor() {
			const pairs = [
				["disabled", 0],
				["enabled", 1]
			];
			super("Porn Broadcasting Status", pairs);
			this.setValue(current_rule.set.pornFeed);
			this.onchange = (value) => current_rule.set.pornFeed = value;
		}
	}

	class PornList extends ListCapitalized {
		constructor() {
			const pairs = [
				/* ["No broadcasting", -1], **This has changed, it would now use .pornFeed** */
				["no subsidy", 0],
				["1000", 1000],
				["2000", 2000],
				["3000", 3000],
				["4000", 4000],
				["5000", 5000]
			];
			super("Weekly porn publicity subsidy", pairs);
			this.setValue(current_rule.set.pornFameSpending);
			this.onchange = (value) => current_rule.set.pornFameSpending = value;
		}
	}

	class EyewearList extends ListCapitalized {
		constructor() {
			const pairs = [
				["none"],
				["correct with glasses"],
				["correct with contacts"],
				["universal glasses"],
				["blur with glasses"],
				["blur with contacts"]
			];
			super("Eyewear", pairs);
			this.setValue(current_rule.set.eyewear);
			this.onchange = (value) => current_rule.set.eyewear = value;
		}
	}

	class LensesList extends Element {
		constructor() {
			super(current_rule.set.eyeColor);
			this.appendChild(new OptionsItem("No default setting", () => this.setValue(null)));
			this.colorlist = new LensesColorList();
			this.shapelist = new LensesShapeList();
			this.appendChild(this.colorlist);
			this.appendChild(this.shapelist);
		}

		render(color) {
			const elem = document.createElement("div");
			elem.innerHTML = "Eye coloring: ";
			this.label = document.createElement("strong");
			this.label.innerText = color;
			elem.appendChild(this.label);
			return elem;
		}

		combine() {
			const lst = [];
			if (this.colorlist.value !== null)
				lst.push(this.colorlist.value);
			if (this.shapelist.value !== null)
				lst.push(this.shapelist.value);
			if (lst.length === 0) return null;
			else return lst.join(" ");
		}

		setValue(val) {
			if (val === undefined) { val = this.combine(); }
			this.label.innerText = `${val} `;
			current_rule.set.eyeColor = val;
		}
	}

	class LensesColorList extends Options {
		constructor() {
			const items = [];
			[
				null,
				"blue",
				"black",
				"brown",
				"green",
				"turquoise",
				"sky-blue",
				"hazel",
				"pale-grey",
				"white",
				"pink",
				"yellow",
				"orange",
				"amber",
				"red"
			].forEach(i => items.push(new OptionsItem(i, item => {
				this.value = item.label;
				this.parent.setValue();
			})));
			super(items);
		}
	}

	class LensesShapeList extends Options {
		constructor() {
			const items = [];
			[
				null,
				"catlike",
				"serpent-like",
				"goat-like",
				"devilish",
				"demonic",
				"hypnotic",
				"heart-shaped",
				"star-shaped",
				"wide-eyed",
				"almond-shaped",
				"bright",
				"teary",
				"vacant"
			].forEach(i => items.push(new OptionsItem(i, item => {
				this.value = item.label;
				this.parent.setValue();
			})));
			super(items);
		}
	}

	class EarwearList extends ListCapitalized {
		constructor() {
			const pairs = [
				["none"],
				["correct with hearing aids"],
				["muffle with ear plugs"],
				["deafen with ear plugs"]
			];
			super("Earwear", pairs);
			this.setValue(current_rule.set.earwear);
			this.onchange = (value) => current_rule.set.earwear = value;
		}
	}

	class MakeupList extends ListCapitalized {
		constructor() {
			const pairs = [
				["makeup-free", 0],
				["nice", 1],
				["gorgeous", 2],
				["color-coordinate with hair", 3],
				["slutty", 4]
			];
			super("Makeup", pairs);
			this.setValue(current_rule.set.makeup);
			this.onchange = (value) => current_rule.set.makeup = value;
		}
	}

	class NailsList extends ListCapitalized {
		constructor() {
			const pairs = [
				["clipped", 0],
				["extended", 1],
				["color-coordinate with hair", 2],
				["sharp and claw-like", 3],
				["bright and glittery", 4],
				["hooker nails", 5],
				["neon colored", 6],
				["neon color-coordinate with hair", 7],
				["metallic painted", 8],
				["metallic color-coordinate with hair", 9]
			];
			super("Nails", pairs);
			this.setValue(current_rule.set.nails);
			this.onchange = (value) => current_rule.set.nails = value;
		}
	}

	class HairLengthList extends ListCapitalized {
		constructor() {
			const pairs = [
				["very short", 5],
				["short", 10],
				["shoulder length", 30],
				["long", 60],
				["very long", 100],
				["floor length", 150]
			];
			super("Hair length", pairs);
			this.setValue(current_rule.set.hLength);
			this.onchange = (value) => current_rule.set.hLength = value;
		}
	}

	class HaircutsList extends ListCapitalized {
		constructor() {
			const pairs = [
				["maintain hair length", 1],
				["do not maintain hair length", 0]
			];
			super("Hair length maintenance", pairs);
			this.setValue(current_rule.set.haircuts);
			this.onchange = (value) => current_rule.set.haircuts = value;
		}
	}

	class HairColorList extends ListCapitalized {
		constructor() {
			const pairs = [
				["blonde"],
				["golden"],
				["platinum blonde"],
				["strawberry-blonde"],
				["copper"],
				["ginger"],
				["red"],
				["deep red"],
				["green"],
				["blue"],
				["pink"],
				["dark brown"],
				["brown"],
				["auburn"],
				["burgundy"],
				["chocolate"],
				["chestnut"],
				["hazel"],
				["jet black"],
				["black"],
				["grey"],
				["silver"],
				["white"],
				["blue-violet"],
				["purple"],
				["dark orchid"],
				["sea green"],
				["green-yellow"],
				["dark blue"],
				["blazing red"],
				["neon green"],
				["neon blue"],
				["neon pink"]
			];
			super("Hair color", pairs);
			this.setValue(current_rule.set.hColor);
			this.onchange = (value) => current_rule.set.hColor = value;
		}
	}

	class HairStyleList extends ListCapitalized {
		constructor() {
			const pairs = [
				["neat"],
				["shaved"],
				["trimmed"],
				["buzzcut"],
				["up"],
				["ponytail"],
				["bun"],
				["messy bun"],
				["messy"],
				["curled"],
				["permed"],
				["luxurious"],
				["dreadlocks"],
				["cornrows"],
				["braided"],
				["tails"],
				["eary"],
				["afro"],
				["strip"]
			];
			super("Hair style", pairs);
			this.setValue(current_rule.set.hStyle);
			this.onchange = (value) => current_rule.set.hStyle = value;
		}
	}

	class EyebrowColorList extends ListCapitalized {
		constructor() {
			const pairs = [
				["blonde"],
				["golden"],
				["platinum blonde"],
				["strawberry-blonde"],
				["copper"],
				["ginger"],
				["red"],
				["deep red"],
				["green"],
				["blue"],
				["pink"],
				["dark brown"],
				["brown"],
				["auburn"],
				["burgundy"],
				["chocolate"],
				["chestnut"],
				["hazel"],
				["jet black"],
				["black"],
				["grey"],
				["silver"],
				["white"],
				["blue-violet"],
				["purple"],
				["dark orchid"],
				["sea green"],
				["green-yellow"],
				["dark blue"],
				["blazing red"],
				["neon green"],
				["neon blue"],
				["neon pink"]
			];
			super("Eyebrow hair color, when present", pairs);
			this.setValue(current_rule.set.eyebrowHColor);
			this.onchange = (value) => current_rule.set.eyebrowHColor = value;
		}
	}

	class EyebrowStyleList extends ListCapitalized {
		constructor() {
			const pairs = [
				["shaved"],
				["straight"],
				["rounded"],
				["natural"],
				["slanted inwards"],
				["slanted outwards"],
				["high-arched"],
				["elongated"],
				["shortened"],
				["curved"]
			];
			super("Eyebrow style", pairs);
			this.setValue(current_rule.set.eyebrowHStyle);
			this.onchange = (value) => current_rule.set.eyebrowHStyle = value;
		}
	}

	class EyebrowFullnessList extends ListCapitalized {
		constructor() {
			const pairs = [
				["pencil-thin"],
				["thin"],
				["threaded"],
				["natural"],
				["tapered"],
				["thick"],
				["bushy"]
			];
			super("Eyebrow fullness", pairs);
			this.setValue(current_rule.set.eyebrowFullness);
			this.onchange = (value) => current_rule.set.eyebrowFullness = value;
		}
	}

	class MarkingsList extends ListCapitalized {
		constructor() {
			const pairs = [
				["remove beauty marks"],
				["remove birthmarks"],
				["remove both"]
			];
			super("Facial markings", pairs);
			this.setValue(current_rule.set.markings);
			this.onchange = (value) => current_rule.set.markings = value;
		}
	}

	class PubicHairColorList extends ListCapitalized {
		constructor() {
			const pairs = [
				["blonde"],
				["golden"],
				["platinum blonde"],
				["strawberry-blonde"],
				["copper"],
				["ginger"],
				["red"],
				["deep red"],
				["green"],
				["blue"],
				["pink"],
				["dark brown"],
				["brown"],
				["auburn"],
				["burgundy"],
				["chocolate"],
				["chestnut"],
				["hazel"],
				["jet black"],
				["black"],
				["grey"],
				["silver"],
				["white"],
				["blue-violet"],
				["purple"],
				["dark orchid"],
				["sea green"],
				["green-yellow"],
				["dark blue"],
				["blazing red"],
				["neon green"],
				["neon blue"],
				["neon pink"]
			];
			super("Pubic hair color, when present", pairs);
			this.setValue(current_rule.set.pubicHColor);
			this.onchange = (value) => current_rule.set.pubicHColor = value;
		}
	}

	class PubicHairStyleList extends ListCapitalized {
		constructor() {
			const pairs = [
				["waxed"],
				["in a strip"],
				["neat"],
				["bushy"],
				["bushy in the front and neat in the rear"],
				["very bushy"]
			];
			super("Pubic hairstyle", pairs);
			this.setValue(current_rule.set.pubicHStyle);
			this.onchange = (value) => current_rule.set.pubicHStyle = value;
		}
	}

	class ArmpitHairColorList extends ListCapitalized {
		constructor() {
			const pairs = [
				["blonde"],
				["golden"],
				["platinum blonde"],
				["strawberry-blonde"],
				["copper"],
				["ginger"],
				["red"],
				["deep red"],
				["green"],
				["blue"],
				["pink"],
				["dark brown"],
				["brown"],
				["auburn"],
				["burgundy"],
				["chocolate"],
				["chestnut"],
				["hazel"],
				["jet black"],
				["black"],
				["grey"],
				["silver"],
				["white"],
				["blue-violet"],
				["purple"],
				["dark orchid"],
				["sea green"],
				["green-yellow"],
				["dark blue"],
				["blazing red"],
				["neon green"],
				["neon blue"],
				["neon pink"]
			];
			super("Underarm hair color, when present", pairs);
			this.setValue(current_rule.set.underArmHColor);
			this.onchange = (value) => current_rule.set.underArmHColor = value;
		}
	}

	class ArmpitHairStyleList extends ListCapitalized {
		constructor() {
			const pairs = [
				["waxed"],
				["shaved"],
				["neat"],
				["bushy"]
			];
			super("Underarm hair style", pairs);
			this.setValue(current_rule.set.underArmHStyle);
			this.onchange = (value) => current_rule.set.underArmHStyle = value;
		}
	}

	function piercingTypes(smartEnabled = false) {
		let res = [
			["none", 0],
			["light", 1],
			["heavy", 2]
		];
		if (smartEnabled) {
			res.push(["Smart (expensive)", 3]);
		}
		return res;
	}

	class EarPiercingList extends ListCapitalized {
		constructor() {
			super("Ear piercings", piercingTypes());
			this.setValue(current_rule.set.earPiercing);
			this.onchange = (value) => current_rule.set.earPiercing = value;
		}
	}

	class NosePiercingList extends ListCapitalized {
		constructor() {
			super("Nasal piercings", piercingTypes());
			this.setValue(current_rule.set.nosePiercing);
			this.onchange = (value) => current_rule.set.nosePiercing = value;
		}
	}

	class EyebrowPiercingList extends ListCapitalized {
		constructor() {
			super("Eyebrow piercings", piercingTypes());
			this.setValue(current_rule.set.eyebrowPiercing);
			this.onchange = (value) => current_rule.set.eyebrowPiercing = value;
		}
	}

	class NavelPiercingList extends ListCapitalized {
		constructor() {
			super("Navel piercings", piercingTypes());
			this.setValue(current_rule.set.navelPiercing);
			this.onchange = (value) => current_rule.set.navelPiercing = value;
		}
	}

	class NipplePiercingList extends ListCapitalized {
		constructor() {
			super("Nipple piercings", piercingTypes());
			this.setValue(current_rule.set.nipplesPiercing);
			this.onchange = (value) => current_rule.set.nipplesPiercing = value;
		}
	}

	class AreolaPiercingList extends ListCapitalized {
		constructor() {
			const pairs = [
				["none", 0],
				["studded", 1]
			];
			super("Areola studs", pairs);
			this.setValue(current_rule.set.areolaePiercing);
			this.onchange = (value) => current_rule.set.areolaePiercing = value;
		}
	}

	class LipPiercingList extends ListCapitalized {
		constructor() {
			super("Lip piercings", piercingTypes());
			this.setValue(current_rule.set.lipsPiercing);
			this.onchange = (value) => current_rule.set.lipsPiercing = value;
		}
	}

	class TonguePiercingList extends ListCapitalized {
		constructor() {
			super("Tongue piercing", piercingTypes());
			this.setValue(current_rule.set.tonguePiercing);
			this.onchange = (value) => current_rule.set.tonguePiercing = value;
		}
	}

	class ClitPiercingList extends ListCapitalized {
		constructor() {
			super("Clit piercing", piercingTypes(true));
			this.setValue(current_rule.set.clitPiercing);
			this.onchange = (value) => current_rule.set.clitPiercing = value;
		}
	}

	class LabiaPiercingList extends ListCapitalized {
		constructor() {
			super("Pussylips piercings", piercingTypes());
			this.setValue(current_rule.set.vaginaPiercing);
			this.onchange = (value) => current_rule.set.vaginaPiercing = value;
		}
	}

	class ShaftPiercingList extends ListCapitalized {
		constructor() {
			super("Shaft piercings", piercingTypes());
			this.setValue(current_rule.set.dickPiercing);
			this.onchange = (value) => current_rule.set.dickPiercing = value;
		}
	}

	class PerineumPiercingList extends ListCapitalized {
		constructor() {
			super("Perianal piercings", piercingTypes());
			this.setValue(current_rule.set.anusPiercing);
			this.onchange = (value) => current_rule.set.anusPiercing = value;
		}
	}

	class CorsetPiercingList extends ListCapitalized {
		constructor() {
			const pairs = [
				["none", 0],
				["applied", 1]
			];
			super("Corset piercings", pairs);
			this.setValue(current_rule.set.corsetPiercing);
			this.onchange = (value) => current_rule.set.corsetPiercing = value;
		}
	}

	class AutoBrandingList extends ListCapitalized {
		constructor() {
			const pairs = [
				["on", 1],
				["off", 0],
			];
			super("Automatic branding", pairs, false);
			this.setValue(current_rule.set.autoBrand);
			this.onchange = (value) => current_rule.set.autoBrand = value;
		}
	}

	class BrandingLocationList extends List {
		constructor() {
			super("Your preferred location for brands is", []);

			// I sorted this next section from top of body down, to make it easier to read for users. Hopefully when making similar lists elsewhere in the game, folks will use the same order. Makes it much easier to compare and make sure nothing is missing. And alphabetical is a poor choice for user facing lists.

			// Head
			const cheeks = new ListSubSection(this, "Cheeks", [
				["Left", "left cheek"],
				["Right", "right cheek"],
				["Both", "cheeks"]
			]);
			this.appendChild(cheeks);

			const ears = new ListSubSection(this, "Ears", [
				["Left", "left ear"],
				["Right", "right ear"],
				["Both", "ears"]

			]);
			this.appendChild(ears);

			// Torso
			const breasts = new ListSubSection(this, "Breasts", [
				["Left", "left breast"],
				["Right", "right breast"],
				["Both", "breasts"]

			]);
			this.appendChild(breasts);

			// Arms
			const shoulders = new ListSubSection(this, "Shoulders", [
				["Left", "left shoulder"],
				["Right", "right shoulder"],
				["Both", "shoulders"]

			]);
			this.appendChild(shoulders);

			const upper_arms = new ListSubSection(this, "Arms, upper", [
				["Left", "left upper arm"],
				["Right", "right upper arm"],
				["Both", "upper arms"]

			]);
			this.appendChild(upper_arms);

			const lower_arms = new ListSubSection(this, "Arms, lower", [
				["Left", "left lower arm"],
				["Right", "right lower arm"],
				["Both", "lower arms"]
			]);
			this.appendChild(lower_arms);

			const wrist = new ListSubSection(this, "Wrist", [
				["Left", "left wrist"],
				["Right", "right wrist"],
				["Both", "wrists"]
			]);
			this.appendChild(wrist);

			const hand = new ListSubSection(this, "Hand", [
				["Left", "left hand"],
				["Right", "right hand"],
				["Both", "hands"]
			]);
			this.appendChild(hand);

			// Legs
			const buttocks = new ListSubSection(this, "Buttocks", [
				["Left", "left buttock"],
				["Right", "right buttock"],
				["Both", "buttocks"]
			]);
			this.appendChild(buttocks);

			const thigh = new ListSubSection(this, "Thigh", [
				["Left", "left thigh"],
				["Right", "right thigh"],
				["Both", "thighs"]
			]);
			this.appendChild(thigh);

			const calf = new ListSubSection(this, "Calf", [
				["Left", "left calf"],
				["Right", "right calf"],
				["Both", "calves"]
			]);
			this.appendChild(calf);

			const ankle = new ListSubSection(this, "Ankle", [
				["Left", "left ankle"],
				["Right", "right ankle"],
				["Both", "ankles"]
			]);
			this.appendChild(ankle);

			const feet = new ListSubSection(this, "Feet", [
				["Left", "left foot"],
				["Right", "right foot"],
				["Both", "feet"]
			]);
			this.appendChild(feet);

			// Other
			const other = new ListSubSection(this, "Other", [
				["Neck", "neck"],
				["Chest", "chest"],
				["Back", "back"],
				["Lower Back", "lower back"],
				["Belly", "belly"],
				["Pubic mound", "pubic mound"]
			]);

			// Ignoring testicles and penis for now, as not all slaves have them.

			this.appendChild(other);

			this.setValue(current_rule.set.brandTarget);
			this.onchange = (value) => current_rule.set.brandTarget = value;
		}
	}

	class BrandDesignList extends List {
		constructor() {
			const items = [
				["Your slaving emblem", "your personal symbol"],
				["Your initials", "your initials"],
				["SLUT"],
				["WHORE"],
				["SLAVE"],
				["COW"],
				["MEAT"],
				["CUMDUMP"],
				["LOVER"],
				["Pussy symbol", "a pussy symbol"],
				["Anus symbol", "an anus symbol"],
				["Penis symbol", "a penis symbol"],
				["Lady", "a lady silhouette"],
				["Princess", "a princess silhouette"],
				["Queen", "a queen silhouette"],
				["Angel", "an angel silhouette"],
				["Devil", "a devil silhouette"]
			];

			if (V.arcologies[0].FSSupremacist !== "unset" || V.arcologies[0].FSSubjugationist !== "unset") {
				items.push(["(FS) Racial Slur", "a racial slur"]);
			}

			if (V.arcologies[0].FSSupremacist !== "unset" && V.arcologies[0].FSSupremacistRace === "white") {
				items.push(["(FS) Swastika", "a swastika"]);
			}

			if (V.arcologies[0].FSSubjugationist !== "unset" && V.arcologies[0].FSSubjugationistRace === "semitic") {
				items.push(["(FS) Star of David", "a Star of David"]);
			}

			if (V.arcologies[0].FSGenderRadicalist !== "unset" || V.arcologies.FSGenderFundamentalist !== "unset") {
				items.push(["(FS) Gender Symbol", "a gender symbol"]);
			}

			if (V.arcologies[0].FSPaternalist !== "unset") {
				items.push(["(FS) Personal Symbol", "$his own personal symbol"]);
			}

			if (V.arcologies[0].FSDegradationist !== "unset") {
				items.push(["(FS) Chain Symbol", "a chain symbol"]);
			}

			if (V.arcologies[0].FSBodyPurist !== "unset") {
				items.push(["(FS) Vitruvian Man", "a Vitruvian man"]);
			}

			if (V.arcologies[0].FSTransformationFetishist !== "unset") {
				items.push(["(FS) Most Desired Implants", "a shortlist of desired implants"]);
			}

			if (V.arcologies[0].FSYouthPreferentialist !== "unset") {
				items.push(["(FS) Virginity Status", "$his virginity status"]);
			}

			if (V.arcologies[0].FSMaturityPreferentialist !== "unset") {
				items.push(["(FS) Sexual Skill Info", "$his sexual skills"]);
			}

			if (V.arcologies[0].FSSlimnessEnthusiast !== "unset") {
				items.push(["(FS) Breast Ceiling", "$his absolute maximum breast size"]);
			}

			if (V.arcologies[0].FSAssetExpansionist !== "unset") {
				items.push(["(FS) Breast Floor", "$his absolute minimum breast size"]);
			}

			if (V.arcologies[0].FSPastoralist !== "unset") {
				items.push(["(FS) Product Quality", "$his body product quality"]);
			}

			if (V.arcologies[0].FSPhysicalIdealist !== "unset") {
				items.push(["(FS) Deadlift Info", "$his deadlift record"]);
			}

			if (V.arcologies[0].FSHedonisticDecadence !== "unset") {
				items.push(["(FS) Weight Record", "$his highest weigh-in"]);
			}

			if (V.arcologies[0].FSHedonisticDecadence && V.PC.refreshmentType === 2) {
				items.push(["(FS) Favorite Food", `a big helping of ${V.PC.refreshment}`]);
			}

			if (V.arcologies[0].FSRepopulationFocus !== "unset") {
				items.push(["(FS) Birth Count", "the number of children $he has birthed"]);
			}

			if (V.arcologies[0].FSChattelReligionist !== "unset") {
				items.push(["(FS) Religious Symbol", "a religious symbol"]);
			}

			if (V.arcologies[0].FSRomanRevivalist !== "unset") {
				items.push(["(FS) Republican Crest", "a small crest of your Republic"]);
			}

			if (V.arcologies[0].FSAztecRevivalist !== "unset") {
				items.push(["(FS) Seven Serpents", "a small symbol of the Aztec gods"]);
			}

			if (V.arcologies[0].FSEgyptianRevivalist !== "unset") {
				items.push(["(FS) Dynastic Sigil", "a small sigil of your Dynasty"]);
			}

			if (V.arcologies[0].FSEdoRevivalist !== "unset") {
				items.push(["(FS) Mon", "a small image of the Shogunate's mon"]);
			}

			if (V.arcologies[0].FSArabianRevivalist !== "unset") {
				items.push(["(FS) Caliphate Symbol", "a small symbol of the Caliphate"]);
			}

			if (V.arcologies[0].FSChineseRevivalist !== "unset") {
				items.push(["(FS) Imperial Seal", "a small image of your Imperial Seal"]);
			}

			super("Your brand design is", items, true);
			this.setValue(current_rule.set.brandDesign);
			this.onchange = (value) => current_rule.set.brandDesign = value;
		}
	}

	function commonTattoos() {
		return [
			["none", 0],
			["tribal patterns"],
			["flowers"],
			["counting"],
			["advertisements"],
			["rude words"],
			["degradation"],
			["bovine patterns"],
			["Asian art"],
			["permanent makeup"],
			["sacrament"],
			["sacrilege"],
			["possessive"],
			["paternalist"]
		];
	}

	class FaceTattooList extends ListCapitalized {
		constructor() {
			super("Facial tattoos", commonTattoos());
			this.setValue(current_rule.set.lipsTat);
			this.onchange = (value) => current_rule.set.lipsTat = value;
		}
	}

	class ShoulderTattooList extends ListCapitalized {
		constructor() {
			super("Shoulder tattoos", commonTattoos());
			this.setValue(current_rule.set.shouldersTat);
			this.onchange = (value) => current_rule.set.shouldersTat = value;
		}
	}

	class ChestTattooList extends ListCapitalized {
		constructor() {
			super("Chest tattoos", commonTattoos());
			this.setValue(current_rule.set.boobsTat);
			this.onchange = (value) => current_rule.set.boobsTat = value;
		}
	}

	class ArmTattooList extends ListCapitalized {
		constructor() {
			super("Arm tattoos", commonTattoos());
			this.setValue(current_rule.set.armsTat);
			this.onchange = (value) => current_rule.set.armsTat = value;
		}
	}

	class UpperBackTattooList extends ListCapitalized {
		constructor() {
			super("Upper back tattoos", commonTattoos());
			this.setValue(current_rule.set.backTat);
			this.onchange = (value) => current_rule.set.backTat = value;
		}
	}

	class LowerBackTattooList extends ListCapitalized {
		constructor() {
			super("Lower back tattoos", commonTattoos());
			this.setValue(current_rule.set.stampTat);
			this.onchange = (value) => current_rule.set.stampTat = value;
		}
	}

	class AbdomenTattooList extends ListCapitalized {
		constructor() {
			super("Abdomen tattoos", commonTattoos());
			this.setValue(current_rule.set.vaginaTat);
			this.onchange = (value) => current_rule.set.vaginaTat = value;
		}
	}

	class DickTattooList extends ListCapitalized {
		constructor() {
			super("Dick tattoos", commonTattoos());
			this.setValue(current_rule.set.dickTat);
			this.onchange = (value) => current_rule.set.dickTat = value;
		}
	}

	class ButtockTattooList extends ListCapitalized {
		constructor() {
			super("Buttock tattoos:", commonTattoos());
			this.setValue(current_rule.set.buttTat);
			this.onchange = (value) => current_rule.set.buttTat = value;
		}
	}

	class AnalTattooList extends ListCapitalized {
		constructor() {
			super("Anal tattoo or bleaching", commonTattoos().concat([['bleached']]));
			this.setValue(current_rule.set.anusTat);
			this.onchange = (value) => current_rule.set.anusTat = value;
		}
	}

	class LegTattooList extends ListCapitalized {
		constructor() {
			super("Leg tattoos", commonTattoos());
			this.setValue(current_rule.set.legsTat);
			this.onchange = (value) => current_rule.set.legsTat = value;
		}
	}

	class VisionSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["fixed", 1],
				["blurred", -1],
			];
			super("Vision correction", items);
			this.setValue(current_rule.set.surgery.eyes);
			this.onchange = (value) => current_rule.set.surgery.eyes = value;
		}
	}

	class HearingSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["fixed", 0],
				["muffled", -1],
			];
			super("Hearing correction", items);
			this.setValue(current_rule.set.surgery.hears);
			this.onchange = (value) => current_rule.set.surgery.hears = value;
		}
	}

	class SmellSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["fixed", 0],
				["disabled", -1],
			];
			super("Olfactory correction", items);
			this.setValue(current_rule.set.surgery.smells);
			this.onchange = (value) => current_rule.set.surgery.smells = value;
		}
	}

	class TasteSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["fixed", 0],
				["disabled", -1],
			];
			super("Gustatory correction", items);
			this.setValue(current_rule.set.surgery.tastes);
			this.onchange = (value) => current_rule.set.surgery.tastes = value;
		}
	}

	class LactationSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["implanted", 1],
				["removed", 0],
			];
			super("Lactation drug implants", items);
			this.setValue(current_rule.set.surgery.lactation);
			this.onchange = (value) => current_rule.set.surgery.lactation = value;
		}
	}

	class SemenSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["implanted", 1],
				["removed", 0],
			];
			super("Prostate production enhancing drug implants", items);
			this.setValue(current_rule.set.surgery.prostate);
			this.onchange = (value) => current_rule.set.surgery.prostate = value;
		}
	}

	class VasectomyList extends ListCapitalized {
		constructor() {
			const items = [
				["apply vasectomy", true],
				["undo vasectomy", false],
			];
			super("Apply or undo vasectomy for slaves with testicles", items);
			this.setValue(current_rule.set.surgery.vasectomy);
			this.onchange = (value) => current_rule.set.surgery.vasectomy = value;
		}
	}

	class CosmeticSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["none", 0],
				["subtle", 1],
				["invasive", 2],
			];
			super("Cosmetic Surgery", items);
			this.setValue(current_rule.set.surgery.cosmetic);
			this.onchange = (value) => current_rule.set.surgery.cosmetic = value;
		}
	}

	class LipSurgeryList extends NumberRange {
		constructor() {
			const items = [
				["removed", 0],
				["plush", 20],
				["big", 40],
				["huge", 70],
				["facepussy", 95],
			];
			super("Lip implants", items, true, 0, 95, true);
			this.setValue(current_rule.set.surgery.lips);
			this.onchange = (value) => current_rule.set.surgery.lips = value;
		}
	}

	class ButtSurgeryList extends NumberRange {
		constructor() {
			const items = [
				["removed", 0],
				["slim", 2],
				["stacked", 4],
				["huge", 6],
				["maximized", 9],
			];
			super("Buttock implants", items, true, 0, 9, true);
			this.setValue(current_rule.set.surgery.butt);
			this.onchange = (value) => current_rule.set.surgery.butt = value;
		}
	}

	class BreastSurgeryList extends NumberRange {
		constructor() {
			const items = [
				["removed", 0],
				["slim", 400],
				["stacked", 1000],
				["huge", 2000],
				["barely functional", 9000],
				["maximized", 48000]
			];
			super("Breast implants", items, true, 0, 48000, true);
			this.setValue(current_rule.set.surgery.boobs);
			this.onchange = (value) => current_rule.set.surgery.boobs = value;
		}
	}

	class TighteningSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["tightening", 1],
				["virginity restoration", 2],
			];
			super("Orifice Tightening", items);
			this.setValue(current_rule.set.surgery.holes);
			this.onchange = (value) => current_rule.set.surgery.holes = value;
		}
	}

	class TummyTuckSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["tuck", 1],
			];
			super("Tummy Tuck", items);
			this.setValue(current_rule.set.surgery.tummy);
			this.onchange = (value) => current_rule.set.surgery.tummy = value;
		}
	}


	class BodyHairSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["keep", 1],
				["removal", 2],
			];
			super("Body Hair", items);
			this.setValue(current_rule.set.surgery.bodyhair);
			this.onchange = (value) => current_rule.set.surgery.bodyhair = value;
		}
	}

	class HairSurgeryList extends ListCapitalized {
		constructor() {
			const items = [
				["keep", 1],
				["removal", 2],
			];
			super("Hair", items);
			this.setValue(current_rule.set.surgery.hair);
			this.onchange = (value) => current_rule.set.surgery.hair = value;
		}
	}

	class AutomaticAssignmentList extends List {
		constructor() {
			const items = [
				["Rest", "rest"],
				["Fucktoy", "please you"],
				["Subordinate Slave", "be a subordinate slave"],
				["House Servant", "be a servant"],
				["Confined", "stay confined"],
				["Whore", "whore"],
				["Public Servant", "serve the public"],
				["Classes", "take classes"],
				["Milked", "get milked"],
				["Gloryhole", "work a glory hole"],
				["Choose Her Own", "choose her own job"]
			];

			if (V.HGSuite > 0) {
				items.push(["Head Girl Suite", "live with your Head Girl"]);
			}
			if (V.brothel > 0) {
				items.push(["Brothel", "work in the brothel"]);
			}
			if (V.club > 0) {
				items.push(["Club", "serve in the club"]);
			}
			if (V.arcade > 0) {
				items.push(["Arcade", "be confined in the arcade"]);
			}
			if (V.dairy > 0) {
				items.push(["Dairy", "work in the dairy"]);
			}
			if (V.farmyard > 0) {
				items.push(["Farmyard", "work as a farmhand"]);
			}
			if (V.servantsQuarters > 0) {
				items.push(["Servant Quarters", "work as a servant"]);
			}
			if (V.masterSuite > 0) {
				items.push(["Master Suite", "serve in the master suite"]);
			}
			if (V.schoolroom > 0) {
				items.push(["Schoolroom", "learn in the schoolroom"]);
			}
			if (V.spa > 0) {
				items.push(["Spa", "rest in the spa"]);
			}
			if (V.clinic > 0) {
				items.push(["Clinic", "get treatment in the clinic"]);
			}
			if (V.cellblock > 0) {
				items.push(["Cellblock", "be confined in the cellblock"]);
			}

			super("Automatically set assignment", items);
			this.setValue(current_rule.set.setAssignment);
			this.onchange = (value) => current_rule.set.setAssignment = value;
		}
	}

	class BellyImplantList extends ListCapitalized {
		constructor() {
			const items = [
				["install", "install"],
				["remove", "remove"],
			];
			super("Belly implant", items);
			this.setValue(current_rule.set.surgery.bellyImplant);
			this.onchange = (value) => current_rule.set.surgery.bellyImplant = value;
		}
	}

	class LabelList extends List {
		constructor() {
			super("Custom label", [], true, true);
			this.setValue(current_rule.set.label);
			this.onchange = (value) => current_rule.set.label = value;
		}
	}

	class LabelRemoveList extends List {
		constructor() {
			super("Remove custom label", [], true, true);
			this.setValue(current_rule.set.removeLabel);
			this.onchange = (value) => current_rule.set.removeLabel = value;
		}
	}

	class SkinColorList extends List {
		constructor() {
			const items = [
				["natural"],
				["pure white"],
				["ivory"],
				["white"],
				["extremely pale"],
				["very pale"],
				["pale"],
				["extremely fair"],
				["very fair"],
				["fair"],
				["light"],
				["light olive"],
				["tan"],
				["olive"],
				["bronze"],
				["dark olive"],
				["dark"],
				["light beige"],
				["beige"],
				["dark beige"],
				["light brown"],
				["brown"],
				["dark brown"],
				["black"],
				["ebony"],
				["pure black"],
				["sun tanned"],
				["spray tanned"],
				["dyed red"],
				["dyed green"],
				["dyed blue"],
				["dyed pink"],
				["dyed gray"],
				["tiger striped"],
				["camouflage patterned"],
			];
			super("Dye or tan skin", items);
			this.setValue(current_rule.set.skinColor);
			this.onchange = (x) => current_rule.set.skinColor = x;
		}
	}

	return rulesAssistantOptions;
})();