code:
package classes.Items
{
	import classes.*;
	import classes.BodyParts.*;
	import classes.GlobalFlags.kFLAGS;
	import classes.internals.LoggerFactory;
	import classes.internals.WeightedChoice;
	import classes.lists.ColorLists;
	import mx.logging.ILogger;

	/**
	 * Helper class to get rid of the copy&paste-mess in classes.Items.Mutations
	 * @since July 8, 2016
	 * @author Stadler76
	 */
	public class MutationsHelper extends BaseContent
	{
		private static const LOGGER:ILogger = LoggerFactory.getLogger(MutationsHelper);

		public var changes:int = 0;
		public var changeLimit:int = 1;

		private var _lizardSkinToneChoices:WeightedChoice = null;
		public function get lizardSkinToneChoices():WeightedChoice
		{
			if (_lizardSkinToneChoices === null) {
				_lizardSkinToneChoices = new WeightedChoice()
					// -> 10% * 1/2 =  5%
					.add(["purple", "deep pink"],     5)
					.add(["silver", "light gray"],    5)
					// -> 90% * 1/5 = 18%
					.add(["red",    "orange"],       18)
					.add(["green",  "yellow green"], 18)
					.add(["white",  "light gray"],   18)
					.add(["blue",   "ocean blue"],   18)
					.add(["black",  "dark gray"],    18);
			}

			return _lizardSkinToneChoices;
		}

		public function MutationsHelper() {}

		/**
		 * Wrapper around rand(x) === 0 with a min and max value scaling with the changes value
		 * @param   min  The minimum roll
		 * @param   max  The maximum roll
		 * @return  true, on success of the 'roll', false otherwise
		 * @author  Stadler76
		 */
		public function tfChance(min:int, max:int):Boolean
		{
			return rand(Math.min(min + changes, max)) === 0;
		}

		public function restoreArms(tfSource:String):int
		{
			LOGGER.debug("called restoreArms(\"{0}\")", tfSource);

			var message:String = "";

			if (tfSource == "gooGasmic") {
				// skin just turned gooey. Now lets fix unusual arms.
				var hasClaws:Boolean = player.arms.claws.type != Claws.NORMAL;

				message = "\n\n";
				if (player.arms.type == Arms.HARPY) {
					message += "The feathers on your arms melt back into your now gooey skin.";
					if (hasClaws) message += " Additionally your now gooey claws melt back into your fingers.";
				} else if (hasClaws) {
					message += "Your now gooey claws melt back into your fingers.";
				}

				if (hasClaws) message += " Well, who cares, gooey claws aren't very useful in combat to begin with.";
				if (hasClaws || player.arms.type == Arms.HARPY) outputText(message + "  You have normal human arms again.");

				player.arms.restore();
				return 1;
			}

			if (changes < changeLimit && player.arms.type != Arms.HUMAN) {
				if ([Arms.HARPY, Arms.SPIDER, Arms.SALAMANDER, Arms.DRAGON, Arms.LIZARD].indexOf(player.arms.type) >= 0)
					message += "\n\nYou scratch at your biceps absentmindedly, but no matter how much you scratch, it isn't getting rid of the itch.";

				switch (player.arms.type) {
					case Arms.HARPY:
						message += "  Glancing down in irritation, you discover that your feathery arms are shedding their feathery coating."
						          +"  The wing-like shape your arms once had is gone in a matter of moments, leaving [skinfurscales] behind.";
						break;

					case Arms.SPIDER:
						message += "  Glancing down in irritation, you discover that your arms' chitinous covering is flaking away."
						          +"  The glossy black coating is soon gone, leaving [skinfurscales] behind.";
						break;

					case Arms.DRAGON:
					case Arms.LIZARD:
					case Arms.SALAMANDER:
						message += "  Glancing down in irritation, you discover that your once scaly arms are shedding their scales and that"
						          +" your claws become normal human fingernails again.";
						break;

					case Arms.PREDATOR:
						switch (player.skin.type) {
							case Skin.GOO:
								if (player.arms.claws.type != Claws.NORMAL)
									message += "\n\nYour gooey claws melt into your fingers."
									          +" Well, who cares, gooey claws aren't very useful in combat to begin with.";
								break;
							case Skin.PLAIN:
							case Skin.FUR:
							case Skin.LIZARD_SCALES:
								message += "\n\nYou feel a sudden tingle in your [claws] and then you realize,"
								          +" that they have become normal human fingernails again.";
								break;
							default: //Move along
						}
						break;

					default:
						message += "\n\nYour unusual arms change more and more until they are normal human arms, leaving [skinfurscales] behind.";
				}
				outputText(message + "  You have normal human arms again.");
				player.arms.restore();
				changes++;
				return 1;
			}

			return 0;
		}

		public function restoreLegs(tfSource:String):Boolean
		{
			LOGGER.debug("called restoreLegs(\"{0}\")", tfSource);

			var doRestore:Boolean = false;
			var tsParts:Array = tfSource.split("-");
			tfSource = tsParts[0];

			//(Centaurs -> Normal Human Legs)
			if (player.isTaur()) {
				outputText("\n\nYour quadrupedal hind-quarters seizes, overbalancing your surprised front-end and causing you to stagger and fall to your side.  Pain lances throughout, contorting your body into a tightly clenched ball of pain while tendons melt and bones break, melt, and regrow.  When it finally stops, you look down to behold your new pair of human legs!");
				doRestore = true;
			}

			//(Goo -> Normal Human Legs)
			if (player.isGoo()) {
				outputText("\n\nYour lower body rushes inward, molding into two leg-like shapes that gradually stiffen up.  In moments they solidify into normal-looking legs, complete with regular, human feet.  You now have normal feet!");
				doRestore = true;
			}

			//(Naga -> Normal Human Legs)
			if (player.isNaga()) {
				outputText("\n\nYou collapse as your sinuous snake-tail tears in half, shifting into legs.  The pain is immense, particularly where your new feet are forming.  You have human legs again.");
				doRestore = true;
			}

			//(Drider -> Normal Human Legs)
			if (tfSource != "sweetGossamer" && player.isDrider()) {
				outputText("\n\nYour legs buckle under you and you fall, smashing your abdomen on the ground."
				          +"  Though your control deserts and you cannot see behind you,"
				          +" still you feel the disgusting sensation of chitin loosening and sloughing off your body,"
				          +" and the dry breeze on your exposed nerves."
				          +"  Reflexively, your legs cling together to protect as much of their now-sensitive surface as possible."
				          +"  When you try to part them, you find you cannot."
				          +"  Several minutes pass uncomfortably until you can again bend your legs,"
				          +" and when you do, you find that all the legs of a side bend together.");
				outputText("  You have human legs again.");
				doRestore = true;
			}

			//(Non-human -> Normal Human Legs)
			if (tfSource == "regularHummus" && player.isBiped() && player.lowerBody.type!= LowerBody.HUMAN) {
				outputText("\n\nYou collapse as your legs shift and twist.  By the time the pain subsides, you notice that you have normal legs and normal feet.  You now have normal feet!");
				doRestore = true;
			}

			if (doRestore) {
				player.lowerBody.type= LowerBody.HUMAN;
				player.lowerBody.legCount = 2;
				changes++;
				return true;
			}

			return false;
		}

		public function restoreNeck(tfSource:String):Boolean
		{
			LOGGER.debug("called restoreNeck(\"{0}\")", tfSource);

			var tsParts:Array = tfSource.split("-");
			if (tsParts.length > 1 && tsParts[0] != "reptilum") // probably later dracolisks would get an elongated neck, too (shorter than the dragon version)
				tfSource = tsParts[0];

			var forceRestore:Boolean = tsParts.indexOf("forceRestoreNeck") != -1;

			switch (player.neck.type) {
				case Neck.DRACONIC:
					if (tfSource == "EmberTFs" || (!forceRestore && player.dragonScore() >= 11))
						return false;

					outputText("\n\nYour draconic neck[if (neckPos) and its position on your head revert|reverts] to its normal"
					          +" [if (neckPos)position and] length. ");
					break;

				case Neck.COCKATRICE:
					if (tfSource == "TonOTrice" || (!forceRestore && player.cockatriceScore() >= 7))
						return false;

					outputText("\n\nYou neck starts to tingle and the feathers that decorate your neck begin to fall out until"
					          +" you're left with a normal neck!");
					break;

				default:
					player.neck.restore(); // Restore leftovers. Failsafe!
					return false;
			}

			if (!forceRestore) changes++;
			player.neck.restore();
			return true;
		}

		public function restoreRearBody(tfSource:String):Boolean
		{
			LOGGER.debug("called restoreRearBody(\"{0}\")", tfSource);

			var tsParts:Array = tfSource.split("-");
			tfSource = tsParts[0];

			var forceRestore:Boolean = tsParts.indexOf("forceRestoreRearBody") != -1;

			switch (player.rearBody.type) {
				case RearBody.SHARK_FIN:
					if (tfSource == "sharkTooth" || (!forceRestore && player.sharkScore() >= 3))
						return false;

					outputText("A wave of tightness spreads through your back, and it feels as if someone is stabbing a dagger into your spine."
					          +" After a moment the pain passes, though your fin is gone!");
					break;

				case RearBody.DRACONIC_MANE:
				case RearBody.DRACONIC_SPIKES:
					if (tfSource == "EmberTFs" || (!forceRestore && player.dragonScore() >= 11))
						return false;

					if (player.rearBody.type == RearBody.DRACONIC_MANE)
						outputText("\n\nYou feel a tingling just above your spine. Your glimpse at your back and see hair falling down from it."
						          +" First in strands, then in bigger and bigger chunks until"
						          +" your hairy draconic mane has completely disappeared.");
					else
						outputText("\n\nYour spine starts to make painful cracking sounds and you feel something retracting back into your rear."
						          +" Soon after the pain ceased the skin above your spine fuses and closes the holes where your spikes once were."
						          +" The spikes on your rear have disappeared.");
					break;

				default:
					player.rearBody.restore();
					return false;
			}

			if (!forceRestore) changes++;
			player.rearBody.restore();
			return true;
		}

		public function removeFeatheryHair():Boolean
		{
			if (changes < changeLimit && player.hair.type == Hair.FEATHER && rand(4) == 0) {
				//(long):
				if (player.hair.length >= 6) outputText("\n\nA lock of your downy-soft feather-hair droops over your eye.  Before you can blow the offending down away, you realize the feather is collapsing in on itself.  It continues to curl inward until all that remains is a normal strand of hair.  Your hair is no longer feathery!");
				//(short)
				else outputText("\n\nYou run your fingers through your downy-soft feather-hair while you await the effects of the item you just ingested.  While your hand is up there, it detects a change in the texture of your feathers.  They're completely disappearing, merging down into strands of regular hair.  Your hair is no longer feathery!");
				player.hair.type = Hair.NORMAL;
				changes++;
				return true;
			}

			return false;
		}

		/**
		 * Removes antennae and display different loss texts depending on the type, if any.
		 * @param	inline  If true, display a short inline text (No bold part, no line breaks)
		 * @return	true:   lost them, false: no change
		 * @author	Stadler76
		 */
		public function removeAntennae(inline:Boolean = false):Boolean
		{
			if (player.antennae.type == Antennae.NONE)
				return false;

			if (inline) {
				switch (player.antennae.type) {
					case Antennae.COCKATRICE:
					case Antennae.BEE:
					default:
						outputText(" Antennae pop free, and float lightly down towards the floor. ");
				}
			} else {
				switch (player.antennae.type) {
					case Antennae.COCKATRICE:
						outputText("\n\nYou feel your antennae like feathers shrivel at the root, the pair of soft quills falling softly to the"
						          +" ground as your pores close.");
						outputText("\nYou’ve lost your antennae like feathers!");
						break;

					case Antennae.BEE:
						outputText("\n\nYour [hair] itches so you give it a scratch, only to have your antennae fall to the ground. What a relief.");
						outputText("\nYou've lost your antennae!");
						break;

					default: // should not happen, but just in case ... (Stadler76)
						outputText("\n\nThe muscles in your brow clench tightly, and you feel a tremendous pressure on your upper forehead."
						          +" When it passes, you touch yourself and discover your antennae have vanished!");
				}
			}

			player.antennae.type = Antennae.NONE;
			changes++;
			return true;
		}

		public function removeBassyHair():Boolean
		{
			// Failsafe, duh
			if ([Hair.BASILISK_PLUME, Hair.BASILISK_SPINES].indexOf(player.hair.type) == -1) return false;

			if (player.hair.type == Hair.BASILISK_PLUME) {
				// TF blurb derived from losing feathery hair
				//(long):
				if (player.hair.length >= 5)
					outputText("\n\nA lock of your feathery plume droops over your eye.  Before you can blow the offending down away,"
					          +" you realize the feather is collapsing in on itself."
					          +" It continues to curl inward until all that remains is a normal strand of hair.");
				//(short)
				else
					outputText("\n\nYou run your fingers through your feathery plume while you await the effects of the item you just ingested."
					          +" While your hand is up there, it detects a change in the texture of your feathers.  They're completely disappearing,"
					          +" merging down into strands of regular hair.");

					outputText("\n\nYour hair is no longer feathery!");
			} else {
				outputText("\n\nYou feel a tingling on your scalp. You reach up to your basilisk spines to find out what is happening. The moment"
					          +" your hand touches a spine, it comes loose and falls in front of you. One after another the other spines fall out,"
					          +" until all the spines that once decorated your head now lay around you, leaving you with a bald head.");

				outputText("\n\nYou realize, that you'll grow normal human hair again!");
				flags[kFLAGS.HAIR_GROWTH_STOPPED_BECAUSE_LIZARD] = 0;
				player.hair.length = 0;
			}
			player.hair.type = Hair.NORMAL;
			changes++;
			return true;
		}

		public function removeBlackNipples(tfSource:String):Boolean
		{
			LOGGER.debug("called removeBlackNipples(\"{0}\")", tfSource);
				//rare .skin.tone

			//non rare .skin.tone
				return false;

			outputText("\n\nSomething invisible brushes against your [nipple], making you twitch.  Undoing your clothes, you take a look at your"
			          +" chest and find that your nipples have turned back to their natural flesh colour.");
			player.removeStatusEffect(StatusEffects.BlackNipples);
			changes++;
			return true;
		}

		public function newLizardSkinTone():Array
		{
			return lizardSkinToneChoices.choose();
		}

		public function newCockatriceColors():Array
		{
			return randomChoice(ColorLists.COCKATRICE);
		}

		public function lizardHairChange(tfSource:String):int
		{
			var hairPinID:int = player.findKeyItem("Feathery hair-pin");
			LOGGER.debug("called lizardHairChange(\"{0}\")", tfSource);

			switch (tfSource) {
				case "reptilum-lizan":
				case "reptilum-dragonewt":
					//-Hair stops growing!
					if (flags[kFLAGS.HAIR_GROWTH_STOPPED_BECAUSE_LIZARD] == 0) {
						outputText("\n\nYour scalp tingles oddly.  In a panic, you reach up to your [hair], but thankfully it appears unchanged.");
						outputText("\n\n(Your hair has stopped growing.)");
						changes++;
						flags[kFLAGS.HAIR_GROWTH_STOPPED_BECAUSE_LIZARD] = 1;
						return -1; // --> hair growth stopped
					}
					return 0;

				case "PlayerEvents-benoitHairPin":
				case "reptilum-basilisk":
				case "reptilum-dracolisk":
					if (player.hair.type == Hair.BASILISK_PLUME && player.cor < 65) return 0;

					if (player.isFemaleOrHerm() && player.cor < 15 && player.featheryHairPinEquipped() && player.isBasilisk()) {
						var benoitMFText:String = game.bazaar.benoit.benoitMF(
							" your hair has changed into a plume of feathers that, like legend is told, belongs to a female basilisk!",
							" you have a plume, like a female basilisk!"
						);

						if (player.hair.type == Hair.GOO)
							outputText("\n\nYour gooey hair begins to fall out in globs, eventually leaving you with a bald head.");

						outputText("\n\nYour head begins to tickle and you reach up to scratch at it, only to be surprised by the softness you feel."
						           +" It reminds you of the down of baby chickens, velvety soft and slightly fluffy. You look at yourself in a nearby"
						           +" puddle and gasp, [if (hairlength <= 0) where your hair once was] you now have red feathers,"
						           +" some longer and larger than others. This floppy but soft plume sits daintily on your head,"
						           +" reminding you of a ladies fascinator. You realise soon that" + benoitMFText);

						if (flags[kFLAGS.HAIR_GROWTH_STOPPED_BECAUSE_LIZARD] != 0)
							outputText("\n\nYour hair is growing again and is now a plume of short red feathers.");
						else
							outputText("\n\nYour hair is now a plume of short red feathers.");

						flags[kFLAGS.HAIR_GROWTH_STOPPED_BECAUSE_LIZARD] = 0;
						player.hair.length = 2;
						player.hair.color = "red";
						player.hair.type = Hair.BASILISK_PLUME;
						if (hairPinID >= 0) {
							player.keyItems[hairPinID].value2 = 0;
						}
						changes++;
						return 1; // --> gained basilisk hair (plume)
					}

					if (player.cor >= 65 && player.hair.type != Hair.BASILISK_SPINES && player.hasLizardScales() && player.hasReptileFace()) {
						// Corrupted Basilisk
						if (player.hair.length > 0 && [Hair.GOO, Hair.BASILISK_PLUME].indexOf(player.hair.type) == -1) {
							outputText("\n\nYour scalp feels tight and hot, causing you to run a hand through your [hair] to rub at it gingerly.");

							if (player.featheryHairPinEquipped())
								outputText("\n\nThe pin in your hair heats up to the point where you have to pull it free and toss it to the ground,"
								           +" it sizzling against the earth.");

							outputText("\n\nTo your shock a handful of your [hair] comes loose, exposing the [skinFurScales] beneath. You wonder why"
							           +" this is happening to you but realise it must be the potion, lizards don't tend to have hair after all."
							           +" You continue to rub at your scalp, the cool air against your slowly revealed skin a welcome relief."
							           +" After several minutes of this, you're certain you've shed all your hair. When you think it's all over,"
							           +" you feel an uncomfortable pressure build, your scalp morphing and changing as you grimace."
							           +" When the sensation finally fades you rush to look at yourself in a puddle. Where you once had hair,"
							           +" you now have a crown of dull reptilian spines!");

						}

						// Female Basilisk to Corrupted Basilisk
						if (player.hair.length > 0 && player.hair.type == Hair.BASILISK_PLUME) {
							outputText("\n\nA sudden sharp pain drills through your skull, a pressure forming across your scalp."
							           +" If you didn't know any better you'd think you were being plucked!");

							if (player.featheryHairPinEquipped())
								outputText("\n\nThe pin in your hair heats up to the point where you have to pull it free and toss it to the ground,"
								           +" it sizzling against the earth.");

							outputText("\n\nYou clutch your head as you feel the feathers on your head push out further, thickening up as they do so."
							           +" The soft vane seems to fall from the spine of the feathers, stripping them bare as the red fluff falls to"
							           +" the floor. Soon the tips of the feathers follow, leaving some rather alien looking spines behind. Your head"
							           +" throbs with dull pain as the transformation seems to end and you go to look at your reflection in a nearby"
							           +" puddle. Your once magnificent plume has turned into a dull crown of spines, like that of the corrupt"
							           +" basilisk of the mountains. As you mourn the loss of your feathered hair, you notice your spines move with"
							           +" your emotions, their sensitive tips picking up on the breeze as they lower closer to your skull.");
						}

						// Corrupted basilisk with gooey or no hair (bald)
						if (player.hair.type == Hair.GOO || player.hair.length <= 0) {
							if (player.hair.type == Hair.GOO)
								outputText("\n\nYour gooey hair begins to fall out in globs, eventually leaving you with a bald head.");

							if (player.featheryHairPinEquipped())
								outputText("\n\nThe pin in your hair heats up to the point where you have to pull it free and toss it to the ground,"
								           +" it sizzling against the earth.");

							outputText("\n\nYour scalp feels tight, as though the skin is shifting and morphing."
							           +" You let out groan as you grip your head, praying for the pain to subside quickly. Pressure builds,"
							           +" your head feeling ready to split. As the sensation fades you're left wondering what just happened and you"
							           +" run to look at yourself in a nearby puddle.");
						}

						// Finalize Corrupted Basilisk TFs
						player.hair.color = player.skin.tone;                   // hairColor always set to player.skin.tone
						player.hair.type = Hair.BASILISK_SPINES;               // hairType set to basilisk spines
						player.hair.length = 2;                                // hairLength set to 2 (inches, displayed as ‘short’)
						flags[kFLAGS.HAIR_GROWTH_STOPPED_BECAUSE_LIZARD] = 1; // Hair growth stops
						changes++;
						outputText("\n\nWhere your hair would be, you now have a crown of dull reptilian spines!");

						if (player.featheryHairPinEquipped())
							outputText("\n\nYou place the hair-pin in your inventory, no longer able to wear it.");
						else if (hairPinID >= 0)
							outputText("\n\nYour thoughts wander to the hair-pin while you adjust to your new [hair]. You probably won't be able to"
							           +" wear it while you're this tainted.");

						if (hairPinID >= 0) {                                 // hair-pin set to unequipped
							player.keyItems[hairPinID].value1 = 0;
							player.keyItems[hairPinID].value2 = 0;
						}
						if (tfSource == "PlayerEvents-benoitHairPin") outputText("\n");
						return 1;
					}
					return 0;

				default:
					return 0;
			}
		}

		/**
		 * Updates the perk Oviposition depending on the class/method stored in tfSource, that called it.
		 * @param	tfSource	The method- or classname plus additional info seperated by the '-'-character
		 * @return	-1: lost the perk, 0: no change, 1: gained the perk
		 * @author	Stadler76
		 */
		public function updateOvipositionPerk(tfSource:String):int
		{
			LOGGER.debug("called updateOvipositionPerk(\"{0}\")", tfSource);

			var tsParts:Array = tfSource.split("-");
			if (tsParts.length > 1 && ["goldenSeed", "catTransformation"].indexOf(tsParts[0]) == -1)
				tfSource = tsParts[0];

			// First things first :)
			if (player.findPerk(PerkLib.BasiliskWomb) >= 0) {
				if (player.findPerk(PerkLib.Oviposition) >= 0)
					return 0; // we already have it => no change

				// Basilisk Womb but no Oviposition? Fix, whats broken
				outputText("\n\nDeep inside yourself there is a change.  It makes you feel a little woozy, but passes quickly."
				          +"  Beyond that, you aren't sure exactly what just happened, but you are sure it originated from your basilisk womb.\n");
				outputText("(Perk Gained: Oviposition)");

				player.createPerk(PerkLib.Oviposition, 0, 0, 0, 0);
				return 1; // true => gained Oviposition Perk
			}

			if (changes >= changeLimit) return 0;

			// Note, that we don't do the score checks anymore. That was just an ugly workaround and we don't want to do that again!
			switch(tfSource) {
				case "EmberTFs":
				case "snakeOil":
				case "goldenSeed-HarpyWomb":
				//case "catTransformation-dragonne": // Keep it? Maybe later.
				// TFs with minor changes. Just in case, we change our mind or if we intend to upgrade them.
				case "winterPudding":
				case "rizzaRootEffect":
					return 0; // Don't change it. So we're done, yay!

				case "reptilum":
				case "echidnaTFs":
				case "TonOTrice":
					if (player.findPerk(PerkLib.Oviposition) >= 0) return 0;
					outputText("\n\nDeep inside yourself there is a change.  It makes you feel a little woozy, but passes quickly."
					          +"  Beyond that, you aren't sure exactly what just happened, but you are sure it originated from your womb.\n");
					outputText("(Perk Gained: Oviposition)");

					player.createPerk(PerkLib.Oviposition, 0, 0, 0, 0);
					changes++;
					return 1; // Gained it
					break;

				default:
					if (player.findPerk(PerkLib.Oviposition) < 0) return 0;
					if (player.lizardScore() >= 8) return 0; // Still high, so don't remove Oviposition yet!
					if (tfSource != "superHummus") {
						outputText("\n\nAnother change in your uterus ripples through your reproductive systems."
						          +"  Somehow you know you've lost a little bit of reptilian reproductive ability.\n");
						outputText("(Perk Lost: Oviposition)\n");
					}
					player.removePerk(PerkLib.Oviposition);
					return -1; // Lost it
			}
		}

		public function updateGills(newGillType:int = Gills.NONE):int
		{
			LOGGER.debug("Called updateGills(\"{0}\")", newGillType);

			var oldgillType:int = player.gills.type;
			if (player.gills.type == newGillType) return 0; // no change

			player.gills.type = newGillType;
			changes++;

			// for now, we only have anemone gills on the chest
			switch (newGillType) {
				case Gills.NONE:
					if (oldgillType == Gills.ANEMONE) {
						outputText("\n\nYour chest itches, and as you reach up to scratch it, you realize your gills have withdrawn into your"
						           +" skin.");
					} else { // losing fish gills
						outputText("\n\nYou feel your gills tighten, the slits seeming to close all at once. As you let out a choked gasp your"
						           +" gills shrink into nothingness, leaving only smooth skin behind. Seems you won't be able to stay in the water"
						           +" quite so long anymore.");
					}
					outputText("  You no longer have gills!");
					return -1; // Gills lost

				case Gills.ANEMONE:
					if (oldgillType == Gills.FISH) {
						outputText("\n\nYou feel your gills tighten, the slits seeming to close all at once. As you let out a choked gasp your"
						           +" gills shrink into nothingness, leaving only smooth skin behind. When you think it's over you feel something"
						           +" emerge from under your neck, flowing down over your chest and brushing your nipples. You look in surprise as"
						           +" your new feathery gills finish growing out, a film of mucus forming over them shortly after.");
					} else { // if no gills
						outputText("\n\nYou feel a pressure in your lower esophageal region and pull your garments down to check the area."
						           +" Before your eyes a pair of feathery gills start to push out of the center of your chest,"
						           +" just below your neckline, parting sideways and draping over your " + player.nippleDescript(0) + "s."
						           +" They feel a bit uncomfortable in the open air at first, but soon a thin film of mucus covers them and you"
						           +" hardly notice anything at all. You redress carefully.");
					}
					outputText("\n\nYou now have feathery gills!");
					return 1; // Gained gills or gillType changed

				case Gills.FISH:
					if (oldgillType == Gills.ANEMONE) {
						outputText("\n\nYou feel your gills tingle, a vague numbness registering across their feathery exterior. You watch in awe as"
						           +" your gill's feathery folds dry out and fall off like crisp autumn leaves. The slits of your gills then"
						           +" rearrange themselves, becoming thinner and shorter, as they shift to the sides of your neck. They now close in"
						           +" a way that makes them almost invisible. As you run a finger over your neck you feel little more than several"
						           +" small raised lines where they meet your skin.");
					} else { // if no gills
						outputText("\n\nYou feel a sudden tingle on your neck. You reach up to it to feel, whats the source of it. When you touch"
						           +" your neck, you feel that it begins to grow several narrow slits which slowly grow longer. After the changes"
						           +" have stopped you quickly head to a nearby puddle to take a closer look at your neck. You realize,"
						           +" that your neck has grown gills allowing you to breathe under water as if you were standing on land.");
					}
					outputText("\n\nYou now have fish like gills!");
					return 1; // Gained gills or gillType changed

				default:
					player.gills.type = oldgillType;
					changes--;
					LOGGER.error("Unimplemented new gillType (\"{0}\") used", newGillType);
					return 0; // failsafe, should hopefully never happen
			}
		}

		public function gainSnakeTongue():Boolean
		{
			if (player.tongue.type != Tongue.SNAKE && changes < changeLimit) {
				if (player.tongue.type == Tongue.HUMAN) {
					outputText("\n\nYour taste-buds start aching as they swell to an uncomfortably large size. "
					          +"Trying to understand what in the world could have provoked such a reaction, you bring your hands up to your mouth, "
					          +"your tongue feeling like it's trying to push its way past your lips.");
					outputText("  The soreness stops and you stick out your tongue to try and see what would have made it feel the way it did. "
					          +"As soon as you stick your tongue out you realize that it sticks out much further than it did before, "
					          +"and now appears to have split at the end, creating a forked tip.");
					outputText("  The scents in the air are much more noticeable to you with your snake-like tongue.");
				} else {
					outputText("\n\nYour inhuman tongue shortens, pulling tight in the very back of your throat.");
					outputText("  After a moment the bunched-up tongue-flesh begins to flatten out, then extend forwards.");
					outputText("  By the time the transformation has finished, your tongue has changed into a long, forked snake-tongue.");
				}
				player.tongue.type = Tongue.SNAKE;
				dynStats("sen", 5);
				changes++;
				return true;
			}

			return false;
		}

		public function gainLizardTongue():Boolean
		{
			if (player.tongue.type != Tongue.LIZARD) {
				outputText("\n\nYour tongue goes numb, making your surprised noise little more than a gurgle as your tongue flops comically. ");
				switch (player.tongue.type) {
					case Tongue.SNAKE:
						outputText("\nSlowly your tongue swells, thickening up until it's about as thick as your thumb, while staying quite "
					              +" flexible. You drool, your tongue lolling out of your mouth as you slowly begin to regain control of your forked"
					              +" organ. When you retract your tongue however, you are shocked to find it is much longer than it used to be,"
					              +" now a foot long. As you cram your newly shifted appendage back in your mouth, you feel a sudden SNAP,"
					              +" and on inspection, find you've snapped off your fangs! Well, you suppose you needed the room anyway.");
						break;

					case Tongue.DEMONIC:
						outputText("\nYour tongue gently shrinks down, the thick appendage remaining flexible but getting much smaller. There's"
					              +" little you can do but endure the weird pinching feeling as your tongue eventually settles at being a foot long."
					              +" The pinching sensation continues as the tip of your tongue morphs, becoming a distinctly forked shape."
					              +" As you inspect your tongue you slowly regain control, retracting it into your mouth, the forked tips picking up"
					              +" on things you couldn't taste before.");
						break;

					case Tongue.DRACONIC:
						outputText("\nYour tongue rapidly shrinks down, the thick appendage remaining flexible but getting much smaller. There's"
					              +" little you can do but endure the weird pinching feeling as your tongue eventually settles at being a foot long."
					              +" The pinching sensation continues as the tip of your tongue morphs, becoming a distinctly forked shape."
					              +" As you inspect your tongue you slowly regain control, retracting it into your mouth, the forked tips picking up"
					              +" on things you couldn't taste before.");
						break;

					case Tongue.ECHIDNA:
						outputText("\nSlowly your tongue swells, thickening up until it’s about as thick as your thumb, while staying long."
					              +" The tip pinches making you wince, morphing into a distinctly forked shape. As you inspect your tongue you slowly"
					              +" regain control, retracting it into your mouth, the forked tips picking up on things you couldn't taste before.");
						break;

					default:
						outputText("\nSlowly your tongue swells, thickening up until it’s about as thick as your thumb, filling your mouth as you"
					              +" splutter. It begins lengthening afterwards, continuing until it hangs out your mouth, settling at 1 foot long."
					              +" The tip pinches making you wince, morphing into a distinctly forked shape. As you inspect your tongue you slowly"
					              +" regain control, retracting it into your mouth, the forked tips picking up on things you couldn't taste before.");
				}
				outputText("\n\nYou now have a lizard tongue!");
				player.tongue.type = Tongue.LIZARD;
				dynStats("sen", 5); // Sensitivy gain since its forked
				changes++;
			}

			return false;
		}

		public function gainDraconicHorns(tfSource:String):void
		{
			LOGGER.debug("called gainDraconicHorns(\"{0}\")", tfSource);

			var tsParts:Array = tfSource.split("-");
			var race:String;

			if (tsParts[0] == "EmberTFs")
				race = "dragon";
			else if (tsParts[0] == "reptilum" && tsParts.length > 1)
				race = tsParts[1];
			else
				throw new Error("Unimplemented tfSource: '" + tfSource + "' used in gainDraconicHorns!");

			//No dragon horns yet.
			if (player.horns.type != Horns.DRACONIC_X2 && player.horns.type != Horns.DRACONIC_X4_12_INCH_LONG) {
				//Already have horns
				if (player.horns.value > 0) {
					//High quantity demon horns
					if (player.horns.type == Horns.DEMON && player.horns.value > 4) {
						outputText("\n\nYour horns condense, twisting around each other and merging into larger, pointed protrusions.  By the time they finish you have four draconic-looking horns, each about twelve inches long.");
						player.horns.value = 12;
						player.horns.type = Horns.DRACONIC_X4_12_INCH_LONG;
					}
					else {
						outputText("\n\nYou feel your horns changing and warping, and reach back to touch them.  They have a slight curve and a gradual taper.  They must look something like the horns the dragons in your village's legends always had.");
						player.horns.type = Horns.DRACONIC_X2;
						if (player.horns.value > 13) {
							outputText("  The change seems to have shrunken the horns, they're about a foot long now.");
							player.horns.value = 12;
						}

					}
					changes++;
				}
				//No horns
				else {
					//-If no horns, grow a pair
					outputText("\n\nWith painful pressure, the skin on the sides of your forehead splits around two tiny nub-like horns.  They're angled back in such a way as to resemble those you saw on the dragons in your village's legends.  A few inches of horn sprout from your head before stopping.  You have about four inches of dragon-like horn.");
					player.horns.value = 4;
					player.horns.type = Horns.DRACONIC_X2;

					changes++;
				}
			}
			//ALREADY DRAGON
			else {
				if (player.horns.type == Horns.DRACONIC_X2) {
					if (player.horns.value < 12) {
						if (rand(3) == 0) {
							outputText("\n\nYou get a headache as an inch of fresh horn escapes from your pounding skull.");
							player.horns.value += 1;
						}
						else {
							outputText("\n\nYour head aches as your horns grow a few inches longer.  They get even thicker about the base, giving you a menacing appearance.");
							player.horns.value += 3 + rand(3);
						}
						if (player.horns.value >= 12) outputText("  Your horns settle down quickly, as if they're reached their full size.");
						changes++;
					}
					//maxxed out, new row
					else {
						//--Next horn growth adds second row and brings length up to 12\"
						outputText("\n\nA second row of horns erupts under the first, and though they are narrower, they grow nearly as long as your first row before they stop.  A sense of finality settles over you.  You have as many horns as a " + race + " can grow.");
						player.horns.type = Horns.DRACONIC_X4_12_INCH_LONG;
						changes++;
					}
				}
			}
		}

		public function removeExtraBreastRow(tfSource:String):void
		{
			LOGGER.debug("called removeExtraBreastRow(\"{0}\")", tfSource);

			if (player.breastRows.length <= 1) {
				return;
			}

			outputText("\n\nYou stumble back when your center of balance shifts, and though you adjust before you can fall over, you're left to watch"
			          +" in awe as your bottom-most [lastbreasts] shrink down, disappearing completely into your [if (breastrows >= 3)abdomen|chest]."
			          +" The [lastnipples] even fade until nothing but [if (isFluffy)[furColor] [skinDesc]|[skintone] [skinDesc]] remains."
			          +" You've lost a row of breasts!");

			if (tfSource !== "regularHummus") {
				dynStats("sen", -5);
			}

			player.removeBreastRow(player.breastRows.length - 1, 1);
			changes++;
		}
	}
}


code:
package classes.Items.Other
{

	public class DebugWand extends SimpleUseable
	{

		public function DebugWand()
		{
			super("DbgWand", "Debug Wand", "a wand of debugging", 1000, "This mysterious wand has an entirely unknown origin but somehow you feel like a cheater when using it.", "You raise the wand and a slab of stone emerges from the ground. The slab has fifteen buttons and a text panel.");
		}

		override public function canUse():Boolean {
			return true;
		}

		override public function useItem():Boolean {
			if (!debug) inventory.takeItem(this, game.debugMenu.accessDebugMenu);
			game.debugMenu.accessDebugMenu();
			return true;
		}

	}

}

license: unknown


code:
package classes.Items.Other
{
	import classes.Items.Useable;
	import classes.Player;
	import classes.internals.Utils;
	import classes.*;
	import classes.GlobalFlags.kFLAGS;
	import classes.Scenes.Combat.*;

	public class RubberBall extends Useable
	{

		public function RubberBall(id:String="", shortName:String="", longName:String="", value:Number=0, description:String="") {
			super("RBRBALL", "R. Ball", "a bouncy rubber ball", 10, "A firm, dark-purple ball that bounces when impacting objects. With practice, you could catch it after it bounces back.");
			invUseOnly = true;
		}

		override public function useItem():Boolean {
			clearOutput();
			if (game.inCombat) {
				switch(rand(4)){
					case 0:
						outputText("You chuck the bouncy ball at [themonster]");
						break;
					case 1:
						outputText("You pitch the ball at high speeds");
						break;
					case 2:
						outputText("You fling the rubber ball");
						break;
					case 3:
						outputText("You bounce the ball at [themonster]");
						break;
				}
				outputText(", ");
				if (monster.short == "goo-girl" || monster.short == "green slime") {
					outputText("slapping the amorphous goo. It looks like an unpleasant impact, but the ball becomes completely submerged inside them. Seems like you can't get that back now.");
					flags[kFLAGS.BONUS_ITEM_AFTER_COMBAT_ID] = useables.RBRBALL.id;
					return false;
				} else if (!combat.combatAvoidDamage({attacker:player,defender:monster,doDodge:true,doParry:false,doBlock:true,toHitChance:monster.standardDodgeFunc(player,10)}).failed) { //Dodged
					outputText("missing completely as the small object is easily avoided.");
					return false;
				} else { //Hit!
					game.combat.damageType = combat.DAMAGE_PHYSICAL_RANGED;
					var damage:Number = monster.reduceDamageCombat(20 + Utils.rand(20));
					damage = game.combat.doDamage(damage, true, true);
					outputText("smacking your adversary with the high-velocity toy. That probably smarts. ");
					if (Utils.rand(100) + player.spe < 50) { //Didn't recover the ball
						outputText("Although the ball ricochets off the point of impact, it flings in a wild direction away from you.");
						player.destroyItems(useables.RBRBALL, 1);
					} else outputText("The ball ricochets off the foe, careening back at you. Easily enough, you catch it, ready to throw again.");
				}
				return true;
			} else {
				var choices:Array = [1, 2, 3];
				if (player.isGoo()) choices.push(4);
				if (player.canFly()) choices.push(5);
				var choice:int = choices[rand(choices.length)];
				switch(choice){
					case 1:
						outputText("With the rubbery ball in hand, you kill some time by tossing it on the ground, bouncing it far into the air. " + (player.spe < 20 ? "It constantly veers wildly away from you after bouncing, forcing you to run off to retrieve it, but you find it fun to play with nonetheless." : (player.spe < 50 ? "As the ball bounces off, you rush into position to catch it. Sometimes you fumble, but it's a fun way to pass the time." : "The ball careens away, colliding with other objects around, before bouncing back toward you. At your level of expertise, you catch it flawlessly and repeat the process.")));
						break;
					case 2:
						outputText("You toss the rubber ball at a nearby rock, causing it to bounce at an awkward angle just over your head. " + (player.spe < 20 ? "With a blank stare, you blink a couple times, relieved it missed your face. As it all happened so fast, it takes a couple minutes to find where the ball ended up" : (player.spe < 50 ? "On reflex, you duck, with the ball careening off behind you. Fortunately, the ball is none-too-difficult to find again" : "Reacting quickly, you catch it")) + ". You continue tossing the ball at the rock, honing your ability to predict the angle it will bounce, and catching it as often as possible.");
						break;
					case 3:
						outputText("You toss the ball around, bouncing it to and fro. " + (game.akky.isOwned() ? "Eyes as wide and dilated as can be, [akky] springs into action on sight of the ball. He leaps to snatch it as it flies, but you manage to get a hold of it again. Amused, you start playing with [akky], watching as he sprints around to keep up with the toy." : ""));
						break;
					case 4:
						outputText("You throw the ball at " + (camp.builtWall ? "the wall" : "a rock") + ", jolting a bit as the toy careens back and slams into your slimy form. It happened so suddenly that the ball slipped right inside. You carefully withdraw the object from your mass, thankful for your lack of rigidity.");
						break;
					case 5:
						outputText("As you bounce the toy around, it flings into the sky. You leap to catch it before it gets too far, flapping your [wings] to aid you. " + (!player.hasBigWings() ? "They do very little to help, but you don't injure yourself. Most importantly, you manage to catch the ball as it was falling!" : "Amidst your graceful flight, you determine where the ball is going to go on its way down, and glide into its path to catch it."));
						break;
				}
				menu();
				addButton(0, "Next", inventory.returnItemToInventory, this, false);
			}
			return true;
		}
	}

}


