code:
:: StoryJS [script]

/*config.history.tracking = false;*/
window.variableAsNumber = function(x, defaultValue, minValue, maxValue) {
	x = Number(x)
	if (x != x) {//NaN
		return defaultValue || 0;//In case the default value was not supplied.
	}
	if (x < minValue) {//Works even if minValue is undefined.
		return minValue;
	}
	if (x > maxValue) {//Works even if maxValue is undefined.
		return maxValue;
	}
	return x;
};
window.isSexuallyPure = function(slave) {
	if (!slave) {
		return null;
	}
	if (slave.vagina < 1 && slave.anus < 1 && !slave.analCount && !slave.vaginalCount && !slave.oralCount && !slave.mammaryCount && !slave.penetrativeCount) {
		return true;
	} else {
		return false;
	}
};
if (typeof interpolate == "undefined") {
	var interpolate = function(x0,y0,x1,y1,x) {
		if(x <= x0) {
			return y0;
		} else if(x >= x1) {
			return y1;
		} else {
			return (x - x0) * ((y1 - y0) / (x1 - x0)) + y0;
		}
	};
	window.interpolate = interpolate;
}
config.history.maxStates = 1;

config.saves.autosave = "autosave";
window.isFullyPotent = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.dick > 0 && slave.balls > 0 && slave.hormoneBalance < 100 && slave.drugs !== 'hormone blockers') {
		return true;
	} else {
		return false;
	}
};

/* mousetrap v1.5.3 craig.is/killing/mice */
(function(C,r,g){function t(a,b,h){a.addEventListener?a.addEventListener(b,h,!1):a.attachEvent("on"+b,h)}function x(a){if("keypress"==a.type){var b=String.fromCharCode(a.which);a.shiftKey||(b=b.toLowerCase());return b}return l[a.which]?l[a.which]:p[a.which]?p[a.which]:String.fromCharCode(a.which).toLowerCase()}function D(a){var b=[];a.shiftKey&&b.push("shift");a.altKey&&b.push("alt");a.ctrlKey&&b.push("ctrl");a.metaKey&&b.push("meta");return b}function u(a){return"shift"==a||"ctrl"==a||"alt"==a||
"meta"==a}function y(a,b){var h,c,e,g=[];h=a;"+"===h?h=["+"]:(h=h.replace(/\+{2}/g,"+plus"),h=h.split("+"));for(e=0;e<h.length;++e)c=h[e],z[c]&&(c=z[c]),b&&"keypress"!=b&&A[c]&&(c=A[c],g.push("shift")),u(c)&&g.push(c);h=c;e=b;if(!e){if(!k){k={};for(var m in l)95<m&&112>m||l.hasOwnProperty(m)&&(k[l[m]]=m)}e=k[h]?"keydown":"keypress"}"keypress"==e&&g.length&&(e="keydown");return{key:c,modifiers:g,action:e}}function B(a,b){return null===a||a===r?!1:a===b?!0:B(a.parentNode,b)}function c(a){function b(a){a=
a||{};var b=!1,n;for(n in q)a[n]?b=!0:q[n]=0;b||(v=!1)}function h(a,b,n,f,c,h){var g,e,l=[],m=n.type;if(!d._callbacks[a])return[];"keyup"==m&&u(a)&&(b=[a]);for(g=0;g<d._callbacks[a].length;++g)if(e=d._callbacks[a][g],(f||!e.seq||q[e.seq]==e.level)&&m==e.action){var k;(k="keypress"==m&&!n.metaKey&&!n.ctrlKey)||(k=e.modifiers,k=b.sort().join(",")===k.sort().join(","));k&&(k=f&&e.seq==f&&e.level==h,(!f&&e.combo==c||k)&&d._callbacks[a].splice(g,1),l.push(e))}return l}function g(a,b,n,f){d.stopCallback(b,
b.target||b.srcElement,n,f)||!1!==a(b,n)||(b.preventDefault?b.preventDefault():b.returnValue=!1,b.stopPropagation?b.stopPropagation():b.cancelBubble=!0)}function e(a){"number"!==typeof a.which&&(a.which=a.keyCode);var b=x(a);b&&("keyup"==a.type&&w===b?w=!1:d.handleKey(b,D(a),a))}function l(a,c,n,f){function e(c){return function(){v=c;++q[a];clearTimeout(k);k=setTimeout(b,1E3)}}function h(c){g(n,c,a);"keyup"!==f&&(w=x(c));setTimeout(b,10)}for(var d=q[a]=0;d<c.length;++d){var p=d+1===c.length?h:e(f||
y(c[d+1]).action);m(c[d],p,f,a,d)}}function m(a,b,c,f,e){d._directMap[a+":"+c]=b;a=a.replace(/\s+/g," ");var g=a.split(" ");1<g.length?l(a,g,b,c):(c=y(a,c),d._callbacks[c.key]=d._callbacks[c.key]||[],h(c.key,c.modifiers,{type:c.action},f,a,e),d._callbacks[c.key][f?"unshift":"push"]({callback:b,modifiers:c.modifiers,action:c.action,seq:f,level:e,combo:a}))}var d=this;a=a||r;if(!(d instanceof c))return new c(a);d.target=a;d._callbacks={};d._directMap={};var q={},k,w=!1,p=!1,v=!1;d._handleKey=function(a,
c,e){var f=h(a,c,e),d;c={};var k=0,l=!1;for(d=0;d<f.length;++d)f[d].seq&&(k=Math.max(k,f[d].level));for(d=0;d<f.length;++d)f[d].seq?f[d].level==k&&(l=!0,c[f[d].seq]=1,g(f[d].callback,e,f[d].combo,f[d].seq)):l||g(f[d].callback,e,f[d].combo);f="keypress"==e.type&&p;e.type!=v||u(a)||f||b(c);p=l&&"keydown"==e.type};d._bindMultiple=function(a,b,c){for(var d=0;d<a.length;++d)m(a[d],b,c)};t(a,"keypress",e);t(a,"keydown",e);t(a,"keyup",e)}var l={8:"backspace",9:"tab",13:"enter",16:"shift",17:"ctrl",18:"alt",
20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"ins",46:"del",91:"meta",93:"meta",224:"meta"},p={106:"*",107:"+",109:"-",110:".",111:"/",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},A={"~":"`","!":"1","@":"2","#":"3",$:"4","%":"5","^":"6","&":"7","*":"8","(":"9",")":"0",_:"-","+":"=",":":";",'"':"'","<":",",">":".","?":"/","|":"\\"},z={option:"alt",command:"meta","return":"enter",
escape:"esc",plus:"+",mod:/Mac|iPod|iPhone|iPad/.test(navigator.platform)?"meta":"ctrl"},k;for(g=1;20>g;++g)l[111+g]="f"+g;for(g=0;9>=g;++g)l[g+96]=g;c.prototype.bind=function(a,b,c){a=a instanceof Array?a:[a];this._bindMultiple.call(this,a,b,c);return this};c.prototype.unbind=function(a,b){return this.bind.call(this,a,function(){},b)};c.prototype.trigger=function(a,b){if(this._directMap[a+":"+b])this._directMap[a+":"+b]({},a);return this};c.prototype.reset=function(){this._callbacks={};this._directMap=
{};return this};c.prototype.stopCallback=function(a,b){return-1<(" "+b.className+" ").indexOf(" mousetrap ")||B(b,this.target)?!1:"INPUT"==b.tagName||"SELECT"==b.tagName||"TEXTAREA"==b.tagName||b.isContentEditable};c.prototype.handleKey=function(){return this._handleKey.apply(this,arguments)};c.init=function(){var a=c(r),b;for(b in a)"_"!==b.charAt(0)&&(c[b]=function(b){return function(){return a[b].apply(a,arguments)}}(b))};c.init();C.Mousetrap=c;"undefined"!==typeof module&&module.exports&&(module.exports=
c);"function"===typeof define&&define.amd&&define(function(){return c})})(window,document);

Mousetrap.bind("enter", function () {
	$("#story-caption #endWeekButton a.macro-link").trigger("click");
});
Mousetrap.bind("space", function () {
	$("#story-caption #nextButton a.macro-link").trigger("click");
});
Mousetrap.bind("c", function () {
	$("#story-caption #manageArcology a.macro-link").trigger("click");
});
Mousetrap.bind("p", function () {
	$("#story-caption #managePenthouse a.macro-link").trigger("click");
});
Mousetrap.bind("left", function () {
	$("#prevSlave a.macro-link").trigger("click");
	$("#prevRule a").trigger("click");
});
Mousetrap.bind("q", function () {
	$("#prevSlave a.macro-link").trigger("click");
	$("#prevRule a").trigger("click");
});
Mousetrap.bind("shift+left", function () {
	$("#firstRule a").trigger("click");
});
Mousetrap.bind("shift+q", function () {
	$("#firstRule a").trigger("click");
});
Mousetrap.bind("right", function () {
	$("#nextSlave a.macro-link").trigger("click");
	$("#nextRule a").trigger("click");
});
Mousetrap.bind("shift+right", function () {
	$("#lastRule a").trigger("click");
});
Mousetrap.bind("e", function () {
	$("#nextSlave a.macro-link").trigger("click");
	$("#nextRule a").trigger("click");
});
Mousetrap.bind("shift+e", function () {
	$("#lastRule a").trigger("click");
});
Mousetrap.bind("f", function () {
	$("#walkpast a.macro-link").trigger("click");
});
Mousetrap.bind("h", function () {
	$("#manageHG a.macro-link").trigger("click");
});
Mousetrap.bind("s", function () {
	$("#buySlaves a.macro-link").trigger("click");
});
Mousetrap.bind("a", function () {
	$("#managePA a.macro-link").trigger("click");
});
Mousetrap.bind("b", function () {
	$("#manageBG a.macro-link").trigger("click");
});
Mousetrap.bind("u", function () {
	$("#manageRecruiter a.macro-link").trigger("click");
});
Mousetrap.bind("o", function () {
	$("#story-caption #optionsButton a.macro-link").trigger("click");
});
Mousetrap.bind("y", function () {
	$("#story-caption #policyButton a.macro-link").trigger("click");
});
Mousetrap.bind("f", function () {
	$("#story-caption #FSButton a.macro-link").trigger("click");
});
Mousetrap.bind("t", function () {
	$("#story-caption #PAOButton a.macro-link").trigger("click");
});
Mousetrap.bind("v", function () {
	$("#story-caption #URButton a.macro-link").trigger("click");
});
Mousetrap.bind("r", function () {
	$("#RAButton a.macro-link").trigger("click");
});
Mousetrap.bind("x", function () {
	$("#story-caption #managePerson a.macro-link").trigger("click");
});
Mousetrap.bind("z", function () {
	$("#story-caption #SFMButton a.macro-link").trigger("click");
});
Mousetrap.bind("d", function () {
	$("#story-caption #edictButton a.macro-link").trigger("click");
});
Mousetrap.bind("shift+s", function () {
	$("#story-caption #securityHQ a.macro-link").trigger("click");
});
Mousetrap.bind("shift+a", function () {
	$("#story-caption #secBarracks a.macro-link").trigger("click");
});
Mousetrap.bind("shift+h", function () {
	$("#story-caption #propHub a.macro-link").trigger("click");
});
Mousetrap.bind("shift+r", function () {
	$("#story-caption #riotCenter a.macro-link").trigger("click");
});
/**
 * BoobGenerator namespace.
 */
if (typeof BoobGenerator == "undefined") {
	var BoobGenerator = {
		rollBreast: function (modif) {
			var volume = [0, 300, 500, 650, 800, 1000, 1200, 1400, 1600, 1800, 2050, 2300, 2600, 2900, 3250, 3600, 3950, 4300, 4700, 5100, 5500, 5900];
			var volume_dist = [90000, 470000, 720000, 840000, 908574, 947759, 970151, 982946, 990258, 994436, 996824, 998188, 998968, 999414, 999669, 999814, 999897, 999945, 999972, 999987, 999995, 1000000];
			var randomRoll = Math.floor(Math.random() * 1000000) + 1
			var actualSize = 0
			while (randomRoll > volume_dist[actualSize]) {
				actualSize = actualSize + 1
			}
			var minorSizeAdjustment = 0
			if (Math.random()<.5) {
				minorSizeAdjustment = (Math.floor(Math.random() * 2) + 1) * 50
			}
			var volResult = volume[actualSize] + minorSizeAdjustment + modif
			if (volResult < 0) {volResult = 0}
			return volResult
		}
	};
	// Raise namespace scope to Global.
	window.BoobGenerator = BoobGenerator;
};

/**
 * Slave checker namespace.
 */
if (typeof SlaveStatsChecker == "undefined") {
	var SlaveStatsChecker = {
		checkForLisp: function (slave) {
				/* Begin mod section: toggle whether slaves lisp. */
				if (SugarCube.State && SugarCube.State.variables.disableLisping == 1) {
					return false;
				}
				/* End mod section: toggle whether slaves lisp. */
				return ((slave.lips > 70) || (slave.lipsPiercing + slave.tonguePiercing > 2))
			}
	};
	// Raise namespace scope to Global.
	window.SlaveStatsChecker = SlaveStatsChecker;
};

window.removeFromArray = function(arr, val) {
	for (var i = 0; i < arr.length; i++) {
		if (val == arr[i])
			return arr.splice(i,1);
	}
	return null;
};

window.filterInPlace = function(arr, callback, thisArg) {
	var j = 0;

	arr.forEach(function(e, i) {
		if (callback.call(thisArg, e, i, arr))
			arr[j++] = e;
	});

	arr.length = j;
	return arr;
};

if (typeof FertilityAge == "undefined") {
	var FertilityAge = {
		setAge: function (age) {
			age = Number(age)
				if (age != age) {
					return 13;
				} else {
				return age
			}
		}
	};
	// Raise namespace scope to Global.
	window.FertilityAge = FertilityAge;
};

window.canGetPregnant = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg == -1) { /* contraceptives check */
		return false;
	} else if (isFertile(slave) == false) { /* check other fertility factors */
		return false;
	} else if ((slave.ovaries == 1) && (canDoVaginal(slave) == true)) {
		return true;
	} else if ((slave.mpreg == 1) && (canDoAnal(slave) == true)) { /* pregmod */
		return true;
	} else {
		return false;
	}
};

/* pregmod: are slave2's sperm compatible with slave1's eggs? */
window.canBreed = function(slave1, slave2) {
	if (!slave1 || !slave2) {
		return null;
	} else if (slave1.eggType == slave2.ballType) {
		return true;
	} else {
		return false;
	}
};

/* assuming slave1 is fertile, could slave2 impregnate slave1? slave2 must have dick and balls with compatible sperm; both slaves must not be in chastity; slave2 need not achieve erection */
window.canImpreg = function(slave1, slave2) {
	if (!slave1 || !slave2) {
		return null;
	} else if (slave2.dick < 1) {
		return false;
	} else if (slave2.balls < 1) {
		return false;
	} else if (slave2.dickAccessory == "chastity") {
		return false;
	} else if (slave2.dickAccessory == "combined chastity") {
		return false;
	} else if (slave2.pubertyXY == 0) { /* pregmod start */
		return false;
	} else if (slave2.vasectomy == 1) {
		return false;
	} else if (canBreed(slave1, slave2) == false) {
		return false;                   /* pregmod end */
	} else if (canGetPregnant(slave1) == false) { /* includes chastity checks */
		return false;
	} else {
		return true;
	}
};

/* contraceptives (.preg == -1) do not negate this function */
window.isFertile = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 0) { /* currently pregnant */
		return false;
	} else if (slave.preg < -1) { /* sterile */
		return false;
	} else if (slave.pubertyXX == 0) { /* pregmod start */
		return false;
	} else if (slave.ovaryAge >= 47) {
		return false;
	} else if (slave.inflation != 0) {
		return false;
	} else if (slave.bellyImplant != -1) {
		return false;
	} else if (slave.mpreg == 1) {
		return true;                   /* pregmod end */
	} else if (slave.ovaries == 1) {
		return true;
	} else {
		return false;
	}
};

//hyperpreg size 2
window.hyperBellyTwo = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 30 && slave.pregType >= 20) {
		return true;
	} else if (slave.bellyImplant >= 32000) {
		return true;
	} else {
		return false;
	}
};

window.hyperPregBellyTwo = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 30 && slave.pregType >= 20) {
		return true;
	} else {
		return false;
	}
};

//hyperpreg size 1
window.hyperBellyOne = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 20 && slave.pregType >= 20) {
		return true;
	} else if (slave.preg > 30 && slave.pregType >= 10) {
		return true;
	} else if (slave.bellyImplant >= 16000) {
		return true;
	} else {
		return false;
	}
};

window.hyperPregBellyOne = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 20 && slave.pregType >= 20) {
		return true;
	} else if (slave.preg > 30 && slave.pregType >= 10) {
		return true;
	} else {
		return false;
	}
};

//full term belly
window.hugeBelly = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 0 && slave.pregType >= 20) {
		return true;
	} else if (slave.preg > 10 && slave.pregType >= 10) {
		return true;
	} else if (slave.preg > 30) {
		return true;
	} else if (slave.inflation >= 3) {
		return true;
	} else if (slave.bellyImplant >= 8000) {
		return true;
	} else {
		return false;
	}
};

window.hugeBellyPreg = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 0 && slave.pregType >= 20) {
		return true;
	} else if (slave.preg > 10 && slave.pregType >= 10) {
		return true;
	} else if (slave.preg > 30) {
		return true;
	} else {
		return false;
	}
};

//is the slave's belly big enough to get in the way
window.bigBellyPreg = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 0 && slave.pregType >= 20) {
		return true;
	} else if (slave.preg > 10 && slave.pregType >= 10) {
		return true;
	} else if (slave.preg > 20) {
		return true;
	} else {
		return false;
	}
};

//is the slave's belly big enough to be noticiable.
window.visibleBellyPreg = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.preg > 0 && slave.pregType >= 20) {
		return true;
	} else if (slave.preg > 5 && slave.pregType >= 10) {
		return true;
	} else if (slave.preg > 10) {
		return true;
	} else {
		return false;
	}
};

window.bigBelly = function(slave) {
	if (!slave) {
		return null;
	} else if (bigBellyPreg(slave) == true) {
		return true;
	} else if (slave.inflation > 2) {
		return true;
	} else if (slave.bellyImplant >= 4000) {
		return true;
	} else {
		return false;
	}
};

window.visibleBelly = function(slave) {
	if (!slave) {
		return null;
	} else if (visibleBellyPreg(slave) == true) {
		return true;
	} else if (slave.inflation > 0) {
		return true;
	} else if (slave.bellyImplant >= 2000) {
		return true;
	} else {
		return false;
	}
};

window.canAchieveErection = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.dick < 7 && slave.dick > 0 && slave.drugs !== 'hormone blockers' && (slave.balls > 0 ? slave.hormoneBalance < 100 : slave.hormoneBalance <= -100)) {
		return true;
	} else {
		return false;
	}
};

window.canPenetrate = function(slave) {
	if (!slave) {
		return null;
	} else if (canAchieveErection(slave) == false) {
		return false;
	} else if (slave.dickAccessory == "chastity") {
		return false;
	} else if (slave.dickAccessory == "combined chastity") {
		return false;
	} else if (slave.dick > 7) {
		return false;
	}
	return true;
};

window.canSee = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.eyes > -2) {
		return true;
	} else {
		return false;
	}
};

window.canWalk = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.amp == 1)  {
		return false;
	} else if (tooFatSlave(slave)) {
		return false;
	} else if (tooBigBreasts(slave)) {
		return false;
	} else if (tooBigDick(slave)) {
		return false;
	} else if (tooBigBalls(slave)) {
		return false;
	} else if (tooBigButt(slave)) {
		return false;
	} else if (tooBigBelly(slave)) {
		return false;
	} else if (slave.heels == 0) {
		return true;
	} else if (slave.shoes == "heels") {
		return true;
	} else if (slave.shoes == "extreme heels") {
		return true;
	} else if (slave.shoes == "boots") {
		return true;
	} else {
		return false;
	}
};

window.canTalk = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.accent > 2)  {
		return false;
	} else if (slave.voice == 0) {
		return false;
	} else if (slave.lips > 95) {
		return false;
	} else if (slave.collar == "dildo gag") {
		return false;
	} else if (slave.collar == "massive dildo gag") {
		return false;
	} else if (slave.collar == "ball gag") {
		return false;
	} else if (slave.collar == "bit gag") {
		return false;
	} else {
		return true;
	}
};

window.canDoAnal = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.vaginalAccessory == "anal chastity") {
		return false;
	} else if (slave.dickAccessory == "anal chastity") {
		return false;
	} else if (slave.vaginalAccessory == "combined chastity") {
		return false;
	} else if (slave.dickAccessory == "combined chastity") {
		return false;
	}
	return true;
};

window.canDoVaginal = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.vagina < 0) {
		return false;
	} else if (slave.vaginalAccessory == "chastity belt") {
		return false;
	} else if (slave.vaginalAccessory == "combined chastity") {
		return false;
	}
	return true;
};

window.tooFatSlave = function(slave){
	if (!slave) {
		return null;
	} else if (slave.weight > 190+(slave.muscles/5) && slave.physicalAge >= 18) {
		return true;
	} else if (slave.weight > 130+(slave.muscles/20) && slave.physicalAge <= 3) {
		return true;
	} else if (slave.weight > 160+(slave.muscles/15) && slave.physicalAge <= 12) {
		return true;
	} else if (slave.weight > 185+(slave.muscles/10) && slave.physicalAge < 18) {
		return true;
	} else {
		return false;
	}
};

window.tooBigBreasts = function(slave){
	if (!slave) {
		return null;
	} else if (slave.boobs > 30000+(slave.muscles*100) && slave.physicalAge >= 18) {
		return true;
	} else if (slave.boobs > 5000+(slave.muscles*10) && slave.physicalAge <= 3) {
		return true;
	} else if (slave.boobs > 10000+(slave.muscles*20) && slave.physicalAge <= 12) {
		return true;
	} else if (slave.boobs > 20000+(slave.muscles*50) && slave.physicalAge < 18) {
		return true;
	} else {
		return false;
	}
};

window.tooBigBelly = function(slave){
	if (!slave) {
		return null;
	} else if (slave.belly >= 450000+(slave.muscles*2000) && slave.physicalAge >= 18) {
		return true;
	} else if (slave.belly >= 350000+(slave.muscles*1000) && slave.physicalAge >= 13) {
		return true;
	} else if (slave.belly >= 150000+(slave.muscles*500) && slave.physicalAge <= 3) {
		return true;
	} else if (slave.belly >= 250000+(slave.muscles*800) && slave.physicalAge <= 12) {
		return true;
	} else {
		return false;
	}
};

window.tooBigBalls = function(slave){
	if (!slave) {
		return null;
	} else if (slave.balls >= 30+(slave.muscles*.3) && slave.physicalAge <= 3) {
		return true;
	} else if (slave.balls >= 60+(slave.muscles*.5) && slave.physicalAge <= 12) {
		return true;
	} else if (slave.balls >= 90+(slave.muscles*.7)) {
		return true;
	} else {
		return false;
	}
};

window.tooBigDick = function(slave){
	if (!slave) {
		return null;
	} else if (slave.dick >= 15+(slave.muscles*.1) && slave.physicalAge <= 3 && slave.dick !== 0) {
		return true;
	} else if (slave.dick >= 30+(slave.muscles*.3) && slave.physicalAge <= 12) {
		return true;
	} else if (slave.dick >= 68+(slave.muscles*.4)) {
		return true;
	} else {
		return false;
	}
};

window.tooBigButt = function(slave){
	if (!slave) {
		return null;
	} else if (slave.butt > 10 && slave.physicalAge <= 3) {
		return true;
	} else if (slave.butt > 14 && slave.physicalAge <= 12) {
		return true;
	} else {
		return false;
	}
};

window.relationTargetWord = function(slave) {
	if (!slave) {
		return null;
	} else if (slave.relation == "daughter") {
		return "mother";
	} else if (slave.relation == "mother") {
		return "daughter";
	}
	return slave.relation;
};

