public static bool TryFindBestBetterStorageFor(Thing t, Pawn carrier, Map map, StoragePriority currentPriority, Faction faction, out IntVec3 foundCell, out IHaulDestination haulDestination, bool needAccurateResult = true) { IntVec3 invalid = IntVec3.Invalid; StoragePriority storagePriority = StoragePriority.Unstored; if (StoreUtility.TryFindBestBetterStoreCellFor(t, carrier, map, currentPriority, faction, out invalid, needAccurateResult)) { storagePriority = invalid.GetSlotGroup(map).Settings.Priority; } IHaulDestination haulDestination2; if (!StoreUtility.TryFindBestBetterNonSlotGroupStorageFor(t, carrier, map, currentPriority, faction, out haulDestination2)) { haulDestination2 = null; } if (storagePriority == StoragePriority.Unstored && haulDestination2 == null) { foundCell = IntVec3.Invalid; haulDestination = null; return(false); } if (haulDestination2 != null && (storagePriority == StoragePriority.Unstored || haulDestination2.GetStoreSettings().Priority > storagePriority)) { foundCell = IntVec3.Invalid; haulDestination = haulDestination2; return(true); } foundCell = invalid; haulDestination = invalid.GetSlotGroup(map).parent; return(true); }
public static void FilledEnough(ref bool __result, IntVec3 c, Map map, Thing thing) { // if base implementation waves of, then don't need to care if (__result) { float num = 100f; bool flag = c.GetSlotGroup(map) != null && c.GetSlotGroup(map).Settings != null; if (flag) { num = StorageSettings_Mapping.Get(c.GetSlotGroup(map).Settings).FillPercent; } __result &= !map.thingGrid.ThingsListAt(c).Any(t => t.def.EverStorable(false) && t.stackCount >= thing.def.stackLimit * (num / 100f)); } }
// credit to bananasss00 for this function public static bool TryGetKanbanSettings(this IntVec3 cell, Map map, out KanbanSettings ks, out SlotGroup slotGroup) { ks = new KanbanSettings(); slotGroup = null; if (map == null) { return(false); } slotGroup = cell.GetSlotGroup(map); if (slotGroup?.Settings == null) { return(false); } // grab latest configs for this stockpile from our state manager ks = State.Get(slotGroup.Settings.owner.ToString()); // skip all this stuff now if stockpile is not configured to use at least one feature if (ks.srt == 100 && ks.ssl == 0) { return(false); } return(true); }
static bool Prefix(ref float __result, IntVec3 c, Map map, List <Thing> countedThings = null) { SlotGroup slotgroup = c.GetSlotGroup(map); if (slotgroup == null) { return(true); } if (!(slotgroup.parent is Building_Storage)) { return(true); } if (!slotgroup.parent.IgnoreStoredThingsBeauty) { return(true); } Building_Storage storage = slotgroup.parent as Building_Storage; if (countedThings != null) { // Ignoring all the other things here because that's best: // What if a pretty rug were here, but also a shelf? Suddenly, // no beauty from the rug, but also no counting its beauty // elsewhere in the room! if (countedThings.Contains(storage)) { __result = map.terrainGrid.TerrainAt(c).GetStatValueAbstract(StatDefOf.Beauty, null); return(false); } countedThings.Add(storage); } __result = storage.GetStatValue(StatDefOf.Beauty, true) + map.terrainGrid.TerrainAt(c).GetStatValueAbstract(StatDefOf.Beauty, null); return(false); }
public override string GetReport() { ThingWithComps cart = TargetThingA as ThingWithComps; IntVec3 destLoc = new IntVec3(-1000, -1000, -1000); string destName = null; SlotGroup destGroup = null; if (pawn.jobs.curJob.targetB != null) { destLoc = pawn.jobs.curJob.targetB.Cell; destGroup = destLoc.GetSlotGroup(); } if (destGroup != null) { destName = destGroup.parent.SlotYielderLabel(); } string repString; if (destName != null) { repString = "ReportDismountingOn".Translate(cart.LabelCap, destName); } else { repString = "ReportDismounting".Translate(cart.LabelCap); } return(repString); }
public override string GetReport() { IntVec3 cell = job.targetB.Cell; Thing thing = null; if (pawn.CurJob == job && pawn.carryTracker.CarriedThing != null) { thing = pawn.carryTracker.CarriedThing; } else if (base.TargetThingA != null && base.TargetThingA.Spawned) { thing = base.TargetThingA; } if (thing == null) { return("ReportHaulingUnknown".Translate()); } string text = null; SlotGroup slotGroup = cell.GetSlotGroup(base.Map); if (slotGroup != null) { text = slotGroup.parent.SlotYielderLabel(); } if (text != null) { return("ReportHaulingTo".Translate(thing.Label, text.Named("DESTINATION"), thing.Named("THING"))); } return("ReportHauling".Translate(thing.Label, thing)); }
// This gets checked a lot. Sometimes the test is done in-place (if will // need to use the slotGroup later, for example), but when using Harmony // Transpiler, tests are easier via function call // Most of the bulk here is debugging stuff public static bool CanStoreMoreThanOneThingAt(Map map, IntVec3 loc) { var slotGroup = loc.GetSlotGroup(map); if (slotGroup == null || !(slotGroup?.parent is ThingWithComps) || (slotGroup.parent as ThingWithComps).TryGetComp<CompDeepStorage>() == null) { return false; #pragma warning disable CS0162 // Unreachable code detected Log.Warning("CanStoreMoreThanOneThingAt: " + loc + "? false"); return false; if (slotGroup == null) Log.Warning(" null slotGroup"); else if (slotGroup.parent == null) Log.Warning(" null slotGroup.parent"); else if (!(slotGroup.parent is ThingWithComps)) Log.Warning(" slotGroup.parent is not ThingWithComps"); else Log.Warning(" no CompDeepStorage"); Log.Warning("Just for the record, " + (Scribe.mode == LoadSaveMode.LoadingVars) + (Scribe.mode == LoadSaveMode.PostLoadInit) + Scribe.mode); var l = map.thingGrid.ThingsListAt(loc); foreach (var t in l) Log.Error("Did find a " + t + " here at " + loc); return false; } // Log.Warning("CanStoreMoreThanOneThingAt: " + loc.ToString() + "? true"); return true; Log.Warning("CanStoreMoreThanOneThingAt: " + loc + "? true!"); var lx = map.thingGrid.ThingsListAt(loc); foreach (var t in lx) Log.Error("Did find a " + t + " here at " + loc); return true; #pragma warning restore CS0162 // Unreachable code detected }
public static bool IsValidStorageFor(this IntVec3 c, Map map, Thing storable) { if (!StoreUtility.NoStorageBlockersIn(c, map, storable)) { return(false); } SlotGroup slotGroup = c.GetSlotGroup(map); return(slotGroup != null && slotGroup.parent.Accepts(storable)); }
// duplicated to make changes public bool IsValidStorageFor(IntVec3 c, Thing storable) { // call duplicated to make changes if (!WorkGiver_HaulGeneral.NoStorageBlockersIn(c, storable)) { return(false); } SlotGroup slotGroup = c.GetSlotGroup(); return(slotGroup != null && slotGroup.Settings.AllowedToAccept(storable)); }
public static List <Thing> ThingListToDisplay(Map map, IntVec3 loc) { CompDeepStorage cds; var slotGroup = loc.GetSlotGroup(map); if (!(slotGroup?.parent is ThingWithComps) || (cds = (slotGroup.parent as ThingWithComps).TryGetComp <CompDeepStorage>()) == null || cds.showContents) { return(map.thingGrid.ThingsListAt(loc)); } // only return non-storable things to be drawn: return(map.thingGrid.ThingsListAt(loc).FindAll(t => !t.def.EverStorable(false))); }
public static bool Postfix(bool result, IntVec3 c, Map map, Thing thing) { if (result == false) { return(result); } var slotgroup = c.GetSlotGroup(map); if (slotgroup == null) { return(result); } var setting = slotgroup.Settings; var limit = setting.GetStacklimit(); var refillpercent = setting.GetRefillPercent(); if (setting.IsRefillingDisabled() || limit == 0) { return(false); } if (limit < 0) { if (refillpercent == 100) { return(result); } limit = thing.def.stackLimit; } limit *= refillpercent; foreach (var thing2 in map.thingGrid.ThingsListAt(c)) { if (thing2.def.EverStorable(false)) { if (!thing2.CanStackWith(thing)) { return(false); } if (thing2.stackCount * 100 >= limit) { return(false); } } if (thing2.def.entityDefToBuild != null && thing2.def.entityDefToBuild.passability != Traversability.Standable) { return(false); } if (thing2.def.surfaceType == SurfaceType.None && thing2.def.passability != Traversability.Standable) { return(false); } } return(true); }
public static bool IsValidStorageFor(this IntVec3 c, Map map, Thing storable) { if (!NoStorageBlockersIn(c, map, storable)) { return(false); } SlotGroup slotGroup = c.GetSlotGroup(map); if (slotGroup == null || !slotGroup.parent.Accepts(storable)) { return(false); } return(true); }
public static bool IsValidStorageFor(this IntVec3 c, Map map, Thing storable) { if (!StoreUtility.NoStorageBlockersIn(c, map, storable)) { return(false); } SlotGroup slotGroup = c.GetSlotGroup(map); if (slotGroup != null && slotGroup.Settings.AllowedToAccept(storable)) { return(true); } return(false); }
public static bool TryGetHaulingDestination(this Job job, out Map map, out IntVec3 dest, out SlotGroup slotGroup) { map = null; slotGroup = null; dest = new IntVec3(); // Could print warnings here, hopefully these don't fail silently if (job == null) { return(false); } if (job.def == JobDefOf.HaulToContainer) { Thing container = job.targetB.Thing; if (container == null) { return(false); } map = container.Map; dest = container.Position; } else { // case JobDefOf.HaulToCell dest = job.targetB.Cell; map = job.targetA.Thing.Map ?? job.targetA.Thing.MapHeld; } if (map == null) { return(false); } if (dest == null) { return(false); } slotGroup = dest.GetSlotGroup(map); if (slotGroup == null) { return(false); } return(true); }
public static void ReplimatHopperFilledEnough(ref bool __result, IntVec3 c, Map map, Thing thing) { if (__result) { if (c.GetSlotGroup(map).parent.GetType().ToString() == "Replimat.Building_ReplimatHopper") { // Apply the minimum Hopper refilling threshold only to Replimat Hoppers __result &= !map.thingGrid.ThingsListAt(c).Any(t => t.def.EverStorable(false) && t.stackCount >= thing.def.stackLimit * Settings.HopperRefillThresholdPercent); } else { // Ignore the threshold for all other storage buildings and stockpiles // This ensures compatibility with other mods that control stack refilling thresholds on a more general level (e.g. Hauling Hysteresis, Satisfied Storage) __result = __result; } } }
public override string GetReport() { IntVec3 cell = this.job.targetB.Cell; Thing thing = null; if (this.pawn.CurJob == this.job && this.pawn.carryTracker.CarriedThing != null) { thing = this.pawn.carryTracker.CarriedThing; } else if (base.TargetThingA != null && base.TargetThingA.Spawned) { thing = base.TargetThingA; } string result; if (thing == null) { result = "ReportHaulingUnknown".Translate(); } else { string text = null; SlotGroup slotGroup = cell.GetSlotGroup(base.Map); if (slotGroup != null) { text = slotGroup.parent.SlotYielderLabel(); } if (text != null) { result = "ReportHaulingTo".Translate(new object[] { thing.Label, text }); } else { result = "ReportHauling".Translate(new object[] { thing.Label }); } } return(result); }
} // end Transpiler // TODO: move this logic to DeepStorage.cs public static Thing NullOrLastThingAt(Map map, IntVec3 c, ThingDef def) { var cds = (c.GetSlotGroup(map).parent as ThingWithComps).GetComp <CompDeepStorage>(); var l = map.thingGrid.ThingsListAtFast(c); // we know it's a slotgroup, so it's valid :p var freeSlots = cds.maxNumberStacks; Utils.Err(HaulToCellStorageJob, " testing for def " + def + " at " + c + "; " + freeSlots + " slots."); Thing lastThing = null; for (var i = 0; i < l.Count; i++) { if (!l[i].def.EverStorable(false)) { continue; } freeSlots--; Utils.Mess(HaulToCellStorageJob, " Checking item " + l[i] + "; now have " + freeSlots + " left."); if (!(l[i].def == def)) { continue; // possible problem if defs are same but cannot stack? } if (lastThing == null) { lastThing = l[i]; } else { if (l[i].stackCount <= lastThing.stackCount) { lastThing = l[i]; } } } if (freeSlots > 0) { Utils.Err(HaulToCellStorageJob, " Final count of free slots: " + freeSlots); return(null); } Utils.Warn(HaulToCellStorageJob, " Final item count: " + (lastThing == null ? "NULL" : lastThing.stackCount.ToString())); return(lastThing); // if this is also null, we have a problem :p }
// This gets checked a lot. Sometimes the test is done in-place (if will // need to use the slotGroup later, for example), but when using Harmony // Transpiler, tests are easier via function call // Most of the bulk here is debugging stuff public static bool CanStoreMoreThanOneThingAt(Map map, IntVec3 loc, Thing thing) { if (!GetDeepStorageOnCell(loc, map, out CompDeepStorage comp)) { return false; #pragma warning disable CS0162 // Unreachable code detected SlotGroup slotGroup = loc.GetSlotGroup(map); Log.Warning("CanStoreMoreThanOneThingAt: " + loc + "? false"); return false; if (slotGroup == null) Log.Warning(" null slotGroup"); else if (slotGroup.parent == null) Log.Warning(" null slotGroup.parent"); else if (!(slotGroup.parent is ThingWithComps)) Log.Warning(" slotGroup.parent is not ThingWithComps"); else Log.Warning(" no CompDeepStorage"); Log.Warning("Just for the record, " + (Scribe.mode == LoadSaveMode.LoadingVars) + (Scribe.mode == LoadSaveMode.PostLoadInit) + Scribe.mode); List<Thing> l = map.thingGrid.ThingsListAt(loc); foreach (Thing t in l) { Log.Error("Did find a " + t.ToString() + " here at " + loc.ToString()); } return false; } // Log.Warning("CanStoreMoreThanOneThingAt: " + loc.ToString() + "? true"); if (comp is CompCachedDeepStorage compCached) { bool result = compCached.StorageSettings.AllowedToAccept(thing) && compCached.StackableAt(thing, loc, map); return result; } return true; Log.Warning("CanStoreMoreThanOneThingAt: " + loc.ToString() + "? true!"); List<Thing> lx = map.thingGrid.ThingsListAt(loc); foreach (Thing t in lx) { Log.Error("Did find a " + t.ToString() + " here at " + loc.ToString()); } return true; #pragma warning restore CS0162 // Unreachable code detected }
public override string GetReport() { Thing hauledThing = null; hauledThing = this.TargetThingA; IntVec3 destLoc = IntVec3.Invalid; string destName = null; SlotGroup destGroup = null; if (this.pawn.jobs.curJob.targetB != null) { destLoc = this.pawn.jobs.curJob.targetB.Cell; destGroup = destLoc.GetSlotGroup(Map); } this.FailOn(() => !this.pawn.CanReserveAndReach(this.TargetThingA, PathEndMode.ClosestTouch, Danger.Some)); if (destGroup != null) { destName = destGroup.parent.SlotYielderLabel(); } string repString; if (destName != null && hauledThing != null) { repString = "ReportHaulingTo".Translate(hauledThing.LabelCap, destName); } else if (hauledThing != null) { repString = "ReportHauling".Translate(hauledThing.LabelCap); } else { repString = "ReportHauling".Translate(); } return(repString); }
public override string GetReport() { IntVec3 cell = base.job.targetB.Cell; Thing thing = null; thing = ((base.pawn.CurJob != base.job || base.pawn.carryTracker.CarriedThing == null) ? base.TargetThingA : base.pawn.carryTracker.CarriedThing); if (thing == null) { return("ReportHaulingUnknown".Translate()); } string text = null; SlotGroup slotGroup = cell.GetSlotGroup(base.Map); if (slotGroup != null) { text = slotGroup.parent.SlotYielderLabel(); } if (text != null) { return("ReportHaulingTo".Translate(thing.LabelCap, text)); } return("ReportHauling".Translate(thing.LabelCap)); }
public override string GetReport() { Thing hauledThing = null; hauledThing = TargetThingA; if (TargetThingA == null) //Haul Cart { hauledThing = CurJob.targetC.Thing; } IntVec3 destLoc = IntVec3.Invalid; string destName = null; SlotGroup destGroup = null; if (pawn.jobs.curJob.targetB != null) { destLoc = pawn.jobs.curJob.targetB.Cell; destGroup = destLoc.GetSlotGroup(); } if (destGroup != null) { destName = destGroup.parent.SlotYielderLabel(); } string repString; if (destName != null) { repString = "ReportHaulingTo".Translate(hauledThing.LabelCap, destName); } else { repString = "ReportHauling".Translate(hauledThing.LabelCap); } return(repString); }
public override string GetReport() { Thing hauledThing = TargetThingA; if (TargetThingA == null) //Haul Cart { hauledThing = CurJob.targetC.Thing; } this.FailOn(() => !pawn.CanReach(hauledThing, PathEndMode.ClosestTouch, Danger.Some)); IntVec3 destLoc = IntVec3.Invalid; string destName = null; SlotGroup destGroup = null; if (pawn.jobs.curJob.targetB != null) { destLoc = pawn.jobs.curJob.targetB.Cell; destGroup = destLoc.GetSlotGroup(); } if (destGroup != null) { destName = destGroup.parent.SlotYielderLabel(); } string repString; if (destName != null) { repString = "ReportHaulingTo".Translate(hauledThing.LabelCap, destName); } else { repString = "ReportHauling".Translate(hauledThing.LabelCap); } return(repString); }
// Original LWM DeepStorage Right Click // We have to run as Prefix, because we need to intercept the incoming List. public static bool OldContextMenu(Vector3 clickPosition, IntVec3 c, Pawn pawn, List<FloatMenuOption> opts) { if (failsafe++ > 500) runningPatchLogic = false; if (runningPatchLogic) return true; // Only give nice tidy menu if items are actually in Deep Storage: otherwise, they // are a jumbled mess on the floor, and pawns can only interact with what's on // top until they've cleaned up the mess. // I *could* do better and throw away all items below, but whatev's this is good enuf. c = IntVec3.FromVector3(clickPosition); if ((c.GetSlotGroup(pawn.Map)?.parent as ThingWithComps)?.AllComps .FirstOrDefault(x => x is IHoldMultipleThings.IHoldMultipleThings) == null) { Utils.Warn(RightClickMenu, "Location " + c + " is not in any DSU; continuing."); return true; // out of luck, so sorry! // Note: also need to handle this case in Postfix! } failsafe = 0; Utils.Err(RightClickMenu, "Testing Location " + c); runningPatchLogic = true; // TODO: get default set of menus and tidy them away somehow? This seems to be unnecessary so far. // ThingsListAt: var workingThingList = c.GetThingList(pawn.Map); var origThingList = new List<Thing>(workingThingList); workingThingList.Clear(); // ...other ...things. var origPositions = new Dictionary<Thing, IntVec3>(); var TPeverything = new TargetingParameters { canTargetBuildings = false, canTargetItems = false, canTargetFires = true, canTargetPawns = true, canTargetSelf = true }; foreach (var localTargetInfo in GenUI.TargetsAt_NewTemp(clickPosition, TPeverything)) { if (localTargetInfo.Thing == null) { Log.Warning("LWM.DeepStorage: got null target but should only have things?"); continue; } origPositions.Add(localTargetInfo.Thing, localTargetInfo.Thing.Position); Utils.Warn(RightClickMenu, "Adding position information for LocalTargetInfo " + localTargetInfo.Thing); SetPosition(localTargetInfo.Thing, IntVec3.Invalid); } var origParams = new object[] {clickPosition, pawn, opts}; foreach (var k in origPositions) { SetPosition(k.Key, k.Value); Utils.Mess(RightClickMenu, " Doing Menu for Target " + k.Key); AHlO.Invoke(null, origParams); //showOpts(opts); SetPosition(k.Key, IntVec3.Invalid); } foreach (var t in origThingList) { workingThingList.Add(t); Utils.Mess(RightClickMenu, " Doing Menu for Item " + t); AHlO.Invoke(null, origParams); //showOpts(opts); workingThingList.Remove(t); } workingThingList.Clear(); workingThingList.AddRange(origThingList); foreach (var t in origPositions) SetPosition(t.Key, t.Value); runningPatchLogic = false; realList.Clear(); foreach (var m in opts) realList.Add(m); // got to store it in case anything adjusts it in a different Postfix return false; }
private static bool HaulablePlaceValidator(Thing haulable, Pawn worker, IntVec3 c, out string debugText) { if (!worker.CanReserveAndReach(c, PathEndMode.OnCell, worker.NormalMaxDanger())) { debugText = "Could not reserve or reach"; return(false); } if (GenPlace.HaulPlaceBlockerIn(haulable, c, worker.Map, true) != null) { debugText = "Place was blocked"; return(false); } var thisIsAPile = c.GetSlotGroup(worker.Map); if (thisIsAPile != null) { if (!thisIsAPile.Settings.AllowedToAccept(haulable)) { debugText = "Stockpile does not accept"; return(false); } } if (!c.Standable(worker.Map)) { debugText = "Cell not standable"; return(false); } if (c == haulable.Position && haulable.Spawned) { debugText = "Current position of thing to be hauled"; return(false); } if (c.ContainsStaticFire(worker.Map)) { debugText = "Cell has fire"; return(false); } if (haulable != null && haulable.def.BlockPlanting) { Zone zone = worker.Map.zoneManager.ZoneAt(c); if (zone is Zone_Growing) { debugText = "Growing zone here"; return(false); } } if (haulable.def.passability != Traversability.Standable) { for (int i = 0; i < 8; i++) { IntVec3 adjCell = c + GenAdj.AdjacentCells[i]; if (!adjCell.InBounds(worker.Map)) { continue; } if (worker.Map.designationManager.DesignationAt(adjCell, DesignationDefOf.Mine) != null) { debugText = "Mining designated nearby"; return(false); } } } bool validPositionExists = false; var crossGrid = GenAdj.CardinalDirectionsAndInside; for (int a = 0; a < crossGrid.CountAllowNull(); a++) { IntVec3 adjCell = c + crossGrid[a]; if (!adjCell.InBounds(worker.Map)) { continue; } Building restrictedBuildingAdj = adjCell.GetEdifice(worker.Map); if (restrictedBuildingAdj != null) { if (restrictedBuildingAdj is Building_Door) { break; } if (restrictedBuildingAdj is Building_WorkTable) { thisIsAPile = adjCell.GetSlotGroup(worker.Map); if (thisIsAPile != null) { if (thisIsAPile.Settings.AllowedToAccept(haulable)) { validPositionExists = true; } } } } else { validPositionExists = true; } } if (!validPositionExists) { debugText = "No valid position could be found."; return(false); } Building edifice = c.GetEdifice(worker.Map); if (edifice != null) { if (edifice is Building_Trap) { debugText = "It's a trap."; return(false); } if (edifice is Building_WorkTable) { debugText = "Worktable here."; return(false); } } debugText = "OK"; return(true); }
// allows to place items to container stacks public static bool TryPlaceDirect(Thing thing, IntVec3 loc, out Thing resultingThing, Action<Thing, int> placedAction = null) { #region CONTAINER CASE var container = Find.ThingGrid.ThingsListAt(loc).Find(building => building is Container && building.Spawned) as Container; if (container != null) { // stackables (like resources) if (thing.def.stackLimit > 1) { foreach (var item in container.StoredItems) { // if item can stack with another // Required, because thing reference is changed to the absorber, if absorbed if (item.TryAbsorbStack(thing, true)) { // former item gets absorbed and destroyed resultingThing = item; // hide texture and label of the stored item container.HideItem(resultingThing); return true; } } } resultingThing = GenSpawn.Spawn(thing, loc); // hide texture and label of the stored item container.HideItem(resultingThing); return true; } #endregion #region VANILLA CASE // used to keep original thing reference var initialThing = thing; var flag = false; if (thing.stackCount > thing.def.stackLimit) { thing = thing.SplitOff(thing.def.stackLimit); flag = true; } if (thing.def.stackLimit > 1) { var thingList = loc.GetThingList(); var i = 0; while (i < thingList.Count) { var thing3 = thingList[i]; if (!thing3.CanStackWith(thing)) { i++; } else { var stackCount = thing.stackCount; if (thing3.TryAbsorbStack(thing, true)) { resultingThing = thing3; placedAction?.Invoke(thing3, stackCount); return !flag; } resultingThing = null; if (placedAction != null && stackCount != thing.stackCount) { placedAction(thing3, stackCount - thing.stackCount); } if (initialThing != thing) { initialThing.TryAbsorbStack(thing, false); } return false; } } } resultingThing = GenSpawn.Spawn(thing, loc); placedAction?.Invoke(thing, thing.stackCount); var slotGroup = loc.GetSlotGroup(); slotGroup?.parent?.Notify_ReceivedThing(resultingThing); return !flag; #endregion }
protected static bool Prefix(IntVec3 c, Map map, Thing thing, ref bool __result) { Utils.Err(NoStorageBlockerseIn, "Looking for blockers for " + thing + " at " + c); // Check if storage location is in an uber-storage building: SlotGroup slotGroup = c.GetSlotGroup(map); CompDeepStorage cds = null; if (slotGroup == null || !(slotGroup?.parent is ThingWithComps) || (cds = (slotGroup.parent as ThingWithComps).TryGetComp <CompDeepStorage>()) == null) { // Log.Warning(" ...letting vanilla handle it."); return(true); // normal spot, NoStorageBlockersIn() will handle it } //TODO: Make this IHoldMultipleThings // __result = false; // NoStorageBlockersIn returns false if there's a blocker // Default to having a blocker unless EVERYTHING is okay // (We return false from this Patch function to skip original method) __result = cds.StackableAt(thing, c, map); Utils.Warn(NoStorageBlockerseIn, "Final result for " + thing + " at " + c + ": " + __result); return(false); #if false // If there is a maximum size of items that will fit in the unit, quit: if (cds.limitingFactorForItem > 0f) { if (thing.GetStatValue(cds.stat) > cds.limitingFactorForItem) { Utils.Warn(NoStorageBlockerseIn, "Thing has " + cds.stat + " of " + thing.GetStatValue(StatDefOf.Mass) + " but max allowed is " + cds.limitingFactorForItem); return(false); } } // We will usually care how many stacks can fit here: var maxStacks = cds.maxNumberStacks; // If maxTotalMass is set, we will keep track of how much "room" we have as well: float totalAmountHereSoFar = 0f; if (cds.limitingTotalFactorForCell > 0f) { totalAmountHereSoFar = thing.GetStatValue(cds.stat); } var objInStack = 0; List <Thing> list = map.thingGrid.ThingsListAt(c); for (int i = 0; i < list.Count; i++) //loop thru cell's contents { Thing thing2 = list[i]; Utils.Warn(NoStorageBlockerseIn, " ...checking: does " + thing2 + " block?"); if (thing2.def.EverStorable(false)) // an "item" as it were { if (cds.limitingTotalFactorForCell > 0f) { totalAmountHereSoFar += (thing2.GetStatValue(cds.stat) * thing2.stackCount); Utils.Warn(NoStorageBlockerseIn, " added 'mass' " + thing2.GetStatValue(cds.stat) + " to running total " + totalAmountHereSoFar + " / " + cds.limitingTotalFactorForCell + "(" + (objInStack + 1) + " v " + cds.minNumberStacks + ")"); if (totalAmountHereSoFar > cds.limitingTotalFactorForCell && objInStack + 1 >= cds.minNumberStacks) // Must accept minimum (haven't incremented objInStack yet) // but if reached minimum and over capacity, cannot store here { Utils.Warn(NoStorageBlockerseIn, " BLOCKS: Over mass limit (" + cds.limitingTotalFactorForCell + ")"); return(false); } } if (!thing2.CanStackWith(thing)) { objInStack++; } else if (thing2.stackCount >= thing.def.stackLimit) { objInStack++; } else // it can stack and there's room in the stack for more... { // go ahead and get out of here with the good news! __result = true; Utils.Warn(NoStorageBlockerseIn, thing.ToString() + " at " + c.ToString() + ": " + __result); return(false); } if (objInStack >= maxStacks) { return(false); } continue; } if (thing2.def.entityDefToBuild != null && thing2.def.entityDefToBuild.passability != Traversability.Standable) { Utils.Warn(NoStorageBlockerseIn, thing.ToString() + " at " + c.ToString() + ": " + __result); return(false); } if (thing2.def.surfaceType == SurfaceType.None && thing2.def.passability != Traversability.Standable) { Utils.Warn(NoStorageBlockerseIn, thing.ToString() + " at " + c.ToString() + ": " + __result); return(false); } } //You know what I can't get running in Linux? Monodevelop's debugger. //Log.Warning("No storage blockers for "+thing.ToString()+" in "+slotGroup.ToString()); __result = true; // no blockers after all! Utils.Warn(NoStorageBlockerseIn, thing.ToString() + " at " + c.ToString() + ": " + __result); return(false); #endif }
// Always use this if you are placing an item. /// <summary> /// Place a Thing (onto the ground or into a proper receptacle) /// Any time ANY PRF building tries to place an item, this is THE way to do it: /// It checks if the space is clear, it checks for conveyor belts, in checks /// for other storage mods (well, Deep Storage, anyway). It does all the /// things! /// </summary> /// <returns><c>true</c>, if item was placed, <c>false</c> if it could not.</returns> /// <param name="placer">The PRF_Building doing the placing</param> /// <param name="t">Thing to place.</param> /// <param name="cell">Where to place.</param> /// <param name="map">Map.</param> /// <param name="forcePlace">If set to <c>true</c>, forces placing. For when /// you absolutely positively have to put it down.</param> public static bool PRFTryPlaceThing(this IPRF_Building placer, Thing t, IntVec3 cell, Map map, bool forcePlace = false) { if (!cell.InBounds(map)) { Log.Error("PRF Error Attempting to place Thing out of bounds."); return(false); } // Storage: SlotGroup slotGroup = cell.GetSlotGroup(map); if (slotGroup != null) { Debug.Warning(Debug.Flag.PlaceThing, "Placing " + t + " in slotGroup: " + slotGroup.parent + " at " + cell); if (slotGroup.parent is IPRF_Building) { if (placer.PlaceThingNextBuilding((slotGroup.parent as IPRF_Building), t, cell, map)) { Debug.Message(Debug.Flag.PlaceThing, " which is owned by PRF " + slotGroup.parent); return(true); } if (forcePlace) { goto ForcePlace; } return(false); } if (placer.PlaceThingInSlotGroup(t, slotGroup, cell, map)) { return(true); } if (forcePlace) { goto ForcePlace; } return(false); } Debug.Warning(Debug.Flag.PlaceThing, "Place request: " + placer == null ? "NoPlacer" : placer.ToString() + " is trying to place " + t + " at " + cell); // Search through all items in cell: see if any will absorb // our thing. If we find a PRF_Building, stop looking and // try to pass it on. bool cellIsImpassible = false; foreach (Thing otherThing in map.thingGrid.ThingsListAt(cell)) { if (otherThing.TryAbsorbStack(t, true)) { Debug.Message(Debug.Flag.PlaceThing, " absorbed by " + otherThing); placer.EffectOnPlaceThing(otherThing); // I think? return(true); } if (otherThing.def.passability == Traversability.Impassable) { cellIsImpassible = true; } if (otherThing is IPRF_Building) { if (placer.PlaceThingNextBuilding((otherThing as IPRF_Building), t, cell, map)) { Debug.Message(Debug.Flag.PlaceThing, placer + " gave " + t + " to " + otherThing); placer.EffectOnPlaceThing(t); return(true); } // Continue loop - may be more than 1 PRF_Building here } } // There is no IPRF Building to take this from us if (cellIsImpassible) { Debug.Message(Debug.Flag.PlaceThing, " cell is impassable."); if (forcePlace) { goto ForcePlace; } return(false); } // The cell is not impassible! Try to place: if (CallNoStorageBlockersIn(cell, map, t)) { Debug.Message(Debug.Flag.PlaceThing, " placing directly."); bool wasSpawned = t.Spawned; if (wasSpawned) { t.DeSpawn(); } if (!GenPlace.TryPlaceThing(t, cell, map, ThingPlaceMode.Direct)) { // some of the stack Debug.Message(Debug.Flag.PlaceThing, " some absorbed, still have " + t.stackCount + " left"); // I think this is safe, as it shoudl still have its position? if (wasSpawned) { GenPlace.TryPlaceThing(t, t.Position, map, ThingPlaceMode.Near); } placer.EffectOnPlaceThing(t); if (forcePlace) { goto ForcePlace; } return(false); } placer.EffectOnPlaceThing(t); if (placer.ForbidOnPlacing(t)) { t.SetForbidden(true, false); } return(true); } if (!forcePlace) { return(false); } ForcePlace: Debug.Warning(Debug.Flag.PlaceThing, " Placement is forced!"); if (t.Spawned) { t.DeSpawn(); } GenPlace.TryPlaceThing(t, cell, map, ThingPlaceMode.Near); placer.EffectOnPlaceThing(t); if (placer.ForbidOnPlacing(t)) { t.SetForbidden(true, false); } return(true); }
// We have to run as Prefix, because we need to intercept the incoming List. public static bool Prefix(Vector3 clickPosition, IntVec3 c, Pawn pawn, List <FloatMenuOption> opts, bool runningAJGWO, bool drafted /*only if runningAJGWO*/) { Utils.Mess(RightClickMenu, "" + (runningAJGWO?"AddJobGiverWorkOrders":"AddHumanlikeOrders") + " called. Currently " + (runningPatchLogic?"":" not ") + "running special Patch Logic"); if (runningAJGWO) { return(true); } if (failsafe++ > 500) { runningPatchLogic = false; } if (runningPatchLogic) { return(true); } // Only give nice tidy menu if items are actually in Deep Storage: otherwise, they // are a jumbled mess on the floor, and pawns can only interact with what's on // top until they've cleaned up the mess. // I *could* do better and throw away all items below, but whatev's this is good enuf. if (!runningAJGWO) { clickPos = clickPosition; c = IntVec3.FromVector3(clickPos); } if (((c.GetSlotGroup(pawn.Map)?.parent) as ThingWithComps)?.AllComps .FirstOrDefault(x => x is IHoldMultipleThings.IHoldMultipleThings) == null) { Utils.Warn(RightClickMenu, "Location " + c + " is not in any DSU; continuing."); return(true); // out of luck, so sorry! // Note: also need to handle this case in Postfix! } failsafe = 0; Utils.Err(RightClickMenu, "Testing Location " + c); runningPatchLogic = true; // TODO: get default set of menus and tidy them away somehow? This seems to be unnecessary so far. /************* Move all things away **************/ // ThingsListAt: List <Thing> workingThingList = c.GetThingList(pawn.Map); List <Thing> origThingList = new List <Thing>(workingThingList); workingThingList.Clear(); // ...other ...things. Dictionary <Thing, IntVec3> origPositions = new Dictionary <Thing, IntVec3>(); TargetingParameters TPeverything = new TargetingParameters(); TPeverything.canTargetBuildings = false; //already got it TPeverything.canTargetItems = false; //already got it TPeverything.canTargetFires = true; //?? TPeverything.canTargetPawns = true; TPeverything.canTargetSelf = true; foreach (var localTargetInfo in GenUI.TargetsAt(clickPos, TPeverything)) { if (localTargetInfo.Thing == null) { Log.Warning("LWM.DeepStorage: got null target but should only have things?"); continue; } origPositions.Add(localTargetInfo.Thing, localTargetInfo.Thing.Position); Utils.Warn(RightClickMenu, "Adding position information for LocalTargetInfo " + localTargetInfo.Thing); SetPosition(localTargetInfo.Thing, IntVec3.Invalid); } /***************** Do magic ****************/ object[] origParams; if (runningAJGWO) { origParams = new object[] { c, pawn, opts, drafted }; } else { origParams = new object[] { clickPos, pawn, opts }; } foreach (var k in origPositions) { SetPosition(k.Key, k.Value); Utils.Mess(RightClickMenu, " Doing Menu for Target " + k.Key); if (runningAJGWO) { AJGWO.Invoke(null, origParams); } else { AHlO.Invoke(null, origParams); } //showOpts(opts); SetPosition(k.Key, IntVec3.Invalid); } foreach (var t in origThingList) { workingThingList.Add(t); Utils.Mess(RightClickMenu, " Doing Menu for Item " + t); AHlO.Invoke(null, origParams); //showOpts(opts); workingThingList.Remove(t); } /************ Cleanup: Put everything back! ***********/ workingThingList.Clear(); foreach (var t in origThingList) { workingThingList.Add(t); } foreach (var t in origPositions) { SetPosition(t.Key, t.Value); } runningPatchLogic = false; realList.Clear(); foreach (var m in opts) { realList.Add(m); // got to store it in case anything adjusts it in a different Postfix } return(false); } // end Prefix
public static bool Prefix(Vector3 clickPos, Pawn pawn, List <FloatMenuOption> opts) { if (!Settings.useDeepStorageRightClickLogic) { return(true); } // return Patch_FloatMenuMakerMap.Prefix(clickPos,IntVec3.Invalid,pawn,opts,false,false); // if expicitly told to run vanilla, run vanilla. // (note vanilla may have any number of mods attached) if (runVanillaAHlO) { Utils.Warn(RightClickMenu, "-------Running Vanilla AddHumanlikeOrders" + pawn + "-------"); return(true); } // if not in storage, don't worry about it: IntVec3 clickCell = IntVec3.FromVector3(clickPos); if (!Utils.CanStoreMoreThanOneThingAt(pawn.Map, clickCell)) { Utils.Warn(RightClickMenu, "-----Running Vanilla AddHumanlikeOrders" + pawn + " - not in storage-----"); return(true); } Utils.Warn(RightClickMenu, "-----Running Custom AddHumanlikeOrders for " + pawn + "-----"); // We will fill listOfOptionsWeFilled - this lets us properly handle other mods' // right click modifications (we will use listOfOptionsWeFilled in our postfix // to provide the actual result). In the meantime, we use listOfOptions as our // list - we will move everything over at the end. // Why? Because we have to move over our final result in the Postfix List <FloatMenuOption> listOfOptions = new List <FloatMenuOption>(); // Prepare Faerie to accept pawns/etc. thingsInRealmOfFaerie.Clear(); // get menu with no items. This will include commands such as "clean dirt." I think. // and dealing with fire. I think. And pawns. Definitely dealing with pawns. /*var index = pawn.Map.cellIndices.CellToIndex(cpos.ToIntVec3()); * var listArray = (List<Thing>[]) thingListTG.GetValue(pawn.Map.thingGrid); * var origList = listArray[index]; * * listArray[index] = new List<Thing> {thingList[i]}; * rows[i] = new DSGUI_ListItem(pawn, thingList[i], cpos, boxHeight); * listArray[index] = origList; */ // Clear thingsList if (mapOfCachedThingGrid != pawn.Map) { mapOfCachedThingGrid = pawn.Map; cachedThingGridThingList = (List <Thing>[])thingGridThingList.GetValue(pawn.Map.thingGrid); } var index = pawn.Map.cellIndices.CellToIndex(clickCell); List <Thing> origList = cachedThingGridThingList[index]; cachedThingGridThingList[index] = tmpEmpty; runVanillaAHlO = true; Utils.Mess(RightClickMenu, "Get menu with no items: invoke vanilla:"); // List<Thing> thingList=clickCell.GetThingList(pawn.Map); // List<Thing> tmpList=new List<Thing>(thingList); // thingList.Clear(); AHlO(clickPos, pawn, listOfOptions); /* var origParams=new object[] {clickPos, pawn, listOfOptions}; * AHlO.Invoke(null, origParams);*/ runVanillaAHlO = false; // return things cachedThingGridThingList[index] = origList; Utils.Mess(RightClickMenu, " ->" + String.Join("; ", listOfOptions)); // thingList.AddRange(tmpList); // ReturnThingsFromFaerie(); // get sorted list of all things in dsu List <Thing> allThings = clickCell.GetSlotGroup(pawn.Map).HeldThings.OrderBy(t => t.LabelCap).ToList(); // Multilpe Options here, depending on size of allThings. // 6 or fewer => should just display each set of options // 7-12 => show entry for each, with options // LOTS => open f***ing window w/ search options, etc. // for each thing(mod label) in storage, add menu entry // ...for now, just print them all: for (int i = 0; i < allThings.Count; i++) { Thing t = allThings[i]; /* int howManyOfThis=1; * while (i<allThings.Count-1 && t.Label==allThings[i+1].Label) { * howManyOfThis++; * i++; * } * string label; * if (howManyOfThis==1) * label=t.Label; * else * label=howManyOfThis.ToString()+" x "+t.Label;//TODO: translate * //TODO: what if no option for this item???? */ GeneratePawnOptionsFor(pawn, t, listOfOptions); // listOfOptions.Add(MakeMenuOptionFor(pawn, t, howManyOfThis)); } listOfOptionsWeFilled = listOfOptions; #if DEBUG if (Utils.showDebug[(int)RightClickMenu]) { for (int ijk = 0; ijk < 1000; ijk++) { Log.Message("pausing log..."); } } #endif return(false); //return Patch_FloatMenuMakerMap.Prefix(clickPos,IntVec3.Invalid,pawn,opts,false,false); }
// duplicated to make changes public bool IsValidStorageFor(IntVec3 c, Thing storable) { // call duplicated to make changes if (!WorkGiver_HaulGeneral.NoStorageBlockersIn(c, storable)) { return false; } SlotGroup slotGroup = c.GetSlotGroup(); return slotGroup != null && slotGroup.Settings.AllowedToAccept(storable); }
public static bool TryFindStoreCellNearColonyDesperate(Thing item, Pawn carrier, out IntVec3 storeCell) { if (StoreUtility.TryFindBestBetterStoreCellFor(item, carrier, carrier.Map, StoragePriority.Unstored, carrier.Faction, out storeCell, true)) { return(true); } for (int i = -4; i < 20; i++) { int num = (i >= 0) ? i : Rand.RangeInclusive(0, 4); IntVec3 intVec = carrier.Position + GenRadial.RadialPattern[num]; if (intVec.InBounds(carrier.Map) && carrier.Map.areaManager.Home[intVec] && carrier.CanReach(intVec, PathEndMode.ClosestTouch, Danger.Deadly, false, TraverseMode.ByPawn) && intVec.GetSlotGroup(carrier.Map) == null && StoreUtility.IsGoodStoreCell(intVec, carrier.Map, item, carrier, carrier.Faction)) { storeCell = intVec; return(true); } } if (RCellFinder.TryFindRandomSpotJustOutsideColony(carrier.Position, carrier.Map, carrier, out storeCell, (IntVec3 x) => x.GetSlotGroup(carrier.Map) == null && StoreUtility.IsGoodStoreCell(x, carrier.Map, item, carrier, carrier.Faction))) { return(true); } storeCell = IntVec3.Invalid; return(false); }
// duplicated to make changes (added container support) public bool TryPlaceDirect(Thing thing, IntVec3 loc, out Thing resultingThing) { // boolean success indicator bool flag = false; // container code part List<Thing> list = Find.ThingGrid.ThingsListAt(loc); if (list.Exists(storage => storage.TryGetComp<CompContainer>() != null)) { resultingThing = null; // stackables (like resources) if (thing.def.stackLimit > 1) { foreach (Thing item in list) { if (!item.CanStackWith(thing)) { continue; } // if item can stack with another else { // Required, because thing reference is changed to the absorber, if absorbed Thing t = thing; if (item.TryAbsorbStack(thing, true)) { // Clean up to prevent haulables lists overflow RemoveHaulableFromLists(t); // it gets absorbed and destroyed resultingThing = item; return !flag; } } } } resultingThing = GenSpawn.Spawn(thing, loc); // Clean up to prevent haulables lists overflow RemoveHaulableFromLists(thing); return !flag; } // vanilla code part if (thing.stackCount > thing.def.stackLimit) { thing = thing.SplitOff(thing.def.stackLimit); flag = true; } if (thing.def.stackLimit > 1) { List<Thing> thingList = loc.GetThingList(); int i = 0; while (i < thingList.Count) { Thing thing2 = thingList[i]; if (!thing2.CanStackWith(thing)) { i++; } else { // Required, because thing reference is changed to the absorber, if absorbed Thing t = thing; if (thing2.TryAbsorbStack(thing, true)) { // Clean up to prevent haulables lists overflow RemoveHaulableFromLists(t); resultingThing = thing2; return !flag; } resultingThing = null; return false; } } } resultingThing = GenSpawn.Spawn(thing, loc); // Clean up to prevent haulables lists overflow RemoveHaulableFromLists(thing); SlotGroup slotGroup1 = loc.GetSlotGroup(); if (slotGroup1 != null && slotGroup1.parent != null) { slotGroup1.parent.Notify_ReceivedThing(resultingThing); } return !flag; }
static void Postfix(ref bool __result, Thing __state, IntVec3 loc, Map map, ref Thing resultingThing, Action <Thing, int> placedAction) { Thing thing = __state; // First check if we're dropping off in Deep Storage: SlotGroup slotGroup = loc.GetSlotGroup(map); if (slotGroup == null || !(slotGroup?.parent is ThingWithComps) || ((ThingWithComps)slotGroup.parent).TryGetComp <CompDeepStorage>() == null) { Utils.Warn(TryPlaceDirect, " (placed " + __state + " NOT in Deep Storage: with result " + __result + ")"); return; } if (resultingThing != null) { // Two cases here if resultingThing exists: // 1. There's a new object on the map // (so resultingThing is the original thing, we don't have anyting to put down) // 2. Everything we put down was able to stack with something on the map // (so nothing else to put down!) // (case three - something weird going on possibly involving carrying?) // (all bets are off anyway >_< ) // Probably, the pawn put down what they were carrying, and all is good. Utils.Warn(TryPlaceDirect, " successfully placed " + resultingThing.stackCount + resultingThing.ToString() + " (" + __result + ")"); Utils.TidyStacksOf(resultingThing); return; } // Ok, so we still have something we want to place, and it goes in Deep Storage // (We will still return __result=false if we can't place everything in the desired space.) Utils.Err(TryPlaceDirect, "LWM:TryPlaceDirect tried to place " + thing.ToString() + " in " + slotGroup.ToString()); // Let's see if there's still room in the Deep Storage area the pawn is using: List <Thing> list = map.thingGrid.ThingsListAt(loc); int maxNumberStacks = ((ThingWithComps)slotGroup.parent).GetComp <CompDeepStorage>().maxNumberStacks; // We know there was at least one thing here, and it either doesn't stack with our thing // or its stack is full. // So, we go thru the items that are there int thingsHere = 0; for (int i = 0; i < list.Count; i++) { Thing thing2 = list[i]; if (!thing2.def.EverStorable(false)) { //not an object we count continue; } thingsHere++; Utils.Warn(TryPlaceDirect, " Currently there are " + thingsHere + " things here, max: " + maxNumberStacks); if (thingsHere == 1) { // (should be safe because 1st haulable stack would already have been // tested by default vanilla function) continue; } //unfortunately, we have to duplicate some of the original code: if (!thing2.CanStackWith(thing)) { Utils.Warn(TryPlaceDirect, "...ignoring \"other\" stack " + thing2.ToString()); continue; // am carrying wood, but this is sheep. Or rock, whatever. } // Okay, can stack. if (thing2.stackCount >= thing2.def.stackLimit) { Utils.Warn(TryPlaceDirect, "...ignoring full stack " + thing2.ToString()); continue; // stack is full. } // Put some down in the non-full stack! var origStackCount = thing.stackCount; if (thing2.TryAbsorbStack(thing, true)) { // the "thing2" stack could hold everything we wanted to put down! Utils.Warn(TryPlaceDirect, "... Object " + thing2.ToString() + " absorbed ALL of " + thing.ToString()); resultingThing = thing2; // Okay, I like this notation: only call if non-null: placedAction?.Invoke(thing2, origStackCount); Utils.TidyStacksOf(thing2); __result = true; // Could put down everything! return; } Utils.Warn(TryPlaceDirect, "... Object " + thing2.ToString() + " absorbed SOME of " + thing.ToString()); // Since we tried to put some down in that stack, do we do placedAction? if (placedAction != null && origStackCount != thing.stackCount) { placedAction(thing2, origStackCount - thing.stackCount); } // ...but there's still more to place, so keep going: } // end loop of objects in this location if (thingsHere >= maxNumberStacks) // Ran out of room in the storage object but still want to put stuff down ;_; { Utils.Warn(TryPlaceDirect, "...But ran out of stack space here: all " + maxNumberStacks + " filled"); __result = false; return; // no need to TidyStacks here - they have to be all full } // if we reach here, we found an empty space, // so put something down! Utils.Warn(TryPlaceDirect, "...Found empty stack space at " + loc.ToString() + ". Trying to create from " + thing.ToString()); // In some circumstances (e.g., butchering a large animal), a pawn can be putting // down way more than stackLimit of something. We deal with that scenario here //////////////////////// NOTE: POSSIBLE PROBLEM: //////////////////// // resultingThing is set to only the last thing here - it's possible we miss something // important by placing as much as we can at once: while (thing.stackCount > thing.def.stackLimit) // put down part of a carried load? { Thing thing2 = thing.SplitOff(thing.def.stackLimit); resultingThing = GenSpawn.Spawn(thing2, loc, map); Utils.Warn(TryPlaceDirect, "...put down one full stack (" + resultingThing.ToString() + "); " + thing.stackCount + " left"); placedAction?.Invoke(thing2, thing2.stackCount); thingsHere++; if (thingsHere >= maxNumberStacks) // Oh dear. There was still at least SOME left... { Utils.Warn(TryPlaceDirect, "...But ran out of stack space here: all " + maxNumberStacks + " filled but still have " + thing.stackCount + " left"); __result = false; // couldn't put things down return; // no need to TidyStacks - they all have to be full } } // Either Spawn the thing (or Spawn the final part of the thing): resultingThing = GenSpawn.Spawn(thing, loc, map); __result = true; // nothing unplaced! Utils.Warn(TryPlaceDirect, "...created " + resultingThing.ToString()); placedAction?.Invoke(thing, thing.stackCount); // Okay, I like this notation Utils.TidyStacksOf(resultingThing); return; // with __result=!flag (probably "true"); } // end TryPlaceDirect's Postfix