code:
{
	class ScrotalTuck extends App.Medicine.Surgery.Reaction {
		get key() { return "scrotalTuck"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, his} = getPronouns(slave);
			const r = [];

			if (slave.fetish === "mindbroken") {
				r.push(`When ${he} begins surgical recovery, ${his} groin hurts quite a lot, and ${he} can't quite tell what's going on down there. Since the surgery was invasive, ${his} health has been seriously affected.`);
			} else if (slave.devotion > 50) {
				r.push(`${He} exits the surgery gingerly, since ${he} can feel that something was done to ${his} testicles. ${He} examines them carefully`);
				if (canSee(slave)) {
					r.push(`in the mirror.`);
				} else {
					r.push(`by feeling alone.`);
				}
				r.push(`Realizing that ${his} balls fit ${his} scrotum perfectly, ${he}'s very happy you'd take an interest in ${his} balls, and looks forward to discovering how it moves during sex. ${his} health has been slightly affected.`);
				reaction.devotion += 4;
			} else if (slave.devotion > 20) {
				r.push(`${He} exits the surgery gingerly, since ${he} can feel that something was done to ${his} testicles. ${He} examines them carefully`);
				if (canSee(slave)) {
					r.push(`in the mirror.`);
				} else {
					r.push(`by feeling alone.`);
				}
				r.push(`Realizing that ${his} balls fit ${his} scrotum perfectly, ${he}'s flooded with relief. ${He}'s so elated that ${he} hasn't been severely damaged that ${he} doesn't mind your taking surgical control of ${his} genitals. ${his} health has been slightly affected.`);
			} else {
				r.push(`${He} exits the surgery gingerly, since ${he} can feel that something was done to ${his} testicles. ${He} examines them carefully`);
				if (canSee(slave)) {
					r.push(`in the mirror.`);
				} else {
					r.push(`by feeling alone.`);
				}
				r.push(`Realizing that ${his} balls fit ${his} scrotum perfectly, ${he}'s flooded with relief, since ${he} was afraid that you'd done something far more terrible. Once ${he} has time to think about it, though, ${he}'s somewhat resentful, since ${he} naturally feels a certain proprietary interest in ${his} own genitals. ${his} health has been slightly affected.`);
				reaction.devotion -= 5;
			}

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new ScrotalTuck();
}


code:
{
	class Sharp extends App.Medicine.Surgery.Reaction {
		get key() { return "sharp"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, his, him, himself} = getPronouns(slave);
			const r = [];

			if (slave.fetish === "mindbroken") {
				r.push(`${He} clearly feels quite normal except for a vague ache around ${his} jaw, yet fails to understand that ${his} teeth have changed shape. Since the surgery was invasive, ${his} health has been greatly affected.`);
			} else if (slave.devotion > 50) {
				r.push(`${He} clearly feels quite normal except for a vague ache around ${his} jaw. It takes a good while for ${him} to figure out what has happened, but eventually ${he} gets enough sensation in ${his} mouth to realize that ${he} now has teeth fit for a carnivore. ${He} seems doubtful, but then works up ${his} courage and bares them`);
				if (canSee(slave)) {
					r.push(`at a mirror.`);
				} else {
					r.push(`so you may see.`);
				}
				r.push(`${He} menaces`);
				if (canSee(slave)) {
					r.push(`${himself}`);
				}
				r.push(`for a while before laughing quietly. ${He} has become more submissive due to your radical reshaping of ${his} body. Since the surgery was invasive, ${his} health has been greatly affected.`);
				reaction.devotion += 4;
			} else if (slave.devotion > 20) {
				r.push(`${He} clearly feels quite normal except for a vague ache around ${his} jaw. It takes a good while for ${him} to figure out what has happened, but eventually ${he} gets enough sensation in ${his} mouth to realize that ${he} now has teeth fit for a carnivore. ${He} gasps with shock, but eventually ${his} shoulders slump and ${he} tries to carry on. ${He} isn't much affected mentally. Since the surgery was invasive, ${his} health has been greatly affected.`);
			} else {
				r.push(`${He} clearly feels quite normal except for a vague ache around ${his} jaw. It takes a good while for ${him} to figure out what has happened, but eventually ${he} gets enough sensation in ${his} mouth to realize that ${he} now has teeth fit for a carnivore. ${He} gasps with shock, accidentally gets ${his} tongue in the way in ${his} anguish, bites ${himself}, and starts gagging and spitting blood. The surgical invasion has filled ${him} with horror and anger. Since the surgery was invasive, ${his} health has been greatly affected. ${He} is terribly afraid of your total power over ${his} body.`);
				reaction.trust -= 10;
				reaction.devotion -= 10;
			}

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new Sharp();
}

repo_name: CloudyCoffee/fc

license: mit


code:
{
	class Sterilize extends App.Medicine.Surgery.Reaction {
		get key() { return "ster"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, his, him} = getPronouns(slave);
			const r = [];

			if (slave.fetish === "mindbroken") {
				r.push(`${He} leaves the surgery with nothing but a nonspecific ache in ${his} lower abdomen, but lacks the mental faculties to know that ${he}'ll never have a child now. As with all surgery ${his} health has been slightly affected.`);
			} else if (this._strongKnownFetish(slave, "pregnancy")) {
				r.push(`${He} leaves the surgery with nothing but a nonspecific ache in ${his} lower abdomen, but ${he} knows enough about surgery and sex slaves to know that it means ${he}'ll never have a child. ${He} is filled with grief whenever ${he} thinks about the fact that feeling a life growing within ${him} is now, finally and forever, beyond ${him}. As with all surgery ${his} health has been slightly affected. ${He} is terribly afraid of your total power over ${his} body.`);
				reaction.trust -= 10;
				reaction.devotion -= 20;
			} else if (slave.energy > 95) {
				r.push(`${He} leaves the surgery with nothing but a nonspecific ache in ${his} lower abdomen, but ${he} knows enough about surgery and sex slaves to know that it means ${he}'ll never have to worry about a pussyfuck getting ${him} pregnant again. As far as ${he}'s concerned, easier, less worrisome vaginal intercourse is a good thing; one less thing to keep in mind when a guy blows his load inside ${him}. As with all surgery ${his} health has been slightly affected.`);
				reaction.devotion += 4;
			} else if (slave.devotion > 50) {
				r.push(`${He} leaves the surgery with nothing but a nonspecific ache in ${his} lower abdomen, but ${he} knows enough about surgery and sex slaves to know that it means ${he}'ll never have to worry about a pregnancy, ever again. ${He} has many rules and duties to worry about as a slave, so ${he}'s grateful that ${he} has one less thing to worry about as ${he} serves you. As with all surgery ${his} health has been slightly affected.`);
				reaction.devotion += 4;
			} else if (slave.devotion >= -20) {
				r.push(`${He} leaves the surgery with nothing but a nonspecific ache in ${his} lower abdomen, but ${he} knows enough about surgery and sex slaves to know that it means ${he}'ll never get pregnant. ${He} understands the realities of ${his} life as a slave, and that a traditional life with a child was never going to happen for ${him} anyway, so it isn't much of a shock. As with all surgery ${his} health has been slightly affected. ${He} is sensibly fearful of your total power over ${his} body.`);
				reaction.trust -= 5;
			} else {
				r.push(`${He} leaves the surgery with nothing but a nonspecific ache in ${his} lower abdomen, but ${he} knows enough about surgery and sex slaves to know that it means ${he}'ll never get pregnant. ${He} does not understand the realities of ${his} life as a slave at a core level, so despite the fact that ${he} was never going to have a family, fertile or not, ${he}'s resentful of the surgical invasion. As with all surgery ${his} health has been slightly affected. ${He} is sensibly fearful of your total power over ${his} body.`);
				reaction.trust -= 5;
				reaction.devotion -= 5;
			}

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new Sterilize();
}


code:
{
	class TailInterface extends App.Medicine.Surgery.Reaction {
		get key() { return "tailInterface"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {his} = getPronouns(slave);
			const r = [];

			r.push(`Implanting a tail socket and interfacing it with ${his} spinal column is delicate and invasive procedure <span class="health dec">${his} health has been greatly affected.</span>`);

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new TailInterface();
}


code:
{
	class Teeth extends App.Medicine.Surgery.Reaction {
		get key() { return "teeth"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, his, him, himself} = getPronouns(slave);
			const r = [];

			if (slave.fetish === "mindbroken") {
				r.push(`${He} clearly feels quite normal except for a vague ache around ${his} jaw, yet fails to understand ${his} teeth are now fake. Since the surgery was invasive, ${his} health has been greatly affected.`);
			} else if (slave.devotion > 50) {
				r.push(`${He} clearly feels quite normal except for a vague ache around ${his} jaw. It takes a good while for ${him} to figure out what has happened, but eventually ${he} gets enough sensation in ${his} mouth to realize that ${he} is wearing prosthetic teeth that ${he} can remove. ${He} gasps with shock, but figures out the idea quickly enough by experimentally`);
				if (hasAnyArms(slave)) {
					r.push(`sucking on two fingers.`);
				} else {
					r.push(`simulating oral sex with ${his} own tongue.`);
				}
				r.push(`${He} has become more submissive due to your radical reshaping of ${his} body. Since the surgery was invasive, ${his} health has been greatly affected.`);
				reaction.devotion += 4;
			} else if (slave.devotion > 20) {
				r.push(`${He} clearly feels quite normal except for a vague ache around ${his} jaw. It takes a good while for ${him} to figure out what has happened, but eventually ${he} gets enough sensation in ${his} mouth to realize that ${he} is wearing prosthetic teeth that ${he} can remove. ${He} gasps with shock, but eventually ${his} shoulders slump and ${he} tries to carry on. ${He} isn't much affected mentally. Since the surgery was invasive, ${his} health has been greatly affected.`);
			} else {
				r.push(`${He} clearly feels quite normal except for a vague ache around ${his} jaw. It takes a good while for ${him} to figure out what has happened, but eventually ${he} gets enough sensation in ${his} mouth to realize that ${he} is wearing prosthetic teeth that ${he} can remove. ${He} gasps with shock, gags, spits out the prosthetics,`);
				if (canSee(slave)) {
					r.push(`looks at ${himself} in the mirror,`);
				} else {
					r.push(`runs ${his} tongue across ${his} empty gums,`);
				}
				r.push(`and sobs. The surgical invasion has filled ${him} with horror and anger. Since the surgery was invasive, ${his} health has been greatly affected. ${He} is terribly afraid of your total power over ${his} body.`);
				reaction.trust -= 10;
				reaction.devotion -= 10;
			}

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new Teeth();
}


code:
{
	class Blind extends App.Medicine.Surgery.Reaction {
		// unifies "elasticity treatment", "immortality treatment" and "gene treatment"
		get key() { return "gene treatment"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, his, him} = getPronouns(slave);
			const r = [];

			r.push(`The procedure spans the week, with ${him} spending every other day in the surgery room for a series of 4 sets of injections. A few hours after each session, ${he} feels terribly ill. ${He} doesn't quite understand what it's about, just that ${he} feels pretty bad. The process involves`);
			if (V.PC.skill.medicine >= 100) {
				r.push(`you`);
			} else {
				r.push(`the remote surgeon`);
			}
			r.push(`injecting the serum across ${his} entire body, every few`);
			if (V.showInches === 2) {
				r.push(`inches,`);
			} else {
				r.push(`centimeters,`);
			}
			r.push(`leaving small needle marks that fade out within minutes. Despite not leaving a lasting evidence, the process is very invasive work, and leaves ${him} feeling weak and tired.`);

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new Blind();
}


code:
{
	class Vagina extends App.Medicine.Surgery.Reaction {
		get key() { return "vagina"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, his, him} = getPronouns(slave);
			const r = [];

			if (slave.fetish === "mindbroken") {
				r.push(`${He} leaves the surgery with a terribly sore pussy and little desire to mess with it. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
			} else {
				if (slave.energy > 95) {
					r.push(`${He} leaves the surgery with a terribly sore pussy. ${He} is filled with joy at the prospect of having a tight cunt again, so much so that ${he} now trusts your plans for ${his} body. If ${he} had much in the way of vanilla sex skills, they've likely suffered. As with all surgery ${his} health has been slightly affected.`);
					reaction.trust += 4;
					reaction.devotion += 10;
				} else if (slave.devotion > 50) {
					r.push(`${He} leaves the surgery with a terribly sore pussy. ${He}'s happy that you think ${him} worth the effort of surgical improvement, and a little excited to feel like ${he}'s a girl again. If ${he} had much in the way of vanilla sex skills, they've likely suffered. As with all surgery ${his} health has been slightly affected.`);
					reaction.devotion += 4;
				} else if (slave.devotion >= -20) {
					r.push(`${He} leaves the surgery with a terribly sore pussy. ${He}'s somewhat revolted by the surgery, but as far as ${he}'s concerned, the short recovery period will mean a time during which ${he} can be sure no one will use ${his} pussy. If ${he} had much in the way of vanilla sex skills, they've likely suffered. As with all surgery ${his} health has been slightly affected. ${He} is sensibly fearful of your total power over ${his} body.`);
					reaction.trust -= 5;
				} else {
					r.push(`${He} leaves the surgery with a terribly sore pussy. ${He}'s horrified at surgical alteration of ${his} womanhood; this is probably more of an invasion than ${he} could readily imagine before today. If ${he} had much in the way of vanilla sex skills, they've likely suffered. As with all surgery ${his} health has been slightly affected. ${He} is terribly afraid of your total power over ${his} most intimate parts.`);
					reaction.trust -= 10;
					reaction.devotion -= 5;
				}
				if (slave.skill.vaginal > 10) {
					slave.skill.vaginal -= 10;
				}
			}

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new Vagina();
}


code:
{
	class VaginaRemoval extends App.Medicine.Surgery.Reaction {
		get key() { return "vaginaRemoval"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, His, his, him, himself} = getPronouns(slave);
			const r = [];

			if (slave.fetish === "mindbroken") {
				r.push(`Surprisingly, ${he} already realized while exiting that ${his} genitalia was completely reshaped. The reasons why are lost to ${him}, though.`);
			} else if (slave.dick === 0) {
				r.push(`Of course, ${he} already realized while exiting that something very dear to ${him} has been taken away.`);
				if (slave.devotion > 50 && slave.sexualFlaw === "self hating") {
					r.push(`Strong emotions play out on ${his} face as ${he} experiences a watershed in ${his} life of sexual slavery, perhaps the most radical one ${he}'ll ever experience. ${He} loves you with all ${his} being, and truly hates ${himself}. ${He} finds ${himself} in an emotional place where ${he}'s willing to accept having ${his} womanhood torn out. ${He} knows ${he}'s a worthless piece of trash, and realizes that if you feel like removing the parts of ${his} body that can feel sexual pleasure, that's your prerogative. In moments, ${he}'s confirmed in total, final, fanatical submission to your will. It's almost frightening.`);
					reaction.devotion += 50;
				} else if (slave.devotion > 20 && slave.fetishKnown === 1 && slave.fetishStrength > 95 && slave.fetish === "buttslut") {
					r.push(`${He}'s such a complete buttslut, though, that ${he} finds ${he} doesn't really care. ${He} never really paid much attention to ${his} front hole; it got wet when ${he} got buttfucked, but that was mostly useful to provide a little lube for phalli that fucked it before switching to ${his} ass. If anything, ${he}'s reassured by the implication that ${his} asshole is all that matters about ${him}, and of course is forced even further into submission to your will.`);
					reaction.devotion += 8;
					reaction.trust += 8;
				} else if (slave.devotion > 20 && slave.fetishKnown === 1 && slave.fetishStrength > 95 && slave.fetish === "cumslut") {
					r.push(`${He}'s such an oral slut, though, that ${he} finds ${he} doesn't really care. ${He} viewed ${his} pussy as a nice place for stimulation while ${he} licked, blew, and sucked; if ${he} has to learn to get off from throat stimulation ${he} will. As far as ${he}'s concerned, you've simply confirmed that ${his} most important hole is the one in ${his} face. If anything, ${he}'s reassured by the implication that ${his} real pussy has always been the one in the middle of ${his} face, and of course, ${he}'s forced even further into submission to your will.`);
					reaction.devotion += 8;
					reaction.trust += 8;
				} else if (slave.devotion > 20 && slave.fetishKnown === 1 && slave.fetishStrength > 95 && slave.fetish === "submissive") {
					r.push(`${He}'s such a total submissive, though, that ${he} accepts even this. Having ${his} womanhood torn out, greatly curtailing ${his} physical ability to enjoy sex, is perhaps the final step in sexual slavery, and ${he}'s a little awed by it. If anything, ${he}'s reassured by the implicit promise that ${his} body exists for the degrading sexual gratification of others, and of course, ${he}'s forced even further into submission to your will.`);
					reaction.devotion += 8;
					reaction.trust += 8;
				} else if (slave.devotion > 95) {
					r.push(`Doubt, fear, and even nausea play across ${his} face, but ${he}'s so worshipful of you that ${he} masters them all, accepting that this is your will. There's surprisingly little mental effect on ${him}. The horror of having ${his} womanhood torn out is so strong that it breaks through to ${him}, and the only way ${he} can find to deal with it is to think about it as little as possible. ${He} falls back on worshipful duty.`);
				} else if (slave.devotion > 50) {
					r.push(`${He}'s devoted to you, but doubt and fear play across ${his} face as ${he} comes to terms with what's happened. ${He} does not see how having ${his} womanhood torn out will improve ${his} sexual slavery. ${He} does not rage or scream, but ${he} does cry, sadly wondering whether ${he}'s valuable to you at all, or if ${he}'s just a surgical plaything to be degraded. ${He}'s very frightened that you'll cut up more of ${him}.`);
					reaction.trust -= 10;
					reaction.devotion -= 10;
				} else if (slave.devotion > 20) {
					r.push(`${He}'s properly broken to slavery, but this is much too far. Having ${his} womanhood torn out cuts straight through ${his} conditioning, and tears roll down ${his} face as the horrible permanence of what's happened to ${him} really sinks in. Open anger begins to become apparent through ${his} tears; ${he} thought that ${his} obedience merited better than this, and ${he}'s extremely terrified to find that despite being a decent slave, ${he} remains a surgical playground.`);
					reaction.trust -= 20;
					reaction.devotion -= 20;
				} else {
					r.push(`${He}'s horrified almost beyond the ability of ${his} mind to comprehend. ${His} face takes on a blank look as ${he} goes into shock. There's no screaming and no tears, not yet; it will take ${him} time to process what's happened to ${him}. Without a doubt, ${he}'ll be absolutely filled by rage and terror.`);
					reaction.trust -= 40;
					reaction.devotion -= 40;
				}
			} else {
				r.push(`Of course, ${he} already realized while exiting that ${his} genitalia has been radically simplified. As a herm slave ${he} knew this day might come,`);
				if (slave.devotion > 50) {
					r.push(`and ${his} face is a strange mix of hope, happiness, satisfaction, and the tiniest tinge of soul-crushing sadness as ${he}`);
					if (canSee(slave)) {
						r.push(`views`);
					} else {
						r.push(`feels`);
					}
					r.push(`the cock that is all that remains of ${his} once-unique hermaphroditic genitalia. ${He} has become more submissive due to your radical reshaping of ${his} body. Since the surgery was invasive, ${his} health has been greatly affected.`);
					reaction.devotion += 4;
				} else if (slave.devotion > 20) {
					r.push(`and ${his} face is a strange mix of fear, hope, and resignation as ${he}`);
					if (canSee(slave)) {
						r.push(`views`);
					} else {
						r.push(`feels`);
					}
					r.push(`the cock that is all that remains of ${his} once-unique hermaphroditic genitalia. Eventually ${his} shoulders slump and ${he} tries to carry on. ${He} will struggle with feelings of confusion and loss. Since the surgery was invasive, ${his} health has been greatly affected. ${He} is terribly afraid of your total power over ${his} body.`);
					reaction.trust -= 10;
					reaction.devotion -= 5;
				} else {
					r.push(`but expectations and the reality are two different things. ${He}'s beside ${himself}, weeping gently.`);
					if (hasAnyArms(slave)) {
						r.push(`${He} absently moves a hand to where ${his} pussy would have been and snatches it away with a sob.`);
					}
					r.push(`<span class="devotion dec">The surgical invasion has filled ${him} with horror and anger.</span> ${He} is <span class="trust dec">cripplingly afraid</span> of your total power over ${his} body.`);
					reaction.trust -= 20;
					reaction.devotion -= 20;
				}
			}
			r.push(`Naturally, <span class="health dec">${his} health has been greatly affected</span> by such serious surgery.`);

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new VaginaRemoval();
}


code:
{
	class Vasectomy extends App.Medicine.Surgery.Reaction {
		get key() { return "vasectomy"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, His, his} = getPronouns(slave);
			const r = [];

			r.push(`${His} groin is a little sore, and ${he} examines it closely, but ${he} can't find much difference. ${He} likely won't realize what happened${(slave.ballType === "sterile") ? ` given that ${he} couldn't get girls pregnant in the first place` : `, but may piece things together when ${he} realizes the girls ${he} fucks never get pregnant`}. As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new Vasectomy();
}


code:
{
	class VasectomyUndo extends App.Medicine.Surgery.Reaction {
		get key() { return "vasectomy undo"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, His, his} = getPronouns(slave);
			const r = [];

			r.push(`${His} groin is a little sore, and ${he} examines it closely, but ${he} can't find much difference. ${He} likely won't ever realize what happened`);
			if (slave.ballType === "sterile") {
				r.push(`since ${his} balls don't work in the first place.`);
			} else {
				r.push(`until ${he} gets a girl pregnant.`);
			}
			r.push(`As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new VasectomyUndo();
}


code:
{
	class VoiceLower extends App.Medicine.Surgery.Reaction {
		get key() { return "voice2"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, his, him, himself, hers} = getPronouns(slave);
			const r = [];

			if (slave.fetish === "mindbroken") {
				r.push(`The autosurgery instructed ${him} in no uncertain terms not to speak during recovery, but ${he} fails to recall that. ${He} doesn't notice anything different. As with all surgery ${his} health has been slightly affected.`);
			} else if (slave.devotion > 50) {
				r.push(`Before surgery, ${he} was warned repeatedly not to try talking for a while, and ${he} obeys. When ${he} finally does, ${his} voice is raspy and weak, but it gains strength gradually. It comes out`);
				if (slave.voice === 1) {
					r.push(`far`);
				}
				r.push(`lower and more manly than it was before, and ${he} laughs at ${himself} as ${he} gets used to it. ${He} has become more dominant, since this allows ${him} to think of ${himself} as`);
				if (slave.voice === 1) {
					r.push(`an action movie hero.`);
				} else if (slave.voice === 2) {
					r.push(`a mature career woman.`);
				}
				r.push(`As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
				reaction.devotion += 4;
			} else if (slave.devotion > 20) {
				r.push(`Before surgery, ${he} was warned repeatedly not to try talking for a while, and ${he} obeys. When ${he} finally does, ${his} voice is raspy and weak, but it gains strength gradually. It comes out`);
				if (slave.voice === 1) {
					r.push(`far`);
				}
				r.push(`lower and more manly than it was before, and ${he} laughs grimly at ${himself} as ${he} gets used to it. ${He} carries on regardless, accepting it as one more thing to learn to accept. As with all surgery ${his} health has been slightly affected.`);
			} else {
				r.push(`Before surgery, ${he} was warned repeatedly not to try talking for a while, and ${he} obeys. When ${he} finally does, ${his} voice is raspy and weak, but it gains strength gradually. It comes out`);
				if (slave.voice === 1) {
					r.push(`far`);
				}
				r.push(`lower and more manly than it was before. For now, <span class="devotion dec">${he} feels this`);
				if (slave.voice === 1) {
					r.push(`gravelly`);
				} else if (slave.voice === 2) {
					r.push(`subdued`);
				}
				r.push(`voice is not ${hers}, a cruel mockery. As with all surgery ${his} health has been slightly affected. ${He} is now terribly afraid of your total power over ${his} body.`);
				reaction.trust -= 10;
				reaction.devotion -= 5;
			}

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new VoiceLower();
}


code:
{
	class VoiceRaise extends App.Medicine.Surgery.Reaction {
		get key() { return "voice"; }

		reaction(slave, diff) {
			const reaction = super.reaction(slave, diff);
			const {He, he, his, him, himself, hers} = getPronouns(slave);
			const r = [];

			if (slave.fetish === "mindbroken") {
				r.push(`The autosurgery instructed ${him} in no uncertain terms not to speak during recovery, but ${he} fails to recall that. ${He} doesn't notice anything different. As with all surgery ${his} health has been slightly affected.`);
			} else if (slave.devotion > 50) {
				r.push(`Before surgery, ${he} was warned repeatedly not to try talking for a while, and ${he} obeys. When ${he} finally does, ${his} voice is raspy and weak, but it gains strength gradually. It comes out`);
				if (slave.voice === 3) {
					r.push(`far`);
				}
				r.push(`higher and more girly than it was before, and ${he} laughs at ${himself} as ${he} gets used to it. ${He} has become more submissive, since this helps ${him} to think of ${himself} as a`);
				if (slave.voice === 3) {
					r.push(`bimbo slut.`);
				} else {
					r.push(`real woman.`);
				}
				r.push(`As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);
				reaction.devotion += 4;
			} else if (slave.devotion > 20) {
				r.push(`Before surgery, ${he} was warned repeatedly not to try talking for a while, and ${he} obeys. When ${he} finally does, ${his} voice is raspy and weak, but it gains strength gradually. It comes out`);
				if (slave.voice === 3) {
					r.push(`far`);
				}
				r.push(`higher and more girly than it was before, and ${he} laughs grimly at ${himself} as ${he} gets used to it. ${He} carries on regardless, accepting it as one more thing to learn to accept. As with all surgery ${his} health has been slightly affected.`);
			} else {
				r.push(`Before surgery, ${he} was warned repeatedly not to try talking for a while, and ${he} obeys. When ${he} finally does, ${his} voice is raspy and weak, but it gains strength gradually. It comes out`);
				if (slave.voice === 3) {
					r.push(`far`);
				}
				r.push(`higher and more girly than it was before. For now, <span class="devotion dec">${he} feels this`);
				if (slave.voice === 3) {
					r.push(`bimbo`);
				} else {
					r.push(`feminine`);
				}
				r.push(`voice is not ${hers}, a cruel mockery. As with all surgery ${his} health has been slightly affected. ${He} is now terribly afraid of your total power over ${his} body.`);
				reaction.trust -= 10;
				reaction.devotion -= 5;
			}

			reaction.longReaction.push(r);
			return reaction;
		}
	}

	new VoiceRaise();
}


code:
App.Medicine.Surgery.Reactions.ShortenTendons = class extends App.Medicine.Surgery.SimpleReaction {
	reaction(slave, diff) {
		const reaction = super.reaction(slave, diff);
		const {He, he, His, his, him} = getPronouns(slave);
		const r = [];

		if (slave.fetish === "mindbroken") {
			r.push(`${He} exits the surgery on all fours, unable to stand due to the changes to ${his} legs. A whorish pair of heels are slipped onto ${him} and ${he} is forced to ${his} feet to show that ${he} can indeed walk comfortably while wearing them. As with all surgery ${his} health has been slightly affected.`);
		} else if (slave.devotion > 50) {
			r.push(`${He} exits the surgery on all fours, unable to stand due to the changes to ${his} legs. ${He} finds a whorish pair of heels waiting for ${him} and eagerly puts them on, gingerly standing to find that ${he} can indeed walk comfortably while wearing them. ${He} struts back and forth experimentally and then offers`);
			if (canSee(slave)) {
				r.push(`the mirror`);
			} else {
				r.push(`you`);
			}
			r.push(`${his} asshole to check to see that ${his} ability to take a standing buttfuck hasn't been affected. ${He} has become more submissive due to your radical reshaping of ${his} body. As with all surgery ${his} health has been slightly affected.`);
			reaction.devotion += 4;
		} else if (slave.devotion > 20) {
			r.push(`${He} exits the surgery on all fours, unable to stand due to the changes to ${his} legs. ${He} finds a whorish pair of heels waiting for ${him} and obediently puts them on, gingerly standing to find that ${he} can indeed walk comfortably while wearing them. After a while ${he} realizes that ${he}'ll never walk without looking like a stripper again, but ${he} accepts it. ${He} isn't much affected mentally. As with all surgery ${his} health has been slightly affected.`);
		} else {
			r.push(`${He} exits the surgery on all fours, unable to stand due to the changes to ${his} legs. ${He} finds a whorish pair of heels waiting for ${him}, but obstinately ignores them. After some time, ${he} realizes that the situation isn't changing and puts them on, gingerly standing to find that ${he} can indeed walk comfortably while wearing them. ${His}`);
			if (canSee(slave)) {
				r.push(`eyes are`);
			} else {
				r.push(`face is`);
			}
			r.push(`filled with hatred when ${he}`);
			if (canSee(slave)) {
				r.push(`sees ${his} new and permanent appearance in the mirror.`);
			} else {
				r.push(`discovers this is the only way ${he}'ll be able to stand now.`);
			}
			r.push(`The surgical invasion has filled ${him} with horror and anger. As with all surgery ${his} health has been slightly affected. ${He} is terribly afraid of your total power over ${his} body.`);
			reaction.trust -= 10;
			reaction.devotion -= 10;
		}

		reaction.longReaction.push(r);
		return reaction;
	}
};

App.Medicine.Surgery.Reactions.ReplaceTendons = class extends App.Medicine.Surgery.SimpleReaction {
	reaction(slave, diff) {
		const reaction = super.reaction(slave, diff);
		const {He, he, his, him} = getPronouns(slave);
		const r = [];

		if (slave.fetish === "mindbroken") {
			r.push(`${He} exits the surgery on ${his} own two feet. ${He}'s still sore, but the modern surgery is fast and effective, and ${he} can use ${his} restored legs immediately. ${He} gingerly stands on one foot, then the other; and even takes a few little hops. ${He} obviously confused, but will get used to it soon enough. As with all surgery ${his} health has been slightly affected.`);
		} else if (slave.devotion >= -20) {
			r.push(`${He} exits the surgery on ${his} own two feet. ${He}'s still sore, but the modern surgery is fast and effective, and ${he} can use ${his} restored legs immediately. ${He} gingerly stands on one foot, then the other; and even takes a few little hops. ${He}'s grinning like a fool the whole time. ${He} has become more obedient due to gratitude. As with all surgery ${his} health has been slightly affected.`);
			reaction.devotion += 4;
		} else {
			r.push(`${He} exits the surgery on ${his} own two feet. ${He}'s still sore, but the modern surgery is fast and effective, and ${he} can use ${his} restored legs immediately. ${He} gingerly stands on one foot, then the other; and even takes a few little hops. ${He} obviously confused, finding it surprising that you would go to the expense and trouble of repairing ${him}. As with all surgery ${his} health has been slightly affected.`);
		}

		reaction.longReaction.push(r);
		return reaction;
	}
};

App.Medicine.Surgery.Procedures.ShortenTendons = class extends App.Medicine.Surgery.Procedure {
	get name() { return "Shorten tendons"; }

	get description() {
		const {him} = getPronouns(this._slave);
		return `Prevents ${him} from walking in anything but very high heels`;
	}

	get healthCost() { return 20; }

	apply(cheat) {
		this._slave.heels = 1;
		this._slave.shoes = "heels";
		return this._assemble(new App.Medicine.Surgery.Reactions.ShortenTendons());
	}
};

App.Medicine.Surgery.Procedures.ReplaceTendons = class extends App.Medicine.Surgery.Procedure {
	get name() { return "Replace tendons"; }

	get healthCost() { return 10; }

	apply(cheat) {
		this._slave.heels = 0;
		this._slave.shoes = "none";
		return this._assemble(new App.Medicine.Surgery.Reactions.ReplaceTendons());
	}
};


code:
App.Medicine.Surgery.Reactions.Height = class extends App.Medicine.Surgery.SimpleReaction {
	reaction(slave, diff) {
		const reaction = super.reaction(slave, diff);
		const {He, he, his, him} = getPronouns(slave);
		const r = [];

		if (slave.fetish === "mindbroken") {
			r.push(`${He} exits the surgery very slowly, since ${he}'s terribly sore and isn't yet used to the novel lengths of the legs ${he} walks on. ${He}'ll be clumsy for some time. Since the surgery was invasive, ${his} health has been greatly affected.`);
		} else if (slave.devotion > 50) {
			r.push(`${He} exits the surgery very cautiously, since ${he}'s terribly sore and isn't yet used to the novel lengths of the legs ${he} walks on. ${He}'ll be clumsy and hesitant for some time, but ${he}'s happy with ${his} new body. ${He} has become more submissive due to your radical reshaping of ${his} body. Since the surgery was invasive, ${his} health has been greatly affected.`);
			reaction.devotion += 4;
		} else if (slave.devotion > 20) {
			r.push(`${He} exits the surgery very cautiously, since ${he}'s terribly sore and isn't yet used to the novel lengths of the legs ${he} walks on. ${He}'ll be clumsy and hesitant for some time, but ${he}'s accepted ${his} new body. ${He} isn't much affected mentally. Since the surgery was invasive, ${his} health has been greatly affected.`);
		} else {
			r.push(`${He} exits the surgery very cautiously, since ${he}'s terribly sore and isn't yet used to the novel lengths of the legs ${he} walks on. ${He}'ll be clumsy and hesitant for some time, and ${he} refuses to accept that this new body is really what ${he}'s been condemned to. The surgical invasion has filled ${him} with horror and anger. Since the surgery was invasive, ${his} health has been greatly affected. ${He} is terribly afraid of your total power over ${his} body.`);
			reaction.trust -= 10;
			reaction.devotion -= 10;
		}

		reaction.longReaction.push(r);
		return reaction;
	}
};

App.Medicine.Surgery.Procedures.IncreaseHeight = class extends App.Medicine.Surgery.Procedure {
	get name() {
		if (this._slave.heightImplant === 0) {
			return "Lengthen major bones";
		} else if (this._slave.heightImplant >= 1) {
			return "Advanced height gain surgery";
		} else if (this._slave.heightImplant === -1) {
			return "Reverse existing height surgery";
		} else {
			return "Revert a stage of existing height surgery";
		}
	}

	get healthCost() { return 40; }

	apply(cheat) {
		this._slave.heightImplant += 1;
		this._slave.height += 10;
		return this._assemble(new App.Medicine.Surgery.Reactions.ShortenTendons());
	}
};

App.Medicine.Surgery.Procedures.DecreaseHeight = class extends App.Medicine.Surgery.Procedure {
	get name() {
		if (this._slave.heightImplant === 0) {
			return "Shorten major bones";
		} else if (this._slave.heightImplant <= -1) {
			return "Advanced height reduction surgery";
		} else if (this._slave.heightImplant === 1) {
			return "Reverse existing height surgery";
		} else {
			return "Revert a stage of existing height surgery";
		}
	}

	get healthCost() { return 40; }

	apply(cheat) {
		this._slave.heightImplant -= 1;
		this._slave.height -= 10;
		return this._assemble(new App.Medicine.Surgery.Reactions.ShortenTendons());
	}
};


code:
App.Medicine.Surgery.Reactions.Hips = class extends App.Medicine.Surgery.SimpleReaction {
	reaction(slave, diff) {
		const reaction = super.reaction(slave, diff);
		const {He, he, his, him} = getPronouns(slave);
		const r = [];

		if (slave.fetish === "mindbroken") {
			r.push(`${He} exits the surgery very slowly, since ${he}'s still in considerable pain and isn't yet used to the way ${his} pelvis now fits together with the rest of ${his} body. Since the surgery was invasive, ${his} health has been greatly affected.`);
		} else if (slave.devotion > 50) {
			r.push(`${He} exits the surgery very cautiously, since ${he}'s still in considerable pain and isn't yet used to the way ${his} pelvis now fits together with the rest of ${his} body. ${He}'ll be clumsy and hesitant for some time, but ${he}'s happy with ${his} new hips. ${He} has become more submissive due to your radical reshaping of ${his} body. Since the surgery was invasive, ${his} health has been greatly affected.`);
			reaction.devotion += 4;
		} else if (slave.devotion > 20) {
			r.push(`${He} exits the surgery very cautiously, since ${he}'s still in considerable pain and isn't yet used to the way ${his} pelvis now fits together with the rest of ${his} body. ${He}'ll be clumsy and hesitant for some time, but ${he}'s accepted ${his} new hips. ${He} isn't much affected mentally. Since the surgery was invasive, ${his} health has been greatly affected.`);
		} else {
			r.push(`${He} exits the surgery very cautiously, since ${he}'s still in considerable pain and isn't yet used to the way ${his} pelvis now fits together with the rest of ${his} body. ${He}'ll be clumsy and hesitant for some time, and ${he} refuses to accept that you are both willing and able to reshape ${him} down to ${his} very bones. The surgical invasion has filled ${him} with horror and anger. Since the surgery was invasive, ${his} health has been greatly affected. ${He} is terribly afraid of your total power over ${his} body.`);
			reaction.trust -= 10;
			reaction.devotion -= 10;
		}

		reaction.longReaction.push(r);
		return reaction;
	}
};

App.Medicine.Surgery.Procedures.BroadenPelvis = class extends App.Medicine.Surgery.Procedure {
	get name() { return this._slave.shouldersImplant === 0 ? "Broaden pelvis" : "Advanced pelvis broadening"; }

	get healthCost() { return 40; }

	apply(cheat) {
		this._slave.hipsImplant++;
		this._slave.hips++;
		return this._assemble(new App.Medicine.Surgery.Reactions.Hips());
	}
};

App.Medicine.Surgery.Procedures.NarrowPelvis = class extends App.Medicine.Surgery.Procedure {
	get name() { return this._slave.shouldersImplant === 0 ? "Narrow pelvis" : "Advanced pelvis narrowing"; }

	get healthCost() { return 40; }

	apply(cheat) {
		this._slave.hipsImplant--;
		this._slave.hips--;
		return this._assemble(new App.Medicine.Surgery.Reactions.Hips());
	}
};


code:
App.Medicine.Surgery.Reactions.Shoulders = class extends App.Medicine.Surgery.SimpleReaction {
	reaction(slave, diff) {
		const reaction = super.reaction(slave, diff);
		const {He, he, his, him} = getPronouns(slave);
		const r = [];

		if (slave.fetish === "mindbroken") {
			r.push(`${He} exits the surgery gingerly, since the changes of proportion in ${his} upper body have seriously affected ${his} sense of balance. Since the surgery was invasive, ${his} health has been greatly affected.`);
		} else if (slave.devotion > 50) {
			r.push(`${He} exits the surgery gingerly, since the changes of proportion in ${his} upper body have seriously affected ${his} sense of balance, albeit temporarily. ${He}'ll be clumsy and hesitant for some time, but ${he}'s happy with ${his} new shoulders. ${He} has become more submissive due to your radical reshaping of ${his} body. Since the surgery was invasive, ${his} health has been greatly affected.`);
			reaction.devotion += 4;
		} else if (slave.devotion > 20) {
			r.push(`${He} exits the surgery gingerly, since the changes of proportion in ${his} upper body have seriously affected ${his} sense of balance, albeit temporarily. ${He}'ll be clumsy and hesitant for some time, but ${he}'s accepted ${his} new shoulders. ${He} isn't much affected mentally. Since the surgery was invasive, ${his} health has been greatly affected.`);
		} else {
			r.push(`${He} exits the surgery gingerly, since the changes of proportion in ${his} upper body have seriously affected ${his} sense of balance, albeit temporarily. ${He}'ll be clumsy and hesitant for some time, and ${he} refuses to accept that you are both willing and able to reshape ${him} down to ${his} very bones. The surgical invasion has filled ${him} with horror and anger. Since the surgery was invasive, ${his} health has been greatly affected. ${He} is terribly afraid of your total power over ${his} body.`);
			reaction.trust -= 10;
			reaction.devotion -= 10;
		}

		reaction.longReaction.push(r);
		return reaction;
	}
};

App.Medicine.Surgery.Procedures.BroadenShoulders = class extends App.Medicine.Surgery.Procedure {
	get name() { return this._slave.shouldersImplant === 0 ? "Restructure shoulders more broadly" : "Advanced shoulder broadening surgery"; }

	get healthCost() { return 40; }

	apply(cheat) {
		this._slave.shouldersImplant++;
		this._slave.shoulders++;
		return this._assemble(new App.Medicine.Surgery.Reactions.Shoulders());
	}
};

App.Medicine.Surgery.Procedures.NarrowShoulders = class extends App.Medicine.Surgery.Procedure {
	get name() { return this._slave.shouldersImplant === 0 ? "Restructure shoulders more narrowly" : "Advanced shoulder narrowing surgery"; }

	get healthCost() { return 40; }

	apply(cheat) {
		this._slave.shouldersImplant--;
		this._slave.shoulders--;
		return this._assemble(new App.Medicine.Surgery.Reactions.Shoulders());
	}
};


code:
App.Medicine.Surgery.Reactions.NewVoice = class extends App.Medicine.Surgery.SimpleReaction {
	reaction(slave, diff) {
		const reaction = super.reaction(slave, diff);
		const {He, he, his, him} = getPronouns(slave);
		const r = [];

		if (slave.fetish === "mindbroken") {
			r.push(`The autosurgery instructed ${him} in no uncertain terms not to speak during recovery, but ${he} fails to recall that. ${He} doesn't notice anything different, though.`);
		} else {
			r.push(`Before surgery, ${he} was warned repeatedly not to try talking for a while, and ${he} obeys. When ${he} finally does, ${his} voice is raspy and weak, but as it gradually gains strength ${he} notices that it sounds more natural and human than ${his} old electrolarynx.`);
		}
		r.push(`As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);

		reaction.longReaction.push(r);
		return reaction;
	}
};

App.Medicine.Surgery.Procedures.OFNewVoice = class extends App.Medicine.Surgery.Procedure {
	get name() {
		return "Replace";
	}

	get description() {
		return "remove electrolarynx and implant organic vocal cords";
	}

	get healthCost() {
		return 20;
	}

	apply(cheat) {
		this._slave.electrolarynx = 0;
		if (this._slave.ovaries === 1 && this._slave.hormoneBalance >= 200) {
			this._slave.voice = 3;
		} else if (this._slave.balls > 0 || this._slave.hormoneBalance < -20) {
			this._slave.voice = 1;
		} else {
			this._slave.voice = 2;
		}

		return this._assemble(new App.Medicine.Surgery.Reactions.NewVoice());
	}
};


code:
App.Medicine.Surgery.Reactions.RestoreVoice = class extends App.Medicine.Surgery.SimpleReaction {
	reaction(slave, diff) {
		const reaction = super.reaction(slave, diff);
		const {He, he, his, him} = getPronouns(slave);
		const r = [];

		if (slave.fetish === "mindbroken") {
			r.push(`The autosurgery instructed ${him} in no uncertain terms not to speak during recovery, but ${he} fails to recall that. ${He} doesn't any effort to speak either, though.`);
		} else if (slave.devotion > 50) {
			r.push(`The autosurgery instructed ${him} in no uncertain terms not to speak during recovery, so ${he} knows that ${his} voice has been restored. When you come in to check on ${him} late in the process, though, ${he} does ${his} best to communicate adoration with ${his}`);
			if (canSee(slave)) {
				r.push(`eyes`);
			} else {
				r.push(`face`);
			}
			r.push(`alone. Finally, the accelerated healing process reaches the time when ${he}'s to test ${his} new vocal organs. ${He}`);
			if (canSee(slave)) {
				r.push(`looks at`);
			} else {
				r.push(`turns to`);
			}
			r.push(`you shyly, and`);
			if (hasAnyArms(slave)) {
				r.push(`meekly beckons to you from where ${he}'s still`);
			} else {
				r.push(`meekly mouths a request that you come over to where ${he}'s`);
			}
			r.push(`restrained by the autosurgery. When you're near, ${he} clears ${his} sore throat gingerly and then whispers, "I love you, ${getWrittenTitle(slave)}." ${He} is far more trusting of your plans for ${him} in the future.`);

			reaction.trust += 5;
			reaction.devotion += 5;
		} else if (slave.devotion >= -20) {
			r.push(`The autosurgery instructed ${him} in no uncertain terms not to speak during recovery, so ${he} knows that ${his} voice has been restored. When you come in to check on ${him} late in the process, though, ${he} does ${his} best to communicate gratitude with ${his}`);
			if (canSee(slave)) {
				r.push(`eyes`);
			} else {
				r.push(`face`);
			}
			r.push(`alone. Finally, the accelerated healing process reaches the time when ${he}'s to test ${his} new vocal organs. ${He}`);
			if (canSee(slave)) {
				r.push(`looks at`);
			} else {
				r.push(`turns to`);
			}
			r.push(`you shyly, and hesitantly`);
			if (hasAnyArms(slave)) {
				r.push(`beckons to you from where ${he}'s still`);
			} else {
				r.push(`mouths a request that you come over to where ${he}'s`);
			}
			r.push(`restrained by the autosurgery. When you're near, ${he} clears ${his} sore throat gingerly and then whispers, "Thank you, ${getWrittenTitle(slave)}." ${He} has a much higher regard for you due to this display of care for ${him}.`);
			reaction.devotion += 10;
		} else {
			r.push(`The autosurgery instructed ${him} in no uncertain terms not to speak during recovery, so ${he} knows that ${his} voice has been restored. When you come in to check on ${him} late in the process, though, ${he}`);
			if (canSee(slave)) {
				r.push(`stares at`);
			} else {
				r.push(`faces`);
			}
			r.push(`you for a long moment before turning away from you, towards the wall, and beginning to cry quietly. It seems ${he}'s surprised that you would do this for ${him}, and does not understand how to deal with the situation. In any case, ${he} hates you far less than ${he} once did.`);
			reaction.devotion += 10;
		}
		r.push(`As with all surgery <span class="health dec">${his} health has been slightly affected.</span>`);

		reaction.longReaction.push(r);
		return reaction;
	}
};

App.Medicine.Surgery.Procedures.OFRestoreVoice = class extends App.Medicine.Surgery.Procedure {
	get name() {
		return "Implant";
	}

	get healthCost() {
		return 10;
	}

	apply(cheat) {
		if (this._slave.ovaries === 1 && this._slave.hormoneBalance >= 200) {
			this._slave.voice = 3;
		} else if (this._slave.balls > 0 || this._slave.hormoneBalance < -20) {
			this._slave.voice = 1;
		} else {
			this._slave.voice = 2;
		}
		return this._assemble(new App.Medicine.Surgery.Reactions.RestoreVoice());
	}
};


code:
App.Medicine.OrganFarm.Organ = class {
	/**
	 * @param {Object} params
	 * @param {string} params.name - display name
	 * @param {string|function(App.Entity.SlaveState):string} [params.tooltip] - full sentence, uncapitalized and unpunctuated
	 * @param {number} params.cost - how much it costs to grow the organ
	 * @param {number} params.time - how long it takes to grow the organ (without upgrades)
	 * @param {function(App.Entity.SlaveState):boolean} [params.canGrow]
	 * @param {string[]} [params.dependencies] - organs that are implanted first if possible, use type of other organs as values
	 * @param {App.Medicine.OrganFarm.OrganImplantAction[]} [params.actions]
	 * @param {boolean} [params.displayMultipleActions=false] allow multiple implant links to be displayed
	 */
	constructor({
		name, tooltip = "", cost, time, canGrow = () => true, dependencies = [],
		displayMultipleActions = false, actions = []
	}) {
		this.name = name;
		this.tooltip = tooltip;
		this.cost = cost;
		this.time = time;
		/** @type {function(App.Entity.SlaveState):boolean} */
		this.canGrow = canGrow;
		/** @type {string[]} */
		this.dependencies = dependencies;
		this.displayMultipleActions = displayMultipleActions;
		/** @type {App.Medicine.OrganFarm.OrganImplantAction[]} */
		this.implantActions = actions;
	}
};

App.Medicine.OrganFarm.OrganImplantAction = class {
	/**
	 * @param {object} params
	 * @param {string} params.name - display name, shown when showing error (otherwise shows procedure name)
	 * @param {typeof App.Medicine.Surgery.Procedure} params.surgeryProcedure - surgery reaction to show
	 * @param {Array} [params.surgeryProcedureParams] - parameters for surgeryProcedure, the slave argument will be added to the front automatically
	 * @param {boolean} [params.autoImplant]
	 * @param {function(App.Entity.SlaveState):boolean} params.canImplant
	 * @param {function(App.Entity.SlaveState):string} params.implantError - message to show if this action cannot be used
	 */
	constructor({
		name,
		surgeryProcedure,
		surgeryProcedureParams = [],
		autoImplant = true,
		canImplant,
		implantError
	}) {
		this.name = name;
		this.surgeryProcedure = surgeryProcedure;
		this.surgeryProcedureParams = surgeryProcedureParams;
		this.autoImplant = autoImplant;
		/**
		 * True if this action can implant the organ
		 * @type {function(App.Entity.SlaveState):boolean}
		 */
		this.canImplant = canImplant;
		/**
		 * Error message if the organ cannot be implanted.
		 * @type {function(App.Entity.SlaveState):string}
		 */
		this.implantError = implantError;
	}
};


code:
App.Medicine.OrganFarm.TesticlesImplantAction = class extends App.Medicine.OrganFarm.OrganImplantAction {
	/**
	 * @param {object} params
	 * @param {string} params.name
	 * @param {boolean} params.animal
	 * @param {typeof App.Medicine.Surgery.Procedure} params.surgeryProcedure
	 * @param {Array} [params.surgeryProcedureParams]
	 * @param {boolean} [params.autoImplant]
	 * @param {function(App.Entity.SlaveState):boolean} params.canImplant
	 * @param {function(App.Entity.SlaveState):string} params.implantError
	 */
	constructor({
		name,
		animal,
		surgeryProcedure,
		surgeryProcedureParams,
		autoImplant = true,
		canImplant,
		implantError,
	}) {
		super({
			name: name,
			surgeryProcedure: surgeryProcedure,
			surgeryProcedureParams: surgeryProcedureParams,
			autoImplant: autoImplant,
			canImplant: s => (!this.animal || V.animalTesticles > 0) && canImplant(s),
			implantError: s => (this.animal && V.animalTesticles === 0) ? "" : implantError(s),
		});
		this.animal = animal;
	}
};

App.Medicine.OrganFarm.Testicles = class extends App.Medicine.OrganFarm.Organ {
	/**
	 * @param {Object} params
	 * @param {string} params.name
	 * @param {FC.AnimalType} params.ballType
	 */
	constructor({name, ballType}) {
		super({
			name: name,
			tooltip: "will add a prostate if one is not already present; requires a penis for successful implantation",
			cost: 5000, time: 10,
			canGrow: () => (this.ballType === "human" || V.animalTesticles > 0), dependencies: ["penis"],
			actions: [
				new App.Medicine.OrganFarm.TesticlesImplantAction({
					name: "Implant", animal: ballType !== "human",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFAddBalls,
					surgeryProcedureParams: [ballType],
					canImplant: slave => (slave.balls <= 0 && slave.dick > 0),
					implantError: slave => {
						if (slave.dick === 0) {
							return "This slave lacks the penis necessary to accept testicles.";
						} else {
							return "This slave already has testicles.";
						}
					},
				}),
				new App.Medicine.OrganFarm.TesticlesImplantAction({
					name: "Implant",
					animal: ballType !== "human", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFAddTesticles,
					surgeryProcedureParams: [ballType],
					canImplant: slave => (slave.dick === 0 && slave.balls <= 0),
					implantError: slave => ((slave.balls > 0) ? "This slave already has testicles." : ""),
				}),
				new App.Medicine.OrganFarm.TesticlesImplantAction({
					name: "Replace",
					animal: ballType !== "human", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFReplaceTesticles,
					surgeryProcedureParams: [ballType],
					canImplant: slave => (slave.balls > 0 && slave.ballType !== this.ballType),
					implantError: slave => (slave.balls > 0 ? `This slave already has ${this.ballType} testicles.` : ""),

				})
			]
		});
		this.ballType = ballType;
	}
};

App.Medicine.OrganFarm.OvariesImplantAction = class extends App.Medicine.OrganFarm.OrganImplantAction {
	/**
	 * @param {object} params
	 * @param {string} params.name
	 * @param {boolean} params.animal
	 * @param {typeof App.Medicine.Surgery.Procedure} params.surgeryProcedure
	 * @param {Array} [params.surgeryProcedureParams]
	 * @param {boolean} [params.autoImplant]
	 * @param {function(App.Entity.SlaveState):boolean} params.canImplant
	 * @param {function(App.Entity.SlaveState):string} params.implantError
	 */
	constructor({
		name,
		animal,
		surgeryProcedure,
		surgeryProcedureParams,
		autoImplant = true,
		canImplant,
		implantError
	}) {
		super({
			name: name,
			//healthImpact: 20,
			surgeryType: animal ? "addAnimalOvaries" : "addOvaries", autoImplant: autoImplant,
			surgeryProcedure: surgeryProcedure,
			surgeryProcedureParams: surgeryProcedureParams,
			canImplant: s => (!this.animal || V.animalTesticles > 0) && canImplant(s),
			implantError: s => (this.animal && V.animalTesticles === 0) ? "" : implantError(s),
		});
		this.animal = animal;
	}
};

App.Medicine.OrganFarm.Ovaries = class extends App.Medicine.OrganFarm.Organ {
	/**
	 * @param {object} params
	 * @param {string} params.name
	 * @param {FC.AnimalType} params.eggType
	 * @param {string} params.pregData
	 */
	constructor({name, eggType, pregData}) {
		super({
			name: name, tooltip: "requires a vagina for successful implantation",
			cost: 10000, time: 10,
			canGrow: () => (this.eggType === "human" || V.animalOvaries > 0),
			actions: [
				new App.Medicine.OrganFarm.OvariesImplantAction({
					name: "Implant", animal: eggType !== "human",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFAddOvaries,
					surgeryProcedureParams: [eggType, pregData],
					canImplant: s => (s.vagina >= 0 && s.ovaries <= 0 && s.mpreg === 0 && s.bellyImplant === -1),
					implantError: s => {
						if (s.vagina < 0) {
							return "This slave lacks the vagina necessary to accept ovaries.";
						}
						if (s.ovaries > 0 || s.mpreg > 0) {
							return "This slave already has ovaries.";
						}
						return "This slave's body cavity is filled with another organ.";
					},
				}),
				new App.Medicine.OrganFarm.OvariesImplantAction({
					name: "Replace", animal: eggType !== "human",
					autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFReplaceOvaries,
					surgeryProcedureParams: [eggType, pregData],
					canImplant: s => (s.vagina >= 0 && s.mpreg === 0 && s.bellyImplant === -1 && (s.eggType !== this.eggType || s.preg === -3)),
					implantError: s => (s.eggType === this.eggType) ? `This slave already has ${s.eggType} ovaries.` : "",
				})
			]
		});
		this.eggType = eggType;
		this.pregData = pregData;
	}
};

App.Medicine.OrganFarm.AnalWombImplantAction = class extends App.Medicine.OrganFarm.OrganImplantAction {
	/**
	 * @param {object} params
	 * @param {FC.AnimalType} params.eggType
	 * @param {string} params.pregData
	 */
	constructor({eggType, pregData}) {
		super({
			name: "Implant",
			surgeryProcedure: App.Medicine.Surgery.Procedures.OFAddMPreg,
			surgeryProcedureParams: [],
			canImplant: s => ((this.eggType === "human" || V.animalTesticles > 0) && s.ovaries === 0 && s.vagina <= -1 && s.mpreg === 0 && s.bellyImplant < 0),
			implantError: s => {
				if (this.eggType !== "human" && V.animalTesticles === 0) {
					return "";
				}
				if (s.bellyImplant >= 0) {
					return "This slave has a fillable abdominal implant.";
				}
				return "This slave has existing reproductive Organs.";
			},
		});
		this.eggType = eggType;
		this.pregData = pregData;
	}
};

App.Medicine.OrganFarm.AnalWomb = class extends App.Medicine.OrganFarm.Organ {
	/**
	 * @param {object} params
	 * @param {string} params.name
	 * @param {FC.AnimalType} params.eggType
	 * @param {string} params.pregData
	 */
	constructor({name, eggType, pregData}) {
		super({
			name: name,
			tooltip: "the slave must not have female reproductive organs for successful implantation",
			cost: 20000, time: 10,
			canGrow: () => (V.arcologies[0].FSGenderRadicalistResearch === 1 && (eggType === "human" || V.animalMpreg > 0)),
			actions: [
				new App.Medicine.OrganFarm.AnalWombImplantAction({
					eggType: eggType,
					pregData: pregData
				})
			]
		});
	}
};


code:
/**
 * Organs will usually be displayed in this order.
 *
/** @type {Map<string, App.Medicine.OrganFarm.Organ>} */
App.Medicine.OrganFarm.Organs = new Map([
	["penis",
		new App.Medicine.OrganFarm.Organ({
			name: "Penis", cost: 5000, time: 5,
			canGrow: () => V.seeDicks !== 0 || V.makeDicks === 1,
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFAddDick,
					canImplant: slave => slave.dick <= 0,
					implantError: () => "this slave already has a penis",
				})
			]
		})],
	["testicles",
		new App.Medicine.OrganFarm.Testicles({
			name: "Testicles", ballType: "human"
		})],
	["pigTesticles",
		new App.Medicine.OrganFarm.Testicles({
			name: "Pig testicles", ballType: "pig"
		})],
	["dogTesticles",
		new App.Medicine.OrganFarm.Testicles({
			name: "Dog testicles", ballType: "dog"
		})],
	["horseTesticles",
		new App.Medicine.OrganFarm.Testicles({
			name: "Horse testicles", ballType: "horse"
		})],
	["cowTesticles",
		new App.Medicine.OrganFarm.Testicles({
			name: "Cow testicles", ballType: "cow"
		})],
	["scrotum",
		new App.Medicine.OrganFarm.Organ({
			name: "Scrotum", tooltip: "requires balls for successful implantation", cost: 2500, time: 5,
			dependencies: ["testicles", "pigTesticles", "dogTesticles", "horseTesticles", "cowTesticles"],
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Graft on",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFAddScrotum,
					canImplant: slave => slave.scrotum <= 0 && slave.balls > 0,
					implantError: slave => slave.scrotum > 0 ? "This slave already has a scrotum." : "This slave lacks the balls necessary to accept a scrotum.",
				})
			]
		})],
	["foreskin",
		new App.Medicine.OrganFarm.Organ({
			name: "Foreskin", cost: 2500, time: 5,
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Graft on",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFAddForeskin,
					canImplant: slave => slave.foreskin <= 0,
					implantError: slave => `This slave already has a ${slave.dick > 0 ? "foreskin" : "clitoral hood"}.`,
				})
			]
		})],
	["prostate",
		new App.Medicine.OrganFarm.Organ({
			name: "Prostate", cost: 5000, time: 5,
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFAddProstate,
					canImplant: s => s.prostate === 0,
					implantError: () => "This slave already has a prostate.",
				})
			]
		})],
	["ovaries",
		new App.Medicine.OrganFarm.Ovaries({
			name: "Ovaries", eggType: "human", pregData: "human"
		})],
	["freshOvaries",
		new App.Medicine.OrganFarm.Organ({
			name: "Younger Ovaries",
			tooltip: "requires a womb for successful implantation",
			cost: 10000, time: 10,
			canGrow: () => V.youngerOvaries === 1,
			dependencies: ["ovaries", "pigOvaries", "dogOvaries", "horseOvaries", "cowOvaries", "mpreg", "mpregPig", "mpregDog", "mpregHorse", "mpregCow"],
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFFreshOvaries,
					canImplant: s => (s.mpreg !== 0 || s.ovaries !== 0) && s.bellyImplant === -1 && s.physicalAge < 70,
					implantError: s => s.physicalAge >= 70 ? "This slave's body is too old to handle pregnancy." : "This slave lacks a viable womb.",
				})
			]
		})],
	["asexualReproOvaries",
		new App.Medicine.OrganFarm.Organ({
			name: "Asexual reproduction modification",
			tooltip: "requires existing ovaries for successful implantation",
			cost: 10000, time: 10,
			canGrow: () => V.asexualReproduction === 1,
			dependencies: ["ovaries", "pigOvaries", "dogOvaries", "horseOvaries", "cowOvaries", "mpreg", "mpregPig", "mpregDog", "mpregHorse", "mpregCow"],
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFAsexualReproOvaries,
					canImplant: s => s.mpreg !== 0 || s.ovaries !== 0,
					implantError: () => "This slave lacks ovaries.",
				})
			]
		})],
	["pigOvaries",
		new App.Medicine.OrganFarm.Ovaries({
			name: "Pig ovaries", eggType: "pig", pregData: "pig"
		})],
	["dogOvaries",
		new App.Medicine.OrganFarm.Ovaries({
			name: "Dog ovaries", eggType: "dog", pregData: "canineM",
		})],
	["horseOvaries",
		new App.Medicine.OrganFarm.Ovaries({
			name: "Horse ovaries", eggType: "horse", pregData: "equine",
		})],
	["cowOvaries",
		new App.Medicine.OrganFarm.Ovaries({
			name: "Cow ovaries", eggType: "cow", pregData: "cow"
		})],
	["leftEye",
		new App.Medicine.OrganFarm.Organ({
			name: "Left Eye", cost: 5000, time: 10,
			tooltip: s => getLeftEyeVision(s) === 2 ? "would not improve this slave" : "",
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFNewEyes,
					surgeryProcedureParams: ["left"],
					canImplant: s => getLeftEyeVision(s) === 0 && getBestVision(s) !== 0 && getLeftEyeType(s) !== 2,
					implantError: s => getLeftEyeVision(s) !== 0 ? "Slave has a working left eye." : "",
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFUnblind,
					surgeryProcedureParams: ["left"],
					canImplant: s => getBestVision(s) === 0 && getLeftEyeType(s) !== 2,
					implantError: () => "",
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Replace", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFNewEyes,
					surgeryProcedureParams: ["left"],
					canImplant: s => getLeftEyeType(s) === 2,
					implantError: () => "",
				})
			]
		})],
	["rightEye",
		new App.Medicine.OrganFarm.Organ({
			name: "Right Eye", cost: 5000, time: 10,
			tooltip: s => getRightEyeVision(s) === 2 ? "would not improve this slave" : "",
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFNewEyes,
					surgeryProcedureParams: ["right"],
					canImplant: s => getRightEyeVision(s) === 0 && getBestVision(s) !== 0 && getRightEyeType(s) !== 2,
					implantError: s => getRightEyeVision(s) !== 0 ? "Slave has a working right eye." : "",
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFUnblind,
					surgeryProcedureParams: ["right"],
					canImplant: s => getBestVision(s) === 0 && getRightEyeType(s) !== 2,
					implantError: () => "",
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Replace", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFNewEyes,
					surgeryProcedureParams: ["right"],
					canImplant: s => getRightEyeType(s) === 2,
					implantError: () => "",
				})
			]
		})],
	["ears",
		new App.Medicine.OrganFarm.Organ({
			name: "Normal Ears", cost: 1000, time: 2,
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Attach",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFImplantEars,
					canImplant: s => s.earShape === "none",
					implantError: () => "This slave already has ears."
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Replace",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFImplantEars,
					autoImplant: false,
					canImplant: s => s.earShape !== "normal",
					implantError: () => "This slave already has normal ears."
				})
			]
		})],
	["topEars",
		new App.Medicine.OrganFarm.Organ({
			name: "Top Ears", cost: 1000, time: 2,
			canGrow: () => V.surgeryUpgrade >= 1,
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Attach",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFImplantTopEars,
					canImplant: s => s.earT === "none",
					implantError: () => "This slave already has ears at the top of the head."
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Replace",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFImplantTopEars,
					autoImplant: false,
					canImplant: s => s.earT !== "normal",
					implantError: () => "This slave already has normal ears at the top of the head."
				})
			]
		})],
	["cochleae",
		new App.Medicine.OrganFarm.Organ({
			name: "Cochleae", cost: 8000, time: 6,
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFUndeafen,
					canImplant: s => s.hears <= -2 && s.earImplant === 0,
					implantError: () => "This slave already has working ears.",
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Replace", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFNewEars,
					canImplant: s => s.earImplant === 1,
					implantError: () => "",
				})
			]
		})],
	["voicebox",
		new App.Medicine.OrganFarm.Organ({
			name: "Vocal Cords", cost: 5000, time: 5,
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Implant",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFRestoreVoice,
					canImplant: s => s.voice === 0 && s.electrolarynx === 0,
					implantError: () => "This slave is not mute.",
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Replace", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFNewVoice,
					canImplant: s => s.electrolarynx === 1,
					implantError: () => "",
				})
			]
		})],
	["hair",
		new App.Medicine.OrganFarm.Organ({
			name: "Hair Follicles", cost: 500, time: 2, displayMultipleActions: true,
			actions: [
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Scalp",
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFRestoreHairHead,
					canImplant: s => s.bald !== 0,
					implantError: () => "This slave already has hair.",
				}),
				/* So apparently hair is tracked via the .earTColor variable with no differential between possible hair origin. Not worth the variable, currently.
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Ears", healthImpact: 5,
					surgeryType: "addHairEarsH", autoImplant: false,
					canImplant: s => (s.earTShape !== "normal"),
					implantError: "",
					implant: s => {
						s.earTColor = getGeneticHairColor(s);
					}
				}),
				*/
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Brow", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFRestoreHairBrow,
					canImplant: s => s.eyebrowHStyle === "bald",
					implantError: () => "",
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Axillary", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFRestoreHairPits,
					canImplant: s => s.underArmHStyle === "bald" || s.underArmHStyle === "hairless",
					implantError: () => "",
				}),
				new App.Medicine.OrganFarm.OrganImplantAction({
					name: "Pubic", autoImplant: false,
					surgeryProcedure: App.Medicine.Surgery.Procedures.OFRestoreHairPubes,
					canImplant: s => s.pubicHStyle === "bald" || s.pubicHStyle === "hairless",
					implantError: () => "",
				})
			]
		})],
	["mpreg",
		new App.Medicine.OrganFarm.AnalWomb({
			name: "Anal womb and ovaries", eggType: "human", pregData: "human",
		})],
	["mpregPig",
		new App.Medicine.OrganFarm.AnalWomb({
			name: "Anal pig womb and ovaries", eggType: "pig", pregData: "pig",
		})],
	["mpregDog",
		new App.Medicine.OrganFarm.AnalWomb({
			name: "Anal dog womb and ovaries", eggType: "dog", pregData: "canineM",
		})],
	["mpregHorse",
		new App.Medicine.OrganFarm.AnalWomb({
			name: "Anal horse womb and ovaries", eggType: "horse", pregData: "equine",
		})],
	["mpregCow",
		new App.Medicine.OrganFarm.AnalWomb({
			name: "Anal cow womb and ovaries", eggType: "cow", pregData: "cow",
		})],
]);


