MultiFuelSpawner
This commit is contained in:
Binary file not shown.
103
1.6/1.6/Defs/ThingDefs_Buildings/Buildings_Spawner.xml
Normal file
103
1.6/1.6/Defs/ThingDefs_Buildings/Buildings_Spawner.xml
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<ThingDef ParentName="BuildingBase">
|
||||||
|
<defName>WULA_ComponentAssembler</defName>
|
||||||
|
<label>零件组装机</label>
|
||||||
|
<description>一台复杂的机器,可以缓慢地用原材料组装零部件。需要钢铁和木材才能运作。</description>
|
||||||
|
<thingClass>Building</thingClass>
|
||||||
|
<graphicData>
|
||||||
|
<texPath>Things/Building/Production/FabricationBench</texPath>
|
||||||
|
<graphicClass>Graphic_Multi</graphicClass>
|
||||||
|
<drawSize>(3.5, 1.5)</drawSize>
|
||||||
|
<damageData>
|
||||||
|
<cornerTL>Damage/Corner</cornerTL>
|
||||||
|
<cornerTR>Damage/Corner</cornerTR>
|
||||||
|
<cornerBL>Damage/Corner</cornerBL>
|
||||||
|
<cornerBR>Damage/Corner</cornerBR>
|
||||||
|
</damageData>
|
||||||
|
</graphicData>
|
||||||
|
<altitudeLayer>Building</altitudeLayer>
|
||||||
|
<passability>PassThroughOnly</passability>
|
||||||
|
<fillPercent>0.5</fillPercent>
|
||||||
|
<pathCost>70</pathCost>
|
||||||
|
<rotatable>true</rotatable>
|
||||||
|
<statBases>
|
||||||
|
<MaxHitPoints>350</MaxHitPoints>
|
||||||
|
<WorkToBuild>4000</WorkToBuild>
|
||||||
|
<Flammability>1.0</Flammability>
|
||||||
|
<Beauty>-15</Beauty>
|
||||||
|
</statBases>
|
||||||
|
<tickerType>Normal</tickerType>
|
||||||
|
<size>(3,1)</size>
|
||||||
|
<costList>
|
||||||
|
<Steel>200</Steel>
|
||||||
|
<ComponentIndustrial>8</ComponentIndustrial>
|
||||||
|
</costList>
|
||||||
|
<comps>
|
||||||
|
<!-- Power Connection -->
|
||||||
|
<li Class="CompProperties_Power">
|
||||||
|
<compClass>CompPowerTrader</compClass>
|
||||||
|
<basePowerConsumption>350</basePowerConsumption>
|
||||||
|
</li>
|
||||||
|
<li Class="CompProperties_Flickable"/>
|
||||||
|
|
||||||
|
<!-- Our simple spawner component -->
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_MultiFuelSpawner">
|
||||||
|
<spawnIntervalRange>120000~120000</spawnIntervalRange>
|
||||||
|
<products>
|
||||||
|
<li>
|
||||||
|
<thingDef>ComponentIndustrial</thingDef>
|
||||||
|
<count>5</count>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<thingDef>ComponentSpacer</thingDef>
|
||||||
|
<count>1</count>
|
||||||
|
</li>
|
||||||
|
</products>
|
||||||
|
<showMessageIfOwned>true</showMessageIfOwned>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- First fuel component: Steel -->
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_RefuelableWithKey">
|
||||||
|
<saveKeysPrefix>steel</saveKeysPrefix>
|
||||||
|
<fuelLabel>钢材储量</fuelLabel>
|
||||||
|
<fuelFilter>
|
||||||
|
<thingDefs>
|
||||||
|
<li>Steel</li>
|
||||||
|
</thingDefs>
|
||||||
|
</fuelFilter>
|
||||||
|
<fuelCapacity>200</fuelCapacity>
|
||||||
|
<fuelConsumptionRate>1</fuelConsumptionRate>
|
||||||
|
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
|
||||||
|
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
|
||||||
|
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<!-- Second fuel component: Wood -->
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_RefuelableWithKey">
|
||||||
|
<saveKeysPrefix>wood</saveKeysPrefix>
|
||||||
|
<fuelLabel>木材储量</fuelLabel>
|
||||||
|
<fuelFilter>
|
||||||
|
<thingDefs>
|
||||||
|
<li>WoodLog</li>
|
||||||
|
</thingDefs>
|
||||||
|
</fuelFilter>
|
||||||
|
<fuelCapacity>300</fuelCapacity>
|
||||||
|
<fuelConsumptionRate>2</fuelConsumptionRate>
|
||||||
|
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
|
||||||
|
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
|
||||||
|
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li Class="CompProperties_Breakdownable"/>
|
||||||
|
</comps>
|
||||||
|
<terrainAffordanceNeeded>Heavy</terrainAffordanceNeeded>
|
||||||
|
<designationCategory>Production</designationCategory>
|
||||||
|
<constructionSkillPrerequisite>6</constructionSkillPrerequisite>
|
||||||
|
<researchPrerequisites>
|
||||||
|
<li>Fabrication</li>
|
||||||
|
</researchPrerequisites>
|
||||||
|
</ThingDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
File diff suppressed because one or more lines are too long
128
Source/WulaFallenEmpire/Spawner/CompMultiFuelSpawner.cs
Normal file
128
Source/WulaFallenEmpire/Spawner/CompMultiFuelSpawner.cs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class SpawnerProduct
|
||||||
|
{
|
||||||
|
public ThingDef thingDef;
|
||||||
|
public int count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Properties Class ---
|
||||||
|
public class CompProperties_MultiFuelSpawner : CompProperties
|
||||||
|
{
|
||||||
|
public List<SpawnerProduct> 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<CompRefuelableWithKey> fuelComps;
|
||||||
|
|
||||||
|
public CompProperties_MultiFuelSpawner Props => (CompProperties_MultiFuelSpawner)props;
|
||||||
|
|
||||||
|
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.PostSpawnSetup(respawningAfterLoad);
|
||||||
|
if (!respawningAfterLoad)
|
||||||
|
{
|
||||||
|
ResetCountdown();
|
||||||
|
}
|
||||||
|
fuelComps = parent.GetComps<CompRefuelableWithKey>().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<CompPowerTrader>()?.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
138
Source/WulaFallenEmpire/Spawner/CompRefuelableSpawner.cs
Normal file
138
Source/WulaFallenEmpire/Spawner/CompRefuelableSpawner.cs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
// --- Properties Class ---
|
||||||
|
public class CompProperties_RefuelableSpawner : CompProperties_Refuelable
|
||||||
|
{
|
||||||
|
public List<SpawnerProduct> products;
|
||||||
|
public IntRange spawnIntervalRange = new IntRange(100, 100);
|
||||||
|
public bool spawnForbidden;
|
||||||
|
public bool inheritFaction;
|
||||||
|
public bool showMessageIfOwned;
|
||||||
|
|
||||||
|
public CompProperties_RefuelableSpawner()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompRefuelableSpawner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Component Class ---
|
||||||
|
public class CompRefuelableSpawner : CompRefuelable
|
||||||
|
{
|
||||||
|
private int ticksUntilSpawn;
|
||||||
|
|
||||||
|
public new CompProperties_RefuelableSpawner Props => (CompProperties_RefuelableSpawner)props;
|
||||||
|
|
||||||
|
public override void PostExposeData()
|
||||||
|
{
|
||||||
|
base.PostExposeData();
|
||||||
|
Scribe_Values.Look(ref ticksUntilSpawn, "ticksUntilSpawn", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.PostSpawnSetup(respawningAfterLoad);
|
||||||
|
if (!respawningAfterLoad)
|
||||||
|
{
|
||||||
|
ResetCountdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void CompTick()
|
||||||
|
{
|
||||||
|
base.CompTick();
|
||||||
|
|
||||||
|
if (HasFuel && (parent.GetComp<CompPowerTrader>()?.PowerOn ?? true))
|
||||||
|
{
|
||||||
|
ticksUntilSpawn--;
|
||||||
|
if (ticksUntilSpawn <= 0)
|
||||||
|
{
|
||||||
|
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 (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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||||
|
{
|
||||||
|
foreach (var g in base.CompGetGizmosExtra())
|
||||||
|
{
|
||||||
|
yield return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Prefs.DevMode)
|
||||||
|
{
|
||||||
|
yield return new Command_Action
|
||||||
|
{
|
||||||
|
defaultLabel = "DEBUG: Spawn items",
|
||||||
|
action = delegate
|
||||||
|
{
|
||||||
|
TryDoSpawn();
|
||||||
|
ResetCountdown();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class SpawnerProduct
|
||||||
|
{
|
||||||
|
public ThingDef thingDef;
|
||||||
|
public int count = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Source/WulaFallenEmpire/Spawner/CompRefuelableWithKey.cs
Normal file
24
Source/WulaFallenEmpire/Spawner/CompRefuelableWithKey.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
// 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.
|
||||||
|
}
|
||||||
|
}
|
||||||
163
Source/WulaFallenEmpire/Spawner/CompRefuelable_WithKey.cs
Normal file
163
Source/WulaFallenEmpire/Spawner/CompRefuelable_WithKey.cs
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using RimWorld;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
// --- 1. The Properties Class ---
|
||||||
|
public class CompProperties_Refuelable_WithKey : CompProperties
|
||||||
|
{
|
||||||
|
public float fuelConsumptionRate = 1f;
|
||||||
|
public float fuelCapacity = 2f;
|
||||||
|
public float initialFuelPercent;
|
||||||
|
public float autoRefuelPercent = 0.3f;
|
||||||
|
public ThingFilter fuelFilter;
|
||||||
|
public bool consumeFuelOnlyWhenUsed = true;
|
||||||
|
public bool showFuelGizmo = true;
|
||||||
|
public bool targetFuelLevelConfigurable;
|
||||||
|
public float initialConfigurableTargetFuelLevel = -1f;
|
||||||
|
public string fuelLabel;
|
||||||
|
public string outOfFuelMessage;
|
||||||
|
public bool showAllowAutoRefuelToggle;
|
||||||
|
public string saveKeysPrefix; // The only field we are adding
|
||||||
|
|
||||||
|
public CompProperties_Refuelable_WithKey()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompRefuelable_WithKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 2. The Component Class (Full Re-implementation) ---
|
||||||
|
public class CompRefuelable_WithKey : ThingComp
|
||||||
|
{
|
||||||
|
// Re-implemented fields from CompRefuelable
|
||||||
|
private float fuel;
|
||||||
|
private float configuredTargetFuelLevel = -1f;
|
||||||
|
public bool allowAutoRefuel = true;
|
||||||
|
private CompFlickable flickComp;
|
||||||
|
|
||||||
|
public new CompProperties_Refuelable_WithKey Props => (CompProperties_Refuelable_WithKey)props;
|
||||||
|
|
||||||
|
public float Fuel => fuel;
|
||||||
|
public bool HasFuel => fuel > 0f;
|
||||||
|
public bool IsFull => TargetFuelLevel - fuel < 1f;
|
||||||
|
public float FuelPercentOfMax => fuel / Props.fuelCapacity;
|
||||||
|
public float TargetFuelLevel
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (configuredTargetFuelLevel >= 0f) return configuredTargetFuelLevel;
|
||||||
|
if (Props.targetFuelLevelConfigurable) return Props.initialConfigurableTargetFuelLevel;
|
||||||
|
return Props.fuelCapacity;
|
||||||
|
}
|
||||||
|
set => configuredTargetFuelLevel = Mathf.Clamp(value, 0f, Props.fuelCapacity);
|
||||||
|
}
|
||||||
|
public bool ShouldAutoRefuelNow => Fuel / TargetFuelLevel <= Props.autoRefuelPercent && !IsFull && TargetFuelLevel > 0f && (flickComp == null || flickComp.SwitchIsOn);
|
||||||
|
|
||||||
|
|
||||||
|
public override void Initialize(CompProperties props)
|
||||||
|
{
|
||||||
|
base.Initialize(props);
|
||||||
|
allowAutoRefuel = true; // Simplified from base
|
||||||
|
fuel = Props.fuelCapacity * Props.initialFuelPercent;
|
||||||
|
if(Props.initialConfigurableTargetFuelLevel > 0)
|
||||||
|
{
|
||||||
|
configuredTargetFuelLevel = Props.initialConfigurableTargetFuelLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.PostSpawnSetup(respawningAfterLoad);
|
||||||
|
flickComp = parent.GetComp<CompFlickable>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The ONLY method we actually change
|
||||||
|
public override void PostExposeData()
|
||||||
|
{
|
||||||
|
base.PostExposeData();
|
||||||
|
string prefix = Props.saveKeysPrefix;
|
||||||
|
if (prefix.NullOrEmpty())
|
||||||
|
{
|
||||||
|
Log.ErrorOnce($"CompRefuelable_WithKey on {parent.def.defName} has a null or empty saveKeysPrefix.", GetHashCode());
|
||||||
|
// Fallback to default scribing to avoid data loss
|
||||||
|
Scribe_Values.Look(ref fuel, "fuel", 0f);
|
||||||
|
Scribe_Values.Look(ref configuredTargetFuelLevel, "configuredTargetFuelLevel", -1f);
|
||||||
|
Scribe_Values.Look(ref allowAutoRefuel, "allowAutoRefuel", true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Scribe_Values.Look(ref fuel, prefix + "_fuel", 0f);
|
||||||
|
Scribe_Values.Look(ref configuredTargetFuelLevel, prefix + "_configuredTargetFuelLevel", -1f);
|
||||||
|
Scribe_Values.Look(ref allowAutoRefuel, prefix + "_allowAutoRefuel", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConsumeFuel(float amount)
|
||||||
|
{
|
||||||
|
if (fuel <= 0f) return;
|
||||||
|
fuel -= amount;
|
||||||
|
if (fuel <= 0f)
|
||||||
|
{
|
||||||
|
fuel = 0f;
|
||||||
|
parent.BroadcastCompSignal("RanOutOfFuel");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refuel(float amount)
|
||||||
|
{
|
||||||
|
fuel += amount;
|
||||||
|
if (fuel > Props.fuelCapacity)
|
||||||
|
{
|
||||||
|
fuel = Props.fuelCapacity;
|
||||||
|
}
|
||||||
|
parent.BroadcastCompSignal("Refueled");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Notify_UsedThisTick()
|
||||||
|
{
|
||||||
|
if (Props.consumeFuelOnlyWhenUsed)
|
||||||
|
{
|
||||||
|
ConsumeFuel(Props.fuelConsumptionRate / 60000f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||||
|
{
|
||||||
|
if (!Props.showFuelGizmo || parent.Faction != Faction.OfPlayer) yield break;
|
||||||
|
|
||||||
|
// Simplified Gizmo Status (can be replaced with copied Gizmo_RefuelableFuelStatus later)
|
||||||
|
yield return new Gizmo_FuelStatus_Spawner(new FuelSystem(this)); // Using a dummy adapter
|
||||||
|
|
||||||
|
// Copied Set Target Level Command
|
||||||
|
if (Props.targetFuelLevelConfigurable)
|
||||||
|
{
|
||||||
|
var command = new Command_Action
|
||||||
|
{
|
||||||
|
defaultLabel = "CommandSetTargetFuelLevel".Translate(),
|
||||||
|
defaultDesc = "CommandSetTargetFuelLevelDesc".Translate(),
|
||||||
|
icon = ContentFinder<Texture2D>.Get("UI/Commands/SetTargetFuelLevel"),
|
||||||
|
action = delegate
|
||||||
|
{
|
||||||
|
Dialog_Slider dialog = new Dialog_Slider(
|
||||||
|
"SetTargetFuelLevel".Translate(), 0, (int)Props.fuelCapacity,
|
||||||
|
(val) => TargetFuelLevel = val, (int)TargetFuelLevel);
|
||||||
|
Find.WindowStack.Add(dialog);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
yield return command;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dummy adapter to make the new Gizmo work temporarily
|
||||||
|
public class FuelSystem
|
||||||
|
{
|
||||||
|
public CompRefuelable_WithKey comp;
|
||||||
|
public FuelSystem(CompRefuelable_WithKey comp) { this.comp = comp; }
|
||||||
|
public float Fuel => comp.Fuel;
|
||||||
|
public float FuelPercent => comp.FuelPercentOfMax;
|
||||||
|
public CompProperties_Refuelable props => comp.Props;
|
||||||
|
public float TargetFuelLevel { get => comp.TargetFuelLevel; set => comp.TargetFuelLevel = value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
303
Source/WulaFallenEmpire/Spawner/Utility.cs
Normal file
303
Source/WulaFallenEmpire/Spawner/Utility.cs
Normal file
@@ -0,0 +1,303 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using RimWorld;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public static class Lifespan_Utility
|
||||||
|
{
|
||||||
|
public static IEnumerable<ThoughtDef> deathThought = new List<ThoughtDef>
|
||||||
|
{
|
||||||
|
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<Thing> 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<Thing> 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<PawnRelationDef> relationT = PawnRelationUtility.GetRelations(
|
||||||
|
deadPawn,
|
||||||
|
p
|
||||||
|
);
|
||||||
|
if (relationT.EnumerableNullOrEmpty())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
List<Thought> pThoughts = new List<Thought>();
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -194,6 +194,12 @@
|
|||||||
<Compile Include="Verb\Verb_Excalibur.cs" />
|
<Compile Include="Verb\Verb_Excalibur.cs" />
|
||||||
<Compile Include="HediffComp_TopTurret.cs" />
|
<Compile Include="HediffComp_TopTurret.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Spawner\CompMultiFuelSpawner.cs" />
|
||||||
|
<Compile Include="Spawner\CompRefuelableWithKey.cs" />
|
||||||
|
<Compile Include="Spawner\Patch_CompRefuelableWithKey.cs" />
|
||||||
|
<Compile Include="Spawner\Utility.cs" />
|
||||||
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Thing_ExcaliburBeam.cs" />
|
<Compile Include="Thing_ExcaliburBeam.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Reference in New Issue
Block a user