/* intended to condense the clothing/toy/etc availability checks into something less asinine */
window.isItemAccessible = function(string) {
	if (State.variables.cheatMode === 1){
		return true;
	} else {
		switch(string) {
		case 'attractive lingerie for a pregnant woman':
			if ((State.variables.arcologies[0].FSRepopulationFocus > 0) || (State.variables.clothesBoughtMaternityLingerie === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a bunny outfit':
			if ((State.variables.arcologies[0].FSGenderFundamentalist > 0) || (State.variables.clothesBoughtBunny === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'body oil':
			if ((State.variables.arcologies[0].FSPhysicalIdealist > 0) || (State.variables.clothesBoughtOil === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'chains':
			if ((State.variables.arcologies[0].FSDegradationist > 0) || (State.variables.clothesBoughtChains === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a chattel habit':
			if ((State.variables.arcologies[0].FSChattelReligionist > 0) || (State.variables.clothesBoughtHabit === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'conservative clothing':
			if ((State.variables.arcologies[0].FSPaternalist > 0) || (State.variables.clothesBoughtConservative === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'harem gauze':
			if ((State.variables.arcologies[0].FSArabianRevivalist > 0) || (State.variables.clothesBoughtHarem === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a huipil':
			if ((State.variables.arcologies[0].FSAztecRevivalist > 0) || (State.variables.clothesBoughtHuipil === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a kimono':
			if ((State.variables.arcologies[0].FSEdoRevivalist > 0) || (State.variables.clothesBoughtKimono === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a maternity dress':
			if ((State.variables.arcologies[0].FSRepopulationFocus > 0) || (State.variables.clothesBoughtMaternityDress === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a slutty qipao':
			if ((State.variables.arcologies[0].FSChineseRevivalist > 0) || (State.variables.clothesBoughtQipao === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'stretch pants and a crop-top':
			if ((State.variables.arcologies[0].FSHedonisticDecadence > 0) || (State.variables.clothesBoughtLazyClothes === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'a toga':
			if ((State.variables.arcologies[0].FSRomanRevivalist > 0) || (State.variables.clothesBoughtToga === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'Western clothing':
			if ((State.variables.arcologies[0].FSPastoralist > 0) || (State.variables.clothesBoughtWestern === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'bowtie':
			if ((State.variables.arcologies[0].FSGenderFundamentalist > 0) || (State.variables.clothesBoughtBunny === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'ancient Egyptian':
			if ((State.variables.arcologies[0].FSEgyptianRevivalist > 0) || (State.variables.clothesBoughtEgypt === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'massive dildo gag':
			if (State.variables.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 ((State.variables.arcologies[0].FSRepopulationFocus > 0) || (State.variables.clothesBoughtBelly === 1)) {
				return true;
			} else {
				return false;
			}
			break;
		case 'long dildo': case 'long, large dildo': case 'long, huge dildo':
			if (State.variables.toysBoughtDildos === 1) {
				return true;
			} else {
				return false;
			}
			break;
		case 'long plug': case 'long, large plug': case 'long, huge plug':
			if (State.variables.toysBoughtButtPlugs === 1) {
				return true;
			} else {
				return false;
			}
			break;
		case 'tail':
			if (State.variables.toysBoughtButtPlugTails === 1) {
				return true;
			} else {
				return false;
			}
			break;
		default:
			return true;
			break;
		}
	}
};

window.ruleApplied = function(slave, ID) {
	if (!slave || !slave.currentRules)
		return null;
	return slave.currentRules.includes(ID);
};

window.expandFacilityAssignments = function(facilityAssignments) {
	var assignmentPairs = {
		"serve in the club": "be the DJ",
		"rest in the spa": "be the Attendant",
		"work in the brothel": "be the Madam",
		"work in the dairy": "be the Milkmaid",
		"work as a servant": "be the Stewardess",
		"get treatment in the clinic": "be the Nurse",
		"live with your Head Girl": "be your Head Girl",
		"serve in the master suite": "be your Concubine",
		"learn in the schoolroom": "be the Schoolteacher",
		"be confined in the cellblock": "be the Wardeness",
	};

	if (!facilityAssignments || !facilityAssignments.length)
		return [];
	var fullList = facilityAssignments.map(function(a) {
		if (a in assignmentPairs)
			return [a, assignmentPairs[a]];
		return a;
	});
	return fullList.flatten();
};

window.ruleSlaveSelected = function(slave, rule) {
	if (!slave || !rule || !rule.selectedSlaves)
		return false;
	return rule.selectedSlaves.includes(slave.ID);
};

window.ruleSlaveExcluded = function(slave, rule) {
	if (!slave || !rule || !rule.excludedSlaves)
		return false;
	return rule.excludedSlaves.includes(slave.ID);
};

window.ruleAssignmentSelected = function(slave, rule) {
	if (!slave || !rule || (!rule.assignment && !rule.facility))
		return false;
	var assignment = rule.assignment.concat(expandFacilityAssignments(rule.facility));
	return assignment.includes(slave.assignment);
}

window.ruleAssignmentExcluded = function(slave, rule) {
	if (!slave || !rule || (!rule.excludeAssignment && !rule.excludeFacility))
		return false;
	var excludeAssignment = rule.excludeAssignment.concat(expandFacilityAssignments(rule.excludeFacility));
	return excludeAssignment.includes(slave.assignment);
}

window.hasSurgeryRule = function(slave, rules) {
	if (!slave || !rules || !slave.currentRules)
		return false;

	for (var d = rules.length-1; d >= 0; d--) {
		if (ruleApplied(slave, rules[d].ID)) {
			if (rules[d].autoSurgery > 0) {
				return true;
			}
		}
	}
	return false;
};

window.hasRuleFor = function(slave, rules, what) {
	if (!slave || !rules || !slave.currentRules)
		return false;

	for (var d = rules.length-1; d >= 0; d--) {
		if (ruleApplied(slave, rules[d].ID)) {
			if (rules[d][what] !== "no default setting") {
				return true;
			}
		}
	}
	return false;
};

window.hasHColorRule = function(slave, rules) {
	return hasRuleFor(slave, rules, "hColor");
}

window.hasHStyleRule = function(slave, rules) {
	return hasRuleFor(slave, rules, "hStyle");
};

window.hasEyeColorRule = function(slave, rules) {
	return hasRuleFor(slave, rules, "eyeColor");
};

window.lastPregRule = function(slave, rules) {
	if (!slave || !rules)
		return null;
	if (!slave.currentRules)
		return false;

	for (var d = rules.length-1; d >= 0; d--) {
		if (ruleApplied(slave, rules[d].ID)) {
			if (rules[d].preg == -1) {
				return true;
			}
		}
	}

	return null;
};

window.lastSurgeryRuleFor = function(slave, rules, what) {
	if (!slave || !rules || !slave.currentRules)
		return null;

	for (var d = rules.length-1; d >= 0; d--) {
		if (!rules[d].surgery)
			return null;

		if (ruleApplied(slave, rules[d].ID)) {
			if (rules[d].surgery[what] != "no default setting") {
				return rules[d];
			}
		}
	}

	return null;
};

window.lastEyeSurgeryRule = function(slave, rules) {
	return lastSurgeryRuleFor(slave, rules, "eyes");
}

window.lastLactationSurgeryRule = function(slave, rules) {
	return lastSurgeryRuleFor(slave, rules, "lactation");
}

window.lastProstateSurgeryRule = function(slave, rules) {
	return lastSurgeryRuleFor(slave, rules, "prostate");
}

window.lastLipSurgeryRule = function(slave, rules) {
	return lastSurgeryRuleFor(slave, rules, "lips");
};

window.lastBoobSurgeryRule = function(slave, rules) {
	return lastSurgeryRuleFor(slave, rules, "boobs");
};

window.lastButtSurgeryRule = function(slave, rules) {
	return lastSurgeryRuleFor(slave, rules, "butt");
};

window.lastHairSurgeryRule = function(slave, rules) {
	return lastSurgeryRuleFor(slave, rules, "hair");
}

window.lastBodyHairSurgeryRule = function(slave, rules) {
	return lastSurgeryRuleFor(slave, rules, "bodyhair");
}

window.milkAmount = function(slave) {
	var milk;
	var calcs;
	if (!slave) {
		return null;
	} else {
		calcs = slave.boobs-slave.boobsImplant
		if (calcs > 40000) {
			milk = (158+((calcs-10000)/600))
		} else if (calcs > 25000) {
			milk = (128+((calcs-10000)/500))
		} else if (calcs > 10000) {
			milk = (78+((calcs-10000)/300))
		} else if (calcs > 5000) {
			milk = (53+((calcs-5000)/200))
		} else if (calcs > 2000) {
			milk = (29+((calcs-2000)/125))
		} else if (calcs > 800) {
			milk = (16+((calcs-800)/80))
		} else {
			milk = (8+((calcs-400)/50))
		}
		if (slave.lactation == 2) {
			milk *= 1.2
		}
		milk += (milk*((slave.devotion-50)/200))
		if (slave.boobsImplant > 200) {
			milk *= 0.9
		}
		calcs = (slave.hormoneBalance/50)
		if (slave.balls != 0 && calcs > -2) {
			calcs -= 1
		} else if (slave.ovaries != 1 && calcs < 2) {
			calcs += 1
		}
		milk *= (1+(calcs*0.1))
		milk *= (1+(slave.preg/100))
		milk *= (1+(slave.health/50))
		milk *= (1+(slave.weight/500))
		milk *= (1+(slave.lactationAdaptation/500))
		milk = Math.trunc(milk)
		milk = Math.clamp(milk,1,1000000000000000000)
		return milk
	}
};

window.cumAmount = function(slave) {
	var cum = 0;
	var calcs = 0;
	if (!slave) {
		return null;
	} else {
		if (slave.drugs == "testicle enhancement") {
			cum = ((slave.balls*3.5)+1)
		} else if (slave.drugs == "hyper testicle enhancement") {
			cum = ((slave.balls*5)+1)
		} else {
			cum = ((slave.balls*2.5)+1)
		}
		if (slave.diet == "cum production") {
			cum *= 1.2
		}
		calcs = (slave.hormoneBalance/50)
		cum *= (1-(calcs*0.1))
		if (slave.scrotum == 0) {
			cum *= 0.8
		}
		if (slave.prostate == 0) {
			cum *= 0.2 // being generous here
		} else if (slave.prostate == 2) {
			cum *= 1.2
		} else if (slave.prostate == 3) {
			cum *= 1.5
		}
		if (slave.devotion > 50) {
			cum += (cum*(slave.devotion/100))
		} else if (slave.devotion < -50) {
			cum += (cum*(slave.devotion/100))
		}
		if (slave.health > 50) {
			cum += (cum*(slave.health/50))
		} else if (slave.health < -50) {
			cum += (cum*(slave.health/50))
		}
		cum = Math.trunc(cum)
		cum = Math.clamp(cum,1,1000000000000000000)
		return cum
	}
};

window.mergeRules = function(rules) {
    var combinedRule = {};

    for (var i = 0; i < rules.length; i++) {
        for (var prop in rules[i]) {
            // we don't manage setAssignment here, we do it in <<DefaultRules>>
            if (prop === "setAssignment")
                continue;

            // A rule overrides any preceding ones if,
            //   * there are no preceding ones,
            //   * or it sets autoBrand,
            //   * or it does not set autoBrand and is not "no default setting"
            var applies = (
                combinedRule[prop] === undefined
                || (prop === "autoBrand" && rules[i][prop])
                || (prop !== "autoBrand" && rules[i][prop] !== "no default setting")
            );

            if (applies)
            {

            	//Objects in JS in operaions "=" pass by reference, so we need completly new object to avoid mess up previous rules.
            	if ("object" == typeof rules[i][prop] && "object" != typeof combinedRule[prop])
            		combinedRule[prop] = new Object();

            	//If we already have object - now we will process it's properties, but object itself should be skiped.
            	if ("object" != typeof combinedRule[prop])
                	combinedRule[prop] = rules[i][prop];

            	/*Some properties of rules now have second level properties. We need to check it, and change ones in combinedRule. (Good example - growth drugs. Breasts, butt, etc...) */
            	if ( "object" == typeof rules[i][prop])
    	        {
	            	for (var subprop in rules[i][prop])
    	        	{
    	           		var subapplies = (
            	    	combinedRule[prop][subprop] === undefined
                			|| (rules[i][prop][subprop] !== "no default setting")
	            		);

		            	if (subapplies)
    		            	combinedRule[prop][subprop] = rules[i][prop][subprop];
    		        }

            	}
           	}

        }

    }

    return combinedRule;
}

window.isVegetable = function(slave) {
	slave = slave || State.variables.activeSlave;
	if(!slave) { return false; }
	return (slave.fetish === 'mindbroken');
};

window.repGainSacrifice = function(slave, arcology) {
	slave = slave || State.variables.activeSlave;
	arcology = arcology || State.variables.arcologies[0];
	if(!slave || !arcology || arcology.FSAztecRevivalist === "unset" || arcology.FSAztecRevivalist <= 0) {
		return 0;
	}
	return Math.ceil(
		(Math.min(100, Math.pow(1.0926, State.variables.week - slave.weekAcquired)) + slave.prestige * 30)
			* arcology.FSAztecRevivalist / 100 / ((State.variables.slavesSacrificedThisWeek || 0) + 1));
};

window.ngUpdateGenePool = function(genePool) {
  var transferredSlaveIds = (SugarCube.State.variables.slaves || [])
    .filter(function(s) { return s.ID >= 1200000; })
    .map(function(s) { return s.ID - 1200000; });
  return (genePool || [])
    .filter(function(s) { return transferredSlaveIds.indexOf(s.ID) >= 0; })
    .map(function(s) {
      var result = jQuery.extend(true, {}, s);
      result.ID += 1200000;
      return result;
    });
}

window.toJson = function(obj) {
    var jsontext = JSON.stringify(obj);
    jsontext = jsontext.replace(/^{/,"");
    jsontext = jsontext.replace(/}$/,"");
    return jsontext;
};

window.nippleColor = function(slave) {
	slave = slave || State.variables.activeSlave;
	if(slave.skin === 'tanned' || slave.skin === 'fair') {
		if(slave.preg > 10 || (slave.birthsTotal > 0 && slave.lactation > 0)) {
			return 'dark brown';
		} else {
			return 'pink';
		}
	} else if((slave.skin === 'pale' || slave.race === 'white')) {
		if(slave.preg > 10 || (slave.birthsTotal > 0 && slave.lactation > 0)) {
			return 'brown';
		} else {
			return 'pink';
		}
	} else if((slave.skin === 'brown' || slave.race === 'black')) {
		if(slave.preg > 10 || (slave.birthsTotal > 0 && slave.lactation > 0)) {
			return 'black';
		} else {
			return 'dark brown';
		}
	} else {
		if(slave.preg > 10 || (slave.birthsTotal > 0 && slave.lactation > 0)) {
			return 'dark brown';
		} else {
			return 'brown';
		}
	}
}

window.jsAlert = function(obj)
{
	alert(obj);
}

window.jsConsoleInfo = function(obj)
{
	console.info(obj);
}


code:
:: textinput [script]

Macro.add("textinput", {
    // Signifies that the macro is a container macro.
    tags: null,

    handler: function() {
        if (this.args.length < 2) {
            var errors = [];
            if (this.args.length < 1) { errors.push("variable name"); }
            if (this.args.length < 2) { errors.push("default 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");
        }

        var 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 '" + varName + "' is missing its sigil ($ or _)");
        }

        var that = this;
        var defaultValue = this.args[1];
        var el = document.createElement("textarea");

        // Setup and append the textarea element to the output buffer.
        jQuery(el)
            .attr({
                rows: 4,
                // cols: 68, // instead of setting "cols" we set the `min-width` in CSS
                tabindex: 0 // for accessiblity
            })
            .addClass("macro-textarea")  // "hijack" the .macro-textarea class
            .on("input", function() {
                Wikifier.setValue(varName, this.value);
                if (that.payload[0].contents !== "")
                    Wikifier.wikifyEval(that.payload[0].contents.trim());
            })
            .appendTo(this.output);

        // Set the story variable and textarea element to the default value.
        Wikifier.setValue(varName, defaultValue);

        // Ideally, we should be setting `.defaultValue` here, but IE doesn't support it,
        // so we have to use `.textContent`, which is equivalent.
        el.textContent = defaultValue;
    }
});

license: mit


code:
:: textboxJS [script]

/* Nicked off greyelf, works for replace textboxes */
window.setReplaceTextboxMaxLength = function (storyVarName, maxLength) {
	var textboxId = '#textbox-' + Util.slugify(storyVarName);
	$(textboxId)
			.attr('maxlength', maxLength)
			.css({
				'min-width' : 'initial',
				width       : maxLength + 'em',
				padding		: '3px 2px'
			});
};

/* Nicked off TheMadExile, works for non-replace textboxes */
window.setTextboxMaxLength = function (storyVarName, maxLength) {
	var textboxId = '#textbox-' + Util.slugify(storyVarName);
	postdisplay[textboxId + '-maxlength'] = function (taskName) {
		delete postdisplay[taskName];
		$(textboxId)
			.attr('maxlength', maxLength)
			.css({
				'min-width' : 'initial',
				width       : maxLength + 'em',
				padding		: '3px 2px'
			});
	};
};


code:
:: UtilJS [script]

/*
 * Height.mean(nationality, race, genes, age) - returns the mean height for the given combination and age in years (>=2)
 * Height.mean(nationality, race, genes) - returns the mean adult height for the given combination
 * Height.mean(slave) - returns the mean (expected) height for the given slave
 *
 * Height.random(nationality, race, genes, age) - returns a random height for the given combination,
 *     with Gaussian distribution (mean = 1, standard deviation = 0.05) around the mean height
 * Height.random(nationality, race, genes) - returns a random height for the given combination of an adult, as above
 * Height.random(slave[, options]) - returns a random height for the given slave, as above.
 *                                   The additional options object can modify how the values are generated
 *                                   in the same way setting them as global configuration would, but only for this
 *                                   specific generation.
 *
 *                                   Example: Only generate above-average heights based on $activeSlave:
 *                                   Height.random($activeSlave, {limitMult: [0, 5]})
 *
 * Height.forAge(height, age, genes) - returns the height adapted to the age and genes
 * Height.forAge(height, slave) - returns the height adapted to the slave's age and genes
 *
 * Height.config(configuration) - configures the random height generator globally and returns the current configuration
 *   The options and their default values are:
 *   limitMult: [-3, 3] - Limit to the values the underlying (normal) random generator returns.
 *                        In normal use, the values are almost never reached; only 0.27% of values are
 *                        outside this range and need to be regenerated. With higher skew (see below),
 *                        this might change.
 *   spread: 0.05 - The random values generated are multiplied by this and added to 1 to generate
 *                  the final height multiplier. The default value together with the default limitMult
 *                  means that the generated height will always fall within (1 - 0.05 * 3) = 85% and
 *                  (1 + 0.05 * 3) = 115% of the mean height.
 *                  Minimum value: 0.001; maximum value: 0.5
 *   skew: 0 - How much the height distribution skews to the right (positive) or left (negative) side
 *             of the height.
 *             Minimum value: -1000, maximum value: 1000
 *   limitHeight: [0, 999] - Limit the resulting height range. Warning: A small height limit range
 *                           paired with a high spread value results in the generator having to
 *                           do lots of work generating and re-generating random heights until
 *                           one "fits".
 *
 * Anon's explination:
 *limitMult: [0, -30]
 *
 *This specifies a range going up from 0 to -30. It needs to go [-30, 0] instead. Same thing with [0, -5] two lines down. note: technically, this isn't true, because for some bizarre reason Height.random reverses the numbers for you if you get them wrong. But it's important to establish good habits, so.
 *
 *Skew, spread, limitMult: These are statistics things. BTW, a gaussian distribution is a normal distribution. Just a naming thing.
 *
 *Skew: See https://brownmath.com/stat/shape.htm#Skewness. Basically a measure of how asymmetrical the curve is. It doesn't change the mean, and can't move the main mass of the distribution far from the mean! The normal distribution can't do that. You can, however, use it to fatten one or the other tail.
 *
 *Spread: Changes the average distance from the mean, making the graph wider and shorter. Moves "mass" from the center to the tail. It's basically standard deviation, but named funny because FC codebase.
 *
 *limitMult: A clamp, expressed in z-score. (z=1 is one standard dev above mean, for ex.) If it excludes too much of the distribution the other parameters describe, you're going to spend lots of CPU making and throwing away heights. Don't worry about this unless you run into it.
 *
 *There's also limitHeight which you're not using. It's basically limitMult in different units.
 */
window.Height = (function(){
	'use strict';

	// Global configuration (for different game modes/options/types)
	var minMult = -3.0;
	var maxMult = 3.0;
	var skew = 0.0;
	var spread = 0.05;
	var minHeight = 0;
	var maxHeight = 999;

	// Configuration method for the above values
	const _config = function(conf) {
		if(_.isUndefined(conf)) {
			return {limitMult: [minMult, maxMult], limitHeight: [minHeight, maxHeight], skew: skew, spread: spread};
		}
		if(_.isFinite(conf.skew)) { skew = Math.clamp(conf.skew, -1000, 1000); }
		if(_.isFinite(conf.spread)) { spread = Math.clamp(conf.spread, 0.001, 0.5); }
		if(_.isArray(conf.limitMult) && conf.limitMult.length === 2 && conf.limitMult[0] !== conf.limitMult[1]
			&& _.isFinite(conf.limitMult[0]) && _.isFinite(conf.limitMult[1])) {
			minMult = Math.min(conf.limitMult[0], conf.limitMult[1]);
			maxMult = Math.max(conf.limitMult[0], conf.limitMult[1]);
		}
		if(_.isArray(conf.limitHeight) && conf.limitHeight.length === 2 && conf.limitHeight[0] !== conf.limitHeight[1]
			&& _.isFinite(conf.limitHeight[0]) && _.isFinite(conf.limitHeight[1])) {
			minHeight = Math.min(conf.limitHeight[0], conf.limitHeight[1]);
			maxHeight = Math.max(conf.limitHeight[0], conf.limitHeight[1]);
		}
		return {limitMult: [minMult, maxMult], limitHeight: [minHeight, maxHeight], skew: skew, spread: spread};
	}

	/* if you can find an average for an undefined, add it in! */
	const xxMeanHeight = {
		"American.white": 165, "American.black": 163.6, "American.latina": 158.9, "American.asian": 158.4, "American": 161.8,
		"Afghan": 163.8, "Algerian": 162, "Argentinian": 159.6, "Armenian": 158.1, "Australian": 161.8, "Austrian": 166,
		"Bangladeshi": 150.6, "Belarusian": 166.8, "Belgian": 168.1, "Bolivian": 142.2, "Brazilian": 158.8,
		"British": 161.9, "Burmese": undefined, "Canadian": 162.3, "Chilean": 157.2, "Chinese": 155.8, "Colombian": 158.7,
		"Congolese": 157.7, "Cuban": 156, "Czech": 167.22, "Danish": 168.7, "Dominican": 156.4, "Dutch": 169, "Egyptian": 158.9,
		"Emirati": 158.9, "Estonian": 165.5, "Ethiopian": 157.6, "Filipina": 151.8, "Finnish": 165.3, "French": 162.5,
		"German": 162.8, "Ghanan": 158.5, "Greek": 165, "Guatemalan": 147.3, "Haitian": 158.6, "Hungarian": 164,
		"Icelandic": 168, "Indian": 151.9, "Indonesian": 147, "Iranian": 157.2, "Iraqi": 155.8, "Irish": 163, "Israeli": 166,
		"Italian": 162.5, "Jamaican": 160.8, "Japanese": 158, "Jordanian": 158.2, "Kazakh": 159.8, "Kenyan": 159.4,
		"Korean": 156.15, "Lebanese": 165, "Libyan": 160.5, "Lithuanian": 167.5, "Malaysian": 154.7, "Malian": 160.4,
		"Mexican": 154, "Moroccan": 158.5, "Nepalese": 150.8, "Nigerian": 163.8, "Norwegian": 157.8, "Omani": undefined,
		"Pakistani": 151.9, "Peruvian": 151, "Polish": 165.1, "Portuguese": 165.1, "Puerto Rican": 158.9, "Romanian": 157,
		"Russian": 164.1, "Saudi": 156.3, "Scottish": 163, "Serbian": 166.8, "Slovak": 165.6, "South African": 159,
		"Spanish": 162.6, "Sudanese": 180.0, "Swedish": 166.8, "Swiss": 162.5, "Tanzanian": 156.6, "Thai": 159,
		"Tunisian": 160, "Turkish": 161.9, "Ugandan": 159.2, "Ukrainian": 164.8, "Uzbek": 159.9, "Venezuelan": 159,
		"Vietnamese": 155.2, "Yemeni": undefined, "a New Zealander": 164, "Zimbabwean": 160.3, "Sammarinese": undefined,
		"Marshallese": undefined, "Syrian": 156.3, "Bermudian": undefined, "Uruguayan": 158.0, "Monégasque": undefined,
		"Montenegrin": 168.4, "Cambodian": 152.4, "Cameroonian": 161.3, "Gabonese": 158.4, "Djiboutian": undefined,
		"Greenlandic": undefined, "Tuvaluan": undefined, "Zambian": 158.5, "Albanian": 161.8, "Bruneian": undefined,
		"Singaporean": 160.0, "Croatian": 166.3, "Bulgarian": 163.2, "Laotian": undefined, "Mongolian": 157.7, "Taiwanese": 160.4,
		"Belizean": undefined, "Grenadian": undefined, "I-Kiribati": undefined, "Malagasy": 154.3, "Maldivian": undefined,
		"Bosnian": 171.1, "Kosovan": undefined, "Macedonian": undefined, "Honduran": 152.0, "Maltese": 159.9,
		"Nauruan": undefined, "Micronesian": undefined, "Costa Rican": undefined, "Salvadoran": 160.3, "Nicaraguan": 153.7,
		"Panamanian": 152.0, "Nigerien": 157.8, "Andorran": undefined, "Bulgarian": 163.2, "Luxembourgian": 164.8,
		"Moldovan": 161.2, "Antiguan": 156.8, "Aruban": 158, "Azerbaijani": 162.4, "Bahamian": 157.1, "Bahraini": 165.2,
		"Barbadian": 158.5, "Bhutanese": 153.4, "Burundian": 157, "a Cook Islander": 162.3, "Cypriot": 163.4,
		"Dominiquais": 157.2, "East Timorese": 149.1, "Ecuadorian": 153.4, "Fijian": 161.8, "French Guianan": 157,
		"Georgian": 160.5, "Guyanese": 157.9, "Kuwaiti": 156.1, "Kyrgyz": 158.6, "Latvian": 165.7, "a Liechtensteiner": 164.3,
		"Niuean": 160.4, "Palauan": 161.4, "Palestinian": 158.2, "Papua New Guinean": 150.7, "Paraguayan": 158.3, "Qatari": 155,
		"Kittitian": 156.9, "Saint Lucian": 157.1, "Vincentian": 156.2, "Samoan": 161, "Seychellois": 155.8,
		"Slovene": 167.4, "a Solomon Islander": 151.8, "Sri Lankan": 151.4, "Surinamese": 155.7, "Tajik": 161.2, "Tongan": 159.5,
		"Trinidadian": 157.4, "Turkmen": 158.2, "Ni-Vanuatu": 149.8, "Vatican": 162.5,
		"": 162.5 // default
	};
	const xyMeanHeight = {
		"American.white": 178.2, "American.black": 177.4, "American.latina": 172.5, "American.asian": 172.5, "American": 176.4,
		"Afghan": undefined, "Algerian": 172.2, "Argentinian": 174.46, "Armenian": undefined, "Australian": 175.6,
		"Austrian": 179, "Bangladeshi": 150.8, "Belarusian": 176.9, "Belgian": 178.7, "Bolivian": 160, "Brazilian": 170.7,
		"British": 175.3, "Burmese": 168.0, "Canadian": 175.1, "Chilean": 169.6, "Chinese": 167.1, "Colombian": 170.6,
		"Congolese": 158.9, "Cuban": 168, "Czech": 180.31, "Danish": 180.4, "Dominican": 168.4, "Dutch": 181, "Egyptian": 170.3,
		"Emirati": 170.3, "Estonian": 179.1, "Ethiopian": undefined, "Filipina": 163.5, "Finnish": 178.9, "French": 175.6,
		"German": 175.4, "Ghanan": 169.5, "Greek": 177, "Guatemalan": 157.5, "Haitian": undefined, "Hungarian": 176,
		"Icelandic": 181, "Indian": 164.7, "Indonesian": 158, "Iranian": 170.3, "Iraqi": 165.4, "Irish": 177, "Israeli": 177,
		"Italian": 176.5, "Jamaican": 171.8, "Japanese": 172, "Jordanian": undefined, "Kazakh": 169, "Kenyan": 170.0,
		"Korean": 168.15, "Lebanese": 176, "Libyan": 171.3, "Lithuanian": 177.2, "Malaysian": 166.3, "Malian": 171.3,
		"Mexican": 167, "Moroccan": 172.7, "Nepalese": 163, "Nigerian": 163.8, "Norwegian": 179.63, "Omani": undefined,
		"Pakistani": 164.7, "Peruvian": 164, "Polish": 178.7, "Portuguese": 173.9, "Puerto Rican": 172.5, "Romanian": 172,
		"Russian": 177.2, "Saudi": 168.9, "Scottish": 177.6, "Serbian": 182, "Slovak": 179.4, "South African": 168,
		"Spanish": 173.1, "Sudanese": 190.0, "Swedish": 181.5, "Swiss": 178.2, "Tanzanian": undefined, "Thai": 170.3,
		"Tunisian": 172.3, "Turkish": 173.6, "Ugandan": undefined, "Ukrainian": 176.5, "Uzbek": 175.4, "Venezuelan": 169,
		"Vietnamese": 165.7, "Yemeni": 159.9, "a New Zealander": 177, "Zimbabwean": undefined, "Sammarinese": undefined,
		"Marshallese": undefined, "Syrian": 173.0, "Bermudian": undefined, "Uruguayan": 170.0, "Monégasque": undefined,
		"Montenegrin": 183.2, "Cambodian": 162.5, "Cameroonian": 170.6, "Gabonese": undefined, "Djiboutian": undefined,
		"Greenlandic": undefined, "Tuvaluan": undefined, "Zambian": undefined, "Albanian": 174.0, "Bruneian": undefined,
		"Singaporean": 170.6, "Andorran": undefined, "Croatian": 180.5, "Bulgarian": 175.2, "Laotian": 160.5,
		"Mongolian": 168.4, "Taiwanese": 171.4, "Belizean": undefined, "Grenadian": undefined, "I-Kiribati": undefined,
		"Malagasy": 161.5, "Maldivian": undefined, "Bosnian": 183.9, "Kosovan": undefined, "Macedonian": undefined,
		"Honduran": undefined, "Maltese": 169.9, "Nauruan": undefined, "Micronesian": undefined, "Costa Rican": undefined,
		"Salvadoran": undefined, "Nicaraguan": undefined, "Panamanian": 165.0, "Nigerien": 163.8, "Luxembourgian": 179.9,
		"Moldovan": undefined, ".white": 177.6, "Antiguan": 164.8, "Aruban": 165.1, "Azerbaijani": 171.8, "Bahamian": 167.2, "Bahraini": 165.1,
		"Barbadian": 169.3, "Bhutanese": 167.2, "Burundian": 164.1, "a Cook Islander": 173.4, "Cypriot": 177.2,
		"Dominiquais": 168.1, "East Timorese": 161.2, "Ecuadorian": 167.5, "Fijian": 170.4, "French Guianan": 168,
		"Georgian": 172.7, "Guyanese": 168.6, "Kuwaiti": 169.2, "Kyrgyz": 170.4, "Latvian": 174.2, "a Liechtensteiner": 175.4,
		"Niuean": 169.7, "Palauan": 174.6, "Palestinian": 169.7, "Papua New Guinean": 163.5, "Paraguayan": 168.8, "Qatari": 166.2,
		"Kittitian": 164.4, "Saint Lucian": 168.3, "Vincentian": 165.4, "Samoan": 173, "Seychellois": 168.5,
		"Slovene": 180.3, "a Solomon Islander": 163.1, "Sri Lankan": 163.6, "Surinamese": 165.2, "Tajik": 175.2, "Tongan": 168.2,
		"Trinidadian": 170.8, "Turkmen": 171.4, "Ni-Vanuatu": 160.5, "Vatican": 176.5,
		"": 172.5 // defaults
	};

	// Helper method - table lookup for nationality/race combinations
	const nationalityMeanHeight = function(table, nationality, race, def) {
		return table[nationality + "." + race] || table[nationality] || table["." + race] || table[""] || def;
	};

	// Helper method - generate two independent Gaussian numbers using Box-Muller transform
	const gaussianPair = function() {
		let r = Math.sqrt(-2.0 * Math.log(1 - Math.random()));
		let sigma = 2.0 * Math.PI * (1 - Math.random());
		return [r * Math.cos(sigma), r * Math.sin(sigma)];
	};

	// Helper method: Generate a skewed normal random variable with the skew s
	// Reference: http://azzalini.stat.unipd.it/SN/faq-r.html
	const skewedGaussian = function(s) {
		let randoms = gaussianPair();
		if(s === 0) {
			// Don't bother, return an unskewed normal distribution
			return randoms[0];
		}
		let delta = s / Math.sqrt(1 + s * s);
		let result = delta * randoms[0] + Math.sqrt(1 - delta * delta) * randoms[1];
		return randoms[0] >= 0 ? result : -result;
	};

	// Height multiplier generator; skewed gaussian according to global parameters
	const multGenerator = function() {
		let result = skewedGaussian(skew);
		while(result < minMult || result > maxMult) {
			result = skewedGaussian(skew);
		}
		return result;
	};

	// Helper method: Generate a height based on the mean one and limited according to config.
	const heightGenerator = function(mean) {
		let result = mean * (1 + multGenerator() * spread);
		while(result < minHeight || result > maxHeight) {
			result = mean * (1 + multGenerator() * spread);
		}
		return result;
	};

	// Helper method - apply age and genes to the adult height
	const applyAge = function(height, age, genes) {
		if(!_.isFinite(age) || age < 2 || age >= 20) {
			return height;
		}
		let minHeight = 0, midHeight = 0, midAge = 15;
		switch(genes) {
			case 'XX': // female
			case 'XXX': // Triple X syndrome female
				minHeight = 85; midHeight = height * 157 / 164; midAge = 13;
				break;
			case 'XY': // male
			case 'XXY': // Kinefelter syndrome male
			case 'XYY': // XYY syndrome male
				minHeight = 86; midHeight = height * 170 / 178; midAge = 15;
				break;
			case 'X0': case 'X': // Turner syndrome female
				minHeight = 85 * 0.93; midHeight = height * 157 / 164; midAge = 13;
				break;
			default:
				minHeight = 85.5, midHeight = height * 327 / 342, midAge = 14;
				break;
		}
		if(age > midAge) {
			// end of puberty to 20
			return interpolate(midAge, midHeight, 20, height, age);
		} else {
			// 2 to end of puberty
			return interpolate(2, minHeight, midAge, midHeight, age);
		}
	};

	const _meanHeight = function(nationality, race, genes, age) {
		if(_.isObject(nationality)) {
			// We got called with a single slave as the argument
			return _meanHeight(nationality.nationality, nationality.race, nationality.genes, nationality.physicalAge + nationality.birthWeek / 52.0);
		}
		let result = 0;
		switch(genes) {
			case 'XX': // female
				result = nationalityMeanHeight(xxMeanHeight, nationality, race);
				break;
			case 'XY': // male
				result = nationalityMeanHeight(xyMeanHeight, nationality, race);
				break;
			// special cases. Extra SHOX genes on X and Y chromosomes make for larger people
			case 'X0': case 'X': // Turner syndrome female
				result = nationalityMeanHeight(xxMeanHeight, nationality, race) * 0.93;
				break;
			case 'XXX': // Triple X syndrome female
				result = nationalityMeanHeight(xxMeanHeight, nationality, race) * 1.03;
				break;
			case 'XXY': // Kinefelter syndrome male
				result = nationalityMeanHeight(xyMeanHeight, nationality, race) * 1.03;
				break;
			case 'XYY': // XYY syndrome male
				result = nationalityMeanHeight(xyMeanHeight, nationality, race) * 1.04;
				break;
			case 'Y': case 'Y0': case 'YY': case 'YYY':
				console.log("Height.mean(): non-viable genes " + genes);
				break;
			default:
				console.log("Height.mean(): unknown genes " + genes + ", returning mean between XX and XY");
				result = nationalityMeanHeight(xxMeanHeight, nationality, race) * 0.5
					+ nationalityMeanHeight(xyMeanHeight, nationality, race) * 0.5;
				break;
		}
		return applyAge(result, age, genes);
	};

	const _randomHeight = function(nationality, race, genes, age) {
		const mean = _meanHeight(nationality, race, genes, age);
		// If we got called with a slave object and options, temporarily modify
		// our configuration.
		if(_.isObject(nationality) && _.isObject(race)) {
			const currentConfig = _config();
			_config(race);
			const result = heightGenerator(mean);
			_config(currentConfig);
			return result;
		}
		return heightGenerator(mean);
	};

	const _forAge = function(height, age, genes) {
		if(_.isObject(age)) {
			// We got called with a slave as a second argument
			return applyAge(height, age.physicalAge + age.birthWeek / 52.0, age.genes);
		} else {
			return applyAge(height, age, genes);
		}
	};

	return {
		mean: _meanHeight,
		random: _randomHeight,
		forAge: _forAge,
		config: _config,
	};
})();

if(!Array.prototype.findIndex) {
	Array.prototype.findIndex = function(predicate) {
		if (this == null) {
			throw new TypeError('Array.prototype.find called on null or undefined');
		}
		if (typeof predicate !== 'function') {
			throw new TypeError('predicate must be a function');
		}
		var list = Object(this);
		var length = list.length >>> 0;
		var thisArg = arguments[1];
		var value;

		for (var i = 0; i < length; i++) {
			value = list[i];
			if (predicate.call(thisArg, value, i, list)) {
				return i;
			}
		}
		return -1;
	};
};

/*
A categorizer is used to "slice" a value range into distinct categories in an efficient manner.

If the values are objects their property named 'value' will be set to whatever
the value used for the choice was. This is important for getters, where it can be accessed
via this.value.

--- Example ---
Original SugarCube code
<<if _Slave.muscles > 95>>
	Musc++
<<elseif _Slave.muscles > 30>>
	Musc+
<<elseif _Slave.muscles > 5>>
	Toned
<<elseif _Slave.muscles > -6>>
<<elseif _Slave.muscles > -31>>
	@@.red;weak@@
<<elseif _Slave.muscles > -96>>
	@@.red;weak+@@
<<else>>
	@@.red;weak++@@
<</if>>

As a categorizer
<<if ndef $cats>><<set $cats = {}>><</if>>
<<if ndef $cats.muscleCat>>
	<!-- This only gets set once, skipping much of the code evaluation, and can be set outside of the code in an "init" passage for further optimization -->
	<<set $cats.muscleCat = new Categorizer([96, 'Musc++'], [31, 'Musc+'], [6, 'Toned'], [-5, ''], [-30, '@@.red;weak@@'], [-95, '@@.red;weak+@@'], [-Infinity, '@@.red;weak++@@'])>>
<</if>>
<<print $cats.muscleCat.cat(_Slave.muscles)>>
*/
window.Categorizer = function() {
	this.cats = Array.prototype.slice.call(arguments)
		.filter(function(e, i, a) {
			return e instanceof Array && e.length == 2 && typeof e[0] === 'number' && !isNaN(e[0])
				&& a.findIndex(function(val) { return e[0] === val[0]; }) === i; /* uniqueness test */ })
		.sort(function(a, b) { return b[0] - a[0]; /* reverse sort */ });
};
window.Categorizer.prototype.cat = function(val, def) {
	var result = def;
	if(typeof val === 'number' && !isNaN(val)) {
		var foundCat = this.cats.find(function(e) { return val >= e[0]; });
		if(foundCat) {
			result = foundCat[1];
		}
	}
	// Record the value for the result's getter, if it is an object
	// and doesn't have the property yet
	if(result === Object(result)) {
		result['value'] = val;
	}
	return result;
};

window.isFloat = function(n){
    return n === +n && n !== (n|0);
}
window.isInt = function(n) {
    return n === +n && n === (n|0);
}
window.numberWithCommas = function(x) {
    return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

/*
Make everything waiting for this execute. Usage:

let doSomething = function() {
	... your initialization code goes here ...
};
if(typeof Categorizer === 'function') {
	doSomething();
} else {
	jQuery(document).one('categorizer.ready', doSomething);
}
*/
jQuery(document).trigger('categorizer.ready');


code:
:: Abort [nobr]

<<set $nextButton = "Back", $nextLink = "Slave Interact">>
<<ClearSummaryCache $activeSlave>>

The remote surgery makes aborting a pregnancy quick and efficient. $activeSlave.slaveName is
<<if $activeSlave.fetish == "pregnancy">>
	@@.red;fundamentally broken.@@ Her entire concept of self and sexuality was wrapped up in the life growing within her, and now it is gone.
	<<set  $activeSlave.fetish to "mindbroken">>
<<elseif $activeSlave.devotion < -50>>
	@@.mediumorchid;filled with violent, consuming hatred@@. Even though she knew her baby was destined for a slave orphanage, it seems she cared for it and views you as its killer. She is @@.gold;terrified of your power@@ over her body.
	<<set  $activeSlave.trust -= 10>>
	<<set  $activeSlave.devotion -= 25>>
<<elseif $activeSlave.devotion < -20>>
	@@.mediumorchid;afflicted by desperate, inconsolable sobbing@@. Even though she knew her baby was destined for a slave orphanage, it seems she cared for it. She is @@.gold;terrified of your power@@ over her body.
	<<set  $activeSlave.trust -= 10>>
	<<set  $activeSlave.devotion -= 10>>
<<elseif $activeSlave.devotion <= 20>>
	@@.mediumorchid;consumed by muted weeping and enduring sorrow@@. Even though she knew her baby was destined for a slave orphanage, it seems she cared for it. She is @@.gold;terrified of your power@@ over her body.
	<<set  $activeSlave.trust -= 10>>
	<<set  $activeSlave.devotion -= 5>>
<<elseif $activeSlave.devotion <= 50>>
	dully obedient. She has been broken to slave life so thoroughly that even this is neither surprising nor affecting. She is @@.gold;terrified of your power@@ over her body.
	<<set  $activeSlave.trust -= 10>>
<<else>>
	@@.hotpink;pleased by this stark development@@, since she is so attentive to your will. She also expects she'll be able to fuck better now.
	<<set  $activeSlave.devotion += 4>>
<</if>>

<<if lastPregRule($activeSlave,$defaultRules)>><<set $activeSlave.preg = -1>><<else>><<set $activeSlave.preg = 0>><</if>>

<<if $activeSlave.reservedChildren > 0>>
	<<set $reservedChildren -= $activeSlave.reservedChildren>>
<</if>>
<<set $activeSlave.pregType = 0>>
<<set $activeSlave.pregSource = 0>>
<<set $activeSlave.pregKnown = 0>>
<<SetBellySize $activeSlave>>