diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index 3e60e2b2..e5ad8c5a 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/1.6/1.6/Defs/HediffDefs/WULA_Misc_Hediffs.xml b/1.6/1.6/Defs/HediffDefs/WULA_Misc_Hediffs.xml index b825b08b..01fecea4 100644 --- a/1.6/1.6/Defs/HediffDefs/WULA_Misc_Hediffs.xml +++ b/1.6/1.6/Defs/HediffDefs/WULA_Misc_Hediffs.xml @@ -202,6 +202,32 @@ + + WULA_MechShutdown + HediffWithComps + + This mech has stopped due to insufficient fuel, need to assign a colonist to refuel it to restore its operational capability. + This mech has stopped due to insufficient fuel. + + 0.01 + 1 + true + true + true + true + + +
  • + +
  • + Moving + 0.1 +
  • + + +
    +
    + WULA_MechCarrierSwitchHediff diff --git a/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml b/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml index 7d85f771..79489a62 100644 --- a/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml +++ b/1.6/1.6/Defs/JobDefs/WULA_JobDefs.xml @@ -86,7 +86,7 @@ false --> - WULA_Refuel + WULA_RefuelMech WulaFallenEmpire.WorkGiver_RefuelMech Hauling diff --git a/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml b/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml index d85a59d1..6f5f3ee5 100644 --- a/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml +++ b/1.6/1.6/Defs/PawnKinds/PawnKinds_Wula.xml @@ -179,6 +179,7 @@ PlayerColony false true + false Wula/Things/Wula_Mech_Mobile_Factory/Flying/Wula_Mech_Mobile_Factory_Flying_ 1 diff --git a/1.6/1.6/Defs/StatDefs/WULA_Stats.xml b/1.6/1.6/Defs/StatDefs/WULA_Stats.xml index 59021daa..e7d6ff9b 100644 --- a/1.6/1.6/Defs/StatDefs/WULA_Stats.xml +++ b/1.6/1.6/Defs/StatDefs/WULA_Stats.xml @@ -133,4 +133,17 @@ true WulaFallenEmpire.StatWorker_Maintenance + + + + WULA_MechArmor + + 乌拉帝国在大型战争机械和少部分护甲上使用的特殊防御装甲,当 护甲穿透乘数 * 攻击 的最终值小于此值时,该攻击将被完全抵消而不造成任何伤害。 + Apparel + FloatTwo + 0 + 0 + 0 + 5000 + \ No newline at end of file diff --git a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Machine_Weapon.xml b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Machine_Weapon.xml index 59bf4d02..42a240e4 100644 --- a/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Machine_Weapon.xml +++ b/1.6/1.6/Defs/ThingDefs_Misc/Weapons/WULA_FE_Machine_Weapon.xml @@ -477,9 +477,17 @@ 200 12 - +
  • Wula_AI_Heavy_Panzer_Weapon
  • + + +
  • + +
  • Wula_AI_Heavy_Panzer
  • + + +
    Bullet_Wula_AI_Heavy_Panzer_Main_Weapon diff --git a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml index 85cb6430..610a2796 100644 --- a/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml +++ b/1.6/1.6/Defs/ThingDefs_Races/Races_Wulaspecies.xml @@ -1,6 +1,5 @@  - Wula_Human @@ -225,7 +224,6 @@ - 0 @@ -785,7 +783,6 @@ - BulletImpact_Metal @@ -877,7 +874,6 @@ - 5 @@ -1099,695 +1095,6 @@ - - Wula_AI_Heavy_Panzer - - 乌拉帝国的中型战争机械,以悬浮的方式穿梭于战场之上,使用破坏力巨大的自动炮和车体臼炮打击敌方,是乌拉帝国前锋部队的中流砥柱。 - Wula/Things/Wula_AI_Heavy_Panzer/Wula_AI_Heavy_Panzer_Icon - - - -
  • Wula_AI_Heavy_Panzer_Main_Weapon
  • -
    - true - true -
    - - false - -
    - - 1 - 2 - 2 - 0 - - 9999 - 0 - - 1 - 1 - 2 - - - 300 - 18 - 2 - - - WULA_AI_Heavy_Panzer_Body - 20 - -
  • - MechanoidFullyFormed - 0 - Pawn_Wula_AI_Heavy_Panzer_Call -
  • -
    - 10 - - 1 - Heavy - -
    - -
  • - - -
  • Blunt
  • - - 360 - 8 - Torso - true - -
    - -
  • - 0 - Wula_AI_Heavy_Panzer_Turret_Weapon - - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (7, 7) - 20 - Any - - - -90 - - - -90 - - - -90 - - - 90 - - -
  • - - -
  • - 36000 - 5.2 - 5.4 - 0.02 - 4.0 - false -
  • - -
  • - - - Drafted - - - WULA_Hover_FlyNorth - WULA_Hover_FlyEast - WULA_Hover_FlySouth -
  • -
  • - 1.5 - 30 - Crush - 3 - false - false - false - true - false - false - false - 碾压伤害 - HAp-6"战车"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。 - Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage -
  • -
    -
    - - Wula_AI_Rocket_Panzer - - 乌拉帝国的中型战争机械,以悬浮的方式穿梭于战场之上,拥有车体臼炮和两具可以发射大量燃烧火箭弹的转轮导弹巢,但是未像其姊妹型号那样装备护盾。 - Wula/Things/Wula_AI_Heavy_Panzer/Wula_AI_Rocket_Panzer_Icon - - - -
  • Wula_AI_Heavy_Panzer_Main_Weapon
  • -
    - true - true -
    - - false - -
    - - 1 - 2 - 2 - 0 - - 9999 - 0 - - 1 - 1 - 2 - - - 300 - 18 - 2 - - - WULA_AI_Heavy_Panzer_Body - 20 - -
  • - MechanoidFullyFormed - 0 - Pawn_Wula_AI_Heavy_Panzer_Call -
  • -
    - 10 - - 1 - Heavy - -
    - -
  • - - -
  • Blunt
  • - - 360 - 8 - Torso - true - -
    - -
  • - 0 - Wula_AI_Rocket_Panzer_Turret_Weapon - - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (7, 7) - 20 - Any - - - -90 - - - -90 - - - -90 - - - 90 - - -
  • - - - -
  • - - - Drafted - - - WULA_Hover_FlyNorth - WULA_Hover_FlyEast - WULA_Hover_FlySouth -
  • -
  • - 1.5 - 30 - Crush - 3 - false - false - false - true - false - false - false - 碾压伤害 - HRp-3"喷火战车"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。 - Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage -
  • -
    -
    - - Wula_Mech_Mobile_Shield - - 乌拉帝国的中型战争机械,常被用于镇压异族聚居地的暴动。它形状非常奇怪,根本分不出头在哪,但是不要因此小瞧这个机械体——其强大的立场盾能护佑一片区域并反射大量炮火,机体放射出来的辐射则会点燃进入反射盾范围内的敌人。在相关许可开放后,它甚至可以支持机械乌拉使用其内置的相位场进行区域传送,使其获得无与伦比的机动性。 - - - true - true - - - false - - - - 1 - 3 - - 0.5 - 0.5 - 2 - - - WULA_Mech_Mobile_Shield_Body - 50 - -
  • - MechanoidFullyFormed - 0 - Pawn_Wula_Mech_Mobile_Factory_Call -
  • -
    - 5 - Heavy -
    - -
  • - - -
  • Blunt
  • - - 360 - 8 - Torso - true - -
    - - -
  • - 15 - 300 - 2400 - 30 - - - Interceptor_BlockedProjectile - Interceptor_BlockedProjectile - Shield_Break - BulletShieldGenerator_Reactivate - - (0.9, 0.2, 0.2, 0.5) - - - true - false - true - - - true - 0.85 - 30 - 0 - Interceptor_BlockedProjectile -
  • -
  • - 15 - 30 - Flame - 8 - false - false - false - true - false - false - false - 热辐射 - MSm-8"放射盾"可以打开外壳,蒸发胆敢进入反射立场内的敌军——这同时会使得它伤害附近所有的散落物品。 - Wula/UI/Commands/Wula_Psi_Titan_AreaDamage -
  • - -
  • - 15 - 60 - - -
  • WulaSpecies
  • - - true - - Skip_Entry - Skip_Exit - Psycast_Skip_Entry - - true - WULA_Mech_Mobile_Shield_Teleporter_Technology - -
    -
    - - Wula_Mech_Mobile_Factory - - 乌拉帝国的大型战争机械,简直就是一座移动堡垒——它不仅装甲厚实、火炮林立,还能在战场上生产大量的辅助战争机械以形成坚实的弹性阵线,生来就是为了粉碎坚固的要塞和顽强的抵抗。 - - - -
  • WULA_RW_Unlimit_Penetrating_Beam_Cannon
  • -
    - true - true -
    - - false - -
    - - 1 - 1 - 5 - - 9999 - 0 - - 1.75 - 1.75 - 2 - - - WULA_Mech_Mobile_Factory_Body - 50 - -
  • - MechanoidFullyFormed - 0 - Pawn_Wula_Mech_Mobile_Factory_Call -
  • -
    - 25 - - 1 - Heavy - -
    - -
  • - - -
  • Blunt
  • - - 360 - 8 - Torso - true - -
    - -
  • - 0 - Wula_CR_Mobile_Factory_Turret - - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any - - - -90 - (-1, 0, -1.45) - true - -5 - - - 90 - -5 - (3.25, 0, -1) - - - -90 - (1.5, 0, -1.45) - - - 90 - (-3.25, 0, -1) - - -
  • - - -
  • - 1 - Wula_CR_Mobile_Factory_Turret - - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any - - - -90 - (1, 0, -1.45) - -5 - - - -90 - (3.25, 0, -1) - - - -90 - (-1.6, 0, -1.45) - - - 90 - (-3.25, 0, -1) - -5 - - -
  • - - -
  • - 2 - Wula_MR_Mobile_Factory_Turret - - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any - - - -90 - (-1.3, 0, -0.45) - true - -5 - - - 90 - -5 - (2.4, 0, -0.35) - - - -90 - (2.3, 0, -0.45) - - - 90 - (-2.4, 0, -0.35) - - -
  • - - -
  • - 3 - Wula_MR_Mobile_Factory_Turret - - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any - - - -90 - (1.3, 0, -0.45) - true - -5 - - - -90 - (2.4, 0, -0.35) - - - -90 - (-2.3, 0, -0.45) - - - 90 - -5 - (-2.4, 0, -0.35) - - -
  • - - -
  • - 4 - Wula_LR_Mobile_Factory_Turret - - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any - - - -90 - (1.25, 0, 0.45) - true - -5 - - - 90 - -5 - (1.6, 0, 0.25) - - - -90 - (-2.15, 0, 0.65) - - - 90 - (-1.6, 0, 0.25) - - -
  • - - -
  • - 5 - Wula_LR_Mobile_Factory_Turret - - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any - - - -90 - (-1.25, 0, 0.45) - true - -5 - - - -90 - (1.6, 0, 0.25) - - - -90 - (2.05, 0, 0.65) - - - 90 - -5 - (-1.6, 0, 0.25) - - -
  • - - - -
  • - 9 - 300 - 2400 - 30 - - - Interceptor_BlockedProjectile - Interceptor_BlockedProjectile - Shield_Break - BulletShieldGenerator_Reactivate - - (0.9, 0.2, 0.2, 0.5) - - - true - false - true - - - true - 0.6 - 30 - 0 - Interceptor_BlockedProjectile -
  • - -
  • - - - Drafted - - - WULA_Hover_FlyNorth - WULA_Hover_FlyEast - WULA_Hover_FlySouth -
  • -
  • - 3.5 - 30 - Crush - 4 - false - false - false - true - false - false - false - 碾压伤害 - MFm-2"陆行舰"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。 - Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage -
  • -
    -
    WULA_Mech_Flyer @@ -2004,7 +1311,6 @@ - WULA_Fxxk_Goose diff --git a/1.6/1.6/Defs/ThingDefs_Races/WULA_Mechunit_Race.xml b/1.6/1.6/Defs/ThingDefs_Races/WULA_Mechunit_Race.xml index a935b9a5..7af5ee83 100644 --- a/1.6/1.6/Defs/ThingDefs_Races/WULA_Mechunit_Race.xml +++ b/1.6/1.6/Defs/ThingDefs_Races/WULA_Mechunit_Race.xml @@ -56,7 +56,6 @@ 0.1 -
  • WulaFallenEmpire.ITab_MechSkills
  • ITab_Pawn_Health
  • @@ -66,25 +65,11 @@
    - - Wula_AI_Heavy_Panzer + + Wula_AI_Heavy_Panzer - - Wula/Things/Wula_AI_Heavy_Panzer/Wula_AI_Heavy_Panzer_Icon - - - -
  • Wula_AI_Heavy_Panzer_Main_Weapon
  • -
    - true - true -
    - - false - -
    + 乌拉帝国的中型战争机械,以悬浮的方式穿梭于战场之上,使用破坏力巨大的自动炮和车体臼炮打击敌方,是乌拉帝国前锋部队的中流砥柱。 - 1 2 2 0 @@ -101,21 +86,18 @@ 18 2 + + 300 + 18 + 2 + WULA_AI_Heavy_Panzer_Body 20 - -
  • - MechanoidFullyFormed - 0 - Pawn_Wula_AI_Heavy_Panzer_Call -
  • -
    + Wula_Battle_Mech_With_1_Pilot 10 1 - Heavy -
  • @@ -167,11 +149,10 @@ 4.0 false
  • -
  • - Drafted + DraftedAndMove WULA_Hover_FlyNorth @@ -194,46 +175,17 @@ HAp-6"战车"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。 Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage
  • - -
    - - Wula_Basic_Panzer - - 乌拉帝国的中型战争机械,以悬浮的方式穿梭于战场之上,使用破坏力巨大的自动炮和车体臼炮打击敌方,是乌拉帝国前锋部队的中流砥柱。 - - 3.25 - 17500 - 0.35 - 1.25 - 1.25 - 1.5 - - - 2 - - - DD_Race_DHM69_Scythe_Body - DD_Battle_Mech_With_3_Pilot - 10 - 5 - - - 500 - 300 - 12 - - -
  • - 3 +
  • + 1 MechPilot 0.1 - DivineDiurganate/UI/Commands/DD_Enter_Mech - DivineDiurganate/UI/Commands/DD_Exit_Mech + WulaFallenEmpire/UI/Commands/DD_Enter_Mech + WulaFallenEmpire/UI/Commands/DD_Exit_Mech
  • -
  • +
  • Chemfuel 70 35 @@ -248,7 +200,7 @@ true
  • -
  • +
  • true 1 @@ -257,10 +209,10 @@ ConstructMetal
  • -
  • - DD_DHM70_Rampart_Mech_Weapon +
  • + Wula_AI_Heavy_Panzer_Main_Weapon
  • -
  • +
  • true false @@ -271,18 +223,18 @@
  • - DD_Ratkin_Pilot + Wula_PIA_Light_Unit 1 false
  • -
  • +
  • 0 1
  • -
  • + -
  • + -
  • - 0.1 +
  • + 0.1 120~180 true - 11.5 Flame 5 @@ -321,72 +272,22 @@ true 1 - - Explosion_Bomb - true true - 0 + 0
  • -
  • - 0 - DD_DHM69_Scythe_MG_Turret - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any -
  • - - -
  • - 1 - DD_DHM69_Scythe_MG_Turret - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any -
  • - - -
  • - 2 - DD_DHM69_Scythe_MG_Turret - -
  • - PawnRenderNode_TurretGun - PawnRenderNodeWorker_TurretGun - Body - (2, 2) - 20 - Any -
  • - - - -
  • - + 1 false - -
  • DD_DHM70_Rampart_Mech_Prototype @@ -396,59 +297,940 @@ false
  • - + --> + + +
    +
    + + Wula_AI_Rocket_Panzer + + 乌拉帝国的中型战争机械,以悬浮的方式穿梭于战场之上,拥有车体臼炮和两具可以发射大量燃烧火箭弹的转轮导弹巢,但是未像其姊妹型号那样装备护盾。 + + 2 + 2 + 0 + + 9999 + 0 + + 1 + 1 + 2 + + + 300 + 18 + 2 + + + 300 + 18 + 2 + + + WULA_AI_Heavy_Panzer_Body + 20 + Wula_Battle_Mech_With_1_Pilot + 10 + + 1 + + +
  • + + +
  • Blunt
  • + + 360 + 8 + Torso + true + +
    + +
  • + 0 + Wula_AI_Rocket_Panzer_Turret_Weapon + + +
  • + PawnRenderNode_TurretGun + PawnRenderNodeWorker_TurretGun + Body + (7, 7) + 20 + Any + + + -90 + + + -90 + + + -90 + + + 90 + + +
  • + + +
  • + + + DraftedAndMove + + + WULA_Hover_FlyNorth + WULA_Hover_FlyEast + WULA_Hover_FlySouth +
  • +
  • + 1.5 + 30 + Crush + 3 + false + false + false + true + false + false + false + 碾压伤害 + HAp-6"战车"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。 + Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage +
  • + +
  • + 1 + MechPilot + + 0.1 + + WulaFallenEmpire/UI/Commands/DD_Enter_Mech + WulaFallenEmpire/UI/Commands/DD_Exit_Mech +
  • +
  • + Chemfuel + 70 + 35 + + 1 + 240 + + (0.6, 0.5, 0.4) + + true + 1 + + true +
  • +
  • + true + 1 + + 10 + + + ConstructMetal +
  • +
  • + Wula_AI_Heavy_Panzer_Main_Weapon +
  • +
  • + true + false + + 1.0 + true + false + 3 + + +
  • + Wula_PIA_Light_Unit + 1 + false +
  • + + +
  • + 0 + 1 +
  • + + + + + +
  • + 0.1 + 120~180 + true + + 11.5 + Flame + 5 + 2 + 0.25 + true + true + true + 1 + + Explosion_Bomb + + true + true + 0 +
  • + + +
    +
    + + Wula_Mech_Mobile_Shield + + 乌拉帝国的中型战争机械,常被用于镇压异族聚居地的暴动。它形状非常奇怪,根本分不出头在哪,但是不要因此小瞧这个机械体——其强大的立场盾能护佑一片区域并反射大量炮火,机体放射出来的辐射则会点燃进入反射盾范围内的敌人。 + + 3 + + 0.5 + 0.5 + 2 + + + 300 + 18 + 2 + 50 + + + 300 + 18 + 2 + 50 + + + WULA_Mech_Mobile_Shield_Body + 20 + Wula_Battle_Mech_With_1_Pilot + 5 + + +
  • + + +
  • Blunt
  • + + 360 + 8 + Torso + true + +
    + +
  • + 15 + 300 + 2400 + 30 + + + Interceptor_BlockedProjectile + Interceptor_BlockedProjectile + Shield_Break + BulletShieldGenerator_Reactivate + + (0.9, 0.2, 0.2, 0.5) + + + true + false + true + + + true + 0.85 + 30 + 0 + Interceptor_BlockedProjectile +
  • +
  • + 15 + 30 + Flame + 8 + false + false + false + true + false + false + false + 热辐射 + MSm-8"放射盾"可以打开外壳,蒸发胆敢进入反射立场内的敌军——这同时会使得它伤害附近所有的散落物品。 + Wula/UI/Commands/Wula_Psi_Titan_AreaDamage +
  • + + +
  • + 1 + MechPilot + + 0.1 + + WulaFallenEmpire/UI/Commands/DD_Enter_Mech + WulaFallenEmpire/UI/Commands/DD_Exit_Mech +
  • +
  • + Chemfuel + 70 + 35 + + 1 + 240 + + (0.6, 0.5, 0.4) + + true + 1 + + true +
  • +
  • + true + 1 + + 10 + + + ConstructMetal +
  • +
  • + Wula_AI_Heavy_Panzer_Main_Weapon +
  • +
  • + true + false + + 1.0 + true + false + 3 + + +
  • + Wula_PIA_Light_Unit + 1 + false +
  • + + +
  • + 0 + 1 +
  • + + + + + +
  • + 0.1 + 120~180 + true + + 11.5 + Flame + 5 + 2 + 0.25 + true + true + true + 1 + + Explosion_Bomb + + true + true + 0 +
  • + + +
    +
    + + Wula_Mech_Mobile_Factory + + 乌拉帝国的大型战争机械,简直就是一座移动堡垒——它不仅装甲厚实、火炮林立,还能在战场上生产大量的辅助战争机械以形成坚实的弹性阵线,生来就是为了粉碎坚固的要塞和顽强的抵抗。 + + 1 + 5 + + 9999 + 0 + + 1.75 + 1.75 + 2 + + + 300 + 18 + 2 + + + 300 + 18 + 2 + + + WULA_Mech_Mobile_Factory_Body + 50 + Wula_Battle_Mech_With_1_Pilot + 10 + + 1 + + +
  • + + +
  • Blunt
  • + + 360 + 8 + Torso + true + +
    + +
  • + 0 + Wula_CR_Mobile_Factory_Turret + + +
  • + PawnRenderNode_TurretGun + PawnRenderNodeWorker_TurretGun + Body + (2, 2) + 20 + Any + + + -90 + (-1, 0, -1.45) + true + -5 + + + 90 + -5 + (3.25, 0, -1) + + + -90 + (1.5, 0, -1.45) + + + 90 + (-3.25, 0, -1) + + +
  • + + +
  • + 1 + Wula_CR_Mobile_Factory_Turret + + +
  • + PawnRenderNode_TurretGun + PawnRenderNodeWorker_TurretGun + Body + (2, 2) + 20 + Any + + + -90 + (1, 0, -1.45) + -5 + + + -90 + (3.25, 0, -1) + + + -90 + (-1.6, 0, -1.45) + + + 90 + (-3.25, 0, -1) + -5 + + +
  • + + +
  • + 2 + Wula_MR_Mobile_Factory_Turret + + +
  • + PawnRenderNode_TurretGun + PawnRenderNodeWorker_TurretGun + Body + (2, 2) + 20 + Any + + + -90 + (-1.3, 0, -0.45) + true + -5 + + + 90 + -5 + (2.4, 0, -0.35) + + + -90 + (2.3, 0, -0.45) + + + 90 + (-2.4, 0, -0.35) + + +
  • + + +
  • + 3 + Wula_MR_Mobile_Factory_Turret + + +
  • + PawnRenderNode_TurretGun + PawnRenderNodeWorker_TurretGun + Body + (2, 2) + 20 + Any + + + -90 + (1.3, 0, -0.45) + true + -5 + + + -90 + (2.4, 0, -0.35) + + + -90 + (-2.3, 0, -0.45) + + + 90 + -5 + (-2.4, 0, -0.35) + + +
  • + + +
  • + 4 + Wula_LR_Mobile_Factory_Turret + + +
  • + PawnRenderNode_TurretGun + PawnRenderNodeWorker_TurretGun + Body + (2, 2) + 20 + Any + + + -90 + (1.25, 0, 0.45) + true + -5 + + + 90 + -5 + (1.6, 0, 0.25) + + + -90 + (-2.15, 0, 0.65) + + + 90 + (-1.6, 0, 0.25) + + +
  • + + +
  • + 5 + Wula_LR_Mobile_Factory_Turret + + +
  • + PawnRenderNode_TurretGun + PawnRenderNodeWorker_TurretGun + Body + (2, 2) + 20 + Any + + + -90 + (-1.25, 0, 0.45) + true + -5 + + + -90 + (1.6, 0, 0.25) + + + -90 + (2.05, 0, 0.65) + + + 90 + -5 + (-1.6, 0, 0.25) + + +
  • + + +
  • + 9 + 300 + 2400 + 30 + + + Interceptor_BlockedProjectile + Interceptor_BlockedProjectile + Shield_Break + BulletShieldGenerator_Reactivate + + (0.9, 0.2, 0.2, 0.5) + + + true + false + true + + + true + 0.6 + 30 + 0 + Interceptor_BlockedProjectile +
  • +
  • + + + DraftedAndMove + + + WULA_Hover_FlyNorth + WULA_Hover_FlyEast + WULA_Hover_FlySouth +
  • +
  • + 3.5 + 30 + Crush + 4 + false + false + false + true + false + false + false + 碾压伤害 + MFm-2"陆行舰"可以将舰身稍微下沉一些并创造低压区,以碾压靠近的敌军——这同时会使得它伤害附近所有的散落物品。 + Wula/UI/Commands/Wula_Mech_Mobile_Factory_AreaDamage +
  • + +
  • + 1 + MechPilot + + 0.1 + + WulaFallenEmpire/UI/Commands/DD_Enter_Mech + WulaFallenEmpire/UI/Commands/DD_Exit_Mech +
  • +
  • + Chemfuel + 70 + 35 + + 1 + 240 + + (0.6, 0.5, 0.4) + + true + 1 + + true +
  • +
  • + true + 1 + + 10 + + + ConstructMetal +
  • +
  • + Wula_AI_Heavy_Panzer_Main_Weapon +
  • +
  • + true + false + + 1.0 + true + false + 3 + + +
  • + Wula_PIA_Light_Unit + 1 + false +
  • + + +
  • + 0 + 1 +
  • + + + + + +
  • + 0.1 + 120~180 + true + + 11.5 + Flame + 5 + 2 + 0.25 + true + true + true + 1 + + Explosion_Bomb + + true + true + 0 +
  • + +
    - - DD_DHM70_Rampart_Mech - - DD_DHM70_Rampart_Mech - PlayerColony - false - false - false - 800 - -
  • DD_DHM70_Rampart_Mech_Weapon
  • -
    - 99999~99999 - -
  • - - DivineDiurganate/Things/DD_DHM70_Rampart/Naked_Thin - 6.5 - - (2 ,2, 0.8) - (0,0,-0.15) - - -
  • -
    -
    - - DD_DHM70_Rampart_Mech_Prototype - - DD_DHM70_Rampart_Mech - PlayerColony - false - false - false - 900 - -
  • DD_DHM70_Rampart_Mech_Weapon
  • -
    - 99999~99999 - -
  • - - DivineDiurganate/Things/DD_DHM70_Rampart/Prototype - 6.5 - - (2 ,2, 0.8) - (0,0,-0.15) - - -
  • -
    -
    \ No newline at end of file diff --git a/1.6/1.6/Defs/ThinkTreeDefs/WULA_ThinkTrees.xml b/1.6/1.6/Defs/ThinkTreeDefs/WULA_ThinkTrees.xml index 16ec116b..bde0a03f 100644 --- a/1.6/1.6/Defs/ThinkTreeDefs/WULA_ThinkTrees.xml +++ b/1.6/1.6/Defs/ThinkTreeDefs/WULA_ThinkTrees.xml @@ -560,4 +560,99 @@ + + + Wula_Battle_Mech_With_1_Pilot + + + +
  • + Despawned +
  • + + +
  • + +
  • + +
  • + + +
  • + Downed +
  • + + +
  • + 1 + +
  • + +
  • + + +
  • + BerserkMechanoid + +
  • + +
  • +
  • + Deadly +
  • +
    + + + + + +
  • + + +
  • + +
  • + DraftedOrder + +
  • +
  • + +
  • + + + + +
  • + LordDuty +
  • + + +
  • + +
  • + Idle + +
  • + None +
  • + + + + + + +
  • + Idle + +
  • + Deadly +
  • + + + + +
  • + + + diff --git a/Source/WulaFallenEmpire/HarmonyPatches/Hediff_Mechlink_PostAdd_Patch.cs b/Source/WulaFallenEmpire/HarmonyPatches/Hediff_Mechlink_PostAdd_Patch.cs index ac2986de..3a6149bc 100644 --- a/Source/WulaFallenEmpire/HarmonyPatches/Hediff_Mechlink_PostAdd_Patch.cs +++ b/Source/WulaFallenEmpire/HarmonyPatches/Hediff_Mechlink_PostAdd_Patch.cs @@ -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) { diff --git a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_ColonistBarMech.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_ColonistBarMech.cs index cdcec392..9f448009 100644 --- a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_ColonistBarMech.cs +++ b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_ColonistBarMech.cs @@ -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}"); // 出错时不改变原列表 } } diff --git a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_MechSpecificWeapon.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_MechSpecificWeapon.cs index a2b4b048..aff65f78 100644 --- a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_MechSpecificWeapon.cs +++ b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_MechSpecificWeapon.cs @@ -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; } } diff --git a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_RomanceFix.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_RomanceFix.cs index 2d42f528..aeb8e82c 100644 --- a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_RomanceFix.cs +++ b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_RomanceFix.cs @@ -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; } diff --git a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_TakeDamage.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_TakeDamage.cs index b761647c..62201f3f 100644 --- a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_TakeDamage.cs +++ b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_TakeDamage.cs @@ -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.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) diff --git a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_mechunit.cs b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_mechunit.cs index 238de388..ef4e4bef 100644 --- a/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_mechunit.cs +++ b/Source/WulaFallenEmpire/HarmonyPatches/WULA_MechUnit/Patch_mechunit.cs @@ -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; // ʱִԭʼ } } diff --git a/Source/WulaFallenEmpire/HediffComp/WULA_SyncedWithMech/HediffCompProperties_SyncedWithMech.cs b/Source/WulaFallenEmpire/HediffComp/WULA_SyncedWithMech/HediffCompProperties_SyncedWithMech.cs index 3b9afd4b..516cf358 100644 --- a/Source/WulaFallenEmpire/HediffComp/WULA_SyncedWithMech/HediffCompProperties_SyncedWithMech.cs +++ b/Source/WulaFallenEmpire/HediffComp/WULA_SyncedWithMech/HediffCompProperties_SyncedWithMech.cs @@ -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}"); } } diff --git a/Source/WulaFallenEmpire/ITab/ITab_MechSkills.cs b/Source/WulaFallenEmpire/ITab/ITab_MechSkills.cs index e495e336..4008fd4a 100644 --- a/Source/WulaFallenEmpire/ITab/ITab_MechSkills.cs +++ b/Source/WulaFallenEmpire/ITab/ITab_MechSkills.cs @@ -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() == 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(); 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) diff --git a/Source/WulaFallenEmpire/Pawn/WULA_BrokenPersonality/MentalBreakWorker_BrokenPersonality.cs b/Source/WulaFallenEmpire/MentalState/BrokenPersonality/MentalBreakWorker_BrokenPersonality.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_BrokenPersonality/MentalBreakWorker_BrokenPersonality.cs rename to Source/WulaFallenEmpire/MentalState/BrokenPersonality/MentalBreakWorker_BrokenPersonality.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_BrokenPersonality/MentalStateDefExtension_BrokenPersonality.cs b/Source/WulaFallenEmpire/MentalState/BrokenPersonality/MentalStateDefExtension_BrokenPersonality.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_BrokenPersonality/MentalStateDefExtension_BrokenPersonality.cs rename to Source/WulaFallenEmpire/MentalState/BrokenPersonality/MentalStateDefExtension_BrokenPersonality.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_BrokenPersonality/MentalState_BrokenPersonality.cs b/Source/WulaFallenEmpire/MentalState/BrokenPersonality/MentalState_BrokenPersonality.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_BrokenPersonality/MentalState_BrokenPersonality.cs rename to Source/WulaFallenEmpire/MentalState/BrokenPersonality/MentalState_BrokenPersonality.cs diff --git a/Source/WulaFallenEmpire/MentalState/MentalState_MechNoPilot.cs b/Source/WulaFallenEmpire/MentalState/MentalState_MechNoPilot.cs new file mode 100644 index 00000000..56eb3aed --- /dev/null +++ b/Source/WulaFallenEmpire/MentalState/MentalState_MechNoPilot.cs @@ -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; + } + } +} diff --git a/Source/WulaFallenEmpire/Pawn/Mechunit.cs b/Source/WulaFallenEmpire/Pawn/Mechunit.cs index 75e2c988..68aae6c4 100644 --- a/Source/WulaFallenEmpire/Pawn/Mechunit.cs +++ b/Source/WulaFallenEmpire/Pawn/Mechunit.cs @@ -63,7 +63,7 @@ namespace WulaFallenEmpire var pilotComp = this.TryGetComp(); 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(); if (pilotComp != null && !pilotComp.HasPilots) { - command_Toggle.Disable("DD_NoPilot".Translate()); + command_Toggle.Disable("WULA_NoPilot".Translate()); } command_Toggle.tutorTag = ((!base.Drafted) ? "Draft" : "Undraft"); diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutoMechCarrier/CompAutoMechCarrier.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutoMechCarrier/CompAutoMechCarrier.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutoMechCarrier/CompAutoMechCarrier.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutoMechCarrier/CompAutoMechCarrier.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutoMechCarrier/CompProperties_AutoMechCarrier.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutoMechCarrier/CompProperties_AutoMechCarrier.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutoMechCarrier/CompProperties_AutoMechCarrier.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutoMechCarrier/CompProperties_AutoMechCarrier.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutoMechCarrier/PawnProductionEntry.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutoMechCarrier/PawnProductionEntry.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutoMechCarrier/PawnProductionEntry.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutoMechCarrier/PawnProductionEntry.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/CompAutonomousMech.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/CompAutonomousMech.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/CompAutonomousMech.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/CompAutonomousMech.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/DroneGizmo.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/DroneGizmo.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/DroneGizmo.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/DroneGizmo.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/JobGiver_DroneSelfShutdown.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/JobGiver_DroneSelfShutdown.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/JobGiver_DroneSelfShutdown.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/JobGiver_DroneSelfShutdown.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneEnergy.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/PawnColumnWorker_DroneEnergy.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneEnergy.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/PawnColumnWorker_DroneEnergy.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneWorkMode.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/PawnColumnWorker_DroneWorkMode.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/PawnColumnWorker_DroneWorkMode.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/PawnColumnWorker_DroneWorkMode.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalAutonomousWorkMode.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/ThinkNode_ConditionalAutonomousWorkMode.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalAutonomousWorkMode.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/ThinkNode_ConditionalAutonomousWorkMode.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalLowEnergy_Drone.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/ThinkNode_ConditionalLowEnergy_Drone.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalLowEnergy_Drone.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/ThinkNode_ConditionalLowEnergy_Drone.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalNeedRecharge.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/ThinkNode_ConditionalNeedRecharge.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalNeedRecharge.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/ThinkNode_ConditionalNeedRecharge.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalWorkMode_Drone.cs b/Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/ThinkNode_ConditionalWorkMode_Drone.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_AutonomousMech/ThinkNode_ConditionalWorkMode_Drone.cs rename to Source/WulaFallenEmpire/Pawn_Comps/AutonomousMech/ThinkNode_ConditionalWorkMode_Drone.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Cat_Invisible/CompFighterInvisible.cs b/Source/WulaFallenEmpire/Pawn_Comps/Cat_Invisible/CompFighterInvisible.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_Cat_Invisible/CompFighterInvisible.cs rename to Source/WulaFallenEmpire/Pawn_Comps/Cat_Invisible/CompFighterInvisible.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Cat_Invisible/CompProperties_FighterInvisible.cs b/Source/WulaFallenEmpire/Pawn_Comps/Cat_Invisible/CompProperties_FighterInvisible.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_Cat_Invisible/CompProperties_FighterInvisible.cs rename to Source/WulaFallenEmpire/Pawn_Comps/Cat_Invisible/CompProperties_FighterInvisible.cs diff --git a/Source/WulaFallenEmpire/Pawn_Comps/DefaultPilotEntry/CompMechDefaultPilot.cs b/Source/WulaFallenEmpire/Pawn_Comps/DefaultPilotEntry/CompMechDefaultPilot.cs index f45717e3..2a0c12ff 100644 --- a/Source/WulaFallenEmpire/Pawn_Comps/DefaultPilotEntry/CompMechDefaultPilot.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/DefaultPilotEntry/CompMechDefaultPilot.cs @@ -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; } } diff --git a/Source/WulaFallenEmpire/Pawn/WULA_CompHediffGiver/CompHediffGiver.cs b/Source/WulaFallenEmpire/Pawn_Comps/HediffGiver/CompHediffGiver.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_CompHediffGiver/CompHediffGiver.cs rename to Source/WulaFallenEmpire/Pawn_Comps/HediffGiver/CompHediffGiver.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_CompHediffGiver/CompProperties_HediffGiver.cs b/Source/WulaFallenEmpire/Pawn_Comps/HediffGiver/CompProperties_HediffGiver.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_CompHediffGiver/CompProperties_HediffGiver.cs rename to Source/WulaFallenEmpire/Pawn_Comps/HediffGiver/CompProperties_HediffGiver.cs diff --git a/Source/WulaFallenEmpire/Pawn_Comps/HighSpeedCollision/CompHighSpeedCollision.cs b/Source/WulaFallenEmpire/Pawn_Comps/HighSpeedCollision/CompHighSpeedCollision.cs new file mode 100644 index 00000000..91ab5cce --- /dev/null +++ b/Source/WulaFallenEmpire/Pawn_Comps/HighSpeedCollision/CompHighSpeedCollision.cs @@ -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 +{ + /// + /// 高速移动撞击组件 + /// + public class CompHighSpeedCollision : ThingComp + { + // === 运行时状态 === + private enum SpeedStage + { + Stage0, // 0阶段:不移动 + Stage1, // 1阶段:低速碰撞 + Stage2 // 2阶段:高速击飞 + } + + private SpeedStage currentStage = SpeedStage.Stage0; + private int stageTransitionCooldown = 0; + + // 用于计算速度的帧历史 + private Queue speedHistory = new Queue(); + private IntVec3 lastPosition = IntVec3.Invalid; + private int lastPositionTick = -1; + + // 已处理的敌人记录(避免同一帧重复处理) + private HashSet processedPawnsThisTick = new HashSet(); + + // === 缓存 === + 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); + } + + /// + /// 处理每帧逻辑 + /// + 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); + } + + /// + /// 计算当前速度(每秒格数) + /// + 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; + } + + /// + /// 更新速度历史 + /// + private void UpdateSpeedHistory(float currentSpeed) + { + speedHistory.Enqueue(currentSpeed); + while (speedHistory.Count > Props.speedHistoryFrameCount) + { + speedHistory.Dequeue(); + } + } + + /// + /// 获取平均速度 + /// + private float GetAverageSpeed() + { + if (speedHistory.Count == 0) + return 0f; + + float sum = 0f; + foreach (float speed in speedHistory) + { + sum += speed; + } + + return sum / speedHistory.Count; + } + + /// + /// 确定速度阶段 + /// + 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"); + } + } + } + + /// + /// 应用阶段效果 + /// + private void ApplyStageEffects(Pawn pawn) + { + if (currentStage == SpeedStage.Stage0) + return; + + // 获取碰撞区域内的所有敌人 + List 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); + } + } + + /// + /// 应用阶段1效果(伤害+hediff) + /// + 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()?.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}"); + } + } + + /// + /// 应用阶段2效果(伤害+击飞) + /// + 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"); + } + } + + /// + /// 执行击飞(参考CompAbilityEffect_FanShapedStunKnockback) + /// + 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); + } + } + + /// + /// 计算击飞方向 + /// + 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); + } + + /// + /// 寻找击飞位置 + /// + 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; + } + + /// + /// 检查击飞位置是否有效 + /// + 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; + } + + /// + /// 创建击飞飞行器 + /// + 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}"); + } + } + + /// + /// 应用伤害 + /// + 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); + } + + /// + /// 播放阶段1效果 + /// + 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)); + } + } + + /// + /// 播放阶段2效果 + /// + 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)); + } + } + + /// + /// 获取碰撞区域内的所有敌人 + /// + private List GetEnemiesInCollisionArea(Pawn pawn) + { + List enemies = new List(); + + // 获取碰撞区域 + CellRect collisionArea = GetCollisionArea(pawn); + + // 检查区域内的每个单元格 + foreach (IntVec3 cell in collisionArea) + { + if (!cell.InBounds(pawn.Map)) + continue; + + // 获取单元格内的所有pawn + List 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; + } + + /// + /// 获取碰撞区域 + /// + 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; + } + + /// + /// 检查是否是有效目标 + /// + 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; + } + + /// + /// 检查Pawn是否可以移动 + /// + private bool CanMove(Pawn pawn) + { + if (pawn.Downed || pawn.Dead || pawn.InMentalState) + return false; + + if (pawn.stances?.stunner?.Stunned ?? false) + return false; + + return true; + } + + /// + /// 重置到阶段0 + /// + private void ResetToStage0() + { + currentStage = SpeedStage.Stage0; + + // 清空速度历史 + speedHistory.Clear(); + for (int i = 0; i < Props.speedHistoryFrameCount; i++) + { + speedHistory.Enqueue(0f); + } + } + + /// + /// 绘制调试视觉效果 + /// + 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)); + } + + /// + /// 获取阶段颜色 + /// + 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; + } + } + + /// + /// 获取调试信息 + /// + 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 speedList = speedHistory.ToList(); + Scribe_Collections.Look(ref speedList, "speedHistory", LookMode.Value); + } + else if (Scribe.mode == LoadSaveMode.LoadingVars) + { + List 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); + } + } + } + } + + /// + /// 高速移动撞击组件属性 + /// + public class CompProperties_HighSpeedCollision : CompProperties + { + // === 速度阈值配置 === + + /// + /// 进入阶段1所需的最小速度(格/秒) + /// + public float minSpeedForStage1 = 3f; + + /// + /// 进入阶段2所需的最小速度(格/秒) + /// + public float minSpeedForStage2 = 6f; + + /// + /// 速度历史帧数(用于计算平均速度) + /// + public int speedHistoryFrameCount = 10; + + /// + /// 阶段转换冷却时间(tick) + /// + public int stageTransitionCooldownTicks = 5; + + // === 碰撞区域配置 === + + /// + /// 碰撞区域半径(以pawn为中心的正方形) + /// + public int collisionAreaRadius = 1; + + // === 目标过滤 === + + /// + /// 只影响敌人 + /// + public bool onlyAffectEnemies = true; + + /// + /// 排除友方单位 + /// + public bool excludeAlliedPawns = true; + + /// + /// 排除中立单位 + /// + public bool excludeNeutralPawns = false; + + // === 阶段1效果配置 === + + /// + /// 阶段1伤害类型 + /// + public DamageDef stage1DamageDef = DamageDefOf.Blunt; + + /// + /// 阶段1伤害量 + /// + public float stage1DamageAmount = 5f; + + /// + /// 阶段1护甲穿透 + /// + public float armorPenetration = 0f; + + /// + /// 阶段1应用的hediff + /// + public HediffDef stage1Hediff; + + /// + /// 阶段1hediff持续时间(tick) + /// + public int stage1HediffDurationTicks = 60; + + /// + /// 拥有hediff的目标是否免疫伤害 + /// + public bool stage1HediffPreventsDamage = true; + + /// + /// 阶段1效果器 + /// + public EffecterDef stage1Effecter; + + /// + /// 阶段1音效 + /// + public SoundDef stage1Sound; + + // === 阶段2效果配置 === + + /// + /// 阶段2伤害类型 + /// + public DamageDef stage2DamageDef = DamageDefOf.Blunt; + + /// + /// 阶段2伤害量 + /// + public float stage2DamageAmount = 10f; + + /// + /// 阶段2击退距离 + /// + public int stage2KnockbackDistance = 3; + + /// + /// 击退是否需要视线 + /// + public bool requireLineOfSightForKnockback = true; + + /// + /// 阶段2效果器 + /// + public EffecterDef stage2Effecter; + + /// + /// 阶段2音效 + /// + public SoundDef stage2Sound; + + // === 击飞配置 === + + /// + /// 击退飞行器定义 + /// + public ThingDef knockbackFlyerDef; + + /// + /// 飞行效果器 + /// + public EffecterDef flightEffecterDef; + + /// + /// 落地音效 + /// + public SoundDef landingSound; + + // === 调试配置 === + + /// + /// 启用调试日志 + /// + public bool enableDebugLogging = false; + + /// + /// 启用调试视觉效果 + /// + public bool enableDebugVisuals = false; + + /// + /// 绘制速度历史图 + /// + public bool debugDrawSpeedHistory = false; + + public CompProperties_HighSpeedCollision() + { + compClass = typeof(CompHighSpeedCollision); + } + } +} diff --git a/Source/WulaFallenEmpire/Pawn_Comps/MechArmor/CompMechArmor.cs b/Source/WulaFallenEmpire/Pawn_Comps/MechArmor/CompMechArmor.cs deleted file mode 100644 index de554798..00000000 --- a/Source/WulaFallenEmpire/Pawn_Comps/MechArmor/CompMechArmor.cs +++ /dev/null @@ -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 -{ - /// - /// 机甲装甲组件:提供基于装甲值的伤害减免系统 - /// - 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; - - /// - /// 检查伤害是否被装甲抵消 - /// - /// 伤害信息 - /// true=伤害被抵消,false=伤害有效 - 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; - } - - /// - /// 显示阻挡效果 - /// - 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 - ); - } - } - - /// - /// 获取装甲信息(用于调试) - /// - public string GetArmorInfo() - { - return $"{parent.LabelShort}的装甲系统\n" + - $"当前装甲值: {CurrentArmor:F1}\n" + - $"阻挡规则: 穿甲伤害 < 装甲值\n" + - $"统计: 已阻挡 {blockedHits}/{totalHits} 次攻击\n" + - $"阻挡率: {(totalHits > 0 ? (float)blockedHits / totalHits * 100 : 0):F1}%"; - } - - /// - /// 获取调试按钮 - /// - public override IEnumerable 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); - } - } - - /// - /// 机甲装甲组件属性 - /// - 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); - } - } -} diff --git a/Source/WulaFallenEmpire/Pawn_Comps/MechFuel/CompMechFuel.cs b/Source/WulaFallenEmpire/Pawn_Comps/MechFuel/CompMechFuel.cs index 57e96611..1c4c7219 100644 --- a/Source/WulaFallenEmpire/Pawn_Comps/MechFuel/CompMechFuel.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/MechFuel/CompMechFuel.cs @@ -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.Get("WulaFallenEmpire/UI/Commands/DD_Refuel_Mech"), + defaultLabel = "WULA_RefuelNow".Translate(), + defaultDesc = "WULA_RefuelNowDesc".Translate(), + icon = ContentFinder.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.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.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.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") + "%)"; diff --git a/Source/WulaFallenEmpire/Pawn_Comps/MechFuel/Gizmo_MechFuelStatus.cs b/Source/WulaFallenEmpire/Pawn_Comps/MechFuel/Gizmo_MechFuelStatus.cs index 2ea975fa..28dc7eab 100644 --- a/Source/WulaFallenEmpire/Pawn_Comps/MechFuel/Gizmo_MechFuelStatus.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/MechFuel/Gizmo_MechFuelStatus.cs @@ -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() ); } diff --git a/Source/WulaFallenEmpire/Pawn_Comps/MechMovementSound/CompMechMovementSound.cs b/Source/WulaFallenEmpire/Pawn_Comps/MechMovementSound/CompMechMovementSound.cs index adaf0f04..5870af53 100644 --- a/Source/WulaFallenEmpire/Pawn_Comps/MechMovementSound/CompMechMovementSound.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/MechMovementSound/CompMechMovementSound.cs @@ -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; } } diff --git a/Source/WulaFallenEmpire/Pawn_Comps/MechMovementSound/CompProperties_MechMovementSound.cs b/Source/WulaFallenEmpire/Pawn_Comps/MechMovementSound/CompProperties_MechMovementSound.cs index be894f41..b0b9f9f5 100644 --- a/Source/WulaFallenEmpire/Pawn_Comps/MechMovementSound/CompProperties_MechMovementSound.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/MechMovementSound/CompProperties_MechMovementSound.cs @@ -59,7 +59,7 @@ namespace WulaFallenEmpire // 如果需要驾驶员,检查是否配置了驾驶员容器 if (requirePilot && parentDef.GetCompProperties() == 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}"); } } } diff --git a/Source/WulaFallenEmpire/Pawn_Comps/MechPilotHolder/CompMechPilotHolder.cs b/Source/WulaFallenEmpire/Pawn_Comps/MechPilotHolder/CompMechPilotHolder.cs index 62ad3c17..238eb8e9 100644 --- a/Source/WulaFallenEmpire/Pawn_Comps/MechPilotHolder/CompMechPilotHolder.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/MechPilotHolder/CompMechPilotHolder.cs @@ -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); } diff --git a/Source/WulaFallenEmpire/Pawn_Comps/MechRepairable/CompMechRepairable.cs b/Source/WulaFallenEmpire/Pawn_Comps/MechRepairable/CompMechRepairable.cs index 6f68edf5..08f1df22 100644 --- a/Source/WulaFallenEmpire/Pawn_Comps/MechRepairable/CompMechRepairable.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/MechRepairable/CompMechRepairable.cs @@ -84,16 +84,16 @@ namespace WulaFallenEmpire { Command_Action repairCommand = new Command_Action { - defaultLabel = "DD_ForceRepair".Translate(), - defaultDesc = "DD_ForceRepairDesc".Translate(), - icon = ContentFinder.Get("WulaFallenEmpire/UI/Commands/DD_Repair_Mech"), + defaultLabel = "WULA_ForceRepair".Translate(), + defaultDesc = "WULA_ForceRepairDesc".Translate(), + icon = ContentFinder.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.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.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.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()) diff --git a/Source/WulaFallenEmpire/Pawn_Comps/MoteEmitterNorthward/CompMoteEmitterNorthward.cs b/Source/WulaFallenEmpire/Pawn_Comps/MoteEmitterNorthward/CompMoteEmitterNorthward.cs index 1c9cc887..8c76b946 100644 --- a/Source/WulaFallenEmpire/Pawn_Comps/MoteEmitterNorthward/CompMoteEmitterNorthward.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/MoteEmitterNorthward/CompMoteEmitterNorthward.cs @@ -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}"); } } diff --git a/Source/WulaFallenEmpire/Pawn/WULA_PawnRenderExtra/Comp_PawnRenderExtra.cs b/Source/WulaFallenEmpire/Pawn_Comps/PawnRenderExtra/Comp_PawnRenderExtra.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_PawnRenderExtra/Comp_PawnRenderExtra.cs rename to Source/WulaFallenEmpire/Pawn_Comps/PawnRenderExtra/Comp_PawnRenderExtra.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Flight/CompPawnFlight.cs b/Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/CompPawnFlight.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_Flight/CompPawnFlight.cs rename to Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/CompPawnFlight.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Flight/CompProperties_PawnFlight.cs b/Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/CompProperties_PawnFlight.cs similarity index 77% rename from Source/WulaFallenEmpire/Pawn/WULA_Flight/CompProperties_PawnFlight.cs rename to Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/CompProperties_PawnFlight.cs index ae079fbb..62618e9e 100644 --- a/Source/WulaFallenEmpire/Pawn/WULA_Flight/CompProperties_PawnFlight.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/CompProperties_PawnFlight.cs @@ -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 mechForbiddenJobs = new List - { - 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); } } -} +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Flight/PawnRenderNodeWorker_AttachmentBody_NoFlight.cs b/Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/PawnRenderNodeWorker_AttachmentBody_NoFlight.cs similarity index 100% rename from Source/WulaFallenEmpire/Pawn/WULA_Flight/PawnRenderNodeWorker_AttachmentBody_NoFlight.cs rename to Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/PawnRenderNodeWorker_AttachmentBody_NoFlight.cs diff --git a/Source/WulaFallenEmpire/Pawn/WULA_Flight/Pawn_FlightTrackerPatches.cs b/Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/Pawn_FlightTrackerPatches.cs similarity index 84% rename from Source/WulaFallenEmpire/Pawn/WULA_Flight/Pawn_FlightTrackerPatches.cs rename to Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/Pawn_FlightTrackerPatches.cs index 000a9bb3..cfb5324b 100644 --- a/Source/WulaFallenEmpire/Pawn/WULA_Flight/Pawn_FlightTrackerPatches.cs +++ b/Source/WulaFallenEmpire/Pawn_Comps/Pawn_Flight/Pawn_FlightTrackerPatches.cs @@ -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) { diff --git a/Source/WulaFallenEmpire/Utils/DefInjectedExportUtility.cs b/Source/WulaFallenEmpire/Utils/DefInjectedExportUtility.cs index 77160374..7e281f1a 100644 --- a/Source/WulaFallenEmpire/Utils/DefInjectedExportUtility.cs +++ b/Source/WulaFallenEmpire/Utils/DefInjectedExportUtility.cs @@ -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); diff --git a/Source/WulaFallenEmpire/Work/EnterMech/FloatMenuOptionProvider_EnterMech.cs b/Source/WulaFallenEmpire/Work/EnterMech/FloatMenuOptionProvider_EnterMech.cs new file mode 100644 index 00000000..ec8039bd --- /dev/null +++ b/Source/WulaFallenEmpire/Work/EnterMech/FloatMenuOptionProvider_EnterMech.cs @@ -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(); + 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(); + 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(); + + 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; + } + } +} diff --git a/Source/WulaFallenEmpire/Work/EnterMech/JobDriver_EnterMech.cs b/Source/WulaFallenEmpire/Work/EnterMech/JobDriver_EnterMech.cs new file mode 100644 index 00000000..40b9616e --- /dev/null +++ b/Source/WulaFallenEmpire/Work/EnterMech/JobDriver_EnterMech.cs @@ -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 MakeNewToils() + { + // 0. 初始检查 + AddFailCondition(() => + { + var mech = TargetThingA as Wulamechunit; + if (mech == null || mech.Destroyed) + { + return true; + } + + var comp = mech.GetComp(); + 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(); + 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; + } + } +} diff --git a/Source/WulaFallenEmpire/Work/EnterMech/WorkGiver_EnterMech.cs b/Source/WulaFallenEmpire/Work/EnterMech/WorkGiver_EnterMech.cs new file mode 100644 index 00000000..6e9ed9d5 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/EnterMech/WorkGiver_EnterMech.cs @@ -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 cachedMechDefs = null; + + public override PathEndMode PathEndMode => PathEndMode.Touch; + + // 获取所有机甲定义的列表 + private List GetAllMechDefs() + { + if (cachedMechDefs == null) + { + cachedMechDefs = new List(); + + // 搜索所有ThingDef,找出继承自Wulamechunit的类 + foreach (var def in DefDatabase.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(); + 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 PotentialWorkThingsGlobal(Pawn pawn) + { + try + { + // 只搜索玩家拥有的机甲 + List potentialMechs = new List(); + + // 获取地图中的所有机甲 + 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() != 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(); + } + } + + 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; + } + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Work/ForceEjectPilot/FloatMenuOptionProvider_ForceEjectPilot.cs b/Source/WulaFallenEmpire/Work/ForceEjectPilot/FloatMenuOptionProvider_ForceEjectPilot.cs new file mode 100644 index 00000000..81e7f1b5 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/ForceEjectPilot/FloatMenuOptionProvider_ForceEjectPilot.cs @@ -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(); + 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(); + + 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; + } + } +} diff --git a/Source/WulaFallenEmpire/Work/ForceEjectPilot/JobDriver_ForceEjectPilot.cs b/Source/WulaFallenEmpire/Work/ForceEjectPilot/JobDriver_ForceEjectPilot.cs new file mode 100644 index 00000000..a7963936 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/ForceEjectPilot/JobDriver_ForceEjectPilot.cs @@ -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 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(); + 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(); + 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 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); + } + } + } + } +} diff --git a/Source/WulaFallenEmpire/Job/JobDriver_InspectBuilding.cs b/Source/WulaFallenEmpire/Work/InspectBuilding/JobDriver_InspectBuilding.cs similarity index 100% rename from Source/WulaFallenEmpire/Job/JobDriver_InspectBuilding.cs rename to Source/WulaFallenEmpire/Work/InspectBuilding/JobDriver_InspectBuilding.cs diff --git a/Source/WulaFallenEmpire/Job/JobGiver_InspectBuilding.cs b/Source/WulaFallenEmpire/Work/InspectBuilding/JobGiver_InspectBuilding.cs similarity index 100% rename from Source/WulaFallenEmpire/Job/JobGiver_InspectBuilding.cs rename to Source/WulaFallenEmpire/Work/InspectBuilding/JobGiver_InspectBuilding.cs diff --git a/Source/WulaFallenEmpire/Work/JobDriver_CarryToMech.cs b/Source/WulaFallenEmpire/Work/JobDriver_CarryToMech.cs new file mode 100644 index 00000000..95c0b351 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/JobDriver_CarryToMech.cs @@ -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(); + + 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 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 + }; + } + } +} diff --git a/Source/WulaFallenEmpire/Work/JobGiver_NoPilot.cs b/Source/WulaFallenEmpire/Work/JobGiver_NoPilot.cs new file mode 100644 index 00000000..5a41a600 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/JobGiver_NoPilot.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/Work/RefuelMech/JobDriver_RefuelMech.cs b/Source/WulaFallenEmpire/Work/RefuelMech/JobDriver_RefuelMech.cs new file mode 100644 index 00000000..db020b66 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/RefuelMech/JobDriver_RefuelMech.cs @@ -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(); + 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 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(); + 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(); + + if (fuelComp != null) + { + // 获取所有燃料物品 + List fuelThings; + if (actor.CurJob.placedThings.NullOrEmpty()) + { + // 如果没有 placedThings,则使用燃料目标 + Thing fuel = curJob.GetTarget(fuelInd).Thing; + if (fuel != null) + { + fuelThings = new List { 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; + } + } +} diff --git a/Source/WulaFallenEmpire/Work/RefuelMech/WorkGiver_RefuelMech.cs b/Source/WulaFallenEmpire/Work/RefuelMech/WorkGiver_RefuelMech.cs new file mode 100644 index 00000000..3544ab46 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/RefuelMech/WorkGiver_RefuelMech.cs @@ -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 PotentialWorkThingsGlobal(Pawn pawn) + { + // 返回所有需要燃料的机甲 + // 修复:使用 LINQ 的 Where 方法而不是 FindAll + var mechs = pawn.Map.mapPawns.AllPawnsSpawned.Where(p => + p.TryGetComp() != 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(); + 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(); + 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(); + 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); + } + } +} diff --git a/Source/WulaFallenEmpire/Work/RepairMech/JobDriver_RepairMech.cs b/Source/WulaFallenEmpire/Work/RepairMech/JobDriver_RepairMech.cs new file mode 100644 index 00000000..e08f9028 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/RepairMech/JobDriver_RepairMech.cs @@ -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(); + + // 使用配置的修复周期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 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 GetAllRepairableHediffs() + { + var repairableHediffs = new List(); + + 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); + } + } +} diff --git a/Source/WulaFallenEmpire/Work/RepairMech/WorkGiver_RepairMech.cs b/Source/WulaFallenEmpire/Work/RepairMech/WorkGiver_RepairMech.cs new file mode 100644 index 00000000..c2004b7e --- /dev/null +++ b/Source/WulaFallenEmpire/Work/RepairMech/WorkGiver_RepairMech.cs @@ -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 PotentialWorkThingsGlobal(Pawn pawn) + { + if (pawn.Faction != Faction.OfPlayer || pawn.Map == null) + return Enumerable.Empty(); + + // 获取所有需要维修的玩家机甲 + return pawn.Map.mapPawns.AllPawnsSpawned + .Where(p => + p.Faction == Faction.OfPlayer && + p.health != null && + !p.Dead && + p.TryGetComp()?.CanAutoRepair == true + ) + .Cast(); + } + + 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(); + 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); + } + } +} diff --git a/Source/WulaFallenEmpire/Work/ThinkNode_ConditionalMechHasPilot.cs b/Source/WulaFallenEmpire/Work/ThinkNode_ConditionalMechHasPilot.cs new file mode 100644 index 00000000..cec11484 --- /dev/null +++ b/Source/WulaFallenEmpire/Work/ThinkNode_ConditionalMechHasPilot.cs @@ -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(); + 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; + } + } +} \ No newline at end of file diff --git a/Source/WulaFallenEmpire/WulaDefOf.cs b/Source/WulaFallenEmpire/WulaDefOf.cs index e1245472..627e86af 100644 --- a/Source/WulaFallenEmpire/WulaDefOf.cs +++ b/Source/WulaFallenEmpire/WulaDefOf.cs @@ -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; diff --git a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj index 54bfbef4..90c889b3 100644 --- a/Source/WulaFallenEmpire/WulaFallenEmpire.csproj +++ b/Source/WulaFallenEmpire/WulaFallenEmpire.csproj @@ -95,7 +95,23 @@ - + + + + + + + + + + + + + + + + + @@ -333,29 +349,27 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + @@ -371,10 +385,10 @@ - - - - + + + + @@ -388,7 +402,6 @@ -