Пример #1
0
        protected byte GrowCockGeneric(Creature creature, byte count)
        {
            if (count == 0)
            {
                return(0);
            }

            byte added = 0;

            while (count-- > 0 && creature.AddCock(CockType.HUMAN, Utils.Rand(3) + 5, 0.75))
            {
                added++;
            }
            return(added);
        }
        protected internal override string DoTransformation(Creature target, out bool isBadEnd)
        {
            isBadEnd = false;

            //huh. no rng rolls, just starts at 3. cool
            int changeCount      = GenerateChangeCount(target, null, 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);

            //Stats and genital changes
            if (Utils.Rand(2) == 0)
            {
                target.ChangeLust(25);
                if (target.relativeLibido < 100)
                {
                    if (target.relativeLibido < 50)
                    {
                        target.ChangeLibido(1);
                    }

                    target.ChangeLibido(1);
                }
            }
            C**k smallest = target.genitals.ShortestCock();

            if (target.hasCock && smallest.length < 12 && Utils.Rand(3) == 0)
            {
                smallest.IncreaseLength();

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

            C**k thinnest = target.genitals.ThinnestCock();

            if (target.hasCock && thinnest.girth < 4 && Utils.Rand(3) == 0)
            {
                thinnest.IncreaseThickness(0.5);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (target.balls.count > 0 && target.genitals.cumMultiplier < 50 && Utils.Rand(3) == 0)
            {
                target.ChangeLust(20);

                if (target.genitals.cumMultiplier < 10)
                {
                    target.genitals.IncreaseCumMultiplier(1);
                }

                if (target.genitals.cumMultiplier < 50)
                {
                    target.genitals.IncreaseCumMultiplier(0.5);
                }

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.hasVagina && target.genitals.standardBonusVaginalCapacity > 0)
            {
                target.genitals.DecreaseBonusVaginalCapacity((ushort)(Utils.Rand(5) + 5));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.hasVagina && !target.hasCock)
            {
                target.RemoveAllVaginas();
                target.AddCock(CockType.HUMAN, 6, 1);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.hasCock && !target.balls.hasBalls)
            {
                target.balls.GrowBalls();

                target.HaveGenericCockOrgasm(0, true, true);

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Transformations
            //Neck restore
            if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0)
            {
                target.RestoreNeck();
            }
            //Rear body restore
            if (!target.back.isDefault && Utils.Rand(5) == 0)
            {
                target.RestoreBack();
            }
            //Ovi perk loss
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                if (target.womb.ClearOviposition())
                {
                    sb.Append(RemovedOvipositionText(target));
                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }

            //remove anything with a scaly or partially scaly body. now affects cockatrice too!
            if (Utils.Rand(3) == 0 && target.body.HasAny(EpidermisType.SCALES))
            {
                BodyData oldData = target.body.AsReadOnlyData();
                target.RestoreBody();
                sb.Append(RestoredBodyText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.arms.type != ArmType.HUMAN)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                target.RestoreArms();
                sb.Append(RestoredArmsText(target, oldData));

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.lowerBody.type == LowerBodyType.CLOVEN_HOOVED && target.horns.type == HornType.GOAT && target.face.type != FaceType.HUMAN)
            {
                FaceData oldData = target.face.AsReadOnlyData();
                target.UpdateFace(FaceType.HUMAN);
                sb.Append(UpdateFaceText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //MOD: anything with scales will prevent this, including partial scales (a la cockatrice). since body is reworked a lot of these checks are hard to port,
            //but this i think is the closest i can get.
            if (Utils.Rand(4) == 0 && !target.body.HasAny(EpidermisType.SCALES) && target.ears.type != EarType.ELFIN)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                target.UpdateEars(EarType.ELFIN);
                sb.Append(UpdateEarsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.horns.type == HornType.NONE)
            {
                HornData oldData = target.horns.AsReadOnlyData();
                target.UpdateHorns(HornType.GOAT);
                sb.Append(UpdateHornsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Mod Note: Horns:

            if (Utils.Rand(3) == 0 && target.horns.type != HornType.GOAT)
            {
                HornData oldData = target.horns.AsReadOnlyData();
                target.UpdateHorns(HornType.GOAT);
                sb.Append(UpdateHornsText(target, oldData));

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.horns.type == HornType.GOAT && target.horns.CanStrengthen)
            {
                target.horns.StrengthenTransform();

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

                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.cocks.Count == 1 && target.cocks[0].type != CockType.HUMAN)
            {
                target.genitals.UpdateCock(0, CockType.HUMAN);
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            if (Utils.Rand(3) == 0 && target.cocks.Count > 1 && !target.genitals.OnlyHasCocksOfType(CockType.HUMAN))
            {
                target.genitals.UpdateCock(target.cocks.First(x => x.type != CockType.HUMAN), CockType.HUMAN);

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

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

            //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes
            //occurred, then return the contents of the stringbuilder.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }
Пример #3
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));
        }
Пример #4
0
        //MOD NOTE: this has been changed to work like bimbo liqueur - it will remove all vaginas and restore breasts to manly defaults if
        //the futa perk is not active.
        protected override string OnConsumeAttempt(Creature consumer, out bool consumeItem, out bool isBadEnd)
        {
            isBadEnd = false;

            StringBuilder sb = new StringBuilder();
            //get our common bimbo/bro/futa perk store. Note that it may be null (if we don't have it).
            BimBro bimbro = consumer.GetPerkData <BimBro>();

            //intro text.

            //first check: we don't have any bro/futa/bimbo perks, because it's not in our perk list. do the standard faire.
            sb.Append(Intro(consumer, bimbro));

            if (bimbro?.hasBroEffect == true)
            {
                (consumer as CombatCreature)?.RecoverFatigue(33);
                (consumer as CombatCreature)?.AddHPPercent(1);
            }

            VaginaCollectionData oldVaginaCollection = consumer.genitals.allVaginas.AsReadOnlyData();

            //get taller. only occurs if the player does not already have the bro perk in any form.
            if ((bimbro is null || !bimbro.broBody) && consumer.build.heightInInches < 77)
            {
                short delta = consumer.build.SetHeight(77);

                sb.Append(GrewTaller(consumer, delta));
            }

            //(T**s b' gone)
            //Does not affect creature if they already have bimbo or futa perks.
            if (bimbro is null || bimbro.broBody)
            {
                //Note: minimum cup size respects current gender. we'll need to make the creature male (or genderless) first.
                consumer.RemoveAllVaginas();

                if (consumer.genitals.BiggestCupSize() >= consumer.genitals.smallestPossibleMaleCupSize)
                {
                    BreastCollectionData oldRows = consumer.genitals.allBreasts.AsReadOnlyData();
                    consumer.RemovePerk <Feeder>();
                    bool stoppedLactation = consumer.genitals.SetLactationTo(LactationStatus.NOT_LACTATING);

                    sb.Append(ChangedBreastsText(consumer, bimbro, oldRows, stoppedLactation));
                }
            }

            //Body tone Flavor text. No effect on stats (yet)
            sb.Append(BecomeRippedFlavorText(consumer, bimbro));



            //Dick&balls growth. Affects all.

            //(has dick less than 10 inches)
            if (consumer.hasCock)
            {
                bool grewBalls      = !consumer.hasBalls;
                bool lengthenedCock = false;

                if (consumer.cocks[0].length < 10)
                {
                    lengthenedCock = true;
                    consumer.cocks[0].SetLength(10);
                    if (consumer.cocks[0].girth < 2.75)
                    {
                        consumer.cocks[0].SetGirth(2.75);
                    }
                }

                if (!consumer.hasBalls)
                {
                    consumer.balls.GrowBalls(2, 3);
                }

                sb.Append(LengthenedCock(consumer, bimbro, lengthenedCock, grewBalls));
            }
            //(No dick)
            else
            {
                consumer.AddCock(CockType.defaultValue, 12, 2.75);
                bool grewBalls = false;
                if (!consumer.balls.hasBalls)
                {
                    grewBalls = true;
                    consumer.balls.GrowBalls(2, 3);
                }
                sb.Append(GrewCock(consumer, bimbro, grewBalls));
            }

            //(Pussy b gone)
            //Note: only applies if consumer does not have bimbo or futa perks. also note that we already silently removed them so the breasts would resize correctly.
            //so all this does is print out the data.
            if ((bimbro is null || bimbro.broBody) && oldVaginaCollection.hasVagina)
            {
                sb.Append(RemovedAllVaginas(consumer, oldVaginaCollection));
            }

            //(below max masculinity)
            if ((bimbro is null || bimbro.broBody) && consumer.femininity > 0)
            {
                FemininityData oldFem = consumer.femininity.AsReadOnlyData();
                sb.Append(consumer.ModifyFemininity(0, 100));
                sb.Append(Masculinize(consumer, bimbro is null, oldFem));
            }

            //max tone. Thickness + 50
            //both are silent.
            consumer.build.ChangeMuscleToneToward(100, 100);
            consumer.build.ChangeThicknessToward(100, 50);

            if (consumer.intelligence > 21)
            {
                consumer.SetIntelligence((byte)Math.Floor(Math.Max(consumer.intelligenceTrue / 5.0, 21)));
            }
            consumer.DeltaCreatureStats(str: 33, tou: 33, inte: -1, lib: 4, lus: 40);

            sb.Append(Outro(consumer, bimbro, consumer.perks.HasPerk <Feeder>()));

            //apply the perks. this will also correct any silent data we missed.
            if (bimbro is null)
            {
                consumer.AddPerk(new BimBro(Gender.MALE));
            }
            else
            {
                bimbro.Broify();
            }


            ////Bonus cum production!
            //sb.Append("<b>(Bro Body - Perk Gained!)" + Environment.NewLine);
            //sb.Append("(Bro Brains - Perk Gained!)</b>" + Environment.NewLine);//int to 20. max int 50)
            //if (consumer.HasPerk<Feeder>())
            //{
            //	sb.Append("<b>(Perk Lost - Feeder!)</b>" + Environment.NewLine);
            //	consumer.RemovePerk<Feeder>();
            //}

            consumeItem = true;
            return(sb.ToString());
        }
        private string DoTransformationCommon(Creature target, bool currentlyInCombat, out bool isBadEnd)
        {
            isBadEnd = false;

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

            bool statsChanged = false;

            StringBuilder sb = new StringBuilder();

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

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

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

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

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

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

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



            //Neck restore
            if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0)
            {
                NeckData oldData = target.neck.AsReadOnlyData();
                target.RestoreNeck();
                sb.Append(RestoredNeckText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Rear body restore
            if (target.back.type != BackType.SHARK_FIN && !target.back.isDefault && Utils.Rand(5) == 0)
            {
                BackData oldData = target.back.AsReadOnlyData();
                target.RestoreBack();
                sb.Append(RestoredBackText(target, oldData));
                if (--remainingChanges <= 0)
                {
                    return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                }
            }
            //Ovi perk loss
            if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0)
            {
                target.womb.ClearOviposition();
                sb.Append(ClearOvipositionText(target));

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

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

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

            //Sexual changes

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

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

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

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

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

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

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

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

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

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

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

            // Remove gills
            if (Utils.Rand(4) == 0 && !target.gills.isDefault)
            {
                GillData oldData = target.gills.AsReadOnlyData();
                target.RestoreGills();
                sb.Append(RestoredGillsText(target, oldData));

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

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

            //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes
            //occurred, then return the contents of the stringbuilder.
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }
Пример #6
0
        //MOD: large eggs will now firmly make you male, just like pink eggs do (except female, of course)
        //This means they will grow balls and a c**k if you don't have them.
        //small eggs will now remove one v****a at a time, and are able to handle multiple vaginas.
        //similarly, small eggs will now remove one extra breast row at a time.
        //a large egg will remove each additional breast row, and will also make the last pair as manly as possible. they will remove fuckable nipples while doing so,
        //but they will become inverted if fuckable.
        protected override string OnConsumeAttempt(Creature consumer, out bool consumeItem, out bool isBadEnd)
        {
            isBadEnd = false;

            StringBuilder sb = new StringBuilder();

            sb.Append("You devour the egg, momentarily sating your hunger.");

            //MOD: doing this is logical order - start with breasts and work our way down.

            //remove extra breast rows.
            //if only 1 extra row or it's a small egg, remove 1 extra row.
            if (consumer.breasts.Count > 1 && (consumer.breasts.Count == 2 || !isLarge))
            {
                sb.Append("Your back relaxes as extra weight vanishes from your chest. <b>Your lowest " + consumer.breasts[consumer.breasts.Count - 1].LongDescription()
                          + " have vanished.</b>");

                consumer.RemoveBreastRow();
                sb.Append(GlobalStrings.NewParagraph());
            }
            //if large egg, remove all extra breast rows.
            else if (consumer.breasts.Count > 2 && isLarge)
            {
                sb.Append("Your back relaxes as a significant amount of weight vanishes from your chest. <b>You've lost all but your top-most row of breasts!</b>");

                consumer.RemoveExtraBreastRows();
                sb.Append(GlobalStrings.NewParagraph());
            }

            //Kill pussies!
            //if small on only have 1, remove 1.
            if (consumer.hasVagina && (consumer.vaginas.Count == 1 || !isLarge))
            {
                sb.Append(consumer.genitals.OneVaginaOrVaginasNoun(Conjugate.YOU).CapitalizeFirstLetter() + "clenches in pain, doubling you over. " +
                          "You slip a hand down to check on it, only to feel the slit growing smaller and smaller until it disappears");

                //if large, and we don't have a c**k, grow one out of old c**t.
                if (isLarge && !consumer.hasCock)
                {
                    sb.Append(". Your c**t, however, seems to be resisting the change, and instead expands outward. The top flares outward into a distinct mushroom-shape," +
                              "and you quickly realize " + SafelyFormattedString.FormattedText("Your v****a has been replaced with a c**k!", StringFormats.BOLD));

                    double length = consumer.clits[0].length + 5;
                    double width  = length / 5;

                    //also grow a pair of balls if needed.
                    consumer.AddCock(CockType.defaultValue, length, width);

                    if (!consumer.hasBalls)
                    {
                        consumer.balls.GrowBalls();
                        sb.Append(SafelyFormattedString.FormattedText("A pair of balls drop in below it, completing the change in gender.", StringFormats.BOLD));
                    }
                }
                else
                {
                    sb.Append(", taking your c**t with it!");
                    if (consumer.vaginas.Count == 1)
                    {
                        sb.Append(" <b>Your v****a is gone!</b>");
                    }
                }
                consumer.RemoveVagina();

                sb.Append(GlobalStrings.NewParagraph());
            }
            //if large, and more than one, remove them both.
            else if (consumer.hasVagina && isLarge)
            {
                sb.Append("You double over in pain, and quickly pinpoint the source - your female sexes. You slip a hand down to check on them, and realize " +
                          "they are both shrinking inward, your feminine entrances shrinking smaller and smaller until both disappear completely");

                if (!consumer.hasCock)
                {
                    sb.Append(". Your clits, however, seem to resist the change, at least momentarily. Instead, they merge together, then expand outward, growing wider and longer." +
                              "The top flares outward into a distinct mushroom-shape, and you quickly realize " + SafelyFormattedString.FormattedText("Your feminine sexes have been " +
                                                                                                                                                      "replaced by a c**k!", StringFormats.BOLD));

                    double length = consumer.clits.Sum(x => x.length) + 5;
                    double width  = length / 5;

                    consumer.AddCock(CockType.defaultValue, length, width);


                    if (!consumer.hasBalls)
                    {
                        consumer.balls.GrowBalls();
                        sb.Append(SafelyFormattedString.FormattedText("A pair of balls drop in below it, completing the change in gender.", StringFormats.BOLD));
                    }
                }
                else
                {
                    sb.Append(", taking their clits with them! " + SafelyFormattedString.FormattedText("Your vaginas are gone!", StringFormats.BOLD));
                }
                consumer.RemoveAllVaginas();
                sb.Append(GlobalStrings.NewParagraph());
            }

            //Dickz
            //if you have any, grow them, regardless of size. the large egg grows more.
            if (consumer.hasCock)
            {
                sb.Append(GlobalStrings.NewParagraph() + "Your " + consumer.genitals.AllCocksLongDescription(out bool isPlural)
                          + (isPlural ? " fill" : "fills") + " to full-size... and " + (isPlural ? "begin" : "begins") + " growing obscenely.");

                CockCollectionData cockCollection = consumer.genitals.allCocks.AsReadOnlyData();

                double averageWidthDelta = 0;
                foreach (C**k c**k in consumer.cocks)
                {
                    c**k.IncreaseLength(3 + Utils.Rand(isLarge ? 5 : 2));
                    averageWidthDelta += c**k.IncreaseThickness(1);
                }

                averageWidthDelta /= consumer.cocks.Count;

                sb.Append(consumer.genitals.allCocks.GenericChangeCockLengthText(cockCollection));
                sb.Append(GlobalStrings.NewParagraph());
                //Display the degree of thickness change.
                if (averageWidthDelta >= 1)
                {
                    sb.Append("Your " + consumer.genitals.AllCocksShortDescription() + (isPlural ? " spread" : " spreads") +
                              " rapidly, swelling with over an inch of added girth, making " + (isPlural ? "them" : "it") + " feel fat and floppy.");
                }
                else if (averageWidthDelta > 0.5)
                {
                    sb.Append("Your " + consumer.genitals.AllCocksShortDescription() + (isPlural ? " seem" : " seems") + " to swell up, feeling heavier. " +
                              "You look down and watch " + (isPlural ? "them growing fatter as they thicken." : "it growing fatter as it thickens."));
                }
                else
                {
                    sb.Append("Your " + consumer.genitals.AllCocksShortDescription() + (isPlural ? " feel" : " feels") + " swollen and heavy. " +
                              "With a firm, but gentle, squeeze, you confirm your suspicions - " + (isPlural ? "they are" : "it is") + " definitely thicker.");
                }

                if (!consumer.balls.hasBalls && isLarge)
                {
                    sb.Append("A pair of balls grow in, complementing your enlarged " + (isPlural ? "cocks" : "c**k") + " nicely, completing your manly sex.");

                    consumer.balls.GrowBalls();
                }

                consumer.DeltaCreatureStats(lib: 1, sens: 1, lus: 20);
            }
            //if you don't, and it's a large egg, get one. it's just gonna be default size.
            else if (isLarge)
            {
                consumer.AddCock();
                sb.Append("A sudden pressure forms in your groin, mixed with an inescapable sense of enquenched sexual need, like an itch you can't reach. " +
                          "It threatens to overwhelm you until suddenly a length of flesh bursts from your loins. You immediately realize "
                          + SafelyFormattedString.FormattedText("you now have a c**k!", StringFormats.BOLD));

                consumer.AddCock();

                if (!consumer.hasBalls)
                {
                    sb.Append(SafelyFormattedString.FormattedText("A pair of balls drop below your newly formed c**k, completing the look.", StringFormats.BOLD));

                    consumer.balls.GrowBalls();
                }
            }

            //butt and hips.
            if (isLarge)
            {
                //Ass/hips shrinkage!
                if (consumer.butt.size > consumer.butt.smallestPossibleButtSize)
                {
                    sb.Append("Muscles firm and tone as you feel your " + consumer.build.ButtLongDescription() + " becomes smaller and tighter.");
                    if (consumer.hips.size > 5)
                    {
                        sb.Append(" ");
                    }

                    consumer.butt.ShrinkButt(2);
                }
                if (consumer.hips.size > consumer.hips.smallestPossibleHipSize)
                {
                    sb.Append("Feeling the sudden burning of lactic acid in your " + consumer.build.HipsLongDescription() + ", you realize they have slimmed down and firmed up some.");

                    consumer.hips.ShrinkHips(2);
                }
                //Shrink t**s!
                if (consumer.breasts[0].cupSize > consumer.genitals.smallestPossibleMaleCupSize)
                {
                    consumer.breasts[0].MakeMale();
                }
            }

            //femininity if possible.
            if (Utils.Rand(3) == 0)
            {
                byte target, delta;
                if (isLarge)
                {
                    target = 0;
                    delta  = 8;
                }
                else
                {
                    target = 5;
                    delta  = 3;
                }
                sb.Append(consumer.ModifyFemininity(target, delta));
            }

            consumeItem = true;
            return(sb.ToString());
        }
Пример #7
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();

            sb.Append(InitialTransformText(target));
            target.DeltaCreatureStats(lus: 3, corr: 1);

            uint hpDelta;

            if (target.hasCock)
            {
                if (target.cocks[0].length < 12)
                {
                    CockData oldData = target.cocks[0].AsReadOnlyData();
                    double   temp    = target.cocks[0].IncreaseLength(Utils.Rand(2) + 2);
                    sb.Append(OneCockGrewLarger(target, oldData, temp));
                }
                hpDelta = 30;
            }
            else
            {
                hpDelta = 20;
            }

            if (target is CombatCreature healthCheck)
            {
                healthCheck.AddHP((uint)(hpDelta + healthCheck.toughness / 3));
                sb.Append(GainVitalityText(target));
            }
            if (remainingChanges <= 0)
            {
                return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
            }

            //Red or orange skin!
            if (Utils.Rand(30) == 0 && !Array.Exists(Species.IMP.availableTones, x => x == target.body.primarySkin.tone))
            {
                Tones oldSkinTone = target.body.primarySkin.tone;
                if (target.body.ChangeMainSkin(Utils.RandomChoice(Species.IMP.availableTones)))
                {
                    sb.Append(ChangeSkinColorText(target, oldSkinTone));

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


            //Shrinkage!
            if (Utils.Rand(2) == 0 && target.build.heightInInches > 42)
            {
                byte heightDelta = target.build.DecreaseHeight((byte)(1 + Utils.Rand(3)));
                if (heightDelta > 0)
                {
                    sb.Append(GetShorterText(target, heightDelta));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //Imp wings - I just kinda robbed this from demon changeCount ~Foxwells
            if (Utils.Rand(3) == 0 && ((target.wings.type != WingType.IMP && target.IsCorruptEnough(25)) || (target.wings.type == WingType.IMP && target.IsCorruptEnough(50))))
            {
                bool changedWings = false;
                //grow smalls to large
                if (target.wings.type == WingType.IMP)
                {
                    if (target.wings.GrowLarge())
                    {
                        sb.Append(EnlargenedImpWingsText(target));
                        changedWings = true;
                    }
                }
                else
                {
                    WingData oldData = target.wings.AsReadOnlyData();
                    if (target.UpdateWings(WingType.IMP))
                    {
                        sb.Append(GrowOrChangeWingsText(target, oldData));
                        changedWings = true;
                    }
                }

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

            //Imp tail, because that's a unique thing from what I see?
            if (Utils.Rand(3) == 0 && target.tail.type != TailType.IMP)
            {
                TailData oldData = target.tail.AsReadOnlyData();
                if (target.UpdateTail(TailType.IMP))
                {
                    sb.Append(GrowOrChangeTailText(target, oldData));
                    target.IncreaseCorruption(2);

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

            //Feets, needs red/orange skin and tail
            if (Species.IMP.availableTones.Contains(target.body.primarySkin.tone) && target.tail.type == TailType.IMP && target.lowerBody.type != LowerBodyType.IMP && Utils.Rand(3) == 0)
            {
                LowerBodyData oldData = target.lowerBody.AsReadOnlyData();
                if (target.UpdateLowerBody(LowerBodyType.IMP))
                {
                    sb.Append(ChangeLowerBodyText(target, oldData));

                    target.IncreaseCorruption(2);

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

            //Imp ears, needs red/orange skin and horns
            if (target.horns.type == HornType.IMP && Array.Exists(Species.IMP.availableTones, x => target.body.primarySkin.tone == x) && target.ears.type != EarType.IMP && Utils.Rand(3) == 0)
            {
                EarData oldData = target.ears.AsReadOnlyData();
                if (target.UpdateEars(EarType.IMP))
                {
                    sb.Append(ChangeEarsText(target, oldData));

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

            //Horns, because why not?
            if (target.horns.type != HornType.IMP && Utils.RandBool())
            {
                HornData oldData = target.horns.AsReadOnlyData();
                if (target.UpdateHorns(HornType.IMP))
                {
                    sb.Append(ChangeOrGrowHornsText(target, oldData));

                    target.IncreaseCorruption(2);

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

            //Imp arms, needs orange/red skin. Also your hands turn human.
            if (Species.IMP.availableTones.Contains(target.body.primarySkin.tone) && target.arms.type != ArmType.IMP && Utils.Rand(3) == 0)
            {
                ArmData oldData = target.arms.AsReadOnlyData();
                if (target.UpdateArms(ArmType.IMP))
                {
                    sb.Append(ChangeArmText(target, oldData));

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

            //Changes hair to red/dark red, shortens it, sets it normal, and makes it curly.
            if (!Species.IMP.availableHairColors.Contains(target.hair.hairColor) && Utils.Rand(3) == 0)
            {
                HairData oldHairData = target.hair.AsReadOnlyData();

                HairFurColors hairColor  = Utils.RandomChoice(Species.IMP.availableHairColors);
                double        hairLength = 1;

                if (target.hair.type != HairType.NORMAL)
                {
                    //also restarts hair growth if disabled.
                    target.UpdateHair(HairType.NORMAL, true, hairColor, newHairLength: hairLength, newStyle: HairStyle.CURLY);
                }
                else
                {
                    //also restarts hair growth if disabled.
                    target.hair.SetAll(hairLength, true, hairColor, style: HairStyle.CURLY);
                }
                sb.Append(HairChangedText(target, oldHairData));

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

            //Remove spare titties
            if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !hyperHappy)
            {
                BreastData toRemove = target.breasts[target.breasts.Count - 1].AsReadOnlyData();

                if (target.genitals.RemoveBreastRows() > 0)
                {
                    sb.Append(RemovedAnExtraRowOfBreasts(target, toRemove));

                    if (--remainingChanges <= 0)
                    {
                        return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
                    }
                }
            }
            //Shrink titties
            if (target.genitals.BiggestCupSize() > CupSize.FLAT && Utils.Rand(3) == 0 && !hyperHappy)
            {
                byte rowsAlreadyModified = 0;
                //temp3 stores how many rows are changed
                foreach (Breasts breast in target.breasts)
                {
                    //If this row is over threshhold
                    if (breast.cupSize > CupSize.FLAT)
                    {
                        CupSize oldSize = breast.cupSize;

                        byte delta;
                        //Big change
                        if (breast.cupSize > CupSize.EE_BIG)
                        {
                            delta = breast.ShrinkBreasts((byte)(2 + Utils.Rand(3)));
                        }
                        else
                        {
                            delta = breast.ShrinkBreasts(1);
                        }

                        if (delta != 0)
                        {
                            sb.Append(CurrentBreastRowChanged(target, breast.rowIndex, delta, rowsAlreadyModified));

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


            //Free extra nipple removal service
            if (target.genitals.hasQuadNipples && Utils.Rand(3) == 0)
            {
                target.genitals.SetQuadNipples(false);

                sb.Append(RemovedQuadNippleText(target));

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

            //Neck restore
            if (target.neck.type != NeckType.defaultValue && Utils.Rand(4) == 0)
            {
                NeckData oldData = target.neck.AsReadOnlyData();
                if (target.RestoreNeck())
                {
                    sb.Append(RestoredNeckText(target, oldData));

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

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

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

            //You lotta imp? Time to turn male!
            //Unless you're one of the hyper happy assholes I guess
            //For real tho doesn't seem like female imps exist? Guess they're goblins
            if (target.ImpScore() >= 4 && !hyperHappy)
            {
                bool         changedSomething = false;
                GenitalsData oldGenitals      = target.genitals.AsReadOnlyData();

                changedSomething |= target.genitals.RemoveExtraBreastRows() > 0;
                changedSomething |= target.breasts[0].MakeMale(true);
                changedSomething |= target.RemoveAllVaginas() > 0;

                if (!target.hasCock)
                {
                    changedSomething |= target.AddCock(CockType.HUMAN, 12, 2);
                }
                if (target.balls.count == 0)
                {
                    changedSomething |= target.genitals.GrowBalls(2);
                }

                if (changedSomething)
                {
                    sb.Append(ImpifiedText(target, oldGenitals));
                    remainingChanges--;
                    target.IncreaseCorruption(20);
                }
            }
            return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges));
        }