/// <summary>
                /// Update the mesh of the balls and set the visibility
                /// </summary>
                internal static void ReloadCharacterBalls(ChaControl chaControl, BallsData ballsData, bool showBalls)
                {
#if KK
                    if (chaControl.hiPoly == false)
                    {
                        return;
                    }
#endif

                    if (ballsData != null)
                    {
                        GameObject balls = CommonLib.LoadAsset <GameObject>(ballsData.File, ballsData.Asset, true);
                        foreach (var mesh in balls.gameObject.GetComponentsInChildren <SkinnedMeshRenderer>(true))
                        {
                            if (BallsParts.Contains(mesh.name))
                            {
                                UpdateMeshRenderer(chaControl, mesh, chaControl.objBody.GetComponentsInChildren <SkinnedMeshRenderer>(true).FirstOrDefault(x => x.name == mesh.name), true);
                            }
                        }

                        Destroy(balls);
                    }

                    SkinnedMeshRenderer ballsSMR = chaControl?.objBody?.GetComponentsInChildren <SkinnedMeshRenderer>(true).FirstOrDefault(x => x?.name == "o_dan_f");
                    if (ballsSMR != null)
                    {
                        ballsSMR.gameObject.GetComponent <Renderer>().enabled = showBalls;
                    }
                }
        protected bool ChangeCock(Creature target, StringBuilder sb)
        {
            if (HyperHappySettings.isEnabled)
            {
                return(false);
            }

            //find the largest c**k. shrink it. if it gets small enough to remove it, do so. if it's the only c**k the creature has, grow a v****a in its place.
            if (target.hasCock)
            {
                GenitalsData oldGenitals = target.genitals.AsReadOnlyData();
                C**k         largest     = target.genitals.LongestCock();

                if (largest.DecreaseLengthAndCheckIfNeedsRemoval(Utils.Rand(3) + 1))
                {
                    target.genitals.RemoveCock(largest);

                    if (!target.hasCock && !target.hasVagina)
                    {
                        target.AddVagina(.25);
                        target.IncreaseCorruption();

                        BallsData oldBalls = target.balls.AsReadOnlyData();
                        target.balls.RemoveAllBalls();
                    }
                }

                sb.Append(CockChangedText(target, oldGenitals, largest.cockIndex));
                return(true);
            }
            return(false);
        }
                private static void SetLineVisibility(ChaControl chaControl, BallsData ballsData)
                {
                    if (ballsData == null)
                    {
                        return;
                    }

                    foreach (ColorMatchPart colorMatchPart in ballsData.ColorMatchList)
                    {
                        SetLineVisibility(chaControl, colorMatchPart);
                    }
                }
                private static void SetSkinGloss(ChaControl chaControl, BallsData ballsData)
                {
                    if (ballsData == null)
                    {
                        return;
                    }

                    foreach (ColorMatchPart colorMatchPart in ballsData.ColorMatchList)
                    {
                        SetSkinGloss(chaControl, colorMatchPart);
                    }
                }
Пример #5
0
        public override void ApplyData(BallsData data, bool force = false)
        {
            for (int i = 0; i < size; i++)
            {
                transforms[i].localPosition = data.positions[i];
                materials[i].SetVector(positionId, data.positions[i]);
            }

            for (int i = 0; i < size; i++)
            {
                materials[i].SetVector(appearanceId, data.ballsAppearance[i]);
            }

            for (int i = 0; i < size; i++)
            {
                Vector4 thisPosition = data.positions[i];
                thisPosition.w = 0f;

                Vector4 n1 = data.positions[data._neighborIndexes[i, 0]] - thisPosition;
                Vector4 n2 = data.positions[data._neighborIndexes[i, 1]] - thisPosition;
                Vector4 n3 = data.positions[data._neighborIndexes[i, 2]] - thisPosition;
                Vector4 n4 = data.positions[data._neighborIndexes[i, 3]] - thisPosition;
                Vector4 n5 = data.positions[data._neighborIndexes[i, 4]] - thisPosition;
                Vector4 n6 = data.positions[data._neighborIndexes[i, 5]] - thisPosition;

                if (i == 0)
                {
                    Debug.LogFormat("{0} {1} {2}", (Vector3)n1, ((Vector3)n1).magnitude, n1.w);
                }

                materials[i].SetVector(neighborId1, n1);
                materials[i].SetVector(neighborId2, n2);
                materials[i].SetVector(neighborId3, n3);
                materials[i].SetVector(neighborId4, n4);
                materials[i].SetVector(neighborId5, n5);
                materials[i].SetVector(neighborId6, n6);
            }

            for (int i = 0; i < size; i++)
            {
                ApplyCachedVariables(materials[i]);
            }

            /*
             * if (data.highlightColorsDirty || force) {
             * material.SetVectorArray(highlightColorsArrayId, data.highlightColors);
             * data.highlightColorsDirty = false;
             * }
             *
             */
        }
Пример #6
0
 private static string EnlargedBallsText(Creature target, BallsData oldData)
 {
     if (oldData.count == 1)
     {
         return(GlobalStrings.NewParagraph() + "A wave of heat reaches your groin, quickly followed by a bit of discomfort. A sudden weight develops in your groin, alongside your uniball. " +
                "A quick examination confirms the obvious - your uniball is now a pair of regular balls.");
     }
     else if (oldData.size <= 2)
     {
         return(GlobalStrings.NewParagraph() + "A flash of warmth passes through you, finally settling in your groin, which feels heavier. You pause to examine the changes and " +
                "your roving fingers discover your " + target.balls.ShortDescription() + " have grown larger than a human's.");
     }
     else
     {
         return(GlobalStrings.NewParagraph() + "A sudden onset of heat envelops your groin, focusing on your " + target.balls.SackDescription() + ". Walking becomes difficult as you discover your " +
                target.balls.ShortDescription() + " have enlarged further.");
     }
 }
Пример #7
0
    public void Init(int size, BallsRendererSettings _settings = null)
    {
        settings = (_settings == null) ? BallsRendererSettings.Batch : _settings;

        bucketData = new BallsData(size);
        balls      = new BallInfo[size];
        for (int i = 0; i < size; i++)
        {
            balls[i] = new BallInfo(bucketData, i);
        }

        // Switch-case settings?
        if (settings.mode == Mode.BATCH)
        {
            bucket = new BallsBatchBucket(settings, this.transform);
        }
        else if (settings.mode == Mode.SINGLE)
        {
            bucket = new BallsListBucket(settings, size, this.transform);
        }

        isInit = true;
    }
Пример #8
0
        private void ApplyBatchData(BallsData data, bool force = false)
        {
            if (data.positionsDirty || force)
            {
                material.SetVectorArray(positionsArrayId, data.positions);
                data.positionsDirty = false;
            }

            if (data.ballsAppearanceDirty || force)
            {
                material.SetVectorArray(appearanceId, data.ballsAppearance);
                data.ballsAppearanceDirty = false;
            }

            if (data._neighborsDirty || force)
            {
                for (int i = 0; i < data.size; i++)
                {
                    data._atomsNeighbors1[i]   = new Vector4(data._neighborIndexes[i, 0], data._neighborIndexes[i, 1], data._neighborIndexes[i, 2], data._neighborIndexes[i, 3]) + INDEX_OFFSET * Vector4.one;
                    data._atomsNeighbors2[i].x = data._neighborIndexes[i, 4] + INDEX_OFFSET;
                    data._atomsNeighbors2[i].y = data._neighborIndexes[i, 5] + INDEX_OFFSET;
                }

                material.SetVectorArray(neighborsArrayId1, data._atomsNeighbors1);
                material.SetVectorArray(neighborsArrayId2, data._atomsNeighbors2);

                data._neighborsDirty = false;
            }

            if (data.highlightColorsDirty || force)
            {
                material.SetVectorArray(highlightColorsArrayId, data.highlightColors);
                data.highlightColorsDirty = false;
            }

            ApplyCachedVariables(material);
        }
 /// <summary>
 /// Set the skin line visibility for every color matching object configured in the manifest.xml
 /// </summary>
 internal static void SetLineVisibility(ChaControl chaControl, BodyData bodyData, PenisData penisData, BallsData ballsData)
 {
     SetLineVisibility(chaControl, bodyData);
     SetLineVisibility(chaControl, penisData);
     SetLineVisibility(chaControl, ballsData);
 }
Пример #10
0
 protected abstract string EnlargedBallsText(Creature target, BallsData oldData);
                internal static IEnumerator ReloadCharacterUncensor(ChaControl chaControl, BodyData bodyData, PenisData penisData, bool penisVisible, BallsData ballsData, bool ballsVisible)
                {
                    while (chaControl.objBody == null)
                    {
                        yield return(null);
                    }

                    if (ExType(chaControl) == 0) //exType of 1 indicates Janitor, don't modify his body.
                    {
                        ReloadCharacterBody(chaControl, bodyData);
                    }
                    ReloadCharacterPenis(chaControl, penisData, penisVisible);
                    ReloadCharacterBalls(chaControl, ballsData, ballsVisible);

                    UpdateSkin(chaControl, bodyData);
                    if (ExType(chaControl) == 0)
                    {
                        chaControl.updateBustSize = true;
                        Traverse.Create(chaControl).Method("UpdateSiru", new object[] { true }).GetValue();
                        SetChestNormals(chaControl, bodyData);

                        chaControl.customMatBody.SetTexture(ChaShader._AlphaMask, Traverse.Create(chaControl).Property("texBodyAlphaMask").GetValue() as Texture);
                        Traverse.Create(chaControl).Property("updateAlphaMask").SetValue(true);
                    }
                }
Пример #12
0
            //removed last/only c**k, obtained a v****a instead.
            protected override string MadeFemale(Creature target, CockData preChange, BallsData oldBalls)
            {
                bool removedBalls = oldBalls.hasBalls;

                StringBuilder sb = new StringBuilder();

                sb.Append("Your " + preChange.LongDescription() + " suddenly starts tingling. It's a familiar feeling, similar to an o****m. " +
                          "However, this one seems to start from the top down, instead of gushing up from your loins. You spend a few seconds frozen to the odd sensation, " +
                          "when it suddenly feels as though your own body starts sucking on the base of your shaft. Almost instantly, your c**k sinks " +
                          "into your crotch with a wet slurp. The tip gets stuck on the front of your body on the way down, but your glans soon loses all volume to turn " +
                          "into a shiny new c**t.");

                if (target.balls.hasBalls)
                {
                    sb.Append(" At the same time, your " + oldBalls.ShortDescription() + " fall victim to the same sensation; eagerly swallowed whole by your crotch.");
                }

                sb.Append(" Curious, you touch around down there, to find you don't have any exterior organs left. All of it got swallowed into the gash you " +
                          "now have running between two fleshy folds, like sensitive lips. It suddenly occurs to you; <b>you now have a v****a!</b>");

                return(sb.ToString());
            }
Пример #13
0
        /// <summary>
        /// Read all the manifest.xml files and generate a dictionary of uncensors to be used in config manager dropdown
        /// </summary>
        private static void PopulateUncensorLists()
        {
            BodyDictionary.Clear();
            BodyConfigListFull.Clear();
            PenisDictionary.Clear();
            PenisConfigListFull.Clear();
            BallsDictionary.Clear();
            BallsConfigListFull.Clear();

            //Add the default body options
            BodyConfigListFull["Random"] = "Random";

#if KK || EC
            BodyData DefaultMale = new BodyData(0, DefaultBodyMaleGUID, "Default Body M");
            BodyDictionary[DefaultMale.BodyGUID] = DefaultMale;
            BodyConfigListFull[$"[{(DefaultMale.Sex == 0 ? "Male" : "Female")}] {DefaultMale.DisplayName}"] = DefaultMale.BodyGUID;
#endif

            BodyData DefaultFemale = new BodyData(1, DefaultBodyFemaleGUID, "Default Body F");
            BodyDictionary[DefaultFemale.BodyGUID] = DefaultFemale;
            BodyConfigListFull[$"[{(DefaultFemale.Sex == 0 ? "Male" : "Female")}] {DefaultFemale.DisplayName}"] = DefaultFemale.BodyGUID;

            //Add the default penis options
            PenisConfigListFull["Random"] = "Random";

            PenisData DefaultPenis = new PenisData(DefaultPenisGUID, "Mosaic Penis");
            PenisDictionary[DefaultPenis.PenisGUID]       = DefaultPenis;
            PenisConfigListFull[DefaultPenis.DisplayName] = DefaultPenis.PenisGUID;

            //Add the default balls options
            BallsConfigListFull["Random"] = "Random";

            BallsData DefaultBalls = new BallsData(DefaultBallsGUID, "Mosaic Balls");
            BallsDictionary[DefaultBalls.BallsGUID]       = DefaultBalls;
            BallsConfigListFull[DefaultBalls.DisplayName] = DefaultBalls.BallsGUID;

            var loadedManifests = Sideloader.Sideloader.Manifests;

            foreach (var manifest in loadedManifests.Values)
            {
                XDocument manifestDocument        = manifest.manifestDocument;
                XElement  uncensorSelectorElement = manifestDocument?.Root?.Element(PluginNameInternal);
                if (uncensorSelectorElement != null && uncensorSelectorElement.HasElements)
                {
                    foreach (XElement uncensorElement in uncensorSelectorElement.Elements("body"))
                    {
                        BodyData bodyData = new BodyData(uncensorElement);
                        if (bodyData.BodyGUID == null)
                        {
                            Logger.LogWarning("Body failed to load due to missing GUID.");
                            continue;
                        }
                        if (bodyData.DisplayName == null)
                        {
                            Logger.LogWarning("Body failed to load due to missing display name.");
                            continue;
                        }
                        if (bodyData.OOBase == Defaults.OOBase)
                        {
                            Logger.LogWarning("Body was not loaded because oo_base is the default.");
                            continue;
                        }
                        BodyDictionary[bodyData.BodyGUID] = bodyData;
                        BodyConfigListFull[$"[{(bodyData.Sex == 0 ? "Male" : "Female")}] {bodyData.DisplayName}"] = bodyData.BodyGUID;
                        foreach (var part in bodyData.AdditionalParts)
                        {
                            AllAdditionalParts.Add(part);
                        }
                    }
                    foreach (XElement uncensorElement in uncensorSelectorElement.Elements("penis"))
                    {
                        PenisData penisData = new PenisData(uncensorElement);
                        if (penisData.PenisGUID == null)
                        {
                            Logger.LogWarning("Penis failed to load due to missing GUID.");
                            continue;
                        }
                        if (penisData.DisplayName == null)
                        {
                            Logger.LogWarning("Penis failed to load due to missing display name.");
                            continue;
                        }
                        if (penisData.File == null)
                        {
                            Logger.LogWarning("Penis failed to load due to missing file.");
                            continue;
                        }
                        if (penisData.Asset == null)
                        {
                            Logger.LogWarning("Penis failed to load due to missing asset.");
                            continue;
                        }
                        PenisDictionary[penisData.PenisGUID]       = penisData;
                        PenisConfigListFull[penisData.DisplayName] = penisData.PenisGUID;
                    }
                    foreach (XElement uncensorElement in uncensorSelectorElement.Elements("balls"))
                    {
                        BallsData ballsData = new BallsData(uncensorElement);
                        if (ballsData.BallsGUID == null)
                        {
                            Logger.LogWarning("Balls failed to load due to missing GUID.");
                            continue;
                        }
                        if (ballsData.DisplayName == null)
                        {
                            Logger.LogWarning("Balls failed to load due to missing display name.");
                            continue;
                        }
                        if (ballsData.File == null)
                        {
                            Logger.LogWarning("Balls failed to load due to missing file.");
                            continue;
                        }
                        if (ballsData.Asset == null)
                        {
                            Logger.LogWarning("Balls failed to load due to missing asset.");
                            continue;
                        }
                        BallsDictionary[ballsData.BallsGUID]       = ballsData;
                        BallsConfigListFull[ballsData.DisplayName] = ballsData.BallsGUID;
                    }
                    foreach (XElement uncensorElement in uncensorSelectorElement.Elements("migration"))
                    {
                        MigrationData migrationData = new MigrationData(uncensorElement);
                        if (migrationData.UncensorGUID == null)
                        {
                            Logger.LogWarning("Migration data failed to load due to missing Uncensor GUID.");
                            continue;
                        }
                        if (migrationData.BodyGUID == null)
                        {
                            Logger.LogWarning("Migration data failed to load due to missing Body GUID.");
                            continue;
                        }
                        MigrationDictionary[migrationData.UncensorGUID] = migrationData;
                    }
                }
            }
        }
 protected override string EnlargedBallsText(Creature target, BallsData oldData) => CaninePepperGeneric.EnlargedBallsText(target, oldData);
