Esempio n. 1
0
 protected virtual string RestoredGillsText(Creature target, GillData oldData)
 {
     return(target.gills.RestoredText(oldData));
 }
Esempio n. 2
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 });
            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);


            //Text go!

            //Speed raises up to 75
            if (target.relativeSpeed < 75 && Utils.Rand(3) == 0)
            {
                //low speed
                if (target.relativeSpeed <= 30)
                {
                    target.ChangeSpeed(2);
                }
                //medium speed
                else if (target.relativeSpeed <= 60)
                {
                    target.ChangeSpeed(1);
                }
                //high speed
                else
                {
                    target.ChangeSpeed(.5);
                }
            }
            //Strength raises to 40
            if (target.relativeStrength < 40 && Utils.Rand(3) == 0)
            {
                target.ChangeStrength(1);
            }
            //Strength ALWAYS drops if over 60
            //Does not add to change total
            else if (target.relativeStrength > 60 && Utils.Rand(2) == 0)
            {
                target.ChangeStrength(-2);
            }
            //Toughness drops if over 50
            //Does not add to change total
            if (target.relativeToughness > 50 && Utils.Rand(2) == 0)
            {
                target.ChangeToughness(-2);
            }
            //Intelliloss
            if (Utils.Rand(4) == 0)
            {
                target.ChangeIntelligence(-1);
            }

            //Libido gain
            if (target.relativeLibido < 80 && Utils.Rand(4) == 0)
            {
                target.DeltaCreatureStats(lib: 1, sens: .25);
            }

            //Sexual changes would go here if I wasn't a tard.
            //Heat
            if (Utils.Rand(4) == 0)
            {
                bool intensified = target.HasTimedEffect <Heat>();

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

            //Shrink the boobalies down to A for men or C for girls.
            if (Utils.Rand(4) == 0 && !hyperHappy)
            {
                CupSize targetSize;
                //female or herm. targeting C cup, but will respect smallest possible cup if it's somehow above a c cup.
                if (target.gender.HasFlag(Gender.FEMALE))
                {
                    targetSize = EnumHelper.Max(CupSize.C, target.genitals.smallestPossibleFemaleCupSize);
                }
                //male or genderless. targeting A cup, but will respect smallest possible cup if somehow above an a cup.
                else
                {
                    targetSize = EnumHelper.Max(CupSize.A, target.genitals.smallestPossibleMaleCupSize);
                }
                //IT IS!
                int rowsShrunk = 0;
                foreach (Breasts row in target.breasts)
                {
                    //If this row is over threshhold
                    if (row.cupSize > targetSize)
                    {
                        //Big change
                        if (row.cupSize > CupSize.EE_BIG)
                        {
                            row.ShrinkBreasts((byte)(2 + Utils.Rand(3)));
                        }
                        //Small change
                        else
                        {
                            row.ShrinkBreasts(1);
                        }
                        rowsShrunk++;
                    }
                }
                //Count that t**s were shrunk
                if (rowsShrunk > 0)
                {
                    remainingChanges--;
                    if (remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //Cat dangly-doo.
            if (target.cocks.Count > 0 && target.cocks.Any(x => x.type != CockType.CAT) && (target.ears.type == EarType.CAT || Utils.Rand(3) > 0) &&
                (target.tail.type == TailType.CAT || Utils.Rand(3) > 0) && Utils.Rand(4) == 0)
            {
                C**k nonCat = target.cocks.First(x => x.type != CockType.CAT);

                target.genitals.UpdateCock(nonCat, CockType.CAT);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Cat penorz shrink
            if (target.genitals.HasAnyCocksOfType(CockType.CAT) && Utils.Rand(3) == 0 && !hyperHappy)
            {
                //loop through and find a cat wang.

                foreach (C**k catCock in target.cocks.Where(x => x.type == CockType.CAT))
                {
                    //lose 33% size until under 10, then lose 2" at a time
                    if (catCock.length > 16)
                    {
                        catCock.SetLength(catCock.length * .66);
                    }
                    else if (catCock.length > 6)
                    {
                        catCock.DecreaseLength(2);
                    }
                    if (catCock.length / 5 < catCock.girth && catCock.girth > 1.25)
                    {
                        catCock.SetGirth(catCock.length / 6);
                    }
                }

                //(big sensitivity boost)
                target.ChangeSensitivity(5);
                //Make note of other dicks changing
                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));
                }
            }
            //Body type changes. Teh rarest of the rare.
            // Catgirl-face -> cat-morph-face
            if (target.face.type == FaceType.CAT && !target.face.isFullMorph && target.tongue.type == TongueType.CAT && target.ears.type == EarType.CAT &&
                target.tail.type == TailType.CAT && target.lowerBody.type == LowerBodyType.CAT && target.arms.type == ArmType.CAT &&
                (target.body.IsFurBodyType() /*|| (target.body.type == BodyType.REPTILE && Species.SPHINX.Score(target) >= 4)*/) && Utils.Rand(5) == 0)
            {
                target.face.StrengthenFacialMorph();
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //DA EARZ
            if (target.ears.type != EarType.CAT && Utils.Rand(5) == 0)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.CAT);
                sb.Append(UpdateEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //DA TAIL (IF ALREADY HAZ URZ)
            if (target.tail.type != TailType.CAT && target.ears.type == EarType.CAT && Utils.Rand(5) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.CAT);
                sb.Append(UpdateTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Da paws (if already haz ears & tail)
            if (target.tail.type == TailType.CAT && target.ears.type == EarType.CAT && Utils.Rand(5) == 0 && target.lowerBody.type != LowerBodyType.CAT)
            {
                //hoof to cat:
                if (target.lowerBody.type == LowerBodyType.HOOVED)
                {
                }
                //Goo to cat
                else if (target.lowerBody.type == LowerBodyType.GOO)
                {
                }
                //non hoof to cat:
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.CAT);
                sb.Append(UpdateLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //TURN INTO A FURRAH! OH SHIT
            if (target.tail.type == TailType.CAT && target.ears.type == EarType.CAT && Utils.Rand(5) == 0 && target.lowerBody.type == LowerBodyType.CAT && !target.body.IsFurBodyType())
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.UpdateBody(BodyType.UNDERBODY_FUR);
                sb.Append(UpdateBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            // Fix old cat faces without cat-eyes.
            if (target.face.type == FaceType.CAT && target.eyes.type != EyeType.CAT && Utils.Rand(3) == 0)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.UpdateEyes(EyeType.CAT);
                sb.Append(UpdateEyesText(target, oldData));

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

            //CAT-FACE! FULL ON FURRY! RAGE AWAY NEKOZ
            if (target.tail.type == TailType.CAT && target.ears.type == EarType.CAT && target.lowerBody.type == LowerBodyType.CAT && target.face.type != FaceType.CAT && Utils.Rand(5) == 0)
            {
                //Gain cat face, replace old face
                //MOD NOTE: but not a full-morph cat face. just level 1.
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.CAT);
                sb.Append(UpdateFaceText(target, oldData));

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

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            // Remove 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));
                }
            }
            //FAILSAFE CHANGE
            if (remainingChanges == changeCount)
            {
                (target as CombatCreature)?.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));
        }
Esempio n. 3
0
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

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

            StringBuilder sb = new StringBuilder();

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

            int currentChanges() => changeCount - remainingChanges;

            //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);

            //clear screen

            //Statistical changes:
            //-Reduces speed down to 50.
            if (target.speed > ngPlus(50) && Utils.Rand(4) == 0)
            {
                target.ChangeSpeed(-1);
            }
            //-Raises toughness to 70
            //(+3 to 40, +2 to 55, +1 to 70)
            if (target.toughness < ngPlus(70) && Utils.Rand(3) == 0)
            {
                //(+3)
                if (target.toughness < ngPlus(40))
                {
                    target.ChangeToughness(3);
                }
                //(+2)
                else if (target.toughness < ngPlus(55))
                {
                    target.ChangeToughness(2);
                }
                //(+1)
                else
                {
                    target.ChangeToughness(1);
                }
            }
            //-Reduces sensitivity.
            if (target.relativeSensitivity > 20 && Utils.Rand(3) == 0)
            {
                target.ChangeSensitivity(-1);
            }
            //Raises libido greatly to 50, then somewhat to 75, then slowly to 100.
            if (target.relativeLibido < 100 && Utils.Rand(3) == 0)
            {
                //+3 lib if less than 50
                if (target.relativeLibido < 50)
                {
                    target.ChangeLibido(1);
                }
                //+2 lib if less than 75
                if (target.relativeLibido < 75)
                {
                    target.ChangeLibido(1);
                }
                //+1 if above 75.
                target.ChangeLibido(1);
            }


            //Sexual Changes:
            //-Lizard dick - first one
            if (target.cocks.Any(x => x.type != CockType.LIZARD) && target.cocks.Count > 0 && ScalingChance(2, currentChanges(), 4))
            {
                //Find the first non-lizzy dick
                C**k firstNonLizard = target.cocks.First(x => x.type != CockType.LIZARD);
                //Actually xform it nau
                target.genitals.UpdateCock(firstNonLizard, CockType.LIZARD);

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

                target.DeltaCreatureStats(lib: 3, lus: 10);
            }
            //(CHANGE OTHER DICK)
            int lizardCocks = target.genitals.CountCocksOfType(CockType.LIZARD);
            //Requires 1 lizard c**k, multiple cocks
            if (target.hasCock && (lizardCocks != target.cocks.Count || target.cocks.Count == 1) && ScalingChance(2, currentChanges(), 4))
            {
                if (target.cocks.Count > 1)
                {
                    C**k nonLizard = target.cocks.First(x => x.type != CockType.LIZARD);

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

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

                    target.DeltaCreatureStats(lib: 3, lus: 10);
                }
                else
                {
                    target.genitals.AddCock(CockType.LIZARD, target.cocks[0].length, target.cocks[0].girth);

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

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

            //MOD NOTE: REMOVED. SEE RANT IN SALAMANDER TRANSFORMATIONS

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

            //-Breasts vanish to 0 rating if male
            if (target.genitals.BiggestCupSize() > target.genitals.smallestPossibleMaleCupSize && target.gender == Gender.MALE && ScalingChance(2, currentChanges(), 3))
            {
                //(HUEG)
                foreach (Breasts breast in target.breasts)
                {
                    if (breast.cupSize > CupSize.E_BIG)
                    {
                        breast.ShrinkBreasts(((byte)breast.cupSize).div(2));
                    }
                    else
                    {
                        breast.SetCupSize(target.genitals.smallestPossibleMaleCupSize);
                    }
                }
                //(+2 speed)
                target.IncreaseSpeed(2);
                target.ChangeLibido(2);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Lactation stoppage.
            if (target.genitals.isLactating && ScalingChance(2, currentChanges(), 4))
            {
                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 && ScalingChance(2, currentChanges(), 4))
            {
                target.genitals.SetQuadNipples(false);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Remove extra breast rows
            if (target.breasts.Count > 1 && ScalingChance(2, currentChanges(), 3) && !hyperHappy)
            {
                target.RemoveExtraBreastRows();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-VAGs
            if (target.hasVagina && target.womb.canObtainOviposition && Species.LIZARD.Score(target) > 3 && ScalingChance(3, currentChanges(), 5))
            {
                target.womb.GrantOviposition();
                sb.Append(GrantOvipositionText(target));

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

            //Physical changes:
            //-Existing horns become draconic, max of 4, max length of 1'
            if (target.horns.type != HornType.DRACONIC && ScalingChance(3, currentChanges(), 5))
            {
                HornData oldData = target.horns.AsReadOnlyData();
                target.UpdateHorns(HornType.DRACONIC);
                sb.Append(UpdateHornsText(target, oldData));

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

            //Neck restore
            if (target.neck.type != NeckType.HUMANOID && ScalingChance(2, currentChanges(), 4))
            {
                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 && ScalingChance(3, currentChanges(), 5))
            {
                BackData oldData = target.back.AsReadOnlyData();
                target.RestoreBack();
                sb.Append(RestoredBackText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Hair changes
            if (ScalingChance(2, currentChanges(), 4))
            {
                if (target.eyes.type == EyeType.BASILISK)
                {
                    if (target.corruption > 65 && target.face.IsReptilian() && target.body.type == BodyType.REPTILE && target.hair.type != HairType.BASILISK_SPINES)
                    {
                        HairData oldData = target.hair.AsReadOnlyData();
                        target.UpdateHair(HairType.BASILISK_SPINES);
                        sb.Append(UpdateHairText(target, oldData));

                        if (--remainingChanges <= 0)
                        {
                            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                        }
                    }
                    else if (target.corruption < 15 && target.gender.HasFlag(Gender.FEMALE) && target.hair.type != HairType.BASILISK_PLUME)
                    {
                        target.hair.ResumeNaturalGrowth();
                        HairData oldData = target.hair.AsReadOnlyData();
                        target.UpdateHair(HairType.BASILISK_PLUME);
                        sb.Append(UpdateHairText(target, oldData));

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

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //Remove beard!
#warning BEARDS NOT YET IMPLEMENTED
            //Mod note: Nords are so serious about beards. ... Mai'q thinks they wish they had GLORIOUS pubes like Khajiit.

            //if (target.hasBeard() && ScalingChance(2, currentChanges(), 3))
            //{
            //	target.beard.length = 0;
            //	target.beard.style = 0;
            //}
            //Big physical changes:
            //-Legs – Draconic, clawed feet
            if (target.lowerBody.type != LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5))
            {
                //Hooves -
                //TAURS -
                //feet types -
                //Else –
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.LIZARD);
                sb.Append(UpdateLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            // <mod name="Predator arms" author="Stadler76">
            //Gain predator arms
            //Claw transition
            //MOD OF MOD (LOL): predator arms are a collection of arm types now, not a single type. thus, we can combine the claw and type checks.
            //now, you get the arms (and thus, the claws) if they aren't a predator type and have lizard legs, or switch to lizard arms from any other predator type.
            if ((target.arms.IsPredatorArms() || target.lowerBody.type == LowerBodyType.LIZARD) && target.body.type == BodyType.REPTILE && ScalingChance(2, currentChanges(), 3))
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.UpdateArms(ArmType.LIZARD);
                sb.Append(UpdateArmsText(target, oldData));

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

            //-Tail – sinuous lizard tail
            if (target.tail.type != TailType.LIZARD && target.lowerBody.type == LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5))
            {
                //No tail
                //Yes tail
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.LIZARD);
                sb.Append(UpdateTailText(target, oldData));

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Ears become smaller nub-like openings?
            if (target.ears.type != EarType.LIZARD && target.tail.type == TailType.LIZARD && target.lowerBody.type == LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5))
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.LIZARD);
                sb.Append(UpdateEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Scales – color changes to red, green, white, blue, or black. Rarely: purple or silver.
            if (target.body.type != BodyType.REPTILE && target.ears.type == EarType.LIZARD && target.tail.type == TailType.LIZARD && target.lowerBody.type == LowerBodyType.LIZARD &&
                ScalingChance(3, currentChanges(), 5))
            {
                Species.LIZARD.GetRandomSkinTone(out Tones primary, out Tones underbody);

                target.UpdateBody(BodyType.REPTILE, primary, underbody);

                //kGAMECLASS.rathazul.addMixologyXP(20);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Lizard-like face.
            if (target.face.type != FaceType.LIZARD && target.body.type == BodyType.REPTILE && target.ears.type == EarType.LIZARD && target.tail.type == TailType.LIZARD &&
                target.lowerBody.type == LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5))
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.LIZARD);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Lizard tongue
            if (target.tongue.type == TongueType.SNAKE && Utils.Rand(10) < 6)
            {
                // Higher (60%) chance to be 'fixed' if old variant
                TongueData oldData = target.tongue.AsReadOnlyData();
                target.UpdateTongue(TongueType.LIZARD);
                sb.Append(UpdateTongueText(target, oldData));

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

            if (target.tongue.type != TongueType.LIZARD && target.tongue.type != TongueType.SNAKE && target.face.IsReptilian() && ScalingChance(2, currentChanges(), 3))
            {
                TongueData oldData = target.tongue.AsReadOnlyData();
                target.UpdateTongue(TongueType.LIZARD);
                sb.Append(UpdateTongueText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Remove Gills
            if (ScalingChance(2, currentChanges(), 4) && !target.gills.isDefault)
            {
                GillData oldData = target.gills.AsReadOnlyData();
                target.RestoreGills();
                sb.Append(RestoredGillsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //<mod name="Reptile eyes" author="Stadler76">
            //-Lizard eyes
            if (target.eyes.type != EyeType.LIZARD && target.face.type == FaceType.LIZARD && target.body.type == BodyType.REPTILE && target.ears.type == EarType.LIZARD &&
                ScalingChance(2, currentChanges(), 4))
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.UpdateEyes(EyeType.LIZARD);
                sb.Append(UpdateEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //</mod>
            //FAILSAFE CHANGE
            if (remainingChanges == changeCount)
            {
                (target as CombatCreature)?.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));
        }
Esempio n. 4
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));
        }
        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, 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);


            //clear screen
            //Statistical changes:
            //-Reduces speed down to 70.
            if (target.relativeSpeed > 70 && Utils.Rand(4) == 0)
            {
                target.ChangeSpeed(-1);
            }
            //-Reduces intelligence down to 60.
            if (target.relativeIntelligence > 60 && Utils.Rand(4) == 0)
            {
                target.ChangeIntelligence(-1);
            }

            //-Raises toughness up to 90.
            //(+3 to 50, +2 to 70, +1 to 90)
            if (target.relativeToughness < 90 && Utils.Rand(3) == 0)
            {
                //(+3)
                if (target.relativeToughness < 50)
                {
                    target.ChangeToughness(3);
                }
                //(+2)
                else if (target.relativeToughness < 70)
                {
                    target.ChangeToughness(2);
                }
                //(+1)
                else
                {
                    target.ChangeToughness(1);
                }
            }
            //-Raises strength to 80.
            if (target.relativeStrength < 80 && Utils.Rand(3) == 0)
            {
                target.ChangeStrength(1);
            }
            //-Raises libido up to 90.
            if (target.relativeLibido < 90 && Utils.Rand(3) == 0)
            {
                target.ChangeLibido(2);
            }
            //Sexual Changes:
            //MOD: lizard dicks get a common rng roll now. The only difference is the text.
            if (target.hasCock && !target.genitals.OnlyHasCocksOfType(CockType.LIZARD) && Utils.Rand(4) == 0)
            {
                C**k nonLizard = target.cocks.First(x => x.type != CockType.LIZARD);
                //Actually xform it nau
                target.genitals.UpdateCock(nonLizard, CockType.LIZARD);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }

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

            //-Breasts vanish to 0 rating if male
            if (target.genitals.BiggestCupSize() >= target.genitals.smallestPossibleMaleCupSize && target.gender == Gender.MALE && Utils.Rand(3) == 0)
            {
                //(HUEG)
                //Loop through behind the scenes and adjust all t**s.
                foreach (Breasts breastRow in target.breasts)
                {
                    if (breastRow.cupSize > CupSize.E_BIG)
                    {
                        breastRow.ShrinkBreasts(((byte)breastRow.cupSize).div(2));
                    }
                    else
                    {
                        breastRow.SetCupSize(target.genitals.smallestPossibleMaleCupSize);
                    }
                }
                //(+2 speed)
                //target.IncreaseSpeed(2);
                target.ChangeLibido(2);
                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));
                }
            }

            Breasts smallestTits = target.genitals.SmallestBreast();

            //Increase target's breast size, if they are big DD or smaller
            if (smallestTits.cupSize <= CupSize.DD_BIG && target.gender == Gender.FEMALE && Utils.Rand(4) == 0)
            {
                smallestTits.GrowBreasts();
                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();

                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));
                }
            }
            //Physical changes:
            //Tail - 1st gain reptilian tail, 2nd unlocks enhanced with fire tail whip attack
            if (target.tail.type != TailType.LIZARD && target.tail.type != TailType.SALAMANDER && Utils.Rand(3) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.LIZARD);
                sb.Append(UpdateTailText(target, oldData));

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Legs
            if (target.lowerBody.type != LowerBodyType.SALAMANDER && target.tail.type == TailType.SALAMANDER && Utils.Rand(3) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.SALAMANDER);
                sb.Append(UpdateLowerBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Arms
            if (target.arms.type != ArmType.SALAMANDER && target.lowerBody.type == LowerBodyType.SALAMANDER && Utils.Rand(3) == 0)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.UpdateArms(ArmType.SALAMANDER);
                sb.Append(UpdateArmsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove odd eyes
            if (Utils.Rand(4) == 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));
                }
            }
            //Human face
            if (target.face.type != FaceType.HUMAN && Utils.Rand(4) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.HUMAN);
                sb.Append(UpdateFaceText(target, oldData));

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Skin color change
            if (!Species.SALAMANDER.availableTones.Contains(target.body.primarySkin.tone) && Utils.Rand(4) == 0)
            {
                target.body.ChangeMainSkin(Utils.RandomChoice(Species.SALAMANDER.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 && Utils.Rand(3) == 0)
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.RestoreBody();
                sb.Append(RestoredBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Removing 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));
                }
            }
            //FAILSAFE CHANGE
            if (remainingChanges == changeCount)
            {
                (target as CombatCreature)?.AddHP(100);
                target.ChangeLust(5);
            }

            //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));
        }
Esempio n. 6
0
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

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

            //crit is our modifier for basically all stats. 1 is default, though any non-standard will proc the non-default
            //standard also has a 15% chance of proccing it too.
            int crit = 1;

            if (modifiers > CanineModifiers.STANDARD || Utils.Rand(20) < 3)
            {
                crit = Utils.Rand(20) / 10 + 2;
            }
            bool hasCrit() => crit > 1;

            StringBuilder sb = new StringBuilder();

            bool DoKnotChanges(double delta)
            {
                if (target.hasCock)
                {
                    bool changed      = false;
                    int  dogCockCount = target.genitals.CountCocksOfType(CockType.DOG);
                    if (dogCockCount > 0)
                    {
                        C**k smallestKnot = target.cocks.MinItem(x => x.type == CockType.DOG ? (double?)x.knotMultiplier : null);
                        if (smallestKnot.knotMultiplier > 2)
                        {
                            delta /= 10;
                        }
                        else if (smallestKnot.knotMultiplier > 1.75)
                        {
                            delta /= 5;
                        }
                        else if (smallestKnot.knotMultiplier > 1.5)
                        {
                            delta /= 2;
                        }

                        double knotMultiplierDelta = smallestKnot.IncreaseKnotMultiplier(delta);
                        if (knotMultiplierDelta != 0)
                        {
                            sb.Append(EnlargedSmallestKnotText(target, target.cocks.IndexOf(smallestKnot), knotMultiplierDelta, dogCockCount > 1));
                            changed = true;
                        }
                    }


                    target.DeltaCreatureStats(sens: 0.5, lus: 5 * crit);
                    return(changed);
                }
                return(false);
            }

            sb.Append(InitialTransformationText(modifiers, hasCrit()));

            //bad end related checks
            if (hasCrit() && target.body.hasActiveFurData && target.face.type == FaceType.DOG && target.ears.type == EarType.DOG &&
                target.lowerBody.type == LowerBodyType.DOG && target.tail.type == TailType.DOG && target is IExtendedCreature extended)
            {
                //can get bad end.
                if (extended.extendedData.hasDoggoWarning && !extended.extendedData.resistsTFBadEnds)
                {
                    //bad end.
                    if (Utils.RandBool())
                    {
                        sb.Append(BadEndText(target));
                        isBadEnd = true;
                        return(ApplyChangesAndReturn(target, sb, 0));
                    }
                    //get lucky, but warn that they got lucky
                    else
                    {
                        sb.Append(DoggoWarningText(target, true));
                    }
                }
                //not warned
                else if (!extended.extendedData.hasDoggoWarning)
                {
                    //warn
                    extended.extendedData.hasDoggoWarning = true;
                    sb.Append(DoggoWarningText(target, false));
                }
            }
            //stat changes
            if (modifiers.HasFlag(CanineModifiers.BLACK))
            {
                target.IncreaseCreatureStats(lus: (byte)(5 + Utils.Rand(5)), lib: (byte)(2 + Utils.Rand(4)), corr: (byte)(2 + Utils.Rand(4)));
            }
            //stat changes (cont.)
            double strengthIncrease     = 0;
            double speedIncrease        = 0;
            double intelligenceDecrease = 0;

            if (target.relativeStrength < 50 && Utils.Rand(3) == 0)
            {
                strengthIncrease = target.IncreaseStrength(crit);
            }
            if (target.relativeSpeed < 30 && Utils.Rand(3) == 0)
            {
                speedIncrease = target.IncreaseSpeed(crit);
            }
            if (target.relativeIntelligence > 30 && Utils.Rand(3) == 0)
            {
                intelligenceDecrease = target.DecreaseIntelligence(crit);
            }

            sb.Append(StatChangeText(target, strengthIncrease, speedIncrease, intelligenceDecrease));

            //modifier effects (no cost)

            //double pepper
            if (modifiers.HasFlag(CanineModifiers.DOUBLE))
            {
                int dogCocks = target.genitals.CountCocksOfType(CockType.DOG);
                //already has 2+ dog cocks.
                if (dogCocks >= 2)
                {
                    //just treat it like a large. so we'll just bitwise or the
                    //large flag in.
                    modifiers |= CanineModifiers.LARGE;
                }
                //has no dog cocks.
                else if (dogCocks == 0)
                {
                    //has no cocks. - grow 2
                    if (target.cocks.Count == 0)
                    {
                        target.AddCock(CockType.DOG, 7 + Utils.Rand(7), 1.5 + Utils.Rand(10) / 10, 1.7);
                        target.AddCock(CockType.DOG, 7 + Utils.Rand(7), 1.5 + Utils.Rand(10) / 10, 1.7);

                        sb.Append(GrewTwoDogCocksHadNone(target));
                    }
                    //has one c**k. - grow one, change one
                    else if (target.cocks.Count == 1)
                    {
                        CockData oldCockData = target.cocks[0].AsReadOnlyData();

                        target.genitals.UpdateCockWithKnot(0, CockType.DOG, 1.5);
                        if (target.cocks[0].length < 10)
                        {
                            target.cocks[0].SetLength(10);
                        }
                        target.AddCock(CockType.DOG, 7 + Utils.Rand(7), 1.5 + Utils.Rand(10) / 10.0, 1.7);

                        sb.Append(ConvertedFirstDogCockGrewSecond(target, oldCockData));
                    }
                    //has 2+ cocks. -change 2
                    else
                    {
                        CockData firstOldData  = target.cocks[0].AsReadOnlyData();
                        CockData secondOldData = target.cocks[1].AsReadOnlyData();

                        target.genitals.UpdateCockWithKnot(0, CockType.DOG, 1.5);
                        if (target.cocks[0].length < 10)
                        {
                            target.cocks[0].SetLength(10);
                        }
                        target.genitals.UpdateCockWithKnot(1, CockType.DOG, 1.5);
                        if (target.cocks[1].length < 10)
                        {
                            target.cocks[1].SetLength(10);
                        }
                        sb.Append(ConvertedTwoCocksToDog(target, firstOldData, secondOldData));
                    }
                }
                //one dog c**k.
                else
                {
                    if (target.cocks.Count == 1)
                    {
                        target.AddCock(CockType.DOG, 7 + Utils.Rand(7), 1.5 + Utils.Rand(10) / 10.0, 1.7);
                        sb.Append(GrewSecondDogCockHadOne(target));
                    }
                    else
                    {
                        if (target.cocks[0].type == CockType.DOG)
                        {
                            CockData oldData = target.cocks[1].AsReadOnlyData();
                            target.genitals.UpdateCockWithKnot(1, CockType.DOG, 1.5);
                            if (target.cocks[1].length < 10)
                            {
                                target.cocks[1].SetLength(10);
                            }
                            sb.Append(ConvertedOneCockToDogHadOne(target, 1, oldData));
                        }
                        else
                        {
                            CockData oldData = target.cocks[0].AsReadOnlyData();

                            target.genitals.UpdateCockWithKnot(0, CockType.DOG, 1.5);
                            if (target.cocks[0].length < 10)
                            {
                                target.cocks[0].SetLength(10);
                            }
                            sb.Append(ConvertedOneCockToDogHadOne(target, 0, oldData));
                        }
                    }
                }

                target.IncreaseCreatureStats(lib: 2, lus: 50);
            }

            //there are two ways to proc this - either via a knotty modifier (written here), or via random chance and/or with a large pepper (written later)
            //however, a knotty pepper modifier has no tf cost, the other check does. also, this will modify a c**k if none have a dog knot, the other will not.
            if (modifiers.HasFlag(CanineModifiers.KNOTTY))
            {
                double delta = ((Utils.Rand(2) + 5) / 20) * crit;

                if (!DoKnotChanges(delta))
                {
                    if (target.hasCock)
                    {
                        CockData oldCockData = target.cocks[0].AsReadOnlyData();

                        double knotSize = 1.75;

                        if (target.cocks[0].hasKnot)
                        {
                            knotSize = Math.Max(2.1, target.cocks[0].knotMultiplier);
                        }

                        target.genitals.UpdateCockWithKnot(0, CockType.DOG, knotSize);
                        if (target.cocks[0].length < 10)
                        {
                            target.cocks[0].SetLength(10);
                        }
                        sb.Append(ConvertedOneCockToDog(target, 0, oldCockData));
                    }


                    else
                    {
                        sb.Append(WastedKnottyText(target));
                    }
                }
            }

            //bulby
            if (modifiers.HasFlag(CanineModifiers.BULBY))
            {
                if (!target.hasBalls)
                {
                    target.genitals.GrowBalls(2, 1);
                    target.DeltaCreatureStats(lib: 2, lus: -10);
                    sb.Append(GrewBallsText(target));
                }
                else if (target.balls.uniBall)
                {
                    BallsData oldData = target.balls.AsReadOnlyData();
                    target.genitals.ConvertToNormalBalls();
                    target.DeltaCreatureStats(lib: 1, lus: 1);

                    sb.Append(EnlargedBallsText(target, oldData));
                }
                else
                {
                    BallsData oldData = target.balls.AsReadOnlyData();

                    byte enlargeAmount = target.genitals.balls.EnlargeBalls(1);
                    target.IncreaseCreatureStats(lib: 1, lus: 3);

                    sb.Append(EnlargedBallsText(target, oldData));
                }
            }

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

            //tfs (cost 1).

            //restore neck
            if (target.neck.type != NeckType.defaultValue && Utils.Rand(4) == 0)
            {
                NeckData oldNeck = target.neck.AsReadOnlyData();
                target.RestoreNeck();

                sb.Append(RestoredNeckText(target, oldNeck));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                }
            }

            //remove oviposition
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                if (target.womb.ClearOviposition())
                {
                    sb.Append(RemovedOvipositionText(target));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                    }
                }
            }

            //remove feather-hair
            if (RemoveFeatheryHair(target))
            {
                sb.Append(RemovedFeatheryHairText(target));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                }
            }

            //knot multiplier (but not knotty)
            if (!modifiers.HasFlag(CanineModifiers.KNOTTY) && target.genitals.CountCocksOfType(CockType.DOG) > 0 && (modifiers.HasFlag(CanineModifiers.LARGE) || Utils.Rand(7) < 5))
            {
                double delta = ((Utils.Rand(2) + 1) / 20.0) * crit;
                if (DoKnotChanges(delta))
                {
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                    }
                }
            }

            //transform a c**k.
            if (target.genitals.CountCocksOfType(CockType.DOG) < target.cocks.Count && (modifiers.HasFlag(CanineModifiers.LARGE) || Utils.Rand(8) < 5))
            {
                C**k nonDoggo = target.cocks.FirstOrDefault(x => x.type != CockType.DOG && x.type != CockType.DEMON);                 //find any c**k that isn't a dog, but also isn't a demon c**k.
                if (nonDoggo != null)
                {
                    CockData oldData = nonDoggo.AsReadOnlyData();
                    int      index   = target.cocks.IndexOf(nonDoggo);
                    target.genitals.UpdateCock(index, CockType.DOG);

                    sb.Append(ConvertedOneCockToDog(target, index, oldData));


                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                    }
                }
                else
                {
                    C**k demonSpecialCase = target.cocks.FirstOrDefault(x => x.type == CockType.DEMON);
                    int  index            = demonSpecialCase.cockIndex;
                    if (demonSpecialCase != null)
                    {
                        double delta = demonSpecialCase.IncreaseThickness(2);

                        if (delta != 0)
                        {
                            sb.Append(CouldntConvertDemonCockThickenedInstead(target, index, delta));
                            if (--remainingChanges <= 0)
                            {
                                return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                            }
                        }
                    }
                }
            }

            //update cum. now, if it reaches the cap, it simply uses the new flat amount adder (up to a certain point). it does not use the messy o****m perk
            //anymore because i've tried to remove calls to random perks because it's not very future-proof - what happens when a new perk is added that has a
            //similar effect? do we add that check literally everywhere too? (of course, if a perk is unique enough that nothing else will act in the same way,
            //that's fine. i dunno, it's difficult to try and clean everything up without being able to predict the future, which is admittedly impossible)
            if (target.hasCock && (target.genitals.cumMultiplier < 1.5 || target.genitals.additionalCum < 500) && Utils.RandBool())
            {
                double delta;
                bool   wasMultiplier;
                if (target.genitals.cumMultiplier < 1.5)
                {
                    delta         = target.genitals.IncreaseCumMultiplier(0.05 * crit);
                    wasMultiplier = true;
                }
                else
                {
                    delta         = target.genitals.AddFlatCumAmount(50);
                    wasMultiplier = false;
                }
                sb.Append(AddedCumText(target, delta, wasMultiplier));
            }

            if (target.hasCock && modifiers.HasFlag(CanineModifiers.LARGE))
            {
                C**k     smallest = target.genitals.ShortestCock();
                CockData oldData  = smallest.AsReadOnlyData();

                smallest.IncreaseLength(Utils.Rand(4) + 3);
                if (smallest.girth < 1)
                {
                    double delta = 1 - smallest.girth;
                    smallest.IncreaseThickness(delta);
                }
                sb.Append(GrewSmallestCockText(target, smallest.cockIndex, oldData));
            }

            //do female changes.

            //if we have a vag and > flat breasts.
            if (target.hasVagina && target.genitals.BiggestCupSize() > CupSize.FLAT)
            {
                byte breastCount = (byte)target.breasts.Count;
                if (breastCount < 3)
                {
                    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, changeLimit - 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));
                    }
                }
            }

            //Go into heat
            if (EnterHeat(target, out bool increased))
            {
                sb.Append(EnterOrIncreaseHeatText(target, increased));

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

            //doggo fantasies
            if (target.DogScore() > 3 && Utils.Rand(4) == 0)
            {
                sb.Append(DoggoFantasyText(target));
                target.IncreaseLust(5 + (target.libidoTrue / 20));

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

            //doggo tfs.

            if (!target.eyes.isDefault && Utils.Rand(5) == 0)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();

                target.RestoreEyes();

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

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

            if (modifiers.HasFlag(CanineModifiers.BLACK) && !target.body.mainEpidermis.fur.IsIdenticalTo(HairFurColors.BLACK))
            {
                //for now, we're ignoring underbody, apparently.
                if (target.body.type != BodyType.SIMPLE_FUR)
                //if (target.body.mainEpidermis.currentType != EpidermisType.FUR)
                {
                    BodyData oldBodyData = target.body.AsReadOnlyData();

                    target.UpdateBody(BodyType.SIMPLE_FUR, new FurColor(HairFurColors.BLACK), FurTexture.THICK);

                    sb.Append(ChangedBodyTypeText(target, oldBodyData));
                }
                else
                {
                    ReadOnlyFurColor oldFur = target.body.mainEpidermis.fur.AsReadOnly();
                    target.body.ChangeMainFur(new FurColor(HairFurColors.MIDNIGHT_BLACK), FurTexture.THICK);
                    sb.Append(ChangedFurText(target, oldFur));
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                }
            }
            //again, we're ignoring underbody for now, idk.
            else if (target.lowerBody.type == LowerBodyType.DOG && target.tail.type == TailType.DOG &&
                     //target.body.mainEpidermis.currentType != EpidermisType.FUR
                     target.body.type != BodyType.SIMPLE_FUR &&
                     Utils.Rand(4) == 0)
            {
                BodyData oldBodyData = target.body.AsReadOnlyData();

                FurColor oldFur = target.body.mainEpidermis.fur;

                target.UpdateBody(BodyType.SIMPLE_FUR, Utils.RandomChoice(Species.DOG.availableColors));

                sb.Append(ChangedBodyTypeText(target, oldBodyData));

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

            if (target.lowerBody.type != LowerBodyType.DOG && target.tail.type == TailType.DOG && target.ears.type == EarType.DOG && Utils.Rand(3) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();

                target.UpdateLowerBody(LowerBodyType.DOG);
                sb.Append(ChangedLowerBodyText(target, oldData));

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

            if (target.ears.type != EarType.DOG && target.tail.type == TailType.DOG && Utils.RandBool())
            {
                EarData oldData = target.ears.AsReadOnlyData();

                target.UpdateEars(EarType.DOG);

                sb.Append(ChangedEarsText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                }
            }

            if (target.tail.type != TailType.DOG && Utils.Rand(3) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();

                target.UpdateTail(TailType.DOG);

                sb.Append(ChangedTailText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                }
            }

            if (target.arms.type != ArmType.DOG && target.body.isFurry && target.tail.type == TailType.DOG &&
                target.lowerBody.type == LowerBodyType.DOG && Utils.Rand(4) == 0)
            {
                ArmData oldArmData = target.arms.AsReadOnlyData();

                target.UpdateArms(ArmType.DOG);

                sb.Append(ChangedArmsText(target, oldArmData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                }
            }

            if (!target.gills.isDefault && Utils.Rand(4) == 0)
            {
                GillData oldGillData = target.gills.AsReadOnlyData();

                target.RestoreGills();

                sb.Append(RemovedGillsText(target, oldGillData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
                }
            }

            if (target.body.isFurry && Utils.Rand(3) == 0)
            {
                target.DeltaCreatureStats(tou: 4, sens: -3);


                sb.Append(FallbackToughenUpText(target));

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

            if (target is CombatCreature cc2 && remainingChanges == changeLimit)
            {
                cc2.AddHP(20);
                sb.Append(NothingHappenedGainHpText(target));
                target.IncreaseLust(3);
            }

            return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges));
        }
Esempio n. 7
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 });
            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);

            //Corruption reduction
            if (isPure)
            {             //Special honey will also reduce corruption, but uses different text and is handled separately
                target.ChangeCorruption(-(1 + (target.corruptionTrue / 20)));
                //Libido Reduction
                if (target.corruption > 0 && Utils.Rand(3) < 2 && target.relativeLibido > 40)
                {
                    target.DeltaCreatureStats(lib: -3, lus: -20);
                }
            }
            //Intelligence Boost
            if (Utils.Rand(2) == 0 && target.relativeIntelligence < 80)
            {
                target.IncreaseIntelligence(0.1 * (80 - target.relativeIntelligence));
            }
            //bee item corollary:
            if (target.hair.type == HairType.ANEMONE && Utils.Rand(2) == 0)
            {
                HairData oldData = target.hair.AsReadOnlyData();
                target.RestoreHair();
                sb.Append(RestoredHairText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //Sexual Stuff
            //No idears
            //Appearance Stuff
            //Hair Color
            if (target.hair.hairColor != HairFurColors.BLACK && target.hair.length > 10 && Utils.Rand(5) == 0)
            {
                if (Utils.Rand(9) == 0)
                {
                    target.hair.SetBothHairColors(HairFurColors.BLACK, HairFurColors.YELLOW);
                }
                else
                {
                    target.hair.SetHairColor(HairFurColors.BLACK);
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Hair Length
            if (target.hair.length < 25 && target.hair.type.canLengthen && Utils.Rand(3) == 0)
            {
                target.hair.GrowHair(Utils.Rand(4) + 1);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Remove extra breast rows
            if (target.breasts.Count > 2 && Utils.Rand(3) == 0 && !hyperHappy)
            {
                target.genitals.RemoveBreastRows();
            }
            //Antennae
            if (target.antennae.type == AntennaeType.NONE && target.horns.numHorns == 0 && Utils.Rand(3) == 0)
            {
                AntennaeData oldData = target.antennae.AsReadOnlyData();
                target.UpdateAntennae(AntennaeType.BEE);
                sb.Append(UpdateAntennaeText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Horns
            if (!target.horns.isDefault && 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));
                }
            }
            //Bee Legs
            if (target.lowerBody.type != LowerBodyType.BEE && Utils.Rand(4) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                target.UpdateLowerBody(LowerBodyType.BEE);
                sb.Append(UpdateLowerBodyText(target, oldData));

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

                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));
                }
            }
            //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));
                }
            }
            //Lose reptile oviposition!
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                target.womb.ClearOviposition();
                sb.Append(ClearOvipositionText(target));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Gain bee ovipositor!
            if (target.tail.type == TailType.BEE_STINGER && !target.tail.hasOvipositor && Utils.Rand(2) == 0)
            {
                target.tail.GrantOvipositor();
            }

            //Bee butt - 66% lower chance if already has a tail
            if (target.tail.type != TailType.BEE_STINGER && (target.tail.type == TailType.NONE || Utils.Rand(3) < 2) && Utils.Rand(4) == 0)
            {
                target.UpdateTail(TailType.BEE_STINGER);
                target.tail.UpdateResources((short)(10 - target.tail.resources), (short)(2 - target.tail.regenRate));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Venom Increase
            if (target.tail.type == TailType.BEE_STINGER && target.tail.regenRate < 15 && Utils.Rand(2) == 0)
            {
                short additionalRegen = 1;
                if (target.tail.regenRate < 5)
                {
                    additionalRegen = 3;
                }
                else if (target.tail.regenRate < 10)
                {
                    additionalRegen = 2;
                }

                target.tail.UpdateResources(50, additionalRegen);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Wings
            //Grow bigger bee wings!
            if (target.wings.type == WingType.BEE_LIKE && !target.wings.isLarge && Utils.Rand(4) == 0)
            {
                target.wings.GrowLarge();

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

            //Grow new bee wings if target has none.
            if (target.wings.type == WingType.NONE && Utils.Rand(4) == 0)
            {
                if (target.back.type == BackType.SHARK_FIN)
                {
                    target.RestoreBack();
                }
                WingData oldData = target.wings.AsReadOnlyData();
                target.UpdateWings(WingType.BEE_LIKE);
                sb.Append(UpdateWingsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Melt demon wings!
            if (target.wings.type == WingType.BAT_LIKE)
            {
                WingData oldData = target.wings.AsReadOnlyData();
                target.RestoreWings();
                sb.Append(RestoredWingsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove 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));
                }
            }

            //All the speical honey effects occur after any normal bee transformations (if the target wasn't a full bee morph)
            if (isSpecial)
            {
                //if no c**k: grow one.
                if (!target.hasCock)
                {
                    target.genitals.AddCock(CockType.defaultValue, Utils.Rand(3) + 8, 2);
                    target.HaveGenericCockOrgasm(0, false, true);
                    target.ChangeSensitivity(10);
                }
                //if multiple cocks, remove the largest c**k by area. combine its length/girth into the first c**k.
                else if (target.cocks.Count > 1)
                {
                    //find the biggest c**k that isn't the first one.
                    C**k   biggest = target.cocks.Skip(1).MaxItem(x => x.area);
                    double delta   = (double)(5 * Math.Sqrt(0.2 * biggest.area));

                    target.cocks[0].DeltaLengthAndGirth(delta, delta);
                    target.HaveGenericCockOrgasm(0, false, true);
                }
                //one c**k. grow it to 100 area total or larger
                else if (target.cocks[0].area < 100)
                {
                    target.cocks[0].DeltaLengthAndGirth(Utils.Rand(3) + 4, 0.1 * Utils.Rand(5) + 0.5);
                }
                //
                else
                {
                    double baseLengthChange;
                    double baseGirthChange;
                    if (target.cocks[0].type != CockType.BEE && Species.CurrentSpecies(target) == Species.BEE)
                    {
                        target.genitals.UpdateCock(0, CockType.BEE);
                        target.ChangeSensitivity(15);

                        baseLengthChange = 5;
                        baseGirthChange  = 1;
                    }
                    else
                    {
                        baseLengthChange = 0.1 * Utils.Rand(10) + 1;
                        baseGirthChange  = 0.1 * Utils.Rand(2) + 1;
                    }

                    double mult;
                    C**k   c**k = target.cocks[0];
                    if (c**k.area >= 400)
                    {
                        mult = 0;                         //C**k stops growing at that point.
                    }
                    else if (c**k.area >= 300)
                    {
                        mult = 0.1;
                    }
                    if (c**k.area > 100)
                    {
                        int offset = (((int)c**k.area) - 100) / 40;
                        mult = 1 - 0.2 * offset;
                    }
                    else
                    {
                        mult = 1;
                    }

                    double deltaLength = (double)(mult * baseLengthChange);
                    double deltaGirth  = (double)(mult * baseGirthChange);

                    target.cocks[0].DeltaLengthAndGirth(deltaLength, deltaGirth);
                }


                if (target.corruption >= 5)
                {
                    double corrLoss = Math.Min(0.1 * target.corruptionTrue + 5, target.corruptionTrue);
                    target.DeltaCreatureStats(corr: -corrLoss, lib: corrLoss);                     //Lose corruption and gains that much libido
                }
                else
                {
                    target.ChangeLibido(5);
                }

                if (target.femininity >= 60 || target.femininity <= 40)
                {
                    if (target.femininity >= 60)
                    {
                        target.femininity.IncreaseMasculinity(3);
                    }
                    else
                    {
                        target.femininity.IncreaseFemininity(3);
                    }
                }
                target.ChangeLust(0.2 * target.libidoTrue + 5);
            }


            //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 virtual string UpdateGillsText(Creature target, GillData oldData)
 {
     return(target.gills.TransformFromText(oldData));
 }
        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, 3 });
            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);

            //possible use effects:
            //- corruption increases by 1 up to low threshold (~20)
            if (Utils.Rand(3) == 0 && target.corruption < 20)
            {
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }

                target.ChangeCorruption(1);
            }

            //- toughess up, sensitivity down
            if (Utils.Rand(3) == 0 && target.relativeToughness < 50)
            {
                target.DeltaCreatureStats(tou: 1, sens: -1);
            }
            //- speed down
            if (Utils.Rand(3) == 0 && target.relativeSpeed > 40)
            {
                target.ChangeSpeed(-1);
            }
            //-always increases lust by a function of sensitivity

            //"The tingling of the tentacle

            //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));
                }
            }

            //physical changes:
            //- may randomly remove bee abdomen, if present; always checks and does so when any changes to hair might happen
            if (Utils.Rand(4) == 0 && target.tail.type == TailType.BEE_STINGER)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.NONE);
                sb.Append(UpdateTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-may randomly remove bee wings:
            if (Utils.Rand(4) == 0 && target.wings.type == WingType.BEE_LIKE)
            {
                WingData oldData = target.wings.AsReadOnlyData();
                target.RestoreWings();
                sb.Append(RestoredWingsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-hair morphs to anemone tentacles, retains color, hair shrinks back to med-short('shaggy') and stops growing, lengthening treatments don't work and goblins won't cut it, but more anemone items can lengthen it one level at a time
            if (target.gills.type == GillType.ANEMONE && target.hair.type != HairType.ANEMONE && Utils.Rand(5) == 0)
            {
                HairData oldData = target.hair.AsReadOnlyData();
                target.UpdateHair(HairType.ANEMONE, newStyle: HairStyle.WAVY);
                sb.Append(UpdateHairText(target, oldData));

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

            //-feathery gills sprout from chest and drape sensually over nipples (cumulative swimming power boost with fin, if swimming is implemented)
            if (Utils.Rand(5) == 0 && target.gills.type != GillType.ANEMONE && target.body.primarySkin.tone == Tones.BLUE_BLACK)
            {
                GillData oldData = target.gills.AsReadOnlyData();
                target.UpdateGills(GillType.ANEMONE);
                sb.Append(UpdateGillsText(target, oldData));

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

            //-[aphotic] skin tone (blue-black)
            if (Utils.Rand(5) == 0 && target.body.primarySkin.tone != Tones.BLUE_BLACK)
            {
                target.body.ChangeAllSkin(Tones.BLUE_BLACK);

                //kGAMECLASS.rathazul.addMixologyXP(20);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-eat more, grow more 'hair':
            if (target.hair.type == HairType.ANEMONE && target.hair.length < 36 && Utils.Rand(2) == 0)
            {
                double temp = 5 + Utils.Rand(3);

                //grow it, and force growth. ignore the natural growth because that's how anemone hair works - it lengthens with tf item.
                target.hair.GrowHair(temp, true);

                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));
        }
Esempio n. 10
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 }, 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.
            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);


            //STATS
            if (target.relativeSensitivity > 25 && Utils.Rand(3) < 2)
            {
                target.ChangeSensitivity((-1 - Utils.Rand(3)));
            }
            //Increase strength 1-2 points (Up to 50) (60 for tiger)
            if (((target.relativeStrength < 60 && isTigerShark) || target.relativeStrength < 50) && Utils.Rand(3) == 0)
            {
                target.ChangeStrength(1 + Utils.Rand(2));
            }
            //Increase Speed 1-3 points (Up to 75) (100 for tigers)
            if (((target.relativeSpeed < 100 && isTigerShark) || target.relativeSpeed < 75) && Utils.Rand(3) == 0)
            {
                target.ChangeSpeed(1 + Utils.Rand(3));
            }
            //Reduce sensitivity 1-3 Points (Down to 25 points)

            //Increase Libido 2-4 points (Up to 75 points) (100 for tigers)
            if (((target.relativeLibido < 100 && isTigerShark) || target.relativeLibido < 75) && Utils.Rand(3) == 0)
            {
                target.ChangeLibido((1 + Utils.Rand(3)));
            }
            //Decrease intellect 1-3 points (Down to 40 points)
            if (target.relativeIntelligence > 40 && Utils.Rand(3) == 0)
            {
                target.ChangeIntelligence(-(1 + Utils.Rand(3)));
            }
            //Smexual stuff!
            //-TIGGERSHARK ONLY: Grow a c**t (guaranteed if no gender)
            if (isTigerShark && (target.gender == 0 || (!target.hasVagina && Utils.Rand(3) == 0)))
            {
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
                //(balls)
                //(dick)
                //(neither)
                target.genitals.AddVagina();
                target.ChangeSensitivity(10);
            }
            //WANG GROWTH - TIGGERSHARK ONLY
            if (isTigerShark && (!target.hasCock) && Utils.Rand(3) == 0)
            {
                //Genderless:
                //Female:
                if (target.balls.count == 0)
                {
                    target.balls.GrowBalls();
                }
                target.genitals.AddCock(CockType.defaultValue, 7, 1.4);

                target.DeltaCreatureStats(lib: 4, sens: 5, lus: 20);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //(Requires the target having two testicles)
            if (isTigerShark && (target.balls.count == 0 || target.balls.count == 2) && target.hasCock && Utils.Rand(3) == 0)
            {
                if (target.balls.count == 2)
                {
                    target.balls.AddBalls(2);
                }
                else if (target.balls.count == 0)
                {
                    target.balls.GrowBalls();
                }
                target.DeltaCreatureStats(lib: 2, sens: 3, lus: 10);
                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();
            }
            //Neck restore
            if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0)
            {
                target.RestoreNeck();
            }
            //Rear body restore
            if (!target.back.isDefault && Utils.Rand(5) == 0)
            {
                target.RestoreBack();
            }
            //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));
                }
            }
            //Transformations:
            //Mouth TF
            if (target.face.type != FaceType.SHARK && Utils.Rand(3) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.SHARK);
                sb.Append(UpdateFaceText(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));
                }
            }
            //Tail TF
            if (target.tail.type != TailType.SHARK && Utils.Rand(3) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                target.UpdateTail(TailType.SHARK);
                sb.Append(UpdateTailText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Gills TF
            if (target.gills.type != GillType.FISH && target.tail.type == TailType.SHARK && target.face.type == FaceType.SHARK && Utils.Rand(3) == 0)
            {
                GillData oldData = target.gills.AsReadOnlyData();
                target.UpdateGills(GillType.FISH);
                sb.Append(UpdateGillsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Hair
            if (target.hair.hairColor != HairFurColors.SILVER && Utils.Rand(4) == 0)
            {
                target.hair.SetHairColor(HairFurColors.SILVER);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Skin
            //MOD NOTE: F**K. A TWO TONED SKIN TONE. F**K F**K F**K.
            //MOD NOTE (continued): tigershark skin tone is hacked in. it's a hack. it's an ugly as f**k hack and i need a shower now. but it'll work. i really, really, really
            //dont want to add a new body type for tigersharks, and i am not adding a fur color equivalent for tones for literally just tigersharks.
            if (((target.body.primarySkin.tone != Tones.GRAY && target.body.primarySkin.tone != Tones.TIGERSHARK) || target.body.type != BodyType.HUMANOID) && Utils.Rand(7) == 0)
            {
                Tones       targetTone    = isTigerShark ? Tones.TIGERSHARK : Tones.GRAY;
                SkinTexture targetTexture = SkinTexture.ROUGH;
                //getGame().rathazul.addMixologyXP(20);

                BodyData oldData = target.body.AsReadOnlyData();
                target.UpdateBody(BodyType.HUMANOID, targetTone, targetTexture);
                sb.Append(UpdateBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //FINZ R WINGS
            if ((target.wings.type != WingType.NONE || target.back.type != BackType.SHARK_FIN) && Utils.Rand(3) == 0)
            {
                target.UpdateBack(BackType.SHARK_FIN);
                WingData oldData = target.wings.AsReadOnlyData();
                target.RestoreWings();
                sb.Append(RestoredWingsText(target, oldData));

                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));
        }
Esempio n. 11
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, 3, 3 }, 1, 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.
            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);


            //STATS
            //Strength h
            if (Utils.Rand(3) == 0)
            {
                //weaker characters gain more
                if (target.relativeStrength <= 50)
                {
                    //very weak targets gain more
                    if (target.relativeStrength <= 20)
                    {
                        target.ChangeStrength(3);
                    }
                    else
                    {
                        target.ChangeStrength(2);
                    }
                }
                //stronger characters gain less
                else
                {
                    //small growth if over 75
                    if (target.relativeStrength >= 75)
                    {
                        target.ChangeStrength(.5);
                    }
                    //faster from 50-75
                    else
                    {
                        target.ChangeStrength(1);
                    }
                }
                //Chance of speed drop
                if (Utils.Rand(2) == 0 && target.relativeStrength > 50)
                {
                    target.ChangeSpeed(-1);
                }
            }
            //Toughness (chance of - sensitivity)
            if (Utils.Rand(3) == 0)
            {
                //weaker characters gain more
                if (target.relativeToughness <= 50)
                {
                    //very weak targets gain more
                    if (target.relativeToughness <= 20)
                    {
                        target.ChangeToughness(3);
                    }
                    else
                    {
                        target.ChangeToughness(2);
                    }
                }
                //stronger characters gain less
                else
                {
                    //small growth if over 75
                    if (target.relativeToughness >= 75)
                    {
                        target.ChangeToughness(.5);
                    }
                    //faster from 50-75
                    else
                    {
                        target.ChangeToughness(1);
                    }
                }
                //chance of less sensitivity
                if (Utils.Rand(2) == 0 && target.relativeSensitivity > 10)
                {
                    if (target.relativeToughness > 75)
                    {
                        target.ChangeSensitivity(-3);
                    }
                    if (target.relativeToughness <= 75 && target.relativeToughness > 50)
                    {
                        target.ChangeSensitivity(-2);
                    }
                    if (target.relativeToughness <= 50)
                    {
                        target.ChangeSensitivity(-3);
                    }
                }
            }
            //SEXUAL
            //Boosts ball size MORE than equinum :D:D:D:D:D:D:
            if (Utils.Rand(2) == 0 && target.balls.size <= 5 && target.genitals.HasAnyCocksOfType(CockType.HORSE))
            {
                //Chance of ball growth if not 3" yet
                if (target.balls.count == 0)
                {
                    target.balls.GrowBalls();

                    target.DeltaCreatureStats(lib: 2, lus: 5);
                }
                else
                {
                    target.balls.EnlargeBalls(1);
                    target.DeltaCreatureStats(lib: 1, lus: 3);
                }
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Neck restore
            if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0)
            {
                target.RestoreNeck();
            }
            //Rear body restore
            if (!target.back.isDefault && Utils.Rand(5) == 0)
            {
                target.RestoreBack();
            }
            //Ovi perk loss
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                target.womb.ClearOviposition();
                //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges);
            }
            //Restore arms to become human arms again
            if (Utils.Rand(4) == 0)
            {
                target.RestoreArms();
            }
            //+hooves
            if (target.lowerBody.type != LowerBodyType.HOOVED)
            {
                if (Utils.Rand(3) == 0)
                {
                    //Catch-all
                    target.UpdateLowerBody(LowerBodyType.HOOVED);
                    target.ChangeSpeed(1);

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            if (!hyperHappy)
            {
                //Kills v****a size (and eventually the whole v****a)
                if (target.vaginas.Count > 0)
                {
                    //behavior not defined for multi vag. i'
                    VaginalLooseness minLooseness = target.genitals.minVaginalLooseness;
                    //
                    if (target.genitals.LargestVaginalLooseness() > minLooseness)
                    {
                        //tighten that bitch up!
                        foreach (V****a vag in target.vaginas)
                        {
                            if (vag.looseness > minLooseness)
                            {
                                vag.DecreaseLooseness();
                            }
                        }
                    }
                    else if (target.vaginas.Count > 1)
                    {
                        target.RemoveExtraVaginas();
                    }
                    else
                    {
                        if (target.cocks.Count == 0)
                        {
                            target.genitals.AddCock(CockType.HORSE, target.vaginas[0].c**t.length + 2, 1);
                        }

                        //Goodbye womanhood!
                        target.genitals.RemoveAllVaginas();
                    }
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                //-Remove extra breast rows
                if (target.breasts.Count > 1 && Utils.Rand(3) == 0)
                {
                    target.RemoveExtraBreastRows();
                }

                //Shrink boobages till they are normal
                else if (Utils.Rand(2) == 0 && target.breasts.Count > 0)
                {
                    //Single row
                    CupSize smallestCup = EnumHelper.Max(target.genitals.smallestPossibleCupSize, CupSize.B);

                    bool changedAny = false;

                    foreach (Breasts row in target.breasts.Where(x => x.cupSize > smallestCup))
                    {
                        changedAny |= row.ShrinkBreasts() > 0;
                        if (row.cupSize > CupSize.E_BIG)
                        {
                            changedAny |= row.ShrinkBreasts() > 0;
                        }
                    }

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


            //Boosts c**k size up to 36"x5".
            //MOD: Used linq: find the first c**k that matches the requirement, or if no cocks exist, return null.
            C**k firstCockToGrow = target.cocks.FirstOrDefault(x => x.type == CockType.HORSE && x.length < 36 || x.girth < 5);

            //MOD: thus, we can simply place the null check here to see if we actually grow it.
            if (!(firstCockToGrow is null) && Utils.Rand(2) == 0)
            {
                if (firstCockToGrow.girth < 5 && firstCockToGrow.length < 36)
                {
                    firstCockToGrow.IncreaseThickness(1);
                    firstCockToGrow.IncreaseLength(2 + Utils.Rand(8));
                }
                else if (firstCockToGrow.length < 36)
                {
                    firstCockToGrow.IncreaseLength(2 + Utils.Rand(8));
                }
                else
                {
                    firstCockToGrow.IncreaseThickness(1);
                }

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

            //Morph dick to horsediiiiick
            C**k firstNonHorse = target.cocks.FirstOrDefault(x => x.type != CockType.HORSE);

            if (!(firstNonHorse is null) && Utils.Rand(2) == 0)
            {
                //Text for humandicks or others
                //Text for dogdicks
                target.genitals.UpdateCockWithLength(firstNonHorse, CockType.HORSE, firstNonHorse.length + 4);

                target.DeltaCreatureStats(lib: 5, sens: 4, lus: 35);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //Males go into rut
            if (Utils.Rand(4) == 0)
            {
                if (target.GoIntoRut())
                {
                    remainingChanges--;
                    if (remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }

            //Anti-masturbation status
            if (Utils.Rand(4) == 0 && !target.HasTimedEffect <Dysfunction>() && target.gender != Gender.GENDERLESS)
            {
                target.AddTimedEffect <Dysfunction>();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Appearance shit:
            //Tail, Ears, Hooves, Horns, Height (no prereq), Face
            //+height up to 9 foot
            //Mod note: rand(1.7) seriously? rand only works with ints, guys. that's straight from vanilla. though to be fair, it's not caught there b/c actionscript is shit.
            if (Utils.Rand(17) < 10 && target.build.heightInInches < 108)
            {
                byte temp = (byte)(Utils.Rand(5) + 3);
                //Slow rate of growth near ceiling
                if (target.build.heightInInches > 90)
                {
                    temp /= 2;
                }
                //Never 0
                if (temp == 0)
                {
                    temp = 1;
                }
                //Flavor texts. Flavored like 1950's cigarettes. Yum.
                target.build.IncreaseHeight(temp);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Face change, requires Ears + Height + Hooves
            if (target.ears.type == EarType.COW && target.lowerBody.type == LowerBodyType.HOOVED && target.build.heightInInches >= 90 &&
                target.face.type != FaceType.COW_MINOTAUR && Utils.Rand(3) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.COW_MINOTAUR);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //+mino horns require ears/tail
            if (Utils.Rand(3) == 0 && target.ears.type == EarType.COW && target.tail.type == TailType.COW)
            {
                //New horns or expanding mino horns
                if (target.horns.type == HornType.BOVINE || target.horns.type == HornType.NONE)
                {
                    //Get bigger if target has horns
                    if (target.horns.type == HornType.BOVINE)
                    {
                        //Fems horns don't get bigger.
                        if (target.vaginas.Count > 0)
                        {
                            if (target.horns.significantHornSize > 4)
                            {
                                target.genitals.AddPentUpTime(200);
                                target.ChangeLust(20);
                            }
                            else
                            {
                                target.horns.StrengthenTransform(2);
                            }
                            if (--remainingChanges <= 0)
                            {
                                return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                            }
                        }
                        //Males horns get 'uge.
                        else
                        {
                            target.horns.StrengthenTransform(2);

                            //boys get a cum refill sometimes
                            if (Utils.Rand(2) == 0)
                            {
                                target.genitals.AddPentUpTime(200);
                                target.ChangeLust(20);
                            }
                            if (--remainingChanges <= 0)
                            {
                                return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                            }
                        }
                    }
                    //If no horns yet..
                    else
                    {
                        HornData oldData = target.horns.AsReadOnlyData();
                        target.UpdateHorns(HornType.BOVINE);
                        sb.Append(UpdateHornsText(target, oldData));

                        if (--remainingChanges <= 0)
                        {
                            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                        }
                    }
                }
                //Not mino horns, change to cow-horns
                else
                {
                    HornData oldData = target.horns.AsReadOnlyData();
                    target.UpdateHorns(HornType.BOVINE);
                    sb.Append(UpdateHornsText(target, oldData));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //+cow ears	- requires tail
            if (target.ears.type != EarType.COW && target.tail.type == TailType.COW && Utils.Rand(2) == 0)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.COW);
                sb.Append(UpdateEarsText(target, oldData));

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            // Remove 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));
                }
            }

            if (Utils.Rand(4) == 0 && target.ass.wetness > target.ass.minWetness)
            {
                target.ass.DecreaseWetness();
                AnalLooseness targetLooseness = EnumHelper.Max(AnalLooseness.LOOSE, target.ass.minLooseness);
                if (target.ass.looseness > targetLooseness)
                {
                    target.ass.DecreaseLooseness();
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Give you that mino build!
            //Default
            if (remainingChanges == changeCount)
            {
                if (target.balls.count > 0)
                {
                    target.genitals.AddPentUpTime(200);
                }
                (target as CombatCreature)?.AddHP(50);
                target.ChangeLust(50);
            }

            //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));
        }
        private string DoTransformationCommon(Creature target, bool currentlyInCombat, 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;

            bool statsChanged = false;

            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, currentlyInCombat));

            //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.

            if (target.relativeSpeed < 70 && Utils.Rand(2) == 0)
            {
                target.IncreaseSpeed(2 - (target.relativeSpeed / 50));
                statsChanged = true;
            }

            //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);



            //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.type != BackType.SHARK_FIN && !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));
                }
            }

            //Removes wings and shark fin
            if ((target.wings.type != WingType.NONE || target.back.type == BackType.SHARK_FIN) && Utils.Rand(3) == 0)
            {
                if (target.back.type == BackType.SHARK_FIN)
                {
                    target.RestoreBack();
                }
                WingData oldData = target.wings.AsReadOnlyData();
                target.RestoreWings();
                sb.Append(RestoredWingsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //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));
                }
            }

            //Sexual changes

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

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

            //adjust cocks until the target has 2 reptilian cocks. only occurs for males or herms, or genderless if the hyper happy flag is off and a 1/2 roll procs true.
            if ((target.gender.HasFlag(Gender.MALE) || (target.gender == Gender.GENDERLESS && !HyperHappySettings.isEnabled && Utils.Rand(2) == 0)) &&
                (target.cocks.Count != 2 || target.genitals.CountCocksOfType(CockType.LIZARD) != 2) && Utils.Rand(3) == 0)
            {
                int lizardCocks = target.genitals.CountCocksOfType(CockType.LIZARD);
                //grant up to 2 lizard cocks.
                if (lizardCocks < 2)
                {
                    //if we have two or less cocks, convert all that we do have to lizard.
                    if (target.cocks.Count < 3)
                    {
                        foreach (C**k c**k in target.cocks)
                        {
                            target.genitals.UpdateCock(c**k, CockType.LIZARD);
                        }

                        //then, add c**k(s) until we have 2 lizard cocks
                        while (target.cocks.Count < 2)
                        {
                            target.AddCock(CockType.LIZARD);
                        }
                    }
                    else if (lizardCocks == 1)
                    {
                        int toChange = target.cocks[0].type == CockType.LIZARD ? 0 : 1;
                        target.genitals.UpdateCock(toChange, CockType.LIZARD);
                    }
                }
                //otherwise, we already have 2 lizard cocks (or more). we're only going to keep 2 lizard cocks, so we need to remove extra ones.
                else
                {
                    //any non-lizard c**k gets removed first.
                    C**k toRemove = target.cocks.FirstOrDefault(x => x.type != CockType.LIZARD);
                    //if we can't find one, it means we only have lizard cocks. remove the last one.
                    if (toRemove is null)
                    {
                        toRemove = target.cocks[target.cocks.Count - 1];
                    }
                }
            }

            //9c) II The tongue (sensitivity bonus, stored as a perk?)
            if (changeCount == remainingChanges && Utils.Rand(3) == 0)
            {
                TongueData oldData = target.tongue.AsReadOnlyData();
                target.UpdateTongue(TongueType.SNAKE);
                sb.Append(UpdateTongueText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //9c) III The fangs
            if (changeCount == remainingChanges && target.tongue.type == TongueType.SNAKE && target.face.type != FaceType.SNAKE && Utils.Rand(3) == 0)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.SNAKE);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //9c) I The tail ( http://tvtropes.org/pmwiki/pmwiki.php/Main/TransformationIsAFreeAction ) (Shouldn't we try to avert this? -Ace)
            //Should the enemy "kill" you during the transformation, it skips the scene and immediately goes to tthe rape scene.
            //(Now that I'm thinking about it, we should add some sort of appendix where the target realizes how much he's/she's changed. -Ace)
            //MOD NOTE: this also silently grants a reptilian body. there's also a check for this if you somehow lose the reptilian body and have a naga lower body later.
            if (changeCount == remainingChanges && target.face.type == FaceType.SNAKE && target.lowerBody.type != LowerBodyType.NAGA && Utils.Rand(4) == 0)
            {
                target.UpdateLowerBody(LowerBodyType.NAGA);

                // Naga lower body plus a tail may look awkward, so silently discard it (Stadler76)
                target.RestoreTail();
                //convert body to match lower body.
                if (target.body.type != BodyType.REPTILE)
                {
                    target.UpdateBody(BodyType.REPTILE, Tones.GREEN, Tones.LIGHT_GREEN);
                }

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

            if (target.body.type != BodyType.REPTILE && target.lowerBody.type == LowerBodyType.NAGA)
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.UpdateBody(BodyType.REPTILE, Tones.GREEN, Tones.LIGHT_GREEN);
                sb.Append(UpdateBodyText(target, oldData));

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

            // Remove 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));
                }
            }

            //Default change - blah
            if (changeCount == remainingChanges && !statsChanged)
            {
            }

            //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));
        }
        //trying to think of a good way to do nipple piercings for goblins when you go full goblin without being too intrusive.
        //can't. oh well. but hey, at least it removes inverted nipples without requiring piercings, i guess

        //fun fact: this is (as of writing) the only way to restore nipples back to normal after you've made them fuckable. it's now possible to remove fuckable nipples just
        //by shrinking them down (they become inverted), but they're still not 'normal.' this will convert inverted nipples back to normal. so, if you want to remove fuckable nipples,
        //shrink them down, then proc this a few times. It's also possible that nipple piercings remove inverted nipples over time, but as of writing, that's not (currently) implemented
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

            //rolls of 1/2, 1/3, 1/4, and 1/5 for extra changes. i don't feel like figuring out the odds, so here's the simple split:
            //the most changes (+4) has an odds of 1/120, and the least changes (+0) have an odds of 1/5. the remaining change counts vary, totaling 19/24.
            int changeCount      = GenerateChangeCount(target, new int[] { 2, 3, 4, 5 });
            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.

            target.IncreaseLust(15);
            //Stronger
            if (target.relativeStrength > 50)
            {
                if (target.relativeStrength > 90)
                {
                    target.DecreaseStrengthByPercent(3);
                }
                else if (target.relativeStrength > 70)
                {
                    target.DecreaseStrengthByPercent(2);
                }
                else
                {
                    target.DecreaseStrengthByPercent(1);
                }
            }
            ///Less tough
            if (target.relativeToughness > 50)
            {
                if (target.relativeToughness > 90)
                {
                    target.DecreaseToughnessByPercent(4);
                }
                else if (target.relativeToughness > 70)
                {
                    target.DecreaseToughnessByPercent(2);
                }
                else
                {
                    target.DecreaseToughnessByPercent(1);
                }
            }
            //Speed boost
            if (Utils.Rand(3) == 0 && target.relativeSpeed < 50)
            {
                target.IncreaseSpeed(1 + Utils.Rand(2));
            }
            //antianemone corollary:
            if (target.hair.type == HairType.ANEMONE && Utils.Rand(2) == 0)
            {
                //-insert anemone hair removal into them under whatever criteria you like, though hair removal should precede abdomen growth; here's some sample text:
                HairData oldData = target.hair.AsReadOnlyData();
                target.RestoreHair();
                sb.Append(RestoredHairText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Shrink
            if (Utils.Rand(2) == 0 && target.heightInInches > 48)
            {
                target.build.DecreaseHeight((byte)(1 + Utils.Rand(5)));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

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

            //Normal transforms (Cost 1)
            //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)
            {
                target.womb.ClearOviposition();
                sb.Append(ClearOvipositionText(target));

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //SEXYTIEMS
            //Multidick killa!
            if (target.cocks.Count > 1 && Utils.Rand(3) == 0)
            {
                target.genitals.RemoveCock();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Boost vaginal capacity without gaping
            if (Utils.Rand(3) == 0 && target.hasVagina && target.genitals.standardBonusVaginalCapacity < 40)
            {
                target.genitals.IncreaseBonusVaginalCapacity(5);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Boost fertility
            if (Utils.Rand(4) == 0 && target.fertility.totalFertility < 40 && target.hasVagina)
            {
                target.fertility.IncreaseFertility((byte)(2 + Utils.Rand(5)));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Shrink primary dick to no longer than 12 inches
            else if (target.cocks.Count == 1 && Utils.Rand(2) == 0 && !hyperHappy)
            {
                if (target.cocks[0].length > 12)
                {
                    double delta = target.cocks[0].DecreaseLength(1 + Utils.Rand(3));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //GENERAL APPEARANCE STUFF BELOW
            //REMOVAL STUFF
            //Removes wings!
            if ((target.wings.type != WingType.NONE) && Utils.Rand(4) == 0)
            {
                WingData oldData = target.wings.AsReadOnlyData();
                target.RestoreWings();
                sb.Append(RestoredWingsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //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));
                }
            }
            //Remove odd eyes
            if (Utils.Rand(5) == 0 && target.eyes.count != 2)
            {
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.RestoreEyes();
                sb.Append(RestoredEyesText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //-Remove extra breast rows
            if (target.breasts.Count > 1 && Utils.Rand(3) == 0)
            {
                target.genitals.RemoveExtraBreastRows();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Skin/fur
            if (target.body.type != BodyType.HUMANOID && Utils.Rand(4) == 0 && target.face.type == FaceType.HUMAN)
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.RestoreBody();
                sb.Append(RestoredBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //skinTone
            if (!Species.GOBLIN.availableTones.Contains(target.body.primarySkin.tone) && Utils.Rand(2) == 0)
            {
                if (Utils.Rand(10) != 0)
                {
                    target.body.ChangeMainSkin(Tones.DARK_GREEN);
                }
                else if (Utils.RandBool())
                {
                    target.body.ChangeMainSkin(Tones.PALE_YELLOW);
                }
                else
                {
                    target.body.ChangeMainSkin(Tones.GRAYISH_BLUE);
                }

                //MOD note: rathazal mixology called here - how do we want to standardize that?

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Face!
            if (target.face.type != FaceType.HUMAN && Utils.Rand(4) == 0 && target.ears.type == EarType.ELFIN)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.HUMAN);
                sb.Append(UpdateFaceText(target, oldData));

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            // Remove 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);

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

            //MOD: Added: remove inverted nipples. this by entension is able to remove fuckable nipples, assuming you shrunk them down until they became inverted.
            if (target.genitals.nippleType.IsInverted() && Utils.Rand(3) == 0)
            {
                target.genitals.SetNippleStatus(NippleStatus.NORMAL);

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

            //Debugcunt
            if (Utils.Rand(3) == 0 && target.hasVagina && target.vaginas.Any(x => !x.isDefault))
            {
                foreach (V****a vag in target.vaginas)
                {
                    if (!vag.isDefault)
                    {
                        target.genitals.RestoreVagina(vag);
                    }
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(4) == 0 && target.ass.wetness > target.ass.minWetness)
            {
                if (target.ass.wetness - target.ass.minWetness > 1)
                {
                    target.ass.DecreaseWetness(2);
                }
                else
                {
                    target.ass.DecreaseWetness();
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0)
            {
                if (Utils.Rand(2) == 0)
                {
                    target.femininity.ChangeFemininityToward(85, 3);
                }

                if (Utils.Rand(2) == 0)
                {
                    target.build.ChangeThicknessToward(20, 3);
                }

                if (Utils.Rand(2) == 0)
                {
                    target.build.ChangeMuscleToneToward(15, 5);
                }
            }

            //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;

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

            StringBuilder sb = new StringBuilder();

            //Zero Cost/Stat changes.

            //None at the moment

            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);

            // Normal TFs
            //------------
            if (Utils.Rand(4) == 0 && target.hair.type != HairType.NORMAL && target.hair.type != HairType.NO_HAIR && target.hair.type != HairType.QUILL)
            {
                //sb.Append("\n\nYour scalp feels really strange, but the sensation is brief. You feel your hair, and you immediately notice the change. <b>It would seem that your hair is normal again!</b>");
                HairData oldData = target.hair.AsReadOnlyData();
                target.UpdateHair(HairType.NORMAL);
                sb.Append(UpdateHairText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(4) == 0 && target.arms.type == ArmType.HARPY)
            {
                //sb.Append("\n\nYour arm feathers fall out completely, <b>leaving only the " + target.skinFurScales() + " underneath.</b>");
                ArmData oldData = target.arms.AsReadOnlyData();
                target.RestoreArms();
                sb.Append(RestoredArmsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Remove gills
            if (Utils.Rand(3) == 0 && target.gills.type != GillType.NONE)
            {
                GillData oldData = target.gills.AsReadOnlyData();
                target.RestoreGills();
                sb.Append(RestoredGillsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //restore spider eyes.
            if (Utils.Rand(3) == 0 && target.eyes.type == EyeType.SPIDER)
            {
                //sb.Append("\n\nYour eyes start throbbing painfully, your sight in them eventually going dark. You touch your head to inspect your eyes, only to find out that they have changed. <b>You have human eyes now!</b>");
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.UpdateEyes(EyeType.HUMAN);
                sb.Append(UpdateEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.genitals.hasQuadNipples)
            {
                //sb.Append("\n\nA tightness arises in your nipples as three out of four on each breast recede completely, the leftover nipples migrating to the middle of your breasts. <b>You are left with only one nipple on each breast.</b>");
                target.genitals.SetQuadNipples(false);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            // Main TFs
            //------------
            //Change to fur
            if (Utils.Rand(3) == 0 && !target.body.IsFurBodyType())
            {
                target.UpdateBody(BodyType.SIMPLE_FUR, new FurColor(HairFurColors.BROWN));
                //sb.Append("\n\nYou shiver, feeling a bit cold. Just as you begin to wish for something to cover up with, it seems your request is granted; <b>fur begins to grow all over your body!</b> You tug at the tufts in alarm, but they're firmly rooted and... actually pretty soft. Huh. ");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Gain Echidna ears
            if (Utils.Rand(3) == 0 && target.ears.type != EarType.ECHIDNA)
            {
                target.UpdateEars(EarType.ECHIDNA);
                //sb.Append("\n\n");
                //sb.Append(" <b>You now have echidna ears!</b>");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Gain Echidna tail
            if (Utils.Rand(3) == 0 && target.ears.type == EarType.ECHIDNA && target.tail.type != TailType.ECHIDNA)
            {
                target.UpdateTail(TailType.ECHIDNA);
                //sb.Append("\n\n");
                //sb.Append(" <b>You now have an echidna tail!</b>");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Gain Echidna legs
            if (Utils.Rand(3) == 0 && target.ears.type == EarType.ECHIDNA && target.tail.type == TailType.ECHIDNA && target.lowerBody.type != LowerBodyType.ECHIDNA)
            {
                target.UpdateLowerBody(LowerBodyType.ECHIDNA);
                //sb.Append("\n\n");
                //sb.Append(" <b>They actually look like the feet of an echidna!</b>");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Convert one existing c**k to Echidna
            if (Utils.Rand(3) == 0 && target.hasCock && target.genitals.CountCocksOfType(CockType.ECHIDNA) < target.cocks.Count)
            {
                int firstNonEchidna = target.cocks.FirstIndexOf(x => x.type != CockType.ECHIDNA);
                target.genitals.UpdateCock(firstNonEchidna, CockType.ECHIDNA);

                //sb.Append("\n\n");
                //if (target.cocks.Count == 1) //sb.Append("Your [c**k] suddenly becomes rock hard out of nowhere. You " + target.clothedOrNakedLower("pull it out from your [armor], right in the middle of the food tent, watching", "watch") + " as it begins to shift and change. It becomes pink in color, and you feel a pinch at the head as it splits to become four heads. " + (target.hasSheath() ? "" : "The transformation finishes off with a fleshy sheath forming at the base.") + " It ejaculates before going limp, retreating into your sheath.");
                //else //sb.Append("One of your penises begins to feel strange. You " + target.clothedOrNakedLower("pull the offending c**k out from your [armor], right in the middle of the food tent, watching", "watch") + " as it begins to shift and change. It becomes pink in color, and you feel a pinch at the head as it splits to become four heads. " + (target.hasSheath() ? "" : "The transformation finishes off with a fleshy sheath forming at the base.") + " It ejaculates before going limp, retreating into your sheath.");
                //sb.Append(" <b>You now have an echidna penis!</b>");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Gain Echidna tongue
            if (Utils.Rand(3) == 0 && Species.ECHIDNA.Score(target) >= 2 && target.tongue.type != TongueType.ECHIDNA)
            {
                target.UpdateTongue(TongueType.ECHIDNA);
                //sb.Append("\n\nYou feel an uncomfortable pressure in your tongue as it begins to shift and change. Within moments, you are able to behold your long, thin tongue. It has to be at least a foot long. <b>You now have an echidna tongue!</b>");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Gain quill hair
            if (Utils.Rand(4) == 0 && ((target.hair.type == HairType.NORMAL && !target.hair.isGrowing) || target.hair.type == HairType.NO_HAIR))
            {
                target.UpdateHair(HairType.QUILL);
                //sb.Append("\n\nYour scalp begins to tingle as your hair falls out in clumps, leaving you with a bald head. You aren't bald for long, though. An uncomfortable pressure racks the entirety of your scalp as hard quills begin to sprout from your hair pores. Their growth stops as they reach shoulder length. <b>You now have quills in place of your hair!</b>");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Gain Echidna face if you have the right conditions.
            if (Utils.Rand(4) == 0 && target.body.isFurry && target.ears.type == EarType.ECHIDNA && target.tail.type == TailType.ECHIDNA && target.tongue.type == TongueType.ECHIDNA)
            {
                target.UpdateFace(FaceType.ECHIDNA);
                //sb.Append("You groan loudly as the bones in your face begin to reshape and rearrange. Most notable, you feel your mouth lengthening into a long, thin snout. <b>You now have an echidna face!</b>");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            // Other Changes
            //------------
            //Hair stops growing
            if (Utils.Rand(4) == 0 && Species.ECHIDNA.Score(target) >= 2 && target.hair.isGrowing && !target.hair.growthArtificallyDisabled)
            {
                if (target.hair.StopNaturalGrowth())
                {
                    //sb.Append("\n\nYour scalp tingles oddly. In a panic, you reach up to your " + target.hairDescript() + ", but thankfully it appears unchanged.\n");
                    //sb.Append("(<b>Your hair has stopped growing.</b>)");
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }

            //Sexual changes
            if (Utils.Rand(3) == 0 && target.hasCock && target.genitals.cumMultiplierTrue < 25)
            {
                double temp = 1 + Utils.Rand(5);
                //really not a fan of this hard coded check b/c its not future proof, but it seems to be limited in scope enough i guess i can allow it.
                //Lots of cum raises cum multiplier cap to 2 instead of 1.5
                if (target.HasPerk <MessyOrgasms>())
                {
                    temp += 1 + Utils.Rand(10);
                }
                temp *= 0.1;

                target.genitals.IncreaseCumMultiplier(temp);
                //Flavor text
                //if (target.balls.count == 0) //sb.Append("\n\nYou feel a churning inside your gut as something inside you changes.");
                //else //if (target.balls.count > 0) //sb.Append("\n\nYou feel a churning in your " + target.ballsDescriptLight() + ". It quickly settles, leaving them feeling somewhat more dense.");
                //sb.Append(" A bit of milky pre dribbles from your " + target.multiCockDescriptLight() + ", pushed out by the change.");

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.gender == Gender.MALE && target.genitals.AverageCupSize() > target.genitals.smallestPossibleMaleCupSize && !hyperHappy)
            {
                //sb.Append("\n\nYou cup your t**s as they begin to tingle strangely. You can actually feel them getting smaller in your hands!");
                foreach (Breasts tit in target.breasts.Where(x => x.cupSize > target.genitals.smallestPossibleMaleCupSize))
                {
                    tit.ShrinkBreasts(1);
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Neck restore
            if (target.neck.type != NeckType.defaultValue && 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.defaultValue && 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 gain
            if (Utils.Rand(4) == 0 && Species.ECHIDNA.Score(target) >= 3 && target.hasVagina && target.womb.canObtainOviposition)
            {
                if (target.womb.GrantOviposition())
                {
                    sb.Append(GrantOvipositionText(target));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            if ((target.hasVagina && !target.HasTimedEffect <Heat>() && Utils.Rand(3) == 0) ||
                (target.perks.HasTimedEffect <Heat>() && Utils.RandBool() && target.GetTimedEffectData <Heat>().totalAddedLibido < 30))
            {
                target.GoIntoHeat();

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Thickness and hip modifier
            if (Utils.Rand(2) == 0 && target.build.thickness < 90)
            {
                target.build.GetThicker(2);
            }
            //old was rand(2.4) what the actual f**k. i assume that means a 1 in 2.4 chance, but why??? regardless, it's now a 5 in 12 chance (which is 1:2.4)
            if (Utils.Rand(12) < 5 && target.hasVagina && !target.genitals.AppearsMoreMaleThanFemale() && target.hips.size < 14)
            {
                target.build.GrowHips();
                //sb.Append("\n\nAfter finishing, you find that your gait has changed. Did your [hips] widen?");

                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));
        }