1
This commit is contained in:
Binary file not shown.
@@ -40,7 +40,6 @@
|
||||
<severityPerDayBeforeThreshold>0.05</severityPerDayBeforeThreshold> <!-- 严重退化前的速率 -->
|
||||
<severityPerDayAfterThreshold>0.1</severityPerDayAfterThreshold> <!-- 严重退化前的速率 -->
|
||||
<thresholdDays>5</thresholdDays> <!-- 严重退化预期天数 -->
|
||||
<maintenanceWorkType>PatientBedRest</maintenanceWorkType>
|
||||
<minorBreakdownHediff>WULA_Maintenance_MinorBreakdown</minorBreakdownHediff>
|
||||
<majorBreakdownHediff>WULA_Maintenance_MajorBreakdown</majorBreakdownHediff>
|
||||
<criticalFailureHediff>WULA_Maintenance_CriticalFailuren</criticalFailureHediff>
|
||||
|
||||
@@ -235,6 +235,15 @@
|
||||
<description>一台仿制乌拉帝国科技而建造的塑性构造体,不仅要消耗大量木头用以提供生物能,还只能生产基础的衣物和能源核心用以维持生存——不过它很轻,可以随探险队一起移动。</description>
|
||||
<thingClass>WulaFallenEmpire.Building_GlobalWorkTable</thingClass>
|
||||
<drawerType>MapMeshAndRealTime</drawerType>
|
||||
<tickerType>Normal</tickerType>
|
||||
<modExtensions>
|
||||
<li Class="WulaFallenEmpire.GlobalWorkTableAirdropExtension">
|
||||
<maxRange>50</maxRange>
|
||||
<randomRange>15</randomRange>
|
||||
<minPods>1</minPods>
|
||||
<maxPods>10</maxPods>
|
||||
</li>
|
||||
</modExtensions>
|
||||
<graphicData>
|
||||
<texPath>Wula/Building/WULA_Cube_Productor_BIO</texPath>
|
||||
<graphicClass>Graphic_Multi</graphicClass>
|
||||
@@ -259,7 +268,6 @@
|
||||
<WorkToBuild>2000</WorkToBuild>
|
||||
<MaxHitPoints>180</MaxHitPoints>
|
||||
<Flammability>1.0</Flammability>
|
||||
<WorkTableWorkSpeedFactor>0.5</WorkTableWorkSpeedFactor>
|
||||
</statBases>
|
||||
<size>(1,1)</size>
|
||||
<designationCategory>WULA_Buildings</designationCategory>
|
||||
@@ -282,21 +290,6 @@
|
||||
<li>WulaFallenEmpire.ITab_GlobalBills</li>
|
||||
</inspectorTabs>
|
||||
<comps>
|
||||
<li Class="CompProperties_Refuelable">
|
||||
<fuelConsumptionRate>300.0</fuelConsumptionRate>
|
||||
<fuelCapacity>150.0</fuelCapacity>
|
||||
<fuelFilter>
|
||||
<thingDefs>
|
||||
<li>WoodLog</li>
|
||||
</thingDefs>
|
||||
</fuelFilter>
|
||||
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
|
||||
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
|
||||
</li>
|
||||
<li Class="CompProperties_HeatPusher">
|
||||
<compClass>CompHeatPusherPowered</compClass>
|
||||
<heatPerSecond>4</heatPerSecond>
|
||||
</li>
|
||||
</comps>
|
||||
<placeWorkers>
|
||||
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
|
||||
@@ -307,4 +300,53 @@
|
||||
<heatPerTickWhileWorking>0.10</heatPerTickWhileWorking>
|
||||
</building>
|
||||
</ThingDef>
|
||||
<ThingDef ParentName="BuildingBase">
|
||||
<defName>WULA_ResourceSubmitter</defName>
|
||||
<label>乌拉帝国物资交换舱</label>
|
||||
<description>乌拉帝国从地面向舰队提交资源的特殊交换仓。</description>
|
||||
<thingClass>WulaFallenEmpire.Building_ResourceSubmitter</thingClass>
|
||||
<graphicData>
|
||||
<texPath>Things/Building/Furniture/Shelf</texPath>
|
||||
<graphicClass>Graphic_Multi</graphicClass>
|
||||
<drawSize>(1,1)</drawSize>
|
||||
</graphicData>
|
||||
<size>(1,1)</size>
|
||||
<rotatable>false</rotatable>
|
||||
<tickerType>Normal</tickerType>
|
||||
<altitudeLayer>BuildingOnTop</altitudeLayer>
|
||||
<passability>PassThroughOnly</passability>
|
||||
<castEdgeShadows>false</castEdgeShadows>
|
||||
<designationHotKey>Misc12</designationHotKey>
|
||||
<fillPercent>0.5</fillPercent>
|
||||
<designationCategory>WULA_Buildings</designationCategory>
|
||||
<uiOrder>2200</uiOrder>
|
||||
<statBases>
|
||||
<MaxHitPoints>250</MaxHitPoints>
|
||||
<WorkToBuild>1600</WorkToBuild>
|
||||
<Flammability>0.5</Flammability>
|
||||
</statBases>
|
||||
<costList>
|
||||
<Steel>20</Steel>
|
||||
</costList>
|
||||
<!-- <comps>
|
||||
<li Class="CompProperties_Transporter">
|
||||
<massCapacity>300</massCapacity>
|
||||
<restEffectiveness>0.8</restEffectiveness>
|
||||
<canChangeAssignedThingsAfterStarting>true</canChangeAssignedThingsAfterStarting>
|
||||
<max1PerGroup>true</max1PerGroup>
|
||||
</li>
|
||||
<li Class="CompProperties_Launchable_TransportPod">
|
||||
<skyfallerLeaving>ARA_DropPodLeaving</skyfallerLeaving>
|
||||
<requiresFuelingPort>false</requiresFuelingPort>
|
||||
<fixedLaunchDistanceMax>53</fixedLaunchDistanceMax>
|
||||
</li>
|
||||
</comps> -->
|
||||
<inspectorTabs>
|
||||
<li>WulaFallenEmpire.ITab_ResourceSubmitterContents</li>
|
||||
</inspectorTabs>
|
||||
<placeWorkers>
|
||||
<li>PlaceWorker_NotUnderRoof</li>
|
||||
</placeWorkers>
|
||||
<uiIconScale>0.65</uiIconScale>
|
||||
</ThingDef>
|
||||
</Defs>
|
||||
@@ -59,84 +59,6 @@
|
||||
</ThingDef>
|
||||
|
||||
<!-- 制造机 -->
|
||||
<ThingDef ParentName="BenchBase">
|
||||
<defName>WULA_Cube_Productor_BIO</defName>
|
||||
<label>乌拉帝国编织体(生物能)</label>
|
||||
<description>一台仿制乌拉帝国科技而建造的塑性构造体,不仅要消耗大量木头用以提供生物能,还只能生产基础的衣物和能源核心用以维持生存——不过它很轻,可以随探险队一起移动。</description>
|
||||
<thingClass>Building_WorkTable_HeatPush</thingClass>
|
||||
<drawerType>MapMeshAndRealTime</drawerType>
|
||||
<graphicData>
|
||||
<texPath>Wula/Building/WULA_Cube_Productor_BIO</texPath>
|
||||
<graphicClass>Graphic_Multi</graphicClass>
|
||||
<drawSize>(1,1)</drawSize>
|
||||
<damageData>
|
||||
<enabled>false</enabled>
|
||||
</damageData>
|
||||
<shadowData>
|
||||
<volume>(0.75, 0.75, 0.5)</volume>
|
||||
</shadowData>
|
||||
</graphicData>
|
||||
<constructEffect>ConstructMetal</constructEffect>
|
||||
<costList>
|
||||
<Steel>50</Steel>
|
||||
</costList>
|
||||
<altitudeLayer>Building</altitudeLayer>
|
||||
<castEdgeShadows>false</castEdgeShadows>
|
||||
<fillPercent>0.5</fillPercent>
|
||||
<useHitPoints>True</useHitPoints>
|
||||
<statBases>
|
||||
<Mass>5</Mass>
|
||||
<WorkToBuild>2000</WorkToBuild>
|
||||
<MaxHitPoints>180</MaxHitPoints>
|
||||
<Flammability>1.0</Flammability>
|
||||
<WorkTableWorkSpeedFactor>0.5</WorkTableWorkSpeedFactor>
|
||||
</statBases>
|
||||
<size>(1,1)</size>
|
||||
<designationCategory>WULA_Buildings</designationCategory>
|
||||
<uiOrder>2120</uiOrder>
|
||||
<passability>PassThroughOnly</passability>
|
||||
<pathCost>50</pathCost>
|
||||
<hasInteractionCell>True</hasInteractionCell>
|
||||
<interactionCellOffset>(0,0,-1)</interactionCellOffset>
|
||||
<surfaceType>Item</surfaceType>
|
||||
<researchPrerequisites>
|
||||
<li>WULA_Base_Technology</li>
|
||||
</researchPrerequisites>
|
||||
<!-- 可用配方 -->
|
||||
<recipes>
|
||||
<li>Make_WULA_Charge_Cube</li>
|
||||
<li>Recharge_WULA_Charge_Cube</li>
|
||||
<li MayRequire="Ludeon.RimWorld.Anomaly">Wula_Make_Zro</li>
|
||||
</recipes>
|
||||
<inspectorTabs>
|
||||
<li>ITab_Bills</li>
|
||||
</inspectorTabs>
|
||||
<comps>
|
||||
<li Class="CompProperties_Refuelable">
|
||||
<fuelConsumptionRate>300.0</fuelConsumptionRate>
|
||||
<fuelCapacity>150.0</fuelCapacity>
|
||||
<fuelFilter>
|
||||
<thingDefs>
|
||||
<li>WoodLog</li>
|
||||
</thingDefs>
|
||||
</fuelFilter>
|
||||
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
|
||||
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
|
||||
</li>
|
||||
<li Class="CompProperties_HeatPusher">
|
||||
<compClass>CompHeatPusherPowered</compClass>
|
||||
<heatPerSecond>4</heatPerSecond>
|
||||
</li>
|
||||
</comps>
|
||||
<placeWorkers>
|
||||
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
|
||||
</placeWorkers>
|
||||
<building>
|
||||
<!-- <isMealSource>true</isMealSource> -->
|
||||
<spawnedConceptLearnOpportunity>BillsTab</spawnedConceptLearnOpportunity>
|
||||
<heatPerTickWhileWorking>0.10</heatPerTickWhileWorking>
|
||||
</building>
|
||||
</ThingDef>
|
||||
<ThingDef ParentName="BenchBase">
|
||||
<defName>WULA_Cube_Productor_Energy</defName>
|
||||
<label>乌拉帝国编织体(电能)</label>
|
||||
|
||||
@@ -148,12 +148,87 @@
|
||||
<WULA_GlobalBillsTab>全球生产</WULA_GlobalBillsTab>
|
||||
<WULA_GlobalProduction>生产订单</WULA_GlobalProduction>
|
||||
<WULA_AddProductionOrder>添加生产订单</WULA_AddProductionOrder>
|
||||
<WULA_Resume>Resume</WULA_Resume>
|
||||
<WULA_Pause>Pause</WULA_Pause>
|
||||
<WULA_Delete>Delete</WULA_Delete>
|
||||
<WULA_Resume>恢复</WULA_Resume>
|
||||
<WULA_Pause>暂停</WULA_Pause>
|
||||
<WULA_Delete>删除</WULA_Delete>
|
||||
<WULA_WaitingForResources>等待资源</WULA_WaitingForResources>
|
||||
<WULA_Completed>Completed</WULA_Completed>
|
||||
<WULA_Unknown>Unknown</WULA_Unknown>
|
||||
<WULA_Completed>完成</WULA_Completed>
|
||||
<WULA_Unknown>未知</WULA_Unknown>
|
||||
<WULA_InsufficientResources>全球存储中的资源不足</WULA_InsufficientResources>
|
||||
<WULA_NoAvailableRecipes>无可用配方</WULA_NoAvailableRecipes>
|
||||
<!-- 添加到您的语言文件中 -->
|
||||
<WULA_RequiredIngredients>可用配方</WULA_RequiredIngredients>
|
||||
<WULA_Products>产物</WULA_Products>
|
||||
<WULA_WorkAmount>工作量</WULA_WorkAmount>
|
||||
|
||||
<!-- 中文翻译 -->
|
||||
<WULA_ViewStorage>查看存储</WULA_ViewStorage>
|
||||
<WULA_InputStorage>输入存储(原材料)</WULA_InputStorage>
|
||||
<WULA_OutputStorage>输出存储(产品)</WULA_OutputStorage>
|
||||
<WULA_NoGlobalStorage>未找到全局存储组件</WULA_NoGlobalStorage>
|
||||
<WULA_NoItems>无物品</WULA_NoItems>
|
||||
<WULA_StorageStats>存储统计</WULA_StorageStats>
|
||||
<WULA_InputItems>种输入物品</WULA_InputItems>
|
||||
<WULA_OutputItems>种输出物品</WULA_OutputItems>
|
||||
|
||||
<!-- 中文翻译 -->
|
||||
<WULA_AirdropProducts>空投产品</WULA_AirdropProducts>
|
||||
<WULA_AirdropProductsDesc>将产物储存器中的所有物品通过空投舱分发到指定区域</WULA_AirdropProductsDesc>
|
||||
<WULA_CannotAirdrop>无法执行空投:工作台未就绪</WULA_CannotAirdrop>
|
||||
<WULA_NoProductsToAirdrop>没有可空投的产品</WULA_NoProductsToAirdrop>
|
||||
<WULA_AirdropTargetTooFar>目标距离超出最大空投范围({0})</WULA_AirdropTargetTooFar>
|
||||
<WULA_NoValidDropSpots>没有有效的空投落点。请选择另一个位置</WULA_NoValidDropSpots>
|
||||
<WULA_FailedToDistributeItems>无法分配物品到空投舱</WULA_FailedToDistributeItems>
|
||||
<WULA_AirdropSuccessful>成功空投了{0}个空投舱</WULA_AirdropSuccessful>
|
||||
|
||||
<WULA_ResourceSubmitter>资源提交器</WULA_ResourceSubmitter>
|
||||
<WULA_SubmitToStorage>提交到储存器</WULA_SubmitToStorage>
|
||||
<WULA_SubmitToStorageDesc>将所有存储物品转移到全局储存</WULA_SubmitToStorageDesc>
|
||||
<WULA_StoredItems>存储物品</WULA_StoredItems>
|
||||
<WULA_ItemsSubmitted>{0}个物品已提交到储存器</WULA_ItemsSubmitted>
|
||||
<WULA_SubmissionFailed>提交物品失败</WULA_SubmissionFailed>
|
||||
<WULA_NoItemsToSubmit>没有可提交的物品</WULA_NoItemsToSubmit>
|
||||
<WULA_DeviceNotOperational>设备不可用</WULA_DeviceNotOperational>
|
||||
<WULA_NoPower>无电力</WULA_NoPower>
|
||||
<WULA_NoFuel>无燃料</WULA_NoFuel>
|
||||
<WULA_SwitchOff>开关关闭</WULA_SwitchOff>
|
||||
<WULA_Inoperative>不可用</WULA_Inoperative>
|
||||
<WULA_Status>状态</WULA_Status>
|
||||
|
||||
<WULA_SubmitterContents>提交器内容</WULA_SubmitterContents>
|
||||
<WULA_Operational>运行正常</WULA_Operational>
|
||||
<WULA_Inoperative>无法运行</WULA_Inoperative>
|
||||
<WULA_Status>状态</WULA_Status>
|
||||
<WULA_Items>物品</WULA_Items>
|
||||
<WULA_Stacks>堆叠</WULA_Stacks>
|
||||
<WULA_NoItemsInStorage>存储中无物品</WULA_NoItemsInStorage>
|
||||
<WULA_ItemName>物品名称</WULA_ItemName>
|
||||
<WULA_Count>数量</WULA_Count>
|
||||
<WULA_ItemTooltip>{0}\n总数: {1}\n堆叠数: {2}\n价值: {3} 白银</WULA_ItemTooltip>
|
||||
<WULA_SubmitToStorage>提交到存储</WULA_SubmitToStorage>
|
||||
<WULA_SubmitToStorageDesc>将所有存储物品提交到全局存储</WULA_SubmitToStorageDesc>
|
||||
<WULA_DeviceInoperative>设备无法运行</WULA_DeviceInoperative>
|
||||
<WULA_NoItemsToSubmit>无物品可提交</WULA_NoItemsToSubmit>
|
||||
<WULA_NoItemsToSubmitDesc>没有可以提交的物品</WULA_NoItemsToSubmitDesc>
|
||||
<WULA_ViewGlobalStorage>查看全局存储</WULA_ViewGlobalStorage>
|
||||
<WULA_ViewGlobalStorageDesc>查看全局输入和输出存储内容</WULA_ViewGlobalStorageDesc>
|
||||
<WULA_UnknownReason>未知原因</WULA_UnknownReason>
|
||||
<WULA_GlobalStorage>全局存储</WULA_GlobalStorage>
|
||||
<WULA_InputStorage>输入存储</WULA_InputStorage>
|
||||
<WULA_OutputStorage>输出存储</WULA_OutputStorage>
|
||||
<WULA_NoItems>无物品</WULA_NoItems>
|
||||
<WULA_Value>价值</WULA_Value>
|
||||
<!-- 中文翻译 -->
|
||||
<WULA_Items>物品</WULA_Items>
|
||||
<WULA_Stacks>堆叠</WULA_Stacks>
|
||||
<WULA_ItemName>物品名称</WULA_ItemName>
|
||||
<WULA_Count>数量</WULA_Count>
|
||||
<WULA_NoItemsInStorage>存储中无物品</WULA_NoItemsInStorage>
|
||||
<WULA_ItemTooltip>{0}\n总数: {1}\n堆叠: {2}\n价值: {3}</WULA_ItemTooltip>
|
||||
<WULA_DeviceInoperative>设备不可用</WULA_DeviceInoperative>
|
||||
<WULA_NoItemsToSubmitDesc>没有可提交的物品</WULA_NoItemsToSubmitDesc>
|
||||
<WULA_ViewGlobalStorage>查看全局存储</WULA_ViewGlobalStorage>
|
||||
<WULA_ViewGlobalStorageDesc>查看全局存储中的物品</WULA_ViewGlobalStorageDesc>
|
||||
<WULA_UnknownReason>未知原因</WULA_UnknownReason>
|
||||
<WULA_Value>价值</WULA_Value>
|
||||
</LanguageData>
|
||||
@@ -1,7 +1,10 @@
|
||||
// Building_GlobalWorkTable.cs (修复版)
|
||||
// Building_GlobalWorkTable.cs (调整为每秒处理)
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Verse;
|
||||
using Verse.AI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
@@ -10,8 +13,9 @@ namespace WulaFallenEmpire
|
||||
public GlobalProductionOrderStack globalOrderStack;
|
||||
|
||||
private CompPowerTrader powerComp;
|
||||
private CompBreakdownable breakdownableComp;
|
||||
private int lastProcessTick = -1;
|
||||
private const int ProcessInterval = 60; // 每60tick处理一次
|
||||
private const int ProcessInterval = 1; // 改为每tick处理,以实现每秒1工作量
|
||||
|
||||
public Building_GlobalWorkTable()
|
||||
{
|
||||
@@ -28,34 +32,281 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
base.SpawnSetup(map, respawningAfterLoad);
|
||||
powerComp = GetComp<CompPowerTrader>();
|
||||
breakdownableComp = GetComp<CompBreakdownable>();
|
||||
}
|
||||
|
||||
public override void Tick()
|
||||
protected override void Tick()
|
||||
{
|
||||
base.Tick();
|
||||
|
||||
// 每60tick处理一次生产订单
|
||||
// 改为每tick处理,以实现精确的工作量控制
|
||||
if (Find.TickManager.TicksGame % ProcessInterval == 0 &&
|
||||
Find.TickManager.TicksGame != lastProcessTick)
|
||||
{
|
||||
lastProcessTick = Find.TickManager.TicksGame;
|
||||
|
||||
if (powerComp == null || powerComp.PowerOn)
|
||||
if (CurrentlyUsableForGlobalBills())
|
||||
{
|
||||
Log.Message($"[DEBUG] Processing orders at tick {Find.TickManager.TicksGame}");
|
||||
globalOrderStack.ProcessOrders();
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Message("[DEBUG] No power, skipping order processing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool CurrentlyUsableForGlobalBills()
|
||||
{
|
||||
return (powerComp == null || powerComp.PowerOn) &&
|
||||
(GetComp<CompBreakdownable>() == null || !GetComp<CompBreakdownable>().BrokenDown);
|
||||
if (powerComp != null && !powerComp.PowerOn)
|
||||
return false;
|
||||
|
||||
if (breakdownableComp != null && breakdownableComp.BrokenDown)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 新增:获取空投扩展参数
|
||||
public GlobalWorkTableAirdropExtension AirdropExtension =>
|
||||
def.GetModExtension<GlobalWorkTableAirdropExtension>();
|
||||
|
||||
// 新增:添加空投命令到技能栏
|
||||
public override IEnumerable<Gizmo> GetGizmos()
|
||||
{
|
||||
foreach (Gizmo g in base.GetGizmos())
|
||||
{
|
||||
yield return g;
|
||||
}
|
||||
|
||||
// 只有在有输出物品时才显示空投按钮
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage != null && globalStorage.outputStorage.Any(kvp => kvp.Value > 0))
|
||||
{
|
||||
yield return new Command_Action
|
||||
{
|
||||
action = StartAirdropTargeting,
|
||||
defaultLabel = "WULA_AirdropProducts".Translate(),
|
||||
defaultDesc = "WULA_AirdropProductsDesc".Translate(),
|
||||
icon = ContentFinder<Texture2D>.Get("UI/Commands/Airdrop"),
|
||||
disabledReason = "WULA_CannotAirdrop".Translate()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:开始空投目标选择
|
||||
private void StartAirdropTargeting()
|
||||
{
|
||||
// 检查是否有输出物品
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage == null || !globalStorage.outputStorage.Any(kvp => kvp.Value > 0))
|
||||
{
|
||||
Messages.Message("WULA_NoProductsToAirdrop".Translate(), MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
// 启动目标选择
|
||||
Find.Targeter.BeginTargeting(new TargetingParameters
|
||||
{
|
||||
canTargetLocations = true,
|
||||
canTargetPawns = false,
|
||||
canTargetBuildings = false,
|
||||
canTargetItems = false
|
||||
}, OnAirdropTargetSelected, null, OnAirdropTargetingCancelled);
|
||||
}
|
||||
|
||||
private void OnAirdropTargetSelected(LocalTargetInfo target)
|
||||
{
|
||||
ExecuteAirdrop(target.Cell);
|
||||
}
|
||||
|
||||
private void OnAirdropTargetingCancelled()
|
||||
{
|
||||
// 目标选择取消,不做任何操作
|
||||
}
|
||||
|
||||
// 新增:执行空投逻辑
|
||||
private void ExecuteAirdrop(IntVec3 targetCell)
|
||||
{
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage == null)
|
||||
return;
|
||||
|
||||
// 获取空投参数
|
||||
var airdropExt = AirdropExtension;
|
||||
float maxRange = airdropExt?.maxRange ?? 50f;
|
||||
float randomRange = airdropExt?.randomRange ?? 15f;
|
||||
int minPods = airdropExt?.minPods ?? 1;
|
||||
int maxPods = airdropExt?.maxPods ?? 10;
|
||||
|
||||
// 检查目标距离
|
||||
if (targetCell.DistanceTo(Position) > maxRange)
|
||||
{
|
||||
Messages.Message("WULA_AirdropTargetTooFar".Translate(maxRange), MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找有效的落点
|
||||
List<IntVec3> validDropSpots = FindValidDropSpots(targetCell, randomRange, maxPods);
|
||||
|
||||
if (validDropSpots.Count == 0)
|
||||
{
|
||||
Messages.Message("WULA_NoValidDropSpots".Translate(), MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算实际空投舱数量
|
||||
int actualPodCount = Mathf.Clamp(validDropSpots.Count, minPods, maxPods);
|
||||
|
||||
// 分配物品到空投舱
|
||||
List<List<Thing>> podContents = DistributeItemsToPods(globalStorage, actualPodCount);
|
||||
|
||||
if (podContents.Count == 0)
|
||||
{
|
||||
Messages.Message("WULA_FailedToDistributeItems".Translate(), MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
// 生成空投舱
|
||||
int successfulDrops = 0;
|
||||
for (int i = 0; i < Mathf.Min(actualPodCount, podContents.Count); i++)
|
||||
{
|
||||
if (CreateDropPod(validDropSpots[i], podContents[i]))
|
||||
{
|
||||
successfulDrops++;
|
||||
}
|
||||
}
|
||||
|
||||
Messages.Message("WULA_AirdropSuccessful".Translate(successfulDrops), MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
|
||||
// 新增:查找有效落点
|
||||
private List<IntVec3> FindValidDropSpots(IntVec3 center, float radius, int maxSpots)
|
||||
{
|
||||
List<IntVec3> validSpots = new List<IntVec3>();
|
||||
Map map = Map;
|
||||
|
||||
// 在指定半径内搜索有效格子
|
||||
foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, radius, true))
|
||||
{
|
||||
if (!cell.IsValid || !cell.InBounds(map))
|
||||
continue;
|
||||
|
||||
// 检查是否为厚岩顶
|
||||
if (map.roofGrid.RoofAt(cell)?.isThickRoof ?? false)
|
||||
continue;
|
||||
|
||||
// 检查是否可以放置空投舱
|
||||
if (DropCellFinder.IsGoodDropSpot(cell, map, false, true))
|
||||
{
|
||||
validSpots.Add(cell);
|
||||
|
||||
if (validSpots.Count >= maxSpots)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有效格子太少,放宽条件(但仍然排除厚岩顶)
|
||||
if (validSpots.Count < 3)
|
||||
{
|
||||
foreach (IntVec3 cell in GenRadial.RadialCellsAround(center, radius, true))
|
||||
{
|
||||
if (!cell.IsValid || !cell.InBounds(map))
|
||||
continue;
|
||||
|
||||
if (map.roofGrid.RoofAt(cell)?.isThickRoof ?? false)
|
||||
continue;
|
||||
|
||||
if (!validSpots.Contains(cell) && cell.Standable(map))
|
||||
{
|
||||
validSpots.Add(cell);
|
||||
|
||||
if (validSpots.Count >= maxSpots)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return validSpots;
|
||||
}
|
||||
|
||||
// 新增:分配物品到空投舱
|
||||
private List<List<Thing>> DistributeItemsToPods(GlobalStorageWorldComponent storage, int podCount)
|
||||
{
|
||||
List<List<Thing>> podContents = new List<List<Thing>>();
|
||||
|
||||
// 初始化空投舱内容列表
|
||||
for (int i = 0; i < podCount; i++)
|
||||
{
|
||||
podContents.Add(new List<Thing>());
|
||||
}
|
||||
|
||||
// 获取所有输出物品并转换为Thing列表
|
||||
List<Thing> allItems = new List<Thing>();
|
||||
foreach (var kvp in storage.outputStorage.ToList())
|
||||
{
|
||||
if (kvp.Value <= 0) continue;
|
||||
|
||||
ThingDef thingDef = kvp.Key;
|
||||
int remainingCount = kvp.Value;
|
||||
|
||||
// 按照堆叠限制分割物品
|
||||
while (remainingCount > 0)
|
||||
{
|
||||
int stackSize = Mathf.Min(remainingCount, thingDef.stackLimit);
|
||||
Thing thing = ThingMaker.MakeThing(thingDef);
|
||||
thing.stackCount = stackSize;
|
||||
allItems.Add(thing);
|
||||
remainingCount -= stackSize;
|
||||
}
|
||||
}
|
||||
|
||||
if (allItems.Count == 0)
|
||||
return podContents;
|
||||
|
||||
// 平均分配物品到空投舱
|
||||
int currentPod = 0;
|
||||
foreach (Thing item in allItems)
|
||||
{
|
||||
podContents[currentPod].Add(item);
|
||||
currentPod = (currentPod + 1) % podCount;
|
||||
}
|
||||
|
||||
// 从存储中移除已分配的物品
|
||||
foreach (var kvp in storage.outputStorage.ToList())
|
||||
{
|
||||
storage.outputStorage[kvp.Key] = 0;
|
||||
}
|
||||
|
||||
return podContents;
|
||||
}
|
||||
|
||||
// 新增:创建空投舱
|
||||
private bool CreateDropPod(IntVec3 dropCell, List<Thing> contents)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (contents == null || contents.Count == 0)
|
||||
return false;
|
||||
|
||||
// 创建空投舱信息
|
||||
ActiveTransporterInfo dropPodInfo = new ActiveTransporterInfo();
|
||||
|
||||
// 添加所有物品到空投舱
|
||||
foreach (Thing thing in contents)
|
||||
{
|
||||
dropPodInfo.innerContainer.TryAdd(thing, true);
|
||||
}
|
||||
|
||||
// 设置空投舱参数
|
||||
dropPodInfo.openDelay = 180; // 3秒后打开
|
||||
dropPodInfo.leaveSlag = true;
|
||||
|
||||
// 生成空投舱
|
||||
DropPodUtility.MakeDropPodAt(dropCell, Map, dropPodInfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"Failed to create drop pod at {dropCell}: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,348 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class Building_ResourceSubmitter : Building_Storage
|
||||
{
|
||||
private CompPowerTrader powerComp;
|
||||
private CompRefuelable refuelableComp;
|
||||
private CompFlickable flickableComp;
|
||||
|
||||
public override void SpawnSetup(Map map, bool respawningAfterLoad)
|
||||
{
|
||||
base.SpawnSetup(map, respawningAfterLoad);
|
||||
powerComp = GetComp<CompPowerTrader>();
|
||||
refuelableComp = GetComp<CompRefuelable>();
|
||||
flickableComp = GetComp<CompFlickable>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查建筑是否可用(电力、燃料、开关等)
|
||||
/// </summary>
|
||||
public bool IsOperational
|
||||
{
|
||||
get
|
||||
{
|
||||
if (powerComp != null && !powerComp.PowerOn)
|
||||
return false;
|
||||
if (refuelableComp != null && !refuelableComp.HasFuel)
|
||||
return false;
|
||||
if (flickableComp != null && !flickableComp.SwitchIsOn)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取建筑的中心位置(用于生成 Skyfaller)
|
||||
/// </summary>
|
||||
public IntVec3 CenterPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
// 对于偶数尺寸的建筑,返回中心附近的单元格
|
||||
var center = Position + new IntVec3(def.Size.x / 2, 0, def.Size.z / 2);
|
||||
// 确保在建筑范围内
|
||||
return center;
|
||||
}
|
||||
}
|
||||
|
||||
public override IEnumerable<Gizmo> GetGizmos()
|
||||
{
|
||||
foreach (Gizmo g in base.GetGizmos())
|
||||
{
|
||||
yield return g;
|
||||
}
|
||||
|
||||
// 添加提交到资源储存器的命令
|
||||
yield return new Command_Action
|
||||
{
|
||||
action = SubmitContentsToStorage,
|
||||
defaultLabel = "WULA_SubmitToStorage".Translate(),
|
||||
defaultDesc = "WULA_SubmitToStorageDesc".Translate(),
|
||||
icon = ContentFinder<Texture2D>.Get("UI/Commands/Upload"),
|
||||
disabledReason = GetDisabledReason()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取存储的物品列表 - 修复版本
|
||||
/// </summary>
|
||||
private List<Thing> GetStoredItems()
|
||||
{
|
||||
var items = new List<Thing>();
|
||||
|
||||
// 方法1:通过直接持有的物品获取(如果建筑本身是容器)
|
||||
if (this is IThingHolder thingHolder)
|
||||
{
|
||||
ThingOwner directlyHeldThings = thingHolder.GetDirectlyHeldThings();
|
||||
if (directlyHeldThings != null)
|
||||
{
|
||||
items.AddRange(directlyHeldThings);
|
||||
}
|
||||
}
|
||||
|
||||
// 方法2:通过存储设置获取地图上的物品
|
||||
if (items.Count == 0)
|
||||
{
|
||||
// 获取建筑的存储设置
|
||||
var storageSettings = GetStoreSettings();
|
||||
if (storageSettings != null)
|
||||
{
|
||||
// 查找地图上被此建筑接受的物品
|
||||
foreach (Thing thing in Map.listerThings.ThingsInGroup(ThingRequestGroup.HaulableAlways))
|
||||
{
|
||||
if (thing.Position.InHorDistOf(Position, 2f) && storageSettings.AllowedToAccept(thing))
|
||||
{
|
||||
items.Add(thing);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取禁用原因
|
||||
/// </summary>
|
||||
private string GetDisabledReason()
|
||||
{
|
||||
if (!IsOperational)
|
||||
{
|
||||
if (powerComp != null && !powerComp.PowerOn)
|
||||
return "WULA_NoPower".Translate();
|
||||
if (refuelableComp != null && !refuelableComp.HasFuel)
|
||||
return "WULA_NoFuel".Translate();
|
||||
if (flickableComp != null && !flickableComp.SwitchIsOn)
|
||||
return "WULA_SwitchOff".Translate();
|
||||
}
|
||||
|
||||
if (GetStoredItems().Count == 0)
|
||||
return "WULA_NoItemsToSubmit".Translate();
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 提交内容到资源储存器
|
||||
/// </summary>
|
||||
private void SubmitContentsToStorage()
|
||||
{
|
||||
if (!IsOperational)
|
||||
{
|
||||
Messages.Message("WULA_DeviceNotOperational".Translate(), MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
var storedItems = GetStoredItems();
|
||||
if (storedItems.Count == 0)
|
||||
{
|
||||
Messages.Message("WULA_NoItemsToSubmit".Translate(), MessageTypeDefOf.RejectInput);
|
||||
return;
|
||||
}
|
||||
|
||||
// 执行提交逻辑
|
||||
if (TrySubmitItems(storedItems))
|
||||
{
|
||||
// 生成 Skyfaller 演出效果
|
||||
CreateSubmissionEffect();
|
||||
|
||||
Messages.Message("WULA_ItemsSubmitted".Translate(storedItems.Count), MessageTypeDefOf.PositiveEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message("WULA_SubmissionFailed".Translate(), MessageTypeDefOf.NegativeEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试提交物品到资源储存器
|
||||
/// </summary>
|
||||
private bool TrySubmitItems(List<Thing> items)
|
||||
{
|
||||
try
|
||||
{
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage == null)
|
||||
{
|
||||
Log.Error("GlobalStorageWorldComponent not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
int submittedCount = 0;
|
||||
var processedItems = new List<Thing>();
|
||||
|
||||
foreach (Thing item in items)
|
||||
{
|
||||
if (item == null || item.Destroyed)
|
||||
continue;
|
||||
|
||||
// 检查是否为装备或武器
|
||||
if (IsEquipment(item.def))
|
||||
{
|
||||
// 装备和武器直接添加到输出存储
|
||||
globalStorage.AddToOutputStorage(item.def, item.stackCount);
|
||||
processedItems.Add(item);
|
||||
submittedCount += item.stackCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 其他物品添加到输入存储
|
||||
globalStorage.AddToInputStorage(item.def, item.stackCount);
|
||||
processedItems.Add(item);
|
||||
submittedCount += item.stackCount;
|
||||
}
|
||||
}
|
||||
|
||||
// 从世界中移除已提交的物品
|
||||
foreach (Thing item in processedItems)
|
||||
{
|
||||
// 如果物品在建筑的直接容器中
|
||||
if (this is IThingHolder thingHolder)
|
||||
{
|
||||
ThingOwner directlyHeldThings = thingHolder.GetDirectlyHeldThings();
|
||||
if (directlyHeldThings != null && directlyHeldThings.Contains(item))
|
||||
{
|
||||
directlyHeldThings.Remove(item);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果物品在地图上,直接销毁
|
||||
if (item.Spawned)
|
||||
{
|
||||
item.Destroy();
|
||||
}
|
||||
}
|
||||
|
||||
Log.Message($"Successfully submitted {submittedCount} items to global storage");
|
||||
return submittedCount > 0;
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"Error submitting items to storage: {ex}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查是否为装备或武器
|
||||
/// </summary>
|
||||
private bool IsEquipment(ThingDef thingDef)
|
||||
{
|
||||
return thingDef.IsApparel || thingDef.IsWeapon || thingDef.category == ThingCategory.Building;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建提交效果(Skyfaller)
|
||||
/// </summary>
|
||||
private void CreateSubmissionEffect()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取 Skyfaller 定义
|
||||
ThingDef skyfallerDef = DefDatabase<ThingDef>.GetNamedSilentFail("DropPodIncoming");
|
||||
if (skyfallerDef == null)
|
||||
{
|
||||
// 备用方案:使用简单的效果
|
||||
CreateFallbackEffect();
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建空的 Skyfaller
|
||||
Skyfaller skyfaller = (Skyfaller)ThingMaker.MakeThing(skyfallerDef);
|
||||
|
||||
// 设置位置(建筑中心)
|
||||
IntVec3 dropPos = CenterPosition;
|
||||
|
||||
// 确保位置有效
|
||||
if (!dropPos.IsValid || !dropPos.InBounds(Map))
|
||||
{
|
||||
dropPos = Position; // 回退到建筑位置
|
||||
}
|
||||
|
||||
// 生成 Skyfaller
|
||||
GenSpawn.Spawn(skyfaller, dropPos, Map);
|
||||
|
||||
// 可选:添加一些视觉效果
|
||||
FleckMaker.ThrowLightningGlow(dropPos.ToVector3Shifted(), Map, 2f);
|
||||
|
||||
Log.Message("Created submission skyfaller effect");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"Error creating skyfaller effect: {ex}");
|
||||
CreateFallbackEffect();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 备用效果(如果 Skyfaller 失败)
|
||||
/// </summary>
|
||||
private void CreateFallbackEffect()
|
||||
{
|
||||
try
|
||||
{
|
||||
IntVec3 center = CenterPosition;
|
||||
|
||||
// 生成闪光效果
|
||||
for (int i = 0; i < 3; i++)
|
||||
{
|
||||
FleckMaker.ThrowLightningGlow(center.ToVector3Shifted(), Map, 1.5f);
|
||||
}
|
||||
|
||||
// 生成烟雾效果
|
||||
FleckMaker.ThrowSmoke(center.ToVector3Shifted(), Map, 2f);
|
||||
|
||||
Log.Message("Created fallback submission effect");
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Log.Error($"Error creating fallback effect: {ex}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 修复的检查字符串方法 - 避免空行问题
|
||||
/// </summary>
|
||||
public override string GetInspectString()
|
||||
{
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
// 获取基础检查字符串
|
||||
string baseString = base.GetInspectString();
|
||||
if (!baseString.NullOrEmpty())
|
||||
{
|
||||
stringBuilder.Append(baseString);
|
||||
}
|
||||
|
||||
// 获取存储信息
|
||||
var storedItems = GetStoredItems();
|
||||
int itemCount = storedItems.Count;
|
||||
int totalStack = storedItems.Sum(item => item.stackCount);
|
||||
|
||||
// 添加存储信息
|
||||
if (stringBuilder.Length > 0)
|
||||
{
|
||||
stringBuilder.AppendLine();
|
||||
}
|
||||
stringBuilder.Append($"{"WULA_StoredItems".Translate()}: {itemCount} ({totalStack} {"WULA_Items".Translate()})");
|
||||
|
||||
// 添加状态信息(如果不工作)
|
||||
if (!IsOperational)
|
||||
{
|
||||
if (stringBuilder.Length > 0)
|
||||
{
|
||||
stringBuilder.AppendLine();
|
||||
}
|
||||
stringBuilder.Append($"{"WULA_Status".Translate()}: {"WULA_Inoperative".Translate()}");
|
||||
}
|
||||
|
||||
return stringBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
// GlobalProductionOrder.cs (修复版)
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
@@ -36,63 +37,71 @@ namespace WulaFallenEmpire
|
||||
public string Label => recipe.LabelCap;
|
||||
public string Description => $"{currentCount}/{targetCount} {recipe.products[0].thingDef.label}";
|
||||
|
||||
// 检查是否有足够资源 - 修复逻辑,只检查costList
|
||||
// 检查是否有足够资源 - 修复逻辑
|
||||
public bool HasEnoughResources()
|
||||
{
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage == null)
|
||||
{
|
||||
Log.Warning("GlobalStorageWorldComponent not found");
|
||||
return false;
|
||||
}
|
||||
if (globalStorage == null) return false;
|
||||
|
||||
// 只检查costList,不检查ingredients
|
||||
if (recipe.costList != null && recipe.costList.Count > 0)
|
||||
// 遍历所有配料要求
|
||||
foreach (var ingredient in recipe.ingredients)
|
||||
{
|
||||
foreach (var cost in recipe.costList)
|
||||
bool hasEnoughForThisIngredient = false;
|
||||
|
||||
// 检查这个配料的所有允许物品类型
|
||||
foreach (var thingDef in ingredient.filter.AllowedThingDefs)
|
||||
{
|
||||
int required = cost.count;
|
||||
int available = globalStorage.GetInputStorageCount(cost.thingDef);
|
||||
int requiredCount = ingredient.CountRequiredOfFor(thingDef, recipe);
|
||||
int availableCount = globalStorage.GetInputStorageCount(thingDef);
|
||||
|
||||
Log.Message($"[DEBUG] Checking {cost.thingDef.defName}: required={required}, available={available}");
|
||||
|
||||
if (available < required)
|
||||
if (availableCount >= requiredCount)
|
||||
{
|
||||
Log.Message($"[DEBUG] Insufficient {cost.thingDef.defName}");
|
||||
return false;
|
||||
hasEnoughForThisIngredient = true;
|
||||
break; // 这个配料有足够的资源
|
||||
}
|
||||
}
|
||||
Log.Message("[DEBUG] All resources available");
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Log.Warning($"Recipe {recipe.defName} has no costList");
|
||||
return false;
|
||||
|
||||
// 如果任何一个配料没有足够资源,整个配方就无法生产
|
||||
if (!hasEnoughForThisIngredient)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 消耗资源 - 修复逻辑,只消耗costList
|
||||
// 消耗资源 - 修复逻辑
|
||||
public bool ConsumeResources()
|
||||
{
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage == null) return false;
|
||||
|
||||
// 只消耗costList中的资源
|
||||
if (recipe.costList != null)
|
||||
// 遍历所有配料要求
|
||||
foreach (var ingredient in recipe.ingredients)
|
||||
{
|
||||
foreach (var cost in recipe.costList)
|
||||
bool consumedThisIngredient = false;
|
||||
|
||||
// 尝试消耗这个配料的允许物品类型
|
||||
foreach (var thingDef in ingredient.filter.AllowedThingDefs)
|
||||
{
|
||||
if (!globalStorage.RemoveFromInputStorage(cost.thingDef, cost.count))
|
||||
int requiredCount = ingredient.CountRequiredOfFor(thingDef, recipe);
|
||||
int availableCount = globalStorage.GetInputStorageCount(thingDef);
|
||||
|
||||
if (availableCount >= requiredCount)
|
||||
{
|
||||
Log.Warning($"Failed to consume {cost.count} {cost.thingDef.defName}");
|
||||
return false;
|
||||
if (globalStorage.RemoveFromInputStorage(thingDef, requiredCount))
|
||||
{
|
||||
consumedThisIngredient = true;
|
||||
break; // 成功消耗这个配料
|
||||
}
|
||||
}
|
||||
Log.Message($"[DEBUG] Consumed {cost.count} {cost.thingDef.defName}");
|
||||
}
|
||||
return true;
|
||||
|
||||
// 如果任何一个配料无法消耗,整个生产失败
|
||||
if (!consumedThisIngredient)
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 生产一个产品
|
||||
@@ -104,7 +113,6 @@ namespace WulaFallenEmpire
|
||||
foreach (var product in recipe.products)
|
||||
{
|
||||
globalStorage.AddToOutputStorage(product.thingDef, product.count);
|
||||
Log.Message($"[DEBUG] Produced {product.count} {product.thingDef.defName}");
|
||||
}
|
||||
|
||||
currentCount++;
|
||||
@@ -113,8 +121,142 @@ namespace WulaFallenEmpire
|
||||
if (currentCount >= targetCount)
|
||||
{
|
||||
state = ProductionState.Completed;
|
||||
Log.Message("[DEBUG] Order completed");
|
||||
}
|
||||
}
|
||||
|
||||
// 新增方法:检查并更新状态
|
||||
public void UpdateState()
|
||||
{
|
||||
if (state == ProductionState.Completed) return;
|
||||
|
||||
if (HasEnoughResources())
|
||||
{
|
||||
if (state == ProductionState.Waiting && !paused)
|
||||
{
|
||||
state = ProductionState.Producing;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (state == ProductionState.Producing)
|
||||
{
|
||||
state = ProductionState.Waiting;
|
||||
progress = 0f; // 重置进度
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取配方材料信息的字符串
|
||||
public string GetIngredientsInfo()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// 添加标题
|
||||
sb.AppendLine("WULA_RequiredIngredients".Translate() + ":");
|
||||
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
|
||||
foreach (var ingredient in recipe.ingredients)
|
||||
{
|
||||
bool firstAllowedThing = true;
|
||||
|
||||
foreach (var thingDef in ingredient.filter.AllowedThingDefs)
|
||||
{
|
||||
int requiredCount = ingredient.CountRequiredOfFor(thingDef, recipe);
|
||||
int availableCount = globalStorage?.GetInputStorageCount(thingDef) ?? 0;
|
||||
|
||||
if (firstAllowedThing)
|
||||
{
|
||||
sb.Append(" - ");
|
||||
firstAllowedThing = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(" / ");
|
||||
}
|
||||
|
||||
sb.Append($"{requiredCount} {thingDef.label}");
|
||||
|
||||
// 添加可用数量信息
|
||||
if (availableCount < requiredCount)
|
||||
{
|
||||
sb.Append($" (<color=red>{availableCount}</color>/{requiredCount})");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append($" ({availableCount}/{requiredCount})");
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
}
|
||||
|
||||
// 添加产品信息
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("WULA_Products".Translate() + ":");
|
||||
foreach (var product in recipe.products)
|
||||
{
|
||||
sb.AppendLine($" - {product.count} {product.thingDef.label}");
|
||||
}
|
||||
|
||||
// 添加工作量信息
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("WULA_WorkAmount".Translate() + ": " + recipe.workAmount.ToStringWorkAmount());
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
// 获取简化的材料信息(用于Tooltip)
|
||||
public string GetIngredientsTooltip()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.AppendLine(recipe.LabelCap);
|
||||
sb.AppendLine();
|
||||
|
||||
// 材料需求
|
||||
sb.AppendLine("WULA_RequiredIngredients".Translate() + ":");
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
|
||||
foreach (var ingredient in recipe.ingredients)
|
||||
{
|
||||
bool ingredientSatisfied = false;
|
||||
StringBuilder ingredientSB = new StringBuilder();
|
||||
|
||||
foreach (var thingDef in ingredient.filter.AllowedThingDefs)
|
||||
{
|
||||
int requiredCount = ingredient.CountRequiredOfFor(thingDef, recipe);
|
||||
int availableCount = globalStorage?.GetInputStorageCount(thingDef) ?? 0;
|
||||
|
||||
if (ingredientSB.Length > 0)
|
||||
ingredientSB.Append(" / ");
|
||||
|
||||
ingredientSB.Append($"{requiredCount} {thingDef.label}");
|
||||
|
||||
if (availableCount >= requiredCount)
|
||||
{
|
||||
ingredientSatisfied = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (ingredientSatisfied)
|
||||
{
|
||||
sb.AppendLine($" <color=green>{ingredientSB}</color>");
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.AppendLine($" <color=red>{ingredientSB}</color>");
|
||||
}
|
||||
}
|
||||
|
||||
// 产品
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("WULA_Products".Translate() + ":");
|
||||
foreach (var product in recipe.products)
|
||||
{
|
||||
sb.AppendLine($" {product.count} {product.thingDef.label}");
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// GlobalProductionOrderStack.cs (修复版)
|
||||
// GlobalProductionOrderStack.cs (调整为每秒1工作量)
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using Verse;
|
||||
@@ -10,6 +10,11 @@ namespace WulaFallenEmpire
|
||||
public Building_GlobalWorkTable table;
|
||||
public List<GlobalProductionOrder> orders = new List<GlobalProductionOrder>();
|
||||
|
||||
// 调整为每秒1工作量 - RimWorld中1秒=60ticks
|
||||
private const float WorkPerSecond = 1f;
|
||||
private const float TicksPerSecond = 60f;
|
||||
private const float WorkPerTick = WorkPerSecond / TicksPerSecond;
|
||||
|
||||
public GlobalProductionOrderStack(Building_GlobalWorkTable table)
|
||||
{
|
||||
this.table = table;
|
||||
@@ -25,7 +30,6 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
orders.Add(order);
|
||||
|
||||
// 添加到全局存储中统一管理
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage != null)
|
||||
{
|
||||
@@ -48,48 +52,55 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
foreach (var order in orders)
|
||||
{
|
||||
// 首先更新状态
|
||||
order.UpdateState();
|
||||
|
||||
if (order.paused || order.state == GlobalProductionOrder.ProductionState.Completed)
|
||||
continue;
|
||||
|
||||
// 检查资源并更新状态
|
||||
if (order.state == GlobalProductionOrder.ProductionState.Waiting)
|
||||
{
|
||||
if (order.HasEnoughResources())
|
||||
{
|
||||
order.state = GlobalProductionOrder.ProductionState.Producing;
|
||||
Log.Message($"[DEBUG] Order {order.recipe.defName} started producing");
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 生产中
|
||||
if (order.state == GlobalProductionOrder.ProductionState.Producing)
|
||||
{
|
||||
// 更清晰的进度计算
|
||||
float progressPerTick = 1f / (order.recipe.workAmount * 5f); // 调整系数以控制生产速度
|
||||
order.progress += progressPerTick;
|
||||
// 计算每tick的工作量进度
|
||||
float workAmount = order.recipe.workAmount;
|
||||
float progressIncrement = WorkPerTick / workAmount;
|
||||
|
||||
order.progress += progressIncrement;
|
||||
|
||||
// 调试信息 - 减少频率以免太吵
|
||||
if (Find.TickManager.TicksGame % 600 == 0) // 每10秒输出一次调试信息
|
||||
{
|
||||
Log.Message($"[DEBUG] Order {order.recipe.defName} progress: {order.progress:P0}, " +
|
||||
$"workAmount: {workAmount}, increment: {progressIncrement:F6}");
|
||||
}
|
||||
|
||||
if (order.progress >= 1f)
|
||||
{
|
||||
// 消耗资源并生产 - 在结束时扣除资源
|
||||
// 生产完成,消耗资源
|
||||
if (order.ConsumeResources())
|
||||
{
|
||||
order.Produce();
|
||||
order.UpdateState();
|
||||
|
||||
Log.Message($"[SUCCESS] Produced {order.recipe.products[0].thingDef.defName}, " +
|
||||
$"count: {order.currentCount}/{order.targetCount}, " +
|
||||
$"workAmount: {workAmount}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// 资源被其他订单消耗,回到等待状态
|
||||
order.state = GlobalProductionOrder.ProductionState.Waiting;
|
||||
order.progress = 0f;
|
||||
Log.Message("[DEBUG] Resources consumed by another order, returning to waiting state");
|
||||
Log.Message($"[WARNING] Failed to consume resources for {order.recipe.defName}");
|
||||
}
|
||||
}
|
||||
else
|
||||
}
|
||||
else if (order.state == GlobalProductionOrder.ProductionState.Waiting && !order.paused)
|
||||
{
|
||||
// 调试:检查为什么订单在等待状态
|
||||
if (Find.TickManager.TicksGame % 1200 == 0) // 每20秒检查一次
|
||||
{
|
||||
Log.Message($"[DEBUG] Order {order.recipe.defName} progress: {order.progress:P0}");
|
||||
Log.Message($"[DEBUG] Order {order.recipe.defName} is waiting. " +
|
||||
$"HasEnoughResources: {order.HasEnoughResources()}, paused: {order.paused}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class GlobalWorkTableAirdropExtension : DefModExtension
|
||||
{
|
||||
public float maxRange = 50f; // 最大空投范围
|
||||
public float randomRange = 15f; // 随机散布范围
|
||||
public int minPods = 1; // 最少空投舱数量
|
||||
public int maxPods = 10; // 最多空投舱数量
|
||||
|
||||
// 必须添加无参数构造函数
|
||||
public GlobalWorkTableAirdropExtension() { }
|
||||
|
||||
// 可以添加其他参数,比如冷却时间、消耗资源等
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
// ITab_GlobalBills.cs (修复版)
|
||||
// ITab_GlobalBills.cs (添加存储查看功能)
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using RimWorld;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
@@ -33,6 +35,10 @@ namespace WulaFallenEmpire
|
||||
Widgets.Label(new Rect(mainRect.x, mainRect.y, mainRect.width, 30f), "WULA_GlobalProduction".Translate());
|
||||
Text.Font = GameFont.Small;
|
||||
|
||||
// 存储查看按钮 - 放在标题旁边
|
||||
Rect storageButtonRect = new Rect(mainRect.xMax - 120f, mainRect.y, 120f, 25f);
|
||||
DoStorageButton(storageButtonRect);
|
||||
|
||||
// 开发模式按钮区域
|
||||
if (Prefs.DevMode)
|
||||
{
|
||||
@@ -51,6 +57,95 @@ namespace WulaFallenEmpire
|
||||
{
|
||||
Find.WindowStack.Add(new FloatMenu(GenerateRecipeOptions()));
|
||||
}
|
||||
|
||||
// 绘制鼠标悬停信息 - 使用简单的Tooltip方式
|
||||
if (mouseoverOrder != null)
|
||||
{
|
||||
// 使用Tooltip显示材料信息,不绘制额外窗口
|
||||
// 信息已经在DoOrderRow中的TooltipHandler显示
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:存储查看按钮
|
||||
private void DoStorageButton(Rect rect)
|
||||
{
|
||||
// 绘制按钮
|
||||
if (Widgets.ButtonText(rect, "WULA_ViewStorage".Translate()))
|
||||
{
|
||||
// 点击按钮时也可以做一些事情,比如打开详细存储窗口
|
||||
// 暂时只显示Tooltip
|
||||
SoundDefOf.Click.PlayOneShotOnCamera();
|
||||
}
|
||||
|
||||
// 鼠标悬停时显示存储信息Tooltip
|
||||
if (Mouse.IsOver(rect))
|
||||
{
|
||||
TooltipHandler.TipRegion(rect, GetStorageTooltip());
|
||||
}
|
||||
}
|
||||
|
||||
// 新增:获取存储信息的Tooltip
|
||||
private string GetStorageTooltip()
|
||||
{
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage == null)
|
||||
return "WULA_NoGlobalStorage".Translate();
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
// 输入存储(原材料)
|
||||
sb.AppendLine("WULA_InputStorage".Translate() + ":");
|
||||
sb.AppendLine();
|
||||
|
||||
var inputItems = globalStorage.inputStorage
|
||||
.Where(kvp => kvp.Value > 0)
|
||||
.OrderByDescending(kvp => kvp.Value)
|
||||
.ThenBy(kvp => kvp.Key.label)
|
||||
.ToList();
|
||||
|
||||
if (inputItems.Count == 0)
|
||||
{
|
||||
sb.AppendLine("WULA_NoItems".Translate());
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var kvp in inputItems)
|
||||
{
|
||||
sb.AppendLine($" {kvp.Value} {kvp.Key.label}");
|
||||
}
|
||||
}
|
||||
|
||||
sb.AppendLine();
|
||||
|
||||
// 输出存储(产品)
|
||||
sb.AppendLine("WULA_OutputStorage".Translate() + ":");
|
||||
sb.AppendLine();
|
||||
|
||||
var outputItems = globalStorage.outputStorage
|
||||
.Where(kvp => kvp.Value > 0)
|
||||
.OrderByDescending(kvp => kvp.Value)
|
||||
.ThenBy(kvp => kvp.Key.label)
|
||||
.ToList();
|
||||
|
||||
if (outputItems.Count == 0)
|
||||
{
|
||||
sb.AppendLine("WULA_NoItems".Translate());
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var kvp in outputItems)
|
||||
{
|
||||
sb.AppendLine($" {kvp.Value} {kvp.Key.label}");
|
||||
}
|
||||
}
|
||||
|
||||
// 添加存储统计信息
|
||||
sb.AppendLine();
|
||||
sb.AppendLine("WULA_StorageStats".Translate());
|
||||
sb.AppendLine($" {inputItems.Count} {("WULA_InputItems".Translate())}");
|
||||
sb.AppendLine($" {outputItems.Count} {("WULA_OutputItems".Translate())}");
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
private void DoDevButtons(Rect rect)
|
||||
@@ -211,15 +306,28 @@ namespace WulaFallenEmpire
|
||||
GlobalProductionOrder.ProductionState.Completed => "WULA_Completed".Translate(),
|
||||
_ => "WULA_Unknown".Translate()
|
||||
};
|
||||
|
||||
// 如果暂停,在状态前添加暂停标识
|
||||
if (order.paused && order.state != GlobalProductionOrder.ProductionState.Completed)
|
||||
{
|
||||
statusText = $"[Paused] {statusText}";
|
||||
}
|
||||
|
||||
Widgets.Label(statusRect, statusText);
|
||||
}
|
||||
|
||||
// 控制按钮
|
||||
float buttonY = rect.y + padding;
|
||||
Rect pauseButtonRect = new Rect(rect.xMax - 90f, buttonY, 40f, 25f);
|
||||
if (Widgets.ButtonText(pauseButtonRect, order.paused ? "WULA_Resume".Translate() : "WULA_Pause".Translate()))
|
||||
|
||||
string pauseButtonText = order.paused ? "WULA_Resume".Translate() : "WULA_Pause".Translate();
|
||||
if (Widgets.ButtonText(pauseButtonRect, pauseButtonText))
|
||||
{
|
||||
order.paused = !order.paused;
|
||||
|
||||
// 暂停/恢复时更新状态
|
||||
order.UpdateState();
|
||||
|
||||
SoundDefOf.Click.PlayOneShotOnCamera();
|
||||
}
|
||||
|
||||
@@ -230,9 +338,10 @@ namespace WulaFallenEmpire
|
||||
SoundDefOf.Click.PlayOneShotOnCamera();
|
||||
}
|
||||
|
||||
// 资源检查提示 - 修复逻辑
|
||||
bool shouldShowRedBorder = (order.state == GlobalProductionOrder.ProductionState.Waiting && !order.HasEnoughResources());
|
||||
if (shouldShowRedBorder)
|
||||
// 资源检查提示 - 只在等待资源且不暂停时显示红色边框
|
||||
if (!order.HasEnoughResources() &&
|
||||
order.state == GlobalProductionOrder.ProductionState.Waiting &&
|
||||
!order.paused)
|
||||
{
|
||||
TooltipHandler.TipRegion(rect, "WULA_InsufficientResources".Translate());
|
||||
GUI.color = Color.red;
|
||||
@@ -240,6 +349,12 @@ namespace WulaFallenEmpire
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
// 添加材料信息的Tooltip - 这是核心功能
|
||||
if (Mouse.IsOver(rect))
|
||||
{
|
||||
TooltipHandler.TipRegion(rect, order.GetIngredientsTooltip());
|
||||
}
|
||||
|
||||
return Mouse.IsOver(rect);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,409 @@
|
||||
using RimWorld;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using Verse;
|
||||
|
||||
namespace WulaFallenEmpire
|
||||
{
|
||||
public class ITab_ResourceSubmitterContents : ITab
|
||||
{
|
||||
private Vector2 scrollPosition;
|
||||
private float scrollViewHeight;
|
||||
|
||||
private static readonly Vector2 WinSize = new Vector2(420f, 480f);
|
||||
|
||||
protected Building_ResourceSubmitter SelSubmitter => (Building_ResourceSubmitter)base.SelThing;
|
||||
|
||||
public ITab_ResourceSubmitterContents()
|
||||
{
|
||||
size = WinSize;
|
||||
labelKey = "WULA_SubmitterContents";
|
||||
tutorTag = "SubmitterContents";
|
||||
}
|
||||
|
||||
protected override void FillTab()
|
||||
{
|
||||
Rect mainRect = new Rect(0f, 0f, WinSize.x, WinSize.y).ContractedBy(10f);
|
||||
|
||||
// 标题区域
|
||||
Text.Font = GameFont.Medium;
|
||||
Widgets.Label(new Rect(mainRect.x, mainRect.y, mainRect.width, 30f), "WULA_SubmitterContents".Translate());
|
||||
Text.Font = GameFont.Small;
|
||||
|
||||
// 状态信息
|
||||
Rect statusRect = new Rect(mainRect.x, mainRect.y + 35f, mainRect.width, 40f);
|
||||
DoStatusInfo(statusRect);
|
||||
|
||||
// 存储物品列表
|
||||
Rect itemsRect = new Rect(mainRect.x, mainRect.y + 80f, mainRect.width, mainRect.height - 120f);
|
||||
DoItemsListing(itemsRect);
|
||||
|
||||
// 操作按钮区域
|
||||
Rect buttonsRect = new Rect(mainRect.x, mainRect.yMax - 35f, mainRect.width, 30f);
|
||||
DoActionButtons(buttonsRect);
|
||||
}
|
||||
|
||||
private void DoStatusInfo(Rect rect)
|
||||
{
|
||||
Widgets.DrawMenuSection(rect);
|
||||
Rect innerRect = rect.ContractedBy(5f);
|
||||
|
||||
// 运行状态
|
||||
string statusText = SelSubmitter.IsOperational ?
|
||||
"WULA_Operational".Translate() : "WULA_Inoperative".Translate();
|
||||
Color statusColor = SelSubmitter.IsOperational ? Color.green : Color.red;
|
||||
|
||||
Text.Anchor = TextAnchor.MiddleLeft;
|
||||
Rect statusLabelRect = new Rect(innerRect.x, innerRect.y, innerRect.width * 0.6f, innerRect.height);
|
||||
Widgets.Label(statusLabelRect, "WULA_Status".Translate() + ": " + statusText);
|
||||
|
||||
// 物品数量
|
||||
var storedItems = SelSubmitter.GetStoredItems();
|
||||
int totalItems = storedItems.Count;
|
||||
int totalStacks = storedItems.Sum(item => item.stackCount);
|
||||
|
||||
Rect countRect = new Rect(innerRect.x + innerRect.width * 0.6f, innerRect.y, innerRect.width * 0.4f, innerRect.height);
|
||||
Widgets.Label(countRect, $"{totalItems} {"WULA_Items".Translate()} ({totalStacks} {"WULA_Stacks".Translate()})");
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
|
||||
// 状态颜色指示
|
||||
GUI.color = statusColor;
|
||||
Widgets.DrawBox(new Rect(statusLabelRect.x - 15f, statusLabelRect.y + 7f, 10f, 10f), 1);
|
||||
GUI.color = Color.white;
|
||||
}
|
||||
|
||||
private void DoItemsListing(Rect rect)
|
||||
{
|
||||
Widgets.DrawMenuSection(rect);
|
||||
Rect outRect = rect.ContractedBy(5f);
|
||||
Rect viewRect = new Rect(0f, 0f, outRect.width - 16f, scrollViewHeight);
|
||||
|
||||
var storedItems = SelSubmitter.GetStoredItems();
|
||||
|
||||
Widgets.BeginScrollView(outRect, ref scrollPosition, viewRect);
|
||||
|
||||
float curY = 0f;
|
||||
|
||||
// 列标题
|
||||
Rect headerRect = new Rect(0f, curY, viewRect.width, 25f);
|
||||
DoColumnHeaders(headerRect);
|
||||
curY += 30f;
|
||||
|
||||
if (storedItems.Count == 0)
|
||||
{
|
||||
Rect emptyRect = new Rect(0f, curY, viewRect.width, 30f);
|
||||
Text.Anchor = TextAnchor.MiddleCenter;
|
||||
Widgets.Label(emptyRect, "WULA_NoItemsInStorage".Translate());
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
curY += 35f;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 按物品类型分组显示
|
||||
var groupedItems = storedItems
|
||||
.GroupBy(item => item.def)
|
||||
.OrderByDescending(g => g.Sum(item => item.stackCount))
|
||||
.ThenBy(g => g.Key.label);
|
||||
|
||||
foreach (var group in groupedItems)
|
||||
{
|
||||
ThingDef thingDef = group.Key;
|
||||
int totalCount = group.Sum(item => item.stackCount);
|
||||
int stackCount = group.Count();
|
||||
|
||||
Rect itemRect = new Rect(0f, curY, viewRect.width, 28f);
|
||||
if (DoItemRow(itemRect, thingDef, totalCount, stackCount))
|
||||
{
|
||||
// 鼠标悬停时显示详细信息
|
||||
string tooltip = GetItemTooltip(thingDef, totalCount, stackCount);
|
||||
TooltipHandler.TipRegion(itemRect, tooltip);
|
||||
}
|
||||
|
||||
curY += 32f;
|
||||
|
||||
// 分隔线
|
||||
if (curY < viewRect.height - 5f)
|
||||
{
|
||||
Widgets.DrawLineHorizontal(0f, curY - 2f, viewRect.width);
|
||||
curY += 5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.Layout)
|
||||
{
|
||||
scrollViewHeight = curY;
|
||||
}
|
||||
|
||||
Widgets.EndScrollView();
|
||||
}
|
||||
|
||||
private void DoColumnHeaders(Rect rect)
|
||||
{
|
||||
float columnWidth = rect.width / 4f;
|
||||
|
||||
// 物品名称列
|
||||
Rect nameRect = new Rect(rect.x, rect.y, columnWidth * 2f, rect.height);
|
||||
Text.Anchor = TextAnchor.MiddleLeft;
|
||||
Widgets.Label(nameRect, "WULA_ItemName".Translate());
|
||||
|
||||
// 数量列
|
||||
Rect countRect = new Rect(rect.x + columnWidth * 2f, rect.y, columnWidth, rect.height);
|
||||
Text.Anchor = TextAnchor.MiddleCenter;
|
||||
Widgets.Label(countRect, "WULA_Count".Translate());
|
||||
|
||||
// 堆叠列
|
||||
Rect stacksRect = new Rect(rect.x + columnWidth * 3f, rect.y, columnWidth, rect.height);
|
||||
Widgets.Label(stacksRect, "WULA_Stacks".Translate());
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
|
||||
// 标题下划线
|
||||
Widgets.DrawLineHorizontal(rect.x, rect.yMax - 2f, rect.width);
|
||||
}
|
||||
|
||||
private bool DoItemRow(Rect rect, ThingDef thingDef, int totalCount, int stackCount)
|
||||
{
|
||||
Widgets.DrawHighlightIfMouseover(rect);
|
||||
|
||||
float columnWidth = rect.width / 4f;
|
||||
|
||||
// 物品图标
|
||||
Rect iconRect = new Rect(rect.x + 2f, rect.y + 2f, 24f, 24f);
|
||||
Widgets.ThingIcon(iconRect, thingDef);
|
||||
|
||||
// 物品名称
|
||||
Rect nameRect = new Rect(rect.x + 30f, rect.y, columnWidth * 2f - 30f, rect.height);
|
||||
Text.Anchor = TextAnchor.MiddleLeft;
|
||||
string label = thingDef.LabelCap;
|
||||
if (label.Length > 25)
|
||||
{
|
||||
label = label.Substring(0, 25) + "...";
|
||||
}
|
||||
Widgets.Label(nameRect, label);
|
||||
|
||||
// 总数量
|
||||
Rect countRect = new Rect(rect.x + columnWidth * 2f, rect.y, columnWidth, rect.height);
|
||||
Text.Anchor = TextAnchor.MiddleCenter;
|
||||
Widgets.Label(countRect, totalCount.ToString());
|
||||
|
||||
// 堆叠数量
|
||||
Rect stacksRect = new Rect(rect.x + columnWidth * 3f, rect.y, columnWidth, rect.height);
|
||||
Widgets.Label(stacksRect, stackCount.ToString());
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
|
||||
return Mouse.IsOver(rect);
|
||||
}
|
||||
|
||||
private string GetItemTooltip(ThingDef thingDef, int totalCount, int stackCount)
|
||||
{
|
||||
return string.Format("WULA_ItemTooltip".Translate(),
|
||||
thingDef.LabelCap,
|
||||
totalCount,
|
||||
stackCount,
|
||||
thingDef.BaseMarketValue * totalCount);
|
||||
}
|
||||
|
||||
private void DoActionButtons(Rect rect)
|
||||
{
|
||||
float buttonWidth = rect.width / 2f - 5f;
|
||||
|
||||
// 提交按钮
|
||||
Rect submitRect = new Rect(rect.x, rect.y, buttonWidth, rect.height);
|
||||
bool hasItems = SelSubmitter.GetStoredItems().Count > 0;
|
||||
bool isOperational = SelSubmitter.IsOperational;
|
||||
|
||||
string submitLabel = "WULA_SubmitToStorage".Translate();
|
||||
string submitDesc = "WULA_SubmitToStorageDesc".Translate();
|
||||
|
||||
if (!isOperational)
|
||||
{
|
||||
submitLabel = "WULA_DeviceInoperative".Translate();
|
||||
submitDesc = GetInoperativeReason();
|
||||
}
|
||||
else if (!hasItems)
|
||||
{
|
||||
submitLabel = "WULA_NoItemsToSubmit".Translate();
|
||||
submitDesc = "WULA_NoItemsToSubmitDesc".Translate();
|
||||
}
|
||||
|
||||
if (Widgets.ButtonText(submitRect, submitLabel))
|
||||
{
|
||||
if (isOperational && hasItems)
|
||||
{
|
||||
SelSubmitter.SubmitContentsToStorage();
|
||||
}
|
||||
else if (!isOperational)
|
||||
{
|
||||
Messages.Message(GetInoperativeReason(), MessageTypeDefOf.RejectInput);
|
||||
}
|
||||
else
|
||||
{
|
||||
Messages.Message("WULA_NoItemsToSubmit".Translate(), MessageTypeDefOf.RejectInput);
|
||||
}
|
||||
}
|
||||
|
||||
// 工具提示
|
||||
if (Mouse.IsOver(submitRect))
|
||||
{
|
||||
TooltipHandler.TipRegion(submitRect, submitDesc);
|
||||
}
|
||||
|
||||
// 查看全局存储按钮
|
||||
Rect storageRect = new Rect(rect.x + buttonWidth + 10f, rect.y, buttonWidth, rect.height);
|
||||
if (Widgets.ButtonText(storageRect, "WULA_ViewGlobalStorage".Translate()))
|
||||
{
|
||||
Find.WindowStack.Add(new Dialog_GlobalStorage());
|
||||
}
|
||||
|
||||
if (Mouse.IsOver(storageRect))
|
||||
{
|
||||
TooltipHandler.TipRegion(storageRect, "WULA_ViewGlobalStorageDesc".Translate());
|
||||
}
|
||||
}
|
||||
|
||||
private string GetInoperativeReason()
|
||||
{
|
||||
var submitter = SelSubmitter;
|
||||
|
||||
if (submitter.powerComp != null && !submitter.powerComp.PowerOn)
|
||||
return "WULA_NoPower".Translate();
|
||||
|
||||
if (submitter.refuelableComp != null && !submitter.refuelableComp.HasFuel)
|
||||
return "WULA_NoFuel".Translate();
|
||||
|
||||
if (submitter.flickableComp != null && !submitter.flickableComp.SwitchIsOn)
|
||||
return "WULA_SwitchOff".Translate();
|
||||
|
||||
return "WULA_UnknownReason".Translate();
|
||||
}
|
||||
|
||||
public override void TabUpdate()
|
||||
{
|
||||
base.TabUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
// 简单的全局存储查看对话框
|
||||
public class Dialog_GlobalStorage : Window
|
||||
{
|
||||
private Vector2 scrollPosition;
|
||||
private float scrollViewHeight;
|
||||
|
||||
public override Vector2 InitialSize => new Vector2(500f, 600f);
|
||||
|
||||
public Dialog_GlobalStorage()
|
||||
{
|
||||
forcePause = false;
|
||||
doCloseX = true;
|
||||
doCloseButton = true;
|
||||
closeOnClickedOutside = true;
|
||||
absorbInputAroundWindow = true;
|
||||
}
|
||||
|
||||
public override void DoWindowContents(Rect inRect)
|
||||
{
|
||||
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
|
||||
if (globalStorage == null)
|
||||
{
|
||||
Widgets.Label(inRect, "WULA_NoGlobalStorage".Translate());
|
||||
return;
|
||||
}
|
||||
|
||||
Rect titleRect = new Rect(0f, 0f, inRect.width, 30f);
|
||||
Text.Font = GameFont.Medium;
|
||||
Widgets.Label(titleRect, "WULA_GlobalStorage".Translate());
|
||||
Text.Font = GameFont.Small;
|
||||
|
||||
// 输入存储
|
||||
Rect inputRect = new Rect(0f, 40f, inRect.width, (inRect.height - 100f) / 2f);
|
||||
DoStorageSection(inputRect, globalStorage.inputStorage, "WULA_InputStorage".Translate());
|
||||
|
||||
// 输出存储
|
||||
Rect outputRect = new Rect(0f, 40f + (inRect.height - 100f) / 2f + 10f, inRect.width, (inRect.height - 100f) / 2f);
|
||||
DoStorageSection(outputRect, globalStorage.outputStorage, "WULA_OutputStorage".Translate());
|
||||
}
|
||||
|
||||
private void DoStorageSection(Rect rect, Dictionary<ThingDef, int> storage, string label)
|
||||
{
|
||||
Widgets.DrawMenuSection(rect);
|
||||
Rect innerRect = rect.ContractedBy(5f);
|
||||
|
||||
// 标题
|
||||
Text.Font = GameFont.Medium;
|
||||
Widgets.Label(new Rect(innerRect.x, innerRect.y, innerRect.width, 25f), label);
|
||||
Text.Font = GameFont.Small;
|
||||
|
||||
Rect listRect = new Rect(innerRect.x, innerRect.y + 30f, innerRect.width, innerRect.height - 35f);
|
||||
DoStorageList(listRect, storage);
|
||||
}
|
||||
|
||||
private void DoStorageList(Rect rect, Dictionary<ThingDef, int> storage)
|
||||
{
|
||||
Rect outRect = rect;
|
||||
Rect viewRect = new Rect(0f, 0f, rect.width - 16f, scrollViewHeight);
|
||||
|
||||
var items = storage
|
||||
.Where(kvp => kvp.Value > 0)
|
||||
.OrderByDescending(kvp => kvp.Value)
|
||||
.ThenBy(kvp => kvp.Key.label)
|
||||
.ToList();
|
||||
|
||||
Widgets.BeginScrollView(outRect, ref scrollPosition, viewRect);
|
||||
|
||||
float curY = 0f;
|
||||
|
||||
if (items.Count == 0)
|
||||
{
|
||||
Rect emptyRect = new Rect(0f, curY, viewRect.width, 30f);
|
||||
Text.Anchor = TextAnchor.MiddleCenter;
|
||||
Widgets.Label(emptyRect, "WULA_NoItems".Translate());
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
curY += 35f;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var kvp in items)
|
||||
{
|
||||
Rect itemRect = new Rect(0f, curY, viewRect.width, 25f);
|
||||
DoStorageItemRow(itemRect, kvp.Key, kvp.Value);
|
||||
curY += 28f;
|
||||
}
|
||||
}
|
||||
|
||||
if (Event.current.type == EventType.Layout)
|
||||
{
|
||||
scrollViewHeight = curY;
|
||||
}
|
||||
|
||||
Widgets.EndScrollView();
|
||||
}
|
||||
|
||||
private void DoStorageItemRow(Rect rect, ThingDef thingDef, int count)
|
||||
{
|
||||
Widgets.DrawHighlightIfMouseover(rect);
|
||||
|
||||
// 图标
|
||||
Rect iconRect = new Rect(rect.x + 2f, rect.y + 2f, 20f, 20f);
|
||||
Widgets.ThingIcon(iconRect, thingDef);
|
||||
|
||||
// 名称
|
||||
Rect nameRect = new Rect(rect.x + 25f, rect.y, rect.width - 80f, rect.height);
|
||||
Text.Anchor = TextAnchor.MiddleLeft;
|
||||
Widgets.Label(nameRect, thingDef.LabelCap);
|
||||
|
||||
// 数量
|
||||
Rect countRect = new Rect(rect.xMax - 50f, rect.y, 50f, rect.height);
|
||||
Text.Anchor = TextAnchor.MiddleRight;
|
||||
Widgets.Label(countRect, count.ToString());
|
||||
Text.Anchor = TextAnchor.UpperLeft;
|
||||
|
||||
// 工具提示
|
||||
if (Mouse.IsOver(rect))
|
||||
{
|
||||
string tooltip = $"{thingDef.LabelCap}\n{count} {"WULA_Items".Translate()}\n{"WULA_Value".Translate()}: {thingDef.BaseMarketValue * count}";
|
||||
TooltipHandler.TipRegion(rect, tooltip);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -107,9 +107,11 @@
|
||||
<Compile Include="Flyover\WULA_SpawnFlyOver\CompAbilityEffect_SpawnFlyOver.cs" />
|
||||
<Compile Include="Flyover\WULA_SpawnFlyOver\CompProperties_AbilitySpawnFlyOver.cs" />
|
||||
<Compile Include="GlobalWorkTable\Building_GlobalWorkTable.cs" />
|
||||
<Compile Include="GlobalWorkTable\Building_ResourceSubmitter.cs" />
|
||||
<Compile Include="GlobalWorkTable\GlobalProductionOrder.cs" />
|
||||
<Compile Include="GlobalWorkTable\GlobalProductionOrderStack.cs" />
|
||||
<Compile Include="GlobalWorkTable\GlobalStorageWorldComponent.cs" />
|
||||
<Compile Include="GlobalWorkTable\GlobalWorkTableAirdropExtension.cs" />
|
||||
<Compile Include="GlobalWorkTable\ITab_GlobalBills.cs" />
|
||||
<Compile Include="HediffComp\HediffCompProperties_NanoRepair.cs" />
|
||||
<Compile Include="HediffComp\WULA_HediffDamgeShield\DRMDamageShield.cs" />
|
||||
|
||||
Reference in New Issue
Block a user