code:
package classes.Items.Other
{
	import classes.Items.Useable;
	import classes.Player;

	public class SimpleUseable extends Useable
	{
		//This class should be used for items which the player cannot consume, wear or use directly.
		//The useFunction or useText should describe the item or give a hint as to its purpose. After attempted use SimpleUseables return to the inventory automatically.
		private var canUseFunction:Function;
		private var canUseText:String;

		public function SimpleUseable(id:String, shortName:String, longName:String, value:Number, description:String, useText:String, useFunction:Function = null)
		{
			super(id, shortName, longName, value, description);
			canUseFunction = useFunction;
			canUseText = useText;
			invUseOnly = true;
		}

		override public function canUse():Boolean
		{
			clearOutput();
			if (canUseFunction != null)
			{
				canUseFunction();
			}
			else
			{
				outputText(canUseText);
			}
			return false;
		}
	}
}


code:
package classes.Items.Other {
	import classes.Items.Useable;

	public class TeddyBear extends Useable {

		public function TeddyBear() {
			super("TelBear", "Teddy Bear", "a cuddly stuffed bear", 50, "It's a simple and cute teddy bear, a common luxury item for children and adults alike.");
			invUseOnly = true;
		}

		override public function useItem():Boolean {
			clearOutput();
			outputText("It's a simple and cute teddy bear, a common luxury item for children and adults alike.");
			menu();
			addButton(0, "Next", inventory.returnItemToInventory, this, false);
			if (!game.inCombat) addButton(1, "Snuggle", snuggle);
			return true;
		}

		public function snuggle():void {
			outputText("[pg]You hold the bear tightly to your body, pressing your face against the top of its head as you squeeze your worries away. With a contented sigh, you smile softly at the teddy bear before putting it back.");
			menu();
			addButton(0, "Next", inventory.returnItemToInventory, this, false);
		}
	}
}


code:
/**
 * Created by Kitteh6660 on 01.29.15.
 */
package classes.Items
{
	/**
	 * ...
	 * @author Kitteh6660
	 */
	import classes.ItemType;
	import classes.PerkLib;
	import classes.Player;
	import classes.GlobalFlags.kFLAGS

public class Shield extends Useable //Equipable
	{
		public static const WEIGHT_LIGHT:String = "Light";
		public static const WEIGHT_MEDIUM:String = "Medium";
		public static const WEIGHT_HEAVY:String = "Heavy";

		public static const PERK_ABSORPTION:String = "Absorption";

		private var _block:Number;
		private var _perk:String;
		private var _name:String;
		private var _weight:String = WEIGHT_MEDIUM; //Defaults to medium

		public function Shield(id:String, shortName:String, name:String, longName:String, block:Number, value:Number = 0, description:String = null, perk:String = "") {
			super(id, shortName, longName, value, description);
			this._name = name;
			this._block = block;
			this._perk = perk;
		}

		public function get block():Number { return _block; }

		public function get perk():String { return _perk; }

		public function get name():String { return _name; }

		override public function get description():String {
			var desc:String = _description;
			var diff:int = 0;
			//Type
			desc += "\n\nType: Shield";
			//Block Rating
			desc += "\nBlock: " + String(block);
			diff = block - (player.shield.block);
			if (diff > 0)
				desc += " (+" + String(Math.abs(diff)) + ")";
			else if (diff < 0)
				desc += " (-" + String(Math.abs(diff)) + ")";
			//Value
			desc += "\nBase value: " + String(value);
			desc += generateStatsTooltip();
			return desc;
		}

		override public function useText():void {
			outputText("You equip " + longName + ".  ");
		}

		override public function canUse():Boolean {
			if (player.weapon.isTwoHanded()) {
				outputText("Your current weapon requires two hands. Unequip your current weapon or switch to one-handed before equipping this shield. ");
				return false;
			}
			return true;
		}

		public function playerEquip():Shield { //This item is being equipped by the player. Add any perks, etc. - This function should only handle mechanics, not text output
			return this;
		}

		public function playerRemove():Shield { //This item is being removed by the player. Remove any perks, etc. - This function should only handle mechanics, not text output
			return this;
		}

		public function removeText():void {} //Produces any text seen when removing the armor normally

		override public function getMaxStackSize():int {
			return 1;
		}

		public function set weightCategory(newWeight:String):void {
			this._weight = newWeight;
		}
		public function get weightCategory():String {
			return this._weight;
		}
	}
}


code:
package classes.Items
{
	/**
	 * ...
	 * @author Kitteh6660
	 */
	import classes.Items.Shields.*;
	import classes.PerkLib;
	import classes.PerkType;

	public final class ShieldLib
	{
		public static const DEFAULT_VALUE:Number = 6;

		public static const NOTHING:Nothing = new Nothing();

		public const BUCKLER:Shield = new Shield("Buckler", "Buckler", "buckler", "a buckler", 5, 50, "A simple wooden rounded shield.");
		public const GREATSH:Shield = new Shield("GreatSh", "GreatShld", "greatshield", "a greatshield", 12, 300, "A large metal shield. It's a bit heavy.");
		public const KITE_SH:Shield = new Shield("Kite Sh", "KiteShield", "kite shield", "a kite shield", 8, 150, "An average-sized kite shield.");
		public const FLMGRIT_SH:Shield = new FlamegritShield();
		public const TOWERSH:Shield = new TowerShield();
		public const DRGNSHL:DragonShellShield = new DragonShellShield();
		public const RUNESHL:RunedShellShield = new RunedShellShield();
		public const WOODSHL:woodshield = new woodshield();  // new wood shield yay!
		public const AKBSHLD:AkbalShield = new AkbalShield();
		/*private static function mk(id:String,shortName:String,name:String,longName:String,effectId:Number,effectMagnitude:Number,value:Number,description:String,type:String,perk:String=""):Jewelry {
			return new Shield(id,shortName,name,longName,effectId,effectMagnitude,value,description,type,perk);
		}*/

		public function ShieldLib()
		{
		}
	}

}


code:
package classes.Items.Shields {
import classes.Items.Shield;

public class AkbalShield extends Shield {
    public function AkbalShield() {
        super("AkbalShield", "JaguarShield","jaguar shield","a jaguar-faced war shield",14,2000,"A large, oval war shield made of layered wood and copper, with a jaguar-fur fringe along the edges. Affixed to the top of the shield is the head of a jaguar, locked in a fierce expression.");
    }

	override public function get description():String {
		var desc:String = super.description;
		desc += "\nSpecial: Greatly improved block chance vs. demons";
		return desc;
	}
}
}


code:
package classes.Items.Shields
{
	import classes.Items.Shield;

	public class Buckler extends Shield
	{
		public function Buckler(tier:int)
		{
			var ids:Array = ["Buckler", "Bucklr1", "Bucklr2"];
			var eqptNames:Array = ["wooden buckler", "fine buckler", "masterwork buckler"];
			var longNames:Array = ["a wooden buckler", "a fine, reinforced buckler", "a masterwork, metal buckler"];
			this.weightCategory = Shield.WEIGHT_LIGHT;
			this.tier = tier;
			super(ids[tier], "Buckler", eqptNames[tier], longNames[tier], 6, 50, "A small, " + (tier < 2 ? tier == 1 ? "reinforced, wooden" : "wooden" : "metal") + " rounded shield that's light and easy to hold. Doesn't offer much protection but it's better than nothing.");
		}

	}

}


code:
/**
 * Created by aimozg on 10.01.14.
 */
package classes.Items.Shields
{
	import classes.GlobalFlags.kFLAGS;
	import classes.GlobalFlags.kGAMECLASS;
	import classes.Items.Shield;
	import classes.Player;

	public class DragonShellShield extends Shield {

		public function DragonShellShield() {
			super("DrgnShl", "DrgnShl", "dragon-shell shield", "a dragon-shell shield", 14, 1500, "A durable shield that has been forged from the remains of the dragon egg you found in the swamp.  Absorbs any fluid attacks you can catch, rendering them useless.");
		}

		override public function get description():String {
			_description = flags[kFLAGS.EMBER_HATCHED] > 0 ? "A durable shield that has been forged from the dragon eggshell Ember gave you for maxing out " + game.emberScene.emberMF("his", "her") + " affection." : "A durable shield that has been forged from the remains of the dragon egg you found in the swamp.";
			var desc:String = super.description;
			desc += "\nSpecial: Absorbs any fluid attacks you can catch, rendering them useless";
			return desc;
		}

		override public function useText():void { //Produces any text seen when equipping the armor normally
			if (flags[kFLAGS.TIMES_EQUIPPED_EMBER_SHIELD] == 0) {
				clearOutput();
				outputText("Turning the sturdy shield over in inspection, you satisfy yourself as to its craftsmanship and adjust the straps to fit your arm snugly.  You try a few practice swings, but find yourself overbalancing at each one due to the deceptive lightness of the material.  Eventually, though, you pick up the knack of putting enough weight behind it to speed it through the air while thrusting a leg forward to stabilize yourself, and try bashing a nearby rock with it.  You smile with glee as ");
				if (player.str < 80) outputText("bits and pieces from the surface of the");
				else outputText("huge shards of the shattered");
				outputText(" rock are sent flying in all directions.");
				outputText("\n\nAfter a few more practice bashes and shifts to acquaint yourself with its weight, you think you're ready to try facing an enemy with your new protection.  One last thing... taking off the shield and turning it straps-down, you spit onto the surface.  Satisfyingly, the liquid disappears into the shell as soon as it touches.");
			}
			else {
				outputText("You equip " + this.longName + ".  ");
			}
			flags[kFLAGS.TIMES_EQUIPPED_EMBER_SHIELD]++;
		}

	}
}


code:
package classes.Items.Shields {
import classes.Items.Shield;

public class FlamegritShield extends Shield{
    public function FlamegritShield() {
        super("Flamegrit Shield", "FlamegritShield","Flamegrit Shield","a Flamegrit Shield",14,1500,"This large, black and gold circular shield pulses constantly with orange waves of energy. An image of an everlasting flame is engraved in its center. This shield will help you regain health every turn, proportional to the number of followers and lovers in your camp.\n<i>Inquisitors knew that only through unity would they prevail against the demon menace, and some say they fell due to betrayal.</i>");
        boostsHealthRegenFlat(getHealMag);
    }

    public function getHealMag():Number{
        return (camp.followersCount() + camp.loversCount()) * 1.75;
    }

}
}


code:
package classes.Items.Shields
{
	import classes.Items.Shield;

	public class GreatShield extends Shield
	{
		public function GreatShield(tier:int)
		{
			var ids:Array = ["GreatSh", "GrtShl1", "GrtShl2"];
			var eqptNames:Array = ["greatshield", "fine greatshield", "masterwork greatshield"];
			var longNames:Array = ["a greatshield", "a fine greatshield", "a masterwork greatshield"];
			this.weightCategory = Shield.WEIGHT_MEDIUM;
			this.tier = tier;
			super(ids[tier], "GreatShld", eqptNames[tier], longNames[tier], 10, 300, "A large, rectangular metal shield. It's a bit heavy.");
		}

	}

}


code:
package classes.Items.Shields
{
	import classes.Items.Shield;

	public class KiteShield extends Shield
	{
		public function KiteShield(tier:int)
		{
			var ids:Array = ["Kite Sh", "KiteSh1", "KiteSh2"];
			var eqptNames:Array = ["kiteshield", "fine kiteshield", "masterwork kiteshield"];
			var longNames:Array = ["a kiteshield", "a fine kiteshield", "a masterwork kiteshield"];
			this.weightCategory = Shield.WEIGHT_MEDIUM;
			this.tier = tier;
			super(ids[tier], "KiteShld", eqptNames[tier], longNames[tier], 10, 300, "A teardrop-shaped kiteshield made of durable wood.");
		}

	}

}


code:
package classes.Items.Shields
{
	import classes.ItemType;
	import classes.Items.Shield;
	import classes.Player;

	public class Nothing extends Shield
	{
		public function Nothing()
		{
			//this.weightCategory = Shield.WEIGHT_LIGHT;
			super("noshild", "noshield", "nothing", "nothing", 0, 0, "no shield", "shield");
		}

		override public function playerRemove():Shield {
			return null; //There is nothing!
		}
	}
}


code:
package classes.Items.Shields
{
	import classes.Items.Shield;
	import classes.GlobalFlags.kFLAGS;

	public class RunedShellShield extends Shield
	{

		public function RunedShellShield()
		{
			super("RDgnShl", "RDgnShl", "runed dragon-shell shield", "a runed dragon-shell shield", 16, 2000, "A durable shield that has been forged from the remains of the dragon egg you found in the swamp.  Absorbs any fluid attacks you can catch, rendering them useless.");
		}

		override public function get description():String {
			var desc:String = flags[kFLAGS.EMBER_HATCHED] > 0 ? "A durable shield that has been forged from the dragon eggshell Ember gave you for maxing out " + game.emberScene.emberMF("his", "her") + " affection." : "A durable shield that has been forged from the remains of the dragon egg you found in the swamp.";
			desc += " This shield has since been enhanced and now intricate glowing runes surround the edges.";
			//Type
			desc += "\n\nType: Shield";
			//Block Rating
			desc += "\nBlock: " + String(block);
			//Value
			desc += "\nBase value: " + String(value);
			desc += generateStatsTooltip();
			return desc;
		}

	}

}


code:
package classes.Items.Shields
{
	import classes.Items.Shield;

	public class TowerShield extends Shield
	{

		public function TowerShield()
		{
			super("TowerSh", "TowerShld", "tower shield", "a tower shield", 16, 500, "A towering metal shield. It looks heavy!");
		}

		override public function canUse():Boolean {
			if (player.str >= 40) return true;
			outputText("This shield is too heavy for you to hold effectively. Perhaps you should try again when you have at least 40 strength?");
			return false;
		}
	}

}


code:
/**
 * ...
 * @author Melchi ...
 */
package classes.Items.Shields
{
	import classes.GlobalFlags.kFLAGS;
	import classes.GlobalFlags.kGAMECLASS;
	import classes.Items.Shield;
	import classes.Player;

	public class WoodenShield extends Shield
	{

		public function WoodenShield()
		{
			this.weightCategory = Shield.WEIGHT_LIGHT;
			super("WoodShl", "WoodShld", "wood shield", "a wooden shield", 6, 10, "A crude wooden shield. It doesn't look very sturdy");
		}

	}

}


code:
/**
 * ...
 * @author Melchi ...
 */
package classes.Items.Shields
{
	import classes.GlobalFlags.kFLAGS;
	import classes.GlobalFlags.kGAMECLASS;
	import classes.Items.Shield;
	import classes.Player;

	public class woodshield extends Shield
	{

		public function woodshield()
		{
			super("woodSh", "WoodShld", "wood shield", "a wooden shield", 6, 10, "A simple wooden shield.  It doesn't look very sturdy");
		}

	}

}


code:
package classes.Items
{
	/**
	 * @author Kitteh6660
	 */
	public class Undergarment extends Useable //Equipable
	{
		private var _type:Number;
		private var _perk:String;
		private var _name:String;

		public function Undergarment(id:String = "", shortName:String = "", name:String = "", longName:String = "", undergarmentType:Number = 0, value:Number = 0, description:String = null, perk:String = "")
		{
			super(id, shortName, longName, value, description);
			this._type = undergarmentType;
			this._name = name;
			this._perk = perk;
		}

		public function get type():Number { return _type; }

		public function get perk():String { return _perk; }

		public function get name():String { return _name; }

		override public function useText():void {
			outputText("You equip " + longName + ".  ");
		}

		override public function get description():String {
			var desc:String = _description;
			var diff:int = 0;
			desc += "\n\nType: Undergarment ";
			if (type == 0) desc += "(Upper)";
			else if (type == 1) desc += "(Lower)";
			else if (type == 2) desc += "(Full)";
			//Defense
			if (type == UndergarmentLib.TYPE_LOWERWEAR) diff = armorDef - (player.lowerGarment.armorDef);
			else diff = armorDef - (player.upperGarment.armorDef);
			if (armorDef > 0 || diff != 0) desc += "\nDefense: " + String(armorDef);
			desc += appendStatsDifference(diff);
			//Sexiness
			if (type == UndergarmentLib.TYPE_LOWERWEAR) diff = sexiness - (player.lowerGarment.sexiness);
			else diff = sexiness - (player.upperGarment.sexiness);
			if (sexiness > 0 || diff != 0) desc += "\nSexiness: " + String(sexiness);
			desc += appendStatsDifference(diff);
			//Value
			desc += "\nBase value: " + String(value);
			//Naga wearable?
			if (type == 1 && perk == "NagaWearable" && player.isNaga()) desc += "\nNagas aren't restricted from wearing this type of lower undergarment.";
			return desc;
		}

		public function get armorDef():int {
			switch(this.name) {
				case "spider-silk bra":
				case "spider-silk loincloth":
				case "spider-silk panties":
				case "jaguar fur panties":
				case "jaguar fur loincloth":
				case "jaguar fur bra":
					return 1;
				case "dragonscale bra":
				case "dragonscale loincloth":
				case "dragonscale thong":
					return 2;
				case "Ebonweave vest":
				case "Ebonweave corset":
				case "Ebonweave jockstrap":
				case "Ebonweave thong":
				case "Ebonweave loincloth":
				case "runed Ebonweave jockstrap":
				case "runed Ebonweave thong":
				case "runed Ebonweave loincloth":
					return 3;
				default:
					return 0;
			}
		}

		public function get sexiness():int {
			switch(this.name) {
				case "spider-silk bra":
				case "spider-silk loincloth":
				case "spider-silk panties":
				case "dragonscale bra":
				case "dragonscale loincloth":
				case "dragonscale thong":
					return 1;
				case "fundoshi":
				case "fur loincloth":
				case "Ebonweave vest":
				case "Ebonweave corset":
				case "Ebonweave jockstrap":
				case "Ebonweave thong":
				case "Ebonweave loincloth":
					return 2;
				case "latex bra":
				case "latex shorts":
				case "latex thong":
				case "stockings and garters":
				case "runed Ebonweave jockstrap":
				case "runed Ebonweave thong":
				case "runed Ebonweave loincloth":
					return 3;
				case "jaguar fur panties":
				case "jaguar fur loincloth":
				case "jaguar fur bra":
					return 4;
				default:
					return 0;
			}
		}

		override public function canUse():Boolean {
			if (player.armor.id == armors.VINARMR.id) {
				outputText("You attempt to put on your " + name + ", but the instant a bit of it presses down on your vines, a terrible burning sensation shoots across your [skinshort]. This plant does not like being covered.");
				return false;
			}
			if (!player.armor.supportsUndergarment) {
				outputText("It would be awkward to put on undergarments when you're currently wearing your type of clothing. You should consider switching to different clothes. You put it back into your inventory.");
				return false;
			}
			if (type == UndergarmentLib.TYPE_LOWERWEAR) {
				if (player.isBiped() || player.isGoo()) {
					return true; //It doesn't matter what leg type you have as long as you're biped.
				}
				else if (player.isTaur() || player.isDrider()) {
					outputText("Your form makes it impossible to put on any form of lower undergarments. You put it back into your inventory.");
					return false;
				}
				else if (player.isNaga()) {
					if (perk != "NagaWearable") {
						outputText("It's impossible to put on this undergarment as it's designed for someone with two legs. You put it back into your inventory.");
						return false;
					}
					else return true;
				}
			}
			return true;
		}

		public function playerEquip():Undergarment { //This item is being equipped by the player. Add any perks, etc. - This function should only handle mechanics, not text output
			return this;
		}

		public function playerRemove():Undergarment { //This item is being removed by the player. Remove any perks, etc. - This function should only handle mechanics, not text output
			return this;
		}

		public function removeText():void {} //Produces any text seen when removing the undergarment normally

		override public function getMaxStackSize():int {
			return 5;
		}

	}

}


code:
package classes.Items
{
	/**
	 * ...
	 * @author Kitteh6660
	 */
	import classes.Items.Undergarments.*;
	import classes.PerkLib;
	import classes.PerkType;

	public final class UndergarmentLib
	{
		public static const DEFAULT_VALUE:Number = 6;

		public static const TYPE_UPPERWEAR:int = 0;
		public static const TYPE_LOWERWEAR:int = 1;
		public static const TYPE_FULLWEAR:int  = 2;

		//public static const COMFORTABLE_UNDERCLOTHES:Undergarment = new ComfortableUnderclothes();
		public static const NOTHING:Undergarment = new Nothing();

		//Upper
		public const C_BRA  :Undergarment = new Undergarment("C. Bra ", "C. Bra", "comfortable bra", "a comfortable bra", TYPE_UPPERWEAR, DEFAULT_VALUE, "A generic bra.");
		public const LOLIBRA:Undergarment = new Undergarment("LoliBra", "Train. Bra", "training bra", "a comfortable training bra", TYPE_UPPERWEAR, 20, "This bra is made from thin and lightweight cotton. It provides no real support and is designed to protect a young girl's sensitive, budding breasts.");
		public const DS_BRA :Undergarment = new Undergarment("DS. Bra", "D.Scale Bra", "dragonscale bra", "a dragonscale bra", TYPE_UPPERWEAR, 360, "This bra appears to be made of dragon scale. It's held together with leather straps for flexibility. Great for those on the primal side!");
		public const LTX_BRA:Undergarment = new Undergarment("Ltx.Bra", "Latex Bra", "latex bra", "a latex bra", TYPE_UPPERWEAR, 250, "This bra is black and shiny, obviously made of latex. It's designed to fit snugly against your breasts.");
		public const SS_BRA :Undergarment = new Undergarment("SS. Bra", "S.Silk Bra", "spider-silk bra", "a spider-silk bra", TYPE_UPPERWEAR, 1000, "This bra looks incredibly comfortable. It's as white as snow and finely woven with hundreds of strands of spider silk.");
		public const EBNVEST:Undergarment = new Undergarment("EW Vest", "EW Vest", "Ebonweave vest", "an Ebonweave vest", TYPE_UPPERWEAR, 900, "This vest is made of ebonweave, created using refined Ebonbloom petals. Elastic, form-fitting, and somewhat transparent, this comfortable vest will display your curves, masculine or feminine.");
		public const EBNCRST:Undergarment = new Undergarment("EW Crst", "EW Corset", "Ebonweave corset", "an Ebonweave corset", TYPE_UPPERWEAR, 900, "This corset is made of ebonweave, created using refined Ebonbloom petals. The ebonweave is elastic, making the corset surprisingly comfortable to wear, while displaying your bust down to the most subtle curves.");
		public const JAG_BRA:Undergarment = new Undergarment("JagBra", "Jag. Bra", "jaguar fur bra", "a shimmering jaguar fur bra", TYPE_UPPERWEAR, 2000, "This atrapless bra has a rough, primitive design, and is covered in a layer of glossy jaguar fur.");

		//Lower
		public const BLOOMER:Undergarment = new Undergarment("Bloomers", "Bloomers", "bloomers", "a pair of bloomers", TYPE_LOWERWEAR, 40, "A pair of baggy, old-fashioned bloomers. An air of innocence surrounds them.");
		public const C_LOIN :Undergarment = new Undergarment("C. Loin", "C. Loin", "comfortable loincloth", "a comfortable loincloth", TYPE_LOWERWEAR, DEFAULT_VALUE, "A generic loincloth.", "NagaWearable");
		public const C_PANTY:Undergarment = new Undergarment("C.Panty", "C.Panties", "comfortable panties", "a pair of comfortable panties", TYPE_LOWERWEAR, DEFAULT_VALUE, "A generic pair of panties.");
		public const LOLIPAN:Undergarment = new Undergarment("LoliPan", "ChildPanties", "children's panties", "a cute pair of children's panties", TYPE_LOWERWEAR, 20, "These panties are made from pure white, extra-soft cotton, with cute designs embroidered all over and a tiny pink ribbon on the front.");
		public const DS_LOIN:Undergarment = new Undergarment("DS.Loin", "D.Scale Loin", "dragonscale loincloth", "a dragonscale loincloth", TYPE_LOWERWEAR, 360, "This loincloth appears to be made of dragonscale and held together with a leather strap that goes around your waist. Great for those on the wild side!", "NagaWearable");
		public const DSTHONG:Undergarment = new Undergarment("DSPanty", "D.Scale Thong", "dragonscale thong", "a dragonscale thong", TYPE_LOWERWEAR, 360, "This thong appears to be made of dragonscale and held together with a leather strap that goes around your waist. Great for those on the wild side!");
		public const FUNDOSH:Undergarment = new Undergarment("Fundosh", "Fundoshi", "fundoshi", "a fundoshi", TYPE_LOWERWEAR, 20, "This Japanese-styled undergarment resembles a cross between a thong and a loincloth.");
		public const FURLOIN:Undergarment = new Undergarment("FurLoin", "FurLoin", "fur loincloth", "a front and back set of loincloths", TYPE_LOWERWEAR, DEFAULT_VALUE, "A loincloth to cover your crotch and butt.  Typically worn by people named 'Conan'. ", "NagaWearable");
		public const GARTERS:Undergarment = new Undergarment("Garters", "Garters", "stockings and garters", "a pair of stockings and garters", TYPE_LOWERWEAR, DEFAULT_VALUE, "These pairs of stockings, garters and lingerie are perfect for seducing your partner!");
		public const LTXSHRT:Undergarment = new Undergarment("LtxShrt", "LatexShorts", "latex shorts", "a pair of latex shorts", TYPE_LOWERWEAR, 300, "These shorts are black and shiny, obviously made of latex. It's designed to fit snugly against your form.");
		public const LTXTHNG:Undergarment = new Undergarment("LtxThng", "LatexThong", "latex thong", "a latex thong", TYPE_LOWERWEAR, 300, "This thong is black and shiny, obviously made of latex. It's designed to fit snugly against your form.");
		public const SS_LOIN:Undergarment = new Undergarment("SS.Loin", "S.Silk Loin", "spider-silk loincloth", "a spider-silk loincloth", TYPE_LOWERWEAR, 1000, "This loincloth looks incredibly comfortable. It's as white as snow and finely woven with hundreds of strands of spider silk.", "NagaWearable");
		public const SSPANTY:Undergarment = new Undergarment("SSPanty", "S.Silk Panty", "spider-silk panties", "a pair of spider-silk panties", TYPE_LOWERWEAR, 1000, "These panties look incredibly comfortable. It's as white as snow and finely woven with hundreds of strands of spider silk.");
		public const EBNJOCK:Undergarment = new Undergarment("EWStrap", "Ebon Jock", "Ebonweave jockstrap", "an Ebonweave jockstrap", TYPE_LOWERWEAR, 900, "This jock is ebonweave, made of refined Ebonbloom petals. It’s comfortable and elastic, providing support while containing assets of any size.");
		public const EBNTHNG:Undergarment = new Undergarment("EWThong", "Ebon Thong", "Ebonweave thong", "an Ebonweave thong", TYPE_LOWERWEAR, 900, "This thong is made of ebonweave, designed to fit snugly around your form. Thanks to alchemic treatments, it’s elastic enough to hold assets of any size.");
		public const EBNCLTH:Undergarment = new Undergarment("EWCloth", "Ebon Loin", "Ebonweave loincloth", "an Ebonweave loincloth", TYPE_LOWERWEAR, 900, "This loincloth is made of ebonweave, designed to fit snugly around your form. Thanks to alchemic treatments, it’s elastic enough to hold assets of any size.", "NagaWearable");
		public const EBNRJCK:Undergarment = new UndergarmentWithPerk("RnStrap", "Rune Jock", "runed Ebonweave jockstrap", "a runed Ebonweave jockstrap", TYPE_LOWERWEAR, 1200, "This jock is ebonweave, made of refined Ebonbloom petals. Adorning the pouch is a rune of lust, glowing with dark magic.",
				PerkLib.WellspringOfLust,0,0,0,0,"At the beginning of combat, lust raises to black magic threshold if lust is below black magic threshold.");
		public const EBNRTNG:Undergarment = new UndergarmentWithPerk("RnThong", "Rune Thong", "runed Ebonweave thong", "a runed Ebonweave thong", TYPE_LOWERWEAR, 1200, "This thong is made of ebonweave, designed to fit snugly around your form. Adorning the front is a rune of lust, glowing with dark magic.",
				PerkLib.WellspringOfLust,0,0,0,0,"At the beginning of combat, lust raises to black magic threshold if lust is below black magic threshold.");
		public const EBNRLNC:Undergarment = new UndergarmentWithPerk("RnCloth", "Rune Loin", "runed Ebonweave loincloth", "a runed Ebonweave loincloth", TYPE_LOWERWEAR, 1200, "This loincloth is made of ebonweave, designed to fit snugly around your form. Adorning the front is a rune of lust, glowing with dark magic.",
				PerkLib.WellspringOfLust,0,0,0,0,"At the beginning of combat, lust raises to black magic threshold if lust is below black magic threshold.","NagaWearable");
		public const JAG_PAN:Undergarment = new Undergarment("JagPan", "Jag. Panties", "jaguar fur panties", "a pair of shimmering jaguar fur panties", TYPE_LOWERWEAR, 2000, "These panties are covered in a layer of lustrous jaguar fur. They look like they'd be warm and cozy if they weren't so tiny.");
		public const JAGLOIN:Undergarment = new Undergarment("JagLoin", "Jag. Loin", "jaguar fur loincloth", "a shimmering jaguar fur loincloth", TYPE_LOWERWEAR, 2000, "A barbaric loincloth made of jaguar fur, with a string of sharp jaguar teeth decorating the front.", "NagaWearable");
		public const PHPANTY:Undergarment = new Undergarment("PHPanty", "PHeartPan", "pink-hearted panties", "a pair of white panties with pink hearts", TYPE_LOWERWEAR, 30, "A pair of innocent, white cotton panties, fashioned with a generous sprinkle of light-pink hearts across their soft fabric. Quite popular with little girls of all upbringings.");
		public const WHPANTY:Undergarment = new Undergarment("WHPanty", "WHeartPan", "white-hearted panties", "a pair of pink panties with white hearts", TYPE_LOWERWEAR, 30, "A pair of innocent, light-pink cotton panties, fashioned with a generous sprinkle of white hearts across their soft fabric. Especially popular with very young girls of all upbringings.");

		public function UndergarmentLib()
		{
		}

	}

}


code:
package classes.Items.Undergarments
{
	import classes.Items.Undergarment;

	public final class ComfortableUnderclothes extends Undergarment
	{

		public function ComfortableUnderclothes() {
			super("c.under", "c.under", "comfortable underclothes", "comfortable underclothes", 1, 6, "A pair of comfortable undergarment. \n\nType: Undergarments");
		}

	}
}


code:
package classes.Items.Undergarments
{
	import classes.Items.Undergarment;

	public class Nothing extends Undergarment
	{
		public function Nothing() {
			super("nounder", "nounder", "nothing", "nothing", -1, 0, "nothing", "light");
		}

		override public function playerRemove():Undergarment {
			return null; //Player never picks up their underclothes
		}
	}
}


