code:
package classes
{
	import classes.BodyParts.*;
	import classes.GlobalFlags.kFLAGS;

	/**
	 * This contains some of the helper methods for the player-object I've written
	 * @since June 29, 2016
	 * @author Stadler76
	 */
	public class PlayerHelper extends Character
	{
		public function PlayerHelper() {}

		public function hasDifferentUnderBody():Boolean
		{
			if ([UnderBody.NONE, UnderBody.NAGA].indexOf(underBody.type) != -1)
				return false;

			/* // Example for later use
			if ([UNDER_BODY_TYPE_MERMAID, UNDER_BODY_TYPE_WHATEVER].indexOf(underBody.type) != -1)
				return false; // The underBody is (mis)used for secondary skin, not for the underBody itself
			*/

			return underBody.skin.type != skin.type || underBody.skin.tone != skin.tone ||
			       underBody.skin.adj  != skin.adj  || underBody.skin.desc != skin.desc ||
			       (underBody.skin.hasFur() && hasFur() && underBody.skin.furColor != skin.furColor);
		}

		public function hasUnderBody(noSnakes:Boolean = false):Boolean
		{
			var normalUnderBodies:Array = [UnderBody.NONE];

			if (noSnakes) {
				normalUnderBodies.push(UnderBody.NAGA);
			}

			return normalUnderBodies.indexOf(underBody.type) == -1;
		}

		public function hasFurryUnderBody(noSnakes:Boolean = false):Boolean
		{
			return hasUnderBody(noSnakes) && underBody.skin.hasFur();
		}

		public function hasFeatheredUnderBody(noSnakes:Boolean = false):Boolean
		{
			return hasUnderBody(noSnakes) && underBody.skin.hasFeathers();
		}

		public function hasDragonHorns(fourHorns:Boolean = false):Boolean
		{
			return (!fourHorns && horns.value > 0 && horns.type == Horns.DRACONIC_X2) || horns.type == Horns.DRACONIC_X4_12_INCH_LONG;
		}

		public function hasReptileEyes():Boolean
		{
			return [Eyes.LIZARD, Eyes.DRAGON, Eyes.BASILISK].indexOf(eyes.type) != -1;
		}

		public function hasLizardEyes():Boolean
		{
			return [Eyes.LIZARD, Eyes.BASILISK].indexOf(eyes.type) != -1;
		}

		public function hasReptileFace():Boolean
		{
			return [Face.SNAKE_FANGS, Face.LIZARD, Face.DRAGON].indexOf(face.type) != -1;
		}

		public function hasReptileUnderBody(withSnakes:Boolean = false):Boolean
		{
			var underBodies:Array = [
				UnderBody.REPTILE,
			];

			if (withSnakes) {
				underBodies.push(UnderBody.NAGA);
			}

			return underBodies.indexOf(underBody.type) != -1;
		}

		public function hasCockatriceSkin():Boolean
		{
			return skin.type == Skin.LIZARD_SCALES && underBody.type == UnderBody.COCKATRICE;
		}

		public function hasNonCockatriceAntennae():Boolean
		{
			return [Antennae.NONE, Antennae.COCKATRICE].indexOf(antennae.type) === -1;
		}

		public function hasInsectAntennae():Boolean
		{
			return antennae.type === Antennae.BEE;
		}

		public function hasDragonWings(large:Boolean = false):Boolean
		{
			if (large)
				return wings.type == Wings.DRACONIC_LARGE;
			else
				return [Wings.DRACONIC_SMALL, Wings.DRACONIC_LARGE].indexOf(wings.type) != -1;
		}

		public function hasBatLikeWings(large:Boolean = false):Boolean
		{
			if (large)
				return wings.type == Wings.BAT_LIKE_LARGE;
			else
				return [Wings.BAT_LIKE_TINY, Wings.BAT_LIKE_LARGE].indexOf(wings.type) != -1;
		}

		public function hasLeatheryWings(large:Boolean = false):Boolean
		{
			return hasDragonWings(large) || hasBatLikeWings(large);
		}

		public function hasBigWings():Boolean {
			return [Wings.BAT_LIKE_LARGE, Wings.BEE_LIKE_LARGE, Wings.DRACONIC_LARGE, Wings.FAERIE_LARGE, Wings.FEATHERED_LARGE, Wings.IMP_LARGE].indexOf(wings.type) != -1;
		}

		// To be honest: I seriously considered naming it drDragonCox() :D
		public function dragonCocks():int
		{
			return countCocksOfType(CockTypesEnum.DRAGON);
		}

		public function lizardCocks():int
		{
			return countCocksOfType(CockTypesEnum.LIZARD);
		}

		public function hasDragonfire():Boolean
		{
			return findPerk(PerkLib.Dragonfire) >= 0;
		}

		public function hasDragonWingsAndFire(largeWings:Boolean = true):Boolean
		{
			return hasDragonWings(largeWings) && hasDragonfire();
		}

		public function isBasilisk():Boolean
		{
			return game.bazaar.benoit.benoitBigFamily() && eyes.type == Eyes.BASILISK;
		}

		public function hasReptileTail():Boolean
		{
			return [Tail.LIZARD, Tail.DRACONIC, Tail.SALAMANDER].indexOf(tail.type) != -1;
		}

		public function hasMultiTails():Boolean
		{
			return (tail.type === Tail.FOX && tail.venom > 1);
		}

		// For reptiles with predator arms I recommend to require hasReptileScales() before doing the armType TF to Arms.PREDATOR
		public function hasReptileArms():Boolean
		{
			return arms.type == (Arms.SALAMANDER || Arms.DRAGON || Arms.LIZARD);
		}

		public function hasReptileLegs():Boolean
		{
			return [LowerBody.LIZARD, LowerBody.DRAGON, LowerBody.SALAMANDER].indexOf(lowerBody.type) != -1;
		}

		public function hasDraconicBackSide():Boolean
		{
			return hasDragonWings(true) && hasDragonScales() && hasReptileTail() && hasReptileArms() && hasReptileLegs();
		}

		public function hasDragonNeck():Boolean
		{
			return neck.type == Neck.DRACONIC && neck.isFullyGrown();
		}

		public function hasNormalNeck():Boolean
		{
			return neck.len <= 2;
		}

		public function hasDragonRearBody():Boolean
		{
			return [RearBody.DRACONIC_MANE, RearBody.DRACONIC_SPIKES].indexOf(rearBody.type) != -1;
		}

		public function hasNonSharkRearBody():Boolean
		{
			return [RearBody.NONE, RearBody.SHARK_FIN].indexOf(rearBody.type) == -1;
		}

		public function fetchEmberRearBody():Number
		{
			return flags[kFLAGS.EMBER_HAIR] == 2 ? RearBody.DRACONIC_MANE : RearBody.DRACONIC_SPIKES;
		}

		public function featheryHairPinEquipped():Boolean
		{
			return hasKeyItem("Feathery hair-pin") && keyItemv1("Feathery hair-pin") == 1;
		}
	}
}


code:
package classes
{
	import classes.*;
	import classes.GlobalFlags.*;
	import classes.GlobalFlags.kGAMECLASS;
	import classes.Scenes.NPCs.IsabellaScene;
	import classes.internals.*;
	import classes.lists.Age;
	import flash.events.Event;
import flash.events.TextEvent;

/**
	 * The new home of Stats and Perks
	 * @author Kitteh6660
	 */
	public class PlayerInfo extends BaseContent
	{
		public function PlayerInfo() {}

		//------------
		// STATS
		//------------
		public function getLinkedStatHeader(derivedStat:String):String {
			return ""+derivedStat+": ";
		}

		public function displayStatSummary(e:TextEvent):void{
			clearOutput();
            mainView.mainText.removeEventListener(TextEvent.LINK, displayStatSummary);
			switch(e.text){
				case "Attack Damage":
					displayAttackDamageSummary();
					return;
				case BonusDerivedStats.critC:
					outputText("Base: 5%\n");
					break;
				case BonusDerivedStats.spellMod:
					outputText("Base: 100%\n");
					break;
				case BonusDerivedStats.dodge:
					outputText("Base: " + Math.round(player.spe / 10) + "%\n");
					break;
                case BonusDerivedStats.spellCost:
                    outputText("Base: 100%\n");
                    break;
				case BonusDerivedStats.physDmg:
					outputText("Base: " + (((player.isChild() || player.isElder()) && !player.weapon.isFirearm()) ? "85%" : "100%") + "\n");
					break;
				case BonusDerivedStats.lustRes:
					outputText("Base: " + (100 - player.getLustPercentBase()) + "%\n");
					break;
			}
			outputText(player.getBonusStatSummary(e.text));
			doNext(displayStats)
		}

		public function displayAttackDamageSummary():void{//Damage is a bit more complicated.
            var indent:String = "\t";
            var weaponAttackSummary:String = indent + "Weapon Damage Bonuses: \n" + player.getBonusStatSummary(BonusDerivedStats.weaponDamage,indent + "\t");
            if(weaponAttackSummary != indent + "Weapon Damage Bonuses: \n"){
				indent += "\t";
			}else weaponAttackSummary = "";
			weaponAttackSummary += indent.substring(0,indent.length-1) + "Calculated Weapon Damage:" + player.weaponAttack + "\n";
			var attackDamageSummary:String = indent + "Attack Damage Bonuses: \n" + player.getBonusStatSummary(BonusDerivedStats.attackDamage,indent);
            if(attackDamageSummary != indent + "Attack Damage Bonuses: \n"){
                indent += "\t";
            }else attackDamageSummary = "";
            var physDmgSummary:String = indent + "Physical Damage Bonuses: \n" + player.getBonusStatSummary(BonusDerivedStats.physDmg,indent);
            if(physDmgSummary != indent + "Physical Damage Bonuses: \n"){
                indent += "\t";
            }else physDmgSummary = "";
            var globalModSummary:String = indent + "Global Damage Bonuses: \n" + player.getBonusStatSummary(BonusDerivedStats.globalMod,indent);
			if(physDmgSummary != indent + "Global Damage Bonuses: \n"){
				globalModSummary = "\t";
			}else globalModSummary = "";

			outputText("Stat Base(With Attribute Bonus):" + combat.getBaseAndAttributeBonus(player.isDoubleAttacking()));
			outputText("\n\tWeapon Damage(Base): " + player.weapon.attack + "\n");
            outputText(weaponAttackSummary + attackDamageSummary + physDmgSummary + globalModSummary);

			doNext(displayStats);

		}

		public function displayStats():void {
            mainView.mainText.addEventListener(TextEvent.LINK, displayStatSummary);
			spriteSelect(null);
			imageSelect(null);
			clearOutput();
			displayHeader("Stats");
			// Begin Combat Stats
			var combatStats:String = "";

			//adjusted max skill cap displayed if player has Ancestral Archery perk 16/4/18 ; Somorac
			combatStats += getLinkedStatHeader("Attack Damage") + game.combat.calcDamage(false,false) + (player.findPerk(PerkLib.DoubleAttack) >= 0 ? "(Double: " + game.combat.calcDamage(false,true) + ")" : "") + "\n";
			combatStats += getLinkedStatHeader(BonusDerivedStats.physDmg) + Math.round(100*player.physMod()) + "%\n";
			combatStats += getLinkedStatHeader(BonusDerivedStats.seduction) + player.getBonusStat(BonusDerivedStats.seduction) + "\n";

			combatStats += getLinkedStatHeader(BonusDerivedStats.critC) + Math.round(player.getBaseCritChance()) + "%\n";

			combatStats += getLinkedStatHeader(BonusDerivedStats.dodge) + Math.round(player.getEvasionChance()) + "%\n";

			combatStats += "Damage Resistance: " + (100 - player.damagePercent(true,false,false,true)) +"-"+ (100-player.damagePercent(false,true,false,true)) + "% (Higher is better.)\n";

			var regenCombatObj:Object = player.regeneration(true,false);
			var regen:Number = (Math.round(player.maxHP() * regenCombatObj.percent / 100) + regenCombatObj.bonus);
			combatStats += "Health Regen(Combat/Camp): " + regen + "/" + regen*2 + "(" + regenCombatObj.percent + "% + " + regenCombatObj.bonus + ")\n";

			combatStats += getLinkedStatHeader(BonusDerivedStats.lustRes) + (100 - Math.round(player.lustPercent())) + "% (Higher is better.)\n";

			combatStats += getLinkedStatHeader(BonusDerivedStats.spellMod) + Math.round(100 * player.spellMod()) + "%\n";

			combatStats += getLinkedStatHeader(BonusDerivedStats.spellCost) + player.spellCost(100) + "%\n";

			if (flags[kFLAGS.RAPHAEL_RAPIER_TRANING] > 0)
				combatStats += "Rapier Skill: " + flags[kFLAGS.RAPHAEL_RAPIER_TRANING] + " / 4\n";

			if (combatStats != "")
				outputText("Combat Stats\n" + combatStats);
			// End Combat Stats

			if (prison.inPrison || flags[kFLAGS.PRISON_CAPTURE_COUNTER] > 0) prison.displayPrisonStats();

			// Begin Children Stats
			var childStats:String = "";

			if (player.statusEffectv1(StatusEffects.Birthed) > 0)
				childStats += "Times Given Birth: " + player.statusEffectv1(StatusEffects.Birthed) + "\n";

			if (flags[kFLAGS.AMILY_MET] > 0 && (flags[kFLAGS.AMILY_BIRTH_TOTAL] + flags[kFLAGS.PC_TIMES_BIRTHED_AMILYKIDS]) > 0)
				childStats += "Litters With Amily: " + (flags[kFLAGS.AMILY_BIRTH_TOTAL] + flags[kFLAGS.PC_TIMES_BIRTHED_AMILYKIDS]) + "\n";

			if (flags[kFLAGS.BEHEMOTH_CHILDREN] > 0)
				childStats += "Children With Behemoth: " + flags[kFLAGS.BEHEMOTH_CHILDREN] + "\n";

			if (flags[kFLAGS.BENOIT_EGGS] > 0)
				childStats += "Benoit Eggs Laid: " + flags[kFLAGS.BENOIT_EGGS] + "\n";
			if (flags[kFLAGS.FEMOIT_EGGS_LAID] > 0)
				childStats += "Benoite Eggs Produced: " + flags[kFLAGS.FEMOIT_EGGS_LAID] + "\n";

			if (flags[kFLAGS.VOLCWITCHNUMBEROFBIRTHS] + flags[kFLAGS.VOLCWITCHNUMBEROFCHILDREN] > 0)
				childStats += "Children With Corrupted Witches: " + (flags[kFLAGS.VOLCWITCHNUMBEROFBIRTHS]+flags[kFLAGS.VOLCWITCHNUMBEROFCHILDREN]) + "\n";

			if (flags[kFLAGS.COTTON_KID_COUNT] > 0)
				childStats += "Children With Cotton: " + flags[kFLAGS.COTTON_KID_COUNT] + "\n";

			if (flags[kFLAGS.EDRYN_NUMBER_OF_KIDS] > 0)
				childStats += "Children With Edryn: " + flags[kFLAGS.EDRYN_NUMBER_OF_KIDS] + "\n";

			if (flags[kFLAGS.EMBER_CHILDREN_MALES] > 0)
				childStats += "Ember Offspring (Males): " + flags[kFLAGS.EMBER_CHILDREN_MALES] + "\n";
			if (flags[kFLAGS.EMBER_CHILDREN_FEMALES] > 0)
				childStats += "Ember Offspring (Females): " + flags[kFLAGS.EMBER_CHILDREN_FEMALES] + "\n";
			if (flags[kFLAGS.EMBER_CHILDREN_HERMS] > 0)
				childStats += "Ember Offspring (Herms): " + flags[kFLAGS.EMBER_CHILDREN_HERMS] + "\n";
			if (game.emberScene.emberChildren() > 0)
				childStats += "Total Children With Ember: " + (game.emberScene.emberChildren()) + "\n";

			if (flags[kFLAGS.EMBER_EGGS] > 0)
				childStats += "Ember Eggs Produced: " + flags[kFLAGS.EMBER_EGGS] + "\n";

			if (game.isabellaScene.totalIsabellaChildren() > 0) {
				if (game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_HUMAN_BOYS) > 0)
					childStats += "Children With Isabella (Human, Males): " + game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_HUMAN_BOYS) + "\n";
				if (game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_HUMAN_GIRLS) > 0)
					childStats += "Children With Isabella (Human, Females): " + game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_HUMAN_GIRLS) + "\n";
				if (game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_HUMAN_HERMS) > 0)
					childStats += "Children With Isabella (Human, Herms): " + game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_HUMAN_HERMS) + "\n";
				if (game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_COWGIRLS) > 0)
					childStats += "Children With Isabella (Cowgirl, Females): " + game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_COWGIRLS) + "\n";
				if (game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_COWFUTAS) > 0)
					childStats += "Children With Isabella (Cowgirl, Herms): " + game.isabellaScene.getIsabellaChildType(IsabellaScene.OFFSPRING_COWFUTAS) + "\n";
				childStats += "Total Children With Isabella: " + game.isabellaScene.totalIsabellaChildren() + "\n"
			}

			if (flags[kFLAGS.IZMA_CHILDREN_SHARKGIRLS] > 0)
				childStats += "Children With Izma (Sharkgirls): " + flags[kFLAGS.IZMA_CHILDREN_SHARKGIRLS] + "\n";
			if (flags[kFLAGS.IZMA_CHILDREN_TIGERSHARKS] > 0)
				childStats += "Children With Izma (Tigersharks): " + flags[kFLAGS.IZMA_CHILDREN_TIGERSHARKS] + "\n";
			if (flags[kFLAGS.IZMA_CHILDREN_SHARKGIRLS] > 0 && flags[kFLAGS.IZMA_CHILDREN_TIGERSHARKS] > 0)
				childStats += "Total Children with Izma: " + (flags[kFLAGS.IZMA_CHILDREN_SHARKGIRLS] + flags[kFLAGS.IZMA_CHILDREN_TIGERSHARKS]) + "\n";

			if (game.joyScene.getTotalLitters() > 0)
				childStats += "Litters With " + (flags[kFLAGS.JOJO_BIMBO_STATE] >= 3 ? "Joy" : "Jojo") + ": " + game.joyScene.getTotalLitters() + "\n";

			if (flags[kFLAGS.KELLY_KIDS_MALE] > 0)
				childStats += "Children With Kelly (Males): " + flags[kFLAGS.KELLY_KIDS_MALE] + "\n";
			if (flags[kFLAGS.KELLY_KIDS] - flags[kFLAGS.KELLY_KIDS_MALE] > 0)
				childStats += "Children With Kelly (Females): " + (flags[kFLAGS.KELLY_KIDS] - flags[kFLAGS.KELLY_KIDS_MALE]) + "\n";
			if (flags[kFLAGS.KELLY_KIDS] > 0)
				childStats += "Total Children With Kelly: " + flags[kFLAGS.KELLY_KIDS] + "\n";
			//if (game.kihaFollower.pregnancy.isPregnant) This was originally a debug.
			//	childStats += "Kiha's Pregnancy: " + game.kihaFollower.pregnancy.incubation + "\n";
			if (flags[kFLAGS.KIHA_CHILDREN_BOYS] > 0)
				childStats += "Kiha Offspring (Males): " + flags[kFLAGS.KIHA_CHILDREN_BOYS] + "\n";
			if (flags[kFLAGS.KIHA_CHILDREN_GIRLS] > 0)
				childStats += "Kiha Offspring (Females): " + flags[kFLAGS.KIHA_CHILDREN_GIRLS] + "\n";
			if (flags[kFLAGS.KIHA_CHILDREN_HERMS] > 0)
				childStats += "Kiha Offspring (Herms): " + flags[kFLAGS.KIHA_CHILDREN_HERMS] + "\n";
			if (game.kihaFollower.totalKihaChildren() > 0)
				childStats += "Total Children With Kiha: " + game.kihaFollower.totalKihaChildren() + "\n";

			if (game.mountain.salon.lynnetteApproval() != 0)
				childStats += "Lynnette Children: " + flags[kFLAGS.LYNNETTE_BABY_COUNT] + "\n";

			if (flags[kFLAGS.MARBLE_KIDS] > 0)
				childStats += "Children With Marble: " + flags[kFLAGS.MARBLE_KIDS] + "\n";

			if (flags[kFLAGS.MINERVA_CHILDREN] > 0)
				childStats += "Children With Minerva: " + flags[kFLAGS.MINERVA_CHILDREN] + "\n";

			if (flags[kFLAGS.ANT_KIDS] > 0)
				childStats += "Ant Children With Phylla: " + flags[kFLAGS.ANT_KIDS] + "\n";
			if (flags[kFLAGS.PHYLLA_DRIDER_BABIES_COUNT] > 0)
				childStats += "Drider Children With Phylla: " + flags[kFLAGS.PHYLLA_DRIDER_BABIES_COUNT] + "\n";
			if (flags[kFLAGS.ANT_KIDS] > 0 && flags[kFLAGS.PHYLLA_DRIDER_BABIES_COUNT] > 0)
				childStats += "Total Children With Phylla: " + (flags[kFLAGS.ANT_KIDS] + flags[kFLAGS.PHYLLA_DRIDER_BABIES_COUNT]) + "\n";

			if (flags[kFLAGS.SHEILA_JOEYS] > 0)
				childStats += "Children With Sheila (Joeys): " + flags[kFLAGS.SHEILA_JOEYS] + "\n";
			if (flags[kFLAGS.SHEILA_IMPS] > 0)
				childStats += "Children With Sheila (Imps): " + flags[kFLAGS.SHEILA_IMPS] + "\n";
			if (flags[kFLAGS.SHEILA_JOEYS] > 0 && flags[kFLAGS.SHEILA_IMPS] > 0)
				childStats += "Total Children With Sheila: " + (flags[kFLAGS.SHEILA_JOEYS] + flags[kFLAGS.SHEILA_IMPS]) + "\n";

			if (flags[kFLAGS.SOPHIE_ADULT_KID_COUNT] > 0 || flags[kFLAGS.SOPHIE_DAUGHTER_MATURITY_COUNTER] > 0)
			{
				childStats += "Children With Sophie: ";
				var sophie:int = 0;
				if (flags[kFLAGS.SOPHIE_DAUGHTER_MATURITY_COUNTER] > 0) sophie++;
				sophie += flags[kFLAGS.SOPHIE_ADULT_KID_COUNT];
				if (flags[kFLAGS.SOPHIE_CAMP_EGG_COUNTDOWN] > 0) sophie++;
				childStats += sophie + "\n";
			}

			if (flags[kFLAGS.SOPHIE_EGGS_LAID] > 0)
				childStats += "Eggs Fertilized For Sophie: " + (flags[kFLAGS.SOPHIE_EGGS_LAID] + sophie) + "\n";

			if (flags[kFLAGS.TAMANI_NUMBER_OF_DAUGHTERS] > 0)
				childStats += "Children With Tamani: " + flags[kFLAGS.TAMANI_NUMBER_OF_DAUGHTERS] + " (after all forms of natural selection)\n";

			if (game.urtaPregs.urtaKids() > 0)
				childStats += "Children With Urta: " + game.urtaPregs.urtaKids() + "\n";

			//Mino sons
			if (flags[kFLAGS.ADULT_MINOTAUR_OFFSPRINGS] > 0)
				childStats += "Number of Adult Minotaur Offspring: " + flags[kFLAGS.ADULT_MINOTAUR_OFFSPRINGS] + "\n";

			if (childStats != "")
				outputText("\nChildren\n" + childStats);
			// End Children Stats

			// Begin Body Stats
			var bodyStats:String = "";

			if (flags[kFLAGS.HUNGER_ENABLED] > 0 || flags[kFLAGS.IN_PRISON] > 0)
			{
				bodyStats += "Satiety: " + Math.floor(player.hunger) + " / 100 (";
				if (player.hunger <= 0) bodyStats += "Dying";
				if (player.hunger > 0 && player.hunger < 10) bodyStats += "Starving";
				if (player.hunger >= 10 && player.hunger < 25) bodyStats += "Very hungry";
				if (player.hunger >= 25 && player.hunger100 < 50) bodyStats += "Hungry";
				if (player.hunger100 >= 50 && player.hunger100 < 75) bodyStats += "Not hungry";
				if (player.hunger100 >= 75 && player.hunger100 < 90) bodyStats += "Satiated";
				if (player.hunger100 >= 90 && player.hunger100 < 100) bodyStats += "Full";
				if (player.hunger100 >= 100) bodyStats += "Very full";
				bodyStats += ")\n";
			}

			bodyStats += "Anal Capacity: " + Math.round(player.analCapacity()) + "\n";
			bodyStats += "Anal Looseness: " + Math.round(player.ass.analLooseness) + "\n";

			bodyStats += "Fertility (Base) Rating: " + Math.round(player.fertility) + "\n";
			bodyStats += "Fertility (With Bonuses) Rating: " + Math.round(player.totalFertility()) + "\n";

			if (player.cumQ() > 0) {
				bodyStats += "Virility Rating: " + Math.round(player.virilityQ() * 100) + "\n";
				if (flags[kFLAGS.HUNGER_ENABLED] >= 1) bodyStats += "Cum Production: " + addComma(Math.round(player.cumQ())) + " / " + addComma(Math.round(player.cumCapacity())) + "mL (" + Math.round((player.cumQ() / player.cumCapacity()) * 100) + "%) \n";
				else bodyStats += "Cum Production: " + addComma(Math.round(player.cumQ())) + "mL\n";
			}
			if (player.lactationQ() > 0)
				bodyStats += "Milk Production: " + addComma(Math.round(player.lactationQ())) + "mL\n";

			if (player.hasStatusEffect(StatusEffects.Feeder)) {
				bodyStats += "Hours Since Last Time Breastfed Someone:  " + player.statusEffectv2(StatusEffects.Feeder);
				if (player.statusEffectv2(StatusEffects.Feeder) >= 72)
					bodyStats += " (Too long! Sensitivity Increasing!)";

				bodyStats += "\n";
			}
			bodyStats += "Sexual Orientation: ";
			if (player.sexOrientation >= 90) bodyStats += "Strictly attracted to males";
			if (player.sexOrientation < 90 && player.sexOrientation >= 70 ) bodyStats += "Attracted to males";
			if (player.sexOrientation == 50) bodyStats += "Bisexual";
			else if (player.sexOrientation < 70 && player.sexOrientation >= 30) bodyStats += "Bicurious";
			if (player.sexOrientation < 30 && player.sexOrientation > 10) bodyStats += "Attracted to females";
			if (player.sexOrientation <= 10) bodyStats += "Strictly attracted to females";
			bodyStats += " (" + player.sexOrientation +")\n";
			bodyStats += "Pregnancy Speed Multiplier: ";
			var preg:Number = 1;
			if (player.findPerk(PerkLib.Diapause) >= 0)
				bodyStats += "? (Variable due to Diapause)\n";
			else {
				if (player.findPerk(PerkLib.MaraesGiftFertility) >= 0) preg++;
				if (player.findPerk(PerkLib.BroodMother) >= 0) preg++;
				if (player.findPerk(PerkLib.FerasBoonBreedingBitch) >= 0) preg++;
				if (player.findPerk(PerkLib.MagicalFertility) >= 0) preg++;
				if (player.findPerk(PerkLib.FerasBoonWideOpen) >= 0 || player.findPerk(PerkLib.FerasBoonMilkingTwat) >= 0) preg++;
				bodyStats += preg + "\n";
			}

			if (player.cocks.length > 0) {
				bodyStats += "Total Cocks: " + player.cocks.length + "\n";

				var totalCockLength:Number = 0;
				var totalCockGirth:Number = 0;

				for (var i:Number = 0; i < player.cocks.length; i++) {
						totalCockLength += player.cocks[i].cockLength;
						totalCockGirth += player.cocks[i].cockThickness
				}

				bodyStats += "Total Cock Length: " + Math.round(totalCockLength) + " inches\n";
				bodyStats += "Total Cock Girth: " + Math.round(totalCockGirth) + " inches\n";

			}

			if (player.vaginas.length > 0)
				bodyStats += "Vaginal Capacity: " + Math.round(player.vaginalCapacity()) + "\n" + "Vaginal Looseness: " + Math.round(player.looseness()) + "\n";

			if (player.findPerk(PerkLib.SpiderOvipositor) >= 0 || player.findPerk(PerkLib.BeeOvipositor) >= 0)
				bodyStats += "Ovipositor Total Egg Count: " + player.eggs() + "\nOvipositor Fertilized Egg Count: " + player.fertilizedEggs() + "\n";

			if (player.hasStatusEffect(StatusEffects.SlimeCraving)) {
				if (player.statusEffectv1(StatusEffects.SlimeCraving) >= 18)
					bodyStats += "Slime Craving: Active! You are currently losing strength and speed.  You should find fluids.\n";
				else {
					if (player.findPerk(PerkLib.SlimeCore) >= 0)
						bodyStats += "Slime Stored: " + ((17 - player.statusEffectv1(StatusEffects.SlimeCraving)) * 2) + " hours until you start losing strength.\n";
					else
						bodyStats += "Slime Stored: " + (17 - player.statusEffectv1(StatusEffects.SlimeCraving)) + " hours until you start losing strength.\n";
				}
			}

			if (bodyStats != "")
				outputText("\nBody Stats\n" + bodyStats);
			// End Body Stats

			// Begin Racial Scores display -Foxwells
			var raceScores:String = "";

			if (player.humanScore() > 0) {
				raceScores += "Human Score: " + player.humanScore() + "\n";
			}
			if (player.mutantScore() > 0) {
				raceScores += "Mutant Score: " + player.mutantScore() + "\n";
			}
			if (player.demonScore() > 0) {
				raceScores += "Demon Score: " + player.demonScore() + "\n";
			}
			if (player.goblinScore() > 0) {
				raceScores += "Goblin Score: " + player.goblinScore() + "\n";
			}
			if (player.gooScore() > 0) {
				raceScores += "Goo Score: " + player.gooScore() + "\n";
			}
			if (player.cowScore() > 0) {
				raceScores += "Cow Score: " + player.cowScore() + "\n";
			}
			if (player.minoScore() > 0) {
				raceScores += "Minotaur Score: " + player.minoScore() + "\n";
			}
			if (player.catScore() > 0) {
				raceScores += "Cat Score: " + player.catScore() + "\n";
			}
			if (player.dragonneScore() > 0) {
				raceScores += "Dragonne Score: " + player.dragonneScore() + "\n";
			}
			if (player.manticoreScore() > 0) {
				raceScores += "Manticore Score: " + player.manticoreScore() + "\n";
			}
			if (player.lizardScore() > 0) {
				raceScores += "Lizard Score: " + player.lizardScore() + "\n";
			}
			if (player.salamanderScore() > 0) {
				raceScores += "Salamander Score: " + player.salamanderScore() + "\n";
			}
			if (player.dragonScore() > 0) {
				raceScores += "Dragon Score: " + player.dragonScore() + "\n";
			}
			if (player.nagaScore() > 0) {
				raceScores += "Naga Score: " + player.nagaScore() + "\n";
			}
			if (player.sandTrapScore() > 0) {
				raceScores += "Sand Trap Score: " + player.sandTrapScore() + "\n";
			}
			if (player.harpyScore() > 0) {
				raceScores += "Avian Score: " + player.harpyScore() + "\n";
			}
			if (player.sharkScore() > 0) {
				raceScores += "Shark Score: " + player.sharkScore() + "\n";
			}
			if (player.sirenScore() > 0) {
				raceScores += "Siren Score: " + player.sirenScore() + "\n";
			}
			if (player.dogScore() > 0) {
				raceScores += "Dog Score: " + player.dogScore() + "\n";
			}
			if (player.wolfScore() > 0) {
				raceScores += "Wolf Score: " + player.wolfScore() + "\n";
			}
			if (player.foxScore() > 0) {
				raceScores += "Fox Score: " + player.foxScore() + "\n";
			}
			if (player.kitsuneScore() > 0) {
				raceScores += "Kitsune Score: " + player.kitsuneScore() + "\n";
			}
			if (player.echidnaScore() > 0) {
				raceScores += "Echidna Score: " + player.echidnaScore() + "\n";
			}
			if (player.mouseScore() > 0) {
				raceScores += "Mouse Score: " + player.mouseScore() + "\n";
			}
			if (player.ferretScore() > 0) {
				raceScores += "Ferret Score: " + player.ferretScore() + "\n";
			}
			if (player.raccoonScore() > 0) {
				raceScores += "Raccoon Score: " + player.raccoonScore() + "\n";
			}
			if (player.bunnyScore() > 0) {
				raceScores += "Bunny Score: " + player.bunnyScore() + "\n";
			}
			if (player.kangaScore() > 0) {
				raceScores += "Kangaroo Score: " + player.kangaScore() + "\n";
			}
			if (player.horseScore() > 0) {
				raceScores += "Horse Score: " + player.horseScore() + "\n";
			}
			if (player.deerScore() > 0) {
				raceScores += "Deer Score: " + player.deerScore() + "\n";
			}
			if (player.satyrScore() > 0) {
				raceScores += "Satyr Score: " + player.satyrScore() + "\n";
			}
			if (player.rhinoScore() > 0) {
				raceScores += "Rhino Score: " + player.rhinoScore() + "\n";
			}
			if (player.spiderScore() > 0) {
				raceScores += "Spider Score: " + player.spiderScore() + "\n";
			}
			if (player.pigScore() > 0) {
				raceScores += "Pig Score: " + player.pigScore() + "\n";
			}
			if (player.beeScore() > 0) {
				raceScores += "Bee Score: " + player.beeScore() + "\n";
			}
			if (player.cockatriceScore() > 0) {
				raceScores += "Cockatrice Score: " + player.cockatriceScore() + "\n";
			}
			if (player.redPandaScore() > 0) {
				raceScores += "Red-Panda Score: " + player.redPandaScore() + "\n";
			}
			if (player.redPandaScore() > 0) {
				raceScores += "Red-Panda Score: " + player.redPandaScore() + "\n";
			}

			if (raceScores != "")
				outputText("\nRacial Scores\n" + raceScores);
			// End Racial Scores display -Foxwells

			// Begin Misc Stats
			var miscStats:String = "";

			if (camp.getCampPopulation() > 0)
				miscStats += "Camp Population: " + camp.getCampPopulation() + "\n";

			if (flags[kFLAGS.CORRUPTED_GLADES_DESTROYED] > 0) {
				if (flags[kFLAGS.CORRUPTED_GLADES_DESTROYED] < 100)
					miscStats += "Corrupted Glades Status: " + (100 - flags[kFLAGS.CORRUPTED_GLADES_DESTROYED]) + "% remaining\n";
				else
					miscStats += "Corrupted Glades Status: Extinct\n";
			}

			if (flags[kFLAGS.EGGS_BOUGHT] > 0)
				miscStats += "Eggs Traded For: " + flags[kFLAGS.EGGS_BOUGHT] + "\n";

			if (flags[kFLAGS.TIMES_AUTOFELLATIO_DUE_TO_CAT_FLEXABILITY] > 0)
				miscStats += "Times Had Fun with Feline Flexibility: " + flags[kFLAGS.TIMES_AUTOFELLATIO_DUE_TO_CAT_FLEXABILITY] + "\n";

			if (flags[kFLAGS.FAP_ARENA_SESSIONS] > 0)
				miscStats += "Times Circle Jerked in the Arena: " + flags[kFLAGS.FAP_ARENA_SESSIONS] + "\nVictories in the Arena: " + flags[kFLAGS.FAP_ARENA_VICTORIES] + "\n";

			if (flags[kFLAGS.SPELLS_CAST] > 0)
				miscStats += "Spells Cast: " + flags[kFLAGS.SPELLS_CAST] + "\n";

			if (flags[kFLAGS.TIMES_BAD_ENDED] > 0)
				miscStats += "Times Bad-Ended: " + flags[kFLAGS.TIMES_BAD_ENDED] + "\n";

			if (flags[kFLAGS.TIMES_ORGASMED] > 0)
				miscStats += "Times Orgasmed: " + flags[kFLAGS.TIMES_ORGASMED] + "\n";

			if (game.bimboProgress.ableToProgress()) {
				if (flags[kFLAGS.TIMES_ORGASM_DICK] > 0)
					miscStats += "Dick tension: " + flags[kFLAGS.TIMES_ORGASM_DICK] + "\n";
				if (flags[kFLAGS.TIMES_ORGASM_ANAL] > 0)
					miscStats += "Butt tension: " + flags[kFLAGS.TIMES_ORGASM_ANAL] + "\n";
				if (flags[kFLAGS.TIMES_ORGASM_VAGINAL] > 0)
					miscStats += "Pussy tension: " + flags[kFLAGS.TIMES_ORGASM_VAGINAL] + "\n";
				if (flags[kFLAGS.TIMES_ORGASM_TITS] > 0)
					miscStats += "Tits tension: " + flags[kFLAGS.TIMES_ORGASM_TITS] + "\n";
				if (flags[kFLAGS.TIMES_ORGASM_LIPS] > 0)
					miscStats += "Lips tension: " + flags[kFLAGS.TIMES_ORGASM_LIPS] + "\n";
				miscStats += "Bimbo score: " + Math.round(player.bimboScore() * 10) + "\n";
			}

			if (miscStats != "")
				outputText("\nMiscellaneous Stats\n" + miscStats);
			// End Misc Stats

			// Begin Addition Stats
			var addictStats:String = "";
			//Marble Milk Addition
			if (player.statusEffectv3(StatusEffects.Marble) > 0) {
				addictStats += "Marble Milk: ";
				if (player.findPerk(PerkLib.MarbleResistant) < 0 && player.findPerk(PerkLib.MarblesMilk) < 0)
					addictStats += Math.round(player.statusEffectv2(StatusEffects.Marble)) + "%\n";
				else if (player.findPerk(PerkLib.MarbleResistant) >= 0)
					addictStats += "0%\n";
				else
					addictStats += "100%\n";
			}

			// Corrupted Minerva's Cum Addiction
			if (flags[kFLAGS.MINERVA_CORRUPTION_PROGRESS] >= 10 && flags[kFLAGS.MINERVA_CORRUPTED_CUM_ADDICTION] > 0) {
				addictStats += "Minerva's Cum: " + (flags[kFLAGS.MINERVA_CORRUPTED_CUM_ADDICTION] * 20) + "%";
			}

			// Mino Cum Addiction
			if (flags[kFLAGS.MINOTAUR_CUM_INTAKE_COUNT] > 0 || flags[kFLAGS.MINOTAUR_CUM_ADDICTION_TRACKER] > 0 || player.findPerk(PerkLib.MinotaurCumAddict) >= 0 || player.findPerk(PerkLib.MinotaurCumResistance) >= 0) {
				if (player.findPerk(PerkLib.MinotaurCumAddict) < 0)
					addictStats += "Minotaur Cum: " + Math.round(flags[kFLAGS.MINOTAUR_CUM_ADDICTION_TRACKER] * 10)/10 + "%\n";
				else if (player.findPerk(PerkLib.MinotaurCumResistance) >= 0)
					addictStats += "Minotaur Cum: 0% (Immune)\n";
				else
					addictStats += "Minotaur Cum: 100+%\n";
			}

			if (addictStats != "")
				outputText("\nAddictions\n" + addictStats);
			// End Addition Stats

			// Begin Interpersonal Stats
			var interpersonStats:String = "";
			if((flags[kFLAGS.AMILY_MET] == 1 || flags[kFLAGS.AMILY_FOLLOWER] == 1)) {
				interpersonStats += "Amily's Affection: " + flags[kFLAGS.AMILY_AFFECTION] + "\n"
			}

			if (game.dungeons.palace.anzuScene.anzuRelationshipLevel() > 0) {
				interpersonStats += "Anzu's Affection: " + flags[kFLAGS.ANZU_AFFECTION] + "%\n";
				interpersonStats += "Anzu's Relationship Level: " + (flags[kFLAGS.ANZU_RELATIONSHIP_LEVEL] == 1 ? "Acquaintances" : flags[kFLAGS.ANZU_RELATIONSHIP_LEVEL] == 2 ? "Friend" : flags[kFLAGS.ANZU_RELATIONSHIP_LEVEL] == 3 ? "Close Friend" : flags[kFLAGS.ANZU_RELATIONSHIP_LEVEL] == 4 ? "Lover" : "Undefined") + "\n";
			}

			if (flags[kFLAGS.ARIAN_PARK] > 0)
				interpersonStats += "Arian's Health: " + Math.round(game.arianScene.arianHealth()) + "\n";

			if (flags[kFLAGS.ARIAN_VIRGIN] > 0)
				interpersonStats += "Arian Sex Counter: " + Math.round(flags[kFLAGS.ARIAN_VIRGIN]) + "\n";

			if (game.bazaar.benoit.benoitAffection() > 0)
				interpersonStats += "" + game.bazaar.benoit.benoitMF("Benoit", "Benoite") + " Affection: " + Math.round(game.bazaar.benoit.benoitAffection()) + "%\n";

			if (flags[kFLAGS.BROOKE_MET] > 0)
				interpersonStats += "Brooke's Affection: " + Math.round(game.telAdre.brooke.brookeAffection()) + "\n";

			if (flags[kFLAGS.CERAPH_DICKS_OWNED] + flags[kFLAGS.CERAPH_PUSSIES_OWNED] + flags[kFLAGS.CERAPH_TITS_OWNED] > 0)
				interpersonStats += "Body Parts Taken By Ceraph: " + (flags[kFLAGS.CERAPH_DICKS_OWNED] + flags[kFLAGS.CERAPH_PUSSIES_OWNED] + flags[kFLAGS.CERAPH_TITS_OWNED]) + "\n";

			if (game.emberScene.emberAffection() > 0)
				interpersonStats += "Ember's Affection: " + Math.round(game.emberScene.emberAffection()) + "%\n";
			if (game.emberScene.emberSparIntensity() > 0)
				interpersonStats += "Ember Spar Intensity: " + game.emberScene.emberSparIntensity() + "\n";

			if (game.helFollower.helAffection() > 0)
				interpersonStats += "Helia's Affection: " + Math.round(game.helFollower.helAffection()) + "%\n";
			if (game.helFollower.helAffection() >= 100)
				interpersonStats += "Helia Bonus Points: " + Math.round(flags[kFLAGS.HEL_BONUS_POINTS]) + "\n";
			if (game.helFollower.followerHel())
				interpersonStats += "Helia Spar Intensity: " + game.helScene.heliaSparIntensity() + "\n";
			if (flags[kFLAGS.HELIA_ADDICTION_LEVEL] > 0){
				interpersonStats += "Helia Cum Addiction: " + Math.min(flags[kFLAGS.HELIA_ADDICTION_LEVEL],100) + "%\n";
			}
			if (flags[kFLAGS.ISABELLA_AFFECTION] > 0) {
				interpersonStats += "Isabella's Affection: ";

				if (!game.isabellaFollowerScene.isabellaFollower())
					interpersonStats += Math.round(flags[kFLAGS.ISABELLA_AFFECTION]) + "%\n", false;
				else
					interpersonStats += "100%\n";
			}

			if (flags[kFLAGS.JOJO_BIMBO_STATE] >= 3) {
				interpersonStats += "Joy's Intelligence: " + flags[kFLAGS.JOY_INTELLIGENCE];
				if (flags[kFLAGS.JOY_INTELLIGENCE] >= 50) interpersonStats += " (MAX)"
				interpersonStats += "\n";
			}

			if (flags[kFLAGS.KATHERINE_UNLOCKED] >= 4) {
				interpersonStats += "Katherine's Submissiveness: " + game.telAdre.katherine.submissiveness() + "\n";
			}

			if (player.hasStatusEffect(StatusEffects.Kelt) && flags[kFLAGS.KELT_BREAK_LEVEL] == 0 && flags[kFLAGS.KELT_KILLED] == 0) {
				if (player.statusEffectv2(StatusEffects.Kelt) >= 130)
					interpersonStats += "Submissiveness To Kelt: " + 100 + "%\n";
				else
					interpersonStats += "Submissiveness To Kelt: " + Math.round(player.statusEffectv2(StatusEffects.Kelt) / 130 * 100) + "%\n";

			}

			if (flags[kFLAGS.ANEMONE_KID] > 0)
				interpersonStats += "Kid A's Confidence: " + game.anemoneScene.kidAXP() + "%\n";

			if (flags[kFLAGS.KIHA_AFFECTION_LEVEL] >= 2 || game.kihaFollower.followerKiha()) {
				if (game.kihaFollower.followerKiha())
					interpersonStats += "Kiha's Affection: " + 100 + "%\n";
				else
					interpersonStats += "Kiha's Affection: " + Math.round(flags[kFLAGS.KIHA_AFFECTION]) + "%\n";
			}
			//Lottie stuff
			if (flags[kFLAGS.LOTTIE_ENCOUNTER_COUNTER] > 0)
				interpersonStats += "Lottie's Encouragement: " + game.telAdre.lottie.lottieMorale() + " (higher is better)\n" + "Lottie's Figure: " + game.telAdre.lottie.lottieTone() + " (higher is better)\n";

			if (game.mountain.salon.lynnetteApproval() != 0)
				interpersonStats += "Lynnette's Approval: " + game.mountain.salon.lynnetteApproval() + "\n";

			if (player.statusEffectv1(StatusEffects.Marble) > 0)
				interpersonStats += "Marble's Affection: " + player.statusEffectv1(StatusEffects.Marble) + "%\n";

			if (flags[kFLAGS.OWCAS_ATTITUDE] > 0)
				interpersonStats += "Owca's Attitude: " + flags[kFLAGS.OWCAS_ATTITUDE] + "\n";

			if (game.telAdre.pablo.pabloAffection() > 0)
				interpersonStats += "Pablo's Affection: " + flags[kFLAGS.PABLO_AFFECTION] + "%\n";

			if (game.telAdre.rubi.rubiAffection() > 0)
				interpersonStats += "Rubi's Affection: " + Math.round(game.telAdre.rubi.rubiAffection()) + "%\n" + "Rubi's Orifice Capacity: " + Math.round(game.telAdre.rubi.rubiCapacity()) + "%\n";

			if (flags[kFLAGS.SHEILA_XP] != 0) {
				interpersonStats += "Sheila's Corruption: " + game.sheilaScene.sheilaCorruption();
				if (game.sheilaScene.sheilaCorruption() > 100)
					interpersonStats += " (Yes, it can go above 100)";
				interpersonStats += "\n";
			}

			if (game.sylviaScene.sylviaProg > 0) {
				interpersonStats += "Sylvia's Affection: " + game.sylviaScene.sylviaGetAff + "%\n";
				interpersonStats += "Sylvia's Dominance: " + game.sylviaScene.sylviaGetDom + "%\n";
			}

			if (game.valeria.valeriaFluidsEnabled()) {
				interpersonStats += "Valeria's Fluid: " + flags[kFLAGS.VALERIA_FLUIDS] + "%\n"
			}

			if (flags[kFLAGS.URTA_COMFORTABLE_WITH_OWN_BODY] != 0 && !urtaDisabled) {
				if (game.urta.urtaLove()) {
					if (flags[kFLAGS.URTA_QUEST_STATUS] == -1) interpersonStats += "Urta's Status: Gone\n";
					if (flags[kFLAGS.URTA_QUEST_STATUS] == 0) interpersonStats += "Urta's Status: Lover\n";
					if (flags[kFLAGS.URTA_QUEST_STATUS] == 1) interpersonStats += "Urta's Status: Lover+\n";
				}
				else if (flags[kFLAGS.URTA_COMFORTABLE_WITH_OWN_BODY] == -1)
					interpersonStats += "Urta's Status: Ashamed\n";
				else if (flags[kFLAGS.URTA_PC_AFFECTION_COUNTER] < 30)
					interpersonStats += "Urta's Affection: " + Math.round(flags[kFLAGS.URTA_PC_AFFECTION_COUNTER] * 3.3333) + "%\n";
				else
					interpersonStats += "Urta's Status: Ready To Confess Love\n";
			}

			if (flags[kFLAGS.AIKO_TIMES_MET] > 0) {
				interpersonStats  += "Aiko affection: "+flags[kFLAGS.AIKO_AFFECTION]+"\n";
				if (flags[kFLAGS.AIKO_CORRUPTION_ACTIVE] > 0)
					interpersonStats  += "Aiko corruption: "+flags[kFLAGS.AIKO_CORRUPTION]+"\n";
			}

			if (interpersonStats != "")
				outputText("\nInterpersonal Stats\n" + interpersonStats);
			// End Interpersonal Stats

			// Begin Ongoing Stat Effects
			var statEffects:String = "";

			if (player.inHeat)
				statEffects += "Heat - " + Math.round(player.statusEffectv3(StatusEffects.Heat)) + " hours remaining\n";

			if (player.inRut)
				statEffects += "Rut - " + Math.round(player.statusEffectv3(StatusEffects.Rut)) + " hours remaining\n";

			if (player.hasStatusEffect(StatusEffects.ParasiteEel)){
				statEffects += "Number of eel parasites: " + player.statusEffectv1(StatusEffects.ParasiteEel) + "\n";
			}
			if (player.hasStatusEffect(StatusEffects.ParasiteEelNeedCum)){
				switch(player.statusEffectv2(StatusEffects.ParasiteEelNeedCum)){
					case 1:
						statEffects += "Eel parasite hungers for imp cum\n";
						break;
					case 4:
						statEffects += "Eel parasite hungers for mouse cum\n";
						break;
					case 2:
						statEffects += "Eel parasite hungers for minotaur cum\n";
						break;
					case 10:
						statEffects += "Eel parasite hungers for anemone cum\n";
						break;
                    case PregnancyStore.PREGNANCY_TENTACLE_BEAST_SEED:
                        statEffects += "Eel parasite hungers for tentacle beast cum\n";
                        break;
				}
			}
			if (player.hasStatusEffect(StatusEffects.ParasiteNephila)){
				statEffects += "Nephila Parasite Infestation Level: " + player.statusEffectv1(StatusEffects.ParasiteNephila) + "\n";
			}
			if (player.hasStatusEffect(StatusEffects.ParasiteNephilaNeedCum)){
				switch(player.statusEffectv2(StatusEffects.ParasiteNephilaNeedCum)){
					case 1:
						statEffects += "Nephila parasites hunger for " + Math.round(player.statusEffectv3(StatusEffects.ParasiteNephilaNeedCum)) + " liters of imp cum.\n";
						break;
					case 4:
						statEffects += "Nephila parasites hunger for " + Math.round(player.statusEffectv3(StatusEffects.ParasiteNephilaNeedCum)) + " liters of mouse cum.\n";
						break;
					case 2:
						statEffects += "Nephila parasites hunger  for " + Math.round(player.statusEffectv3(StatusEffects.ParasiteNephilaNeedCum)) + " liters of minotaur cum.\n";
						break;
					case 10:
						statEffects += "Nephila parasites hunger for " + Math.round(player.statusEffectv3(StatusEffects.ParasiteNephilaNeedCum)) + " liters of anemone cum.\n";
						break;
				}
			}

			if (player.statusEffectv1(StatusEffects.Luststick) > 0)
				statEffects += "Luststick - " + Math.round(player.statusEffectv1(StatusEffects.Luststick)) + " hours remaining\n";

			if (player.statusEffectv1(StatusEffects.LustStickApplied) > 0)
				statEffects += "Luststick Application - " + Math.round(player.statusEffectv1(StatusEffects.LustStickApplied)) + " hours remaining\n";

			if (player.statusEffectv1(StatusEffects.LustyTongue) > 0)
				statEffects += "Lusty Tongue - " + Math.round(player.statusEffectv1(StatusEffects.LustyTongue)) + " hours remaining\n";

			if (player.statusEffectv1(StatusEffects.BlackCatBeer) > 0)
				statEffects += "Black Cat Beer - " + player.statusEffectv1(StatusEffects.BlackCatBeer) + " hours remaining (Lust resistance 20% lower, physical resistance 25% higher.)\n";

			if (player.statusEffectv1(StatusEffects.AndysSmoke) > 0)
				statEffects += "Andy's Pipe Smoke - " + player.statusEffectv1(StatusEffects.AndysSmoke) + " hours remaining (Speed temporarily lowered, intelligence temporarily increased.)\n";

			if (player.statusEffectv1(StatusEffects.UmasMassage) > 0)
				statEffects += "Uma's Massage - " + player.statusEffectv3(StatusEffects.UmasMassage) + " hours remaining.\n";

			if (player.statusEffectv1(StatusEffects.Dysfunction) > 0)
				statEffects += "Dysfunction - " + player.statusEffectv1(StatusEffects.Dysfunction) + " hours remaining. (Disables masturbation)\n";

			if (player.statusEffectv2(StatusEffects.GlobalFatigue) > 0)
				statEffects += "Exhaustion - " + player.statusEffectv2(StatusEffects.GlobalFatigue) + " hours remaining. (increases fatigue costs by " + player.statusEffectv1(StatusEffects.GlobalFatigue) + "%)";

			if (statEffects != "")
				outputText("\nOngoing Status Effects\n" + statEffects);
			// End Ongoing Stat Effects
			menu();
			if (player.statPoints > 0) {
				outputText("\n\nYou have " + num2Text(player.statPoints) + " attribute point" + (player.statPoints == 1 ? "" : "s") + " to distribute.");
				addButton(0, "Stat Up", attributeMenu);
			}
			addButtonDisabled(0, "Stats");
			addButton(1, "Mastery", displayMastery);
			addButton(14, "Back", playerMenu);
		}

		//------------
		// PERKS
		//------------
		public function displayPerks():void {
			clearOutput();
			displayHeader("Perks");
			for (var i:int = 0; i < player.perks.length; i++) {
				outputText("" + player.perks[i].perkName + " - " + player.perks[i].perkDesc + "\n");
			}
			menu();
			addButtonDisabled(0, "Perks");
			addButton(1, "Database", perkDatabase);
			addButton(14, "Back", playerMenu);
			if (player.perkPoints > 0) {
				outputText("\nYou have " + num2Text(player.perkPoints) + " perk point");
				if (player.perkPoints > 1) outputText("s");
				outputText(" to spend.");
				addNextButton("Perk Up", perkBuyMenu);
			}
			if (player.findPerk(PerkLib.DoubleAttack) >= 0) {
				outputText("\nYou can adjust your double attack settings.");
				addNextButton("Dbl Options",doubleAttackOptions).hint("Set double attack options.");
			}
			if (player.findPerk(PerkLib.AscensionTolerance) >= 0){
				outputText("\nYou can adjust your Corruption Tolerance threshold.");
				addNextButton("Tol. Options",ascToleranceOption).hint("Set whether or not Corruption Tolerance is applied.");
			}
			if (player.findPerk(PerkLib.Battlemage) >= 0){
				outputText("\nYou can set whether or not to automatically cast Might when combat starts.");
				addNextButton("Might Opt.",mightOption).hint("Set whether or not automatic Might is applied.");
			}
			if (player.findPerk(PerkLib.Spellsword) >= 0){
				outputText("\nYou can set whether or not to automatically cast Charge Weapon when combat starts.");
				addNextButton("Charge Opt.",chargeOption).hint("Set whether or not automatic Charge Weapon is applied.");
			}
			if (player.hasPerk(PerkLib.TransformationResistance)) {
				outputText("\nYou can enable or disable your transformation resistance.");
				addNextButton("TF Resist",tfResistOption).hint("Choose whether or not to resist transformation and avoid bad ends.");
			}
		}

		public function doubleAttackOptions():void {
			clearOutput();
			menu();
			if (flags[kFLAGS.DOUBLE_ATTACK_STYLE] == 0) {
				outputText("You will currently always double attack in combat.  If your strength exceeds sixty, your double-attacks will be done at sixty strength in order to double-attack.");
				outputText("\n\nYou can change it to double attack until sixty strength and then dynamically switch to single attacks.");
				outputText("\nYou can change it to always single attack.");
			}
			else if (flags[kFLAGS.DOUBLE_ATTACK_STYLE] == 1) {
				outputText("You will currently double attack until your strength exceeds sixty, and then single attack.");
				outputText("\n\nYou can choose to force double attacks at reduced strength (when over sixty, it makes attacks at a strength of sixty.");
				outputText("\nYou can change it to always single attack.");
			}
			else {
				outputText("You will always single attack your foes in combat.");
				outputText("\n\nYou can choose to force double attacks at reduced strength (when over sixty, it makes attacks at a strength of sixty.");
				outputText("\nYou can change it to double attack until sixty strength and then switch to single attacks.");
			}
			addButton(0, "All Double", doubleAttackForce).disableIf(flags[kFLAGS.DOUBLE_ATTACK_STYLE] == 0, "This is the current setting.");
			addButton(1, "Dynamic", doubleAttackDynamic).disableIf(flags[kFLAGS.DOUBLE_ATTACK_STYLE] == 1, "This is the current setting.");
			addButton(2, "Single", doubleAttackOff).disableIf(flags[kFLAGS.DOUBLE_ATTACK_STYLE] == 2, "This is the current setting.");
			addButton(14, "Back", displayPerks);
		}

		public function doubleAttackForce():void {
			flags[kFLAGS.DOUBLE_ATTACK_STYLE] = 0;
			doubleAttackOptions();
		}
		public function doubleAttackDynamic():void {
			flags[kFLAGS.DOUBLE_ATTACK_STYLE] = 1;
			doubleAttackOptions();
		}
		public function doubleAttackOff():void {
			flags[kFLAGS.DOUBLE_ATTACK_STYLE] = 2;
			doubleAttackOptions();
		}

		public function ascToleranceOption():void{
			clearOutput();
			menu();
			if (player.perkv2(PerkLib.AscensionTolerance) == 0){
				outputText("Corruption Tolerance is under effect, giving you " + player.corruptionTolerance() + " tolerance on most corruption events." +
				"\n\nYou can disable this perk's effects at any time.Some camp followers may leave you immediately after doing this. Save beforehand!");
				addButton(0, "Disable", disableTolerance);
			}else addButtonDisabled(0, "Disable", "The perk is already disabled.");
			if (player.perkv2(PerkLib.AscensionTolerance) == 1){
				outputText("Ascension Tolerance is not under effect. You may enable it at any time.");
				addButton(1, "Enable", enableTolerance);
			}else addButtonDisabled(1, "Enable", "The perk is already enabled.");
			addButton(14, "Back", displayPerks);
		}

		public function mightOption():void{
			clearOutput();
			menu();
			if (player.perkv1(PerkLib.Battlemage) == 0){
				outputText("Battlemage is under effect, automatically casting Might when combat begins." +
				"\n\nYou can disable this perk's effects at any time.");
				addButton(0, "Disable", disableMight);
			}else addButtonDisabled(0, "Disable", "The perk is already disabled.");
			if (player.perkv1(PerkLib.Battlemage) == 1){
				outputText("Battlemage is not under effect. You may enable it at any time.");
				addButton(1, "Enable", enableMight);
			}else addButtonDisabled(1, "Enable", "The perk is already enabled.");
			addButton(14, "Back", displayPerks);
		}

		public function disableMight():void{
			player.setPerkValue(PerkLib.Battlemage, 1, 1);
			mightOption();
		}
		public function enableMight():void{
			player.setPerkValue(PerkLib.Battlemage, 1, 0);
			mightOption();
		}

		public function chargeOption():void{
			clearOutput();
			menu();
			if (player.perkv1(PerkLib.Spellsword) == 0){
				outputText("Spellsword is under effect, automatically casting Charge Weapon when combat begins." +
				"\n\nYou can disable this perk's effects at any time.");
				addButton(1, "Disable", disableCharge);
			}else addButtonDisabled(1, "Disable", "The perk is already disabled.");
			if (player.perkv1(PerkLib.Spellsword) == 1){
				outputText("Spellsword is not under effect. You may enable it at any time.");
				addButton(0, "Enable", enableCharge);
			}else addButtonDisabled(0, "Enable", "The perk is already enabled.");
			addButton(14, "Back", displayPerks);
		}

		public function disableCharge():void{
			player.setPerkValue(PerkLib.Spellsword, 1, 1);
			chargeOption();
		}
		public function enableCharge():void{
			player.setPerkValue(PerkLib.Spellsword, 1, 0);
			chargeOption();
		}

		public function disableTolerance():void{
			player.setPerkValue(PerkLib.AscensionTolerance, 2, 1);
			ascToleranceOption();
		}
		public function enableTolerance():void{
			player.setPerkValue(PerkLib.AscensionTolerance, 2, 0);
			ascToleranceOption();
		}

		public function tfResistOption():void {
			clearOutput();
			if (player.perkv2(PerkLib.TransformationResistance) == 0) outputText("Transformation resistance is enabled. Transformative items are less effective on you and you won't get Bad Ends from overusing transformatives.");
			else outputText("Transformation resistance is disabled. Enable it to reduce transformation chance and avoid Bad Ends from overusing transformations.");
			menu();
			addButton(0, "Enable", enableResistance).disableIf(player.perkv2(PerkLib.TransformationResistance) == 0, "The perk is enabled.");
			addButton(1, "Disable", disableResistance).disableIf(player.perkv2(PerkLib.TransformationResistance) != 0, "The perk is disabled.");
			addButton(14, "Back", displayPerks);
		}

		public function enableResistance():void{
			player.setPerkValue(PerkLib.TransformationResistance, 2, 0);
			tfResistOption();
		}
		public function disableResistance():void{
			player.setPerkValue(PerkLib.TransformationResistance, 2, 1);
			tfResistOption();
		}

		public function perkDatabase(page:int=0, count:int=20):void {
			var allPerks:Array = PerkTree.obtainablePerks();
			clearOutput();
			var perks:Array = allPerks.slice(page*count,(page+1)*count);
			displayHeader("All Perks ("+(1+page*count)+"-"+(page*count+perks.length)+
					"/"+allPerks.length+")");
			for each (var ptype:PerkType in perks) {
				var pclass:Perk = player.perk(player.findPerk(ptype));

				var color:String;
				if (pclass) color='#000000'; // has perk
				else if (ptype.available(player)) color='#228822'; // can take on next lvl
				else color='#aa8822'; // requirements not met

				outputText(""+ptype.name+": ");
				outputText(pclass?ptype.desc(pclass):ptype.longDesc);
				if (!pclass && ptype.requirements.length>0) {
					var reqs:Array = [];
					for each (var cond:Object in ptype.requirements) {
						if (cond.fn(player)) color='#000000';
						else color='#aa2222';
						reqs.push(""+cond.text+"");
					}
					outputText("
Requires: " + reqs.join(", ")+".
");
				} else {
					outputText("\n");
				}
			}
			menu();
			addButton(0, "Perks", displayPerks);
			addButtonDisabled(1, "Database");
			addButton(4, "Next", perkDatabase,page+1).disableIf((page+1)*count>=allPerks.length);
			addButton(9, "Previous", perkDatabase,page-1).disableIf(page<=0);
			addButton(14, "Back", playerMenu);
		}

		//------------
		// LEVEL UP
		//------------
		public function raiseLevel():void {
			player.XP -= player.requiredXP();
			player.level++;
			player.perkPoints++;
			switch (player.age) { //Age modifies stat points gained
				case Age.AGE_CHILD:
					player.statPoints += 6;
					break;
				case Age.AGE_ELDER:
					player.statPoints += 2;
					break;
				default: //Defaults to adult
					player.statPoints += 5;
					break;
			}
			if (player.level % 2 == 0) player.ascensionPerkPoints++;
		}
		public function levelUpGo():void {
			clearOutput();
			hideMenus();
			//Level up
			if (player.XP >= player.requiredXP() && player.level < game.levelCap) {
				var tempPerkPoints:int = player.perkPoints;
				var tempStatPoints:int = player.statPoints;
				if (flags[kFLAGS.SHIFT_KEY_DOWN] == 1) {
					while (player.XP >= player.requiredXP() && player.level < game.levelCap) {
						raiseLevel();
					}
				}
				else raiseLevel();
				tempPerkPoints = player.perkPoints - tempPerkPoints;
				tempStatPoints = player.statPoints - tempStatPoints;
				outputText("You are now level " + num2Text(player.level) + "!\n\nYou have gained " + num2Text(tempStatPoints) + " attribute points and " + num2Text(tempPerkPoints) + " perk point" + (tempPerkPoints>1?"s":"") + "!");
				doNext(attributeMenu);
			}
			//Spend attribute points
			else if (player.statPoints > 0) {
				attributeMenu();
			}
			//Spend perk points
			else if (player.perkPoints > 0) {
				perkBuyMenu();
			}
			else {
				outputText("ERROR.  LEVEL UP PUSHED WHEN PC CANNOT LEVEL OR GAIN PERKS.  PLEASE REPORT THE STEPS TO REPRODUCE THIS BUG TO FENOXO@GMAIL.COM OR THE FENOXO.COM BUG REPORT FORUM.");
				doNext(playerMenu);
			}
		}

		//Attribute menu
		private function attributeMenu():void {
			clearOutput();
			outputText("You have " + (player.statPoints) + " left to spend.\n\n");

			outputText("Strength: ");
			if (player.str < player.getMaxStats("str")) outputText("" + Math.floor(player.str) + " + " + player.tempStr + " → " + Math.floor(player.str + player.tempStr) + "\n");
			else outputText("" + Math.floor(player.str) + " (Maximum)\n");

			outputText("Toughness: ");
			if (player.tou < player.getMaxStats("tou")) outputText("" + Math.floor(player.tou) + " + " + player.tempTou + " → " + Math.floor(player.tou + player.tempTou) + "\n");
			else outputText("" + Math.floor(player.tou) + " (Maximum)\n");

			outputText("Speed: ");
			if (player.spe < player.getMaxStats("spe")) outputText("" + Math.floor(player.spe) + " + " + player.tempSpe + " → " + Math.floor(player.spe + player.tempSpe) + "\n");
			else outputText("" + Math.floor(player.spe) + " (Maximum)\n");

			outputText("Intelligence: ");
			if (player.inte < player.getMaxStats("int")) outputText("" + Math.floor(player.inte) + " + " + player.tempInt + " → " + Math.floor(player.inte + player.tempInt) + "\n");
			else outputText("" + Math.floor(player.inte) + " (Maximum)\n");

			menu();
			//Add
			if (player.statPoints > 0) {
				if ((player.str + player.tempStr) < player.getMaxStats("str")) addButton(0, "Add STR", addAttribute, "str").hint("Add 1 point to Strength.", "Add Strength");
				if ((player.tou + player.tempTou) < player.getMaxStats("tou")) addButton(1, "Add TOU", addAttribute, "tou").hint("Add 1 point to Toughness.", "Add Toughness");
				if ((player.spe + player.tempSpe) < player.getMaxStats("spe")) addButton(2, "Add SPE", addAttribute, "spe").hint("Add 1 point to Speed.", "Add Speed");
				if ((player.inte + player.tempInt) < player.getMaxStats("int")) addButton(3, "Add INT", addAttribute, "int").hint("Add 1 point to Intelligence.", "Add Intelligence");
			}
			//Subtract
			if (player.tempStr > 0) addButton(5, "Sub STR", subtractAttribute, "str").hint("Subtract 1 point from Strength.", "Subtract Strength");
			if (player.tempTou > 0) addButton(6, "Sub TOU", subtractAttribute, "tou").hint("Subtract 1 point from Toughness.", "Subtract Toughness");
			if (player.tempSpe > 0) addButton(7, "Sub SPE", subtractAttribute, "spe").hint("Subtract 1 point from Speed.", "Subtract Speed");
			if (player.tempInt > 0) addButton(8, "Sub INT", subtractAttribute, "int").hint("Subtract 1 point from Intelligence.", "Subtract Intelligence");

			addButton(4, "Reset", resetAttributes);
			addButton(9, "Done", finishAttributes);
		}

		private function addAttribute(attribute:String):void {
			switch(attribute) {
				case "str":
					player.tempStr++;
					break;
				case "tou":
					player.tempTou++;
					break;
				case "spe":
					player.tempSpe++;
					break;
				case "int":
					player.tempInt++;
					break;
				default:
					player.statPoints++; //Failsafe
			}
			player.statPoints--;
			attributeMenu();
		}
		private function subtractAttribute(attribute:String):void {
			switch(attribute) {
				case "str":
					player.tempStr--;
					break;
				case "tou":
					player.tempTou--;
					break;
				case "spe":
					player.tempSpe--;
					break;
				case "int":
					player.tempInt--;
					break;
				default:
					player.statPoints--; //Failsafe
			}
			player.statPoints++;
			attributeMenu();
		}
		private function resetAttributes():void {
			//Increment unspent attribute points.
			player.statPoints += player.tempStr;
			player.statPoints += player.tempTou;
			player.statPoints += player.tempSpe;
			player.statPoints += player.tempInt;
			//Reset temporary attributes to 0.
			player.tempStr = 0;
			player.tempTou = 0;
			player.tempSpe = 0;
			player.tempInt = 0;
			//DONE!
			attributeMenu();
		}
		private function finishAttributes():void {
			clearOutput()
			if (player.tempStr > 0)
			{
				if (player.tempStr >= 3) outputText("Your muscles feel significantly stronger from your time adventuring.\n");
				else outputText("Your muscles feel slightly stronger from your time adventuring.\n");
			}
			if (player.tempTou > 0)
			{
				if (player.tempTou >= 3) outputText("You feel tougher from all the fights you have endured.\n");
				else outputText("You feel slightly tougher from all the fights you have endured.\n");
			}
			if (player.tempSpe > 0)
			{
				if (player.tempSpe >= 3) outputText("Your time in combat has driven you to move faster.\n");
				else outputText("Your time in combat has driven you to move slightly faster.\n");
			}
			if (player.tempInt > 0)
			{
				if (player.tempInt >= 3) outputText("Your time spent fighting the creatures of this realm has sharpened your wit.\n");
				else outputText("Your time spent fighting the creatures of this realm has sharpened your wit slightly.\n");
			}
			if (player.tempStr + player.tempTou + player.tempSpe + player.tempInt <= 0 || player.statPoints > 0)
			{
				outputText("\nYou may allocate your remaining stat points later.");
			}
			dynStats("str", player.tempStr, "tou", player.tempTou, "spe", player.tempSpe, "int", player.tempInt, "scale", false);
			player.tempStr = 0;
			player.tempTou = 0;
			player.tempSpe = 0;
			player.tempInt = 0;
			if (player.perkPoints > 0)
				doNext(perkBuyMenu);
			else
				doNext(playerMenu);
		}
		//Perk menu
		private function perkBuyMenu():void {
			clearOutput();
			var preList:Array = PerkTree.availablePerks(player);
			if (preList.length == 0) {
				outputText(images.showImage("event-cross"));
				outputText("You do not qualify for any perks at present.  In case you qualify for any in the future, you will keep your " + num2Text(player.perkPoints) + " perk point");
				if (player.perkPoints > 1) outputText("s");
				outputText(".");
				doNext(playerMenu);
				return;
			}
			outputText(images.showImage("event-arrow-up"));
			outputText("Please select a perk from the list, then click 'Okay'.  You can press 'Skip' to save your perk point for later.\n\n");
			perkListDisplay();
		}

		private function perkListDisplay(selPerk:PerkType = null):void {
			var perks:/*PerkType*/Array = PerkTree.availablePerks(player);
			var unavailable:Array       = PerkTree.obtainablePerks().filter(function (e:*, i:int, a:Array):Boolean {
				return !player.hasPerk(e) && perks.indexOf(e) < 0;
			});

			mainView.mainText.addEventListener(TextEvent.LINK, perkLinkHandler);

			if (player.perkPoints > 1) {
				outputText("You have " + numberOfThings(player.perkPoints, "perk point", "perk points") + ".\n\n");
			}

			for each(var perk:PerkType in perks) {
				outputText("" + perk.name + "\n");
				if (selPerk === perk) {
					outputText(perk.longDesc + "\n");
					var unlocks:Array = game.perkTree.listUnlocks(perk);
					if (unlocks.length > 0) {
						outputText("Unlocks:
");
						for each (var pt:PerkType in unlocks) {
							outputText("
" + pt.name + " (" + pt.longDesc + ")
");
						}
						outputText("
\n");
					}
					outputText("\n");
				}
			}

			outputText("\n\n");

			for each (perk in unavailable) {
				outputText("" + perk.name + " ");
				outputText(" Requires: " + getRequirements(perk) + "\n");
				if (selPerk === perk) {
					outputText(perk.longDesc + "\n\n")
				}
			}
			menu();
			addButton(1, "Skip", perkSkip);

			function perkSkip():void {
				clearListener();
				playerMenu();
			}

			function getRequirements(pk:PerkType):String {
				var colour:String;
				const dark:Boolean     = game.mainViewManager.isDarkTheme();
				const darkMeets:String = dark ? '#ffffff' : '#000000';
				const darkNeeds:String = dark ? '#ff4444' : '#aa2222';
				var requirements:Array = [];
				for each(var cond:* in pk.requirements) {
					colour = cond.fn(player) ? darkMeets : darkNeeds;
					requirements.push("" + cond.text + "");
				}
				return requirements.join(", ");
			}
		}

		private function clearListener():void {
			mainView.mainText.removeEventListener(TextEvent.LINK, perkLinkHandler);
		}

		public function perkLinkHandler(event:TextEvent):void {
			clearListener();
			var lastPos:int = mainView.mainText.scrollV;
			var selected:PerkType = PerkType.lookupPerk(event.text);

			clearOutput();
			outputText("You have selected the following perk:\n");
			outputText("" + selected.name + "\n");

			perkListDisplay(selected);
			addButton(0, "Okay", perkSelect, selected).disableIf(!selected.available(player));
			mainView.mainText.scrollV = lastPos;

			function perkSelect(sel:PerkType):void {
				clearListener();
				applyPerk(sel);
			}
		}

		public function applyPerk(perk:PerkType):void {
			clearOutput();
			player.perkPoints--;
			//Apply perk here.
			outputText("" + perk.name + " gained!");
			player.createPerk(perk, perk.defaultValue1, perk.defaultValue2, perk.defaultValue3, perk.defaultValue4);
			if (perk == PerkLib.StrongBack2 || perk == PerkLib.StrongBack) inventory.unlockSlots();
			if (perk == PerkLib.Tank2) {
				player.HPChange(player.tou, false);
				statScreenRefresh();
			}
			doNext(player.perkPoints > 0 ? perkBuyMenu : playerMenu);
		}

		//------------
		// MASTERY
		//------------
		public function displayMastery():void {
			spriteSelect(null);
			imageSelect(null);
			clearOutput();
			displayHeader("Mastery");

			//General
			var masteryText:String = "";
			var mtype:MasteryType;
			var found:Boolean = false;
			for (var i:int = 0; i < MasteryLib.MASTERY_GENERAL.length; i++) {
				mtype = MasteryLib.MASTERY_GENERAL[i];
				if (player.hasMastery(mtype)) {
					found = true;
					masteryText += ("" + mtype.name + ": " + player.masteryLevel(mtype) + " / " + mtype.maxLevel + " (Exp: " + ((player.masteryLevel(mtype) == mtype.maxLevel) ? "MAX" : player.masteryXP(mtype) + " / " + player.masteryMaxXP(mtype)) + ")\n");
				}
			}
			if (found) outputText("\nGeneral Mastery\n" + masteryText + "\n");

			//Weapon
			masteryText = "";
			found = false;
			for (var i:int = 0; i < MasteryLib.MASTERY_WEAPONS.length; i++) {
				mtype = MasteryLib.MASTERY_WEAPONS[i];
				if (player.hasMastery(mtype)) {
					found = true;
					masteryText += ("" + mtype.name + ": " + player.masteryLevel(mtype) + " / " + mtype.maxLevel + " (Exp: " + ((player.masteryLevel(mtype) == mtype.maxLevel) ? "MAX" : player.masteryXP(mtype) + " / " + player.masteryMaxXP(mtype)) + ")\n");
				}
			}
			if (found) outputText("Weapon Mastery\n" + masteryText + "\n");

			//Crafting
			masteryText = "";
			found = false;
			for (var i:int = 0; i < MasteryLib.MASTERY_CRAFTING.length; i++) {
				mtype = MasteryLib.MASTERY_CRAFTING[i];
				if (player.hasMastery(mtype)) {
					found = true;
					masteryText += ("" + mtype.name + ": " + player.masteryLevel(mtype) + " / " + mtype.maxLevel + " (Exp: " + ((player.masteryLevel(mtype) == mtype.maxLevel) ? "MAX" : player.masteryXP(mtype) + " / " + player.masteryMaxXP(mtype)) + ")\n");
				}
			}
			if (found) outputText("Crafting Mastery\n" + masteryText + "\n");

			menu();
			if (player.statPoints > 0) {
				addButton(0, "Stat Up", attributeMenu);
			}
			addButton(0, "Stats", displayStats);
			addButtonDisabled(1, "Mastery");
			addButton(14, "Back", playerMenu);
		}
	}
}

license: unknown


code:
package classes
{
import classes.GlobalFlags.kFLAGS;
import classes.GlobalFlags.kGAMECLASS;
	import flash.system.System;

	public class PregnancyStore extends BaseContent
	{
		//Pregancy types. Both butt and normal. Each type represents the father of this baby.
		public static const PREGNANCY_IMP:int                   =   1;
		public static const PREGNANCY_MINOTAUR:int              =   2;
		public static const PREGNANCY_COCKATRICE:int            =   3;
		public static const PREGNANCY_MOUSE:int                 =   4;
		public static const PREGNANCY_OVIELIXIR_EGGS:int        =   5; //Also caused by Phoenixes apparently
		public static const PREGNANCY_HELL_HOUND:int            =   6;
		public static const PREGNANCY_CENTAUR:int               =   7;
		public static const PREGNANCY_MARBLE:int                =   8;
		public static const PREGNANCY_BUNNY:int                 =   9;
		public static const PREGNANCY_ANEMONE:int               =  10;
		public static const PREGNANCY_AMILY:int                 =  11;
		public static const PREGNANCY_IZMA:int                  =  12;
		public static const PREGNANCY_SPIDER:int                =  13;
		public static const PREGNANCY_BASILISK:int              =  14;
		public static const PREGNANCY_DRIDER_EGGS:int           =  15;
		public static const PREGNANCY_GOO_GIRL:int              =  16;
		public static const PREGNANCY_EMBER:int                 =  17;
		public static const PREGNANCY_BENOIT:int                =  18;
		public static const PREGNANCY_SATYR:int                 =  19;
		public static const PREGNANCY_COTTON:int                =  20;
		public static const PREGNANCY_URTA:int                  =  21;
		public static const PREGNANCY_SAND_WITCH:int            =  22;
		public static const PREGNANCY_FROG_GIRL:int             =  23;
		public static const PREGNANCY_FAERIE:int                =  24; //Indicates you are carrying either a phouka or faerie baby. Which one is determined by the PREGNANCY_CORRUPTION flag
		public static const PREGNANCY_PLAYER:int                =  25; //The player is the father. Will be used when an NPC is able to have children from multiple different fathers.
		public static const PREGNANCY_BEE_EGGS:int              =  26;
		public static const PREGNANCY_SANDTRAP_FERTILE:int      =  27;
		public static const PREGNANCY_SANDTRAP:int              =  28;
		public static const PREGNANCY_JOJO:int                  =  29; //So we can track them separately from other mouse pregnancies
		public static const PREGNANCY_KELT:int                  =  30; //So we can track them separately from other centaur pregnancies
		public static const PREGNANCY_TAOTH:int                 =  31;
		public static const PREGNANCY_GOO_STUFFED:int           =  32; //Used to fill the player's ass and/or vagina when Valeria has a goo girl take up residence. This prevents any other
																	   //form of pregnancy from taking hold. Does not respond to ovielixirs.
		public static const PREGNANCY_WORM_STUFFED:int          =  33; //Used to fill the player's vagina when the worms take up residence. This prevents any other form of
																	   //pregnancy from taking hold. Does not respond to ovielixirs.
		public static const PREGNANCY_MINERVA:int				=  34;
		public static const PREGNANCY_BEHEMOTH:int				=  35;
		public static const PREGNANCY_PHOENIX:int				=  36;
		public static const PREGNANCY_ANDY:int                  =  37; //This is functionally the same as Satyr but less corrupt. 10% chance of fauns, if ever implemented.
		public static const PREGNANCY_CORRWITCH:int             =  38;
		public static const PREGNANCY_TENTACLE_BEAST_SEED:int   =  39;

		public static const PREG_NOT_PREGANT:int                =   0; //The PREG_* consts are returned by the size function
		public static const PREG_NO_SIGNS_UNKNOWN:int           =   1; //NPC has conceived but doesn’t know she’s pregnant, no visible signs
		public static const PREG_NO_SIGNS_KNOWN:int             =   2; //NPC is in the first trimester, knows she’s pregnant
		public static const PREG_START_BULGE:int                =   3; //NPC is in the first trimester, belly is just starting to bulge
		public static const PREG_SWOLLEN:int                    =   4; //NPC is in the second trimester, belly is small but definitely swollen
		public static const PREG_SIZEABLE:int                   =   5; //NPC is in the second trimester, belly is now sizable
		public static const PREG_BLATANT:int                    =   6; //NPC is in the third trimester, belly is blatantly bulging
		public static const PREG_FULL_TERM:int                  =   7; //NPC is in the third trimester, belly is big as it will get for a normal pregnancy
		public static const PREG_OVERDUE:int                    =   8; //NPC is overdue. Usually means a centaur baby, twins or some similar condition. Effectively looks 10 months pregnant
		public static const PREG_VERY_OVERDUE:int               =   9; //NPC is very overdue. Probably triplets or more. Effectively looks 11 months pregnant
//Old Value, replaced in Saves.unFuckSave()		public static const PREGNANCY_BUTT_BEE:int              =   2;
//Old Value, replaced in Saves.unFuckSave()		public static const PREGNANCY_BUTT_DRIDER:int           =   3;
//Old Value, replaced in Saves.unFuckSave()		public static const PREGNANCY_BUTT_SANDTRAP_FERTILE:int =   4;
//Old Value, replaced in Saves.unFuckSave()		public static const PREGNANCY_BUTT_SANDTRAP:int         =   5; //Sandtrap did not have fertilized eggs

		public static const INCUBATION_IMP:int                  = 432; //Time for standard imps. Imp lords, Ceraph, Lilium and the imp horde cause slightly faster pregnancies
		public static const INCUBATION_MINOTAUR:int             = 432;
		public static const INCUBATION_MOUSE:int                = 350;
		public static const INCUBATION_OVIELIXIR_EGGS:int       =  50;
		public static const INCUBATION_HELL_HOUND:int           = 352;
		public static const INCUBATION_CENTAUR:int              = 420;
		public static const INCUBATION_CORRWITCH:int            = 240; //Relatively short, but hard to get pregnancy.
		public static const INCUBATION_MARBLE:int               = 368;
		public static const INCUBATION_BUNNY_BABY:int           = 200;
		public static const INCUBATION_BUNNY_EGGS:int           = 808; //High time indicates neon egg pregnancy
		public static const INCUBATION_ANEMONE:int              = 256;
		public static const INCUBATION_IZMA:int                 = 300;
		public static const INCUBATION_SPIDER:int               = 400;
		public static const INCUBATION_BASILISK:int             = 250;
		public static const INCUBATION_COCKATRICE:int           = 225;
		public static const INCUBATION_DRIDER:int               = 400;
		public static const INCUBATION_GOO_GIRL:int             =  85;
		public static const INCUBATION_EMBER:int                = 336;
		public static const INCUBATION_SATYR:int                = 160;
		public static const INCUBATION_COTTON:int               = 350;
		public static const INCUBATION_URTA:int                 = 515;
		public static const INCUBATION_SAND_WITCH:int           = 360;
		public static const INCUBATION_FROG_GIRL:int            =  30;
		public static const INCUBATION_FAERIE:int               = 200;
		public static const INCUBATION_BEE:int                  =  48;
		public static const INCUBATION_SANDTRAP:int             =  42;
		public static const INCUBATION_HARPY:int                = 168;
		public static const INCUBATION_SHIELA:int               =  72;
		public static const INCUBATION_SALAMANDER:int           = 336;
		public static const INCUBATION_MINERVA:int           	= 216;
		public static const INCUBATION_BEHEMOTH:int           	=1440; //Sorry Behemoth, but Isabella wins.
		public static const INCUBATION_PHOENIX:int           	= 168;
		public static const INCUBATION_KIHA:int                 = 336;
		public static const INCUBATION_ISABELLA:int             = 2160; //Longest pregnancy ever.
		public static const INCUBATION_TENTACLE_BEAST_SEED:int  =  120; //short stuff, really.
		public static const INCUBATION_SYLVIA:int				= 336;
		private static const MAX_FLAG_VALUE:int                 = 2999;
		private static const PREG_TYPE_MASK:int                 = 0x0000FFFF; //Should be safe with 65535 different pregnancy types
		private static const PREG_NOTICE_MASK:int               = 0x7FFF0000; //Use upper half to store the latest stages of pregnancy the player has noticed

		private var _pregnancyTypeFlag:int;
		private var _pregnancyIncubationFlag:int;
		private var _buttPregnancyTypeFlag:int;
		private var _buttPregnancyIncubationFlag:int;
		private var _pregnancyEventValue:Vector.< Vector.<int> >; //Using a vector of vectors so that each different pregnancy type can have its own set of events
		private var _buttPregnancyEventValue:Vector.< Vector.<int> >;
		public var allowHerm:Boolean = false;

		//All the flags are passed through the constructor so that they can be different in every class that uses PregnancyStore but the pregnancy code remains the same
		public function PregnancyStore(pregType:int, pregInc:int, buttPregType:int, buttPregInc:int) {
			_pregnancyTypeFlag = pregType;
			_pregnancyIncubationFlag = pregInc;
			_buttPregnancyTypeFlag = buttPregType;
			_buttPregnancyIncubationFlag = buttPregInc;
			_pregnancyEventValue = new Vector.< Vector.<int> >();
			_buttPregnancyEventValue = new Vector.< Vector.<int> >();
			if (pregType < 0 || pregType > MAX_FLAG_VALUE || pregInc < 0 || pregInc > MAX_FLAG_VALUE || buttPregType < 0 || buttPregType > MAX_FLAG_VALUE || buttPregInc < 0 || buttPregInc > MAX_FLAG_VALUE || pregType == buttPregType || pregInc == buttPregInc) {
				CoC_Settings.error("Error: PregnancyStore created with invalid values for its flags. PregnancyStore(" + pregType + ", " + pregInc + ", " + buttPregType + ", " + buttPregInc + ")");
			}
		}

		public function get type():int { return (_pregnancyTypeFlag == 0 ? 0 : game.flags[_pregnancyTypeFlag] & PREG_TYPE_MASK); }

		public function get incubation():int { return (_pregnancyIncubationFlag == 0 ? 0 : game.flags[_pregnancyIncubationFlag]); }

		public function get buttType():int { return (_buttPregnancyTypeFlag == 0 ? 0 : game.flags[_buttPregnancyTypeFlag] & PREG_TYPE_MASK); }

		public function get buttIncubation():int { return (_buttPregnancyIncubationFlag == 0 ? 0 : game.flags[_buttPregnancyIncubationFlag]); }

		public function get isPregnant():Boolean { return type != 0; } //At birth the incubation can be zero so a check vs. type is safer

		public function get isButtPregnant():Boolean { return buttType != 0; } //At birth the incubation can be zero so a check vs. type is safer

		/* Using this function adds a series of events which happen during the pregnancy. They must be added in descending order (ex. 500, 450, 350, 225, 100, 25)
		   to work properly. For NPCs who have multiple pregnancy types each type has its own set of events. Events can be used to see how far along the NPC
		   is in her pregnancy with the event property. They can also be checked using the eventTriggered() function. This checks to see which was the latest event
		   the player noticed. The eventTriggered() function only triggers once per event per pregnancy. */
		public function addPregnancyEventSet(pregType:int, ... pregStage):void
		{
			var pregVector:Vector.<int> = new Vector.<int>(pregStage.length + 2);
			pregVector[0] = pregType; //First element is the pregnancy type
			for (var i:int = 0; i < pregStage.length; i++) pregVector[i + 1] = pregStage[i];
			pregVector[pregVector.length - 1] = -1; //Make last element -1 to ensure there is always a match
			_pregnancyEventValue.push(pregVector);
		}

		//Same as addPregnancyEventSet, but for butts
		public function addButtPregnancyEventSet(buttPregType:int, ... buttPregStage):void
		{
			var pregVector:Vector.<int> = new Vector.<int>(buttPregStage.length + 1);
			pregVector[0] = buttPregType; //First element is the butt pregnancy type
			for (var i:int = 0; i < buttPregStage.length; i++) pregVector[i + 1] = buttPregStage[i];
			pregVector[pregVector.length - 1] = -1; //Make last element -1 to ensure there is always a match
			_buttPregnancyEventValue.push(pregVector);
		}

		public function knockUp(newPregType:int = 0, newPregIncubation:int = 0, forceAllowHerm:Boolean = false):void
		{
			if (!isPregnant) knockUpForce(newPregType, newPregIncubation, forceAllowHerm);
		}

		public function knockUpForce(newPregType:int = 0, newPregIncubation:int = 0, forceAllowHerm:Boolean = false):void
		{
			if (_pregnancyTypeFlag == 0 || _pregnancyIncubationFlag == 0) return; //Check that these variables were provided by the containing class
			if (newPregType != 0) newPregType = (game.flags[_pregnancyTypeFlag] & PREG_NOTICE_MASK) + newPregType;
				//If a pregnancy 'continues' an existing pregnancy then do not change the value for last noticed stage
			game.flags[_pregnancyTypeFlag] = newPregType;
			game.flags[_pregnancyIncubationFlag] = (newPregType == 0 ? 0 : newPregIncubation); //Won't allow incubation time without pregnancy type
			if (forceAllowHerm || player.isHerm()) allowHerm = true;
		}

		public function buttKnockUp(newPregType:int = 0, newPregIncubation:int = 0):void
		{
			if (!isButtPregnant) buttKnockUpForce(newPregType, newPregIncubation);
		}

		public function buttKnockUpForce(newPregType:int = 0, newPregIncubation:int = 0):void
		{
			if (_buttPregnancyTypeFlag == 0 || _buttPregnancyIncubationFlag == 0) return; //Check that these variables were provided by the containing class
			if (newPregType != 0) newPregType = (game.flags[_buttPregnancyTypeFlag] & PREG_NOTICE_MASK) + newPregType;
				//If a pregnancy 'continues' an existing pregnancy then do not change the value for last noticed stage
			game.flags[_buttPregnancyTypeFlag] = newPregType;
			game.flags[_buttPregnancyIncubationFlag] = (newPregType == 0 ? 0 : newPregIncubation); //Won't allow incubation time without pregnancy type
		}

		//The containing class is responsible for calling pregnancyAdvance, usually once per timeChange()
		public function pregnancyAdvance():void //Separate function so it can be called more often than timeChange if neccessary
		{
			if (incubation != 0) {
				game.flags[_pregnancyIncubationFlag]--;
				if (game.flags[_pregnancyIncubationFlag] < 0) game.flags[_pregnancyIncubationFlag] = 0;
			}
			if (buttIncubation != 0) {
				game.flags[_buttPregnancyIncubationFlag]--;
				if (game.flags[_buttPregnancyIncubationFlag] < 0) game.flags[_buttPregnancyIncubationFlag] = 0;
			}
		}

		/* Many NPCs go through several events during their pregnancies. This function returns the latest event the NPC qualifies for.
		   When the NPC is not pregnant this always returns 0, when pregnant it will return at least 1. The further along the NPC is the larger the value. Each NPC
		   is free to have as many event as desired. They must be added using the addPregnancyEventSet function and are unique to each pregnancy type. */
		public function get event():int
		{
			var pregType:int = type;
			if (pregType == 0) return 0; //Not pregnant
			var incubationValue:int = incubation;
			var pregEventVector:Vector.<int> = null;
			for (var i:int = 0; i < _pregnancyEventValue.length; i++) {
				pregEventVector = _pregnancyEventValue[i];
				if (pregEventVector[0] == pregType) {
					for (var j:int = 1; j < pregEventVector.length; j++) { //Skip element zero, the pregnancy type
						if (incubationValue > pregEventVector[j]) return j; //Will always find a value that is < incubationValue as last value is -1
					}
				}
			}
			return 1; //If there are no pregnancy events for this type of pregnancy then return 1
		}

		//The same event system as for vaginal pregnacies, but for butts
		public function get buttEvent():int
		{
			var pregType:int = buttType;
			if (pregType == 0) return 0; //Not pregnant
			var incubationValue:int = buttIncubation;
			var pregEventVector:Vector.<int> = null;
			for (var i:int = 0; i < _buttPregnancyEventValue.length; i++) {
				pregEventVector = _buttPregnancyEventValue[i];
				if (pregEventVector[0] == pregType) {
					for (var j:int = 1; j < pregEventVector.length; j++) { //Skip element zero, the pregnancy type
						if (incubationValue > pregEventVector[j]) return j; //Will always find a value that is < incubationValue as last value is -1
					}
				}
			}
			return 1; //If there are no pregnancy events for this type of pregnancy then return 1
		}

		//Returns either zero - for no change - or the value of the new pregnancy event which the player has not yet noticed
		//This function updates the noticed pregnancy event, so it only triggers once per event per pregnancy.
		public function eventTriggered():int
		{
			var currentStage:int = event;
			var lastNoticed:int = game.flags[_pregnancyTypeFlag] & PREG_NOTICE_MASK;
			if (currentStage * 65536 == lastNoticed) return 0; //Player has already noticed this stage
			game.flags[_pregnancyTypeFlag] = (game.flags[_pregnancyTypeFlag] & PREG_TYPE_MASK) + (currentStage * 65536);
				//Strip off the old noticed value by ANDing with PREG_TYPE_MASK
			return currentStage;
		}

		//Same as eventTriggered, but for butts
		public function buttEventTriggered():int
		{
			var currentStage:int = buttEvent;
			var lastNoticed:int = game.flags[_buttPregnancyTypeFlag] & PREG_NOTICE_MASK;
			if (currentStage * 65536 == lastNoticed) return 0; //Player has already noticed this stage
			game.flags[_buttPregnancyTypeFlag] = (game.flags[_buttPregnancyTypeFlag] & PREG_TYPE_MASK) + (currentStage * 65536);
				//Strip off the old noticed value by ANDing with PREG_TYPE_MASK
			return currentStage;
		}

		public function get size():int {
			//This function exists to provide consistency across different NPC's pregnancies. This is most useful when trying to write descriptions of different belly sizes
			//in threesomes, where the author might not be familiar with how the different pregnancy events relate to belly size.
			return PREG_NOT_PREGANT;
		}
	}
}


code:
package classes
{
	//Call CoC.saveAwareClassAdd if you want a class that implements this interface to receive updates when the game is loaded or saved
	public interface SaveAwareInterface
	{
		/*	Called for every Class in the _saveAwareClassList just after the game is loaded from a save.
			Each class should use this opportunity to read all its stored information from one or more flag variables. */
		function updateAfterLoad(game:CoC):void;

		/*	Called for every Class in the _saveAwareClassList just before the game is to be saved.
			Each class should use this opportunity to write all its stored information to one or more flag variables so that it will be saved. */
		function updateBeforeSave(game:CoC):void;

	}
}