code:
/**
 * generate two independent Gaussian numbers using Box-Muller transform.
 * mean and deviation specify the desired mean and standard deviation.
 * @param {number} [mean]
 * @param {number} [deviation]
 * @returns {number[]}
 */
function gaussianPair(mean = 0, deviation = 1) {
	const r = Math.sqrt(-2.0 * Math.log(1 - Math.random()));
	const sigma = 2.0 * Math.PI * (1 - Math.random());
	return [r * Math.cos(sigma), r * Math.sin(sigma)].map(val => val * deviation + mean);
}

/**
 * Generate a random integer with a normal distribution between min and max (both inclusive).
 * Default parameters result in truncating the standard normal distribution between -3 and +3.
 * Not specifying min/max results in rerolling val approximately 0.3% of the time.
 * @param {number} [mean]
 * @param {number} [deviation]
 * @param {number} [min]
 * @param {number} [max]
 * @returns {number}
 */
/* exported normalRandInt */
function normalRandInt(mean = 0, deviation = 1, min = mean - 3 * deviation, max = mean + 3 * deviation) {
	let val = gaussianPair(mean, deviation)[0];
	while (val < min || val > max) {
		val = gaussianPair(mean, deviation)[0];
	}
	return Math.round(val);
}

/**
 * Returns a random integer between min and max (both inclusive).
 * If count is defined, chooses that many random numbers between min and max and returns the average. This is an approximation of a normal distribution.
 * @param {number} min
 * @param {number} max
 * @param {number} [count]
 * @returns {number}
 */
/* exported jsRandom */
function jsRandom(min, max, count = 1) {
	function rand() {
		return Math.random() * (max - min + 1) + min;
	}

	if (count === 1) {
		return Math.floor(rand());
	}

	let total = 0;
	for (let i = 0; i < count; i++) {
		total += rand();
	}
	return Math.floor(total / count);
}

/**
 * Chooses multiple random elements of an array.
 * @template {*} T
 * @param {Array<T>} arr
 * @param {number} count
 * @returns {Array<T>}
 */
/* exported jsRandomMany */
function jsRandomMany(arr, count) {
	let result = [];
	let tmp = arr.slice();
	for (let i = 0; i < count; i++) {
		let index = Math.floor(Math.random() * tmp.length);
		result.push(tmp.splice(index, 1)[0]);
	}
	return result;
}

/**
 * Accepts both an array and a list, returns undefined if nothing is passed.
 * @template {*} T
 * @param {Array<T>} choices
 * @param {...T} [otherChoices]
 * @returns {T}
 */
/* exported jsEither */
function jsEither(choices, ...otherChoices) {
	if (otherChoices.length === 0 && Array.isArray(choices)) {
		return choices[Math.floor(Math.random() * choices.length)];
	}
	const allChoices = otherChoices;
	allChoices.push(...choices);
	return allChoices[Math.floor(Math.random() * allChoices.length)];
}


code:
/* eslint no-unused-vars: [2, { "vars": "local" }] */

/* This file contains only JS functions without dependencies on FC specific variables/conventions and do not rely on
 * custom functions outside this file
 */

/**
 * Returns whether x is defined. Port of SugarCube's def.
 * @param {any} x
 * @returns {boolean}
 */
function jsDef(x) {
	return !(typeof x === "undefined" || x === null || x === undefined);
}

/**
 * @template {PropertyKey} T
 * @param {Object.<T, number>} obj
 * @returns {T}
 */
function hashChoice(obj) {
	let randint = Math.floor(Math.random() * hashSum(obj));
	let ret;
	Object.keys(obj).some((key) => {
		if (randint < obj[key]) {
			ret = key;
			return true;
		} else {
			randint -= obj[key];
			return false;
		}
	});
	return ret;
}

/**
 * @param {Object.<PropertyKey, number>} obj
 * @returns {number}
 */
function hashSum(obj) {
	let sum = 0;
	Object.keys(obj).forEach((key) => {
		sum += obj[key];
	});
	return sum;
}

/**
 * @template {PropertyKey} T
 * @param {T[]} arr
 * @returns {Object.<T, number>}
 */
function arr2obj(arr) {
	/** @type {Object.<T, number>} */
	const obj = {};
	arr.forEach((item) => {
		obj[item] = 1;
	});
	return obj;
}

