Merge branch 'master' of https://git.ra3battle.cn/Kalospacer/ArachnaeSwarm
This commit is contained in:
24
1.6/1.6/Defs/HediffDefs/ARA_PowerArmor_Hediffs.xml
Normal file
24
1.6/1.6/Defs/HediffDefs/ARA_PowerArmor_Hediffs.xml
Normal file
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<HediffDef>
|
||||
<defName>ARA_PowerArmor_NoFuel</defName>
|
||||
<label>外骨骼营养不良</label>
|
||||
<description>生物外骨骼因缺少养分而营养不良,这会对宿主造成不良影响.</description>
|
||||
<hediffClass>HediffWithComps</hediffClass>
|
||||
<defaultLabelColor>(0.6, 0.6, 0.6)</defaultLabelColor>
|
||||
<isBad>true</isBad>
|
||||
<scenarioCanAdd>false</scenarioCanAdd>
|
||||
<stages>
|
||||
<li>
|
||||
<capMods>
|
||||
<li>
|
||||
<capacity>Moving</capacity>
|
||||
<offset>-0.4</offset>
|
||||
</li>
|
||||
</capMods>
|
||||
</li>
|
||||
</stages>
|
||||
</HediffDef>
|
||||
|
||||
</Defs>
|
||||
@@ -12,4 +12,11 @@
|
||||
<reportString>将 TargetA 带到 TargetB.</reportString>
|
||||
<suspendable>false</suspendable>
|
||||
</JobDef>
|
||||
|
||||
<JobDef>
|
||||
<defName>ARA_EnterPowerArmor</defName>
|
||||
<driverClass>ArachnaeSwarm.JobDriver_EnterPowerArmor</driverClass>
|
||||
<reportString>entering TargetA.</reportString>
|
||||
<allowOpportunisticPrefix>true</allowOpportunisticPrefix>
|
||||
</JobDef>
|
||||
</Defs>
|
||||
@@ -349,6 +349,7 @@
|
||||
<li>ARA_Latex_Catsuit</li>
|
||||
<li>ARA_Pantyhose_Black</li>
|
||||
<li>ARA_Pantyhose_White</li>
|
||||
<li>ARA_SpiderOne_PowerArmor</li>
|
||||
</apparelList>
|
||||
<blackApparelList>
|
||||
<li>Apparel_AdvancedHelmet</li>
|
||||
|
||||
127
1.6/1.6/Defs/Thing_Misc/ARA_PowerArmor_Defs.xml
Normal file
127
1.6/1.6/Defs/Thing_Misc/ARA_PowerArmor_Defs.xml
Normal file
@@ -0,0 +1,127 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<!-- 动力甲 - 建筑形态 -->
|
||||
<ThingDef Name="ARA_PowerArmorBuilding_Base" ParentName="BuildingBase">
|
||||
<defName>ARA_Building_SpiderOne</defName>
|
||||
<label>阿拉克涅动力装甲</label>
|
||||
<description>阿拉克涅动力装甲</description>
|
||||
<graphicData>
|
||||
<graphicClass>Graphic_Multi</graphicClass>
|
||||
<drawSize>(1,1)</drawSize>
|
||||
<texPath>ArachnaeSwarm/Apparel/ARA_Bunny_Girl_Uniform</texPath>
|
||||
</graphicData>
|
||||
<thingClass>Building</thingClass>
|
||||
<altitudeLayer>Building</altitudeLayer>
|
||||
<passability>PassThroughOnly</passability>
|
||||
<pathCost>70</pathCost>
|
||||
<castEdgeShadows>true</castEdgeShadows>
|
||||
<fillPercent>0.5</fillPercent>
|
||||
<canOverlapZones>false</canOverlapZones>
|
||||
<size>(1,1)</size>
|
||||
<designationCategory>Misc</designationCategory>
|
||||
<rotatable>true</rotatable>
|
||||
<selectable>true</selectable>
|
||||
<tickerType>Normal</tickerType>
|
||||
<drawerType>MapMeshAndRealTime</drawerType>
|
||||
<hasInteractionCell>true</hasInteractionCell>
|
||||
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
|
||||
<statBases>
|
||||
<MaxHitPoints>500</MaxHitPoints>
|
||||
<WorkToBuild>2000</WorkToBuild>
|
||||
<Mass>50</Mass>
|
||||
<Flammability>0.5</Flammability>
|
||||
</statBases>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_PowerArmorStation">
|
||||
<apparelDef>ARA_SpiderOne_PowerArmor</apparelDef>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
|
||||
<fuelCapacity>10.0</fuelCapacity>
|
||||
<fuelFilter>
|
||||
<categories>
|
||||
<li>FoodMeals</li>
|
||||
<li>FoodRaw</li>
|
||||
</categories>
|
||||
</fuelFilter>
|
||||
<initialFuelPercent>1</initialFuelPercent>
|
||||
<autoRefuelPercent>0.5</autoRefuelPercent>
|
||||
<showFuelGizmo>true</showFuelGizmo>
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
|
||||
<ThingDef ParentName="ARA_ClothBase">
|
||||
<defName>ARA_SpiderOne_PowerArmor</defName>
|
||||
<label>阿拉克涅动力装甲</label>
|
||||
<description>阿拉克涅动力装甲</description>
|
||||
<descriptionHyperlinks>
|
||||
<ThingDef>ARA_Cocoon_Cloth_1Stage</ThingDef>
|
||||
</descriptionHyperlinks>
|
||||
<thingClass>ArachnaeSwarm.ARA_PowerArmor</thingClass>
|
||||
<modExtensions>
|
||||
<li Class="ArachnaeSwarm.PowerArmorExtension">
|
||||
<structurePointsMax>500</structurePointsMax>
|
||||
<buildingDef>ARA_Building_SpiderOne</buildingDef>
|
||||
<hediffOnEmptyFuel>ARA_PowerArmor_NoFuel</hediffOnEmptyFuel>
|
||||
<fuelConsumptionRate>0.5</fuelConsumptionRate>
|
||||
</li>
|
||||
</modExtensions>
|
||||
<recipeMaker>
|
||||
<recipeUsers Inherit="False" />
|
||||
<researchPrerequisite>ARA_Technology_6DIL</researchPrerequisite>
|
||||
<unfinishedThingDef>UnfinishedArmor</unfinishedThingDef>
|
||||
</recipeMaker>
|
||||
<costList Inherit="False">
|
||||
<ARA_Carapace>25</ARA_Carapace>
|
||||
</costList>
|
||||
<graphicData>
|
||||
<texPath>ArachnaeSwarm/Apparel/ARA_Bunny_Girl_Uniform</texPath>
|
||||
</graphicData>
|
||||
<apparel>
|
||||
<bodyPartGroups>
|
||||
<li>Torso</li>
|
||||
<li>Shoulders</li>
|
||||
<li>Arms</li>
|
||||
<li>Legs</li>
|
||||
</bodyPartGroups>
|
||||
<layers>
|
||||
<!-- <li>OnSkin</li> -->
|
||||
<li>Middle</li>
|
||||
</layers>
|
||||
<wornGraphicPath>ArachnaeSwarm/Apparel/ARA_Bunny_Girl_Uniform</wornGraphicPath>
|
||||
</apparel>
|
||||
<statBases>
|
||||
<ARA_IncubationCost>120</ARA_IncubationCost>
|
||||
<ARA_IncubationTime>2.5</ARA_IncubationTime>
|
||||
</statBases>
|
||||
<equippedStatOffsets>
|
||||
</equippedStatOffsets>
|
||||
<costStuffCount>0</costStuffCount>
|
||||
<comps>
|
||||
<li Class="CompProperties_CauseHediff_Apparel">
|
||||
<hediff>ARA_TerrainMoveSpeedHediff</hediff>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_ExtraIncubationInfo">
|
||||
<cocoonDefs>
|
||||
<li>ARA_Cocoon_Cloth_1Stage</li>
|
||||
<li>ARA_BioforgeIncubator_Thing</li>
|
||||
</cocoonDefs>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
|
||||
<fuelCapacity>10.0</fuelCapacity>
|
||||
<fuelFilter>
|
||||
<categories>
|
||||
<li>FoodMeals</li>
|
||||
<li>FoodRaw</li>
|
||||
</categories>
|
||||
</fuelFilter>
|
||||
<initialFuelPercent>1</initialFuelPercent>
|
||||
<autoRefuelPercent>0.5</autoRefuelPercent>
|
||||
<showFuelGizmo>true</showFuelGizmo>
|
||||
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
|
||||
</Defs>
|
||||
133
1.6/Defs/Thing_Misc/ARA_PowerArmor_Defs.xml
Normal file
133
1.6/Defs/Thing_Misc/ARA_PowerArmor_Defs.xml
Normal file
@@ -0,0 +1,133 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<Defs>
|
||||
|
||||
<!-- 动力甲 - 建筑形态 (基类) -->
|
||||
<ThingDef Name="ARA_PowerArmorBuilding_Base" ParentName="BuildingBase" Abstract="True">
|
||||
<thingClass>Building</thingClass>
|
||||
<graphicData>
|
||||
<graphicClass>Graphic_Multi</graphicClass>
|
||||
<drawSize>(3,3)</drawSize>
|
||||
</graphicData>
|
||||
<altitudeLayer>Building</altitudeLayer>
|
||||
<passability>PassThroughOnly</passability>
|
||||
<pathCost>70</pathCost>
|
||||
<castEdgeShadows>true</castEdgeShadows>
|
||||
<fillPercent>0.5</fillPercent>
|
||||
<canOverlapZones>false</canOverlapZones>
|
||||
<size>(2,2)</size>
|
||||
<designationCategory>Misc</designationCategory>
|
||||
<rotatable>true</rotatable>
|
||||
<selectable>true</selectable>
|
||||
<tickerType>Never</tickerType>
|
||||
<drawerType>MapMeshAndRealTime</drawerType>
|
||||
<statBases>
|
||||
<MaxHitPoints>250</MaxHitPoints>
|
||||
<WorkToBuild>2000</WorkToBuild>
|
||||
<Mass>50</Mass>
|
||||
<Flammability>0.5</Flammability>
|
||||
</statBases>
|
||||
</ThingDef>
|
||||
|
||||
<!-- 动力甲 - 服装形态 (基类) -->
|
||||
<ThingDef Name="ARA_PowerArmorApparel_Base" ParentName="ApparelBase" Abstract="True">
|
||||
<thingClass>ArachnaeSwarm.ARA_PowerArmor</thingClass>
|
||||
<graphicData>
|
||||
<drawSize>2</drawSize>
|
||||
</graphicData>
|
||||
<statBases>
|
||||
<MaxHitPoints>250</MaxHitPoints>
|
||||
<Mass>30</Mass>
|
||||
<Flammability>0</Flammability>
|
||||
<ArmorRating_Sharp>0.85</ArmorRating_Sharp>
|
||||
<ArmorRating_Blunt>0.80</ArmorRating_Blunt>
|
||||
<ArmorRating_Heat>0.90</ArmorRating_Heat>
|
||||
<Insulation_Cold>40</Insulation_Cold>
|
||||
<Insulation_Heat>20</Insulation_Heat>
|
||||
</statBases>
|
||||
<equippedStatOffsets>
|
||||
<MoveSpeed>-0.5</MoveSpeed>
|
||||
</equippedStatOffsets>
|
||||
<apparel>
|
||||
<bodyPartGroups>
|
||||
<li>Torso</li>
|
||||
<li>Neck</li>
|
||||
<li>Shoulders</li>
|
||||
<li>Arms</li>
|
||||
<li>Legs</li>
|
||||
</bodyPartGroups>
|
||||
<layers>
|
||||
<li>Shell</li>
|
||||
<li>Middle</li>
|
||||
</layers>
|
||||
<blocksVision>false</blocksVision>
|
||||
</apparel>
|
||||
</ThingDef>
|
||||
|
||||
|
||||
<!-- 示例: 蜘蛛初号机 (具体实现) -->
|
||||
|
||||
<ThingDef ParentName="ARA_PowerArmorApparel_Base">
|
||||
<defName>ARA_Apparel_SpiderOne</defName>
|
||||
<label>Spider-I Power Armor</label>
|
||||
<description>A prototype power armor with an arachnid design.</description>
|
||||
<graphicData>
|
||||
<texPath>Things/Pawn/Humanlike/Apparel/SpiderArmor/SpiderArmor</texPath>
|
||||
</graphicData>
|
||||
<modExtensions>
|
||||
<li Class="ArachnaeSwarm.PowerArmorExtension">
|
||||
<buildingDef>ARA_Building_SpiderOne</buildingDef>
|
||||
<structurePointsMax>625</structurePointsMax>
|
||||
<hediffOnEmptyFuel>ARA_PowerArmor_NoFuel</hediffOnEmptyFuel>
|
||||
<fuelConsumptionRate>0.5</fuelConsumptionRate>
|
||||
</li>
|
||||
</modExtensions>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
|
||||
<fuelCapacity>10.0</fuelCapacity>
|
||||
<fuelFilter>
|
||||
<thingDefs>
|
||||
<li>Meat_Human</li>
|
||||
</thingDefs>
|
||||
<categories>
|
||||
<li>FoodMeals</li>
|
||||
<li>FoodRaw</li>
|
||||
</categories>
|
||||
</fuelFilter>
|
||||
<initialFuelPercent>1</initialFuelPercent>
|
||||
<autoRefuelPercent>0.5</autoRefuelPercent>
|
||||
<showFuelGizmo>true</showFuelGizmo>
|
||||
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
|
||||
<ThingDef ParentName="ARA_PowerArmorBuilding_Base">
|
||||
<defName>ARA_Building_SpiderOne</defName>
|
||||
<label>Spider-I Power Armor (Station)</label>
|
||||
<description>A stationary housing for the Spider-I Power Armor.</description>
|
||||
<graphicData>
|
||||
<texPath>Things/Building/PowerArmorStation</texPath>
|
||||
</graphicData>
|
||||
<comps>
|
||||
<li Class="ArachnaeSwarm.CompProperties_PowerArmorStation">
|
||||
<apparelDef>ARA_Apparel_SpiderOne</apparelDef>
|
||||
</li>
|
||||
<li Class="ArachnaeSwarm.CompProperties_RefuelableNutrition">
|
||||
<fuelCapacity>10.0</fuelCapacity>
|
||||
<fuelFilter>
|
||||
<thingDefs>
|
||||
<li>Meat_Human</li>
|
||||
</thingDefs>
|
||||
<categories>
|
||||
<li>FoodMeals</li>
|
||||
<li>FoodRaw</li>
|
||||
</categories>
|
||||
</fuelFilter>
|
||||
<initialFuelPercent>1</initialFuelPercent>
|
||||
<autoRefuelPercent>0.5</autoRefuelPercent>
|
||||
<showFuelGizmo>true</showFuelGizmo>
|
||||
</li>
|
||||
</comps>
|
||||
</ThingDef>
|
||||
|
||||
</Defs>
|
||||
12
1.6/Languages/English/Keyed/PowerArmor_Keys.xml
Normal file
12
1.6/Languages/English/Keyed/PowerArmor_Keys.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<LanguageData>
|
||||
|
||||
<!-- Power Armor Interaction -->
|
||||
<EnterPowerArmor>Enter {0}</EnterPowerArmor>
|
||||
<CannotEnterPowerArmor>Cannot enter power armor</CannotEnterPowerArmor>
|
||||
<PowerArmorBroken>{0} has been broken!</PowerArmorBroken>
|
||||
|
||||
<!-- Gizmo Labels -->
|
||||
<StructurePoints>Structure</StructurePoints>
|
||||
|
||||
</LanguageData>
|
||||
13
Languages/ChineseSimplified/Keyed/PowerArmor_Keys.xml
Normal file
13
Languages/ChineseSimplified/Keyed/PowerArmor_Keys.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<LanguageData>
|
||||
|
||||
<!-- Job Reports -->
|
||||
<ARA_EnterPowerArmor>进入动力甲</ARA_EnterPowerArmor>
|
||||
|
||||
<!-- Gizmo Labels -->
|
||||
<StructurePoints>结构点</StructurePoints>
|
||||
|
||||
<!-- Messages -->
|
||||
<PowerArmorBroken>{0} 已被摧毁!驾驶员 {1} 被暴露在外。</PowerArmorBroken>
|
||||
|
||||
</LanguageData>
|
||||
@@ -267,6 +267,12 @@
|
||||
<Compile Include="Buildings\Building_CatastropheMissileSilo\WorldObject_CatastropheMissile.cs" />
|
||||
<Compile Include="HarmonyPatches\Patch_ForceTargetable.cs" />
|
||||
<Compile Include="Building_Comps\CompForceTargetable.cs" />
|
||||
<Compile Include="PowerArmor\ARA_PowerArmor.cs" />
|
||||
<Compile Include="PowerArmor\CompPowerArmorStation.cs" />
|
||||
<Compile Include="PowerArmor\Gizmo_StructurePanel.cs" />
|
||||
<Compile Include="PowerArmor\JobDriver_EnterPowerArmor.cs" />
|
||||
<Compile Include="PowerArmor\Gizmo_FuelPanel.cs" />
|
||||
<Compile Include="PowerArmor\Harmony_ThingWithComps_GetFloatMenuOptions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Jobs\JobDriver_SuperCarry\SuperCarryExtension.cs" />
|
||||
|
||||
252
Source/ArachnaeSwarm/PowerArmor/ARA_PowerArmor.cs
Normal file
252
Source/ArachnaeSwarm/PowerArmor/ARA_PowerArmor.cs
Normal file
@@ -0,0 +1,252 @@
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public interface IStructurePoints
|
||||
{
|
||||
float StructurePoints { get; }
|
||||
float StructurePointsMax { get; }
|
||||
float StructurePointsPercent { get; }
|
||||
string Label { get; }
|
||||
}
|
||||
|
||||
public class PowerArmorExtension : DefModExtension
|
||||
{
|
||||
public ThingDef buildingDef;
|
||||
public float structurePointsMax = 500f;
|
||||
public HediffDef hediffOnEmptyFuel;
|
||||
public float fuelConsumptionRate = 0.5f; // Nutrition per day
|
||||
}
|
||||
|
||||
[StaticConstructorOnStartup]
|
||||
public class ARA_PowerArmor : Apparel, IStructurePoints
|
||||
{
|
||||
#region Properties
|
||||
private PowerArmorExtension ext;
|
||||
public PowerArmorExtension Ext => ext ??= def.GetModExtension<PowerArmorExtension>();
|
||||
public override string Label => base.Label;
|
||||
|
||||
private float structurePoints = -1f;
|
||||
public float StructurePointsMax => Ext?.structurePointsMax ?? 500f;
|
||||
|
||||
public float StructurePoints
|
||||
{
|
||||
get
|
||||
{
|
||||
if (structurePoints < 0)
|
||||
{
|
||||
structurePoints = StructurePointsMax;
|
||||
}
|
||||
return structurePoints;
|
||||
}
|
||||
set
|
||||
{
|
||||
structurePoints = Mathf.Clamp(value, 0, StructurePointsMax);
|
||||
}
|
||||
}
|
||||
|
||||
public float StructurePointsPercent => StructurePoints / StructurePointsMax;
|
||||
|
||||
public Building sourceBuilding;
|
||||
#endregion
|
||||
|
||||
#region Ticker
|
||||
protected override void Tick()
|
||||
{
|
||||
base.Tick(); // Call Apparel's Tick
|
||||
|
||||
if (this.Wearer == null) return; // Only tick if worn
|
||||
|
||||
var fuelComp = this.GetComp<CompRefuelableNutrition>();
|
||||
if (fuelComp != null)
|
||||
{
|
||||
// Explicitly call the component's Tick method to ensure fuel consumption logic is executed.
|
||||
// This is needed because Apparel's base Tick does not automatically call component Ticks.
|
||||
// First, update consumption rate and hediffs
|
||||
if (this.Wearer.IsHashIntervalTick(60)) // Check every second
|
||||
{
|
||||
if (this.Wearer.pather != null && this.Wearer.pather.MovingNow)
|
||||
{
|
||||
fuelComp.currentConsumptionRate = Ext?.fuelConsumptionRate ?? 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
fuelComp.currentConsumptionRate = 0f;
|
||||
}
|
||||
|
||||
// Handle hediff for empty fuel
|
||||
var hediffDef = Ext?.hediffOnEmptyFuel;
|
||||
if (hediffDef != null)
|
||||
{
|
||||
var hediff = this.Wearer.health.hediffSet.GetFirstHediffOfDef(hediffDef);
|
||||
if (!fuelComp.HasFuel)
|
||||
{
|
||||
if (hediff == null)
|
||||
{
|
||||
this.Wearer.health.AddHediff(hediffDef);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (hediff != null)
|
||||
{
|
||||
this.Wearer.health.RemoveHediff(hediff);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Then, explicitly call the component's Tick method to ensure fuel consumption logic is executed with the UPDATED rate.
|
||||
fuelComp.CompTick();
|
||||
}
|
||||
} // Correctly close the Tick method
|
||||
#endregion
|
||||
|
||||
#region Data
|
||||
public override void ExposeData()
|
||||
{
|
||||
base.ExposeData();
|
||||
Scribe_Values.Look(ref structurePoints, "structurePoints", -1f);
|
||||
Scribe_References.Look(ref sourceBuilding, "sourceBuilding");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Gizmo
|
||||
public override IEnumerable<Gizmo> GetWornGizmos()
|
||||
{
|
||||
// Always yield base gizmos first.
|
||||
foreach (var gizmo in base.GetWornGizmos())
|
||||
{
|
||||
yield return gizmo;
|
||||
}
|
||||
|
||||
// Yield our custom structure points gizmo.
|
||||
yield return new Gizmo_StructurePanel(this);
|
||||
|
||||
// Yield our custom fuel panel gizmo.
|
||||
var fuelComp = this.GetComp<CompRefuelableNutrition>();
|
||||
if (fuelComp != null)
|
||||
{
|
||||
yield return new Gizmo_FuelPanel(fuelComp);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region State-Switching
|
||||
public override void Notify_Unequipped(Pawn pawn)
|
||||
{
|
||||
base.Notify_Unequipped(pawn);
|
||||
|
||||
Building building = sourceBuilding;
|
||||
|
||||
// If the source building reference is lost, create a new one as a fallback.
|
||||
if (building == null)
|
||||
{
|
||||
ThingDef buildingDef = Ext?.buildingDef;
|
||||
if (buildingDef == null)
|
||||
{
|
||||
Log.Error($"[ArachnaeSwarm] Power Armor {this.def.defName} unequipped, but has no buildingDef defined in its PowerArmorExtension and the source building reference was lost.");
|
||||
this.Destroy(DestroyMode.Vanish);
|
||||
return;
|
||||
}
|
||||
building = (Building)ThingMaker.MakeThing(buildingDef);
|
||||
}
|
||||
|
||||
// Sync health back to the building, ensuring it's at least 1 to prevent it from being destroyed instantly.
|
||||
building.HitPoints = Mathf.Max(1, Mathf.RoundToInt(this.StructurePoints));
|
||||
|
||||
// Sync fuel back to the building
|
||||
var apparelFuelComp = this.GetComp<CompRefuelableNutrition>();
|
||||
var buildingFuelComp = building.GetComp<CompRefuelableNutrition>();
|
||||
if (apparelFuelComp != null && buildingFuelComp != null)
|
||||
{
|
||||
// Reset building fuel and then set it to the apparel's current fuel to avoid overflow.
|
||||
buildingFuelComp.ConsumeFuel(buildingFuelComp.Fuel);
|
||||
buildingFuelComp.ReceiveFuel(apparelFuelComp.Fuel);
|
||||
}
|
||||
|
||||
Log.Message($"[PA_Debug] Notify_Unequipped: Before spawning building (ID: {building.thingIDNumber}) - HitPoints: {building.HitPoints}, StackCount: {building.stackCount}");
|
||||
|
||||
// Ensure stackCount is at least 1 for buildings, as 0 stackCount causes errors during spawning
|
||||
if (building.stackCount <= 0)
|
||||
{
|
||||
building.stackCount = 1;
|
||||
Log.Warning($"[PA_Debug] Notify_Unequipped: Corrected building (ID: {building.thingIDNumber}) stackCount to 1 as it was 0.");
|
||||
}
|
||||
|
||||
// Set the faction to the pawn's faction before spawning
|
||||
building.SetFaction(pawn.Faction);
|
||||
|
||||
// Re-spawn the original building instance
|
||||
GenPlace.TryPlaceThing(building, pawn.Position, pawn.Map, ThingPlaceMode.Near);
|
||||
Log.Message($"[PA_Debug] Notify_Unequipped: After spawning building (ID: {building.thingIDNumber}) - HitPoints: {building.HitPoints}, StackCount: {building.stackCount}");
|
||||
|
||||
// Destroy the apparel to prevent duplication
|
||||
this.Destroy(DestroyMode.Vanish);
|
||||
Log.Message($"[PA_Debug] Notify_Unequipped: Apparel {this.Label} (ID: {this.thingIDNumber}) destroyed.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Damage Handling - THE FINAL, CORRECT IMPLEMENTATION
|
||||
public override bool CheckPreAbsorbDamage(DamageInfo dinfo)
|
||||
{
|
||||
if (this.Wearer == null || !dinfo.Def.harmsHealth || dinfo.Amount <= 0.001f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float finalDamage = GetPostArmorDamage(ref dinfo);
|
||||
|
||||
if (finalDamage > 0)
|
||||
{
|
||||
this.StructurePoints -= finalDamage;
|
||||
EffecterDefOf.DamageDiminished_Metal.SpawnAttached(this.Wearer, this.Wearer.Map, 1f);
|
||||
|
||||
if (this.StructurePoints <= 0)
|
||||
{
|
||||
Messages.Message("PowerArmorBroken".Translate(this.Label, this.Wearer.LabelShort), this, MessageTypeDefOf.NegativeEvent);
|
||||
this.Destroy(DestroyMode.KillFinalize);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
EffecterDefOf.Deflect_Metal_Bullet.SpawnAttached(this.Wearer, this.Wearer.Map, 1f);
|
||||
}
|
||||
|
||||
// By returning true, we tell the game the damage has been fully handled and should not proceed to the pawn.
|
||||
return true;
|
||||
}
|
||||
|
||||
private float GetPostArmorDamage(ref DamageInfo dinfo)
|
||||
{
|
||||
float amount = dinfo.Amount;
|
||||
if (dinfo.Def.armorCategory != null)
|
||||
{
|
||||
StatDef armorRatingStat = dinfo.Def.armorCategory.armorRatingStat;
|
||||
float armorRating = this.GetStatValue(armorRatingStat);
|
||||
|
||||
float armorPenetration = dinfo.ArmorPenetrationInt;
|
||||
float num = Mathf.Max(armorRating - armorPenetration, 0f);
|
||||
float value = Rand.Value;
|
||||
float num2 = num * 0.5f;
|
||||
float num3 = num;
|
||||
if (value < num2)
|
||||
{
|
||||
amount = 0f;
|
||||
}
|
||||
else if (value < num3)
|
||||
{
|
||||
amount = GenMath.RoundRandom(amount / 2f);
|
||||
if (dinfo.Def.armorCategory == DamageArmorCategoryDefOf.Sharp)
|
||||
{
|
||||
dinfo.Def = DamageDefOf.Blunt;
|
||||
}
|
||||
}
|
||||
}
|
||||
return amount;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
20
Source/ArachnaeSwarm/PowerArmor/CompPowerArmorStation.cs
Normal file
20
Source/ArachnaeSwarm/PowerArmor/CompPowerArmorStation.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using RimWorld;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class CompProperties_PowerArmorStation : CompProperties
|
||||
{
|
||||
public ThingDef apparelDef;
|
||||
|
||||
public CompProperties_PowerArmorStation()
|
||||
{
|
||||
compClass = typeof(CompPowerArmorStation);
|
||||
}
|
||||
}
|
||||
|
||||
public class CompPowerArmorStation : ThingComp
|
||||
{
|
||||
public CompProperties_PowerArmorStation Props => (CompProperties_PowerArmorStation)props;
|
||||
}
|
||||
}
|
||||
55
Source/ArachnaeSwarm/PowerArmor/Gizmo_FuelPanel.cs
Normal file
55
Source/ArachnaeSwarm/PowerArmor/Gizmo_FuelPanel.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
using RimWorld; // For SolidColorMaterials
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[StaticConstructorOnStartup]
|
||||
public class Gizmo_FuelPanel : Gizmo
|
||||
{
|
||||
private static readonly Texture2D FullBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.9f, 0.7f, 0.2f)); // Orange for fuel
|
||||
private static readonly Texture2D EmptyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.1f, 0.1f));
|
||||
|
||||
private CompRefuelableNutrition fuelComp;
|
||||
|
||||
public Gizmo_FuelPanel(CompRefuelableNutrition fuelComp)
|
||||
{
|
||||
this.fuelComp = fuelComp;
|
||||
this.Order = -90f; // Slightly higher order than structure panel
|
||||
}
|
||||
|
||||
public override float GetWidth(float maxWidth)
|
||||
{
|
||||
return 140f;
|
||||
}
|
||||
|
||||
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
|
||||
{
|
||||
Rect overRect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f);
|
||||
Find.WindowStack.ImmediateWindow(984989, overRect, WindowLayer.GameUI, delegate
|
||||
{
|
||||
Rect rect = overRect.AtZero().ContractedBy(6f);
|
||||
|
||||
// Draw label
|
||||
Rect labelRect = rect;
|
||||
labelRect.height = overRect.height / 2f;
|
||||
Text.Font = GameFont.Tiny;
|
||||
Widgets.Label(labelRect, "Fuel".Translate()); // Use "Fuel" or custom text
|
||||
|
||||
// Draw bar
|
||||
Rect barRect = rect;
|
||||
barRect.yMin = overRect.height / 2f;
|
||||
float fillPercent = fuelComp.FuelPercentOfMax;
|
||||
|
||||
Widgets.FillableBar(barRect, fillPercent, FullBarTex, EmptyBarTex, false);
|
||||
|
||||
// Draw text on bar
|
||||
Text.Font = GameFont.Small;
|
||||
Text.Anchor = TextAnchor.MiddleCenter;
|
||||
Widgets.Label(barRect, $"{fuelComp.Fuel:F0} / {fuelComp.Props.fuelCapacity:F0}");
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
});
|
||||
return new GizmoResult(GizmoState.Clear);
|
||||
}
|
||||
}
|
||||
}
|
||||
55
Source/ArachnaeSwarm/PowerArmor/Gizmo_StructurePanel.cs
Normal file
55
Source/ArachnaeSwarm/PowerArmor/Gizmo_StructurePanel.cs
Normal file
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[StaticConstructorOnStartup]
|
||||
public class Gizmo_StructurePanel : Gizmo
|
||||
{
|
||||
private static readonly Texture2D FullBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.2f, 0.8f, 0.2f));
|
||||
private static readonly Texture2D EmptyBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.1f, 0.1f, 0.1f));
|
||||
private static readonly Texture2D UnderflowBarTex = SolidColorMaterials.NewSolidColorTexture(new Color(0.9f, 0.1f, 0.1f));
|
||||
|
||||
private IStructurePoints armor;
|
||||
|
||||
public Gizmo_StructurePanel(IStructurePoints armor)
|
||||
{
|
||||
this.armor = armor;
|
||||
this.Order = -100f; // Correctly use the 'Order' property
|
||||
}
|
||||
|
||||
public override float GetWidth(float maxWidth)
|
||||
{
|
||||
return 140f;
|
||||
}
|
||||
|
||||
public override GizmoResult GizmoOnGUI(Vector2 topLeft, float maxWidth, GizmoRenderParms parms)
|
||||
{
|
||||
Rect overRect = new Rect(topLeft.x, topLeft.y, GetWidth(maxWidth), 75f);
|
||||
Find.WindowStack.ImmediateWindow(984988, overRect, WindowLayer.GameUI, delegate
|
||||
{
|
||||
Rect rect = overRect.AtZero().ContractedBy(6f);
|
||||
|
||||
// Draw label
|
||||
Rect labelRect = rect;
|
||||
labelRect.height = overRect.height / 2f;
|
||||
Text.Font = GameFont.Tiny;
|
||||
Widgets.Label(labelRect, armor.Label);
|
||||
|
||||
// Draw bar
|
||||
Rect barRect = rect;
|
||||
barRect.yMin = overRect.height / 2f;
|
||||
float fillPercent = armor.StructurePointsPercent;
|
||||
|
||||
Widgets.FillableBar(barRect, fillPercent, FullBarTex, EmptyBarTex, false);
|
||||
|
||||
// Draw text on bar
|
||||
Text.Font = GameFont.Small;
|
||||
Text.Anchor = TextAnchor.MiddleCenter;
|
||||
Widgets.Label(barRect, $"{armor.StructurePoints:F0} / {armor.StructurePointsMax:F0}");
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
});
|
||||
return new GizmoResult(GizmoState.Clear);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using HarmonyLib;
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
[HarmonyPatch(typeof(ThingWithComps), "GetFloatMenuOptions")]
|
||||
public static class Harmony_ThingWithComps_GetFloatMenuOptions
|
||||
{
|
||||
[HarmonyPostfix]
|
||||
public static IEnumerable<FloatMenuOption> Postfix(IEnumerable<FloatMenuOption> values, ThingWithComps __instance, Pawn selPawn)
|
||||
{
|
||||
// First, return all original options
|
||||
foreach (var value in values)
|
||||
{
|
||||
yield return value;
|
||||
}
|
||||
|
||||
// --- DEBUG LOGGING ---
|
||||
// Use a more specific check to avoid log spam
|
||||
if (__instance.def.defName != null && __instance.def.defName.StartsWith("ARA_"))
|
||||
{
|
||||
Log.Message($"[PA_Debug] GetFloatMenuOptions Postfix triggered for: {__instance.def.defName}");
|
||||
}
|
||||
|
||||
// Check if the thing is our power armor building
|
||||
var comp = __instance.GetComp<CompPowerArmorStation>();
|
||||
|
||||
if (comp == null && __instance.def.defName != null && __instance.def.defName.StartsWith("ARA_"))
|
||||
{
|
||||
Log.Message($"[PA_Debug] CompPowerArmorStation is NULL for {__instance.def.defName}");
|
||||
}
|
||||
|
||||
if (comp != null)
|
||||
{
|
||||
Log.Message($"[PA_Debug] CompPowerArmorStation FOUND for {__instance.def.defName}. Checking reachability.");
|
||||
|
||||
// Check if the pawn can interact
|
||||
if (!selPawn.CanReserveAndReach(__instance, PathEndMode.InteractionCell, Danger.Deadly))
|
||||
{
|
||||
yield return new FloatMenuOption("CannotEnterPowerArmor".Translate() + ": " + "CannotReach".Translate(), null);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Action to give the job
|
||||
void enterAction()
|
||||
{
|
||||
Job job = JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("ARA_EnterPowerArmor"), __instance);
|
||||
selPawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
|
||||
}
|
||||
|
||||
yield return new FloatMenuOption("EnterPowerArmor".Translate(__instance.Label), enterAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
68
Source/ArachnaeSwarm/PowerArmor/JobDriver_EnterPowerArmor.cs
Normal file
68
Source/ArachnaeSwarm/PowerArmor/JobDriver_EnterPowerArmor.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
|
||||
namespace ArachnaeSwarm
|
||||
{
|
||||
public class JobDriver_EnterPowerArmor : JobDriver
|
||||
{
|
||||
private const TargetIndex BuildingInd = TargetIndex.A;
|
||||
private Building PowerArmorBuilding => (Building)job.GetTarget(BuildingInd).Thing;
|
||||
|
||||
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||
{
|
||||
return pawn.Reserve(PowerArmorBuilding, job, 1, -1, null, errorOnFailed);
|
||||
}
|
||||
|
||||
protected override IEnumerable<Toil> MakeNewToils()
|
||||
{
|
||||
this.FailOnDespawnedNullOrForbidden(BuildingInd);
|
||||
this.FailOnSomeonePhysicallyInteracting(BuildingInd);
|
||||
|
||||
yield return Toils_Goto.GotoThing(BuildingInd, PathEndMode.InteractionCell);
|
||||
yield return Toils_General.Wait(120, BuildingInd).WithProgressBarToilDelay(BuildingInd);
|
||||
|
||||
Toil enter = new Toil();
|
||||
enter.initAction = () =>
|
||||
{
|
||||
Pawn actor = enter.actor;
|
||||
var building = (Building)actor.CurJob.GetTarget(BuildingInd).Thing;
|
||||
|
||||
// Get the CompProperties from the building's new component
|
||||
var compProps = building.GetComp<CompPowerArmorStation>()?.Props;
|
||||
if (compProps != null && compProps.apparelDef != null)
|
||||
{
|
||||
// Create the apparel
|
||||
ARA_PowerArmor apparel = (ARA_PowerArmor)ThingMaker.MakeThing(compProps.apparelDef);
|
||||
|
||||
// CRITICAL STEP: Link the apparel back to the building
|
||||
apparel.sourceBuilding = building;
|
||||
|
||||
// Sync health from building to apparel
|
||||
apparel.StructurePoints = building.HitPoints;
|
||||
|
||||
// Sync fuel from building to apparel
|
||||
var buildingFuelComp = building.GetComp<CompRefuelableNutrition>();
|
||||
var apparelFuelComp = apparel.GetComp<CompRefuelableNutrition>();
|
||||
if (buildingFuelComp != null && apparelFuelComp != null)
|
||||
{
|
||||
apparelFuelComp.ReceiveFuel(buildingFuelComp.Fuel);
|
||||
}
|
||||
|
||||
// Wear the apparel
|
||||
actor.apparel.Wear(apparel, true, true);
|
||||
|
||||
// Despawn the building
|
||||
building.DeSpawn();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Error($"[ArachnaeSwarm] Power Armor Building {building.def.defName} is missing CompProperties_PowerArmorStation or apparelDef.");
|
||||
}
|
||||
};
|
||||
enter.defaultCompleteMode = ToilCompleteMode.Instant;
|
||||
yield return enter;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user