code:
:: FamilyTreeJS [script]
'use strict';

var lastActiveSlave, lastSlaves, lastPC;

/*
	To use, add something like:

		<<link "Pull up the file on her family tree.">>
		<<replace #familyTreeLink>>
			<<run renderFamilyTree($slaves, $activeSlave.ID)>>
		<</replace>>
	<</link>>

*/

window.renderFamilyTree = function(slaves, filterID) {

	var ftreeWidth,ftreeHeight;
	var chartWidth, chartHeight;
	var margin;
	d3.select('#ftree-canvas').remove();
	var svg = d3.select('#familyTree')
		.append('svg')
		.attr('id','ftree-canvas');
	var chartLayer = svg.append('g').classed('chartLayer', true);

	var range = 100;
	var data = buildFamilyTree(slaves, filterID);

	initFtreeSVG(data);
	runFtreeSim(data);

	function initFtreeSVG(data) {
		ftreeWidth = data.nodes.length * 45;
		ftreeHeight = data.nodes.length * 35;
		if(ftreeWidth < 600) {
			ftreeWidth = 600;
		} else if(ftreeWidth > 1920) {
			ftreeWidth = 1920;
		}
		if(ftreeHeight < 480) {
			ftreeHeight = 480;
		} else if(ftreeHeight > 1200) {
			ftreeHeight = 1200;
		}

		margin = {top:0, left:0, bottom:0, right:0 }

		chartWidth = ftreeWidth - (margin.left+margin.right)
		chartHeight = ftreeHeight - (margin.top+margin.bottom)

		svg.attr('width', ftreeWidth).attr('height', ftreeHeight)

		var defs = svg.append('defs');

		svg.append('defs').append('marker')
			.attr('id','arrowhead')
			.attr('viewBox','-0 -5 10 10')
			.attr('refX',13)
			.attr('refY',0)
			.attr('orient','auto')
			.attr('markerWidth',13)
			.attr('markerHeight',13)
			.attr('xoverflow','visible')
			.append('svg:path')
			.attr('d', 'M 0,-1 L 5,0 L 0,1')
			.attr('fill', '#a1a1a1')
			.style('stroke','none');

		chartLayer
			.attr('width', chartWidth)
			.attr('height', chartHeight)
			.attr('transform', 'translate('+[margin.left, margin.top]+')')
	}

	function runFtreeSim(data) {
		var simulation = d3.forceSimulation()
			.force('link', d3.forceLink().id(function(d) { return d.index }))
			.force('collide',d3.forceCollide( function(d){ return 60; }).iterations(4) )
			.force('charge', d3.forceManyBody().strength(-200).distanceMin(100).distanceMax(1000))
			.force('center', d3.forceCenter(chartWidth / 2, chartHeight / 2))
			.force('y', d3.forceY(100))
			.force('x', d3.forceX(200))

		var link = svg.append('g')
			.attr('class', 'link')
			.selectAll('link')
			.data(data.links)
			.enter()
			.append('line')
			.attr('marker-end','url(#arrowhead)')
			.attr('stroke', function(d) {
				if(d.type == 'homologous') {
					return '#862d59';
				} else if(d.type == 'paternal') {
					return '#24478f';
				} else {
					return '#aa909b';
				}
			})
			.attr('stroke-width', 2)
			.attr('fill', 'none');

		var node = svg.selectAll('.node')
			.data(data.nodes)
			.enter().append('g')
			.attr('class', 'node')
			.call(d3.drag()
			.on('start', dragstarted)
			.on('drag', dragged)
			.on('end', dragended));

		node.append('circle')
			.attr('r', function(d){ return d.r })
			.attr('stroke', function(d) {
				if(d.ID == filterID) {
					return '#ffff20';
				} else {
					return '#5a5a5a';
				}
			})
			.attr('class', 'node-circle')
			.attr('r', 20);

		node.append('text')
			.text(function(d) {
				var ssym;
				if(d.ID == -1) {
					if(d.dick == 1 && d.vagina == 1) {
						ssym = '☿'
					} else if (d.dick == 1) {
						ssym = '♂';
					} else if (d.vagina == 1) {
						ssym = '♀';
					}
				} else if (d.dick > 0 && d.vagina > -1) {
					ssym = '☿'
				} else if (d.dick > 0) {
					ssym = '♂';
				} else if (d.vagina > -1) {
					ssym = '♀';
				} else {
					ssym = '?';
				}
				return d.name + '('+ssym+')';
			})
			.attr('dy', 4)
			.attr('dx', function(d) { return -(8*d.name.length)/2; })
			.attr('class', 'node-text')
			.style('fill', function(d) {
				if(d.is_mother && d.is_father) {
					return '#b84dff';
				} else if(d.is_father) {
					return '#00ffff';
				} else if(d.is_mother) {
					return '#ff3399';
				} else if(d.unborn) {
					return '#a3a3c2';
				} else {
					return '#66cc66';
				}
			});

		var circles = svg.selectAll('.node-circle');
		var texts = svg.selectAll('.node-text');

		var ticked = function() {
			link
				.attr('x1', function(d) { return d.source.x; })
				.attr('y1', function(d) { return d.source.y; })
				.attr('x2', function(d) { return d.target.x; })
				.attr('y2', function(d) { return d.target.y; });

			node
				.attr("transform", function (d) {return "translate(" + d.x + ", " + d.y + ")";});
		}

		simulation.nodes(data.nodes)
			.on('tick', ticked);

		simulation.force('link')
			.links(data.links);

		function dragstarted(d) {
			if (!d3.event.active) simulation.alphaTarget(0.3).restart();
			d.fx = d.x;
			d.fy = d.y;
		}

		function dragged(d) {
			d.fx = d3.event.x;
			d.fy = d3.event.y;
		}

		function dragended(d) {
			if (!d3.event.active) simulation.alphaTarget(0);
			d.fx = null;
			d.fy = null;
		}

	}
};

window.buildFamilyTree = function(slaves = State.variables.slaves, filterID) {
	var family_graph = {
		nodes: [],
		links: []
	};
	var node_lookup = {};
	var preset_lookup = {
		'-2': 'Social elite',
		'-3': 'Client',
		'-4': 'Former master',
		'-5': 'An arcology owner',
		'-6': 'A citizen'
	};
	var outdads = {};
	var outmoms = {};
	var kids = {};

	var fake_pc = {
		slaveName: State.variables.PC.name + '(You)',
		mother: State.variables.PC.mother,
		father: State.variables.PC.father,
		dick: State.variables.PC.dick,
		vagina: State.variables.PC.vagina,
		ID: State.variables.PC.ID
	};
	var charList = [fake_pc];
	charList.push.apply(charList, slaves);
	charList.push.apply(charList, State.variables.tanks);

	var unborn = {};
	for(var i = 0; i < State.variables.tanks.length; i++) {
		unborn[State.variables.tanks[i].ID] = true;
	}
	for (var i = 0; i < State.variables.cribs.length; i++) {
		unborn[State.variables.cribs[i].ID] = true;
	}

	for(var i = 0; i < charList.length; i++) {
		var mom = charList[i].mother;
		var dad = charList[i].father;

		if(mom) {
			if(!kids[mom]) {
				kids[mom] = {};
			}
			kids[mom].mother = true;
		}
		if(dad) {
			if(!kids[dad]) {
				kids[dad] = {};
			}
			kids[dad].father = true;
		}
	}

	for(var i = 0; i < charList.length; i++) {
		var character = charList[i];
		if(character.mother == 0 && character.father == 0 && !kids[character.ID]) {
			continue;
		}
		var mom = character.mother;
		if(mom < -6) {
			if (mom in State.variables.missingTable && State.variables.showMissingSlaves) {
				node_lookup[mom] = family_graph.nodes.length;
				var missing = State.variables.missingTable[mom];
				charList.push({ID: mom, mother: 0, father: 0, is_mother: true, dick: missing.dick, vagina: missing.vagina, slaveName: missing.slaveName});
			} else {
				if(typeof outmoms[mom] == 'undefined') {
					outmoms[mom] = [];
				}
				outmoms[mom].push(character.slaveName);
			}
		} else if(mom < 0 && typeof node_lookup[mom] == 'undefined' && typeof preset_lookup[mom] != 'undefined') {
			node_lookup[mom] = family_graph.nodes.length;
			charList.push({ID: mom, mother: 0, father: 0, is_father: true, dick: 0, vagina: 1, slaveName: preset_lookup[mom]});
		}

		var dad = character.father;
		if(dad < -6) {
			if (dad in State.variables.missingTable && State.variables.showMissingSlaves) {
				node_lookup[dad] = family_graph.nodes.length;
				var missing = State.variables.missingTable[dad];
				charList.push({ID: dad, mother: 0, father: 0, is_father: true, dick: missing.dick, vagina: missing.vagina, slaveName: missing.slaveName});
			} else {
				if(typeof outdads[dad] == 'undefined') {
					outdads[dad] = [];
				}
				outdads[dad].push(character.slaveName);
			}
		} else if(dad < 0 && typeof node_lookup[dad] == 'undefined' && typeof preset_lookup[dad] != 'undefined') {
			node_lookup[dad] = family_graph.nodes.length;
			charList.push({ID: dad, mother: 0, father: 0, is_father: true, dick: 1, vagina: -1, slaveName: preset_lookup[dad]});
		}
	}
	var mkeys = Object.keys(outmoms);
	for(var i = 0; i < mkeys.length; i++) {
		var name;
		var key = mkeys[i];
		var names = outmoms[key];
		if(names.length == 1) {
			name = names[0];
		} else if(names.length == 2) {
			name = names.join(' and ');
		} else {
			names[-1] = 'and '+names[-1];
			name = names.join(', ');
		}
		node_lookup[key] = family_graph.nodes.length;
		//Outside extant slaves set
		charList.push({ID: key, mother: 0, father: 0, is_mother: true, dick: 0, vagina: 1, slaveName: name+"'s mother"});
	}

	var dkeys = Object.keys(outdads);
	for(var i = 0; i < dkeys.length; i++) {
		var name;
		var key = dkeys[i];
		var names = outdads[key];
		if(names.length == 1) {
			name = names[0];
		} else if(names.length == 2) {
			name = names.join(' and ');
		} else {
			names[-1] = 'and '+names[-1];
			name = names.join(', ');
		}
		node_lookup[key] = family_graph.nodes.length;
		//Outside extant slaves set
		charList.push({ID: key, mother: 0, father: 0, is_father: true, dick: 1, vagina: -1, slaveName: name+"'s father"});
	}

	var charHash = {};
	for(var i = 0; i < charList.length; i++) {
		charHash[charList[i].ID] = charList[i];
	}

	var related = {};
	var seen = {};
	var saveTree = {};
	function relatedTo(character, targetID, relIDs = {tree: {}, related: false}) {
		relIDs.tree[character.ID] = true;
		if(related[character.ID]) {
			relIDs.related = true;
			return relIDs;
		}
		if(character.ID == targetID) {
			relIDs.related = true;
		}
		if(seen[character.ID]) {
			return relIDs;
		}
		seen[character.ID] = true;
		if(character.mother != 0) {
			if(charHash[character.mother]) {
				relatedTo(charHash[character.mother], targetID, relIDs);
			}
		}
		if(character.father != 0) {
			if(charHash[character.father]) {
				relatedTo(charHash[character.father], targetID, relIDs);
			}
		}
		return relIDs;
	}
	if(filterID) {
		if(charHash[filterID]) {
			var relIDs = relatedTo(charHash[filterID], filterID);
			for(var k in relIDs.tree) {
				related[k] = true;
			}
			for(var i = 0; i < charList.length; i++) {
				if(charHash[charList[i].ID]) {
					var pRelIDs = relatedTo(charHash[charList[i].ID], filterID);
					if(pRelIDs.related) {
						for(var k in pRelIDs.tree) {
							related[k] = true;
							if(saveTree[k]) {
								for(var k2 in saveTree[k].tree) {
									related[k2] = true;
								}
							}
						}
					}
					saveTree[charList[i].ID] = pRelIDs;
				}
			}
		}
	}

	for(var i = 0; i < charList.length; i++) {
		var character = charList[i];
		var char_id = character.ID;
		if(character.mother == 0 && character.father == 0 && !kids[char_id]) {
			continue;
		}
		if(filterID && !related[char_id]) {
			continue;
		}
		node_lookup[char_id] = family_graph.nodes.length;
		var char_obj = {
			ID: char_id,
			name: character.slaveName,
			dick: character.dick,
			unborn: !!unborn[char_id],
			vagina: character.vagina
		};
		if(kids[char_id]) {
			char_obj.is_mother = !!kids[char_id].mother;
			char_obj.is_father = !!kids[char_id].father;
		}	else {
			char_obj.is_mother = false;
			char_obj.is_father = false;
		}
		family_graph.nodes.push(char_obj);
	}

	for(var i = 0; i < charList.length; i++) {
		var character = charList[i];
		var char_id = character.ID;
		if(character.mother == 0 && character.father == 0 && !kids[char_id]) {
			continue;
		}
		if(filterID && !related[char_id]) {
			if(related[character.mother]) {
				console.log('wtf, mom');
			}
			if(related[character.father]) {
				console.log('wtf, dad');
			}
			continue;
		}
		if(typeof node_lookup[character.mother] != 'undefined') {
			var ltype;
			if(character.mother == character.father) {
				ltype = 'homologous';
			} else {
				ltype = 'maternal';
			}
			family_graph.links.push({
				type: ltype,
				target: node_lookup[char_id]*1,
				source: node_lookup[character.mother]*1
			});
		}
		if(character.mother == character.father) {
			continue;
		}
		if(typeof node_lookup[character.father] != 'undefined') {
			family_graph.links.push({
				type: 'paternal',
				target: node_lookup[char_id]*1,
				source: node_lookup[character.father]*1
			});
		}
	}
	return family_graph;
};

/*Old version. To use, do something like:

<<run updateFamilyTree($activeSlave, $slaves, $PC)>>

If you want to update the tree, just re-call the run line.

If there's no active slave, you can do:

<<run updateFamilyTree(null, $slaves, $PC)>>
*/

window.updateFamilyTree = function(activeSlave = lastActiveSlave, slaves = lastSlaves, PC = lastPC) {
	lastActiveSlave = activeSlave;
	lastSlaves = slaves;
	lastPC = PC;
	var treeDepth = 0;
	var numTreeNodes = 0;

	var graphElement = document.getElementById("graph");
	if(!graphElement)
		return;
	graphElement.innerHTML = "";

	/* The way this code works is that we start with the activeSlave then we call
		slaveInfo() recursively to work our way up the tree finding their parents.

	*/

	function getSlave(id, expectedGenes) {
		if(id == -1) {
			return {"slaveName":"YOU", "ID":id, "physicalAge":PC.physicalAge, "genes":PC.genes, father:PC.father, mother:PC.mother};
		}
		if(id == 0) {
			return {"slaveName":"-", "ID":id, "genes":expectedGenes};
		}
		if(id == activeSlave.ID) {
			return activeSlave;
		}
		for(var i = 0; i < slaves.length; ++i) {
			if(slaves[i].ID == id)
				return slaves[i];
			}
		return {"slaveName":"-", "ID":id, "genes":expectedGenes};
		}

	function slaveInfo(slave, activeSlaveId, recursionProtectSlaveId = {}) {
	numTreeNodes = 0;
	treeDepth = 0;
	if(recursionProtectSlaveId[slave.ID]) {
		console.log("Recursion protection");
		return slaveInfo_(slave, activeSlaveId);
	}
	recursionProtectSlaveId[slave.ID] = true;

	if(typeof slave.father == "undefined" || typeof slave.mother == "undefined")
		return slaveInfo_(slave, activeSlaveId);

	if(slave.father == -1 || slave.mother == -1) {
		return slaveInfo(getSlave(-1), activeSlaveId, recursionProtectSlaveId);
	}
	if(slave.father != 0) {
		return slaveInfo(getSlave(slave.father, "unknownXY"), activeSlaveId, recursionProtectSlaveId);
	}

	if(slave.mother != 0) {
		return slaveInfo(getSlave(slave.mother, "unknownXX"), activeSlaveId, recursionProtectSlaveId);
	}
	return slaveInfo_(slave, activeSlaveId);
	}
	function slaveInfo_(slave, activeSlaveId, slavesAdded={}, depth = 1) {
	numTreeNodes += 1;
	treeDepth = Math.max(treeDepth, depth);
	var shouldAddChildren = false;
	if(!slavesAdded[slave.ID]) {
		shouldAddChildren = true;
		slavesAdded[slave.ID] = true;
	}
	var data = {
		"name": slave.slaveName + (slave.physicalAge?(" (" + slave.physicalAge + ")"):""),
		"class" : slave.genes,
		"textClass": (activeSlaveId == slave.ID)?"emphasis":"",
		"marriages": [],
	};

	var spouseToChild = {};

	function maybeAddSpouseToChild(child) {
		if(child.ID == slave.ID)
		return;
		if (child.father == slave.ID) {
		if(!spouseToChild[child.mother]) {
			spouseToChild[child.mother] = []
		}
		spouseToChild[child.mother].push(child);
		} else if (child.mother == slave.ID) {
		if(!spouseToChild[child.father]) {
			spouseToChild[child.father] = []
		}
		spouseToChild[child.father].push(child);
		}
	}

	if(activeSlave.ID != PC.ID)
		maybeAddSpouseToChild(activeSlave);
		maybeAddSpouseToChild(getSlave(-1));

	for(var i = 0; i < slaves.length; ++i) {
		var child = slaves[i];
		if(child.ID != activeSlave.ID)
		maybeAddSpouseToChild(child);
	}

	for(var key in spouseToChild) {
		if(spouseToChild.hasOwnProperty(key)) {
			var children = shouldAddChildren?spouseToChild[key]:[];
			var spouse = getSlave(key, (slaves.genes=="XX")?"unknownXY":(slaves.genes=="XY")?"unknownXX":"unknown");
			var spouseName;
			if (spouse.ID != slave.ID){
				spouseName = spouse.slaveName + (spouse.physicalAge?(" (" + spouse.physicalAge + ")"):"")
			} else {
				spouseName = (spouse.ID==-1)?"(yourself)":"(themselves)";
			}
			var marriage = {
				"spouse": {"name": spouseName, "class": spouse.genes},
				"children": children.map(function(x) { return slaveInfo_(x, activeSlaveId, slavesAdded, depth+1)} ),
			};
			data.marriages.push(marriage);
			}
		}
	return data;
	}

	if(activeSlave == PC || activeSlave == null)
		activeSlave = getSlave(-1)
		const treeData = [slaveInfo(activeSlave, activeSlave.ID)];
		console.log("Family tree is", treeData, 'and has:', numTreeNodes);

		var parentWidth = document.getElementById('editFamily').offsetWidth;

		console.log(parentWidth, document.getElementById('passages').offsetWidth);
		if(!parentWidth)
			parentWidth = document.body.offsetWidth - 483;

			console.log(parentWidth, Math.min(200 + 40*numTreeNodes,parentWidth-200) + 200);

			dTree.init(treeData, {
			target: "#graph",
			debug: true,
			height: 50 + 50*treeDepth, /* very rough heuristics */
			width: Math.min(200 + 40*numTreeNodes,
					parentWidth-200) + 200,
			callbacks: {
				nodeClick: function(name, extra) {
			}
		}
	});

};


code:
:: Foreach Macro JS [script]

Macro.add('foreach', {
	skipArgs : true,
	tags : null,

	handler() {
		const payload = this.payload[0].contents.replace(/\n$/, '');
		let statement = this.args.raw.trim();
		let variable;
		let array;
		let result;

		if (statement.length !== 0) {
			const parts = statement.match(/^(\S+?)\s+of\s+(\S.*?)\s*$/i);
			if(parts !== null) {
				variable = parts[1];
				array = parts[2];
			}
		}
		if(!variable || !array) {
			return this.error('invalid syntax, format: <<foreach variable of array-expression>>... <</foreach>>');
		}

		try {
			result = Scripting.evalTwineScript(array);
		} catch (ex) {
			return this.error(`bad evaluation: ${typeof ex === 'object' ? ex.message : ex}`);
		}

		let resultLength = result['length'];

		// We don't check for "instanceof Array" to also be able to pass arguments or other strange objects
		if(typeof resultLength !== 'number' || (resultLength % 1) !== 0) {
			return this.error("bad evaluation: '" + result + "' is not an array or array-like object");
		}

		if(resultLength > Config.macros.maxLoopIterations) {
			return this.error('Array too large for maxLoopIterations (' + resultLength + ' > ' + Config.macros.maxLoopIterations + ')');
		}

		if(!new RegExp(`^(${Patterns.variable})$`).test(variable)) {
			return this.error('not a variable identifier: ' + variable);
		}

		if(resultLength <= 0) {
			// Don't bother with empty stuff
			return;
		}

		Config.debug && this.debugView.modes({block: true});

		payload.replace(/^\n/, '');
		for(let i = 0; i < resultLength; ++ i) {
			Wikifier.setValue(variable, result[i]);
			new Wikifier(this.output, payload);
		}
	}
});


code:
:: FResult [script]

// this is a port of the FResult widget
// it has been broken up into several functions, because it grew too long
// it has been wrapped in a closure so as not to pollute the global namespace
// and so that nested functions are only evaluated once

window.FResult = (function() {
	"use strict";
	// we can't initialise our global variables on load, because SugarCube.State isn't initialised
	// instead, declare them and initialise on run time
	let V, incest_bonus;
	function FResult(slave) {
		V = State.variables;
		incest_bonus = V.arcologies[0].FSEgyptianRevivalist > 20 || V.arcologies[0].FSEgyptianRevivalistIncestPolicy === 1;

		calcUseWeights(slave);
		if (!slave.fuckdoll)
			calcNotFuckdoll(slave);
		else
			V.FResult += slave.fuckdoll/10;

		V.FResult += Math.max(0, slave.aphrodisiacs) * 2;

		if (slave.inflationType === "aphrodisiac")
			V.FResult += slave.inflation*4;

		if (slave.lactation > 0)
			V.FResult += 1;

		if (slave.nipples === "fuckable")
			calcFuckableTits(slave);

		if (V.seeAge === 1)
			calcAge(slave);
		if (slave.fetish === "mindbroken")
			V.FResult = Math.trunc(V.FResult*0.4);
		else
			V.FResult = Math.trunc(V.FResult*0.7);

		if (slave.pregWeek < 0)
			V.FResult -= Math.trunc(V.FResult*slave.pregWeek/10); // reduced the most just after birth

		calcAmputation(slave);

		if (V.arcologies[0].FSHedonisticDecadence > 20)
			calcHedonismWeight(slave);
		if (V.FResult < 2) {
			if (supremeRaceP(slave) && V.arcologies[0].FSSupremacist > 20)
				V.FResult = 0;
			else
				V.FResult = 2;
		}
	}

	function calcUseWeights(slave) {
		V.FResult = (3 - slave.anus)+(slave.muscles/30);
		if (slave.muscles < -95)
			V.FResult -= 5;
		else if (slave.muscles < -30)
			V.FResult -= 2;

		const uses = V.oralUseWeight + V.vaginalUseWeight + V.analUseWeight;
		if (uses <= 0) return;

		V.FResult += (6+slave.tonguePiercing) * (V.oralUseWeight/uses) * (slave.oralSkill/30);
		if (slave.sexualFlaw === "cum addict")
			V.FResult += (V.oralUseWeight/uses) * (slave.oralSkill/30);
		if (canDoVaginal(slave)) {
			V.FResult += 6 * (V.vaginalUseWeight/uses) * (slave.vaginalSkill/30);
			V.FResult += (3 - slave.vagina);
			V.FResult += slave.vaginaLube;
		}
		if (canDoAnal(slave)) {
			V.FResult += 6 * (V.analUseWeight/uses) * (slave.analSkill/30);
			if (slave.sexualFlaw === "anal addict")
				V.FResult += (V.analUseWeight/uses) * (slave.analSkill/30);
			if (slave.inflationType === "aphrodisiac")
				V.FResult += (V.analUseWeight/uses) * (slave.inflation * 3);
		}
	}

	function calcFuckableTits(slave) {
		V.FResult += 2;
		if (slave.fetish === "boobs")
			V.FResult += Math.trunc(slave.fetishStrength/20);
	}

	function calcWorksWithRelatives(slave) {
		V.slaves.forEach(islave => {
			if (isParentP(slave, islave) && sameAssignmentP(slave, islave)) {
				V.FResult += 1;
				if (incest_bonus) V.FResult += 1;
			}
			if (areSisters(slave, islave) > 0 && sameAssignmentP(slave, islave)) {
				V.FResult += 1;
				if (incest_bonus) V.FResult += 1;
			}
		});
	}

	function calcWorksWithRelativesVanilla(slave) {
		const fre = getSlave(slave.relationTarget)
		if (fre !== undefined && sameAssignmentP(slave, fre)) {
			V.FResult += 2;
			if (incest_bonus) V.FResult += 2;
		}
	}

	function calcWorksWithRelationship(slave) {
		const fre = V.slaves.findIndex(s => {
			return haveRelationshipP(slave, s) && sameAssignmentP(slave, s);
		});
		if (fre !== -1) V.FResult += 1;
	}

	function calcWorksWithRival(slave) {
		const en = V.slaves.findIndex(s => {
			return isRivalP(slave, s) && sameAssignmentP(slave, s);
		});
		if (en !== -1) V.FResult -= 1;
	}

	function calcHInjectionsDiet(slave) {
		if (slave.drugs === "male hormone injections" || slave.drugs === "female hormone injections")
			V.FResult -= 10;
		if (slave.diet === "XXY")
			V.FResult += 2;
		else if (slave.diet === "XY" || slave.diet === "XX")
			V.FResult += 1;
		else if (slave.diet === "cum production")
			V.FResult += 1;
		else if (slave.diet === "fertility")
			V.FResult += 1;
	}
	function calcPreg(slave) {
		if (V.arcologies[0].FSRepopulationFocus > 20) {
			if (slave.preg > 10) V.FResult += 2;
			else V.FResult -= 2;
		} else if (V.arcologies[0].FSRestart > 20) {
			if (slave.bellyPreg >= 500 && slave.breedingMark === 1 && V.propOutcome === 1)
				V.FResult += 1;
			else if (slave.preg > 10)
				V.FResult -= 10;
			else
				V.FResult += 0;
		}
	}

	function calcRace(slave) {
		if (V.arcologies[0].FSSupremacist !== "unset" && supremeRaceP(slave))
			V.FResult -= (V.arcologies[0].FSSupremacist/5) + (V.arcologies[0].FSSupremacistLawME*10);
		if (V.arcologies[0].FSSubjugationist !== "unset" && inferiorRaceP(slave))
			V.FResult += (V.arcologies[0].FSSubjugationist/10) + (V.arcologies[0].FSSubjugationistLawME);
	}

	function calcSexAttributes(slave) {
		if (slave.clitPiercing > 2) V.FResult += 1;
		if (slave.fetishKnown === 1 && slave.fetishStrength > 60 && slave.fetish != "none")
			V.FResult += slave.fetishStrength/5;

		if (slave.attrKnown === 1) {
			V.FResult += Math.trunc(slave.attrXX/20);
			V.FResult += Math.trunc(slave.attrXY/20);
			if (slave.energy > 95) V.FResult += 3;
			else if (slave.energy > 80) V.FResult += 2;
			else if (slave.energy > 60) V.FResult += 1;
			else if (slave.energy <= 20) V.FResult -= 2;
			else if (slave.energy <= 40) V.FResult -= 1;
		}
		if (slave.sexualFlaw !== "none") V.FResult -= 2;
		if (slave.sexualQuirk !== "none") V.FResult += 2;
		if (slave.behavioralFlaw !== "none") V.FResult -= 2;
		if (slave.behavioralQuirk !== "none") V.FResult += 2;
	}

	function calcCareer(slave) {
		if (setup.whoreCareers.includes(slave.career))
			V.FResult += 1;
		else if (slave.oralCount + slave.analCount + slave.vaginalCount + slave.mammaryCount + slave.penetrativeCount > 1000)
			V.FResult += 1;
	}

	function calcSight(slave) {
		if (!canSee(slave)) V.FResult -= 3;
		else if (slave.eyes <= -1) {
			if (slave.eyewear !== "corrective glasses" && slave.eyewear !== "corrective contacts")
				V.FResult -= 1;
		} else if (slave.eyewear === "blurring glasses")
			V.FResult -= 1;
		else if (slave.eyewear === "blurring contacts")
			V.FResult -= 1;
	}

	function calcHearing(slave) {
		if (!canHear(slave)) V.FResult -= 2;
		else if (slave.hears <= -1) {
			if (slave.earwear !== "hearing aids")
				V.FResult -= 1;
		} else if (slave.earwear === "muffling ear plugs")
			V.FResult -= 1;
	}

	function calcEgyptianBonus(slave) {
		if (V.racialVarieties === undefined) V.racialVarieties = [];
		if (!V.racialVarieties.includes(slave.race))
			V.racialVarieties.push(slave.race);
	}

	function calcYouthBonus(slave) {
		if (slave.visualAge < 30) {
			if (slave.actualAge > 30)
				V.FResult += 5; // experienced for her apparent age
			if (slave.physicalAge > 30)
				V.FResult -= slave.physicalAge/2; // too old :(
		}
	}

	function calcMatureBonus(slave) {
		if (slave.visualAge >= 30 && slave.actualAge >= 30 && slave.physicalAge < slave.visualAge)
			V.FResult += Math.min((slave.physicalAge - slave.visualAge) * 2, 20); // looks and acts mature, but has a body that just won't quit
	}

	function calcNotFuckdoll(slave) {
		if (V.familyTesting === 1 && totalRelatives(slave) > 0)
			calcWorksWithRelatives(slave);
		else if(!V.familyTesting && slave.relation !==0)
			calcWorksWithRelativesVanilla(slave);
		if (slave.relationship > 0) calcWorksWithRelationship(slave);
		if (slave.rivalry !== 0) calcWorksWithRival(slave);
		calcHInjectionsDiet(slave);
		calcPreg(slave);
		calcRace(slave);
		calcSexAttributes(slave);
		calcCareer(slave);
		calcSight(slave);
		calcHearing(slave);
		if (V.arcologies[0].FSEgyptianRevivalist !== "unset")
			calcEgyptianBonus(slave);
		if (V.arcologies[0].FSYouthPreferentialist !== "unset")
			calcYouthBonus(slave);
		else if (V.arcologies[0].FSMaturityPreferentialist !== "unset")
			calcMatureBonus(slave);
	}

	function calcAge(slave) {
		if ((V.arcologies[0].FSRepopulationFocus !== "unset" || V.arcologies[0].FSGenderFundamentalist !== "unset") && slave.physicalAge === V.minimumSlaveAge && slave.physicalAge === V.fertilityAge && canGetPregnant(slave)) {
			V.FResult += 1;
			if (slave.birthWeek === 0) V.FResult += V.FResult;
			else if (slave.birthWeek < 4) V.FResult += 0.2*V.FResult;
		} else if (slave.physicalAge === V.minimumSlaveAge) {
			V.FResult += 1;
			if (slave.birthWeek === 0 ) V.FResult += 0.5*V.FResult;
			else if (slave.birthWeek < 4) V.FResult += 0.1*V.FResult;
		} else if (slave.physicalAge === V.fertilityAge && canGetPregnant(slave) && (V.arcologies[0].FSRepopulationFocus !== "unset" || V.arcologies[0].FSGenderFundamentalist !== "unset")) {
			V.FResult += 1;
			if (slave.birthWeek === 0)
				V.FResult += 0.5*V.FResult;
			else if (slave.birthWeek < 4)
				V.FResult += 0.1*V.FResult;
		}
	}

	function calcAmputation(slave) {
		switch(slave.amp) {
		case 0:
			break;
		case 1:
			V.FResult -= 2;
			break;
		case -2:
			break;
		case -5:
			break;
		default:
			V.FResult -= 1;
		}
	}

	function calcHedonismWeight(slave) {
		if (slave.weight < 10)
			V.FResult -= 2;
		else if (slave.weight > 190)
			V.FResult -= 5; // too fat
	}
	return FResult;
})();


code:
:: Generate New Slave JS [script]

window.GenerateNewSlave = (function(){
	'use strict';
	let V, slave;

	function GenerateNewSlave(sex) {
		V = State.variables;
		BaseSlave();
		slave = V.activeSlave;

		preGenCombinedStats();
		if (!sex) {
			if (jsRandom(0,99) < V.seeDicks) {
				GenerateXYSlave();
			} else if (V.seeDicks > 0) {
				var femaleSlaveGen = 80;
				if (V.arcologies[0].FSGenderFundamentalistSMR == 1 || V.arcologies[0].FSRepopulationFocusSMR == 1) {
					femaleSlaveGen = 90;
				} else if (V.arcologies[0].FSGenderRadicalist != "unset") {
					femaleSlaveGen = 50;
				}
				if (jsRandom(1,100) > femaleSlaveGen && jsRandom(0,99) < V.seeDicks) {
					GenerateXYSlave();
				} else {
					GenerateXXSlave();
				}
			} else {
				GenerateXXSlave();
			}
		} else if (sex == "XY") {
			GenerateXYSlave();
		} else {
			GenerateXXSlave();
		}

		postGenCleanup();
	};

	function preGenCombinedStats() {
		slave.ID = V.IDNumber++;
		slave.weekAcquired = V.week;
		slave.canRecruit = 1;
		slave.devotion = jsRandom(-90,-60);
		slave.trust = jsRandom(-45,-25);
		slave.weight = jsRandom(-100,180);
		slave.health = jsRandom(-50,50);

		WombInit(slave);
		generateAge();
		generateIntelligence();
		generateCareer();
		generateNationality(); /* includes race selection */
		generateAccent();
		nationalityToName(slave);
		generateRacialTraits();
	};

	function postGenCleanup() {
		generateBoobTweaks(); /* split this up for female vs. male? */
		generateSkills();
		generateDisabilities();
		generatePronouns(slave);
		slave.origRace = slave.race;
		slave.origEye = slave.eyeColor;
		slave.origHColor = slave.hColor;
		slave.origSkin = slave.skin;
	};

	function GenerateXXSlave() {
		slave.ovaries = 1;
		slave.pubertyXY = 0;
		slave.energy = jsRandom(1,85);
		/* Superfetation as rare genetic disorder 1 from 100, not active for now.
		if (jsRandom(0,99) < 1) {
			slave.superfetation = 1;
		}
		*/

		generateXXBodyProportions();
		generateVagina();
		generateXXPreferences();
		generateXXButt();
		generateXXBoobs();
		generateXXFace();
		generateXXPregAdaptation();
		generateXXVoice();
		generateXXTeeth();
		generateXXMods();
		generateXXBodyHair();
		generateXXPuberty();
	};

	function GenerateXYSlave() {
		slave.genes = "XY";
		slave.hLength = 10;
		slave.prostate = 1;
		slave.pubertyXX = 0;
		slave.energy = jsRandom(15,90);

		generateXYBodyProportions();
		generateDick();
		generateCircumcision();
		generateXYPreferences(); /* must happen after genitalia generation */
		generateXYButt(); /* must happen after preferences */
		generateXYBoobs();
		generateXYFace();
		generateXYPregAdaptation();
		generateXYVoice();
		generateXYTeeth();
		generateXYMods();
		generateXYBodyHair();
		generateXYPuberty();
	};

	function generateXXBodyProportions() {
		slave.height = Math.round(Height.random(slave));
		if (slave.height >= Height.mean(slave) * 170/162.5) {
			slave.hips = jsEither([-1, 0, 0, 1, 1, 2, 2]);
			slave.shoulders = jsEither([-1, -1, 0, 0, 0, 1]);
		} else {
			slave.hips = jsEither([-1, 0, 0, 0, 1, 1, 2]);
			slave.shoulders = jsEither([-2, -1, -1, 0, 0, 1]);
		}
		if (slave.physicalAge <= 11) {
			slave.hips = jsEither([-2, -2, -1, -1, 0]);
		} else if (slave.physicalAge <= 13) {
			slave.hips = jsEither([-2, -1, -1, 0, 1]);
		}
		if (slave.weight < -30) {
			slave.waist = jsRandom(-55,0);
		} else if (slave.physicalAge < 13) {
			slave.waist = jsRandom(-25,25);
		} else if (slave.weight <= 30) {
			slave.waist = jsRandom(-45,45);
		} else if (slave.weight <= 160) {
			slave.waist = jsRandom(0,55);
		} else {
			slave.waist = jsRandom(50,100);
		}
	};

	function generateXYBodyProportions() {
		slave.height = Math.round(Height.random(slave));
		if (slave.physicalAge <= 13) {
			if (slave.height > Height.mean(slave) * 170/172.5) {
				slave.hips = jsEither([-2, -1, -1, 0, 1]);
				slave.shoulders = jsEither([-1, -1, 0, 0, 0, 1]);
			} else {
				slave.hips = jsEither([-2, -2, -1, -1, 0]);
				slave.shoulders = jsEither([-2, -1, -1, 0, 0, 1]);
			}
		} else {
			if (slave.height > Height.mean(slave) * 170/172.5) {
				slave.hips = jsEither([-2, -1, -1, 0, 1]);
				slave.shoulders = jsEither([-1, 0, 1, 1, 2, 2]);
			} else {
				slave.hips = jsEither([-2, -2, -1, -1, 0]);
				slave.shoulders = jsEither([-1, 0, 0, 1, 1, 2]);
			}
		}
		if (slave.physicalAge < 13) {
			slave.waist = jsRandom(-15,25);
		} else if (slave.weight < -30) {
			slave.waist = jsRandom(-45,45);
		} else if (slave.weight <= 30) {
			slave.waist = jsRandom(-15,65);
		} else if (slave.weight <= 160) {
			slave.waist = jsRandom(5,100);
		} else {
			slave.waist = jsRandom(50,100);
		}
	};

	function generateVagina() {
		if (slave.physicalAge <= 13) {
			slave.vagina = jsEither([0, 0, 0, 0, 0, 0, 0, 1]);
		} else if (slave.physicalAge <= 15) {
			slave.vagina = jsEither([0, 0, 0, 0, 0, 0, 1, 1]);
		} else if (slave.physicalAge <= 17) {
			slave.vagina = jsEither([0, 0, 0, 0, 0, 1, 1, 1]);
		} else if (slave.physicalAge < 20) {
			slave.vagina = jsEither([0, 1]);
		} else if (slave.physicalAge > 30) {
			slave.vagina = jsEither([1, 1, 1, 1, 2]);
		} else {
			slave.vagina = jsEither([0, 0, 1, 1, 1]);
		}

		if (slave.physicalAge <= 11) {
			slave.clit = jsEither([0, 0, 0, 0, 0, 0, 0, 0, 1]);
		} else if (slave.physicalAge <= 13) {
			slave.clit = jsEither([0, 0, 0, 0, 0, 0, 0, 1, 1]);
		} else if (slave.physicalAge <= 15) {
			slave.clit = jsEither([0, 0, 0, 0, 0, 0, 0, 1, 2]);
		} else {
			slave.clit = jsEither([0, 0, 0, 0, 0, 0, 1, 1, 2]);
		}

		if (slave.physicalAge <= 11) {
			slave.labia = jsEither([0, 0, 0, 0, 0, 0, 0, 1, 1]);
		} else if (slave.physicalAge <= 12) {
			slave.labia = jsEither([0, 0, 0, 0, 0, 0, 1, 1, 1]);
		} else if (slave.physicalAge <= 13) {
			slave.labia = jsEither([0, 0, 0, 0, 0, 1, 1, 1, 1]);
		} else if (slave.physicalAge <= 14) {
			slave.labia = jsEither([0, 0, 0, 0, 1, 1, 1, 1, 2]);
		} else if (slave.physicalAge <= 15) {
			slave.labia = jsEither([0, 0, 0, 1, 1, 1, 1, 2, 2]);
		} else {
			slave.labia = jsEither([0, 0, 0, 1, 1, 1, 1, 2, 2, 3]);
		}

		if (slave.energy < random(1,80)) {
			slave.vaginaLube = 0;
		} else if ( slave.physicalAge > random(35,60)) {
			slave.vaginaLube = 0;
		} else {
			slave.vaginaLube = 1;
		}
		slave.foreskin = jsRandom(0,4);
	};

	function generateDick() {
		slave.vagina = -1;
		slave.clit = 0;
		slave.preg = 0;

		if (slave.physicalAge <= 13) {
			slave.dick = jsEither([1, 1, 1, 1, 2, 2, 2, 3]);
			if (V.seeExtreme == 1) {
				slave.balls = jsEither([0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3]);
			} else {
				slave.balls = jsEither([1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3]);
			}
			slave.scrotum = slave.balls;
		} else if (slave.physicalAge <= 15) {
			slave.dick = jsEither([1, 1, 1, 2, 2, 2, 3]);
			if (V.seeExtreme == 1) {
				slave.balls = jsEither([0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4]);
			} else {
				slave.balls = jsEither([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4]);
			}
			slave.scrotum = slave.balls;
		} else if (slave.physicalAge <= 17) {
			slave.dick = jsEither([1, 1, 2, 2, 3, 3]);
			if (V.seeExtreme == 1) {
				slave.balls = jsEither([0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5]);
			} else {
				slave.balls = jsEither([1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 5]);
			}
			slave.scrotum = slave.balls;
		} else {
			slave.dick = jsEither([1, 2, 2, 2, 3, 3, 3, 4, 4, 5]);
			if (V.seeExtreme == 1) {
				slave.balls = jsEither([0, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5]);
			} else {
				slave.balls = jsEither([1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5]);
			}
			if (slave.balls != 0) {
				slave.scrotum = slave.balls + jsEither([0, 0, 1]);
			} else {
				slave.scrotum = 0;
			}
			if (jsRandom(1,100) < 3) {
				slave.vasectomy = 1;
			}
		}
	};

	function generateCircumcision() {
		/* The default rate of 50* is wildly unrepresentative, and there is extreme regional variation. */
		/*
		 What we want is the prevalence among newborns, since this game
		 happens about 20 years in the future, but we'll use this lacking
		 something better.
		 https://pophealthmetrics.biomedcentral.com/articles/10.1186/s12963-016-0073-5
		 Right now we mostly just break it down by country.
		 It would be better to break it down by both country
		 and race if statistics are available.
		*/
		if (V.seeCircumcision == 0) {
			slave.foreskin = slave.dick + jsRandom(0,1);
		} else {
			/* Temporarily use activeSlave.foreskin to store the chance of circumcision. */
			switch (slave.nationality) {
				case "Afghan":
				case "Iranian":
				case "Moroccan":
				case "Palestinian":
				case "Sahrawi":
				case "Tunisian":
					slave.foreskin = 100;
					break;
				case "Comorian":
				case "Gabonese":
				case "Iraqi":
				case "Jordanian":
				case "Kurdish":
				case "Mauritanian":
				case "Nigerian":
				case "Tajik":
				case "Turkish":
				case "Yemeni":
					slave.foreskin = 99;
					break;
				case "Algerian":
				case "Azerbaijani":
				case "Liberian":
				case "Maldivian":
					slave.foreskin = 98;
					break;
				case "Djiboutian":
				case "Eritrean":
				case "Ivorian":
				case "Libyan":
				case "Saudi":
				case "Uzbek":
					slave.foreskin = 97;
					break;
				case "Nigerien":
				case "Pakistani":
				case "Sierra Leonean":
				case "Turkmen":
				case "Zairian":
					slave.foreskin = 96;
					break;
				case "a Cook Islander":
				case "Egyptian":
				case "Gambian":
				case "Guamanian":
				case "Malagasy":
				case "Nauruan":
				case "Ni-Vanuatu":
				case "Niuean":
				case "Palauan":
				case "Samoan":
				case "a Solomon Islander":
				case "Togolese":
				case "Tongan":
				case "Tuvaluan":
					slave.foreskin = 95;
					break;
				case "Cameroonian":
				case "Senegalese":
				case "Somali":
					slave.foreskin = 94;
					break;
				case "Bangladeshi":
				case "Beninese":
				case "Bissau-Guinean":
				case "Indonesian":
				case "Syrian":
					slave.foreskin = 93;
					break;
				case "Ethiopian":
				case "Filipina":
				case "Ghanan":
				case "Israeli":
				case "Kosovan":
					slave.foreskin = 92;
					break;
				case "Kenyan":
				case "Kyrgyz":
					slave.foreskin = 91;
					break;
				case "Burkinabé":
				case "Omani":
					slave.foreskin = 88;
					break;
				case "Equatoguinean":
					slave.foreskin = 87;
					break;
				case "Kuwaiti":
				case "Malian":
					slave.foreskin = 86;
					break;
				case "Guinean":
					slave.foreskin = 84;
					break;
				case "Bahraini":
					slave.foreskin = 81;
					break;
				case "French Polynesian":
					slave.foreskin = 78;
					break;
				case "American":
				case "Qatari":
					slave.foreskin = 77;
					break;
				case "Emirati":
					slave.foreskin = 76;
					break;
				case "Chadian":
					slave.foreskin = 74;
					break;
				case "Tanzanian":
					slave.foreskin = 72;
					break;
				case "Congolese":
					slave.foreskin = 70;
					break;
				case "Central African":
					slave.foreskin = 63;
					break;
				case "Burundian":
				case "Malaysian":
					slave.foreskin = 61;
					break;
				case "Lebanese":
					slave.foreskin = 60;
					break;
				case "Angolan":
					slave.foreskin = 58;
					break;
				case "Fijian":
				case "Kazakh":
					slave.foreskin = 56;
					break;
				case "Bruneian":
				case "Korean":
				case "Mosotho":
					/* Population-weighted average of South Korea and North Korea. */
					slave.foreskin = 52;
					break;
				case "New Caledonian":
					slave.foreskin = 50;
					break;
				case "Albanian":
					slave.foreskin = 48;
					break;
				case "Mozambican":
					slave.foreskin = 47;
					break;
				case "South African":
					slave.foreskin = 45;
					break;
				case "Dominican":
					slave.foreskin = 43;
					break;
				case "Bosnian":
					slave.foreskin = 42;
					break;
				case "Sudanese":
					slave.foreskin = 39;
					break;
				case "Mexican":
					slave.foreskin = 38;
					break;
				case "Macedonian":
					slave.foreskin = 34;
					break;
				case "a New Zealander":
					slave.foreskin = 33;
					break;
				case "Canadian":
					slave.foreskin = 32;
					break;
				case "Scottish":
					slave.foreskin = 28;
					break;
				case "Australian":
				case "Ugandan":
					slave.foreskin = 27;
					break;
				case "Namibian":
					slave.foreskin = 26;
					break;
				case "South Sudanese":
					slave.foreskin = 24;
					break;
				case "Belgian":
				case "Cypriot":
				case "Thai":
					slave.foreskin = 23;
					break;
				case "Malawian":
					slave.foreskin = 22;
					break;
				case "British":
					slave.foreskin = 21;
					break;
				case "Puerto Rican":
					slave.foreskin = 20;
					break;
				case "Montenegrin":
					slave.foreskin = 19;
					break;
				case "Mauritian":
					slave.foreskin = 17;
					break;
				case "Motswana":
				case "Singaporean":
				case "Surinamese":
					slave.foreskin = 15;
					break;
				case "Chinese":
				case "French":
				case "Indian":
				case "Jamaican":
					slave.foreskin = 14;
					break;
				case "Bulgarian":
				case "Rwandan":
				case "Zambian":
					slave.foreskin = 13;
					break;
				case "French Guianan":
				case "Guyanese":
				case "Russian":
					slave.foreskin = 12;
					break;
				case "German":
					slave.foreskin = 11;
					break;
				case "Belarusian":
				case "Georgian":
				case "Papua New Guinean":
					slave.foreskin = 10;
					break;
				case "Japanese":
				case "Zimbabwean":
					slave.foreskin = 9;
					break;
				case "Slovene":
				case "Sri Lankan":
				case "Swazi":
				case "Taiwanese":
					slave.foreskin = 8;
					break;
				case "Catalan":
				case "Haitian":
				case "Spanish":
					slave.foreskin = 7;
					break;
				case "Austrian":
				case "Dutch":
				case "East Timorese":
				case "Swiss":
				case "Trinidadian":
					slave.foreskin = 6;
					break;
				case "Danish":
				case "Greek":
				case "a Liechtensteiner":
				case "Swedish":
					slave.foreskin = 5;
					break;
				case "Burmese":
				case "Cambodian":
				case "Mongolian":
				case "Nepalese":
				case "Peruvian":
				case "Serbian":
					slave.foreskin = 4;
					break;
				case "Argentinian":
				case "Italian":
				case "Norwegian":
					slave.foreskin = 3;
					break;
				case "Luxembourgian":
				case "Tibetan":
				case "Ukrainian":
				case "Vincentian":
					slave.foreskin = 2;
					break;
				case "Andorran":
				case "Barbadian":
				case "Bermudian":
				case "Bhutanese":
				case "Brazilian":
				case "Croatian":
				case "Finnish":
				case "Hungarian":
				case "Irish":
				case "Moldovan":
				case "Monégasque":
				case "Panamanian":
				case "Portuguese":
				case "Seychellois":
				case "Uruguayan":
					slave.foreskin = 1;
					break;
				case "Antiguan":
				case "Armenian":
				case "Bahamian":
				case "Belizean":
				case "Bolivian":
				case "Cape Verdean":
				case "Chilean":
				case "Colombian":
				case "Costa Rican":
				case "Cuban":
				case "Czech":
				case "Dominiquais":
				case "Ecuadorian":
				case "Estonian":
				case "Greenlandic":
				case "Grenadian":
				case "Guatemalan":
				case "Honduran":
				case "I-Kiribati":
				case "Icelandic":
				case "Kittitian":
				case "Laotian":
				case "Latvian":
				case "Lithuanian":
				case "Maltese":
				case "Marshallese":
				case "Micronesian":
				case "Nicaraguan":
				case "Paraguayan":
				case "Polish":
				case "Romanian":
				case "Saint Lucian":
				case "Salvadoran":
				case "Sammarinese":
				case "São Toméan":
				case "Slovak":
				case "Vatican":
				case "Venezuelan":
				case "Vietnamese":
					slave.foreskin = 0;
					break;
				default:
					/* Some overlooked country, or possibly stateless. Use global average. */
					slave.foreskin = 38;
			}
			/* Second pass for minorities in other countries. */
			if (slave.race == "middle eastern" && slave.foreskin < 76) {
				slave.foreskin = 76;
			}
			if (slave.race == "semitic" && slave.foreskin < 90) {
				slave.foreskin = 90;
			}
			/* Chance activeSlave.foreskin back to the normal meaning. */
			if (jsRandom(0,99) < slave.foreskin) {
				slave.foreskin = 0;
			} else {
				slave.foreskin = slave.dick + jsRandom(0,1);
			}
		}
	};

	function generateXXPreferences() {
		randomizeAttraction(slave);
		slave.fetishStrength = jsRandom(0,90);
		slave.fetish = jsEither(["none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "submissive", "submissive", "cumslut", "humiliation", "humiliation", "buttslut", "boobs", "pregnancy", "dom", "sadist", "masochist"]);
		slave.behavioralFlaw = jsEither(["none", "none", "none", "arrogant", "bitchy", "odd", "hates men", "hates women", "hates women", "anorexic", "gluttonous", "devout", "liberated"]);

		if (slave.behavioralFlaw == "devout") {
			slave.sexualFlaw = jsEither(["none", "repressed", "shamefast", "apathetic", "crude", "judgemental"]);
		} else {
			slave.sexualFlaw = jsEither(["none", "none", "none", "none", "hates oral", "hates anal", "hates penetration", "repressed", "idealistic", "shamefast", "apathetic", "crude", "judgemental"]);
		}
		if (slave.behavioralFlaw == "none" && jsRandom(1,10) == 1) {
			slave.behavioralQuirk = jsEither(["confident", "cutting", "funny", "adores women", "adores men", "fitness", "insecure", "sinful", "advocate"]);
		}
		if (slave.sexualFlaw == "none" && jsRandom(1,10) == 1) {
			slave.sexualQuirk = jsEither(["gagfuck queen", "painal queen", "strugglefuck queen", "tease", "romantic", "perverted", "caring", "unflinching", "size queen"]);
		}
	};

	function generateXYPreferences() {
		randomizeAttraction(slave);
		slave.fetishStrength = jsRandom(0,90);
		slave.fetish = jsEither(["none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "none", "submissive", "cumslut", "humiliation", "buttslut", "buttslut", "boobs", "dom", "sadist", "masochist"]);
		slave.behavioralFlaw = jsEither(["none", "none", "none", "arrogant", "bitchy", "odd", "hates men", "hates men", "hates men", "hates women", "anorexic", "gluttonous", "devout", "liberated"]);

		if (slave.behavioralFlaw == "devout") {
			slave.sexualFlaw = jsEither(["none", "repressed", "shamefast", "apathetic", "crude", "judgemental"]);
		} else {
			slave.sexualFlaw = jsEither(["none", "none", "none", "none", "hates oral", "hates anal", "hates anal", "repressed", "idealistic", "shamefast", "apathetic", "crude", "judgemental"]);
		}
		if (slave.behavioralFlaw == "none" && jsRandom(1,10) == 1) {
			slave.behavioralQuirk = jsEither(["confident", "cutting", "funny", "adores women", "adores men", "fitness", "insecure", "sinful", "advocate"]);
		}
		if (slave.sexualFlaw == "none" && jsRandom(1,10) == 1) {
			slave.sexualQuirk = jsEither(["gagfuck queen", "painal queen", "strugglefuck queen", "tease", "romantic", "perverted", "caring", "unflinching", "size queen"]);
		}
	};

	function generateXXButt() {
		if (slave.physicalAge <= 11) {
			slave.butt = jsEither([1, 1, 1, 1, 1, 1, 1]);
		} else if (slave.physicalAge <= 12) {
			slave.butt = jsEither([1, 1, 1, 1, 1, 2, 2]);
		} else if (slave.physicalAge <= 13) {
			slave.butt = jsEither([1, 1, 1, 1, 2, 2, 2]);
		} else if (slave.physicalAge <= 14) {
			slave.butt = jsEither([1, 1, 1, 2, 2, 2, 3]);
		} else if (slave.physicalAge <= 15) {
			slave.butt = jsEither([1, 1, 2, 2, 2, 2, 3]);
		} else {
			switch (slave.race) {
				case "black":
					slave.butt = jsEither([1, 2, 2, 3, 3, 4, 4]);
					break;
				case "indo-aryan":
				case "malay":
				case "pacific islander":
				case "amerindian":
				case "asian":
				case "middle eastern":
				case "semitic":
				case "southern european":
					slave.butt = jsEither([1, 2, 2, 3, 3]);
					break;
				default:
					slave.butt = jsEither([1, 2, 2, 3, 3, 4]);
			}
		}
		if (V.weightAffectsAssets != 0) {
			if (slave.weight < -10 && slave.butt > 1) {
				slave.butt -= 1;
			} else if (slave.weight > 100 && slave.butt < 6) {
				slave.butt += jsRandom(1,2);
			} else if (slave.weight > 10 && slave.butt < 4) {
				slave.butt += 1;
			}
		}
		slave.anus = jsEither([0, 0, 1, 1, 2]);
		slave.analArea = slave.anus + jsEither([0, 0, 0, 1]);
	};

	function generateXYButt() {
		if (slave.physicalAge <= 13) {
			slave.butt = jsEither([1, 1, 1, 2, 2, 3, 3, 4]);
		} else {
			slave.butt = jsEither([1, 1, 2, 3]);
		}
		if (V.weightAffectsAssets != 0) {
			if (slave.weight < -10 && slave.butt > 1) {
				slave.butt -= 1;
			} else if (slave.weight > 100 && slave.butt < 6) {
				slave.butt += jsRandom(1,2);
			} else if (slave.weight > 10 && slave.butt < 4) {
				slave.butt += 1;
			}
		}
		if (slave.attrXY > 0) {
			slave.anus = jsEither([0, 1, 2]);
		} else {
			if (slave.physicalAge <= 13) {
				slave.anus = jsEither([0, 0, 0, 0, 0, 1]);
			} else if (slave.physicalAge <= 15) {
				slave.anus = jsEither([0, 0, 0, 0, 1, 1, 1]);
			} else if (slave.physicalAge <= 17) {
				slave.anus = jsEither([0, 0, 0, 1, 1, 1]);
			} else {
				slave.anus = jsEither([0, 0, 1, 1, 2]);
			}
		}
		slave.analArea = slave.anus + jsEither([0, 0, 0, 1]);
	};

	function generateXXBoobs() {
		if (slave.physicalAge <= 10) {
			slave.boobs = 100;
		} else if (slave.physicalAge == 11) {
			slave.boobs = jsEither([100, 100, 150, 150, 150, 300]);
		} else if (slave.physicalAge == 12) {
			slave.boobs = jsEither([100, 100, 150, 150, 150, 200, 200, 300]);
		} else if (slave.physicalAge == 13) {
			slave.boobs = jsEither([100, 150, 200, 200, 300, 300, 300, 400]);
		} else if (slave.physicalAge == 14) {
			slave.boobs = jsEither([100, 150, 200, 300, 300, 300, 350, 400, 400]);
		} else if (slave.physicalAge == 15) {
			slave.boobs = jsEither([150, 200, 300, 300, 300, 350, 350, 350, 400, 400, 450, 450]);
		} else {
			switch (slave.race) {
				case "black":
					slave.boobs = BoobGenerator.rollBreast(150);
					break;
				case "white":
					slave.boobs = BoobGenerator.rollBreast(50);
					break;
				case "asian":
					slave.boobs = BoobGenerator.rollBreast(-100);
					break;
				default:
					slave.boobs = BoobGenerator.rollBreast(0);
			}
		}
	};

	function generateXYBoobs() {
		slave.boobs = jsEither([100, 200, 200, 300, 300, 400, 500]);
	};

	function generateXXFace() {
		slave.face = jsRandom(-60,60);
		if (jsRandom(0,2) == 0) {
			slave.face = jsRandom(-10,10);
		}
		if (slave.physicalAge > 40) {
			slave.face -= jsRandom(0,20);
		} else if (slave.physicalAge > 35) {
			slave.face -= jsRandom(0,10);
		} else if (slave.physicalAge <= 20) {
			slave.face += jsRandom(0,20);
		} else if (slave.physicalAge <= 25) {
			slave.face += jsRandom(0,10);
		}
		if (slave.physicalAge > 10) {
			slave.faceShape = jsEither(["androgynous", "cute", "sensual", "exotic", "normal", "normal"]);
		} else {
			slave.faceShape = jsEither(["androgynous", "androgynous", "cute", "cute", "sensual", "exotic", "normal", "normal"]);
		}
		switch (slave.faceShape) {
			case "sensual":
			case "cute":
				slave.face += jsRandom(0,20);
				break;
			case "exotic":
			case "androgynous":
				slave.face += jsRandom(-10,10);
				break;
			case "masculine":
				slave.face += jsRandom(-10,0);
		}
	};

	function generateXYFace() {
		slave.face = jsRandom(-70,20);
		if (jsRandom(0,2) == 0) {
			slave.face = jsRandom(-40,-10);
		}
		if (slave.physicalAge > 40) {
			slave.face -= jsRandom(0,20);
		} else if (slave.physicalAge > 35) {
			slave.face -= jsRandom(0,10);
		} else if (slave.physicalAge <= 20) {
			slave.face += jsRandom(0,20);
		} else if (slave.physicalAge <= 25) {
			slave.face += jsRandom(0,10);
		}
		if (slave.physicalAge >= 17) {
			slave.faceShape = jsEither(["masculine", "masculine", "masculine", "androgynous"]);
		} else if (slave.physicalAge >= 15) {
			slave.faceShape = jsEither(["masculine", "masculine", "masculine", "masculine", "masculine", "masculine", "masculine", "masculine", "androgynous", "sensual", "exotic", "normal"]);
		} else if (slave.physicalAge >= 13) {
			slave.faceShape = jsEither(["masculine", "masculine", "masculine", "androgynous", "cute", "sensual", "exotic", "normal"]);
		} else if (slave.physicalAge >= 11) {
			slave.faceShape = jsEither(["masculine", "androgynous", "cute", "sensual", "exotic", "normal", "normal"]);
		} else {
			slave.faceShape = jsEither(["androgynous", "androgynous", "cute", "cute", "sensual", "exotic", "normal", "normal"]);
		}
		switch (slave.faceShape) {
			case "sensual":
			case "cute":
				slave.face += jsRandom(0,20);
				break;
			case "exotic":
			case "androgynous":
				slave.face += jsRandom(-10,10);
				break;
			case "masculine":
				slave.face += jsRandom(-10,0);
		}
	};

	function generateXXPregAdaptation() {
		if (slave.physicalAge <= 6) {
			slave.pregAdaptation = 5;
		} else if (slave.physicalAge <= 11) {
			slave.pregAdaptation = slave.physicalAge - 1;
		} else if (slave.physicalAge <= 14) {
			slave.pregAdaptation = 4*(slave.physicalAge - 12) + 14;
		} else if (slave.physicalAge <= 15) {
			slave.pregAdaptation = 28;
		} else if (slave.physicalAge <= 16) {
			slave.pregAdaptation = 34;
		} else if (slave.physicalAge <= 17) {
			slave.pregAdaptation = 42;
		} else {
			slave.pregAdaptation = 50;
		}
	};

	function generateXYPregAdaptation() {
		if (slave.physicalAge <= 6) {
			slave.pregAdaptation = 5;
		} else if (slave.physicalAge <= 11) {
			slave.pregAdaptation = slave.physicalAge - 1;
		} else if (slave.physicalAge <= 15) {
			slave.pregAdaptation = 2*(slave.physicalAge - 12) + 12;
		} else {
			slave.pregAdaptation = 20;
		}
	};

	function generateXXVoice() {
		if (slave.physicalAge <= 13) {
			slave.voice = jsEither([2, 2, 2, 3, 3, 3, 3, 3, 3]);
		} else if (slave.physicalAge <= 16) {
			slave.voice = jsEither([2, 2, 2, 2, 2, 3, 3, 3, 3]);
		} else {
			slave.voice = jsEither([1, 2, 2, 2, 2, 2, 2, 3, 3, 3]);
		}
	};

	function generateXYVoice() {
		if (slave.physicalAge <= 11) {
			slave.voice = jsEither([2, 2, 2, 3, 3, 3, 3, 3, 3]);
		} else if (slave.physicalAge <= 13) {
			slave.voice = jsEither([1, 1, 2, 2, 2, 2, 2, 3, 3]);
		} else if (slave.physicalAge <= 16) {
			slave.voice = jsEither([1, 1, 1, 2, 2, 2, 2, 2, 3]);
		} else {
			if (slave.balls > 2) {
				slave.voice = 1;
			} else if (slave.balls > 0) {
				slave.voice = jsEither([1, 1, 2]);
			} else {
				slave.voice = jsEither([1, 2, 2]);
			}
		}
	};

	function generateXXTeeth() {
		var femaleCrookedTeethGen = slave.intelligence+slave.intelligenceImplant;
		if ("American" == slave.nationality) {
			femaleCrookedTeethGen += 20;
		} else if (["Andorran", "Antiguan", "Argentinian", "Aruban", "Australian", "Austrian", "Bahamian", "Bahraini", "Barbadian", "Belarusian", "Belgian", "Bermudian", "Brazilian", "British", "Bruneian", "Bulgarian", "Canadian", "Catalan", "Chilean", "a Cook Islander", "Croatian", "Czech", "Cypriot", "Danish", "Dutch", "Emirati", "Estonian", "Finnish", "French", "German", "Greek", "Greenlandic", "Guamanian", "Hungarian", "Icelandic", "Irish", "Israeli", "Italian", "Japanese", "Kazakh", "Korean", "Kuwaiti", "Latvian", "a Liechtensteiner", "Lithuanian", "Luxembourgian", "Malaysian", "Maltese", "Mauritian", "Monégasque", "Montenegrin", "New Caledonian", "a New Zealander", "Niuean", "Norwegian", "Omani", "Palauan", "Panamanian", "Polish", "Portuguese", "Puerto Rican", "Qatari", "Romanian", "Russian", "Sammarinese", "Saudi", "Seychellois", "Singaporean", "Slovak", "Slovene", "Spanish", "Swedish", "Swiss", "Taiwanese", "Trinidadian", "Uruguayan", "Vatican"].includes(slave.nationality)) {
			/* do nothing */
		} else {
			femaleCrookedTeethGen -= 20;
		}

		if (jsRandom(0, femaleCrookedTeethGen) <= 15 && slave.physicalAge >= 12) {
			slave.teeth = "crooked";
		}

		if (slave.physicalAge < 6) {
			slave.teeth = "baby";
		} else if (slave.physicalAge < 12) {
			slave.teeth = "mixed";
		}
	};

	function generateXYTeeth() {
		var maleCrookedTeethGen = slave.intelligence+slave.intelligenceImplant;
		if ("American" == slave.nationality) {
			maleCrookedTeethGen += 22;
		} else if (["Andorran", "Antiguan", "Argentinian", "Aruban", "Australian", "Austrian", "Bahamian", "Bahraini", "Barbadian", "Belarusian", "Belgian", "Bermudian", "Brazilian", "British", "Bruneian", "Bulgarian", "Canadian", "Catalan", "Chilean", "a Cook Islander", "Croatian", "Czech", "Cypriot", "Danish", "Dutch", "Emirati", "Estonian", "Finnish", "French", "German", "Greek", "Greenlandic", "Guamanian", "Hungarian", "Icelandic", "Irish", "Israeli", "Italian", "Japanese", "Kazakh", "Korean", "Kuwaiti", "Latvian", "a Liechtensteiner", "Lithuanian", "Luxembourgian", "Malaysian", "Maltese", "Mauritian", "Monégasque", "Montenegrin", "New Caledonian", "a New Zealander", "Niuean", "Norwegian", "Omani", "Palauan", "Panamanian", "Polish", "Portuguese", "Puerto Rican", "Qatari", "Romanian", "Russian", "Sammarinese", "Saudi", "Seychellois", "Singaporean", "Slovak", "Slovene", "Spanish", "Swedish", "Swiss", "Taiwanese", "Trinidadian", "Uruguayan", "Vatican"].includes(slave.nationality)) {
			/* do nothing */
		} else {
			maleCrookedTeethGen -= 20;
		}

		if (jsRandom(0, maleCrookedTeethGen) <= 15 && slave.physicalAge >= 12) {
			slave.teeth = "crooked";
		}

		if (slave.physicalAge < 6) {
			slave.teeth = "baby";
		} else if (slave.physicalAge < 12) {
			slave.teeth = "mixed";
		}
	};

	function generateXXMods() {
		if (passage() != "Starting Girls") {
			slave.earPiercing = jsEither([0,1]);
			slave.nosePiercing = jsEither([0,0,0,1]);
			slave.eyebrowPiercing = jsEither([0,0,0,0,0,1]);
			slave.clitPiercing = jsEither([0,0,0,0,0,1]);
			slave.lipsPiercing = jsEither([0,0,0,0,0,1]);
			slave.navelPiercing = jsEither([0,0,0,1]);
			slave.nipplesPiercing = jsEither([0,0,0,0,1]);
		}
		if (slave.anus != 0 && Math.random() < 0.25) {
			slave.anusTat = "bleached";
		}
	};

	function generateXYMods() {
		if (passage() != "Starting Girls") {
			slave.earPiercing = jsEither([0,0,0,1]);
			slave.nosePiercing = jsEither([0,0,0,0,1]);
			slave.eyebrowPiercing = jsEither([0,0,0,0,0,1]);
			slave.clitPiercing = jsEither([0,0,0,0,0,1]);
			slave.lipsPiercing = jsEither([0,0,0,0,0,1]);
			slave.navelPiercing = jsEither([0,0,0,0,1]);
			slave.nipplesPiercing = jsEither([0,0,0,0,1]);
		}
		if (slave.anus != 0 && Math.random() < 0.25) {
			slave.anusTat = "bleached";
		}
	};

	function generateXXBodyHair() {
		slave.pubicHColor = slave.hColor;
		slave.underArmHColor = slave.hColor;
		slave.pubicHStyle = jsEither(["hairless", "bald", "bald", "waxed", "waxed", "waxed", "waxed", "waxed", "waxed", "in a strip", "in a strip", "in a strip", "in a strip", "in a strip", "neat", "neat", "neat", "neat", "neat", "bushy in the front and neat in the rear", "bushy in the front and neat in the rear", "bushy in the front and neat in the rear", "bushy in the front and neat in the rear", "bushy", "bushy", "bushy", "bushy", "bushy", "very bushy", "very bushy"]);
		slave.underArmHStyle = jsEither(["hairless", "bald", "bald", "waxed", "waxed", "waxed", "waxed", "shaved", "shaved", "shaved", "shaved", "shaved", "neat", "neat", "neat", "neat", "neat", "bushy", "bushy", "bushy"]);
		if ((slave.pubicHStyle == "hairless" || slave.underArmHStyle == "hairless") && Math.random() > 0.4) {
			slave.pubicHStyle = "hairless";
			slave.underArmHStyle = "hairless";
		}
		if (slave.hColor == "blonde" && Math.random() > 0.85) {
			slave.eyebrowHColor = jsEither(["brown", "brown", "brown", "brown", "black"]);
			slave.override_Brow_H_Color = 1;
		} else {
			slave.eyebrowHColor = slave.hColor;
		}
		slave.eyebrowHStyle = jsEither(["bald", "shaved", "shaved", "straight", "straight", "straight", "straight", "straight", "straight", "rounded", "rounded", "rounded", "rounded", "rounded", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "slanted inwards", "slanted inwards", "slanted outwards", "slanted outwards", "high-arched", "high-arched", "high-arched", "elongated", "elongated", "elongated", "shortened", "shortened", "shortened", "curved", "curved", "curved", "curved", "curved", "curved", "curved"]);
		slave.eyebrowFullness = jsEither(["pencil-thin", "pencil-thin", "thin", "thin", "thin", "threaded", "threaded", "threaded", "threaded", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "tapered", "tapered", "tapered", "tapered", "thick", "thick", "thick", "bushy", "bushy"]);
	};

	function generateXYBodyHair() {
		slave.pubicHColor = slave.hColor;
		slave.underArmHColor = slave.hColor;
		slave.pubicHStyle = jsEither(["hairless", "bald", "bald", "waxed", "waxed", "waxed", "waxed", "waxed", "waxed", "in a strip", "in a strip", "in a strip", "neat", "neat", "neat", "neat", "neat", "neat", "bushy in the front and neat in the rear", "bushy in the front and neat in the rear", "bushy in the front and neat in the rear", "bushy", "bushy", "bushy", "bushy", "bushy", "bushy", "very bushy", "very bushy"]);
		slave.underArmHStyle = jsEither(["hairless", "bald", "bald", "waxed", "waxed", "waxed", "waxed", "shaved", "shaved", "shaved", "shaved", "shaved", "neat", "neat", "neat", "neat", "neat", "neat", "neat", "bushy", "bushy", "bushy", "bushy", "bushy"]);
		if ((slave.pubicHStyle == "hairless" || slave.underArmHStyle == "hairless") && Math.random() > 0.4) {
			slave.pubicHStyle = "hairless";
			slave.underArmHStyle = "hairless";
		}
		if (slave.hColor == "blonde" && Math.random() > 0.85) {
			slave.eyebrowHColor = jsEither(["brown", "brown", "brown", "brown", "black"]);
			slave.override_Brow_H_Color = 1;
		} else {
			slave.eyebrowHColor = slave.hColor;
		}
		slave.eyebrowHStyle = jsEither(["bald", "shaved", "shaved", "straight", "straight", "straight", "straight", "straight", "straight", "rounded", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "slanted inwards", "slanted outwards", "high-arched", "elongated", "shortened", "curved", "curved", "curved", "curved", "curved"]);
		slave.eyebrowFullness = jsEither(["pencil-thin", "thin", "thin", "threaded", "threaded", "threaded", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "natural", "tapered", "tapered", "tapered", "thick", "thick", "bushy"]);
	};

	function generateXXPuberty() {
		if (slave.physicalAge >= slave.pubertyAgeXX) {
			slave.pubertyXX = 1;
		} else {
			slave.pubertyXX = 0;
		}
	}

	function generateXYPuberty() {
		if (slave.balls > 0) {
			if (slave.physicalAge >= slave.pubertyAgeXY) {
				slave.pubertyXY = 1;
			} else {
				slave.pubertyXY = 0;
			}
		} else {
			slave.pubertyXY = 0;
		}
	};

	function generateAge() {
		if (V.activeSlaveOneTimeMaxAge > 998) {
			V.activeSlaveOneTimeMaxAge = (V.pedo_mode === 1) ? 18 : 42;
		} else if (V.pedo_mode === 1 && V.one_time_age_overrides_pedo_mode === 0 && V.activeSlaveOneTimeMaxAge > 18) {
			V.activeSlaveOneTimeMaxAge = 18;
		}
		if (V.retirementAge < V.activeSlaveOneTimeMaxAge) {
			V.activeSlaveOneTimeMaxAge = (V.retirementAge - 1);
		}
		if (V.activeSlaveOneTimeMinAge < V.minimumSlaveAge) {
			V.activeSlaveOneTimeMinAge = V.minimumSlaveAge;
		} else if (V.pedo_mode === 1 && V.one_time_age_overrides_pedo_mode === 0) {
			V.activeSlaveOneTimeMinAge = V.minimumSlaveAge;
		}
		if (V.activeSlaveOneTimeMaxAge < V.activeSlaveOneTimeMinAge) {
			V.activeSlaveOneTimeMaxAge = V.activeSlaveOneTimeMinAge;
		}
		if (V.activeSlaveOneTimeMaxAge >= 30 && V.arcologies[0].FSMaturityPreferentialist !== "unset" && V.ageAdjustOverride !== 1) {
			V.activeSlaveOneTimeMaxAge += 10;
		}
		slave.actualAge = jsRandom(V.activeSlaveOneTimeMinAge, V.activeSlaveOneTimeMaxAge);
		const secondAgeRoll = jsRandom(V.activeSlaveOneTimeMinAge, V.activeSlaveOneTimeMaxAge);
		if (V.arcologies[0].FSYouthPreferentialist !== "unset" && V.arcologies[0].FSYouthPreferentialist >= jsRandom(1, 100)) {
			slave.actualAge = Math.min(slave.actualAge, secondAgeRoll);
		} else if (V.arcologies[0].FSMaturityPreferentialist !== "unset" && V.arcologies[0].FSMaturityPreferentialist >= jsRandom(1, 100)) {
			slave.actualAge = Math.max(slave.actualAge, secondAgeRoll);
		}
		if (slave.actualAge >= V.retirementAge) {
			slave.actualAge = (V.retirementAge - 2);
		}
		V.activeSlaveOneTimeMinAge = 0;
		V.activeSlaveOneTimeMaxAge = 999;
		V.one_time_age_overrides_pedo_mode = 0;
		V.ageAdjustOverride = 0;
		slave.visualAge = slave.actualAge;
		slave.physicalAge = slave.actualAge;
		slave.ovaryAge = slave.actualAge;
		slave.age = slave.actualAge; /*compatibility*/
		slave.pubertyAgeXX = V.fertilityAge;
		slave.pubertyAgeXY = V.potencyAge;
	};

	function generateIntelligence() {
		const gaussian = gaussianPair();
		slave.intelligence = Intelligence.random();
		if (V.AgePenalty == 1 && slave.actualAge <= 24) {
			if (gaussian[0] < gaussian[1] + slave.intelligence/29 + (slave.actualAge - 24)/8 - 0.35) {
				slave.intelligenceImplant = 15;
				if (slave.intelligenceImplant > 0 && jsRandom(15,150) < slave.intelligence) {
					slave.intelligenceImplant = 30;
				}
			}
		} else {
			if (gaussian[0] < gaussian[1] + slave.intelligence/29 - 0.35) { /* 40.23% chance if intelligence is 0, 99.26% chance if intelligence is 100 */
				slave.intelligenceImplant = 15;
				if (slave.intelligenceImplant > 0 && jsRandom(15,150) < slave.intelligence) {
					slave.intelligenceImplant = 30;
				}
			}
		}
	};

	function generateCareer() {
		if (V.AgePenalty == 1) {
			if (slave.actualAge < 16) {
				slave.career = setup.veryYoungCareers.random();
			} else if (slave.actualAge <= 24) {
				slave.career = setup.youngCareers.random();
			} else if (slave.intelligenceImplant >= 15) {
				slave.career = setup.educatedCareers.random();
			} else {
				slave.career = setup.uneducatedCareers.random();
			}
		} else {
			if (slave.actualAge < 16) {
				slave.career = setup.veryYoungCareers.random();
			} else if (slave.intelligenceImplant >= 15) {
				slave.career = setup.educatedCareers.random();
			} else if (slave.actualAge <= 24) {
				slave.career = setup.youngCareers.random();
			} else {
				slave.career = setup.uneducatedCareers.random();
			}
		}
	};

	function generateNationality() {
		if (V.fixedRace == 0) {
			if (V.fixedNationality == 0) {
				slave.nationality = hashChoice(V.nationalities);
			} else {
				slave.nationality = V.fixedNationality;
				V.fixedNationality = 0;
			}
			nationalityToRace(slave);

			if (passage() == "Corporate Market" && (V.captureUpgradeRace != "unselected") && (V.captureUpgradeRace != "none")) {
				if (V.captureUpgradeRace == "mixed race") {
					slave.race = "mixed race";
				} else {
					const capUpgradeCheck = setup[V.captureUpgradeRace.toLowerCase().replace(/[ -]/g, '')+'Nationalities'];
					const keys = Object.keys(capUpgradeCheck);
					var capUpgradeNationalities = {};
					for (var i = 0; i < keys.length; i++) {
						if (keys[i] in V.nationalities) {
							capUpgradeNationalities[keys[i]] = capUpgradeCheck[keys[i]];
						}
					}
					if (Object.keys(capUpgradeNationalities).length > 0) {
						slave.nationality = hashChoice(capUpgradeNationalities);
					} else {
						slave.nationality = hashChoice(capUpgradeCheck);
					}
					slave.race = V.captureUpgradeRace;
				}
			}

		} else {
			switch (V.fixedRace) {
				case "white":
					slave.nationality = hashChoice(setup.whiteNationalities);
					break;
				case "asian":
					slave.nationality = hashChoice(setup.asianNationalities);
					break;
				case "latina":
					slave.nationality = hashChoice(setup.latinaNationalities);
					break;
				case "black":
					slave.nationality = hashChoice(setup.blackNationalities);
					break;
				case "pacific islander":
					slave.nationality = hashChoice(setup.pacificislanderNationalities);
					break;
				case "southern european":
					slave.nationality = hashChoice(setup.southerneuropeanNationalities);
					break;
				case "amerindian":
					slave.nationality = hashChoice(setup.amerindianNationalities);
					break;
				case "semitic":
					slave.nationality = hashChoice(setup.semiticNationalities);
					break;
				case "middle eastern":
					slave.nationality = hashChoice(setup.middleeasternNationalities);
					break;
				case "indo-aryan":
					slave.nationality = hashChoice(setup.indoaryanNationalities);
					break;
				case "malay":
					slave.nationality = hashChoice(setup.malayNationalities);
					break;
				default:
					slave.nationality = hashChoice(V.nationalities);
			}
			slave.race = V.fixedRace;
			V.fixedRace = 0;
		}
	};

	function generateAccent() {
		nationalityToAccent(slave);
		if ((slave.intelligenceImplant >= 15 || slave.intelligence > 95) && slave.accent >= 3 && slave.intelligence > jsRandom(0,100)) {
			slave.accent -= 1;
		}
	};

	function generateRacialTraits() {
		switch (slave.race) {
			case "black":
				slave.lips = jsRandom(5,30);
				slave.skin = jsEither(["black", "dark brown", "brown"]);
				slave.hColor = jsEither(["black", "black", "black", "brown"]);
				slave.hStyle = jsEither(["neat", "crinkled"]);
				break;
			case "white":
				slave.lips = jsRandom(5,25);
				if (slave.nationality == "German") {
					slave.skin = jsEither(["pale", "light", "fair"]);
					slave.eyeColor = jsEither(["blue", "blue", "blue", "brown", "green"]);
					slave.hColor = jsEither(["black", "blonde", "blonde", "blonde", "red", "brown"]);
				} else if (slave.nationality == "Icelandic") {
					slave.skin = jsEither(["pale", "pale", "light", "fair"]);
					slave.eyeColor = jsEither(["blue", "blue", "blue", "brown", "green"]);
					slave.hColor = jsEither(["black", "blonde", "blonde", "blonde", "red", "brown"]);
				} else if (slave.nationality == "Irish") {
					slave.skin = jsEither(["pale", "pale", "light", "fair"]);
					slave.eyeColor = jsEither(["blue", "brown", "green", "green", "green"]);
					slave.hColor = jsEither(["black", "blonde", "red", "red", "red", "brown"]);
				} else if (slave.nationality == "Scottish") {
					slave.skin = jsEither(["pale", "fair", "light", "fair"]);
					slave.eyeColor = jsEither(["blue", "brown", "brown", "green", "green", "green"]);
					slave.hColor = jsEither(["black", "black", "blonde", "red", "red", "red", "brown"]);
				} else {
					slave.skin = jsEither(["pale", "light", "fair"]);
					slave.eyeColor = jsEither(["blue", "brown", "green"]);
					slave.hColor = jsEither(["black", "blonde", "red", "brown"]);
				}
				slave.hStyle = "neat";
				break;
			case "latina":
				slave.lips = jsRandom(5,25);
				slave.skin = jsEither(["light olive", "brown", "dark brown", "tanned", "dark olive"]);
				slave.hColor = jsEither(["black", "brown"]);
				slave.hStyle = "neat";
				break;
			case "indo-aryan":
			case "malay":
			case "pacific islander":
			case "amerindian":
				slave.lips = jsRandom(5,25);
				slave.skin = jsEither(["dark", "light"]);
				slave.hColor = "black";
				slave.hStyle = "neat";
				break;
			case "asian":
				slave.lips = jsRandom(5,25);
				slave.skin = jsEither(["light olive", "dark olive", "light"]);
				slave.hColor = "black";
				slave.hStyle = "neat";
				break;
			case "middle eastern":
			case "semitic":
			case "southern european":
				slave.lips = jsRandom(5,25);
				slave.skin = jsEither(["fair", "light olive", "tanned", "light"]);
				slave.hColor = "black";
				slave.hStyle = "neat";
				break;
			default:
				slave.lips = jsRandom(5,25);
				slave.skin = jsEither(["pale", "dark", "light"]);
				slave.hColor = jsEither(["black", "black", "black", "black", "brown", "brown", "blonde", "red"]);
				slave.hStyle = "neat";
		}
		if (slave.skin == "pale" || slave.skin == "fair") {
			if (jsRandom(1,4) == 1) {
				slave.markings = jsEither(["freckles", "freckles", "freckles", "heavily freckled", "beauty mark", "beauty mark", "birthmark", "birthmark"]);
			}
		} else if (jsRandom(1,8) == 1) {
			slave.markings = jsEither(["beauty mark", "birthmark"]);
		}
	};

	function generateBoobTweaks() {
		if (V.weightAffectsAssets != 0) {
			if (slave.weight < -10 && slave.boobs > 200) {
				slave.boobs -= 100;
			} else if (slave.weight > 190 && slave.boobs < 3000) {
				slave.boobs += (jsRandom(3,8)*100);
			} else if (slave.weight > 160 && slave.boobs < 1500) {
				slave.boobs += (jsRandom(2,6)*100);
			} else if (slave.weight > 130 && slave.boobs < 1500) {
				slave.boobs += (jsRandom(1,4)*100);
			} else if (slave.weight > 95 && slave.boobs < 1200) {
				slave.boobs += (jsRandom(1,3)*100);
			} else if (slave.weight > 30 && slave.boobs < 1000) {
				slave.boobs += 100;
			}
		}

		var BoobShapeGen = [];
		if (slave.boobs > 250 && slave.boobs < 800) {
			BoobShapeGen.push("perky");
			BoobShapeGen.push("downward-facing");
		}
		if (slave.boobs > 400 && slave.boobs < 1200) {
			BoobShapeGen.push("torpedo-shaped");
			BoobShapeGen.push("wide-set");
		}
		if (slave.boobs > 800 && slave.physicalAge > jsRandom(10,50)) {
			BoobShapeGen.push("saggy");
		}
		if (BoobShapeGen.length == 1) {
			if (Math.random() < 0.5) {
				slave.boobShape = jsEither(BoobShapeGen);
			}
		} else if (BoobShapeGen.length > 1) {
			if (jsRandom(1,3) != 1) {
				slave.boobShape = jsEither(BoobShapeGen);
			}
		}

		if (slave.boobs < 250) {
			slave.nipples = jsEither(["tiny", "tiny", "tiny", "tiny", "cute", "cute", "puffy", "partially inverted"]);
		} else if (slave.boobs < 500) {
			slave.nipples = jsEither(["tiny", "cute", "cute", "cute", "puffy", "partially inverted"]);
		} else if (slave.boobs < 1000) {
			slave.nipples = jsEither(["tiny", "cute", "cute", "cute", "puffy", "puffy", "partially inverted", "inverted"]);
		} else {
			slave.nipples = jsEither(["cute", "puffy", "partially inverted", "inverted", "huge"]);
		}
	};

	function generateSkills() {
		slave.vaginalSkill = (slave.vagina == 0 ? 0 : jsRandom(0,15));
		slave.analSkill = (slave.anus == 0 ? 0 : jsRandom(0,15));
		slave.oralSkill = jsRandom(0,15);
		slave.entertainSkill = jsRandom(0,15);
		slave.whoreSkill = jsRandom(0,15);
	};

	function generateDisabilities() {
		if (slave.physicalAge >= jsRandom(0,100)) {
			slave.eyes = -1;
		}
		if (slave.physicalAge >= jsRandom(30,100)) {
			slave.hears = -1;
		}
		if (V.seeExtreme == 1) {
			let disList = [];
			disList.push("hearNot");
			disList.push("seeNot");
			disList.push("speakNot");
			let disableCount = 0;
			if (V.oneTimeDisableDisability === 0) {
				while (disList.length > 0) {
					let rolled = jsEither(disList);
					switch (rolled) {
					case "hearNot":
						if ((jsRandom(1,100)-(disableCount*2)) > 90) {
							slave.hears = -2;
						}
						disList.delete("hearNot");
						disableCount++;
						break;
					case "seeNot":
						if ((jsRandom(1,100)-(disableCount*2)) > 90) {
							slave.eyes = -2;
						}
						disList.delete("seeNot");
						disableCount++;
						break;
					case "speakNot":
						if ((jsRandom(1,100)-(disableCount*2)) > 90) {
							slave.voice = 0;
						}
						disList.delete("speakNot");
						disableCount++;
						break;
					}
				}
			}
			V.oneTimeDisableDisability = 0;
		}
	};

	return GenerateNewSlave;
})();


code:
:: HTAG Macro JS [script]

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

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

		jQuery('<' + htag + ' ' + attributes + ' />')
			.wiki(payload)
			.appendTo(this.output);
	}
});


code:
:: heroCreator.tw [script]

window.getHeroSlave = function(heroSlave, baseHeroSlave) {
	var newSlave = clone(baseHeroSlave);
	for (var attrname in heroSlave) {
		newSlave[attrname] = heroSlave[attrname];
	};
	return newSlave;
}


code:
:: itemAvailability [script]

/* intended to condense the clothing/toy/etc availability checks into something less asinine */
window.isItemAccessible = function(string) {

	const V = State.variables;

	if (State.variables.cheatMode === 1){
		return true;
	} else {
		switch(string) {
		case 'attractive lingerie for a pregnant woman':
			if ((V.arcologies[0].FSRepopulationFocus > 0) || (V.clothesBoughtMaternityLingerie === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a bunny outfit':
			if ((V.arcologies[0].FSGenderFundamentalist > 0) || (V.clothesBoughtBunny === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'body oil':
			if ((V.arcologies[0].FSPhysicalIdealist > 0) || (V.clothesBoughtOil === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'chains':
			if ((V.arcologies[0].FSDegradationist > 0) || (V.clothesBoughtChains === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a chattel habit':
			if ((V.arcologies[0].FSChattelReligionist > 0) || (V.clothesBoughtHabit === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'conservative clothing':
			if ((V.arcologies[0].FSPaternalist > 0) || (V.clothesBoughtConservative === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'harem gauze':
			if ((V.arcologies[0].FSArabianRevivalist > 0) || (V.clothesBoughtHarem === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a huipil':
			if ((V.arcologies[0].FSAztecRevivalist > 0) || (V.clothesBoughtHuipil === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a kimono':
			if ((V.arcologies[0].FSEdoRevivalist > 0) || (V.clothesBoughtKimono === 1) || (V.continent === 'Japan')) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a maternity dress':
			if ((V.arcologies[0].FSRepopulationFocus > 0) || (V.clothesBoughtMaternityDress === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a slutty qipao':
			if ((V.arcologies[0].FSChineseRevivalist > 0) || (V.clothesBoughtQipao === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a long qipao':
			if ((V.arcologies[0].FSChineseRevivalist > 0) || (V.clothesBoughtCultural === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'stretch pants and a crop-top':
			if ((V.arcologies[0].FSHedonisticDecadence > 0) || (V.clothesBoughtLazyClothes === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a toga':
			if ((V.arcologies[0].FSRomanRevivalist > 0) || (V.clothesBoughtToga === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'Western clothing':
			if ((V.arcologies[0].FSPastoralist > 0) || (V.clothesBoughtWestern === 1)) {
				return true;
			} else {
				return false;
			}
			break;

		case 'battlearmor':
		case 'a military uniform':
		case 'a red army uniform':
		case 'battledress':
			if (V.clothesBoughtMilitary === 1) {
				return true;
			} else {
				return false;
			}
			break;

		case 'a biyelgee costume':
		case 'a dirndl':
		case 'lederhosen':
		case 'a mounty outfit':
			if (V.clothesBoughtCultural === 1) {
				return true;
			} else {
				return false;
			}
			break;

		case 'a burqa':
		case 'a burkini':
		case 'a blouse and hijab':
		case 'a niqab and abaya':
			if (V.clothesBoughtMiddleEastern === 1 || V.continent === 'the Middle East') {
				return true;
			} else {
				return false;
			}
			break;

		case 'a klan robe':
		case 'a schutzstaffel uniform':
		case 'a slutty schutzstaffel uniform':
			if (V.clothesBoughtPol === 1) {
				return true;
			} else {
				return false;
			}
			break;

		case 'shimapan panties':
			if (V.clothesBoughtPantsu === 1 || V.continent === 'Japan') {
				return true;
			} else {
				return false;
			}
			break;

		case 'bowtie':
			if ((V.arcologies[0].FSGenderFundamentalist > 0) || (V.clothesBoughtBunny === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'ancient Egyptian':
			if ((V.arcologies[0].FSEgyptianRevivalist > 0) || (V.clothesBoughtEgypt === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'massive dildo gag':
			if (V.toysBoughtGags === 1) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a small empathy belly': case 'a medium empathy belly': case 'a large empathy belly': case 'a huge empathy belly':
			if ((V.arcologies[0].FSRepopulationFocus > 0) || (V.clothesBoughtBelly === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'long dildo': case 'long, large dildo': case 'long, huge dildo':
			if (V.toysBoughtDildos === 1) {
				return true;
			} else {
				return false;
			}
			break;
		case 'long plug': case 'long, large plug': case 'long, huge plug':
			if (V.toysBoughtButtPlugs === 1) {
				return true;
			} else {
				return false;
			}
			break;
		case 'tail': case 'cat tail': case 'fox tail':
			if (V.toysBoughtButtPlugTails === 1) {
				return true;
			} else {
				return false;
			}
			break;
		default:
			return true;
			break;
		}
	}
};


code:
:: pregJS [script]

/* Major props to the anons who worked together to forge the Super Pregnancy Project. Let your legacy go unforgotten.*/
window.getPregBellySize = function(s) {
	var gestastionWeek = s.preg;
	var fetuses = s.pregType;
	var phi = 1.618;

	if(gestastionWeek <= 32) {
		var targetLen = ((0.00006396 * Math.pow(gestastionWeek, 4)) - (0.005501 * Math.pow(gestastionWeek, 3)) + (0.161 * Math.pow(gestastionWeek, 2)) - (0.76 * gestastionWeek) + 0.208);
	} else if(gestastionWeek <= 106) {
		var targetLen = ((-0.0000004675 * Math.pow(gestastionWeek, 4)) + (0.0001905 * Math.pow(gestastionWeek, 3)) - (0.029 * Math.pow(gestastionWeek, 2)) + (2.132 * gestastionWeek) - 16.575);
	} else {
		var targetLen = ((-0.00003266 * Math.pow(gestastionWeek,2)) + (0.076 * gestastionWeek) + 43.843);
	}

	var bellySize = ((4 / 3) * (Math.PI) * (phi / 2) * (Math.pow((targetLen / 2), 3)) * fetuses);
	return bellySize;
};

window.bellyAdjective = function(slave) {
	slave = slave || State.variables.activeSlave;
	if(slave.belly >= 1500) {
		if(slave.belly >= 1000000) {
			if(slave.preg > 10) {
				return 'unfathomably distended, brimming with life';
			} else {
				return 'unfathomable';
			}
		} else if(slave.belly >= 750000) {
			if(slave.preg > 10) {
				return 'monolithic bulging';
			} else {
				return 'monolithic';
			}
		} else if(slave.belly >= 600000) {
			if(slave.preg > 10) {
				return 'titanic bulging';
			} else {
				return 'titanic';
			}
		} else if(slave.belly >= 450000) {
			if(slave.preg > 10) {
				return 'gigantic bulgy';
			} else {
				return 'gigantic';
			}
		} else if(slave.belly >= 300000) {
			return 'massive';
		} else if(slave.belly >= 100000) {
			return 'giant';
		} else if(slave.belly >= 15000) {
			return 'huge';
		} else if(slave.belly >= 10000) {
			return 'big';
		} else {
			return 'swollen';
		}
	} else {
		return '';
	}
}

/* calculates and returns expected ovum count during conception*/
window.setPregType = function(actor) {
	/* IMHO rework is posssible. Can be more interesting to play, if this code will take in account more body conditions - age, fat, food, hormone levels, etc. */

	var ovum = 1;
	var fertilityStack = 0;	// adds an increasing bonus roll for stacked fertility drugs

	if(actor.broodmother < 1) { // Broodmothers should be not processed here. Necessary now.
		if(typeof actor.readyOva == "number" && actor.readyOva != 0) {
			ovum = actor.readyOva; //just single override; for delayed impregnation cases
		} else if(actor.ID == -1) {
			if(actor.birthMaster > 0) { // Predisposed to twins
				if(actor.fertDrugs == 1) {
					ovum += jsEither([1, 1, 2, 2, 2, 2, 3, 3]);
				} else {
					ovum += jsEither([0, 0, 0, 1, 1, 1, 1, 1, 1, 2]);
				}
				if(actor.forcedFertDrugs > 0) {
					ovum += jsEither([1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4]);
				}
			} else {
				if(actor.fertDrugs == 1) {
					ovum += jsEither([0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3]);
				} else {
					ovum += jsEither([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
				}
				if(actor.forcedFertDrugs > 0) {
					ovum += jsEither([0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 4]);
				}
			}
		} else if(actor.pregType == 0) {
			ovum += jsEither([0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);	//base chance for twins
			if(actor.hormones == 2) {
				ovum += jsEither([0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2]);
				fertilityStack++;
			}
			if(actor.hormoneBalance >= 200) {
				ovum += jsEither([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2]);
				fertilityStack++;
			}
			if(actor.diet == "fertility") {
				ovum += jsEither([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]);
				fertilityStack++;
			}
			if(State.variables.masterSuitePregnancyFertilitySupplements == 1 && ((actor.assignment == "serve in the master suite" || actor.assignment == "be your Concubine"))) {
				ovum += jsEither([0, 0, 0, 1, 1, 2, 2, 2, 3, 3]);
				fertilityStack++;
				fertilityStack++;
			}
			if(State.variables.reproductionFormula == 1 && (State.variables.week-actor.weekAcquired > 0)) {
				fertilityStack++;
			}
			if(actor.drugs == "super fertility drugs") {
				ovum += jsEither([1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5]);
				fertilityStack++;
				fertilityStack++;
				fertilityStack++;
				fertilityStack++;
				fertilityStack++;
			} else if(actor.drugs == "fertility drugs") {
				ovum += jsEither([0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 3, 3]);
				fertilityStack++;
			}
			if(State.variables.seeHyperPreg == 1) {
				if(actor.drugs == "super fertility drugs") {
					ovum += jsRandom(0, fertilityStack*2);
				} else {
					ovum += jsRandom(0, fertilityStack);
				}
			} else {
				ovum += jsRandom(0, fertilityStack);
				if(ovum > 12) {
					ovum = jsRandom(6, 12);
				}
			}
		}
	}
	return ovum;
}

/*
 Penetrative ability, ability to become pregnant, and canBreed() must be checked outside of this. Designed to assume .eggType == "human".
 target is the slave to get pregnant. Also accepts the PC.
 chance is the % chance to conceive.
 hole control's the hole involved (0 - vagina, 1 - ass, 2 - both). .mpreg did this.
 fatherID is the ID of her sire or 0 if undefined.
 displayOverride is an override if defined - fatherID must be defined in this case.
*/
window.knockMeUp = function(target, chance, hole, fatherID, displayOverride) {
	const V = State.variables;
	let r = "";
	if (V.seePreg !== 0) {
		if (jsRandom(0,99) < (chance + (V.reproductionFormula*((target.pregSource <= 0) ? ((target.ID == -1) ? 0 : 10) : 20)))) {
			if (target.mpreg === hole) {

				target.preg = 1;
				target.pregSource = (!fatherID ? 0 : fatherID);

				if (target.ID !== -1) {
					target.pregWeek = 1;
				}

				target.pregType = setPregType(target);
				WombImpregnate(target, target.pregType, target.pregSource, 1);

				if (V.menstruation === 1) {}
				else if (!displayOverride) {
					target.pregKnown = 1;
					if (target.ID === -1) {
						/* r += "@@.lime;You have gotten pregnant.@@"; */
					} else if (target.fuckdoll === 0) {
						r += "@@.lime;She has become pregnant.@@";
					} else {
						r += "@@.lime;It has become pregnant.@@";
					}
				} else {
					target.pregKnown = 1;
				}

			} else if (hole === 2) {

				target.preg = 1;
				target.pregSource = (!fatherID ? 0 : fatherID);

				if (target.ID !== -1) {
					target.pregWeek = 1;
				}

				target.pregType = setPregType(target);
				WombImpregnate(target, target.pregType, target.pregSource, 1);

				if (V.menstruation === 1) {}
				else if (!displayOverride) {
					target.pregKnown = 1;
					if (target.ID === -1) {
						/* r += "@@.lime;You have gotten pregnant.@@"; */
					} else if (target.fuckdoll === 0) {
						r += "@@.lime;She has become pregnant.@@";
					} else {
						r += "@@.lime;It has become pregnant.@@";
					}
				} else {
					target.pregKnown = 1;
				}
			}
		}
	}
	return r;
}

window.getIncubatorReserved = function(slaves) {
	var count = 0;
	slaves.forEach(function(s){
		if (s.reservedChildren > 0)
			count += s.reservedChildren;
		});
	return count;
}

window.getNurseryReserved = function (slaves) {
	var count = 0;
	slaves.forEach(function (s) {
		if (s.reservedChildrenNursery > 0)
			count += s.reservedChildrenNursery;
		});
	return count;
}

/* not to be used until that last part is defined. It may become slave.boobWomb.volume or some shit */
window.getBaseBoobs = function(slave) {
	return slave.boobs-slave.boobsImplant-slave.boobsWombVolume;
}


code:
:: Quick List [script]

window.sortDomObjects = function (objects, attrName, reverse = 0) {
	reverse = (reverse) ? -1 : 1;
	function sortingByAttr (a, b) {
		var aVal = a.getAttribute(attrName);
		var bVal = b.getAttribute(attrName);
		var aInt = parseInt(aVal);
		if (!isNaN(aInt))
			return ((parseInt(bVal) - aInt) * reverse);
		else if (bVal > aVal)
			return -1 * reverse;
		return ((aVal == bVal) ? 0 : 1) * reverse;
	}
	return objects.toArray().sort(sortingByAttr);
};

window.sortButtonsByDevotion = function () {
	var $sortedButtons = $('#qlWrapper button').remove();
	$sortedButtons = sortDomObjects($sortedButtons, 'data-devotion');
	$($sortedButtons).appendTo($('#qlWrapper'));
	quickListBuildLinks();
};

window.sortButtonsByTrust = function () {
	var $sortedButtons = $('#qlWrapper button').remove();
	$sortedButtons = sortDomObjects($sortedButtons, 'data-trust');
	$($sortedButtons).appendTo($('#qlWrapper'));
	quickListBuildLinks();
};

window.quickListBuildLinks = function () {
	$("[data-scroll-to]").click(function() {
		var $this = $(this), $toElement = $this.attr('data-scroll-to');
		// note the * 1 enforces $offset to be an integer, without
		// it we scroll to True, which goes nowhere fast.
				var $offset = $this.attr('data-scroll-offset') * 1 || 0;
				var $speed = $this.attr('data-scroll-speed') * 1 || 500;
		// Use javascript scrollTop animation for in page navigation.
		$('html, body').animate({
			scrollTop: $($toElement).offset().top + $offset
		}, $speed);
	});
};

window.sortIncubatorPossiblesByName = function () {
	var $sortedIncubatorPossibles = $('#qlIncubator div.possible').detach();
	$sortedIncubatorPossibles = sortDomObjects($sortedIncubatorPossibles, 'data-name');
	$($sortedIncubatorPossibles).appendTo($('#qlIncubator'));
};

window.sortIncubatorPossiblesByPregnancyWeek = function () {
	var $sortedIncubatorPossibles = $('#qlIncubator div.possible').detach();
	$sortedIncubatorPossibles = sortDomObjects($sortedIncubatorPossibles, 'data-preg-week');
	$($sortedIncubatorPossibles).appendTo($('#qlIncubator'));
};

window.sortIncubatorPossiblesByPregnancyCount = function () {
	var $sortedIncubatorPossibles = $('#qlIncubator div.possible').detach();
	$sortedIncubatorPossibles = sortDomObjects($sortedIncubatorPossibles, 'data-preg-count');
	$($sortedIncubatorPossibles).appendTo($('#qlIncubator'));
};

window.sortIncubatorPossiblesByReservedSpots = function () {
	var $sortedIncubatorPossibles = $('#qlIncubator div.possible').detach();
	$sortedIncubatorPossibles = sortDomObjects($sortedIncubatorPossibles, 'data-reserved-spots');
	$($sortedIncubatorPossibles).appendTo($('#qlIncubator'));
};

window.sortIncubatorPossiblesByPreviousSort = function () {
	var sort = State.variables.sortIncubatorList;
	console.log(State.variables);
	console.log('sort', sort);
	if ('unsorted' !== sort) {
		console.log("sort isn't unsorted", sort);
		if ('Name' === sort) {
			console.log("sort is name", sort);
			sortIncubatorPossiblesByName();
		} else if ('Reserved Incubator Spots' === sort) {
			console.log("sort is spots", sort);
			sortIncubatorPossiblesByReservedSpots();
		} else if ('Pregnancy Week' === sort) {
			console.log("sort is week", sort);
			sortIncubatorPossiblesByPregnancyWeek();
		} else if ('Number of Children' === sort) {
			console.log("sort is count", sort);
			sortIncubatorPossiblesByPregnancyCount();
		}
	}
};

window.sortNurseryPossiblesByName = function () {
	var $sortedNurseryPossibles = $('#qlNursery div.possible').detach();
	$sortedNurseryPossibles = sortDomObjects($sortedNurseryPossibles, 'data-name');
	$($sortedNurseryPossibles).appendTo($('#qlNursery'));
};

window.sortNurseryPossiblesByPregnancyWeek = function () {
	var $sortedNurseryPossibles = $('#qlNursery div.possible').detach();
	$sortedNurseryPossibles = sortDomObjects($sortedNurseryPossibles, 'data-preg-week');
	$($sortedNurseryPossibles).appendTo($('#qlNursery'));
};

window.sortNurseryPossiblesByPregnancyCount = function () {
	var $sortedNurseryPossibles = $('#qlNursery div.possible').detach();
	$sortedNurseryPossibles = sortDomObjects($sortedNurseryPossibles, 'data-preg-count');
	$($sortedNurseryPossibles).appendTo($('#qlNursery'));
};

window.sortNurseryPossiblesByReservedSpots = function () {
	var $sortedNurseryPossibles = $('#qlNursery div.possible').detach();
	$sortedNurseryPossibles = sortDomObjects($sortedNurseryPossibles, 'data-reserved-spots');
	$($sortedNurseryPossibles).appendTo($('#qlNursery'));
};

window.sortNurseryPossiblesByPreviousSort = function () {
	var sort = State.variables.sortNurseryList;
	console.log(State.variables);
	console.log('sort', sort);
	if ('unsorted' !== sort) {
		console.log("sort isn't unsorted", sort);
		if ('Name' === sort) {
			console.log("sort is name", sort);
			sortNurseryPossiblesByName();
		} else if ('Reserved Nursery Spots' === sort) {
			console.log("sort is spots", sort);
			sortNurseryPossiblesByReservedSpots();
		} else if ('Pregnancy Week' === sort) {
			console.log("sort is week", sort);
			sortNurseryPossiblesByPregnancyWeek();
		} else if ('Number of Children' === sort) {
			console.log("sort is count", sort);
			sortNurseryPossiblesByPregnancyCount();
		}
	}
};


code:
:: rbuttonJS [script]
/* This is modifed radiobutton macro, for automatic checked state setup*/
/* Usage (be sure to use quotes around parameters):

	<<rbutton "$variable" "value">>
Or:
	<<rbutton "$variable" "value" "HTML_element_ID" "Text to replace with, inside html element with ID from previous parameter. <br> HTML tags allowed.">>

Group of radiobutton will be created based on variable name. Checked state will be setted up if variable contain value matched with second parameter. Full form of macro call can be used to display extended description of selected value.
*/

Macro.add('rbutton', {
	handler() {
		if (this.args.length < 2) {
		const errors = [];
		if (this.args.length < 1) { errors.push('variable name'); }
		if (this.args.length < 2) { errors.push('checked value'); }
		return this.error(`no ${errors.join(' or ')} specified`);
		}

		// Ensure that the variable name argument is a string.
		if (typeof this.args[0] !== 'string') {
		return this.error('variable name argument is not a string');
		}

		const varName = this.args[0].trim();

		// Try to ensure that we receive the variable's name (incl. sigil), not its value.
		if (varName[0] !== '$' && varName[0] !== '_') {
		return this.error(`variable name "${this.args[0]}" is missing its sigil ($ or _)`);
		}

		const initValue = Wikifier.getValue(this.args[0]);
		const varId = Util.slugify(varName);
		const checkValue = this.args[1];
		const el = document.createElement('input');

		var replaceID = "";
		var replaceText = "";
		if (typeof this.args[2] === 'string') {
			replaceID = this.args[2];
		}
		if (typeof this.args[3] === 'string') {
			replaceText = this.args[3];
		}

		/*
		Setup and initialize the group counter.
		*/
		if (!TempState.hasOwnProperty(this.name)) {
		TempState[this.name] = {};
		}

		if (!TempState[this.name].hasOwnProperty(varId)) {
		TempState[this.name][varId] = 0;
		}

		/*
		Setup and append the input element to the output buffer.
		*/
		jQuery(el)
		.attr({
			id : `${this.name}-${varId}-${TempState[this.name][varId]++}`,
			name : `${this.name}-${varId}`,
			type : 'radio',
			tabindex : 0 // for accessiblity
		})
		.addClass(`macro-${this.name}`)
		.on('change', function () {
			if (this.checked) {
			Wikifier.setValue(varName, checkValue);

			if (replaceID.length > 0 && replaceText.length > 0){

				var replaceEl = document.getElementById(replaceID);
				//alert (replaceEl);
				if (replaceEl != null) {
					replaceEl.innerHTML = replaceText;
					}

				}
			}
		})
		.ready (function () {
			//alert ("DOM finished");
			if (el.checked && replaceID.length > 0 && replaceText.length > 0){

				var replaceEl = document.getElementById(replaceID);
				//alert (replaceEl);
				if (replaceEl != null) {
					replaceEl.innerHTML = replaceText;
				}

			}
		})
		.appendTo(this.output);

		/*
		Set the story variable to the checked value and the input element to checked, if requested.
		*/
		if (initValue == checkValue) {
		el.checked = true;
		Wikifier.setValue(varName, checkValue);
		}
	}
	});