1
This commit is contained in:
Binary file not shown.
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
|
||||
@@ -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
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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}");
|
||||
// 出错时不改变原列表
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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; // 出错时继续执行原始方法
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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") + "%)";
|
||||
|
||||
|
||||
@@ -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()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
188
Source/WulaFallenEmpire/Work/EnterMech/WorkGiver_EnterMech.cs
Normal file
188
Source/WulaFallenEmpire/Work/EnterMech/WorkGiver_EnterMech.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
82
Source/WulaFallenEmpire/Work/JobDriver_CarryToMech.cs
Normal file
82
Source/WulaFallenEmpire/Work/JobDriver_CarryToMech.cs
Normal 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
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
18
Source/WulaFallenEmpire/Work/JobGiver_NoPilot.cs
Normal file
18
Source/WulaFallenEmpire/Work/JobGiver_NoPilot.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
176
Source/WulaFallenEmpire/Work/RefuelMech/JobDriver_RefuelMech.cs
Normal file
176
Source/WulaFallenEmpire/Work/RefuelMech/JobDriver_RefuelMech.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
123
Source/WulaFallenEmpire/Work/RefuelMech/WorkGiver_RefuelMech.cs
Normal file
123
Source/WulaFallenEmpire/Work/RefuelMech/WorkGiver_RefuelMech.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
416
Source/WulaFallenEmpire/Work/RepairMech/JobDriver_RepairMech.cs
Normal file
416
Source/WulaFallenEmpire/Work/RepairMech/JobDriver_RepairMech.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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" />
|
||||
|
||||
Reference in New Issue
Block a user