/// <summary>Tries the apply the mutation to the given pawn</summary> /// <param name="pawn">The pawn.</param> /// <param name="mutagenDef">The mutagen definition. used to determine if it's a valid target or not</param> /// <param name="outAddedHediffs">The out added hediffs.</param> /// <param name="cause">The cause.</param> /// <param name="addLogEntry">if set to <c>true</c> [add log entry].</param> /// <returns>if the mutation was added or not</returns> public bool TryApply(Pawn pawn, MutagenDef mutagenDef, List <Hediff> outAddedHediffs = null, Hediff cause = null, bool addLogEntry = true) { if (!mutagenDef.CanInfect(pawn)) { return(false); } if (!hediff.IsValidFor(pawn)) { return(false); } bool added = PawnmorphHediffGiverUtility.TryApply(pawn, hediff, partsToAffect, canAffectAnyLivePart, countToAffect, outAddedHediffs); if (addLogEntry && added && partsToAffect != null) { AddMutationLogFor(pawn); } if (added) { var cDef = cause?.def; if (cDef != null) { AspectUtils.TryApplyAspectsFrom(cDef, pawn); } } return(added); }
/// <summary>tries to apply the mutations to the given body part records</summary> /// <param name="pawn">The pawn.</param> /// <param name="recordsToAdd">The records to add.</param> /// <param name="mutagen">The mutagen.</param> /// <returns></returns> public bool TryApply(Pawn pawn, List <BodyPartRecord> recordsToAdd, MutagenDef mutagen = null) { mutagen = mutagen ?? MutagenDefOf.defaultMutagen; if (!mutagen.CanInfect(pawn)) { return(false); } if (!hediff.IsValidFor(pawn)) { return(false); } bool anyAdded = false; HashSet <BodyPartRecord> nonMissingRecords = new HashSet <BodyPartRecord>(pawn.health.hediffSet.GetNotMissingParts()); foreach (BodyPartRecord bodyPartRecord in recordsToAdd) { if (!nonMissingRecords.Contains(bodyPartRecord)) { continue; } anyAdded |= TryApply(pawn, bodyPartRecord, mutagen, nonMissingRecords); } return(anyAdded); }
/// <summary> The function that does the heavy lifting for a HediffGiver. </summary> /// <param name="pawn"> The pawn the parent hediff is applied to. </param> /// <param name="cause"> The parent hediff where this HediffGiver is located in. </param> public override void OnIntervalPassed(Pawn pawn, [NotNull] Hediff cause) { // Push a multiplay-safe randomization seed. RandUtilities.PushState(); var singleComp = cause.TryGetComp <HediffComp_Single>(); float mult = singleComp?.stacks ?? 1; //the more stacks of partial morphs the pawn has the faster the mutation rate should be mult *= pawn.GetStatValue(PMStatDefOf.MutagenSensitivity); mult *= singleComp?.Props?.mutationRateMultiplier ?? 1; mult = Mathf.Max(0.001f, mult); //prevent division by zero // After roughly this duration, try to apply the hediff if the pawn is of human-like intelligence. if (Rand.MTBEventOccurs(mtbDays / mult, mtbUnits, 30f) && pawn.RaceProps.intelligence == Intelligence.Humanlike) { //mutagen is what contains information like infect-ability of a pawn and post mutation effects MutagenDef mutagen = cause.def?.GetMutagenDef() ?? MutagenDefOf.defaultMutagen; // Check if this HediffGiver has the HediffComp_Single property (basically a dummy property that only comes into play in this function). var comp = singleComp; // If we haven't already tried to apply this giver's hediff and the pawn either passes a percentile roll or are of the right gender, try and apply the hediff. if (!_triggered.TryGetValue(cause) && (gender == pawn.gender || Rand.RangeInclusive(0, 100) <= chance) && TryApply(pawn, mutagen, null, cause)) { _triggered[cause] = true; DoMutationAddedEffects(pawn); // If the parent has the single comp, decrement it's current count and remove it if it's out of charges. if (comp != null) { comp.stacks--; if (comp.stacks <= 0) { pawn.health.RemoveHediff(cause); } } } else { _triggered[cause] = true; // If the giver had the single comp, clear the list so it can try and apply the hediff again. // While this does prevent the partial hediffs from getting "stuck", it does mean that they // can apply a hediff that already exists or failed because of the %chance rule. Need to find // a better solution to this problem eventually. if (comp != null) { ClearHediff(cause); } } } // Pop the seed to restore balance to the universe (and the game). RandUtilities.PopState(); }
/// <summary> /// Gets the net mutagenic buildup multiplier for this pawn. /// </summary> /// <param name="pawn">The pawn.</param> /// <param name="mutagenDef">The mutagen definition.</param> /// <returns></returns> public static float GetMutagenicBuildupMultiplier([NotNull] this Pawn pawn, MutagenDef mutagenDef = null) { mutagenDef = mutagenDef ?? MutagenDefOf.defaultMutagen; if (!mutagenDef.CanInfect(pawn)) { return(0); } return(pawn.GetStatValue(StatDefOf.ToxicSensitivity) * pawn.GetStatValue(PMStatDefOf.MutagenSensitivity)); }
/// <summary>Tries to apply the mutation to the given body part record</summary> /// <param name="pawn">The pawn.</param> /// <param name="recordToAdd">The record to add.</param> /// <param name="mutagen">The mutagen.</param> /// <returns></returns> public bool TryApply(Pawn pawn, BodyPartRecord recordToAdd, MutagenDef mutagen = null) { mutagen = mutagen ?? MutagenDefOf.defaultMutagen; if (!mutagen.CanInfect(pawn)) { return(false); } HashSet <BodyPartRecord> nonMissingRecords = new HashSet <BodyPartRecord>(pawn.health.hediffSet.GetNotMissingParts()); return(TryApply(pawn, recordToAdd, mutagen, nonMissingRecords)); }
private static void MutatePawn(Pawn pawn, MutagenDef mutagen) { HediffSet hediffSet = pawn.health.hediffSet; if (!pawn.Spawned || !mutagen.CanInfect(pawn)) { return; } pawn.health.AddHediff(MorphTransformationDefOf.FullRandomTF); IntermittentMagicSprayer.ThrowMagicPuffDown(pawn.Position.ToVector3(), pawn.Map); }
/// <summary> /// Tries the apply aspects that might be given from this mutagen /// </summary> /// <param name="mutagen">The mutagen.</param> /// <param name="pawn">The pawn.</param> /// <exception cref="ArgumentNullException"> /// mutagen /// or /// pawn /// </exception> public static void TryApplyAspects([NotNull] this MutagenDef mutagen, [NotNull] Pawn pawn) { if (mutagen == null) { throw new ArgumentNullException(nameof(mutagen)); } if (pawn == null) { throw new ArgumentNullException(nameof(pawn)); } foreach (AspectGiver aspectGiver in mutagen.aspectGivers.MakeSafe()) { aspectGiver.TryGiveAspects(pawn); } }
private static bool Prefix_AddHumanlikeOrders(Vector3 clickPos, Pawn pawn, List <FloatMenuOption> opts) { if (pawn.health.capacities.CapableOf(PawnCapacityDefOf.Manipulation)) { foreach (LocalTargetInfo localTargetInfo3 in GenUI.TargetsAt(clickPos, TargetingParameters.ForRescue(pawn), true)) { LocalTargetInfo localTargetInfo4 = localTargetInfo3; var victim = (Pawn)localTargetInfo4.Thing; MutagenDef mutagen = MutagenDefOf.MergeMutagen; if (mutagen.CanTransform(victim) && pawn.CanReserveAndReach(victim, PathEndMode.OnCell, Danger.Deadly, 1, -1, null, true) && Building_MutagenChamber.FindCryptosleepCasketFor(victim, pawn, true) != null) { string text4 = "CarryToChamber".Translate(localTargetInfo4.Thing.LabelCap, localTargetInfo4.Thing); JobDef jDef = Mutagen_JobDefOf.CarryToMutagenChamber; Action action3 = delegate { Building_MutagenChamber building_chamber = Building_MutagenChamber.FindCryptosleepCasketFor(victim, pawn); if (building_chamber == null) { building_chamber = Building_MutagenChamber.FindCryptosleepCasketFor(victim, pawn, true); } if (building_chamber == null) { Messages.Message("CannotCarryToChamber".Translate() + ": " + "NoChamber".Translate(), victim, MessageTypeDefOf.RejectInput, false); return; } var job = new Job(jDef, victim, building_chamber); job.count = 1; pawn.jobs.TryTakeOrderedJob(job); }; string label = text4; Action action2 = action3; Pawn revalidateClickTarget = victim; opts.Add(FloatMenuUtility .DecoratePrioritizedTask(new FloatMenuOption(label, action2, MenuOptionPriority.Default, null, revalidateClickTarget), pawn, victim)); } } } return(true); }
/// <summary> Determines whether this instance can infect the specified pawn. </summary> /// <param name="mutationDef"> The mutation definition. </param> /// <param name="pawn"> The pawn. </param> /// <returns> <c>true</c> if this instance can infect the specified pawn; otherwise, <c>false</c>. </returns> /// <exception cref="System.ArgumentNullException"> mutationDef or pawn is null. </exception> public static bool CanInfect([NotNull] this HediffDef mutationDef, [NotNull] Pawn pawn) { if (mutationDef == null) { throw new ArgumentNullException(nameof(mutationDef)); } if (pawn == null) { throw new ArgumentNullException(nameof(pawn)); } MutagenDef mutagenSource = mutationDef.GetMutagenDef(); return(mutagenSource.CanInfect(pawn)); }
bool TryApply(Pawn pawn, BodyPartRecord recordToAdd, [NotNull] MutagenDef mutagen, HashSet <BodyPartRecord> nonMissingRecords) { if (!mutagen.CanInfect(pawn)) { return(false); } if (!nonMissingRecords.Contains(recordToAdd)) { return(false); } if (!hediff.IsValidFor(pawn)) { return(false); } var hediffInst = HediffMaker.MakeHediff(hediff, pawn, recordToAdd); pawn.health.AddHediff(hediffInst, recordToAdd); DoMutationAddedEffects(pawn); AddMutationLogFor(pawn); return(true); }
/// <summary> /// applies damage to all apparel the pawn is wearing based on /// </summary> /// <param name="pawn">The pawn.</param> /// <param name="newRace">The new race.</param> /// <param name="mutagen">the mutagen that caused the transformation, if null uses default values for <see cref="MutagenDamageProperties"/></param> /// <exception cref="System.ArgumentNullException"> /// pawn /// or /// newRace /// </exception> public static void ApplyTfDamageToApparel([NotNull] Pawn pawn, [NotNull] ThingDef newRace, [CanBeNull] MutagenDef mutagen) { if (pawn == null) { throw new ArgumentNullException(nameof(pawn)); } if (newRace == null) { throw new ArgumentNullException(nameof(newRace)); } Pawn_ApparelTracker apparelTracker = pawn.apparel; List <Apparel> cachedApparel = apparelTracker?.WornApparel?.ToList(); //make a copy of all worn apparel if (cachedApparel == null || cachedApparel.Count == 0) { return; } MutagenDamageProperties damageProps = mutagen?.damageProperties ?? DefaultDamageValues; float oldSize = pawn.RaceProps.baseBodySize; float newSize = newRace.race.baseBodySize; float percentDiff; if (oldSize < EPSILON && newSize < EPSILON) //prevent division by zero { percentDiff = 0; //if they're both really small say no change } else if (oldSize < EPSILON) { percentDiff = MAX_APPAREL_PDIFF; //if just old size is small then completely destroy the apparel } else { percentDiff = (newSize - oldSize) / oldSize; //signed percent difference between percentDiff += APPAREL_PDIFF_OFFSET; //add a little offset so if the body size is the same or slightly smaller we still apply some damage //trying to account for differences in 'body shape' } float percentDamage = Mathf.Clamp(percentDiff, 0, MAX_APPAREL_PDIFF); //clamp pDiff between [0, Max], if they shrink don't damage apparel percentDamage /= MAX_APPAREL_PDIFF; //normalize the percentDifference to get a percentage to damage apparel by int totalStuffProduced = 0; foreach (Apparel apparel in cachedApparel) { int damage = Mathf.CeilToInt(apparel.MaxHitPoints * percentDamage * damageProps.apparelDamageMultiplier) + damageProps.apparelDamageOffset; int newHitPoints = Mathf.Max(apparel.HitPoints - damage, 0); var damageDone = apparel.HitPoints - newHitPoints; //save the actual damage done apparel.HitPoints = newHitPoints; if (apparel.HitPoints == 0) { apparelTracker.Remove(apparel); apparelTracker.Notify_ApparelRemoved(apparel); apparel.Destroy(); } var stuffProduced = Mathf.FloorToInt(damageDone * damageProps.spawnedBiproductMult); totalStuffProduced += Mathf.Min(stuffProduced, MAX_APPAREL_DAMAGE_PRODUCT_PER_APPAREL); } if (damageProps.biproduct != null && damageProps.spawnedBiproductMult > EPSILON) { Thing thing = ThingMaker.MakeThing(damageProps.biproduct); thing.stackCount = totalStuffProduced; if (pawn.Spawned) { GenPlace.TryPlaceThing(thing, pawn.PositionHeld, pawn.MapHeld, ThingPlaceMode.Near); } else { Caravan caravan = pawn.GetCaravan(); caravan?.AddPawnOrItem(thing, false); } } }
/// <summary> Add mutations to the given part. </summary> private void AddMutationToPart(BodyPartRecord record, [NotNull] Pawn pawn, AnimalClassBase aClass = null, bool recursive = false, MutagenDef mutagen = null) { MutationDef mutation; if (aClass != null) { mutation = aClass?.GetAllMorphsInClass() .SelectMany(m => m.GetMutationForPart(record.def)) .RandomElementWithFallback(); } else { mutation = MutationUtilities.GetMutationsByPart(record.def).RandomElementWithFallback(); } if (mutation != null) { MutationUtilities.AddMutation(pawn, mutation, record); } mutagen?.TryApplyAspects(pawn); if (recursive) // Recursively add mutations to child parts. { foreach (BodyPartRecord cPart in record.GetDirectChildParts()) { AddMutationToPart(cPart, pawn, aClass, true, mutagen); } } }