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; //for some unknown reason, demon tfs roll out their own chance system completely unique to them. ok. StringBuilder sb = new StringBuilder(); //For all of these, any text regarding the transformation should be instead abstracted out as an abstract string function. append the result of this abstract function //to the string builder declared above (aka sb.Append(FunctionCall(variables));) string builder is just a fancy way of telling the compiler that you'll be creating a //long string, piece by piece, so don't do any crazy optimizations first. //the initial text for starting the transformation. feel free to add additional variables to this if needed. sb.Append(InitialTransformationText(target)); //Add any free changes here - these can occur even if the change count is 0. these include things such as change in stats (intelligence, etc) //change in height, hips, and/or butt, or other similar stats. int rando = Utils.Rand(100); int delta = target.GetExtraData()?.deltaTransforms ?? 0; if (delta != 0) { rando += 5 * delta + Utils.Rand(5 * delta); } //First, check if this tf has the male flag set (for male or herm tfs). If it does, add or grow cocks. if (desiredGender.HasFlag(Gender.MALE)) { byte addedCocks = 0; //if our initial roll was a crit, roll again. if we crit again, we may add several cocks, if possible. if (rando >= 85 && target.cocks.Count < Genitals.MAX_COCKS && Utils.Rand(10) < target.corruptionTrue / 25) { addedCocks = GrowCockGeneric(target, (byte)(Utils.Rand(2) + 2)); target.DeltaCreatureStats(lib: 3 * addedCocks, sens: 5 * addedCocks, lus: 10 * addedCocks); if (!isPurified) { target.IncreaseCorruption(8); } } //otherwise, only add a c**k if we have none or we originally rolled a crit (but failed to crit again) else if (target.cocks.Count == 0 || (rando >= 85 && target.cocks.Count < Genitals.MAX_COCKS)) { addedCocks = GrowCockGeneric(target, 1); target.DeltaCreatureStats(lib: 3, sens: 5, lus: 10); if (!isPurified) { target.IncreaseCorruption(5); } } //if that fails, it means we had a c**k already, or can't grow any more of them. else { C**k shortest = target.genitals.ShortestCock(); double lengthDelta; if (rando >= 45) { lengthDelta = shortest.IncreaseLength(Utils.Rand(3) + 3); shortest.IncreaseThickness(1); } else if (Utils.Rand(4) == 0) { lengthDelta = shortest.IncreaseLength(3); } else { lengthDelta = shortest.IncreaseLength(1); } target.DeltaCreatureStats(lib: 2, sens: 1, lus: 5 + lengthDelta * 3); if (!isPurified) { target.IncreaseCorruption(); } //no idea why this occurs, but ok. target.IncreaseIntelligence(1); } } //Otherwise, we're targeting female demon tfs only. this means we need to shrink (and possibly remove) the largest c**k the target has, unless hyper happy is on. else if (!hyperHappy && target.hasCock) { C**k largest = target.genitals.LongestCock(); //this loops through all the cocks and finds the longest. obviously, if the count is 1, this is simply the first element. //we'll need this if it gets removed. if not, this can still be used, this time to determine how much we shrunk. CockData oldData = largest.AsReadOnlyData(); //try decreasing it by 1-3. if this causes it to be removed instead, that's fine, but we need to know. //Note that this remove is now IN PLACE, not LAST. so if we remove the 3rd one, the old 4th is now 3rd, and so on. bool removed = target.genitals.ShrinkCockAndRemoveIfTooSmall(largest, Utils.Rand(3) + 1); } //Then, check if the tf has the female flag set (for female or herm tfs). if it does, add a v****a (if needed), and increase breast size. if (desiredGender.HasFlag(Gender.FEMALE)) { //don't currently have a v****a and herm tf or we're genderless, or it's a crit, or we rerolled a crit. if (!target.hasVagina && (desiredGender == Gender.HERM || target.gender == Gender.GENDERLESS || rando > 65 || Utils.Rand(3) == 0)) { target.genitals.AddVagina(VaginaType.HUMAN); } //do have one, and rolled a high crit. else if (target.hasVagina && rando >= 85) { foreach (V****a vag in target.vaginas) { //grow each c**t anywhere from 0.25in to 1in, if they are below the largest normal size. if (vag.c**t.length < vag.c**t.largestNormalSize) { vag.GrowClit((Utils.Rand(4) + 1) * 0.25); //cap it at the largest normal size. if (vag.c**t.length > vag.c**t.largestNormalSize) { vag.SetClitSize(vag.c**t.largestNormalSize); } } } } //do have one, rolled a crit, but not a high crit. else if (target.hasVagina && rando > 65) { target.HaveGenericVaginalOrgasm(0, true, true); target.vaginas.ForEach(x => x.IncreaseWetness()); } //now, breasts. these are the fallback, of sorts, so they grow larger when we don't crit. they also grow larger when we crit if we're targeting herms. if (rando < 85 || desiredGender == Gender.HERM) { //only occurs via herm. if (rando >= 85) { target.breasts.ForEach(x => x.GrowBreasts(3)); } else { byte temp = (byte)(1 + Utils.Rand(3)); CupSize largestSize = target.genitals.BiggestCupSize(); if (largestSize < CupSize.B && Utils.Rand(3) == 0) { temp++; } if (largestSize < CupSize.DD && Utils.Rand(4) == 0) { temp++; } if (largestSize < CupSize.DD_BIG && Utils.Rand(5) == 0) { temp++; } target.breasts.ForEach(x => x.GrowBreasts(temp)); } } } //if not, we're targeting male demon tfs only. this means we may need to shrink any overlarge breasts. Additionally, higher rng rolls may now remove vaginas. else if (!hyperHappy) { //if high crit: decrease bonus capacity, and remove a v****a, flat-out. if (rando >= 85 && target.hasVagina) { target.genitals.DecreaseBonusVaginalCapacity(5); target.genitals.RemoveVagina(); } //otherwise, if somewhat high, decrease bonus vaginal capacity (all vaginas), and wetness (last v****a). if this causes the bonus capacity to drop to 0 //and would cause it to go negative if we allowed that, remove the last v****a. else if (rando >= 65) { V****a lastVagina = target.vaginas[target.vaginas.Count - 1]; lastVagina.DecreaseWetness(1); //this is being super pedantic, but i'd prefer it lower the stat, then remove the v****a. hence this bool here. bool remove = target.genitals.standardBonusVaginalCapacity < 5; //decrease first. target.genitals.DecreaseBonusVaginalCapacity(5); //then remove it. if (remove) { target.genitals.RemoveVagina(); } } // if (target.genitals.BiggestCupSize() > target.genitals.smallestPossibleCupSize && (rando >= 85 || (rando > 65 && Utils.RandBool()) || (rando <= 65 && Utils.Rand(4) == 0))) { foreach (Breasts breast in target.breasts) { if (breast.cupSize > target.genitals.smallestPossibleCupSize) { byte amount = 1; if (rando >= 85) { amount++; } breast.ShrinkBreasts(amount); } } } } //never called in vanilla. if i read it correctly, it just initializes to 0 with a max of 1. int changeCount = base.GenerateChangeCount(target, new int[] { 3 }, 0, 0); if (changeCount == 0) { return(ApplyChangesAndReturn(target, sb, 0)); } int remainingChanges = changeCount; //Neck restore if (!target.neck.isDefault && Utils.Rand(4) == 0) { NeckData oldData = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Rear body restore if (!target.back.isDefault && Utils.Rand(5) == 0) { BackData oldData = target.back.AsReadOnlyData(); target.RestoreBack(); sb.Append(RestoredBackText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Ovi perk loss if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { target.womb.ClearOviposition(); sb.Append(ClearOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Demonic changes - higher chance with higher corruption. if (Utils.Rand(40) + target.corruption / 3 > 35 && !isPurified) { //Change tail if already horned. if (target.tail.type != TailType.DEMONIC && !target.horns.isDefault) { target.IncreaseCorruption(4); TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.DEMONIC); sb.Append(UpdateTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //grow horns! if (target.horns.numHorns == 0 || (Utils.Rand(target.horns.numHorns + 3) == 0)) { if (target.horns.numHorns < 12 && (target.horns.type == HornType.NONE || target.horns.type == HornType.DEMON)) { if (target.horns.type == HornType.NONE) { target.UpdateHorns(HornType.DEMON); } target.IncreaseCorruption(3); } //Text for shifting horns else if (target.horns.type != HornType.DEMON) { target.UpdateHorns(HornType.DEMON); target.IncreaseCorruption(3); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Nipples Turn Back: if (target.genitals.hasBlackNipples && Utils.Rand(3) == 0) { target.genitals.SetBlackNipples(false); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //remove fur if (target.face.type != FaceType.HUMAN || (target.body.type != BodyType.HUMANOID && Utils.Rand(3) == 0)) { //Remove face before fur! if (target.face.type != FaceType.HUMAN) { target.RestoreFace(); } //De-fur else if (target.body.type != BodyType.HUMANOID) { target.RestoreBody(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Demon tongue if (target.tongue.type == TongueType.SNAKE && Utils.Rand(3) == 0) { TongueData oldData = target.tongue.AsReadOnlyData(); target.UpdateTongue(TongueType.DEMONIC); sb.Append(UpdateTongueText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //foot changes - requires furless if (target.body.type == BodyType.HUMANOID && Utils.Rand(4) == 0) { bool changed; //Males/genderless get clawed feet if (!target.gender.HasFlag(Gender.FEMALE) || (target.gender == Gender.HERM && target.genitals.AppearsMoreMaleThanFemale())) { changed = target.UpdateLowerBody(LowerBodyType.DEMONIC_CLAWS); } //Females/futa get high heels else { changed = target.UpdateLowerBody(LowerBodyType.DEMONIC_HIGH_HEELS); } if (changed && --remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } //Grow demon wings if ((target.wings.type != WingType.BAT_LIKE || !target.wings.isLarge || target.back.type == BackType.SHARK_FIN) && Utils.Rand(8) == 0 && target.IsCorruptEnough(50)) { //grow smalls to large if (target.wings.type == WingType.BAT_LIKE && target.IsCorruptEnough(75)) { target.wings.GrowLarge(); } else { target.UpdateWings(WingType.BAT_LIKE); } if (target.back.type == BackType.SHARK_FIN) { target.RestoreBack(); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); } } } //this is the fallthrough that occurs when a tf item goes through all the changes, but does not proc enough of them to exit early. it will apply however many changes //occurred, then return the contents of the stringbuilder. return(ApplyChangesAndReturn(target, sb, changeCount - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; int changeLimit = GenerateChangeCount(target, new int[] { 2, 2 }); int remainingChanges = changeLimit; //crit is our modifier for basically all stats. 1 is default, though any non-standard will proc the non-default //standard also has a 15% chance of proccing it too. int crit = 1; if (modifiers > CanineModifiers.STANDARD || Utils.Rand(20) < 3) { crit = Utils.Rand(20) / 10 + 2; } bool hasCrit() => crit > 1; StringBuilder sb = new StringBuilder(); bool DoKnotChanges(double delta) { if (target.hasCock) { bool changed = false; int dogCockCount = target.genitals.CountCocksOfType(CockType.DOG); if (dogCockCount > 0) { C**k smallestKnot = target.cocks.MinItem(x => x.type == CockType.DOG ? (double?)x.knotMultiplier : null); if (smallestKnot.knotMultiplier > 2) { delta /= 10; } else if (smallestKnot.knotMultiplier > 1.75) { delta /= 5; } else if (smallestKnot.knotMultiplier > 1.5) { delta /= 2; } double knotMultiplierDelta = smallestKnot.IncreaseKnotMultiplier(delta); if (knotMultiplierDelta != 0) { sb.Append(EnlargedSmallestKnotText(target, target.cocks.IndexOf(smallestKnot), knotMultiplierDelta, dogCockCount > 1)); changed = true; } } target.DeltaCreatureStats(sens: 0.5, lus: 5 * crit); return(changed); } return(false); } sb.Append(InitialTransformationText(modifiers, hasCrit())); //bad end related checks if (hasCrit() && target.body.hasActiveFurData && target.face.type == FaceType.DOG && target.ears.type == EarType.DOG && target.lowerBody.type == LowerBodyType.DOG && target.tail.type == TailType.DOG && target is IExtendedCreature extended) { //can get bad end. if (extended.extendedData.hasDoggoWarning && !extended.extendedData.resistsTFBadEnds) { //bad end. if (Utils.RandBool()) { sb.Append(BadEndText(target)); isBadEnd = true; return(ApplyChangesAndReturn(target, sb, 0)); } //get lucky, but warn that they got lucky else { sb.Append(DoggoWarningText(target, true)); } } //not warned else if (!extended.extendedData.hasDoggoWarning) { //warn extended.extendedData.hasDoggoWarning = true; sb.Append(DoggoWarningText(target, false)); } } //stat changes if (modifiers.HasFlag(CanineModifiers.BLACK)) { target.IncreaseCreatureStats(lus: (byte)(5 + Utils.Rand(5)), lib: (byte)(2 + Utils.Rand(4)), corr: (byte)(2 + Utils.Rand(4))); } //stat changes (cont.) double strengthIncrease = 0; double speedIncrease = 0; double intelligenceDecrease = 0; if (target.relativeStrength < 50 && Utils.Rand(3) == 0) { strengthIncrease = target.IncreaseStrength(crit); } if (target.relativeSpeed < 30 && Utils.Rand(3) == 0) { speedIncrease = target.IncreaseSpeed(crit); } if (target.relativeIntelligence > 30 && Utils.Rand(3) == 0) { intelligenceDecrease = target.DecreaseIntelligence(crit); } sb.Append(StatChangeText(target, strengthIncrease, speedIncrease, intelligenceDecrease)); //modifier effects (no cost) //double pepper if (modifiers.HasFlag(CanineModifiers.DOUBLE)) { int dogCocks = target.genitals.CountCocksOfType(CockType.DOG); //already has 2+ dog cocks. if (dogCocks >= 2) { //just treat it like a large. so we'll just bitwise or the //large flag in. modifiers |= CanineModifiers.LARGE; } //has no dog cocks. else if (dogCocks == 0) { //has no cocks. - grow 2 if (target.cocks.Count == 0) { target.AddCock(CockType.DOG, 7 + Utils.Rand(7), 1.5 + Utils.Rand(10) / 10, 1.7); target.AddCock(CockType.DOG, 7 + Utils.Rand(7), 1.5 + Utils.Rand(10) / 10, 1.7); sb.Append(GrewTwoDogCocksHadNone(target)); } //has one c**k. - grow one, change one else if (target.cocks.Count == 1) { CockData oldCockData = target.cocks[0].AsReadOnlyData(); target.genitals.UpdateCockWithKnot(0, CockType.DOG, 1.5); if (target.cocks[0].length < 10) { target.cocks[0].SetLength(10); } target.AddCock(CockType.DOG, 7 + Utils.Rand(7), 1.5 + Utils.Rand(10) / 10.0, 1.7); sb.Append(ConvertedFirstDogCockGrewSecond(target, oldCockData)); } //has 2+ cocks. -change 2 else { CockData firstOldData = target.cocks[0].AsReadOnlyData(); CockData secondOldData = target.cocks[1].AsReadOnlyData(); target.genitals.UpdateCockWithKnot(0, CockType.DOG, 1.5); if (target.cocks[0].length < 10) { target.cocks[0].SetLength(10); } target.genitals.UpdateCockWithKnot(1, CockType.DOG, 1.5); if (target.cocks[1].length < 10) { target.cocks[1].SetLength(10); } sb.Append(ConvertedTwoCocksToDog(target, firstOldData, secondOldData)); } } //one dog c**k. else { if (target.cocks.Count == 1) { target.AddCock(CockType.DOG, 7 + Utils.Rand(7), 1.5 + Utils.Rand(10) / 10.0, 1.7); sb.Append(GrewSecondDogCockHadOne(target)); } else { if (target.cocks[0].type == CockType.DOG) { CockData oldData = target.cocks[1].AsReadOnlyData(); target.genitals.UpdateCockWithKnot(1, CockType.DOG, 1.5); if (target.cocks[1].length < 10) { target.cocks[1].SetLength(10); } sb.Append(ConvertedOneCockToDogHadOne(target, 1, oldData)); } else { CockData oldData = target.cocks[0].AsReadOnlyData(); target.genitals.UpdateCockWithKnot(0, CockType.DOG, 1.5); if (target.cocks[0].length < 10) { target.cocks[0].SetLength(10); } sb.Append(ConvertedOneCockToDogHadOne(target, 0, oldData)); } } } target.IncreaseCreatureStats(lib: 2, lus: 50); } //there are two ways to proc this - either via a knotty modifier (written here), or via random chance and/or with a large pepper (written later) //however, a knotty pepper modifier has no tf cost, the other check does. also, this will modify a c**k if none have a dog knot, the other will not. if (modifiers.HasFlag(CanineModifiers.KNOTTY)) { double delta = ((Utils.Rand(2) + 5) / 20) * crit; if (!DoKnotChanges(delta)) { if (target.hasCock) { CockData oldCockData = target.cocks[0].AsReadOnlyData(); double knotSize = 1.75; if (target.cocks[0].hasKnot) { knotSize = Math.Max(2.1, target.cocks[0].knotMultiplier); } target.genitals.UpdateCockWithKnot(0, CockType.DOG, knotSize); if (target.cocks[0].length < 10) { target.cocks[0].SetLength(10); } sb.Append(ConvertedOneCockToDog(target, 0, oldCockData)); } else { sb.Append(WastedKnottyText(target)); } } } //bulby if (modifiers.HasFlag(CanineModifiers.BULBY)) { if (!target.hasBalls) { target.genitals.GrowBalls(2, 1); target.DeltaCreatureStats(lib: 2, lus: -10); sb.Append(GrewBallsText(target)); } else if (target.balls.uniBall) { BallsData oldData = target.balls.AsReadOnlyData(); target.genitals.ConvertToNormalBalls(); target.DeltaCreatureStats(lib: 1, lus: 1); sb.Append(EnlargedBallsText(target, oldData)); } else { BallsData oldData = target.balls.AsReadOnlyData(); byte enlargeAmount = target.genitals.balls.EnlargeBalls(1); target.IncreaseCreatureStats(lib: 1, lus: 3); sb.Append(EnlargedBallsText(target, oldData)); } } if (remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } //tfs (cost 1). //restore neck if (target.neck.type != NeckType.defaultValue && Utils.Rand(4) == 0) { NeckData oldNeck = target.neck.AsReadOnlyData(); target.RestoreNeck(); sb.Append(RestoredNeckText(target, oldNeck)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } //remove oviposition if (target.womb.canRemoveOviposition && Utils.Rand(5) == 0) { if (target.womb.ClearOviposition()) { sb.Append(RemovedOvipositionText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } } //remove feather-hair if (RemoveFeatheryHair(target)) { sb.Append(RemovedFeatheryHairText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } //knot multiplier (but not knotty) if (!modifiers.HasFlag(CanineModifiers.KNOTTY) && target.genitals.CountCocksOfType(CockType.DOG) > 0 && (modifiers.HasFlag(CanineModifiers.LARGE) || Utils.Rand(7) < 5)) { double delta = ((Utils.Rand(2) + 1) / 20.0) * crit; if (DoKnotChanges(delta)) { if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } } //transform a c**k. if (target.genitals.CountCocksOfType(CockType.DOG) < target.cocks.Count && (modifiers.HasFlag(CanineModifiers.LARGE) || Utils.Rand(8) < 5)) { C**k nonDoggo = target.cocks.FirstOrDefault(x => x.type != CockType.DOG && x.type != CockType.DEMON); //find any c**k that isn't a dog, but also isn't a demon c**k. if (nonDoggo != null) { CockData oldData = nonDoggo.AsReadOnlyData(); int index = target.cocks.IndexOf(nonDoggo); target.genitals.UpdateCock(index, CockType.DOG); sb.Append(ConvertedOneCockToDog(target, index, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } else { C**k demonSpecialCase = target.cocks.FirstOrDefault(x => x.type == CockType.DEMON); int index = demonSpecialCase.cockIndex; if (demonSpecialCase != null) { double delta = demonSpecialCase.IncreaseThickness(2); if (delta != 0) { sb.Append(CouldntConvertDemonCockThickenedInstead(target, index, delta)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } } } } //update cum. now, if it reaches the cap, it simply uses the new flat amount adder (up to a certain point). it does not use the messy o****m perk //anymore because i've tried to remove calls to random perks because it's not very future-proof - what happens when a new perk is added that has a //similar effect? do we add that check literally everywhere too? (of course, if a perk is unique enough that nothing else will act in the same way, //that's fine. i dunno, it's difficult to try and clean everything up without being able to predict the future, which is admittedly impossible) if (target.hasCock && (target.genitals.cumMultiplier < 1.5 || target.genitals.additionalCum < 500) && Utils.RandBool()) { double delta; bool wasMultiplier; if (target.genitals.cumMultiplier < 1.5) { delta = target.genitals.IncreaseCumMultiplier(0.05 * crit); wasMultiplier = true; } else { delta = target.genitals.AddFlatCumAmount(50); wasMultiplier = false; } sb.Append(AddedCumText(target, delta, wasMultiplier)); } if (target.hasCock && modifiers.HasFlag(CanineModifiers.LARGE)) { C**k smallest = target.genitals.ShortestCock(); CockData oldData = smallest.AsReadOnlyData(); smallest.IncreaseLength(Utils.Rand(4) + 3); if (smallest.girth < 1) { double delta = 1 - smallest.girth; smallest.IncreaseThickness(delta); } sb.Append(GrewSmallestCockText(target, smallest.cockIndex, oldData)); } //do female changes. //if we have a vag and > flat breasts. if (target.hasVagina && target.genitals.BiggestCupSize() > CupSize.FLAT) { byte breastCount = (byte)target.breasts.Count; if (breastCount < 3) { BreastCollectionData oldBreastData = target.genitals.allBreasts.AsReadOnlyData(); //in vanilla code, we had some strange checks here that required the first row (and only the first row) be a certain size. //now, we check all the rows, but no longer require any of them to be a certain size. instead, if it's not the right size, we make it that size, //then add the row. so, for two rows, your first row must be at least a B-Cup. for 3: first must be a C cup, second an A cup. //if we supported 4 rows, it'd be D, B, A. for (int x = 0; x < target.breasts.Count; x++) { CupSize requiredSize = (CupSize)(byte)(target.breasts.Count - x); if (x == 0) { requiredSize++; } if (target.breasts[x].cupSize < requiredSize) { target.breasts[x].SetCupSize(requiredSize); } } target.genitals.AddBreastRow(target.breasts[breastCount - 1].cupSize.ByteEnumSubtract(1)); bool doCrit = false, uberCrit = false; if (target.breasts.Count == 2) { target.IncreaseCreatureStats(lus: 5, sens: 6); } else if (hasCrit()) { doCrit = true; if (crit > 2) { target.IncreaseCreatureStats(sens: 6, lus: 15); uberCrit = true; } else { target.IncreaseCreatureStats(sens: 3, lus: 10); } } sb.Append(UpdateAndGrowAdditionalRowText(target, oldBreastData, doCrit, uberCrit)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } else { BreastCollectionData oldBreastData = target.genitals.allBreasts.AsReadOnlyData(); //only call the normalize text if we actually did anything. related, anthro breasts now returns a bool. thanks, fox tfs. if (target.genitals.AnthropomorphizeBreasts()) { sb.Append(NormalizedBreastSizeText(target, oldBreastData)); } } } //Go into heat if (EnterHeat(target, out bool increased)) { sb.Append(EnterOrIncreaseHeatText(target, increased)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } //doggo fantasies if (target.DogScore() > 3 && Utils.Rand(4) == 0) { sb.Append(DoggoFantasyText(target)); target.IncreaseLust(5 + (target.libidoTrue / 20)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } //doggo tfs. if (!target.eyes.isDefault && Utils.Rand(5) == 0) { EyeData oldData = target.eyes.AsReadOnlyData(); target.RestoreEyes(); sb.Append(RestoredEyesText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } if (modifiers.HasFlag(CanineModifiers.BLACK) && !target.body.mainEpidermis.fur.IsIdenticalTo(HairFurColors.BLACK)) { //for now, we're ignoring underbody, apparently. if (target.body.type != BodyType.SIMPLE_FUR) //if (target.body.mainEpidermis.currentType != EpidermisType.FUR) { BodyData oldBodyData = target.body.AsReadOnlyData(); target.UpdateBody(BodyType.SIMPLE_FUR, new FurColor(HairFurColors.BLACK), FurTexture.THICK); sb.Append(ChangedBodyTypeText(target, oldBodyData)); } else { ReadOnlyFurColor oldFur = target.body.mainEpidermis.fur.AsReadOnly(); target.body.ChangeMainFur(new FurColor(HairFurColors.MIDNIGHT_BLACK), FurTexture.THICK); sb.Append(ChangedFurText(target, oldFur)); } if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } //again, we're ignoring underbody for now, idk. else if (target.lowerBody.type == LowerBodyType.DOG && target.tail.type == TailType.DOG && //target.body.mainEpidermis.currentType != EpidermisType.FUR target.body.type != BodyType.SIMPLE_FUR && Utils.Rand(4) == 0) { BodyData oldBodyData = target.body.AsReadOnlyData(); FurColor oldFur = target.body.mainEpidermis.fur; target.UpdateBody(BodyType.SIMPLE_FUR, Utils.RandomChoice(Species.DOG.availableColors)); sb.Append(ChangedBodyTypeText(target, oldBodyData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } if (target.lowerBody.type != LowerBodyType.DOG && target.tail.type == TailType.DOG && target.ears.type == EarType.DOG && Utils.Rand(3) == 0) { LowerBodyData oldData = target.lowerBody.AsReadOnlyData(); target.UpdateLowerBody(LowerBodyType.DOG); sb.Append(ChangedLowerBodyText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } if (target.ears.type != EarType.DOG && target.tail.type == TailType.DOG && Utils.RandBool()) { EarData oldData = target.ears.AsReadOnlyData(); target.UpdateEars(EarType.DOG); sb.Append(ChangedEarsText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } if (target.tail.type != TailType.DOG && Utils.Rand(3) == 0) { TailData oldData = target.tail.AsReadOnlyData(); target.UpdateTail(TailType.DOG); sb.Append(ChangedTailText(target, oldData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } if (target.arms.type != ArmType.DOG && target.body.isFurry && target.tail.type == TailType.DOG && target.lowerBody.type == LowerBodyType.DOG && Utils.Rand(4) == 0) { ArmData oldArmData = target.arms.AsReadOnlyData(); target.UpdateArms(ArmType.DOG); sb.Append(ChangedArmsText(target, oldArmData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } if (!target.gills.isDefault && Utils.Rand(4) == 0) { GillData oldGillData = target.gills.AsReadOnlyData(); target.RestoreGills(); sb.Append(RemovedGillsText(target, oldGillData)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } if (target.body.isFurry && Utils.Rand(3) == 0) { target.DeltaCreatureStats(tou: 4, sens: -3); sb.Append(FallbackToughenUpText(target)); if (--remainingChanges <= 0) { return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); } } if (target is CombatCreature cc2 && remainingChanges == changeLimit) { cc2.AddHP(20); sb.Append(NothingHappenedGainHpText(target)); target.IncreaseLust(3); } return(ApplyChangesAndReturn(target, sb, changeLimit - remainingChanges)); }
protected internal override string DoTransformation(Creature target, out bool isBadEnd) { isBadEnd = false; //by default, this is 2 rolls at 50%, so a 25% chance of 0 additional tfs, 50% chance of 1 additional tf, 25% chance of 2 additional tfs. //also takes into consideration any perks that increase or decrease tf effectiveness. if you need to roll out your own, feel free to do so. int changeCount = GenerateChangeCount(target, new int[] { 2, 2, 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, 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 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)); }