using RimWorld; using rjw; using System; using System.Collections.Generic; using System.Linq; using Verse; namespace RJW_Menstruation { public class CompProperties_Pheromones : HediffCompProperties { public float daysToMaxSeverity; public float range; public CompProperties_Pheromones() { compClass = typeof(HediffComp_Pheromones); } } public class HediffComp_Pheromones : HediffComp { public CompProperties_Pheromones Props => (CompProperties_Pheromones)props; public const int emitInterval = GenTicks.TickRareInterval; public const float emitRatio = (float)emitInterval / GenDate.TicksPerDay; public override void CompPostTick(ref float severityAdjustment) { try { if (!Configurations.EnablePheromones) return; if (!Pawn.RaceProps.Humanlike && (!Configurations.EnableAnimalCycle || Configurations.AnimalPheromoneEffect <= 0.0f)) return; if (!Pawn.IsHashIntervalTick(emitInterval)) return; if (!Pawn.SpawnedOrAnyParentSpawned) return; foreach (Pawn pawn in AffectedPawns()) ApplyEffectToPawn(pawn); } catch (Exception ex) { Log.Error($"Error when trying to emit pheromones from pawn {Pawn}: {ex}"); } } protected IEnumerable AffectedPawns() { Map mapHeld = Pawn.MapHeld; if (mapHeld == null) yield break; foreach (Pawn pawn in mapHeld.mapPawns.AllPawnsSpawned) { if (pawn == Pawn) continue; if (!pawn.RaceProps.Humanlike) continue; if (Pawn.PositionHeld.InHorDistOf(pawn.PositionHeld, Props.range) && Pawn.GetRoom() == pawn.GetRoom()) yield return pawn; } } protected float GetEffectOnPawn(Pawn target) { if (target.Suspended || target.Dead) return 0.0f; if (target.needs?.TryGetNeed(VariousDefOf.SexNeed) == null) return 0.0f; if (!xxx.can_do_loving(target)) return 0.0f; if (target.story?.traits.HasTrait(TraitDefOf.Asexual) ?? true) return 0.0f; float penisEffect; if (Genital_Helper.has_penis_fertile(target)) penisEffect = 1.0f; else if (target.gender == Gender.Male) penisEffect = 0.25f; else return 0.0f; BodyDef body = target.RaceProps.body; if (!PawnCapacityUtility.BodyCanEverDoCapacity(body, PawnCapacityDefOf.Breathing)) return 0.0f; HediffSet hediffs = target.health.hediffSet; float breathingEffect = PawnCapacityUtility.CalculateCapacityLevel(hediffs, PawnCapacityDefOf.Breathing); float noseEffect = 1.0f; List noses = body.GetPartsWithDef(BodyPartDefOf.Nose); if (noses.Any()) // if the body doesn't have a nose but still breathes, then let it be affected noseEffect = noses.Average(nose => PawnCapacityUtility.CalculatePartEfficiency(hediffs, nose)); return penisEffect * breathingEffect * noseEffect; } protected void ApplyEffectToPawn(Pawn target) { float intensity = GetEffectOnPawn(target); if (intensity <= 0.0f) return; Hediff pheromones = target.health.hediffSet.GetFirstHediffOfDef(VariousDefOf.Hediff_AffectedByPheromones); float decay = VariousDefOf.Hediff_AffectedByPheromones.CompProps().severityPerDay; float raiseSeverityPerDay = intensity / Props.daysToMaxSeverity - decay; // Desired increase plus enough to overcome pheromone decay if (!Pawn.RaceProps.Humanlike) raiseSeverityPerDay *= Configurations.AnimalPheromoneEffect; float amountToApply = emitRatio * raiseSeverityPerDay; if (pheromones != null) pheromones.Severity += amountToApply; else { pheromones = HediffMaker.MakeHediff(VariousDefOf.Hediff_AffectedByPheromones, target); pheromones.Severity = amountToApply; target.health.AddHediff(pheromones); } return; } } }