/// <summary> /// GC().The best way to shrink Rimworld saves,I think. /// </summary> /// <param name="verbose">Determine if GC() should log details very very verbosely</param> /// <returns>Count of disposed World pawns</returns> public int GC(bool verbose = false) { /* * TODO Log * 1.talelog by interest -X * 2.animal - deconstruct relation -Done * 3.deeperclean?remove hediffs -X * 5.correct verbose log -Done * 6.yield return "status" -X * 7.UI compability -Done * 8.Filth cleaner -Done * 9.Fix:Faction Leader -Done * 10.Fix:Faction Relations -Done * 12.Warp->Wrap -Done */ /* * TODO A18 * 4.adjustable GC depth -X * 11.GC boostup -Done * 13.remake GC System -Done * 13.Keyed in Float menu items -Done * 14.help contents -Done * 15.Optimize Cleanser frame -Done * 16.Optimize Floatmenu System -Done * 17.Debug only options -Done */ /* * TODO 1.0 * 18.Clean snow -Done * 19.whole-map clean -Done * 20.remake log -Done * 21.Mod framework -Done * 22.settings -Done * 23.MuteGC -Done * 24.MuteCL -Done * 25.remake FloatMenuUtil -Done * 26.timer of gc -X * 27.toolbox integration -Done * 28.MainButtonDef into xml -Done * 29.try catch -Done * 30.Find.CurrentMap==null check -Done * 31.MainButtonWorker -Done * 32.Messages.Message(str,historical) settings -Done * 33.AvoidGrid rework -Done * 34.Faction rework & cleanup -Done * 35.Close letter stack -Done */ if (Current.ProgramState != ProgramState.Playing) { Verse.Log.Error("You must be kidding me...GC a save without loading one?"); return(0); } /*Initialization*/ Verse.Log.Message("[GC Log] Pre-Initializing GC..."); this.reference = Find.WorldPawns.AllPawnsAliveOrDead.ToList(); this.allFlags.Clear(); this.verbose = verbose; if (verbose) { allFlagsCounter.Clear(); allFlagsCounter.Add(Flags.None, 0); for (int j = 0; j < FlagsCountNotNull; j++) { allFlagsCounter.Add((Flags)(1 << j), 0); } } /*Generate EntryPoints from Map Pawns*/ Verse.Log.Message("[GC Log] Generating EntryPoints from Map Pawns..."); List <Pawn> mapPawnEntryPoints; DiagnoseMapPawns(out mapPawnEntryPoints); if (verbose) { Verse.Log.Message("[GC Log][Verbose] " + allPawnsCounter.Count().ToString() + " Map Pawns marked during diagnosis"); } /*Reset counters*/ allPawnsCounter.Clear(); if (verbose) { allFlagsCounter.Clear(); allFlagsCounter.Add(Flags.None, 0); for (int j = 0; j < FlagsCountNotNull; j++) { allFlagsCounter.Add((Flags)(1 << j), 0); } } /*Generate a list of pawns concerned by used Tales*/ Verse.Log.Message("[GC Log] Collecting Pawns concerned by Used Tales..."); List <Pawn> allUsedTalePawns; CleanserUtil.InitUsedTalePawns(out allUsedTalePawns); /*Diagnosis:marking entries on WorldPawns.*/ Verse.Log.Message("[GC Log] Running diagnosis on WorldPawns..."); foreach (Pawn p in reference) { if (p.IsColonist) { addFlag(p, Flags.Colonist | Flags.RelationLvl2); } if (p.IsPrisonerOfColony) { addFlag(p, Flags.Prisoner | Flags.RelationLvl2); } if (PawnUtility.IsFactionLeader(p)) { addFlag(p, Flags.KeptWorldPawn | Flags.FactionLeader | Flags.RelationLvl1); } if (PawnUtility.IsKidnappedPawn(p)) { addFlag(p, Flags.KeptWorldPawn | Flags.RelationLvl2); } if (p.Corpse != null) { addFlag(p, Flags.CorpseOwner | Flags.RelationLvl1); } if (allUsedTalePawns.Contains(p)) { addFlag(p, Flags.TaleEntryOwner | Flags.RelationLvl0); } if (p.InContainerEnclosed) { addFlag(p, Flags.KeptWorldPawn | Flags.RelationLvl0); } if (p.Spawned) { addFlag(p, Flags.RelationLvl0); } if (p.IsPlayerControlledCaravanMember()) { addFlag(p, Flags.KeptWorldPawn | Flags.RelationLvl2); } if (PawnUtility.IsTravelingInTransportPodWorldObject(p)) { addFlag(p, Flags.KeptWorldPawn | Flags.RelationLvl2); } //Patch:A18 new entry if (PawnUtility.ForSaleBySettlement(p)) { addFlag(p, Flags.OnSale | Flags.RelationLvl0); } if (verbose) { Verse.Log.Message("[worldPawn] " + p.LabelShort + " [flag] " + markedFlagsString(p)); } } if (verbose) { Verse.Log.Message("[GC Log][Verbose] " + allPawnsCounter.Count().ToString() + " World Pawns marked during diagnosis"); } int i; /*Expansion 1:Expand relation network from map pawns.*/ Verse.Log.Message("[GC Log] Expanding Relation networks through Map Pawn Entry Points..."); for (i = mapPawnEntryPoints.Count - 1; i > -1; i--) { if (containsFlag(mapPawnEntryPoints[i], Flags.RelationLvl2)) { expandRelation(mapPawnEntryPoints[i], Flags.RelationLvl1); mapPawnEntryPoints.RemoveAt(i); } } for (i = mapPawnEntryPoints.Count - 1; i > -1; i--) { if (containsFlag(mapPawnEntryPoints[i], Flags.RelationLvl1)) { expandRelation(mapPawnEntryPoints[i], Flags.RelationLvl0); mapPawnEntryPoints.RemoveAt(i); } } /*Its unnecessary to process RelationLvl0 in mapPawnEntryPoints, * for they are not related to any world pawns. */ /*Expansion 2:Expand relation network from world pawns.*/ Verse.Log.Message("[GC Log] Expanding Relation networks on marked World Pawns..."); for (i = reference.Count - 1; i > -1; i--) { if (containsFlag(reference[i], Flags.RelationLvl2)) { expandRelation(reference[i], Flags.RelationLvl1); reference.RemoveAt(i); } } for (i = reference.Count - 1; i > -1; i--) { if (containsFlag(reference[i], Flags.RelationLvl1)) { expandRelation(reference[i], Flags.RelationLvl0); reference.RemoveAt(i); } } for (i = reference.Count - 1; i > -1; i--) { if (containsFlag(reference[i], Flags.RelationLvl0)) { reference.RemoveAt(i); } } int a = 0; /*VerboseMode:counting addFlag() calls.*/ if (verbose) { foreach (KeyValuePair <Pawn, int> p in allPawnsCounter) { a += p.Value; } Verse.Log.Message("[GC Log][Verbose] " + allPawnsCounter.Count().ToString() + " World Pawns marked during Expanding"); if (debug) { Verse.Log.Message("addFlag() called " + a + " times"); } } /*Posfix:remove UsedTalePawns.*/ Verse.Log.Message("[GC Log] Excluding Pawns concerned by Used Tales..."); foreach (Pawn p in allUsedTalePawns) { reference.Remove(p); } /*Exclude quest pawns*/ var questThings = Find.QuestManager.QuestsListForReading .Where(q => q.State == QuestState.NotYetAccepted) .SelectMany(q => q.QuestLookTargets) .Where(x => x.Thing != null) .Select(x => x.Thing) .Distinct() .ToList(); Verse.Log.Message($"[GC Log] Excluding Quest Pawns: {reference.RemoveAll(p => questThings.Contains(p))}"); /*GC Core:dispose all pawns left in reference list.*/ Verse.Log.Message("[GC Log] Disposing World Pawns..."); a = reference.Count; Pawn pawn; for (i = reference.Count - 1; i > -1; i--) { pawn = reference[i]; //Patch:Mysterious WorldPawn.missing //Update:This patch is disabled due to safety concerns. //if(Find.WorldPawns.Contains(pawn)) Find.WorldPawns.RemovePawn(pawn); if (!pawn.Destroyed) { pawn.Destroy(DestroyMode.Vanish); } if (!pawn.Discarded) { pawn.Discard(true); } } /*VerboseMode:Finalize output*/ if (verbose) { string s = "[GC Log][Verbose] Flag calls stat:"; allFlagsCounter.Remove(Flags.None); foreach (KeyValuePair <Flags, int> pair in allFlagsCounter) { s += "\n " + pair.Key.ToString() + " : " + pair.Value; } Verse.Log.Message(s); } Verse.Log.Message("[GC Log] GC() completed with " + a + " World Pawns disposed"); return(a); }
private string GetCriticalPawnReason(Pawn pawn) { if (pawn.Discarded) { return(null); } if (PawnUtility.EverBeenColonistOrTameAnimal(pawn) && pawn.RaceProps.Humanlike) { return("Colonist"); } if (PawnGenerator.IsBeingGenerated(pawn)) { return("Generating"); } if (PawnUtility.IsFactionLeader(pawn)) { return("FactionLeader"); } if (PawnUtility.IsKidnappedPawn(pawn)) { return("Kidnapped"); } if (pawn.IsCaravanMember()) { return("CaravanMember"); } if (PawnUtility.IsTravelingInTransportPodWorldObject(pawn)) { return("TransportPod"); } if (PawnUtility.ForSaleBySettlement(pawn)) { return("ForSale"); } if (Find.WorldPawns.ForcefullyKeptPawns.Contains(pawn)) { return("ForceKept"); } if (pawn.SpawnedOrAnyParentSpawned) { return("Spawned"); } if (!pawn.Corpse.DestroyedOrNull()) { return("CorpseExists"); } if (pawn.RaceProps.Humanlike && Current.ProgramState == ProgramState.Playing) { if (Find.PlayLog.AnyEntryConcerns(pawn)) { return("InPlayLog"); } if (Find.BattleLog.AnyEntryConcerns(pawn)) { return("InBattleLog"); } } if (Current.ProgramState == ProgramState.Playing && Find.TaleManager.AnyActiveTaleConcerns(pawn)) { return("InActiveTale"); } return(null); }
private static void _AppendThoughts_Humanlike(Pawn victim, DamageInfo?dinfo, Hediff hediff, PawnDiedOrDownedThoughtsKind thoughtsKind, List <IndividualThoughtToAdd> outIndividualThoughts, List <ThoughtDef> outAllColonistsThoughts) { var IsExecution = typeof(PawnDiedOrDownedThoughtsUtility).GetMethod("IsExecution", BindingFlags.Static | BindingFlags.NonPublic); var IsInnocentPrisoner = typeof(PawnDiedOrDownedThoughtsUtility).GetMethod("IsInnocentPrisoner", BindingFlags.Static | BindingFlags.NonPublic); bool flag = (bool)IsExecution.Invoke(null, new object[] { dinfo, hediff }); bool flag2 = (bool)IsInnocentPrisoner.Invoke(null, new object[] { victim }); bool flag3 = dinfo.HasValue && dinfo.Value.Def.externalViolence && dinfo.Value.Instigator != null && dinfo.Value.Instigator is Pawn; if (flag3) { Pawn pawn = (Pawn)dinfo.Value.Instigator; if (!pawn.Dead && pawn.needs.mood != null && pawn.story != null && pawn != victim) { if (thoughtsKind == PawnDiedOrDownedThoughtsKind.Died) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.KilledHumanlikeBloodlust, pawn, null, 1f, 1f)); } if (thoughtsKind == PawnDiedOrDownedThoughtsKind.Died && victim.HostileTo(pawn)) { if (victim.Faction != null && PawnUtility.IsFactionLeader(victim) && victim.Faction.HostileTo(pawn.Faction)) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.DefeatedHostileFactionLeader, pawn, victim, 1f, 1f)); } else if (victim.Faction != null && victim.Faction.HostileTo(pawn.Faction) && !flag2) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.KilledHumanlikeEnemy, pawn, victim, 1f, 1f)); } if (victim.kindDef.combatPower > 250f) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.DefeatedMajorEnemy, pawn, victim, 1f, 1f)); } } } } if (thoughtsKind == PawnDiedOrDownedThoughtsKind.Died && victim.Spawned) { List <Pawn> allPawnsSpawned = victim.Map.mapPawns.AllPawnsSpawned; for (int i = 0; i < allPawnsSpawned.Count; i++) { Pawn pawn2 = allPawnsSpawned[i]; if (pawn2 != victim && pawn2.needs.mood != null) { if (!flag && (pawn2.MentalStateDef != MentalStateDefOf.SocialFighting || ((MentalState_SocialFighting)pawn2.MentalState).otherPawn != victim)) { if (pawn2.Position.InHorDistOf(victim.Position, 12f) && GenSight.LineOfSight(victim.Position, pawn2.Position, victim.Map, false) && pawn2.Awake() && pawn2.health.capacities.CapableOf(PawnCapacityDefOf.Sight)) { if (pawn2.Faction == victim.Faction) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.WitnessedDeathAlly, pawn2, null, 1f, 1f)); outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.WitnessedDeathAllyBleedingHeart, pawn2, null, 1f, 1f)); } else if (victim.Faction == null || (!victim.Faction.HostileTo(pawn2.Faction) || pawn2.story.traits.HasTrait(TraitDefOfPsychology.BleedingHeart))) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.WitnessedDeathNonAlly, pawn2, null, 1f, 1f)); outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.WitnessedDeathNonAllyBleedingHeart, pawn2, null, 1f, 1f)); } if (pawn2.relations.FamilyByBlood.Contains(victim)) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.WitnessedDeathFamily, pawn2, null, 1f, 1f)); } outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.WitnessedDeathBloodlust, pawn2, null, 1f, 1f)); if (!pawn2.story.traits.HasTrait(TraitDefOfPsychology.BleedingHeart) && !pawn2.story.traits.HasTrait(TraitDefOf.Psychopath) && !pawn2.story.traits.HasTrait(TraitDefOf.Bloodlust) && !pawn2.story.traits.HasTrait(TraitDefOfPsychology.Desensitized)) { if (((pawn2.GetHashCode() ^ (GenLocalDate.DayOfYear(pawn2) + GenLocalDate.Year(pawn2) + (int)(GenLocalDate.DayPercent(pawn2) * 5) * 60) * 391) % 1000) == 0) { pawn2.story.traits.GainTrait(new Trait(TraitDefOfPsychology.Desensitized)); pawn2.needs.mood.thoughts.memories.TryGainMemoryThought(ThoughtDefOfPsychology.RecentlyDesensitized, null); } } } else if (victim.Faction == Faction.OfPlayer && victim.Faction == pawn2.Faction && victim.HostFaction != pawn2.Faction) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.KnowColonistDied, pawn2, null, 1f, 1f)); outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.KnowColonistDiedBleedingHeart, pawn2, null, 1f, 1f)); } if (flag2 && pawn2.Faction == Faction.OfPlayer && !pawn2.IsPrisoner) { outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOf.KnowPrisonerDiedInnocent, pawn2, null, 1f, 1f)); outIndividualThoughts.Add(new IndividualThoughtToAdd(ThoughtDefOfPsychology.KnowPrisonerDiedInnocentBleedingHeart, pawn2, null, 1f, 1f)); } } } } } if (thoughtsKind == PawnDiedOrDownedThoughtsKind.Abandoned && victim.IsColonist) { outAllColonistsThoughts.Add(ThoughtDefOf.ColonistAbandoned); outAllColonistsThoughts.Add(ThoughtDefOfPsychology.ColonistAbandonedBleedingHeart); } if (thoughtsKind == PawnDiedOrDownedThoughtsKind.AbandonedToDie) { if (victim.IsColonist) { outAllColonistsThoughts.Add(ThoughtDefOf.ColonistAbandonedToDie); outAllColonistsThoughts.Add(ThoughtDefOfPsychology.ColonistAbandonedToDieBleedingHeart); } else if (victim.IsPrisonerOfColony) { outAllColonistsThoughts.Add(ThoughtDefOf.PrisonerAbandonedToDie); outAllColonistsThoughts.Add(ThoughtDefOfPsychology.PrisonerAbandonedToDieBleedingHeart); } } }
public static bool Replacement(ref string __result, Pawn pawn, Pawn fromPOV) { if (pawn.Dead) { __result = "Dead".Translate(); return(false); } if (pawn.Destroyed) { __result = "Missing".Translate(); return(false); } if (PawnUtility.IsKidnappedPawn(pawn)) { __result = "Kidnapped".Translate(); return(false); } if (pawn.kindDef == PawnKindDefOf.Slave) { __result = "Slave".Translate(); return(false); } if (PawnUtility.IsFactionLeader(pawn)) { __result = "FactionLeader".Translate(); return(false); } Faction faction = pawn.Faction; if (faction == fromPOV.Faction) { __result = string.Empty; return(false); } if (faction == null || fromPOV.Faction == null) { __result = "Neutral".Translate(); return(false); } #region ADDED if (faction == Faction.OfPlayer) { __result = "Colony".Translate(); return(false); } #endregion switch (faction.RelationKindWith(fromPOV.Faction)) { case FactionRelationKind.Hostile: __result = "Hostile".Translate() + ", " + faction.Name; return(false); case FactionRelationKind.Neutral: __result = "Neutral".Translate() + ", " + faction.Name; return(false); case FactionRelationKind.Ally: __result = "Ally".Translate() + ", " + faction.Name; return(false); default: __result = ""; return(false); } }
private void HandleExcitementOfKiller(Pawn recipient) { // Killer != null => DamageInfo != null // The obligatory .Value is extremely triggering. if (Killer != null) { if (Killer == PrimaryVictim) { // We don't handle suicide cases. return; } if (recipient == Killer && KillingBlowDamageDef.ExternalViolenceFor(Victim)) { // Confirmed NewsReceipient is Killer // IsCapableOfThought has already been called. // NewsReceipient is definitely capable of thought. // Why this check tho if (recipient.story != null) { // Bloodlust thoughts for Bloodlust guys, currently only for human victims // We can expand upon this, and add in witnessed death (animals) with bloodlust if (Victim.RaceProps.Humanlike) { // Try to add Bloodlust thoughts; will be auto-rejected if recipient does not have Bloodlust new IndividualThoughtToAdd(ThoughtDefOf.KilledHumanlikeBloodlust, recipient).Add(); // Try to add Defeated Hostile Leader thoughts if (Victim.HostileTo(Killer) && Victim.Faction != null && PawnUtility.IsFactionLeader(Victim) && Victim.Faction.HostileTo(Killer.Faction)) { new IndividualThoughtToAdd(ThoughtDefOf.DefeatedHostileFactionLeader, Killer, Victim).Add(); } } } } } }
/// <summary> /// Attempts to give the killer their appropriate excitement (or disappointment). Aborts if there is no killer. /// </summary> /// <param name="recipient"></param> private void TryProcessKillerHigh(Pawn recipient) { // Killer != null => DamageInfo != null if (Killer != null) { // Currently you can't really kill yourself. // That would be something interesting, but we don't do that here. if (recipient == Killer) { // IDK, is this something we can consider checking? if (KillingBlowDamageDef.ExternalViolenceFor(Victim)) { // Why this check tho if (recipient.story != null) { // Bloodlust thoughts for Bloodlust guys, currently only for human victims // We can expand upon this, and add in witnessed death (animals) with bloodlust if (Victim.RaceProps.Humanlike) { // Try to add Bloodlust thoughts; will be auto-rejected if recipient does not have Bloodlust new IndividualThoughtToAdd(ThoughtDefOf.KilledHumanlikeBloodlust, recipient).Add(); // Try to add Defeated Hostile Leader thoughts if (Victim.HostileTo(Killer) && Victim.Faction != null && PawnUtility.IsFactionLeader(Victim) && Victim.Faction.HostileTo(Killer.Faction)) { new IndividualThoughtToAdd(ThoughtDefOf.DefeatedHostileFactionLeader, Killer, Victim).Add(); } } } } } } /* * // Note that "animal bonds" is a type of relationships. * if (Killer != Victim && Killer == recipient) * { * HandleExcitementOfKiller(recipient); * } */ }
IEnumerable <Thing> GenerateThingEnumer(int forTile, Faction forFaction) { int numKinds = kindCountRange.RandomInRange; int count = countRange.RandomInRange; List <PawnKindDef> kinds = new List <PawnKindDef>(); for (int j = 0; j < numKinds; j++) { if (!(from k in DefDatabase <PawnKindDef> .AllDefs where !kinds.Contains(k) && PawnKindAllowed(k, forTile) select k).TryRandomElementByWeight((PawnKindDef k) => SelectionChance(k), out PawnKindDef result)) { break; } kinds.Add(result); } for (int i = 0; i < count; i++) { if (!kinds.TryRandomElement(out PawnKindDef kind)) { break; } PawnKindDef kind2 = kind; int tile = forTile; Pawn pawnOriginal = Find.WorldPawns.AllPawnsAlive.Where(p => !p.IsPlayerControlledCaravanMember() && (PawnUtility.ForSaleBySettlement(p) || p.kindDef == PawnKindDefOf.Slave || (PawnUtility.IsKidnappedPawn(p) && p.RaceProps.Humanlike) && !PawnUtility.IsFactionLeader(p))).RandomElement(); PawnGenerationRequest request = new PawnGenerationRequest(kind2, null, PawnGenerationContext.NonPlayer, tile); Pawn pawn = PawnGenerator.GeneratePawn(request); //Generate the animal! if (pawnOriginal == null) { Gender newGender = pawn.gender; if (Rand.RangeInclusive(0, 100) <= 25) { switch (pawn.gender) { case (Gender.Male): newGender = Gender.Female; break; case (Gender.Female): newGender = Gender.Male; break; default: break; } } float animalAge = pawn.ageTracker.AgeBiologicalYearsFloat; float animalLifeExpectancy = pawn.def.race.lifeExpectancy; float humanLifeExpectancy = 80f; float age = animalAge * humanLifeExpectancy / animalLifeExpectancy; age = Mathf.Max(age, 17); //make sure the human is at least 17 years old float chronoAge = pawn.ageTracker.AgeChronologicalYears * age / animalAge; var pkds = new List <PawnKindDef> { PawnKindDefOf.Slave, PawnKindDefOf.Colonist, PawnKindDefOf.SpaceRefugee, PawnKindDefOf.Villager, PawnKindDefOf.Drifter, PawnKindDefOf.AncientSoldier }; PawnKindDef rKind = pkds.RandElement(); var hRequest = new PawnGenerationRequest(rKind, Faction.OfPlayer, PawnGenerationContext.NonPlayer, fixedBiologicalAge: age, fixedChronologicalAge: chronoAge, fixedGender: newGender); pawnOriginal = PawnGenerator.GeneratePawn(hRequest); pawn.Name = pawnOriginal.Name; } else { pawn.Name = pawnOriginal.Name; Find.WorldPawns.RemovePawn(pawnOriginal); } var pm = TfSys.TransformedPawn.Create(pawnOriginal, pawn); //pawnOriginal is human, pawn is animal FormerHumanUtilities.MakeAnimalSapient(pawnOriginal, pawn, Rand.Range(0.5f, 1f)); Find.World.GetComponent <PawnmorphGameComp>().AddTransformedPawn(pm); yield return(pawn); } }