protected byte GrowCockGeneric(Creature creature, byte count) { if (count == 0) { return(0); } byte added = 0; while (count-- > 0 && creature.AddCock(CockType.HUMAN, Utils.Rand(3) + 5, 0.75)) { added++; } return(added); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //huh. no rng rolls, just starts at 3. cool int changeCount = GenerateChangeCount(target, null, 3); int remainingChanges = changeCount; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //Stats and genital changes if (Utils.Rand(2) == 0) { target.ChangeLust(25); if (target.relativeLibido < 100) { if (target.relativeLibido < 50) { target.ChangeLibido(1); } target.ChangeLibido(1); } } C**k smallest = target.genitals.ShortestCock(); if (target.hasCock && smallest.length < 12 && Utils.Rand(3) == 0) { smallest.IncreaseLength(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } C**k thinnest = target.genitals.ThinnestCock(); if (target.hasCock && thinnest.girth < 4 && Utils.Rand(3) == 0) { thinnest.IncreaseThickness(0.5); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.balls.count > 0 && target.genitals.cumMultiplier < 50 && Utils.Rand(3) == 0) { target.ChangeLust(20); if (target.genitals.cumMultiplier < 10) { target.genitals.IncreaseCumMultiplier(1); } if (target.genitals.cumMultiplier < 50) { target.genitals.IncreaseCumMultiplier(0.5); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.hasVagina && target.genitals.standardBonusVaginalCapacity > 0) { target.genitals.DecreaseBonusVaginalCapacity((ushort)(Utils.Rand(5) + 5)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.hasVagina && !target.hasCock) { target.RemoveAllVaginas(); target.AddCock(CockType.HUMAN, 6, 1); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.hasCock && !target.balls.hasBalls) { target.balls.GrowBalls(); target.HaveGenericCockOrgasm(0, true, true); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Transformations //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { target.RestoreNeck(); } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { target.RestoreBack(); } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { if (target.womb.ClearOviposition()) { sb.Append(RemovedOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //remove anything with a scaly or partially scaly body. now affects cockatrice too! if (Utils.Rand(3) == 0 && target.body.HasAny(EpidermisType.SCALES)) { BodyData oldData = target.body.AsReadOnlyData(); target.RestoreBody(); sb.Append(RestoredBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.arms.type != ArmType.HUMAN) { ArmData oldData = target.arms.AsReadOnlyData(); target.RestoreArms(); sb.Append(RestoredArmsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(4) == 0 && target.lowerBody.type != LowerBodyType.CLOVEN_HOOVED) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.CLOVEN_HOOVED); sb.Append(UpdateLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.lowerBody.type == LowerBodyType.CLOVEN_HOOVED && target.horns.type == HornType.GOAT && target.face.type != FaceType.HUMAN) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.HUMAN); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //MOD: anything with scales will prevent this, including partial scales (a la cockatrice). since body is reworked a lot of these checks are hard to port, //but this i think is the closest i can get. if (Utils.Rand(4) == 0 && !target.body.HasAny(EpidermisType.SCALES) && target.ears.type != EarType.ELFIN) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.ELFIN); sb.Append(UpdateEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.horns.type == HornType.NONE) { HornData oldData = target.horns.AsReadOnlyData(); target.UpdateHorns(HornType.GOAT); sb.Append(UpdateHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Mod Note: Horns: if (Utils.Rand(3) == 0 && target.horns.type != HornType.GOAT) { HornData oldData = target.horns.AsReadOnlyData(); target.UpdateHorns(HornType.GOAT); sb.Append(UpdateHornsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.horns.type == HornType.GOAT && target.horns.CanStrengthen) { target.horns.StrengthenTransform(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(4) == 0 && target.antennae.type != AntennaeType.NONE) { AntennaeData oldData = target.antennae.AsReadOnlyData(); target.RestoreAntennae(); sb.Append(RestoredAntennaeText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.cocks.Count == 1 && target.cocks[0].type != CockType.HUMAN) { target.genitals.UpdateCock(0, CockType.HUMAN); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.cocks.Count > 1 && !target.genitals.OnlyHasCocksOfType(CockType.HUMAN)) { target.genitals.UpdateCock(target.cocks.First(x => x.type != CockType.HUMAN), CockType.HUMAN); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (Utils.Rand(3) == 0 && target.tail.type != TailType.SATYR) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.SATYR); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
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)); }
//MOD NOTE: this has been changed to work like bimbo liqueur - it will remove all vaginas and restore breasts to manly defaults if //the futa perk is not active. protected override string OnConsumeAttempt(Creature consumer, out bool consumeItem, out bool isBadEnd) { isBadEnd = false; StringBuilder sb = new StringBuilder(); //get our common bimbo/bro/futa perk store. Note that it may be null (if we don't have it). BimBro bimbro = consumer.GetPerkData <BimBro>(); //intro text. //first check: we don't have any bro/futa/bimbo perks, because it's not in our perk list. do the standard faire. sb.Append(Intro(consumer, bimbro)); if (bimbro?.hasBroEffect == true) { (consumer as CombatCreature)?.RecoverFatigue(33); (consumer as CombatCreature)?.AddHPPercent(1); } VaginaCollectionData oldVaginaCollection = consumer.genitals.allVaginas.AsReadOnlyData(); //get taller. only occurs if the player does not already have the bro perk in any form. if ((bimbro is null || !bimbro.broBody) && consumer.build.heightInInches < 77) { short delta = consumer.build.SetHeight(77); sb.Append(GrewTaller(consumer, delta)); } //(T**s b' gone) //Does not affect creature if they already have bimbo or futa perks. if (bimbro is null || bimbro.broBody) { //Note: minimum cup size respects current gender. we'll need to make the creature male (or genderless) first. consumer.RemoveAllVaginas(); if (consumer.genitals.BiggestCupSize() >= consumer.genitals.smallestPossibleMaleCupSize) { BreastCollectionData oldRows = consumer.genitals.allBreasts.AsReadOnlyData(); consumer.RemovePerk <Feeder>(); bool stoppedLactation = consumer.genitals.SetLactationTo(LactationStatus.NOT_LACTATING); sb.Append(ChangedBreastsText(consumer, bimbro, oldRows, stoppedLactation)); } } //Body tone Flavor text. No effect on stats (yet) sb.Append(BecomeRippedFlavorText(consumer, bimbro)); //Dick&balls growth. Affects all. //(has dick less than 10 inches) if (consumer.hasCock) { bool grewBalls = !consumer.hasBalls; bool lengthenedCock = false; if (consumer.cocks[0].length < 10) { lengthenedCock = true; consumer.cocks[0].SetLength(10); if (consumer.cocks[0].girth < 2.75) { consumer.cocks[0].SetGirth(2.75); } } if (!consumer.hasBalls) { consumer.balls.GrowBalls(2, 3); } sb.Append(LengthenedCock(consumer, bimbro, lengthenedCock, grewBalls)); } //(No dick) else { consumer.AddCock(CockType.defaultValue, 12, 2.75); bool grewBalls = false; if (!consumer.balls.hasBalls) { grewBalls = true; consumer.balls.GrowBalls(2, 3); } sb.Append(GrewCock(consumer, bimbro, grewBalls)); } //(Pussy b gone) //Note: only applies if consumer does not have bimbo or futa perks. also note that we already silently removed them so the breasts would resize correctly. //so all this does is print out the data. if ((bimbro is null || bimbro.broBody) && oldVaginaCollection.hasVagina) { sb.Append(RemovedAllVaginas(consumer, oldVaginaCollection)); } //(below max masculinity) if ((bimbro is null || bimbro.broBody) && consumer.femininity > 0) { FemininityData oldFem = consumer.femininity.AsReadOnlyData(); sb.Append(consumer.ModifyFemininity(0, 100)); sb.Append(Masculinize(consumer, bimbro is null, oldFem)); } //max tone. Thickness + 50 //both are silent. consumer.build.ChangeMuscleToneToward(100, 100); consumer.build.ChangeThicknessToward(100, 50); if (consumer.intelligence > 21) { consumer.SetIntelligence((byte)Math.Floor(Math.Max(consumer.intelligenceTrue / 5.0, 21))); } consumer.DeltaCreatureStats(str: 33, tou: 33, inte: -1, lib: 4, lus: 40); sb.Append(Outro(consumer, bimbro, consumer.perks.HasPerk <Feeder>())); //apply the perks. this will also correct any silent data we missed. if (bimbro is null) { consumer.AddPerk(new BimBro(Gender.MALE)); } else { bimbro.Broify(); } ////Bonus cum production! //sb.Append("<b>(Bro Body - Perk Gained!)" + Environment.NewLine); //sb.Append("(Bro Brains - Perk Gained!)</b>" + Environment.NewLine);//int to 20. max int 50) //if (consumer.HasPerk<Feeder>()) //{ // sb.Append("<b>(Perk Lost - Feeder!)</b>" + Environment.NewLine); // consumer.RemovePerk<Feeder>(); //} consumeItem = true; return(sb.ToString()); }
private string DoTransformationCommon(Creature target, bool currentlyInCombat, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 2 }); int remainingChanges = changeCount; bool statsChanged = false; StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target, currentlyInCombat)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. if (target.relativeSpeed < 70 && Utils.Rand(2) == 0) { target.IncreaseSpeed(2 - (target.relativeSpeed / 50)); statsChanged = true; } //this will handle the edge case where the change count starts out as 0. if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } //Any transformation related changes go here. these typically cost 1 change. these can be anything from body parts to gender (which technically also changes body parts, //but w/e). You are required to make sure you return as soon as you've applied changeCount changes, but a single line of code can be applied at the end of a change to do //this for you. //paste this line after any tf is applied, and it will: automatically decrement the remaining changes count. if it becomes 0 or less, apply the total number of changes //underwent to the target's change count (if applicable) and then return the StringBuilder content. //if (--remainingChanges <= 0) return ApplyChangesAndReturn(target, sb, changeCount - remainingChanges); //Neck restore if (target.neck.type != NeckType.HUMANOID && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (target.back.type != BackType.SHARK_FIN && !target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removes wings and shark fin if ((target.wings.type != WingType.NONE || target.back.type == BackType.SHARK_FIN) && Utils.Rand(3) == 0) { if (target.back.type == BackType.SHARK_FIN) { target.RestoreBack(); } WingData oldData = target.wings.AsReadOnlyData(); target.RestoreWings(); sb.Append(RestoredWingsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Removes antennae if (target.antennae.type != AntennaeType.NONE && Utils.Rand(3) == 0) { AntennaeData oldData = target.antennae.AsReadOnlyData(); target.RestoreAntennae(); sb.Append(RestoredAntennaeText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Sexual changes //-Remove extra breast rows if (target.breasts.Count > 1 && Utils.Rand(3) == 0 && !HyperHappySettings.isEnabled) { target.genitals.RemoveExtraBreastRows(); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //adjust cocks until the target has 2 reptilian cocks. only occurs for males or herms, or genderless if the hyper happy flag is off and a 1/2 roll procs true. if ((target.gender.HasFlag(Gender.MALE) || (target.gender == Gender.GENDERLESS && !HyperHappySettings.isEnabled && Utils.Rand(2) == 0)) && (target.cocks.Count != 2 || target.genitals.CountCocksOfType(CockType.LIZARD) != 2) && Utils.Rand(3) == 0) { int lizardCocks = target.genitals.CountCocksOfType(CockType.LIZARD); //grant up to 2 lizard cocks. if (lizardCocks < 2) { //if we have two or less cocks, convert all that we do have to lizard. if (target.cocks.Count < 3) { foreach (C**k c**k in target.cocks) { target.genitals.UpdateCock(c**k, CockType.LIZARD); } //then, add c**k(s) until we have 2 lizard cocks while (target.cocks.Count < 2) { target.AddCock(CockType.LIZARD); } } else if (lizardCocks == 1) { int toChange = target.cocks[0].type == CockType.LIZARD ? 0 : 1; target.genitals.UpdateCock(toChange, CockType.LIZARD); } } //otherwise, we already have 2 lizard cocks (or more). we're only going to keep 2 lizard cocks, so we need to remove extra ones. else { //any non-lizard c**k gets removed first. C**k toRemove = target.cocks.FirstOrDefault(x => x.type != CockType.LIZARD); //if we can't find one, it means we only have lizard cocks. remove the last one. if (toRemove is null) { toRemove = target.cocks[target.cocks.Count - 1]; } } } //9c) II The tongue (sensitivity bonus, stored as a perk?) if (changeCount == remainingChanges && Utils.Rand(3) == 0) { TongueData oldData = target.tongue.AsReadOnlyData(); target.UpdateTongue(TongueType.SNAKE); sb.Append(UpdateTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //9c) III The fangs if (changeCount == remainingChanges && target.tongue.type == TongueType.SNAKE && target.face.type != FaceType.SNAKE && Utils.Rand(3) == 0) { FaceData oldData = target.face.AsReadOnlyData(); target.UpdateFace(FaceType.SNAKE); sb.Append(UpdateFaceText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //9c) I The tail ( http://tvtropes.org/pmwiki/pmwiki.php/Main/TransformationIsAFreeAction ) (Shouldn't we try to avert this? -Ace) //Should the enemy "kill" you during the transformation, it skips the scene and immediately goes to tthe rape scene. //(Now that I'm thinking about it, we should add some sort of appendix where the target realizes how much he's/she's changed. -Ace) //MOD NOTE: this also silently grants a reptilian body. there's also a check for this if you somehow lose the reptilian body and have a naga lower body later. if (changeCount == remainingChanges && target.face.type == FaceType.SNAKE && target.lowerBody.type != LowerBodyType.NAGA && Utils.Rand(4) == 0) { target.UpdateLowerBody(LowerBodyType.NAGA); // Naga lower body plus a tail may look awkward, so silently discard it (Stadler76) target.RestoreTail(); //convert body to match lower body. if (target.body.type != BodyType.REPTILE) { target.UpdateBody(BodyType.REPTILE, Tones.GREEN, Tones.LIGHT_GREEN); } if (--remainingChanges <= 0 || currentlyInCombat) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } if (target.body.type != BodyType.REPTILE && target.lowerBody.type == LowerBodyType.NAGA) { BodyData oldData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.REPTILE, Tones.GREEN, Tones.LIGHT_GREEN); sb.Append(UpdateBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } // Remove gills if (Utils.Rand(4) == 0 && !target.gills.isDefault) { GillData oldData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RestoredGillsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Default change - blah if (changeCount == remainingChanges && !statsChanged) { } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
//MOD: large eggs will now firmly make you male, just like pink eggs do (except female, of course) //This means they will grow balls and a c**k if you don't have them. //small eggs will now remove one v****a at a time, and are able to handle multiple vaginas. //similarly, small eggs will now remove one extra breast row at a time. //a large egg will remove each additional breast row, and will also make the last pair as manly as possible. they will remove fuckable nipples while doing so, //but they will become inverted if fuckable. protected override string OnConsumeAttempt(Creature consumer, out bool consumeItem, out bool isBadEnd) { isBadEnd = false; StringBuilder sb = new StringBuilder(); sb.Append("You devour the egg, momentarily sating your hunger."); //MOD: doing this is logical order - start with breasts and work our way down. //remove extra breast rows. //if only 1 extra row or it's a small egg, remove 1 extra row. if (consumer.breasts.Count > 1 && (consumer.breasts.Count == 2 || !isLarge)) { sb.Append("Your back relaxes as extra weight vanishes from your chest. <b>Your lowest " + consumer.breasts[consumer.breasts.Count - 1].LongDescription() + " have vanished.</b>"); consumer.RemoveBreastRow(); sb.Append(GlobalStrings.NewParagraph()); } //if large egg, remove all extra breast rows. else if (consumer.breasts.Count > 2 && isLarge) { sb.Append("Your back relaxes as a significant amount of weight vanishes from your chest. <b>You've lost all but your top-most row of breasts!</b>"); consumer.RemoveExtraBreastRows(); sb.Append(GlobalStrings.NewParagraph()); } //Kill pussies! //if small on only have 1, remove 1. if (consumer.hasVagina && (consumer.vaginas.Count == 1 || !isLarge)) { sb.Append(consumer.genitals.OneVaginaOrVaginasNoun(Conjugate.YOU).CapitalizeFirstLetter() + "clenches in pain, doubling you over. " + "You slip a hand down to check on it, only to feel the slit growing smaller and smaller until it disappears"); //if large, and we don't have a c**k, grow one out of old c**t. if (isLarge && !consumer.hasCock) { sb.Append(". Your c**t, however, seems to be resisting the change, and instead expands outward. The top flares outward into a distinct mushroom-shape," + "and you quickly realize " + SafelyFormattedString.FormattedText("Your v****a has been replaced with a c**k!", StringFormats.BOLD)); double length = consumer.clits[0].length + 5; double width = length / 5; //also grow a pair of balls if needed. consumer.AddCock(CockType.defaultValue, length, width); if (!consumer.hasBalls) { consumer.balls.GrowBalls(); sb.Append(SafelyFormattedString.FormattedText("A pair of balls drop in below it, completing the change in gender.", StringFormats.BOLD)); } } else { sb.Append(", taking your c**t with it!"); if (consumer.vaginas.Count == 1) { sb.Append(" <b>Your v****a is gone!</b>"); } } consumer.RemoveVagina(); sb.Append(GlobalStrings.NewParagraph()); } //if large, and more than one, remove them both. else if (consumer.hasVagina && isLarge) { sb.Append("You double over in pain, and quickly pinpoint the source - your female sexes. You slip a hand down to check on them, and realize " + "they are both shrinking inward, your feminine entrances shrinking smaller and smaller until both disappear completely"); if (!consumer.hasCock) { sb.Append(". Your clits, however, seem to resist the change, at least momentarily. Instead, they merge together, then expand outward, growing wider and longer." + "The top flares outward into a distinct mushroom-shape, and you quickly realize " + SafelyFormattedString.FormattedText("Your feminine sexes have been " + "replaced by a c**k!", StringFormats.BOLD)); double length = consumer.clits.Sum(x => x.length) + 5; double width = length / 5; consumer.AddCock(CockType.defaultValue, length, width); if (!consumer.hasBalls) { consumer.balls.GrowBalls(); sb.Append(SafelyFormattedString.FormattedText("A pair of balls drop in below it, completing the change in gender.", StringFormats.BOLD)); } } else { sb.Append(", taking their clits with them! " + SafelyFormattedString.FormattedText("Your vaginas are gone!", StringFormats.BOLD)); } consumer.RemoveAllVaginas(); sb.Append(GlobalStrings.NewParagraph()); } //Dickz //if you have any, grow them, regardless of size. the large egg grows more. if (consumer.hasCock) { sb.Append(GlobalStrings.NewParagraph() + "Your " + consumer.genitals.AllCocksLongDescription(out bool isPlural) + (isPlural ? " fill" : "fills") + " to full-size... and " + (isPlural ? "begin" : "begins") + " growing obscenely."); CockCollectionData cockCollection = consumer.genitals.allCocks.AsReadOnlyData(); double averageWidthDelta = 0; foreach (C**k c**k in consumer.cocks) { c**k.IncreaseLength(3 + Utils.Rand(isLarge ? 5 : 2)); averageWidthDelta += c**k.IncreaseThickness(1); } averageWidthDelta /= consumer.cocks.Count; sb.Append(consumer.genitals.allCocks.GenericChangeCockLengthText(cockCollection)); sb.Append(GlobalStrings.NewParagraph()); //Display the degree of thickness change. if (averageWidthDelta >= 1) { sb.Append("Your " + consumer.genitals.AllCocksShortDescription() + (isPlural ? " spread" : " spreads") + " rapidly, swelling with over an inch of added girth, making " + (isPlural ? "them" : "it") + " feel fat and floppy."); } else if (averageWidthDelta > 0.5) { sb.Append("Your " + consumer.genitals.AllCocksShortDescription() + (isPlural ? " seem" : " seems") + " to swell up, feeling heavier. " + "You look down and watch " + (isPlural ? "them growing fatter as they thicken." : "it growing fatter as it thickens.")); } else { sb.Append("Your " + consumer.genitals.AllCocksShortDescription() + (isPlural ? " feel" : " feels") + " swollen and heavy. " + "With a firm, but gentle, squeeze, you confirm your suspicions - " + (isPlural ? "they are" : "it is") + " definitely thicker."); } if (!consumer.balls.hasBalls && isLarge) { sb.Append("A pair of balls grow in, complementing your enlarged " + (isPlural ? "cocks" : "c**k") + " nicely, completing your manly sex."); consumer.balls.GrowBalls(); } consumer.DeltaCreatureStats(lib: 1, sens: 1, lus: 20); } //if you don't, and it's a large egg, get one. it's just gonna be default size. else if (isLarge) { consumer.AddCock(); sb.Append("A sudden pressure forms in your groin, mixed with an inescapable sense of enquenched sexual need, like an itch you can't reach. " + "It threatens to overwhelm you until suddenly a length of flesh bursts from your loins. You immediately realize " + SafelyFormattedString.FormattedText("you now have a c**k!", StringFormats.BOLD)); consumer.AddCock(); if (!consumer.hasBalls) { sb.Append(SafelyFormattedString.FormattedText("A pair of balls drop below your newly formed c**k, completing the look.", StringFormats.BOLD)); consumer.balls.GrowBalls(); } } //butt and hips. if (isLarge) { //Ass/hips shrinkage! if (consumer.butt.size > consumer.butt.smallestPossibleButtSize) { sb.Append("Muscles firm and tone as you feel your " + consumer.build.ButtLongDescription() + " becomes smaller and tighter."); if (consumer.hips.size > 5) { sb.Append(" "); } consumer.butt.ShrinkButt(2); } if (consumer.hips.size > consumer.hips.smallestPossibleHipSize) { sb.Append("Feeling the sudden burning of lactic acid in your " + consumer.build.HipsLongDescription() + ", you realize they have slimmed down and firmed up some."); consumer.hips.ShrinkHips(2); } //Shrink t**s! if (consumer.breasts[0].cupSize > consumer.genitals.smallestPossibleMaleCupSize) { consumer.breasts[0].MakeMale(); } } //femininity if possible. if (Utils.Rand(3) == 0) { byte target, delta; if (isLarge) { target = 0; delta = 8; } else { target = 5; delta = 3; } sb.Append(consumer.ModifyFemininity(target, delta)); } consumeItem = true; return(sb.ToString()); }
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)); }