Пример #15
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));
        }
Пример #16
0
 public BallInfo(BallsData _data, int _index)
 {
     data  = _data;
     index = _index;
 }
Пример #17
0
 public abstract void ApplyData(BallsData data, bool force = false);
Пример #18
0
 public override void ApplyData(BallsData data, bool force = false)
 {
     ApplyBatchData(data, force);
 }
 /// <summary>
 /// Set the skin gloss for every color matching object configured in the manifest.xml
 /// </summary>
 internal static void SetSkinGloss(ChaControl chaControl, BodyData bodyData, PenisData penisData, BallsData ballsData)
 {
     SetSkinGloss(chaControl, bodyData);
     SetSkinGloss(chaControl, penisData);
     SetSkinGloss(chaControl, ballsData);
 }
Пример #20
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));
        }
Пример #21
0
        /// <summary>
        /// Read all the manifest.xml files and generate a dictionary of uncensors to be used in config manager dropdown
        /// </summary>
        private static void PopulateUncensorLists()
        {
            BodyDictionary.Clear();
            BodyConfigListFull.Clear();
            PenisDictionary.Clear();
            PenisConfigListFull.Clear();
            BallsDictionary.Clear();
            BallsConfigListFull.Clear();

            //Add the default body options
            BodyConfigListFull.Add("Random", "Random");

            BodyData DefaultMale = new BodyData(0, "Default.Body.Male", "Default Body M");

            BodyDictionary.Add(DefaultMale.BodyGUID, DefaultMale);
            BodyConfigListFull.Add($"[{(DefaultMale.Sex == 0 ? "Male" : "Female")}] {DefaultMale.DisplayName}", DefaultMale.BodyGUID);

            BodyData DefaultFemale = new BodyData(1, "Default.Body.Female", "Default Body F");

            BodyDictionary.Add(DefaultFemale.BodyGUID, DefaultFemale);
            BodyConfigListFull.Add($"[{(DefaultFemale.Sex == 0 ? "Male" : "Female")}] {DefaultFemale.DisplayName}", DefaultFemale.BodyGUID);

            //Add the default penis options
            PenisConfigListFull.Add("Random", "Random");

            PenisData DefaultPenis = new PenisData("Default.Penis", "Mosaic Penis");

            PenisDictionary.Add(DefaultPenis.PenisGUID, DefaultPenis);
            PenisConfigListFull.Add(DefaultPenis.DisplayName, DefaultPenis.PenisGUID);

            //Add the default balls options
            BallsConfigListFull.Add("Random", "Random");

            BallsData DefaultBalls = new BallsData("Default.Balls", "Mosaic Balls");

            BallsDictionary.Add(DefaultBalls.BallsGUID, DefaultBalls);
            BallsConfigListFull.Add(DefaultBalls.DisplayName, DefaultBalls.BallsGUID);

#if KK
            var loadedManifests = Sideloader.Sideloader.LoadedManifests;
#elif EC
            var loadedManifests = Sideloader.LoadedManifests;
#endif

            foreach (var manifest in loadedManifests)
            {
                XDocument manifestDocument        = manifest.manifestDocument;
                XElement  uncensorSelectorElement = manifestDocument?.Root?.Element(PluginNameInternal);
                if (uncensorSelectorElement != null && uncensorSelectorElement.HasElements)
                {
                    foreach (XElement uncensorElement in uncensorSelectorElement.Elements("body"))
                    {
                        BodyData bodyData = new BodyData(uncensorElement);
                        if (bodyData.BodyGUID == null)
                        {
                            Log(LogLevel.Warning, "Body failed to load due to missing GUID.");
                            continue;
                        }
                        if (bodyData.DisplayName == null)
                        {
                            Log(LogLevel.Warning, "Body failed to load due to missing display name.");
                            continue;
                        }
                        if (bodyData.OOBase == Defaults.OOBase)
                        {
                            Log(LogLevel.Warning, "Body was not loaded because oo_base is the default.");
                            continue;
                        }
                        BodyDictionary.Add(bodyData.BodyGUID, bodyData);
                        BodyConfigListFull.Add($"[{(bodyData.Sex == 0 ? "Male" : "Female")}] {bodyData.DisplayName}", bodyData.BodyGUID);
                        foreach (var part in bodyData.AdditionalParts)
                        {
                            AllAdditionalParts.Add(part);
                        }
                    }
                    foreach (XElement uncensorElement in uncensorSelectorElement.Elements("penis"))
                    {
                        PenisData penisData = new PenisData(uncensorElement);
                        if (penisData.PenisGUID == null)
                        {
                            Log(LogLevel.Warning, "Penis failed to load due to missing GUID.");
                            continue;
                        }
                        if (penisData.DisplayName == null)
                        {
                            Log(LogLevel.Warning, "Penis failed to load due to missing display name.");
                            continue;
                        }
                        if (penisData.File == null)
                        {
                            Log(LogLevel.Warning, "Penis failed to load due to missing file.");
                            continue;
                        }
                        if (penisData.Asset == null)
                        {
                            Log(LogLevel.Warning, "Penis failed to load due to missing asset.");
                            continue;
                        }
                        PenisDictionary.Add(penisData.PenisGUID, penisData);
                        PenisConfigListFull.Add(penisData.DisplayName, penisData.PenisGUID);
                    }
                    foreach (XElement uncensorElement in uncensorSelectorElement.Elements("balls"))
                    {
                        BallsData ballsData = new BallsData(uncensorElement);
                        if (ballsData.BallsGUID == null)
                        {
                            Log(LogLevel.Warning, "Balls failed to load due to missing GUID.");
                            continue;
                        }
                        if (ballsData.DisplayName == null)
                        {
                            Log(LogLevel.Warning, "Balls failed to load due to missing display name.");
                            continue;
                        }
                        if (ballsData.File == null)
                        {
                            Log(LogLevel.Warning, "Balls failed to load due to missing file.");
                            continue;
                        }
                        if (ballsData.Asset == null)
                        {
                            Log(LogLevel.Warning, "Balls failed to load due to missing asset.");
                            continue;
                        }
                        BallsDictionary.Add(ballsData.BallsGUID, ballsData);
                        BallsConfigListFull.Add(ballsData.DisplayName, ballsData.BallsGUID);
                    }
                    foreach (XElement uncensorElement in uncensorSelectorElement.Elements("migration"))
                    {
                        MigrationData migrationData = new MigrationData(uncensorElement);
                        if (migrationData.UncensorGUID == null)
                        {
                            Log(LogLevel.Warning, "Migration data failed to load due to missing Uncensor GUID.");
                            continue;
                        }
                        if (migrationData.BodyGUID == null)
                        {
                            Log(LogLevel.Warning, "Migration data failed to load due to missing Body GUID.");
                            continue;
                        }
                        MigrationDictionary.Add(migrationData.UncensorGUID, migrationData);
                    }
                }
            }
        }
Пример #22
0
 protected abstract string MadeFemale(Creature target, CockData preChange, BallsData oldBalls);