暂存feat(maintenance): 添加维护舱及相关需求机制
为乌拉族引入了新的“机体维护”需求,通过`WULA_Maintenance_Neglect`健康状况(Hediff)体现。该状况会随时间推移而恶化,影响角色能力。 新增建筑“维护舱”(`WULA_MaintenancePod`),乌拉族成员可进入其中进行维护,以清除“维护疏忽”的负面效果。维护过程需要消耗电力和零部件,所需零部件数量与负面效果的严重程度相关。 实现了配套的自动化工作逻辑: - 当维护需求达到阈值时,角色会自动进入维护舱。 - 当维护舱缺少零部件时,搬运工会自动为其装填。 此外,事件系统中增加了一个新的条件 `Condition_FactionExists`。
This commit is contained in:
Binary file not shown.
60
1.6/Defs/HediffDefs/Hediffs_WULA_Maintenance.xml
Normal file
60
1.6/Defs/HediffDefs/Hediffs_WULA_Maintenance.xml
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
<HediffDef>
|
||||||
|
<defName>WULA_Maintenance_Neglect</defName>
|
||||||
|
<label>维护</label>
|
||||||
|
<description>如果缺乏定期维护,身体机能将会出现衰退迹象。需要进入维护舱进行修复。</description>
|
||||||
|
<hediffClass>HediffWithComps</hediffClass>
|
||||||
|
<defaultLabelColor>(0.8, 0.35, 0.35)</defaultLabelColor>
|
||||||
|
<isBad>true</isBad>
|
||||||
|
<comps>
|
||||||
|
<li Class="WulaFallenEmpire.HediffCompProperties_MaintenanceNeed">
|
||||||
|
<thresholdDays>60</thresholdDays>
|
||||||
|
<severityPerDayBeforeThreshold>0.004</severityPerDayBeforeThreshold>
|
||||||
|
<severityPerDayAfterThreshold>0.02</severityPerDayAfterThreshold>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
|
<stages>
|
||||||
|
<li>
|
||||||
|
<label>极佳</label>
|
||||||
|
<minSeverity>0</minSeverity>
|
||||||
|
<capMods>
|
||||||
|
<li>
|
||||||
|
<capacity>Consciousness</capacity>
|
||||||
|
<offset>0.1</offset>
|
||||||
|
</li>
|
||||||
|
</capMods>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>稳定</label>
|
||||||
|
<minSeverity>0.5</minSeverity>
|
||||||
|
<capMods>
|
||||||
|
<li>
|
||||||
|
<capacity>Consciousness</capacity>
|
||||||
|
<offset>0</offset>
|
||||||
|
</li>
|
||||||
|
</capMods>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>需要</label>
|
||||||
|
<minSeverity>0.75</minSeverity>
|
||||||
|
<capMods>
|
||||||
|
<li>
|
||||||
|
<capacity>Consciousness</capacity>
|
||||||
|
<offset>-0.10</offset>
|
||||||
|
</li>
|
||||||
|
</capMods>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>损坏</label>
|
||||||
|
<minSeverity>1.0</minSeverity>
|
||||||
|
<capMods>
|
||||||
|
<li>
|
||||||
|
<capacity>Consciousness</capacity>
|
||||||
|
<offset>-0.25</offset>
|
||||||
|
</li>
|
||||||
|
</capMods>
|
||||||
|
</li>
|
||||||
|
</stages>
|
||||||
|
</HediffDef>
|
||||||
|
</Defs>
|
||||||
17
1.6/Defs/JobDefs/WULA_Jobs_Maintenance.xml
Normal file
17
1.6/Defs/JobDefs/WULA_Jobs_Maintenance.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<JobDef>
|
||||||
|
<defName>WULA_LoadComponentsToMaintenancePod</defName>
|
||||||
|
<driverClass>WulaFallenEmpire.JobDriver_LoadComponents</driverClass>
|
||||||
|
<reportString>正在为维护舱装填零部件。</reportString>
|
||||||
|
<haulMode>ToCellNonStorage</haulMode>
|
||||||
|
</JobDef>
|
||||||
|
|
||||||
|
<JobDef>
|
||||||
|
<defName>WULA_EnterMaintenancePod</defName>
|
||||||
|
<driverClass>WulaFallenEmpire.JobDriver_EnterMaintenancePod</driverClass>
|
||||||
|
<reportString>正在进入维护舱。</reportString>
|
||||||
|
</JobDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
87
1.6/Defs/ThingDefs_Buildings/WULA_Buildings_Maintenance.xml
Normal file
87
1.6/Defs/ThingDefs_Buildings/WULA_Buildings_Maintenance.xml
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<ThingDef ParentName="BuildingBase">
|
||||||
|
<defName>WULA_MaintenancePod</defName>
|
||||||
|
<label>维护舱</label>
|
||||||
|
<description>一个为乌拉族设计的全自动维护舱。乌拉族需要定期进入其中进行身体机能的维护和校准,否则他们的身体会逐渐衰弱。维护过程需要消耗零部件。</description>
|
||||||
|
<containedPawnsSelectable>true</containedPawnsSelectable>
|
||||||
|
<graphicData>
|
||||||
|
<texPath>Things/Building/Misc/BiosculpterPod/BiosculpterPod</texPath>
|
||||||
|
<graphicClass>Graphic_Multi</graphicClass>
|
||||||
|
<shadowData>
|
||||||
|
<volume>(2.9,0.6,1.9)</volume>
|
||||||
|
</shadowData>
|
||||||
|
<drawSize>(3, 2)</drawSize>
|
||||||
|
</graphicData>
|
||||||
|
<drawerType>RealtimeOnly</drawerType>
|
||||||
|
<drawGUIOverlay>true</drawGUIOverlay>
|
||||||
|
<defaultPlacingRot>South</defaultPlacingRot>
|
||||||
|
<altitudeLayer>Building</altitudeLayer>
|
||||||
|
<passability>PassThroughOnly</passability>
|
||||||
|
<pathCost>42</pathCost>
|
||||||
|
<blockWind>true</blockWind>
|
||||||
|
<fillPercent>0.5</fillPercent>
|
||||||
|
<canOverlapZones>false</canOverlapZones>
|
||||||
|
<statBases>
|
||||||
|
<MaxHitPoints>250</MaxHitPoints>
|
||||||
|
<WorkToBuild>20000</WorkToBuild>
|
||||||
|
<Mass>50</Mass>
|
||||||
|
<Flammability>0.5</Flammability>
|
||||||
|
<Beauty>-5</Beauty>
|
||||||
|
</statBases>
|
||||||
|
<size>(3,2)</size>
|
||||||
|
<interactionCellOffset>(0,0,2)</interactionCellOffset>
|
||||||
|
<hasInteractionCell>true</hasInteractionCell>
|
||||||
|
<costList>
|
||||||
|
<Steel>150</Steel>
|
||||||
|
<ComponentIndustrial>10</ComponentIndustrial>
|
||||||
|
<ComponentSpacer>2</ComponentSpacer>
|
||||||
|
</costList>
|
||||||
|
<constructionSkillPrerequisite>8</constructionSkillPrerequisite>
|
||||||
|
<building>
|
||||||
|
<destroySound>BuildingDestroyed_Metal_Big</destroySound>
|
||||||
|
<uninstallWork>1800</uninstallWork>
|
||||||
|
<defaultStorageSettings>
|
||||||
|
<filter>
|
||||||
|
<thingDefs>
|
||||||
|
<li>ComponentIndustrial</li>
|
||||||
|
</thingDefs>
|
||||||
|
</filter>
|
||||||
|
</defaultStorageSettings>
|
||||||
|
</building>
|
||||||
|
<designationCategory>Misc</designationCategory>
|
||||||
|
<minifiedDef>MinifiedThing</minifiedDef>
|
||||||
|
<thingCategories>
|
||||||
|
<li>BuildingsMisc</li>
|
||||||
|
</thingCategories>
|
||||||
|
<tickerType>Normal</tickerType>
|
||||||
|
<comps>
|
||||||
|
<li Class="CompProperties_Power">
|
||||||
|
<compClass>CompPowerTrader</compClass>
|
||||||
|
<basePowerConsumption>50</basePowerConsumption> <!-- This is now idle power -->
|
||||||
|
</li>
|
||||||
|
<li Class="CompProperties_Flickable"/>
|
||||||
|
<li Class="WulaFallenEmpire.CompProperties_MaintenancePod">
|
||||||
|
<durationTicks>60000</durationTicks> <!-- 1 day -->
|
||||||
|
<powerConsumptionRunning>250</powerConsumptionRunning>
|
||||||
|
<powerConsumptionIdle>50</powerConsumptionIdle>
|
||||||
|
<hediffToRemove>WULA_Maintenance_Neglect</hediffToRemove>
|
||||||
|
<componentDef>ComponentIndustrial</componentDef>
|
||||||
|
<componentCostPerSeverity>5</componentCostPerSeverity> <!-- 5 components per 100% severity -->
|
||||||
|
<baseComponentCost>0</baseComponentCost>
|
||||||
|
<minSeverityToMaintain>0.75</minSeverityToMaintain>
|
||||||
|
<enterSound>BiosculpterPod_Enter</enterSound>
|
||||||
|
<exitSound>BiosculpterPod_Exit</exitSound>
|
||||||
|
<operatingEffecter>BiosculpterPod_Operating</operatingEffecter>
|
||||||
|
</li>
|
||||||
|
</comps>
|
||||||
|
<placeWorkers>
|
||||||
|
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
|
||||||
|
</placeWorkers>
|
||||||
|
<inspectorTabs>
|
||||||
|
<li>ITab_Storage</li>
|
||||||
|
</inspectorTabs>
|
||||||
|
</ThingDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
27
1.6/Defs/WorkGiverDefs/WULA_WorkGivers.xml
Normal file
27
1.6/Defs/WorkGiverDefs/WULA_WorkGivers.xml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" ?>
|
||||||
|
<Defs>
|
||||||
|
|
||||||
|
<WorkGiverDef>
|
||||||
|
<defName>WULA_LoadComponentsToMaintenancePod</defName>
|
||||||
|
<label>装填维护舱</label>
|
||||||
|
<giverClass>WulaFallenEmpire.WorkGiver_LoadComponents</giverClass>
|
||||||
|
<workType>Hauling</workType>
|
||||||
|
<verb>装填</verb>
|
||||||
|
<gerund>装填于</gerund>
|
||||||
|
<priorityInType>110</priorityInType> <!-- High priority for hauling -->
|
||||||
|
<requiredCapacities>
|
||||||
|
<li>Manipulation</li>
|
||||||
|
</requiredCapacities>
|
||||||
|
</WorkGiverDef>
|
||||||
|
|
||||||
|
<WorkGiverDef>
|
||||||
|
<defName>WULA_EnterMaintenancePod</defName>
|
||||||
|
<label>进行机体维护</label>
|
||||||
|
<giverClass>WulaFallenEmpire.WorkGiver_EnterMaintenancePod</giverClass>
|
||||||
|
<workType>Patient</workType>
|
||||||
|
<verb>进行维护</verb>
|
||||||
|
<gerund>进行维护于</gerund>
|
||||||
|
<priorityInType>100</priorityInType> <!-- High priority for self-care -->
|
||||||
|
</WorkGiverDef>
|
||||||
|
|
||||||
|
</Defs>
|
||||||
35
Source/WulaFallenEmpire/Building_MaintenancePod.cs
Normal file
35
Source/WulaFallenEmpire/Building_MaintenancePod.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class CompProperties_MaintenanceCycle : CompProperties_BiosculpterPod_BaseCycle
|
||||||
|
{
|
||||||
|
public HediffDef hediffToRemove;
|
||||||
|
|
||||||
|
public CompProperties_MaintenanceCycle()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompMaintenanceCycle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompMaintenanceCycle : CompBiosculpterPod_Cycle
|
||||||
|
{
|
||||||
|
public new CompProperties_MaintenanceCycle Props => (CompProperties_MaintenanceCycle)props;
|
||||||
|
|
||||||
|
public override void CycleCompleted(Pawn pawn)
|
||||||
|
{
|
||||||
|
if (pawn == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.hediffToRemove);
|
||||||
|
if (hediff != null)
|
||||||
|
{
|
||||||
|
hediff.Severity = 0f;
|
||||||
|
Messages.Message("WULA_MaintenanceCycleComplete".Translate(pawn.Named("PAWN")), pawn, MessageTypeDefOf.PositiveEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
298
Source/WulaFallenEmpire/CompMaintenancePod.cs
Normal file
298
Source/WulaFallenEmpire/CompMaintenancePod.cs
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
// Properties for our custom maintenance pod
|
||||||
|
public class CompProperties_MaintenancePod : CompProperties
|
||||||
|
{
|
||||||
|
public SoundDef enterSound;
|
||||||
|
public SoundDef exitSound;
|
||||||
|
public EffecterDef operatingEffecter;
|
||||||
|
public int durationTicks = 60000; // Default to 1 day
|
||||||
|
public float powerConsumptionRunning = 250f;
|
||||||
|
public float powerConsumptionIdle = 50f;
|
||||||
|
public HediffDef hediffToRemove;
|
||||||
|
public ThingDef componentDef;
|
||||||
|
public float componentCostPerSeverity = 1f; // How many components per 1.0 severity
|
||||||
|
public int baseComponentCost = 0; // A flat cost in addition to severity cost
|
||||||
|
public float minSeverityToMaintain = 0.75f; // The hediff severity required to trigger maintenance
|
||||||
|
|
||||||
|
public CompProperties_MaintenancePod()
|
||||||
|
{
|
||||||
|
compClass = typeof(CompMaintenancePod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[StaticConstructorOnStartup]
|
||||||
|
public class CompMaintenancePod : ThingComp, IThingHolder, IStoreSettingsParent
|
||||||
|
{
|
||||||
|
// ===================== Fields =====================
|
||||||
|
private ThingOwner innerContainer;
|
||||||
|
private CompPowerTrader powerComp;
|
||||||
|
private StorageSettings allowedComponentSettings;
|
||||||
|
|
||||||
|
public float storedComponents = 0f;
|
||||||
|
private int ticksRemaining;
|
||||||
|
private MaintenancePodState state = MaintenancePodState.Idle;
|
||||||
|
|
||||||
|
private static readonly Texture2D CancelIcon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel");
|
||||||
|
private static readonly Texture2D EnterIcon = ContentFinder<Texture2D>.Get("UI/Commands/PodEject"); // Re-using an icon
|
||||||
|
|
||||||
|
// ===================== Properties =====================
|
||||||
|
public CompProperties_MaintenancePod Props => (CompProperties_MaintenancePod)props;
|
||||||
|
public MaintenancePodState State => state;
|
||||||
|
public Pawn Occupant => innerContainer.FirstOrDefault() as Pawn;
|
||||||
|
public bool PowerOn => powerComp != null && powerComp.PowerOn;
|
||||||
|
public float RequiredComponents(Pawn pawn)
|
||||||
|
{
|
||||||
|
if (pawn == null || Props.hediffToRemove == null) return Props.baseComponentCost;
|
||||||
|
Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(Props.hediffToRemove);
|
||||||
|
if (hediff == null) return Props.baseComponentCost;
|
||||||
|
return Props.baseComponentCost + (int)(hediff.Severity * Props.componentCostPerSeverity);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== Setup =====================
|
||||||
|
public CompMaintenancePod()
|
||||||
|
{
|
||||||
|
innerContainer = new ThingOwner<Thing>(this, false, LookMode.Deep);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize(CompProperties props)
|
||||||
|
{
|
||||||
|
base.Initialize(props);
|
||||||
|
// Setup allowed thing filter for components
|
||||||
|
allowedComponentSettings = new StorageSettings(this);
|
||||||
|
if (parent.def.building.defaultStorageSettings != null)
|
||||||
|
{
|
||||||
|
allowedComponentSettings.CopyFrom(parent.def.building.defaultStorageSettings);
|
||||||
|
}
|
||||||
|
else if (Props.componentDef != null)
|
||||||
|
{
|
||||||
|
allowedComponentSettings.filter = new ThingFilter();
|
||||||
|
allowedComponentSettings.filter.SetAllow(Props.componentDef, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostSpawnSetup(bool respawningAfterLoad)
|
||||||
|
{
|
||||||
|
base.PostSpawnSetup(respawningAfterLoad);
|
||||||
|
powerComp = parent.TryGetComp<CompPowerTrader>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void PostExposeData()
|
||||||
|
{
|
||||||
|
base.PostExposeData();
|
||||||
|
Scribe_Values.Look(ref state, "state", MaintenancePodState.Idle);
|
||||||
|
Scribe_Values.Look(ref storedComponents, "storedComponents", 0f);
|
||||||
|
Scribe_Values.Look(ref ticksRemaining, "ticksRemaining", 0);
|
||||||
|
Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
|
||||||
|
Scribe_Deep.Look(ref allowedComponentSettings, "allowedComponentSettings", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== IThingHolder Implementation =====================
|
||||||
|
public void GetChildHolders(List<IThingHolder> outChildren)
|
||||||
|
{
|
||||||
|
ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ThingOwner GetDirectlyHeldThings()
|
||||||
|
{
|
||||||
|
return innerContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== IStoreSettingsParent Implementation =====================
|
||||||
|
public StorageSettings GetStoreSettings() => allowedComponentSettings;
|
||||||
|
public StorageSettings GetParentStoreSettings() => parent.def.building.fixedStorageSettings;
|
||||||
|
public void Notify_SettingsChanged() { }
|
||||||
|
public bool StorageTabVisible => true;
|
||||||
|
|
||||||
|
|
||||||
|
// ===================== Core Logic =====================
|
||||||
|
public override void CompTick()
|
||||||
|
{
|
||||||
|
base.CompTick();
|
||||||
|
|
||||||
|
if (!parent.Spawned) return;
|
||||||
|
|
||||||
|
if (state == MaintenancePodState.Running)
|
||||||
|
{
|
||||||
|
if (PowerOn)
|
||||||
|
{
|
||||||
|
ticksRemaining--;
|
||||||
|
if (ticksRemaining <= 0)
|
||||||
|
{
|
||||||
|
CycleFinished();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update power consumption based on state
|
||||||
|
if (powerComp != null)
|
||||||
|
{
|
||||||
|
powerComp.PowerOutput = - (state == MaintenancePodState.Running ? Props.powerConsumptionRunning : Props.powerConsumptionIdle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartCycle(Pawn pawn)
|
||||||
|
{
|
||||||
|
float required = RequiredComponents(pawn);
|
||||||
|
if (storedComponents < required)
|
||||||
|
{
|
||||||
|
Log.Error($"[WulaFallenEmpire] Tried to start maintenance cycle for {pawn.LabelShort} without enough components. This should have been checked earlier.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storedComponents -= required;
|
||||||
|
state = MaintenancePodState.Running;
|
||||||
|
ticksRemaining = Props.durationTicks;
|
||||||
|
|
||||||
|
// Move pawn inside
|
||||||
|
pawn.DeSpawn();
|
||||||
|
innerContainer.TryAdd(pawn, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CycleFinished()
|
||||||
|
{
|
||||||
|
Pawn occupant = Occupant;
|
||||||
|
if (occupant == null)
|
||||||
|
{
|
||||||
|
Log.Error("[WulaFallenEmpire] Maintenance cycle finished, but no one was inside.");
|
||||||
|
state = MaintenancePodState.Idle;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply effects
|
||||||
|
if (Props.hediffToRemove != null)
|
||||||
|
{
|
||||||
|
Hediff hediff = occupant.health.hediffSet.GetFirstHediffOfDef(Props.hediffToRemove);
|
||||||
|
if (hediff != null)
|
||||||
|
{
|
||||||
|
occupant.health.RemoveHediff(hediff);
|
||||||
|
Messages.Message("WULA_MaintenanceComplete".Translate(occupant.Named("PAWN")), occupant, MessageTypeDefOf.PositiveEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EjectPawn();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void EjectPawn()
|
||||||
|
{
|
||||||
|
Pawn occupant = Occupant;
|
||||||
|
if (occupant != null)
|
||||||
|
{
|
||||||
|
GenPlace.TryPlaceThing(occupant, parent.InteractionCell, parent.Map, ThingPlaceMode.Near);
|
||||||
|
if (Props.exitSound != null)
|
||||||
|
{
|
||||||
|
Props.exitSound.PlayOneShot(new TargetInfo(parent.Position, parent.Map));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
innerContainer.Clear();
|
||||||
|
state = MaintenancePodState.Idle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddComponents(Thing components)
|
||||||
|
{
|
||||||
|
int count = components.stackCount;
|
||||||
|
storedComponents += count;
|
||||||
|
components.Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===================== UI & Gizmos =====================
|
||||||
|
public override string CompInspectStringExtra()
|
||||||
|
{
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.AppendLine("WULA_MaintenancePod_Status".Translate() + ": " + $"WULA_MaintenancePod_State_{state}".Translate());
|
||||||
|
|
||||||
|
if (state == MaintenancePodState.Running)
|
||||||
|
{
|
||||||
|
sb.AppendLine("Contains".Translate() + ": " + Occupant.NameShortColored.Resolve());
|
||||||
|
sb.AppendLine("TimeLeft".Translate() + ": " + ticksRemaining.ToStringTicksToPeriod());
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine("WULA_MaintenancePod_StoredComponents".Translate() + ": " + storedComponents.ToString("F0"));
|
||||||
|
|
||||||
|
if (!PowerOn)
|
||||||
|
{
|
||||||
|
sb.AppendLine("NoPower".Translate().Colorize(Color.red));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString().TrimEnd();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IEnumerable<Gizmo> CompGetGizmosExtra()
|
||||||
|
{
|
||||||
|
// Gizmo to order a pawn to enter
|
||||||
|
if (state == MaintenancePodState.Idle && PowerOn)
|
||||||
|
{
|
||||||
|
var enterCommand = new Command_Action
|
||||||
|
{
|
||||||
|
defaultLabel = "WULA_MaintenancePod_Enter".Translate(),
|
||||||
|
defaultDesc = "WULA_MaintenancePod_EnterDesc".Translate(),
|
||||||
|
icon = EnterIcon,
|
||||||
|
action = () =>
|
||||||
|
{
|
||||||
|
List<FloatMenuOption> options = new List<FloatMenuOption>();
|
||||||
|
foreach (Pawn p in parent.Map.mapPawns.FreeColonists)
|
||||||
|
{
|
||||||
|
if (p.health.hediffSet.HasHediff(Props.hediffToRemove))
|
||||||
|
{
|
||||||
|
float required = RequiredComponents(p);
|
||||||
|
if (storedComponents >= required)
|
||||||
|
{
|
||||||
|
options.Add(new FloatMenuOption(p.LabelShort, () =>
|
||||||
|
{
|
||||||
|
// TODO: Create and assign job
|
||||||
|
Messages.Message("This needs a JobDriver.", MessageTypeDefOf.RejectInput);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
options.Add(new FloatMenuOption(p.LabelShort + " (" + "WULA_MaintenancePod_NotEnoughComponents".Translate(required.ToString("F0")) + ")", null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (options.Any())
|
||||||
|
{
|
||||||
|
Find.WindowStack.Add(new FloatMenu(options));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Messages.Message("WULA_MaintenancePod_NoOneNeeds".Translate(), MessageTypeDefOf.RejectInput);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
yield return enterCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gizmo to cancel and eject
|
||||||
|
if (state == MaintenancePodState.Running)
|
||||||
|
{
|
||||||
|
var cancelCommand = new Command_Action
|
||||||
|
{
|
||||||
|
defaultLabel = "CommandCancelConstructionLabel".Translate(),
|
||||||
|
defaultDesc = "WULA_MaintenancePod_CancelDesc".Translate(),
|
||||||
|
icon = CancelIcon,
|
||||||
|
action = () =>
|
||||||
|
{
|
||||||
|
EjectPawn();
|
||||||
|
Messages.Message("WULA_MaintenanceCanceled".Translate(), MessageTypeDefOf.NegativeEvent);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
yield return cancelCommand;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum MaintenancePodState
|
||||||
|
{
|
||||||
|
Idle, // Waiting for a pawn or components
|
||||||
|
Running, // Occupied and performing maintenance
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -151,4 +151,28 @@ namespace WulaFallenEmpire
|
|||||||
return met;
|
return met;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public class Condition_FactionExists : Condition
|
||||||
|
{
|
||||||
|
public FactionDef factionDef;
|
||||||
|
|
||||||
|
public override bool IsMet(out string reason)
|
||||||
|
{
|
||||||
|
if (factionDef == null)
|
||||||
|
{
|
||||||
|
reason = "FactionDef not specified in Condition_FactionExists.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool exists = Find.FactionManager.FirstFactionOfDef(factionDef) != null;
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
reason = $"Faction '{factionDef.label}' does not exist in the world.";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reason = "";
|
||||||
|
}
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
Source/WulaFallenEmpire/HediffComp_MaintenanceNeed.cs
Normal file
40
Source/WulaFallenEmpire/HediffComp_MaintenanceNeed.cs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
using Verse;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class HediffCompProperties_MaintenanceNeed : HediffCompProperties
|
||||||
|
{
|
||||||
|
public float severityPerDayBeforeThreshold = 0.0f;
|
||||||
|
public float severityPerDayAfterThreshold = 0.0f;
|
||||||
|
public float thresholdDays = 0.0f;
|
||||||
|
|
||||||
|
public HediffCompProperties_MaintenanceNeed()
|
||||||
|
{
|
||||||
|
compClass = typeof(HediffComp_MaintenanceNeed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class HediffComp_MaintenanceNeed : HediffComp
|
||||||
|
{
|
||||||
|
private HediffCompProperties_MaintenanceNeed Props => (HediffCompProperties_MaintenanceNeed)props;
|
||||||
|
|
||||||
|
public override void CompPostTick(ref float severityAdjustment)
|
||||||
|
{
|
||||||
|
base.CompPostTick(ref severityAdjustment);
|
||||||
|
|
||||||
|
// We adjust severity once per game day (60000 ticks)
|
||||||
|
if (parent.ageTicks % 60000 == 0)
|
||||||
|
{
|
||||||
|
float ageInDays = (float)parent.ageTicks / 60000f;
|
||||||
|
if (ageInDays < Props.thresholdDays)
|
||||||
|
{
|
||||||
|
severityAdjustment += Props.severityPerDayBeforeThreshold;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
severityAdjustment += Props.severityPerDayAfterThreshold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
42
Source/WulaFallenEmpire/JobDriver_EnterMaintenancePod.cs
Normal file
42
Source/WulaFallenEmpire/JobDriver_EnterMaintenancePod.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class JobDriver_EnterMaintenancePod : JobDriver
|
||||||
|
{
|
||||||
|
private const TargetIndex PodIndex = TargetIndex.A;
|
||||||
|
|
||||||
|
protected Thing Pod => job.GetTarget(PodIndex).Thing;
|
||||||
|
|
||||||
|
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||||
|
{
|
||||||
|
return pawn.Reserve(Pod, job, 1, -1, null, errorOnFailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<Toil> MakeNewToils()
|
||||||
|
{
|
||||||
|
this.FailOnDespawnedNullOrForbidden(PodIndex);
|
||||||
|
this.FailOnBurningImmobile(PodIndex);
|
||||||
|
|
||||||
|
var podComp = Pod.TryGetComp<CompMaintenancePod>();
|
||||||
|
this.FailOn(() => podComp == null || podComp.State != MaintenancePodState.Idle || !podComp.PowerOn);
|
||||||
|
|
||||||
|
// Go to the pod's interaction cell
|
||||||
|
yield return Toils_Goto.GotoThing(PodIndex, PathEndMode.InteractionCell);
|
||||||
|
|
||||||
|
// Enter the pod
|
||||||
|
yield return new Toil
|
||||||
|
{
|
||||||
|
initAction = () =>
|
||||||
|
{
|
||||||
|
podComp.StartCycle(pawn);
|
||||||
|
},
|
||||||
|
defaultCompleteMode = ToilCompleteMode.Instant
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
53
Source/WulaFallenEmpire/JobDriver_LoadComponents.cs
Normal file
53
Source/WulaFallenEmpire/JobDriver_LoadComponents.cs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class JobDriver_LoadComponents : JobDriver
|
||||||
|
{
|
||||||
|
private const TargetIndex PodIndex = TargetIndex.A;
|
||||||
|
private const TargetIndex ComponentIndex = TargetIndex.B;
|
||||||
|
|
||||||
|
protected Thing Pod => job.GetTarget(PodIndex).Thing;
|
||||||
|
protected Thing Component => job.GetTarget(ComponentIndex).Thing;
|
||||||
|
|
||||||
|
public override bool TryMakePreToilReservations(bool errorOnFailed)
|
||||||
|
{
|
||||||
|
if (pawn.Reserve(Pod, job, 1, -1, null, errorOnFailed))
|
||||||
|
{
|
||||||
|
return pawn.Reserve(Component, job, 1, -1, null, errorOnFailed);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<Toil> MakeNewToils()
|
||||||
|
{
|
||||||
|
this.FailOnDespawnedNullOrForbidden(PodIndex);
|
||||||
|
this.FailOnBurningImmobile(PodIndex);
|
||||||
|
|
||||||
|
var podComp = Pod.TryGetComp<CompMaintenancePod>();
|
||||||
|
this.FailOn(() => podComp == null || podComp.State != MaintenancePodState.Idle);
|
||||||
|
|
||||||
|
// Go and get the components
|
||||||
|
yield return Toils_Goto.GotoThing(ComponentIndex, PathEndMode.OnCell).FailOnSomeonePhysicallyInteracting(ComponentIndex);
|
||||||
|
yield return Toils_Haul.StartCarryThing(ComponentIndex);
|
||||||
|
|
||||||
|
// Carry them to the pod
|
||||||
|
yield return Toils_Goto.GotoThing(PodIndex, PathEndMode.InteractionCell);
|
||||||
|
|
||||||
|
// Load the components
|
||||||
|
yield return Toils_General.WaitWith(60, TargetIndex.A, true, true, false, PodIndex);
|
||||||
|
yield return new Toil
|
||||||
|
{
|
||||||
|
initAction = () =>
|
||||||
|
{
|
||||||
|
podComp.AddComponents(this.GetActor().carryTracker.CarriedThing);
|
||||||
|
},
|
||||||
|
defaultCompleteMode = ToilCompleteMode.Instant
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
Source/WulaFallenEmpire/Job_Maintenance.cs
Normal file
25
Source/WulaFallenEmpire/Job_Maintenance.cs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class JobDriver_EnterMaintenancePod : JobDriver_EnterBiosculpterPod
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public class WorkGiver_DoMaintenance : WorkGiver_Scanner
|
||||||
|
{
|
||||||
|
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForDef(ThingDef.Named("WULA_MaintenancePod"));
|
||||||
|
|
||||||
|
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||||
|
{
|
||||||
|
return pawn.CanReserve(t, 1, -1, null, forced);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||||
|
{
|
||||||
|
return JobMaker.MakeJob(DefDatabase<JobDef>.GetNamed("WULA_EnterMaintenancePod"), t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
62
Source/WulaFallenEmpire/WorkGiver_EnterMaintenancePod.cs
Normal file
62
Source/WulaFallenEmpire/WorkGiver_EnterMaintenancePod.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class WorkGiver_EnterMaintenancePod : WorkGiver_Scanner
|
||||||
|
{
|
||||||
|
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForDef(ThingDef.Named("WULA_MaintenancePod"));
|
||||||
|
|
||||||
|
public override PathEndMode PathEndMode => PathEndMode.InteractionCell;
|
||||||
|
|
||||||
|
// This method now checks the severity of the hediff.
|
||||||
|
public override bool ShouldSkip(Pawn pawn, bool forced = false)
|
||||||
|
{
|
||||||
|
var podDef = ThingDef.Named("WULA_MaintenancePod");
|
||||||
|
var podProps = podDef.GetCompProperties<CompProperties_MaintenancePod>();
|
||||||
|
if (podProps?.hediffToRemove == null) return true;
|
||||||
|
|
||||||
|
Hediff hediff = pawn.health.hediffSet.GetFirstHediffOfDef(podProps.hediffToRemove);
|
||||||
|
|
||||||
|
// Skip if no hediff or if severity is below the configured threshold.
|
||||||
|
if (hediff == null || hediff.Severity < podProps.minSeverityToMaintain)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||||
|
{
|
||||||
|
if (!(t is Building building) || !building.Spawned || building.IsForbidden(pawn) || !pawn.CanReserve(building, 1, -1, null, forced))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var podComp = building.GetComp<CompMaintenancePod>();
|
||||||
|
if (podComp == null || podComp.State != MaintenancePodState.Idle || !podComp.PowerOn)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float requiredComponents = podComp.RequiredComponents(pawn);
|
||||||
|
if (podComp.storedComponents < requiredComponents)
|
||||||
|
{
|
||||||
|
JobFailReason.Is("WULA_MaintenancePod_NotEnoughComponents".Translate(requiredComponents.ToString("F0")));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||||
|
{
|
||||||
|
return JobMaker.MakeJob(JobDefOf.WULA_EnterMaintenancePod, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
66
Source/WulaFallenEmpire/WorkGiver_LoadComponents.cs
Normal file
66
Source/WulaFallenEmpire/WorkGiver_LoadComponents.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using RimWorld;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Verse;
|
||||||
|
using Verse.AI;
|
||||||
|
|
||||||
|
namespace WulaFallenEmpire
|
||||||
|
{
|
||||||
|
public class WorkGiver_LoadComponents : WorkGiver_Scanner
|
||||||
|
{
|
||||||
|
public override ThingRequest PotentialWorkThingRequest => ThingRequest.ForDef(ThingDef.Named("WULA_MaintenancePod"));
|
||||||
|
|
||||||
|
public override PathEndMode PathEndMode => PathEndMode.Touch;
|
||||||
|
|
||||||
|
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||||
|
{
|
||||||
|
if (!(t is Building building) || !building.Spawned || building.IsForbidden(pawn) || !pawn.CanReserve(building, 1, -1, null, forced))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var podComp = building.GetComp<CompMaintenancePod>();
|
||||||
|
if (podComp == null || podComp.State != MaintenancePodState.Idle)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We define a "needed" threshold. Let's say we want to keep at least 10 components stocked.
|
||||||
|
// This prevents pawns from hauling one component at a time.
|
||||||
|
const int desiredStockpile = 10;
|
||||||
|
if (podComp.storedComponents >= desiredStockpile)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FindBestComponent(pawn, podComp) == null)
|
||||||
|
{
|
||||||
|
JobFailReason.Is("WULA_NoComponentsToLoad".Translate());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
|
||||||
|
{
|
||||||
|
var podComp = t.GetComp<CompMaintenancePod>();
|
||||||
|
Thing component = FindBestComponent(pawn, podComp);
|
||||||
|
if (component == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return JobMaker.MakeJob(JobDefOf.WULA_LoadComponentsToMaintenancePod, t, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Thing FindBestComponent(Pawn pawn, CompMaintenancePod pod)
|
||||||
|
{
|
||||||
|
ThingDef componentDef = pod.Props.componentDef;
|
||||||
|
if (componentDef == null) return null;
|
||||||
|
|
||||||
|
Predicate<Thing> validator = (Thing x) => !x.IsForbidden(pawn) && pawn.CanReserve(x);
|
||||||
|
return GenClosest.ClosestThingReachable(pawn.Position, pawn.Map, ThingRequest.ForDef(componentDef), PathEndMode.ClosestTouch, TraverseParms.For(pawn), 9999f, validator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
<Compile Include="PsychicRitual_TechOffering.cs" />
|
<Compile Include="PsychicRitual_TechOffering.cs" />
|
||||||
<Compile Include="PsychicRitualDef_AddHediff.cs" />
|
<Compile Include="PsychicRitualDef_AddHediff.cs" />
|
||||||
<Compile Include="PsychicRitualToil_AddHediff.cs" />
|
<Compile Include="PsychicRitualToil_AddHediff.cs" />
|
||||||
<Compile Include="MoharHediffs\HediffCompProperties_Spawner.cs" />
|
<Compile Include="MoharHediffs\HediffCompProperties_Spawner.cs" />
|
||||||
<Compile Include="MoharHediffs\HediffComp_Spawner.cs" />
|
<Compile Include="MoharHediffs\HediffComp_Spawner.cs" />
|
||||||
<Compile Include="MoharHediffs\Tools.cs" />
|
<Compile Include="MoharHediffs\Tools.cs" />
|
||||||
<Compile Include="RitualTagExtension.cs" />
|
<Compile Include="RitualTagExtension.cs" />
|
||||||
@@ -138,6 +138,9 @@
|
|||||||
<Compile Include="WulaCaravanEnergyDef.cs" />
|
<Compile Include="WulaCaravanEnergyDef.cs" />
|
||||||
<Compile Include="WulaFallenEmpireMod.cs" />
|
<Compile Include="WulaFallenEmpireMod.cs" />
|
||||||
<Compile Include="WulaStatDefOf.cs" />
|
<Compile Include="WulaStatDefOf.cs" />
|
||||||
|
<Compile Include="Building_MaintenancePod.cs" />
|
||||||
|
<Compile Include="HediffComp_MaintenanceNeed.cs" />
|
||||||
|
<Compile Include="Job_Maintenance.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||||
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
<!-- 自定义清理任务,删除obj文件夹中的临时文件 -->
|
||||||
|
|||||||
Reference in New Issue
Block a user