diff --git a/1.6/1.6/Assemblies/ArachnaeSwarm.dll b/1.6/1.6/Assemblies/ArachnaeSwarm.dll index cdff7de..33071fb 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/ARA_BioforgeIncubator.xml b/1.6/1.6/Defs/Thing_building/ARA_BioforgeIncubator.xml index 4901023..8ccb10d 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_BioforgeIncubator.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_BioforgeIncubator.xml @@ -236,4 +236,94 @@ + + + ARA_JellyVat + + 一个活体虫族器官,通过分别消化植物和肉类物质,来缓慢培育出营养丰富的阿拉克涅虫蜜。需要同时填充素食和肉类才能工作。 + Building + + Things/Building/Natural/Hive + Graphic_Random + 2 + + (2,2) + Normal + + 0 + + 50 + + false + 0 + Building + PassThroughOnly + ARA_Creep + 50 + + 250 + 2800 + 1.0 + + +
  • PlaceWorker_PreventInteractionSpotOverlap
  • +
    + 0.8 + (0,0,-1) + true + ARA_Buildings + 2600 + Item + + Laboratory + 0.8 + + +
  • + +
  • + + 120000 + 120000 + + +
  • + ARA_InsectJelly + 150 +
  • + + true + + + +
  • + veg_vat + 植物原料 + + +
  • PlantFoodRaw
  • + + + 50 + 25 + true + + + +
  • + meat_vat + 动物蛋白 + + +
  • MeatRaw
  • +
  • AnimalProductRaw
  • + + + 50 + 25 + true + +
    + +
    \ No newline at end of file diff --git a/1.6/1.6/Defs/Thing_building/ARA_InteractiveProducer.xml b/1.6/1.6/Defs/Thing_building/ARA_InteractiveProducer.xml index 45eeed9..840b605 100644 --- a/1.6/1.6/Defs/Thing_building/ARA_InteractiveProducer.xml +++ b/1.6/1.6/Defs/Thing_building/ARA_InteractiveProducer.xml @@ -261,4 +261,5 @@ + \ No newline at end of file diff --git a/1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml b/1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml index c0ce780..b838d25 100644 --- a/1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml +++ b/1.6/1.6/Defs/Thing_building/Building_SmartThermostat.xml @@ -3,8 +3,8 @@ ARA_SmartThermostat - - 一个先进的、不耗电的温控设备。它是一个可逆的热泵,能自动制热或制冷以维持设定的目标温度。必须像制冷器一样安装在墙上。 + + 一个不耗电的温控虫虫。它是一个可逆的热泵,能自动制热或制冷以维持设定的目标温度。必须像制冷器一样安装在墙上。 ArachnaeSwarm.Building_SmartThermostat Things/Building/Misc/TempControl/Cooler @@ -15,40 +15,50 @@ true 1 true + true + true + true + false + 1.0 - 2000 + 400 100 - 0.7 + 1.0 Rare - 120 - 4 + 30 Medium -
  • PlaceWorker_Cooler
  • +
  • PlaceWorker_Vent
  • true true + true + true + +
  • AirConditioning
  • +
    + ARA_Buildings -
  • +
  • + UI/Commands/Vent + CommandDesignateOpenCloseVentLabel + CommandDesignateOpenCloseVentDesc +
  • - 21 + 34
  • - Temperature - -
  • AirConditioning
  • -
    \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ARA_CompInteractiveProducer/CompRefuelableNutrition.cs b/Source/ArachnaeSwarm/ARA_CompInteractiveProducer/CompRefuelableNutrition.cs index a76339d..52e59be 100644 --- a/Source/ArachnaeSwarm/ARA_CompInteractiveProducer/CompRefuelableNutrition.cs +++ b/Source/ArachnaeSwarm/ARA_CompInteractiveProducer/CompRefuelableNutrition.cs @@ -17,7 +17,7 @@ namespace ArachnaeSwarm } [StaticConstructorOnStartup] - public class CompRefuelableNutrition : CompRefuelable + public class CompRefuelableNutrition : CompRefuelable, IFuelSource { public float currentConsumptionRate = 0f; public float NutritionStored => Fuel; @@ -90,5 +90,13 @@ namespace ArachnaeSwarm return text; } + + public new void Notify_UsedThisTick() + { + if (Props.consumeFuelOnlyWhenUsed) + { + ConsumeFuel(Props.fuelConsumptionRate / 60000f); + } + } } } \ No newline at end of file diff --git a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj index 63ae437..04de4a0 100644 --- a/Source/ArachnaeSwarm/ArachnaeSwarm.csproj +++ b/Source/ArachnaeSwarm/ArachnaeSwarm.csproj @@ -217,10 +217,11 @@ + - + diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompMultiFuelSpawner.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompMultiFuelSpawner.cs index e64123d..47bdfb6 100644 --- a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompMultiFuelSpawner.cs +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompMultiFuelSpawner.cs @@ -11,7 +11,6 @@ namespace ArachnaeSwarm public int count = 1; } - // --- Properties Class --- public class CompProperties_MultiFuelSpawner : CompProperties { public List products; @@ -26,11 +25,10 @@ namespace ArachnaeSwarm } } - // --- Component Class --- public class CompMultiFuelSpawner : ThingComp { private int ticksUntilSpawn; - private List fuelComps; + private List fuelComps; // Changed to use the interface public CompProperties_MultiFuelSpawner Props => (CompProperties_MultiFuelSpawner)props; @@ -41,7 +39,8 @@ namespace ArachnaeSwarm { ResetCountdown(); } - fuelComps = parent.GetComps().ToList(); + // Find all components that implement our interface + fuelComps = parent.GetComps().OfType().ToList(); } public override void PostExposeData() @@ -56,6 +55,7 @@ namespace ArachnaeSwarm if (fuelComps.NullOrEmpty()) return; + // Check if all fuel sources have fuel bool allFuelsOk = fuelComps.All(c => c.HasFuel); if (allFuelsOk && (parent.GetComp()?.PowerOn ?? true)) @@ -63,6 +63,7 @@ namespace ArachnaeSwarm ticksUntilSpawn--; if (ticksUntilSpawn <= 0) { + // Consume fuel from all sources foreach (var comp in fuelComps) { comp.Notify_UsedThisTick(); @@ -112,7 +113,7 @@ namespace ArachnaeSwarm { string text = base.CompInspectStringExtra(); - if (fuelComps.All(c => c.HasFuel)) + if (!fuelComps.NullOrEmpty() && fuelComps.All(c => c.HasFuel)) { if (!text.NullOrEmpty()) { diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableNutrition_WithKey.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableNutrition_WithKey.cs new file mode 100644 index 0000000..9a59c22 --- /dev/null +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableNutrition_WithKey.cs @@ -0,0 +1,65 @@ +using RimWorld; +using Verse; +using System.Reflection; +using HarmonyLib; + +namespace ArachnaeSwarm +{ + public class CompProperties_RefuelableNutrition_WithKey : CompProperties_RefuelableNutrition + { + public string saveKeysPrefix; + + public CompProperties_RefuelableNutrition_WithKey() + { + compClass = typeof(CompRefuelableNutrition_WithKey); + } + } + + public class CompRefuelableNutrition_WithKey : CompRefuelableNutrition, IFuelSource + { + public new CompProperties_RefuelableNutrition_WithKey Props => (CompProperties_RefuelableNutrition_WithKey)props; + + public override void PostExposeData() + { + string prefix = Props.saveKeysPrefix; + if (prefix.NullOrEmpty()) + { + Log.ErrorOnce($"CompRefuelableNutrition_WithKey on {parent.def.defName} has a null or empty saveKeysPrefix. Defaulting to standard save.", GetHashCode()); + base.PostExposeData(); + return; + } + + // --- Accessing private fields from CompRefuelable (base of CompRefuelableNutrition) --- + FieldInfo fuelField = AccessTools.Field(typeof(CompRefuelable), "fuel"); + FieldInfo configuredTargetFuelLevelField = AccessTools.Field(typeof(CompRefuelable), "configuredTargetFuelLevel"); + FieldInfo allowAutoRefuelField = AccessTools.Field(typeof(CompRefuelable), "allowAutoRefuel"); + + // Get current values + float currentFuel = (float)fuelField.GetValue(this); + float currentConfiguredLevel = (float)configuredTargetFuelLevelField.GetValue(this); + bool currentAllowAuto = (bool)allowAutoRefuelField.GetValue(this); + + // Scribe with prefix + Scribe_Values.Look(ref currentFuel, prefix + "_fuel", 0f); + Scribe_Values.Look(ref currentConfiguredLevel, prefix + "_configuredTargetFuelLevel", -1f); + Scribe_Values.Look(ref currentAllowAuto, prefix + "_allowAutoRefuel", true); + + // Set values back if loading + if (Scribe.mode == LoadSaveMode.LoadingVars) + { + fuelField.SetValue(this, currentFuel); + configuredTargetFuelLevelField.SetValue(this, currentConfiguredLevel); + allowAutoRefuelField.SetValue(this, currentAllowAuto); + } + + // --- Accessing private fields from CompRefuelableNutrition --- + // (Assuming there are any. If not, this part is not needed) + // Example: + // FieldInfo someOtherField = AccessTools.Field(typeof(CompRefuelableNutrition), "someOtherPrivateField"); + // ... and so on + } + + // We already have Notify_UsedThisTick from the previous step. + // If not, we would add it here. + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableWithKey.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableWithKey.cs index cd1e432..39e4107 100644 --- a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableWithKey.cs +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/CompRefuelableWithKey.cs @@ -3,7 +3,6 @@ using Verse; namespace ArachnaeSwarm { - // 1. New Properties class that adds the save key public class CompProperties_RefuelableWithKey : CompProperties_Refuelable { public string saveKeysPrefix; @@ -14,11 +13,16 @@ namespace ArachnaeSwarm } } - // 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 + public class CompRefuelableWithKey : CompRefuelable, IFuelSource { - // We will override PostExposeData using a Harmony patch - // to avoid re-implementing the entire class. + public new CompProperties_RefuelableWithKey Props => (CompProperties_RefuelableWithKey)props; + + public new void Notify_UsedThisTick() + { + if (Props.consumeFuelOnlyWhenUsed) + { + ConsumeFuel(Props.fuelConsumptionRate / 60000f); + } + } } } \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/IFuelSource.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/IFuelSource.cs new file mode 100644 index 0000000..5aa4459 --- /dev/null +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/IFuelSource.cs @@ -0,0 +1,8 @@ +namespace ArachnaeSwarm +{ + public interface IFuelSource + { + bool HasFuel { get; } + void Notify_UsedThisTick(); + } +} \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Patch_CompRefuelableWithKey.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Patch_CompRefuelableWithKey.cs index 311bcdf..7cd7009 100644 --- a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Patch_CompRefuelableWithKey.cs +++ b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Patch_CompRefuelableWithKey.cs @@ -5,46 +5,38 @@ 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; + return true; // If it's not our class, run the original method } - // 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; } + + // Use reflection to get/set private fields from the base class + FieldInfo fuelField = AccessTools.Field(typeof(CompRefuelable), "fuel"); + FieldInfo configuredTargetFuelLevelField = AccessTools.Field(typeof(CompRefuelable), "configuredTargetFuelLevel"); + FieldInfo allowAutoRefuelField = AccessTools.Field(typeof(CompRefuelable), "allowAutoRefuel"); - // 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); @@ -52,8 +44,7 @@ namespace ArachnaeSwarm allowAutoRefuelField.SetValue(refuelableWithKey, allowAutoRefuel); } - // Prevent the original PostExposeData from running - return false; + return false; // Prevent the original PostExposeData from running } } } \ No newline at end of file diff --git a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Utility.cs b/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Utility.cs deleted file mode 100644 index ae08615..0000000 --- a/Source/ArachnaeSwarm/WULA_MutiFuelSpawner/Utility.cs +++ /dev/null @@ -1,303 +0,0 @@ -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