code:
package classes.Items.Undergarments {

	import classes.Items.Undergarment;
	import classes.Items.UndergarmentLib;
	import classes.PerkType;
	import classes.PerkLib;
	import classes.Player;

	//Ebonweave makin me code new shit -w- ~Foxwells

	public class UndergarmentWithPerk extends Undergarment {
		private var _type:Number;
		private var _perk:String;
		private var _name:String;
		private var playerPerk:PerkType;
		private var playerPerkV1:Number;
		private var playerPerkV2:Number;
		private var playerPerkV3:Number;
		private var playerPerkV4:Number;

		public function UndergarmentWithPerk(id:String, shortName:String, name:String, longName:String, undergarmentType:Number, value:Number, description:String, playerPerk:PerkType, playerPerkV1:Number, playerPerkV2:Number, playerPerkV3:Number, playerPerkV4:Number, playerPerkDesc:String = "", perk:String = "") {
			super(id, shortName, name, longName, undergarmentType, value, description, perk);
			this._type = undergarmentType;
			this._name = name;
			this._perk = perk;
			this.playerPerk = playerPerk;
			this.playerPerkV1 = playerPerkV1;
			this.playerPerkV2 = playerPerkV2;
			this.playerPerkV3 = playerPerkV3;
			this.playerPerkV4 = playerPerkV4;
		}

		override public function get type():Number { return _type; }

		override public function get perk():String { return _perk; }

		override public function get name():String { return _name; }

		override public function useText():void {
			outputText("You equip " + longName + ". ");
		}

		override public function playerEquip():Undergarment { //This item is being equipped by the player. Add any perks, etc.
			while (player.findPerk(playerPerk) >= 0) player.removePerk(playerPerk);
			player.createPerk(playerPerk, playerPerkV1, playerPerkV2, playerPerkV3, playerPerkV4);
			return super.playerEquip();
		}

		override public function playerRemove():Undergarment { //This item is being removed by the player. Remove any perks, etc.
			while (player.findPerk(playerPerk) >= 0) player.removePerk(playerPerk);
			return super.playerRemove();
		}

		override public function removeText():void {} //Produces any text seen when removing the undergarment normally

		override public function get description():String {
			var desc:String = super.description;
			desc += "\nSpecials: " + playerPerk.name;
			if (playerPerk == PerkLib.WizardsEndurance) desc += " (-" + playerPerkV1 + "% Spell Cost)";
			else if (playerPerk == PerkLib.WellspringOfLust) {
				if (player.lust < 50) {
					desc += " (+" + (50 - player.lust) + " lust)";
				}
			}
			else if (playerPerkV1 > 0) desc += " (Magnitude: " + playerPerkV1 + ")";
			return desc;
		}

		override public function get armorDef():int {
			switch(this.name) {
				case "runed Ebonweave jockstrap":
				case "runed Ebonweave thong":
				case "runed Ebonweave loincloth":
					return 3;
				default:
					return 0;
			}
		}

		override public function get sexiness():int {
			switch(this.name) {
				case "runed Ebonweave jockstrap":
				case "runed Ebonweave thong":
				case "runed Ebonweave loincloth":
					return 3;
				default:
					return 0;
			}
		}

		override public function canUse():Boolean {
			if (!player.armor.supportsUndergarment) {
				outputText("It would be awkward to put on undergarments when you're currently wearing your type of clothing. You should consider switching to different clothes. You put it back into your inventory.");
				return false;
			}
			if (type == UndergarmentLib.TYPE_LOWERWEAR) {
				if (player.isBiped() || player.isGoo()) {
					return true; //It doesn't matter what leg type you have as long as you're biped.
				}
				else if (player.isTaur() || player.isDrider()) {
					outputText("Your form makes it impossible to put on any form of lower undergarments. You put it back into your inventory.");
					return false;
				}
				else if (player.isNaga()) {
					if (perk != "NagaWearable") {
						outputText("It's impossible to put on this undergarment as it's designed for someone with two legs. You put it back into your inventory.");
						return false;
					}
					else return true;
				}
			}
			return true;
		}

	}
}


code:
/**
 * Created by aimozg on 09.01.14.
 */
package classes.Items
{
import classes.BonusDerivedStats;
import classes.CoC;
	import classes.CoC_Settings;
import classes.Creature;
import classes.GlobalFlags.kGAMECLASS;
	import classes.ItemType;

	/**
	 * Represent item that can be used but does not necessarily disappears on use. Direct subclasses should overrride
	 * "useItem" method.
	 */
	public class Useable extends ItemType {

		public function Useable(id:String = "", shortName:String = "", longName:String = "", value:Number = 0, description:String = "") {
			super(id, shortName, longName, value, description);
		}

		override public function set description(newDesc:String):void {
			this._description = newDesc;
		}

		override public function get description():String {
			var desc:String = _description;
			//Type
			desc += "\n\nType: ";
			if (shortName == "Condom" || shortName == "GldStat") desc += "Miscellaneous";
			else if (shortName == "Debug Wand") desc += "Miscellaneous (Cheat Item)";
			else desc += "Material";
			//Value
			desc += "\nBase value: " + String(value);
			desc += generateStatsTooltip();
			return desc;
		}

        public function onUse():void{

		}

		public function canUse():Boolean { return prison.prisonCanUseItem(this); } //If an item cannot be used it should provide some description of why not

		public function useItem():Boolean {
			CoC_Settings.errorAMC("Useable", "useItem", id);
			return(false);
		}

		public function generateStatsTooltip():String{
			var retv:String = "";
			var resetHost:Boolean = false;
			if(host == null){
				host = player;
				resetHost = true;
            }

            for(var key:String in bonusStats.statArray){
				if(bonusStats.statArray[key]){
					var value:Number = 0;
					if(bonusStats.statArray[key].value is Function) value =  bonusStats.statArray[key].value();
					else value =  bonusStats.statArray[key].value;
					if(bonusStats.statArray[key].multiply) {
						retv += "\n" + key + ": " + "<font color=\"" + (((value < 1 && BonusDerivedStats.goodNegatives.indexOf(key) == -1) || (value >= 1 && BonusDerivedStats.goodNegatives.indexOf(key) != -1)) ? mainViewManager.colorHpMinus() : mainViewManager.colorHpPlus()) + "\">x" + Math.round(value*100) + "%";
                    }
                    else retv += "\n" + key + ": " + "<font color=\"" + (((value < 0 && BonusDerivedStats.goodNegatives.indexOf(key) == -1) || (value >= 0 && BonusDerivedStats.goodNegatives.indexOf(key) != -1)) ? mainViewManager.colorHpMinus() : mainViewManager.colorHpPlus()) + "\">" + (value >= 0 ? "+" + value : value) + (BonusDerivedStats.percentageAdditions.indexOf(key) != -1 ? "%" : "") +"";
                }
			}
			if(resetHost) host = null;
			return retv;
		}
		public function useText():void {} //Produces any text seen when using or equipping the item normally

		public var invUseOnly:Boolean = false; //Set to true to prevent this item from being used outside of the inventory (when picking it up with a full inventory for example)
	}
}


code:
/**
 * Created by aimozg on 10.01.14.
 */
package classes.Items
{
	import classes.GlobalFlags.kGAMECLASS;
	import classes.BaseContent;
	import classes.Items.Other.*;

	use namespace kGAMECLASS;

	public final class UseableLib extends BaseContent
	{
		public function UseableLib() {}

		//MATERIALS
		public const B_CHITN:SimpleUseable = new SimpleUseable("B.Chitn", "B.Chitn", "a large shard of chitinous plating", 6,
			"A perfect piece of black chitin from a bee-girl. It still has some fuzz on it.",
			"You look over the chitin carefully, but cannot find a use for it. Maybe someone else will know how to use it.");
		public const GREENGL:SimpleUseable = new SimpleUseable("GreenGl", "GreenGl", "a clump of green gel", 6,
			"This tough substance has no obvious use that you can discern.",
			"You examine the gel thoroughly, noting it is tough and resiliant, yet extremely pliable. Somehow, you know eating it would not be a good idea.");
		public const T_SSILK:SimpleUseable = new SimpleUseable("T.SSilk", "T.SSilk", "a bundle of tough spider-silk", 6,
			"This bundle of fibrous silk is incredibly tough and strong, though somehow not sticky in the slightest. You have no idea how to work these tough little strands into anything usable.  Perhaps one of this land's natives might have an idea?",
			"You look over the tough webbing, confusion evident in your expression. There's really nothing practical you can do with these yourself. It might be best to find someone more familiar with the odd materials in this land to see if they can make sense of it.");
		public const D_SCALE:SimpleUseable = new SimpleUseable("D.Scale", "Dragonscale", "a freshly-shed dragonscale", 45,
			"This sheet of dragon scale is incredibly strong and flexible.  No dragons were seriously harmed in the acquisition of this item.",
			"You look over the sheet of dragon scale. You've seen various legends about how the scales of a dragon can be worked into tough armor or used in alchemy.");
		public const LETHITE:SimpleUseable = new SimpleUseable("Lethite", "Lethicite", "a chunk of lethicite", 1000,
			"A chunk of lethicite. It's very rare, as lethicite is only produced when a mortal becomes a demon and cums their souls out. A demon's favourite treat.",
			"You examine the purple crystal. It must be lethicite. You know that the demons like to consume them, but you're sure there might be a use for it.");
		public const EBNFLWR:SimpleUseable = new SimpleUseable("E.Flower", "E.Flower", "an Ebonbloom flower", 600,
			"Gray metallic flowers that are known to bloom in the deepest caves below the mountains.",
			"You look over the Ebonbloom flower. It's rather pretty, outside of the fact that it reflects sunlight and blinds you. They're rare, so you're sure you could sell it if you wanted, though you can't help but wonder if there's another use for it.");
		public const OBSHARD:SimpleUseable = new SimpleUseable("ObShard", "Ob.Shard", "a shard of obsidian", 200,
			"A small shard of obsidian. Formed from rapidly-cooling lava, it's a volcanic glass that is known to be very sharp albeit fragile.",
			"You look over the shard of obsidian, holding the shard with care. It's rather shiny, easily reflecting the sunlight. Knowing how sharp the shard is, you could find someone who could work it into some deadly weapons.");
		public const AKBPELT:SimpleUseable = new SimpleUseable("AkbPelt", "AkbalPelt", "a luxurious jaguar skin", 1000,
			"A pelt of tawny fur with black rosettes, the head and skull still attached. Despite the soft and silky feel, it seems to be incredibly durable.",
			"As you stroke the fur, you think about what you could use it for. It's certainly strong enough to be useful as armor, or it would make a nice rug to add a touch of style to your home. You can still feel demonic magic coming from the pelt, unsurprising when you consider the strength of the greater demon it came from.\n(Just hold on to it for now, the ability to make something out of it will be coming eventually.)");
		//WALL DECOR
		public const IMPSKLL:SimpleUseable = new SimpleUseable("ImpSkll", "ImpSkull", "an imp skull", 25,
			"A skull taken from a slain imp.",
			"You look at the imp skull.  A pair of horns protrude from the skull.  You admire the overall frame of the skull yet you find no obvious uses for it.");
		public const A_SHARD:SimpleUseable = new SimpleUseable("A. Shard", "AbyssalShard", "A pitch black crystal", 0, "A crystal taken from the Necromancer's corpse. It is mostly pitch black, but you can spot some blinking dots in its interior, as if it was a night sky dotted with stars. It doesn't appear to have any real use, but, maybe?", "",game.dungeons.manor.useCrystal);
		//MISCELLANEOUS
		public const CONDOM :SimpleUseable = new SimpleUseable("Condom ", "Condom", "a packet of condoms", 6,
			"This wrapper contains a latex condom that can be worn over the penis. It's designed to prevent pregnancy most of the time. Can be used in certain sex scenes.",
			"You look at the unopened packet of condom.  If applicable, you can use the condom to prevent pregnancy most of the time.");
		public const GLDSTAT:SimpleUseable = new SimpleUseable("GldStat", "GldStat", "a golden statue", 600,
			"An intricate golden idol of an androgynous humanoid figure with nine long tails.  It probably had some spiritual significance to its owner.",
			"", game.forest.kitsuneScene.kitsuneStatue);
		public const TELBEAR:TeddyBear = new TeddyBear();
		public const RBRBALL:RubberBall = new RubberBall();
		//CHEAT ITEM
		public const DBGWAND:DebugWand = new DebugWand();
	}
}


code:
/**
 * Created by aimozg on 09.01.14.
 */
