protected bool ChangeEars(Creature target, StringBuilder sb) { EarData oldData = target.ears.AsReadOnlyData(); if (target.UpdateEars(EarType.ELFIN)) { sb.Append(ChangeEarText(target, oldData)); return(true); } return(false); }
protected override string ChangeEarText(Creature target, EarData oldEars) { return("There is a tingling on the sides of your head as your ears change to pointed elfin ears."); }
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; int changeCount = GenerateChangeCount(target, new int[] { 2, 2, 3, 4 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); #warning fix me int ngPlus(int value) => value; int currentChanges() => changeCount - remainingChanges; //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //clear screen //Statistical changes: //-Reduces speed down to 50. if (target.speed > ngPlus(50) && Utils.Rand(4) == 0) { target.ChangeSpeed(-1); } //-Raises toughness to 70 //(+3 to 40, +2 to 55, +1 to 70) if (target.toughness < ngPlus(70) && Utils.Rand(3) == 0) { //(+3) if (target.toughness < ngPlus(40)) { target.ChangeToughness(3); } //(+2) else if (target.toughness < ngPlus(55)) { target.ChangeToughness(2); } //(+1) else { target.ChangeToughness(1); } } //-Reduces sensitivity. if (target.relativeSensitivity > 20 && Utils.Rand(3) == 0) { target.ChangeSensitivity(-1); } //Raises libido greatly to 50, then somewhat to 75, then slowly to 100. if (target.relativeLibido < 100 && Utils.Rand(3) == 0) { //+3 lib if less than 50 if (target.relativeLibido < 50) { target.ChangeLibido(1); } //+2 lib if less than 75 if (target.relativeLibido < 75) { target.ChangeLibido(1); } //+1 if above 75. target.ChangeLibido(1); } //Sexual Changes: //-Lizard dick - first one if (target.cocks.Any(x => x.type != CockType.LIZARD) && target.cocks.Count > 0 && ScalingChance(2, currentChanges(), 4)) { //Find the first non-lizzy dick C**k firstNonLizard = target.cocks.First(x => x.type != CockType.LIZARD); //Actually xform it nau target.genitals.UpdateCock(firstNonLizard, CockType.LIZARD); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } target.DeltaCreatureStats(lib: 3, lus: 10); } //(CHANGE OTHER DICK) int lizardCocks = target.genitals.CountCocksOfType(CockType.LIZARD); //Requires 1 lizard c**k, multiple cocks if (target.hasCock && (lizardCocks != target.cocks.Count || target.cocks.Count == 1) && ScalingChance(2, currentChanges(), 4)) { if (target.cocks.Count > 1) { C**k nonLizard = target.cocks.First(x => x.type != CockType.LIZARD); target.genitals.UpdateCock(nonLizard, CockType.LIZARD); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } target.DeltaCreatureStats(lib: 3, lus: 10); } else { target.genitals.AddCock(CockType.LIZARD, target.cocks[0].length, target.cocks[0].girth); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } target.DeltaCreatureStats(lib: 3, lus: 10); } } //MOD NOTE: REMOVED. SEE RANT IN SALAMANDER TRANSFORMATIONS //--Worms leave if 100% lizard dicks? //Require mammals? //if (target.genitals.CountCocksOfType(CockType.LIZARD) == target.cocks.Count && target.hasStatusEffect(StatusEffects.Infested)) //{ // target.removeStatusEffect(StatusEffects.Infested); // if (--remainingChanges <= 0) // { // return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); // } //} //-Breasts vanish to 0 rating if male if (target.genitals.BiggestCupSize() > target.genitals.smallestPossibleMaleCupSize && target.gender == Gender.MALE && ScalingChance(2, currentChanges(), 3)) { //(HUEG) foreach (Breasts breast in target.breasts) { if (breast.cupSize > CupSize.E_BIG) { breast.ShrinkBreasts(((byte)breast.cupSize).div(2)); } else { breast.SetCupSize(target.genitals.smallestPossibleMaleCupSize); } } //(+2 speed) target.IncreaseSpeed(2); target.ChangeLibido(2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Lactation stoppage. if (target.genitals.isLactating && ScalingChance(2, currentChanges(), 4)) { if (target.HasPerk <Feeder>()) { target.RemovePerk <Feeder>(); } target.genitals.SetLactationTo(LactationStatus.NOT_LACTATING); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Nipples reduction to 1 per tit. if (target.genitals.hasQuadNipples && ScalingChance(2, currentChanges(), 4)) { target.genitals.SetQuadNipples(false); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove extra breast rows if (target.breasts.Count > 1 && ScalingChance(2, currentChanges(), 3) && !hyperHappy) { target.RemoveExtraBreastRows(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-VAGs if (target.hasVagina && target.womb.canObtainOviposition && Species.LIZARD.Score(target) > 3 && ScalingChance(3, currentChanges(), 5)) { target.womb.GrantOviposition(); sb.Append(GrantOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Physical changes: //-Existing horns become draconic, max of 4, max length of 1' if (target.horns.type != HornType.DRACONIC && ScalingChance(3, currentChanges(), 5)) { HornData oldData = target.horns.AsReadOnlyData(); target.UpdateHorns(HornType.DRACONIC); sb.Append(UpdateHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && ScalingChance(2, currentChanges(), 4)) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && ScalingChance(3, currentChanges(), 5)) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Hair changes if (ScalingChance(2, currentChanges(), 4)) { if (target.eyes.type == EyeType.BASILISK) { if (target.corruption > 65 && target.face.IsReptilian() && target.body.type == BodyType.REPTILE && target.hair.type != HairType.BASILISK_SPINES) { HairData oldData = target.hair.AsReadOnlyData(); target.UpdateHair(HairType.BASILISK_SPINES); sb.Append(UpdateHairText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else if (target.corruption < 15 && target.gender.HasFlag(Gender.FEMALE) && target.hair.type != HairType.BASILISK_PLUME) { target.hair.ResumeNaturalGrowth(); HairData oldData = target.hair.AsReadOnlyData(); target.UpdateHair(HairType.BASILISK_PLUME); sb.Append(UpdateHairText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } else { target.hair.StopNaturalGrowth(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Remove beard! #warning BEARDS NOT YET IMPLEMENTED //Mod note: Nords are so serious about beards. ... Mai'q thinks they wish they had GLORIOUS pubes like Khajiit. //if (target.hasBeard() && ScalingChance(2, currentChanges(), 3)) //{ // target.beard.length = 0; // target.beard.style = 0; //} //Big physical changes: //-Legs – Draconic, clawed feet if (target.lowerBody.type != LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5)) { //Hooves - //TAURS - //feet types - //Else – LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.LIZARD); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // <mod name="Predator arms" author="Stadler76"> //Gain predator arms //Claw transition //MOD OF MOD (LOL): predator arms are a collection of arm types now, not a single type. thus, we can combine the claw and type checks. //now, you get the arms (and thus, the claws) if they aren't a predator type and have lizard legs, or switch to lizard arms from any other predator type. if ((target.arms.IsPredatorArms() || target.lowerBody.type == LowerBodyType.LIZARD) && target.body.type == BodyType.REPTILE && ScalingChance(2, currentChanges(), 3)) { ArmData oldData = target.arms.AsReadOnlyData(); target.UpdateArms(ArmType.LIZARD); sb.Append(UpdateArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // </mod> //-Tail – sinuous lizard tail if (target.tail.type != TailType.LIZARD && target.lowerBody.type == LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5)) { //No tail //Yes tail TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.LIZARD); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove odd eyes if (ScalingChance(3, currentChanges(), 5) && target.eyes.type != EyeType.HUMAN && !target.eyes.isReptilian) { EyeData oldData = target.eyes.AsReadOnlyData(); target.RestoreEyes(); sb.Append(RestoredEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Ears become smaller nub-like openings? if (target.ears.type != EarType.LIZARD && target.tail.type == TailType.LIZARD && target.lowerBody.type == LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5)) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.LIZARD); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Scales – color changes to red, green, white, blue, or black. Rarely: purple or silver. if (target.body.type != BodyType.REPTILE && target.ears.type == EarType.LIZARD && target.tail.type == TailType.LIZARD && target.lowerBody.type == LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5)) { Species.LIZARD.GetRandomSkinTone(out Tones primary, out Tones underbody); target.UpdateBody(BodyType.REPTILE, primary, underbody); //kGAMECLASS.rathazul.addMixologyXP(20); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Lizard-like face. if (target.face.type != FaceType.LIZARD && target.body.type == BodyType.REPTILE && target.ears.type == EarType.LIZARD && target.tail.type == TailType.LIZARD && target.lowerBody.type == LowerBodyType.LIZARD && ScalingChance(3, currentChanges(), 5)) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.LIZARD); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Lizard tongue if (target.tongue.type == TongueType.SNAKE && Utils.Rand(10) < 6) { // Higher (60%) chance to be 'fixed' if old variant TongueData oldData = target.tongue.AsReadOnlyData(); target.UpdateTongue(TongueType.LIZARD); sb.Append(UpdateTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.tongue.type != TongueType.LIZARD && target.tongue.type != TongueType.SNAKE && target.face.IsReptilian() && ScalingChance(2, currentChanges(), 3)) { TongueData oldData = target.tongue.AsReadOnlyData(); target.UpdateTongue(TongueType.LIZARD); sb.Append(UpdateTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove Gills if (ScalingChance(2, currentChanges(), 4) && !target.gills.isDefault) { GillData oldData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RestoredGillsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //<mod name="Reptile eyes" author="Stadler76"> //-Lizard eyes if (target.eyes.type != EyeType.LIZARD && target.face.type == FaceType.LIZARD && target.body.type == BodyType.REPTILE && target.ears.type == EarType.LIZARD && ScalingChance(2, currentChanges(), 4)) { EyeData oldData = target.eyes.AsReadOnlyData(); target.UpdateEyes(EyeType.LIZARD); sb.Append(UpdateEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //</mod> //FAILSAFE CHANGE if (remainingChanges == changeCount) { (target as CombatCreature)?.AddHP(50); target.ChangeLust(3); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //huh. no rng rolls, just starts at 3. cool int changeCount = GenerateChangeCount(target, null, 3); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //Stats and genital changes if (Utils.Rand(2) == 0) { target.ChangeLust(25); if (target.relativeLibido < 100) { if (target.relativeLibido < 50) { target.ChangeLibido(1); } target.ChangeLibido(1); } } C**k smallest = target.genitals.ShortestCock(); if (target.hasCock && smallest.length < 12 && Utils.Rand(3) == 0) { smallest.IncreaseLength(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } C**k thinnest = target.genitals.ThinnestCock(); if (target.hasCock && thinnest.girth < 4 && Utils.Rand(3) == 0) { thinnest.IncreaseThickness(0.5); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.balls.count > 0 && target.genitals.cumMultiplier < 50 && Utils.Rand(3) == 0) { target.ChangeLust(20); if (target.genitals.cumMultiplier < 10) { target.genitals.IncreaseCumMultiplier(1); } if (target.genitals.cumMultiplier < 50) { target.genitals.IncreaseCumMultiplier(0.5); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.hasVagina && target.genitals.standardBonusVaginalCapacity > 0) { target.genitals.DecreaseBonusVaginalCapacity((ushort)(Utils.Rand(5) + 5)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.hasVagina && !target.hasCock) { target.RemoveAllVaginas(); target.AddCock(CockType.HUMAN, 6, 1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.hasCock && !target.balls.hasBalls) { target.balls.GrowBalls(); target.HaveGenericCockOrgasm(0, true, true); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Transformations //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { target.RestoreNeck(); } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { target.RestoreBack(); } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { if (target.womb.ClearOviposition()) { sb.Append(RemovedOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //remove anything with a scaly or partially scaly body. now affects cockatrice too! if (Utils.Rand(3) == 0 && target.body.HasAny(EpidermisType.SCALES)) { BodyData oldData = target.body.AsReadOnlyData(); target.RestoreBody(); sb.Append(RestoredBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.arms.type != ArmType.HUMAN) { ArmData oldData = target.arms.AsReadOnlyData(); target.RestoreArms(); sb.Append(RestoredArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(4) == 0 && target.lowerBody.type != LowerBodyType.CLOVEN_HOOVED) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.CLOVEN_HOOVED); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.lowerBody.type == LowerBodyType.CLOVEN_HOOVED && target.horns.type == HornType.GOAT && target.face.type != FaceType.HUMAN) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.HUMAN); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //MOD: anything with scales will prevent this, including partial scales (a la cockatrice). since body is reworked a lot of these checks are hard to port, //but this i think is the closest i can get. if (Utils.Rand(4) == 0 && !target.body.HasAny(EpidermisType.SCALES) && target.ears.type != EarType.ELFIN) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.ELFIN); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.horns.type == HornType.NONE) { HornData oldData = target.horns.AsReadOnlyData(); target.UpdateHorns(HornType.GOAT); sb.Append(UpdateHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Mod Note: Horns: if (Utils.Rand(3) == 0 && target.horns.type != HornType.GOAT) { HornData oldData = target.horns.AsReadOnlyData(); target.UpdateHorns(HornType.GOAT); sb.Append(UpdateHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.horns.type == HornType.GOAT && target.horns.CanStrengthen) { target.horns.StrengthenTransform(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(4) == 0 && target.antennae.type != AntennaeType.NONE) { AntennaeData oldData = target.antennae.AsReadOnlyData(); target.RestoreAntennae(); sb.Append(RestoredAntennaeText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.cocks.Count == 1 && target.cocks[0].type != CockType.HUMAN) { target.genitals.UpdateCock(0, CockType.HUMAN); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.cocks.Count > 1 && !target.genitals.OnlyHasCocksOfType(CockType.HUMAN)) { target.genitals.UpdateCock(target.cocks.First(x => x.type != CockType.HUMAN), CockType.HUMAN); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.tail.type != TailType.SATYR) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.SATYR); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; int changeCount = GenerateChangeCount(target, new int[] { 2, 2 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //----------------------- // MAJOR TRANSFORMATIONS //----------------------- //1st priority: Change lower body to bipedal. if (Utils.Rand(4) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.RestoreLowerBody(); sb.Append(RestoredLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove Oviposition Perk if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove Incorporeality Perk, if not permanent if (target.HasPerk <Incorporeal>() && Utils.Rand(4) == 0) { target.RemovePerk <Incorporeal>(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Restore neck if (target.neck.type != NeckType.HUMANOID && Utils.Rand(5) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Skin color change – light, fair, olive, dark, ebony, mahogany, russet if (!Species.HUMAN.availableTones.Contains(target.body.primarySkin.tone) && Utils.Rand(5) == 0) { target.body.ChangeAllSkin(Utils.RandomChoice(Species.HUMAN.availableTones)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Change skin to normal if (target.body.type != BodyType.HUMANOID && (target.ears.type == EarType.HUMAN || target.ears.type == EarType.ELFIN) && Utils.Rand(4) == 0) { BodyData oldData = target.body.AsReadOnlyData(); target.RestoreBody(); sb.Append(RestoredBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Restore arms to become human arms again if (Utils.Rand(4) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.RestoreArms(); sb.Append(RestoredArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //----------------------- // MINOR TRANSFORMATIONS //----------------------- //-Human face if (target.face.type != FaceType.HUMAN && Utils.Rand(4) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.RestoreFace(); sb.Append(RestoreFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Human tongue if (target.tongue.type != TongueType.HUMAN && Utils.Rand(4) == 0) { TongueData oldData = target.tongue.AsReadOnlyData(); target.RestoreTongue(); sb.Append(RestoreTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove odd eyes if (Utils.Rand(5) == 0 && target.eyes.type != EyeType.HUMAN) { EyeData oldData = target.eyes.AsReadOnlyData(); target.RestoreEyes(); sb.Append(RestoredEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Gain human ears (If you have human face) if (target.ears.type != EarType.HUMAN && target.face.type == FaceType.HUMAN && Utils.Rand(4) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.RestoreEar(); sb.Append(RestoreEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removes gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { GillData oldData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RestoredGillsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Nipples Turn Back: if (target.genitals.hasBlackNipples && Utils.Rand(3) == 0) { target.genitals.SetBlackNipples(false); } //Remove extra nipples if (target.genitals.hasQuadNipples && Utils.Rand(3) == 0) { target.genitals.SetQuadNipples(false); } //Hair turns normal //Restart hair growth, if hair's normal but growth isn't on. Or just over turning hair normal. The power of rng. if ((target.hair.type != HairType.NORMAL || target.hair.growthArtificallyDisabled) && Utils.Rand(3) == 0) { target.UpdateHair(HairType.NORMAL); if (target.hair.growthArtificallyDisabled) { target.hair.SetHairGrowthStatus(true); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //----------------------- // EXTRA PARTS REMOVAL //----------------------- //Removes antennae if (target.antennae.type != AntennaeType.NONE && Utils.Rand(3) == 0) { AntennaeData oldData = target.antennae.AsReadOnlyData(); target.RestoreAntennae(); sb.Append(RestoredAntennaeText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removes horns if ((target.horns.type != HornType.NONE) && Utils.Rand(5) == 0) { HornData oldData = target.horns.AsReadOnlyData(); target.RestoreHorns(); sb.Append(RestoredHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removes wings if (target.wings.type != WingType.NONE && Utils.Rand(5) == 0) { WingData oldData = target.wings.AsReadOnlyData(); target.RestoreWings(); sb.Append(RestoredWingsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removes tail if (target.tail.type != TailType.NONE && Utils.Rand(5) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.RestoreTail(); sb.Append(RestoredTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Increase height up to 4ft 10in. if (Utils.Rand(2) == 0 && target.build.heightInInches < 58) { int temp = Utils.Rand(5) + 3; //Flavor texts. Flavored like 1950's cigarettes. Yum. target.build.IncreaseHeight((byte)temp); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Decrease height down to a maximum of 6ft 2in. if (Utils.Rand(2) == 0 && target.build.heightInInches > 74) { target.build.DecreaseHeight((byte)(3 + Utils.Rand(5))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //----------------------- // SEXUAL TRANSFORMATIONS //----------------------- //Remove additional cocks if (target.cocks.Count > 1 && Utils.Rand(3) == 0) { target.RemoveCock(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove additional balls/remove uniball if (target.balls.hasBalls && (target.balls.count != 2 || target.balls.uniBall) && Utils.Rand(3) == 0) { if (target.balls.size > 2) { if (target.balls.size > 5) { target.balls.ShrinkBalls((byte)(1 + Utils.Rand(3))); } target.balls.ShrinkBalls(1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else if (target.balls.count > 2) { target.balls.RemoveExtraBalls(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else //if (target.balls.count == 1 || target.balls.uniBall) { target.balls.MakeStandard(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //remove second v****a. if (target.vaginas.Count > 1 && Utils.Rand(3) == 0) { target.genitals.RemoveExtraVaginas(); } //Change c**k back to normal if (target.hasCock && !target.genitals.OnlyHasCocksOfType(CockType.HUMAN) && Utils.Rand(3) == 0) { //Select first non-human c**k C**k firstNonHuman = target.cocks.First(x => x.type != CockType.HUMAN); target.genitals.UpdateCock(firstNonHuman, CockType.HUMAN); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } C**k longest = target.genitals.LongestCock(); double targetSize = Math.Max(7, target.genitals.minimumCockLength); //Shrink oversized cocks if (target.hasCock && longest.length > targetSize && Utils.Rand(3) == 0) { longest.DecreaseLength((Utils.Rand(10) + 2) / 10.0); if (longest.girth > 1) { longest.DecreaseThickness((Utils.Rand(4) + 1) / 10.0); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove additional breasts if (target.breasts.Count > 1 && Utils.Rand(3) == 0) { target.RemoveExtraBreastRows(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } Breasts biggestCup = target.genitals.LargestBreast(); CupSize targetCup = EnumHelper.Max(CupSize.D, target.genitals.smallestPossibleFemaleCupSize); //Shrink t**s! if (Utils.Rand(3) == 0 && biggestCup.cupSize > targetCup) { foreach (Breasts t**s in target.breasts) { t**s.ShrinkBreasts(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Change v****a back to normal if (Utils.Rand(3) == 0 && target.hasVagina && !target.genitals.OnlyHasVaginasOfType(VaginaType.defaultValue)) { foreach (V****a vag in target.vaginas) { target.genitals.RestoreVagina(vag); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } VaginalWetness targetWetness = EnumHelper.Max(VaginalWetness.WET, target.genitals.minVaginalWetness); //Reduce wetness down to a minimum of 2 if (Utils.Rand(3) == 0 && target.hasVagina && target.genitals.LargestVaginalWetness() > targetWetness) { foreach (V****a vag in target.vaginas) { if (vag.wetness > targetWetness) { vag.DecreaseWetness(); } } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Fertility Decrease: if (target.hasVagina && target.fertility.baseFertility > 10 && Utils.Rand(3) == 0) { //High fertility: //Average fertility: target.fertility.DecreaseFertility((byte)(1 + Utils.Rand(3))); if (target.fertility.baseFertility < 10) { target.fertility.SetFertility(10); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Cum Multiplier Decrease: if (target.hasCock && target.genitals.cumMultiplier > 5 && Utils.Rand(3) == 0) { target.genitals.DecreaseCumMultiplier(1 + (Utils.Rand(20) / 10.0)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Anal wetness decrease if (target.ass.wetness > 0 && Utils.Rand(3) == 0) { target.ass.DecreaseWetness(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 3, 4 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //clear screen //Statistical changes: //-Reduces speed down to 70. if (target.relativeSpeed > 70 && Utils.Rand(4) == 0) { target.ChangeSpeed(-1); } //-Reduces intelligence down to 60. if (target.relativeIntelligence > 60 && Utils.Rand(4) == 0) { target.ChangeIntelligence(-1); } //-Raises toughness up to 90. //(+3 to 50, +2 to 70, +1 to 90) if (target.relativeToughness < 90 && Utils.Rand(3) == 0) { //(+3) if (target.relativeToughness < 50) { target.ChangeToughness(3); } //(+2) else if (target.relativeToughness < 70) { target.ChangeToughness(2); } //(+1) else { target.ChangeToughness(1); } } //-Raises strength to 80. if (target.relativeStrength < 80 && Utils.Rand(3) == 0) { target.ChangeStrength(1); } //-Raises libido up to 90. if (target.relativeLibido < 90 && Utils.Rand(3) == 0) { target.ChangeLibido(2); } //Sexual Changes: //MOD: lizard dicks get a common rng roll now. The only difference is the text. if (target.hasCock && !target.genitals.OnlyHasCocksOfType(CockType.LIZARD) && Utils.Rand(4) == 0) { C**k nonLizard = target.cocks.First(x => x.type != CockType.LIZARD); //Actually xform it nau target.genitals.UpdateCock(nonLizard, CockType.LIZARD); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } target.DeltaCreatureStats(lib: 3, lus: 10); } //-Breasts vanish to 0 rating if male if (target.genitals.BiggestCupSize() >= target.genitals.smallestPossibleMaleCupSize && target.gender == Gender.MALE && Utils.Rand(3) == 0) { //(HUEG) //Loop through behind the scenes and adjust all t**s. foreach (Breasts breastRow in target.breasts) { if (breastRow.cupSize > CupSize.E_BIG) { breastRow.ShrinkBreasts(((byte)breastRow.cupSize).div(2)); } else { breastRow.SetCupSize(target.genitals.smallestPossibleMaleCupSize); } } //(+2 speed) //target.IncreaseSpeed(2); target.ChangeLibido(2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Nipples reduction to 1 per tit. if (target.genitals.hasQuadNipples && Utils.Rand(4) == 0) { target.genitals.SetQuadNipples(false); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } Breasts smallestTits = target.genitals.SmallestBreast(); //Increase target's breast size, if they are big DD or smaller if (smallestTits.cupSize <= CupSize.DD_BIG && target.gender == Gender.FEMALE && Utils.Rand(4) == 0) { smallestTits.GrowBreasts(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove extra breast rows if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !hyperHappy) { target.RemoveExtraBreastRows(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Physical changes: //Tail - 1st gain reptilian tail, 2nd unlocks enhanced with fire tail whip attack if (target.tail.type != TailType.LIZARD && target.tail.type != TailType.SALAMANDER && Utils.Rand(3) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.LIZARD); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.tail.type == TailType.LIZARD && Utils.Rand(3) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.SALAMANDER); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Legs if (target.lowerBody.type != LowerBodyType.SALAMANDER && target.tail.type == TailType.SALAMANDER && Utils.Rand(3) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.SALAMANDER); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Arms if (target.arms.type != ArmType.SALAMANDER && target.lowerBody.type == LowerBodyType.SALAMANDER && Utils.Rand(3) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.UpdateArms(ArmType.SALAMANDER); sb.Append(UpdateArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove odd eyes if (Utils.Rand(4) == 0 && !target.eyes.isDefault) { EyeData oldData = target.eyes.AsReadOnlyData(); target.RestoreEyes(); sb.Append(RestoredEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Human face if (target.face.type != FaceType.HUMAN && Utils.Rand(4) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.HUMAN); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Human ears if (target.face.type == FaceType.HUMAN && target.ears.type != EarType.HUMAN && Utils.Rand(4) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.HUMAN); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Skin color change if (!Species.SALAMANDER.availableTones.Contains(target.body.primarySkin.tone) && Utils.Rand(4) == 0) { target.body.ChangeMainSkin(Utils.RandomChoice(Species.SALAMANDER.availableTones)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Change skin to normal if (target.body.type != BodyType.HUMANOID && target.ears.type == EarType.HUMAN && Utils.Rand(3) == 0) { BodyData oldData = target.body.AsReadOnlyData(); target.RestoreBody(); sb.Append(RestoredBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removing gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { GillData oldData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RestoredGillsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //FAILSAFE CHANGE if (remainingChanges == changeCount) { (target as CombatCreature)?.AddHP(100); target.ChangeLust(5); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
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)); //************* //Stat Changes //************* //(increases sensitivity) if (Utils.Rand(3) == 0) { target.ChangeSensitivity(1); } //(Increase libido) if (Utils.Rand(3) == 0) { target.ChangeLibido(1); } //MOD: speed now has a common roll. if (Utils.Rand(3) == 0) { //(If speed<70, increases speed) if (target.relativeSpeed < 70) { target.ChangeSpeed(1.5); } //(If speed>80, decreases speed down to minimum of 80) else if (target.relativeSpeed > 80) { target.ChangeSpeed(-1.5); } } //(increase toughness to 60) if (Utils.Rand(3) == 0 && target.relativeToughness < 60) { target.ChangeToughness(1); } //(decrease strength to 70) if (target.relativeStrength > 70 && Utils.Rand(3) == 0) { target.ChangeStrength(-1); } //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //**************** //Sexual Changes //**************** //Increase venom recharge if (target.tail.type == TailType.SPIDER_SPINNERET && target.tail.regenRate < target.tail.maxRegen) { target.tail.UpdateResources(regenRateDelta: 5); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //(tightens v****a to 1, increases lust/libido) if (target.hasVagina && Utils.Rand(3) == 0) { //respects any perks that prevent us from dropping too loose (like marae perk). //shrink largest if possible. if 2 (or more if that happens) and both(all) at min, remove extra one. VaginalLooseness minVaginalLooseness = EnumHelper.Max(VaginalLooseness.NORMAL, target.genitals.minVaginalLooseness); if (target.genitals.LargestVaginalLooseness() > minVaginalLooseness) { foreach (V****a v****a in target.vaginas) { if (v****a.looseness > minVaginalLooseness) { v****a.DecreaseLooseness(); } } target.DeltaCreatureStats(lib: 2, lus: 25); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else if (target.vaginas.Count > 1) { target.RemoveExtraVaginas(); } } AnalLooseness minAnalLooseness = EnumHelper.Max(AnalLooseness.LOOSE, target.genitals.minAnalLooseness); //(tightens asshole to 1, increases lust) if (target.ass.looseness > minAnalLooseness && Utils.Rand(3) == 0) { target.ass.DecreaseLooseness(); target.DeltaCreatureStats(lib: 2, lus: 25); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //[Requires penises] //(Thickens all cocks to a ratio of 1\" thickness per 5.5\" //use a handly linq conversion to grab all the cocks that are below this threshold. C**k[] toThicken = target.cocks.Where(x => x.girth * 5.5 < x.length).ToArray(); if (toThicken.Length > 0 && Utils.Rand(4) == 0) { int amountChanged = toThicken.Sum(x => x.IncreaseThickness(0.1) != 0 ? 1 : 0); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //[Increase to Breast Size] - up to Large DD if (target.genitals.SmallestCupSize() < CupSize.DD_BIG && Utils.Rand(4) == 0) { target.genitals.SmallestBreast().GrowBreasts(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //[Increase to Ass Size] - to 11 if (target.butt.size < 11 && Utils.Rand(4) == 0) { target.butt.GrowButt(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //*************** //Appearance Changes //*************** //(Ears become pointed if not human) if (target.ears.type != EarType.HUMAN && target.ears.type != EarType.ELFIN && Utils.Rand(4) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.ELFIN); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //(Fur/Scales fall out) if (target.body.type != BodyType.HUMANOID && (target.ears.type == EarType.HUMAN || target.ears.type == EarType.ELFIN) && Utils.Rand(4) == 0) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.HUMANOID, Tones.PALE); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //(Gain human face) if (target.body.type == BodyType.HUMANOID && (target.face.type != FaceType.SPIDER && target.face.type != FaceType.HUMAN) && Utils.Rand(4) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.HUMAN); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove breast rows over 2. if (target.breasts.Count > 2 && Utils.Rand(3) == 0 && !hyperHappy) { target.RemoveExtraBreastRows(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Nipples reduction to 1 per tit. if (target.genitals.hasQuadNipples && Utils.Rand(4) == 0) { target.genitals.SetQuadNipples(false); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Nipples Turn Black: if (!target.genitals.hasBlackNipples && Utils.Rand(6) == 0) { target.genitals.SetBlackNipples(true); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //eyes! if (target.body.type == BodyType.HUMANOID && target.eyes.type != EyeType.SPIDER && Utils.Rand(4) == 0) { target.IncreaseIntelligence(5); EyeData oldData = target.eyes.AsReadOnlyData(); target.UpdateEyes(EyeType.SPIDER); sb.Append(UpdateEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //(Gain spider fangs) if (target.face.type == FaceType.HUMAN && target.body.type == BodyType.HUMANOID && Utils.Rand(4) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.SPIDER); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //(Arms to carapace-covered arms) if (target.arms.type != ArmType.SPIDER && Utils.Rand(4) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.UpdateArms(ArmType.SPIDER); sb.Append(UpdateArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(4) == 0 && target.lowerBody.type != LowerBodyType.DRIDER && target.lowerBody.type != LowerBodyType.CHITINOUS_SPIDER) { target.RestoreLowerBody(); } //Drider butt if (isDrider && target.tail.type == TailType.SPIDER_SPINNERET && target.tail.ovipositor.type != OvipositorType.SPIDER && target.lowerBody.type == LowerBodyType.DRIDER && Utils.Rand(3) == 0 && (target.hasVagina || Utils.Rand(2) == 0)) { //V1 - Egg Count //V2 - Fertilized Count target.tail.GrantOvipositor(); //Opens up drider ovipositor scenes from available mobs. The character begins producing unfertilized eggs in their arachnid abdomen. Egg buildup raises minimum lust and eventually lowers speed until the target has gotten rid of them. This perk may only be used with the drider lower body, so your scenes should reflect that. //Any PC can get an Ovipositor perk, but it will be much rarer for characters without vaginas. //Eggs are unfertilized by default, but can be fertilized: //-female/herm characters can fertilize them by taking in s***n; successfully passing a pregnancy check will convert one level ofunfertilized eggs to fertilized, even if the PC is already pregnant. //-male/herm characters will have a sex dream if they reach stage three of unfertilized eggs; this will represent their bee/drider parts drawing their own s***n from their body to fertilize the eggs, and is accompanied by a nocturnal emission. //-unsexed characters cannot currently fertilize their eggs. //Even while unfertilized, eggs can be deposited inside NPCs - obviously, unfertilized eggs will never hatch and cannot lead to any egg-birth scenes that may be written later. if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //(Normal Biped Legs -> Carapace-Clad Legs) if (((isDrider && target.lowerBody.type != LowerBodyType.DRIDER && target.lowerBody.type != LowerBodyType.CHITINOUS_SPIDER) || (!isDrider && target.lowerBody.type != LowerBodyType.CHITINOUS_SPIDER)) && target.lowerBody.isBiped && Utils.Rand(4) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.CHITINOUS_SPIDER); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //(Tail becomes spider abdomen GRANT WEB ATTACK) if (target.tail.type != TailType.SPIDER_SPINNERET && (target.lowerBody.type == LowerBodyType.CHITINOUS_SPIDER || target.lowerBody.type == LowerBodyType.DRIDER) && target.arms.type == ArmType.SPIDER && Utils.Rand(4) == 0) { //(Pre-existing tails) //(No tail) TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.SPIDER_SPINNERET); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //(Drider Item Only: Carapace-Clad Legs to Drider Legs) if (isDrider && target.lowerBody.type == LowerBodyType.CHITINOUS_SPIDER && Utils.Rand(4) == 0 && target.tail.type == TailType.SPIDER_SPINNERET) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.DRIDER); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Remove gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { target.RestoreGills(); } if (remainingChanges == changeCount && target is CombatCreature fatigueCheck) { fatigueCheck.RecoverFatigue(33); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 3, 3 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //use: //stat changes: //lose height + gain speed (42" height floor, no speed ceiling but no speed changes without height change) if (target.build.heightInInches >= 45 && Utils.Rand(3) == 0) { target.IncreaseSpeed(); target.build.DecreaseHeight(); if (target.build.heightInInches > 60) { target.build.DecreaseHeight(); } if (target.build.heightInInches > 70) { target.build.DecreaseHeight(); } if (target.build.heightInInches > 80) { target.build.DecreaseHeight(); } if (target.build.heightInInches > 90) { target.build.DecreaseHeight(2); } if (target.build.heightInInches > 100) { target.build.DecreaseHeight(2); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //lose tough if (target.relativeToughness > 50 && Utils.Rand(3) == 0) { target.ChangeToughness(-1); if (target.relativeToughness >= 75) { target.ChangeToughness(-1); } if (target.relativeToughness >= 90) { target.ChangeToughness(-1); } } //SEXYYYYYYYYYYY //vag-anal capacity up for non-goo (available after PC < 5 ft; capacity ceiling reasonable but not horse-like or gooey) if (target.build.heightInInches < 60 && (target.genitals.standardBonusAnalCapacity < 100 || (target.genitals.standardBonusVaginalCapacity < 100 && target.hasVagina)) && Utils.Rand(3) == 0) { //adds some lust target.ChangeLust(10 + target.sensitivity / 5); if (target.genitals.standardBonusVaginalCapacity < 100 && target.hasVagina) { target.genitals.IncreaseBonusVaginalCapacity(5); } else { target.genitals.IncreaseBonusAnalCapacity(5); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //fem fertility up and heat (suppress if pregnant) //not already in heat (add heat and lust) if (!target.HasTimedEffect <Heat>() || target.GetTimedEffectData <Heat>().totalAddedLibido < 30 && Utils.Rand(2) == 0) { bool intensified = target.HasTimedEffect <Heat>(); if (target.GoIntoHeat()) { if (intensified) { ////[(no mino cum in inventory)] //if (!target.hasItem(consumables.MINOCUM)) //{ //} ////(mino cum in inventory and non-horse, 100 lust) //else //{ // //(consumes item, increment addiction/output addict message, small chance of mino preg, reduce lust)]"); // target.minoCumAddiction(5); // target.knockUp(PregnancyStore.PREGNANCY_MINOTAUR, PregnancyStore.INCUBATION_MINOTAUR, 175); // ConsumableLib consumables = game.consumables; // target.consumeItem(consumables.MINOCUM); //} #warning Not Implemented: consume minotaur cum, or if you don't have it, go f**k a minotaur to get it, and immediately consume it. do whatever sex is required for getting it. } else { // Also make a permanent nudge. target.fertility.IncreaseFertility(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //bodypart changes: //gain ears if (target.ears.type != EarType.MOUSE && Utils.Rand(4) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.MOUSE); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //gain tail //from no tail if (target.ears.type == EarType.MOUSE && target.tail.type != TailType.MOUSE && Utils.Rand(4) == 0) { //from other tail TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.MOUSE); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //get teeth - from human, bunny, coonmask, or other humanoid teeth faces if (target.ears.type == EarType.MOUSE && target.face.isHumanoid && target.face.type != FaceType.MOUSE && Utils.Rand(4) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.MOUSE); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //get mouse muzzle from mouse teeth or other muzzle if (target.body.IsFurBodyType() && target.face.hasMuzzle && Utils.Rand(4) == 0) { target.UpdateFace(FaceType.MOUSE); target.face.StrengthenFacialMorph(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //get fur if (!target.body.IsFurBodyType() || (!target.body.mainEpidermis.fur.IsIdenticalTo(HairFurColors.BROWN) && !target.body.mainEpidermis.fur.IsIdenticalTo(HairFurColors.WHITE)) && Utils.Rand(4) == 0) { FurColor color; int temp = Utils.Rand(10); if (temp < 8) { color = new FurColor(HairFurColors.BROWN); } else { color = new FurColor(HairFurColors.WHITE); } BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.SIMPLE_FUR, color); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 2 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //STATS CHANGURYUUUUU //Boost speed (max 80!) if (Utils.Rand(3) == 0 && target.relativeSpeed < 80) { target.DeltaCreatureStats(spe: target.relativeSpeed < 35 ? 2 : 1); } //Boost libido if (Utils.Rand(5) == 0) { target.DeltaCreatureStats(lib: 1, lus: (5 + target.libido / 7)); if (target.relativeLibido < 30) { target.ChangeLibido(1); } if (target.relativeLibido < 40) { target.ChangeLibido(1); } if (target.relativeLibido < 60) { target.ChangeLibido(1); } //Lower ones are gender specific for some reason } //BIG sensitivity gains to 60. if (target.relativeSensitivity < 60 && Utils.Rand(3) == 0) { //(low) if (Utils.Rand(3) != 2) { target.ChangeSensitivity(5); } //(BIG boost 1/3 chance) else { target.ChangeSensitivity(15); } } //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //Makes girls very girl(90), guys somewhat girly (61). if (Utils.Rand(2) == 0) { short res = target.femininity.ChangeFemininityToward((byte)(target.gender.HasFlag(Gender.FEMALE) ? 90 : 61), 4); if (res != 0) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //De-wettification of c**t (down to 3?)! VaginalWetness targetWetness = EnumHelper.Max(target.genitals.minVaginalWetness, VaginalWetness.SLICK); if (target.hasVagina && target.genitals.SmallestVaginalWetness() > targetWetness && Utils.Rand(3) == 0) { //Just to be safe foreach (V****a vag in target.vaginas) { vag.DecreaseWetness(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Fertility boost! if (Utils.Rand(4) == 0 && target.fertility.totalFertility < 50 && target.hasVagina) { target.fertility.IncreaseFertility((byte)(2 + Utils.Rand(5))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-VAGs //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)); } } //MOD: Bunny eggs and oviposition are now one and the same. the idea of removing oviposition and adding bunny eggs - that's just dumb. if (target.womb.canObtainOviposition && Utils.Rand(4) == 0) { target.womb.GrantOviposition(); sb.Append(GrantOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Shrink Balls! if (target.balls.count > 0 && target.balls.size > 5 && Utils.Rand(3) == 0) { if (target.balls.size < 10) { target.balls.ShrinkBalls(); } else if (target.balls.size < 25) { target.balls.ShrinkBalls((byte)(2 + Utils.Rand(3))); } else { target.balls.ShrinkBalls((byte)(6 + Utils.Rand(3))); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Get rid of extra balls if (target.balls.count > 2 && Utils.Rand(3) == 0) { target.balls.RemoveExtraBalls(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Boost cum production if ((target.balls.count > 0 || target.hasCock) && target.genitals.totalCum < 3000 && Utils.Rand(3) == 0) { target.genitals.IncreaseCumMultiplier(3 + Utils.Rand(7)); if (target.genitals.totalCum >= 250) { target.ChangeLust(3); } else if (target.genitals.totalCum >= 750) { target.ChangeLust(4); } else if (target.genitals.totalCum >= 2000) { target.ChangeLust(5); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Bunny feet! - requirez earz if (target.lowerBody.type != LowerBodyType.BUNNY && Utils.Rand(5) == 0 && target.ears.type == EarType.BUNNY) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.BUNNY); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //BUN FACE! REQUIREZ EARZ if (target.ears.type == EarType.BUNNY && target.face.type != FaceType.BUNNY && Utils.Rand(3) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.BUNNY); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else if (target.ears.type == EarType.BUNNY && target.face.type == FaceType.BUNNY && !target.face.isFullMorph && Utils.Rand(3) == 0) { target.face.StrengthenFacialMorph(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //DAH BUNBUN EARZ - requires poofbutt! if (target.ears.type != EarType.BUNNY && Utils.Rand(3) == 0 && target.tail.type == TailType.RABBIT) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.BUNNY); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //DAH BUNBUNTAILZ if (target.tail.type != TailType.RABBIT && Utils.Rand(2) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.RABBIT); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Remove gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { target.RestoreGills(); } // 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 wings if ((target.wings.type != WingType.NONE || target.back.type == BackType.SHARK_FIN) && Utils.Rand(4) == 0) { target.RestoreWings(); BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Bunny Breeder Perk? //FAILSAAAAFE if (remainingChanges == changeCount) { if (target.relativeLibido < 100) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } target.DeltaCreatureStats(lib: 1, lus: (5 + target.libido / 7)); if (target.relativeLibido < 30) { target.ChangeLibido(1); } if (target.relativeLibido < 40) { target.ChangeLibido(1); } if (target.relativeLibido < 60) { target.ChangeLibido(1); } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 2, 3, 4 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); #warning fix me int ngPlus(int value) => value; if (target.speed < ngPlus(100) && Utils.Rand(3) == 0) { //+3 spe if less than 50 if (target.speed < ngPlus(50)) { target.ChangeSpeed(1); } //+2 spe if less than 75 if (target.speed < ngPlus(75)) { target.ChangeSpeed(1); } //+1 if above 75. target.ChangeSpeed(1); } if (target.toughness > ngPlus(80) && Utils.Rand(4) == 0) { target.ChangeToughness(-1); } //-Reduces sensitivity. if (target.sensitivity > 20 && Utils.Rand(3) == 0) { target.ChangeSensitivity(-1); } //Raises libido greatly to 50, then somewhat to 75, then slowly to 100. if (target.libido < 100 && Utils.Rand(3) == 0) { //+3 lib if less than 50 if (target.libido < 50) { target.ChangeLibido(1); } //+2 lib if less than 75 if (target.libido < 75) { target.ChangeLibido(1); } //+1 if above 75. target.ChangeLibido(1); } //Sexual changes //-Lactation stoppage. if (target.genitals.isLactating && Utils.Rand(4) == 0) { if (target.HasPerk <Feeder>()) { target.RemovePerk <Feeder>(); } target.genitals.SetLactationTo(LactationStatus.NOT_LACTATING); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Nipples reduction to 1 per tit. if (target.genitals.hasQuadNipples && Utils.Rand(4) == 0) { target.genitals.SetQuadNipples(false); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove extra breast rows if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !hyperHappy) { target.RemoveExtraBreastRows(); } //-Butt > 5 - decrease butt size if (target.butt.size > 5 && Utils.Rand(4) == 0) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } target.butt.ShrinkButt(); } if (target.gender.HasFlag(Gender.FEMALE)) { CupSize minCup = EnumHelper.Max(CupSize.D, target.genitals.smallestPossibleFemaleCupSize); //Breasts > D cup - Decrease breast size by up to 3 cups //MOD NOTE: Now respects minimum cup size from perks. if (target.gender.HasFlag(Gender.FEMALE) && target.genitals.BiggestCupSize() > minCup && Utils.Rand(3) == 0) { foreach (Breasts breast in target.breasts) { if (breast.cupSize > CupSize.D) { breast.ShrinkBreasts((byte)(1 + Utils.Rand(3))); } } target.IncreaseSpeed(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Breasts < B cup - Increase breast size by 1 cup if (target.gender.HasFlag(Gender.FEMALE) && target.genitals.SmallestCupSize() < CupSize.B && Utils.Rand(3) == 0) { for (int i = 0; i < target.breasts.Count; i++) { if (target.breasts[i].cupSize < CupSize.B) { target.breasts[i].GrowBreasts(); } } target.ChangeLibido(1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Hips > 12 - decrease hip size by 1-3 sizes if (target.hips.size > 12 && Utils.Rand(3) == 0) { target.hips.ShrinkHips((byte)(1 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Hips < 6 - increase hip size by 1-3 sizes if (target.hips.size < 6 && Utils.Rand(3) == 0) { target.hips.GrowHips((byte)(1 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.genitals.nippleLength > 1 && Utils.Rand(3) == 0) { target.genitals.SetNippleLength(target.genitals.nippleLength / 2); } VaginalWetness desiredWetness = EnumHelper.Min(target.genitals.maxVaginalWetness, VaginalWetness.SLICK); //MOD NOTE: now respects all vaginas, and the maximum wetness allowed by perks (if applicable) if (target.hasVagina && target.genitals.SmallestVaginalWetness() < desiredWetness && Utils.Rand(4) == 0) { foreach (V****a vag in target.vaginas) { if (vag.wetness < desiredWetness) { vag.IncreaseWetness(); } } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Increase tone (up to 65) if (target.build.muscleTone < 65 && Utils.Rand(3) == 0) { target.build.ChangeMuscleToneToward(65, 2); } //Decrease thickness (down to 35) if (target.build.thickness > 35 && Utils.Rand(3) == 0) { target.build.ChangeThicknessToward(35, 5); } //Grant oviposition. if (target.womb.canObtainOviposition && Species.COCKATRICE.Score(target) > 3 && Utils.Rand(5) == 0) { target.womb.GrantOviposition(); sb.Append(GrantOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } if (target.gender == Gender.MALE) { //Breasts > B cup - decrease by 1 cup size if (target.genitals.BiggestCupSize() > CupSize.B && Utils.Rand(3) == 0) { for (int i = 0; i < target.breasts.Count; i++) { if (target.breasts[i].cupSize > CupSize.B) { target.breasts[i].ShrinkBreasts(); } } target.IncreaseSpeed(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.genitals.nippleLength > 1 && Utils.Rand(3) == 0) { target.genitals.SetNippleLength(target.genitals.nippleLength / 2); } //Hips > 10 - decrease hip size by 1-3 sizes if (target.hips.size > 10 && Utils.Rand(3) == 0) { target.hips.ShrinkHips((byte)(1 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Hips < 2 - increase hip size by 1-3 sizes if (target.hips.size < 2 && Utils.Rand(3) == 0) { target.hips.GrowHips((byte)(1 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Increase tone (up to 70) if (target.build.muscleTone < 70 && Utils.Rand(3) == 0) { target.build.ChangeMuscleToneToward(70, 2); } //Decrease thickness (down to 35) if (target.build.thickness > 35 && Utils.Rand(3) == 0) { target.build.ChangeThicknessToward(35, 5); } } if (target.gender.HasFlag(Gender.MALE)) { //C**k < 6 inches - increase by 1-2 inches C**k shortest = target.genitals.ShortestCock(); if (shortest.length < 6 && Utils.Rand(3) == 0) { double increment = shortest.IncreaseLength(1 + Utils.Rand(2)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } C**k longest = target.genitals.LongestCock(); //Shrink oversized cocks if (longest.length > 16 && Utils.Rand(3) == 0) { longest.DecreaseLength((Utils.Rand(10) + 5) / 10.0); if (longest.girth > 3) { longest.DecreaseThickness((Utils.Rand(4) + 1) / 10.0); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } C**k thinnest = target.genitals.ThinnestCock(); //C**k thickness <2 - Increase c**k thickness if (thinnest.girth < 2 && Utils.Rand(3) == 0) { thinnest.IncreaseThickness(1.5); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } int lizardCocks = target.genitals.CountCocksOfType(CockType.LIZARD); if (target.hasCock && target.cocks.Count > lizardCocks && Utils.Rand(4) == 0) { //-Lizard dick - first one if (lizardCocks == 0) { //Actually xform it nau if (target.genitals.hasSheath) { target.genitals.UpdateCock(0, CockType.LIZARD); } else { target.genitals.UpdateCock(0, CockType.LIZARD); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } target.DeltaCreatureStats(lib: 3, lus: 10); } //(CHANGE OTHER DICK) //Requires 1 lizard c**k, multiple cocks else //if (target.cocks.Count > 1 && target.cocks.Count > lizardCocks) { C**k firstNonLizard = target.cocks.First(x => x.type != CockType.LIZARD); target.genitals.UpdateCock(firstNonLizard, CockType.LIZARD); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } target.DeltaCreatureStats(lib: 3, lus: 10); } } //MOD NOTE: Worms are being removed??? from the game due to that absolutely shitty license that prevents me from porting any content 'created' by //its creator (forget the name at this time). it's a bad license because it takes credit from other content creators that use its content. that's like saying //the creator of the rubber tire gets credit for all modern car designs. that's bullshit. of course cars require tires, and the original copyright required users //to pay a licensing fee for their use. that didn't grant the copyright holder for rubber tires the credit for the cars that used them. If anyone wants to get ahold //of the og worms creator and get him/her to relax that requirement to make it reasonable (and future content-change/port friendly), we'll see. until then, this is //removed. //For the record, that would grant him creator's credit on this document. which he/she had no input on. that's not right. and, by extension, the inventory system, since //this item is in use in the inventory system. and the time engine, because worms regen over time. I am the content creator for both of those, and i refuse to allow him/her //any credit for either of those. those took time and effort and were difficult - i'm not freely handing credit to him/her because that license would require me to. Either //his/her license changes, or the offending content is removed, or the entire game engine goes. Right now i think it's better go with the game engine because it make all of //this shit work. - JSG. ////--Worms leave if 100% lizard dicks? ////Require mammals? //if (target.genitals.CountCocksOfType(CockType.LIZARD) == target.cocks.Count && target.hasStatusEffect(StatusEffects.Infested)) //{ // if (target.balls.count > 1) // target.removeStatusEffect(StatusEffects.Infested); // if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //} //Increase height up to 5ft 7in. if (target.build.heightInInches < 67 && Utils.Rand(5) == 0) { target.build.IncreaseHeight((byte)(Utils.Rand(3) + 1)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Decrease height down to a maximum of 6ft 8in. if (target.build.heightInInches > 80 && Utils.Rand(5) == 0) { target.build.DecreaseHeight((byte)(Utils.Rand(3) + 1)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Physical changes: //Removes other antennae if (target.antennae.type != AntennaeType.COCKATRICE && !target.antennae.isDefault && Utils.Rand(3) == 0) { target.RestoreAntennae(); } //Gain antennae like feathers if (target.antennae.type == AntennaeType.NONE && target.face.type == FaceType.COCKATRICE && target.ears.type == EarType.COCKATRICE && Utils.Rand(3) == 0) { // Other antennae types are handled above! (Stadler76) AntennaeData oldData = target.antennae.AsReadOnlyData(); target.UpdateAntennae(AntennaeType.COCKATRICE); sb.Append(UpdateAntennaeText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removes horns if (target.horns.type != HornType.NONE && Utils.Rand(5) == 0) { HornData oldData = target.horns.AsReadOnlyData(); target.RestoreHorns(); sb.Append(RestoredHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Face TF if (target.face.type != FaceType.COCKATRICE && target.arms.type == ArmType.COCKATRICE && target.lowerBody.type == LowerBodyType.COCKATRICE && Utils.Rand(3) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.COCKATRICE); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Hair TF if (target.hair.type != HairType.FEATHER && Utils.Rand(4) == 0) { HairData oldData = target.hair.AsReadOnlyData(); target.UpdateHair(HairType.FEATHER); sb.Append(UpdateHairText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Eye TF if (target.eyes.type != EyeType.COCKATRICE && target.face.type == FaceType.COCKATRICE && target.body.type == BodyType.COCKATRICE && target.ears.type == EarType.COCKATRICE && Utils.Rand(3) == 0) { EyeData oldData = target.eyes.AsReadOnlyData(); target.UpdateEyes(EyeType.COCKATRICE); sb.Append(UpdateEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Lizard tongue TF if (target.tongue.type != TongueType.LIZARD && target.face.type == FaceType.COCKATRICE && Utils.Rand(3) == 0) { TongueData oldData = target.tongue.AsReadOnlyData(); target.UpdateTongue(TongueType.LIZARD); sb.Append(UpdateTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ears TF if (target.ears.type != EarType.COCKATRICE && target.face.type == FaceType.COCKATRICE && Utils.Rand(3) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.COCKATRICE); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Arm TF if (target.arms.type != ArmType.COCKATRICE && Utils.Rand(4) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.UpdateArms(ArmType.COCKATRICE); sb.Append(UpdateArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck loss, if not cockatrice neck if (target.neck.type != NeckType.COCKATRICE && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (target.back.type != BackType.NORMAL && 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)); } } //Body TF if (target.body.type != BodyType.COCKATRICE && target.face.type == FaceType.COCKATRICE && Utils.Rand(3) == 0) { Species.COCKATRICE.GetRandomCockatriceColors(out FurColor feathers, out Tones scales); target.UpdateBody(BodyType.COCKATRICE, feathers, scales); NeckData oldData = target.neck.AsReadOnlyData(); target.UpdateNeck(NeckType.COCKATRICE, feathers.primaryColor); sb.Append(UpdateNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck TF, if not already TFed from Body TF above if (target.neck.type != NeckType.COCKATRICE && target.body.type == BodyType.COCKATRICE && target.face.type == FaceType.COCKATRICE && Utils.Rand(3) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.UpdateNeck(NeckType.COCKATRICE, Utils.RandomChoice(Species.COCKATRICE.availablePrimaryFeatherColors)); sb.Append(UpdateNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Leg TF if (target.lowerBody.type != LowerBodyType.COCKATRICE && Utils.Rand(4) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.COCKATRICE); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Tail TF if (target.tail.type != TailType.COCKATRICE && Utils.Rand(4) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.COCKATRICE); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Wings TF //feathered wings and not large and a target? that shouldn't happen. silently make them large if (target.wings.type == WingType.FEATHERED && !target.wings.isLarge && target is Player) { target.wings.GrowLarge(); } else if (target.wings.type != WingType.FEATHERED && target.arms.type == ArmType.COCKATRICE && Utils.Rand(4) == 0) { HairFurColors wingColor = !target.body.activeFur.isEmpty ? target.body.activeFur.fur.primaryColor : target.hair.hairColor; WingData oldData = target.wings.AsReadOnlyData(); target.UpdateWings(WingType.FEATHERED); sb.Append(UpdateWingsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //FAILSAFE CHANGE if (remainingChanges == changeCount) { if (target is CombatCreature failSafe) { failSafe.AddHP(50); } target.ChangeLust(3); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
//trying to think of a good way to do nipple piercings for goblins when you go full goblin without being too intrusive. //can't. oh well. but hey, at least it removes inverted nipples without requiring piercings, i guess //fun fact: this is (as of writing) the only way to restore nipples back to normal after you've made them fuckable. it's now possible to remove fuckable nipples just //by shrinking them down (they become inverted), but they're still not 'normal.' this will convert inverted nipples back to normal. so, if you want to remove fuckable nipples, //shrink them down, then proc this a few times. It's also possible that nipple piercings remove inverted nipples over time, but as of writing, that's not (currently) implemented protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //rolls of 1/2, 1/3, 1/4, and 1/5 for extra changes. i don't feel like figuring out the odds, so here's the simple split: //the most changes (+4) has an odds of 1/120, and the least changes (+0) have an odds of 1/5. the remaining change counts vary, totaling 19/24. int changeCount = GenerateChangeCount(target, new int[] { 2, 3, 4, 5 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. target.IncreaseLust(15); //Stronger if (target.relativeStrength > 50) { if (target.relativeStrength > 90) { target.DecreaseStrengthByPercent(3); } else if (target.relativeStrength > 70) { target.DecreaseStrengthByPercent(2); } else { target.DecreaseStrengthByPercent(1); } } ///Less tough if (target.relativeToughness > 50) { if (target.relativeToughness > 90) { target.DecreaseToughnessByPercent(4); } else if (target.relativeToughness > 70) { target.DecreaseToughnessByPercent(2); } else { target.DecreaseToughnessByPercent(1); } } //Speed boost if (Utils.Rand(3) == 0 && target.relativeSpeed < 50) { target.IncreaseSpeed(1 + Utils.Rand(2)); } //antianemone corollary: if (target.hair.type == HairType.ANEMONE && Utils.Rand(2) == 0) { //-insert anemone hair removal into them under whatever criteria you like, though hair removal should precede abdomen growth; here's some sample text: HairData oldData = target.hair.AsReadOnlyData(); target.RestoreHair(); sb.Append(RestoredHairText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Shrink if (Utils.Rand(2) == 0 && target.heightInInches > 48) { target.build.DecreaseHeight((byte)(1 + Utils.Rand(5))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Normal transforms (Cost 1) //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //Neck restore if (!target.neck.isDefault && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Restore arms to become human arms again if (Utils.Rand(4) == 0 && !target.arms.isDefault) { ArmData oldData = target.arms.AsReadOnlyData(); target.RestoreArms(); sb.Append(RestoredArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //SEXYTIEMS //Multidick killa! if (target.cocks.Count > 1 && Utils.Rand(3) == 0) { target.genitals.RemoveCock(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Boost vaginal capacity without gaping if (Utils.Rand(3) == 0 && target.hasVagina && target.genitals.standardBonusVaginalCapacity < 40) { target.genitals.IncreaseBonusVaginalCapacity(5); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Boost fertility if (Utils.Rand(4) == 0 && target.fertility.totalFertility < 40 && target.hasVagina) { target.fertility.IncreaseFertility((byte)(2 + Utils.Rand(5))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Shrink primary dick to no longer than 12 inches else if (target.cocks.Count == 1 && Utils.Rand(2) == 0 && !hyperHappy) { if (target.cocks[0].length > 12) { double delta = target.cocks[0].DecreaseLength(1 + Utils.Rand(3)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //GENERAL APPEARANCE STUFF BELOW //REMOVAL STUFF //Removes wings! if ((target.wings.type != WingType.NONE) && Utils.Rand(4) == 0) { WingData oldData = target.wings.AsReadOnlyData(); target.RestoreWings(); sb.Append(RestoredWingsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removes antennae! if (target.antennae.type != AntennaeType.NONE && Utils.Rand(3) == 0) { AntennaeData oldData = target.antennae.AsReadOnlyData(); target.RestoreAntennae(); sb.Append(RestoredAntennaeText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove odd eyes if (Utils.Rand(5) == 0 && target.eyes.count != 2) { EyeData oldData = target.eyes.AsReadOnlyData(); target.RestoreEyes(); sb.Append(RestoredEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove extra breast rows if (target.breasts.Count > 1 && Utils.Rand(3) == 0) { target.genitals.RemoveExtraBreastRows(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Skin/fur if (target.body.type != BodyType.HUMANOID && Utils.Rand(4) == 0 && target.face.type == FaceType.HUMAN) { BodyData oldData = target.body.AsReadOnlyData(); target.RestoreBody(); sb.Append(RestoredBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //skinTone if (!Species.GOBLIN.availableTones.Contains(target.body.primarySkin.tone) && Utils.Rand(2) == 0) { if (Utils.Rand(10) != 0) { target.body.ChangeMainSkin(Tones.DARK_GREEN); } else if (Utils.RandBool()) { target.body.ChangeMainSkin(Tones.PALE_YELLOW); } else { target.body.ChangeMainSkin(Tones.GRAYISH_BLUE); } //MOD note: rathazal mixology called here - how do we want to standardize that? if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Face! if (target.face.type != FaceType.HUMAN && Utils.Rand(4) == 0 && target.ears.type == EarType.ELFIN) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.HUMAN); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ears! if (target.ears.type != EarType.ELFIN && Utils.Rand(3) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.ELFIN); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Remove gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { GillData oldData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RestoredGillsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Nipples Turn Back: if (target.genitals.hasBlackNipples && Utils.Rand(3) == 0) { target.genitals.SetBlackNipples(false); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //MOD: Added: remove inverted nipples. this by entension is able to remove fuckable nipples, assuming you shrunk them down until they became inverted. if (target.genitals.nippleType.IsInverted() && Utils.Rand(3) == 0) { target.genitals.SetNippleStatus(NippleStatus.NORMAL); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Debugcunt if (Utils.Rand(3) == 0 && target.hasVagina && target.vaginas.Any(x => !x.isDefault)) { foreach (V****a vag in target.vaginas) { if (!vag.isDefault) { target.genitals.RestoreVagina(vag); } } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(4) == 0 && target.ass.wetness > target.ass.minWetness) { if (target.ass.wetness - target.ass.minWetness > 1) { target.ass.DecreaseWetness(2); } else { target.ass.DecreaseWetness(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0) { if (Utils.Rand(2) == 0) { target.femininity.ChangeFemininityToward(85, 3); } if (Utils.Rand(2) == 0) { target.build.ChangeThicknessToward(20, 3); } if (Utils.Rand(2) == 0) { target.build.ChangeMuscleToneToward(15, 5); } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; int changeCount = GenerateChangeCount(target, new int[] { 2, 2 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); sb.Append(InitialTransformText(target)); target.DeltaCreatureStats(lus: 3, corr: 1); uint hpDelta; if (target.hasCock) { if (target.cocks[0].length < 12) { CockData oldData = target.cocks[0].AsReadOnlyData(); double temp = target.cocks[0].IncreaseLength(Utils.Rand(2) + 2); sb.Append(OneCockGrewLarger(target, oldData, temp)); } hpDelta = 30; } else { hpDelta = 20; } if (target is CombatCreature healthCheck) { healthCheck.AddHP((uint)(hpDelta + healthCheck.toughness / 3)); sb.Append(GainVitalityText(target)); } if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Red or orange skin! if (Utils.Rand(30) == 0 && !Array.Exists(Species.IMP.availableTones, x => x == target.body.primarySkin.tone)) { Tones oldSkinTone = target.body.primarySkin.tone; if (target.body.ChangeMainSkin(Utils.RandomChoice(Species.IMP.availableTones))) { sb.Append(ChangeSkinColorText(target, oldSkinTone)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Shrinkage! if (Utils.Rand(2) == 0 && target.build.heightInInches > 42) { byte heightDelta = target.build.DecreaseHeight((byte)(1 + Utils.Rand(3))); if (heightDelta > 0) { sb.Append(GetShorterText(target, heightDelta)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Imp wings - I just kinda robbed this from demon changeCount ~Foxwells if (Utils.Rand(3) == 0 && ((target.wings.type != WingType.IMP && target.IsCorruptEnough(25)) || (target.wings.type == WingType.IMP && target.IsCorruptEnough(50)))) { bool changedWings = false; //grow smalls to large if (target.wings.type == WingType.IMP) { if (target.wings.GrowLarge()) { sb.Append(EnlargenedImpWingsText(target)); changedWings = true; } } else { WingData oldData = target.wings.AsReadOnlyData(); if (target.UpdateWings(WingType.IMP)) { sb.Append(GrowOrChangeWingsText(target, oldData)); changedWings = true; } } if (changedWings) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Imp tail, because that's a unique thing from what I see? if (Utils.Rand(3) == 0 && target.tail.type != TailType.IMP) { TailData oldData = target.tail.AsReadOnlyData(); if (target.UpdateTail(TailType.IMP)) { sb.Append(GrowOrChangeTailText(target, oldData)); target.IncreaseCorruption(2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Feets, needs red/orange skin and tail if (Species.IMP.availableTones.Contains(target.body.primarySkin.tone) && target.tail.type == TailType.IMP && target.lowerBody.type != LowerBodyType.IMP && Utils.Rand(3) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); if (target.UpdateLowerBody(LowerBodyType.IMP)) { sb.Append(ChangeLowerBodyText(target, oldData)); target.IncreaseCorruption(2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Imp ears, needs red/orange skin and horns if (target.horns.type == HornType.IMP && Array.Exists(Species.IMP.availableTones, x => target.body.primarySkin.tone == x) && target.ears.type != EarType.IMP && Utils.Rand(3) == 0) { EarData oldData = target.ears.AsReadOnlyData(); if (target.UpdateEars(EarType.IMP)) { sb.Append(ChangeEarsText(target, oldData)); target.IncreaseCorruption(2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Horns, because why not? if (target.horns.type != HornType.IMP && Utils.RandBool()) { HornData oldData = target.horns.AsReadOnlyData(); if (target.UpdateHorns(HornType.IMP)) { sb.Append(ChangeOrGrowHornsText(target, oldData)); target.IncreaseCorruption(2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Imp arms, needs orange/red skin. Also your hands turn human. if (Species.IMP.availableTones.Contains(target.body.primarySkin.tone) && target.arms.type != ArmType.IMP && Utils.Rand(3) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); if (target.UpdateArms(ArmType.IMP)) { sb.Append(ChangeArmText(target, oldData)); target.IncreaseCorruption(2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Changes hair to red/dark red, shortens it, sets it normal, and makes it curly. if (!Species.IMP.availableHairColors.Contains(target.hair.hairColor) && Utils.Rand(3) == 0) { HairData oldHairData = target.hair.AsReadOnlyData(); HairFurColors hairColor = Utils.RandomChoice(Species.IMP.availableHairColors); double hairLength = 1; if (target.hair.type != HairType.NORMAL) { //also restarts hair growth if disabled. target.UpdateHair(HairType.NORMAL, true, hairColor, newHairLength: hairLength, newStyle: HairStyle.CURLY); } else { //also restarts hair growth if disabled. target.hair.SetAll(hairLength, true, hairColor, style: HairStyle.CURLY); } sb.Append(HairChangedText(target, oldHairData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove spare titties if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !hyperHappy) { BreastData toRemove = target.breasts[target.breasts.Count - 1].AsReadOnlyData(); if (target.genitals.RemoveBreastRows() > 0) { sb.Append(RemovedAnExtraRowOfBreasts(target, toRemove)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Shrink titties if (target.genitals.BiggestCupSize() > CupSize.FLAT && Utils.Rand(3) == 0 && !hyperHappy) { byte rowsAlreadyModified = 0; //temp3 stores how many rows are changed foreach (Breasts breast in target.breasts) { //If this row is over threshhold if (breast.cupSize > CupSize.FLAT) { CupSize oldSize = breast.cupSize; byte delta; //Big change if (breast.cupSize > CupSize.EE_BIG) { delta = breast.ShrinkBreasts((byte)(2 + Utils.Rand(3))); } else { delta = breast.ShrinkBreasts(1); } if (delta != 0) { sb.Append(CurrentBreastRowChanged(target, breast.rowIndex, delta, rowsAlreadyModified)); rowsAlreadyModified++; } } } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Free extra nipple removal service if (target.genitals.hasQuadNipples && Utils.Rand(3) == 0) { target.genitals.SetQuadNipples(false); sb.Append(RemovedQuadNippleText(target)); target.DecreaseSensitivity(3); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.defaultValue && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); if (target.RestoreNeck()) { sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Rear body restore if (target.back.type != BackType.defaultValue && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); if (target.RestoreBack()) { sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Ovi perk loss if (target is Player && Utils.Rand(5) == 0) { if (((PlayerWomb)target.womb).ClearOviposition()) { sb.Append(RemovedOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //You lotta imp? Time to turn male! //Unless you're one of the hyper happy assholes I guess //For real tho doesn't seem like female imps exist? Guess they're goblins if (target.ImpScore() >= 4 && !hyperHappy) { bool changedSomething = false; GenitalsData oldGenitals = target.genitals.AsReadOnlyData(); changedSomething |= target.genitals.RemoveExtraBreastRows() > 0; changedSomething |= target.breasts[0].MakeMale(true); changedSomething |= target.RemoveAllVaginas() > 0; if (!target.hasCock) { changedSomething |= target.AddCock(CockType.HUMAN, 12, 2); } if (target.balls.count == 0) { changedSomething |= target.genitals.GrowBalls(2); } if (changedSomething) { sb.Append(ImpifiedText(target, oldGenitals)); remainingChanges--; target.IncreaseCorruption(20); } } return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
//Original credits: //since March 26, 2018 //@author Stadler76 // //Porter's note: Nearly all of the comments here are from the vanilla. comments from modification of code will be marked with <MODIFICATION> protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //<MODIFICATION NOTE> //potent tf has an initial count of 3. int changeCount = GenerateChangeCount(target, new int[] { 2, 2 }, enhanced ? 3 : 1); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); sb.Append(InitialTransformationText(target)); if (target.face.type == FaceType.FOX && target.tail.type == TailType.FOX && target.ears.type == EarType.FOX && target.lowerBody.type == LowerBodyType.FOX && target.body.IsFurBodyType() && Utils.Rand(3) == 0 && target is IExtendedCreature extended && !extended.extendedData.resistsTFBadEnds) { if (!extended.extendedData.hasFoxWarning) { extended.extendedData.hasFoxWarning = true; } else { isBadEnd = true; return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //<MODIFICATION NOTE> //Free Changes. //[decrease Strength] (to some floor) // I figured 15 was fair, but you're in a better position to judge that than I am. if (Utils.Rand(3) == 0 && target.relativeStrength > 40) { if (target.relativeStrength > 90) { target.DecreaseStrength(4); } else if (target.relativeStrength > 80) { target.DecreaseStrength(3); } else if (target.relativeStrength > 60) { target.DecreaseStrength(2); } else { target.DecreaseStrength(); } } //[decrease Toughness] (to some floor) // 20 or so was my thought here if (Utils.Rand(3) == 0 && target.relativeToughness > 30) { if (target.relativeToughness > 90) { target.DecreaseToughness(4); } else if (target.relativeToughness > 80) { target.DecreaseToughness(3); } else if (target.relativeToughness > 60) { target.DecreaseToughness(2); } else { target.DecreaseToughness(); } } //[increase Intelligence, Libido and Sensitivity] if (Utils.Rand(3) == 0 && (target.relativeLibido < 80 || target.relativeSensitivity < 80 || target.relativeIntelligence < 80)) { if (target.relativeIntelligence < 80) { target.IncreaseIntelligence(4); } if (target.relativeLibido < 80) { target.IncreaseLibido(1); } if (target.relativeSensitivity < 80) { target.IncreaseSensitivity(1); } //gain small lust also target.IncreaseLust(10); } //Modification Note: move this free change up here, where it makes the most sense. if (target.build.muscleTone > 40 && Utils.Rand(2) == 0) { target.build.DecreaseMuscleTone(4); } if (!Species.FOX.AvailableHairColors.Contains(target.hair.hairColor) && !Species.KITSUNE.elderKitsuneHairColors.Contains(target.hair.hairColor) && !Species.KITSUNE.kitsuneHairColors.Contains(target.hair.hairColor) && Utils.Rand(4) == 0) { HairFurColors targetColor; if (target.tail.type == TailType.FOX && target.tail.tailCount > 1) { if (target.tail.tailCount < 9) { targetColor = Utils.RandomChoice(Species.KITSUNE.kitsuneHairColors); } targetColor = Utils.RandomChoice(Species.KITSUNE.elderKitsuneHairColors); } else { targetColor = Utils.RandomChoice(Species.FOX.AvailableHairColors); } } //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //<MODIFICATION NOTE> //Transformation Changes. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //[Adjust hips toward 10 – wide/curvy/flared] if (Utils.Rand(3) == 0 && target.hips.size != 10) { //from narrow to wide if (target.hips.size < 7) { target.hips.GrowHips(2); } else if (target.hips.size < 10) { target.hips.GrowHips(); } //from wide to narrower else if (target.hips.size > 13) { target.hips.ShrinkHips(2); } else { target.hips.ShrinkHips(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //[Remove tentacle hair] //required if the hair length change below is triggered if (target.hair.type == HairType.ANEMONE && Utils.Rand(3) == 0) { //-insert anemone hair removal into them under whatever criteria you like, though hair removal should precede abdomen growth; HairData oldData = target.hair.AsReadOnlyData(); target.RestoreHair(); sb.Append(RestoredHairText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //[Adjust hair length toward range of 16-26 – very long to ass-length] if (target.hair.type == HairType.ANEMONE && (target.hair.length > 26 || target.hair.length < 16) && target.hair.canGrowNaturally && Utils.Rand(4) == 0) { if (target.hair.length < 16) { target.hair.GrowHair(1 + Utils.Rand(4)); } else { target.hair.ShortenHair(1 + Utils.Rand(4)); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(10) == 0) { //MOD NOTE: Don't remove ?. operator. if ((target as CombatCreature)?.RecoverFatigue(10) > 0) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //dog cocks! //MOD NOTE: consider adding back in fox cocks and using them? they could literally just be dog cocks but with a slightly different text descriptions. //MOD NOTE: this is a linq check that sees if any available cocks aren't a dog c**k. it's actually faster than count of type, because it stops as soon as it hits //a c**k that isn't a dog, instead of checking all of them. Also, this function finds all non-dog cocks and converts one at random. if (Utils.Rand(3) == 0 && target.hasCock && target.cocks.Any(x => x.type != CockType.DOG)) { C**k toChange = Utils.RandomChoice(target.cocks.Where(x => x.type != CockType.DOG).ToArray()); if (toChange.type == CockType.HUMAN) { toChange.IncreaseThickness(.3); target.DeltaCreatureStats(sens: 10, lus: 5); } //Horse else if (toChange.type == CockType.HORSE) { //Tweak length/thickness. double deltaLength; if (toChange.length > 6) { deltaLength = -2; } else { deltaLength = -.5; } toChange.SetLengthAndGirth(toChange.length + deltaLength, toChange.girth + 0.5); target.DeltaCreatureStats(sens: 4, lus: 5); } else { target.DeltaCreatureStats(sens: 4, lus: 10); } target.genitals.UpdateCock(toChange, CockType.DOG); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Cum Multiplier Xform if (target.genitals.totalCum < 5000 && Utils.Rand(3) == 0 && target.hasCock) { int temp = 2 + Utils.Rand(4); //Lots of cum raises cum multiplier cap to 2 instead of 1.5 if (target.HasPerk <MessyOrgasms>()) { temp += Utils.Rand(10); } //MOD NOTE: not sure if cum calculations changed, (i think they have) and if so, that's a lot of multiplier gain holy shit. meh. whatever. target.genitals.IncreaseCumMultiplier(temp); //Flavor text if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.balls.count > 0 && target.balls.size > 4 && Utils.Rand(3) == 0) { int targetSize; //currently above max, but whatever, that'll probably get raised anyway. if (target.balls.size > 50) { targetSize = target.balls.size / 5; } else if (target.balls.size > 10) { targetSize = target.balls.size / 2; } else { targetSize = target.balls.size - 1; } //allow perks to work. target.balls.ShrinkBalls((byte)(target.balls.size - targetSize)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Sprouting more! if (enhanced && target.breasts.Count < 4 && target.breasts[target.breasts.Count - 1].cupSize > CupSize.A) { target.genitals.AddBreastRow(target.breasts[target.breasts.Count - 1].cupSize); target.DeltaCreatureStats(sens: 2, lus: 30); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Find out if t**s are eligible for evening //MOD NOTE: there's an easier way - just run the normalize breasts. it'll return true if it changed anything. since we display the results of the old //breast data anyway, this is way simpler. Also, normalize breasts now returns a bool. woo! //MOD NOTE 2: Fox rules here seem to use the same size as previous. if they want to function under anthro rules (size is in decreasing order, but otherwise //roughly even) you can use AnthropomorphizeBreasts() instead. see canine tfs for an example. if (target.genitals.NormalizeBreasts()) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //HEAT! if (!target.HasTimedEffect <Heat>() || target.GetTimedEffectData <Heat>().totalAddedLibido < 30 && Utils.Rand(6) == 0) { target.GoIntoHeat(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (!target.neck.isDefault && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { if (target.womb.ClearOviposition()) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //[Grow Fur] //FOURTH //MOD NOTE: Body has been reworked since this was written, we'll allow kitsune's to remain unchanged, but all others need to be full fur after this. if ((enhanced || target.lowerBody.type == LowerBodyType.FOX) && target.body.type != BodyType.KITSUNE && !target.body.IsFurBodyType() && Utils.Rand(4) == 0) { BodyData oldBodyData = target.body.AsReadOnlyData(); if (Species.KITSUNE.Score(target) >= 4) { FurColor[] colorChoices; if (Species.KITSUNE.elderKitsuneFurColors.Any(x => x.IsIdenticalTo(target.hair.hairColor)) || Species.KITSUNE.allKitsuneColors.Any(y => y.IsIdenticalTo(target.hair.hairColor))) { colorChoices = new FurColor[] { new FurColor(target.hair.hairColor) }; } else if (target.tail.type == TailType.FOX && target.tail.tailCount == TailType.FOX.maxTailCount) { colorChoices = Species.KITSUNE.allKitsuneColors; } else { colorChoices = Species.KITSUNE.elderKitsuneFurColors; } target.UpdateBody(BodyType.UNDERBODY_FUR, Utils.RandomChoice(colorChoices)); } else { Species.FOX.GetRandomFurColors(out FurColor primary, out FurColor underbody); target.UpdateBody(BodyType.UNDERBODY_FUR, primary, underbody); } //should always be true, but whatever. if (oldBodyData.type != target.body.type) { remainingChanges--; if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //[Grow Fox Legs] //THIRD if ((enhanced || target.ears.type == EarType.FOX) && target.lowerBody.type != LowerBodyType.FOX && Utils.Rand(5) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.FOX); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Grow Fox Ears] //SECOND if ((enhanced || target.tail.type == TailType.FOX) && target.ears.type != EarType.FOX && Utils.Rand(4) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.FOX); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //[Grow Fox Tail](fairly common) //FIRST if (target.tail.type != TailType.FOX && Utils.Rand(4) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.FOX); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //[Grow Fox Face] //LAST - muzzlygoodness //should work from any face, including other muzzles if (target.body.HasAny(EpidermisType.FUR) && target.face.type != FaceType.FOX && Utils.Rand(5) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.FOX); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Arms if (target.arms.type != ArmType.FOX && target.body.HasAny(EpidermisType.FUR) && target.tail.type == TailType.FOX && target.lowerBody.type == LowerBodyType.FOX && Utils.Rand(4) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.UpdateArms(ArmType.FOX); sb.Append(UpdateArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (remainingChanges == changeCount && !(target is null)) { (target as CombatCreature)?.RecoverFatigue(5); } //<MODIFICATION NOTE> //Fall through, cleanup and return. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 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; int changeCount = GenerateChangeCount(target, new int[] { 2, 2 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //sb.Append(InitialTransformText(target)); // Stat changes! if (target.intelligence > 90 && Utils.Rand(3) == 0) { target.ChangeIntelligence(-(Utils.Rand(2) + 1)); } if (Utils.Rand(3) == 0) { target.ChangeToughness(Utils.Rand(2) + 1); } if (target.speed < 75 && Utils.Rand(3) == 0) { target.ChangeSpeed(Utils.Rand(2) + 1); } if (Utils.Rand(3) == 0) { target.ChangeSensitivity(-(Utils.Rand(2) + 1)); } if (Utils.Rand(3) == 0) { target.ChangeCorruption(-(Utils.Rand(3) + 2)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.heightInInches > 67 && Utils.Rand(2) == 0) { target.build.DecreaseHeight((byte)(1 + Utils.Rand(4))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.butt.size < 6 && Utils.Rand(3) == 0) { if (target.butt.size < 5) { target.butt.GrowButt((byte)(1 + Utils.Rand(2))); } else { target.butt.GrowButt(1); } } if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Neck restore if (target.neck.type != NeckType.defaultValue && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (target.back.type != BackType.defaultValue && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.ears.type != EarType.SHEEP && Utils.Rand(3) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.SHEEP); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.tail.type != TailType.SHEEP && Utils.Rand(3) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.SHEEP); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.lowerBody.type != LowerBodyType.CLOVEN_HOOVED && target.tail.type == TailType.SHEEP && Utils.Rand(3) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.CLOVEN_HOOVED); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.horns.type != HornType.SHEEP && target.ears.type == EarType.SHEEP && Utils.Rand(3) == 0) { HornData oldData = target.horns.AsReadOnlyData(); target.UpdateHorns(HornType.SHEEP); sb.Append(UpdateHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.lowerBody.type == LowerBodyType.CLOVEN_HOOVED && target.horns.type == HornType.SHEEP && target.tail.type == TailType.SHEEP && target.ears.type == EarType.SHEEP && target.body.type != BodyType.WOOL) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.WOOL); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //shrinks horns if too feminine and grows if masculine if (target.horns.type == HornType.SHEEP && target.body.type == BodyType.WOOL && target.femininity <= 45 && Utils.Rand(3) == 0) { HornData oldHornData = target.horns.AsReadOnlyData(); target.horns.StrengthenTransform(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.body.type == BodyType.WOOL && target.hair.type != HairType.WOOL && target.femininity >= 65 && Utils.Rand(3) == 0) { HairData oldHair = target.hair.AsReadOnlyData(); target.UpdateHair(HairType.WOOL); } if (target.hips.size < 10 && target.femininity >= 65 && Utils.Rand(3) == 0) { byte oldSize = target.hips.size; if (target.hips.size == 9) { target.hips.GrowHips(1); } else { target.hips.GrowHips((byte)(Utils.Rand(2) + 1)); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } ; } if (target.breasts[0].cupSize < CupSize.DD && target.femininity >= 65 && Utils.Rand(3) == 0) { if (target.breasts[0].cupSize == CupSize.D) { target.breasts[0].GrowBreasts(1); } else { target.breasts[0].GrowBreasts((byte)(Utils.Rand(2) + 1)); } } return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected virtual string ChangedEarsText(Creature target, EarData oldEars) { return(target.ears.TransformFromText(oldEars)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 3, 3 }, 1, 2); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //STATS //Strength h if (Utils.Rand(3) == 0) { //weaker characters gain more if (target.relativeStrength <= 50) { //very weak targets gain more if (target.relativeStrength <= 20) { target.ChangeStrength(3); } else { target.ChangeStrength(2); } } //stronger characters gain less else { //small growth if over 75 if (target.relativeStrength >= 75) { target.ChangeStrength(.5); } //faster from 50-75 else { target.ChangeStrength(1); } } //Chance of speed drop if (Utils.Rand(2) == 0 && target.relativeStrength > 50) { target.ChangeSpeed(-1); } } //Toughness (chance of - sensitivity) if (Utils.Rand(3) == 0) { //weaker characters gain more if (target.relativeToughness <= 50) { //very weak targets gain more if (target.relativeToughness <= 20) { target.ChangeToughness(3); } else { target.ChangeToughness(2); } } //stronger characters gain less else { //small growth if over 75 if (target.relativeToughness >= 75) { target.ChangeToughness(.5); } //faster from 50-75 else { target.ChangeToughness(1); } } //chance of less sensitivity if (Utils.Rand(2) == 0 && target.relativeSensitivity > 10) { if (target.relativeToughness > 75) { target.ChangeSensitivity(-3); } if (target.relativeToughness <= 75 && target.relativeToughness > 50) { target.ChangeSensitivity(-2); } if (target.relativeToughness <= 50) { target.ChangeSensitivity(-3); } } } //SEXUAL //Boosts ball size MORE than equinum :D:D:D:D:D:D: if (Utils.Rand(2) == 0 && target.balls.size <= 5 && target.genitals.HasAnyCocksOfType(CockType.HORSE)) { //Chance of ball growth if not 3" yet if (target.balls.count == 0) { target.balls.GrowBalls(); target.DeltaCreatureStats(lib: 2, lus: 5); } else { target.balls.EnlargeBalls(1); target.DeltaCreatureStats(lib: 1, lus: 3); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { target.RestoreNeck(); } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { target.RestoreBack(); } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); } //Restore arms to become human arms again if (Utils.Rand(4) == 0) { target.RestoreArms(); } //+hooves if (target.lowerBody.type != LowerBodyType.HOOVED) { if (Utils.Rand(3) == 0) { //Catch-all target.UpdateLowerBody(LowerBodyType.HOOVED); target.ChangeSpeed(1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } if (!hyperHappy) { //Kills v****a size (and eventually the whole v****a) if (target.vaginas.Count > 0) { //behavior not defined for multi vag. i' VaginalLooseness minLooseness = target.genitals.minVaginalLooseness; // if (target.genitals.LargestVaginalLooseness() > minLooseness) { //tighten that bitch up! foreach (V****a vag in target.vaginas) { if (vag.looseness > minLooseness) { vag.DecreaseLooseness(); } } } else if (target.vaginas.Count > 1) { target.RemoveExtraVaginas(); } else { if (target.cocks.Count == 0) { target.genitals.AddCock(CockType.HORSE, target.vaginas[0].c**t.length + 2, 1); } //Goodbye womanhood! target.genitals.RemoveAllVaginas(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove extra breast rows if (target.breasts.Count > 1 && Utils.Rand(3) == 0) { target.RemoveExtraBreastRows(); } //Shrink boobages till they are normal else if (Utils.Rand(2) == 0 && target.breasts.Count > 0) { //Single row CupSize smallestCup = EnumHelper.Max(target.genitals.smallestPossibleCupSize, CupSize.B); bool changedAny = false; foreach (Breasts row in target.breasts.Where(x => x.cupSize > smallestCup)) { changedAny |= row.ShrinkBreasts() > 0; if (row.cupSize > CupSize.E_BIG) { changedAny |= row.ShrinkBreasts() > 0; } } if (changedAny && --remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Boosts c**k size up to 36"x5". //MOD: Used linq: find the first c**k that matches the requirement, or if no cocks exist, return null. C**k firstCockToGrow = target.cocks.FirstOrDefault(x => x.type == CockType.HORSE && x.length < 36 || x.girth < 5); //MOD: thus, we can simply place the null check here to see if we actually grow it. if (!(firstCockToGrow is null) && Utils.Rand(2) == 0) { if (firstCockToGrow.girth < 5 && firstCockToGrow.length < 36) { firstCockToGrow.IncreaseThickness(1); firstCockToGrow.IncreaseLength(2 + Utils.Rand(8)); } else if (firstCockToGrow.length < 36) { firstCockToGrow.IncreaseLength(2 + Utils.Rand(8)); } else { firstCockToGrow.IncreaseThickness(1); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Morph dick to horsediiiiick C**k firstNonHorse = target.cocks.FirstOrDefault(x => x.type != CockType.HORSE); if (!(firstNonHorse is null) && Utils.Rand(2) == 0) { //Text for humandicks or others //Text for dogdicks target.genitals.UpdateCockWithLength(firstNonHorse, CockType.HORSE, firstNonHorse.length + 4); target.DeltaCreatureStats(lib: 5, sens: 4, lus: 35); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Males go into rut if (Utils.Rand(4) == 0) { if (target.GoIntoRut()) { remainingChanges--; if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Anti-masturbation status if (Utils.Rand(4) == 0 && !target.HasTimedEffect <Dysfunction>() && target.gender != Gender.GENDERLESS) { target.AddTimedEffect <Dysfunction>(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Appearance shit: //Tail, Ears, Hooves, Horns, Height (no prereq), Face //+height up to 9 foot //Mod note: rand(1.7) seriously? rand only works with ints, guys. that's straight from vanilla. though to be fair, it's not caught there b/c actionscript is shit. if (Utils.Rand(17) < 10 && target.build.heightInInches < 108) { byte temp = (byte)(Utils.Rand(5) + 3); //Slow rate of growth near ceiling if (target.build.heightInInches > 90) { temp /= 2; } //Never 0 if (temp == 0) { temp = 1; } //Flavor texts. Flavored like 1950's cigarettes. Yum. target.build.IncreaseHeight(temp); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Face change, requires Ears + Height + Hooves if (target.ears.type == EarType.COW && target.lowerBody.type == LowerBodyType.HOOVED && target.build.heightInInches >= 90 && target.face.type != FaceType.COW_MINOTAUR && Utils.Rand(3) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.COW_MINOTAUR); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //+mino horns require ears/tail if (Utils.Rand(3) == 0 && target.ears.type == EarType.COW && target.tail.type == TailType.COW) { //New horns or expanding mino horns if (target.horns.type == HornType.BOVINE || target.horns.type == HornType.NONE) { //Get bigger if target has horns if (target.horns.type == HornType.BOVINE) { //Fems horns don't get bigger. if (target.vaginas.Count > 0) { if (target.horns.significantHornSize > 4) { target.genitals.AddPentUpTime(200); target.ChangeLust(20); } else { target.horns.StrengthenTransform(2); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Males horns get 'uge. else { target.horns.StrengthenTransform(2); //boys get a cum refill sometimes if (Utils.Rand(2) == 0) { target.genitals.AddPentUpTime(200); target.ChangeLust(20); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //If no horns yet.. else { HornData oldData = target.horns.AsReadOnlyData(); target.UpdateHorns(HornType.BOVINE); sb.Append(UpdateHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Not mino horns, change to cow-horns else { HornData oldData = target.horns.AsReadOnlyData(); target.UpdateHorns(HornType.BOVINE); sb.Append(UpdateHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //+cow ears - requires tail if (target.ears.type != EarType.COW && target.tail.type == TailType.COW && Utils.Rand(2) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.COW); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //+cow tail if (Utils.Rand(2) == 0 && target.tail.type != TailType.COW) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.COW); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Remove gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { GillData oldData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RestoredGillsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(4) == 0 && target.ass.wetness > target.ass.minWetness) { target.ass.DecreaseWetness(); AnalLooseness targetLooseness = EnumHelper.Max(AnalLooseness.LOOSE, target.ass.minLooseness); if (target.ass.looseness > targetLooseness) { target.ass.DecreaseLooseness(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Give you that mino build! //Default if (remainingChanges == changeCount) { if (target.balls.count > 0) { target.genitals.AddPentUpTime(200); } (target as CombatCreature)?.AddHP(50); target.ChangeLust(50); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected abstract string RestoreEarsText(Creature target, EarData oldData);
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 2, 4 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); #warning fix me int ngPlus(int value) => value; //+3 spe if less than 50 if (target.speed < ngPlus(50)) { target.IncreaseSpeed(3); } //+2 spe if less than 75 else if (target.speed < ngPlus(75)) { target.IncreaseSpeed(2); } //+1 if above 75. else { target.IncreaseSpeed(); } // ------------- Sexual changes ------------- //-Nipples reduction to 1 per tit. if (target.genitals.hasQuadNipples && Utils.Rand(4) == 0) { target.genitals.SetQuadNipples(false); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove extra breast rows if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !hyperHappy) { target.RemoveExtraBreastRows(); } //-Butt > 5 - decrease butt size if (target.butt.size > 5 && Utils.Rand(4) == 0) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } target.butt.ShrinkButt(); } if (target.gender.HasFlag(Gender.FEMALE)) { //Breasts > D cup - Decrease breast size by up to 3 cups //Breasts < B cup - Increase breast size by 1 cup if (target.breasts.Any(x => x.cupSize > CupSize.D || x.cupSize < CupSize.B) && Utils.Rand(3) == 0) { foreach (Breasts breast in target.breasts) { if (breast.cupSize > CupSize.D) { breast.ShrinkBreasts((byte)(1 + Utils.Rand(3))); } else if (breast.cupSize < CupSize.B) { breast.GrowBreasts(); } } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Hips > 12 - decrease hip size by 1-3 sizes if (target.hips.size > 12 && Utils.Rand(3) == 0) { target.hips.ShrinkHips((byte)(1 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Hips < 6 - increase hip size by 1-3 sizes if (target.hips.size < 6 && Utils.Rand(3) == 0) { target.hips.GrowHips((byte)(1 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.genitals.nippleLength > 1 && Utils.Rand(3) == 0) { target.genitals.SetNippleLength(target.genitals.nippleLength / 2); } if (target.hasVagina && target.vaginas[0].wetness < VaginalWetness.SLICK && Utils.Rand(4) == 0) { target.vaginas[0].IncreaseWetness(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Increase tone (up to 65) if (target.build.muscleTone < 65 && Utils.Rand(3) == 0) { } //Decrease thickness (down to 35) if (target.build.thickness > 35 && Utils.Rand(3) == 0) { } } if (target.gender == Gender.MALE) { //Breasts > B cup (or applicable male max size if it's somehow > B cup) - decrease by 1 cup size CupSize targetSize = EnumHelper.Max(target.genitals.smallestPossibleMaleCupSize, CupSize.B); if (target.genitals.BiggestCupSize() > targetSize && Utils.Rand(3) == 0) { foreach (Breasts breast in target.breasts) { if (breast.cupSize > targetSize) { breast.ShrinkBreasts(); } } target.IncreaseSpeed(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.genitals.nippleLength > 1 && Utils.Rand(3) == 0) { target.genitals.SetNippleLength(target.genitals.nippleLength / 2); } //Hips > 10 - decrease hip size by 1-3 sizes if (target.hips.size > 10 && Utils.Rand(3) == 0) { target.hips.ShrinkHips((byte)(1 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Hips < 2 - increase hip size by 1-3 sizes if (target.hips.size < 2 && Utils.Rand(3) == 0) { target.hips.GrowHips((byte)(1 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Increase tone (up to 70) if (target.build.muscleTone < 70 && Utils.Rand(3) == 0) { } //Decrease thickness (down to 35) if (target.build.thickness > 35 && Utils.Rand(3) == 0) { } } if (target.gender.HasFlag(Gender.MALE)) { //C**k -> Red Panda C**k if (target.hasCock && target.cocks[0].type != CockType.RED_PANDA && Utils.Rand(3) == 0) { target.genitals.UpdateCock(0, CockType.RED_PANDA); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } C**k shortest = target.genitals.ShortestCock(); //C**k < 6 inches - increase by 1-2 inches if (shortest.length < 6 && Utils.Rand(3) == 0) { double increment = shortest.IncreaseLength(1 + Utils.Rand(2)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } C**k longest = target.genitals.LongestCock(); //Shrink oversized cocks if (longest.length > 16 && Utils.Rand(3) == 0) { longest.DecreaseLength((Utils.Rand(10) + 5) / 10); if (longest.girth > 3) { longest.DecreaseThickness((Utils.Rand(4) + 1) / 10); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } C**k smallestArea = target.genitals.SmallestCockByArea(); //C**k thickness <2 - Increase c**k thickness if (smallestArea.area < 10 && Utils.Rand(3) == 0) { smallestArea.IncreaseThickness(1.5); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Remove additional cocks if (target.cocks.Count > 1 && Utils.Rand(3) == 0) { //what a dick. removes the second, and only the second. not the last one or anything. ok. it's supported now. target.genitals.RemoveCockAt(1, 1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove additional balls/remove uniball if (target.balls.count > 0 && Utils.Rand(3) == 0) { if (target.balls.size > 5) { target.balls.ShrinkBalls((byte)(2 + Utils.Rand(3))); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else if (target.balls.size > 2) { target.balls.ShrinkBalls(1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else if (target.balls.count != 2) { //removes uniball, sets count to 2. target.balls.MakeStandard(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); } // ------------- Physical changes ------------- // Ears if (target.ears.type != EarType.RED_PANDA && Utils.Rand(3) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.RED_PANDA); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Remove non-cockatrice antennae if (target.antennae.type != AntennaeType.COCKATRICE && !target.antennae.isDefault && 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)); } } // Restore eyes, if more than two if (target.eyes.count > 2 && Utils.Rand(4) == 0) { EyeData oldData = target.eyes.AsReadOnlyData(); target.RestoreEyes(); sb.Append(RestoredEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Hair // store current states first bool hasPandaHairColor = Species.RED_PANDA.availableHairColors.Contains(target.hair.hairColor); bool hasNormalHair = target.hair.type == HairType.NORMAL; HairType oldHairType = target.hair.type; if ((!hasNormalHair || target.hair.length == 0 || !hasPandaHairColor) && Utils.Rand(3) == 0) { target.UpdateHair(HairType.NORMAL); if (!hasPandaHairColor) { target.hair.SetHairColor(Utils.RandomChoice(Species.RED_PANDA.availableHairColors)); } if (target.hair.length == 0) { // target is bald target.hair.SetHairLength(1); } target.hair.SetHairGrowthStatus(true); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Face if (target.face.type != FaceType.RED_PANDA && target.ears.type == EarType.RED_PANDA && target.body.IsFurBodyType() && Utils.Rand(3) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.RED_PANDA); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Arms if (target.arms.type != ArmType.RED_PANDA && target.ears.type == EarType.RED_PANDA && target.tail.type == TailType.RED_PANDA && Utils.Rand(3) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.UpdateArms(ArmType.RED_PANDA); sb.Append(UpdateArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Legs if (target.lowerBody.type != LowerBodyType.RED_PANDA && target.arms.type == ArmType.RED_PANDA && Utils.Rand(4) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.RED_PANDA); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Tail if (target.tail.type != TailType.RED_PANDA && Utils.Rand(4) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.RED_PANDA); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // SKin // Fix the underBody, if the skin is already furred if (target.body.type == BodyType.SIMPLE_FUR && Utils.Rand(3) == 0) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.UNDERBODY_FUR, new FurColor(HairFurColors.RUSSET), new FurColor(HairFurColors.BLACK)); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } else if (!target.body.IsFurBodyType() && target.arms.type == ArmType.RED_PANDA && target.lowerBody.type == LowerBodyType.RED_PANDA && Utils.Rand(4) == 0) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.UNDERBODY_FUR, new FurColor(HairFurColors.RUSSET), new FurColor(HairFurColors.BLACK)); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //FAILSAFE CHANGE if (remainingChanges == changeCount) { if (Utils.Rand(100) == 0) { } else { if (target is CombatCreature) { ((CombatCreature)target).AddHP(250); } target.ChangeLust(3); } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 2, 3 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //Text go! //Speed raises up to 75 if (target.relativeSpeed < 75 && Utils.Rand(3) == 0) { //low speed if (target.relativeSpeed <= 30) { target.ChangeSpeed(2); } //medium speed else if (target.relativeSpeed <= 60) { target.ChangeSpeed(1); } //high speed else { target.ChangeSpeed(.5); } } //Strength raises to 40 if (target.relativeStrength < 40 && Utils.Rand(3) == 0) { target.ChangeStrength(1); } //Strength ALWAYS drops if over 60 //Does not add to change total else if (target.relativeStrength > 60 && Utils.Rand(2) == 0) { target.ChangeStrength(-2); } //Toughness drops if over 50 //Does not add to change total if (target.relativeToughness > 50 && Utils.Rand(2) == 0) { target.ChangeToughness(-2); } //Intelliloss if (Utils.Rand(4) == 0) { target.ChangeIntelligence(-1); } //Libido gain if (target.relativeLibido < 80 && Utils.Rand(4) == 0) { target.DeltaCreatureStats(lib: 1, sens: .25); } //Sexual changes would go here if I wasn't a tard. //Heat if (Utils.Rand(4) == 0) { bool intensified = target.HasTimedEffect <Heat>(); if (target.GoIntoHeat()) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Shrink the boobalies down to A for men or C for girls. if (Utils.Rand(4) == 0 && !hyperHappy) { CupSize targetSize; //female or herm. targeting C cup, but will respect smallest possible cup if it's somehow above a c cup. if (target.gender.HasFlag(Gender.FEMALE)) { targetSize = EnumHelper.Max(CupSize.C, target.genitals.smallestPossibleFemaleCupSize); } //male or genderless. targeting A cup, but will respect smallest possible cup if somehow above an a cup. else { targetSize = EnumHelper.Max(CupSize.A, target.genitals.smallestPossibleMaleCupSize); } //IT IS! int rowsShrunk = 0; foreach (Breasts row in target.breasts) { //If this row is over threshhold if (row.cupSize > targetSize) { //Big change if (row.cupSize > CupSize.EE_BIG) { row.ShrinkBreasts((byte)(2 + Utils.Rand(3))); } //Small change else { row.ShrinkBreasts(1); } rowsShrunk++; } } //Count that t**s were shrunk if (rowsShrunk > 0) { remainingChanges--; if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //Cat dangly-doo. if (target.cocks.Count > 0 && target.cocks.Any(x => x.type != CockType.CAT) && (target.ears.type == EarType.CAT || Utils.Rand(3) > 0) && (target.tail.type == TailType.CAT || Utils.Rand(3) > 0) && Utils.Rand(4) == 0) { C**k nonCat = target.cocks.First(x => x.type != CockType.CAT); target.genitals.UpdateCock(nonCat, CockType.CAT); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Cat penorz shrink if (target.genitals.HasAnyCocksOfType(CockType.CAT) && Utils.Rand(3) == 0 && !hyperHappy) { //loop through and find a cat wang. foreach (C**k catCock in target.cocks.Where(x => x.type == CockType.CAT)) { //lose 33% size until under 10, then lose 2" at a time if (catCock.length > 16) { catCock.SetLength(catCock.length * .66); } else if (catCock.length > 6) { catCock.DecreaseLength(2); } if (catCock.length / 5 < catCock.girth && catCock.girth > 1.25) { catCock.SetGirth(catCock.length / 6); } } //(big sensitivity boost) target.ChangeSensitivity(5); //Make note of other dicks changing if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Body type changes. Teh rarest of the rare. // Catgirl-face -> cat-morph-face if (target.face.type == FaceType.CAT && !target.face.isFullMorph && target.tongue.type == TongueType.CAT && target.ears.type == EarType.CAT && target.tail.type == TailType.CAT && target.lowerBody.type == LowerBodyType.CAT && target.arms.type == ArmType.CAT && (target.body.IsFurBodyType() /*|| (target.body.type == BodyType.REPTILE && Species.SPHINX.Score(target) >= 4)*/) && Utils.Rand(5) == 0) { target.face.StrengthenFacialMorph(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //DA EARZ if (target.ears.type != EarType.CAT && Utils.Rand(5) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.CAT); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //DA TAIL (IF ALREADY HAZ URZ) if (target.tail.type != TailType.CAT && target.ears.type == EarType.CAT && Utils.Rand(5) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.CAT); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Da paws (if already haz ears & tail) if (target.tail.type == TailType.CAT && target.ears.type == EarType.CAT && Utils.Rand(5) == 0 && target.lowerBody.type != LowerBodyType.CAT) { //hoof to cat: if (target.lowerBody.type == LowerBodyType.HOOVED) { } //Goo to cat else if (target.lowerBody.type == LowerBodyType.GOO) { } //non hoof to cat: LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.CAT); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //TURN INTO A FURRAH! OH SHIT if (target.tail.type == TailType.CAT && target.ears.type == EarType.CAT && Utils.Rand(5) == 0 && target.lowerBody.type == LowerBodyType.CAT && !target.body.IsFurBodyType()) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.UNDERBODY_FUR); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Fix old cat faces without cat-eyes. if (target.face.type == FaceType.CAT && target.eyes.type != EyeType.CAT && Utils.Rand(3) == 0) { EyeData oldData = target.eyes.AsReadOnlyData(); target.UpdateEyes(EyeType.CAT); sb.Append(UpdateEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //CAT-FACE! FULL ON FURRY! RAGE AWAY NEKOZ if (target.tail.type == TailType.CAT && target.ears.type == EarType.CAT && target.lowerBody.type == LowerBodyType.CAT && target.face.type != FaceType.CAT && Utils.Rand(5) == 0) { //Gain cat face, replace old face //MOD NOTE: but not a full-morph cat face. just level 1. FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.CAT); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Cat-tongue if (target.face.type == FaceType.CAT && target.tongue.type != TongueType.CAT && Utils.Rand(5) == 0) { TongueData oldData = target.tongue.AsReadOnlyData(); target.UpdateTongue(TongueType.CAT); sb.Append(UpdateTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Arms if (target.arms.type != ArmType.CAT && target.body.IsFurBodyType() && target.tail.type == TailType.CAT && target.lowerBody.type == LowerBodyType.CAT && Utils.Rand(4) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.UpdateArms(ArmType.CAT); sb.Append(UpdateArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Remove gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { GillData oldData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RestoredGillsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //FAILSAFE CHANGE if (remainingChanges == changeCount) { (target as CombatCreature)?.AddHP(50); target.ChangeLust(3); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
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)); }
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 }, isEnhanced ? 2 : 1); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //----------------------- // SIZE MODIFICATIONS //----------------------- //Increase thickness if (Utils.Rand(3) == 0 && target.build.thickness < 75) { target.build.ChangeThicknessToward(75, 3); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Decrease muscle tone if (Utils.Rand(3) == 0 && target.gender.HasFlag(Gender.FEMALE) && target.build.muscleTone > 20) { target.build.ChangeMuscleToneToward(20, 4); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Increase hip rating if (Utils.Rand(3) == 0 && target.gender.HasFlag(Gender.FEMALE) && target.hips.size < 15) { target.hips.GrowHips(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Increase ass rating if (Utils.Rand(3) == 0 && target.butt.size < 12) { target.butt.GrowButt(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Increase ball size if you have balls. if (Utils.Rand(3) == 0 && target.balls.count > 0 && target.balls.size < 4) { target.balls.EnlargeBalls(1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //----------------------- // TRANSFORMATIONS //----------------------- //Gain pig c**k, independent of other pig TFs. if (Utils.Rand(4) == 0 && target.hasCock && !target.genitals.OnlyHasCocksOfType(CockType.PIG)) { if (target.cocks.Count == 1) { //Single c**k target.genitals.UpdateCock(0, CockType.PIG); } else { //Multiple cocks target.genitals.UpdateCock(target.cocks.First(x => x.type != CockType.PIG), CockType.PIG); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Gain pig ears! if (Utils.Rand(isEnhanced ? 3 : 4) == 0 && target.ears.type != EarType.PIG) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.PIG); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Gain pig tail if you already have pig ears! if (Utils.Rand(isEnhanced ? 2 : 3) == 0 && target.ears.type == EarType.PIG && target.tail.type != TailType.PIG) { if (!target.tail.isDefault) //If you have non-pig tail. { } else //If you don't have a tail. { target.UpdateTail(TailType.PIG); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Gain pig tail even when centaur, needs pig ears. if (Utils.Rand(isEnhanced ? 2 : 3) == 0 && target.ears.type == EarType.PIG && target.tail.type != TailType.PIG && target.lowerBody.isQuadruped && (target.lowerBody.type == LowerBodyType.HOOVED || target.lowerBody.type == LowerBodyType.PONY)) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.PIG); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Turn your lower body into pig legs if you have pig ears and tail. if (Utils.Rand(isEnhanced ? 3 : 4) == 0 && target.ears.type == EarType.PIG && target.tail.type == TailType.PIG && target.lowerBody.type != LowerBodyType.CLOVEN_HOOVED) { if (target.lowerBody.isQuadruped) //Centaur { } else if (target.lowerBody.type == LowerBodyType.NAGA) //Naga { } else //Bipedal { target.UpdateLowerBody(LowerBodyType.CLOVEN_HOOVED); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Gain pig face when you have the first three pig TFs. if (Utils.Rand(isEnhanced ? 2 : 3) == 0 && target.ears.type == EarType.PIG && target.tail.type == TailType.PIG && target.lowerBody.type == LowerBodyType.CLOVEN_HOOVED && target.face.type != FaceType.PIG) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.PIG); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Gain enhanced face if you have pig face. if (Utils.Rand(3) == 0 && target.ears.type == EarType.PIG && target.tail.type == TailType.PIG && target.lowerBody.type == LowerBodyType.CLOVEN_HOOVED && target.face.type == FaceType.PIG && !target.face.isFullMorph) { target.face.StrengthenFacialMorph(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Change skin colour if (Utils.Rand(isEnhanced ? 3 : 4) == 0) { //String skinToBeChosen = Utils.RandomChoice(enhanced?["dark brown", "brown", "brown"] : ["pink", "tan", "sable"]); //target.body.primarySkin.tone = skinToBeChosen; //target.arms.updateClaws(target.arms.claws.type); //getGame().rathazul.addMixologyXP(20); //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); } if (remainingChanges == changeCount) { } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
/** Original Credits: * @since March 26, 2018 * @author Stadler76 */ 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 }, isEnhanced ? 3 : 1); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligencelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //Used as a holding variable for biggest dicks and the like //**************** //General Effects: //**************** //-Int less than 10 if (target is IExtendedCreature extendedCreature && !extendedCreature.extendedData.resistsTFBadEnds && target.intelligence < 10) { if (target.intelligence < 8 && Species.KANGAROO.Score(target) >= 5) { isBadEnd = true; return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Speed to 70 if (target.relativeSpeed < 70 && Utils.Rand(3) == 0) { //2 points up if below 40! if (target.relativeSpeed < 40) { target.ChangeSpeed(1); } target.ChangeSpeed(1); } //-Int to 10 if (target.intelligence > 2 && Utils.Rand(3) == 0) { //Gain dumb (smart!) //gain dumb (30-10 int): //gain dumb (10-1 int): target.ChangeIntelligence(-1); } //**************** //Appearance Effects: //**************** //-Hip widening funtimes if (Utils.Rand(4) == 0 && target.hips.size < 40) { target.hips.GrowHips(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Restore arms to become human arms again if (Utils.Rand(4) == 0) { ArmData oldData = target.arms.AsReadOnlyData(); target.RestoreArms(); sb.Append(RestoredArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Remove feathery hair if (RemoveFeatheryHair(target)) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Remove odd eyes if (Utils.Rand(5) == 0 && !target.eyes.isDefault) { EyeData oldData = target.eyes.AsReadOnlyData(); target.RestoreEyes(); sb.Append(RestoredEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //**************** //Sexual: //**************** //-Shrink balls down to reasonable size (3?) if (target.balls.size >= 4 && Utils.Rand(2) == 0) { target.balls.ShrinkBalls(); target.genitals.IncreaseCumMultiplier(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Shorten clits to reasonable size //MOD NOTE: lets do one at a time. i generally do all because that's the easiest way to port it, but f**k it. V****a largestClit = target.genitals.LargestVaginaByClitSize(); if (target.hasVagina && largestClit.c**t.length >= 4 && Utils.Rand(5) == 0) { largestClit.ShrinkClit(largestClit.c**t.length / 2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Find biggest dick! C**k biggestCock = target.genitals.LongestCock(); //-Shrink dicks down to 8\" max. if (target.hasCock && biggestCock.length >= 16 && Utils.Rand(5) == 0) { biggestCock.DecreaseLength(biggestCock.length / 2); biggestCock.DecreaseThickness(2 * biggestCock.length / 3); if (biggestCock.girth * 6 > biggestCock.length) { biggestCock.DecreaseThickness(.4); } else if (biggestCock.girth * 8 > biggestCock.length) { biggestCock.DecreaseThickness(.2); } if (biggestCock.girth < .5) { biggestCock.SetGirth(0.5); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //C**K TF! if (target.cocks.Any(x => x.type != CockType.KANGAROO) && Utils.Rand(isEnhanced ? 2 : 3) == 0) { C**k notRoo = target.cocks.First(x => x.type != CockType.KANGAROO); target.genitals.UpdateCock(notRoo, CockType.KANGAROO); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //**************** //Big Kanga Morphs //type 1 ignores normal restrictions //**************** //-Face (Req: Fur + Feet) if (target.face.type != FaceType.KANGAROO && ((target.body.IsFurBodyType() && target.lowerBody.type == LowerBodyType.KANGAROO) || isEnhanced) && Utils.Rand(4) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.KANGAROO); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Fur (Req: Footsies) if (!target.body.IsFurBodyType() && (target.lowerBody.type == LowerBodyType.KANGAROO || isEnhanced) && Utils.Rand(4) == 0) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.SIMPLE_FUR, new FurColor(HairFurColors.BROWN)); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Roo footsies (Req: Tail) if (target.lowerBody.type != LowerBodyType.KANGAROO && (isEnhanced || target.tail.type == TailType.KANGAROO) && Utils.Rand(4) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.KANGAROO); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Roo tail (Req: Ears) if (target.tail.type != TailType.KANGAROO && Utils.Rand(4) == 0 && (isEnhanced || target.ears.type == EarType.KANGAROO)) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.KANGAROO); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //-Roo ears if (target.ears.type != EarType.KANGAROO && Utils.Rand(4) == 0) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.KANGAROO); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //UBEROOOO //kangaroo perk: - any liquid or food intake will accelerate a pregnancy, but it will not progress otherwise if (target.womb.canObtainDiapause && Species.KANGAROO.Score(target) > 4 && Utils.Rand(4) == 0 && target.hasVagina) { target.womb.EnableDiapause(); //Perk name and description: if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //trigger effect: Your body reacts to the influx of nutrition, accelerating your pregnancy. Your belly bulges outward slightly. } // Remove gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { target.RestoreGills(); } if (remainingChanges == changeCount) { (target as CombatCreature)?.RecoverFatigue(40); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected virtual string UpdateEarsText(Creature target, EarData oldData) { return(target.ears.TransformFromText(oldData)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 3, 3 }); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //eat it: //stat gains: //gain speed to ceiling of 80 if (target.relativeSpeed < 80 && Utils.Rand(3) == 0) { if (target.relativeSpeed < 40) { target.IncreaseSpeed(2); } else { target.IncreaseSpeed(); } } //lose toughness to floor of 50 if (Utils.Rand(4) == 0 && target.relativeToughness > 50) { if (target.relativeToughness > 75) { target.DecreaseToughness(2); } else { target.DecreaseToughness(); } } //gain sensitivity if (target.relativeSensitivity < 80 && Utils.Rand(3) == 0) { if (target.relativeSensitivity < 60) { target.IncreaseSensitivity(4); } else { target.IncreaseSensitivity(2); } } //Sex stuff if (target.hasCock) { //gain ball size if (target.balls.count > 0 && target.balls.size < 15 && Utils.Rand(4) == 0) { target.balls.EnlargeBalls((byte)(2 + Utils.Rand(3))); target.ChangeLibido(1); } } //gain balls up to 2 (only if full-coon face and fur; no dick required) if (target.balls.count == 0 && target.body.IsFurBodyType() && target.face.type == FaceType.RACCOON && Utils.Rand(3) == 0) { target.balls.GrowBalls(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //gain thickness or lose tone or whatever - standard message if (Utils.Rand(4) == 0 && target.build.thickness < 80) { target.build.ChangeThicknessToward(80, 2); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //bodypart changes: if (target.tail.type != TailType.RACCOON && Utils.Rand(4) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.RACCOON); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //gain fur if (target.lowerBody.type == LowerBodyType.RACCOON && target.ears.type == EarType.RACCOON && !target.body.IsFurBodyType() && Utils.Rand(4) == 0) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.SIMPLE_FUR, new FurColor(HairFurColors.GRAY)); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //gain coon ears if (target.tail.type == TailType.RACCOON && target.ears.type != EarType.RACCOON && Utils.Rand(4) == 0) { //from dog, kangaroo, bunny, other long ears //from cat, horse, cow ears //from human, goblin, lizard or other short ears EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.RACCOON); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //gain feet-coon if (target.ears.type == EarType.RACCOON && target.lowerBody.type != LowerBodyType.RACCOON && Utils.Rand(4) == 0) { //from naga non-feet (gain fatigue and lose lust) if (target.lowerBody.type == LowerBodyType.NAGA) { target.ChangeLust(-30); (target as CombatCreature)?.GainFatigue(5); } LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.RACCOON); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //gain half-coon face (prevented if already full-coon) if (target.face.type != FaceType.RACCOON && Utils.Rand(4) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.RACCOON); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //gain full-coon face (requires half-coon and fur) //from humanoid - should be the only one possible else if (target.face.type == FaceType.RACCOON && !target.face.isFullMorph && target.lowerBody.type == LowerBodyType.RACCOON && target.body.IsFurBodyType() && Utils.Rand(4) == 0) { target.face.StrengthenFacialMorph(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //fatigue damage (only if face change was not triggered) else if (target is CombatCreature && Utils.Rand(2) == 0 && target.face.type != FaceType.RACCOON) { (target as CombatCreature).GainFatigue(10); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target is CombatCreature && remainingChanges == changeCount) { (target as CombatCreature).GainFatigue(5); } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }