protected bool ChangeBreasts(Creature target, StringBuilder sb)
        {
            bool changed = false;

            BreastCollectionData oldData = target.genitals.allBreasts.AsReadOnlyData();

            if (target.breasts.Count > 1)
            {
                changed = target.genitals.RemoveExtraBreastRows() > 0;
            }

            CupSize targetSize = EnumHelper.Max(target.genitals.smallestPossibleCupSize, CupSize.D);

            if (target.breasts[0].cupSize != targetSize)
            {
                changed |= target.breasts[0].SetCupSize(targetSize) != 0;
            }

            if (changed)
            {
                sb.Append(BreastsChangedText(target, oldData));
                return(true);
            }

            return(false);
        }
예제 #2
0
        float FromSizeToPrice(CupSize size)
        {
            float price = 314f;

            switch (size)
            {
            case CupSize.S:
            {
                price = 0.90f;
                break;
            }

            case CupSize.M:
            {
                price = 1.00f;
                break;
            }

            case CupSize.L:
            {
                price = 1.10f;
                break;
            }

            default:
            {
                price = 666f;
                break;
            }
            }

            return(price);
        }
예제 #3
0
        static T MakeCoffee <T>(CupSize CupSize) where T : IDrink
        {
            BoilWater(3000);
            var grnds = GrindBeans(new List <CoffeeBeans> {
                new CoffeeBeans()
            });

            return(PourCoffee <T>(grnds, 2000));
        }
예제 #4
0
 public void OrderCoffee(CupSize size)
 {
     _currentCup = new CoffeeOrder()
     {
         Coffee = new Coffee()
         {
             Size  = size,
             Price = CoffeePrice(size)
         }
     };
 }
예제 #5
0
        static IOrderedDrink MakeCoffee(CupSize CupSize, DrinkName DrinkName)
        {
            // Cupsize decides how much water we need.
            // but CupSize is a property of the drink order
            var sizes = GetMeasures(CupSize, DrinkName);

            BoilWater(3000);
            var grnds = GrindBeans(sizes.Item1);

            return(null);
        }
예제 #6
0
        public static string AsText(this CupSize cupSize, bool withArticle = false)
        {
            int index = Math.Min(cupText.Length - 1, (byte)cupSize);

            if (withArticle)
            {
                return(articleCupText[index]);
            }
            else
            {
                return(cupText[index]);
            }
        }
예제 #7
0
        private static Tuple <List <CoffeeBeans>, MilliLitres> GetMeasures(CupSize cupSize, DrinkName drinkName)
        {
            if (drinkName == DrinkName.Espresso)
            {
                switch (cupSize)
                {
                case CupSize.Shot:
                case CupSize.Small:
                    return(Tuple.Create(new List <CoffeeBeans> {
                        { new CoffeeBeans() }, { new CoffeeBeans() }
                    }, new MilliLitres(75)));

                case CupSize.Large:
                    return(Tuple.Create(new List <CoffeeBeans> {
                        { new CoffeeBeans() },
                        { new CoffeeBeans() },
                        { new CoffeeBeans() }
                    }, new MilliLitres(150)));

                default:
                    throw new ArgumentOutOfRangeException("Unknown Size!");
                }
            }
            else
            {
                switch (cupSize)
                {
                case CupSize.Small:
                    return(Tuple.Create(new List <CoffeeBeans> {
                        { new CoffeeBeans() },
                        { new CoffeeBeans() }
                    }, new MilliLitres(400)));

                case CupSize.Medium:
                    return(Tuple.Create(new List <CoffeeBeans> {
                        { new CoffeeBeans() },
                        { new CoffeeBeans() }
                    }, new MilliLitres(500)));

                case CupSize.Large:
                    return(Tuple.Create(new List <CoffeeBeans> {
                        { new CoffeeBeans() },
                        { new CoffeeBeans() },
                        { new CoffeeBeans() }
                    }, new MilliLitres(500)));

                default:
                    throw new ArgumentOutOfRangeException("Unknown Size!");
                }
            }
        }
예제 #8
0
 public static string DescribeSize(this CupSize cupSize, Gender gender = Gender.MALE, bool withArticle = false)
 {
     //Catch all for dudes.
     if (cupSize == CupSize.FLAT)
     {
         return(gender.HasFlag(Gender.FEMALE) ? "flat" : "manly");
     }
     //Small - A->B
     if (cupSize <= CupSize.B)
     {
         return(Utils.RandomChoice("palmable", "tight", "perky", "baseball-sized"));
     }
     //C-D
     else if (cupSize <= CupSize.D)
     {
         return(Utils.RandomChoice("nice", "hand-filling", "well-rounded", "supple", "softball-sized"));
     }
     //DD->big EE
     else if (cupSize < CupSize.F)
     {
         return(Utils.RandomChoice("big", "large", "pillowy", "jiggly", "volleyball-sized"));
     }
     //F->big FF
     else if (cupSize < CupSize.G)
     {
         return(Utils.RandomChoice("soccerball-sized", "hand-overflowing", "generous", "jiggling"));
     }
     //G-> HHH
     else if (cupSize < CupSize.I)
     {
         return(Utils.RandomChoice("basketball-sized", "whorish", "cushiony", "wobbling"));
     }
     //I-> KK
     else if (cupSize < CupSize.L)
     {
         return(Utils.RandomChoice("massive motherly", "luscious", "smothering", "prodigious"));
     }
     //L-> ZZZ_BIG+
     else if (cupSize < CupSize.HYPER_A)
     {
         return(Utils.RandomChoice("mountainous", "monumental", "back-breaking", "exercise-ball-sized", "immense"));
     }
     //Hyper sizes
     else
     {
         return(Utils.RandomChoice("ludicrously-sized", "hideously large", "absurdly large", "back-breaking", "colossal", "immense"));
     }
 }
예제 #9
0
        public CoffeeCup MakeBeverage(CupSize cupSize)
        {
            CoffeeCup espresso = new FluentCoffee()
                                 .ChooseCupSize(cupSize)
                                 .AddBeans(BeanSort.Arabica, 5)
                                 .AddWater(20)
                                 .Heating(waterTemp => waterTemp >= 95)
                                 .ToBeverage();

            Console.WriteLine("Your Espresso is ready!");
            Console.WriteLine();
            Console.WriteLine("You chose a " + cupSize.ToString() + " cup");
            Console.WriteLine("It was made with these ingredients: ");

            return(espresso);
        }
예제 #10
0
        private decimal CoffeePrice(CupSize size)
        {
            switch (size)
            {
            case CupSize.Small:
                return(_appSettings.SmallCoffeePrice);

            case CupSize.Medium:
                return(_appSettings.MediumCoffeePrice);

            case CupSize.Large:
                return(_appSettings.LargeCoffeePrice);

            default:
                throw new ArgumentOutOfRangeException("Invalid coffee size");
            }
        }
예제 #11
0
        public CoffeeCup MakeBeverage(CupSize cupSize)
        {
            CoffeeCup americano = new FluentCoffee()
                                  .ChooseCupSize(cupSize)
                                  .AddBeans(BeanSort.Robusta, 6)
                                  .AddWater(15)
                                  .Heating(waterTemp => waterTemp >= 95)
                                  .AddWater(15)
                                  .ToBeverage();

            Console.WriteLine("Your Americano is ready");
            Console.WriteLine();
            Console.WriteLine("You chose a " + cupSize.ToString() + " cup");
            Console.WriteLine("It was made with these ingredients: ");

            return(americano);
        }
예제 #12
0
        public CoffeeCup MakeBeverage(CupSize cupSize)
        {
            CoffeeCup latte = new FluentCoffee()
                              .ChooseCupSize(cupSize)
                              .AddBeans(BeanSort.Robusta, 5)
                              .AddWater(10)
                              .AddMilk(20)
                              .Heating(waterTemp => waterTemp >= 95)
                              .ToBeverage();

            Console.WriteLine("Your Latte is ready!");
            Console.WriteLine();
            Console.WriteLine("You chose a " + cupSize.ToString() + " cup");
            Console.WriteLine("It was made with these ingredients: ");

            return(latte);
        }
예제 #13
0
        public CupJava MakeBeverage(CupSize cupSize)
        {
            CupJava mocha = new FluentCoffee()
                            .ChooseCupSize(cupSize)
                            .AddBeans(BeanSort.Robusta, 6)
                            .AddWater(15)
                            .Heating(waterTemp => waterTemp >= 95)
                            .AddChocolateSyrup(5)
                            .AddMilk(20)
                            .ToBeverage();

            Console.WriteLine("Your Mocha is ready!");
            Console.WriteLine();
            Console.WriteLine("You chose a " + cupSize.ToString() + " cup");
            Console.WriteLine("It was made with these ingredients: ");

            return(mocha);
        }
예제 #14
0
        public CupJava MakeBeverage(CupSize cupSize)
        {
            CupJava cappuccino = new FluentCoffee()
                                 .ChooseCupSize(cupSize)
                                 .AddBeans(BeanSort.Robusta, 6)
                                 .AddWater(15)
                                 .Heating(waterTemp => waterTemp >= 95)
                                 .AddMilk(10)
                                 .AddMilkFoam(6)
                                 .ToBeverage();

            Console.WriteLine("Your Cappuccino is ready!");
            Console.WriteLine();
            Console.WriteLine("You chose a " + cupSize.ToString() + " cup");
            Console.WriteLine("It was made with these ingredients: ");

            return(cappuccino);
        }
예제 #15
0
        protected override void OnEquip(Creature wearer)
        {
            wearer.IncreaseLust(5);

            CupSize largestCup = wearer.genitals.BiggestCupSize();

            if (!wearer.wearingUpperGarment)
            {
                wearer.HaveGenericTitOrgasm(0, false, false);

                if (largestCup < CupSize.E_BIG && largestCup >= CupSize.DD)
                {
                    wearer.IncreaseLust(2);
                }
                else if (largestCup >= CupSize.B)
                {
                    wearer.IncreaseLust(5);
                }
                else if (largestCup >= CupSize.A)
                {
                    wearer.IncreaseLust(10);
                }
                else
                {
                    wearer.IncreaseLust(15);
                }
            }
            if (!wearer.wearingLowerGarment)
            {
                var wettest = wearer.genitals.LargestVaginalByWetness();
                if (wearer.hasVagina && !wearer.hasCock && wettest.wetness > VaginalWetness.WET)
                {
                    wearer.IncreaseLust(5);
                }

                wearer.HaveGenericAnalOrgasm(false, false);

                if (wearer.hasVagina)
                {
                    wearer.HaveGenericVaginalOrgasm(wettest.vaginaIndex, false, false);
                }
            }
            wearer.HaveGenericOralOrgasm(true, false);
        }
예제 #16
0
 public DarkRoast(CupSize cupSize)
 {
     SetSize(cupSize);
 }
예제 #17
0
 public IFluentCoffee ChooseCupSize(CupSize cupSize)
 {
     coffeeCup.CupType = cupSize.ToString();
     return(this);
 }
예제 #18
0
 public Coffee(CupSize size)
 {
     Size  = size;
     Price = FromSizeToPrice(size);
 }
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

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

            StringBuilder sb = new StringBuilder();

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

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

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



            double crit = 0;

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

            bool hasCrit() => crit > 1;

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


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

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

            //Non-free changes.

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


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

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

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

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

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

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

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

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

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

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

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

            HairData oldHair = target.hair.AsReadOnlyData();

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

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

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

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

                sb.Append(BecameMaleByGrowingCock(target));

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

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

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

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

                    firstNonWolf.IncreaseThickness(.5);
                }

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


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

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

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

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

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

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

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


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

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


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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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



            //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes
            //occurred, then return the contents of the stringbuilder.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }
예제 #20
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));
        }
예제 #21
0
 public Americano(CupSize size) : base(size)
 {
 }
예제 #22
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 }, isEnhanced ? 3 : 1);
            int totalChanges = 0;

            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.

            //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
            //Increase player str:
            if (target.strength < 60 && Utils.Rand(3) == 0)
            {
                double delta = target.IncreaseStrength((60 - target.strength) / 10.0);
                sb.Append(StrengthUpText(delta));
            }
            //Increase player tou:
            if (target.toughness < 60 && Utils.Rand(3) == 0)
            {
                double delta = target.IncreaseToughness((60 - target.toughness) / 10.0);
                sb.Append(ToughnessUpText(delta));
            }
            //Decrease player spd if it is over 30:
            if (target.relativeSpeed > 30 && target.speed > 30 && Utils.Rand(3) == 0)
            {
                double decrease = target.DecreaseSpeed((target.speed - 30) / 10.0);
                sb.Append(SpeedDownText(decrease));
            }
            //Increase Corr, up to a max of 50.
            //this is silent, apparently.
            if (!isPurified && target.corruption < 50)
            {
                target.IncreaseCorruptionBy((50 - target.corruption) / 10.0);
            }

            if (totalChanges >= changeCount)
            {
                return(FinalizeTransformation(target, sb, totalChanges));
            }

            //Sex bits - Duderiffic
            if (target.cocks.Count > 0 && Utils.Rand(2) == 0 && !hyperHappy)
            {
                //If the player has at least one dick, decrease the size of each slightly,

                C**k     biggest   = target.genitals.LongestCock();
                CockData preChange = biggest.AsReadOnlyData();

                if (biggest.DecreaseLengthAndCheckIfNeedsRemoval(Utils.Rand(3) + 1))
                {
                    target.genitals.RemoveCock(biggest);
                    if (target.cocks.Count == 0 && !target.hasVagina)
                    {
                        BallsData oldBalls = target.balls.AsReadOnlyData();
                        target.AddVagina(0.25);
                        target.genitals.RemoveAllBalls();

                        sb.Append(MadeFemale(target, preChange, oldBalls));
                    }
                    else
                    {
                        sb.Append(ShrunkOneCock(target, preChange, true));
                    }
                }
                else
                {
                    sb.Append(ShrunkOneCock(target, preChange, false));
                }

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //Sex bits - girly
            bool boobsGrew = false;
            //Increase player's breast size, if they are FF or bigger
            //do not increase size, but do the other actions:
            CupSize biggestCup = target.genitals.BiggestCupSize();

            if ((biggestCup < CupSize.DD || (!isPurified && biggestCup < CupSize.FF)) && (isEnhanced || Utils.Rand(3) == 0))
            {
                BreastData oldBreasts = target.breasts[0].AsReadOnlyData();
                if (target.breasts[0].GrowBreasts((byte)(1 + Utils.Rand(3))) > 0)
                {
                    boobsGrew = true;
                    sb.Append(GrewFirstBreastRow(target, oldBreasts));
                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
                target.DeltaCreatureStats(sens: .5);
            }

            //Remove feathery hair
            HairData oldHair = target.hair.AsReadOnlyData();

            if (base.RemoveFeatheryHair(target))
            {
                sb.Append(RemovedFeatheryHairText(target, oldHair));
                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }

            //refresh the biggest cup size because we may have grown some breasts earlier.
            biggestCup = target.genitals.BiggestCupSize();

            //If breasts are D or bigger and are not lactating, they also start lactating:
            if (biggestCup >= CupSize.D && !target.genitals.isLactating && (isEnhanced || boobsGrew || Utils.Rand(3) == 0))
            {
                BreastData preLactation = target.breasts[0].AsReadOnlyData();
                target.genitals.StartLactating();

                sb.Append(StartedLactatingText(target, preLactation));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }

                target.DeltaCreatureStats(sens: .5);
            }
            //Quad nipples and other 'special isEnhanced things.
            if (isEnhanced)
            {
                //QUAD DAMAGE!
                if (!target.genitals.hasQuadNipples)
                {
                    BreastCollectionData oldBreasts = target.genitals.allBreasts.AsReadOnlyData();
                    target.genitals.SetQuadNipples(true);

                    sb.Append(GrantQuadNippleText(target, oldBreasts));

                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
                else if (target.genitals.isLactating)
                {
                    BreastCollectionData oldBreasts = target.genitals.allBreasts.AsReadOnlyData();

                    target.genitals.BoostLactation(2.5);
                    double delta = 0;

                    if (target.genitals.nippleLength < 1 || (target.genitals.nippleLength < 1.5 && !isPurified))
                    {
                        delta = target.genitals.GrowNipples(0.25);
                        target.DeltaCreatureStats(sens: .5);
                    }

                    sb.Append(BoostedLactationText(target, oldBreasts, delta));

                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
            }
            //If breasts are already lactating and the player is not lactating beyond a reasonable level, they start lactating more:
            else
            {
                if (target.genitals.isLactating && (target.genitals.lactationStatus < LactationStatus.MODERATE ||
                                                    (!isPurified && target.genitals.lactationStatus < LactationStatus.STRONG)) && (isEnhanced || Utils.Rand(3) == 0))
                {
                    BreastCollectionData oldBreasts = target.genitals.allBreasts.AsReadOnlyData();
                    double delta = 0;

                    target.genitals.BoostLactation(0.75);

                    if (target.genitals.nippleLength < 1 || (target.genitals.nippleLength < 1.5 && !isPurified))
                    {
                        delta = target.genitals.GrowNipples(0.25);
                        target.DeltaCreatureStats(sens: .5);
                    }

                    sb.Append(BoostedLactationText(target, oldBreasts, delta));

                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
                else if (isPurified && target.genitals.lactationStatus >= LactationStatus.STRONG)
                {
                    BreastCollectionData oldData = target.genitals.allBreasts.AsReadOnlyData();

                    target.DeltaCreatureStats(sens: .5);
                    target.genitals.BoostLactation(-1);

                    sb.Append(BoostedLactationText(target, oldData, 0));


                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
            }
            //If breasts are lactating at a fair level
            //and the player has not received this status,
            //apply an effect where the player really wants
            //to give their milk to other creatures
            //(capable of getting them addicted):
            biggestCup = target.genitals.BiggestCupSize();

            if (!target.HasPerk <Feeder>() && target.genitals.lactationStatus >= LactationStatus.MODERATE && Utils.Rand(2) == 0 && biggestCup >= CupSize.DD &&
                target.IsCorruptEnough(35))
            {
                target.perks.AddPerk <Feeder>();
                sb.Append(GainedFeederPerk(target));
                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //UNFINISHED
            //If player has addictive quality and drinks pure version, removes addictive quality.
            //if the player has a v****a and it is tight, it loosens.
            if (target.hasVagina)
            {
                VaginalLooseness targetLooseness = EnumHelper.Min(VaginalLooseness.LOOSE, target.genitals.maxVaginalLooseness);
                V****a           tightest        = target.genitals.TightestVagina();

                if (tightest.looseness < targetLooseness && Utils.Rand(2) == 0)
                {
                    VaginaData preLoosened = tightest.AsReadOnlyData();


                    tightest.IncreaseLooseness(2);
                    sb.Append(LoosenedTwatText(target, preLoosened));

                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }

                    target.DeltaCreatureStats(lus: 10);
                }
            }
            //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 (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //Rear body restore
            if (target.back.type != BackType.SHARK_FIN && Utils.Rand(5) == 0)
            {
                BackData oldData = target.back.AsReadOnlyData();
                target.RestoreBack();
                sb.Append(RestoredBackText(target, oldData));
                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //Ovi perk loss
            if (!isPurified && target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                target.womb.ClearOviposition();
                sb.Append(ClearOvipositionText(target));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //General Appearance (Tail -> Ears -> Paws(fur stripper) -> Face -> Horns
            //Give the player a bovine tail, same as the minotaur
            if (!isPurified && target.tail.type != TailType.COW && Utils.Rand(3) == 0)
            {
                TailData oldData = target.tail.AsReadOnlyData();

                target.UpdateTail(TailType.COW);
                sb.Append(ChangedTailText(target, oldData));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //Give the player bovine ears, same as the minotaur
            if (!isPurified && target.ears.type != EarType.COW && Utils.Rand(4) == 0 && target.tail.type == TailType.COW)
            {
                EarData oldEars = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.COW);

                sb.Append(ChangedEarsText(target, oldEars));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //If the player is under 7 feet in height, increase their height, similar to the minotaur
            if (((isEnhanced && target.build.heightInInches < 96) || target.build.heightInInches < 84) && Utils.Rand(2) == 0)
            {
                int temp = Utils.Rand(5) + 3;
                //Slow rate of growth near ceiling
                if (target.build.heightInInches > 74)
                {
                    temp = (int)Math.Floor(temp / 2.0);
                }
                //Never 0
                if (temp == 0)
                {
                    temp = 1;
                }


                byte delta = target.build.IncreaseHeight((byte)temp);

                sb.Append(GrowTaller(target, delta));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //Give the player hoofs, if the player already has hoofs STRIP FUR
            if (!isPurified && target.lowerBody.type != LowerBodyType.HOOVED && target.ears.type == EarType.COW)
            {
                if (Utils.Rand(3) == 0)
                {
                    var oldData = target.lowerBody.AsReadOnlyData();
                    target.UpdateLowerBody(LowerBodyType.HOOVED);

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

                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
            }
            //If the player's face is non-human, they gain a human face
            if (!isEnhanced && target.lowerBody.type == LowerBodyType.HOOVED && target.face.type != FaceType.HUMAN && Utils.Rand(4) == 0)
            {
                //Remove face before fur!
                var oldData = target.face.AsReadOnlyData();
                target.RestoreFace();
                sb.Append(RestoredFaceText(target, oldData));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //isEnhanced get shitty fur
            var targetFur = new FurColor(HairFurColors.BLACK, HairFurColors.WHITE, FurMulticolorPattern.SPOTTED);

            if (isEnhanced && (!target.body.IsFurBodyType() || !target.body.mainEpidermis.fur.Equals(targetFur)))
            {
                var oldData = target.body.AsReadOnlyData();

                if (target.body.type == BodyType.SIMPLE_FUR)
                {
                    target.body.ChangeMainFur(targetFur);
                }
                else
                {
                    target.UpdateBody(BodyType.SIMPLE_FUR, targetFur);
                }

                if (!oldData.IsFurBodyType())
                {
                    sb.Append(ChangedBodyText(target, oldData));
                }
                else
                {
                    sb.Append(ChandedFurToSpots(target, oldData));
                }

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //if isEnhanced to probova give a shitty cow face
            else if (isEnhanced && target.face.type != FaceType.COW_MINOTAUR)
            {
                var oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.COW_MINOTAUR);
                sb.Append(ChangedFaceText(target, oldData));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //Give the player bovine horns, or increase their size, same as the minotaur
            //New horns or expanding mino horns
            if (!isPurified && Utils.Rand(3) == 0)
            {
                var oldHorns = target.horns.AsReadOnlyData();
                if (target.horns.type != HornType.BOVINE)
                {
                    target.UpdateHorns(HornType.BOVINE);
                    sb.Append(ChangedHornsText(target, oldHorns));

                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
                else if (target.horns.type == HornType.BOVINE && target.horns.CanStrengthen && target.horns.significantHornSize < 5)
                {
                    target.horns.StrengthenTransform();
                    sb.Append(MadeHornsBigger(target, oldHorns));

                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
            }

            //Increase the size of the player's hips, if they are not already childbearing or larger
            if (Utils.Rand(2) == 0 && target.hips.size < 15)
            {
                if (isPurified && target.hips.size < 8 || !isPurified)
                {
                    var oldHips = target.hips.AsReadOnlyData();

                    if (target.build.GrowHips((byte)(1 + Utils.Rand(4))) > 0)
                    {
                        sb.Append(WidenedHipsText(target, oldHips));
                        if (++totalChanges >= changeCount)
                        {
                            return(FinalizeTransformation(target, sb, totalChanges));
                        }
                    }
                }
            }
            // Remove gills
            if (Utils.Rand(4) == 0 && target.gills.type != GillType.NONE)
            {
                var oldData = target.gills.AsReadOnlyData();
                target.RestoreGills();

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

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }

            //Increase the size of the player's ass (less likely then hips), if it is not already somewhat big
            if (Utils.Rand(2) == 0 && target.butt.size < 8 || (!isPurified && target.butt.size < 13))
            {
                var oldButt = target.butt.AsReadOnlyData();
                if (target.butt.GrowButt((byte)(1 + Utils.Rand(2))) > 0)
                {
                    sb.Append(GrewButtText(target, oldButt));
                    if (++totalChanges >= changeCount)
                    {
                        return(FinalizeTransformation(target, sb, totalChanges));
                    }
                }
            }
            //Nipples Turn Back:
            if (target.genitals.hasBlackNipples && Utils.Rand(3) == 0)
            {
                target.genitals.SetBlackNipples(false);
                sb.Append(RemovedBlackNippleText(target));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }
            //Debugcunt
            if (target.hasVagina && target.vaginas.Any(x => x.type != VaginaType.defaultValue) && Utils.Rand(3) == 0)
            {
                var oldCollection = target.genitals.allVaginas.AsReadOnlyData();
                target.vaginas.ForEach(x => target.genitals.RestoreVagina(x));
                sb.Append(RestoreAllVaginasText(target, oldCollection));

                if (++totalChanges >= changeCount)
                {
                    return(FinalizeTransformation(target, sb, totalChanges));
                }
            }


            //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(FinalizeTransformation(target, sb, changeCount - totalChanges));
        }
        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 });
            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);


            //Speed Increase:
            if (target.relativeSpeed < 100 && Utils.Rand(3) == 0)
            {
                target.ChangeSpeed(1);
            }
            //Strength Loss:
            else if (target.relativeStrength > 40 && Utils.Rand(3) == 0)
            {
                target.ChangeStrength(-1);
            }
            //Sensitivity Increase:
            if (target.relativeSensitivity < 70 && target.hasCock && Utils.Rand(3) == 0)
            {
                target.ChangeSensitivity(5);
            }
            //Libido Increase:
            if (target.relativeLibido < 70 && target.hasVagina && Utils.Rand(3) == 0)
            {
                target.ChangeLibido(2);
                if (target.relativeLibido < 30)
                {
                    target.ChangeLibido(2);
                }
            }
            //Body Mass Loss:
            if (target.build.thickness > 40 && Utils.Rand(3) == 0)
            {
                target.build.ChangeThicknessToward(40, 3);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //Thigh Loss: (towards \"girly\")
            if (target.hips.size >= 10 && Utils.Rand(4) == 0)
            {
                target.hips.ShrinkHips();
                if (target.hips.size > 15)
                {
                    target.hips.ShrinkHips((byte)(2 + Utils.Rand(3)));
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Thigh Gain: (towards \"girly\")
            if (target.hips.size < 6 && Utils.Rand(4) == 0)
            {
                target.hips.GrowHips();
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }

            //Breast Loss: (towards A cup)
            CupSize largestSize = target.genitals.BiggestCupSize();

            CupSize targetCup = EnumHelper.Max(target.genitals.smallestPossibleCupSize, CupSize.A);

            //Mod note: both shrink and gain combined into one check.
            if (largestSize != targetCup && Utils.Rand(4) == 0)
            {
                foreach (Breasts breast in target.breasts)
                {
                    //Mod Note: Generally i prefer the enum value over magic constants, but i have no idea what 70 is in cup land and don't want to look it up.
                    if (breast.cupSize > (CupSize)70)
                    {
                        breast.ShrinkBreasts((byte)(Utils.Rand(3) + 15));
                    }
                    else if (breast.cupSize > (CupSize)50)
                    {
                        breast.ShrinkBreasts((byte)(Utils.Rand(3) + 10));
                    }
                    else if (breast.cupSize > (CupSize)30)
                    {
                        breast.ShrinkBreasts((byte)(Utils.Rand(3) + 7));
                    }
                    else if (breast.cupSize > (CupSize)15)
                    {
                        breast.ShrinkBreasts((byte)(Utils.Rand(3) + 4));
                    }
                    else if (breast.cupSize > targetCup)
                    {
                        breast.ShrinkBreasts((byte)(2 + Utils.Rand(2)));
                    }

                    //Mod note: if we shrunk it below the target cup size or it already was,
                    if (breast.cupSize < targetCup)
                    {
                        breast.SetCupSize(targetCup);
                    }
                }
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Penis Reduction towards 3.5 Inches:
            if (target.genitals.LongestCockLength() >= 3.5 && target.hasCock && Utils.Rand(2) == 0)
            {
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Testicle Reduction:
            if (target.balls.count > 0 && target.hasCock && (target.balls.size > 1 || target.balls.count > 1) && Utils.Rand(4) == 0)
            {
                if (target.balls.size > 20)
                {
                    target.balls.ShrinkBalls(6);
                }
                else if (target.balls.size > 15)
                {
                    target.balls.ShrinkBalls(5);
                }
                else if (target.balls.size > 12)
                {
                    target.balls.ShrinkBalls(4);
                }
                else if (target.balls.size > 10)
                {
                    target.balls.ShrinkBalls(3);
                }
                else if (target.balls.size > 8)
                {
                    target.balls.ShrinkBalls(2);
                }
                else if (target.balls.size > 1)
                {
                    target.balls.ShrinkBalls(1);
                }
                else
                {
                    target.balls.MakeUniBall();
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Anal Wetness Increase:
            if (target.ass.wetness < AnalWetness.SLIME_DROOLING && Utils.Rand(4) == 0)
            {
                //Anal Wetness Increase Final (always loose):
                target.ass.IncreaseWetness();
                //buttChange(30,false,false,false);
                if (target.ass.looseness < AnalLooseness.STRETCHED)
                {
                    target.ass.IncreaseLooseness();
                }

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

                target.ChangeSensitivity(2);
            }
            //Fertility Decrease:
            if (target.hasVagina && Utils.Rand(4) == 0)
            {
                target.ChangeSensitivity(-2);

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Male Effects
            if (target.gender == Gender.MALE)
            {
                //Femininity Increase Final (max femininity allowed increased by +10):
                if (Utils.Rand(4) == 0)
                {
                    if (target.femininity < 70 && target.femininity >= 60)
                    {
                        if (target.femininity.femininityLimitedByGender)
                        {
                            target.femininity.ActivateAndrogyny();
                        }
                        target.femininity.IncreaseFemininity(10);
                        if (target.femininity > 70)
                        {
                            target.femininity.SetFemininity(70);
                        }

                        if (--remainingChanges <= 0)
                        {
                            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                        }
                    }
                    //Femininity Increase:
                    else
                    {
                        target.femininity.IncreaseFemininity(10);
                        if (--remainingChanges <= 0)
                        {
                            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                        }
                    }
                }
                //Muscle tone reduction:
                if (target.build.muscleTone > 20 && Utils.Rand(4) == 0)
                {
                    target.build.DecreaseMuscleTone(10);
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //Female Effects
            else if (target.gender == Gender.FEMALE)
            {
                //Masculinity Increase:
                if (target.femininity > 30 && Utils.Rand(4) == 0)
                {
                    target.femininity.IncreaseMasculinity(10);
                    if (target.femininity < 30)
                    {
                        target.femininity.SetFemininity(30);
                        //Masculinity Increase Final (max masculinity allowed increased by +10):

                        if (target.femininity.femininityLimitedByGender)
                        {
                            target.femininity.ActivateAndrogyny();
                        }
                    }

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                //Muscle tone gain:
                if (target.build.muscleTone < 80 && Utils.Rand(4) == 0)
                {
                    target.build.GainMuscle(10);
                    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));
                }
            }
            //Nipples Turn Black:
            if (!target.genitals.hasBlackNipples && Utils.Rand(6) == 0)
            {
                target.genitals.SetBlackNipples(true);

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //PC Trap Effects
            if (target.eyes.type != EyeType.SAND_TRAP && Utils.Rand(4) == 0)
            {
                //Eyes Turn Black:
                EyeData oldData = target.eyes.AsReadOnlyData();
                target.UpdateEyes(EyeType.SAND_TRAP);
                sb.Append(UpdateEyesText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //V****a Turns Black:
            //has a v****a and any v****a is not a sand trap (aka, not all of the vaginas are sand trap vags)
            if (target.hasVagina && !target.genitals.OnlyHasVaginasOfType(VaginaType.SAND_TRAP) && Utils.Rand(4) == 0)
            {
                //(Wet:
                //(Corruption <50:
                target.DeltaCreatureStats(sens: 2, lus: 10);

                target.vaginas.ForEach(x => target.genitals.UpdateVagina(x, VaginaType.SAND_TRAP));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Dragonfly Wings:
            if (target.wings.type != WingType.DRAGONFLY && Utils.Rand(4) == 0)
            {
                //Wings Fall Out: You feel a sharp pinching sensation in your shoulders and you cringe slightly. Your former dragonfly wings make soft, papery sounds as they fall into the dirt behind you.
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }

                target.UpdateWings(WingType.DRAGONFLY);
            }
            if (remainingChanges == changeCount)
            {
            }


            //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));
        }
예제 #24
0
 public Espresso(CupSize size) : base(size)
 {
 }
예제 #25
0
        public Coffee(string strSize)
        {
            CupSize Size = FromStringToSize(strSize);

            Price = FromSizeToPrice(Size);
        }
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

            //for some unknown reason, demon tfs roll out their own chance system completely unique to them. ok.

            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.

            int rando = Utils.Rand(100);
            int delta = target.GetExtraData()?.deltaTransforms ?? 0;

            if (delta != 0)
            {
                rando += 5 * delta + Utils.Rand(5 * delta);
            }

            //First, check if this tf has the male flag set (for male or herm tfs). If it does, add or grow cocks.
            if (desiredGender.HasFlag(Gender.MALE))
            {
                byte addedCocks = 0;
                //if our initial roll was a crit, roll again. if we crit again, we may add several cocks, if possible.
                if (rando >= 85 && target.cocks.Count < Genitals.MAX_COCKS && Utils.Rand(10) < target.corruptionTrue / 25)
                {
                    addedCocks = GrowCockGeneric(target, (byte)(Utils.Rand(2) + 2));

                    target.DeltaCreatureStats(lib: 3 * addedCocks, sens: 5 * addedCocks, lus: 10 * addedCocks);
                    if (!isPurified)
                    {
                        target.IncreaseCorruption(8);
                    }
                }
                //otherwise, only add a c**k if we have none or we originally rolled a crit (but failed to crit again)
                else if (target.cocks.Count == 0 || (rando >= 85 && target.cocks.Count < Genitals.MAX_COCKS))
                {
                    addedCocks = GrowCockGeneric(target, 1);

                    target.DeltaCreatureStats(lib: 3, sens: 5, lus: 10);
                    if (!isPurified)
                    {
                        target.IncreaseCorruption(5);
                    }
                }
                //if that fails, it means we had a c**k already, or can't grow any more of them.
                else
                {
                    C**k   shortest = target.genitals.ShortestCock();
                    double lengthDelta;

                    if (rando >= 45)
                    {
                        lengthDelta = shortest.IncreaseLength(Utils.Rand(3) + 3);
                        shortest.IncreaseThickness(1);
                    }
                    else if (Utils.Rand(4) == 0)
                    {
                        lengthDelta = shortest.IncreaseLength(3);
                    }
                    else
                    {
                        lengthDelta = shortest.IncreaseLength(1);
                    }

                    target.DeltaCreatureStats(lib: 2, sens: 1, lus: 5 + lengthDelta * 3);
                    if (!isPurified)
                    {
                        target.IncreaseCorruption();
                    }
                    //no idea why this occurs, but ok.
                    target.IncreaseIntelligence(1);
                }
            }
            //Otherwise, we're targeting female demon tfs only. this means we need to shrink (and possibly remove) the largest c**k the target has, unless hyper happy is on.
            else if (!hyperHappy && target.hasCock)
            {
                C**k largest = target.genitals.LongestCock();                 //this loops through all the cocks and finds the longest. obviously, if the count is 1, this is simply the first element.

                //we'll need this if it gets removed. if not, this can still be used, this time to determine how much we shrunk.
                CockData oldData = largest.AsReadOnlyData();

                //try decreasing it by 1-3. if this causes it to be removed instead, that's fine, but we need to know.
                //Note that this remove is now IN PLACE, not LAST. so if we remove the 3rd one, the old 4th is now 3rd, and so on.
                bool removed = target.genitals.ShrinkCockAndRemoveIfTooSmall(largest, Utils.Rand(3) + 1);
            }

            //Then, check if the tf has the female flag set (for female or herm tfs). if it does, add a v****a (if needed), and increase breast size.
            if (desiredGender.HasFlag(Gender.FEMALE))
            {
                //don't currently have a v****a and herm tf or we're genderless, or it's a crit, or we rerolled a crit.
                if (!target.hasVagina && (desiredGender == Gender.HERM || target.gender == Gender.GENDERLESS || rando > 65 || Utils.Rand(3) == 0))
                {
                    target.genitals.AddVagina(VaginaType.HUMAN);
                }
                //do have one, and rolled a high crit.
                else if (target.hasVagina && rando >= 85)
                {
                    foreach (V****a vag in target.vaginas)
                    {
                        //grow each c**t anywhere from 0.25in to 1in, if they are below the largest normal size.
                        if (vag.c**t.length < vag.c**t.largestNormalSize)
                        {
                            vag.GrowClit((Utils.Rand(4) + 1) * 0.25);
                            //cap it at the largest normal size.
                            if (vag.c**t.length > vag.c**t.largestNormalSize)
                            {
                                vag.SetClitSize(vag.c**t.largestNormalSize);
                            }
                        }
                    }
                }
                //do have one, rolled a crit, but not a high crit.
                else if (target.hasVagina && rando > 65)
                {
                    target.HaveGenericVaginalOrgasm(0, true, true);
                    target.vaginas.ForEach(x => x.IncreaseWetness());
                }


                //now, breasts. these are the fallback, of sorts, so they grow larger when we don't crit. they also grow larger when we crit if we're targeting herms.

                if (rando < 85 || desiredGender == Gender.HERM)
                {
                    //only occurs via herm.
                    if (rando >= 85)
                    {
                        target.breasts.ForEach(x => x.GrowBreasts(3));
                    }
                    else
                    {
                        byte    temp        = (byte)(1 + Utils.Rand(3));
                        CupSize largestSize = target.genitals.BiggestCupSize();
                        if (largestSize < CupSize.B && Utils.Rand(3) == 0)
                        {
                            temp++;
                        }

                        if (largestSize < CupSize.DD && Utils.Rand(4) == 0)
                        {
                            temp++;
                        }

                        if (largestSize < CupSize.DD_BIG && Utils.Rand(5) == 0)
                        {
                            temp++;
                        }

                        target.breasts.ForEach(x => x.GrowBreasts(temp));
                    }
                }
            }
            //if not, we're targeting male demon tfs only. this means we may need to shrink any overlarge breasts. Additionally, higher rng rolls may now remove vaginas.
            else if (!hyperHappy)
            {
                //if high crit: decrease bonus capacity, and remove a v****a, flat-out.
                if (rando >= 85 && target.hasVagina)
                {
                    target.genitals.DecreaseBonusVaginalCapacity(5);
                    target.genitals.RemoveVagina();
                }
                //otherwise, if somewhat high, decrease bonus vaginal capacity (all vaginas), and wetness (last v****a). if this causes the bonus capacity to drop to 0
                //and would cause it to go negative if we allowed that, remove the last v****a.
                else if (rando >= 65)
                {
                    V****a lastVagina = target.vaginas[target.vaginas.Count - 1];
                    lastVagina.DecreaseWetness(1);

                    //this is being super pedantic, but i'd prefer it lower the stat, then remove the v****a. hence this bool here.
                    bool remove = target.genitals.standardBonusVaginalCapacity < 5;
                    //decrease first.
                    target.genitals.DecreaseBonusVaginalCapacity(5);

                    //then remove it.
                    if (remove)
                    {
                        target.genitals.RemoveVagina();
                    }
                }

                //
                if (target.genitals.BiggestCupSize() > target.genitals.smallestPossibleCupSize && (rando >= 85 || (rando > 65 && Utils.RandBool()) || (rando <= 65 && Utils.Rand(4) == 0)))
                {
                    foreach (Breasts breast in target.breasts)
                    {
                        if (breast.cupSize > target.genitals.smallestPossibleCupSize)
                        {
                            byte amount = 1;
                            if (rando >= 85)
                            {
                                amount++;
                            }

                            breast.ShrinkBreasts(amount);
                        }
                    }
                }
            }

            //never called in vanilla. if i read it correctly, it just initializes to 0 with a max of 1.
            int changeCount = base.GenerateChangeCount(target, new int[] { 3 }, 0, 0);

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

            int remainingChanges = changeCount;


            //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));
                }
            }
            //Demonic changes - higher chance with higher corruption.
            if (Utils.Rand(40) + target.corruption / 3 > 35 && !isPurified)
            {
                //Change tail if already horned.
                if (target.tail.type != TailType.DEMONIC && !target.horns.isDefault)
                {
                    target.IncreaseCorruption(4);
                    TailData oldData = target.tail.AsReadOnlyData();
                    target.UpdateTail(TailType.DEMONIC);
                    sb.Append(UpdateTailText(target, oldData));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                //grow horns!
                if (target.horns.numHorns == 0 || (Utils.Rand(target.horns.numHorns + 3) == 0))
                {
                    if (target.horns.numHorns < 12 && (target.horns.type == HornType.NONE || target.horns.type == HornType.DEMON))
                    {
                        if (target.horns.type == HornType.NONE)
                        {
                            target.UpdateHorns(HornType.DEMON);
                        }

                        target.IncreaseCorruption(3);
                    }
                    //Text for shifting horns
                    else if (target.horns.type != HornType.DEMON)
                    {
                        target.UpdateHorns(HornType.DEMON);
                        target.IncreaseCorruption(3);
                    }

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

                //remove fur
                if (target.face.type != FaceType.HUMAN || (target.body.type != BodyType.HUMANOID && Utils.Rand(3) == 0))
                {
                    //Remove face before fur!
                    if (target.face.type != FaceType.HUMAN)
                    {
                        target.RestoreFace();
                    }
                    //De-fur
                    else if (target.body.type != BodyType.HUMANOID)
                    {
                        target.RestoreBody();
                    }

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                //Demon tongue
                if (target.tongue.type == TongueType.SNAKE && Utils.Rand(3) == 0)
                {
                    TongueData oldData = target.tongue.AsReadOnlyData();
                    target.UpdateTongue(TongueType.DEMONIC);
                    sb.Append(UpdateTongueText(target, oldData));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                //foot changes - requires furless
                if (target.body.type == BodyType.HUMANOID && Utils.Rand(4) == 0)
                {
                    bool changed;
                    //Males/genderless get clawed feet
                    if (!target.gender.HasFlag(Gender.FEMALE) || (target.gender == Gender.HERM && target.genitals.AppearsMoreMaleThanFemale()))
                    {
                        changed = target.UpdateLowerBody(LowerBodyType.DEMONIC_CLAWS);
                    }
                    //Females/futa get high heels
                    else
                    {
                        changed = target.UpdateLowerBody(LowerBodyType.DEMONIC_HIGH_HEELS);
                    }

                    if (changed && --remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
                //Grow demon wings
                if ((target.wings.type != WingType.BAT_LIKE || !target.wings.isLarge || target.back.type == BackType.SHARK_FIN) && Utils.Rand(8) == 0 && target.IsCorruptEnough(50))
                {
                    //grow smalls to large

                    if (target.wings.type == WingType.BAT_LIKE && target.IsCorruptEnough(75))
                    {
                        target.wings.GrowLarge();
                    }
                    else
                    {
                        target.UpdateWings(WingType.BAT_LIKE);
                    }

                    if (target.back.type == BackType.SHARK_FIN)
                    {
                        target.RestoreBack();
                    }

                    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));
        }
예제 #27
0
 private void MinMaleCupChanged(CupSize oldValue)
 {
     source.genitals.perkData.ValidateCupSize();
 }
예제 #28
0
 public Beverage(CupSize size)
 {
     this.size = size;
 }
예제 #29
0
 public HouseBlend(CupSize cupSize) : this()
 {
     SetSize(cupSize);
 }
예제 #30
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));
        }