private void expandRelation(Pawn p, Flags flag)
        {
            //Patch:null Relation_tracker for mechanoids & insects.
            if (p.relations == null)
            {
                return;
            }

            if (debug)
            {
                foreach (Pawn p0 in p.relations.FamilyByBlood)
                {
                    foreach (PawnRelationDef d in p.GetRelations(p0))
                    {
                        Verse.Log.Message("(Family) " + p.LabelShort + " <" + d.label + "> " + p0.LabelShort);
                    }
                }
            }

            foreach (DirectPawnRelation r in p.relations.DirectRelations)
            {
                if (reference.Contains(r.otherPawn))
                {
                    addFlag(r.otherPawn, flag);
                    if (verbose)
                    {
                        Verse.Log.Message("(Relation) " + p.LabelShort + " <" + r.def.label + "> " + r.otherPawn.LabelShort);
                    }
                }
            }

            foreach (Pawn p2 in CleanserUtil.getPawnsWithDirectRelationsWithMe(p.relations))
            {
                if (reference.Contains(p2) && p2.GetRelations(p).Count <PawnRelationDef>() > 0)
                {
                    addFlag(p2, flag);
                    if (verbose)
                    {
                        Verse.Log.Message("(Reflexed) <" + p2.LabelShort + "," + p.LabelShort + ">");
                    }
                }
            }
        }
        static FloatMenuUtil()
        {
            groups  = new Dictionary <string, List <string> >();
            items   = new Dictionary <string, Action>();
            devOnly = new Dictionary <string, bool>();

            string group;

            group = GroupFix;
            Add(group, "FloatDebugLog".Translate(), delegate {
                if (!Find.WindowStack.TryRemove(typeof(EditWindow_Log), true))
                {
                    Find.WindowStack.Add(new EditWindow_Log());
                }
            });
            Add(group, "FloatAGRegen".Translate(), delegate {
                foreach (Map map in Find.Maps)
                {
                    map.avoidGrid.Regenerate();
                }
                Message("MsgTextAGR".Translate(), MessageTypeDefOf.PositiveEvent);
            });
            Add(group, "FloatFactionFixItems1".Translate(), delegate {
                CleanserUtil.FixFactionRelationships();
                Message("MsgTextFFR".Translate(), MessageTypeDefOf.PositiveEvent);
            });
            Add(group, "FloatFactionFixItems2".Translate(), delegate {
                Message("MsgTextFFL".Translate(CleanserUtil.FixFactionLeader_Wrapped()), MessageTypeDefOf.PositiveEvent);
            });

            group = GroupTools;
            Add(group, "FloatToolsItems1".Translate(), delegate {
                int a = CleanserUtil.DeconstructAnimalFamily();
                Verse.Log.Message("CleanserUtil.DeconstructAnimalFamily():Round 1 completed.");
                CleanserUtil.DeconstructAnimalFamily();
                Verse.Log.Message("CleanserUtil.DeconstructAnimalFamily():Round 2 completed.");
                Message("MsgTextAFT".Translate(a), MessageTypeDefOf.PositiveEvent);
            });

            Add(group, "FloatToolsItems2".Translate(), delegate {
                Message("MsgTextRFH".Translate(CleanserUtil.RemoveFilth(Find.CurrentMap, true)), MessageTypeDefOf.PositiveEvent);
            });
            Add(group, "FloatToolsItems2Dev1".Translate(), delegate {
                Message("MsgTextRFM".Translate(CleanserUtil.RemoveFilth(Find.CurrentMap, false)), MessageTypeDefOf.PositiveEvent);
            }, true);
            Add(group, "FloatToolsItems2Dev2".Translate(), delegate {
                int i = 0;
                foreach (Map m in Find.Maps)
                {
                    i += CleanserUtil.RemoveFilth(m, false);
                }
                Message("MsgTextRFW".Translate(i), MessageTypeDefOf.PositiveEvent);
            }, true);

            Add(group, "FloatToolsItems2Snow".Translate(), delegate {
                CleanserUtil.RemoveSnow(Find.CurrentMap, true);
                Message("MsgTextRSH".Translate(), MessageTypeDefOf.PositiveEvent);
            });
            Add(group, "FloatToolsItems2SnowDev1".Translate(), delegate {
                CleanserUtil.RemoveSnow(Find.CurrentMap, false);
                Message("MsgTextRSM".Translate(), MessageTypeDefOf.PositiveEvent);
            }, true);
            Add(group, "FloatToolsItems2SnowDev2".Translate(), delegate {
                foreach (Map m in Find.Maps)
                {
                    CleanserUtil.RemoveSnow(m, false);
                }
                Message("MsgTextRSW".Translate(), MessageTypeDefOf.PositiveEvent);
            }, true);

            Add(group, "FloatToolsItems3".Translate(), delegate {
                Message("MsgTextRCM".Translate(CleanserUtil.RemoveCorpses()), MessageTypeDefOf.NeutralEvent);
            });
            Add(group, "FloatToolsItems4".Translate(), delegate {
                Message("MsgTextRBL".Translate(CleanserUtil.RemoveAllBattleLogEntries()), MessageTypeDefOf.PositiveEvent);
            });

            Add(group, "FloatToolsItems5".Translate(), delegate {
                Message("MsgTextRAM".Translate(CleanserUtil.RemoveIArchivable(false)), MessageTypeDefOf.PositiveEvent);
            });
            Add(group, "FloatToolsItems5Dev".Translate(), delegate {
                Message("MsgTextRAM".Translate(CleanserUtil.RemoveIArchivable(true)), MessageTypeDefOf.PositiveEvent);
            }, true);

            //StorytellerFix menus here

            //Storyteller Info Window for non Dev Mode
            Add(group, "StorytellerFixQueueInfo".Translate(), delegate { StorytellerFix.StorytellerFix_GetInfoButton(); }, false);

            //Storyteller Clear Queue for Dev Mode only
            Add(group, "StorytellerFixQueueClearDev".Translate(), delegate { StorytellerFix.StorytellerFix_ClearButton(); }, true);

            //end of StorytellerFix


            group = GroupQuickbar;
            Add(group, "QuickCloseLetStack".Translate(), delegate {
                LetterStack ls = Find.LetterStack;
                if (ls == null)
                {
                    return;
                }
                for (int i = ls.LettersListForReading.Count - 1; i > -1; i--)
                {
                    ls.RemoveLetter(ls.LettersListForReading[i]);
                }
            });
            Add(group, "QuickUnlockSpeedLimit".Translate(), delegate {
                CleanserUtil.UnlockNormalSpeedLimit();
                Find.WindowStack.TryRemove(typeof(UserInterface));
            });
            Add(group, "QuickOpenSettings".Translate(), delegate {
                CleanserUtil.OpenModSettingsPage();
            });


            group = GroupMMUpdateMode;
            foreach (MemoryMonitorUpdateMode m in Enum.GetValues(typeof(MemoryMonitorUpdateMode)))
            {
                Add(group, ("MMUpdate_" + m.ToString()).Translate(), delegate
                {
                    RuntimeGC.Settings.MemoryMonitorUpdateInterval = (int)m;
                    UIUtil.Notify_MMBtnLabelChanged();
                }, m.ToString().Contains("Debug_"));
            }
        }
        /// <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);
            }


            /*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);
        }
Exemple #4
0
        /// <summary>
        /// Manual finalizer for WorldPawnCleaner.GC().
        /// Deconstruct all animal families on map and discard the redundant members.
        /// </summary>
        /// <returns>The count of discarded members.</returns>
        public static int DeconstructAnimalFamily()
        {
            List <Pawn> worldpawns = new List <Pawn>();

            foreach (Pawn p in Find.WorldPawns.AllPawnsAliveOrDead)
            {
                worldpawns.Add(p);
            }

            List <Pawn> queue    = new List <Pawn>();
            List <Pawn> pawnlist = new List <Pawn>();

            foreach (Map map in Find.Maps)
            {
                foreach (Pawn p in map.mapPawns.AllPawns)
                {
                    if ((p.records.GetAsInt(RecordDefOf.TimeAsColonistOrColonyAnimal) > 0) && !(p.RaceProps.Humanlike))
                    {
                        pawnlist.Add(p);
                    }
                }
            }

            foreach (Pawn p in pawnlist)
            {
                //Patch:null relationship on robots.
                if (p.relations != null)
                {
                    foreach (Pawn p2 in expandRelation(p))
                    {
                        if (worldpawns.Contains(p2) && (!p2.Spawned) && (!p2.IsPlayerControlledCaravanMember()) && (!PawnUtility.IsTravelingInTransportPodWorldObject(p2))
                            //Patch:Corpses remained on maps.
                            && (p.Corpse == null)
                            )
                        {
                            queue.Add(p2);
                            worldpawns.Remove(p2);
                        }
                    }
                }
            }


            //Patch:2nd Pawn of a used Tale_DoublePawn will raise Scribe Warnings if discarded
            List <Pawn> allUsedTaleOwner;

            CleanserUtil.InitUsedTalePawns(out allUsedTaleOwner);
            foreach (Pawn pawn in allUsedTaleOwner)
            {
                queue.Remove(pawn);
            }

            int a = queue.Count;

            foreach (Pawn pawn in queue)
            {
                Find.WorldPawns.RemovePawn(pawn);
                if (!pawn.Destroyed)
                {
                    pawn.Destroy(DestroyMode.Vanish);
                }
                if (!pawn.Discarded)
                {
                    pawn.Discard(true);
                }
            }

            Find.WindowStack.WindowOfType <UserInterface>().Notify_PawnsCountDirty();
            return(a);
        }