/**
 * @param {object} object
 * @param {PropertyKey[]} rest
 */
function hashPush(object, ...rest) {
	rest.forEach((item) => {
		if (object[item] === undefined) {
			object[item] = 1;
		} else {
			object[item] += 1;
		}
	});
}

/**
 * @param {Object.<PropertyKey, number>} obj
 * @param {Object.<PropertyKey, number>} other
 */
function hashMerge(obj, other) {
	Object.keys(other).forEach((key) => {
		if (obj[key] === undefined) {
			obj[key] = other[key];
		} else {
			obj[key] += other[key];
		}
	});
}

/**
 * @template {PropertyKey} T
 * @param {T[]} array
 * @returns {Object.<T, number>}
 */
function weightedArray2HashMap(array) {
	/** @type {Object.<T, number>} */
	const obj = {};
	array.forEach((item) => {
		if (obj[item] === undefined) {
			obj[item] = 1;
		} else {
			obj[item] += 1;
		}
	});
	return obj;
}

/**
 * generate a random, almost unique ID that is compliant (possibly) with RFC 4122
 *
 * @returns {string}
 */
function generateNewID() {
	let date = Date.now(); // high-precision timer
	let uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
		let r = (date + Math.random() * 16) % 16 | 0;
		date = Math.floor(date / 16);
		return (c === "x" ? r : (r & 0x3 | 0x8)).toString(16);
	});
	return uuid;
}

/**
 * Swaps two values in an array.
 * @param {Array} array
 * @param {number} indexA
 * @param {number} indexB
 */
function arraySwap(array, indexA, indexB) {
	const tmp = array[indexA];
	array[indexA] = array[indexB];
	array[indexB] = tmp;
}

/**
 * @param {string} string
 * @returns {string}
 */
function capFirstChar(string) {
	return string.charAt(0).toUpperCase() + string.substr(1);
}

/**
 * @param {string} string
 * @returns {string}
 */
function uncapFirstChar(string) {
	return string.charAt(0).toLowerCase() + string.substr(1);
}

/**
 * @param {string} word
 * @returns {string}
 */
function addA(word) {
	let vocal = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
	if (vocal.includes(word.charAt(0))) {
		return `an ${word}`;
	}
	return `a ${word}`;
}

/**
 * Takes a number and appends the appropriate suffix.
 * Example: `ordinalSuffix(1)` gives `1st`.
 * @param {number} i
 * @returns {string}
 */
function ordinalSuffix(i) {
	let j = i % 10;
	let k = i % 100;
	if (j === 1 && k !== 11) {
		return `${i}st`;
	}
	if (j === 2 && k !== 12) {
		return `${i}nd`;
	}
	if (j === 3 && k !== 13) {
		return `${i}rd`;
	}
	return `${i}th`;
}

