//Original credits:
        //since March 26, 2018
        //@author Stadler76
        //
        //Porter's note: Nearly all of the comments here are from the vanilla. comments from modification of code will be marked with <MODIFICATION>
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;
            //<MODIFICATION NOTE>
            //potent tf has an initial count of 3.
            int changeCount      = GenerateChangeCount(target, new int[] { 2, 2 }, enhanced ? 3 : 1);
            int remainingChanges = changeCount;

            StringBuilder sb = new StringBuilder();

            sb.Append(InitialTransformationText(target));

            if (target.face.type == FaceType.FOX && target.tail.type == TailType.FOX && target.ears.type == EarType.FOX && target.lowerBody.type == LowerBodyType.FOX &&
                target.body.IsFurBodyType() && Utils.Rand(3) == 0 && target is IExtendedCreature extended && !extended.extendedData.resistsTFBadEnds)
            {
                if (!extended.extendedData.hasFoxWarning)
                {
                    extended.extendedData.hasFoxWarning = true;
                }
                else
                {
                    isBadEnd = true;
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //<MODIFICATION NOTE>
            //Free Changes.

            //[decrease Strength] (to some floor) // I figured 15 was fair, but you're in a better position to judge that than I am.
            if (Utils.Rand(3) == 0 && target.relativeStrength > 40)
            {
                if (target.relativeStrength > 90)
                {
                    target.DecreaseStrength(4);
                }
                else if (target.relativeStrength > 80)
                {
                    target.DecreaseStrength(3);
                }
                else if (target.relativeStrength > 60)
                {
                    target.DecreaseStrength(2);
                }
                else
                {
                    target.DecreaseStrength();
                }
            }
            //[decrease Toughness] (to some floor) // 20 or so was my thought here
            if (Utils.Rand(3) == 0 && target.relativeToughness > 30)
            {
                if (target.relativeToughness > 90)
                {
                    target.DecreaseToughness(4);
                }
                else if (target.relativeToughness > 80)
                {
                    target.DecreaseToughness(3);
                }
                else if (target.relativeToughness > 60)
                {
                    target.DecreaseToughness(2);
                }
                else
                {
                    target.DecreaseToughness();
                }
            }
            //[increase Intelligence, Libido and Sensitivity]
            if (Utils.Rand(3) == 0 && (target.relativeLibido < 80 || target.relativeSensitivity < 80 || target.relativeIntelligence < 80))
            {
                if (target.relativeIntelligence < 80)
                {
                    target.IncreaseIntelligence(4);
                }

                if (target.relativeLibido < 80)
                {
                    target.IncreaseLibido(1);
                }

                if (target.relativeSensitivity < 80)
                {
                    target.IncreaseSensitivity(1);
                }
                //gain small lust also
                target.IncreaseLust(10);
            }
            //Modification Note: move this free change up here, where it makes the most sense.
            if (target.build.muscleTone > 40 && Utils.Rand(2) == 0)
            {
                target.build.DecreaseMuscleTone(4);
            }


            if (!Species.FOX.AvailableHairColors.Contains(target.hair.hairColor) && !Species.KITSUNE.elderKitsuneHairColors.Contains(target.hair.hairColor) &&
                !Species.KITSUNE.kitsuneHairColors.Contains(target.hair.hairColor) && Utils.Rand(4) == 0)
            {
                HairFurColors targetColor;
                if (target.tail.type == TailType.FOX && target.tail.tailCount > 1)
                {
                    if (target.tail.tailCount < 9)
                    {
                        targetColor = Utils.RandomChoice(Species.KITSUNE.kitsuneHairColors);
                    }

                    targetColor = Utils.RandomChoice(Species.KITSUNE.elderKitsuneHairColors);
                }
                else
                {
                    targetColor = Utils.RandomChoice(Species.FOX.AvailableHairColors);
                }
            }

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

            //<MODIFICATION NOTE>
            //Transformation Changes.
            //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);

            //[Adjust hips toward 10 – wide/curvy/flared]
            if (Utils.Rand(3) == 0 && target.hips.size != 10)
            {
                //from narrow to wide
                if (target.hips.size < 7)
                {
                    target.hips.GrowHips(2);
                }
                else if (target.hips.size < 10)
                {
                    target.hips.GrowHips();
                }
                //from wide to narrower
                else if (target.hips.size > 13)
                {
                    target.hips.ShrinkHips(2);
                }
                else
                {
                    target.hips.ShrinkHips();
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //[Remove tentacle hair]
            //required if the hair length change below is triggered
            if (target.hair.type == HairType.ANEMONE && Utils.Rand(3) == 0)
            {
                //-insert anemone hair removal into them under whatever criteria you like, though hair removal should precede abdomen growth;
                HairData oldData = target.hair.AsReadOnlyData();
                target.RestoreHair();
                sb.Append(RestoredHairText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //[Adjust hair length toward range of 16-26 – very long to ass-length]
            if (target.hair.type == HairType.ANEMONE && (target.hair.length > 26 || target.hair.length < 16) && target.hair.canGrowNaturally && Utils.Rand(4) == 0)
            {
                if (target.hair.length < 16)
                {
                    target.hair.GrowHair(1 + Utils.Rand(4));
                }
                else
                {
                    target.hair.ShortenHair(1 + Utils.Rand(4));
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(10) == 0)
            {
                //MOD NOTE: Don't remove ?. operator.
                if ((target as CombatCreature)?.RecoverFatigue(10) > 0)
                {
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }

            //dog cocks!

            //MOD NOTE: consider adding back in fox cocks and using them? they could literally just be dog cocks but with a slightly different text descriptions.
            //MOD NOTE: this is a linq check that sees if any available cocks aren't a dog c**k. it's actually faster than count of type, because it stops as soon as it hits
            //a c**k that isn't a dog, instead of checking all of them. Also, this function finds all non-dog cocks and converts one at random.
            if (Utils.Rand(3) == 0 && target.hasCock && target.cocks.Any(x => x.type != CockType.DOG))
            {
                C**k toChange = Utils.RandomChoice(target.cocks.Where(x => x.type != CockType.DOG).ToArray());

                if (toChange.type == CockType.HUMAN)
                {
                    toChange.IncreaseThickness(.3);
                    target.DeltaCreatureStats(sens: 10, lus: 5);
                }
                //Horse
                else if (toChange.type == CockType.HORSE)
                {
                    //Tweak length/thickness.

                    double deltaLength;
                    if (toChange.length > 6)
                    {
                        deltaLength = -2;
                    }
                    else
                    {
                        deltaLength = -.5;
                    }

                    toChange.SetLengthAndGirth(toChange.length + deltaLength, toChange.girth + 0.5);
                    target.DeltaCreatureStats(sens: 4, lus: 5);
                }
                else
                {
                    target.DeltaCreatureStats(sens: 4, lus: 10);
                }

                target.genitals.UpdateCock(toChange, CockType.DOG);

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

            //Cum Multiplier Xform
            if (target.genitals.totalCum < 5000 && Utils.Rand(3) == 0 && target.hasCock)
            {
                int temp = 2 + Utils.Rand(4);
                //Lots of cum raises cum multiplier cap to 2 instead of 1.5
                if (target.HasPerk <MessyOrgasms>())
                {
                    temp += Utils.Rand(10);
                }
                //MOD NOTE: not sure if cum calculations changed, (i think they have) and if so, that's a lot of multiplier gain holy shit. meh. whatever.
                target.genitals.IncreaseCumMultiplier(temp);
                //Flavor text
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (target.balls.count > 0 && target.balls.size > 4 && Utils.Rand(3) == 0)
            {
                int targetSize;
                //currently above max, but whatever, that'll probably get raised anyway.
                if (target.balls.size > 50)
                {
                    targetSize = target.balls.size / 5;
                }
                else if (target.balls.size > 10)
                {
                    targetSize = target.balls.size / 2;
                }
                else
                {
                    targetSize = target.balls.size - 1;
                }

                //allow perks to work.
                target.balls.ShrinkBalls((byte)(target.balls.size - targetSize));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Sprouting more!
            if (enhanced && target.breasts.Count < 4 && target.breasts[target.breasts.Count - 1].cupSize > CupSize.A)
            {
                target.genitals.AddBreastRow(target.breasts[target.breasts.Count - 1].cupSize);

                target.DeltaCreatureStats(sens: 2, lus: 30);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Find out if t**s are eligible for evening
            //MOD NOTE: there's an easier way - just run the normalize breasts. it'll return true if it changed anything. since we display the results of the old
            //breast data anyway, this is way simpler. Also, normalize breasts now returns a bool. woo!
            //MOD NOTE 2: Fox rules here seem to use the same size as previous. if they want to function under anthro rules (size is in decreasing order, but otherwise
            //roughly even) you can use AnthropomorphizeBreasts() instead. see canine tfs for an example.
            if (target.genitals.NormalizeBreasts())
            {
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }



            //HEAT!
            if (!target.HasTimedEffect <Heat>() || target.GetTimedEffectData <Heat>().totalAddedLibido < 30 && Utils.Rand(6) == 0)
            {
                target.GoIntoHeat();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Neck restore
            if (!target.neck.isDefault && 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)
            {
                if (target.womb.ClearOviposition())
                {
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //[Grow Fur]
            //FOURTH
            //MOD NOTE: Body has been reworked since this was written, we'll allow kitsune's to remain unchanged, but all others need to be full fur after this.
            if ((enhanced || target.lowerBody.type == LowerBodyType.FOX) && target.body.type != BodyType.KITSUNE && !target.body.IsFurBodyType() && Utils.Rand(4) == 0)
            {
                BodyData oldBodyData = target.body.AsReadOnlyData();

                if (Species.KITSUNE.Score(target) >= 4)
                {
                    FurColor[] colorChoices;
                    if (Species.KITSUNE.elderKitsuneFurColors.Any(x => x.IsIdenticalTo(target.hair.hairColor)) ||
                        Species.KITSUNE.allKitsuneColors.Any(y => y.IsIdenticalTo(target.hair.hairColor)))
                    {
                        colorChoices = new FurColor[] { new FurColor(target.hair.hairColor) };
                    }
                    else if (target.tail.type == TailType.FOX && target.tail.tailCount == TailType.FOX.maxTailCount)
                    {
                        colorChoices = Species.KITSUNE.allKitsuneColors;
                    }
                    else
                    {
                        colorChoices = Species.KITSUNE.elderKitsuneFurColors;
                    }

                    target.UpdateBody(BodyType.UNDERBODY_FUR, Utils.RandomChoice(colorChoices));
                }
                else
                {
                    Species.FOX.GetRandomFurColors(out FurColor primary, out FurColor underbody);

                    target.UpdateBody(BodyType.UNDERBODY_FUR, primary, underbody);
                }

                //should always be true, but whatever.
                if (oldBodyData.type != target.body.type)
                {
                    remainingChanges--;
                    if (remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //[Grow Fox Legs]
            //THIRD
            if ((enhanced || target.ears.type == EarType.FOX) && target.lowerBody.type != LowerBodyType.FOX && Utils.Rand(5) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.FOX);
                sb.Append(UpdateLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Grow Fox Ears]
            //SECOND
            if ((enhanced || target.tail.type == TailType.FOX) && target.ears.type != EarType.FOX && Utils.Rand(4) == 0)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.FOX);
                sb.Append(UpdateEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //[Grow Fox Tail](fairly common)
            //FIRST
            if (target.tail.type != TailType.FOX && Utils.Rand(4) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.FOX);
                sb.Append(UpdateTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //[Grow Fox Face]
            //LAST - muzzlygoodness
            //should work from any face, including other muzzles
            if (target.body.HasAny(EpidermisType.FUR) && target.face.type != FaceType.FOX && Utils.Rand(5) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.FOX);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Arms
            if (target.arms.type != ArmType.FOX && target.body.HasAny(EpidermisType.FUR) && target.tail.type == TailType.FOX && target.lowerBody.type == LowerBodyType.FOX && Utils.Rand(4) == 0)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.UpdateArms(ArmType.FOX);
                sb.Append(UpdateArmsText(target, oldData));

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


            if (remainingChanges == changeCount && !(target is null))
            {
                (target as CombatCreature)?.RecoverFatigue(5);
            }

            //<MODIFICATION NOTE>
            //Fall through, cleanup and return.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }