예제 #1
0
 public static void Postfix(Zone_Stockpile __instance)
 {
     KSLog.Message("[KanbanStockpile] Zone_Stockpile_PostDeregister_Patch.Postfix()");
     if (State.Exists(__instance.ToString()))
     {
         KSLog.Message("[KanbanStockpile] Removing " + __instance.ToString());
         State.Del(__instance.ToString());
     }
 }
예제 #2
0
        public static bool Prefix(Pawn p, Thing t, IntVec3 storeCell, bool fitInStoreCell, ref Job __result)
        {
            if (!storeCell.TryGetKanbanSettings(t.Map, out var ks, out var slotGroup))
            {
                return(true);
            }

            int stackLimit = Math.Max(1, (int)(t.def.stackLimit * ks.srt / 100f));

            KSLog.Message($"[UnloadHauledInventory] {t.LabelCap} x{t.stackCount} / limit = {stackLimit}");

            int          countToDrop = -1;
            List <Thing> things      = t.Map.thingGrid.ThingsListAt(storeCell);

            for (int i = 0; i < things.Count; i++)
            {
                Thing t2 = things[i];
                if (!t2.def.EverStorable(false))
                {
                    continue;                              // skip non-storable things as they aren't actually *in* the stockpile
                }
                if (!t2.CanStackWith(t))
                {
                    continue;                      // skip it if it cannot stack with thing to haul
                }
                if (t2.stackCount >= stackLimit)
                {
                    continue;                              // no need to refill until count is below threshold
                }
                int needMax = stackLimit - t2.stackCount;
                countToDrop = Math.Min(t.stackCount, needMax);
                KSLog.Message($"  drop to stack => stack: {t2.stackCount}, countToDrop: {countToDrop}");
                break;
            }

            if (countToDrop > 0)
            {
                Job job = new Job(JobDefOf.HaulToCell, t, storeCell)
                {
                    count = countToDrop,
                    haulOpportunisticDuplicates = true,
                    haulMode = HaulMode.ToCellStorage
                };
                __result = job;
                KSLog.Message($"  dispatch job1, thing={t},cell={storeCell},countToDrop={countToDrop}");
                return(false);
            }

            Job job2 = new Job(JobDefOf.HaulToCell, t, storeCell);

            job2.count = t.stackCount > stackLimit ? stackLimit : t.stackCount;
            job2.haulOpportunisticDuplicates = false;
            job2.haulMode = HaulMode.ToCellStorage;
            __result      = job2;
            KSLog.Message($"  dispatch job2(empty cell), thing={t},cell={storeCell},countToDrop={job2.count}");
            return(false);
        }
예제 #3
0
        static KanbanStockpileLoader()
        {
            var harmony = new Harmony("net.ubergarm.rimworld.mods.kanbanstockpile");

            harmony.PatchAll();

            if (ModLister.GetActiveModWithIdentifier("LWM.DeepStorage") != null)
            {
                IsLWMDeepStorageLoaded = true;
                Log.Message("[KanbanStockpile] Detected LWM Deep Storage is loaded!");
            }
            else
            {
                IsLWMDeepStorageLoaded = false;
                KSLog.Message("[KanbanStockpile] Did *NOT* detect LWM Deep Storage...");
            }

            if (ModLister.GetActiveModWithIdentifier("Uuugggg.StockpileRanking") != null)
            {
                IsStockpileRankingLoaded = true;
                Log.Message("[KanbanStockpile] Detected Uuugggg's StockpileRanking is loaded!");
            }
            else
            {
                IsStockpileRankingLoaded = false;
                KSLog.Message("[KanbanStockpile] Did *NOT* detect Uuugggg's StockpileRanking...");
            }

            // Check for both the original and the re-uploaded one (which is basically the same)
            if ((ModLister.GetActiveModWithIdentifier("Mehni.PickUpAndHaul") != null) ||
                (ModLister.GetActiveModWithIdentifier("Mlie.PickUpAndHaul") != null))
            {
                IsPickUpAndHaulLoaded = true;
                Log.Message("[KanbanStockpile] Detected Mehni or Mlie PickUpAndHaul is loaded!");
                if (KanbanStockpile.Settings.PreventPickUpAndHaulOverHauling)
                {
                    PickUpAndHaul_WorkGiver_HaulToInventory_Patch.ApplyPatch(harmony);
                }
            }
            else
            {
                IsPickUpAndHaulLoaded = false;
                KSLog.Message("[KanbanStockpile] Did *NOT* detect Mehni or Mlie PickUpAndHaul...");
            }

            if (MP.enabled)
            {
                MP.RegisterSyncMethod(typeof(State), nameof(State.Set));
                MP.RegisterSyncMethod(typeof(State), nameof(State.Del));
                MP.RegisterSyncWorker <KanbanSettings>(State.SyncKanbanSettings, typeof(KanbanSettings), false, false);
            }
        }
예제 #4
0
        public static void Prefix(Zone ___zone, string name)
        {
            //private Zone zone;
            string oldName = ___zone?.label ?? "N/A";

            KSLog.Message("[KanbanStockpile] Dialog_RenameZone.SetName() oldName: " + oldName);
            KSLog.Message("[KanbanStockpile] Dialog_RenameZone.SetName() newName: " + name);
            if (oldName == "N/A")
            {
                return;
            }

            State.Set(name, State.Get(oldName));
            State.Del(oldName);
        }
예제 #5
0
        //public void CopyFrom(StorageSettings other)
        public static void CopyFrom(StorageSettings __instance, StorageSettings other)
        {
            var st = new StackTrace();

            if (st.FrameCount > 3 && st.GetFrame(2).GetMethod() == Building_Storage_PostMake)
            {
                return; // prevent copy settings when called from Building_Storage:PostMake, new storage
            }
            KSLog.Message("[KanbanStockpile] CopyFrom()");
            string         label = other?.owner?.ToString() ?? "___clipboard";
            KanbanSettings ks    = State.Get(label);

            label = __instance?.owner?.ToString() ?? "___clipboard";
            State.Set(label, ks);
        }
예제 #6
0
        public static void Postfix(StorageSettings __instance)
        {
            // The clipboard StorageSettings has no owner, so assume a null is the clipboard...
            string label = __instance?.owner?.ToString() ?? "___clipboard";

            KSLog.Message("[KanbanStockpile] ExposeData() with owner name: " + label);
            KanbanSettings ks = State.Get(label);

            if (Scribe.mode == LoadSaveMode.Saving)
            {
                // this mode implicitly takes the value currently in srt and saves it out
                Scribe_Values.Look(ref ks.srt, "stackRefillThreshold", 100, true);
                Scribe_Values.Look(ref ks.ssl, "similarStackLimit", 0, true);
            }
            else if (Scribe.mode == LoadSaveMode.LoadingVars)
            {
                // this mode implicitly loads some other value into this instance of srt
                Scribe_Values.Look(ref ks.srt, "stackRefillThreshold", 100, false);
                Scribe_Values.Look(ref ks.ssl, "similarStackLimit", 0, false);
                State.Set(label, ks);
            }
        }
예제 #7
0
        static bool CheckKanbanSettings(Map map, IntVec3 cell, ThingCount thingCount, ref int countToDrop)
        {
            if (!cell.TryGetKanbanSettings(map, out var ks, out var slotGroup))
            {
                return(false);
            }

            var thing      = thingCount.Thing;
            int stackLimit = Math.Max(1, (int)(thing.def.stackLimit * ks.srt / 100f));

            KSLog.Message($"[UnloadHauledInventory] {thing.LabelCap} x{thing.stackCount} / limit = {stackLimit}");

            List <Thing> things = map.thingGrid.ThingsListAt(cell);

            for (int i = 0; i < things.Count; i++)
            {
                Thing t = things[i];
                if (!t.def.EverStorable(false))
                {
                    continue;                             // skip non-storable things as they aren't actually *in* the stockpile
                }
                if (!t.CanStackWith(thing))
                {
                    continue;                         // skip it if it cannot stack with thing to haul
                }
                if (t.stackCount >= stackLimit)
                {
                    continue;                             // no need to refill until count is below threshold
                }
                int needMax = stackLimit - t.stackCount;
                countToDrop = Math.Min(thing.stackCount, needMax);
                KSLog.Message($"  drop to stack => stack: {t.stackCount}, countToDrop: {countToDrop}");
                return(true);
            }

            countToDrop = thing.stackCount > stackLimit ? stackLimit : thing.stackCount;
            KSLog.Message($"  drop to empty cell => countToDrop: {countToDrop}");
            return(true);
        }
