示例#1
0
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

            int changeCount      = GenerateChangeCount(target, new int[] { 2, 2 });
            int remainingChanges = changeCount;

            StringBuilder sb = new StringBuilder();

            //the initial text for starting the transformation. feel free to add additional variables to this if needed.
            sb.Append(InitialTransformationText(target));

            //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc)
            //change in height, hips, and/or butt, or other similar stats.

            //this will handle the edge case where the change count starts out as 0.
            if (remainingChanges <= 0)
            {
                return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
            }

            //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts,
            //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do
            //this for you.

            //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes
            //underwent to the target's change count (if applicable) and then return the StringBuilder content.
            //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);

            //-----------------------
            // MAJOR TRANSFORMATIONS
            //-----------------------
            //1st priority: Change lower body to bipedal.
            if (Utils.Rand(4) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.RestoreLowerBody();
                sb.Append(RestoredLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove Oviposition Perk
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                target.womb.ClearOviposition();
                sb.Append(ClearOvipositionText(target));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove Incorporeality Perk, if not permanent
            if (target.HasPerk <Incorporeal>() && Utils.Rand(4) == 0)
            {
                target.RemovePerk <Incorporeal>();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Restore neck
            if (target.neck.type != NeckType.HUMANOID && Utils.Rand(5) == 0)
            {
                NeckData oldData = target.neck.AsReadOnlyData();
                target.RestoreNeck();
                sb.Append(RestoredNeckText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Rear body restore
            if (!target.back.isDefault && Utils.Rand(5) == 0)
            {
                BackData oldData = target.back.AsReadOnlyData();
                target.RestoreBack();
                sb.Append(RestoredBackText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Skin color change – light, fair, olive, dark, ebony, mahogany, russet
            if (!Species.HUMAN.availableTones.Contains(target.body.primarySkin.tone) && Utils.Rand(5) == 0)
            {
                target.body.ChangeAllSkin(Utils.RandomChoice(Species.HUMAN.availableTones));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Change skin to normal
            if (target.body.type != BodyType.HUMANOID && (target.ears.type == EarType.HUMAN || target.ears.type == EarType.ELFIN) && Utils.Rand(4) == 0)
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.RestoreBody();
                sb.Append(RestoredBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Restore arms to become human arms again
            if (Utils.Rand(4) == 0)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.RestoreArms();
                sb.Append(RestoredArmsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-----------------------
            // MINOR TRANSFORMATIONS
            //-----------------------
            //-Human face
            if (target.face.type != FaceType.HUMAN && Utils.Rand(4) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.RestoreFace();
                sb.Append(RestoreFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Human tongue
            if (target.tongue.type != TongueType.HUMAN && Utils.Rand(4) == 0)
            {
                TongueData oldData = target.tongue.AsReadOnlyData();
                target.RestoreTongue();
                sb.Append(RestoreTongueText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove odd eyes
            if (Utils.Rand(5) == 0 && target.eyes.type != EyeType.HUMAN)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.RestoreEyes();
                sb.Append(RestoredEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Gain human ears (If you have human face)
            if (target.ears.type != EarType.HUMAN && target.face.type == FaceType.HUMAN && Utils.Rand(4) == 0)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.RestoreEar();
                sb.Append(RestoreEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Removes gills
            if (Utils.Rand(4) == 0 && !target.gills.isDefault)
            {
                GillData oldData = target.gills.AsReadOnlyData();
                target.RestoreGills();
                sb.Append(RestoredGillsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Nipples Turn Back:
            if (target.genitals.hasBlackNipples && Utils.Rand(3) == 0)
            {
                target.genitals.SetBlackNipples(false);
            }
            //Remove extra nipples
            if (target.genitals.hasQuadNipples && Utils.Rand(3) == 0)
            {
                target.genitals.SetQuadNipples(false);
            }
            //Hair turns normal
            //Restart hair growth, if hair's normal but growth isn't on. Or just over turning hair normal. The power of rng.
            if ((target.hair.type != HairType.NORMAL || target.hair.growthArtificallyDisabled) && Utils.Rand(3) == 0)
            {
                target.UpdateHair(HairType.NORMAL);
                if (target.hair.growthArtificallyDisabled)
                {
                    target.hair.SetHairGrowthStatus(true);
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-----------------------
            // EXTRA PARTS REMOVAL
            //-----------------------
            //Removes antennae
            if (target.antennae.type != AntennaeType.NONE && Utils.Rand(3) == 0)
            {
                AntennaeData oldData = target.antennae.AsReadOnlyData();
                target.RestoreAntennae();
                sb.Append(RestoredAntennaeText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Removes horns
            if ((target.horns.type != HornType.NONE) && Utils.Rand(5) == 0)
            {
                HornData oldData = target.horns.AsReadOnlyData();
                target.RestoreHorns();
                sb.Append(RestoredHornsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Removes wings
            if (target.wings.type != WingType.NONE && Utils.Rand(5) == 0)
            {
                WingData oldData = target.wings.AsReadOnlyData();
                target.RestoreWings();
                sb.Append(RestoredWingsText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Removes tail
            if (target.tail.type != TailType.NONE && Utils.Rand(5) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.RestoreTail();
                sb.Append(RestoredTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Increase height up to 4ft 10in.
            if (Utils.Rand(2) == 0 && target.build.heightInInches < 58)
            {
                int temp = Utils.Rand(5) + 3;
                //Flavor texts. Flavored like 1950's cigarettes. Yum.
                target.build.IncreaseHeight((byte)temp);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Decrease height down to a maximum of 6ft 2in.
            if (Utils.Rand(2) == 0 && target.build.heightInInches > 74)
            {
                target.build.DecreaseHeight((byte)(3 + Utils.Rand(5)));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-----------------------
            // SEXUAL TRANSFORMATIONS
            //-----------------------
            //Remove additional cocks
            if (target.cocks.Count > 1 && Utils.Rand(3) == 0)
            {
                target.RemoveCock();
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove additional balls/remove uniball
            if (target.balls.hasBalls && (target.balls.count != 2 || target.balls.uniBall) && Utils.Rand(3) == 0)
            {
                if (target.balls.size > 2)
                {
                    if (target.balls.size > 5)
                    {
                        target.balls.ShrinkBalls((byte)(1 + Utils.Rand(3)));
                    }

                    target.balls.ShrinkBalls(1);
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                else if (target.balls.count > 2)
                {
                    target.balls.RemoveExtraBalls();

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                else                 //if (target.balls.count == 1 || target.balls.uniBall)
                {
                    target.balls.MakeStandard();

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }

            //remove second v****a.
            if (target.vaginas.Count > 1 && Utils.Rand(3) == 0)
            {
                target.genitals.RemoveExtraVaginas();
            }

            //Change c**k back to normal
            if (target.hasCock && !target.genitals.OnlyHasCocksOfType(CockType.HUMAN) && Utils.Rand(3) == 0)
            {
                //Select first non-human c**k
                C**k firstNonHuman = target.cocks.First(x => x.type != CockType.HUMAN);

                target.genitals.UpdateCock(firstNonHuman, CockType.HUMAN);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            C**k   longest    = target.genitals.LongestCock();
            double targetSize = Math.Max(7, target.genitals.minimumCockLength);

            //Shrink oversized cocks
            if (target.hasCock && longest.length > targetSize && Utils.Rand(3) == 0)
            {
                longest.DecreaseLength((Utils.Rand(10) + 2) / 10.0);
                if (longest.girth > 1)
                {
                    longest.DecreaseThickness((Utils.Rand(4) + 1) / 10.0);
                }
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove additional breasts
            if (target.breasts.Count > 1 && Utils.Rand(3) == 0)
            {
                target.RemoveExtraBreastRows();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            Breasts biggestCup = target.genitals.LargestBreast();
            CupSize targetCup  = EnumHelper.Max(CupSize.D, target.genitals.smallestPossibleFemaleCupSize);

            //Shrink t**s!
            if (Utils.Rand(3) == 0 && biggestCup.cupSize > targetCup)
            {
                foreach (Breasts t**s in target.breasts)
                {
                    t**s.ShrinkBreasts();
                }
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Change v****a back to normal
            if (Utils.Rand(3) == 0 && target.hasVagina && !target.genitals.OnlyHasVaginasOfType(VaginaType.defaultValue))
            {
                foreach (V****a vag in target.vaginas)
                {
                    target.genitals.RestoreVagina(vag);
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            VaginalWetness targetWetness = EnumHelper.Max(VaginalWetness.WET, target.genitals.minVaginalWetness);

            //Reduce wetness down to a minimum of 2
            if (Utils.Rand(3) == 0 && target.hasVagina && target.genitals.LargestVaginalWetness() > targetWetness)
            {
                foreach (V****a vag in target.vaginas)
                {
                    if (vag.wetness > targetWetness)
                    {
                        vag.DecreaseWetness();
                    }
                }
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Fertility Decrease:
            if (target.hasVagina && target.fertility.baseFertility > 10 && Utils.Rand(3) == 0)
            {
                //High fertility:
                //Average fertility:

                target.fertility.DecreaseFertility((byte)(1 + Utils.Rand(3)));
                if (target.fertility.baseFertility < 10)
                {
                    target.fertility.SetFertility(10);
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Cum Multiplier Decrease:
            if (target.hasCock && target.genitals.cumMultiplier > 5 && Utils.Rand(3) == 0)
            {
                target.genitals.DecreaseCumMultiplier(1 + (Utils.Rand(20) / 10.0));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Anal wetness decrease
            if (target.ass.wetness > 0 && Utils.Rand(3) == 0)
            {
                target.ass.DecreaseWetness();
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }


            //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes
            //occurred, then return the contents of the stringbuilder.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }
        /** Original Credits:
         * @since March 26, 2018
         * @author Stadler76
         */
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

            //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs.
            //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so.
            int changeCount      = GenerateChangeCount(target, new int[] { 2, 2 }, isEnhanced ? 3 : 1);
            int remainingChanges = changeCount;

            StringBuilder sb = new StringBuilder();

            //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function
            //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a
            //long string, piece by piece, so don't do any crazy optimizations first.

            //the initial text for starting the transformation. feel free to add additional variables to this if needed.
            sb.Append(InitialTransformationText(target));

            //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligencelligence, etc)
            //change in height, hips, and/or butt, or other similar stats.

            //this will handle the edge case where the change count starts out as 0.
            if (remainingChanges <= 0)
            {
                return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
            }

            //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts,
            //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do
            //this for you.

            //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes
            //underwent to the target's change count (if applicable) and then return the StringBuilder content.
            //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);

            //Used as a holding variable for biggest dicks and the like
            //****************
            //General Effects:
            //****************
            //-Int less than 10
            if (target is IExtendedCreature extendedCreature && !extendedCreature.extendedData.resistsTFBadEnds && target.intelligence < 10)
            {
                if (target.intelligence < 8 && Species.KANGAROO.Score(target) >= 5)
                {
                    isBadEnd = true;
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //-Speed to 70
            if (target.relativeSpeed < 70 && Utils.Rand(3) == 0)
            {
                //2 points up if below 40!
                if (target.relativeSpeed < 40)
                {
                    target.ChangeSpeed(1);
                }

                target.ChangeSpeed(1);
            }
            //-Int to 10
            if (target.intelligence > 2 && Utils.Rand(3) == 0)
            {
                //Gain dumb (smart!)
                //gain dumb (30-10 int):

                //gain dumb (10-1 int):
                target.ChangeIntelligence(-1);
            }

            //****************
            //Appearance Effects:
            //****************
            //-Hip widening funtimes
            if (Utils.Rand(4) == 0 && target.hips.size < 40)
            {
                target.hips.GrowHips();
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Restore arms to become human arms again
            if (Utils.Rand(4) == 0)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.RestoreArms();
                sb.Append(RestoredArmsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Remove feathery hair
            if (RemoveFeatheryHair(target))
            {
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove odd eyes
            if (Utils.Rand(5) == 0 && !target.eyes.isDefault)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.RestoreEyes();
                sb.Append(RestoredEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //****************
            //Sexual:
            //****************
            //-Shrink balls down to reasonable size (3?)
            if (target.balls.size >= 4 && Utils.Rand(2) == 0)
            {
                target.balls.ShrinkBalls();
                target.genitals.IncreaseCumMultiplier();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Shorten clits to reasonable size
            //MOD NOTE: lets do one at a time. i generally do all because that's the easiest way to port it, but f**k it.
            V****a largestClit = target.genitals.LargestVaginaByClitSize();

            if (target.hasVagina && largestClit.c**t.length >= 4 && Utils.Rand(5) == 0)
            {
                largestClit.ShrinkClit(largestClit.c**t.length / 2);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Find biggest dick!
            C**k biggestCock = target.genitals.LongestCock();

            //-Shrink dicks down to 8\" max.
            if (target.hasCock && biggestCock.length >= 16 && Utils.Rand(5) == 0)
            {
                biggestCock.DecreaseLength(biggestCock.length / 2);
                biggestCock.DecreaseThickness(2 * biggestCock.length / 3);

                if (biggestCock.girth * 6 > biggestCock.length)
                {
                    biggestCock.DecreaseThickness(.4);
                }

                else if (biggestCock.girth * 8 > biggestCock.length)
                {
                    biggestCock.DecreaseThickness(.2);
                }

                if (biggestCock.girth < .5)
                {
                    biggestCock.SetGirth(0.5);
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //C**K TF!
            if (target.cocks.Any(x => x.type != CockType.KANGAROO) && Utils.Rand(isEnhanced ? 2 : 3) == 0)
            {
                C**k notRoo = target.cocks.First(x => x.type != CockType.KANGAROO);
                target.genitals.UpdateCock(notRoo, CockType.KANGAROO);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Neck restore
            if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0)
            {
                NeckData oldData = target.neck.AsReadOnlyData();
                target.RestoreNeck();
                sb.Append(RestoredNeckText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Rear body restore
            if (!target.back.isDefault && Utils.Rand(5) == 0)
            {
                BackData oldData = target.back.AsReadOnlyData();
                target.RestoreBack();
                sb.Append(RestoredBackText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Ovi perk loss
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                target.womb.ClearOviposition();
                sb.Append(ClearOvipositionText(target));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //****************
            //Big Kanga Morphs
            //type 1 ignores normal restrictions
            //****************
            //-Face (Req: Fur + Feet)
            if (target.face.type != FaceType.KANGAROO && ((target.body.IsFurBodyType() && target.lowerBody.type == LowerBodyType.KANGAROO) || isEnhanced) && Utils.Rand(4) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.KANGAROO);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Fur (Req: Footsies)
            if (!target.body.IsFurBodyType() && (target.lowerBody.type == LowerBodyType.KANGAROO || isEnhanced) && Utils.Rand(4) == 0)
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.UpdateBody(BodyType.SIMPLE_FUR, new FurColor(HairFurColors.BROWN));
                sb.Append(UpdateBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Roo footsies (Req: Tail)
            if (target.lowerBody.type != LowerBodyType.KANGAROO && (isEnhanced || target.tail.type == TailType.KANGAROO) && Utils.Rand(4) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.KANGAROO);
                sb.Append(UpdateLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Roo tail (Req: Ears)
            if (target.tail.type != TailType.KANGAROO && Utils.Rand(4) == 0 && (isEnhanced || target.ears.type == EarType.KANGAROO))
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.KANGAROO);
                sb.Append(UpdateTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Roo ears
            if (target.ears.type != EarType.KANGAROO && Utils.Rand(4) == 0)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.KANGAROO);
                sb.Append(UpdateEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //UBEROOOO
            //kangaroo perk: - any liquid or food intake will accelerate a pregnancy, but it will not progress otherwise
            if (target.womb.canObtainDiapause && Species.KANGAROO.Score(target) > 4 && Utils.Rand(4) == 0 && target.hasVagina)
            {
                target.womb.EnableDiapause();

                //Perk name and description:
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
                //trigger effect: Your body reacts to the influx of nutrition, accelerating your pregnancy. Your belly bulges outward slightly.
            }
            // Remove gills
            if (Utils.Rand(4) == 0 && !target.gills.isDefault)
            {
                target.RestoreGills();
            }
            if (remainingChanges == changeCount)
            {
                (target as CombatCreature)?.RecoverFatigue(40);
            }


            //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes
            //occurred, then return the contents of the stringbuilder.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

            //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs.
            //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so.
            int changeCount      = GenerateChangeCount(target, new int[] { 2, 2, 4 });
            int remainingChanges = changeCount;

            StringBuilder sb = new StringBuilder();

            //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function
            //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a
            //long string, piece by piece, so don't do any crazy optimizations first.

            //the initial text for starting the transformation. feel free to add additional variables to this if needed.
            sb.Append(InitialTransformationText(target));

            //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc)
            //change in height, hips, and/or butt, or other similar stats.

            //this will handle the edge case where the change count starts out as 0.
            if (remainingChanges <= 0)
            {
                return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
            }

            //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts,
            //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do
            //this for you.

            //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes
            //underwent to the target's change count (if applicable) and then return the StringBuilder content.
            //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);

#warning fix me
            int ngPlus(int value) => value;


            //+3 spe if less than 50
            if (target.speed < ngPlus(50))
            {
                target.IncreaseSpeed(3);
            }
            //+2 spe if less than 75
            else if (target.speed < ngPlus(75))
            {
                target.IncreaseSpeed(2);
            }
            //+1 if above 75.
            else
            {
                target.IncreaseSpeed();
            }

            // ------------- Sexual changes -------------
            //-Nipples reduction to 1 per tit.
            if (target.genitals.hasQuadNipples && Utils.Rand(4) == 0)
            {
                target.genitals.SetQuadNipples(false);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //-Remove extra breast rows
            if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !hyperHappy)
            {
                target.RemoveExtraBreastRows();
            }

            //-Butt > 5 - decrease butt size
            if (target.butt.size > 5 && Utils.Rand(4) == 0)
            {
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }

                target.butt.ShrinkButt();
            }

            if (target.gender.HasFlag(Gender.FEMALE))
            {
                //Breasts > D cup - Decrease breast size by up to 3 cups
                //Breasts < B cup - Increase breast size by 1 cup
                if (target.breasts.Any(x => x.cupSize > CupSize.D || x.cupSize < CupSize.B) && Utils.Rand(3) == 0)
                {
                    foreach (Breasts breast in target.breasts)
                    {
                        if (breast.cupSize > CupSize.D)
                        {
                            breast.ShrinkBreasts((byte)(1 + Utils.Rand(3)));
                        }
                        else if (breast.cupSize < CupSize.B)
                        {
                            breast.GrowBreasts();
                        }
                    }

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Hips > 12 - decrease hip size by 1-3 sizes
                if (target.hips.size > 12 && Utils.Rand(3) == 0)
                {
                    target.hips.ShrinkHips((byte)(1 + Utils.Rand(3)));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Hips < 6 - increase hip size by 1-3 sizes
                if (target.hips.size < 6 && Utils.Rand(3) == 0)
                {
                    target.hips.GrowHips((byte)(1 + Utils.Rand(3)));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                if (target.genitals.nippleLength > 1 && Utils.Rand(3) == 0)
                {
                    target.genitals.SetNippleLength(target.genitals.nippleLength / 2);
                }

                if (target.hasVagina && target.vaginas[0].wetness < VaginalWetness.SLICK && Utils.Rand(4) == 0)
                {
                    target.vaginas[0].IncreaseWetness();
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Increase tone (up to 65)
                if (target.build.muscleTone < 65 && Utils.Rand(3) == 0)
                {
                }

                //Decrease thickness (down to 35)
                if (target.build.thickness > 35 && Utils.Rand(3) == 0)
                {
                }
            }

            if (target.gender == Gender.MALE)
            {
                //Breasts > B cup (or applicable male max size if it's somehow > B cup) - decrease by 1 cup size
                CupSize targetSize = EnumHelper.Max(target.genitals.smallestPossibleMaleCupSize, CupSize.B);
                if (target.genitals.BiggestCupSize() > targetSize && Utils.Rand(3) == 0)
                {
                    foreach (Breasts breast in target.breasts)
                    {
                        if (breast.cupSize > targetSize)
                        {
                            breast.ShrinkBreasts();
                        }
                    }

                    target.IncreaseSpeed();

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                if (target.genitals.nippleLength > 1 && Utils.Rand(3) == 0)
                {
                    target.genitals.SetNippleLength(target.genitals.nippleLength / 2);
                }

                //Hips > 10 - decrease hip size by 1-3 sizes
                if (target.hips.size > 10 && Utils.Rand(3) == 0)
                {
                    target.hips.ShrinkHips((byte)(1 + Utils.Rand(3)));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Hips < 2 - increase hip size by 1-3 sizes
                if (target.hips.size < 2 && Utils.Rand(3) == 0)
                {
                    target.hips.GrowHips((byte)(1 + Utils.Rand(3)));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Increase tone (up to 70)
                if (target.build.muscleTone < 70 && Utils.Rand(3) == 0)
                {
                }

                //Decrease thickness (down to 35)
                if (target.build.thickness > 35 && Utils.Rand(3) == 0)
                {
                }
            }

            if (target.gender.HasFlag(Gender.MALE))
            {
                //C**k -> Red Panda C**k
                if (target.hasCock && target.cocks[0].type != CockType.RED_PANDA && Utils.Rand(3) == 0)
                {
                    target.genitals.UpdateCock(0, CockType.RED_PANDA);
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                C**k shortest = target.genitals.ShortestCock();

                //C**k < 6 inches - increase by 1-2 inches
                if (shortest.length < 6 && Utils.Rand(3) == 0)
                {
                    double increment = shortest.IncreaseLength(1 + Utils.Rand(2));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                C**k longest = target.genitals.LongestCock();

                //Shrink oversized cocks
                if (longest.length > 16 && Utils.Rand(3) == 0)
                {
                    longest.DecreaseLength((Utils.Rand(10) + 5) / 10);
                    if (longest.girth > 3)
                    {
                        longest.DecreaseThickness((Utils.Rand(4) + 1) / 10);
                    }
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                C**k smallestArea = target.genitals.SmallestCockByArea();

                //C**k thickness <2 - Increase c**k thickness
                if (smallestArea.area < 10 && Utils.Rand(3) == 0)
                {
                    smallestArea.IncreaseThickness(1.5);
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }

            //Remove additional cocks
            if (target.cocks.Count > 1 && Utils.Rand(3) == 0)
            {
                //what a dick. removes the second, and only the second. not the last one or anything. ok. it's supported now.
                target.genitals.RemoveCockAt(1, 1);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //Remove additional balls/remove uniball
            if (target.balls.count > 0 && Utils.Rand(3) == 0)
            {
                if (target.balls.size > 5)
                {
                    target.balls.ShrinkBalls((byte)(2 + Utils.Rand(3)));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                else if (target.balls.size > 2)
                {
                    target.balls.ShrinkBalls(1);

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                else if (target.balls.count != 2)
                {
                    //removes uniball, sets count to 2.
                    target.balls.MakeStandard();

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }

            //Ovi perk loss
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                target.womb.ClearOviposition();
                //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);
            }

            // ------------- Physical changes -------------
            // Ears
            if (target.ears.type != EarType.RED_PANDA && Utils.Rand(3) == 0)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.RED_PANDA);
                sb.Append(UpdateEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            // Remove non-cockatrice antennae
            if (target.antennae.type != AntennaeType.COCKATRICE && !target.antennae.isDefault && Utils.Rand(3) == 0)
            {
                AntennaeData oldData = target.antennae.AsReadOnlyData();
                target.RestoreAntennae();
                sb.Append(RestoredAntennaeText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            // Restore eyes, if more than two
            if (target.eyes.count > 2 && Utils.Rand(4) == 0)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.RestoreEyes();
                sb.Append(RestoredEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            // Hair
            // store current states first
            bool     hasPandaHairColor = Species.RED_PANDA.availableHairColors.Contains(target.hair.hairColor);
            bool     hasNormalHair     = target.hair.type == HairType.NORMAL;
            HairType oldHairType       = target.hair.type;

            if ((!hasNormalHair || target.hair.length == 0 || !hasPandaHairColor) && Utils.Rand(3) == 0)
            {
                target.UpdateHair(HairType.NORMAL);
                if (!hasPandaHairColor)
                {
                    target.hair.SetHairColor(Utils.RandomChoice(Species.RED_PANDA.availableHairColors));
                }

                if (target.hair.length == 0)
                {                 // target is bald
                    target.hair.SetHairLength(1);
                }

                target.hair.SetHairGrowthStatus(true);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            // Face
            if (target.face.type != FaceType.RED_PANDA && target.ears.type == EarType.RED_PANDA && target.body.IsFurBodyType() && Utils.Rand(3) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.RED_PANDA);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            // Arms
            if (target.arms.type != ArmType.RED_PANDA && target.ears.type == EarType.RED_PANDA && target.tail.type == TailType.RED_PANDA && Utils.Rand(3) == 0)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.UpdateArms(ArmType.RED_PANDA);
                sb.Append(UpdateArmsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            // Legs
            if (target.lowerBody.type != LowerBodyType.RED_PANDA && target.arms.type == ArmType.RED_PANDA && Utils.Rand(4) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.RED_PANDA);
                sb.Append(UpdateLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            // Tail
            if (target.tail.type != TailType.RED_PANDA && Utils.Rand(4) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.RED_PANDA);
                sb.Append(UpdateTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            // SKin


            // Fix the underBody, if the skin is already furred
            if (target.body.type == BodyType.SIMPLE_FUR && Utils.Rand(3) == 0)
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.UpdateBody(BodyType.UNDERBODY_FUR, new FurColor(HairFurColors.RUSSET), new FurColor(HairFurColors.BLACK));
                sb.Append(UpdateBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            else if (!target.body.IsFurBodyType() && target.arms.type == ArmType.RED_PANDA && target.lowerBody.type == LowerBodyType.RED_PANDA && Utils.Rand(4) == 0)
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.UpdateBody(BodyType.UNDERBODY_FUR, new FurColor(HairFurColors.RUSSET), new FurColor(HairFurColors.BLACK));
                sb.Append(UpdateBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //FAILSAFE CHANGE
            if (remainingChanges == changeCount)
            {
                if (Utils.Rand(100) == 0)
                {
                }
                else
                {
                    if (target is CombatCreature)
                    {
                        ((CombatCreature)target).AddHP(250);
                    }

                    target.ChangeLust(3);
                }
            }


            //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes
            //occurred, then return the contents of the stringbuilder.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

            //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs.
            //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so.
            int changeCount      = GenerateChangeCount(target, new int[] { 2, 2 });
            int remainingChanges = changeCount;

            StringBuilder sb = new StringBuilder();

            //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function
            //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a
            //long string, piece by piece, so don't do any crazy optimizations first.

            //the initial text for starting the transformation. feel free to add additional variables to this if needed.

            //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc)
            //change in height, hips, and/or butt, or other similar stats.



            double crit = 0;

            if (Utils.Rand(100) < 15)
            {
                crit = Utils.Rand(20) / 10.0 + 2;
            }

            bool hasCrit() => crit > 1;

            sb.Append(InitialTransformationText(target, crit));


            //STAT CHANGES - TOU SPE INT RANDOM CHANCE, LIB LUST COR ALWAYS UPPED
            target.DeltaCreatureStats(lib: 1 + Utils.Rand(2), lus: 5 + Utils.Rand(10), corr: 1 + Utils.Rand(5));
            if (target.relativeToughness < 70 && Utils.Rand(3) == 0)
            {
                double delta = target.ChangeToughness(crit);
                sb.Append(IncreasedToughnessText(crit, delta));
            }
            if (target.relativeSpeed > 30 && Utils.Rand(7) == 0)
            {
                double loss = target.DecreaseSpeed(crit);
                sb.Append(DecreasedSpeedText(crit, loss));
            }
            if (target.relativeIntelligence < 60 && Utils.Rand(7) == 0)
            {
                double delta = target.ChangeIntelligence(crit);
                sb.Append(IncreasedIntelligenceText(crit, delta));
            }

            //handle edge case where we start at 0.
            if (remainingChanges <= 0)
            {
                return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
            }

            //Non-free changes.

            //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);


            //MUTATIONZZZZZ
            //PRE-CHANGES: become biped, remove horns, remove wings, give human tongue, remove claws, remove antennea
            //no claws
            if (Utils.Rand(4) == 0 && target.arms.hands.isClaws)
            {
                ArmData oldData = target.arms.AsReadOnlyData();

                if (target.RestoreArms())
                {
                    sb.Append(RestoredArmsText(target, oldData));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //remove antennae
            if (target.antennae.type != AntennaeType.NONE && Utils.Rand(3) == 0)
            {
                AntennaeData oldData = target.antennae.AsReadOnlyData();
                target.RestoreAntennae();

                sb.Append(RestoredAntennaeText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //remove horns
            if (target.horns.type != HornType.NONE && Utils.Rand(3) == 0)
            {
                HornData oldData = target.horns.AsReadOnlyData();
                target.RestoreHorns();
                sb.Append(RestoredHornsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //remove wings
            if ((target.wings.type != WingType.NONE || target.back.type == BackType.SHARK_FIN) && Utils.Rand(3) == 0)
            {
                BackData oldBack = target.back.AsReadOnlyData();
                if (target.back.type == BackType.SHARK_FIN)
                {
                    target.RestoreBack();
                }

                WingData oldWings = target.wings.AsReadOnlyData();
                target.RestoreWings();
                sb.Append(RestoredBackAndWings(target, oldWings, oldBack));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //give human tongue
            if (target.tongue.type != TongueType.HUMAN && Utils.Rand(3) == 0)
            {
                //MOD NOTE: this was incorrect - this actually was === (equal with strict typechecking) instead of = (assign). so this was an implicit bool.
                //this is part of the reason why the rework forces all value updates as function calls. the more you know.
                TongueData oldData = target.tongue.AsReadOnlyData();
                target.RestoreTongue();
                sb.Append(RestoredTongueText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //remove non-wolf eyes
            if (Utils.Rand(3) == 0 && target.eyes.type != EyeType.HUMAN && target.eyes.type != EyeType.WOLF)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.RestoreEyes();
                sb.Append(RestoredEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //normal legs
            if (target.lowerBody.type != LowerBodyType.WOLF && Utils.Rand(4) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.RestoreLowerBody();
                sb.Append(RestoredLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //normal arms
            if (Utils.Rand(4) == 0)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.RestoreArms();
                sb.Append(RestoredArmsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            HairData oldHair = target.hair.AsReadOnlyData();

            //remove feather hair
            if (Utils.Rand(4) == 0 && RemoveFeatheryHair(target))
            {
                sb.Append(RemovedFeatheryHairText(target, oldHair));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //remove basilisk hair
            if (Utils.Rand(4) == 0 && target.hair.IsBasiliskHair())
            {
                HairData oldData = target.hair.AsReadOnlyData();
                target.RestoreHair();
                target.hair.SetHairLength(0);
                target.hair.ResumeNaturalGrowth();

                sb.Append(RemovedBasiliskHair(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //MUTATIONZ AT ANY TIME: wolf dick, add/decrease breasts, decrease breast size if above D
            //get a wolf dick
            //if ya genderless we give ya a dick cuz we nice like that
            //MOD: Now respects hyper happy.
            if (target.gender == Gender.GENDERLESS && !hyperHappy)
            {
                target.genitals.AddCock(CockType.WOLF, Utils.Rand(4) + 4, Utils.Rand(8) / 4.0 + 0.25, 1.5);

                sb.Append(BecameMaleByGrowingCock(target));

                target.DeltaCreatureStats(lib: 3, sens: 2, lus: 25);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //if ya got a dick that's ok too we'll change it to wolf
            //MOD NOTE: but only if you don't only have wolf cocks, of course (because if they are all wolf cocks, we can't make anything wolf cocks, duh).
            if (target.hasCock && !target.genitals.OnlyHasCocksOfType(CockType.WOLF) && Utils.RandBool())
            {
                //MOD NOTE: no longer shamelessly copy/pasted from dog c**k, because we have better ways of looping in C#.

                C**k     firstNonWolf = target.cocks.First(x => x.type != CockType.WOLF);
                CockData oldData      = firstNonWolf.AsReadOnlyData();

                //Select first non-wolf c**k
                //MOD: now using type description because we can, so the fact this used generic is irrelevant now :)

                if (firstNonWolf.type == CockType.HORSE)
                {                 //horses get changes
                    if (firstNonWolf.length > 6)
                    {
                        firstNonWolf.DecreaseLength(2);
                    }
                    else
                    {
                        firstNonWolf.DecreaseLength(.5);
                    }

                    firstNonWolf.IncreaseThickness(.5);
                }

                target.DeltaCreatureStats(sens: 3, lus: 5 * crit);


                target.genitals.UpdateCockWithKnot(firstNonWolf, CockType.WOLF, 1.5);
                firstNonWolf.IncreaseThickness(2);

                sb.Append(ChangedCockToWolf(target, oldData, oldData.cockIndex));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //titties for those who got titties
            //wolfs have 8 nips so, 4 rows max. fen has no power here I'm making a wolf not a dog.
            //MOD: still stolen shamelessly from dog and updated, but now with the modern format. woo!

            //MOD: if breasts can be updated (not flat) and we flip a coin.
            if (target.genitals.BiggestCupSize() > CupSize.FLAT && Utils.RandBool())
            {
                if (target.breasts.Count < 4)
                {
                    byte breastCount = (byte)target.breasts.Count;
                    BreastCollectionData oldBreastData = target.genitals.allBreasts.AsReadOnlyData();

                    //in vanilla code, we had some strange checks here that required the first row (and only the first row) be a certain size.
                    //now, we check all the rows, but no longer require any of them to be a certain size. instead, if it's not the right size, we make it that size,
                    //then add the row. so, for two rows, your first row must be at least a B-Cup. for 3: first must be a C cup, second an A cup.
                    //if we supported 4 rows, it'd be D, B, A.

                    for (int x = 0; x < target.breasts.Count; x++)
                    {
                        CupSize requiredSize = (CupSize)(byte)(target.breasts.Count - x);
                        if (x == 0)
                        {
                            requiredSize++;
                        }
                        if (target.breasts[x].cupSize < requiredSize)
                        {
                            target.breasts[x].SetCupSize(requiredSize);
                        }
                    }

                    target.genitals.AddBreastRow(target.breasts[breastCount - 1].cupSize.ByteEnumSubtract(1));


                    bool doCrit = false, uberCrit = false;
                    if (target.breasts.Count == 2)
                    {
                        target.IncreaseCreatureStats(lus: 5, sens: 6);
                    }
                    else if (hasCrit())
                    {
                        doCrit = true;
                        if (crit > 2)
                        {
                            target.IncreaseCreatureStats(sens: 6, lus: 15);
                            uberCrit = true;
                        }
                        else
                        {
                            target.IncreaseCreatureStats(sens: 3, lus: 10);
                        }
                    }

                    sb.Append(UpdateAndGrowAdditionalRowText(target, oldBreastData, doCrit, uberCrit));


                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                else
                {
                    BreastCollectionData oldBreastData = target.genitals.allBreasts.AsReadOnlyData();

                    //only call the normalize text if we actually did anything. related, anthro breasts now returns a bool. thanks, fox tfs.
                    if (target.genitals.AnthropomorphizeBreasts())
                    {
                        sb.Append(NormalizedBreastSizeText(target, oldBreastData));
                    }
                }
            }


            //Remove a breast row if over 4. afaik this should never happen.
            if (target.breasts.Count > 4 && Utils.Rand(3) == 0)
            {
                var oldData = target.breasts[target.breasts.Count - 1].AsReadOnlyData();
                target.genitals.RemoveBreastRows(1);

                sb.Append(RemovedExcessRow(target, oldData));
            }
            //Grow breasts if has v****a and has no breasts/nips
            //Shrink breasts if over D-cup
            CupSize targetSize = EnumHelper.Max(target.genitals.smallestPossibleCupSize, CupSize.D);

            if (!hyperHappy && target.genitals.BiggestCupSize() > targetSize && Utils.Rand(3) == 0)
            {
                BreastCollectionData oldCollection = target.genitals.allBreasts.AsReadOnlyData();

                bool changedAnything = false;
                foreach (Breasts row in target.breasts)
                {
                    //If this row is over threshhold
                    if (row.cupSize > targetSize)
                    {
                        //Big change
                        if (row.cupSize > CupSize.EE_BIG)
                        {
                            changedAnything |= row.ShrinkBreasts((byte)(2 + Utils.Rand(3))) > 0;
                        }
                        //Small change
                        else
                        {
                            changedAnything |= row.ShrinkBreasts() > 0;
                        }
                        //Increment changed rows
                    }
                }
                //Count shrinking
                if (changedAnything)
                {
                    sb.Append(ShrunkRowsText(target, oldCollection));

                    remainingChanges--;
                    if (remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //MUTATIONZ LEVEL 1: fur, stop hair growth, ears, tail
            //Gain fur
            if (Utils.Rand(5) == 0 && !target.body.IsFurBodyType())
            {
                BodyData oldData = target.body.AsReadOnlyData();
                Species.WOLF.GetRandomFurColors(out FurColor primary, out FurColor underbody);
                if (FurColor.IsNullOrEmpty(underbody))
                {
                    target.UpdateBody(BodyType.UNDERBODY_FUR, primary);
                }
                else
                {
                    target.UpdateBody(BodyType.UNDERBODY_FUR, primary, underbody);
                }

                sb.Append(UpdateBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Ears time
            if (Utils.Rand(3) == 0 && target.ears.type != EarType.WOLF)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.WOLF);
                sb.Append(UpdateEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Wolf tail
            if (Utils.Rand(3) == 0 && target.tail.type != TailType.WOLF)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.WOLF);
                sb.Append(UpdateTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Sets hair normal
            if (target.hair.type != HairType.NORMAL && Utils.Rand(3) == 0)
            {
                HairData oldData = target.hair.AsReadOnlyData();
                target.RestoreHair();
                sb.Append(RestoreHairText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //MUTATIONZ LEVEL 2: fur->arms fur+tail+ears->face stophair->nohair fur+tail->legs
            //gain wolf face
            if (target.face.type != FaceType.WOLF && target.ears.type == EarType.WOLF && target.tail.type == TailType.WOLF && target.body.IsFurBodyType() && Utils.Rand(5) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.WOLF);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //legz
            if (target.lowerBody.legCount == 2 && target.lowerBody.type != LowerBodyType.WOLF && target.tail.type == TailType.WOLF && target.body.IsFurBodyType() && Utils.Rand(4) == 0)
            {
                //Hooman feets
                //Hooves -> Paws
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.WOLF);
                sb.Append(UpdateLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //MUTATIONZ LEVEL 3: face->eyes
            if (target.eyes.type != EyeType.WOLF && target.face.type == FaceType.WOLF && Utils.Rand(4) == 0)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.UpdateEyes(EyeType.WOLF);
                sb.Append(UpdateEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //MISC CRAP
            //Neck restore
            if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0)
            {
                NeckData oldData = target.neck.AsReadOnlyData();
                target.RestoreNeck();
                sb.Append(RestoredNeckText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Rear body restore
            if (!target.back.isDefault && Utils.Rand(5) == 0)
            {
                BackData oldData = target.back.AsReadOnlyData();
                target.RestoreBack();
                sb.Append(RestoredBackText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Ovi perk loss
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                target.womb.ClearOviposition();
                sb.Append(ClearOvipositionText(target));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            if (Utils.Rand(3) == 0)
            {
                var res = target.build.ChangeMuscleToneToward(100, 4);
                sb.Append(AdjustToneText(target, 4, res));
            }
            if (Utils.Rand(3) == 0)
            {
                var adjustedAmount = target.build.ChangeThicknessToward(75, 3);
                sb.Append(AdjustThicknessText(target, adjustedAmount));
            }



            //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes
            //occurred, then return the contents of the stringbuilder.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }
示例#5
0
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

            //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs.
            //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so.
            int changeCount      = GenerateChangeCount(target, new int[] { 2, 2, 3, 4 });
            int remainingChanges = changeCount;

            StringBuilder sb = new StringBuilder();

            //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function
            //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a
            //long string, piece by piece, so don't do any crazy optimizations first.

            //the initial text for starting the transformation. feel free to add additional variables to this if needed.
            sb.Append(InitialTransformationText(target));

            //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc)
            //change in height, hips, and/or butt, or other similar stats.

            //this will handle the edge case where the change count starts out as 0.
            if (remainingChanges <= 0)
            {
                return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
            }

            //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts,
            //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do
            //this for you.

            //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes
            //underwent to the target's change count (if applicable) and then return the StringBuilder content.
            //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);
#warning fix me
            int ngPlus(int value) => value;


            if (target.speed < ngPlus(100) && Utils.Rand(3) == 0)
            {
                //+3 spe if less than 50
                if (target.speed < ngPlus(50))
                {
                    target.ChangeSpeed(1);
                }
                //+2 spe if less than 75
                if (target.speed < ngPlus(75))
                {
                    target.ChangeSpeed(1);
                }
                //+1 if above 75.
                target.ChangeSpeed(1);
            }

            if (target.toughness > ngPlus(80) && Utils.Rand(4) == 0)
            {
                target.ChangeToughness(-1);
            }

            //-Reduces sensitivity.
            if (target.sensitivity > 20 && Utils.Rand(3) == 0)
            {
                target.ChangeSensitivity(-1);
            }

            //Raises libido greatly to 50, then somewhat to 75, then slowly to 100.
            if (target.libido < 100 && Utils.Rand(3) == 0)
            {
                //+3 lib if less than 50
                if (target.libido < 50)
                {
                    target.ChangeLibido(1);
                }
                //+2 lib if less than 75
                if (target.libido < 75)
                {
                    target.ChangeLibido(1);
                }
                //+1 if above 75.
                target.ChangeLibido(1);
            }

            //Sexual changes

            //-Lactation stoppage.
            if (target.genitals.isLactating && Utils.Rand(4) == 0)
            {
                if (target.HasPerk <Feeder>())
                {
                    target.RemovePerk <Feeder>();
                }

                target.genitals.SetLactationTo(LactationStatus.NOT_LACTATING);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //-Nipples reduction to 1 per tit.
            if (target.genitals.hasQuadNipples && Utils.Rand(4) == 0)
            {
                target.genitals.SetQuadNipples(false);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //-Remove extra breast rows
            if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !hyperHappy)
            {
                target.RemoveExtraBreastRows();
            }

            //-Butt > 5 - decrease butt size
            if (target.butt.size > 5 && Utils.Rand(4) == 0)
            {
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }

                target.butt.ShrinkButt();
            }

            if (target.gender.HasFlag(Gender.FEMALE))
            {
                CupSize minCup = EnumHelper.Max(CupSize.D, target.genitals.smallestPossibleFemaleCupSize);
                //Breasts > D cup - Decrease breast size by up to 3 cups
                //MOD NOTE: Now respects minimum cup size from perks.
                if (target.gender.HasFlag(Gender.FEMALE) && target.genitals.BiggestCupSize() > minCup && Utils.Rand(3) == 0)
                {
                    foreach (Breasts breast in target.breasts)
                    {
                        if (breast.cupSize > CupSize.D)
                        {
                            breast.ShrinkBreasts((byte)(1 + Utils.Rand(3)));
                        }
                    }

                    target.IncreaseSpeed();

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Breasts < B cup - Increase breast size by 1 cup
                if (target.gender.HasFlag(Gender.FEMALE) && target.genitals.SmallestCupSize() < CupSize.B && Utils.Rand(3) == 0)
                {
                    for (int i = 0; i < target.breasts.Count; i++)
                    {
                        if (target.breasts[i].cupSize < CupSize.B)
                        {
                            target.breasts[i].GrowBreasts();
                        }
                    }
                    target.ChangeLibido(1);
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Hips > 12 - decrease hip size by 1-3 sizes
                if (target.hips.size > 12 && Utils.Rand(3) == 0)
                {
                    target.hips.ShrinkHips((byte)(1 + Utils.Rand(3)));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Hips < 6 - increase hip size by 1-3 sizes
                if (target.hips.size < 6 && Utils.Rand(3) == 0)
                {
                    target.hips.GrowHips((byte)(1 + Utils.Rand(3)));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                if (target.genitals.nippleLength > 1 && Utils.Rand(3) == 0)
                {
                    target.genitals.SetNippleLength(target.genitals.nippleLength / 2);
                }

                VaginalWetness desiredWetness = EnumHelper.Min(target.genitals.maxVaginalWetness, VaginalWetness.SLICK);
                //MOD NOTE: now respects all vaginas, and the maximum wetness allowed by perks (if applicable)
                if (target.hasVagina && target.genitals.SmallestVaginalWetness() < desiredWetness && Utils.Rand(4) == 0)
                {
                    foreach (V****a vag in target.vaginas)
                    {
                        if (vag.wetness < desiredWetness)
                        {
                            vag.IncreaseWetness();
                        }
                    }
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Increase tone (up to 65)
                if (target.build.muscleTone < 65 && Utils.Rand(3) == 0)
                {
                    target.build.ChangeMuscleToneToward(65, 2);
                }

                //Decrease thickness (down to 35)
                if (target.build.thickness > 35 && Utils.Rand(3) == 0)
                {
                    target.build.ChangeThicknessToward(35, 5);
                }
                //Grant oviposition.
                if (target.womb.canObtainOviposition && Species.COCKATRICE.Score(target) > 3 && Utils.Rand(5) == 0)
                {
                    target.womb.GrantOviposition();
                    sb.Append(GrantOvipositionText(target));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }

            if (target.gender == Gender.MALE)
            {
                //Breasts > B cup - decrease by 1 cup size
                if (target.genitals.BiggestCupSize() > CupSize.B && Utils.Rand(3) == 0)
                {
                    for (int i = 0; i < target.breasts.Count; i++)
                    {
                        if (target.breasts[i].cupSize > CupSize.B)
                        {
                            target.breasts[i].ShrinkBreasts();
                        }
                    }

                    target.IncreaseSpeed();

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                if (target.genitals.nippleLength > 1 && Utils.Rand(3) == 0)
                {
                    target.genitals.SetNippleLength(target.genitals.nippleLength / 2);
                }

                //Hips > 10 - decrease hip size by 1-3 sizes
                if (target.hips.size > 10 && Utils.Rand(3) == 0)
                {
                    target.hips.ShrinkHips((byte)(1 + Utils.Rand(3)));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Hips < 2 - increase hip size by 1-3 sizes
                if (target.hips.size < 2 && Utils.Rand(3) == 0)
                {
                    target.hips.GrowHips((byte)(1 + Utils.Rand(3)));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                //Increase tone (up to 70)
                if (target.build.muscleTone < 70 && Utils.Rand(3) == 0)
                {
                    target.build.ChangeMuscleToneToward(70, 2);
                }

                //Decrease thickness (down to 35)
                if (target.build.thickness > 35 && Utils.Rand(3) == 0)
                {
                    target.build.ChangeThicknessToward(35, 5);
                }
            }

            if (target.gender.HasFlag(Gender.MALE))
            {
                //C**k < 6 inches - increase by 1-2 inches
                C**k shortest = target.genitals.ShortestCock();
                if (shortest.length < 6 && Utils.Rand(3) == 0)
                {
                    double increment = shortest.IncreaseLength(1 + Utils.Rand(2));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                C**k longest = target.genitals.LongestCock();
                //Shrink oversized cocks
                if (longest.length > 16 && Utils.Rand(3) == 0)
                {
                    longest.DecreaseLength((Utils.Rand(10) + 5) / 10.0);
                    if (longest.girth > 3)
                    {
                        longest.DecreaseThickness((Utils.Rand(4) + 1) / 10.0);
                    }
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }

                C**k thinnest = target.genitals.ThinnestCock();
                //C**k thickness <2 - Increase c**k thickness
                if (thinnest.girth < 2 && Utils.Rand(3) == 0)
                {
                    thinnest.IncreaseThickness(1.5);

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            int lizardCocks = target.genitals.CountCocksOfType(CockType.LIZARD);

            if (target.hasCock && target.cocks.Count > lizardCocks && Utils.Rand(4) == 0)
            {
                //-Lizard dick - first one
                if (lizardCocks == 0)
                {
                    //Actually xform it nau
                    if (target.genitals.hasSheath)
                    {
                        target.genitals.UpdateCock(0, CockType.LIZARD);
                    }
                    else
                    {
                        target.genitals.UpdateCock(0, CockType.LIZARD);
                    }

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }

                    target.DeltaCreatureStats(lib: 3, lus: 10);
                }
                //(CHANGE OTHER DICK)
                //Requires 1 lizard c**k, multiple cocks
                else                 //if (target.cocks.Count > 1 && target.cocks.Count > lizardCocks)
                {
                    C**k firstNonLizard = target.cocks.First(x => x.type != CockType.LIZARD);

                    target.genitals.UpdateCock(firstNonLizard, CockType.LIZARD);

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }

                    target.DeltaCreatureStats(lib: 3, lus: 10);
                }
            }

            //MOD NOTE: Worms are being removed??? from the game due to that absolutely shitty license that prevents me from porting any content 'created' by
            //its creator (forget the name at this time). it's a bad license because it takes credit from other content creators that use its content. that's like saying
            //the creator of the rubber tire gets credit for all modern car designs. that's bullshit. of course cars require tires, and the original copyright required users
            //to pay a licensing fee for their use. that didn't grant the copyright holder for rubber tires the credit for the cars that used them. If anyone wants to get ahold
            //of the og worms creator and get him/her to relax that requirement to make it reasonable (and future content-change/port friendly), we'll see. until then, this is
            //removed.

            //For the record, that would grant him creator's credit on this document. which he/she had no input on. that's not right. and, by extension, the inventory system, since
            //this item is in use in the inventory system. and the time engine, because worms regen over time. I am the content creator for both of those, and i refuse to allow him/her
            //any credit for either of those. those took time and effort and were difficult - i'm not freely handing credit to him/her because that license would require me to. Either
            //his/her license changes, or the offending content is removed, or the entire game engine goes. Right now i think it's better go with the game engine because it make all of
            //this shit work. - JSG.

            ////--Worms leave if 100% lizard dicks?
            ////Require mammals?
            //if (target.genitals.CountCocksOfType(CockType.LIZARD) == target.cocks.Count && target.hasStatusEffect(StatusEffects.Infested))
            //{
            //	if (target.balls.count > 1)
            //	target.removeStatusEffect(StatusEffects.Infested);
            //	if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);
            //}

            //Increase height up to 5ft 7in.
            if (target.build.heightInInches < 67 && Utils.Rand(5) == 0)
            {
                target.build.IncreaseHeight((byte)(Utils.Rand(3) + 1));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //Decrease height down to a maximum of 6ft 8in.
            if (target.build.heightInInches > 80 && Utils.Rand(5) == 0)
            {
                target.build.DecreaseHeight((byte)(Utils.Rand(3) + 1));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //Physical changes:
            //Removes other antennae
            if (target.antennae.type != AntennaeType.COCKATRICE && !target.antennae.isDefault && Utils.Rand(3) == 0)
            {
                target.RestoreAntennae();
            }
            //Gain antennae like feathers
            if (target.antennae.type == AntennaeType.NONE && target.face.type == FaceType.COCKATRICE && target.ears.type == EarType.COCKATRICE && Utils.Rand(3) == 0)
            {
                // Other antennae types are handled above! (Stadler76)
                AntennaeData oldData = target.antennae.AsReadOnlyData();
                target.UpdateAntennae(AntennaeType.COCKATRICE);
                sb.Append(UpdateAntennaeText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Removes horns
            if (target.horns.type != HornType.NONE && Utils.Rand(5) == 0)
            {
                HornData oldData = target.horns.AsReadOnlyData();
                target.RestoreHorns();
                sb.Append(RestoredHornsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //Face TF
            if (target.face.type != FaceType.COCKATRICE && target.arms.type == ArmType.COCKATRICE && target.lowerBody.type == LowerBodyType.COCKATRICE && Utils.Rand(3) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.COCKATRICE);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Hair TF
            if (target.hair.type != HairType.FEATHER && Utils.Rand(4) == 0)
            {
                HairData oldData = target.hair.AsReadOnlyData();
                target.UpdateHair(HairType.FEATHER);
                sb.Append(UpdateHairText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Eye TF
            if (target.eyes.type != EyeType.COCKATRICE && target.face.type == FaceType.COCKATRICE && target.body.type == BodyType.COCKATRICE && target.ears.type == EarType.COCKATRICE &&
                Utils.Rand(3) == 0)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.UpdateEyes(EyeType.COCKATRICE);
                sb.Append(UpdateEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Lizard tongue TF
            if (target.tongue.type != TongueType.LIZARD && target.face.type == FaceType.COCKATRICE && Utils.Rand(3) == 0)
            {
                TongueData oldData = target.tongue.AsReadOnlyData();
                target.UpdateTongue(TongueType.LIZARD);
                sb.Append(UpdateTongueText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Ears TF
            if (target.ears.type != EarType.COCKATRICE && target.face.type == FaceType.COCKATRICE && Utils.Rand(3) == 0)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.COCKATRICE);
                sb.Append(UpdateEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Arm TF
            if (target.arms.type != ArmType.COCKATRICE && Utils.Rand(4) == 0)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.UpdateArms(ArmType.COCKATRICE);
                sb.Append(UpdateArmsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Neck loss, if not cockatrice neck
            if (target.neck.type != NeckType.COCKATRICE && Utils.Rand(4) == 0)
            {
                NeckData oldData = target.neck.AsReadOnlyData();
                target.RestoreNeck();
                sb.Append(RestoredNeckText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Rear body restore
            if (target.back.type != BackType.NORMAL && Utils.Rand(5) == 0)
            {
                BackData oldData = target.back.AsReadOnlyData();
                target.RestoreBack();
                sb.Append(RestoredBackText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Body TF
            if (target.body.type != BodyType.COCKATRICE && target.face.type == FaceType.COCKATRICE && Utils.Rand(3) == 0)
            {
                Species.COCKATRICE.GetRandomCockatriceColors(out FurColor feathers, out Tones scales);

                target.UpdateBody(BodyType.COCKATRICE, feathers, scales);
                NeckData oldData = target.neck.AsReadOnlyData();
                target.UpdateNeck(NeckType.COCKATRICE, feathers.primaryColor);
                sb.Append(UpdateNeckText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Neck TF, if not already TFed from Body TF above
            if (target.neck.type != NeckType.COCKATRICE && target.body.type == BodyType.COCKATRICE && target.face.type == FaceType.COCKATRICE && Utils.Rand(3) == 0)
            {
                NeckData oldData = target.neck.AsReadOnlyData();
                target.UpdateNeck(NeckType.COCKATRICE, Utils.RandomChoice(Species.COCKATRICE.availablePrimaryFeatherColors));
                sb.Append(UpdateNeckText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Leg TF
            if (target.lowerBody.type != LowerBodyType.COCKATRICE && Utils.Rand(4) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.COCKATRICE);
                sb.Append(UpdateLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Tail TF
            if (target.tail.type != TailType.COCKATRICE && Utils.Rand(4) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.COCKATRICE);
                sb.Append(UpdateTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Wings TF
            //feathered wings and not large and a target? that shouldn't happen. silently make them large
            if (target.wings.type == WingType.FEATHERED && !target.wings.isLarge && target is Player)
            {
                target.wings.GrowLarge();
            }
            else if (target.wings.type != WingType.FEATHERED && target.arms.type == ArmType.COCKATRICE && Utils.Rand(4) == 0)
            {
                HairFurColors wingColor = !target.body.activeFur.isEmpty ? target.body.activeFur.fur.primaryColor : target.hair.hairColor;

                WingData oldData = target.wings.AsReadOnlyData();
                target.UpdateWings(WingType.FEATHERED);
                sb.Append(UpdateWingsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //FAILSAFE CHANGE
            if (remainingChanges == changeCount)
            {
                if (target is CombatCreature failSafe)
                {
                    failSafe.AddHP(50);
                }
                target.ChangeLust(3);
            }

            //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes
            //occurred, then return the contents of the stringbuilder.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }