This commit is contained in:
2026-02-25 17:30:59 +08:00
parent 0509f26c3c
commit fff40b0edb
70 changed files with 3951 additions and 1219 deletions

View File

@@ -202,6 +202,32 @@
</li>
</stages>
</HediffDef>
<HediffDef>
<defName>WULA_MechShutdown</defName>
<hediffClass>HediffWithComps</hediffClass>
<label></label>
<description>This mech has stopped due to insufficient fuel, need to assign a colonist to refuel it to restore its operational capability.</description>
<descriptionShort>This mech has stopped due to insufficient fuel.</descriptionShort>
<!-- <initialSeverity>0.5</initialSeverity> -->
<minSeverity>0.01</minSeverity>
<maxSeverity>1</maxSeverity>
<alwaysShowSeverity>true</alwaysShowSeverity>
<isBad>true</isBad>
<duplicationAllowed>true</duplicationAllowed>
<keepOnBodyPartRestoration>true</keepOnBodyPartRestoration> <!-- true -->
<!-- <defaultInstallPart>Brain</defaultInstallPart> -->
<stages>
<li>
<capMods>
<li>
<capacity>Moving</capacity>
<setMax>0.1</setMax>
</li>
</capMods>
</li>
</stages>
</HediffDef>
<HediffDef>
<defName>WULA_MechCarrierSwitchHediff</defName>

View File

@@ -86,7 +86,7 @@
<makeTargetPrisoner>false</makeTargetPrisoner> -->
</JobDef>
<WorkGiverDef>
<defName>WULA_Refuel</defName>
<defName>WULA_RefuelMech</defName>
<label>refuel Mechs</label>
<giverClass>WulaFallenEmpire.WorkGiver_RefuelMech</giverClass>
<workType>Hauling</workType>

View File

@@ -179,6 +179,7 @@
<defaultFactionType>PlayerColony</defaultFactionType>
<canMeleeAttack>false</canMeleeAttack>
<isGoodBreacher>true</isGoodBreacher>
<collidesWithPawns>false</collidesWithPawns>
<flyingAnimationFramePathPrefix>Wula/Things/Wula_Mech_Mobile_Factory/Flying/Wula_Mech_Mobile_Factory_Flying_</flyingAnimationFramePathPrefix>
<flyingAnimationDrawSize>1</flyingAnimationDrawSize>

View File

@@ -133,4 +133,17 @@
<showOnDrones>true</showOnDrones>
<workerClass>WulaFallenEmpire.StatWorker_Maintenance</workerClass>
</StatDef>
<!-- 机甲装甲值 -->
<StatDef>
<defName>WULA_MechArmor</defName>
<label>复合装甲层</label>
<description>乌拉帝国在大型战争机械和少部分护甲上使用的特殊防御装甲,当 护甲穿透乘数 * 攻击 的最终值小于此值时,该攻击将被完全抵消而不造成任何伤害。</description>
<category>Apparel</category>
<toStringStyle>FloatTwo</toStringStyle>
<defaultBaseValue>0</defaultBaseValue>
<hideAtValue>0</hideAtValue>
<minValue>0</minValue>
<displayPriorityInCategory>5000</displayPriorityInCategory>
</StatDef>
</Defs>

View File

@@ -477,9 +477,17 @@
<Steel>200</Steel>
<ComponentIndustrial>12</ComponentIndustrial>
</costList>
<weaponTags>
<weaponTags Inherit="False">
<li>Wula_AI_Heavy_Panzer_Weapon</li>
</weaponTags>
<thingCategories Inherit="False"/>
<comps>
<li Class="WulaFallenEmpire.CompProperties_MechOnlyWeapon">
<allowedMechRaces>
<li>Wula_AI_Heavy_Panzer</li>
</allowedMechRaces>
</li>
</comps>
</ThingDef>
<ThingDef ParentName="BaseBullet">
<defName>Bullet_Wula_AI_Heavy_Panzer_Main_Weapon</defName>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
<Defs>
<!-- human的克隆防止其他mod修改human导致的不兼容 -->
<ThingDef Abstract="True" Name="Wula_Human_Protect_Superclass" ParentName="BasePawn">
<defName>Wula_Human</defName>
<label>human</label>
@@ -225,7 +224,6 @@
</li>
</comps>
</ThingDef>
<!-- 基础类希望对human的更改在此处应用 -->
<ThingDef Abstract="True" Name="Wula_Human_Base" ParentName="Wula_Human_Protect_Superclass">
<statBases>
<MarketValue>0</MarketValue>
@@ -785,7 +783,6 @@
</comps>
</AlienRace.ThingDef_AlienRace>
<!-- 乌拉基础机械族类 -->
<ThingDef ParentName="BasePawn" Name="WULA_BaseMechanoid" Abstract="True">
<soundImpactDefault>BulletImpact_Metal</soundImpactDefault>
<statBases>
@@ -877,7 +874,6 @@
</comps>
</ThingDef>
<!-- 猫猫族系 -->
<ThingDef Abstract="True" Name="Mech_WULA_Cat_Base" ParentName="WULA_BaseMechanoid">
<statBases>
<MoveSpeed>5</MoveSpeed>
@@ -1099,695 +1095,6 @@
</comps>
</AlienRace.ThingDef_AlienRace>
<AlienRace.ThingDef_AlienRace ParentName="WULA_BaseMechanoid">
<defName>Wula_AI_Heavy_Panzer</defName> <!-- 修改了defName以避免冲突 -->
<label>HAp-6"战车"</label>
<description>乌拉帝国的中型战争机械,以悬浮的方式穿梭于战场之上,使用破坏力巨大的自动炮和车体臼炮打击敌方,是乌拉帝国前锋部队的中流砥柱。</description>
<uiIconPath>Wula/Things/Wula_AI_Heavy_Panzer/Wula_AI_Heavy_Panzer_Icon</uiIconPath>
<alienRace>
<raceRestriction>
<weaponList>
<li>Wula_AI_Heavy_Panzer_Main_Weapon</li>
</weaponList>
<onlyUseRaceRestrictedApparel>true</onlyUseRaceRestrictedApparel>
<onlyUseRaceRestrictedWeapons>true</onlyUseRaceRestrictedWeapons>
</raceRestriction>
<compatibility>
<isFlesh>false</isFlesh>
</compatibility>
</alienRace>
<statBases>
<BandwidthCost>1</BandwidthCost>
<MoveSpeed>2</MoveSpeed>
<EnergyShieldEnergyMax>2</EnergyShieldEnergyMax>
<StaggerDurationFactor>0</StaggerDurationFactor>
<MaxFlightTime>9999</MaxFlightTime>
<FlightCooldown>0</FlightCooldown>
<ArmorRating_Sharp>1</ArmorRating_Sharp>
<ArmorRating_Blunt>1</ArmorRating_Blunt>
<ArmorRating_Heat>2</ArmorRating_Heat>
</statBases>
<costList Inherit="False">
<WULA_Alloy>300</WULA_Alloy>
<WULA_Charge_Cube>18</WULA_Charge_Cube>
<ComponentSpacer>2</ComponentSpacer>
</costList>
<race>
<body>WULA_AI_Heavy_Panzer_Body</body>
<baseBodySize>20</baseBodySize>
<lifeStageAges>
<li>
<def>MechanoidFullyFormed</def>
<minAge>0</minAge>
<soundCall>Pawn_Wula_AI_Heavy_Panzer_Call</soundCall>
</li>
</lifeStageAges>
<baseHealthScale>10</baseHealthScale>
<flightStartChanceOnJobStart>1</flightStartChanceOnJobStart>
<mechWeightClass>Heavy</mechWeightClass>
<!-- <thinkTreeConstant>WarUrchinConstant</thinkTreeConstant> -->
</race>
<tools Inherit="False">
<li>
<label>碾压</label>
<capacities>
<li>Blunt</li>
</capacities>
<power>360</power>
<cooldownTime>8</cooldownTime>
<linkedBodyPartsGroup>Torso</linkedBodyPartsGroup>
<ensureLinkedBodyPartsGroupAlwaysUsable>true</ensureLinkedBodyPartsGroupAlwaysUsable>
</li>
</tools>
<comps>
<li Class="WulaFallenEmpire.CompProperties_MultiTurretGun">
<ID>0</ID>
<turretDef>Wula_AI_Heavy_Panzer_Turret_Weapon</turretDef>
<!-- <angleOffset>-90</angleOffset> -->
<renderNodeProperties>
<li>
<nodeClass>PawnRenderNode_TurretGun</nodeClass>
<workerClass>PawnRenderNodeWorker_TurretGun</workerClass>
<parentTagDef>Body</parentTagDef>
<overrideMeshSize>(7, 7)</overrideMeshSize>
<baseLayer>20</baseLayer>
<pawnType>Any</pawnType>
<drawData>
<dataNorth>
<rotationOffset>-90</rotationOffset>
</dataNorth>
<dataEast>
<rotationOffset>-90</rotationOffset>
</dataEast>
<dataSouth>
<rotationOffset>-90</rotationOffset>
</dataSouth>
<dataWest>
<rotationOffset>90</rotationOffset>
</dataWest>
</drawData>
</li>
</renderNodeProperties>
</li>
<li Class="CompProperties_Shield">
<startingTicksToReset>36000</startingTicksToReset><!-- 10 mins -->
<minDrawSize>5.2</minDrawSize>
<maxDrawSize>5.4</maxDrawSize>
<energyLossPerDamage>0.02</energyLossPerDamage>
<energyOnReset>4.0</energyOnReset>
<blocksRangedWeapons>false</blocksRangedWeapons>
</li>
<!-- 飞行组件 -->
<li Class="WulaFallenEmpire.CompProperties_PawnFlight">
<!-- 飞行触发条件:仅在征召时飞行 -->
<flightCondition>Drafted</flightCondition>
<!-- 链接到我们刚刚创建的 AnimationDef -->
<flyingAnimationNorth>WULA_Hover_FlyNorth</flyingAnimationNorth>
<flyingAnimationEast>WULA_Hover_FlyEast</flyingAnimationEast>
<flyingAnimationSouth>WULA_Hover_FlySouth</flyingAnimationSouth>
</li>
<li Class="WulaFallenEmpire.CompProperties_AreaDamage">
<radius>1.5</radius>
<damageIntervalTicks>30</damageIntervalTicks>
<damageDef>Crush</damageDef>
<damageAmount>3</damageAmount>
<scaleWithPsychicSensitivity>false</scaleWithPsychicSensitivity>
<affectFriendly>false</affectFriendly>
<affectNeutral>false</affectNeutral>
<affectHostile>true</affectHostile>
<affectBuildings>false</affectBuildings>
<ignoreFactionRelations>false</ignoreFactionRelations>
<startEnabled>false</startEnabled>
<toggleLabel>碾压伤害</toggleLabel>
<toggleDescription>HAp-6"战车"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。</toggleDescription>
<toggleIconPath>Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage</toggleIconPath>
</li>
</comps>
</AlienRace.ThingDef_AlienRace>
<AlienRace.ThingDef_AlienRace ParentName="WULA_BaseMechanoid">
<defName>Wula_AI_Rocket_Panzer</defName> <!-- 修改了defName以避免冲突 -->
<label>HRp-3"喷火战车"</label>
<description>乌拉帝国的中型战争机械,以悬浮的方式穿梭于战场之上,拥有车体臼炮和两具可以发射大量燃烧火箭弹的转轮导弹巢,但是未像其姊妹型号那样装备护盾。</description>
<uiIconPath>Wula/Things/Wula_AI_Heavy_Panzer/Wula_AI_Rocket_Panzer_Icon</uiIconPath>
<alienRace>
<raceRestriction>
<weaponList>
<li>Wula_AI_Heavy_Panzer_Main_Weapon</li>
</weaponList>
<onlyUseRaceRestrictedApparel>true</onlyUseRaceRestrictedApparel>
<onlyUseRaceRestrictedWeapons>true</onlyUseRaceRestrictedWeapons>
</raceRestriction>
<compatibility>
<isFlesh>false</isFlesh>
</compatibility>
</alienRace>
<statBases>
<BandwidthCost>1</BandwidthCost>
<MoveSpeed>2</MoveSpeed>
<EnergyShieldEnergyMax>2</EnergyShieldEnergyMax>
<StaggerDurationFactor>0</StaggerDurationFactor>
<MaxFlightTime>9999</MaxFlightTime>
<FlightCooldown>0</FlightCooldown>
<ArmorRating_Sharp>1</ArmorRating_Sharp>
<ArmorRating_Blunt>1</ArmorRating_Blunt>
<ArmorRating_Heat>2</ArmorRating_Heat>
</statBases>
<costList Inherit="False">
<WULA_Alloy>300</WULA_Alloy>
<WULA_Charge_Cube>18</WULA_Charge_Cube>
<ComponentSpacer>2</ComponentSpacer>
</costList>
<race>
<body>WULA_AI_Heavy_Panzer_Body</body>
<baseBodySize>20</baseBodySize>
<lifeStageAges>
<li>
<def>MechanoidFullyFormed</def>
<minAge>0</minAge>
<soundCall>Pawn_Wula_AI_Heavy_Panzer_Call</soundCall>
</li>
</lifeStageAges>
<baseHealthScale>10</baseHealthScale>
<flightStartChanceOnJobStart>1</flightStartChanceOnJobStart>
<mechWeightClass>Heavy</mechWeightClass>
<!-- <thinkTreeConstant>WarUrchinConstant</thinkTreeConstant> -->
</race>
<tools Inherit="False">
<li>
<label>碾压</label>
<capacities>
<li>Blunt</li>
</capacities>
<power>360</power>
<cooldownTime>8</cooldownTime>
<linkedBodyPartsGroup>Torso</linkedBodyPartsGroup>
<ensureLinkedBodyPartsGroupAlwaysUsable>true</ensureLinkedBodyPartsGroupAlwaysUsable>
</li>
</tools>
<comps>
<li Class="WulaFallenEmpire.CompProperties_MultiTurretGun">
<ID>0</ID>
<turretDef>Wula_AI_Rocket_Panzer_Turret_Weapon</turretDef>
<!-- <angleOffset>-90</angleOffset> -->
<renderNodeProperties>
<li>
<nodeClass>PawnRenderNode_TurretGun</nodeClass>
<workerClass>PawnRenderNodeWorker_TurretGun</workerClass>
<parentTagDef>Body</parentTagDef>
<overrideMeshSize>(7, 7)</overrideMeshSize>
<baseLayer>20</baseLayer>
<pawnType>Any</pawnType>
<drawData>
<dataNorth>
<rotationOffset>-90</rotationOffset>
</dataNorth>
<dataEast>
<rotationOffset>-90</rotationOffset>
</dataEast>
<dataSouth>
<rotationOffset>-90</rotationOffset>
</dataSouth>
<dataWest>
<rotationOffset>90</rotationOffset>
</dataWest>
</drawData>
</li>
</renderNodeProperties>
</li>
<!-- 飞行组件 -->
<li Class="WulaFallenEmpire.CompProperties_PawnFlight">
<!-- 飞行触发条件:仅在征召时飞行 -->
<flightCondition>Drafted</flightCondition>
<!-- 链接到我们刚刚创建的 AnimationDef -->
<flyingAnimationNorth>WULA_Hover_FlyNorth</flyingAnimationNorth>
<flyingAnimationEast>WULA_Hover_FlyEast</flyingAnimationEast>
<flyingAnimationSouth>WULA_Hover_FlySouth</flyingAnimationSouth>
</li>
<li Class="WulaFallenEmpire.CompProperties_AreaDamage">
<radius>1.5</radius>
<damageIntervalTicks>30</damageIntervalTicks>
<damageDef>Crush</damageDef>
<damageAmount>3</damageAmount>
<scaleWithPsychicSensitivity>false</scaleWithPsychicSensitivity>
<affectFriendly>false</affectFriendly>
<affectNeutral>false</affectNeutral>
<affectHostile>true</affectHostile>
<affectBuildings>false</affectBuildings>
<ignoreFactionRelations>false</ignoreFactionRelations>
<startEnabled>false</startEnabled>
<toggleLabel>碾压伤害</toggleLabel>
<toggleDescription>HRp-3"喷火战车"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。</toggleDescription>
<toggleIconPath>Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage</toggleIconPath>
</li>
</comps>
</AlienRace.ThingDef_AlienRace>
<AlienRace.ThingDef_AlienRace ParentName="WULA_BaseMechanoid">
<defName>Wula_Mech_Mobile_Shield</defName> <!-- 修改了defName以避免冲突 -->
<label>MSm-8"放射盾"</label>
<description>乌拉帝国的中型战争机械,常被用于镇压异族聚居地的暴动。它形状非常奇怪,根本分不出头在哪,但是不要因此小瞧这个机械体——其强大的立场盾能护佑一片区域并反射大量炮火,机体放射出来的辐射则会点燃进入反射盾范围内的敌人。在相关许可开放后,它甚至可以支持机械乌拉使用其内置的相位场进行区域传送,使其获得无与伦比的机动性。</description>
<alienRace>
<raceRestriction>
<onlyUseRaceRestrictedApparel>true</onlyUseRaceRestrictedApparel>
<onlyUseRaceRestrictedWeapons>true</onlyUseRaceRestrictedWeapons>
</raceRestriction>
<compatibility>
<isFlesh>false</isFlesh>
</compatibility>
</alienRace>
<statBases>
<BandwidthCost>1</BandwidthCost>
<MoveSpeed>3</MoveSpeed>
<ArmorRating_Sharp>0.5</ArmorRating_Sharp>
<ArmorRating_Blunt>0.5</ArmorRating_Blunt>
<ArmorRating_Heat>2</ArmorRating_Heat>
</statBases>
<race>
<body>WULA_Mech_Mobile_Shield_Body</body>
<baseBodySize>50</baseBodySize>
<lifeStageAges>
<li>
<def>MechanoidFullyFormed</def>
<minAge>0</minAge>
<soundCall>Pawn_Wula_Mech_Mobile_Factory_Call</soundCall>
</li>
</lifeStageAges>
<baseHealthScale>5</baseHealthScale>
<mechWeightClass>Heavy</mechWeightClass>
</race>
<tools Inherit="False">
<li>
<label>碾压</label>
<capacities>
<li>Blunt</li>
</capacities>
<power>360</power>
<cooldownTime>8</cooldownTime>
<linkedBodyPartsGroup>Torso</linkedBodyPartsGroup>
<ensureLinkedBodyPartsGroupAlwaysUsable>true</ensureLinkedBodyPartsGroupAlwaysUsable>
</li>
</tools>
<comps>
<!-- 护盾无法在此绘制 -->
<li Class="WulaFallenEmpire.CompProperties_AreaShield">
<radius>15</radius>
<baseHitPoints>300</baseHitPoints>
<rechargeDelay>2400</rechargeDelay>
<rechargeHitPointsIntervalTicks>30</rechargeHitPointsIntervalTicks>
<!-- 效果器配置 -->
<absorbEffecter>Interceptor_BlockedProjectile</absorbEffecter>
<interceptEffecter>Interceptor_BlockedProjectile</interceptEffecter>
<breakEffecter>Shield_Break</breakEffecter>
<reactivateEffecter>BulletShieldGenerator_Reactivate</reactivateEffecter>
<color>(0.9, 0.2, 0.2, 0.5)</color> <!-- 护盾气泡的颜色 (RGBA) -->
<!-- 拦截设置 -->
<interceptGroundProjectiles>true</interceptGroundProjectiles>
<interceptNonHostileProjectiles>false</interceptNonHostileProjectiles>
<interceptAirProjectiles>true</interceptAirProjectiles>
<!-- 反射设置 -->
<canReflect>true</canReflect>
<reflectChance>0.85</reflectChance>
<reflectAngleRange>30</reflectAngleRange>
<reflectCost>0</reflectCost>
<reflectEffecter>Interceptor_BlockedProjectile</reflectEffecter>
</li>
<li Class="WulaFallenEmpire.CompProperties_AreaDamage">
<radius>15</radius>
<damageIntervalTicks>30</damageIntervalTicks>
<damageDef>Flame</damageDef>
<damageAmount>8</damageAmount>
<scaleWithPsychicSensitivity>false</scaleWithPsychicSensitivity>
<affectFriendly>false</affectFriendly>
<affectNeutral>false</affectNeutral>
<affectHostile>true</affectHostile>
<affectBuildings>false</affectBuildings>
<ignoreFactionRelations>false</ignoreFactionRelations>
<startEnabled>false</startEnabled>
<toggleLabel>热辐射</toggleLabel>
<toggleDescription>MSm-8"放射盾"可以打开外壳,蒸发胆敢进入反射立场内的敌军——这同时会使得它伤害附近所有的散落物品。</toggleDescription>
<toggleIconPath>Wula/UI/Commands/Wula_Psi_Titan_AreaDamage</toggleIconPath>
</li>
<!-- 区域传送组件 -->
<li Class="WulaFallenEmpire.CompProperties_AreaTeleporter">
<teleportRadius>15</teleportRadius>
<stunTicks>60</stunTicks>
<allowedRaces>
<li>WulaSpecies</li>
</allowedRaces>
<onlyPawnsInSameFaction>true</onlyPawnsInSameFaction>
<entryEffecter>Skip_Entry</entryEffecter>
<exitEffecter>Skip_Exit</exitEffecter>
<teleportSound>Psycast_Skip_Entry</teleportSound>
<requireResearchToUse>true</requireResearchToUse>
<requiredResearch>WULA_Mech_Mobile_Shield_Teleporter_Technology</requiredResearch>
</li>
</comps>
</AlienRace.ThingDef_AlienRace>
<AlienRace.ThingDef_AlienRace ParentName="WULA_BaseMechanoid">
<defName>Wula_Mech_Mobile_Factory</defName> <!-- 修改了defName以避免冲突 -->
<label>MFm-2"陆行舰"</label>
<description>乌拉帝国的大型战争机械,简直就是一座移动堡垒——它不仅装甲厚实、火炮林立,还能在战场上生产大量的辅助战争机械以形成坚实的弹性阵线,生来就是为了粉碎坚固的要塞和顽强的抵抗。</description>
<alienRace>
<raceRestriction>
<weaponList>
<li>WULA_RW_Unlimit_Penetrating_Beam_Cannon</li>
</weaponList>
<onlyUseRaceRestrictedApparel>true</onlyUseRaceRestrictedApparel>
<onlyUseRaceRestrictedWeapons>true</onlyUseRaceRestrictedWeapons>
</raceRestriction>
<compatibility>
<isFlesh>false</isFlesh>
</compatibility>
</alienRace>
<statBases>
<BandwidthCost>1</BandwidthCost>
<MoveSpeed>1</MoveSpeed>
<EnergyShieldEnergyMax>5</EnergyShieldEnergyMax>
<MaxFlightTime>9999</MaxFlightTime>
<FlightCooldown>0</FlightCooldown>
<ArmorRating_Sharp>1.75</ArmorRating_Sharp>
<ArmorRating_Blunt>1.75</ArmorRating_Blunt>
<ArmorRating_Heat>2</ArmorRating_Heat>
</statBases>
<race>
<body>WULA_Mech_Mobile_Factory_Body</body>
<baseBodySize>50</baseBodySize>
<lifeStageAges>
<li>
<def>MechanoidFullyFormed</def>
<minAge>0</minAge>
<soundCall>Pawn_Wula_Mech_Mobile_Factory_Call</soundCall>
</li>
</lifeStageAges>
<baseHealthScale>25</baseHealthScale>
<flightStartChanceOnJobStart>1</flightStartChanceOnJobStart>
<mechWeightClass>Heavy</mechWeightClass>
<!-- <thinkTreeConstant>WarUrchinConstant</thinkTreeConstant> -->
</race>
<tools Inherit="False">
<li>
<label>碾压</label>
<capacities>
<li>Blunt</li>
</capacities>
<power>360</power>
<cooldownTime>8</cooldownTime>
<linkedBodyPartsGroup>Torso</linkedBodyPartsGroup>
<ensureLinkedBodyPartsGroupAlwaysUsable>true</ensureLinkedBodyPartsGroupAlwaysUsable>
</li>
</tools>
<comps>
<li Class="WulaFallenEmpire.CompProperties_MultiTurretGun">
<ID>0</ID>
<turretDef>Wula_CR_Mobile_Factory_Turret</turretDef>
<!-- <angleOffset>-90</angleOffset> -->
<renderNodeProperties>
<li>
<nodeClass>PawnRenderNode_TurretGun</nodeClass>
<workerClass>PawnRenderNodeWorker_TurretGun</workerClass>
<parentTagDef>Body</parentTagDef>
<overrideMeshSize>(2, 2)</overrideMeshSize>
<baseLayer>20</baseLayer>
<pawnType>Any</pawnType>
<drawData>
<dataNorth>
<rotationOffset>-90</rotationOffset>
<offset>(-1, 0, -1.45)</offset>
<flip>true</flip>
<layer>-5</layer>
</dataNorth>
<dataEast>
<rotationOffset>90</rotationOffset>
<layer>-5</layer>
<offset>(3.25, 0, -1)</offset>
</dataEast>
<dataSouth>
<rotationOffset>-90</rotationOffset>
<offset>(1.5, 0, -1.45)</offset>
</dataSouth>
<dataWest>
<rotationOffset>90</rotationOffset>
<offset>(-3.25, 0, -1)</offset>
</dataWest>
</drawData>
</li>
</renderNodeProperties>
</li>
<li Class="WulaFallenEmpire.CompProperties_MultiTurretGun">
<ID>1</ID>
<turretDef>Wula_CR_Mobile_Factory_Turret</turretDef>
<!-- <angleOffset>-90</angleOffset> -->
<renderNodeProperties>
<li>
<nodeClass>PawnRenderNode_TurretGun</nodeClass>
<workerClass>PawnRenderNodeWorker_TurretGun</workerClass>
<parentTagDef>Body</parentTagDef>
<overrideMeshSize>(2, 2)</overrideMeshSize>
<baseLayer>20</baseLayer>
<pawnType>Any</pawnType>
<drawData>
<dataNorth>
<rotationOffset>-90</rotationOffset>
<offset>(1, 0, -1.45)</offset>
<layer>-5</layer>
</dataNorth>
<dataEast>
<rotationOffset>-90</rotationOffset>
<offset>(3.25, 0, -1)</offset>
</dataEast>
<dataSouth>
<rotationOffset>-90</rotationOffset>
<offset>(-1.6, 0, -1.45)</offset>
</dataSouth>
<dataWest>
<rotationOffset>90</rotationOffset>
<offset>(-3.25, 0, -1)</offset>
<layer>-5</layer>
</dataWest>
</drawData>
</li>
</renderNodeProperties>
</li>
<li Class="WulaFallenEmpire.CompProperties_MultiTurretGun">
<ID>2</ID>
<turretDef>Wula_MR_Mobile_Factory_Turret</turretDef>
<!-- <angleOffset>-90</angleOffset> -->
<renderNodeProperties>
<li>
<nodeClass>PawnRenderNode_TurretGun</nodeClass>
<workerClass>PawnRenderNodeWorker_TurretGun</workerClass>
<parentTagDef>Body</parentTagDef>
<overrideMeshSize>(2, 2)</overrideMeshSize>
<baseLayer>20</baseLayer>
<pawnType>Any</pawnType>
<drawData>
<dataNorth>
<rotationOffset>-90</rotationOffset>
<offset>(-1.3, 0, -0.45)</offset>
<flip>true</flip>
<layer>-5</layer>
</dataNorth>
<dataEast>
<rotationOffset>90</rotationOffset>
<layer>-5</layer>
<offset>(2.4, 0, -0.35)</offset>
</dataEast>
<dataSouth>
<rotationOffset>-90</rotationOffset>
<offset>(2.3, 0, -0.45)</offset>
</dataSouth>
<dataWest>
<rotationOffset>90</rotationOffset>
<offset>(-2.4, 0, -0.35)</offset>
</dataWest>
</drawData>
</li>
</renderNodeProperties>
</li>
<li Class="WulaFallenEmpire.CompProperties_MultiTurretGun">
<ID>3</ID>
<turretDef>Wula_MR_Mobile_Factory_Turret</turretDef>
<!-- <angleOffset>-90</angleOffset> -->
<renderNodeProperties>
<li>
<nodeClass>PawnRenderNode_TurretGun</nodeClass>
<workerClass>PawnRenderNodeWorker_TurretGun</workerClass>
<parentTagDef>Body</parentTagDef>
<overrideMeshSize>(2, 2)</overrideMeshSize>
<baseLayer>20</baseLayer>
<pawnType>Any</pawnType>
<drawData>
<dataNorth>
<rotationOffset>-90</rotationOffset>
<offset>(1.3, 0, -0.45)</offset>
<flip>true</flip>
<layer>-5</layer>
</dataNorth>
<dataEast>
<rotationOffset>-90</rotationOffset>
<offset>(2.4, 0, -0.35)</offset>
</dataEast>
<dataSouth>
<rotationOffset>-90</rotationOffset>
<offset>(-2.3, 0, -0.45)</offset>
</dataSouth>
<dataWest>
<rotationOffset>90</rotationOffset>
<layer>-5</layer>
<offset>(-2.4, 0, -0.35)</offset>
</dataWest>
</drawData>
</li>
</renderNodeProperties>
</li>
<li Class="WulaFallenEmpire.CompProperties_MultiTurretGun">
<ID>4</ID>
<turretDef>Wula_LR_Mobile_Factory_Turret</turretDef>
<!-- <angleOffset>-90</angleOffset> -->
<renderNodeProperties>
<li>
<nodeClass>PawnRenderNode_TurretGun</nodeClass>
<workerClass>PawnRenderNodeWorker_TurretGun</workerClass>
<parentTagDef>Body</parentTagDef>
<overrideMeshSize>(2, 2)</overrideMeshSize>
<baseLayer>20</baseLayer>
<pawnType>Any</pawnType>
<drawData>
<dataNorth>
<rotationOffset>-90</rotationOffset>
<offset>(1.25, 0, 0.45)</offset>
<flip>true</flip>
<layer>-5</layer>
</dataNorth>
<dataEast>
<rotationOffset>90</rotationOffset>
<layer>-5</layer>
<offset>(1.6, 0, 0.25)</offset>
</dataEast>
<dataSouth>
<rotationOffset>-90</rotationOffset>
<offset>(-2.15, 0, 0.65)</offset>
</dataSouth>
<dataWest>
<rotationOffset>90</rotationOffset>
<offset>(-1.6, 0, 0.25)</offset>
</dataWest>
</drawData>
</li>
</renderNodeProperties>
</li>
<li Class="WulaFallenEmpire.CompProperties_MultiTurretGun">
<ID>5</ID>
<turretDef>Wula_LR_Mobile_Factory_Turret</turretDef>
<!-- <angleOffset>-90</angleOffset> -->
<renderNodeProperties>
<li>
<nodeClass>PawnRenderNode_TurretGun</nodeClass>
<workerClass>PawnRenderNodeWorker_TurretGun</workerClass>
<parentTagDef>Body</parentTagDef>
<overrideMeshSize>(2, 2)</overrideMeshSize>
<baseLayer>20</baseLayer>
<pawnType>Any</pawnType>
<drawData>
<dataNorth>
<rotationOffset>-90</rotationOffset>
<offset>(-1.25, 0, 0.45)</offset>
<flip>true</flip>
<layer>-5</layer>
</dataNorth>
<dataEast>
<rotationOffset>-90</rotationOffset>
<offset>(1.6, 0, 0.25)</offset>
</dataEast>
<dataSouth>
<rotationOffset>-90</rotationOffset>
<offset>(2.05, 0, 0.65)</offset>
</dataSouth>
<dataWest>
<rotationOffset>90</rotationOffset>
<layer>-5</layer>
<offset>(-1.6, 0, 0.25)</offset>
</dataWest>
</drawData>
</li>
</renderNodeProperties>
</li>
<!-- 护盾无法在此绘制 -->
<li Class="WulaFallenEmpire.CompProperties_AreaShield">
<radius>9</radius>
<baseHitPoints>300</baseHitPoints>
<rechargeDelay>2400</rechargeDelay>
<rechargeHitPointsIntervalTicks>30</rechargeHitPointsIntervalTicks>
<!-- 效果器配置 -->
<absorbEffecter>Interceptor_BlockedProjectile</absorbEffecter>
<interceptEffecter>Interceptor_BlockedProjectile</interceptEffecter>
<breakEffecter>Shield_Break</breakEffecter>
<reactivateEffecter>BulletShieldGenerator_Reactivate</reactivateEffecter>
<color>(0.9, 0.2, 0.2, 0.5)</color> <!-- 护盾气泡的颜色 (RGBA) -->
<!-- 拦截设置 -->
<interceptGroundProjectiles>true</interceptGroundProjectiles>
<interceptNonHostileProjectiles>false</interceptNonHostileProjectiles>
<interceptAirProjectiles>true</interceptAirProjectiles>
<!-- 反射设置 -->
<canReflect>true</canReflect>
<reflectChance>0.6</reflectChance>
<reflectAngleRange>30</reflectAngleRange>
<reflectCost>0</reflectCost>
<reflectEffecter>Interceptor_BlockedProjectile</reflectEffecter>
</li>
<!-- 飞行组件 -->
<li Class="WulaFallenEmpire.CompProperties_PawnFlight">
<!-- 飞行触发条件:仅在征召时飞行 -->
<flightCondition>Drafted</flightCondition>
<!-- 链接到我们刚刚创建的 AnimationDef -->
<flyingAnimationNorth>WULA_Hover_FlyNorth</flyingAnimationNorth>
<flyingAnimationEast>WULA_Hover_FlyEast</flyingAnimationEast>
<flyingAnimationSouth>WULA_Hover_FlySouth</flyingAnimationSouth>
</li>
<li Class="WulaFallenEmpire.CompProperties_AreaDamage">
<radius>3.5</radius>
<damageIntervalTicks>30</damageIntervalTicks>
<damageDef>Crush</damageDef>
<damageAmount>4</damageAmount>
<scaleWithPsychicSensitivity>false</scaleWithPsychicSensitivity>
<affectFriendly>false</affectFriendly>
<affectNeutral>false</affectNeutral>
<affectHostile>true</affectHostile>
<affectBuildings>false</affectBuildings>
<ignoreFactionRelations>false</ignoreFactionRelations>
<startEnabled>false</startEnabled>
<toggleLabel>碾压伤害</toggleLabel>
<toggleDescription>MFm-2"陆行舰"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。</toggleDescription>
<toggleIconPath>Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage</toggleIconPath>
</li>
</comps>
</AlienRace.ThingDef_AlienRace>
<ThingDef ParentName="BaseMechanoidWalker">
<defName>WULA_Mech_Flyer</defName>
<label>CRm-51"兵蚁"</label>
@@ -2004,7 +1311,6 @@
</comps>
</AlienRace.ThingDef_AlienRace>
<!-- 特殊单位 -->
<ThingDef ParentName="BaseMechanoidWalker">
<defName>WULA_Fxxk_Goose</defName>
<label>神人大鹅</label>

File diff suppressed because it is too large Load Diff

View File

@@ -560,4 +560,99 @@
</subNodes>
</thinkRoot>
</ThinkTreeDef>
<ThinkTreeDef>
<defName>Wula_Battle_Mech_With_1_Pilot</defName>
<thinkRoot Class="ThinkNode_Priority">
<subNodes>
<!-- Despawned -->
<li Class="ThinkNode_Subtree">
<treeDef>Despawned</treeDef>
</li>
<!-- Deactivated -->
<li Class="ThinkNode_ConditionalDeactivated" MayRequire="Ludeon.RimWorld.Odyssey">
<subNodes>
<li Class="JobGiver_Deactivated" />
</subNodes>
</li>
<!-- Downed -->
<li Class="ThinkNode_Subtree">
<treeDef>Downed</treeDef>
</li>
<!-- 重点!你需要在思考树中插入此项,以规定当没有足够的驾驶员时,机甲将执行什么行动 -->
<li Class="WulaFallenEmpire.ThinkNode_ConditionalMechHasPilot">
<minPilotCount>1</minPilotCount>
<subNodes>
<li Class="WulaFallenEmpire.JobGiver_NoPilot"/>
</subNodes>
</li>
<!-- Mental state -->
<li Class="ThinkNode_ConditionalMentalState">
<state>BerserkMechanoid</state>
<subNodes>
<li Class="ThinkNode_Priority">
<subNodes>
<li Class="JobGiver_Berserk" />
<li Class="JobGiver_WanderAnywhere">
<maxDanger>Deadly</maxDanger>
</li>
</subNodes>
</li>
</subNodes>
</li>
<!-- Do a queued job -->
<li Class="ThinkNode_QueuedJob" />
<!-- Wait if drafted -->
<li Class="ThinkNode_ConditionalOfPlayerFaction">
<subNodes>
<li Class="ThinkNode_Tagger">
<tagToGive>DraftedOrder</tagToGive>
<subNodes>
<li Class="JobGiver_MoveToStandable" />
<li Class="JobGiver_Orders" />
</subNodes>
</li>
</subNodes>
</li>
<!-- Lord -->
<li Class="ThinkNode_Subtree">
<treeDef>LordDuty</treeDef>
</li>
<!-- Idle player mech -->
<li Class="ThinkNode_ConditionalOfPlayerFaction">
<subNodes>
<li Class="ThinkNode_Tagger">
<tagToGive>Idle</tagToGive>
<subNodes>
<li Class="JobGiver_WanderColony">
<maxDanger>None</maxDanger>
</li>
</subNodes>
</li>
</subNodes>
</li>
<!-- Idle -->
<li Class="ThinkNode_Tagger">
<tagToGive>Idle</tagToGive>
<subNodes>
<li Class="JobGiver_WanderAnywhere">
<maxDanger>Deadly</maxDanger>
</li>
</subNodes>
</li>
<!-- Idle error -->
<li Class="JobGiver_IdleError"/>
</subNodes>
</thinkRoot>
</ThinkTreeDef>
</Defs>

View File

@@ -5,7 +5,7 @@ using Verse;
namespace ArachnaeSwarm
{
[HarmonyPatch(typeof(Hediff_Mechlink), "PostAdd")]
public static class Hediff_Mechlink_PostAdd_Patch
public static class Hediff_Mechlink_PostAWULA_Patch
{
public static bool Prefix(Hediff_Mechlink __instance, DamageInfo? dinfo)
{

View File

@@ -131,7 +131,7 @@ namespace WulaFallenEmpire
}
catch (System.Exception ex)
{
Log.Error($"[DD] Error in fixed ColonistBar patch: {ex}");
Log.Error($"[WULA] Error in fixed ColonistBar patch: {ex}");
// 出错时不改变原列表
}
}

View File

@@ -34,7 +34,7 @@ namespace WulaFallenEmpire
else
{
// 此机甲不在允许列表中
cantReason = "DD_Equipment_For_Other_Mech".Translate();
cantReason = "WULA_Equipment_For_Other_Mech".Translate();
__result = false;
return false;
}
@@ -42,7 +42,7 @@ namespace WulaFallenEmpire
else
{
// 非机甲尝试装备专用武器,禁止
cantReason = "DD_Human_Cannot_Equip_Mech_Weapon".Translate();
cantReason = "WULA_Human_Cannot_Equip_Mech_Weapon".Translate();
__result = false;
return false;
}
@@ -54,7 +54,7 @@ namespace WulaFallenEmpire
if (pawn is Wulamechunit)
{
// 机甲不能装备普通武器
cantReason = "DD_Equipment_Not_Allow_For_Mech".Translate();
cantReason = "WULA_Equipment_Not_Allow_For_Mech".Translate();
__result = false;
return false;
}
@@ -66,7 +66,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] CanEquip patch error: {ex}");
Log.Error($"[WULA] CanEquip patch error: {ex}");
return true;
}
}

View File

@@ -80,14 +80,14 @@ namespace WulaFallenEmpire
// 如果任一pawn是机甲单位返回拒绝
if (initiator is Wulamechunit || target is Wulamechunit)
{
__result = new AcceptanceReport("DD_MechCannotRomance".Translate());
__result = new AcceptanceReport("WULA_MechCannotRomance".Translate());
return false; // 跳过原始方法
}
// 如果任一pawn没有story组件返回拒绝
if (initiator?.story == null || target?.story == null)
{
__result = new AcceptanceReport("DD_NoStoryComponent".Translate());
__result = new AcceptanceReport("WULA_NoStoryComponent".Translate());
return false;
}

View File

@@ -17,7 +17,7 @@ namespace WulaFallenEmpire.HarmonyPatches
public static class Thing_TakeDamage_Patch
{
// 缓存装甲值StatDef
private static readonly StatDef ArmorStatDef = StatDef.Named("DD_MechArmor");
private static readonly StatDef ArmorStatDef = StatDef.Named("WULA_MechArmor");
// 阻挡效果的MoteDef
private static readonly ThingDef BlockMoteDef = DefDatabase<ThingDef>.GetNamedSilentFail("Mote_Spark");
@@ -102,7 +102,7 @@ namespace WulaFallenEmpire.HarmonyPatches
// 显示文字效果
Vector3 textPos = target.DrawPos + new Vector3(0, 0, 1f);
MoteMaker.ThrowText(textPos, target.Map, "DD_BlockByMechArmor".Translate(), Color.yellow, 2.5f);
MoteMaker.ThrowText(textPos, target.Map, "WULA_BlockByMechArmor".Translate(), Color.yellow, 2.5f);
// 显示粒子效果
if (BlockMoteDef != null)

View File

@@ -277,7 +277,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] Harmony patch error in TryMeleeAttack: {ex}");
Log.Error($"[WULA] Harmony patch error in TryMeleeAttack: {ex}");
return true; // 出错时继续执行原始方法
}
}

View File

@@ -42,7 +42,7 @@ namespace WulaFallenEmpire
}
else if (Prefs.DevMode)
{
Log.Warning($"[DD] OnPilotEnteredMech: 参数不是Wulamechunit类型: {mech?.GetType().Name}");
Log.Warning($"[WULA] OnPilotEnteredMech: 参数不是Wulamechunit类型: {mech?.GetType().Name}");
}
}
@@ -203,7 +203,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] 在机甲{mech.LabelShort}上添加Hediff时出错: {ex}");
Log.Error($"[WULA] 在机甲{mech.LabelShort}上添加Hediff时出错: {ex}");
}
}
@@ -220,7 +220,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] 从机甲{mech.LabelShort}移除Hediff时出错: {ex}");
Log.Error($"[WULA] 从机甲{mech.LabelShort}移除Hediff时出错: {ex}");
}
}
@@ -242,7 +242,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] 触发同步效果时出错: {ex}");
Log.Error($"[WULA] 触发同步效果时出错: {ex}");
}
}

View File

@@ -25,7 +25,7 @@ namespace WulaFallenEmpire
public ITab_MechSkills()
{
this.size = new Vector2(520f, 600f);
this.labelKey = "DD_MechSkills".Translate();
this.labelKey = "WULA_MechSkills".Translate();
}
protected override void FillTab()
@@ -36,7 +36,7 @@ namespace WulaFallenEmpire
if (pawn.TryGetComp<CompMechSkillInheritance>() == null)
{
DrawError("DD_NoMechSkillComps".Translate());
DrawError("WULA_NoMechSkillComps".Translate());
return;
}
@@ -226,7 +226,7 @@ namespace WulaFallenEmpire
);
string status = GetStatus(pawn);
string type = "DD_Mech".Translate();
string type = "WULA_Mech".Translate();
Widgets.Label(statusRect, $"{type} | {status}");
GUI.color = Color.white;
@@ -248,7 +248,7 @@ namespace WulaFallenEmpire
);
Text.Font = GameFont.Medium;
Widgets.Label(titleRect, "DD_PilotTitle".Translate());
Widgets.Label(titleRect, "WULA_PilotTitle".Translate());
Text.Font = GameFont.Small;
// 内容
@@ -268,12 +268,12 @@ namespace WulaFallenEmpire
if (pilot != null) pilotNames.Add(pilot.LabelShort);
}
var pilotNamelist = string.Join(", ", pilotNames);
Widgets.Label(contentRect, $"DD_PilotInfo".Translate(pilotNamelist));
Widgets.Label(contentRect, $"WULA_PilotInfo".Translate(pilotNamelist));
}
else
{
GUI.color = Color.gray;
Widgets.Label(contentRect, "DD_NoPilotShort".Translate());
Widgets.Label(contentRect, "WULA_NoPilotShort".Translate());
GUI.color = Color.white;
}
}
@@ -307,7 +307,7 @@ namespace WulaFallenEmpire
if (pawn.skills == null || pawn.skills.skills.Count == 0)
{
GUI.color = Color.gray;
Widgets.Label(skillsArea.ContractedBy(padding * 2), "DD_MechNoSkill".Translate());
Widgets.Label(skillsArea.ContractedBy(padding * 2), "WULA_MechNoSkill".Translate());
GUI.color = Color.white;
return;
}
@@ -412,9 +412,9 @@ namespace WulaFallenEmpire
var pilotComp = pawn.TryGetComp<CompMechPilotHolder>();
if (pilotComp == null || !pilotComp.HasPilots)
return "DD_NoPilot".Translate();
return "WULA_NoPilot".Translate();
return "DD_Operational".Translate();
return "WULA_Operational".Translate();
}
private void DrawError(string message)

View File

@@ -0,0 +1,62 @@
using RimWorld;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class MentalState_MechNoPilot : MentalState
{
public override void PostStart(string reason)
{
base.PostStart(reason);
// 停止所有工作和移动
pawn.jobs?.StopAll();
pawn.pather?.StopDead();
// 取消征召
if (pawn.drafter != null && pawn.Drafted)
{
pawn.drafter.Drafted = false;
}
// 清除当前敌人目标
pawn.mindState.enemyTarget = null;
}
public override void PostEnd()
{
base.PostEnd();
}
public override void MentalStateTick(int delta)
{
// 使用父类的tick逻辑但不允许自动恢复
if (pawn.IsHashIntervalTick(30, delta))
{
age += 30;
// 只有在有驾驶员时才允许恢复
// 检查会由 CompMechPilotHolder 处理
// 这里不实现自动恢复逻辑
}
}
// 重写以禁用敌对行为
public override bool ForceHostileTo(Thing t)
{
return false;
}
public override bool ForceHostileTo(Faction f)
{
return false;
}
// 重写以禁用社交活动
public override RandomSocialMode SocialModeMax()
{
return RandomSocialMode.Off;
}
}
}

View File

@@ -63,7 +63,7 @@ namespace WulaFallenEmpire
var pilotComp = this.TryGetComp<CompMechPilotHolder>();
if (pilotComp != null && !pilotComp.HasPilots)
{
Messages.Message("DD_CannotDraftWithoutPilot".Translate(this.LabelShort),
Messages.Message("WULA_CannotDraftWithoutPilot".Translate(this.LabelShort),
this, MessageTypeDefOf.RejectInput);
return;
}
@@ -101,7 +101,7 @@ namespace WulaFallenEmpire
var pilotComp = this.TryGetComp<CompMechPilotHolder>();
if (pilotComp != null && !pilotComp.HasPilots)
{
command_Toggle.Disable("DD_NoPilot".Translate());
command_Toggle.Disable("WULA_NoPilot".Translate());
}
command_Toggle.tutorTag = ((!base.Drafted) ? "Draft" : "Undraft");

View File

@@ -109,7 +109,7 @@ namespace WulaFallenEmpire
var pilotKind = Props.SelectRandomPilotKind();
if (pilotKind == null)
{
Log.Warning($"[DD] No valid pilot kind found");
Log.Warning($"[WULA] No valid pilot kind found");
return false;
}
@@ -151,7 +151,7 @@ namespace WulaFallenEmpire
}
else
{
Log.Warning($"[DD] Cannot add pilot {pilot.LabelShortCap} to mech");
Log.Warning($"[WULA] Cannot add pilot {pilot.LabelShortCap} to mech");
// 清理生成的pawn
pilot.Destroy();
return false;
@@ -159,7 +159,7 @@ namespace WulaFallenEmpire
}
catch (System.Exception ex)
{
Log.Error($"[DD] Error generating default pilot: {ex}");
Log.Error($"[WULA] Error generating default pilot: {ex}");
return false;
}
}

View File

@@ -0,0 +1,855 @@
using System;
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using UnityEngine;
using Verse;
using Verse.AI;
using Verse.Sound;
namespace WulaFallenEmpire
{
/// <summary>
/// 高速移动撞击组件
/// </summary>
public class CompHighSpeedCollision : ThingComp
{
// === 运行时状态 ===
private enum SpeedStage
{
Stage0, // 0阶段不移动
Stage1, // 1阶段低速碰撞
Stage2 // 2阶段高速击飞
}
private SpeedStage currentStage = SpeedStage.Stage0;
private int stageTransitionCooldown = 0;
// 用于计算速度的帧历史
private Queue<float> speedHistory = new Queue<float>();
private IntVec3 lastPosition = IntVec3.Invalid;
private int lastPositionTick = -1;
// 已处理的敌人记录(避免同一帧重复处理)
private HashSet<Pawn> processedPawnsThisTick = new HashSet<Pawn>();
// === 缓存 ===
private CellRect collisionAreaCache = default;
private int lastAreaRecalculationTick = -1;
public CompProperties_HighSpeedCollision Props => (CompProperties_HighSpeedCollision)props;
public override void PostSpawnSetup(bool respawningAfterLoad)
{
base.PostSpawnSetup(respawningAfterLoad);
// 初始化速度历史
speedHistory.Clear();
for (int i = 0; i < Props.speedHistoryFrameCount; i++)
{
speedHistory.Enqueue(0f);
}
lastPosition = parent.Position;
lastPositionTick = Find.TickManager.TicksGame;
}
public override void CompTick()
{
base.CompTick();
if (!parent.Spawned || parent.Destroyed)
return;
Pawn pawn = parent as Pawn;
if (pawn == null || pawn.Dead || pawn.Downed)
return;
// 检查是否死亡或不能移动
if (!CanMove(pawn))
{
ResetToStage0();
return;
}
// 每帧更新
ProcessFrame(pawn);
}
/// <summary>
/// 处理每帧逻辑
/// </summary>
private void ProcessFrame(Pawn pawn)
{
int currentTick = Find.TickManager.TicksGame;
// 1. 计算当前速度
float currentSpeed = CalculateCurrentSpeed(pawn, currentTick);
// 2. 更新速度历史
UpdateSpeedHistory(currentSpeed);
// 3. 计算平均速度
float averageSpeed = GetAverageSpeed();
// 4. 确定阶段
DetermineSpeedStage(averageSpeed);
// 5. 根据阶段应用效果
ApplyStageEffects(pawn);
// 6. 清理每帧记录
processedPawnsThisTick.Clear();
// 7. 更新冷却
if (stageTransitionCooldown > 0)
stageTransitionCooldown--;
// 8. 调试绘制
if (Props.enableDebugVisuals && DebugSettings.godMode)
DrawDebugVisuals(pawn);
}
/// <summary>
/// 计算当前速度(每秒格数)
/// </summary>
private float CalculateCurrentSpeed(Pawn pawn, int currentTick)
{
// 如果没有上次位置记录,无法计算速度
if (lastPositionTick < 0 || lastPosition == IntVec3.Invalid)
{
lastPosition = pawn.Position;
lastPositionTick = currentTick;
return 0f;
}
// 计算时间差(秒)
float timeDelta = (currentTick - lastPositionTick) / 60f;
if (timeDelta <= 0f)
return 0f;
// 计算距离(格数)
float distance = pawn.Position.DistanceTo(lastPosition);
// 计算速度(格/秒)
float speed = distance / timeDelta;
// 更新记录
lastPosition = pawn.Position;
lastPositionTick = currentTick;
return speed;
}
/// <summary>
/// 更新速度历史
/// </summary>
private void UpdateSpeedHistory(float currentSpeed)
{
speedHistory.Enqueue(currentSpeed);
while (speedHistory.Count > Props.speedHistoryFrameCount)
{
speedHistory.Dequeue();
}
}
/// <summary>
/// 获取平均速度
/// </summary>
private float GetAverageSpeed()
{
if (speedHistory.Count == 0)
return 0f;
float sum = 0f;
foreach (float speed in speedHistory)
{
sum += speed;
}
return sum / speedHistory.Count;
}
/// <summary>
/// 确定速度阶段
/// </summary>
private void DetermineSpeedStage(float averageSpeed)
{
// 如果有冷却,保持当前阶段
if (stageTransitionCooldown > 0)
return;
SpeedStage newStage;
if (averageSpeed <= Props.minSpeedForStage1)
{
newStage = SpeedStage.Stage0;
}
else if (averageSpeed >= Props.minSpeedForStage2)
{
newStage = SpeedStage.Stage2;
}
else
{
newStage = SpeedStage.Stage1;
}
// 阶段变化时设置冷却
if (newStage != currentStage)
{
currentStage = newStage;
stageTransitionCooldown = Props.stageTransitionCooldownTicks;
if (Props.enableDebugLogging)
{
Log.Message($"[HighSpeedCollision] {parent.Label} transitioned to Stage {(int)currentStage} " +
$"at speed {averageSpeed:F2} cells/sec");
}
}
}
/// <summary>
/// 应用阶段效果
/// </summary>
private void ApplyStageEffects(Pawn pawn)
{
if (currentStage == SpeedStage.Stage0)
return;
// 获取碰撞区域内的所有敌人
List<Pawn> enemiesInArea = GetEnemiesInCollisionArea(pawn);
foreach (Pawn enemy in enemiesInArea)
{
if (enemy == null || enemy.Destroyed || enemy.Dead || processedPawnsThisTick.Contains(enemy))
continue;
switch (currentStage)
{
case SpeedStage.Stage1:
ApplyStage1Effects(pawn, enemy);
break;
case SpeedStage.Stage2:
ApplyStage2Effects(pawn, enemy);
break;
}
processedPawnsThisTick.Add(enemy);
}
}
/// <summary>
/// 应用阶段1效果伤害+hediff
/// </summary>
private void ApplyStage1Effects(Pawn attacker, Pawn target)
{
// 检查目标是否已有hediff
bool alreadyHasHediff = target.health.hediffSet.HasHediff(Props.stage1Hediff);
// 如果已有hediff不造成伤害
if (alreadyHasHediff && Props.stage1HediffPreventsDamage)
return;
// 造成伤害
if (Props.stage1DamageAmount > 0f)
{
ApplyDamage(attacker, target, Props.stage1DamageAmount, Props.stage1DamageDef);
}
// 应用hediff
if (Props.stage1Hediff != null)
{
Hediff hediff = HediffMaker.MakeHediff(Props.stage1Hediff, target);
if (Props.stage1HediffDurationTicks > 0)
{
hediff.Severity = 1f;
hediff.TryGetComp<HediffComp_Disappears>()?.CompPostMake();
}
target.health.AddHediff(hediff);
}
// 播放效果
PlayStage1Effects(attacker, target);
if (Props.enableDebugLogging)
{
Log.Message($"[HighSpeedCollision] Stage1: {attacker.Label} -> {target.Label}, " +
$"Damage: {Props.stage1DamageAmount}, Hediff: {Props.stage1Hediff?.defName}");
}
}
/// <summary>
/// 应用阶段2效果伤害+击飞)
/// </summary>
private void ApplyStage2Effects(Pawn attacker, Pawn target)
{
// 造成伤害
if (Props.stage2DamageAmount > 0f)
{
ApplyDamage(attacker, target, Props.stage2DamageAmount, Props.stage2DamageDef);
}
// 执行击飞
PerformKnockback(attacker, target);
// 播放效果
PlayStage2Effects(attacker, target);
if (Props.enableDebugLogging)
{
Log.Message($"[HighSpeedCollision] Stage2: {attacker.Label} -> {target.Label}, " +
$"Damage: {Props.stage2DamageAmount}, Knockback");
}
}
/// <summary>
/// 执行击飞参考CompAbilityEffect_FanShapedStunKnockback
/// </summary>
private void PerformKnockback(Pawn attacker, Pawn target)
{
if (target == null || target.Destroyed || target.Dead || attacker.Map == null)
return;
// 计算击飞方向(从攻击者指向目标)
IntVec3 knockbackDirection = CalculateKnockbackDirection(attacker, target.Position);
// 寻找击飞位置
IntVec3 knockbackDestination = FindKnockbackDestination(attacker, target, knockbackDirection);
// 如果找到有效位置,执行击飞
if (knockbackDestination.IsValid && knockbackDestination != target.Position)
{
CreateKnockbackFlyer(attacker, target, knockbackDestination);
}
}
/// <summary>
/// 计算击飞方向
/// </summary>
private IntVec3 CalculateKnockbackDirection(Pawn attacker, IntVec3 targetPosition)
{
IntVec3 direction = targetPosition - attacker.Position;
// 标准化方向
if (direction.x != 0 || direction.z != 0)
{
if (Mathf.Abs(direction.x) > Mathf.Abs(direction.z))
{
return new IntVec3(Mathf.Sign(direction.x) > 0 ? 1 : -1, 0, 0);
}
else
{
return new IntVec3(0, 0, Mathf.Sign(direction.z) > 0 ? 1 : -1);
}
}
// 如果攻击者和目标在同一位置,使用随机方向
return new IntVec3(Rand.Value > 0.5f ? 1 : -1, 0, 0);
}
/// <summary>
/// 寻找击飞位置
/// </summary>
private IntVec3 FindKnockbackDestination(Pawn attacker, Pawn target, IntVec3 direction)
{
Map map = attacker.Map;
IntVec3 currentPos = target.Position;
// 从最大距离开始向回找
for (int distance = Props.stage2KnockbackDistance; distance >= 1; distance--)
{
IntVec3 testPos = currentPos + (direction * distance);
if (!IsValidKnockbackDestination(testPos, map, target, attacker))
continue;
return testPos;
}
return currentPos;
}
/// <summary>
/// 检查击飞位置是否有效
/// </summary>
private bool IsValidKnockbackDestination(IntVec3 destination, Map map, Pawn victim, Pawn attacker)
{
if (!destination.IsValid || !destination.InBounds(map))
return false;
if (!destination.Standable(map))
return false;
// 检查是否有其他pawn
Pawn existingPawn = destination.GetFirstPawn(map);
if (existingPawn != null && existingPawn != victim)
return false;
// 检查视线
if (Props.requireLineOfSightForKnockback && !GenSight.LineOfSight(victim.Position, destination, map))
return false;
return true;
}
/// <summary>
/// 创建击飞飞行器
/// </summary>
private void CreateKnockbackFlyer(Pawn attacker, Pawn target, IntVec3 destination)
{
try
{
Map map = attacker.Map;
// 使用自定义飞行器或默认飞行器
ThingDef flyerDef = Props.knockbackFlyerDef ?? ThingDefOf.PawnFlyer;
// 创建飞行器
PawnFlyer flyer = PawnFlyer.MakeFlyer(
flyerDef,
target,
destination,
Props.flightEffecterDef,
Props.landingSound,
false,
null,
null,
new LocalTargetInfo(destination)
);
if (flyer != null)
{
GenSpawn.Spawn(flyer, destination, map);
}
}
catch (Exception ex)
{
Log.Error($"[HighSpeedCollision] Exception creating PawnFlyer: {ex}");
}
}
/// <summary>
/// 应用伤害
/// </summary>
private void ApplyDamage(Pawn attacker, Pawn target, float amount, DamageDef damageDef)
{
if (amount <= 0f || damageDef == null)
return;
DamageInfo damageInfo = new DamageInfo(
damageDef,
amount,
Props.armorPenetration,
-1f,
attacker,
null
);
target.TakeDamage(damageInfo);
}
/// <summary>
/// 播放阶段1效果
/// </summary>
private void PlayStage1Effects(Pawn attacker, Pawn target)
{
if (Props.stage1Effecter != null && attacker.Map != null)
{
Effecter effect = Props.stage1Effecter.Spawn();
effect.Trigger(new TargetInfo(attacker.Position, attacker.Map),
new TargetInfo(target.Position, attacker.Map));
effect.Cleanup();
}
if (Props.stage1Sound != null && attacker.Map != null)
{
Props.stage1Sound.PlayOneShot(new TargetInfo(target.Position, attacker.Map));
}
}
/// <summary>
/// 播放阶段2效果
/// </summary>
private void PlayStage2Effects(Pawn attacker, Pawn target)
{
if (Props.stage2Effecter != null && attacker.Map != null)
{
Effecter effect = Props.stage2Effecter.Spawn();
effect.Trigger(new TargetInfo(attacker.Position, attacker.Map),
new TargetInfo(target.Position, attacker.Map));
effect.Cleanup();
}
if (Props.stage2Sound != null && attacker.Map != null)
{
Props.stage2Sound.PlayOneShot(new TargetInfo(target.Position, attacker.Map));
}
}
/// <summary>
/// 获取碰撞区域内的所有敌人
/// </summary>
private List<Pawn> GetEnemiesInCollisionArea(Pawn pawn)
{
List<Pawn> enemies = new List<Pawn>();
// 获取碰撞区域
CellRect collisionArea = GetCollisionArea(pawn);
// 检查区域内的每个单元格
foreach (IntVec3 cell in collisionArea)
{
if (!cell.InBounds(pawn.Map))
continue;
// 获取单元格内的所有pawn
List<Thing> things = cell.GetThingList(pawn.Map);
foreach (Thing thing in things)
{
if (thing is Pawn otherPawn && otherPawn != pawn)
{
// 检查是否为敌人
if (IsValidTarget(pawn, otherPawn))
{
enemies.Add(otherPawn);
}
}
}
}
return enemies;
}
/// <summary>
/// 获取碰撞区域
/// </summary>
private CellRect GetCollisionArea(Pawn pawn)
{
int currentTick = Find.TickManager.TicksGame;
// 每10帧重新计算一次区域或当位置变化时
if (currentTick - lastAreaRecalculationTick > 10 ||
pawn.Position != collisionAreaCache.CenterCell)
{
int radius = Props.collisionAreaRadius;
IntVec3 center = pawn.Position;
collisionAreaCache = new CellRect(
center.x - radius,
center.z - radius,
radius * 2 + 1,
radius * 2 + 1
);
collisionAreaCache.ClipInsideMap(pawn.Map);
lastAreaRecalculationTick = currentTick;
}
return collisionAreaCache;
}
/// <summary>
/// 检查是否是有效目标
/// </summary>
private bool IsValidTarget(Pawn attacker, Pawn target)
{
if (target == null || target.Destroyed || target.Dead)
return false;
// 检查是否为敌人
if (Props.onlyAffectEnemies && !target.HostileTo(attacker))
return false;
// 检查是否排除友方
if (Props.excludeAlliedPawns && target.Faction == attacker.Faction)
return false;
// 检查是否排除中立
if (Props.excludeNeutralPawns && !target.HostileTo(attacker))
return false;
return true;
}
/// <summary>
/// 检查Pawn是否可以移动
/// </summary>
private bool CanMove(Pawn pawn)
{
if (pawn.Downed || pawn.Dead || pawn.InMentalState)
return false;
if (pawn.stances?.stunner?.Stunned ?? false)
return false;
return true;
}
/// <summary>
/// 重置到阶段0
/// </summary>
private void ResetToStage0()
{
currentStage = SpeedStage.Stage0;
// 清空速度历史
speedHistory.Clear();
for (int i = 0; i < Props.speedHistoryFrameCount; i++)
{
speedHistory.Enqueue(0f);
}
}
/// <summary>
/// 绘制调试视觉效果
/// </summary>
private void DrawDebugVisuals(Pawn pawn)
{
if (!pawn.Spawned)
return;
// 绘制碰撞区域
CellRect area = GetCollisionArea(pawn);
GenDraw.DrawFieldEdges(area.Cells.ToList(), GetStageColor(currentStage));
// 绘制速度指示器
float averageSpeed = GetAverageSpeed();
string speedText = $"Speed: {averageSpeed:F1} cells/sec\nStage: {(int)currentStage}";
Vector3 drawPos = pawn.DrawPos + new Vector3(0, 0, 1f);
GenMapUI.DrawText(drawPos, speedText, GetStageColor(currentStage));
}
/// <summary>
/// 获取阶段颜色
/// </summary>
private Color GetStageColor(SpeedStage stage)
{
switch (stage)
{
case SpeedStage.Stage0: return Color.gray;
case SpeedStage.Stage1: return Color.yellow;
case SpeedStage.Stage2: return Color.red;
default: return Color.white;
}
}
/// <summary>
/// 获取调试信息
/// </summary>
public string GetDebugInfo()
{
float averageSpeed = GetAverageSpeed();
return $"HighSpeedCollision Debug Info:\n" +
$"Current Stage: {(int)currentStage}\n" +
$"Average Speed: {averageSpeed:F2} cells/sec\n" +
$"Stage 1 Threshold: {Props.minSpeedForStage1:F2}\n" +
$"Stage 2 Threshold: {Props.minSpeedForStage2:F2}\n" +
$"Speed History: {speedHistory.Count} frames\n" +
$"Stage Cooldown: {stageTransitionCooldown} ticks";
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref currentStage, "currentStage", SpeedStage.Stage0);
Scribe_Values.Look(ref stageTransitionCooldown, "stageTransitionCooldown", 0);
Scribe_Values.Look(ref lastPosition, "lastPosition", IntVec3.Invalid);
Scribe_Values.Look(ref lastPositionTick, "lastPositionTick", -1);
// 保存速度历史
if (Scribe.mode == LoadSaveMode.Saving)
{
List<float> speedList = speedHistory.ToList();
Scribe_Collections.Look(ref speedList, "speedHistory", LookMode.Value);
}
else if (Scribe.mode == LoadSaveMode.LoadingVars)
{
List<float> speedList = null;
Scribe_Collections.Look(ref speedList, "speedHistory", LookMode.Value);
speedHistory.Clear();
if (speedList != null)
{
foreach (float speed in speedList)
{
speedHistory.Enqueue(speed);
}
}
// 确保有足够的历史数据
while (speedHistory.Count < Props.speedHistoryFrameCount)
{
speedHistory.Enqueue(0f);
}
}
}
}
/// <summary>
/// 高速移动撞击组件属性
/// </summary>
public class CompProperties_HighSpeedCollision : CompProperties
{
// === 速度阈值配置 ===
/// <summary>
/// 进入阶段1所需的最小速度格/秒)
/// </summary>
public float minSpeedForStage1 = 3f;
/// <summary>
/// 进入阶段2所需的最小速度格/秒)
/// </summary>
public float minSpeedForStage2 = 6f;
/// <summary>
/// 速度历史帧数(用于计算平均速度)
/// </summary>
public int speedHistoryFrameCount = 10;
/// <summary>
/// 阶段转换冷却时间tick
/// </summary>
public int stageTransitionCooldownTicks = 5;
// === 碰撞区域配置 ===
/// <summary>
/// 碰撞区域半径以pawn为中心的正方形
/// </summary>
public int collisionAreaRadius = 1;
// === 目标过滤 ===
/// <summary>
/// 只影响敌人
/// </summary>
public bool onlyAffectEnemies = true;
/// <summary>
/// 排除友方单位
/// </summary>
public bool excludeAlliedPawns = true;
/// <summary>
/// 排除中立单位
/// </summary>
public bool excludeNeutralPawns = false;
// === 阶段1效果配置 ===
/// <summary>
/// 阶段1伤害类型
/// </summary>
public DamageDef stage1DamageDef = DamageDefOf.Blunt;
/// <summary>
/// 阶段1伤害量
/// </summary>
public float stage1DamageAmount = 5f;
/// <summary>
/// 阶段1护甲穿透
/// </summary>
public float armorPenetration = 0f;
/// <summary>
/// 阶段1应用的hediff
/// </summary>
public HediffDef stage1Hediff;
/// <summary>
/// 阶段1hediff持续时间tick
/// </summary>
public int stage1HediffDurationTicks = 60;
/// <summary>
/// 拥有hediff的目标是否免疫伤害
/// </summary>
public bool stage1HediffPreventsDamage = true;
/// <summary>
/// 阶段1效果器
/// </summary>
public EffecterDef stage1Effecter;
/// <summary>
/// 阶段1音效
/// </summary>
public SoundDef stage1Sound;
// === 阶段2效果配置 ===
/// <summary>
/// 阶段2伤害类型
/// </summary>
public DamageDef stage2DamageDef = DamageDefOf.Blunt;
/// <summary>
/// 阶段2伤害量
/// </summary>
public float stage2DamageAmount = 10f;
/// <summary>
/// 阶段2击退距离
/// </summary>
public int stage2KnockbackDistance = 3;
/// <summary>
/// 击退是否需要视线
/// </summary>
public bool requireLineOfSightForKnockback = true;
/// <summary>
/// 阶段2效果器
/// </summary>
public EffecterDef stage2Effecter;
/// <summary>
/// 阶段2音效
/// </summary>
public SoundDef stage2Sound;
// === 击飞配置 ===
/// <summary>
/// 击退飞行器定义
/// </summary>
public ThingDef knockbackFlyerDef;
/// <summary>
/// 飞行效果器
/// </summary>
public EffecterDef flightEffecterDef;
/// <summary>
/// 落地音效
/// </summary>
public SoundDef landingSound;
// === 调试配置 ===
/// <summary>
/// 启用调试日志
/// </summary>
public bool enableDebugLogging = false;
/// <summary>
/// 启用调试视觉效果
/// </summary>
public bool enableDebugVisuals = false;
/// <summary>
/// 绘制速度历史图
/// </summary>
public bool debugDrawSpeedHistory = false;
public CompProperties_HighSpeedCollision()
{
compClass = typeof(CompHighSpeedCollision);
}
}
}

View File

@@ -1,199 +0,0 @@
// File: CompMechArmor.cs
using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.Sound;
using static RimWorld.MechClusterSketch;
namespace WulaFallenEmpire
{
/// <summary>
/// 机甲装甲组件:提供基于装甲值的伤害减免系统
/// </summary>
public class CompMechArmor : ThingComp
{
public CompProperties_MechArmor Props =>
(CompProperties_MechArmor)props;
private static StatDef DD_MechArmorDef = null;
// 当前装甲值从Stat获取
public float CurrentArmor
{
get
{
if (DD_MechArmorDef == null)
{
DD_MechArmorDef = StatDef.Named("DD_MechArmor");
if (DD_MechArmorDef == null)
{
Log.Warning("[DD] DD_MechArmor stat definition not found!");
return 0f;
}
}
return parent.GetStatValue(DD_MechArmorDef);
}
}
// 调试信息
private int blockedHits = 0;
private int totalHits = 0;
/// <summary>
/// 检查伤害是否被装甲抵消
/// </summary>
/// <param name="dinfo">伤害信息</param>
/// <returns>true=伤害被抵消false=伤害有效</returns>
public bool TryBlockDamage(ref DamageInfo dinfo)
{
totalHits++;
// 获取穿甲率如果没有则为0
float armorPenetration = dinfo.ArmorPenetrationInt;
// 计算穿甲伤害
float armorPiercingDamage = dinfo.Amount * armorPenetration;
// 获取当前装甲值
float currentArmor = CurrentArmor;
// 检查是否应该被装甲抵消
bool shouldBlock = armorPiercingDamage < currentArmor;
if (shouldBlock)
{
blockedHits++;
// 可选:触发视觉效果
if (Props.showBlockEffect && parent.Spawned)
{
ShowBlockEffect(dinfo);
}
// 可选:播放音效
if (Props.soundOnBlock != null && parent.Spawned)
{
Props.soundOnBlock.PlayOneShot(parent);
}
}
// 调试日志
if (Props.debugLogging && parent.Spawned)
{
Log.Message($"[DD Armor] {parent.LabelShort}: " +
$"Damage={dinfo.Amount}, " +
$"Penetration={armorPenetration:P0}, " +
$"PierceDamage={armorPiercingDamage:F1}, " +
$"Armor={currentArmor:F1}, " +
$"Blocked={shouldBlock}");
}
return shouldBlock;
}
/// <summary>
/// 显示阻挡效果
/// </summary>
private void ShowBlockEffect(DamageInfo dinfo)
{
MoteMaker.ThrowText(parent.DrawPos, parent.Map, "DD_BlockByMechArmor".Translate(), Color.white, 3.5f);
// 创建火花或特效
if (Props.blockEffectMote != null)
{
MoteMaker.MakeStaticMote(
parent.DrawPos + new Vector3(0, 0, 0.5f),
parent.Map,
Props.blockEffectMote,
1.0f
);
}
}
/// <summary>
/// 获取装甲信息(用于调试)
/// </summary>
public string GetArmorInfo()
{
return $"<b>{parent.LabelShort}的装甲系统</b>\n" +
$"当前装甲值: {CurrentArmor:F1}\n" +
$"阻挡规则: 穿甲伤害 < 装甲值\n" +
$"统计: 已阻挡 {blockedHits}/{totalHits} 次攻击\n" +
$"阻挡率: {(totalHits > 0 ? (float)blockedHits / totalHits * 100 : 0):F1}%";
}
/// <summary>
/// 获取调试按钮
/// </summary>
public override IEnumerable<Gizmo> CompGetGizmosExtra()
{
foreach (var gizmo in base.CompGetGizmosExtra())
{
yield return gizmo;
}
// 在开发模式下显示装甲信息
if (DebugSettings.ShowDevGizmos)
{
yield return new Command_Action
{
defaultLabel = "DEBUG: 装甲信息",
defaultDesc = GetArmorInfo(),
//icon = TexCommand.Shield,
action = () =>
{
Find.WindowStack.Add(new Dialog_MessageBox(
GetArmorInfo(),
"关闭",
null,
null,
null,
"机甲装甲信息"
));
}
};
yield return new Command_Action
{
defaultLabel = "DEBUG: 重置统计",
defaultDesc = "重置阻挡统计计数器",
//icon = TexCommand.Clear,
action = () =>
{
blockedHits = 0;
totalHits = 0;
Messages.Message("装甲统计已重置", MessageTypeDefOf.TaskCompletion);
}
};
}
}
public override void PostExposeData()
{
base.PostExposeData();
Scribe_Values.Look(ref blockedHits, "blockedHits", 0);
Scribe_Values.Look(ref totalHits, "totalHits", 0);
}
}
/// <summary>
/// 机甲装甲组件属性
/// </summary>
public class CompProperties_MechArmor : CompProperties
{
// 视觉效果
public bool showBlockEffect = true;
public ThingDef blockEffectMote; // 阻挡时显示的特效
// 音效
public SoundDef soundOnBlock;
// 调试
public bool debugLogging = false;
public CompProperties_MechArmor()
{
compClass = typeof(CompMechArmor);
}
}
}

View File

@@ -26,7 +26,7 @@ namespace WulaFallenEmpire
public ThingDef FuelType => Props.fuelType;
// 停机状态 Hediff
private HediffDef ShutdownHediffDef => HediffDef.Named("DD_MechShutdown");
private HediffDef ShutdownHediffDef => HediffDef.Named("WULA_MechShutdown");
public override void PostSpawnSetup(bool respawningAfterLoad)
{
@@ -162,7 +162,7 @@ namespace WulaFallenEmpire
}
// 播放关机效果
MoteMaker.ThrowText(mech.DrawPos, mech.Map, "DD_Shutdown".Translate(), Color.gray, 3.5f);
MoteMaker.ThrowText(mech.DrawPos, mech.Map, "WULA_Shutdown".Translate(), Color.gray, 3.5f);
}
}
@@ -186,7 +186,7 @@ namespace WulaFallenEmpire
}
}
MoteMaker.ThrowText(mech.DrawPos, mech.Map, "DD_Startup".Translate(), Color.green, 3.5f);
MoteMaker.ThrowText(mech.DrawPos, mech.Map, "WULA_Startup".Translate(), Color.green, 3.5f);
}
}
@@ -236,7 +236,7 @@ namespace WulaFallenEmpire
// 发送调试消息
if (DebugSettings.godMode)
{
Messages.Message($"DD_Debug_FuelSet".Translate(
Messages.Message($"WULA_Debug_FuelSet".Translate(
parent.LabelShort,
fuel.ToString("F1"),
Props.fuelCapacity.ToString("F1"),
@@ -267,7 +267,7 @@ namespace WulaFallenEmpire
if (bestColonist == null)
{
Messages.Message("DD_NoColonistAvailable".Translate(), parent, MessageTypeDefOf.RejectInput);
Messages.Message("WULA_NoColonistAvailable".Translate(), parent, MessageTypeDefOf.RejectInput);
return;
}
@@ -275,7 +275,7 @@ namespace WulaFallenEmpire
Thing fuel = FindFuelForRefuel(bestColonist);
if (fuel == null)
{
Messages.Message("DD_NoFuelAvailable".Translate(FuelType), parent, MessageTypeDefOf.RejectInput);
Messages.Message("WULA_NoFuelAvailable".Translate(FuelType), parent, MessageTypeDefOf.RejectInput);
return;
}
@@ -287,7 +287,7 @@ namespace WulaFallenEmpire
bestColonist.jobs.StartJob(job, JobCondition.InterruptForced, null, resumeCurJobAfterwards: true);
// 显示消息
Messages.Message("DD_OrderedRefuel".Translate(bestColonist.LabelShort, parent.LabelShort),
Messages.Message("WULA_OrderedRefuel".Translate(bestColonist.LabelShort, parent.LabelShort),
parent, MessageTypeDefOf.PositiveEvent);
}
@@ -370,16 +370,16 @@ namespace WulaFallenEmpire
{
Command_Action refuelNow = new Command_Action
{
defaultLabel = "DD_RefuelNow".Translate(),
defaultDesc = "DD_RefuelNowDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("WulaFallenEmpire/UI/Commands/DD_Refuel_Mech"),
defaultLabel = "WULA_RefuelNow".Translate(),
defaultDesc = "WULA_RefuelNowDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("WulaFallenEmpire/UI/Commands/WULA_Refuel_Mech"),
action = () => RefuelNow()
};
// 检查是否可以立刻加注
if (!CanRefuelNow())
{
refuelNow.Disable("DD_CannotRefuelNow".Translate());
refuelNow.Disable("WULA_CannotRefuelNow".Translate());
}
yield return refuelNow;
@@ -391,8 +391,8 @@ namespace WulaFallenEmpire
// 设置燃料为空
Command_Action setEmpty = new Command_Action
{
defaultLabel = "DD_Debug_SetEmpty".Translate(),
defaultDesc = "DD_Debug_SetEmptyDesc".Translate(),
defaultLabel = "WULA_Debug_SetEmpty".Translate(),
defaultDesc = "WULA_Debug_SetEmptyDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/SetEmpty", false) ?? BaseContent.BadTex,
action = () => SetFuel(0f)
};
@@ -401,8 +401,8 @@ namespace WulaFallenEmpire
// 设置燃料为50%
Command_Action setHalf = new Command_Action
{
defaultLabel = "DD_Debug_SetHalf".Translate(),
defaultDesc = "DD_Debug_SetHalfDesc".Translate(),
defaultLabel = "WULA_Debug_SetHalf".Translate(),
defaultDesc = "WULA_Debug_SetHalfDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/SetHalf", false) ?? BaseContent.BadTex,
action = () => SetFuel(Props.fuelCapacity * 0.5f)
};
@@ -411,8 +411,8 @@ namespace WulaFallenEmpire
// 设置燃料为满
Command_Action setFull = new Command_Action
{
defaultLabel = "DD_Debug_SetFull".Translate(),
defaultDesc = "DD_Debug_SetFullDesc".Translate(),
defaultLabel = "WULA_Debug_SetFull".Translate(),
defaultDesc = "WULA_Debug_SetFullDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/SetFull", false) ?? BaseContent.BadTex,
action = () => SetFuel(Props.fuelCapacity)
};
@@ -433,7 +433,7 @@ namespace WulaFallenEmpire
{
string baseString = base.CompInspectStringExtra();
string fuelString = "DD_Fuel".Translate(FuelType) + ": " +
string fuelString = "WULA_Fuel".Translate(FuelType) + ": " +
fuel.ToString("F1") + " / " + Props.fuelCapacity.ToString("F1") +
" (" + (FuelPercent * 100f).ToString("F0") + "%)";

View File

@@ -49,8 +49,8 @@ namespace WulaFallenEmpire
// 在 God Mode 下显示"调试模式"标题
string title = DebugSettings.godMode ?
"DD_MechFuel".Translate().Resolve() + " [DEBUG]" :
"DD_MechFuel".Translate().Resolve();
"WULA_MechFuel".Translate().Resolve() + " [DEBUG]" :
"WULA_MechFuel".Translate().Resolve();
Widgets.Label(titleRect, title);
@@ -88,13 +88,13 @@ namespace WulaFallenEmpire
Text.Font = GameFont.Tiny;
Text.Anchor = TextAnchor.UpperCenter;
GUI.color = Color.red;
Widgets.Label(statusRect, "DD_Shutdown".Translate());
Widgets.Label(statusRect, "WULA_Shutdown".Translate());
GUI.color = Color.white;
Text.Anchor = TextAnchor.UpperLeft;
}
// 工具提示
string tip = "DD_MechFuelTip".Translate(
string tip = "WULA_MechFuelTip".Translate(
fuelComp.FuelPercent.ToStringPercent(),
fuelComp.Props.dailyFuelConsumption,
fuelComp.Props.fuelType.label
@@ -102,20 +102,20 @@ namespace WulaFallenEmpire
if (fuelComp.IsShutdown)
{
tip += "\n\n" + "DD_ShutdownTip".Translate();
tip += "\n\n" + "WULA_ShutdownTip".Translate();
}
else if (fuelComp.NeedsRefueling)
{
tip += "\n\n" + "DD_NeedsRefueling".Translate();
tip += "\n\n" + "WULA_NeedsRefueling".Translate();
}
// 在 God Mode 下添加调试信息到工具提示
if (DebugSettings.godMode)
{
tip += "\n\n" + "DD_Debug_Tip".Translate().Colorize(Color.gray) +
"\n" + "DD_Debug_Status".Translate(
fuelComp.IsShutdown ? "DD_Shutdown".Translate() : "DD_Running".Translate(),
fuelComp.HasPilot() ? "DD_HasPilot".Translate() : "DD_NoPilot".Translate()
tip += "\n\n" + "WULA_Debug_Tip".Translate().Colorize(Color.gray) +
"\n" + "WULA_Debug_Status".Translate(
fuelComp.IsShutdown ? "WULA_Shutdown".Translate() : "WULA_Running".Translate(),
fuelComp.HasPilot() ? "WULA_HasPilot".Translate() : "WULA_NoPilot".Translate()
);
}

View File

@@ -244,7 +244,7 @@ namespace WulaFallenEmpire
}
else
{
Log.Warning($"[DD] Failed to create sustainer for {Props.movementSound.defName}");
Log.Warning($"[WULA] Failed to create sustainer for {Props.movementSound.defName}");
isPlaying = false;
}
}

View File

@@ -59,7 +59,7 @@ namespace WulaFallenEmpire
// 如果需要驾驶员,检查是否配置了驾驶员容器
if (requirePilot && parentDef.GetCompProperties<CompProperties_MechPilotHolder>() == null)
{
Log.Warning($"[DD] requirePilot is true but no CompProperties_MechPilotHolder found for {parentDef.defName}");
Log.Warning($"[WULA] requirePilot is true but no CompProperties_MechPilotHolder found for {parentDef.defName}");
}
}
}

View File

@@ -16,8 +16,8 @@ namespace WulaFallenEmpire
public string pilotWorkTag = "MechPilot";
// 新增:驾驶员图标配置
public string summonPilotIcon = "WulaFallenEmpire/UI/Commands/DD_Enter_Mech";
public string ejectPilotIcon = "WulaFallenEmpire/UI/Commands/DD_Exit_Mech";
public string summonPilotIcon = "WulaFallenEmpire/UI/Commands/WULA_Enter_Mech";
public string ejectPilotIcon = "WulaFallenEmpire/UI/Commands/WULA_Exit_Mech";
public float ejectPilotHealthPercentThreshold = 0.1f; // 默认30%血量
public bool allowEntryBelowThreshold = false; // 血量低于阈值时是否允许进入
@@ -204,7 +204,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] 同步Hediff时出错: {ex}");
Log.Error($"[WULA] 同步Hediff时出错: {ex}");
}
}
@@ -231,7 +231,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] 取消同步Hediff时出错: {ex}");
Log.Error($"[WULA] 取消同步Hediff时出错: {ex}");
}
}
@@ -273,7 +273,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] 自动添加Hediff时出错: {ex}");
Log.Error($"[WULA] 自动添加Hediff时出错: {ex}");
}
}
@@ -333,7 +333,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] CompTick error: {ex}");
Log.Error($"[WULA] CompTick error: {ex}");
}
}
@@ -384,7 +384,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] 检查Hediff同步状态时出错: {ex}");
Log.Error($"[WULA] 检查Hediff同步状态时出错: {ex}");
}
}
@@ -395,7 +395,7 @@ namespace WulaFallenEmpire
if (!(parent is Wulamechunit))
{
Log.Warning($"[DD] CompMechPilotHolder attached to non-mech: {parent}");
Log.Warning($"[WULA] CompMechPilotHolder attached to non-mech: {parent}");
}
// 确保加载后恢复状态
@@ -567,7 +567,7 @@ namespace WulaFallenEmpire
// 发送消息
if (parent.Faction == Faction.OfPlayer)
{
Messages.Message("DD_PilotsEjectedDueToLowHealth".Translate(parent.LabelShort,
Messages.Message("WULA_PilotsEjectedDueToLowHealth".Translate(parent.LabelShort,
(Props.ejectPilotHealthPercentThreshold * 100).ToString("F0")),
parent, MessageTypeDefOf.NegativeEvent);
}
@@ -605,8 +605,8 @@ namespace WulaFallenEmpire
{
Command_Action summonCommand = new Command_Action
{
defaultLabel = "DD_SummonPilot".Translate(),
defaultDesc = "DD_SummonPilotDesc".Translate(),
defaultLabel = "WULA_SummonPilot".Translate(),
defaultDesc = "WULA_SummonPilotDesc".Translate(),
icon = Props.GetSummonPilotIcon(),
action = () =>
{
@@ -618,7 +618,7 @@ namespace WulaFallenEmpire
// 如果血量低于阈值且不允许进入,禁用按钮
if (!Props.allowEntryBelowThreshold && IsBelowHealthThreshold)
{
summonCommand.Disable("DD_MechTooDamagedForEntry".Translate());
summonCommand.Disable("WULA_MechTooDamagedForEntry".Translate());
}
yield return summonCommand;
@@ -629,8 +629,8 @@ namespace WulaFallenEmpire
{
yield return new Command_Action
{
defaultLabel = "DD_EjectAllPilots".Translate(),
defaultDesc = "DD_EjectAllPilotsDesc".Translate(),
defaultLabel = "WULA_EjectAllPilots".Translate(),
defaultDesc = "WULA_EjectAllPilotsDesc".Translate(),
icon = Props.GetEjectPilotIcon(),
action = () =>
{
@@ -723,14 +723,14 @@ namespace WulaFallenEmpire
}
else
{
Log.Error($"[DD] 无法弹出驾驶员: {pawn.LabelShort}");
Log.Error($"[WULA] 无法弹出驾驶员: {pawn.LabelShort}");
}
}
}
}
catch (Exception ex)
{
Log.Error($"[DD] 弹出驾驶员时发生错误: {ex}");
Log.Error($"[WULA] 弹出驾驶员时发生错误: {ex}");
}
finally
{
@@ -778,7 +778,7 @@ namespace WulaFallenEmpire
Map map = parent.Map;
if (map == null)
{
Log.Error($"[DD] 尝试在没有地图的情况下生成驾驶员: {pawn.LabelShort}");
Log.Error($"[WULA] 尝试在没有地图的情况下生成驾驶员: {pawn.LabelShort}");
return false;
}
@@ -810,7 +810,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] 生成驾驶员时发生错误: {ex}");
Log.Error($"[WULA] 生成驾驶员时发生错误: {ex}");
return false;
}
}
@@ -841,7 +841,7 @@ namespace WulaFallenEmpire
{
if (pilot.Faction == Faction.OfPlayer)
{
Messages.Message("DD_PilotEnteredMech".Translate(pilot.LabelShort, parent.LabelShort),
Messages.Message("WULA_PilotEnteredMech".Translate(pilot.LabelShort, parent.LabelShort),
parent, MessageTypeDefOf.PositiveEvent);
}
}
@@ -850,7 +850,7 @@ namespace WulaFallenEmpire
{
if (pilot.Faction == Faction.OfPlayer)
{
Messages.Message("DD_PilotExitedMech".Translate(pilot.LabelShort, parent.LabelShort),
Messages.Message("WULA_PilotExitedMech".Translate(pilot.LabelShort, parent.LabelShort),
parent, MessageTypeDefOf.NeutralEvent);
}
}
@@ -899,7 +899,7 @@ namespace WulaFallenEmpire
// 为能够行动的殖民者创建选项
if (ableColonists.Count == 0 && disabledColonists.Count == 0)
{
options.Add(new FloatMenuOption("DD_NoAvailablePilots".Translate(), null));
options.Add(new FloatMenuOption("WULA_NoAvailablePilots".Translate(), null));
}
else
{
@@ -930,7 +930,7 @@ namespace WulaFallenEmpire
// 无法行动的殖民者:需要搬运
foreach (var colonist in disabledColonists)
{
string colonistLabel = colonist.LabelShortCap + " " + "DD_DisabledColonistRequiresCarry".Translate();
string colonistLabel = colonist.LabelShortCap + " " + "WULA_DisabledColonistRequiresCarry".Translate();
Action action = () => OrderCarryDisabledColonistToMech(colonist);
FloatMenuOption option = new FloatMenuOption(
@@ -977,7 +977,7 @@ namespace WulaFallenEmpire
if (carrier == null)
{
Messages.Message("DD_NoAvailableCarrier".Translate(disabledColonist.LabelShortCap),
Messages.Message("WULA_NoAvailableCarrier".Translate(disabledColonist.LabelShortCap),
parent, MessageTypeDefOf.RejectInput);
return;
}
@@ -986,7 +986,7 @@ namespace WulaFallenEmpire
Job job = JobMaker.MakeJob(Wula_JobDefOf.WULA_CarryToMech, disabledColonist, mech);
carrier.jobs.TryTakeOrderedJob(job, JobTag.Misc);
Messages.Message("DD_CarrierAssigned".Translate(carrier.LabelShortCap, disabledColonist.LabelShortCap),
Messages.Message("WULA_CarrierAssigned".Translate(carrier.LabelShortCap, disabledColonist.LabelShortCap),
parent, MessageTypeDefOf.PositiveEvent);
}

View File

@@ -84,16 +84,16 @@ namespace WulaFallenEmpire
{
Command_Action repairCommand = new Command_Action
{
defaultLabel = "DD_ForceRepair".Translate(),
defaultDesc = "DD_ForceRepairDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("WulaFallenEmpire/UI/Commands/DD_Repair_Mech"),
defaultLabel = "WULA_ForceRepair".Translate(),
defaultDesc = "WULA_ForceRepairDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("WulaFallenEmpire/UI/Commands/WULA_Repair_Mech"),
action = () => ForceRepairNow()
};
// 检查是否可以立即维修
if (!CanRepairNow())
{
repairCommand.Disable("DD_CannotRepairNow".Translate());
repairCommand.Disable("WULA_CannotRepairNow".Translate());
}
yield return repairCommand;
@@ -105,8 +105,8 @@ namespace WulaFallenEmpire
// 模拟受伤按钮
Command_Action damageCommand = new Command_Action
{
defaultLabel = "DD_Debug_Damage".Translate(),
defaultDesc = "DD_Debug_DamageDesc".Translate(),
defaultLabel = "WULA_Debug_Damage".Translate(),
defaultDesc = "WULA_Debug_DamageDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Damage", false) ?? BaseContent.BadTex,
action = () => DebugDamage()
};
@@ -115,8 +115,8 @@ namespace WulaFallenEmpire
// 完全修复按钮
Command_Action fullRepairCommand = new Command_Action
{
defaultLabel = "DD_Debug_FullRepair".Translate(),
defaultDesc = "DD_Debug_FullRepairDesc".Translate(),
defaultLabel = "WULA_Debug_FullRepair".Translate(),
defaultDesc = "WULA_Debug_FullRepairDesc".Translate(),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Repair", false) ?? BaseContent.BadTex,
action = () => DebugFullRepair()
};
@@ -125,8 +125,8 @@ namespace WulaFallenEmpire
// 显示维修统计
Command_Action statsCommand = new Command_Action
{
defaultLabel = "DD_Debug_RepairStats".Translate(),
defaultDesc = "DD_Debug_RepairStatsDesc".Translate(totalRepairedHP.ToString("F1")),
defaultLabel = "WULA_Debug_RepairStats".Translate(),
defaultDesc = "WULA_Debug_RepairStatsDesc".Translate(totalRepairedHP.ToString("F1")),
icon = ContentFinder<Texture2D>.Get("UI/Commands/Stats", false) ?? BaseContent.BadTex,
action = () => DebugShowStats()
};
@@ -145,7 +145,7 @@ namespace WulaFallenEmpire
if (bestColonist == null)
{
Messages.Message("DD_NoColonistAvailable".Translate(), parent, MessageTypeDefOf.RejectInput);
Messages.Message("WULA_NoColonistAvailable".Translate(), parent, MessageTypeDefOf.RejectInput);
return;
}
@@ -156,7 +156,7 @@ namespace WulaFallenEmpire
bestColonist.jobs.StartJob(job, JobCondition.InterruptForced, null, resumeCurJobAfterwards: true);
// 显示消息
Messages.Message("DD_OrderedRepair".Translate(bestColonist.LabelShort, parent.LabelShort),
Messages.Message("WULA_OrderedRepair".Translate(bestColonist.LabelShort, parent.LabelShort),
parent, MessageTypeDefOf.PositiveEvent);
}
@@ -221,7 +221,7 @@ namespace WulaFallenEmpire
DamageInfo dinfo = new DamageInfo(DamageDefOf.Cut, damage, 1f, -1f, null, part);
mech.TakeDamage(dinfo);
Messages.Message($"DD_Debug_Damaged".Translate(parent.LabelShort, damage.ToString("F1")),
Messages.Message($"WULA_Debug_Damaged".Translate(parent.LabelShort, damage.ToString("F1")),
parent, MessageTypeDefOf.NeutralEvent);
}
@@ -246,14 +246,14 @@ namespace WulaFallenEmpire
}
}
Messages.Message($"DD_Debug_FullyRepaired".Translate(parent.LabelShort),
Messages.Message($"WULA_Debug_FullyRepaired".Translate(parent.LabelShort),
parent, MessageTypeDefOf.PositiveEvent);
}
// 调试功能:显示维修统计
private void DebugShowStats()
{
Messages.Message($"DD_Debug_RepairStatsInfo".Translate(
Messages.Message($"WULA_Debug_RepairStatsInfo".Translate(
parent.LabelShort,
totalRepairedHP.ToString("F1"),
Props.repairAmountPerCycle.ToString("F1"),
@@ -277,7 +277,7 @@ namespace WulaFallenEmpire
string repairString = "";
if (NeedsRepair)
{
repairString = "DD_NeedsRepair".Translate().Colorize(Color.yellow);
repairString = "WULA_NeedsRepair".Translate().Colorize(Color.yellow);
}
if (!baseString.NullOrEmpty())

View File

@@ -55,7 +55,7 @@ namespace WulaFallenEmpire
// 如果需要驾驶员但组件不存在,发出警告
if (Props.requirePilot && pilotHolder == null)
{
Log.Warning($"[DD] CompMoteEmitterNorthward on {parent} requires pilot but no CompMechPilotHolder found");
Log.Warning($"[WULA] CompMoteEmitterNorthward on {parent} requires pilot but no CompMechPilotHolder found");
}
// 初始化位置
@@ -154,7 +154,7 @@ namespace WulaFallenEmpire
catch (NullReferenceException ex)
{
// 发生异常时重置状态
Log.Warning($"[DD] Error updating movement state for {parent}: {ex.Message}");
Log.Warning($"[WULA] Error updating movement state for {parent}: {ex.Message}");
isMoving = false;
}
}
@@ -284,7 +284,7 @@ namespace WulaFallenEmpire
}
catch (Exception ex)
{
Log.Error($"[DD] Error emitting mote: {ex}");
Log.Error($"[WULA] Error emitting mote: {ex}");
}
}

View File

@@ -1,13 +1,13 @@
using Verse;
using RimWorld;
using System.Collections.Generic;
namespace WulaFallenEmpire
{
public enum FlightCondition
{
Drafted,
MechAlwaysExceptSpecialJobs // 新增:机械族在非特殊工作状态下始终飞行
DraftedAndMove,
Always
}
public class CompProperties_PawnFlight : CompProperties
@@ -15,13 +15,6 @@ namespace WulaFallenEmpire
// --- Custom Flight Logic ---
public FlightCondition flightCondition = FlightCondition.Drafted;
// --- 新增:机械族特殊工作检查 ---
public List<JobDef> mechForbiddenJobs = new List<JobDef>
{
JobDefOf.MechCharge, // 充电工作
JobDefOf.SelfShutdown // 关机工作
};
// --- Vanilla PawnKindDef Flight Parameters ---
[NoTranslate]
public string flyingAnimationFramePathPrefix;
@@ -40,6 +33,8 @@ namespace WulaFallenEmpire
public bool flyingAnimationInheritColors;
// --- Vanilla PawnKindLifeStage Flight Parameters ---
// Note: These are normally defined per lifestage, we define them once here for simplicity.
// The harmony patch will need to inject these into the correct lifestage at runtime.
public AnimationDef flyingAnimationEast;
public AnimationDef flyingAnimationNorth;
public AnimationDef flyingAnimationSouth;
@@ -47,9 +42,10 @@ namespace WulaFallenEmpire
public AnimationDef flyingAnimationNorthFemale;
public AnimationDef flyingAnimationSouthFemale;
public CompProperties_PawnFlight()
{
compClass = typeof(CompPawnFlight);
}
}
}
}

View File

@@ -62,8 +62,20 @@ namespace WulaFallenEmpire
return true;
}
bool shouldBeFlying = false;
var compProps = flightComp.Props;
bool shouldBeFlying = (compProps.flightCondition == FlightCondition.Drafted && ___pawn.Drafted);
if (compProps.flightCondition == FlightCondition.Always)
{
shouldBeFlying = true;
}
else if (compProps.flightCondition == FlightCondition.DraftedAndMove && ___pawn.Drafted || ___pawn.pather.MovingNow)
{
shouldBeFlying = true;
}
else if (compProps.flightCondition == FlightCondition.Drafted && ___pawn.Drafted)
{
shouldBeFlying = true;
}
if (shouldBeFlying)
{

View File

@@ -33,7 +33,7 @@ namespace WulaFallenEmpire.Utils
string outRoot = Path.Combine(
GenFilePaths.SaveDataFolderPath,
"WulaFallenEmpire_DefInjectedExport",
DateTime.Now.ToString("yyyyMMdd_HHmmss"));
DateTime.Now.ToString("yyyyMMWULA_HHmmss"));
string outDefInjected = Path.Combine(outRoot, "English", "DefInjected");
Directory.CreateDirectory(outDefInjected);

View File

@@ -0,0 +1,213 @@
// File: FloatMenuOptionProvider_EnterMech.cs
using RimWorld;
using System.Collections.Generic;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class FloatMenuOptionProvider_EnterMech : FloatMenuOptionProvider
{
// 检查Thing是否为机甲
private bool IsMech(Thing thing)
{
return thing is Wulamechunit || thing?.GetType()?.IsSubclassOf(typeof(Wulamechunit)) == true;
}
protected override bool Drafted => true; // 征召状态下不能进入机甲
protected override bool Undrafted => true; // 非征召状态下可以进入
protected override bool Multiselect => true; // 不支持多选
// 检查是否适用于当前上下文
protected override bool AppliesInt(FloatMenuContext context)
{
// 必须有选中的殖民者
if (context.FirstSelectedPawn == null)
return false;
// 检查点击的单元格中是否有机甲
var clickedThings = context.ClickedThings;
if (clickedThings == null || clickedThings.Count == 0)
return false;
// 查找第一个机甲
Thing mech = null;
foreach (var thing in clickedThings)
{
if (IsMech(thing))
{
mech = thing;
break;
}
}
if (mech == null)
return false;
// 检查机甲是否有驾驶员组件
var comp = mech.TryGetComp<CompMechPilotHolder>();
if (comp == null)
return false;
// 检查殖民者是否已经在机甲内
// 由于CompMechPilotHolder没有ContainsPilot方法我们需要通过其他方式检查
if (IsPawnInMech(context.FirstSelectedPawn, mech))
return false;
return true;
}
// 检查殖民者是否已经在机甲内替代ContainsPilot
private bool IsPawnInMech(Pawn pawn, Thing mech)
{
var comp = mech.TryGetComp<CompMechPilotHolder>();
if (comp == null)
return false;
// 尝试通过内部容器检查
var holder = comp as IThingHolder;
if (holder != null)
{
var things = holder.GetDirectlyHeldThings();
if (things != null && things.Contains(pawn))
return true;
}
// 或者尝试通过其他属性检查
// 这里假设CompMechPilotHolder有HasPilots属性
if (comp.HasPilots)
{
// 如果有必要,可以通过反射或其他方式检查具体驾驶员
// 暂时返回false假设不在机甲内
return false;
}
return false;
}
// 获取单个选项
protected override FloatMenuOption GetSingleOptionFor(Thing clickedThing, FloatMenuContext context)
{
if (clickedThing == null || context.FirstSelectedPawn == null)
return null;
// 如果不是机甲返回null
if (!IsMech(clickedThing))
return null;
// 获取机甲和组件
var mech = clickedThing as Wulamechunit;
var comp = mech?.TryGetComp<CompMechPilotHolder>();
if (mech == null || comp == null)
return null;
// 检查殖民者是否已经在机甲内
if (IsPawnInMech(context.FirstSelectedPawn, mech))
return null;
// 检查各种条件,生成相应的菜单选项
return CreateEnterMechOption(mech, context.FirstSelectedPawn, comp);
}
// 创建进入机甲的菜单选项
private FloatMenuOption CreateEnterMechOption(Wulamechunit mech, Pawn pawn, CompMechPilotHolder comp)
{
string label = "WULA_EnterMech".Translate(mech.LabelShort);
string disabledReason = "";
// 检查条件是否允许进入
bool canEnter = CanEnterMech(mech, pawn, comp, ref disabledReason);
// 如果条件允许,创建可点击的选项
if (canEnter)
{
return new FloatMenuOption(label, () =>
{
// 创建进入机甲的工作
Job job = JobMaker.MakeJob(Wula_JobDefOf.WULA_EnterMech, mech);
pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
// 播放音效(如果有的话)
FleckMaker.Static(mech.DrawPos, mech.MapHeld, FleckDefOf.FeedbackEquip);
}, MenuOptionPriority.High);
}
else
{
// 创建禁用的选项,显示原因
return new FloatMenuOption(
"WULA_EnterMech".Translate(mech.LabelShort) + ": " + disabledReason,
null,
MenuOptionPriority.DisabledOption);
}
}
// 检查殖民者是否可以进入机甲
private bool CanEnterMech(Wulamechunit mech, Pawn pawn, CompMechPilotHolder comp, ref string disabledReason)
{
// 检查机甲是否已满
if (comp.IsFull)
{
disabledReason = "WULA_MechFull".Translate();
return false;
}
// 检查殖民者是否可以成为驾驶员
if (!comp.CanAddPilot(pawn))
{
disabledReason = "WULA_CannotBecomePilot".Translate();
return false;
}
// 检查距离
if (!pawn.CanReach(mech, PathEndMode.Touch, Danger.Deadly))
{
disabledReason = "NoPath".Translate();
return false;
}
// 检查殖民者状态
if (pawn.Downed)
{
disabledReason = "Downed".Translate();
return false;
}
if (pawn.Dead)
{
disabledReason = "Dead".Translate();
return false;
}
// 检查是否为囚犯
if (pawn.IsPrisoner)
{
disabledReason = "Prisoner".Translate();
return false;
}
// 检查是否为奴隶
if (pawn.IsSlave)
{
disabledReason = "Slave".Translate();
return false;
}
// 检查机甲状态
if (mech.Downed)
{
disabledReason = "Downed".Translate();
return false;
}
if (mech.Dead)
{
disabledReason = "Dead".Translate();
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,76 @@
// File: JobDriver_EnterMech.cs (不再保留机甲)
using RimWorld;
using System.Collections.Generic;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class JobDriver_EnterMech : JobDriver
{
private const TargetIndex MechIndex = TargetIndex.A;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
Pawn pawn = this.pawn;
LocalTargetInfo target = this.job.GetTarget(MechIndex);
// 不再保留机甲,这样多个殖民者可以同时被命令进入同一个机甲
// 只需要检查殖民者是否可以到达机甲
if (!pawn.CanReach(target, PathEndMode.Touch, Danger.Deadly))
{
return false;
}
return true;
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 0. 初始检查
AddFailCondition(() =>
{
var mech = TargetThingA as Wulamechunit;
if (mech == null || mech.Destroyed)
{
return true;
}
var comp = mech.GetComp<CompMechPilotHolder>();
if (comp == null || comp.IsFull || !comp.CanAddPilot(pawn))
{
return true;
}
if (pawn.Downed || pawn.Dead)
return true;
return false;
});
// 1. 走到机甲旁边
yield return Toils_Goto.GotoThing(MechIndex, PathEndMode.Touch);
// 2. 检查是否仍然可以进入
yield return Toils_General.Wait(10).WithProgressBarToilDelay(MechIndex);
// 3. 进入机甲
Toil enterToil = new Toil();
enterToil.initAction = () =>
{
var mech = TargetThingA as Wulamechunit;
if (mech == null)
return;
var comp = mech.GetComp<CompMechPilotHolder>();
if (comp != null && comp.CanAddPilot(pawn))
{
comp.AddPilot(pawn);
Messages.Message("WULA_PilotEnteredMech".Translate(pawn.LabelShort, mech.LabelShort),
MessageTypeDefOf.PositiveEvent, false);
}
};
enterToil.defaultCompleteMode = ToilCompleteMode.Instant;
yield return enterToil;
}
}
}

View File

@@ -0,0 +1,188 @@
// File: WorkGiver_EnterMech.cs
using RimWorld;
using System;
using System.Collections.Generic;
using System.Linq;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class WorkGiver_EnterMech : WorkGiver_Scanner
{
// 缓存机甲定义列表
private static List<ThingDef> cachedMechDefs = null;
public override PathEndMode PathEndMode => PathEndMode.Touch;
// 获取所有机甲定义的列表
private List<ThingDef> GetAllMechDefs()
{
if (cachedMechDefs == null)
{
cachedMechDefs = new List<ThingDef>();
// 搜索所有ThingDef找出继承自Wulamechunit的类
foreach (var def in DefDatabase<ThingDef>.AllDefs)
{
try
{
if (def.thingClass == typeof(Wulamechunit) ||
def.thingClass?.IsSubclassOf(typeof(Wulamechunit)) == true)
{
cachedMechDefs.Add(def);
}
}
catch (Exception ex)
{
// 忽略错误,继续搜索
Log.Warning($"[WULA] Error checking ThingDef {def.defName}: {ex.Message}");
}
}
}
return cachedMechDefs;
}
// 检查Thing是否为机甲
private bool IsMech(Thing thing)
{
return thing is Wulamechunit || thing?.GetType()?.IsSubclassOf(typeof(Wulamechunit)) == true;
}
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
{
try
{
// 检查基本条件
if (t == null || pawn == null)
return false;
// 必须是Wulamechunit或其子类
if (!IsMech(t))
return false;
// 检查距离
if (!pawn.CanReach(t, PathEndMode, Danger.Deadly))
return false;
// 检查机甲是否有驾驶员槽位组件
var comp = t.TryGetComp<CompMechPilotHolder>();
if (comp == null)
return false;
// 检查是否已满
if (comp.IsFull)
return false;
// 检查殖民者是否可以成为驾驶员
if (!comp.CanAddPilot(pawn))
return false;
// 检查殖民者状态
if (pawn.Downed || pawn.Dead)
return false;
// 检查殖民者是否正在执行任务
if (pawn.CurJob != null && pawn.CurJob.def != JobDefOf.Wait)
return false;
// 检查是否被征召
if (pawn.Drafted)
return false;
// 检查是否为囚犯
if (pawn.IsPrisoner)
return false;
// 检查是否为奴隶
if (pawn.IsSlave)
return false;
return true;
}
catch (Exception ex)
{
Log.Error($"[WULA] Error in HasJobOnThing: {ex}");
return false;
}
}
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
{
try
{
if (IsMech(t))
{
// 创建进入机甲的工作
return JobMaker.MakeJob(Wula_JobDefOf.WULA_EnterMech, t);
}
}
catch (Exception ex)
{
Log.Error($"[WULA] Error creating job: {ex}");
}
return null;
}
public override IEnumerable<Thing> PotentialWorkThingsGlobal(Pawn pawn)
{
try
{
// 只搜索玩家拥有的机甲
List<Thing> potentialMechs = new List<Thing>();
// 获取地图中的所有机甲
if (pawn.Map != null)
{
// 使用缓存的机甲定义列表
var mechDefs = GetAllMechDefs();
foreach (var def in mechDefs)
{
try
{
var allMechs = pawn.Map.listerThings.ThingsOfDef(def);
foreach (var mech in allMechs)
{
if (mech.Faction == Faction.OfPlayer &&
mech.TryGetComp<CompMechPilotHolder>() != null)
{
potentialMechs.Add(mech);
}
}
}
catch (Exception ex)
{
Log.Warning($"[WULA] Error getting mechs for def {def.defName}: {ex.Message}");
}
}
}
return potentialMechs;
}
catch (Exception ex)
{
Log.Error($"[WULA] Error in PotentialWorkThingsGlobal: {ex}");
return Enumerable.Empty<Thing>();
}
}
public override bool ShouldSkip(Pawn pawn, bool forced = false)
{
try
{
// 简化版本:只检查殖民者状态
if (pawn.Downed || pawn.Dead || pawn.Drafted || pawn.IsPrisoner || pawn.IsSlave)
return true;
return false;
}
catch (Exception ex)
{
Log.Error($"[WULA] Error in ShouldSkip: {ex}");
return true;
}
}
}
}

View File

@@ -0,0 +1,182 @@
// File: FloatMenuOptionProvider_ForceEjectPilot.cs
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class FloatMenuOptionProvider_ForceEjectPilot : FloatMenuOptionProvider
{
// 征召状态下不能执行此工作
protected override bool Drafted => true;
// 非征召状态下可以执行
protected override bool Undrafted => true;
// 不支持多选
protected override bool Multiselect => false;
// 需要操纵能力
protected override bool RequiresManipulation => true;
// 检查Thing是否为机甲
private bool IsMech(Pawn thing)
{
return thing is Wulamechunit || thing?.GetType()?.IsSubclassOf(typeof(Wulamechunit)) == true;
}
// 检查是否适用于当前上下文
protected override bool AppliesInt(FloatMenuContext context)
{
// 必须有选中的殖民者
if (context.FirstSelectedPawn == null)
return false;
// 检查点击的单元格中是否有机甲
var ClickedPawns = context.ClickedPawns;
if (ClickedPawns == null || ClickedPawns.Count == 0)
return false;
// 查找第一个机甲
Pawn mech = null;
foreach (var thing in ClickedPawns)
{
if (IsMech(thing))
{
mech = thing;
break;
}
}
if (mech == null)
return false;
// 检查机甲是否有驾驶员组件
var comp = mech.TryGetComp<CompMechPilotHolder>();
if (comp == null)
return false;
// 检查机甲是否属于非玩家派系且Downed但未死亡并且有驾驶员
if (mech.Faction == Faction.OfPlayer || !mech.Downed || mech.Dead || !comp.HasPilots)
return false;
return true;
}
// 获取单个选项
protected override FloatMenuOption GetSingleOptionFor(Pawn clickedPawn, FloatMenuContext context)
{
if (clickedPawn == null || context.FirstSelectedPawn == null)
return null;
// 如果不是机甲返回null
if (!IsMech(clickedPawn))
return null;
// 获取机甲和组件
var mech = clickedPawn as Wulamechunit;
var comp = mech?.TryGetComp<CompMechPilotHolder>();
if (mech == null || comp == null)
return null;
// 检查机甲是否属于非玩家派系且Downed但未死亡并且有驾驶员
if (mech.Faction == Faction.OfPlayer || !mech.Downed || mech.Dead || !comp.HasPilots)
return null;
// 检查殖民者是否能够执行此工作
return CreateForceEjectOption(mech, context.FirstSelectedPawn, comp);
}
// 创建强制拉出驾驶员的菜单选项
private FloatMenuOption CreateForceEjectOption(Wulamechunit mech, Pawn pawn, CompMechPilotHolder comp)
{
string label = "WULA_ForceEjectPilot".Translate(mech.LabelShort);
string disabledReason = "";
// 检查条件是否允许执行强制拉出
bool canForceEject = CanForceEject(mech, pawn, comp, ref disabledReason);
if (canForceEject)
{
return new FloatMenuOption(label, () =>
{
// 创建强制拉出驾驶员的工作
Job job = JobMaker.MakeJob(Wula_JobDefOf.WULA_ForceEjectPilot, mech);
pawn.jobs.TryTakeOrderedJob(job, JobTag.Misc);
}, MenuOptionPriority.High);
}
else
{
// 创建禁用的选项,显示原因
return new FloatMenuOption(
"WULA_ForceEjectPilot".Translate(mech.LabelShort) + ": " + disabledReason,
null,
MenuOptionPriority.DisabledOption);
}
}
// 检查殖民者是否可以执行强制拉出
private bool CanForceEject(Wulamechunit mech, Pawn pawn, CompMechPilotHolder comp, ref string disabledReason)
{
// 检查殖民者是否能够到达机甲
if (!pawn.CanReach(mech, PathEndMode.Touch, Danger.Some))
{
disabledReason = "NoPath".Translate();
return false;
}
// 检查殖民者状态
if (pawn.Downed)
{
disabledReason = "Downed".Translate();
return false;
}
if (pawn.Dead)
{
disabledReason = "Dead".Translate();
return false;
}
// 检查是否为囚犯
if (pawn.IsPrisoner)
{
disabledReason = "Prisoner".Translate();
return false;
}
// 检查是否为奴隶
if (pawn.IsSlave)
{
disabledReason = "Slave".Translate();
return false;
}
// 检查机甲是否已经被玩家派系控制
if (mech.Faction == Faction.OfPlayer)
{
disabledReason = "WULA_AlreadyPlayerMech".Translate();
return false;
}
// 检查机甲是否Downed且未死亡
if (!mech.Downed || mech.Dead)
{
disabledReason = "WULA_MechNotDowned".Translate();
return false;
}
// 检查是否有驾驶员
if (!comp.HasPilots)
{
disabledReason = "WULA_NoPilot".Translate();
return false;
}
return true;
}
}
}

View File

@@ -0,0 +1,177 @@
// Jobs/JobDriver_ForceEjectPilot.cs
using RimWorld;
using System.Collections.Generic;
using Verse;
using Verse.AI;
using System.Linq;
namespace WulaFallenEmpire
{
public class JobDriver_ForceEjectPilot : JobDriver
{
private const int WorkDurationTicks = 600; // 10秒60帧/秒)
// 目标机甲
private Pawn TargetMech => job.targetA.Thing as Pawn;
// 工作进度属性
private float WorkProgress
{
get
{
if (TargetMech == null) return 0f;
return (float)ticksLeftThisToil / WorkDurationTicks;
}
}
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
// 为殖民者预留机甲的位置
return pawn.Reserve(TargetMech, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 目标验证
this.FailOnDespawnedNullOrForbidden(TargetIndex.A);
this.FailOn(() => !CanForceEject(TargetMech));
// Toil 1移动到机甲位置
yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch)
.FailOnDespawnedOrNull(TargetIndex.A);
// Toil 2执行强制拉出工作
var workToil = new Toil();
workToil.initAction = () =>
{
pawn.rotationTracker.FaceCell(TargetMech.Position);
pawn.jobs.posture = PawnPosture.Standing;
};
workToil.tickAction = () =>
{
// 每帧工作进度
pawn.skills?.Learn(SkillDefOf.Melee, 0.1f);
// 显示工作条
if (pawn.IsColonistPlayerControlled)
{
TargetMech.Map.overlayDrawer.DrawOverlay(TargetMech, OverlayTypes.QuestionMark);
}
};
workToil.defaultCompleteMode = ToilCompleteMode.Delay;
workToil.WithEffect(EffecterDefOf.MechRepairing, TargetIndex.A);
workToil.defaultDuration = WorkDurationTicks;
workToil.WithProgressBar(TargetIndex.A, () => 1f - WorkProgress);
workToil.handlingFacing = true;
workToil.activeSkill = () => SkillDefOf.Construction;
yield return workToil;
// Toil 3完成工作
yield return new Toil
{
initAction = () =>
{
CompleteForceEject();
},
defaultCompleteMode = ToilCompleteMode.Instant
};
}
// 检查是否可以强制拉出
private bool CanForceEject(Pawn mech)
{
if (mech == null || mech.Dead)
return false;
// 必须是机甲
if (!(mech is Wulamechunit))
return false;
// 必须是玩家派系的目标
if (mech.Faction == Faction.OfPlayer)
return false;
// 必须失去行动能力但未死亡
if (!mech.Downed)
return false;
// 必须有驾驶员
var pilotComp = mech.TryGetComp<CompMechPilotHolder>();
if (pilotComp == null || !pilotComp.HasPilots)
return false;
// 殖民者必须能够接触机甲
if (!pawn.CanReach(mech, PathEndMode.Touch, Danger.Some))
return false;
// 殖民者不能是囚犯或已失去行动能力
if (pawn.Downed || pawn.Dead || pawn.IsPrisoner)
return false;
return true;
}
// 完成强制拉出
private void CompleteForceEject()
{
var mech = TargetMech;
if (mech == null) return;
var pilotComp = mech.TryGetComp<CompMechPilotHolder>();
if (pilotComp == null) return;
try
{
// 1. 弹出所有驾驶员
var ejectedPilots = pilotComp.GetPilots().ToList();
pilotComp.RemoveAllPilots();
// 2. 转换派系为玩家
mech.SetFaction(Faction.OfPlayer);
// 4. 发送消息
SendCompletionMessages(mech, ejectedPilots);
}
catch (System.Exception ex)
{
Log.Error($"[WULA] Error in ForceEjectPilot: {ex}");
}
}
// 发送完成消息
private void SendCompletionMessages(Pawn mech, List<Pawn> ejectedPilots)
{
string message;
if (ejectedPilots.Count > 0)
{
message = "WULA_ForceEjectComplete_WithPilots".Translate(
pawn.LabelShortCap,
mech.LabelShortCap,
ejectedPilots.Count
);
}
else
{
message = "WULA_ForceEjectComplete".Translate(
pawn.LabelShortCap,
mech.LabelShortCap
);
}
Messages.Message(message, MessageTypeDefOf.PositiveEvent);
// 如果弹出的是敌对派系驾驶员,添加额外消息
foreach (var pilot in ejectedPilots)
{
if (pilot.Faction != null && pilot.Faction.HostileTo(Faction.OfPlayer))
{
Messages.Message("WULA_HostilePilotEjected".Translate(
pilot.LabelShortCap,
pilot.Faction.Name
), MessageTypeDefOf.NeutralEvent);
}
}
}
}
}

View File

@@ -0,0 +1,82 @@
// File: JobDriver_CarryToMech.cs (修复count问题)
using System.Collections.Generic;
using RimWorld;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class JobDriver_CarryToMech : JobDriver
{
private const TargetIndex TakeeIndex = TargetIndex.A;
private const TargetIndex MechIndex = TargetIndex.B;
protected Pawn Takee => (Pawn)job.GetTarget(TakeeIndex).Thing;
protected Wulamechunit Mech => (Wulamechunit)job.GetTarget(MechIndex).Thing;
protected CompMechPilotHolder MechComp => Mech?.TryGetComp<CompMechPilotHolder>();
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
// 确保job.count是有效的至少为1
if (job.count <= 0)
{
job.count = 1;
}
// 保留目标和机甲明确指定数量为1
return pawn.Reserve(Takee, job, 1, -1, null, errorOnFailed)
&& pawn.Reserve(Mech, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 确保job.count是有效的
if (job.count <= 0)
{
job.count = 1;
}
// 标准失败条件
this.FailOnDestroyedOrNull(TakeeIndex);
this.FailOnDestroyedOrNull(MechIndex);
this.FailOn(() => MechComp == null);
this.FailOn(() => !Takee.Downed); // 确保被搬运者是Downed状态
// 1. 前往要被搬运的殖民者
yield return Toils_Goto.GotoThing(TakeeIndex, PathEndMode.ClosestTouch)
.FailOnDespawnedNullOrForbidden(TakeeIndex)
.FailOnDespawnedNullOrForbidden(MechIndex)
.FailOnSomeonePhysicallyInteracting(TakeeIndex);
// 2. 开始搬运殖民者 - 使用原版的StartCarryThing方法
yield return Toils_Haul.StartCarryThing(TakeeIndex, false, true, false);
// 3. 携带殖民者前往机甲
yield return Toils_Goto.GotoThing(MechIndex, PathEndMode.Touch);
// 4. 将殖民者放入机甲
yield return new Toil
{
initAction = () =>
{
if (MechComp != null && Takee != null && MechComp.CanAddPilot(Takee))
{
// 放下殖民者
if (pawn.carryTracker.CarriedThing == Takee)
{
pawn.carryTracker.TryDropCarriedThing(pawn.Position, ThingPlaceMode.Near, out Thing droppedThing);
}
// 将殖民者添加到机甲
MechComp.AddPilot(Takee);
}
else
{
Log.Warning($"[WULA] 无法将殖民者添加到机甲");
}
},
defaultCompleteMode = ToilCompleteMode.Instant
};
}
}
}

View File

@@ -0,0 +1,18 @@
using RimWorld;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class JobGiver_NoPilot : ThinkNode_JobGiver
{
private const int WaitTime = 100;
protected override Job TryGiveJob(Pawn pawn)
{
Job job = JobMaker.MakeJob(JobDefOf.Wait);
job.expiryInterval = 100;
return job;
}
}
}

View File

@@ -0,0 +1,176 @@
// JobDriver_RefuelMech.cs
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using Verse;
using Verse.AI;
using UnityEngine;
namespace WulaFallenEmpire
{
public class JobDriver_RefuelMech : JobDriver
{
private const TargetIndex MechInd = TargetIndex.A;
private const TargetIndex FuelInd = TargetIndex.B;
private const int RefuelingDuration = 240; // 基础加注时间
protected Pawn Mech => job.GetTarget(MechInd).Thing as Pawn;
protected CompMechFuel FuelComp => Mech?.TryGetComp<CompMechFuel>();
protected Thing Fuel => job.GetTarget(FuelInd).Thing;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
Pawn pawn = this.pawn;
Job job = this.job;
LocalTargetInfo target = job.GetTarget(MechInd);
if (!pawn.Reserve(target, job, 1, -1, null, errorOnFailed))
{
return false;
}
if (!pawn.Reserve(job.GetTarget(FuelInd), job, 1, -1, null, errorOnFailed))
{
return false;
}
return true;
}
protected override IEnumerable<Toil> MakeNewToils()
{
// 检查目标是否有效
this.FailOnDespawnedNullOrForbidden(MechInd);
this.FailOn(() => FuelComp == null);
// 添加结束条件:燃料已满
AddEndCondition(() => {
if (FuelComp.IsFull)
return JobCondition.Succeeded;
return JobCondition.Ongoing;
});
// 如果不是玩家强制命令,检查是否应该自动加注
AddFailCondition(() => {
if (job.playerForced)
return false;
// 获取驾驶员组件
var pilotComp = Mech.TryGetComp<CompMechPilotHolder>();
bool hasPilot = pilotComp != null && pilotComp.HasPilots;
// 如果有驾驶员且不是玩家强制命令,不自动加注
if (hasPilot && !job.playerForced)
return true;
// 检查燃料组件是否允许自动加注
if (!FuelComp.Props.allowAutoRefuel && !job.playerForced)
return true;
return false;
});
// 第一步:计算需要多少燃料
yield return Toils_General.DoAtomic(delegate
{
if (FuelComp != null)
{
job.count = FuelComp.GetFuelCountToFullyRefuel();
}
});
// 第二步:预留燃料
Toil reserveFuel = Toils_Reserve.Reserve(FuelInd);
yield return reserveFuel;
// 第三步:前往燃料位置
yield return Toils_Goto.GotoThing(FuelInd, PathEndMode.ClosestTouch)
.FailOnDespawnedNullOrForbidden(FuelInd)
.FailOnSomeonePhysicallyInteracting(FuelInd);
// 第四步:拿起燃料
yield return Toils_Haul.StartCarryThing(FuelInd, putRemainderInQueue: false, subtractNumTakenFromJobCount: true)
.FailOnDestroyedNullOrForbidden(FuelInd);
// 第五步:检查是否有机会拿更多燃料
yield return Toils_Haul.CheckForGetOpportunityDuplicate(reserveFuel, FuelInd, TargetIndex.None, takeFromValidStorage: true);
// 第六步:前往机甲位置
yield return Toils_Goto.GotoThing(MechInd, PathEndMode.Touch);
// 第七步:等待加注(有进度条)
Toil refuelToil = Toils_General.Wait(RefuelingDuration)
.FailOnDestroyedNullOrForbidden(FuelInd)
.FailOnDestroyedNullOrForbidden(MechInd)
.FailOnCannotTouch(MechInd, PathEndMode.Touch)
.WithProgressBarToilDelay(MechInd);
// 调整加注时间基于燃料组件的速度因子
refuelToil.defaultDuration = Mathf.RoundToInt(RefuelingDuration / FuelComp.Props.refuelSpeedFactor);
yield return refuelToil;
// 第八步:完成加注 - 模仿 RimWorld 原版实现
yield return FinalizeRefueling(MechInd, FuelInd);
}
// 模仿 RimWorld.Toils_Refuel.FinalizeRefueling 的实现
private static Toil FinalizeRefueling(TargetIndex refuelableInd, TargetIndex fuelInd)
{
Toil toil = ToilMaker.MakeToil("FinalizeRefueling");
toil.initAction = delegate
{
Pawn actor = toil.actor;
Job curJob = actor.CurJob;
Thing refuelable = curJob.GetTarget(refuelableInd).Thing;
CompMechFuel fuelComp = refuelable.TryGetComp<CompMechFuel>();
if (fuelComp != null)
{
// 获取所有燃料物品
List<Thing> fuelThings;
if (actor.CurJob.placedThings.NullOrEmpty())
{
// 如果没有 placedThings则使用燃料目标
Thing fuel = curJob.GetTarget(fuelInd).Thing;
if (fuel != null)
{
fuelThings = new List<Thing> { fuel };
}
else
{
fuelThings = null;
}
}
else
{
// 使用 placedThings 中的所有燃料物品
fuelThings = actor.CurJob.placedThings.Select((ThingCountClass p) => p.thing).ToList();
}
if (fuelThings != null)
{
// 计算总燃料量并销毁燃料物品
float totalFuel = 0f;
foreach (Thing fuelThing in fuelThings)
{
if (fuelThing != null && fuelThing.def == fuelComp.FuelType)
{
totalFuel += fuelThing.stackCount;
fuelThing.Destroy(DestroyMode.Vanish);
}
}
// 添加燃料到机甲
if (totalFuel > 0)
{
fuelComp.Refuel(totalFuel);
}
}
}
};
toil.defaultCompleteMode = ToilCompleteMode.Instant;
return toil;
}
}
}

View File

@@ -0,0 +1,123 @@
// WorkGiver_RefuelMech.cs
using System.Collections.Generic;
using System.Linq;
using RimWorld;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class WorkGiver_RefuelMech : WorkGiver_Scanner
{
public override PathEndMode PathEndMode => PathEndMode.Touch;
public override IEnumerable<Thing> PotentialWorkThingsGlobal(Pawn pawn)
{
// 返回所有需要燃料的机甲
// 修复:使用 LINQ 的 Where 方法而不是 FindAll
var mechs = pawn.Map.mapPawns.AllPawnsSpawned.Where(p =>
p.TryGetComp<CompMechFuel>() != null);
foreach (Pawn mech in mechs)
{
yield return mech;
}
}
public override bool ShouldSkip(Pawn pawn, bool forced = false)
{
// 如果没有需要燃料的机甲,跳过
return !PotentialWorkThingsGlobal(pawn).Any();
}
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
{
if (!(t is Pawn mech))
return false;
var fuelComp = mech.TryGetComp<CompMechFuel>();
if (fuelComp == null)
return false;
// 检查机甲是否已加满燃料
if (fuelComp.IsFull)
return false;
// 检查是否有可用的燃料
if (FindFuel(pawn, fuelComp) == null)
return false;
// 检查是否能接触到机甲
if (!pawn.CanReserveAndReach(t, PathEndMode.Touch, Danger.Some))
return false;
// 检查机甲状态
var pilotComp = mech.TryGetComp<CompMechPilotHolder>();
bool hasPilot = pilotComp != null && pilotComp.HasPilots;
// 如果有驾驶员且不是强制命令,不自动加注
if (hasPilot && !forced)
return false;
// 检查燃料组件是否允许自动加注
if (!fuelComp.Props.allowAutoRefuel && !forced)
return false;
// 检查是否达到自动加注阈值
if (!forced && !fuelComp.NeedsRefueling)
return false;
return true;
}
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
{
var fuelComp = t.TryGetComp<CompMechFuel>();
if (fuelComp == null)
return null;
// 寻找燃料
Thing fuel = FindFuel(pawn, fuelComp);
if (fuel == null)
return null;
// 创建加注工作
Job job = JobMaker.MakeJob(Wula_JobDefOf.WULA_RefuelMech, t, fuel);
job.count = fuelComp.GetFuelCountToFullyRefuel();
return job;
}
// 修改方法:返回 Thing 而不是 bool
private Thing FindFuel(Pawn pawn, CompMechFuel fuelComp)
{
if (fuelComp.FuelType == null)
return null;
// 在库存中寻找燃料
Thing fuel = FindFuelInInventory(pawn, fuelComp.FuelType);
if (fuel != null)
return fuel;
// 在地图上寻找燃料
fuel = GenClosest.ClosestThingReachable(
pawn.Position,
pawn.Map,
ThingRequest.ForDef(fuelComp.FuelType),
PathEndMode.ClosestTouch,
TraverseParms.For(pawn),
9999f,
validator: thing => !thing.IsForbidden(pawn) && pawn.CanReserve(thing)
);
return fuel;
}
private Thing FindFuelInInventory(Pawn pawn, ThingDef fuelType)
{
if (pawn.inventory == null)
return null;
return pawn.inventory.innerContainer.FirstOrDefault(t => t.def == fuelType);
}
}
}