/**
 * @param {number} i
 * @returns {string}
 * Takes a number and returns the appropriate ordinal.
 * Example: `ordinalSuffix(1)` gives `first`.
 * For number greater than 19 identical to `ordinalSuffix(i)`
 */
function ordinalSuffixWords(i) {
	const text = ["zeroth", "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth", "thirteenth", "fourteenth", "fifteenth", "sixteenth", "seventeenth", "eighteenth", "nineteenth"];
	if (i < text.length) {
		return text[i];
	}
	return ordinalSuffix(i);
}

/**
 * Takes an array and returns a new array without duplicate entries.
 * @template T
 * @param {Iterable<T>} array
 * @returns {T[]}
 */
function removeDuplicates(array) {
	return [...new Set(array)];
}

/**
 * Maps an index from one list onto a matching index on the other.
 * The first and last indexes will be matched to the first and last indexes of the other list,
 * while indexes in between will go to the nearest index.
 * @param {number} index The index in original list to map to new list.
 * @param {*} originalList The original list the index refers into.
 * @param {*} newList The new list which we want an index for
 * @returns {number} The new index into newList
 */
App.Utils.mapIndexBetweenLists = function(index, originalList, newList) {
	if (index === 0) { return 0; }
	if (index === originalList.length - 1) { return newList.length - 1; }
	index--;
	const originalLimitedLength = originalList.length - 2;
	const newLimitedLength = newList.length - 2;
	return Math.round((index / originalLimitedLength) * newLimitedLength) + 1;
};

/**
 * replaces special HTML characters with their '&xxx' forms
 * @param {string} text
 * @returns {string}
 */