package classes.Items
{
import classes.BonusDerivedStats;
import classes.ItemType;
	import classes.Player;
	import classes.PerkLib;
	import classes.MasteryType;
	import classes.MasteryLib;
	import classes.Items.WeaponEffects;
	import classes.Scenes.Combat.CombatAttackData;
import classes.Scenes.Combat.CombatAttackData;

public class Weapon extends Useable //Equipable
	{
		public static const WEIGHT_LIGHT:String = "Light";
		public static const WEIGHT_MEDIUM:String = "Medium";
		public static const WEIGHT_HEAVY:String = "Heavy";

		protected var _verb:String;
		protected var _attack:Number;
		protected var _perk:Array;
		protected var _weight:String = WEIGHT_MEDIUM; //Defaults to medium
        protected var _name:String;
		protected var _armorMod:Number;
		protected var _effects:Array;
		protected var _ammoMax:int;
		protected var _accBonus:Number;

        public function set verb(value:String):void {
            _verb = value;
        }

        public function set attack(value:Number):void {
            _attack = value;
        }

        public function set perk(value:Array):void {
            _perk = value;
        }

        public function set name(value:String):void {
            _name = value;
        }

        public function set armorMod(value:Number):void {
            _armorMod = value;
        }

        public function set weight(value:String):void {
            _weight = value;
        }

        public function set effects(value:Array):void {
            _effects = value;
        }

        public function set ammoMax(value:int):void {
            _ammoMax = value;
        }

        public function set accBonus(value:Number):void {
            _accBonus = value;
        }

		public function get verb():String { return _verb; }

		public function get attack():Number { return _attack; }

		public function get perk():Array { return _perk; }

		public function get name():String { return _name; }

		public function get armorMod():Number { return _armorMod; }

		public function get effects():Array { return _effects; }

		public function get ammoMax():int { return _ammoMax; }

		public function get accBonus():Number { return _accBonus; }

		public static const WEAPONEFFECTS:WeaponEffects = new WeaponEffects();

		public function Weapon(id:String = "", shortName:String = "", name:String = "", longName:String = "", verb:String = "", attack:Number = 0, value:Number = 0, description:String = null, perk:Array = null, armorMod:Number = 1, effect:Array = null, ammoMax:int = 0,accBonus:Number = 0) {
			super(id, shortName, longName, value, description);
			this._name = name;
			this._verb = verb;
			this._attack = attack;
			this._perk = perk == null ? [] : perk;
			this._armorMod = armorMod;
			this._effects = effect;
			this._ammoMax = ammoMax;
			this._accBonus = accBonus;
		}

		override public function get description():String {
			var desc:String = _description;
			//Type
			desc += "\n\nType: ";
			if (isLarge()) desc += "(Large) ";
			if (isDual()) desc += "(Dual-wielded) ";
			if (listMasteries() == "") desc += "Unspecified";
			else desc += listMasteries();
 			//Attack
			desc += "\nAttack(Base): " + String(attack) + "\nAttack(Modified): " + String(modifiedAttack());
			if (player.weapon.modifiedAttack() < modifiedAttack()) desc += "(+" + (modifiedAttack() - player.weapon.modifiedAttack())  +")";
			else if (player.weapon.modifiedAttack() > modifiedAttack()) desc += "(-" + (player.weapon.modifiedAttack() - modifiedAttack())  +")";
			else desc += "(0)";
			desc += "\nArmor Penetration: " + String(Math.round((1 - armorMod) * 100)) + "%";
			if (accBonus != 0) desc += "\nAccuracy Modifier: " + accBonus;
			//Value
			desc += "\nBase value: " + String(value);
			desc += generateStatsTooltip();
			return desc;
		}

		public function modifiedAttack():Number {
			var attackMod:Number = attack;
			attackMod += player.getBonusStat(BonusDerivedStats.weaponDamage);
			attackMod *= player.getBonusStatMultiplicative(BonusDerivedStats.weaponDamage);
			//Bonus for being samurai!
			if (player.armor == game.armors.SAMUARM && this == game.weapons.KATANA)
				attackMod += 2;
			return attackMod;
		}

		public function execEffect():void{
			for each(var effect:Function in effects){
				effect();
			}
		}

		override public function useText():void {
			outputText("You equip " + longName + ".  ");
			if (isTwoHanded() && player.shield != ShieldLib.NOTHING && !(player.hasPerk(PerkLib.TitanGrip) && player.str >= 90)) {
				outputText("Because the weapon requires the use of two hands, you have unequipped your shield. ");
			}
		}

		override public function canUse():Boolean {
			return true;
		}

		public function playerEquip():Weapon { //This item is being equipped by the player. Add any perks, etc. - This function should only handle mechanics, not text output
			if (isTwoHanded() && player.shield != ShieldLib.NOTHING && !(player.hasPerk(PerkLib.TitanGrip) && player.str >= 90)) {
				inventory.unequipShield();
			}
			return this;
		}

		public function playerRemove():Weapon { //This item is being removed by the player. Remove any perks, etc. - This function should only handle mechanics, not text output
			return this;
		}

		public function removeText():void {} //Produces any text seen when removing the armor normally

		override public function getMaxStackSize():int {
			return 1;
		}

		public function set weightCategory(newWeight:String):void {
			this._weight = newWeight;
		}

		public function get weightCategory():String {
			return this._weight;
		}

		public function setPerks(perks:Array):void{
			this._perk = perks;
		}

		public function setArmorPenetration(AP:Number):void{
			this._armorMod = AP;
		}

		//Return an array of weapon types. This means a weapon can have multiple types; say you want to make a spear that can be used as a wizard's staff, or have a mastery for swords and another mastery for all one-handed weapons.
		//Will find any masteries in MasteryLib.MASTERY_WEAPONS with an ID that matches one of the weapon's tags.
		public function getMasteries():Array {
			if (perk == null) return [];
			var weaponTypes:Array = [];
			for each (var mastery:MasteryType in MasteryLib.MASTERY_WEAPONS) {
				if (perk.indexOf(mastery.id) != -1) {
					weaponTypes.push(mastery);
				}
			}
			return weaponTypes;
		}

		public function listMasteries():String {
			var stringArray:Array = [];
			for each (var mastery:MasteryType in getMasteries()) {
				stringArray.push(mastery.name);
			}
			return stringArray.join(", ");
		}

		//Add xp to all matching masteries for the weapon
		public function weaponXP(xp:int, announce:Boolean = true):void {
			for each (var mastery:MasteryType in getMasteries()) {
				player.masteryXP(mastery, xp, announce);
			}
		}

		//Return average level of all matching masteries
		public function masteryLevel():int {
			var matches:int = 0;
			var levels:int = 0;
			for each (var mastery:MasteryType in getMasteries()) {
				matches++;
				levels += player.masteryLevel(mastery);
			}
			return levels / matches;
		}

		//Check if a weapon uses multiple masteries
		public function isHybrid():Boolean {
			if (getMasteries().length > 1) return true;
			return false;
		}

		public function isCunning():Boolean{
			return perk != null ? perk.indexOf(WeaponTags.CUNNING) != -1 : false;
		}
		public function isAphrodisiac():Boolean{
			return perk != null ? perk.indexOf(WeaponTags.APHRODISIAC) != -1 : false;
		}
		public function isFirearm():Boolean{
			return perk != null ? perk.indexOf(WeaponTags.FIREARM) != -1 : false;
		}
		public function isHolySword():Boolean{
			return perk != null ? perk.indexOf(WeaponTags.HOLYSWORD) != -1 : false;
		}
		public function isUnholy():Boolean{
			return perk != null ? perk.indexOf(WeaponTags.UGLYSWORD) != -1 : false;
		}
		public function isSpear():Boolean {
            return perk != null ? perk.indexOf(WeaponTags.SPEAR) != -1 : false;
		}
		public function isFist():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.FIST) != -1 : false;
		}
		public function isBow():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.BOW) != -1 : false;
		}
		public function is1HSword():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.SWORD1H) != -1 : false;
		}
		public function is2HSword():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.SWORD2H) != -1 : false;
		}
		public function isKnife():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.KNIFE) != -1 : false;
		}
		public function is1HBlunt():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.BLUNT1H) != -1 : false;
		}
		public function is2HBlunt():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.BLUNT2H) != -1 : false;
		}
		public function isAxe():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.AXE) != -1 : false;
		}
		public function isStaff():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.STAFF) != -1 : false;
		}
		public function isPolearm():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.POLEARM) != -1 : false;
		}
		public function isScythe():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.SCYTHE) != -1 : false;
		}
		public function isWhip():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.WHIP) != -1 : false;
		}
		public function isCrossbow():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.CROSSBOW) != -1 : false;
		}
		public function isMagicStaff():Boolean{
            return perk != null ? perk.indexOf(WeaponTags.MAGIC) != -1 : false;
		}
		public function isAttached():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.ATTACHED) != -1 : false;
		}
		public function isTwoHanded():Boolean {
			return isLarge() || isDual();
		}
		public function isLarge():Boolean{
			if (perk == null) return false;
			if (perk.indexOf(WeaponTags.LARGE) != -1) return true;
			var largeTypes:Array = ["2H Sword", "2H Blunt"];
			for each (var tag:String in largeTypes) {
				if (perk.indexOf(tag) != -1) return true;
			}
			return false;
		}
		public function isDual():Boolean {
			return perk != null ? perk.indexOf(WeaponTags.DUAL) != -1 : false;
		}
		public function isRanged():Boolean{
			if (perk == null) return false;
			if (perk.indexOf(WeaponTags.RANGED) != -1) return true;
			var rangedTypes:Array = ["Bow", "Crossbow", "Firearm"];
			for each (var tag:String in rangedTypes) {
				if (perk.indexOf(tag) != -1) return true;
			}
			return false;
		}
		public function isSharp():Boolean{
			if (perk == null) return false;
			if (perk.indexOf(WeaponTags.SHARP) != -1) return true;
			if (perk.indexOf(WeaponTags.NOTSHARP) != -1) return false;
			var sharpTypes:Array = ["1H Sword", "2H Sword", "Knife", "Scythe", "Axe"];
			for each (var tag:String in sharpTypes) {
				if (perk.indexOf(tag) != -1) return true;
			}
			return false;
		}
        public function isBladed():Boolean{
			if (perk == null) return false;
			if (perk.indexOf(WeaponTags.BLADED) != -1) return true;
			if (perk.indexOf(WeaponTags.NOTBLADED) != -1) return false;
			var bladedTypes:Array = ["1H Sword", "2H Sword", "Scythe", "Axe"];
			for each (var tag:String in bladedTypes) {
				if (perk.indexOf(tag) != -1) return true;
			}
			return false;
        }
		public function isBlunt():Boolean{
			if (perk == null) return false;
			if (perk.indexOf(WeaponTags.BLUNT) != -1) return true;
			if (perk.indexOf(WeaponTags.NOTBLUNT) != -1) return false;
			var bluntTypes:Array = ["1H Blunt", "2H Blunt", "Staff"];
			for each (var tag:String in bluntTypes) {
				if (perk.indexOf(tag) != -1) return true;
			}
			return false;
		}
		public function isOneHandedMelee():Boolean {
			if (perk == null) return false;
			if (perk.indexOf(WeaponTags.LARGE) != -1) return false;
			if (isRanged()) return false;
			var oneHandedTypes:Array = ["Fist", "1H Sword", "Knife", "1H Blunt", "Axe", "Polearm", "Scythe"];
			for each (var tag:String in oneHandedTypes) {
				if (perk.indexOf(tag) != -1) return true;
			}
			return false;
		}
		public function isType(wtype:MasteryType):Boolean {
			return getMasteries().indexOf(wtype) != -1;
        }

		public function addTags(...args):Weapon{
			if(perk == null) _perk = [];
			for each(var tag:String in args){
				_perk.push(tag);
			}
			return this;
		}

		public function getAttackRange():int{
			if(isMagicStaff() && player.hasPerk(PerkLib.StaffChanneling)) return CombatAttackData.RANGE_RANGED;
			else return isRanged() ? CombatAttackData.RANGE_RANGED : CombatAttackData.RANGE_MELEE;
		}
	}
}