예제 #8
0
        public static void Postfix(ref bool __result, IntVec3 c, Map map, Thing thing)
        {
            // NOTE: Likely LWM Deep Storages Prefix() and Vanilla NoStorageBlockersIn() itself have already run
            // returning false means storage is "full" so do *not* try to haul the thing
            // returning true means storage still has space available for thing so try to haul it

            // storage already filled up so no need to try to limit it further
            if (__result == false)
            {
                return;
            }

            // make sure we have everything we need to continue
            if (!c.TryGetKanbanSettings(map, out var ks, out var slotGroup))
            {
                return;
            }

            // Assuming JobDefOf.HaulToContainer for Building_Storage vs JobDefOf.HaulToCell otherwise
            bool isContainer = (slotGroup?.parent is Building_Storage);

            // StackRefillThreshold checks only here at cell c
            List <Thing> things        = map.thingGrid.ThingsListAt(c);
            int          numDuplicates = 0;

            int stackLimit = Math.Max(1, (int)(thing.def.stackLimit * ks.srt / 100f));

            // TODO #5 consider re-ordering to prevent refilling an accidental/leftover duplicate stack
            // Design Decision: use for loops instead of foreach as they may be faster and similar to this vanilla function
            for (int i = 0; i < things.Count; i++)
            {
                Thing t = things[i];
                if (!t.def.EverStorable(false))
                {
                    continue;                             // skip non-storable things as they aren't actually *in* the stockpile
                }
                if (!t.CanStackWith(thing))
                {
                    continue;                         // skip it if it cannot stack with thing to haul
                }
                if (t.stackCount >= stackLimit)
                {
                    continue;                             // no need to refill until count is below threshold
                }
                if (!isContainer)
                {
                    // pawns are smart enough to grab a partial stack for vanilla cell stockpiles so no need to explicitly check here
                    // maybe this is a JobDefOf.HaulToCell job?
                    KSLog.Message("[KanbanStockpile] YES HAUL PARTIAL STACK OF THING TO TOPOFF STACK IN CELL STOCKPILE!");
                    __result = true;
                    return;
                }
                else if (t.stackCount < stackLimit)
                {
                    // pawns seem to try to haul a full stack no matter what for HaulToContainer unlike HaulToCell CurJobDef's
                    // so for here when trying to haul to deep storage explicitly ensure stack to haul is partial stack
                    // maybe this is a JobDefOf.HaulToContainer job?
                    KSLog.Message("[KanbanStockpile] YES HAUL EXISTING PARTIAL STACK OF THING TO BUILDING STORAGE CONTAINER!");
                    __result = true;
                    return;
                }
            }

            if (ks.ssl == 0)
            {
                return;
            }
            // SimilarStackLimit check all cells in the slotgroup (potentially CPU intensive for big zones/limits)
            // SlotGroup.HeldThings
            for (int j = 0; j < slotGroup.CellsList.Count; j++)
            {
                IntVec3 cell = slotGroup.CellsList[j];
                things = map.thingGrid.ThingsListAt(cell);
                for (int i = 0; i < things.Count; i++)
                {
                    Thing t = things[i];
                    if (!t.def.EverStorable(false))
                    {
                        continue;                             // skip non-storable things as they aren't actually *in* the stockpile
                    }
                    if (!t.CanStackWith(thing) && t.def != thing.def)
                    {
                        continue;                                               // skip it if it cannot stack with thing to haul
                    }
                    // even a partial stack is a dupe so count it regardless
                    numDuplicates += Math.Max(1, t.stackCount / stackLimit);
                    if (numDuplicates >= ks.ssl)
                    {
                        KSLog.Message("[KanbanStockpile] NO DON'T HAUL AS THERE IS ALREADY TOO MANY OF THAT KIND OF STACK!");
                        __result = false;
                        return;
                    }
                }
            }

            // iterate over all outstanding reserved jobs to prevent hauling duplicate similar stacks
            if (KanbanStockpile.Settings.aggressiveSimilarStockpileLimiting == false)
            {
                return;
            }
            if (map.reservationManager == null)
            {
                return;
            }
            var reservations = map.reservationManager.reservations;

            if (reservations == null)
            {
                return;
            }
            ReservationManager.Reservation r;
            for (int i = 0; i < reservations.Count; i++)
            {
                r = reservations[i];
                if (r == null)
                {
                    continue;
                }
                if (r.Job == null)
                {
                    continue;
                }
                if (!(r.Job.def == JobDefOf.HaulToCell || r.Job.def == JobDefOf.HaulToContainer))
                {
                    continue;
                }

                Thing t = r.Job.targetA.Thing;
                if (t == null)
                {
                    continue;
                }
                if (t == thing)
                {
                    continue;              // no need to check against itself
                }
                if (!t.CanStackWith(thing))
                {
                    continue;                         // skip it if it cannot stack with thing to haul
                }
                IntVec3 dest;
                if (r.Job.def == JobDefOf.HaulToCell)
                {
                    dest = r.Job.targetB.Cell;
                }
                else
                {
                    // case of JobDefOf.HaulToContainer
                    Thing container = r.Job.targetB.Thing;
                    if (container == null)
                    {
                        continue;
                    }
                    dest = container.Position;
                }

                if (dest == null)
                {
                    continue;
                }
                SlotGroup sg = dest.GetSlotGroup(map);
                if (sg == null)
                {
                    continue;
                }
                if (sg != slotGroup)
                {
                    continue;                  // skip it as the similar thing is being hauled to a different stockpile
                }
                // there is a thing that can stack with this thing and is already reserved for hauling to the desired stockpile: DUPE!
                numDuplicates++;
                if (numDuplicates >= ks.ssl)
                {
                    KSLog.Message("[KanbanStockpile] NO DON'T HAUL AS THERE IS ALREADY SOMEONE RESERVING JOB TO DO IT!");
                    __result = false;
                    return;
                }
            }

            // if we get here, haul that thing!
            return;
        }
예제 #9
0
        public static void DrawKanbanSettings(ITab_Storage tab)
        {
            IHaulDestination haulDestination = SelStoreInfo.GetValue(tab, null) as IHaulDestination;

            if (haulDestination == null)
            {
                return;
            }
            StorageSettings settings = haulDestination.GetStoreSettings();

            if (settings == null)
            {
                return;
            }

            //ITab_Storage.WinSize = 300
            float buttonMargin = ITab_Storage_TopAreaHeight_Patch.extraHeight + 4;
            Rect  rect         = new Rect(0f, (float)GetTopAreaHeight.Invoke(tab, new object[] { }) - ITab_Storage_TopAreaHeight_Patch.extraHeight - 2, 280, ITab_Storage_TopAreaHeight_Patch.extraHeight);

            // if Stockpile Ranking is installed, scootch these widgets up so it doesn't overlap
            // https://github.com/alextd/RimWorld-StockpileRanking/blob/master/Source/RankSelection.cs#L18
            if (KanbanStockpileLoader.IsStockpileRankingLoaded)
            {
                rect.y -= 26f;
            }

            rect.x     += buttonMargin;
            rect.width -= buttonMargin * 3;
            Text.Font   = GameFont.Small;

            KanbanSettings ks, tmp;

            ks      = State.Get(settings.owner.ToString());
            tmp.srt = ks.srt;
            tmp.ssl = ks.ssl;

            string stackRefillThresholdLabel = "KS.StackRefillThreshold".Translate(ks.srt);

            string similarStackLimitLabel;

            if (ks.ssl > 0)
            {
                similarStackLimitLabel = "KS.SimilarStackLimit".Translate(ks.ssl);
            }
            else
            {
                similarStackLimitLabel = "KS.SimilarStackLimitOff".Translate();
            }

            //Stack Refill Threshold Slider
            tmp.srt = (int)Widgets.HorizontalSlider(new Rect(0f, rect.yMin + 10f, 150f, 15f),
                                                    ks.srt, 0f, 100f, false, stackRefillThresholdLabel, null, null, 1f);

            //Similar Stack Limit Slider
            tmp.ssl = (int)Widgets.HorizontalSlider(new Rect(155, rect.yMin + 10f, 125f, 15f),
                                                    ks.ssl, 0f, 8f, false, similarStackLimitLabel, null, null, 1f);


            if ((ks.srt != tmp.srt) ||
                (ks.ssl != tmp.ssl))
            {
                // Accept slider changes no faster than 4Hz (250ms) to prevent spamming multiplayer sync lag
                DateTime curTime = DateTime.Now;
                if ((curTime - lastUpdateTime).TotalMilliseconds < 250)
                {
                    return;
                }
                lastUpdateTime = curTime;

                KSLog.Message("[KanbanStockpile] Changed Stack Refill Threshold for settings with haulDestination named: " + settings.owner.ToString());
                ks.srt = tmp.srt;
                ks.ssl = tmp.ssl;
                State.Set(settings.owner.ToString(), ks);
            }
        }