protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //for some unknown reason, demon tfs roll out their own chance system completely unique to them. ok. StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. int rando = Utils.Rand(100); int delta = target.GetExtraData()?.deltaTransforms ?? 0; if (delta != 0) { rando += 5 * delta + Utils.Rand(5 * delta); } //First, check if this tf has the male flag set (for male or herm tfs). If it does, add or grow cocks. if (desiredGender.HasFlag(Gender.MALE)) { byte addedCocks = 0; //if our initial roll was a crit, roll again. if we crit again, we may add several cocks, if possible. if (rando >= 85 && target.cocks.Count < Genitals.MAX_COCKS && Utils.Rand(10) < target.corruptionTrue / 25) { addedCocks = GrowCockGeneric(target, (byte)(Utils.Rand(2) + 2)); target.DeltaCreatureStats(lib: 3 * addedCocks, sens: 5 * addedCocks, lus: 10 * addedCocks); if (!isPurified) { target.IncreaseCorruption(8); } } //otherwise, only add a c**k if we have none or we originally rolled a crit (but failed to crit again) else if (target.cocks.Count == 0 || (rando >= 85 && target.cocks.Count < Genitals.MAX_COCKS)) { addedCocks = GrowCockGeneric(target, 1); target.DeltaCreatureStats(lib: 3, sens: 5, lus: 10); if (!isPurified) { target.IncreaseCorruption(5); } } //if that fails, it means we had a c**k already, or can't grow any more of them. else { C**k shortest = target.genitals.ShortestCock(); double lengthDelta; if (rando >= 45) { lengthDelta = shortest.IncreaseLength(Utils.Rand(3) + 3); shortest.IncreaseThickness(1); } else if (Utils.Rand(4) == 0) { lengthDelta = shortest.IncreaseLength(3); } else { lengthDelta = shortest.IncreaseLength(1); } target.DeltaCreatureStats(lib: 2, sens: 1, lus: 5 + lengthDelta * 3); if (!isPurified) { target.IncreaseCorruption(); } //no idea why this occurs, but ok. target.IncreaseIntelligence(1); } } //Otherwise, we're targeting female demon tfs only. this means we need to shrink (and possibly remove) the largest c**k the target has, unless hyper happy is on. else if (!hyperHappy && target.hasCock) { C**k largest = target.genitals.LongestCock(); //this loops through all the cocks and finds the longest. obviously, if the count is 1, this is simply the first element. //we'll need this if it gets removed. if not, this can still be used, this time to determine how much we shrunk. CockData oldData = largest.AsReadOnlyData(); //try decreasing it by 1-3. if this causes it to be removed instead, that's fine, but we need to know. //Note that this remove is now IN PLACE, not LAST. so if we remove the 3rd one, the old 4th is now 3rd, and so on. bool removed = target.genitals.ShrinkCockAndRemoveIfTooSmall(largest, Utils.Rand(3) + 1); } //Then, check if the tf has the female flag set (for female or herm tfs). if it does, add a v****a (if needed), and increase breast size. if (desiredGender.HasFlag(Gender.FEMALE)) { //don't currently have a v****a and herm tf or we're genderless, or it's a crit, or we rerolled a crit. if (!target.hasVagina && (desiredGender == Gender.HERM || target.gender == Gender.GENDERLESS || rando > 65 || Utils.Rand(3) == 0)) { target.genitals.AddVagina(VaginaType.HUMAN); } //do have one, and rolled a high crit. else if (target.hasVagina && rando >= 85) { foreach (V****a vag in target.vaginas) { //grow each c**t anywhere from 0.25in to 1in, if they are below the largest normal size. if (vag.c**t.length < vag.c**t.largestNormalSize) { vag.GrowClit((Utils.Rand(4) + 1) * 0.25); //cap it at the largest normal size. if (vag.c**t.length > vag.c**t.largestNormalSize) { vag.SetClitSize(vag.c**t.largestNormalSize); } } } } //do have one, rolled a crit, but not a high crit. else if (target.hasVagina && rando > 65) { target.HaveGenericVaginalOrgasm(0, true, true); target.vaginas.ForEach(x => x.IncreaseWetness()); } //now, breasts. these are the fallback, of sorts, so they grow larger when we don't crit. they also grow larger when we crit if we're targeting herms. if (rando < 85 || desiredGender == Gender.HERM) { //only occurs via herm. if (rando >= 85) { target.breasts.ForEach(x => x.GrowBreasts(3)); } else { byte temp = (byte)(1 + Utils.Rand(3)); CupSize largestSize = target.genitals.BiggestCupSize(); if (largestSize < CupSize.B && Utils.Rand(3) == 0) { temp++; } if (largestSize < CupSize.DD && Utils.Rand(4) == 0) { temp++; } if (largestSize < CupSize.DD_BIG && Utils.Rand(5) == 0) { temp++; } target.breasts.ForEach(x => x.GrowBreasts(temp)); } } } //if not, we're targeting male demon tfs only. this means we may need to shrink any overlarge breasts. Additionally, higher rng rolls may now remove vaginas. else if (!hyperHappy) { //if high crit: decrease bonus capacity, and remove a v****a, flat-out. if (rando >= 85 && target.hasVagina) { target.genitals.DecreaseBonusVaginalCapacity(5); target.genitals.RemoveVagina(); } //otherwise, if somewhat high, decrease bonus vaginal capacity (all vaginas), and wetness (last v****a). if this causes the bonus capacity to drop to 0 //and would cause it to go negative if we allowed that, remove the last v****a. else if (rando >= 65) { V****a lastVagina = target.vaginas[target.vaginas.Count - 1]; lastVagina.DecreaseWetness(1); //this is being super pedantic, but i'd prefer it lower the stat, then remove the v****a. hence this bool here. bool remove = target.genitals.standardBonusVaginalCapacity < 5; //decrease first. target.genitals.DecreaseBonusVaginalCapacity(5); //then remove it. if (remove) { target.genitals.RemoveVagina(); } } // if (target.genitals.BiggestCupSize() > target.genitals.smallestPossibleCupSize && (rando >= 85 || (rando > 65 && Utils.RandBool()) || (rando <= 65 && Utils.Rand(4) == 0))) { foreach (Breasts breast in target.breasts) { if (breast.cupSize > target.genitals.smallestPossibleCupSize) { byte amount = 1; if (rando >= 85) { amount++; } breast.ShrinkBreasts(amount); } } } } //never called in vanilla. if i read it correctly, it just initializes to 0 with a max of 1. int changeCount = base.GenerateChangeCount(target, new int[] { 3 }, 0, 0); if (changeCount == 0) { return(ApplyChangesAndReturn(target, sb, 0)); } int remainingChanges = changeCount; //Neck restore if (!target.neck.isDefault && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Demonic changes - higher chance with higher corruption. if (Utils.Rand(40) + target.corruption / 3 > 35 && !isPurified) { //Change tail if already horned. if (target.tail.type != TailType.DEMONIC && !target.horns.isDefault) { target.IncreaseCorruption(4); TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.DEMONIC); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //grow horns! if (target.horns.numHorns == 0 || (Utils.Rand(target.horns.numHorns + 3) == 0)) { if (target.horns.numHorns < 12 && (target.horns.type == HornType.NONE || target.horns.type == HornType.DEMON)) { if (target.horns.type == HornType.NONE) { target.UpdateHorns(HornType.DEMON); } target.IncreaseCorruption(3); } //Text for shifting horns else if (target.horns.type != HornType.DEMON) { target.UpdateHorns(HornType.DEMON); target.IncreaseCorruption(3); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Nipples Turn Back: if (target.genitals.hasBlackNipples && Utils.Rand(3) == 0) { target.genitals.SetBlackNipples(false); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //remove fur if (target.face.type != FaceType.HUMAN || (target.body.type != BodyType.HUMANOID && Utils.Rand(3) == 0)) { //Remove face before fur! if (target.face.type != FaceType.HUMAN) { target.RestoreFace(); } //De-fur else if (target.body.type != BodyType.HUMANOID) { target.RestoreBody(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Demon tongue if (target.tongue.type == TongueType.SNAKE && Utils.Rand(3) == 0) { TongueData oldData = target.tongue.AsReadOnlyData(); target.UpdateTongue(TongueType.DEMONIC); sb.Append(UpdateTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //foot changes - requires furless if (target.body.type == BodyType.HUMANOID && Utils.Rand(4) == 0) { bool changed; //Males/genderless get clawed feet if (!target.gender.HasFlag(Gender.FEMALE) || (target.gender == Gender.HERM && target.genitals.AppearsMoreMaleThanFemale())) { changed = target.UpdateLowerBody(LowerBodyType.DEMONIC_CLAWS); } //Females/futa get high heels else { changed = target.UpdateLowerBody(LowerBodyType.DEMONIC_HIGH_HEELS); } if (changed && --remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Grow demon wings if ((target.wings.type != WingType.BAT_LIKE || !target.wings.isLarge || target.back.type == BackType.SHARK_FIN) && Utils.Rand(8) == 0 && target.IsCorruptEnough(50)) { //grow smalls to large if (target.wings.type == WingType.BAT_LIKE && target.IsCorruptEnough(75)) { target.wings.GrowLarge(); } else { target.UpdateWings(WingType.BAT_LIKE); } if (target.back.type == BackType.SHARK_FIN) { target.RestoreBack(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
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(InitialTransformationText(target)); //No free changes, as of current implementation. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Transformation Changes //Gain Dragon Dick if (target.genitals.CountCocksOfType(CockType.DRAGON) < target.cocks.Count && Utils.Rand(3) == 0) { int temp = target.cocks.Count; //find all the non-dragon cocks, and choose one randomly. the beauty of linq and useful helper functions. C**k toChange = Utils.RandomChoice(target.cocks.Where(x => x.type != CockType.DRAGON).ToArray()); CockData oldData = toChange.AsReadOnlyData(); if (target.genitals.UpdateCockWithKnot(toChange, CockType.DRAGON, 1.3)) { //lose lust if sens>=50, gain lust if else target.IncreaseCreatureStats(sens: 10, lus: 10); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //-Remove 1 extra breast row if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !hyperHappy) { target.genitals.RemoveBreastRows(1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //no effect on oviposition. thus removed. //Gain Dragon Head if (Utils.Rand(3) == 0 && target.face.type != FaceType.DRAGON && draconicFace) { //OutputText("\n\nYou scream as your face is suddenly twisted; your facial bones begin rearranging themselves under your skin, restructuring into a long, narrow muzzle. Spikes of agony rip through your jaws as your teeth are brutally forced from your gums, giving you new rows of fangs - long, narrow and sharp. Your jawline begins to sprout strange growths; small spikes grow along the underside of your muzzle, giving you an increasingly inhuman visage.\n\nFinally, the pain dies down, and you look for a convenient puddle to examine your changed appearance.\n\nYour head has turned into a reptilian muzzle, with small barbs on the underside of the jaw. <b>You now have a dragon's face.</b>")); FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.DRAGON); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Existing horns become draconic, max of 4, max length of 1' if (target.horns.type != HornType.DRACONIC || target.horns.CanStrengthen && Utils.Rand(5) == 0) { if (target.UpdateOrStrengthenHorns(HornType.DRACONIC)) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Gain Dragon Ears if (Utils.Rand(3) == 0 && target.ears.type != EarType.DRAGON) { target.UpdateEars(EarType.DRAGON); //OutputText("\n\nA prickling sensation suddenly fills your ears; unpleasant, but hardly painful. It grows and grows until you can't stand it any more, and reach up to scratch at them. To your surprise, you find them melting away like overheated candles. You panic as they fade into nothingness, leaving you momentarily deaf and dazed, stumbling around in confusion. Then, all of a sudden, hearing returns to you. Gratefully investigating, you find you now have a pair of reptilian ear-holes, one on either side of your head. A sudden pain strikes your temples, and you feel bony spikes bursting through the sides of your head, three on either side, which are quickly sheathed in folds of skin to resemble fins. With a little patience, you begin to adjust these fins just like ears to aid your hearing. <b>You now have dragon ears!</b>")); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Gain Dragon Tongue if (Utils.Rand(3) == 0 && target.tongue.type != TongueType.DRACONIC) { //OutputText("\n\nYour tongue suddenly falls out of your mouth and begins undulating as it grows longer. For a moment it swings wildly, completely out of control; but then settles down and you find you can control it at will, almost like a limb. You're able to stretch it to nearly 4 feet and retract it back into your mouth to the point it looks like a normal human tongue. <b>You now have a draconic tongue.</b>")); TongueData oldData = target.tongue.AsReadOnlyData(); target.UpdateTongue(TongueType.DRACONIC); sb.Append(UpdateTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Note: This type of tongue should be eligible for all things you can do with demon tongue... Dunno if it's best attaching a boolean just to change the description or creating a whole new tongue type. } //(Pending Tongue Masturbation Variants; if we ever get around to doing that.) //Gain Dragon Scales if (target.body.type != BodyType.DRAGON && Utils.Rand(3) == 0) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.DRAGON); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //<mod name="Reptile eyes" author="Stadler76"> //Gain Dragon Eyes if (target.eyes.type != EyeType.DRAGON && target.body.type == BodyType.DRAGON && target.ears.type == EarType.DRAGON && target.horns.type == HornType.DRACONIC && Utils.Rand(4) == 0) { EyeData oldData = target.eyes.AsReadOnlyData(); target.UpdateEyes(EyeType.DRAGON); sb.Append(UpdateEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //</mod> //Gain Dragon Legs if (target.lowerBody.type != LowerBodyType.DRAGON && Utils.Rand(3) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.DRAGON); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Gain Dragon Tail if (target.tail.type != TailType.DRACONIC && Utils.Rand(3) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.DRACONIC); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Grow Dragon Wings or Enlarge small ones. if ((target.wings.type != WingType.DRACONIC || !target.wings.isLarge) && Utils.Rand(3) == 0) { //set to draconic wings. this will fail if we already have draconic wings. if (target.UpdateWings(WingType.DRACONIC)) { //do text, maybe? idk. } //it failed, which means we already have draconic wings. else { target.wings.GrowLarge(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // <mod name="BodyParts.RearBody" author="Stadler76"> //Gain Dragonic back //you need to have a reptilian arm, leg, and tail type, and, if we are allowing draconic face tfs, you must also have a draconic neck. //additionally, you must have a decent dragon score, which basically means some of those reptilian body parts must be draconic. if (fullStrength && !target.back.IsDraconic() && (target.neck.type == NeckType.DRACONIC || !draconicFace) && BacksideDraconicEnough(target) && Species.DRAGON.Score(target) >= 4 && Utils.Rand(3) == 0) { if (draconicBackIsMane) { target.UpdateBack(BackType.DRACONIC_MANE); } else { target.UpdateBack(BackType.DRACONIC_SPIKES); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // </mod> //Restore non dragon neck if (target.neck.type != NeckType.DRACONIC && !target.neck.isDefault && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Gain Dragon Neck //mod of a mod (again). it now handles other necks a little cleaner. //If you are considered a dragon-morph and if your backside is dragon-ish enough, your neck is eager to allow you to take a look at it, right? ;-) if (fullStrength && (target.neck.type != NeckType.DRACONIC || target.neck.canGrowNeck) && Species.DRAGON.Score(target) >= 6 && BacksideDraconicEnough(target) && target.face.type == FaceType.DRAGON) { if (target.neck.type != NeckType.DRACONIC) { target.UpdateNeck(NeckType.DRACONIC); } else { //4-8 byte nlChange = (byte)(4 + Utils.Rand(5)); // Note: hasNormalNeck checks the length, not the type! target.neck.GrowNeck(nlChange); } //note: draconic neck now positions behind the head regardless of length, though it probably wouldn't be noticable until neck is sufficiently long. //feel free to denote that when it hits full length here. if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //predator arms. (a mod of a mod. woo!. original mod: Stadler76.) //Gain Dragon Arms (Derived from ArmType.SALAMANDER) //requires draconic body, non-draconic arms, and either predator arms or draconic legs. let the arm update text handle the transformation (or do it manually, if you want) if (target.arms.type != ArmType.DRAGON && target.body.type == BodyType.DRAGON && (target.arms.type.IsPredatorArms() || target.lowerBody.type == LowerBodyType.DRAGON) && Utils.Rand(3) == 0) { target.UpdateArms(ArmType.DRAGON); //explain the change. if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // </mod> //Get Dragon Breath (Tainted version) //Can only be obtained if you are considered a dragon-morph, once you do get it though, it won't just go away even if you aren't a dragon-morph anymore. if (Species.DRAGON.Score(target) >= 4 && !target.HasPerk <DragonFire>()) { target.perks.AddPerk <DragonFire>(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //heat or rut, depending on gender. note that for full strength tfs, this uses the source gender. if (Species.DRAGON.Score(target) >= 4 && Utils.Rand(3) == 0 && target.gender > 0 && (!fullStrength || target.gender.CanHaveSexWith(sourceGender))) { if (target.hasCock && (sourceGender.HasFlag(Gender.FEMALE) || !fullStrength) && (!target.hasVagina || Utils.RandBool())) { //If hermaphrodite, the chance is 50/50. target.GoIntoRut(); } else { target.GoIntoHeat(); } } return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
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)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 2, 3 }, isEnhanced ? 3 : 1); int totalChanges = 0; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //STATS //Increase player str: if (target.strength < 60 && Utils.Rand(3) == 0) { double delta = target.IncreaseStrength((60 - target.strength) / 10.0); sb.Append(StrengthUpText(delta)); } //Increase player tou: if (target.toughness < 60 && Utils.Rand(3) == 0) { double delta = target.IncreaseToughness((60 - target.toughness) / 10.0); sb.Append(ToughnessUpText(delta)); } //Decrease player spd if it is over 30: if (target.relativeSpeed > 30 && target.speed > 30 && Utils.Rand(3) == 0) { double decrease = target.DecreaseSpeed((target.speed - 30) / 10.0); sb.Append(SpeedDownText(decrease)); } //Increase Corr, up to a max of 50. //this is silent, apparently. if (!isPurified && target.corruption < 50) { target.IncreaseCorruptionBy((50 - target.corruption) / 10.0); } if (totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } //Sex bits - Duderiffic if (target.cocks.Count > 0 && Utils.Rand(2) == 0 && !hyperHappy) { //If the player has at least one dick, decrease the size of each slightly, C**k biggest = target.genitals.LongestCock(); CockData preChange = biggest.AsReadOnlyData(); if (biggest.DecreaseLengthAndCheckIfNeedsRemoval(Utils.Rand(3) + 1)) { target.genitals.RemoveCock(biggest); if (target.cocks.Count == 0 && !target.hasVagina) { BallsData oldBalls = target.balls.AsReadOnlyData(); target.AddVagina(0.25); target.genitals.RemoveAllBalls(); sb.Append(MadeFemale(target, preChange, oldBalls)); } else { sb.Append(ShrunkOneCock(target, preChange, true)); } } else { sb.Append(ShrunkOneCock(target, preChange, false)); } if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //Sex bits - girly bool boobsGrew = false; //Increase player's breast size, if they are FF or bigger //do not increase size, but do the other actions: CupSize biggestCup = target.genitals.BiggestCupSize(); if ((biggestCup < CupSize.DD || (!isPurified && biggestCup < CupSize.FF)) && (isEnhanced || Utils.Rand(3) == 0)) { BreastData oldBreasts = target.breasts[0].AsReadOnlyData(); if (target.breasts[0].GrowBreasts((byte)(1 + Utils.Rand(3))) > 0) { boobsGrew = true; sb.Append(GrewFirstBreastRow(target, oldBreasts)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } target.DeltaCreatureStats(sens: .5); } //Remove feathery hair HairData oldHair = target.hair.AsReadOnlyData(); if (base.RemoveFeatheryHair(target)) { sb.Append(RemovedFeatheryHairText(target, oldHair)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //refresh the biggest cup size because we may have grown some breasts earlier. biggestCup = target.genitals.BiggestCupSize(); //If breasts are D or bigger and are not lactating, they also start lactating: if (biggestCup >= CupSize.D && !target.genitals.isLactating && (isEnhanced || boobsGrew || Utils.Rand(3) == 0)) { BreastData preLactation = target.breasts[0].AsReadOnlyData(); target.genitals.StartLactating(); sb.Append(StartedLactatingText(target, preLactation)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } target.DeltaCreatureStats(sens: .5); } //Quad nipples and other 'special isEnhanced things. if (isEnhanced) { //QUAD DAMAGE! if (!target.genitals.hasQuadNipples) { BreastCollectionData oldBreasts = target.genitals.allBreasts.AsReadOnlyData(); target.genitals.SetQuadNipples(true); sb.Append(GrantQuadNippleText(target, oldBreasts)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } else if (target.genitals.isLactating) { BreastCollectionData oldBreasts = target.genitals.allBreasts.AsReadOnlyData(); target.genitals.BoostLactation(2.5); double delta = 0; if (target.genitals.nippleLength < 1 || (target.genitals.nippleLength < 1.5 && !isPurified)) { delta = target.genitals.GrowNipples(0.25); target.DeltaCreatureStats(sens: .5); } sb.Append(BoostedLactationText(target, oldBreasts, delta)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } } //If breasts are already lactating and the player is not lactating beyond a reasonable level, they start lactating more: else { if (target.genitals.isLactating && (target.genitals.lactationStatus < LactationStatus.MODERATE || (!isPurified && target.genitals.lactationStatus < LactationStatus.STRONG)) && (isEnhanced || Utils.Rand(3) == 0)) { BreastCollectionData oldBreasts = target.genitals.allBreasts.AsReadOnlyData(); double delta = 0; target.genitals.BoostLactation(0.75); if (target.genitals.nippleLength < 1 || (target.genitals.nippleLength < 1.5 && !isPurified)) { delta = target.genitals.GrowNipples(0.25); target.DeltaCreatureStats(sens: .5); } sb.Append(BoostedLactationText(target, oldBreasts, delta)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } else if (isPurified && target.genitals.lactationStatus >= LactationStatus.STRONG) { BreastCollectionData oldData = target.genitals.allBreasts.AsReadOnlyData(); target.DeltaCreatureStats(sens: .5); target.genitals.BoostLactation(-1); sb.Append(BoostedLactationText(target, oldData, 0)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } } //If breasts are lactating at a fair level //and the player has not received this status, //apply an effect where the player really wants //to give their milk to other creatures //(capable of getting them addicted): biggestCup = target.genitals.BiggestCupSize(); if (!target.HasPerk <Feeder>() && target.genitals.lactationStatus >= LactationStatus.MODERATE && Utils.Rand(2) == 0 && biggestCup >= CupSize.DD && target.IsCorruptEnough(35)) { target.perks.AddPerk <Feeder>(); sb.Append(GainedFeederPerk(target)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //UNFINISHED //If player has addictive quality and drinks pure version, removes addictive quality. //if the player has a v****a and it is tight, it loosens. if (target.hasVagina) { VaginalLooseness targetLooseness = EnumHelper.Min(VaginalLooseness.LOOSE, target.genitals.maxVaginalLooseness); V****a tightest = target.genitals.TightestVagina(); if (tightest.looseness < targetLooseness && Utils.Rand(2) == 0) { VaginaData preLoosened = tightest.AsReadOnlyData(); tightest.IncreaseLooseness(2); sb.Append(LoosenedTwatText(target, preLoosened)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } target.DeltaCreatureStats(lus: 10); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //Rear body restore if (target.back.type != BackType.SHARK_FIN && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //Ovi perk loss if (!isPurified && target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //General Appearance (Tail -> Ears -> Paws(fur stripper) -> Face -> Horns //Give the player a bovine tail, same as the minotaur if (!isPurified && target.tail.type != TailType.COW && Utils.Rand(3) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.COW); sb.Append(ChangedTailText(target, oldData)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //Give the player bovine ears, same as the minotaur if (!isPurified && target.ears.type != EarType.COW && Utils.Rand(4) == 0 && target.tail.type == TailType.COW) { EarData oldEars = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.COW); sb.Append(ChangedEarsText(target, oldEars)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //If the player is under 7 feet in height, increase their height, similar to the minotaur if (((isEnhanced && target.build.heightInInches < 96) || target.build.heightInInches < 84) && Utils.Rand(2) == 0) { int temp = Utils.Rand(5) + 3; //Slow rate of growth near ceiling if (target.build.heightInInches > 74) { temp = (int)Math.Floor(temp / 2.0); } //Never 0 if (temp == 0) { temp = 1; } byte delta = target.build.IncreaseHeight((byte)temp); sb.Append(GrowTaller(target, delta)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //Give the player hoofs, if the player already has hoofs STRIP FUR if (!isPurified && target.lowerBody.type != LowerBodyType.HOOVED && target.ears.type == EarType.COW) { if (Utils.Rand(3) == 0) { var oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.HOOVED); sb.Append(ChangedLowerBodyText(target, oldData)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } } //If the player's face is non-human, they gain a human face if (!isEnhanced && target.lowerBody.type == LowerBodyType.HOOVED && target.face.type != FaceType.HUMAN && Utils.Rand(4) == 0) { //Remove face before fur! var oldData = target.face.AsReadOnlyData(); target.RestoreFace(); sb.Append(RestoredFaceText(target, oldData)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //isEnhanced get shitty fur var targetFur = new FurColor(HairFurColors.BLACK, HairFurColors.WHITE, FurMulticolorPattern.SPOTTED); if (isEnhanced && (!target.body.IsFurBodyType() || !target.body.mainEpidermis.fur.Equals(targetFur))) { var oldData = target.body.AsReadOnlyData(); if (target.body.type == BodyType.SIMPLE_FUR) { target.body.ChangeMainFur(targetFur); } else { target.UpdateBody(BodyType.SIMPLE_FUR, targetFur); } if (!oldData.IsFurBodyType()) { sb.Append(ChangedBodyText(target, oldData)); } else { sb.Append(ChandedFurToSpots(target, oldData)); } if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //if isEnhanced to probova give a shitty cow face else if (isEnhanced && target.face.type != FaceType.COW_MINOTAUR) { var oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.COW_MINOTAUR); sb.Append(ChangedFaceText(target, oldData)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //Give the player bovine horns, or increase their size, same as the minotaur //New horns or expanding mino horns if (!isPurified && Utils.Rand(3) == 0) { var oldHorns = target.horns.AsReadOnlyData(); if (target.horns.type != HornType.BOVINE) { target.UpdateHorns(HornType.BOVINE); sb.Append(ChangedHornsText(target, oldHorns)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } else if (target.horns.type == HornType.BOVINE && target.horns.CanStrengthen && target.horns.significantHornSize < 5) { target.horns.StrengthenTransform(); sb.Append(MadeHornsBigger(target, oldHorns)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } } //Increase the size of the player's hips, if they are not already childbearing or larger if (Utils.Rand(2) == 0 && target.hips.size < 15) { if (isPurified && target.hips.size < 8 || !isPurified) { var oldHips = target.hips.AsReadOnlyData(); if (target.build.GrowHips((byte)(1 + Utils.Rand(4))) > 0) { sb.Append(WidenedHipsText(target, oldHips)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } } } // Remove gills if (Utils.Rand(4) == 0 && target.gills.type != GillType.NONE) { var oldData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RestoredGillsText(target, oldData)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //Increase the size of the player's ass (less likely then hips), if it is not already somewhat big if (Utils.Rand(2) == 0 && target.butt.size < 8 || (!isPurified && target.butt.size < 13)) { var oldButt = target.butt.AsReadOnlyData(); if (target.butt.GrowButt((byte)(1 + Utils.Rand(2))) > 0) { sb.Append(GrewButtText(target, oldButt)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } } //Nipples Turn Back: if (target.genitals.hasBlackNipples && Utils.Rand(3) == 0) { target.genitals.SetBlackNipples(false); sb.Append(RemovedBlackNippleText(target)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //Debugcunt if (target.hasVagina && target.vaginas.Any(x => x.type != VaginaType.defaultValue) && Utils.Rand(3) == 0) { var oldCollection = target.genitals.allVaginas.AsReadOnlyData(); target.vaginas.ForEach(x => target.genitals.RestoreVagina(x)); sb.Append(RestoreAllVaginasText(target, oldCollection)); if (++totalChanges >= changeCount) { return(FinalizeTransformation(target, sb, totalChanges)); } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(FinalizeTransformation(target, sb, changeCount - totalChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 2 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. double crit = 0; if (Utils.Rand(100) < 15) { crit = Utils.Rand(20) / 10.0 + 2; } bool hasCrit() => crit > 1; sb.Append(InitialTransformationText(target, crit)); //STAT CHANGES - TOU SPE INT RANDOM CHANCE, LIB LUST COR ALWAYS UPPED target.DeltaCreatureStats(lib: 1 + Utils.Rand(2), lus: 5 + Utils.Rand(10), corr: 1 + Utils.Rand(5)); if (target.relativeToughness < 70 && Utils.Rand(3) == 0) { double delta = target.ChangeToughness(crit); sb.Append(IncreasedToughnessText(crit, delta)); } if (target.relativeSpeed > 30 && Utils.Rand(7) == 0) { double loss = target.DecreaseSpeed(crit); sb.Append(DecreasedSpeedText(crit, loss)); } if (target.relativeIntelligence < 60 && Utils.Rand(7) == 0) { double delta = target.ChangeIntelligence(crit); sb.Append(IncreasedIntelligenceText(crit, delta)); } //handle edge case where we start at 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Non-free changes. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //MUTATIONZZZZZ //PRE-CHANGES: become biped, remove horns, remove wings, give human tongue, remove claws, remove antennea //no claws if (Utils.Rand(4) == 0 && target.arms.hands.isClaws) { ArmData oldData = target.arms.AsReadOnlyData(); if (target.RestoreArms()) { sb.Append(RestoredArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //remove antennae if (target.antennae.type != AntennaeType.NONE && Utils.Rand(3) == 0) { AntennaeData oldData = target.antennae.AsReadOnlyData(); target.RestoreAntennae(); sb.Append(RestoredAntennaeText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //remove horns if (target.horns.type != HornType.NONE && Utils.Rand(3) == 0) { HornData oldData = target.horns.AsReadOnlyData(); target.RestoreHorns(); sb.Append(RestoredHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //remove wings if ((target.wings.type != WingType.NONE || target.back.type == BackType.SHARK_FIN) && Utils.Rand(3) == 0) { BackData oldBack = target.back.AsReadOnlyData(); if (target.back.type == BackType.SHARK_FIN) { target.RestoreBack(); } WingData oldWings = target.wings.AsReadOnlyData(); target.RestoreWings(); sb.Append(RestoredBackAndWings(target, oldWings, oldBack)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //give human tongue if (target.tongue.type != TongueType.HUMAN && Utils.Rand(3) == 0) { //MOD NOTE: this was incorrect - this actually was === (equal with strict typechecking) instead of = (assign). so this was an implicit bool. //this is part of the reason why the rework forces all value updates as function calls. the more you know. TongueData oldData = target.tongue.AsReadOnlyData(); target.RestoreTongue(); sb.Append(RestoredTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //remove non-wolf eyes if (Utils.Rand(3) == 0 && target.eyes.type != EyeType.HUMAN && target.eyes.type != EyeType.WOLF) { EyeData oldData = target.eyes.AsReadOnlyData(); target.RestoreEyes(); sb.Append(RestoredEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //normal legs if (target.lowerBody.type != LowerBodyType.WOLF && Utils.Rand(4) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.RestoreLowerBody(); sb.Append(RestoredLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //normal arms if (Utils.Rand(4) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.RestoreArms(); sb.Append(RestoredArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } HairData oldHair = target.hair.AsReadOnlyData(); //remove feather hair if (Utils.Rand(4) == 0 && RemoveFeatheryHair(target)) { sb.Append(RemovedFeatheryHairText(target, oldHair)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //remove basilisk hair if (Utils.Rand(4) == 0 && target.hair.IsBasiliskHair()) { HairData oldData = target.hair.AsReadOnlyData(); target.RestoreHair(); target.hair.SetHairLength(0); target.hair.ResumeNaturalGrowth(); sb.Append(RemovedBasiliskHair(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //MUTATIONZ AT ANY TIME: wolf dick, add/decrease breasts, decrease breast size if above D //get a wolf dick //if ya genderless we give ya a dick cuz we nice like that //MOD: Now respects hyper happy. if (target.gender == Gender.GENDERLESS && !hyperHappy) { target.genitals.AddCock(CockType.WOLF, Utils.Rand(4) + 4, Utils.Rand(8) / 4.0 + 0.25, 1.5); sb.Append(BecameMaleByGrowingCock(target)); target.DeltaCreatureStats(lib: 3, sens: 2, lus: 25); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //if ya got a dick that's ok too we'll change it to wolf //MOD NOTE: but only if you don't only have wolf cocks, of course (because if they are all wolf cocks, we can't make anything wolf cocks, duh). if (target.hasCock && !target.genitals.OnlyHasCocksOfType(CockType.WOLF) && Utils.RandBool()) { //MOD NOTE: no longer shamelessly copy/pasted from dog c**k, because we have better ways of looping in C#. C**k firstNonWolf = target.cocks.First(x => x.type != CockType.WOLF); CockData oldData = firstNonWolf.AsReadOnlyData(); //Select first non-wolf c**k //MOD: now using type description because we can, so the fact this used generic is irrelevant now :) if (firstNonWolf.type == CockType.HORSE) { //horses get changes if (firstNonWolf.length > 6) { firstNonWolf.DecreaseLength(2); } else { firstNonWolf.DecreaseLength(.5); } firstNonWolf.IncreaseThickness(.5); } target.DeltaCreatureStats(sens: 3, lus: 5 * crit); target.genitals.UpdateCockWithKnot(firstNonWolf, CockType.WOLF, 1.5); firstNonWolf.IncreaseThickness(2); sb.Append(ChangedCockToWolf(target, oldData, oldData.cockIndex)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //titties for those who got titties //wolfs have 8 nips so, 4 rows max. fen has no power here I'm making a wolf not a dog. //MOD: still stolen shamelessly from dog and updated, but now with the modern format. woo! //MOD: if breasts can be updated (not flat) and we flip a coin. if (target.genitals.BiggestCupSize() > CupSize.FLAT && Utils.RandBool()) { if (target.breasts.Count < 4) { byte breastCount = (byte)target.breasts.Count; BreastCollectionData oldBreastData = target.genitals.allBreasts.AsReadOnlyData(); //in vanilla code, we had some strange checks here that required the first row (and only the first row) be a certain size. //now, we check all the rows, but no longer require any of them to be a certain size. instead, if it's not the right size, we make it that size, //then add the row. so, for two rows, your first row must be at least a B-Cup. for 3: first must be a C cup, second an A cup. //if we supported 4 rows, it'd be D, B, A. for (int x = 0; x < target.breasts.Count; x++) { CupSize requiredSize = (CupSize)(byte)(target.breasts.Count - x); if (x == 0) { requiredSize++; } if (target.breasts[x].cupSize < requiredSize) { target.breasts[x].SetCupSize(requiredSize); } } target.genitals.AddBreastRow(target.breasts[breastCount - 1].cupSize.ByteEnumSubtract(1)); bool doCrit = false, uberCrit = false; if (target.breasts.Count == 2) { target.IncreaseCreatureStats(lus: 5, sens: 6); } else if (hasCrit()) { doCrit = true; if (crit > 2) { target.IncreaseCreatureStats(sens: 6, lus: 15); uberCrit = true; } else { target.IncreaseCreatureStats(sens: 3, lus: 10); } } sb.Append(UpdateAndGrowAdditionalRowText(target, oldBreastData, doCrit, uberCrit)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else { BreastCollectionData oldBreastData = target.genitals.allBreasts.AsReadOnlyData(); //only call the normalize text if we actually did anything. related, anthro breasts now returns a bool. thanks, fox tfs. if (target.genitals.AnthropomorphizeBreasts()) { sb.Append(NormalizedBreastSizeText(target, oldBreastData)); } } } //Remove a breast row if over 4. afaik this should never happen. if (target.breasts.Count > 4 && Utils.Rand(3) == 0) { var oldData = target.breasts[target.breasts.Count - 1].AsReadOnlyData(); target.genitals.RemoveBreastRows(1); sb.Append(RemovedExcessRow(target, oldData)); } //Grow breasts if has v****a and has no breasts/nips //Shrink breasts if over D-cup CupSize targetSize = EnumHelper.Max(target.genitals.smallestPossibleCupSize, CupSize.D); if (!hyperHappy && target.genitals.BiggestCupSize() > targetSize && Utils.Rand(3) == 0) { BreastCollectionData oldCollection = target.genitals.allBreasts.AsReadOnlyData(); bool changedAnything = false; foreach (Breasts row in target.breasts) { //If this row is over threshhold if (row.cupSize > targetSize) { //Big change if (row.cupSize > CupSize.EE_BIG) { changedAnything |= row.ShrinkBreasts((byte)(2 + Utils.Rand(3))) > 0; } //Small change else { changedAnything |= row.ShrinkBreasts() > 0; } //Increment changed rows } } //Count shrinking if (changedAnything) { sb.Append(ShrunkRowsText(target, oldCollection)); remainingChanges--; if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //MUTATIONZ LEVEL 1: fur, stop hair growth, ears, tail //Gain fur if (Utils.Rand(5) == 0 && !target.body.IsFurBodyType()) { BodyData oldData = target.body.AsReadOnlyData(); Species.WOLF.GetRandomFurColors(out FurColor primary, out FurColor underbody); if (FurColor.IsNullOrEmpty(underbody)) { target.UpdateBody(BodyType.UNDERBODY_FUR, primary); } else { target.UpdateBody(BodyType.UNDERBODY_FUR, primary, underbody); } sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ears time if (Utils.Rand(3) == 0 && target.ears.type != EarType.WOLF) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.WOLF); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Wolf tail if (Utils.Rand(3) == 0 && target.tail.type != TailType.WOLF) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.WOLF); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Sets hair normal if (target.hair.type != HairType.NORMAL && Utils.Rand(3) == 0) { HairData oldData = target.hair.AsReadOnlyData(); target.RestoreHair(); sb.Append(RestoreHairText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //MUTATIONZ LEVEL 2: fur->arms fur+tail+ears->face stophair->nohair fur+tail->legs //gain wolf face if (target.face.type != FaceType.WOLF && target.ears.type == EarType.WOLF && target.tail.type == TailType.WOLF && target.body.IsFurBodyType() && Utils.Rand(5) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.WOLF); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //legz if (target.lowerBody.legCount == 2 && target.lowerBody.type != LowerBodyType.WOLF && target.tail.type == TailType.WOLF && target.body.IsFurBodyType() && Utils.Rand(4) == 0) { //Hooman feets //Hooves -> Paws LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.WOLF); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //MUTATIONZ LEVEL 3: face->eyes if (target.eyes.type != EyeType.WOLF && target.face.type == FaceType.WOLF && Utils.Rand(4) == 0) { EyeData oldData = target.eyes.AsReadOnlyData(); target.UpdateEyes(EyeType.WOLF); sb.Append(UpdateEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //MISC CRAP //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0) { var res = target.build.ChangeMuscleToneToward(100, 4); sb.Append(AdjustToneText(target, 4, res)); } if (Utils.Rand(3) == 0) { var adjustedAmount = target.build.ChangeThicknessToward(75, 3); sb.Append(AdjustThicknessText(target, adjustedAmount)); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }