code:
/**
 * This defines the passage contents for surrogacy, transplanting, and cloning surgeries
 * @returns {DocumentFragment}
 */
App.UI.surrogacy = function() {
	const node = new DocumentFragment();
	let r = [];

	switch (V.surgeryType) {
		case "surrogacy":
			if (V.receptrix === -1) {
				const donatrix = (V.donatrix === -1) ? V.PC : getSlave(V.donatrix);
				r.push(`Since the surgery required only a local anesthetic, you remain fully aware of the procedure as the autosurgery carries it out. You slowly rise to your feet, a hand to your lower belly, appreciating the new life growing within you.`);
				V.PC.pregKnown = 1;
				WombSurrogate(V.PC, 1, donatrix, V.impregnatrix, 1);
				WombNormalizePreg(V.PC);
			} else {
				const receptrix = getSlave(V.receptrix);
				const donatrix = (V.donatrix === -1) ? V.PC : getSlave(V.donatrix);
				receptrix.pregKnown = 1;
				WombSurrogate(receptrix, 1, donatrix, V.impregnatrix, 1);
				WombNormalizePreg(receptrix);
				const {
					He,
					he, his, him
				} = getPronouns(receptrix);
				if (receptrix.fetish === Fetish.MINDBROKEN) {
					r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
				} else if (receptrix.fetish === "pregnancy" && receptrix.fetishStrength > 60 && receptrix.fetishKnown === 1) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} was overjoyed at the sight of the syringe containing ${his} future child emptying into ${his} womb.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} is  filled with joy over the life settling into ${his} womb and can't wait to see the result. ${He}'s so pleased that ${he} now trusts your plans for ${his} body.`);
					receptrix.trust += 4;
					receptrix.devotion += 10;
				} else if (receptrix.devotion > 50) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} watched the syringe containing ${his} new pregnancy empty into ${his} womb with rapt attention.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He}'s <span class="devotion inc">grateful</span> that you think ${him} worthy of carrying this child, and a little nervous about how ${he}'ll perform as a surrogate.`);
					receptrix.devotion += 4;
				} else if (receptrix.devotion >= -20) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. From the syringe making contact with ${his} skin, to the egg's delivery into ${his} womb and ${his} subsequent impregnation, ${he} couldn't look away.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} understands the realities of ${his} life as a slave, so it isn't much of a shock. ${He} is <span class="trust dec">sensibly fearful</span> of your total power over ${his} body.`);
					receptrix.trust -= 10;
				} else {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. The moment ${he} realized what was happening, ${he} shut ${his} eyes tight, only opening them again as ${he} feels the slight tingle of the injector exiting ${his} lower abdomen.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} does not understand the realities of ${his} life as a slave at a core level, so ${he}'s terrified and angry that you have forced ${him} to bear this child, even more so as ${he} realizes ${he} doesn't know who the father is. ${He} is sensibly fearful of your total power over ${his} body and the future of the life ${he} now harbors within ${him}.`);
					receptrix.trust -= 15;
					receptrix.devotion -= 15;
				}
			}
			V.receptrix = 0;
			V.impregnatrix = 0;
			V.donatrix = 0;
			break;
		case "transplant": {
			const bulk = V.transplantFetuses.length > 1;
			const child = bulk ? "children" : "child";
			const donatrix = (V.donatrix === -1) ? V.PC : getSlave(V.donatrix);
			if (V.receptrix === -1) {
				r.push(`Since the surgery required only a local anesthetic, you are very aware that you are now carrying ${donatrix.slaveName}'s ${child}. You slowly`);
				if (canWalk(V.PC)) {
					r.push(`rise to your feet, a hand to`);
				} else if (canMove(V.PC)) {
					r.push(`rise to a sitting position, a hand to`);
				} else if (isMovable(V.PC)) {
					r.push(`run a hand across`);
				}
				r.push(`your lower belly, appreciating the new ${bulk ? "lives" : "life"} growing within you.`);
			} else {
				const receptrix = getSlave(V.receptrix);
				const {
					He,
					he, his, him
				} = getPronouns(receptrix);
				if (receptrix.fetish === Fetish.MINDBROKEN) {
					r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
				} else if ((receptrix.fetish === "pregnancy") && (receptrix.fetishStrength > 60) && (receptrix.fetishKnown === 1)) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} was overjoyed at the sight of the syringe containing ${his} future ${child} emptying into ${his} womb.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} is  filled with joy over the life resettling into ${his} womb and can't wait to see the result. ${He}'s so pleased that ${he} now trusts your plans for ${his} body.`);
					receptrix.trust += 4;
					receptrix.devotion += 10;
				} else if (receptrix.devotion > 50) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} watched the syringe containing ${his} new pregnancy empty into ${his} womb with rapt attention.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He}'s <span class="devotion inc">grateful</span> that you think ${him} worthy of carrying ${bulk ? "these children" : "this child"} in`);
					if (donatrix.ID === SID.PC) {
						r.push(`your stead,`);
					} else {
						r.push(`lieu of ${donatrix.slaveName},`);
					}
					r.push(`and a little nervous about how ${he}'ll perform as a surrogate.`);
					receptrix.devotion += 4;
				} else if (receptrix.devotion >= -20) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. From the syringe making contact with ${his} skin, to the egg's delivery into ${his} womb and ${his} subsequent impregnation, ${he} couldn't look away.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} understands the realities of ${his} life as a slave, so it isn't much of a shock. ${He} is sensibly fearful of your total power over ${his} body and how you are forcing`);
					if (donatrix.ID === SID.PC) {
						r.push(`your pregnancy upon ${him}.`);
					} else {
						r.push(`${him} to carry what should have been ${donatrix.slaveName}'s burden.`);
					}
					receptrix.trust -= 10;
				} else {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. The moment ${he} realized what was happening, ${he} shut ${his} eyes tight, only opening them again as ${he} feels the slight tingle of the injector exiting ${his} lower abdomen.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} does not understand the realities of ${his} life as a slave at a core level, so ${he}'s <span class="devotion dec">terrified and angry</span> that you have forced ${him} to`);
					if (donatrix.ID === SID.PC) {
						r.push(`carry your ${child} instead of doing so yourself.`);
					} else {
						r.push(`bear ${bulk ? "these children" : "this child"} in lieu of ${donatrix.slaveName}.`);
					}
					r.push(`${He} is <span class="trust dec">sensibly fearful</span> of your total power over ${his} body and who else's troubles may be levied upon ${him}.`);
					receptrix.trust -= 15;
					receptrix.devotion -= 15;
				}
			}
			const receptrix = (V.receptrix === -1) ? V.PC : getSlave(V.receptrix);
			V.transplantFetuses.reverse().forEach(fetus => {
				WombRemoveFetus(donatrix, donatrix.womb.indexOf(fetus));
				WombAddFetus(receptrix, fetus);
			});
			receptrix.pregKnown = 1;
			receptrix.preg = WombMaxPreg(receptrix);
			donatrix.preg = WombMaxPreg(donatrix);
			WombNormalizePreg(receptrix);
			WombNormalizePreg(donatrix);
			if (donatrix.preg === 0) {
				donatrix.pregWeek = -1;
			}
			V.receptrix = 0;
			V.donatrix = 0;
			V.transplantFetuses = [];
			break;
		}
		case "clone": {
			/** @type {FC.HumanState} */
			const donatrix = (V.donatrix === -1) ? V.PC : getSlave(V.donatrix);
			if (V.receptrix === -1) {
				r.push(`Since the surgery required only a local anesthetic, you remain fully aware of the procedure as the autosurgery carries it out. You slowly rise to your feet, a hand to your lower belly, appreciating the clone growing within you.`);
				V.PC.pregKnown = 1;
				WombImpregnateClone(V.PC, 1, donatrix, 1);
				WombNormalizePreg(V.PC);
			} else {
				const receptrix = getSlave(V.receptrix);
				receptrix.pregKnown = 1;
				WombImpregnateClone(receptrix, 1, donatrix, 1);
				WombNormalizePreg(receptrix);
				const {
					He,
					he, his, him
				} = getPronouns(receptrix);
				if (receptrix.fetish === Fetish.MINDBROKEN) {
					r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen,`);
					if (receptrix.ID === donatrix.ID) {
						r.push(`but has no idea ${he} carries ${his} own clone.`);
					} else {
						r.push(`${he} knows that ${he} has been impregnated.`);
					}
				} else if (receptrix.fetish === "pregnancy" && receptrix.fetishStrength > 60 && receptrix.fetishKnown === 1) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} was overjoyed at the sight of the syringe containing ${his}`);
						if (receptrix.ID === donatrix.ID) {
							r.push(`clone`);
						} else {
							r.push(`future child`);
						}
						r.push(`emptying into ${his} womb.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated${(receptrix.ID === donatrix.ID) ? `, but not that ${he} now bears ${his} own clone` : ''}.`);
					}
					r.push(`${He} is  filled with joy over the life settling into ${his} womb and can't wait to see the result. ${He}'s so pleased that ${he} now trusts your plans for ${his} body.`);
					receptrix.trust += 4;
					receptrix.devotion += 10;
				} else if (receptrix.devotion > 50) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. ${He} watched the syringe containing ${his} new pregnancy empty into ${his} womb with rapt attention.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated${(receptrix.ID === donatrix.ID) ? `, but not that ${he} now bears ${his} own clone` : ''}.`);
					}
					r.push(`${He}'s <span class="devotion inc">grateful</span> that you think ${him} worthy of carrying this child, and a little nervous about how ${he}'ll perform as a surrogate.`);
					receptrix.devotion += 4;
				} else if (receptrix.devotion >= -20) {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. From the syringe making contact with ${his} skin, to the egg's delivery into ${his} womb and ${his} subsequent impregnation, ${he} couldn't look away.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated${(receptrix.ID === donatrix.ID) ? `, but not that ${he} now bears ${his} own clone` : ''}.`);
					}
					r.push(`${He} understands the realities of ${his} life as a slave, so it isn't much of a shock. ${He} is <span class="trust dec">sensibly fearful</span> of your total power over ${his} body.`);
					receptrix.trust -= 10;
				} else {
					if (canSee(receptrix)) {
						r.push(`Since the surgery required only a local anesthetic, ${receptrix.slaveName} remained fully aware throughout the procedure. The moment ${he} realized what was happening, ${he} shut ${his} eyes tight, only opening them again as ${he} feels the slight tingle of the injector exiting ${his} lower abdomen.`);
					} else {
						r.push(`${receptrix.slaveName} leaves the surgery with a certain warmth in ${his} lower abdomen, ${he} knows that ${he} has been impregnated.`);
					}
					r.push(`${He} does not understand the realities of ${his} life as a slave at a core level, so ${he}'s <span class="devotion dec">terrified and angry</span> that you have forced ${him} to bear`);
					if (receptrix.ID === donatrix.ID) {
						r.push(`${his} own clone and potential replacement.`);
					} else {
						r.push(`this child, even more so as ${he} realizes ${he} doesn't know who the father is.`);
					}
					r.push(`${He} is <span class="trust dec">sensibly fearful</span> of your total power over ${his} body and the future of the life ${he} now harbors within ${him}.`);
					receptrix.trust -= 15;
					receptrix.devotion -= 15;
				}
			}
			V.receptrix = 0;
			V.donatrix = 0;
		}
	}
	App.Events.addParagraph(node, r);
	return node;
};


code:
App.Art.cacheArtData();
App.Corporate.Init();
App.RA.Activation.populateGetters();
App.Art.GenAI.UI.QueueOverlay.init();

repo_name: Nithhogg/fc

license: mit


code:
/* subscribe to game save/load events */
Save.onLoad.add(App.EventHandlers.onLoad);
Save.onSave.add(App.EventHandlers.onSave);

/* ### Every-time listeners #### */

$(document).on(":storyready", () => {
	App.EventHandlers.storyReady();
});

$(document).on(":passageinit", () => {
	App.Utils.PassageSwitchHandler.execute();
	App.UI.Tabs.clear();
	App.Reminders.clear();
	App.UI.StoryCaption.encyclopedia = "";
	profileEvents.passageinit();
});

$(document).on(":passagestart", event => {
	Object.defineProperty(State.temporary, "S", {
		get: () => S,
		enumerable: true
	});
	App?.Utils?.passageHistoryCleanup();
	App.Art.GenAI.UI.QueueOverlay.toggle(passage() !== "Options");
	profileEvents.passagestart();
});

$(document).on(":passagerender", () => {
	profileEvents.passagerender();
});

$(document).on(":passagedisplay", () => {
	profileEvents.passagedisplay();
});

$(document).on(":passageend", ev => {
	profileEvents.passageend(ev.content);
	App?.Utils?.passageHistoryAdd();
});

/* ### One-time listeners ### */

$(document).one(":passagestart", () => {
	App.EventHandlers.optionsChanged();
});


code:
[submodule "submodules/sugarcube-2"]
	branch = fc-rebase


code:
{
	const getProxy = App.Utils.Diff.getProxy;

	App.Testing.executeTest("Overwrite first level", () => {
	}, () => {
		let proxy = getProxy({a: 1});

		proxy.a = 2;

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {a: 1});
		App.Testing.equals(proxy.a, 2);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {a: 2});
	}, () => {
	});

	App.Testing.executeTest("Overwrite second level", () => {
	}, () => {
		let proxy = getProxy({c: {a: 1}});

		proxy.c.a = 2;

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {c: {a: 1}});
		App.Testing.equals(proxy.c.a, 2);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {c: {a: 2}});
	}, () => {
	});

	App.Testing.executeTest("Delete first level", () => {
	}, () => {
		let proxy = getProxy({a: 1});

		delete proxy.a;

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {a: 1});
		App.Testing.hasNoProperty(proxy, "a");

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {});
	}, () => {
	});

	App.Testing.executeTest("Delete in second level", () => {
	}, () => {
		let proxy = getProxy({c: {a: 1}});

		delete proxy.c.a;

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {c: {a: 1}});
		App.Testing.hasProperty(proxy, "c");
		App.Testing.hasNoProperty(proxy.c, "a");

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {c: {}});
	}, () => {
	});

	App.Testing.executeTest("Delete with second level", () => {
	}, () => {
		let proxy = getProxy({c: {a: 1}});

		delete proxy.c;

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {c: {a: 1}});
		App.Testing.hasNoProperty(proxy, "c");

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {});
	}, () => {
	});

	App.Testing.executeTest("add value", () => {
	}, () => {
		let proxy = getProxy({});

		proxy.d = 7;

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {});
		App.Testing.hasProperty(proxy, "d");
		App.Testing.equals(proxy.d, 7);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {d: 7});
	}, () => {
	});

	App.Testing.executeTest("add object", () => {
	}, () => {
		let proxy = getProxy({});

		proxy.d = {a: 5};

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {});
		App.Testing.hasProperty(proxy, "d");
		App.Testing.hasProperty(proxy.d, "a");
		App.Testing.equals(proxy.d.a, 5);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {d: {a: 5}});
	}, () => {
	});

	App.Testing.executeTest("Overwrite array entry", () => {
	}, () => {
		let proxy = getProxy({b: [1, 2]});

		proxy.b[1] = 5;

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {b: [1, 2]});
		App.Testing.equals(proxy.b, [1, 5]);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {b: [1, 5]});
	}, () => {
	});

	App.Testing.executeTest("array push", () => {
	}, () => {
		let proxy = getProxy({b: [1, 2]});

		proxy.b.push(5);

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {b: [1, 2]});
		App.Testing.equals(proxy.b, [1, 2, 5]);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {b: [1, 2, 5]});
	}, () => {
	});

	App.Testing.executeTest("array pop", () => {
	}, () => {
		let proxy = getProxy({b: [1, 2]});

		proxy.b.pop();

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {b: [1, 2]});
		App.Testing.equals(proxy.b, [1]);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {b: [1]});
	}, () => {
	});

	App.Testing.executeTest("new array unshift", () => {
	}, () => {
		let proxy = getProxy({});

		proxy.d = {a: [5]};
		proxy.d.a.unshift(9);

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {});
		App.Testing.hasProperty(proxy.d, "a");
		App.Testing.equals(proxy.d.a, [9, 5]);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original, {d: {a: [9, 5]}});
	}, () => {
	});

	App.Testing.executeTest("remove one eye", () => {
	}, () => {
		let proxy = getProxy({eye: new App.Entity.EyeState()});

		eyeSurgery(proxy, "left", "remove");

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {eye: new App.Entity.EyeState()});
		App.Testing.equals(proxy.eye.left, null);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original.eye.left, null);
	}, () => {
	});

	App.Testing.executeTest("remove both eyes", () => {
	}, () => {
		let proxy = getProxy({eye: new App.Entity.EyeState()});

		eyeSurgery(proxy, "both", "remove");

		const original = proxy.diffOriginal;

		App.Testing.equals(original, {eye: new App.Entity.EyeState()});
		App.Testing.equals(proxy.eye.left, null);
		App.Testing.equals(proxy.eye.right, null);

		App.Utils.Diff.applyDiff(original, proxy.diffChange);
		App.Testing.equals(original.eye.left, null);
		App.Testing.equals(original.eye.right, null);
	}, () => {
	});

	// Do this last
	App.Testing.unitDone();
}


code:
{
	class MockSlave {
		ID;
		mother;
		father;

		constructor(id) {
			this.ID = id;
		}
	}

	class MockMating {
		constructor(fatherId, motherId, ...childrenIds) {
			for (let id of childrenIds) {
				if (id === fatherId || id === motherId) {
					throw new Error("cannot give birth to self");
				}
			}

			this.fatherId = fatherId;
			this.motherId = motherId;
			this.childrenIds = childrenIds;
		}
	}

	class MockWorld {
		slavesArray;

		constructor(matings) {
			this.slavesArray = [];

			let slavesById = {};
			let meetSlave = (id) => {
				if (!slavesById[id]) {
					let slave = new MockSlave(id);
					this.slavesArray.push(slave);
					slavesById[id] = slave;
				}

				return slavesById[id];
			};

			for (let mating of matings) {
				let father = meetSlave(mating.fatherId);
				let mother = meetSlave(mating.motherId);
				for (let id of mating.childrenIds) {
					let child = meetSlave(id);
					child.father = father.ID;
					child.mother = mother.ID;
				}
			}
		}

		findSlaveState(id) {
			return this.slavesArray.find(slave => slave.ID === id) || null;
		}
	}

	let testCoefficientForSlave = function(name, mockMatings, slaveId, expectedCoefficientOfInbreeding) {
		App.Testing.executeTest(name, () => {}, () => {
			let tolerance = .00000000001;

			let world = new MockWorld(mockMatings);
			let c = ibc._test.coeff_slave(world, world.findSlaveState(slaveId));
			App.Testing.isType(c, "number");
			App.Testing.notNaN(c);
			App.Testing.inRange(c, expectedCoefficientOfInbreeding, tolerance);
		}, () => {});
	};

	// references:
	// Introduction to Quantitative Genetics - Doulas S. Falconer, 1989
	// Genetic and Quantitative Aspects of Genealogy - F.M. Lancaster, 2015
	testCoefficientForSlave("basic outbred mating, 1",[new MockMating(1, 2, 3, 4)], 1, 0);
	testCoefficientForSlave("basic outbred mating, 2",[new MockMating(1, 2, 3, 4)], 4, 0);
	testCoefficientForSlave("basic self-mating, 1", [new MockMating(1, 1, 3, 4)], 4, 1 / 2);
	testCoefficientForSlave("basic self-mating, 2", [new MockMating(1, 1, 3, 4)], 1, 0);
	testCoefficientForSlave("basic child-parent mating",[new MockMating(1, 2, 3), new MockMating(2, 3, 4)], 4, 1 / 4);
	testCoefficientForSlave("basic sibling mating",[new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5)], 5, 1 / 4);
	testCoefficientForSlave("basic half-sibling mating",[new MockMating(1, 2, 3), new MockMating(2, 4, 5), new MockMating(3, 5, 6)], 6, 1 / 8);
	testCoefficientForSlave("basic first cousin mating",[new MockMating(1, 2, 3, 4), new MockMating(5, 3, 6), new MockMating(4, 7, 8), new MockMating(6, 8, 9)], 9, 1 / 16);
	testCoefficientForSlave("double first cousin mating",[new MockMating(1, 2, 3, 4), new MockMating(5, 6, 7, 8),
		new MockMating(3, 7, 9), new MockMating(4, 8, 10), new MockMating(9, 10, 11)], 11, 1 / 8);
	testCoefficientForSlave("aunt-niece mating",[new MockMating(1, 2, 3, 4), new MockMating(4, 5, 6), new MockMating(3, 6, 7)], 7, 1 / 8);
	let scenario53 = [new MockMating(1, 2, 3, 4), new MockMating(5, 6, 7, 8), new MockMating(3, 7, 9, 10), new MockMating(4, 8, 11),
		new MockMating(12, 9, 13), new MockMating(10, 11, 14), new MockMating(13, 14, 15)];
	testCoefficientForSlave("problem 5.3 from Falconer, 1",scenario53, 14, 1 / 8);  //
	testCoefficientForSlave("problem 5.3 from Falconer, 2",scenario53, 15, 3 / 32);
	testCoefficientForSlave("example in figure 64 from Lancaster",[new MockMating(1, 2, 4, 5), new MockMating(3, 4, 7), new MockMating(5, 6, 8), new MockMating(7, 8, 9), new MockMating(9, 10, 12, 13),
		new MockMating(11, 12, 15), new MockMating(13, 14, 16), new MockMating(15, 16, 17, 18)], 17, 33 / 512);
	testCoefficientForSlave("example in figure 65 from Lancaster",[new MockMating(1, 2, 4, 5), new MockMating(3, 4, 7), new MockMating(5, 6, 8), new MockMating(7, 8, 10, 11),
		new MockMating(9, 10, 13), new MockMating(11, 12, 14), new MockMating(13, 14, 15)], 15, 9 / 128);
	testCoefficientForSlave("example in figure 66 from Lancaster",[new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5, 6), new MockMating(5, 6, 7, 8), new MockMating(7, 8, 9)], 9, 1 / 2);
	testCoefficientForSlave("example in figure 66 from Lancaster, extended by one more generation's worth of mating between siblings",[new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5, 6), new MockMating(5, 6, 7, 8), new MockMating(7, 8, 9, 10),
		new MockMating(9, 10, 11)], 11, 19 / 32);
	// from here the preceding scenario of regular sibling-mating is extended by several more steps; by rules given in Falconer, the coefficient ck for step k, where step k follows
	// step j which follows step i, should be ck = 1/4 + cj/2 + ci/4, and so that is how the expected values used in the next few tests were calculated
	testCoefficientForSlave("extended, 1: 43/64 = 1/4 + (19/32) / 2 + (1/2) / 4",[new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5, 6), new MockMating(5, 6, 7, 8), new MockMating(7, 8, 9, 10),
		new MockMating(9, 10, 11, 12), new MockMating(11, 12, 13)], 13, 43 / 64);
	testCoefficientForSlave("extended, 2",[new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5, 6), new MockMating(5, 6, 7, 8), new MockMating(7, 8, 9, 10),
		new MockMating(9, 10, 11, 12), new MockMating(11, 12, 13, 14), new MockMating(13, 14, 15)], 15, 94 / 128);
	testCoefficientForSlave("extended, 3", [new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5, 6), new MockMating(5, 6, 7, 8), new MockMating(7, 8, 9, 10),
		new MockMating(9, 10, 11, 12), new MockMating(11, 12, 13, 14), new MockMating(13, 14, 15, 16), new MockMating(15, 16, 17)], 17, 201 / 256);
	testCoefficientForSlave("extended, 4", [new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5, 6), new MockMating(5, 6, 7, 8), new MockMating(7, 8, 9, 10),
		new MockMating(9, 10, 11, 12), new MockMating(11, 12, 13, 14), new MockMating(13, 14, 15, 16), new MockMating(15, 16, 17, 18), new MockMating(17, 18, 19)], 19, 423 / 512);
	testCoefficientForSlave("extended, 5", [new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5, 6), new MockMating(5, 6, 7, 8), new MockMating(7, 8, 9, 10),
		new MockMating(9, 10, 11, 12), new MockMating(11, 12, 13, 14), new MockMating(13, 14, 15, 16), new MockMating(15, 16, 17, 18), new MockMating(17, 18, 19, 20),
		new MockMating(19, 20, 21)], 21, 880 / 1024);
	// Does not apply to current algorithm: as of this writing, the ten-generations-of-sisterfucking test immediately above is enough to occupy the game's coefficient-of-inbreeding calculation algorithm for a few minutes
	// on a ~4Ghz CPU.  this is very silly.  when you read this, that algorithm should have been replaced by a much faster one, allowing the above test to be comfortably run again.
	let scenario70 = [new MockMating(1, 2, 6), new MockMating(2, 3, 7), new MockMating(3, 4, 8), new MockMating(5, 6, 9), new MockMating(7, 8, 10),
		new MockMating(9, 10, 12), new MockMating(10, 11, 13), new MockMating(12, 13, 14), new MockMating(12, 14, 15)];  // example in figure 70 from Lancaster
	testCoefficientForSlave("example in figure 70 from Lancaster, 1", scenario70, 2, 0);
	testCoefficientForSlave("example in figure 70 from Lancaster, 2", scenario70, 10, 1 / 8);
	testCoefficientForSlave("example in figure 70 from Lancaster, 3", scenario70, 12, 1 / 32);
	testCoefficientForSlave("example in figure 70 from Lancaster, 4", scenario70, 15, 85 / 256);
	let scenarioXXX = [new MockMating(1, 2, 5, 6), new MockMating(3, 4, 7, 8), new MockMating(5, 6, 9, 10), new MockMating(5, 7, 11), new MockMating(5, 8, 12),
		new MockMating(5, 9, 13), new MockMating(9, 10, 14), new MockMating(5, 11, 15), new MockMating(10, 12, 16), new MockMating(13, 16, 17),
		new MockMating(4, 16, 18), new MockMating(14, 15, 19), new MockMating(6, 17, 20), new MockMating(6, 18, 21), new MockMating(6, 19, 22),
		new MockMating(20, 21, 23), new MockMating(22, 23, 24)];  // an invented example meant to represent a typical situation in a respectable arcology
	testCoefficientForSlave("change, 1",scenarioXXX, 24, 633 / 2048);  // here the expected coefficient was retrieved from the algorithm being tested and was not separately verified by hand,
	// so this test could conceivably be wrong, but at least it can catch changes...
	testCoefficientForSlave("change, 2",[new MockMating(1, 2, 3, 4), new MockMating(3, 4, 5, 6), new MockMating(5, 6, 7, 8), new MockMating(7, 8, 9, 10), new MockMating(9, 10, 11, 12),
		new MockMating(11, 12, 13), new MockMating(13, 14, 15),
		new MockMating(5, 5, 16), new MockMating(15, 16, 17)], 17, 1 / 4);  // an example in which the old and new algorithms actually gave different results - the first one such to have been observed! It seems that the old algorithm was wrong
	testCoefficientForSlave("negative IDs, 1",[new MockMating(-120, -120, 87)], 87, 1 / 2);  // negative IDs need to be accepted (not bothering to test any weird special IDs like -1 here though)
	testCoefficientForSlave("negative IDs, 2",[new MockMating(-999, -998, -300), new MockMating(-300, -998, 300)], 300, 1 / 4);

	// Do this last
	App.Testing.unitDone();
}


code:
// Define Test framework and register all tests

// To run all tests, type 'App.Loader.executeTests()' in the browser console. Make sure the HTML file has not been
// moved from the 'bin/' directory

// First, create the test framework
App.Testing = (function() {
	// First, abstract away anything test unrelated
	const group = App.Loader.getGroup("../tests");

	function addTestUnit(path) {
		group.queueSubscript(path);
	}

	let unitTotal = 0;
	let unitSuccesses = 0;

	function start() {
		App.Loader.nextScript();
	}

	function unitDone() {
		console.log("Group '" + App.Loader.lastScript + "' done. Successful tests:", unitSuccesses, "of", unitTotal);
		unitTotal = 0;
		unitSuccesses = 0;
		App.Loader.nextScript();
	}

	// Actual Testing functionality
	class TestError extends Error {
	}

	/**
	 * @param {string} name Unique test name
	 * @param {null|(()=>void)} prepare Prepare the global state
	 * @param {!(()=>void)} test Do the actual testing
	 * @param {null|(()=>void)} cleanup Clean the global state up
	 */
	function executeTest(name, prepare, test, cleanup) {
		unitTotal++;
		try {
			if (prepare != null) {
				prepare();
			}
		} catch (e) {
			console.log("PREPARE_FAILED", name, e);
			tryCleanup(name, cleanup);
			return;
		}
		try {
			test();
		} catch (e) {
			console.log("TEST_FAILED", name, e);
			tryCleanup(name, cleanup);
			return;
		}
		if (tryCleanup(name, cleanup)) {
			unitSuccesses++;
		}
	}

	/**
	 * @param {string} name Unique test name
	 * @param {null|(()=>void)} cleanup Clean the global state up
	 */
	function tryCleanup(name, cleanup) {
		try {
			if (cleanup != null) {
				cleanup();
			}
		} catch (e) {
			console.log("CLEANUP_FAILED", name, e);
			return false;
		}
		return true;
	}

	function equals(actual, expected) {
		if (!_.isEqual(actual, expected)) {
			throw new TestError("Actual value does not match expected value");
		}
	}

	/**
	 * @param {object} obj
	 * @param {string} property
	 */
	function hasProperty(obj, property) {
		if (obj[property] === undefined) {
			throw new TestError("Expected property does not exist");
		}
	}

	/**
	 * @param {object} obj
	 * @param {string} property
	 */
	function hasNoProperty(obj, property) {
		if (obj[property] !== undefined) {
			throw new TestError("Unexpected property exists");
		}
	}

	function isType(value, expected) {
		if (!(typeof value === expected)) {
			throw new TestError("Actual type does not match expected type");
		}
	}

	function notNaN(value) {
		if (Number.isNaN(value)) {
			throw new TestError("Expected valid number, got NaN.");
		}
	}

	function inRange(value, target, tolerance) {
		if (Math.abs(value - target) > tolerance) {
			throw new TestError(`Value ${value} outside range. Expected ${target} with tolerance ${tolerance}.`);
		}
	}

	return {
		addTestUnit, start, unitDone, executeTest,
		equals, hasProperty, hasNoProperty: hasNoProperty, isType, notNaN, inRange,
	};
})();

// Now load all tests
App.Testing.addTestUnit("diffProxy");
App.Testing.addTestUnit("ibc");

// Finally, execute the tests
App.Testing.start();


code:
:root {
    /* base */
    --background-color: #1c1d24;
    --sidebar-color: #212534;/*#2b2f42;*/
    --sidebar-hover-color: #40486e;
    --text-color: #e1e1e1;
    --divider-color: #888;

    /* button */
    --button-color: #2b2640;
    --button-border-color: #2b2640;
    --button-selected-color: #524064;
    --button-hover-color: #524064;
    --button-disabled-color: #22162e;

    --link-color: #6574cd;
    --link-hover-color: #6589e7;
}

.aquamarine, .aquamarine a, .skill, .skill a { color: #43e9e9 }
.coral, .coral a, .fetish.loss, .fetish.loss a { color: #ff8b39 }
.cyan, .cyan a, .hotkey { color: #00dfdf }
/*
.darkgoldenrod, .darkgoldenrod a, .trust.extremely-terrified, .trust.extremely-terrified a { color: darkgoldenrod }
.darkred, .darkred a, .defiant.full, .defiant.full a { color: darkred }
.darkviolet, .darkviolet a, .devotion.hateful, .devotion.hateful a { color: darkviolet }
*/
.deeppink, .deeppink a, .devotion.devoted, .devotion.devoted a { color: #ff4dad }
/*
.steelblue, .steelblue a { color: steelblue }
*/
.deepskyblue, .deepskyblue a, .intelligent, .intelligent a { color: #26bdef }
/*
.dodgerblue, .dodgerblue a { color: dodgerblue }
.blue, .blue a { color: blue }
*/
.gold, .gold a, .trust.dec, .trust.dec a, .trust.frightened, .trust.frightened a { color: #ffd90c }
/*
.goldenrod, .goldenrod a, .trust.terrified, .trust.terrified a { color: goldenrod }
.gray, .gray a { color: gray }
*/
.green, .green a, .reputation.inc, .reputation.inc a, .improvement, .improvement a, .flaw.break, .flaw.break a, .skill.inc, .skill.inc a, .positive, .positive a { color: #18af18 }
.hotpink, .hotpink a, .devotion.inc, .devotion.inc a, .devotion.accept, .devotion.accept a { color: #ff86c3 }
/*
.lawngreen, .lawngreen a { color: lawngreen }
.lightblue, .lightblue a { color: lightblue }
*/
.lightcoral, .lightcoral a, .fetish.gain, .fetish.gain a { color: #e762e3 }
.lightgreen, .lightgreen a, .relationship, .relationship a { color: #00cb7a }
/*
.lightpink, .lightpink a { color: lightpink }
.lightsalmon, .lightsalmon a, .fetish.inc, .fetish.inc a { color: lightsalmon }
*/
.lime, .lime a, .change.positive, .change.positive a, .virginity.loss, .virginity.loss a, .pregnant, .pregnant a { color: #40ff40 }
.limegreen, .limegreen a { color: limegreen }
.magenta, .magenta a, .devotion.worship, .devotion.worship a { color: #ff36ff }
.mediumaquamarine, .mediumaquamarine a, .trust.inc, .trust.inc a, .trust.careful, .trust.careful a { color: #52d3d3 }

.mediumorchid, .mediumorchid a, .devotion.dec, .devotion.dec a, .devotion.resistant, .devotion.resistant a { color: #bc60d3 }
/*
.mediumseagreen, .mediumseagreen a, .trust.trusting, .trust.trusting a { color: mediumseagreen }
*/
.orange, .orange a, .stupid, .stupid a, .change.negative, .change.negative a, .defiant.careful, .defiant.careful a, .intro.question { color: #ffbb3e }
/*
.orangered, .orangered a, .defiant.inc, .defiant.inc a, .defiant.bold, .defiant.bold, .education.neg a { color: orangered }
.orchid, .orchid a { color: orchid }
*/
.pink, .pink a, .slave.name.simple, .slave-name { color: #ff9fc3 }
.red, .red a, .health.dec, .health.dec a, .cash.dec, .cash.dec a, .flaw.gain, .flaw.gain a, .error, .error a, .elites.loss, .elites.loss a, .reputation.dec, .reputation.dec a, .warning, .warning a { color: #ff3131 }
/*.seagreen, .seagreen a, .trust.prof-trusting, .trust.prof-trusting a { color: seagreen }
.springgreen, .springgreen a, .skill.player, .skill.player a { color: springgreen }
*/
.tan, .tan a { color: #d2b48c }
/*
.chocolate, .chocolate a { color: chocolate }
.saddlebrown, .saddlebrown a { color: saddlebrown }
.teal, .teal a { color: teal }
*/
.yellow, .yellow a, .noteworthy, .noteworthy a, .devotion.ambivalent, .devotion.ambivalent a, .trust.fearful, .trust.fearful a { color: #f7f754 }
.yellowgreen, .yellowgreen a, .cash.inc, .cash.inc a, .cash, .cash a { color: #9acd32 }


code:
#init-screen {
	color: var(--text-color);
	background-color: var(--background-color);
}


code:
body {
	color: var(--text-color);
	background-color: var(--background-color);
}

a {
	color: var(--link-color);
}

a:hover {
	color: var(--link-hover-color);
}

button {
	color: var(--text-color);
	background-color: var(--button-color);
	border: 1px solid var(--button-border-color);
}

button:hover {
	background-color: var(--button-hover-color);
	border-color: var(--button-border-color);
}

button:disabled {
	background-color: var(--button-disabled-color);
	border: 1px solid var(--button-border-color);
}

input, select, textarea {
	color: #e1e1e1;
	background-color: transparent;
	border: 1px solid var(--button-border-color);
}

input:not(:disabled):focus, select:not(:disabled):focus, textarea:not(:disabled):focus,
input:not(:disabled):hover, select:not(:disabled):hover, textarea:not(:disabled):hover {
	background-color: #2b2640;
	border-color: var(--button-selected-color);
}

hr {
	border-top: 1px solid var(--divider-color);
}


code:
#ui-dialog-titlebar {
	background-color: var(--sidebar-color);
}
#ui-dialog-body {
	background-color: var(--background-color);
	border: 1px solid var(--divider-color);
}
#ui-dialog-body hr {
	background-color: var(--divider-color);
}


code:
/* Saves dialog styling. */
#ui-dialog-body.saves > *:not(:first-child) {
	border-top-color: var(--divider-color);
}
#ui-dialog-body.saves tr:not(:first-child) {
	border-top-color: var(--divider-color);
}
#ui-dialog-body.saves .empty {
	color: var(--text-color);
}


code:
#ui-bar {
	background-color: var(--sidebar-color);
	border-right: 1px solid var(--divider-color);
}
#ui-bar a:hover {
	text-decoration: underline;
}
#ui-bar hr {
	border-color: var(--divider-color);
}
#ui-bar-toggle{
	color: #444;
	border: 1px solid var(--divider-color);
	border-right: none;
}
#ui-bar-toggle:hover {
	background-color: var(--sidebar-hover-color);
	border-color: var(--divider-color);
}
#menu ul {
	border: 1px solid var(--divider-color);
}
#menu li:not(:first-child) {
	border-top: 1px solid var(--divider-color);
}
#menu li a {
	color: var(--text-color);
	background-color: transparent;
	border: none;
}
#menu li a:hover {
	background-color: var(--sidebar-hover-color);
	text-decoration: none;
}


code:
div.building.basement {
	border-top-color: #8F8F8F;
	background-image: repeating-linear-gradient(135deg, transparent, transparent 20px, #8F8F8F 20px, #8F8F8F 30px);
}

div.building .row .penthouse{
	border-color: #5daad7;
}

div.building .row .apartments{
	border-color: #3eaf3e;
}

div.building .row .private{
	border-color: #cb1a1a;
}

div.building .row .pit{
	border-color: #d94c18;
}

div.building div.innerCell {
	background-color: var(--background-color);
}

div.building div.decorative {
	outline-color: black;
}

/* introduction special formatting */
/* makes all links unusable */
.intro div.building a {
	color: var(--text-color);
}

div.building div.row div.dairy {
	border-color: saddlebrown;
}


code:
div.card {
	/*border: none;*/
	border-color: #384678;
	background-color: #212534;
	box-shadow: 10px 10px 5px #282d40;
}


code:
.lrgRender > div.mask, .lrgVector > div.mask {
	background: linear-gradient(90deg, rgba(28, 29, 36, 1), rgba(28, 29, 36, 0.8) 60%, rgba(28, 29, 36, 0));
}

.clear-formatting {
	color: var(--text-color);
}


code:
div.options-group .comment {
	color: #575757;
}


code:
.quick-links .toggle {
	background-color: var(--button-color);
}

.quick-links .toggle:hover {
	background-color: var(--button-hover-color);
}

.quick-links div.menu-link {
	background-color: var(--button-disabled-color);
	border-color: var(--button-border-color);
	box-shadow: var(--button-border-color);
}

.quick-links div.notification:not(.category)::after,
.quick-links div.category.notification.collapsed > .toggle::after {
	content: "\e80b";
}


code:
:root {
    /* base */
    --background-color: #fff;
    --sidebar-color: #ccf;
    --sidebar-hover-color: #99f;
    --text-color: #000;
    --divider-color: #888;

    /* button */
    --button-color: #b38cd9;
    --button-border-color: #9966cc;
    --button-selected-color: #663399;
    --button-hover-color: #ccb3e6;
    --button-disabled-color: #8c70a9;

    --link-color: #2A52B7;
    --link-hover-color: #446DD5;
}

.aquamarine, .aquamarine a, .skill, .skill a { color: #008563 }
.coral, .coral a, .fetish.loss, .fetish.loss a { color: #8a5c2e }
.cyan, .cyan a, .hotkey { color: #008585 }
/*
.darkgoldenrod, .darkgoldenrod a, .trust.extremely-terrified, .trust.extremely-terrified a { color: darkgoldenrod }
.darkred, .darkred a, .defiant.full, .defiant.full a { color: darkred }
.darkviolet, .darkviolet a, .devotion.hateful, .devotion.hateful a { color: darkviolet }
.deeppink, .deeppink a, .devotion.devoted, .devotion.devoted a { color: deeppink }
.steelblue, .steelblue a { color: steelblue }
*/
.deepskyblue, .deepskyblue a, .intelligent, .intelligent a { color: #00B4F0 }
/*
.dodgerblue, .dodgerblue a { color: dodgerblue }
.blue, .blue a { color: blue }
*/
.gold, .gold a, .trust.dec, .trust.dec a, .trust.frightened, .trust.frightened a { color: #cc8d00 }
/*
.goldenrod, .goldenrod a, .trust.terrified, .trust.terrified a { color: goldenrod }
.gray, .gray a { color: gray }
*/
.green, .green a, .reputation.inc, .reputation.inc a, .improvement, .improvement a, .flaw.break, .flaw.break a, .skill.inc, .skill.inc a, .positive, .positive a { color: #006600 }
/*
.hotpink, .hotpink a, .devotion.inc, .devotion.inc a, .devotion.accept, .devotion.accept a { color: hotpink }
.lawngreen, .lawngreen a { color: lawngreen }
.lightblue, .lightblue a { color: lightblue }
*/
.lightcoral, .lightcoral a, .fetish.gain, .fetish.gain a { color: #B85C00 }
.lightgreen, .lightgreen a, .relationship, .relationship a { color: #008550 }
/*
.lightpink, .lightpink a { color: lightpink }
.lightsalmon, .lightsalmon a, .fetish.inc, .fetish.inc a { color: lightsalmon }
.lime, .lime a, .change.positive, .change.positive a, .virginity.loss, .virginity.loss a, .pregnant, .pregnant a { color: lime }
.limegreen, .limegreen a { color: limegreen }
.magenta, .magenta a, .devotion.worship, .devotion.worship a { color: magenta }
*/
.mediumaquamarine, .mediumaquamarine a, .trust.inc, .trust.inc a, .trust.careful, .trust.careful a { color: #0F6161 }
/*
.mediumorchid, .mediumorchid a, .devotion.dec, .devotion.dec a, .devotion.resistant, .devotion.resistant a { color: mediumorchid }
.mediumseagreen, .mediumseagreen a, .trust.trusting, .trust.trusting a { color: mediumseagreen }
*/
.orange, .orange a, .stupid, .stupid a, .change.negative, .change.negative a, .defiant.careful, .defiant.careful a, .intro.question { color: #B85C00 }
/*
.orangered, .orangered a, .defiant.inc, .defiant.inc a, .defiant.bold, .defiant.bold, .education.neg a { color: orangered }
.orchid, .orchid a { color: orchid }
*/
.pink, .pink a, .slave.name.simple, .slave-name { color: #E00070 }
/*
.red, .red a, .health.dec, .health.dec a, .cash.dec, .cash.dec a, .flaw.gain, .flaw.gain a, .error, .error a, .elites.loss, .elites.loss a, .reputation.dec, .reputation.dec a, .warning, .warning a { color: red }
.seagreen, .seagreen a, .trust.prof-trusting, .trust.prof-trusting a { color: seagreen }
.springgreen, .springgreen a, .skill.player, .skill.player a { color: springgreen }
*/
.tan, .tan a { color: #846E58 }
/*
.chocolate, .chocolate a { color: chocolate }
.saddlebrown, .saddlebrown a { color: saddlebrown }
.teal, .teal a { color: teal }
*/
.yellow, .yellow a, .noteworthy, .noteworthy a, .devotion.ambivalent, .devotion.ambivalent a, .trust.fearful, .trust.fearful a { color: #cc9600 }
.yellowgreen, .yellowgreen a, .cash.inc, .cash.inc a, .cash, .cash a { color: #677f1f }


code:
body {
	color: var(--text-color);
	background-color: var(--background-color);
}

a {
	color: var(--link-color);
}

a:hover {
	color: var(--link-hover-color);
}

button {
	color: var(--text-color);
	background-color: var(--button-color);
	border: 1px solid var(--button-border-color);
}

button:hover {
	background-color: var(--button-hover-color);
	border-color: var(--button-border-color);
}

button:disabled {
	background-color: var(--button-disabled-color);
	border: 1px solid var(--button-border-color);
}

input, select, textarea {
	color: #333;
	background-color: transparent;
	border: 1px solid var(--button-border-color);
}

input:not(:disabled):focus, select:not(:disabled):focus, textarea:not(:disabled):focus,
input:not(:disabled):hover, select:not(:disabled):hover, textarea:not(:disabled):hover {
	background-color: #e5d9f2;
	border-color: var(--button-selected-color);
}

hr {
	border-top: 1px solid var(--divider-color);
}


code:
#ui-dialog-titlebar {
	background-color: var(--divider-color);
}
#ui-dialog-body {
	background-color: var(--sidebar-color);
	border: 1px solid var(--divider-color);
}
#ui-dialog-body hr {
	background-color: var(--divider-color);
}


code:
div.building.basement {
	border-top-color: #8F8F8F;
	background-image: repeating-linear-gradient(135deg, transparent, transparent 20px, #8F8F8F 20px, #8F8F8F 30px);
}

div.building div.innerCell {
	background-color: var(--background-color);
}

div.building div.decorative {
	outline-color: black;
}

/* introduction special formatting */
/* makes all links unusable */
.intro div.building a {
	color: var(--text-color);
}

div.building div.row div.dairy {
	border-color: saddlebrown;
}


code:
div.card {
	/*border: none;*/
	border-color: #cccccc;
	background-color: #e6e6e6;
	box-shadow: 10px 10px 5px #b3b3b3;
}


code:
.lrgRender > div.mask, .lrgVector > div.mask {
	background: linear-gradient(90deg, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0.8) 60%, rgba(255, 255, 255, 0));
}

.clear-formatting {
	color: var(--text-color);
}


code:
# Themes

Every subdirectory is a distinct theme. The compiled theme is placed in `bin/` and can be loaded ingame via the options
page.

## Creating new themes

Files inside these directories are combined into one CSS file based on
alphabetical ordering. The light theme is recommended as a base for creating new themes.

For quick testing run `App.UI.Theme.loadTheme("yourTheme.css")` in the browser console. This will unload the current
theme and load the new theme. If you modify the same file it is effectively reloading. Supplying no argument (eg.
`App.UI.Theme.loadTheme()`) will unload any theme.


code:
@echo off

REM Echo the steps the script will perform
echo This script will:
echo 1. Check if dependencies are installed.
echo 2. Pull the latest changes from the specified remote repository.
echo 3. If the .git folder is not found, clone the repository from the specified URL. (https://gitgud.io/pregmodfan/fc-pregmod.git)

REM Get the directory where the batch file is located (repository directory)
set SCRIPT_DIR=%~dp0

REM Set the URL to clone the repository
set REPO_URL=https://gitgud.io/pregmodfan/fc-pregmod.git

REM Ask if we should proceed with the Git installation check
set /p PROCEED="Do you want to proceed? (y/n): "
if /I "%PROCEED%" neq "y" (
    echo Operation aborted.
    exit /B 0
)

:: run dependencyCheck.bat
CALL .\devTools\scripts\dependencyCheck.bat
SET CODE=%ERRORLEVEL%

IF %CODE% EQU 0 (
    REM Ensure we are inside the Git repository
    if exist ".git" (
        echo Pulling the latest changes from the specified remote repository...
        git pull origin
    ) else (
        echo .git folder not found. Cloning the repository...

        REM Initialize a new Git repository and set the origin
        git init
        git remote add origin %REPO_URL%
        git pull origin pregmod-master
    )
    echo Update complete!
)

@echo Exit
pause


code:
#!/bin/bash

# Echo the steps the script will perform
echo "This script will:"
echo "1. Check if dependencies are installed."
echo "2. Pull the latest changes from the specified remote repository."
echo "3. If the .git folder is not found, clone the repository from the specified URL. (https://gitgud.io/pregmodfan/fc-pregmod.git)"

# Set the URL to clone the repository
REPO_URL=https://gitgud.io/pregmodfan/fc-pregmod.git

# Ask if we should proceed with the Git installation check
read -p "Do you want to proceed? (y/n): " PROCEED
if [[ ! "$PROCEED" =~ ^[Yy]$ ]]; then
    echo "Operation aborted."
    exit 0
fi

# Run dependencyCheck.sh
./devTools/scripts/dependencyCheck.sh
CODE=$?

if [ $CODE -eq 0 ]; then
    # Ensure we are inside the Git repository
    if [ -d ".git" ]; then
        echo "Pulling the latest changes from the specified remote repository..."
        git pull origin
    else
        echo ".git folder not found. Cloning the repository..."

        # Initialize a new Git repository and set the origin
        git init
        git remote add origin $REPO_URL
        git pull origin pregmod-master
    fi
    echo "Update complete!"
fi


code:
@ECHO off
:: Free Cities File system watcher - Windows

:: run dependencyCheck.bat
CALL .\devTools\scripts\dependencyCheck.bat
SET CODE=%ERRORLEVEL%

IF %CODE% EQU 69 (
	:: if exit code is 69, then we don't have all the dependencies we need
	ECHO.
	ECHO Dependencies not met.
    ECHO.
	EXIT /b 0
) ELSE IF %CODE% EQU 0 (
	:: if exit code is 0, run watcher.js
	CALL node devTools\scripts\watcher.js
	EXIT /b 0
) ELSE (
	:: if exit code is not 0, print error message
	ECHO.
	ECHO dependencyCheck.bat exited with code: %CODE%
	ECHO Dependency check failed unexpectedly.
    ECHO.
    EXIT /b 0
)


code:
#!/bin/bash

# Free Cities File system watcher - Unix

# run dependencyCheck.sh
./devTools/scripts/dependencyCheck.sh
exitCode=$?
# exit code is now stored in $exitCode

# if exit code is 69, then we don't have all the dependencies we need
if [[ $exitCode -eq 69 ]]; then
    echo "Dependencies not met."
    echo ""
   exit 0
# if exit code is not 0, print error message
elif [[ $exitCode -ne 0 ]]; then
	echo "dependencyCheck.sh exited with code: $exitCode"
    echo ""
    exit 0
# if exit code is 0, run watcher.js
else
    node devTools/scripts/watcher.js
fi


code:
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto

###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs     diff=csharp

###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln       merge=binary
#*.csproj    merge=binary
#*.vbproj    merge=binary
#*.vcxproj   merge=binary
#*.vcproj    merge=binary
#*.dbproj    merge=binary
#*.fsproj    merge=binary
#*.lsproj    merge=binary
#*.wixproj   merge=binary
#*.modelproj merge=binary
#*.sqlproj   merge=binary
#*.wwaproj   merge=binary

###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg   binary
#*.png   binary
#*.gif   binary

###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc   diff=astextplain
#*.DOC   diff=astextplain
#*.docx  diff=astextplain
#*.DOCX  diff=astextplain
#*.dot   diff=astextplain
#*.DOT   diff=astextplain
#*.pdf   diff=astextplain
#*.PDF   diff=astextplain
#*.rtf   diff=astextplain
#*.RTF   diff=astextplain

repo_name: Modcollector/rimworld

path: .gitattributes

license: unknown


code:
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Visual Studio 2017 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUNIT
*.VisualState.xml
TestResult.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# JustCode is a .NET coding add-in
.JustCode

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# JetBrains Rider
.idea/
*.sln.iml

# CodeRush personal settings
.cr/personal

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

# Azure Stream Analytics local run output
ASALocalRun/

# MSBuild Binary and Structured Log
*.binlog

# NVidia Nsight GPU debugger configuration file
*.nvuser

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Local History for Visual Studio
.localhistory/

# BeatPulse healthcheck temp database
healthchecksdb
/Source/Patches/PawnAnimationPatches/HarmonyPatch_Pawn_DrawTracker.cs
/Source/Patches/PawnAnimationPatches/HarmonyPatch_PawnRotation.cs
/Source/Patches/PawnAnimationPatches/HarmonyPatch_PawnRenderer.cs
/Source/Patches/OtherModPatches/HarmonyPatch_ShowHairWithHats.cs
/Source/Patches/OtherModPatches/HarmonyPatch_FacialAnimation.cs
/Source/Patches/OtherModPatches/HarmonyPatch_DontShaveYourHead.cs
/Source/Patches/OtherModPatches/HarmonyPatch_CSL.cs
/Source/Patches/OtherModPatches/HarmonyPatch_AlienRace.cs
/Source/Patches/ThingAnimationPatches/HarmonyPatch_ThingDrawAt.cs
/Defs/AnimationDefs/Animations_SexToys.xml
/1.5/Defs/AnimationDefs/BasicBestiality/HumanRotatedOffset.xml
/1.5/Defs/AnimationDefs/BasicBestiality/BasicBestiality4v1.xml
/1.5/Defs/AnimationDefs/BasicBestiality/BasicBestiality3v1.xml
/1.5/Defs/AnimationDefs/BasicBestiality/BasicBestiality2v1.xml
/1.5/Defs/AnimationDefs/BasicBestiality/BasicBestiality1v1.xml
/1.5/Defs/AnimationDefs/BasicBestiality/!BasicBestiality.xml
/1.5/Defs/TestDoNotPush
/1.5/Textures/AnimationProps/BEV

path: .gitignore