View File

@@ -0,0 +1,416 @@
// JobDriver_RepairMech.cs
using RimWorld;
using System.Collections.Generic;
using UnityEngine;
using Verse;
using Verse.AI;
using System.Linq;
namespace WulaFallenEmpire
{
public class JobDriver_RepairMech : JobDriver
{
private const TargetIndex MechInd = TargetIndex.A;
protected int ticksToNextRepair;
protected Pawn Mech => (Pawn)job.GetTarget(TargetIndex.A).Thing;
protected virtual bool Remote => false;
protected CompMechRepairable RepairableComp => Mech?.TryGetComp<CompMechRepairable>();
// 使用配置的修复周期ticks数并根据MechRepairSpeed调整
protected int TicksPerRepairCycle
{
get
{
if (RepairableComp == null)
return 120;
int baseTicks = RepairableComp.Props.ticksPerRepairCycle;
return Mathf.RoundToInt(baseTicks / pawn.GetStatValue(StatDefOf.MechRepairSpeed));
}
}
// 每次修复的HP量
protected float RepairAmountPerCycle
{
get
{
if (RepairableComp == null)
return 1f;
return RepairableComp.Props.repairAmountPerCycle;
}
}
// 新增:缺失部位修复配置
protected float MissingPartRepairCostMultiplier => 2f;
protected HediffDef MissingPartReplacementInjury => HediffDefOf.Misc;
public override bool TryMakePreToilReservations(bool errorOnFailed)
{
return pawn.Reserve(Mech, job, 1, -1, null, errorOnFailed);
}
protected override IEnumerable<Toil> MakeNewToils()
{
this.FailOnDestroyedOrNull(TargetIndex.A);
this.FailOnForbidden(TargetIndex.A);
this.FailOn(() => !MechRepairable() || !MechNeedsRepair());
if (!Remote)
{
yield return Toils_Goto.GotoThing(TargetIndex.A, PathEndMode.Touch);
}
Toil repairToil = (Remote ? Toils_General.Wait(int.MaxValue) : Toils_General.WaitWith(TargetIndex.A, int.MaxValue, useProgressBar: true, maintainPosture: true, maintainSleep: true));
// 添加维修特效
if (RepairableComp?.Props.repairEffect != null)
{
repairToil.WithEffect(RepairableComp.Props.repairEffect, TargetIndex.A);
}
else
{
repairToil.WithEffect(EffecterDefOf.MechRepairing, TargetIndex.A);
}
// 添加维修音效
if (RepairableComp?.Props.repairSound != null)
{
repairToil.PlaySustainerOrSound(RepairableComp.Props.repairSound);
}
else
{
repairToil.PlaySustainerOrSound(Remote ? SoundDefOf.RepairMech_Remote : SoundDefOf.RepairMech_Touch);
}
repairToil.AddPreInitAction(delegate
{
ticksToNextRepair = TicksPerRepairCycle;
});
repairToil.handlingFacing = true;
repairToil.tickIntervalAction = delegate(int delta)
{
ticksToNextRepair -= delta;
if (ticksToNextRepair <= 0)
{
RepairTick(delta);
ticksToNextRepair = TicksPerRepairCycle;
}
pawn.rotationTracker.FaceTarget(Mech);
if (pawn.skills != null)
{
pawn.skills.Learn(SkillDefOf.Crafting, 0.05f * (float)delta);
}
};
repairToil.AddFinishAction(delegate
{
// 维修完成后,如果机甲被征召,恢复其工作
if (Mech.jobs?.curJob != null && job.playerForced)
{
Mech.jobs.EndCurrentJob(JobCondition.InterruptForced);
}
});
repairToil.AddEndCondition(() => MechNeedsRepair() ? JobCondition.Ongoing : JobCondition.Succeeded);
if (!Remote)
{
repairToil.activeSkill = () => SkillDefOf.Crafting;
}
yield return repairToil;
}
private bool MechRepairable()
{
return RepairableComp != null;
}
private bool MechNeedsRepair()
{
return RepairableComp?.NeedsRepair ?? false;
}
private void RepairTick(int delta)
{
if (Mech == null || Mech.health == null || Mech.Dead)
return;
// 计算本次修复的总HP量
float totalRepairAmount = RepairAmountPerCycle;
float originalRepairAmount = totalRepairAmount;
// 第一阶段:先修复现有伤口(非缺失部位)
totalRepairAmount = RepairExistingInjuries(totalRepairAmount);
// 第二阶段:如果还有修复量,并且机甲血量足够安全,再处理缺失部位
if (totalRepairAmount > 0f && IsSafeToRepairMissingParts())
{
// 直接尝试转换缺失部位,不消耗修复量
TryConvertMissingParts();
}
// 记录修复统计(只统计实际修复的伤口)
if (RepairableComp != null && totalRepairAmount < originalRepairAmount)
{
RepairableComp.RecordRepair(originalRepairAmount - totalRepairAmount);
}
}
// 修复现有伤口(非缺失部位)
private float RepairExistingInjuries(float totalRepairAmount)
{
float remainingAmount = totalRepairAmount;
// 获取所有非缺失部位的伤口
var existingInjuries = Mech.health.hediffSet.hediffs
.Where(h =>
(h is Hediff_Injury && h.Severity > 0f) ||
(h.def.tendable && h.Severity > 0f && !(h is Hediff_MissingPart))
)
.OrderByDescending(h => h.Severity) // 优先修复最严重的伤口
.ToList();
foreach (var injury in existingInjuries)
{
if (remainingAmount <= 0f)
break;
if (injury is Hediff_Injury injuryHediff)
{
// 修复伤害
float healAmount = Mathf.Min(remainingAmount, injuryHediff.Severity);
injuryHediff.Severity -= healAmount;
remainingAmount -= healAmount;
if (injuryHediff.Severity <= 0f)
{
Mech.health.RemoveHediff(injuryHediff);
}
}
else if (injury.def.tendable)
{
// 其他可治疗的hediff
float healAmount = Mathf.Min(remainingAmount, injury.Severity);
injury.Severity -= healAmount;
remainingAmount -= healAmount;
if (injury.Severity <= 0f)
{
Mech.health.RemoveHediff(injury);
}
}
}
return remainingAmount;
}
// 检查是否安全可以修复缺失部位
private bool IsSafeToRepairMissingParts()
{
// 获取机甲当前血量百分比
float currentHealthPercent = Mech.health.summaryHealth.SummaryHealthPercent;
// 如果血量低于30%,不安全修复缺失部位
if (currentHealthPercent < 0.3f)
return false;
// 如果有严重伤口严重性大于5先修复它们
bool hasCriticalInjuries = Mech.health.hediffSet.hediffs
.Any(h => h is Hediff_Injury && h.Severity > 5f);
if (hasCriticalInjuries)
return false;
// 检查缺失部位转换是否会致命
if (WouldMissingPartConversionBeFatal())
return false;
return true;
}
// 检查缺失部位转换是否会致命
private bool WouldMissingPartConversionBeFatal()
{
// 获取所有缺失部位
var missingParts = Mech.health.hediffSet.GetMissingPartsCommonAncestors();
if (!missingParts.Any())
return false;
// 计算转换后可能增加的总伤害量
float potentialAddedDamage = 0f;
foreach (var missingPart in missingParts)
{
float partMaxHealth = missingPart.Part.def.GetMaxHealth(Mech);
float injurySeverity = partMaxHealth - 1;
if (partMaxHealth <= 1)
injurySeverity = 0.5f;
potentialAddedDamage += injurySeverity;
}
// 获取当前总伤害量
float currentTotalInjurySeverity = Mech.health.hediffSet.hediffs
.Where(h => h is Hediff_Injury)
.Sum(h => h.Severity);
// 计算转换后的总伤害量
float projectedTotalInjurySeverity = currentTotalInjurySeverity + potentialAddedDamage;
// 获取致命伤害阈值
float lethalDamageThreshold = Mech.health.LethalDamageThreshold;
// 如果转换后的总伤害量超过或接近致命阈值,不安全
return projectedTotalInjurySeverity >= lethalDamageThreshold * 0.8f;
}
// 尝试转换缺失部位
private void TryConvertMissingParts()
{
// 获取所有缺失部位
var missingParts = Mech.health.hediffSet.GetMissingPartsCommonAncestors();
if (!missingParts.Any())
return;
// 选择最小的缺失部件进行转换(成本较低)
Hediff_MissingPart partToRepair = null;
float minHealth = float.MaxValue;
foreach (var missingPart in missingParts)
{
float partHealth = missingPart.Part.def.GetMaxHealth(Mech);
if (partHealth < minHealth)
{
minHealth = partHealth;
partToRepair = missingPart;
}
}
if (partToRepair != null)
{
// 直接转换缺失部位
if (ConvertMissingPartToInjury(partToRepair))
{
}
}
}
// 将缺失部件转换为伤害hediff
private bool ConvertMissingPartToInjury(Hediff_MissingPart missingPart)
{
try
{
float partMaxHealth = missingPart.Part.def.GetMaxHealth(Mech);
// 关键修复:确保转换后的损伤不会导致部位再次缺失
// 设置损伤严重性为最大健康值-1这样部位健康值至少为1
float injurySeverity = partMaxHealth - 1;
// 如果最大健康值为1则设置为0.5确保部位健康值大于0
if (partMaxHealth <= 1)
{
injurySeverity = 0.5f;
}
// 移除缺失部件hediff
Mech.health.RemoveHediff(missingPart);
// 添加指定的伤害hediff
HediffDef injuryDef = MissingPartReplacementInjury;
if (injuryDef == null)
{
Log.Error($"[WULA] 找不到指定的hediff定义: {MissingPartReplacementInjury?.defName ?? "null"}");
return false;
}
// 创建损伤
Hediff injury = HediffMaker.MakeHediff(injuryDef, Mech, missingPart.Part);
injury.Severity = injurySeverity;
Mech.health.AddHediff(injury);
return true;
}
catch (System.Exception ex)
{
Log.Error($"[WULA] 转换缺失部件 {missingPart.Part.def.defName} 时出错: {ex}");
return false;
}
}
// 获取所有可修复的hediff
private List<Hediff> GetAllRepairableHediffs()
{
var repairableHediffs = new List<Hediff>();
if (Mech.health?.hediffSet == null)
return repairableHediffs;
// 获取所有hediff
foreach (var hediff in Mech.health.hediffSet.hediffs)
{
if (CanRepairHediff(hediff))
{
repairableHediffs.Add(hediff);
}
}
return repairableHediffs;
}
// 检查hediff是否可修复
private bool CanRepairHediff(Hediff hediff)
{
// 缺失部位可以修复
if (hediff is Hediff_MissingPart)
return true;
// 伤害可以修复
if (hediff is Hediff_Injury)
return true;
// 可治疗的hediff可以修复
if (hediff.def.tendable && hediff.Severity > 0f)
return true;
// 跳过疾病
if (IsDisease(hediff))
return false;
return false;
}
// 检查是否是疾病
private bool IsDisease(Hediff hediff)
{
// 常见的疾病类型
string[] diseaseKeywords = {
"Disease", "Flu", "Plague", "Infection", "Malaria",
"SleepingSickness", "FibrousMechanites", "SensoryMechanites",
"WoundInfection", "FoodPoisoning", "GutWorms", "MuscleParasites"
};
foreach (string keyword in diseaseKeywords)
{
if (hediff.def.defName.Contains(keyword))
return true;
}
return false;
}
public override void ExposeData()
{
base.ExposeData();
Scribe_Values.Look(ref ticksToNextRepair, "ticksToNextRepair", 0);
}
}
}

