diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index 6a540b9..cdff7de 100644 Binary files a/1.6/1.6/Assemblies/ArachnaeSwarm.dll and b/1.6/1.6/Assemblies/ArachnaeSwarm.dll differ diff --git a/1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml b/1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml new file mode 100644 index 0000000..c0ce780 --- /dev/null +++ b/1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml @@ -0,0 +1,54 @@ + + + + + ARA_SmartThermostat + + 一个先进的、不耗电的温控设备。它是一个可逆的热泵,能自动制热或制冷以维持设定的目标温度。必须像制冷器一样安装在墙上。 + ArachnaeSwarm.Building_SmartThermostat + + Things/Building/Misc/TempControl/Cooler + Graphic_Multi + + Building + Impassable + true + 1 + true + + 2000 + 100 + 0.7 + + Rare + + 120 + 4 + + Medium + +
  • PlaceWorker_Cooler
  • +
    + true + + true + + + +
  • + + +
  • + + 21 +
  • + +
  • + + Temperature + +
  • AirConditioning
  • + +
    + +
    \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 38d1876..63ae437 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -210,6 +210,17 @@ + + + + + + + + + + + diff --git a/Source/ArachnaeSwarm/Building_SmartThermostat.cs b/Source/ArachnaeSwarm/Building_SmartThermostat.cs new file mode 100644 index 0000000..8641159 --- /dev/null +++ b/Source/ArachnaeSwarm/Building_SmartThermostat.cs @@ -0,0 +1,81 @@ +using UnityEngine; +using Verse; +using RimWorld; + +namespace ArachnaeSwarm +{ + public class Building_SmartThermostat : Building_TempControl + { + private CompFlickable flickableComp; + private const float HeatOutputMultiplier = 1.25f; + private const float EfficiencyLossPerDegreeDifference = 1f / 130f; + + public override void SpawnSetup(Map map, bool respawningAfterLoad) + { + base.SpawnSetup(map, respawningAfterLoad); + flickableComp = GetComp(); + } + + public override void TickRare() + { + // 如果设备被玩家关闭,则不工作 + if (flickableComp != null && !flickableComp.SwitchIsOn) + { + compTempControl.operatingAtHighPower = false; + return; + } + + IntVec3 indoorCell = Position + IntVec3.South.RotatedBy(Rotation); + IntVec3 outdoorCell = Position + IntVec3.North.RotatedBy(Rotation); + + if (indoorCell.Impassable(Map) || outdoorCell.Impassable(Map)) + { + compTempControl.operatingAtHighPower = false; + return; + } + + float indoorTemp = indoorCell.GetTemperature(Map); + float outdoorTemp = outdoorCell.GetTemperature(Map); + float targetTemp = compTempControl.TargetTemperature; + + float tempDifference = indoorTemp - outdoorTemp; + float efficiency = 1f - Mathf.Abs(tempDifference) * EfficiencyLossPerDegreeDifference; + if (efficiency < 0f) + { + efficiency = 0f; + } + + bool operating = false; + + if (indoorTemp > targetTemp) // 制冷 + { + float coolingEnergy = compTempControl.Props.energyPerSecond * efficiency * 4.1666665f; + float tempChange = GenTemperature.ControlTemperatureTempChange(indoorCell, Map, -coolingEnergy, targetTemp); + + if (!Mathf.Approximately(tempChange, 0f)) + { + indoorCell.GetRoom(Map).Temperature += tempChange; + GenTemperature.PushHeat(outdoorCell, Map, coolingEnergy * HeatOutputMultiplier); + operating = true; + } + } + else if (indoorTemp < targetTemp) // 制热 + { + float heatingEnergy = compTempControl.Props.energyPerSecond * efficiency * 4.1666665f; + float tempChange = GenTemperature.ControlTemperatureTempChange(indoorCell, Map, heatingEnergy, targetTemp); + + if (!Mathf.Approximately(tempChange, 0f)) + { + if (outdoorTemp > -100) + { + indoorCell.GetRoom(Map).Temperature += tempChange; + GenTemperature.PushHeat(outdoorCell, Map, -heatingEnergy / HeatOutputMultiplier); + operating = true; + } + } + } + + compTempControl.operatingAtHighPower = operating; + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_HediffDamgeShield/DRMDamageShield.cs b/Source/ArachnaeSwarm/WULA_HediffDamgeShield/DRMDamageShield.cs new file mode 100644 index 0000000..84710b5 --- /dev/null +++ b/Source/ArachnaeSwarm/WULA_HediffDamgeShield/DRMDamageShield.cs @@ -0,0 +1,211 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using Verse; +using Verse.Sound; +using HarmonyLib; // For AccessTools + +namespace ArachnaeSwarm +{ + // 自定义 CompProperties_Shield 变体 + public class DRMCompShieldProp : CompProperties + { + public int startingTicksToReset = 3200; + public float minDrawSize = 1.2f; + public float maxDrawSize = 1.55f; + public float energyLossPerDamage = 0.033f; + public float energyOnReset = 0.2f; + public bool blocksRangedWeapons = true; + + public DRMCompShieldProp() + { + compClass = typeof(DRMDamageShield); + } + } + + public class DRMDamageShield : ThingComp + { + // 从 Hediff_DamageShield 获取层数作为能量 + public float Energy + { + get + { + Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff(); + return hediff?.ShieldCharges ?? 0; + } + set + { + Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff(); + if (hediff != null) + { + hediff.ShieldCharges = (int)value; + } + } + } + + public float MaxEnergy + { + get + { + Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff(); + return hediff?.def.maxSeverity ?? 0; + } + set + { + // MaxEnergy 由 HediffDef 控制,这里不需要设置 + } + } + + public bool IsActive = false; // 控制护盾是否激活,由 Hediff_DamageShield 管理 + + // 复制自 CompShield + protected int ticksToReset = -1; + protected int lastKeepDisplayTick = -9999; + private Vector3 impactAngleVect; + private int lastAbsorbDamageTick = -9999; + + public DRMCompShieldProp Props => (DRMCompShieldProp)props; + + public ShieldState ShieldState + { + get + { + if (PawnOwner == null || !IsActive || Energy <= 0) + { + return ShieldState.Disabled; + } + if (ticksToReset <= 0) + { + return ShieldState.Active; + } + return ShieldState.Resetting; + } + } + + protected Pawn PawnOwner + { + get + { + return parent as Pawn; + } + } + + public override void PostExposeData() + { + base.PostExposeData(); + Scribe_Values.Look(ref ticksToReset, "ticksToReset", -1); + Scribe_Values.Look(ref lastKeepDisplayTick, "lastKeepDisplayTick", 0); + Scribe_Values.Look(ref IsActive, "isActive", false); + } + + public override void CompTick() + { + base.CompTick(); + if (PawnOwner == null || !IsActive) + { + return; + } + + if (ShieldState == ShieldState.Resetting) + { + ticksToReset--; + if (ticksToReset <= 0) + { + Reset(); + } + } + else if (ShieldState == ShieldState.Active) + { + // 护盾能量(层数)通过 Hediff_DamageShield 的 Tick 方法管理,这里不需要额外回复 + // 如果需要自动回复层数,可以在这里实现 + } + } + + public override void PostPreApplyDamage(ref DamageInfo dinfo, out bool absorbed) + { + absorbed = false; + // 获取 Hediff_DamageShield 实例 + Hediff_DamageShield damageShield = PawnOwner?.health?.hediffSet.GetFirstHediff(); + + if (ShieldState != ShieldState.Active || !IsActive || damageShield == null || damageShield.ShieldCharges <= 0) + { + return; + } + + // 我们的护盾阻挡所有伤害类型,但不包含手术 + // 如果伤害类型不被认为是“有益的”(例如,不是手术),则阻挡 + if (!dinfo.Def.consideredHelpful) + { + // 消耗一层护盾 + damageShield.ShieldCharges--; + + // 触发护盾吸收效果 + Notify_DamageAbsorbed(dinfo); + + // 护盾抖动效果 + PawnOwner.Drawer.renderer.wiggler.SetToCustomRotation(Rand.Range(-0.05f, 0.05f)); + // 移除文字提示 + // 移除粒子效果 + + absorbed = true; // 伤害被吸收 + + // 如果护盾层数归零,触发护盾击穿效果 + if (damageShield.ShieldCharges <= 0) + { + Notify_ShieldBreak(); + } + } + } + + public void Notify_DamageAbsorbed(DamageInfo dinfo) + { + // 复制自 CompShield.AbsorbedDamage + SoundDefOf.EnergyShield_AbsorbDamage.PlayOneShot(new TargetInfo(PawnOwner.Position, PawnOwner.Map)); + impactAngleVect = Vector3Utility.HorizontalVectorFromAngle(dinfo.Angle); + // 移除 FleckMaker.Static 和 FleckMaker.ThrowDustPuff + lastAbsorbDamageTick = Find.TickManager.TicksGame; + KeepDisplaying(); + } + + public void Notify_ShieldBreak() + { + // 复制自 CompShield.Break + if (parent.Spawned) + { + float scale = Mathf.Lerp(Props.minDrawSize, Props.maxDrawSize, Energy / MaxEnergy); // 根据当前能量比例调整大小 + EffecterDefOf.Shield_Break.SpawnAttached(parent, parent.MapHeld, scale); + // 移除 FleckMaker.Static 和 FleckMaker.ThrowDustPuff + } + ticksToReset = Props.startingTicksToReset; + // 护盾层数归零将由 Hediff_DamageShield 负责移除 Hediff + } + + private void Reset() + { + // 复制自 CompShield.Reset + if (PawnOwner.Spawned) + { + SoundDefOf.EnergyShield_Reset.PlayOneShot(new TargetInfo(PawnOwner.Position, PawnOwner.Map)); + // 移除 FleckMaker.ThrowLightningGlow + } + ticksToReset = -1; + // 能量恢复由 Hediff_DamageShield 负责,这里不需要设置 Energy + // 这里可以添加逻辑,让 Hediff_DamageShield 恢复层数 + Hediff_DamageShield hediff = PawnOwner?.health?.hediffSet.GetFirstHediff(); + if (hediff != null) + { + hediff.ShieldCharges = (int)hediff.def.initialSeverity; // 重置时恢复到初始层数 + } + } + + public void KeepDisplaying() + { + lastKeepDisplayTick = Find.TickManager.TicksGame; + } + + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_HediffDamgeShield/Hediff_DamageShield.cs b/Source/ArachnaeSwarm/WULA_HediffDamgeShield/Hediff_DamageShield.cs new file mode 100644 index 0000000..1edcd38 --- /dev/null +++ b/Source/ArachnaeSwarm/WULA_HediffDamgeShield/Hediff_DamageShield.cs @@ -0,0 +1,115 @@ +using Verse; +using System; // Add for Activator +using System.Text; +using RimWorld; +using UnityEngine; +using HarmonyLib; // Needed for AccessTools if you use it here directly + +namespace ArachnaeSwarm +{ + public class Hediff_DamageShield : HediffWithComps + { + // 伤害抵挡层数 + public int ShieldCharges + { + get => (int)severityInt; + set => severityInt = value; + } + + // 获取或创建 DRMDamageShield 组件 + public DRMDamageShield ShieldComp + { + get + { + DRMDamageShield comp = pawn.GetComp(); + if (comp == null) + { + comp = (DRMDamageShield)Activator.CreateInstance(typeof(DRMDamageShield)); + comp.parent = pawn; + comp.props = new DRMCompShieldProp(); // 确保有属性,即使是默认的 + pawn.AllComps.Add(comp); + comp.Initialize(comp.props); + } + return comp; + } + } + + + public override string LabelInBrackets + { + get + { + if (ShieldCharges > 0) + { + return "层数: " + ShieldCharges; + } + return null; + } + } + + public override string TipStringExtra + { + get + { + StringBuilder sb = new StringBuilder(); + sb.Append(base.TipStringExtra); + if (ShieldCharges > 0) + { + sb.AppendLine(" - 每层抵挡一次伤害。当前层数: " + ShieldCharges); + } + else + { + sb.AppendLine(" - 没有可用的抵挡层数。"); + } + return sb.ToString(); + } + } + + public override void ExposeData() + { + base.ExposeData(); + // severityInt 会自动保存,所以不需要额外处理 ShieldCharges + } + + public override void PostAdd(DamageInfo? dinfo) + { + base.PostAdd(dinfo); + // 确保 Pawn 拥有 DRMCompShield 组件 + DRMDamageShield comp = ShieldComp; // 访问属性以确保组件被添加 + if (comp != null) + { + comp.IsActive = true; // 激活护盾组件 + // 能量同步将在 Tick() 中完成 + } + } + + public override void PostRemoved() + { + base.PostRemoved(); + // 当 Hediff 被移除时,移除对应的 DRMDamageShield 组件 + DRMDamageShield comp = pawn.GetComp(); + if (comp != null) + { + pawn.AllComps.Remove(comp); + comp.IsActive = false; // 确保禁用 + } + } + + public override void Tick() + { + base.Tick(); + // 如果层数归零,移除 Hediff + if (ShieldCharges <= 0) + { + pawn.health.RemoveHediff(this); + } + // 同步能量到 ShieldComp + DRMDamageShield comp = pawn.GetComp(); // 每次 Tick 获取,确保是最新的 + if (comp != null && comp.IsActive) + { + comp.Energy = ShieldCharges; + comp.MaxEnergy = (int)def.maxSeverity; + } + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompMultiFuelSpawner.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompMultiFuelSpawner.cs new file mode 100644 index 0000000..e64123d --- /dev/null +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompMultiFuelSpawner.cs @@ -0,0 +1,128 @@ +using System.Collections.Generic; +using System.Linq; +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + public class SpawnerProduct + { + public ThingDef thingDef; + public int count = 1; + } + + // --- Properties Class --- + public class CompProperties_MultiFuelSpawner : CompProperties + { + public List products; + public IntRange spawnIntervalRange = new IntRange(100, 100); + public bool spawnForbidden; + public bool inheritFaction; + public bool showMessageIfOwned; + + public CompProperties_MultiFuelSpawner() + { + compClass = typeof(CompMultiFuelSpawner); + } + } + + // --- Component Class --- + public class CompMultiFuelSpawner : ThingComp + { + private int ticksUntilSpawn; + private List fuelComps; + + public CompProperties_MultiFuelSpawner Props => (CompProperties_MultiFuelSpawner)props; + + public override void PostSpawnSetup(bool respawningAfterLoad) + { + base.PostSpawnSetup(respawningAfterLoad); + if (!respawningAfterLoad) + { + ResetCountdown(); + } + fuelComps = parent.GetComps().ToList(); + } + + public override void PostExposeData() + { + base.PostExposeData(); + Scribe_Values.Look(ref ticksUntilSpawn, "ticksUntilSpawn", 0); + } + + public override void CompTick() + { + base.CompTick(); + + if (fuelComps.NullOrEmpty()) return; + + bool allFuelsOk = fuelComps.All(c => c.HasFuel); + + if (allFuelsOk && (parent.GetComp()?.PowerOn ?? true)) + { + ticksUntilSpawn--; + if (ticksUntilSpawn <= 0) + { + foreach (var comp in fuelComps) + { + comp.Notify_UsedThisTick(); + } + + TryDoSpawn(); + ResetCountdown(); + } + } + } + + public void TryDoSpawn() + { + if (Props.products.NullOrEmpty()) return; + + foreach (var product in Props.products) + { + Thing thing = ThingMaker.MakeThing(product.thingDef); + thing.stackCount = product.count; + + if (Props.inheritFaction && thing.Faction != parent.Faction) + { + thing.SetFaction(parent.Faction); + } + + if (GenPlace.TryPlaceThing(thing, parent.Position, parent.Map, ThingPlaceMode.Near, out Thing resultingThing)) + { + if (Props.spawnForbidden) + { + resultingThing.SetForbidden(true); + } + + if (Props.showMessageIfOwned && parent.Faction == Faction.OfPlayer) + { + Messages.Message("MessageCompSpawnerSpawnedItem".Translate(resultingThing.LabelCap), resultingThing, MessageTypeDefOf.PositiveEvent); + } + } + } + } + + private void ResetCountdown() + { + ticksUntilSpawn = Props.spawnIntervalRange.RandomInRange; + } + + public override string CompInspectStringExtra() + { + string text = base.CompInspectStringExtra(); + + if (fuelComps.All(c => c.HasFuel)) + { + if (!text.NullOrEmpty()) + { + text += "\n"; + } + string productsStr = Props.products.Select(p => (string)p.thingDef.LabelCap).ToCommaList(); + text += "NextSpawnedItemIn".Translate(productsStr) + ": " + ticksUntilSpawn.ToStringTicksToPeriod(); + } + + return text; + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableWithKey.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableWithKey.cs new file mode 100644 index 0000000..cd1e432 --- /dev/null +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableWithKey.cs @@ -0,0 +1,24 @@ +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + // 1. New Properties class that adds the save key + public class CompProperties_RefuelableWithKey : CompProperties_Refuelable + { + public string saveKeysPrefix; + + public CompProperties_RefuelableWithKey() + { + compClass = typeof(CompRefuelableWithKey); + } + } + + // 2. New Component class. It's empty for now. + // Its purpose is to be a safe target for our Harmony patch. + public class CompRefuelableWithKey : CompRefuelable + { + // We will override PostExposeData using a Harmony patch + // to avoid re-implementing the entire class. + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Patch_CompRefuelableWithKey.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Patch_CompRefuelableWithKey.cs new file mode 100644 index 0000000..311bcdf --- /dev/null +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Patch_CompRefuelableWithKey.cs @@ -0,0 +1,59 @@ +using System.Reflection; +using HarmonyLib; +using RimWorld; +using Verse; + +namespace ArachnaeSwarm +{ + // We patch the base class method + [HarmonyPatch(typeof(CompRefuelable), "PostExposeData")] + public static class Patch_CompRefuelableWithKey_PostExposeData + { + public static bool Prefix(CompRefuelable __instance) + { + // But we only act if the instance is our custom subclass + if (!(__instance is CompRefuelableWithKey refuelableWithKey)) + { + // If it's not our class, run the original method + return true; + } + + // Get the private fields from the base CompRefuelable class using reflection + FieldInfo fuelField = AccessTools.Field(typeof(CompRefuelable), "fuel"); + FieldInfo configuredTargetFuelLevelField = AccessTools.Field(typeof(CompRefuelable), "configuredTargetFuelLevel"); + FieldInfo allowAutoRefuelField = AccessTools.Field(typeof(CompRefuelable), "allowAutoRefuel"); + + // Get the props from our custom component + var props = (CompProperties_RefuelableWithKey)refuelableWithKey.Props; + string prefix = props.saveKeysPrefix; + + if (prefix.NullOrEmpty()) + { + Log.ErrorOnce($"CompRefuelableWithKey on {refuelableWithKey.parent.def.defName} has a null or empty saveKeysPrefix. Defaulting to standard save.", refuelableWithKey.GetHashCode()); + // If no prefix, let the original method run + return true; + } + + // Get current values from the instance + float fuel = (float)fuelField.GetValue(refuelableWithKey); + float configuredTargetFuelLevel = (float)configuredTargetFuelLevelField.GetValue(refuelableWithKey); + bool allowAutoRefuel = (bool)allowAutoRefuelField.GetValue(refuelableWithKey); + + // Scribe the values with our prefix + Scribe_Values.Look(ref fuel, prefix + "_fuel", 0f); + Scribe_Values.Look(ref configuredTargetFuelLevel, prefix + "_configuredTargetFuelLevel", -1f); + Scribe_Values.Look(ref allowAutoRefuel, prefix + "_allowAutoRefuel", true); + + // Set the new values back to the instance + if (Scribe.mode == LoadSaveMode.LoadingVars) + { + fuelField.SetValue(refuelableWithKey, fuel); + configuredTargetFuelLevelField.SetValue(refuelableWithKey, configuredTargetFuelLevel); + allowAutoRefuelField.SetValue(refuelableWithKey, allowAutoRefuel); + } + + // Prevent the original PostExposeData from running + return false; + } + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Utility.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Utility.cs new file mode 100644 index 0000000..ae08615 --- /dev/null +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Utility.cs @@ -0,0 +1,303 @@ +using System.Collections.Generic; +using System.Linq; +using RimWorld; +using UnityEngine; +using Verse; + +namespace ArachnaeSwarm +{ + public static class Lifespan_Utility + { + public static IEnumerable deathThought = new List + { + ThoughtDefOf.KnowColonistDied, + ThoughtDefOf.PawnWithGoodOpinionDied, + ThoughtDefOf.WitnessedDeathFamily, + ThoughtDefOf.WitnessedDeathAlly, + }; + + public static bool IsDeathThought(this ThoughtDef tDef) + { + return (deathThought.Contains(tDef)); + } + + public static Thing ThingInCaseOfDeath(Pawn p) + { + Thing refThing; + if (p.Dead) + { + if (p.Corpse == null) + return null; + refThing = p.Corpse; + } + else + refThing = p; + + return refThing; + } + + public static void TrySpawnFilth(Thing refT, float filthRadius, ThingDef filthDef) + { + if ( + refT.Map != null + && CellFinder.TryFindRandomReachableNearbyCell( + refT.Position, + refT.Map, + filthRadius, + TraverseParms.For(TraverseMode.NoPassClosedDoors), + x => x.Standable(refT.Map), + x => true, + out IntVec3 result + ) + ) + FilthMaker.TryMakeFilth(result, refT.Map, filthDef); + } + + public static void ThrowCustomSmoke(ThingDef moteDef, Vector3 loc, Map map, float size) + { + if (loc.ShouldSpawnMotesAt(map) && !map.moteCounter.SaturatedLowPriority) + { + MoteThrown obj = (MoteThrown)ThingMaker.MakeThing(moteDef); + obj.Scale = Rand.Range(1.5f, 2.5f) * size; + obj.rotationRate = Rand.Range(-30f, 30f); + obj.exactPosition = loc; + obj.SetVelocity(Rand.Range(30, 40), Rand.Range(0.5f, 0.7f)); + GenSpawn.Spawn(obj, loc.ToIntVec3(), map); + } + } + + public static bool TryDoSpawn( + Pawn pawn, + ThingDef thingDef, + int thingNum, + int spawnMaxAdjacent, + bool tryToUnstack, + bool inheritFaction, + bool spawnForbidden, + bool showMessageIfOwned + ) + { + Thing refThing = ThingInCaseOfDeath(pawn); + IntVec3 spawnPos; + Map map; + if (refThing == null) + return false; + else + { + map = refThing.Map; + spawnPos = refThing.Position; + } + + if (spawnMaxAdjacent >= 0) + { + int num = 0; + for (int i = 0; i < 9; i++) + { + IntVec3 c = spawnPos + GenAdj.AdjacentCellsAndInside[i]; + if (!c.InBounds(map)) + continue; + + List thingList = c.GetThingList(map); + + for (int j = 0; j < thingList.Count; j++) + { + if (thingList[j].def == thingDef) + { + if (tryToUnstack) + continue; + + num += thingList[j].stackCount; + if (num >= spawnMaxAdjacent) + return false; + } + } + } + } + if (TryFindSpawnCell(refThing, thingDef, thingNum, tryToUnstack, out IntVec3 result)) + { + Thing thing = ThingMaker.MakeThing(thingDef); + thing.stackCount = thingNum; + if (thing == null) + Log.Error("Could not spawn anything for " + refThing); + + if (inheritFaction && thing.Faction != refThing.Faction) + thing.SetFaction(refThing.Faction); + + GenPlace.TryPlaceThing( + thing, + result, + map, + ThingPlaceMode.Direct, + out Thing lastResultingThing + ); + if (spawnForbidden) + lastResultingThing.SetForbidden(value: true); + + if (showMessageIfOwned && refThing.Faction == Faction.OfPlayer) + Messages.Message( + "MessageCompSpawnerSpawnedItem".Translate(thingDef.LabelCap), + thing, + MessageTypeDefOf.PositiveEvent + ); + + return true; + } + return false; + } + + public static bool TryFindSpawnCell( + Thing parent, + ThingDef thingToSpawn, + int spawnCount, + bool tryToUnstack, + out IntVec3 result + ) + { + foreach (IntVec3 item in GenAdj.CellsAdjacent8Way(parent).InRandomOrder()) + { + if (item.Walkable(parent.Map)) + { + Building edifice = item.GetEdifice(parent.Map); + if (edifice == null || !thingToSpawn.IsEdifice()) + { + Building_Door building_Door = edifice as Building_Door; + if ( + (building_Door == null || building_Door.FreePassage) + && ( + parent.def.passability == Traversability.Impassable + || GenSight.LineOfSight(parent.Position, item, parent.Map) + ) + ) + { + bool flag = false; + List thingList = item.GetThingList(parent.Map); + + for (int i = 0; i < thingList.Count; i++) + { + Thing thing = thingList[i]; + if ( + thing.def.category == ThingCategory.Item + && ( + thing.def != thingToSpawn + || thing.stackCount > thingToSpawn.stackLimit - spawnCount + ) + ) + { + flag = true; + break; + } + } + + if (!flag) + { + result = item; + return true; + } + } + } + } + } + result = IntVec3.Invalid; + return false; + } + + public static bool RemoveBadMemoriesOfDeadPawn(Pawn deadPawn, bool myDebug = false) + { + bool didIt = false; + if (deadPawn == null) + { + Log.Warning("removingRelationAndThoughts, null pawn"); + return didIt; + } + string deadName = deadPawn.LabelShortCap; + Log.Warning(">>>>>" + deadName + " dissappeared, the world must not know"); + + foreach ( + Pawn p in Find.CurrentMap.mapPawns.AllPawnsSpawned.Where(pH => + pH != deadPawn + && pH.needs.mood?.thoughts?.memories != null + && pH.needs.mood.thoughts.memories.AnyMemoryConcerns(deadPawn) + ) + ) + { + Log.Warning(p.LabelShortCap + " has memories of " + deadName); + + Log.Warning( + "pre removal mem count: " + p.needs.mood.thoughts.memories.Memories.Count + ); + p.needs.mood.thoughts.memories.Memories.RemoveAll(TM => + TM.otherPawn == deadPawn && TM.MoodOffset() <= 0f + ); + Log.Warning( + "post removal mem count: " + p.needs.mood.thoughts.memories.Memories.Count + ); + } + + return didIt; + } + + public static void removingRelationAndThoughts(Pawn deadPawn, bool myDebug = false) + { + if (deadPawn == null) + { + Log.Warning("removingRelationAndThoughts, null pawn"); + return; + } + string deadName = deadPawn.LabelShortCap; + + Log.Warning(">>>>>" + deadName + " dissappeared, the world must not know"); + foreach ( + Pawn p in Find.CurrentMap.mapPawns.AllPawnsSpawned.Where(pH => + //!pH.AnimalOrWildMan() && + pH != deadPawn + && !pH.GetRelations(deadPawn).EnumerableNullOrEmpty() + ) + ) + { + string pName = p.LabelShortCap; + Log.Warning("Considering :" + pName); + IEnumerable relationT = PawnRelationUtility.GetRelations( + deadPawn, + p + ); + if (relationT.EnumerableNullOrEmpty()) + continue; + + List pThoughts = new List(); + if (p.needs.mood == null || p.needs.mood.thoughts == null) + continue; + //p.needs.mood.thoughts.memories.AnyMemoryConcerns() + if (pThoughts.NullOrEmpty()) + return; + int tNum = 0; + foreach (Thought thought in pThoughts) + { + Log.Warning(pName + "'s Thought n" + tNum); + tNum++; + + if (thought.pawn == null || deadPawn == null) + continue; + + if (IsDeathThought(thought.def)) + { + if ( + !( + thought is Thought_MemorySocial TMS + && TMS.otherPawn != null + && TMS.otherPawn == deadPawn + ) + ) + continue; + + deadPawn.needs.mood.thoughts.memories.RemoveMemory(TMS); + + Log.Warning( + "removed " + pName + "'s thought " + thought.def.defName + ); + } + } + } + Log.Warning("<<<<<" + deadName); + } + } +} \ No newline at end of file