This commit is contained in:
Tourswen
2025-11-04 00:45:25 +08:00
parent 0c02135040
commit 9905782298
36 changed files with 1851 additions and 1238 deletions

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<JobDef>
<defName>WULA_EnterMaintenancePod</defName>
<driverClass>WulaFallenEmpire.JobDriver_EnterMaintenancePod</driverClass>
@@ -29,4 +28,10 @@
<allowOpportunisticPrefix>true</allowOpportunisticPrefix>
<casualInterruptible>false</casualInterruptible>
</JobDef>
<JobDef>
<defName>WULA_RecycleMechanoid</defName>
<driverClass>WulaFallenEmpire.JobDriver_RecycleMechanoid</driverClass>
<reportString>正在准备进入猫猫地堡。</reportString>
</JobDef>
</Defs>

View File

@@ -1,11 +1,364 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<!-- 工作定义 -->
<JobDef>
<defName>WULA_RecycleMechanoid</defName>
<driverClass>WulaFallenEmpire.JobDriver_RecycleMechanoid</driverClass>
<reportString>正在准备进入猫猫地堡。</reportString>
</JobDef>
<!-- 墙和门 -->
<ThingDef ParentName="Wall">
<defName>WulaWall_Cleanzone</defName>
<label>乌拉帝国堡垒墙</label>
<description>清理出一块场地并准备好资源,使得乌拉帝国母舰可以向此处投放建筑。建造好的信标可以收起或移至他处,但是必须要有母舰或者后勤舰在上空才能投送建筑。\n\n乌拉帝国用于建造堡垒的外壁相当厚实能够抵御大量伤害并且拥有气密性可以用在飞船外壳上。</description>
<uiIconPath>Wula/Building/Linked/WULA_Fortress_Wall_MenuIcon</uiIconPath>
<minifiedDef>MinifiedThing</minifiedDef>
<tickerType>Normal</tickerType>
<thingCategories Inherit="False">
<li>BuildingsMisc</li>
</thingCategories>
<graphicData>
<texPath>Wula/Building/Linked/WulaWall/WulaWall_Atlas</texPath>
<graphicClass>Graphic_Multi</graphicClass>
<drawSize>(1,1)</drawSize>
<color>(73,185,254,155)</color>
</graphicData>
<neverMultiSelect>false</neverMultiSelect>
<blockLight>false</blockLight>
<holdsRoof>false</holdsRoof>
<coversFloor>false</coversFloor>
<blockWind>false</blockWind>
<altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability>
<pathCost>0</pathCost>
<castEdgeShadows>false</castEdgeShadows>
<useStuffTerrainAffordance>false</useStuffTerrainAffordance>
<staticSunShadowHeight Inherit="False" IsNull="True" />
<fillPercent>0</fillPercent>
<canOverlapZones>false</canOverlapZones>
<statBases>
<MaxHitPoints>1</MaxHitPoints>
<WorkToBuild>1</WorkToBuild>
<Mass>1</Mass>
<Flammability>0</Flammability>
</statBases>
<size>(1,1)</size>
<constructionSkillPrerequisite>0</constructionSkillPrerequisite>
<resourcesFractionWhenDeconstructed>1</resourcesFractionWhenDeconstructed>
<stuffCategories Inherit="False"/>
<costStuffCount>0</costStuffCount>
<costList>
<Steel>15</Steel>
</costList>
<building>
<destroySound>BuildingDestroyed_Metal_Small</destroySound>
<isAirtight>false</isAirtight>
<isStuffableAirtight>false</isStuffableAirtight>
</building>
<designationCategory>WULA_Buildings</designationCategory>
<comps>
<li Class="WulaFallenEmpire.CompProperties_SkyfallerCaller">
<skyfallerDef>WulaWall_Incoming</skyfallerDef> <!-- 替换为您想要的Skyfaller类型 -->
<destroyBuilding>true</destroyBuilding>
<delayTicks>1</delayTicks>
<requiredFlyOverType>WULA_Flyover_BaseBuilder</requiredFlyOverType>
<allowThinRoof>true</allowThinRoof>
<allowThickRoof>false</allowThickRoof>
<requiredFlyOverLabel>乌拉帝国母舰或后勤舰</requiredFlyOverLabel> <!-- 新增显示标签 -->
</li>
</comps>
</ThingDef>
<ThingDef ParentName="SkyfallerBase">
<defName>WulaWall_Incoming</defName>
<label>乌拉帝国堡垒墙(空投中)</label>
<size>(1,1)</size>
<graphicData>
<texPath>Wula/Building/Linked/WULA_Fortress_Wall_MenuIcon</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutFlying</shaderType>
<drawSize>(1,1)</drawSize>
</graphicData>
<skyfaller>
<movementType>Accelerate</movementType>
<shadow>Things/Skyfaller/SkyfallerShadowDropPod</shadow>
<shadowSize>(1, 1)</shadowSize>
<anticipationSound>DropPod_Fall</anticipationSound>
<anticipationSoundTicks>100</anticipationSoundTicks>
<impactSound>Explosion_Vaporize</impactSound>
<moteSpawnTime>0.05</moteSpawnTime>
<motesPerCell>1</motesPerCell>
<cameraShake>1</cameraShake>
<angleCurve>
<points>
<li>(0,0)</li>
<li>(1, 1)</li>
</points>
</angleCurve>
<spawnThing>WulaWall</spawnThing>
</skyfaller>
<comps>
<li Class="CompProperties_Effecter">
<effecterDef>Smoke_Joint</effecterDef>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="Wall" Name="WulaWall">
<defName>WulaWall</defName>
<label>乌拉帝国堡垒墙</label>
<description>乌拉帝国堡垒外壁,相当厚实,能够抵御大量伤害,并且拥有气密性,可以用在飞船外壳上。</description>
<uiOrder>1800</uiOrder>
<uiIconPath>Wula/Building/Linked/WULA_Fortress_Wall_MenuIcon</uiIconPath>
<graphicData>
<texPath>Wula/Building/Linked/WulaWall/WulaWall_Atlas</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutComplex</shaderType>
<color>(0.5, 0.5, 0.5)</color>
</graphicData>
<statBases>
<MarketValue>20</MarketValue>
<Beauty>1</Beauty>
<MaxHitPoints>1200</MaxHitPoints>
<WorkToBuild>2500</WorkToBuild>
<Flammability>0</Flammability>
<MeditationFocusStrength>0.22</MeditationFocusStrength>
<Cleanliness>0.1</Cleanliness>
</statBases>
<costList>
<Steel>15</Steel>
</costList>
<stuffCategories Inherit="False"/>
<costStuffCount>0</costStuffCount>
<building>
<isAirtight>true</isAirtight>
</building>
<recipeMaker>
<defaultIngredientFilter>
<categories>
<li>Metallic</li>
</categories>
</defaultIngredientFilter>
</recipeMaker>
<stuffCategories Inherit="False"/>
<useStuffTerrainAffordance>false</useStuffTerrainAffordance>
<terrainAffordanceNeeded>Heavy</terrainAffordanceNeeded>
<!-- <placeWorkers>
<li>PlaceWorker_OnSubstructure</li>
</placeWorkers> -->
<damageMultipliers Inherit="False">
<li>
<damageDef>Bomb</damageDef>
<multiplier>0.01</multiplier>
</li>
<li>
<damageDef>Thump</damageDef>
<multiplier>0.1</multiplier>
</li>
</damageMultipliers>
<comps Inherit="False">
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
<!-- <factionDef>Mechanoid</factionDef> 不写默认玩家派系-->
<usePlayerFactionIfNull>true</usePlayerFactionIfNull>
<overrideExistingFaction>false</overrideExistingFaction>
</li>
<li Class="CompProperties_MeditationFocus">
<statDef>MeditationFocusStrength</statDef>
<focusTypes>
<li>Minimal</li>
</focusTypes>
</li>
<li Class="CompProperties_Power">
<compClass>CompPowerTransmitter</compClass>
<transmitsPower>true</transmitsPower>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="DoorBase">
<defName>WulaDoor_Cleanzone</defName>
<label>乌拉帝国大门</label>
<description>清理出一块场地并准备好资源,使得乌拉帝国母舰可以向此处投放建筑。建造好的信标可以收起或移至他处,但是必须要有母舰或者后勤舰在上空才能投送建筑。\n\n乌拉帝国堡垒的大门不仅能够抵御大量爆炸和震荡伤害还拥有无需通电即可运转的伺服系统来增加大门通过速度。</description>
<thingClass>Building_Door</thingClass>
<category>Building</category>
<altitudeLayer>DoorMoveable</altitudeLayer>
<fillPercent>1</fillPercent>
<useHitPoints>true</useHitPoints>
<graphicData>
<texPath>Wula/Building/Door/WulaAutodoor_Mover</texPath>
<graphicClass>Graphic_Single</graphicClass>
<color>(73,185,254,155)</color>
</graphicData>
<statBases>
<MarketValue>1</MarketValue>
<MaxHitPoints>1</MaxHitPoints>
<Flammability>0</Flammability>
<WorkToBuild>1</WorkToBuild>
</statBases>
<costList>
<Steel>50</Steel>
<ComponentIndustrial>1</ComponentIndustrial>
</costList>
<castEdgeShadows>false</castEdgeShadows>
<staticSunShadowHeight Inherit="False" IsNull="True" />
<leaveResourcesWhenKilled>false</leaveResourcesWhenKilled>
<selectable>true</selectable>
<neverMultiSelect>false</neverMultiSelect>
<blockLight>false</blockLight>
<holdsRoof>false</holdsRoof>
<coversFloor>false</coversFloor>
<blockWind>false</blockWind>
<tickerType>Normal</tickerType>
<passability>PassThroughOnly</passability>
<pathCost>0</pathCost>
<useStuffTerrainAffordance>false</useStuffTerrainAffordance>
<stuffCategories Inherit="False"></stuffCategories>
<terrainAffordanceNeeded>Light</terrainAffordanceNeeded>
<designationCategory>Structure</designationCategory>
<drawerType>RealtimeOnly</drawerType>
<designationCategory>WULA_Buildings</designationCategory>
<building>
<paintable>true</paintable>
<isInert>true</isInert>
<canPlaceOverWall>true</canPlaceOverWall>
<soundDoorOpenPowered>Door_OpenPowered</soundDoorOpenPowered>
<soundDoorClosePowered>Door_ClosePowered</soundDoorClosePowered>
<soundDoorOpenManual>Door_OpenManual</soundDoorOpenManual>
<soundDoorCloseManual>Door_CloseManual</soundDoorCloseManual>
<blueprintClass>Blueprint_Door</blueprintClass>
<blueprintGraphicData>
<texPath>Wula/Building/Door/WulaAutodoor_BluePrint</texPath>
</blueprintGraphicData>
<ai_chillDestination>false</ai_chillDestination>
</building>
<uiOrder>2505</uiOrder>
<uiIconPath>Wula/Building/Door/WulaAutodoor</uiIconPath>
<comps>
<li Class="WulaFallenEmpire.CompProperties_SkyfallerCaller">
<skyfallerDef>WulaDoor_Incoming</skyfallerDef> <!-- 替换为您想要的Skyfaller类型 -->
<destroyBuilding>true</destroyBuilding>
<delayTicks>1</delayTicks>
<requiredFlyOverType>WULA_Flyover_BaseBuilder</requiredFlyOverType>
<allowThinRoof>true</allowThinRoof>
<allowThickRoof>false</allowThickRoof>
<requiredFlyOverLabel>乌拉帝国母舰或后勤舰</requiredFlyOverLabel> <!-- 新增显示标签 -->
</li>
</comps>
</ThingDef>
<ThingDef ParentName="SkyfallerBase">
<defName>WulaDoor_Incoming</defName>
<label>乌拉帝国大门(空投中)</label>
<size>(1,1)</size>
<graphicData>
<texPath>Wula/Building/Linked/WULA_Fortress_Wall_MenuIcon</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutFlying</shaderType>
<drawSize>(1,1)</drawSize>
</graphicData>
<skyfaller>
<movementType>Accelerate</movementType>
<shadow>Things/Skyfaller/SkyfallerShadowDropPod</shadow>
<shadowSize>(1, 1)</shadowSize>
<anticipationSound>DropPod_Fall</anticipationSound>
<anticipationSoundTicks>100</anticipationSoundTicks>
<impactSound>Explosion_Vaporize</impactSound>
<moteSpawnTime>0.05</moteSpawnTime>
<motesPerCell>1</motesPerCell>
<cameraShake>1</cameraShake>
<angleCurve>
<points>
<li>(0,0)</li>
<li>(1, 1)</li>
</points>
</angleCurve>
<spawnThing>WulaDoor</spawnThing>
</skyfaller>
<comps>
<li Class="CompProperties_Effecter">
<effecterDef>Smoke_Joint</effecterDef>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="DoorBase">
<defName>WulaDoor</defName>
<label>乌拉帝国大门</label>
<description>乌拉帝国堡垒的大门,不仅能够抵御大量爆炸和震荡伤害,还拥有无需通电即可运转的伺服系统来增加大门通过速度。</description>
<thingClass>Building_Door</thingClass>
<category>Building</category>
<altitudeLayer>DoorMoveable</altitudeLayer>
<fillPercent>1</fillPercent>
<useHitPoints>true</useHitPoints>
<graphicData>
<texPath>Wula/Building/Door/WulaAutodoor_Mover</texPath>
<graphicClass>Graphic_Single</graphicClass>
<damageData>
<!-- no damage marks because they don't move with the door
<rect>(0,0.12,1,0.76)</rect>-->
<enabled>false</enabled>
</damageData>
</graphicData>
<statBases>
<MarketValue>2</MarketValue>
<MaxHitPoints>1000</MaxHitPoints>
<Flammability>0</Flammability>
<WorkToBuild>3000</WorkToBuild>
<Beauty>20</Beauty>
<DoorOpenSpeed>2</DoorOpenSpeed>
</statBases>
<costList>
<Steel>50</Steel>
<ComponentIndustrial>1</ComponentIndustrial>
</costList>
<leaveResourcesWhenKilled>false</leaveResourcesWhenKilled>
<selectable>true</selectable>
<tickerType>Normal</tickerType>
<!-- <rotatable>false</rotatable> -->
<!--<soundImpactDefault>BulletImpact_Metal</soundImpactDefault>-->
<useStuffTerrainAffordance>false</useStuffTerrainAffordance>
<stuffCategories Inherit="False"></stuffCategories>
<terrainAffordanceNeeded>Light</terrainAffordanceNeeded>
<designationCategory>Structure</designationCategory>
<holdsRoof>true</holdsRoof>
<staticSunShadowHeight>1.0</staticSunShadowHeight>
<blockLight>true</blockLight>
<drawerType>RealtimeOnly</drawerType>
<comps>
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
<!-- <factionDef>Mechanoid</factionDef> 不写默认玩家派系-->
<usePlayerFactionIfNull>true</usePlayerFactionIfNull>
<overrideExistingFaction>false</overrideExistingFaction>
</li>
<li Class="CompProperties_Power">
<compClass>CompPowerTrader</compClass>
<basePowerConsumption>0</basePowerConsumption>
<transmitsPower>true</transmitsPower>
</li>
<li Class="CompProperties_Breakdownable"/>
<li Class="CompProperties_Forbiddable">
<allowNonPlayer>true</allowNonPlayer>
</li>
</comps>
<damageMultipliers>
<li>
<damageDef>Bomb</damageDef>
<multiplier>0.01</multiplier>
</li>
<li>
<damageDef>Thump</damageDef>
<multiplier>0.01</multiplier>
</li>
</damageMultipliers>
<building>
<paintable>true</paintable>
<isInert>true</isInert>
<canPlaceOverWall>true</canPlaceOverWall>
<soundDoorOpenPowered>Door_OpenPowered</soundDoorOpenPowered>
<soundDoorClosePowered>Door_ClosePowered</soundDoorClosePowered>
<soundDoorOpenManual>Door_OpenManual</soundDoorOpenManual>
<soundDoorCloseManual>Door_CloseManual</soundDoorCloseManual>
<blueprintClass>Blueprint_Door</blueprintClass>
<blueprintGraphicData>
<texPath>Wula/Building/Door/WulaAutodoor_BluePrint</texPath>
</blueprintGraphicData>
<ai_chillDestination>false</ai_chillDestination>
</building>
<uiOrder>2505</uiOrder>
<uiIconPath>Wula/Building/Door/WulaAutodoor</uiIconPath>
<designationHotKey>Misc2</designationHotKey>
</ThingDef>
<!-- 猫猫地堡 -->
<ThingDef ParentName="BuildingBase">
<defName>WULA_Cat_Bunker_Cleanzone</defName>
<label>乌拉猫猫地堡</label>
@@ -32,10 +385,10 @@
</graphicData>
<altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability>
<pathCost>0</pathCost>
<castEdgeShadows>false</castEdgeShadows>
<fillPercent>0.5</fillPercent>
<canOverlapZones>false</canOverlapZones>
<pathCost>0</pathCost>
<hasInteractionCell>false</hasInteractionCell>
<!-- <interactionCellOffset>(0,0,-2)</interactionCellOffset> -->
<statBases>
@@ -49,6 +402,7 @@
<resourcesFractionWhenDeconstructed>1</resourcesFractionWhenDeconstructed>
<costList>
<Steel>300</Steel>
<ComponentIndustrial>3</ComponentIndustrial>
</costList>
<building>
<destroySound>BuildingDestroyed_Metal_Small</destroySound>
@@ -134,7 +488,7 @@
<!-- <interactionCellOffset>(0,0,-2)</interactionCellOffset> -->
<resourcesFractionWhenDeconstructed>0</resourcesFractionWhenDeconstructed>
<statBases>
<MaxHitPoints>2500</MaxHitPoints>
<MaxHitPoints>500</MaxHitPoints>
<WorkToBuild>1</WorkToBuild>
<Mass>20</Mass>
<Flammability>0</Flammability>
@@ -148,6 +502,11 @@
<turretBurstCooldownTime>3.5</turretBurstCooldownTime>
</building>
<comps>
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
<!-- <factionDef>Mechanoid</factionDef> 不写默认玩家派系-->
<usePlayerFactionIfNull>true</usePlayerFactionIfNull>
<overrideExistingFaction>false</overrideExistingFaction>
</li>
<li Class="WulaFallenEmpire.CompProperties_MechanoidRecycler">
<maxStorageCapacity>6</maxStorageCapacity>
@@ -228,11 +587,194 @@
</verbs>
</ThingDef>
<!-- 维护舱 -->
<ThingDef ParentName="BuildingBase">
<defName>WULA_MaintenancePod_Cleanzone</defName>
<label>合成人维护舱</label>
<description>清理出一块场地并准备好资源,使得乌拉帝国母舰可以向此处投放建筑。建造好的信标可以收起或移至他处,但是必须要有母舰或者后勤舰在上空才能投送建筑。\n\n合成人维护舱是为乌拉帝国合成人设计的设施机械乌拉需要定期进入其中进行身体机能的维护和校准否则他们的身体会逐渐衰弱。维护需要消耗零部件同时会修理所有的伤口。</description>
<uiIconPath>Wula/Building/WULA_MaintenancePod_south</uiIconPath>
<minifiedDef>MinifiedThing</minifiedDef>
<tickerType>Normal</tickerType>
<descriptionHyperlinks>
<ThingDef>WulaSpecies</ThingDef>
</descriptionHyperlinks>
<thingCategories Inherit="False">
<li>BuildingsMisc</li>
</thingCategories>
<graphicData>
<texPath>Wula/Building/WULA_Dropping_Building_Cleanzone</texPath>
<graphicClass>Graphic_Multi</graphicClass>
<drawSize>(3,3)</drawSize>
<damageData>
<enabled>false</enabled>
</damageData>
</graphicData>
<altitudeLayer>Building</altitudeLayer>
<passability>PassThroughOnly</passability>
<castEdgeShadows>false</castEdgeShadows>
<fillPercent>0.5</fillPercent>
<canOverlapZones>false</canOverlapZones>
<pathCost>0</pathCost>
<hasInteractionCell>false</hasInteractionCell>
<!-- <interactionCellOffset>(0,0,-2)</interactionCellOffset> -->
<statBases>
<MaxHitPoints>1</MaxHitPoints>
<WorkToBuild>1</WorkToBuild>
<Mass>1</Mass>
<Flammability>0</Flammability>
</statBases>
<size>(3,3)</size>
<constructionSkillPrerequisite>0</constructionSkillPrerequisite>
<resourcesFractionWhenDeconstructed>1</resourcesFractionWhenDeconstructed>
<costList>
<Steel>150</Steel>
<ComponentIndustrial>10</ComponentIndustrial>
</costList>
<building>
<destroySound>BuildingDestroyed_Metal_Small</destroySound>
</building>
<designationCategory>WULA_Buildings</designationCategory>
<comps>
<li Class="WulaFallenEmpire.CompProperties_SkyfallerCaller">
<skyfallerDef>WULA_MaintenancePod_Incoming</skyfallerDef> <!-- 替换为您想要的Skyfaller类型 -->
<destroyBuilding>true</destroyBuilding>
<delayTicks>1</delayTicks>
<requiredFlyOverType>WULA_Flyover_BaseBuilder</requiredFlyOverType>
<allowThinRoof>true</allowThinRoof>
<allowThickRoof>false</allowThickRoof>
<requiredFlyOverLabel>乌拉帝国母舰或后勤舰</requiredFlyOverLabel> <!-- 新增显示标签 -->
</li>
</comps>
</ThingDef>
<ThingDef ParentName="SkyfallerBase">
<defName>WULA_MaintenancePod_Incoming</defName>
<label>合成人维护舱(空投中)</label>
<size>(3,3)</size>
<graphicData>
<texPath>Wula/Building/WULA_MaintenancePod_Incoming</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutFlying</shaderType>
<drawSize>(3,3)</drawSize>
</graphicData>
<skyfaller>
<movementType>Accelerate</movementType>
<shadow>Things/Skyfaller/SkyfallerShadowDropPod</shadow>
<shadowSize>(2, 2)</shadowSize>
<anticipationSound>DropPod_Fall</anticipationSound>
<anticipationSoundTicks>100</anticipationSoundTicks>
<impactSound>Explosion_Vaporize</impactSound>
<moteSpawnTime>0.05</moteSpawnTime>
<motesPerCell>1</motesPerCell>
<cameraShake>1</cameraShake>
<angleCurve>
<points>
<li>(0,0)</li>
<li>(1, 1)</li>
</points>
</angleCurve>
<spawnThing>WULA_MaintenancePod</spawnThing>
</skyfaller>
<comps>
<li Class="CompProperties_Effecter">
<effecterDef>Smoke_Joint</effecterDef>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BuildingBase">
<defName>WULA_MaintenancePod</defName>
<label>合成人维护舱</label>
<description>一个为乌拉帝国合成人设计的全自动维护舱,机械乌拉需要定期进入其中进行身体机能的维护和校准,否则他们的身体会逐渐衰弱。维护需要消耗零部件,同时会修理所有的伤口。</description>
<containedPawnsSelectable>true</containedPawnsSelectable>
<graphicData>
<texPath>Wula/Building/WULA_MaintenancePod_south</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>(3, 3)</drawSize>
<shadowData>
<volume>(1.5, 1.5, 1.7)</volume>
<!-- <offset>(-0.05, 0, 0.35)</offset> -->
</shadowData>
</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>
<rotatable>false</rotatable>
<statBases>
<MaxHitPoints>250</MaxHitPoints>
<WorkToBuild>20000</WorkToBuild>
<Mass>50</Mass>
<Flammability>0.5</Flammability>
<Beauty>-5</Beauty>
</statBases>
<size>(3,3)</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>
</building>
<tickerType>Normal</tickerType>
<comps>
<li Class="WulaFallenEmpire.CompProperties_FactionSetter">
<!-- <factionDef>Mechanoid</factionDef> 不写默认玩家派系-->
<usePlayerFactionIfNull>true</usePlayerFactionIfNull>
<overrideExistingFaction>false</overrideExistingFaction>
</li>
<li Class="CompProperties_Power">
<compClass>CompPowerTrader</compClass>
<basePowerConsumption>50</basePowerConsumption> <!-- This is now idle power -->
</li>
<li Class="CompProperties_Flickable"/>
<li Class="CompProperties_Refuelable">
<fuelCapacity>20.0</fuelCapacity>
<fuelFilter>
<thingDefs>
<li>ComponentIndustrial</li>
</thingDefs>
</fuelFilter>
<fuelLabel>零部件</fuelLabel>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
</li>
<li Class="WulaFallenEmpire.CompProperties_MaintenancePod">
<enterSound>BiosculpterPod_Enter</enterSound>
<exitSound>BiosculpterPod_Exit</exitSound>
<operatingEffecter>BiosculpterPod_Operating</operatingEffecter>
<baseDurationTicks>30000</baseDurationTicks> <!-- 1天 -->
<ticksPerNeedLevel>150000</ticksPerNeedLevel> <!-- 每降低1点需求需要2天 -->
<powerConsumptionRunning>500</powerConsumptionRunning>
<powerConsumptionIdle>25</powerConsumptionIdle>
<componentCostPerNeedLevel>2</componentCostPerNeedLevel>
<baseComponentCost>1</baseComponentCost>
<minNeedLevelToMaintain>0.3</minNeedLevelToMaintain>
<needLevelAfterCycle>1.0</needLevelAfterCycle>
<healInjuries>true</healInjuries>
<healMissingParts>true</healMissingParts>
<maxInjuriesHealedPerCycle>5</maxInjuriesHealedPerCycle>
</li>
</comps>
<placeWorkers>
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
</placeWorkers>
</ThingDef>
<!-- 制造机 -->
<ThingDef ParentName="BenchBase">
<defName>WULA_Cube_Productor_BIO</defName>
<label>乌拉帝国编织体(生物能)</label>
<description>一台仿制乌拉帝国科技而建造的塑性构造体,不仅要消耗大量木头用以提供生物能,还只能生产基础的衣物和能源核心用以维持生存——不过它很轻,可以随探险队一起移动</description>
<defName>WULA_WeaponArmor_Productor</defName>
<label>乌拉帝国作业通讯台</label>
<description>一台用于联络乌拉帝国母舰和后勤舰并下达武器、装备订单的特殊通讯台。这些武器装备的生产不需要殖民地参与,只需要使用乌拉帝国物资交换舱将材料发送给帝国舰队,然后根据材料数量下单即可</description>
<thingClass>WulaFallenEmpire.Building_GlobalWorkTable</thingClass>
<drawerType>MapMeshAndRealTime</drawerType>
<tickerType>Normal</tickerType>
@@ -280,12 +822,6 @@
<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>WulaFallenEmpire.ITab_GlobalBills</li>
</inspectorTabs>
@@ -328,21 +864,14 @@
<costList>
<Steel>20</Steel>
</costList>
<!-- <comps>
<li Class="CompProperties_Transporter">
<massCapacity>300</massCapacity>
<restEffectiveness>0.8</restEffectiveness>
<canChangeAssignedThingsAfterStarting>true</canChangeAssignedThingsAfterStarting>
<max1PerGroup>true</max1PerGroup>
<comps>
<li Class="WulaFallenEmpire.CompProperties_ResourceSubmitter">
<massCapacity>500</massCapacity>
</li>
<li Class="CompProperties_Launchable_TransportPod">
<skyfallerLeaving>ARA_DropPodLeaving</skyfallerLeaving>
<requiresFuelingPort>false</requiresFuelingPort>
<fixedLaunchDistanceMax>53</fixedLaunchDistanceMax>
</li>
</comps> -->
</comps>
<inspectorTabs>
<li>WulaFallenEmpire.ITab_ResourceSubmitterContents</li>
<li>ITab_Storage</li>
<!-- <li>WulaFallenEmpire.ITab_ResourceSubmitterContents</li> -->
</inspectorTabs>
<placeWorkers>
<li>PlaceWorker_NotUnderRoof</li>

View File

@@ -1,64 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<ThingDef ParentName="Wall" Name="WulaWall">
<defName>WulaWall</defName>
<label>乌拉帝国堡垒</label>
<description>乌拉帝国堡垒外壁,相当厚实,能够抵御大量伤害,并且拥有气密性,可以用在飞船外壳上。</description>
<uiOrder>1800</uiOrder>
<uiIconPath>Wula/Building/Linked/WULA_Fortress_Wall_MenuIcon</uiIconPath>
<graphicData>
<texPath>Wula/Building/Linked/WulaWall/WulaWall_Atlas</texPath>
<graphicClass>Graphic_Single</graphicClass>
<shaderType>CutoutComplex</shaderType>
<color>(0.5, 0.5, 0.5)</color>
</graphicData>
<designationCategory>Structure</designationCategory>
<researchPrerequisites>
<li>WULA_Structure_Technology</li>
</researchPrerequisites>
<statBases>
<MarketValue>2</MarketValue>
<Beauty>1</Beauty>
<MaxHitPoints>10000</MaxHitPoints>
<WorkToBuild>2500</WorkToBuild>
<Flammability>0</Flammability>
<MeditationFocusStrength>0.22</MeditationFocusStrength>
<Cleanliness>0.1</Cleanliness>
</statBases>
<costStuffCount>15</costStuffCount>
<building>
<isAirtight>true</isAirtight>
</building>
<stuffCategories Inherit="False">
<li>Metallic</li>
</stuffCategories>
<terrainAffordanceNeeded>Heavy</terrainAffordanceNeeded>
<!-- <placeWorkers>
<li>PlaceWorker_OnSubstructure</li>
</placeWorkers> -->
<damageMultipliers Inherit="False">
<li>
<damageDef>Bomb</damageDef>
<multiplier>0.01</multiplier>
</li>
<li>
<damageDef>Thump</damageDef>
<multiplier>0.1</multiplier>
</li>
</damageMultipliers>
<comps Inherit="False">
<li Class="CompProperties_MeditationFocus">
<statDef>MeditationFocusStrength</statDef>
<focusTypes>
<li>Minimal</li>
</focusTypes>
</li>
<li Class="CompProperties_Power">
<compClass>CompPowerTransmitter</compClass>
<transmitsPower>true</transmitsPower>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="WulaWall">
<defName>WulaWall_Indoor</defName>
@@ -66,92 +7,6 @@
<description>乌拉帝国非承重墙体,相当厚实,能够抵御大量伤害,并且拥有气密性,可以用在飞船外壳上。</description>
</ThingDef>
<ThingDef ParentName="DoorBase">
<defName>WulaDoor</defName>
<label>乌拉帝国大门</label>
<description>乌拉帝国堡垒的大门,不仅能够抵御大量爆炸和震荡伤害,还拥有无需通电即可运转的伺服系统来增加大门通过速度。</description>
<thingClass>Building_Door</thingClass>
<category>Building</category>
<altitudeLayer>DoorMoveable</altitudeLayer>
<fillPercent>1</fillPercent>
<useHitPoints>true</useHitPoints>
<graphicData>
<texPath>Wula/Building/Door/WulaAutodoor_Mover</texPath>
<graphicClass>Graphic_Single</graphicClass>
<damageData>
<!-- no damage marks because they don't move with the door
<rect>(0,0.12,1,0.76)</rect>-->
<enabled>false</enabled>
</damageData>
</graphicData>
<statBases>
<MarketValue>2</MarketValue>
<MaxHitPoints>7500</MaxHitPoints>
<Flammability>0</Flammability>
<WorkToBuild>3000</WorkToBuild>
<Beauty>20</Beauty>
<DoorOpenSpeed>2</DoorOpenSpeed>
</statBases>
<costList>
<Steel>50</Steel>
<ComponentIndustrial>1</ComponentIndustrial>
</costList>
<leaveResourcesWhenKilled>false</leaveResourcesWhenKilled>
<selectable>true</selectable>
<tickerType>Normal</tickerType>
<!-- <rotatable>false</rotatable> -->
<!--<soundImpactDefault>BulletImpact_Metal</soundImpactDefault>-->
<useStuffTerrainAffordance>false</useStuffTerrainAffordance>
<stuffCategories Inherit="False"></stuffCategories>
<terrainAffordanceNeeded>Light</terrainAffordanceNeeded>
<designationCategory>Structure</designationCategory>
<holdsRoof>true</holdsRoof>
<staticSunShadowHeight>1.0</staticSunShadowHeight>
<blockLight>true</blockLight>
<drawerType>RealtimeOnly</drawerType>
<comps>
<li Class="CompProperties_Power">
<compClass>CompPowerTrader</compClass>
<basePowerConsumption>0</basePowerConsumption>
<transmitsPower>true</transmitsPower>
</li>
<li Class="CompProperties_Breakdownable"/>
<li Class="CompProperties_Forbiddable">
<allowNonPlayer>true</allowNonPlayer>
</li>
</comps>
<damageMultipliers>
<li>
<damageDef>Bomb</damageDef>
<multiplier>0.01</multiplier>
</li>
<li>
<damageDef>Thump</damageDef>
<multiplier>0.01</multiplier>
</li>
</damageMultipliers>
<building>
<paintable>true</paintable>
<isInert>true</isInert>
<canPlaceOverWall>true</canPlaceOverWall>
<soundDoorOpenPowered>Door_OpenPowered</soundDoorOpenPowered>
<soundDoorClosePowered>Door_ClosePowered</soundDoorClosePowered>
<soundDoorOpenManual>Door_OpenManual</soundDoorOpenManual>
<soundDoorCloseManual>Door_CloseManual</soundDoorCloseManual>
<blueprintClass>Blueprint_Door</blueprintClass>
<blueprintGraphicData>
<texPath>Wula/Building/Door/WulaAutodoor_BluePrint</texPath>
</blueprintGraphicData>
<ai_chillDestination>false</ai_chillDestination>
</building>
<uiOrder>2505</uiOrder>
<uiIconPath>Wula/Building/Door/WulaAutodoor</uiIconPath>
<designationHotKey>Misc2</designationHotKey>
<constructionSkillPrerequisite>6</constructionSkillPrerequisite>
<researchPrerequisites>
<li>WULA_Structure_Technology</li>
</researchPrerequisites>
</ThingDef>
<TerrainDef ParentName="FloorBase">
<defName>WulaFloor</defName>

View File

@@ -655,98 +655,4 @@
</li>
</verbs>
</ThingDef>
<!-- 维护舱 -->
<ThingDef ParentName="BuildingBase">
<defName>WULA_MaintenancePod</defName>
<label>维护舱</label>
<description>一个为乌拉帝国合成人设计的全自动维护舱,机械乌拉需要定期进入其中进行身体机能的维护和校准,否则他们的身体会逐渐衰弱。维护需要消耗零部件,同时会修理所有的伤口。</description>
<containedPawnsSelectable>true</containedPawnsSelectable>
<graphicData>
<texPath>Wula/Building/WULA_Synth_Maintainer_south</texPath>
<graphicClass>Graphic_Single</graphicClass>
<drawSize>(3, 3)</drawSize>
<shadowData>
<volume>(1.5, 1.5, 1.7)</volume>
<!-- <offset>(-0.05, 0, 0.35)</offset> -->
</shadowData>
</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>
<rotatable>false</rotatable>
<statBases>
<MaxHitPoints>250</MaxHitPoints>
<WorkToBuild>20000</WorkToBuild>
<Mass>50</Mass>
<Flammability>0.5</Flammability>
<Beauty>-5</Beauty>
</statBases>
<size>(3,3)</size>
<interactionCellOffset>(0,0,-2)</interactionCellOffset>
<hasInteractionCell>true</hasInteractionCell>
<researchPrerequisites Inherit="False">
<li>WULA_Base_Technology</li>
</researchPrerequisites>
<costList>
<Steel>150</Steel>
<ComponentIndustrial>10</ComponentIndustrial>
<!-- <ComponentSpacer>2</ComponentSpacer> -->
</costList>
<constructionSkillPrerequisite>8</constructionSkillPrerequisite>
<building>
<destroySound>BuildingDestroyed_Metal_Big</destroySound>
<uninstallWork>1800</uninstallWork>
</building>
<designationCategory>WULA_Buildings</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="CompProperties_Refuelable">
<fuelCapacity>20.0</fuelCapacity>
<fuelFilter>
<thingDefs>
<li>ComponentIndustrial</li>
</thingDefs>
</fuelFilter>
<fuelLabel>零部件</fuelLabel>
<consumeFuelOnlyWhenUsed>true</consumeFuelOnlyWhenUsed>
<showAllowAutoRefuelToggle>true</showAllowAutoRefuelToggle>
<targetFuelLevelConfigurable>true</targetFuelLevelConfigurable>
</li>
<li Class="WulaFallenEmpire.CompProperties_MaintenancePod">
<enterSound>BiosculpterPod_Enter</enterSound>
<exitSound>BiosculpterPod_Exit</exitSound>
<operatingEffecter>BiosculpterPod_Operating</operatingEffecter>
<baseDurationTicks>30000</baseDurationTicks> <!-- 1天 -->
<ticksPerNeedLevel>150000</ticksPerNeedLevel> <!-- 每降低1点需求需要2天 -->
<powerConsumptionRunning>500</powerConsumptionRunning>
<powerConsumptionIdle>25</powerConsumptionIdle>
<componentCostPerNeedLevel>2</componentCostPerNeedLevel>
<baseComponentCost>1</baseComponentCost>
<minNeedLevelToMaintain>0.3</minNeedLevelToMaintain>
<needLevelAfterCycle>1.0</needLevelAfterCycle>
<healInjuries>true</healInjuries>
<healMissingParts>true</healMissingParts>
<maxInjuriesHealedPerCycle>5</maxInjuriesHealedPerCycle>
</li>
</comps>
<placeWorkers>
<li>PlaceWorker_PreventInteractionSpotOverlap</li>
</placeWorkers>
</ThingDef>
</Defs>

View File

@@ -30,6 +30,10 @@
<minShadowScale>0</minShadowScale>
<maxShadowScale>0</maxShadowScale>
<!-- 淡入淡出开关 -->
<useFadeEffects>false</useFadeEffects> <!-- 完全禁用淡入淡出 -->
<useFadeIn>false</useFadeIn> <!-- 仅禁用淡入 -->
<useFadeOut>false</useFadeOut> <!-- 仅禁用淡出 -->
<useApproachAnimation>true</useApproachAnimation>
<approachDuration>1</approachDuration>
<approachOffsetDistance>50</approachOffsetDistance>

View File

@@ -61,7 +61,7 @@
</tools>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_2_Stun_Technology</researchPrerequisite>
<skillRequirements>
@@ -117,7 +117,7 @@
</tools>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_2_Melee_Technology</researchPrerequisite>
<skillRequirements>
@@ -211,7 +211,7 @@
</tools>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_2_Stun_Technology</researchPrerequisite>
<skillRequirements>
@@ -315,8 +315,7 @@
<soundInteract>Interact_Rifle</soundInteract>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_BIO</li>
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -444,8 +443,7 @@
<soundInteract>Interact_ChargeRifle</soundInteract>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_BIO</li>
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -525,8 +523,7 @@
<recipeMaker>
<!-- <WorkToMake>60000</WorkToMake> -->
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_BIO</li>
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -613,7 +610,7 @@
<soundInteract>Interact_ChargeRifle</soundInteract>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_3_Bomb_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -765,8 +762,7 @@
<soundInteract>Interact_Rifle</soundInteract>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_BIO</li>
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_2_Laser_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -886,7 +882,7 @@
</costList>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_2_Laser_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -1089,7 +1085,7 @@
<soundInteract>Interact_ChargeRifle</soundInteract>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_2_Ranged_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -1194,7 +1190,7 @@
<tradeability>None</tradeability>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_3_Bomb_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -1571,7 +1567,7 @@
<soundInteract>Interact_Rifle</soundInteract>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_2_Missile_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>
@@ -1737,7 +1733,7 @@
<uiIconScale>0.8</uiIconScale>
<recipeMaker>
<recipeUsers Inherit="False">
<li>WULA_Cube_Productor_Energy</li>
<li>WULA_WeaponArmor_Productor</li>
</recipeUsers>
<researchPrerequisite>WULA_Synth_Weapon_3_Bomb_Technology</researchPrerequisite>
<unfinishedThingDef>UnfinishedWeapon</unfinishedThingDef>

View File

@@ -1,18 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<!-- 这里的节点名 "WulaFallenEmpire.WulaHullDef" 必须与C#代码中的 "命名空间.类名" 完全一致 -->
<WulaFallenEmpire.WulaHullDef>
<!-- <WulaFallenEmpire.WulaHullDef>
<defName>WulaHullConfig</defName>
<label>乌拉船体斜角配置</label>
<!-- 指定这个斜角效果应用在哪个建筑上 (使用建筑的defName) -->
<targetDef>WulaWall</targetDef>
<!-- 指定渲染时使用的着色器类型。CutoutComplex是建筑和墙体很常用的选项 -->
<shaderType>CutoutWithOverlay</shaderType>
<!-- 占位符: 所有贴图路径都暂时指向原版奥德赛DLC的贴图 -->
<texPath_Corner_NW>Wula/Building/Linked/WulaWall/AngledWulaWall_northwest</texPath_Corner_NW>
<texPath_Corner_NE>Wula/Building/Linked/WulaWall/AngledWulaWall_northeast</texPath_Corner_NE>
<texPath_Corner_SW>Wula/Building/Linked/WulaWall/AngledWulaWall_southwest</texPath_Corner_SW>
@@ -21,6 +16,5 @@
<texPath_Diagonal_NE>Wula/Building/Linked/WulaWall/AngledWulaWall_Partial_northeast</texPath_Diagonal_NE>
<texPath_Diagonal_SW>Wula/Building/Linked/WulaWall/AngledWulaWall_Partial_southwest</texPath_Diagonal_SW>
<texPath_Diagonal_SE>Wula/Building/Linked/WulaWall/AngledWulaWall_Partial_southeast</texPath_Diagonal_SE>
</WulaFallenEmpire.WulaHullDef>
</WulaFallenEmpire.WulaHullDef> -->
</Defs>

View File

@@ -145,8 +145,8 @@
<WULA_DaysSinceMaintenance>距上次维护</WULA_DaysSinceMaintenance>
<!-- Global Production System -->
<WULA_GlobalBillsTab>全球生产</WULA_GlobalBillsTab>
<WULA_GlobalProduction>生产订单</WULA_GlobalProduction>
<WULA_GlobalBillsTab>舰队生产</WULA_GlobalBillsTab>
<WULA_GlobalProduction>舰队生产订单</WULA_GlobalProduction>
<WULA_AddProductionOrder>添加生产订单</WULA_AddProductionOrder>
<WULA_Resume>恢复</WULA_Resume>
<WULA_Pause>暂停</WULA_Pause>
@@ -154,7 +154,7 @@
<WULA_WaitingForResources>等待资源</WULA_WaitingForResources>
<WULA_Completed>完成</WULA_Completed>
<WULA_Unknown>未知</WULA_Unknown>
<WULA_InsufficientResources>全球存储的资源不足</WULA_InsufficientResources>
<WULA_InsufficientResources>帝国舰队存储的资源不足</WULA_InsufficientResources>
<WULA_NoAvailableRecipes>无可用配方</WULA_NoAvailableRecipes>
<!-- 添加到您的语言文件中 -->
<WULA_RequiredIngredients>可用配方</WULA_RequiredIngredients>
@@ -172,18 +172,18 @@
<WULA_OutputItems>种输出物品</WULA_OutputItems>
<!-- 中文翻译 -->
<WULA_AirdropProducts>空投</WULA_AirdropProducts>
<WULA_AirdropProductsDesc>产物储存器中的所有物品通过空投舱分发到指定区域</WULA_AirdropProductsDesc>
<WULA_AirdropProducts>空投</WULA_AirdropProducts>
<WULA_AirdropProductsDesc>乌拉帝国母舰和后勤舰上完成加工的所有物品通过空投舱投放到指定区域</WULA_AirdropProductsDesc>
<WULA_CannotAirdrop>无法执行空投:工作台未就绪</WULA_CannotAirdrop>
<WULA_NoProductsToAirdrop>没有可空投的</WULA_NoProductsToAirdrop>
<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_ResourceSubmitter>舰队资源储存</WULA_ResourceSubmitter>
<WULA_SubmitToStorage>提交到舰队贮存</WULA_SubmitToStorage>
<WULA_SubmitToStorageDesc>将所有存储物品转移到舰队贮</WULA_SubmitToStorageDesc>
<WULA_StoredItems>存储物品</WULA_StoredItems>
<WULA_ItemsSubmitted>{0}个物品已提交到储存器</WULA_ItemsSubmitted>
<WULA_SubmissionFailed>提交物品失败</WULA_SubmissionFailed>
@@ -219,16 +219,27 @@
<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>
<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>
<WULA_LoadResourceSubmitter>Load Resource Submitter</WULA_LoadResourceSubmitter>
<WULA_NoItemsAvailable>No items available</WULA_NoItemsAvailable>
<WULA_Available>Available</WULA_Available>
<WULA_ToLoad>To Load</WULA_ToLoad>
<WULA_Max>Max</WULA_Max>
<WULA_SetLoadCount>Set load count for {0}</WULA_SetLoadCount>
<WULA_LoadAll>Load All</WULA_LoadAll>
<WULA_ClearAll>Clear All</WULA_ClearAll>
<WULA_LoadingJobsCreated>Loading jobs created</WULA_LoadingJobsCreated>
<WULA_Description>Description</WULA_Description>
</LanguageData>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 KiB

After

Width:  |  Height:  |  Size: 252 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

View File

@@ -0,0 +1,78 @@
using RimWorld;
using Verse;
namespace WulaFallenEmpire
{
public class CompProperties_FactionSetter : CompProperties
{
// 派系配置
public FactionDef factionDef = null; // 指定的派系定义
public bool usePlayerFactionIfNull = true; // 如果没有指定派系,是否使用玩家派系
public bool overrideExistingFaction = true; // 是否覆盖已有的派系归属
public CompProperties_FactionSetter()
{
compClass = typeof(CompFactionSetter);
}
}
public class CompFactionSetter : ThingComp
{
private bool factionSet = false;
public CompProperties_FactionSetter Props => (CompProperties_FactionSetter)props;
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
if (!respawningAfterLoad && !factionSet)
{
SetFaction();
factionSet = true;
}
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref factionSet, "factionSet", false);
}
private void SetFaction()
{
// 如果不需要覆盖已有派系且建筑已有派系,则跳过
if (!Props.overrideExistingFaction && parent.Faction != null)
return;
Faction faction = GetTargetFaction();
if (faction != null && faction != parent.Faction)
{
parent.SetFaction(faction);
Log.Message($"Set faction for {parent.Label} to {faction.Name}");
}
}
private Faction GetTargetFaction()
{
// 1. 如果指定了派系定义,使用该派系
if (Props.factionDef != null)
{
Faction faction = Find.FactionManager.FirstFactionOfDef(Props.factionDef);
if (faction != null)
return faction;
}
// 2. 默认使用玩家派系
if (Props.usePlayerFactionIfNull)
return Faction.OfPlayer;
return null;
}
public override string CompInspectStringExtra()
{
return base.CompInspectStringExtra();
}
}
}

View File

@@ -44,29 +44,9 @@ namespace WulaFallenEmpire
// 是否已经生成初始单位
private bool initialUnitsSpawned = false;
// 是否已经执行过归属权转换
private bool ownershipTransferred = false;
public int StoredCount => storedMechanoids.Count;
public int MaxStorage => Props.maxStorageCapacity;
// 强制归属权转换
private void TransferOwnership()
{
if (ownershipTransferred)
return;
// 获取目标派系(默认为玩家派系)
Faction targetFaction = Props.ownershipFaction ?? Faction.OfPlayer;
if (Faction != targetFaction)
{
Log.Message($"[MechanoidRecycler] Transferring ownership from {Faction?.Name ?? "NULL"} to {targetFaction.Name}");
SetFaction(targetFaction);
}
ownershipTransferred = true;
}
// 生成初始单位
private void SpawnInitialUnits()
@@ -105,12 +85,6 @@ namespace WulaFallenEmpire
{
base.SpawnSetup(map, respawningAfterLoad);
// 执行归属权转换
if (!respawningAfterLoad)
{
TransferOwnership();
}
// 如果不是从存档加载,生成初始单位
if (!respawningAfterLoad)
{
@@ -400,7 +374,6 @@ namespace WulaFallenEmpire
Scribe_Collections.Look(ref storedMechanoids, "storedMechanoids", LookMode.Reference);
Scribe_Collections.Look(ref spawnQueue, "spawnQueue", LookMode.Deep);
Scribe_Values.Look(ref initialUnitsSpawned, "initialUnitsSpawned", false);
Scribe_Values.Look(ref ownershipTransferred, "ownershipTransferred", false);
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{

View File

@@ -31,6 +31,11 @@ namespace WulaFallenEmpire
public float fadeOutStartProgress = 0.7f; // 开始淡出的进度阈值0-1
public float defaultFadeOutDuration = 1.5f; // 默认淡出持续时间(仅用于销毁)
// 新增:淡入淡出开关
private bool useFadeEffects = true;
private bool useFadeIn = true;
private bool useFadeOut = true;
// 进场动画相关 - 新增
public float approachDuration = 1.0f; // 进场动画持续时间(秒)
public float currentApproachTime = 0f; // 当前进场动画时间
@@ -183,32 +188,33 @@ namespace WulaFallenEmpire
}
}
// 淡入透明度0-1
// 修改后的淡入透明度属性
public float FadeInAlpha
{
get
{
if (fadeInCompleted) return 1f;
if (!useFadeIn || fadeInCompleted) return 1f;
return Mathf.Clamp01(currentFadeInTime / fadeInDuration);
}
}
// 淡出透明度0-1
// 修改后的淡出透明度属性
public float FadeOutAlpha
{
get
{
if (!fadeOutStarted) return 1f;
if (!useFadeOut || !fadeOutStarted) return 1f;
if (fadeOutCompleted) return 0f;
return Mathf.Clamp01(1f - (currentFadeOutTime / fadeOutDuration));
}
}
// 总体透明度(淡入 * 淡出)
// 修改后的总体透明度属性
public float OverallAlpha
{
get
{
if (!useFadeEffects) return 1f;
return FadeInAlpha * FadeOutAlpha;
}
}
@@ -246,7 +252,6 @@ namespace WulaFallenEmpire
{
innerContainer = new ThingOwner<Thing>(this);
}
public override void ExposeData()
{
base.ExposeData();
@@ -262,7 +267,6 @@ namespace WulaFallenEmpire
Scribe_Values.Look(ref fadeInDuration, "fadeInDuration", 1.5f);
Scribe_Values.Look(ref currentFadeInTime, "currentFadeInTime", 0f);
Scribe_Values.Look(ref fadeInCompleted, "fadeInCompleted", false);
// 淡出效果数据保存
Scribe_Values.Look(ref fadeOutDuration, "fadeOutDuration", 0f);
Scribe_Values.Look(ref currentFadeOutTime, "currentFadeOutTime", 0f);
@@ -270,56 +274,60 @@ namespace WulaFallenEmpire
Scribe_Values.Look(ref fadeOutCompleted, "fadeOutCompleted", false);
Scribe_Values.Look(ref fadeOutStartProgress, "fadeOutStartProgress", 0.7f);
Scribe_Values.Look(ref defaultFadeOutDuration, "defaultFadeOutDuration", 1.5f);
// 进场动画数据保存 - 新增
// 进场动画数据保存
Scribe_Values.Look(ref approachDuration, "approachDuration", 1.0f);
Scribe_Values.Look(ref currentApproachTime, "currentApproachTime", 0f);
Scribe_Values.Look(ref approachCompleted, "approachCompleted", false);
Scribe_Values.Look(ref approachOffsetDistance, "approachOffsetDistance", 3f);
Scribe_Values.Look(ref useApproachAnimation, "useApproachAnimation", true);
// 新增:淡入淡出开关保存
Scribe_Values.Look(ref useFadeEffects, "useFadeEffects", true);
Scribe_Values.Look(ref useFadeIn, "useFadeIn", true);
Scribe_Values.Look(ref useFadeOut, "useFadeOut", true);
Scribe_References.Look(ref caster, "caster");
Scribe_References.Look(ref faction, "faction");
}
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
base.SpawnSetup(map, respawningAfterLoad);
Log.Message($"FlyOver Spawned - Start: {startPosition}, End: {endPosition}, Speed: {flightSpeed}, Altitude: {altitude}");
if (!respawningAfterLoad)
{
Log.Message($"FlyOver Direction - Vector: {MovementDirection}, Rotation: {ExactRotation.eulerAngles}");
// 设置初始位置
base.Position = startPosition;
hasStarted = true;
// 重置淡入状态
currentFadeInTime = 0f;
fadeInCompleted = false;
// 重置淡出状态
currentFadeOutTime = 0f;
fadeOutStarted = false;
fadeOutCompleted = false;
fadeOutDuration = 0f;
// 重置进场动画状态 - 新增
currentApproachTime = 0f;
approachCompleted = false;
// 从 ModExtension 加载进场动画配置
// 从 ModExtension 加载配置
var extension = def.GetModExtension<FlyOverShadowExtension>();
if (extension != null)
{
useApproachAnimation = extension.useApproachAnimation;
approachDuration = extension.approachDuration;
approachOffsetDistance = extension.approachOffsetDistance;
// 加载淡入淡出配置
useFadeEffects = extension.useFadeEffects;
useFadeIn = extension.useFadeIn;
useFadeOut = extension.useFadeOut;
// 设置淡入淡出持续时间
fadeInDuration = extension.defaultFadeInDuration;
defaultFadeOutDuration = extension.defaultFadeOutDuration;
fadeOutStartProgress = extension.fadeOutStartProgress;
}
// 重置淡入状态
currentFadeInTime = 0f;
fadeInCompleted = !useFadeIn; // 如果不使用淡入,直接标记为完成
// 重置淡出状态
currentFadeOutTime = 0f;
fadeOutStarted = false;
fadeOutCompleted = false;
fadeOutDuration = 0f;
// 重置进场动画状态
currentApproachTime = 0f;
approachCompleted = !useApproachAnimation; // 如果不使用进场动画,直接标记为完成
Log.Message($"FlyOver fade effects: {useFadeEffects}, fadeIn: {useFadeIn}, fadeOut: {useFadeOut}");
Log.Message($"FlyOver approach animation: {useApproachAnimation}, duration: {approachDuration}s, offset: {approachOffsetDistance}");
// 开始飞行音效
if (playFlyOverSound && def.skyfaller?.floatingSound != null)
{
@@ -329,15 +337,12 @@ namespace WulaFallenEmpire
}
}
}
protected override void Tick()
{
base.Tick();
if (!hasStarted || hasCompleted)
return;
// 更新进场动画 - 新增
// 更新进场动画
if (useApproachAnimation && !approachCompleted)
{
currentApproachTime += 1f / 60f;
@@ -348,9 +353,8 @@ namespace WulaFallenEmpire
Log.Message("FlyOver approach animation completed");
}
}
// 更新淡入效果
if (!fadeInCompleted)
// 更新淡入效果(仅在启用时)
if (useFadeIn && !fadeInCompleted)
{
currentFadeInTime += 1f / 60f;
if (currentFadeInTime >= fadeInDuration)
@@ -359,18 +363,15 @@ namespace WulaFallenEmpire
currentFadeInTime = fadeInDuration;
}
}
// 更新飞行进度
currentProgress += flightSpeed * 0.001f;
// 检查是否应该开始淡出(基于剩余距离动态计算)
if (!fadeOutStarted && currentProgress >= fadeOutStartProgress)
// 检查是否应该开始淡出(仅在启用时)
if (useFadeOut && !fadeOutStarted && currentProgress >= fadeOutStartProgress)
{
StartFadeOut();
}
// 更新淡出效果
if (fadeOutStarted && !fadeOutCompleted)
// 更新淡出效果(仅在启用时)
if (useFadeOut && fadeOutStarted && !fadeOutCompleted)
{
currentFadeOutTime += 1f / 60f;
if (currentFadeOutTime >= fadeOutDuration)
@@ -380,19 +381,15 @@ namespace WulaFallenEmpire
Log.Message("FlyOver fade out completed");
}
}
// 更新当前位置
UpdatePosition();
// 维持飞行音效(在淡出时逐渐降低音量)
UpdateFlightSound();
// 检查是否到达终点
if (currentProgress >= 1f)
{
CompleteFlyOver();
}
// 生成飞行轨迹特效(在淡出时减少特效)
CreateFlightEffects();
}
@@ -419,11 +416,12 @@ namespace WulaFallenEmpire
}
}
// 修改后的音效更新方法
private void UpdateFlightSound()
{
if (flightSoundPlaying != null)
{
if (fadeOutStarted)
if (useFadeOut && fadeOutStarted)
{
// 淡出时逐渐降低音效音量
flightSoundPlaying.externalParams["VolumeFactor"] = FadeOutAlpha;
@@ -456,17 +454,16 @@ namespace WulaFallenEmpire
Destroy();
}
// 新增:紧急销毁方法(使用默认淡出时间)
// 修改后的紧急销毁方法
public void EmergencyDestroy()
{
if (!fadeOutStarted)
if (useFadeOut && !fadeOutStarted)
{
// 如果还没有开始淡出,使用默认淡出时间
fadeOutStarted = true;
fadeOutDuration = defaultFadeOutDuration;
Log.Message($"FlyOver emergency destroy with default fade out: {defaultFadeOutDuration}s");
}
// 设置标记,下一帧会处理淡出
hasCompleted = true;
}
@@ -483,6 +480,7 @@ namespace WulaFallenEmpire
innerContainer.Clear();
}
// 修改后的特效生成方法
private void CreateFlightEffects()
{
// 在飞行轨迹上生成粒子效果
@@ -490,13 +488,11 @@ namespace WulaFallenEmpire
{
Vector3 effectPos = DrawPos;
effectPos.y = AltitudeLayer.MoteOverhead.AltitudeFor();
// 淡出时减少粒子效果强度
float effectIntensity = fadeOutStarted ? FadeOutAlpha : 1f;
float effectIntensity = (useFadeOut && fadeOutStarted) ? FadeOutAlpha : 1f;
FleckMaker.ThrowSmoke(effectPos, base.Map, 1f * effectIntensity);
// 可选:根据速度生成更多效果
if (flightSpeed > 2f && !fadeOutStarted)
if (flightSpeed > 2f && !(useFadeOut && fadeOutStarted))
{
FleckMaker.ThrowAirPuffUp(effectPos, base.Map);
}
@@ -628,11 +624,12 @@ namespace WulaFallenEmpire
return innerContainer[0];
}
// 工具方法:创建飞越物体
// 修改后的 MakeFlyOver 方法,添加淡入淡出参数
public static FlyOver MakeFlyOver(ThingDef flyOverDef, IntVec3 start, IntVec3 end, Map map,
float speed = 1f, float height = 10f, ThingOwner contents = null,
float fadeInDuration = 1.5f, float defaultFadeOutDuration = 1.5f, Pawn casterPawn = null,
bool useApproachAnimation = true, float approachDuration = 1.0f, float approachOffsetDistance = 3f) // 新增参数
bool useApproachAnimation = true, float approachDuration = 1.0f, float approachOffsetDistance = 3f,
bool? useFadeEffects = null, bool? useFadeIn = null, bool? useFadeOut = null) // 新增参数
{
FlyOver flyOver = (FlyOver)ThingMaker.MakeThing(flyOverDef);
flyOver.startPosition = start;
@@ -642,13 +639,16 @@ namespace WulaFallenEmpire
flyOver.fadeInDuration = fadeInDuration;
flyOver.defaultFadeOutDuration = defaultFadeOutDuration;
flyOver.caster = casterPawn;
// 进场动画参数 - 新增
// 进场动画参数
flyOver.useApproachAnimation = useApproachAnimation;
flyOver.approachDuration = approachDuration;
flyOver.approachOffsetDistance = approachOffsetDistance;
// 简化派系设置 - 直接设置 faction 字段
// 淡入淡出参数 - 新增
if (useFadeEffects.HasValue) flyOver.useFadeEffects = useFadeEffects.Value;
if (useFadeIn.HasValue) flyOver.useFadeIn = useFadeIn.Value;
if (useFadeOut.HasValue) flyOver.useFadeOut = useFadeOut.Value;
// 简化派系设置
if (casterPawn != null && casterPawn.Faction != null)
{
flyOver.faction = casterPawn.Faction;
@@ -658,20 +658,18 @@ namespace WulaFallenEmpire
{
Log.Warning($"FlyOver: Cannot set faction - casterPawn: {casterPawn?.Label ?? "NULL"}, casterFaction: {casterPawn?.Faction?.Name ?? "NULL"}");
}
if (contents != null)
{
flyOver.innerContainer.TryAddRangeOrTransfer(contents);
}
GenSpawn.Spawn(flyOver, start, map);
Log.Message($"FlyOver created: {flyOver} from {start} to {end} at altitude {height}, Faction: {flyOver.faction?.Name ?? "NULL"}");
Log.Message($"FlyOver created: {flyOver} from {start} to {end} at altitude {height}, " +
$"FadeEffects: {flyOver.useFadeEffects}, FadeIn: {flyOver.useFadeIn}, FadeOut: {flyOver.useFadeOut}");
return flyOver;
}
}
// 扩展的 ModExtension 配置 - 新增进场动画参数
// 扩展的 ModExtension 配置 - 新增进场动画参数和淡入淡出开关
public class FlyOverShadowExtension : DefModExtension
{
public string customShadowPath;
@@ -684,17 +682,20 @@ namespace WulaFallenEmpire
public float defaultFadeInDuration = 1.5f;
public float defaultFadeOutDuration = 0.5f;
public float fadeOutStartProgress = 0.98f;
// 动态淡出配置
public float minFadeOutDuration = 0.5f;
public float maxFadeOutDuration = 0.5f;
public float fadeOutDistanceFactor = 0.01f;
public float ActuallyHeight = 150f;
// 进场动画配置 - 新增
public float ActuallyHeight = 150f;
// 进场动画配置
public bool useApproachAnimation = true;
public float approachDuration = 1.0f;
public float approachOffsetDistance = 3f;
// 新增:淡入淡出开关
public bool useFadeEffects = true; // 是否启用淡入淡出效果
public bool useFadeIn = true; // 是否启用淡入效果
public bool useFadeOut = true; // 是否启用淡出效果
}
}

View File

@@ -35,16 +35,17 @@ namespace WulaFallenEmpire
breakdownableComp = GetComp<CompBreakdownable>();
}
// 在 Building_GlobalWorkTable 类中修改 Tick 方法
protected override void Tick()
{
base.Tick();
// 改为每tick处理以实现精确的工作量控制
if (Find.TickManager.TicksGame % ProcessInterval == 0 &&
// 修复改为每60 ticks1秒处理一次避免每tick处理导致的精度问题
if (Find.TickManager.TicksGame % 60 == 0 &&
Find.TickManager.TicksGame != lastProcessTick)
{
lastProcessTick = Find.TickManager.TicksGame;
if (CurrentlyUsableForGlobalBills())
{
globalOrderStack.ProcessOrders();
@@ -84,7 +85,7 @@ namespace WulaFallenEmpire
action = StartAirdropTargeting,
defaultLabel = "WULA_AirdropProducts".Translate(),
defaultDesc = "WULA_AirdropProductsDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Airdrop"),
icon = ContentFinder<Texture2D>.Get("Wula/UI/Commands/WULA_AirdropProducts"),
disabledReason = "WULA_CannotAirdrop".Translate()
};
}

View File

@@ -3,14 +3,17 @@ using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
using System;
using System.Text;
namespace WulaFallenEmpire
{
public class Building_ResourceSubmitter : Building_Storage
public class Building_ResourceSubmitter : Building
{
private CompPowerTrader powerComp;
private CompRefuelable refuelableComp;
private CompFlickable flickableComp;
public CompPowerTrader powerComp;
public CompRefuelable refuelableComp;
public CompFlickable flickableComp;
public CompResourceSubmitter submitterComp;
public override void SpawnSetup(Map map, bool respawningAfterLoad)
{
@@ -18,11 +21,9 @@ namespace WulaFallenEmpire
powerComp = GetComp<CompPowerTrader>();
refuelableComp = GetComp<CompRefuelable>();
flickableComp = GetComp<CompFlickable>();
submitterComp = GetComp<CompResourceSubmitter>();
}
/// <summary>
/// 检查建筑是否可用(电力、燃料、开关等)
/// </summary>
public bool IsOperational
{
get
@@ -37,309 +38,31 @@ namespace WulaFallenEmpire
}
}
/// <summary>
/// 获取建筑的中心位置(用于生成 Skyfaller
/// </summary>
public IntVec3 CenterPosition
public string GetInoperativeReason()
{
get
{
// 对于偶数尺寸的建筑,返回中心附近的单元格
var center = Position + new IntVec3(def.Size.x / 2, 0, def.Size.z / 2);
// 确保在建筑范围内
return center;
}
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();
return "WULA_UnknownReason".Translate();
}
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()}");
if (stringBuilder.Length > 0) stringBuilder.AppendLine();
stringBuilder.Append($"{"WULA_Status".Translate()}: {"WULA_Inoperative".Translate()} - {GetInoperativeReason()}");
}
return stringBuilder.ToString();

View File

@@ -0,0 +1,21 @@
using RimWorld;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
{
public class Command_LoadToResourceSubmitter : Command
{
public CompResourceSubmitter submitterComp;
public override void ProcessInput(Event ev)
{
base.ProcessInput(ev);
if (submitterComp?.parent == null) return;
// 打开装载界面,类似运输舱的界面
Find.WindowStack.Add(new Dialog_LoadResourceSubmitter(submitterComp));
}
}
}

View File

@@ -0,0 +1,391 @@
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
using System;
using System.Text;
namespace WulaFallenEmpire
{
public class CompResourceSubmitter : ThingComp, IThingHolder
{
public ThingOwner innerContainer;
public List<TransferableOneWay> leftToLoad;
private bool massUsageDirty = true;
private float cachedMassUsage;
public CompProperties_ResourceSubmitter Props => (CompProperties_ResourceSubmitter)props;
public float MassUsage
{
get
{
if (massUsageDirty)
{
massUsageDirty = false;
cachedMassUsage = CollectionsMassCalculator.MassUsage(innerContainer, IgnorePawnsInventoryMode.IgnoreIfAssignedToUnload, includePawnsMass: false);
}
return cachedMassUsage;
}
}
public bool OverMassCapacity => MassUsage > Props.massCapacity;
public bool AnythingLeftToLoad => FirstThingLeftToLoad != null;
public Thing FirstThingLeftToLoad
{
get
{
if (leftToLoad == null) return null;
for (int i = 0; i < leftToLoad.Count; i++)
{
if (leftToLoad[i].CountToTransfer != 0 && leftToLoad[i].HasAnyThing)
return leftToLoad[i].AnyThing;
}
return null;
}
}
public CompResourceSubmitter()
{
innerContainer = new ThingOwner<Thing>(this);
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Deep.Look(ref innerContainer, "innerContainer", this);
Scribe_Collections.Look(ref leftToLoad, "leftToLoad", LookMode.Deep);
if (Scribe.mode == LoadSaveMode.Saving)
{
leftToLoad?.RemoveWhere(t => t == null);
}
}
public ThingOwner GetDirectlyHeldThings()
{
return innerContainer;
}
public void GetChildHolders(List<IThingHolder> outChildren)
{
ThingOwnerUtility.AppendThingHoldersFromThings(outChildren, GetDirectlyHeldThings());
}
// 在 CompResourceSubmitter 类中添加或更新以下方法:
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
massUsageDirty = true;
}
public override void PostDeSpawn(Map map, DestroyMode mode = DestroyMode.Vanish)
{
base.PostDeSpawn(map, mode);
if (mode != DestroyMode.WillReplace)
{
innerContainer.TryDropAll(parent.Position, map, ThingPlaceMode.Near);
}
}
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (Gizmo g in base.CompGetGizmosExtra())
{
yield return g;
}
// 装载命令
Command_LoadToResourceSubmitter loadCommand = new Command_LoadToResourceSubmitter();
loadCommand.defaultLabel = "WULA_LoadResourceSubmitter".Translate();
loadCommand.defaultDesc = "WULA_LoadResourceSubmitterDesc".Translate();
loadCommand.icon = ContentFinder<Texture2D>.Get("UI/Commands/LoadTransporter");
loadCommand.submitterComp = this;
// 禁用检查
if (!parent.Spawned)
{
loadCommand.Disable("WULA_NotSpawned".Translate());
}
else if (!IsOperational())
{
loadCommand.Disable(GetInoperativeReason());
}
yield return loadCommand;
// 取消装载/卸载命令
if (innerContainer.Any || AnythingLeftToLoad)
{
Command_Action cancelCommand = new Command_Action();
cancelCommand.defaultLabel = innerContainer.Any ? "WULA_Unload".Translate() : "WULA_CancelLoad".Translate();
cancelCommand.defaultDesc = innerContainer.Any ? "WULA_UnloadDesc".Translate() : "WULA_CancelLoadDesc".Translate();
cancelCommand.icon = ContentFinder<Texture2D>.Get("UI/Designators/Cancel");
cancelCommand.action = CancelLoad;
yield return cancelCommand;
}
// 发射命令
Command_Action launchCommand = new Command_Action();
launchCommand.defaultLabel = "WULA_LaunchSubmitter".Translate();
launchCommand.defaultDesc = "WULA_LaunchSubmitterDesc".Translate();
launchCommand.icon = ContentFinder<Texture2D>.Get("UI/Commands/Launch");
launchCommand.action = TryLaunch;
// 发射条件检查
if (!parent.Spawned)
{
launchCommand.Disable("WULA_NotSpawned".Translate());
}
else if (!IsOperational())
{
launchCommand.Disable(GetInoperativeReason());
}
else if (!innerContainer.Any)
{
launchCommand.Disable("WULA_NoItemsToSubmit".Translate());
}
else if (OverMassCapacity)
{
launchCommand.Disable("WULA_OverMassCapacity".Translate(MassUsage.ToString("F1"), Props.massCapacity.ToString("F1")));
}
yield return launchCommand;
}
public override string CompInspectStringExtra()
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("WULA_SubmitterContents".Translate() + ": " + innerContainer.ContentsString.CapitalizeFirst());
string massString = "WULA_Mass".Translate() + ": " + MassUsage.ToString("F1") + " / " + Props.massCapacity.ToString("F1") + " kg";
stringBuilder.AppendLine().Append(OverMassCapacity ? massString.Colorize(ColorLibrary.RedReadable) : massString);
if (!IsOperational())
{
stringBuilder.AppendLine().Append("WULA_Status".Translate() + ": " + "WULA_Inoperative".Translate().Colorize(ColorLibrary.RedReadable));
}
return stringBuilder.ToString();
}
public void AddToTheToLoadList(TransferableOneWay t, int count)
{
if (!t.HasAnyThing || count <= 0) return;
if (leftToLoad == null)
{
leftToLoad = new List<TransferableOneWay>();
}
TransferableOneWay existing = TransferableUtility.TransferableMatching(t.AnyThing, leftToLoad, TransferAsOneMode.PodsOrCaravanPacking);
if (existing != null)
{
for (int i = 0; i < t.things.Count; i++)
{
if (!existing.things.Contains(t.things[i]))
{
existing.things.Add(t.things[i]);
}
}
if (existing.CanAdjustBy(count).Accepted)
{
existing.AdjustBy(count);
}
}
else
{
TransferableOneWay newTransferable = new TransferableOneWay();
leftToLoad.Add(newTransferable);
newTransferable.things.AddRange(t.things);
newTransferable.AdjustTo(count);
}
}
public void Notify_ThingAdded(Thing t)
{
SubtractFromToLoadList(t, t.stackCount);
massUsageDirty = true;
}
public void Notify_ThingRemoved(Thing t)
{
massUsageDirty = true;
}
public void Notify_ThingAddedAndMergedWith(Thing t, int mergedCount)
{
SubtractFromToLoadList(t, mergedCount);
massUsageDirty = true;
}
private int SubtractFromToLoadList(Thing t, int count)
{
if (leftToLoad == null) return 0;
TransferableOneWay transferable = TransferableUtility.TransferableMatchingDesperate(t, leftToLoad, TransferAsOneMode.PodsOrCaravanPacking);
if (transferable == null || transferable.CountToTransfer <= 0) return 0;
int num = Mathf.Min(count, transferable.CountToTransfer);
transferable.AdjustBy(-num);
if (transferable.CountToTransfer <= 0)
{
leftToLoad.Remove(transferable);
}
return num;
}
private void CancelLoad()
{
if (leftToLoad != null)
{
leftToLoad.Clear();
}
innerContainer.TryDropAll(parent.Position, parent.Map, ThingPlaceMode.Near);
massUsageDirty = true;
}
private void TryLaunch()
{
if (!IsOperational())
{
Messages.Message(GetInoperativeReason(), MessageTypeDefOf.RejectInput);
return;
}
if (!innerContainer.Any)
{
Messages.Message("WULA_NoItemsToSubmit".Translate(), MessageTypeDefOf.RejectInput);
return;
}
if (OverMassCapacity)
{
Messages.Message("WULA_OverMassCapacity".Translate(MassUsage.ToString("F1"), Props.massCapacity.ToString("F1")), MessageTypeDefOf.RejectInput);
return;
}
if (SubmitContentsToStorage())
{
CreateLaunchEffect();
parent.Destroy();
}
}
private bool SubmitContentsToStorage()
{
try
{
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
if (globalStorage == null)
{
Log.Error("GlobalStorageWorldComponent not found");
return false;
}
int submittedCount = 0;
List<Thing> processedItems = new List<Thing>();
// 复制列表以避免修改时迭代
List<Thing> itemsToProcess = innerContainer.ToList();
foreach (Thing item in itemsToProcess)
{
if (item == null || item.Destroyed) continue;
if (IsEquipment(item.def))
{
globalStorage.AddToOutputStorage(item.def, item.stackCount);
}
else
{
globalStorage.AddToInputStorage(item.def, item.stackCount);
}
processedItems.Add(item);
submittedCount += item.stackCount;
}
// 从容器中移除已提交的物品
foreach (Thing item in processedItems)
{
innerContainer.Remove(item);
}
Messages.Message("WULA_ItemsSubmitted".Translate(submittedCount), MessageTypeDefOf.PositiveEvent);
Log.Message($"Successfully submitted {submittedCount} items to global storage");
return submittedCount > 0;
}
catch (Exception ex)
{
Log.Error($"Error submitting items to storage: {ex}");
Messages.Message("WULA_SubmissionFailed".Translate(), MessageTypeDefOf.NegativeEvent);
return false;
}
}
private bool IsEquipment(ThingDef thingDef)
{
return thingDef.IsApparel || thingDef.IsWeapon || thingDef.category == ThingCategory.Building;
}
private void CreateLaunchEffect()
{
try
{
// 使用自定义的 Skyfaller 定义
ThingDef skyfallerDef = DefDatabase<ThingDef>.GetNamedSilentFail("ResourceSubmitterSkyfaller");
if (skyfallerDef == null)
{
// 备用:使用运输舱效果
skyfallerDef = DefDatabase<ThingDef>.GetNamedSilentFail("DropPodIncoming");
}
if (skyfallerDef != null)
{
Skyfaller skyfaller = (Skyfaller)ThingMaker.MakeThing(skyfallerDef);
GenSpawn.Spawn(skyfaller, parent.Position, parent.Map);
}
// 视觉效果
for (int i = 0; i < 3; i++)
{
FleckMaker.ThrowLightningGlow(parent.DrawPos, parent.Map, 2f);
}
FleckMaker.ThrowSmoke(parent.DrawPos, parent.Map, 3f);
}
catch (Exception ex)
{
Log.Error($"Error creating launch effect: {ex}");
}
}
private bool IsOperational()
{
var building = parent as Building_ResourceSubmitter;
return building?.IsOperational ?? false;
}
private string GetInoperativeReason()
{
var building = parent as Building_ResourceSubmitter;
return building?.GetInoperativeReason() ?? "WULA_UnknownReason".Translate();
}
}
public class CompProperties_ResourceSubmitter : CompProperties
{
public float massCapacity = 150f;
public CompProperties_ResourceSubmitter()
{
compClass = typeof(CompResourceSubmitter);
}
}
}

View File

@@ -0,0 +1,354 @@
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class Dialog_LoadResourceSubmitter : Window
{
private CompResourceSubmitter submitterComp;
private Vector2 scrollPosition;
private float scrollViewHeight;
private List<TransferableOneWay> transferables;
public override Vector2 InitialSize => new Vector2(800f, 600f);
public Dialog_LoadResourceSubmitter(CompResourceSubmitter submitterComp)
{
this.submitterComp = submitterComp;
forcePause = true;
doCloseX = true;
doCloseButton = true;
absorbInputAroundWindow = true;
transferables = new List<TransferableOneWay>();
RefreshTransferables();
}
public override void DoWindowContents(Rect inRect)
{
Rect titleRect = new Rect(0f, 0f, inRect.width, 35f);
Text.Font = GameFont.Medium;
Widgets.Label(titleRect, "WULA_LoadResourceSubmitter".Translate());
Text.Font = GameFont.Small;
// 简化的物品列表
Rect listRect = new Rect(0f, 40f, inRect.width, inRect.height - 100f);
DoSimpleTransferableList(listRect);
// 按钮区域
Rect buttonRect = new Rect(0f, inRect.height - 55f, inRect.width, 30f);
DoButtons(buttonRect);
// 状态信息
Rect statusRect = new Rect(0f, inRect.height - 25f, inRect.width, 25f);
DoStatusInfo(statusRect);
}
private void DoSimpleTransferableList(Rect rect)
{
Widgets.DrawMenuSection(rect);
Rect outRect = rect.ContractedBy(10f);
Rect viewRect = new Rect(0f, 0f, outRect.width - 16f, scrollViewHeight);
Widgets.BeginScrollView(outRect, ref scrollPosition, viewRect);
float curY = 0f;
// 列标题
Rect headerRect = new Rect(0f, curY, viewRect.width, 25f);
DoColumnHeaders(headerRect);
curY += 30f;
if (transferables.Count == 0)
{
Rect emptyRect = new Rect(0f, curY, viewRect.width, 30f);
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(emptyRect, "WULA_NoItemsAvailable".Translate());
Text.Anchor = TextAnchor.UpperLeft;
curY += 35f;
}
else
{
foreach (var transferable in transferables)
{
if (transferable.things.Count == 0) continue;
Thing sampleThing = transferable.things[0];
Rect rowRect = new Rect(0f, curY, viewRect.width, 30f);
if (DoTransferableRow(rowRect, transferable, sampleThing))
{
TooltipHandler.TipRegion(rowRect, GetThingTooltip(sampleThing, transferable.CountToTransfer));
}
curY += 35f;
}
}
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 availableRect = new Rect(rect.x + columnWidth * 2f, rect.y, columnWidth, rect.height);
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(availableRect, "WULA_Available".Translate());
// 装载数量列
Rect loadRect = new Rect(rect.x + columnWidth * 3f, rect.y, columnWidth, rect.height);
Widgets.Label(loadRect, "WULA_ToLoad".Translate());
Text.Anchor = TextAnchor.UpperLeft;
// 标题下划线
Widgets.DrawLineHorizontal(rect.x, rect.yMax - 2f, rect.width);
}
private bool DoTransferableRow(Rect rect, TransferableOneWay transferable, Thing sampleThing)
{
Widgets.DrawHighlightIfMouseover(rect);
float columnWidth = rect.width / 4f;
// 图标
Rect iconRect = new Rect(rect.x + 2f, rect.y + 2f, 26f, 26f);
Widgets.ThingIcon(iconRect, sampleThing);
// 名称
Rect nameRect = new Rect(rect.x + 32f, rect.y, columnWidth * 2f - 32f, rect.height);
Text.Anchor = TextAnchor.MiddleLeft;
string label = sampleThing.LabelCap;
if (label.Length > 25)
{
label = label.Substring(0, 25) + "...";
}
Widgets.Label(nameRect, label);
// 可用数量
int availableCount = transferable.things.Sum(t => t.stackCount);
Rect availableRect = new Rect(rect.x + columnWidth * 2f, rect.y, columnWidth, rect.height);
Text.Anchor = TextAnchor.MiddleCenter;
Widgets.Label(availableRect, availableCount.ToString());
// 装载数量调整
Rect adjustRect = new Rect(rect.x + columnWidth * 3f, rect.y, columnWidth, rect.height);
DoCountAdjust(adjustRect, transferable, availableCount);
Text.Anchor = TextAnchor.UpperLeft;
return Mouse.IsOver(rect);
}
private void DoCountAdjust(Rect rect, TransferableOneWay transferable, int availableCount)
{
int currentCount = transferable.CountToTransfer;
Rect labelRect = new Rect(rect.x, rect.y, 40f, rect.height);
Rect minusRect = new Rect(rect.x + 45f, rect.y, 25f, rect.height);
Rect plusRect = new Rect(rect.x + 75f, rect.y, 25f, rect.height);
Rect maxRect = new Rect(rect.x + 105f, rect.y, 35f, rect.height);
Text.Anchor = TextAnchor.MiddleCenter;
// 当前数量
Widgets.Label(labelRect, currentCount.ToString());
// 减少按钮
if (Widgets.ButtonText(minusRect, "-") && currentCount > 0)
{
transferable.AdjustBy(-1);
}
// 增加按钮
if (Widgets.ButtonText(plusRect, "+") && currentCount < availableCount)
{
transferable.AdjustBy(1);
}
// 最大按钮
if (Widgets.ButtonText(maxRect, "WULA_Max".Translate()) && availableCount > 0)
{
Find.WindowStack.Add(new Dialog_Slider(
"WULA_SetLoadCount".Translate(transferable.AnyThing.LabelCap),
0, availableCount,
value => transferable.AdjustTo(value),
currentCount
));
}
Text.Anchor = TextAnchor.UpperLeft;
}
private void DoButtons(Rect rect)
{
float buttonWidth = rect.width / 2f - 5f;
// 加载所有按钮
Rect loadAllRect = new Rect(rect.x, rect.y, buttonWidth, rect.height);
if (Widgets.ButtonText(loadAllRect, "WULA_LoadAll".Translate()))
{
foreach (var transferable in transferables)
{
transferable.AdjustTo(transferable.things.Sum(t => t.stackCount));
}
}
// 清除所有按钮
Rect clearAllRect = new Rect(rect.x + buttonWidth + 10f, rect.y, buttonWidth, rect.height);
if (Widgets.ButtonText(clearAllRect, "WULA_ClearAll".Translate()))
{
foreach (var transferable in transferables)
{
transferable.AdjustTo(0);
}
}
}
private void DoStatusInfo(Rect rect)
{
Widgets.DrawMenuSection(rect);
Rect innerRect = rect.ContractedBy(5f);
Text.Anchor = TextAnchor.MiddleLeft;
// 计算总质量
float totalMass = 0f;
foreach (var transferable in transferables)
{
if (transferable.CountToTransfer > 0)
{
float thingMass = transferable.AnyThing.GetStatValue(StatDefOf.Mass);
totalMass += thingMass * transferable.CountToTransfer;
}
}
// 质量信息
string massText = "WULA_Mass".Translate() + ": " + totalMass.ToString("F1") + " / " + submitterComp.Props.massCapacity.ToString("F1") + " kg";
if (totalMass > submitterComp.Props.massCapacity)
{
massText = massText.Colorize(ColorLibrary.RedReadable);
}
Widgets.Label(innerRect, massText);
Text.Anchor = TextAnchor.UpperLeft;
}
private string GetThingTooltip(Thing thing, int count)
{
float mass = thing.GetStatValue(StatDefOf.Mass);
float value = thing.MarketValue * count;
return $"{thing.LabelCap}\n{"WULA_Mass".Translate()}: {mass:F2} kg\n{"WULA_Value".Translate()}: {value}\n{"WULA_Description".Translate()}: {thing.def.description}";
}
private void RefreshTransferables()
{
transferables.Clear();
// 获取地图上所有可搬运的物品
foreach (Thing thing in submitterComp.parent.Map.listerThings.ThingsInGroup(ThingRequestGroup.HaulableAlways))
{
if ((thing.IsInValidStorage() || thing.Spawned) && !thing.Position.Fogged(thing.Map))
{
AddToTransferables(thing);
}
}
// 按物品类型排序
transferables.SortBy(t => t.AnyThing.def.label);
}
private void AddToTransferables(Thing thing)
{
if (thing.def.category == ThingCategory.Item)
{
TransferableOneWay transferable = TransferableUtility.TransferableMatching(thing, transferables, TransferAsOneMode.PodsOrCaravanPacking);
if (transferable == null)
{
transferable = new TransferableOneWay();
transferables.Add(transferable);
transferable.things.Add(thing);
}
else
{
transferable.things.Add(thing);
}
}
}
public override void PostClose()
{
base.PostClose();
// 应用装载设置到提交器
if (submitterComp != null)
{
submitterComp.leftToLoad?.Clear();
foreach (TransferableOneWay transferable in transferables)
{
if (transferable.CountToTransfer > 0)
{
submitterComp.AddToTheToLoadList(transferable, transferable.CountToTransfer);
}
}
// 开始装载工作
StartLoadingJobs();
}
}
private void StartLoadingJobs()
{
if (submitterComp?.parent?.Map == null) return;
foreach (TransferableOneWay transferable in transferables)
{
if (transferable.CountToTransfer > 0)
{
foreach (Thing thing in transferable.things)
{
if (transferable.CountToTransfer <= 0) break;
// 创建搬运到提交器的工作
Job job = JobMaker.MakeJob(JobDefOf.HaulToContainer, thing, submitterComp.parent);
job.count = Mathf.Min(thing.stackCount, transferable.CountToTransfer);
job.haulMode = HaulMode.ToContainer;
// 寻找殖民者执行工作
Pawn pawn = FindBestPawnForJob(job);
if (pawn != null)
{
pawn.jobs.TryTakeOrderedJob(job);
transferable.AdjustBy(-job.count);
}
}
}
}
Messages.Message("WULA_LoadingJobsCreated".Translate(), MessageTypeDefOf.PositiveEvent);
}
private Pawn FindBestPawnForJob(Job job)
{
return submitterComp.parent.Map.mapPawns.FreeColonistsSpawned
.Where(p => p.workSettings.WorkIsActive(WorkTypeDefOf.Hauling))
.FirstOrDefault(p => p.CanReserveAndReach(job.targetA, PathEndMode.ClosestTouch, Danger.Some));
}
}
}

View File

@@ -2,6 +2,7 @@
using RimWorld;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using Verse;
namespace WulaFallenEmpire
@@ -12,7 +13,6 @@ namespace WulaFallenEmpire
public int targetCount = 1;
public int currentCount = 0;
public bool paused = true; // 初始状态为暂停
public float progress = 0f;
// 生产状态
public ProductionState state = ProductionState.Waiting;
@@ -24,18 +24,94 @@ namespace WulaFallenEmpire
Completed // 完成
}
public string Label => recipe.LabelCap;
public string Description => $"{currentCount}/{targetCount} {recipe.products[0].thingDef.label}"; private float _progress = 0f;
public float progress
{
get => _progress;
set
{
// 确保进度在有效范围内
_progress = Mathf.Clamp01(value);
// 如果检测到异常值,记录警告
if (value < 0f || value > 1f)
{
Log.Warning($"Progress clamped from {value} to {_progress} for {recipe?.defName ?? "unknown"}");
}
}
}
public void ExposeData()
{
Scribe_Defs.Look(ref recipe, "recipe");
Scribe_Values.Look(ref targetCount, "targetCount", 1);
Scribe_Values.Look(ref currentCount, "currentCount", 0);
Scribe_Values.Look(ref paused, "paused", true);
Scribe_Values.Look(ref progress, "progress", 0f);
Scribe_Values.Look(ref _progress, "progress", 0f); // 序列化私有字段
Scribe_Values.Look(ref state, "state", ProductionState.Waiting);
// 修复:加载后验证数据
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
// 使用属性设置器来钳制值
progress = _progress;
// 确保状态正确
UpdateState();
}
}
public string Label => recipe.LabelCap;
public string Description => $"{currentCount}/{targetCount} {recipe.products[0].thingDef.label}";
// 修复:改进状态更新逻辑
public void UpdateState()
{
if (state == ProductionState.Completed)
return;
if (currentCount >= targetCount)
{
state = ProductionState.Completed;
progress = 0f;
return;
}
if (HasEnoughResources())
{
if (state == ProductionState.Waiting && !paused)
{
state = ProductionState.Producing;
progress = 0f; // 开始生产时重置进度
}
}
else
{
if (state == ProductionState.Producing)
{
state = ProductionState.Waiting;
progress = 0f; // 资源不足时重置进度
}
}
}
// 修复:改进生产完成逻辑
public void Produce()
{
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
if (globalStorage == null)
return;
foreach (var product in recipe.products)
{
globalStorage.AddToOutputStorage(product.thingDef, product.count);
}
currentCount++;
progress = 0f; // 生产完成后重置进度
if (currentCount >= targetCount)
{
state = ProductionState.Completed;
}
}
// 检查是否有足够资源 - 修复逻辑
public bool HasEnoughResources()
@@ -103,68 +179,51 @@ namespace WulaFallenEmpire
return true;
}
// 生产一个产品
public void Produce()
// 修复:添加获取正确工作量的方法
public float GetWorkAmount()
{
var globalStorage = Find.World.GetComponent<GlobalStorageWorldComponent>();
if (globalStorage == null) return;
if (recipe == null)
return 1000f;
foreach (var product in recipe.products)
{
globalStorage.AddToOutputStorage(product.thingDef, product.count);
}
currentCount++;
progress = 0f;
if (currentCount >= targetCount)
{
state = ProductionState.Completed;
}
}
// 如果配方有明确的工作量且大于0使用配方的工作量
if (recipe.workAmount > 0)
return recipe.workAmount;
// 新增方法:检查并更新状态
public void UpdateState()
{
if (state == ProductionState.Completed) return;
if (HasEnoughResources())
// 否则使用第一个产品的WorkToMake属性
if (recipe.products != null && recipe.products.Count > 0)
{
if (state == ProductionState.Waiting && !paused)
ThingDef productDef = recipe.products[0].thingDef;
if (productDef != null)
{
state = ProductionState.Producing;
}
}
else
{
if (state == ProductionState.Producing)
{
state = ProductionState.Waiting;
progress = 0f; // 重置进度
// 获取产品的WorkToMake统计值
float workToMake = productDef.GetStatValueAbstract(StatDefOf.WorkToMake);
if (workToMake > 0)
return workToMake;
// 如果WorkToMake也是0或无效使用产品的市场价值作为估算
float marketValue = productDef.GetStatValueAbstract(StatDefOf.MarketValue);
if (marketValue > 0)
return marketValue * 10f; // 基于市场价值的估算
}
}
return 1000f; // 默认工作量
}
// 获取配方材料信息的字符串
// 修复:在信息显示中使用正确的工作量
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(" - ");
@@ -174,9 +233,7 @@ namespace WulaFallenEmpire
{
sb.Append(" / ");
}
sb.Append($"{requiredCount} {thingDef.label}");
// 添加可用数量信息
if (availableCount < requiredCount)
{
@@ -187,10 +244,8 @@ namespace WulaFallenEmpire
sb.Append($" ({availableCount}/{requiredCount})");
}
}
sb.AppendLine();
}
// 添加产品信息
sb.AppendLine();
sb.AppendLine("WULA_Products".Translate() + ":");
@@ -198,46 +253,37 @@ namespace WulaFallenEmpire
{
sb.AppendLine($" - {product.count} {product.thingDef.label}");
}
// 添加工作量信息
// 修复:使用正确的工作量信息
sb.AppendLine();
sb.AppendLine("WULA_WorkAmount".Translate() + ": " + recipe.workAmount.ToStringWorkAmount());
sb.AppendLine("WULA_WorkAmount".Translate() + ": " + GetWorkAmount().ToStringWorkAmount());
return sb.ToString();
}
// 获取简化的材料信息用于Tooltip
// 修复在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>");
@@ -247,7 +293,6 @@ namespace WulaFallenEmpire
sb.AppendLine($" <color=red>{ingredientSB}</color>");
}
}
// 产品
sb.AppendLine();
sb.AppendLine("WULA_Products".Translate() + ":");
@@ -255,7 +300,9 @@ namespace WulaFallenEmpire
{
sb.AppendLine($" {product.count} {product.thingDef.label}");
}
// 修复:使用正确的工作量
sb.AppendLine();
sb.AppendLine("WULA_WorkAmount".Translate() + ": " + GetWorkAmount().ToStringWorkAmount());
return sb.ToString();
}
}

View File

@@ -1,7 +1,7 @@
// GlobalProductionOrderStack.cs (调整为每秒1工作量)
using RimWorld;
using System.Collections.Generic;
using Verse;
using UnityEngine;
namespace WulaFallenEmpire
{
@@ -10,10 +10,10 @@ 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 WorkPerSecond = 60f; // 每秒60工作量标准RimWorld速度
private const float TicksPerSecond = 60f;
private const float WorkPerTick = WorkPerSecond / TicksPerSecond;
private const float WorkPerTick = WorkPerSecond / TicksPerSecond; // 每tick 1工作量
public GlobalProductionOrderStack(Building_GlobalWorkTable table)
{
@@ -24,6 +24,12 @@ namespace WulaFallenEmpire
{
Scribe_References.Look(ref table, "table");
Scribe_Collections.Look(ref orders, "orders", LookMode.Deep);
// 修复:加载后验证和修复数据
if (Scribe.mode == LoadSaveMode.PostLoadInit)
{
FixAllOrders();
}
}
public void AddOrder(GlobalProductionOrder order)
@@ -50,8 +56,11 @@ namespace WulaFallenEmpire
public void ProcessOrders()
{
foreach (var order in orders)
// 修复:使用倒序遍历避免修改集合问题
for (int i = orders.Count - 1; i >= 0; i--)
{
var order = orders[i];
// 首先更新状态
order.UpdateState();
@@ -61,49 +70,176 @@ namespace WulaFallenEmpire
// 生产中
if (order.state == GlobalProductionOrder.ProductionState.Producing)
{
// 计算每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($"[WARNING] Failed to consume resources for {order.recipe.defName}");
}
}
ProcessProducingOrder(order, i);
}
else if (order.state == GlobalProductionOrder.ProductionState.Waiting && !order.paused)
{
// 调试:检查为什么订单在等待状态
if (Find.TickManager.TicksGame % 1200 == 0) // 每20秒检查一次
{
Log.Message($"[DEBUG] Order {order.recipe.defName} is waiting. " +
$"HasEnoughResources: {order.HasEnoughResources()}, paused: {order.paused}");
}
ProcessWaitingOrder(order);
}
}
}
private void ProcessProducingOrder(GlobalProductionOrder order, int index)
{
// 修复:使用正确的方法获取工作量
float workAmount = GetWorkAmountForOrder(order);
// 防止除零错误
if (workAmount <= 0)
{
Log.Error($"Invalid workAmount ({workAmount}) for recipe {order.recipe.defName}");
order.state = GlobalProductionOrder.ProductionState.Waiting;
order.progress = 0f;
return;
}
// 修复:正确计算进度增量
float progressIncrement = WorkPerTick / workAmount;
// 修复:确保进度不会变成负数
float newProgress = Mathf.Max(0f, order.progress + progressIncrement);
order.progress = newProgress;
// 调试信息
if (Find.TickManager.TicksGame % 300 == 0) // 每5秒输出一次
{
Log.Message($"[DEBUG] Order {order.recipe.defName}: " +
$"progress={order.progress:P2}, " +
$"workAmount={workAmount}, " +
$"increment={progressIncrement:E4}, " +
$"state={order.state}");
}
// 修复:使用精确比较完成条件
if (order.progress >= 1.0f)
{
CompleteProduction(order, index);
}
}
// 修复:新增方法 - 正确获取订单的工作量
private float GetWorkAmountForOrder(GlobalProductionOrder order)
{
if (order?.recipe == null)
return 1000f; // 默认值
// 如果配方有明确的工作量且大于0使用配方的工作量
if (order.recipe.workAmount > 0)
return order.recipe.workAmount;
// 否则使用第一个产品的WorkToMake属性
if (order.recipe.products != null && order.recipe.products.Count > 0)
{
ThingDef productDef = order.recipe.products[0].thingDef;
if (productDef != null)
{
// 获取产品的WorkToMake统计值
float workToMake = productDef.GetStatValueAbstract(StatDefOf.WorkToMake);
if (workToMake > 0)
return workToMake;
// 如果WorkToMake也是0或无效使用产品的市场价值作为估算
float marketValue = productDef.GetStatValueAbstract(StatDefOf.MarketValue);
if (marketValue > 0)
return marketValue * 10f; // 基于市场价值的估算
}
}
// 最后的回退方案
Log.Warning($"Could not determine work amount for recipe {order.recipe.defName}, using default value");
return 1000f; // 默认工作量
}
private void ProcessWaitingOrder(GlobalProductionOrder order)
{
// 检查是否应该开始生产
if (order.HasEnoughResources())
{
order.state = GlobalProductionOrder.ProductionState.Producing;
order.progress = 0f;
if (Find.TickManager.TicksGame % 600 == 0) // 每10秒记录一次
{
Log.Message($"[INFO] Order {order.recipe.defName} started producing");
}
}
else if (Find.TickManager.TicksGame % 1200 == 0) // 每20秒检查一次
{
Log.Message($"[DEBUG] Order {order.recipe.defName} is waiting. " +
$"HasEnoughResources: {order.HasEnoughResources()}");
}
}
private void CompleteProduction(GlobalProductionOrder order, int index)
{
// 修复:生产完成,消耗资源
if (order.ConsumeResources())
{
order.Produce();
order.UpdateState();
Log.Message($"[SUCCESS] Produced {order.recipe.products[0].thingDef.defName}, " +
$"count: {order.currentCount}/{order.targetCount}");
// 重置进度
order.progress = 0f;
// 如果订单完成,移除它
if (order.state == GlobalProductionOrder.ProductionState.Completed)
{
orders.RemoveAt(index);
Log.Message($"[COMPLETE] Order {order.recipe.defName} completed and removed");
}
}
else
{
// 修复:资源不足,回到等待状态
order.state = GlobalProductionOrder.ProductionState.Waiting;
order.progress = 0f;
Log.Message($"[WARNING] Failed to consume resources for {order.recipe.defName}, resetting");
}
}
// 修复:全面数据修复方法
private void FixAllOrders()
{
for (int i = orders.Count - 1; i >= 0; i--)
{
var order = orders[i];
// 修复进度值
if (float.IsNaN(order.progress) || float.IsInfinity(order.progress))
{
order.progress = 0f;
Log.Warning($"Fixed invalid progress for {order.recipe?.defName ?? "unknown"}");
}
else if (order.progress < 0f)
{
order.progress = 0f;
Log.Warning($"Fixed negative progress for {order.recipe?.defName ?? "unknown"}");
}
else if (order.progress > 1f)
{
order.progress = 1f;
Log.Warning($"Fixed excessive progress for {order.recipe?.defName ?? "unknown"}");
}
// 修复状态
if (order.recipe == null)
{
Log.Warning($"Removing order with null recipe");
orders.RemoveAt(i);
continue;
}
// 强制更新状态
order.UpdateState();
}
}
public void FixNegativeProgress()
{
FixAllOrders();
}
}
}

View File

@@ -310,7 +310,7 @@ namespace WulaFallenEmpire
// 如果暂停,在状态前添加暂停标识
if (order.paused && order.state != GlobalProductionOrder.ProductionState.Completed)
{
statusText = $"[Paused] {statusText}";
statusText = $"[||] {statusText}";
}
Widgets.Label(statusRect, statusText);

View File

@@ -1,409 +0,0 @@
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);
}
}
}
}

View File

@@ -150,12 +150,6 @@ namespace WulaFallenEmpire
// 立即检查状态变化
CheckStatusChanges();
if (pawn.IsColonistPlayerControlled && reduction > 0.01f)
{
Messages.Message("WULA_MaintenanceReducedDueToDamage".Translate(pawn.LabelShort, reduction.ToStringPercent()),
pawn, MessageTypeDefOf.NegativeEvent);
}
}
private void OnMaintenancePerformed(float amount)

View File

@@ -71,6 +71,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="BuildingComp\WULA_InitialFaction\CompProperties_InitialFaction.cs" />
<Compile Include="BuildingComp\WULA_MechanoidRecycler\Building_MechanoidRecycler.cs" />
<Compile Include="BuildingComp\WULA_MechanoidRecycler\CompProperties_MechanoidRecycler.cs" />
<Compile Include="BuildingComp\WULA_MechanoidRecycler\JobDriver_RecycleMechanoid.cs" />
@@ -108,6 +109,9 @@
<Compile Include="Flyover\WULA_SpawnFlyOver\CompProperties_AbilitySpawnFlyOver.cs" />
<Compile Include="GlobalWorkTable\Building_GlobalWorkTable.cs" />
<Compile Include="GlobalWorkTable\Building_ResourceSubmitter.cs" />
<Compile Include="GlobalWorkTable\Command_LoadToResourceSubmitter.cs" />
<Compile Include="GlobalWorkTable\CompResourceSubmitter.cs" />
<Compile Include="GlobalWorkTable\Dialog_LoadResourceSubmitter.cs" />
<Compile Include="GlobalWorkTable\GlobalProductionOrder.cs" />
<Compile Include="GlobalWorkTable\GlobalProductionOrderStack.cs" />
<Compile Include="GlobalWorkTable\GlobalStorageWorldComponent.cs" />
@@ -206,7 +210,6 @@
<Compile Include="HediffComp\WULA_HediffSpawner\HediffCompProperties_Spawner.cs" />
<Compile Include="HediffComp\WULA_HediffSpawner\HediffComp_Spawner.cs" />
<Compile Include="HediffComp\WULA_HediffSpawner\Tools.cs" />
<Compile Include="SectionLayer_WulaHull.cs" />
<Compile Include="HediffComp\HediffComp_TimedExplosion.cs" />
<Compile Include="Projectiles\Projectile_ConfigurableHellsphereCannon.cs" />
<Compile Include="Verb\Verb_ShootBeamSplitAndChain.cs" />