View File

@@ -0,0 +1,73 @@
// WorkGiver_RepairMech.cs
using RimWorld;
using System.Collections.Generic;
using System.Linq;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class WorkGiver_RepairMech : WorkGiver_Scanner
{
public override PathEndMode PathEndMode => PathEndMode.Touch;
public override Danger MaxPathDanger(Pawn pawn)
{
return Danger.Deadly;
}
public override IEnumerable<Thing> PotentialWorkThingsGlobal(Pawn pawn)
{
if (pawn.Faction != Faction.OfPlayer || pawn.Map == null)
return Enumerable.Empty<Thing>();
// 获取所有需要维修的玩家机甲
return pawn.Map.mapPawns.AllPawnsSpawned
.Where(p =>
p.Faction == Faction.OfPlayer &&
p.health != null &&
!p.Dead &&
p.TryGetComp<CompMechRepairable>()?.CanAutoRepair == true
)
.Cast<Thing>();
}
public override bool ShouldSkip(Pawn pawn, bool forced = false)
{
if (pawn.story != null && pawn.WorkTagIsDisabled(WorkTags.Crafting))
return true;
return false;
}
public override bool HasJobOnThing(Pawn pawn, Thing t, bool forced = false)
{
if (!(t is Pawn mech) || mech.Dead)
return false;
var repairableComp = t.TryGetComp<CompMechRepairable>();
if (repairableComp == null || !repairableComp.CanAutoRepair)
return false;
if (!repairableComp.NeedsRepair)
return false;
if (pawn.Faction != Faction.OfPlayer)
return false;
if (!pawn.CanReserveAndReach(t, PathEndMode.Touch, Danger.Some, 1, -1, null, forced))
return false;
// 检查工作标签
if (pawn.story != null && pawn.WorkTagIsDisabled(WorkTags.Crafting))
return false;
return true;
}
public override Job JobOnThing(Pawn pawn, Thing t, bool forced = false)
{
return JobMaker.MakeJob(Wula_JobDefOf.WULA_RepairMech, t);
}
}
}

View File

@@ -0,0 +1,39 @@
// ThinkNode_ConditionalMechHasPilot.cs (修复版)
using RimWorld;
using Verse;
using Verse.AI;
namespace WulaFallenEmpire
{
public class ThinkNode_ConditionalMechHasPilot : ThinkNode_Conditional
{
// 可选的可以在XML中设置的参数
public int minPilotCount = 1; // 最少需要的驾驶员数量
protected override bool Satisfied(Pawn pawn)
{
if (pawn.Faction != Faction.OfPlayer)
return false; // 仅适用于玩家派系的机甲
var pilotComp = pawn.TryGetComp<CompMechPilotHolder>();
if (pilotComp == null)
return false; // 如果没有驾驶员组件,条件满足(允许执行)
int currentPilotCount = pilotComp.CurrentPilotCount;
// 检查是否满足最小驾驶员数量要求
bool hasEnoughPilots = currentPilotCount >= minPilotCount;
// 这意味着机甲可以正常工作
bool conditionSatisfied = hasEnoughPilots;
return !conditionSatisfied;
}
public override ThinkNode DeepCopy(bool resolve = true)
{
ThinkNode_ConditionalMechHasPilot thinkNode_ConditionalMechHasPilot = (ThinkNode_ConditionalMechHasPilot)base.DeepCopy(resolve);
thinkNode_ConditionalMechHasPilot.minPilotCount = minPilotCount;
return thinkNode_ConditionalMechHasPilot;
}
}
}

View File

@@ -27,7 +27,6 @@ namespace WulaFallenEmpire
public static JobDef WULA_Launch_Proj;
public static JobDef WULA_EnterMech;
public static JobDef WULA_RefuelMech;
public static JobDef WULA_Refuel;
public static JobDef WULA_RepairMech;
public static JobDef WULA_ForceEjectPilot;
public static JobDef WULA_CarryToMech;

View File

@@ -95,7 +95,23 @@
<Compile Include="HediffComp\WULA_SyncedWithMech\HediffCompProperties_SyncedWithMech.cs" />
<Compile Include="HediffComp\WULA_TimedExplosion\HediffComp_TimedExplosion.cs" />
<Compile Include="ITab\ITab_MechSkills.cs" />
<Compile Include="Pawn\WULA_PawnRenderExtra\Comp_PawnRenderExtra.cs" />
<Compile Include="MentalState\MentalState_MechNoPilot.cs" />
<Compile Include="Pawn_Comps\PawnRenderExtra\Comp_PawnRenderExtra.cs" />
<Compile Include="Pawn_Comps\HighSpeedCollision\CompHighSpeedCollision.cs" />
<Compile Include="Work\EnterMech\FloatMenuOptionProvider_EnterMech.cs" />
<Compile Include="Work\EnterMech\JobDriver_EnterMech.cs" />
<Compile Include="Work\EnterMech\WorkGiver_EnterMech.cs" />
<Compile Include="Work\ForceEjectPilot\FloatMenuOptionProvider_ForceEjectPilot.cs" />
<Compile Include="Work\ForceEjectPilot\JobDriver_ForceEjectPilot.cs" />
<Compile Include="Work\InspectBuilding\JobDriver_InspectBuilding.cs" />
<Compile Include="Work\InspectBuilding\JobGiver_InspectBuilding.cs" />
<Compile Include="Work\JobDriver_CarryToMech.cs" />
<Compile Include="Work\JobGiver_NoPilot.cs" />
<Compile Include="Work\RefuelMech\JobDriver_RefuelMech.cs" />
<Compile Include="Work\RefuelMech\WorkGiver_RefuelMech.cs" />
<Compile Include="Work\RepairMech\JobDriver_RepairMech.cs" />
<Compile Include="Work\RepairMech\WorkGiver_RepairMech.cs" />
<Compile Include="Work\ThinkNode_ConditionalMechHasPilot.cs" />
<Compile Include="WulaFallenEmpireMod.cs" />
<Compile Include="WulaFallenEmpireSettings.cs" />
<Compile Include="WulaLog.cs" />
@@ -333,29 +349,27 @@
<Compile Include="HediffComp\WULA_HediffSpawner\HediffComp_Spawner.cs" />
<Compile Include="HediffComp\WULA_HediffSpawner\Tools.cs" />
<!-- Job 相关 -->
<Compile Include="Job\JobDriver_InspectBuilding.cs" />
<Compile Include="Job\JobGiver_InspectBuilding.cs" />
<!-- Pawn 相关 -->
<Compile Include="Pawn\Mechunit.cs" />
<Compile Include="Pawn\WULA_AutoMechCarrier\CompAutoMechCarrier.cs" />
<Compile Include="Pawn\WULA_AutoMechCarrier\CompProperties_AutoMechCarrier.cs" />
<Compile Include="Pawn\WULA_AutoMechCarrier\PawnProductionEntry.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\CompAutonomousMech.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\DroneGizmo.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\JobGiver_DroneSelfShutdown.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\PawnColumnWorker_DroneEnergy.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\PawnColumnWorker_DroneWorkMode.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\ThinkNode_ConditionalAutonomousWorkMode.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\ThinkNode_ConditionalLowEnergy_Drone.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\ThinkNode_ConditionalNeedRecharge.cs" />
<Compile Include="Pawn\WULA_AutonomousMech\ThinkNode_ConditionalWorkMode_Drone.cs" />
<Compile Include="Pawn\WULA_BrokenPersonality\MentalBreakWorker_BrokenPersonality.cs" />
<Compile Include="Pawn\WULA_BrokenPersonality\MentalStateDefExtension_BrokenPersonality.cs" />
<Compile Include="Pawn\WULA_BrokenPersonality\MentalState_BrokenPersonality.cs" />
<Compile Include="Pawn\WULA_Cat_Invisible\CompFighterInvisible.cs" />
<Compile Include="Pawn\WULA_Cat_Invisible\CompProperties_FighterInvisible.cs" />
<Compile Include="Pawn\WULA_CompHediffGiver\CompHediffGiver.cs" />
<Compile Include="Pawn\WULA_CompHediffGiver\CompProperties_HediffGiver.cs" />
<Compile Include="Pawn_Comps\AutoMechCarrier\CompAutoMechCarrier.cs" />
<Compile Include="Pawn_Comps\AutoMechCarrier\CompProperties_AutoMechCarrier.cs" />
<Compile Include="Pawn_Comps\AutoMechCarrier\PawnProductionEntry.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\CompAutonomousMech.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\DroneGizmo.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\JobGiver_DroneSelfShutdown.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\PawnColumnWorker_DroneEnergy.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\PawnColumnWorker_DroneWorkMode.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\ThinkNode_ConditionalAutonomousWorkMode.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\ThinkNode_ConditionalLowEnergy_Drone.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\ThinkNode_ConditionalNeedRecharge.cs" />
<Compile Include="Pawn_Comps\AutonomousMech\ThinkNode_ConditionalWorkMode_Drone.cs" />
<Compile Include="MentalState\BrokenPersonality\MentalBreakWorker_BrokenPersonality.cs" />
<Compile Include="MentalState\BrokenPersonality\MentalStateDefExtension_BrokenPersonality.cs" />
<Compile Include="MentalState\BrokenPersonality\MentalState_BrokenPersonality.cs" />
<Compile Include="Pawn_Comps\Cat_Invisible\CompFighterInvisible.cs" />
<Compile Include="Pawn_Comps\Cat_Invisible\CompProperties_FighterInvisible.cs" />
<Compile Include="Pawn_Comps\HediffGiver\CompHediffGiver.cs" />
<Compile Include="Pawn_Comps\HediffGiver\CompProperties_HediffGiver.cs" />
<Compile Include="Pawn\WULA_Energy\CompChargingBed.cs" />
<Compile Include="Pawn\WULA_Energy\HediffComp_WulaCharging.cs" />
<Compile Include="Pawn\WULA_Energy\JobDriver_FeedWulaPatient.cs" />
@@ -371,10 +385,10 @@
<Compile Include="Pawn\WULA_Energy\WorkGiver_Warden_DeliverEnergy.cs" />
<Compile Include="Pawn\WULA_Energy\WorkGiver_Warden_FeedWula.cs" />
<Compile Include="Pawn\WULA_Energy\WulaCaravanEnergyDef.cs" />
<Compile Include="Pawn\WULA_Flight\CompPawnFlight.cs" />
<Compile Include="Pawn\WULA_Flight\CompProperties_PawnFlight.cs" />
<Compile Include="Pawn\WULA_Flight\PawnRenderNodeWorker_AttachmentBody_NoFlight.cs" />
<Compile Include="Pawn\WULA_Flight\Pawn_FlightTrackerPatches.cs" />
<Compile Include="Pawn_Comps\Pawn_Flight\CompPawnFlight.cs" />
<Compile Include="Pawn_Comps\Pawn_Flight\CompProperties_PawnFlight.cs" />
<Compile Include="Pawn_Comps\Pawn_Flight\PawnRenderNodeWorker_AttachmentBody_NoFlight.cs" />
<Compile Include="Pawn_Comps\Pawn_Flight\Pawn_FlightTrackerPatches.cs" />
<Compile Include="Pawn\WULA_Maintenance\Building_MaintenancePod.cs" />
<Compile Include="Pawn\WULA_Maintenance\CompMaintenancePod.cs" />
<Compile Include="Pawn\WULA_Maintenance\HediffCompProperties_MaintenanceDamage.cs" />
@@ -388,7 +402,6 @@
<Compile Include="Pawn_Comps\DefaultPilotEntry\CompProperties_MechDefaultPilot.cs" />
<Compile Include="Pawn_Comps\HediffGiverByKind\CompHediffGiverByKind.cs" />
<Compile Include="Pawn_Comps\HediffGiverByKind\CompProperties_HediffGiverByKind.cs" />
<Compile Include="Pawn_Comps\MechArmor\CompMechArmor.cs" />
<Compile Include="Pawn_Comps\MechFuel\CompMechFuel.cs" />
<Compile Include="Pawn_Comps\MechFuel\CompProperties_MechFuel.cs" />
<Compile Include="Pawn_Comps\MechFuel\Gizmo_MechFuelStatus.cs" />