App.Utils.escapeHtml = function(text) {
	const map = {
		'&': '&',
		'<': '<',
		'>': '>',
		'"': '"',
		"'": '''
	};
	return text.replace(/[&<>"']/g, m => map[m]);
};

/**
 * Creates an object where the items are accessible via their ids.
 *
 * @param {Iterable} list
 * @returns {{}}
 */
function mapIdList(list) {
	let mappedList = {};
	for (const item of list) {
		mappedList[item.id] = item;
	}
	return mappedList;
}

/**
 * Topological sorting algorithm
 * https://gist.github.com/shinout/1232505
 * Added keys parameter since it better suits our needs and updated to project code style.
 *
 * @template {PropertyKey} T
 * @param {T[]} keys
 * @param {T[][]} edges list of edges. each edge forms Array<ID,ID> e.g. [12, 3]
 * @returns {T[]} Array: topological sorted list of IDs
 * @throws Error: in case there is a closed chain.
 **/
App.Utils.topologicalSort = function(keys, edges) {
	class Node {
		/** @param {T} id */
		constructor(id) {
			this.id = id;
			/** @type {T[]} */
			this.afters = [];
		}
	}

	/** @type {Object.<T, Node>} */
	let nodes = {}; // hash: stringified id of the node => { id: id, afters: list of ids }
	let sorted = []; // sorted list of IDs ( returned value )
	/** @type {Object.<T, boolean>} */
	let visited = {}; // hash: id of already visited node => true

	// 1. build data structures
	keys.forEach(key => {
		nodes[key] = new Node(key);
	});

	edges.forEach(edge => {
		const from = edge[0];
		const to = edge[1];
		if (!nodes[from]) { nodes[from] = new Node(from); }
		if (!nodes[to]) { nodes[to] = new Node(to); }
		nodes[from].afters.push(to);
	});

	// 2. topological sort
	Object.keys(nodes).forEach(function visit(idstr, ancestors) {
		let node = nodes[idstr];
		let id = node.id;

		// if already exists, do nothing
		if (visited[idstr]) { return; }

		if (!Array.isArray(ancestors)) { ancestors = []; }

		ancestors.push(id);

		visited[idstr] = true;

		node.afters.forEach((afterID) => {
			if (ancestors.indexOf(afterID) >= 0) { // if already in ancestors, a closed chain exists.
				throw new Error('closed chain : ' + afterID + ' is in ' + id);
			}

			visit(afterID.toString(), ancestors.map(v => v)); // recursive call
		});

		sorted.unshift(id);
	});

	return sorted;
};

/**
 * Sorts an array of objects based on the order of the first array.
 * Sorts by values accessible by unsorted[key]
 * Values of the second array must be a subset of sorted
 *
 * @param {Array<any>} sorted
 * @param {Array<object>} unsorted
 * @param {string} key
 * @returns {Array<object>}
 */
function sortArrayByArray(sorted, unsorted, key) {
	const map = {};
	sorted.forEach((value, index) => {
		map[value] = index;
	});

	return unsorted.sort((a, b) => {
		return map[a[key]] - map[b[key]];
	});
}

/**
 * @param {object} target
 * @param {object} source
 */
function deepAssign(target, source) {
	function isObject(o) {
		return (o !== undefined && o !== null && typeof o === 'object' && !Array.isArray(o));
	}

	if (isObject(target) && isObject(source)) {
		for (const key in source) {
			if (!source.hasOwnProperty(key)) { continue; }
			if (isObject(source[key])) {
				if (!target.hasOwnProperty(key) || !isObject(target[key])) {
					target[key] = {};
				}
				deepAssign(target[key], source[key]);
			} else {
				Object.assign(target, {
					[key]: source[key]
				});
			}
		}
	}
}

/**
 * Returns the median value for an array
 *
 * For more information about mean vs. median see
 * https://www.clinfo.eu/mean-median/
 * @param {number[]} arr Does not need to be sorted
 * @returns {number}
 */
function median(arr = []) {
	const mid = Math.floor(arr.length / 2);
	const nums = [...arr].sort((a, b) => a - b);

	return arr.length % 2 === 0 ? (nums[mid] + nums[mid - 1]) / 2 : nums[mid];
}


free-cities

description: The Free Cities game

type: git

lint: eslint src/**/*.js

compile: sh compile.sh


code:
There are actually two places to add art for FreeCities.  The folder you are currently in, ./resources, is mostly for the git.  Once you compile an HTML, the HTML won't check here for art.

If you are interested in adding the old renders or your own custom art to your local version of FreeCities, then you actually want to have a resources folder in the same folder FC_pregmod.html is in.  By default, that will be ./bin/resources.  If you want any of the art in this folder, make sure to copy it over there.

Two different resources folders, and both connected to art.  But they work very differently.

P.S. make sure you don't stack resources folders, like resources/resources.  That would just be silly.


path: resources/vector/body/addon/boob 0 areola piercing.svg


path: resources/vector/body/addon/boob 0 piercing heavy.svg


path: resources/vector/body/addon/boob 1 areola piercing.svg


path: resources/vector/body/addon/boob 1 piercing heavy.svg


path: resources/vector/body/addon/boob 2 areola piercing.svg


path: resources/vector/body/addon/boob 2 piercing heavy.svg


path: resources/vector/body/addon/boob 3 areola piercing.svg


path: resources/vector/body/addon/boob 3 piercing heavy.svg


path: resources/vector/body/addon/boob 4 areola piercing.svg


path: resources/vector/body/addon/boob 4 piercing heavy.svg


path: resources/vector/body/addon/boob 5 areola piercing.svg


path: resources/vector/body/addon/boob 5 piercing heavy.svg


path: resources/vector/body/addon/boob 6 areola piercing.svg


path: resources/vector/body/addon/boob 6 piercing heavy.svg


path: resources/vector/body/addon/boob 7 areola piercing.svg


path: resources/vector/body/addon/boob 7 piercing heavy.svg


code:
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	 viewBox="0 0 1000 1000" style="enable-background:new 0 0 1000 1000;" xml:space="preserve">
<style type="text/css">
	.st0{fill:#787878;}
</style>
<g id="Clit_Piercing">
	<circle id="XMLID_257_" class="st0" cx="488.3" cy="435.6" r="1.2"/>
	<circle id="XMLID_256_" class="st0" cx="483.6" cy="436.1" r="1.2"/>
</g>
</svg>


path: resources/vector/body/addon/hair aft neat auburn.svg


path: resources/vector/body/addon/hair aft neat black.svg


path: resources/vector/body/addon/hair aft neat blonde.svg


path: resources/vector/body/addon/hair aft neat blue.svg


path: resources/vector/body/addon/hair aft neat brown.svg


path: resources/vector/body/addon/hair aft neat burgundy.svg


path: resources/vector/body/addon/hair aft neat chestnut.svg


path: resources/vector/body/addon/hair aft neat chocolate.svg


path: resources/vector/body/addon/hair aft neat copper.svg


path: resources/vector/body/addon/hair aft neat dark brown.svg


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   version="1.1"
   id="Layer_1"
   x="0px"
   y="0px"
   viewBox="0 0 1000 1000"
   style="enable-background:new 0 0 1000 1000;"
   xml:space="preserve"
   sodipodi:docname="hair aft neat darkbrown.svg"
   inkscape:version="0.92.1 r15371"><metadata
	 id="metadata10"><rdf:RDF><cc:Work
		 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
		   rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
	 id="defs8" /><sodipodi:namedview
	 pagecolor="#ffffff"
	 bordercolor="#666666"
	 borderopacity="1"
	 objecttolerance="10"
	 gridtolerance="10"
	 guidetolerance="10"
	 inkscape:pageopacity="0"
	 inkscape:pageshadow="2"
	 inkscape:window-width="1920"
	 inkscape:window-height="1001"
	 id="namedview6"
	 showgrid="false"
	 inkscape:zoom="0.236"
	 inkscape:cx="-1508.4746"
	 inkscape:cy="500"
	 inkscape:window-x="-9"
	 inkscape:window-y="-9"
	 inkscape:window-maximized="1"
	 inkscape:current-layer="Layer_1" /><style
	 type="text/css"
	 id="style2">
	.st0{fill:#3F4040;}
</style><path
	 id="Hair_Aft_7_"
	 class="st0"
	 d="m 568.83729,121.38644 c 13.5,72.4 34.5,66.5 14,103.3 -43.3,47.7 -90.1,7 -98.1,-9.8 -41.1,-23.4 -0.3,-140.799999 48.3,-130.299999 33.3,7.2 33.3,19.499999 35.8,36.799999"
	 inkscape:connector-curvature="0"
	 style="fill:#463325;fill-opacity:1;" /></svg>

path: resources/vector/body/addon/hair aft neat darkbrown.svg


path: resources/vector/body/addon/hair aft neat dirty blonde.svg


path: resources/vector/body/addon/hair aft neat ginger.svg


path: resources/vector/body/addon/hair aft neat golden.svg


path: resources/vector/body/addon/hair aft neat green.svg


path: resources/vector/body/addon/hair aft neat grey.svg


path: resources/vector/body/addon/hair aft neat hazel.svg


path: resources/vector/body/addon/hair aft neat pink.svg


path: resources/vector/body/addon/hair aft neat platinum blonde.svg


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   version="1.1"
   id="Layer_1"
   x="0px"
   y="0px"
   viewBox="0 0 1000 1000"
   style="enable-background:new 0 0 1000 1000;"
   xml:space="preserve"
   sodipodi:docname="hair aft neat platinumblonde.svg"
   inkscape:version="0.92.1 r15371"><metadata
	 id="metadata10"><rdf:RDF><cc:Work
		 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
		   rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
	 id="defs8" /><sodipodi:namedview
	 pagecolor="#ffffff"
	 bordercolor="#666666"
	 borderopacity="1"
	 objecttolerance="10"
	 gridtolerance="10"
	 guidetolerance="10"
	 inkscape:pageopacity="0"
	 inkscape:pageshadow="2"
	 inkscape:window-width="1920"
	 inkscape:window-height="1001"
	 id="namedview6"
	 showgrid="false"
	 inkscape:zoom="0.236"
	 inkscape:cx="-1527.5424"
	 inkscape:cy="500"
	 inkscape:window-x="-9"
	 inkscape:window-y="-9"
	 inkscape:window-maximized="1"
	 inkscape:current-layer="Layer_1" /><style
	 type="text/css"
	 id="style2">
	.st0{fill:#3F4040;}
</style><path
	 id="Hair_Aft_7_"
	 class="st0"
	 d="m 568.83729,121.38644 c 13.5,72.4 34.5,66.5 14,103.3 -43.3,47.7 -90.1,7 -98.1,-9.8 -41.1,-23.4 -0.3,-140.799999 48.3,-130.299999 33.3,7.2 33.3,19.499999 35.8,36.799999"
	 inkscape:connector-curvature="0"
	 style="fill:#fcf3c1;fill-opacity:1" /></svg>

path: resources/vector/body/addon/hair aft neat platinumblonde.svg


path: resources/vector/body/addon/hair aft neat purple.svg


path: resources/vector/body/addon/hair aft neat red.svg


path: resources/vector/body/addon/hair aft neat silver.svg


path: resources/vector/body/addon/hair aft neat strawberry-blonde.svg


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   version="1.1"
   id="Layer_1"
   x="0px"
   y="0px"
   viewBox="0 0 1000 1000"
   style="enable-background:new 0 0 1000 1000;"
   xml:space="preserve"
   sodipodi:docname="hair aft neat strawberry.svg"
   inkscape:version="0.92.1 r15371"><metadata
	 id="metadata10"><rdf:RDF><cc:Work
		 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
		   rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
	 id="defs8" /><sodipodi:namedview
	 pagecolor="#ffffff"
	 bordercolor="#666666"
	 borderopacity="1"
	 objecttolerance="10"
	 gridtolerance="10"
	 guidetolerance="10"
	 inkscape:pageopacity="0"
	 inkscape:pageshadow="2"
	 inkscape:window-width="1920"
	 inkscape:window-height="1001"
	 id="namedview6"
	 showgrid="false"
	 inkscape:zoom="0.236"
	 inkscape:cx="-1656.7797"
	 inkscape:cy="500"
	 inkscape:window-x="-9"
	 inkscape:window-y="-9"
	 inkscape:window-maximized="1"
	 inkscape:current-layer="Layer_1" /><style
	 type="text/css"
	 id="style2">
	.st0{fill:#3F4040;}
</style><path
	 id="Hair_Aft_7_"
	 class="st0"
	 d="m 568.83729,121.38644 c 13.5,72.4 34.5,66.5 14,103.3 -43.3,47.7 -90.1,7 -98.1,-9.8 -41.1,-23.4 -0.3,-140.799999 48.3,-130.299999 33.3,7.2 33.3,19.499999 35.8,36.799999"
	 inkscape:connector-curvature="0"
	 style="fill:#e6c78c;fill-opacity:1" /></svg>

path: resources/vector/body/addon/hair aft neat strawberry.svg


path: resources/vector/body/addon/hair aft neat white.svg


path: resources/vector/body/addon/hair fore neat auburn.svg


path: resources/vector/body/addon/hair fore neat black.svg


path: resources/vector/body/addon/hair fore neat blonde.svg


path: resources/vector/body/addon/hair fore neat blue.svg


path: resources/vector/body/addon/hair fore neat brown.svg


path: resources/vector/body/addon/hair fore neat burgundy.svg


path: resources/vector/body/addon/hair fore neat chestnut.svg


path: resources/vector/body/addon/hair fore neat chocolate.svg


path: resources/vector/body/addon/hair fore neat copper.svg


path: resources/vector/body/addon/hair fore neat dark brown.svg


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   version="1.1"
   id="Layer_1"
   x="0px"
   y="0px"
   viewBox="0 0 1000 1000"
   style="enable-background:new 0 0 1000 1000;"
   xml:space="preserve"
   sodipodi:docname="hair fore neat darkbrown.svg"
   inkscape:version="0.92.1 r15371"><metadata
	 id="metadata10"><rdf:RDF><cc:Work
		 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
		   rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
	 id="defs8" /><sodipodi:namedview
	 pagecolor="#ffffff"
	 bordercolor="#666666"
	 borderopacity="1"
	 objecttolerance="10"
	 gridtolerance="10"
	 guidetolerance="10"
	 inkscape:pageopacity="0"
	 inkscape:pageshadow="2"
	 inkscape:window-width="1166"
	 inkscape:window-height="991"
	 id="namedview6"
	 showgrid="false"
	 inkscape:zoom="0.236"
	 inkscape:cx="-16.949153"
	 inkscape:cy="500"
	 inkscape:window-x="-8"
	 inkscape:window-y="0"
	 inkscape:window-maximized="0"
	 inkscape:current-layer="Layer_1" /><style
	 type="text/css"
	 id="style2">
	.st0{fill:#3F4040;}
</style><path
	 id="Hair_Black"
	 class="st0"
	 d="M484.6,101.6c5.1,7.6,15,14,52.5,7.3c18.7,5.2,2.6,37.9,1.1,61c-1.3,19.3-7.8,36.4-18.9,51.5  c17.1-12.2,36.2-10.4,50.8-41.2c41.1-23.4-1.2-125.9-49.9-115.4C487.2,71.9,487.1,84.2,484.6,101.6z"
	 style="fill:#463325;fill-opacity:1" /></svg>

path: resources/vector/body/addon/hair fore neat darkbrown.svg


path: resources/vector/body/addon/hair fore neat dirty blonde.svg


path: resources/vector/body/addon/hair fore neat ginger.svg


path: resources/vector/body/addon/hair fore neat golden.svg


path: resources/vector/body/addon/hair fore neat green.svg


path: resources/vector/body/addon/hair fore neat grey.svg


path: resources/vector/body/addon/hair fore neat hazel.svg


path: resources/vector/body/addon/hair fore neat pink.svg


path: resources/vector/body/addon/hair fore neat platinum blonde.svg


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   version="1.1"
   id="Layer_1"
   x="0px"
   y="0px"
   viewBox="0 0 1000 1000"
   style="enable-background:new 0 0 1000 1000;"
   xml:space="preserve"
   sodipodi:docname="hair fore neat platinum.svg"
   inkscape:version="0.92.1 r15371"><metadata
	 id="metadata10"><rdf:RDF><cc:Work
		 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
		   rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
	 id="defs8" /><sodipodi:namedview
	 pagecolor="#ffffff"
	 bordercolor="#666666"
	 borderopacity="1"
	 objecttolerance="10"
	 gridtolerance="10"
	 guidetolerance="10"
	 inkscape:pageopacity="0"
	 inkscape:pageshadow="2"
	 inkscape:window-width="1166"
	 inkscape:window-height="991"
	 id="namedview6"
	 showgrid="false"
	 inkscape:zoom="0.236"
	 inkscape:cx="-777.54237"
	 inkscape:cy="500"
	 inkscape:window-x="-8"
	 inkscape:window-y="0"
	 inkscape:window-maximized="0"
	 inkscape:current-layer="Layer_1" /><style
	 type="text/css"
	 id="style2">
	.st0{fill:#3F4040;}
</style><path
	 id="Hair_Black"
	 class="st0"
	 d="M484.6,101.6c5.1,7.6,15,14,52.5,7.3c18.7,5.2,2.6,37.9,1.1,61c-1.3,19.3-7.8,36.4-18.9,51.5  c17.1-12.2,36.2-10.4,50.8-41.2c41.1-23.4-1.2-125.9-49.9-115.4C487.2,71.9,487.1,84.2,484.6,101.6z"
	 style="fill:#fcf3c1;fill-opacity:1" /></svg>

path: resources/vector/body/addon/hair fore neat platinum.svg


path: resources/vector/body/addon/hair fore neat purple.svg


path: resources/vector/body/addon/hair fore neat red.svg


path: resources/vector/body/addon/hair fore neat silver.svg


path: resources/vector/body/addon/hair fore neat strawberry-blonde.svg


code:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->

<svg
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:cc="http://creativecommons.org/ns#"
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:svg="http://www.w3.org/2000/svg"
   xmlns="http://www.w3.org/2000/svg"
   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
   version="1.1"
   id="Layer_1"
   x="0px"
   y="0px"
   viewBox="0 0 1000 1000"
   style="enable-background:new 0 0 1000 1000;"
   xml:space="preserve"
   sodipodi:docname="hair fore neat strawberry.svg"
   inkscape:version="0.92.1 r15371"><metadata
	 id="metadata10"><rdf:RDF><cc:Work
		 rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
		   rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
	 id="defs8" /><sodipodi:namedview
	 pagecolor="#ffffff"
	 bordercolor="#666666"
	 borderopacity="1"
	 objecttolerance="10"
	 gridtolerance="10"
	 guidetolerance="10"
	 inkscape:pageopacity="0"
	 inkscape:pageshadow="2"
	 inkscape:window-width="1166"
	 inkscape:window-height="991"
	 id="namedview6"
	 showgrid="false"
	 inkscape:zoom="0.236"
	 inkscape:cx="-777.54237"
	 inkscape:cy="500"
	 inkscape:window-x="-8"
	 inkscape:window-y="0"
	 inkscape:window-maximized="0"
	 inkscape:current-layer="Layer_1" /><style
	 type="text/css"
	 id="style2">
	.st0{fill:#3F4040;}
</style><path
	 id="Hair_Black"
	 class="st0"
	 d="M484.6,101.6c5.1,7.6,15,14,52.5,7.3c18.7,5.2,2.6,37.9,1.1,61c-1.3,19.3-7.8,36.4-18.9,51.5  c17.1-12.2,36.2-10.4,50.8-41.2c41.1-23.4-1.2-125.9-49.9-115.4C487.2,71.9,487.1,84.2,484.6,101.6z"
	 style="fill:#e6c78c;fill-opacity:1" /></svg>

path: resources/vector/body/addon/hair fore neat strawberry.svg


path: resources/vector/body/addon/hair fore neat white.svg