This commit is contained in:
2025-12-31 03:02:22 +08:00
parent f29910ad91
commit c345655ca5
5 changed files with 873 additions and 256 deletions

View File

@@ -144,132 +144,130 @@
</terrain> </terrain>
</PrefabDef> </PrefabDef>
<PrefabDef>
<PrefabDef> <defName>WULA_StorageBase</defName> <!-- rename -->
<defName>WULA_StorageBase</defName> <!-- rename --> <size>(13,14)</size>
<size>(13,14)</size> <things>
<things> <WULA_OrbitalTradeBeacon>
<WULA_OrbitalTradeBeacon> <position>(5, 0, 6)</position>
<position>(5, 0, 6)</position> </WULA_OrbitalTradeBeacon>
</WULA_OrbitalTradeBeacon> <WulaDoor>
<WulaDoor> <rects>
<rects> <li>(6,0,6,0)</li>
<li>(6,0,6,0)</li> <li>(6,12,6,12)</li>
<li>(6,12,6,12)</li> </rects>
</rects> </WulaDoor>
</WulaDoor> <WulaDoor>
<WulaDoor> <rects>
<rects> <li>(0,6,0,6)</li>
<li>(0,6,0,6)</li> <li>(12,6,12,6)</li>
<li>(12,6,12,6)</li> </rects>
</rects> <relativeRotation>Clockwise</relativeRotation>
<relativeRotation>Clockwise</relativeRotation> </WulaDoor>
</WulaDoor> <HiddenConduit>
<HiddenConduit> <rects>
<rects> <li>(6,1,6,11)</li>
<li>(6,1,6,11)</li> <li>(1,6,5,6)</li>
<li>(1,6,5,6)</li> <li>(7,6,11,6)</li>
<li>(7,6,11,6)</li> </rects>
</rects> </HiddenConduit>
</HiddenConduit> <WallLamp>
<WallLamp> <rects>
<rects> <li>(4,1,4,1)</li>
<li>(4,1,4,1)</li> <li>(8,1,8,1)</li>
<li>(8,1,8,1)</li> </rects>
</rects> <relativeRotation>Opposite</relativeRotation>
<relativeRotation>Opposite</relativeRotation> </WallLamp>
</WallLamp> <WallLamp>
<WallLamp> <rects>
<rects> <li>(1,4,1,4)</li>
<li>(1,4,1,4)</li> <li>(1,8,1,8)</li>
<li>(1,8,1,8)</li> </rects>
</rects> <relativeRotation>Counterclockwise</relativeRotation>
<relativeRotation>Counterclockwise</relativeRotation> </WallLamp>
</WallLamp> <WallLamp>
<WallLamp> <rects>
<rects> <li>(11,4,11,4)</li>
<li>(11,4,11,4)</li> <li>(11,8,11,8)</li>
<li>(11,8,11,8)</li> </rects>
</rects> <relativeRotation>Clockwise</relativeRotation>
<relativeRotation>Clockwise</relativeRotation> </WallLamp>
</WallLamp> <WallLamp>
<WallLamp> <rects>
<rects> <li>(4,11,4,11)</li>
<li>(4,11,4,11)</li> <li>(8,11,8,11)</li>
<li>(8,11,8,11)</li> </rects>
</rects> </WallLamp>
</WallLamp> <FirefoamPopper>
<FirefoamPopper> <position>(7, 0, 6)</position>
<position>(7, 0, 6)</position> </FirefoamPopper>
</FirefoamPopper> <WulaWall>
<WulaWall> <rects>
<rects> <li>(0,0,5,0)</li>
<li>(0,0,5,0)</li> <li>(7,0,12,0)</li>
<li>(7,0,12,0)</li> <li>(0,1,0,5)</li>
<li>(0,1,0,5)</li> <li>(12,1,12,5)</li>
<li>(12,1,12,5)</li> <li>(0,7,0,12)</li>
<li>(0,7,0,12)</li> <li>(12,7,12,12)</li>
<li>(12,7,12,12)</li> <li>(1,12,5,12)</li>
<li>(1,12,5,12)</li> <li>(7,12,11,12)</li>
<li>(7,12,11,12)</li> </rects>
</rects> </WulaWall>
</WulaWall> <Shelf>
<Shelf> <positions>
<positions> <li>(3, 0, 2)</li>
<li>(3, 0, 2)</li> <li>(5, 0, 2)</li>
<li>(5, 0, 2)</li> <li>(8, 0, 2)</li>
<li>(8, 0, 2)</li> <li>(10, 0, 2)</li>
<li>(10, 0, 2)</li> <li>(3, 0, 4)</li>
<li>(3, 0, 4)</li> <li>(5, 0, 4)</li>
<li>(5, 0, 4)</li> <li>(8, 0, 4)</li>
<li>(8, 0, 4)</li> <li>(10, 0, 4)</li>
<li>(10, 0, 4)</li> <li>(3, 0, 7)</li>
<li>(3, 0, 7)</li> <li>(5, 0, 7)</li>
<li>(5, 0, 7)</li> <li>(8, 0, 7)</li>
<li>(8, 0, 7)</li> <li>(10, 0, 7)</li>
<li>(10, 0, 7)</li> <li>(3, 0, 9)</li>
<li>(3, 0, 9)</li> <li>(5, 0, 9)</li>
<li>(5, 0, 9)</li> <li>(8, 0, 9)</li>
<li>(8, 0, 9)</li> <li>(10, 0, 9)</li>
<li>(10, 0, 9)</li> </positions>
</positions> <relativeRotation>Opposite</relativeRotation>
<relativeRotation>Opposite</relativeRotation> <stuff>WULA_Alloy</stuff>
<stuff>WULA_Alloy</stuff> </Shelf>
</Shelf> <Shelf>
<Shelf> <positions>
<positions> <li>(2, 0, 3)</li>
<li>(2, 0, 3)</li> <li>(4, 0, 3)</li>
<li>(4, 0, 3)</li> <li>(7, 0, 3)</li>
<li>(7, 0, 3)</li> <li>(9, 0, 3)</li>
<li>(9, 0, 3)</li> <li>(2, 0, 5)</li>
<li>(2, 0, 5)</li> <li>(4, 0, 5)</li>
<li>(4, 0, 5)</li> <li>(7, 0, 5)</li>
<li>(7, 0, 5)</li> <li>(9, 0, 5)</li>
<li>(9, 0, 5)</li> <li>(2, 0, 8)</li>
<li>(2, 0, 8)</li> <li>(4, 0, 8)</li>
<li>(4, 0, 8)</li> <li>(7, 0, 8)</li>
<li>(7, 0, 8)</li> <li>(9, 0, 8)</li>
<li>(9, 0, 8)</li> <li>(2, 0, 10)</li>
<li>(2, 0, 10)</li> <li>(4, 0, 10)</li>
<li>(4, 0, 10)</li> <li>(7, 0, 10)</li>
<li>(7, 0, 10)</li> <li>(9, 0, 10)</li>
<li>(9, 0, 10)</li> </positions>
</positions> <stuff>WULA_Alloy</stuff>
<stuff>WULA_Alloy</stuff> </Shelf>
</Shelf> <WULA_AreaTeleportBeacon>
<WULA_AreaTeleportBeacon> <position>(6, 0, 6)</position>
<position>(6, 0, 6)</position> </WULA_AreaTeleportBeacon>
</WULA_AreaTeleportBeacon> </things>
</things> <terrain>
<terrain>
<WulaFloor> <WulaFloor>
<rects> <rects>
<li>(0,1,12,12)</li> <li>(0,1,12,12)</li>
</rects> </rects>
</WulaFloor> </WulaFloor>
</terrain> </terrain>
</PrefabDef> </PrefabDef>
<PrefabDef> <PrefabDef>
<defName>WULA_KitchenBase</defName> <!-- rename --> <defName>WULA_KitchenBase</defName> <!-- rename -->
@@ -417,160 +415,358 @@
</terrain> </terrain>
</PrefabDef> </PrefabDef>
<PrefabDef>
<defName>WULA_HospitalBase</defName> <!-- rename -->
<size>(13,14)</size>
<things>
<VitalsMonitor>
<rects>
<li>(1,2,1,2)</li>
<li>(1,10,1,10)</li>
</rects>
<relativeRotation>Clockwise</relativeRotation>
</VitalsMonitor>
<VitalsMonitor>
<position>(11, 0, 2)</position>
<relativeRotation>Counterclockwise</relativeRotation>
<hp>93</hp>
</VitalsMonitor>
<VitalsMonitor>
<position>(11, 0, 10)</position>
<relativeRotation>Counterclockwise</relativeRotation>
</VitalsMonitor>
<WulaDoor>
<position>(6, 0, 0)</position>
<hp>982</hp>
</WulaDoor>
<WulaDoor>
<position>(6, 0, 12)</position>
<hp>977</hp>
</WulaDoor>
<WULA_MaintenancePod>
<position>(10, 0, 6)</position>
<relativeRotation>Clockwise</relativeRotation>
</WULA_MaintenancePod>
<Heater>
<position>(8, 0, 11)</position>
<hp>92</hp>
</Heater>
<Wula_Fusion_Generators>
<position>(2, 0, 6)</position>
</Wula_Fusion_Generators>
<DiningChair>
<position>(8, 0, 6)</position>
<relativeRotation>Clockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
<quality>Normal</quality>
</DiningChair>
<WallLamp>
<rects>
<li>(4,1,4,1)</li>
<li>(8,1,8,1)</li>
</rects>
<relativeRotation>Opposite</relativeRotation>
</WallLamp>
<WallLamp>
<rects>
<li>(1,4,1,4)</li>
<li>(1,8,1,8)</li>
</rects>
<relativeRotation>Counterclockwise</relativeRotation>
</WallLamp>
<WallLamp>
<rects>
<li>(11,4,11,4)</li>
<li>(11,8,11,8)</li>
</rects>
<relativeRotation>Clockwise</relativeRotation>
</WallLamp>
<WallLamp>
<position>(4, 0, 11)</position>
</WallLamp>
<WallLamp>
<position>(8, 0, 11)</position>
<hp>17</hp>
</WallLamp>
<HospitalBed>
<positions>
<li>(1, 0, 1)</li>
<li>(1, 0, 3)</li>
<li>(1, 0, 9)</li>
<li>(1, 0, 11)</li>
</positions>
<relativeRotation>Clockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
<quality>Normal</quality>
</HospitalBed>
<HospitalBed>
<position>(11, 0, 1)</position>
<relativeRotation>Counterclockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
<quality>Normal</quality>
<hp>149</hp>
</HospitalBed>
<HospitalBed>
<positions>
<li>(11, 0, 3)</li>
<li>(11, 0, 9)</li>
<li>(11, 0, 11)</li>
</positions>
<relativeRotation>Counterclockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
<quality>Normal</quality>
</HospitalBed>
<WulaShelter>
<rects>
<li>(4,5,4,7)</li>
</rects>
</WulaShelter>
<Drape>
<positions>
<li>(1, 0, 4)</li>
<li>(3, 0, 4)</li>
<li>(8, 0, 4)</li>
<li>(10, 0, 4)</li>
<li>(1, 0, 8)</li>
<li>(3, 0, 8)</li>
<li>(8, 0, 8)</li>
<li>(10, 0, 8)</li>
</positions>
<stuff>Synthread</stuff>
</Drape>
<WulaWall>
<rects>
<li>(0,0,5,0)</li>
<li>(7,0,12,0)</li>
<li>(0,1,0,12)</li>
<li>(12,2,12,2)</li>
<li>(12,4,12,12)</li>
<li>(1,12,5,12)</li>
<li>(8,12,11,12)</li>
</rects>
</WulaWall>
<WulaWall>
<rects>
<li>(12,1,12,1)</li>
<li>(12,3,12,3)</li>
<li>(7,12,7,12)</li>
</rects>
<hp>1199</hp>
</WulaWall>
<Shelf>
<positions>
<li>(4, 0, 1)</li>
<li>(7, 0, 1)</li>
</positions>
<stuff>WULA_Alloy</stuff>
</Shelf>
<Shelf>
<position>(5, 0, 11)</position>
<relativeRotation>Opposite</relativeRotation>
<stuff>WULA_Alloy</stuff>
</Shelf>
<WULA_AreaTeleportBeacon>
<position>(6, 0, 6)</position>
</WULA_AreaTeleportBeacon>
</things>
<terrain>
<WulaFloor>
<rects>
<li>(0,1,12,12)</li>
</rects>
</WulaFloor>
</terrain>
</PrefabDef>
<PrefabDef>
<defName>WULA_PowerPlantBase</defName> <!-- rename -->
<size>(13,14)</size>
<things>
<Battery>
<positions>
<li>(5, 0, 3)</li>
<li>(6, 0, 3)</li>
<li>(7, 0, 3)</li>
</positions>
</Battery>
<Battery>
<positions>
<li>(3, 0, 5)</li>
<li>(3, 0, 6)</li>
<li>(3, 0, 7)</li>
</positions>
<relativeRotation>Clockwise</relativeRotation>
</Battery>
<Battery>
<positions>
<li>(9, 0, 5)</li>
<li>(9, 0, 6)</li>
<li>(9, 0, 7)</li>
</positions>
<relativeRotation>Counterclockwise</relativeRotation>
</Battery>
<Battery>
<positions>
<li>(5, 0, 9)</li>
<li>(6, 0, 9)</li>
<li>(7, 0, 9)</li>
</positions>
<relativeRotation>Opposite</relativeRotation>
</Battery>
<WulaDoor>
<rects>
<li>(6,0,6,0)</li>
<li>(6,12,6,12)</li>
</rects>
</WulaDoor>
<WulaDoor>
<rects>
<li>(0,6,0,6)</li>
<li>(12,6,12,6)</li>
</rects>
<relativeRotation>Clockwise</relativeRotation>
</WulaDoor>
<Wula_Fusion_Generators>
<positions>
<li>(3, 0, 3)</li>
<li>(9, 0, 3)</li>
<li>(3, 0, 9)</li>
<li>(9, 0, 9)</li>
</positions>
</Wula_Fusion_Generators>
<HiddenConduit>
<rects>
<li>(3,1,3,1)</li>
<li>(9,1,9,1)</li>
<li>(1,3,1,3)</li>
<li>(11,3,11,3)</li>
<li>(3,11,3,11)</li>
<li>(9,11,9,11)</li>
</rects>
</HiddenConduit>
<WallLamp>
<rects>
<li>(4,1,4,1)</li>
<li>(8,1,8,1)</li>
</rects>
<relativeRotation>Opposite</relativeRotation>
</WallLamp>
<WallLamp>
<rects>
<li>(1,4,1,4)</li>
<li>(1,8,1,8)</li>
</rects>
<relativeRotation>Counterclockwise</relativeRotation>
</WallLamp>
<WallLamp>
<rects>
<li>(11,4,11,4)</li>
<li>(11,8,11,8)</li>
</rects>
<relativeRotation>Clockwise</relativeRotation>
</WallLamp>
<WallLamp>
<rects>
<li>(4,11,4,11)</li>
<li>(8,11,8,11)</li>
</rects>
</WallLamp>
<FirefoamPopper>
<rects>
<li>(5,5,5,5)</li>
<li>(7,7,7,7)</li>
</rects>
</FirefoamPopper>
<ShelfSmall>
<position>(6, 0, 2)</position>
<relativeRotation>Opposite</relativeRotation>
<stuff>WULA_Alloy</stuff>
</ShelfSmall>
<ShelfSmall>
<position>(2, 0, 6)</position>
<relativeRotation>Counterclockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
</ShelfSmall>
<ShelfSmall>
<position>(10, 0, 6)</position>
<relativeRotation>Clockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
</ShelfSmall>
<ShelfSmall>
<position>(6, 0, 10)</position>
<stuff>WULA_Alloy</stuff>
</ShelfSmall>
<WulaWall>
<rects>
<li>(0,0,5,0)</li>
<li>(7,0,12,0)</li>
<li>(0,1,0,5)</li>
<li>(12,1,12,5)</li>
<li>(0,7,0,12)</li>
<li>(12,7,12,12)</li>
<li>(1,12,5,12)</li>
<li>(7,12,11,12)</li>
</rects>
</WulaWall>
<WULA_AreaTeleportBeacon>
<position>(6, 0, 6)</position>
</WULA_AreaTeleportBeacon>
</things>
<terrain>
<WulaFloor>
<rects>
<li>(0,1,12,12)</li>
</rects>
</WulaFloor>
</terrain>
</PrefabDef>
<PrefabDef> <PrefabDef>
<defName>WULA_HospitalBase</defName> <!-- rename --> <defName>WULA_RavenShuttleBase</defName> <!-- rename -->
<size>(13,14)</size> <size>(13,13)</size>
<things> <things>
<VitalsMonitor> <WulaDoor>
<rects>
<li>(1,6,1,6)</li>
<li>(11,6,11,6)</li>
</rects>
<relativeRotation>Clockwise</relativeRotation>
</WulaDoor>
<WallLamp>
<rects> <rects>
<li>(1,2,1,2)</li> <li>(1,2,1,2)</li>
<li>(1,10,1,10)</li> <li>(1,10,1,10)</li>
</rects> </rects>
<relativeRotation>Clockwise</relativeRotation>
</VitalsMonitor>
<VitalsMonitor>
<position>(11, 0, 2)</position>
<relativeRotation>Counterclockwise</relativeRotation>
<hp>93</hp>
</VitalsMonitor>
<VitalsMonitor>
<position>(11, 0, 10)</position>
<relativeRotation>Counterclockwise</relativeRotation>
</VitalsMonitor>
<WulaDoor>
<position>(6, 0, 0)</position>
<hp>982</hp>
</WulaDoor>
<WulaDoor>
<position>(6, 0, 12)</position>
<hp>977</hp>
</WulaDoor>
<WULA_MaintenancePod>
<position>(10, 0, 6)</position>
<relativeRotation>Clockwise</relativeRotation>
</WULA_MaintenancePod>
<Heater>
<position>(8, 0, 11)</position>
<hp>92</hp>
</Heater>
<Wula_Fusion_Generators>
<position>(2, 0, 6)</position>
</Wula_Fusion_Generators>
<DiningChair>
<position>(8, 0, 6)</position>
<relativeRotation>Clockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
<quality>Normal</quality>
</DiningChair>
<WallLamp>
<rects>
<li>(4,1,4,1)</li>
<li>(8,1,8,1)</li>
</rects>
<relativeRotation>Opposite</relativeRotation>
</WallLamp>
<WallLamp>
<rects>
<li>(1,4,1,4)</li>
<li>(1,8,1,8)</li>
</rects>
<relativeRotation>Counterclockwise</relativeRotation> <relativeRotation>Counterclockwise</relativeRotation>
</WallLamp> </WallLamp>
<WallLamp> <WallLamp>
<rects> <rects>
<li>(11,4,11,4)</li> <li>(11,2,11,2)</li>
<li>(11,8,11,8)</li> <li>(11,10,11,10)</li>
</rects> </rects>
<relativeRotation>Clockwise</relativeRotation> <relativeRotation>Clockwise</relativeRotation>
</WallLamp> </WallLamp>
<WallLamp>
<position>(4, 0, 11)</position>
</WallLamp>
<WallLamp>
<position>(8, 0, 11)</position>
<hp>17</hp>
</WallLamp>
<HospitalBed>
<positions>
<li>(1, 0, 1)</li>
<li>(1, 0, 3)</li>
<li>(1, 0, 9)</li>
<li>(1, 0, 11)</li>
</positions>
<relativeRotation>Clockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
<quality>Normal</quality>
</HospitalBed>
<HospitalBed>
<position>(11, 0, 1)</position>
<relativeRotation>Counterclockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
<quality>Normal</quality>
<hp>149</hp>
</HospitalBed>
<HospitalBed>
<positions>
<li>(11, 0, 3)</li>
<li>(11, 0, 9)</li>
<li>(11, 0, 11)</li>
</positions>
<relativeRotation>Counterclockwise</relativeRotation>
<stuff>WULA_Alloy</stuff>
<quality>Normal</quality>
</HospitalBed>
<WulaShelter>
<rects>
<li>(4,5,4,7)</li>
</rects>
</WulaShelter>
<Drape>
<positions>
<li>(1, 0, 4)</li>
<li>(3, 0, 4)</li>
<li>(8, 0, 4)</li>
<li>(10, 0, 4)</li>
<li>(1, 0, 8)</li>
<li>(3, 0, 8)</li>
<li>(8, 0, 8)</li>
<li>(10, 0, 8)</li>
</positions>
<stuff>Synthread</stuff>
</Drape>
<WulaWall> <WulaWall>
<rects> <rects>
<li>(0,0,5,0)</li> <li>(0,0,1,0)</li>
<li>(7,0,12,0)</li> <li>(11,0,12,0)</li>
<li>(0,1,0,12)</li> <li>(0,1,0,5)</li>
<li>(12,2,12,2)</li> <li>(12,1,12,5)</li>
<li>(12,4,12,12)</li> <li>(1,5,1,5)</li>
<li>(1,12,5,12)</li> <li>(11,5,11,5)</li>
<li>(8,12,11,12)</li> <li>(0,7,1,7)</li>
<li>(11,7,12,7)</li>
<li>(0,8,0,12)</li>
<li>(12,8,12,12)</li>
<li>(1,12,1,12)</li>
<li>(11,12,11,12)</li>
</rects> </rects>
</WulaWall> </WulaWall>
<WulaWall> <WULA_ArmedShuttleWithPocket>
<rects>
<li>(12,1,12,1)</li>
<li>(12,3,12,3)</li>
<li>(7,12,7,12)</li>
</rects>
<hp>1199</hp>
</WulaWall>
<Shelf>
<positions>
<li>(4, 0, 1)</li>
<li>(7, 0, 1)</li>
</positions>
<stuff>WULA_Alloy</stuff>
</Shelf>
<Shelf>
<position>(5, 0, 11)</position>
<relativeRotation>Opposite</relativeRotation>
<stuff>WULA_Alloy</stuff>
</Shelf>
<WULA_AreaTeleportBeacon>
<position>(6, 0, 6)</position> <position>(6, 0, 6)</position>
</WULA_AreaTeleportBeacon> </WULA_ArmedShuttleWithPocket>
</things> </things>
<terrain> <terrain>
<WulaFloor> <WulaFloor>

View File

@@ -35,6 +35,7 @@ namespace WulaFallenEmpire.EventSystem.AI
private float _thinkingStartTime; private float _thinkingStartTime;
private int _thinkingPhaseIndex = 1; private int _thinkingPhaseIndex = 1;
private bool _thinkingPhaseRetry; private bool _thinkingPhaseRetry;
private float _lastThinkingDuration;
private bool _lastActionExecuted; private bool _lastActionExecuted;
private bool _lastActionHadError; private bool _lastActionHadError;
@@ -192,6 +193,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
public int ThinkingPhaseIndex => _thinkingPhaseIndex; public int ThinkingPhaseIndex => _thinkingPhaseIndex;
public bool ThinkingPhaseRetry => _thinkingPhaseRetry; public bool ThinkingPhaseRetry => _thinkingPhaseRetry;
public int ThinkingPhaseTotal => _thinkingPhaseTotal; public int ThinkingPhaseTotal => _thinkingPhaseTotal;
public float LastThinkingDuration => _lastThinkingDuration;
public void InitializeConversation(string eventDefName) public void InitializeConversation(string eventDefName)
{ {
if (string.IsNullOrWhiteSpace(eventDefName)) if (string.IsNullOrWhiteSpace(eventDefName))
@@ -439,6 +441,11 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
return; return;
} }
if (_isThinking && !isThinking)
{
_lastThinkingDuration = Mathf.Max(0f, Time.realtimeSinceStartup - _thinkingStartTime);
}
_isThinking = isThinking; _isThinking = isThinking;
OnThinkingStateChanged?.Invoke(_isThinking); OnThinkingStateChanged?.Invoke(_isThinking);
} }

View File

@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using RimWorld; using RimWorld;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
using WulaFallenEmpire;
using WulaFallenEmpire.EventSystem.AI; using WulaFallenEmpire.EventSystem.AI;
using WulaFallenEmpire.EventSystem.AI.Utils; using WulaFallenEmpire.EventSystem.AI.Utils;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
@@ -29,6 +30,13 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
private Dictionary<int, Texture2D> _portraits = new Dictionary<int, Texture2D>(); private Dictionary<int, Texture2D> _portraits = new Dictionary<int, Texture2D>();
private int _currentPortraitId = 0; private int _currentPortraitId = 0;
private static readonly Regex ExpressionTagRegex = new Regex(@"\[EXPR\s*:\s*([1-6])\s*\]", RegexOptions.IgnoreCase); private static readonly Regex ExpressionTagRegex = new Regex(@"\[EXPR\s*:\s*([1-6])\s*\]", RegexOptions.IgnoreCase);
private bool _reactTraceExpanded = false;
private bool _hasReactTrace = false;
private float _reactTraceHeight = 0f;
private float _reactTraceYOffset = 0f;
private float _reactTraceHeaderHeight = 0f;
private string _reactTraceHeader = "";
private List<string> _reactTraceLines = new List<string>();
private class CachedMessage private class CachedMessage
{ {
@@ -40,6 +48,13 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
public GameFont font; public GameFont font;
} }
private class ReactTraceStep
{
public int Step;
public List<string> Calls = new List<string>();
public List<string> Results = new List<string>();
}
public static Dialog_AIConversation Instance { get; private set; } public static Dialog_AIConversation Instance { get; private set; }
public Dialog_AIConversation(EventDef def) : base(def) public Dialog_AIConversation(EventDef def) : base(def)
@@ -353,6 +368,8 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
curY += height + 10f; curY += height + 10f;
} }
UpdateReactTraceCache(history, contentWidth, ref curY);
_cachedTotalHeight = curY; _cachedTotalHeight = curY;
} }
@@ -409,6 +426,15 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
} }
} }
if (_hasReactTrace)
{
Rect traceRect = new Rect(innerPadding, _reactTraceYOffset, contentWidth, _reactTraceHeight);
if (traceRect.yMax >= viewTop && traceRect.y <= viewBottom)
{
DrawReactTracePanel(traceRect);
}
}
if (_isThinking) if (_isThinking)
{ {
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 0f; float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 0f;
@@ -445,6 +471,186 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
return text.Split(new[] { "OPTIONS:" }, StringSplitOptions.None)[0].Trim(); return text.Split(new[] { "OPTIONS:" }, StringSplitOptions.None)[0].Trim();
} }
private void UpdateReactTraceCache(List<(string role, string message)> history, float contentWidth, ref float curY)
{
_hasReactTrace = false;
_reactTraceLines.Clear();
_reactTraceHeader = "";
_reactTraceHeight = 0f;
_reactTraceYOffset = 0f;
_reactTraceHeaderHeight = 0f;
if (WulaFallenEmpireMod.settings?.showReactTraceInUI != true)
{
return;
}
List<ReactTraceStep> steps = BuildReactTraceSteps(history);
if (steps.Count == 0)
{
return;
}
_hasReactTrace = true;
_reactTraceHeader = BuildReactTraceHeader();
foreach (var step in steps)
{
foreach (string call in step.Calls)
{
_reactTraceLines.Add($"Step {step.Step}: call {call}");
}
foreach (string result in step.Results)
{
_reactTraceLines.Add($"Step {step.Step}: result {result}");
}
}
var originalFont = Text.Font;
Text.Font = GameFont.Tiny;
float panelPadding = 6f;
float headerWidth = Mathf.Max(10f, contentWidth - panelPadding * 2f);
string headerLine = $"{(_reactTraceExpanded ? "v" : ">")} {_reactTraceHeader}";
_reactTraceHeaderHeight = Text.CalcHeight(headerLine, headerWidth) + 6f;
float linesHeight = 0f;
if (_reactTraceExpanded && _reactTraceLines.Count > 0)
{
float lineWidth = Mathf.Max(10f, contentWidth - panelPadding * 2f);
foreach (string line in _reactTraceLines)
{
linesHeight += Text.CalcHeight(line, lineWidth) + 2f;
}
linesHeight += 4f;
}
_reactTraceHeight = _reactTraceHeaderHeight + linesHeight;
Text.Font = originalFont;
curY += 6f;
_reactTraceYOffset = curY;
curY += _reactTraceHeight + 8f;
}
private List<ReactTraceStep> BuildReactTraceSteps(List<(string role, string message)> history)
{
var steps = new List<ReactTraceStep>();
if (history == null || history.Count == 0) return steps;
int lastUserIndex = history.FindLastIndex(entry => entry.role == "user");
if (lastUserIndex < 0) return steps;
int stepIndex = 0;
for (int i = lastUserIndex + 1; i < history.Count; i++)
{
var entry = history[i];
if (entry.role != "toolcall") continue;
stepIndex++;
var step = new ReactTraceStep { Step = stepIndex };
if (JsonToolCallParser.TryParseToolCallsFromText(entry.message, out var calls, out _))
{
foreach (var call in calls)
{
if (string.IsNullOrWhiteSpace(call?.Name)) continue;
string args = call.ArgumentsJson;
string callText = string.IsNullOrWhiteSpace(args) || args == "{}"
? call.Name
: $"{call.Name} {TrimForDisplay(args, 160)}";
step.Calls.Add(callText);
}
}
if (i + 1 < history.Count && history[i + 1].role == "tool")
{
step.Results.AddRange(ExtractToolResultLines(history[i + 1].message, 4));
i++;
}
if (step.Calls.Count > 0 || step.Results.Count > 0)
{
steps.Add(step);
}
}
return steps;
}
private static List<string> ExtractToolResultLines(string toolMessage, int maxLines)
{
var lines = new List<string>();
if (string.IsNullOrWhiteSpace(toolMessage)) return lines;
string[] rawLines = toolMessage.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string raw in rawLines)
{
string line = raw.Trim();
if (string.IsNullOrEmpty(line)) continue;
if (line.StartsWith("[Tool Results]", StringComparison.OrdinalIgnoreCase)) continue;
lines.Add(TrimForDisplay(line, 200));
if (lines.Count >= maxLines) break;
}
return lines;
}
private static string TrimForDisplay(string text, int maxChars)
{
if (string.IsNullOrEmpty(text) || text.Length <= maxChars) return text ?? "";
return text.Substring(0, maxChars) + "...";
}
private string BuildReactTraceHeader()
{
string state = _isThinking ? "思考中" : "已思考";
float elapsed = _isThinking
? Mathf.Max(0f, Time.realtimeSinceStartup - (_core?.ThinkingStartTime ?? 0f))
: _core?.LastThinkingDuration ?? 0f;
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", CultureInfo.InvariantCulture) : "0.0";
return $"{state} ({elapsedText}s · Loop {_core?.ThinkingPhaseIndex ?? 0}/{_core?.ThinkingPhaseTotal ?? 0})";
}
private void DrawReactTracePanel(Rect rect)
{
var originalColor = GUI.color;
var originalFont = Text.Font;
var originalAnchor = Text.Anchor;
float padding = 6f;
Rect headerRect = new Rect(rect.x, rect.y, rect.width, _reactTraceHeaderHeight);
GUI.color = new Color(0.15f, 0.15f, 0.15f, 0.8f);
Widgets.DrawBoxSolid(headerRect, GUI.color);
GUI.color = Color.white;
Text.Font = GameFont.Tiny;
Text.Anchor = TextAnchor.MiddleLeft;
string headerLine = $"{(_reactTraceExpanded ? "v" : ">")} {_reactTraceHeader}";
Widgets.Label(headerRect.ContractedBy(padding), headerLine);
if (Widgets.ButtonInvisible(headerRect))
{
_reactTraceExpanded = !_reactTraceExpanded;
_lastHistoryCount = -1;
_lastUsedWidth = -1f;
}
if (_reactTraceExpanded && _reactTraceLines.Count > 0)
{
float y = headerRect.yMax + 2f;
foreach (string line in _reactTraceLines)
{
float lineHeight = Text.CalcHeight(line, rect.width - padding * 2f) + 2f;
Rect lineRect = new Rect(rect.x + padding, y, rect.width - padding * 2f, lineHeight);
GUI.color = new Color(0.85f, 0.85f, 0.85f, 1f);
Widgets.Label(lineRect, line);
y += lineHeight;
}
}
GUI.color = originalColor;
Text.Font = originalFont;
Text.Anchor = originalAnchor;
}
private string BuildThinkingStatus() private string BuildThinkingStatus()
{ {
if (_core == null) return "Thinking..."; if (_core == null) return "Thinking...";

View File

@@ -4,6 +4,7 @@ using System.Linq;
using UnityEngine; using UnityEngine;
using Verse; using Verse;
using RimWorld; using RimWorld;
using WulaFallenEmpire;
using WulaFallenEmpire.EventSystem.AI; using WulaFallenEmpire.EventSystem.AI;
using WulaFallenEmpire.EventSystem.AI.Utils; using WulaFallenEmpire.EventSystem.AI.Utils;
@@ -24,6 +25,13 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
private float _lastUsedWidth = -1f; private float _lastUsedWidth = -1f;
private List<CachedMessage> _cachedMessages = new List<CachedMessage>(); private List<CachedMessage> _cachedMessages = new List<CachedMessage>();
private float _cachedTotalHeight = 0f; private float _cachedTotalHeight = 0f;
private bool _reactTraceExpanded = false;
private bool _hasReactTrace = false;
private float _reactTraceHeight = 0f;
private float _reactTraceYOffset = 0f;
private float _reactTraceHeaderHeight = 0f;
private string _reactTraceHeader = "";
private List<string> _reactTraceLines = new List<string>();
private class CachedMessage private class CachedMessage
{ {
@@ -34,6 +42,13 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
public float yOffset; public float yOffset;
} }
private class ReactTraceStep
{
public int Step;
public List<string> Calls = new List<string>();
public List<string> Results = new List<string>();
}
// HUD / Minimized State // HUD / Minimized State
private bool _isMinimized = false; private bool _isMinimized = false;
@@ -410,6 +425,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
curY += h + reducedSpacing; curY += h + reducedSpacing;
} }
UpdateReactTraceCache(history, width, ref curY);
_cachedTotalHeight = curY; _cachedTotalHeight = curY;
} }
@@ -464,6 +480,15 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
} }
} }
if (_hasReactTrace)
{
Rect traceRect = new Rect(0, _reactTraceYOffset, width, _reactTraceHeight);
if (traceRect.yMax >= viewTop && traceRect.y <= viewBottom)
{
DrawReactTracePanel(traceRect);
}
}
if (_core != null && _core.IsThinking) if (_core != null && _core.IsThinking)
{ {
float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 10f; float thinkingY = _cachedTotalHeight > 0 ? _cachedTotalHeight : 10f;
@@ -490,6 +515,189 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
return text.Remove(index, fragment.Length).Trim(); return text.Remove(index, fragment.Length).Trim();
} }
private void UpdateReactTraceCache(List<(string role, string message)> history, float contentWidth, ref float curY)
{
_hasReactTrace = false;
_reactTraceLines.Clear();
_reactTraceHeader = "";
_reactTraceHeight = 0f;
_reactTraceYOffset = 0f;
_reactTraceHeaderHeight = 0f;
if (WulaFallenEmpireMod.settings?.showReactTraceInUI != true)
{
return;
}
List<ReactTraceStep> steps = BuildReactTraceSteps(history);
if (steps.Count == 0)
{
return;
}
_hasReactTrace = true;
_reactTraceHeader = BuildReactTraceHeader();
foreach (var step in steps)
{
foreach (string call in step.Calls)
{
_reactTraceLines.Add($"Step {step.Step}: call {call}");
}
foreach (string result in step.Results)
{
_reactTraceLines.Add($"Step {step.Step}: result {result}");
}
}
var originalFont = Text.Font;
Text.Font = GameFont.Tiny;
float panelPadding = 8f;
float headerWidth = Mathf.Max(10f, contentWidth - panelPadding * 2f);
string headerLine = $"{(_reactTraceExpanded ? "v" : ">")} {_reactTraceHeader}";
_reactTraceHeaderHeight = Text.CalcHeight(headerLine, headerWidth) + 6f;
float linesHeight = 0f;
if (_reactTraceExpanded && _reactTraceLines.Count > 0)
{
float lineWidth = Mathf.Max(10f, contentWidth - panelPadding * 2f);
foreach (string line in _reactTraceLines)
{
linesHeight += Text.CalcHeight(line, lineWidth) + 2f;
}
linesHeight += 6f;
}
_reactTraceHeight = _reactTraceHeaderHeight + linesHeight;
Text.Font = originalFont;
curY += 6f;
_reactTraceYOffset = curY;
curY += _reactTraceHeight + 6f;
}
private List<ReactTraceStep> BuildReactTraceSteps(List<(string role, string message)> history)
{
var steps = new List<ReactTraceStep>();
if (history == null || history.Count == 0) return steps;
int lastUserIndex = history.FindLastIndex(entry => entry.role == "user");
if (lastUserIndex < 0) return steps;
int stepIndex = 0;
for (int i = lastUserIndex + 1; i < history.Count; i++)
{
var entry = history[i];
if (entry.role != "toolcall") continue;
stepIndex++;
var step = new ReactTraceStep { Step = stepIndex };
if (JsonToolCallParser.TryParseToolCallsFromText(entry.message, out var calls, out _))
{
foreach (var call in calls)
{
if (string.IsNullOrWhiteSpace(call?.Name)) continue;
string args = call.ArgumentsJson;
string callText = string.IsNullOrWhiteSpace(args) || args == "{}"
? call.Name
: $"{call.Name} {TrimForDisplay(args, 160)}";
step.Calls.Add(callText);
}
}
if (i + 1 < history.Count && history[i + 1].role == "tool")
{
step.Results.AddRange(ExtractToolResultLines(history[i + 1].message, 4));
i++;
}
if (step.Calls.Count > 0 || step.Results.Count > 0)
{
steps.Add(step);
}
}
return steps;
}
private static List<string> ExtractToolResultLines(string toolMessage, int maxLines)
{
var lines = new List<string>();
if (string.IsNullOrWhiteSpace(toolMessage)) return lines;
string[] rawLines = toolMessage.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (string raw in rawLines)
{
string line = raw.Trim();
if (string.IsNullOrEmpty(line)) continue;
if (line.StartsWith("[Tool Results]", StringComparison.OrdinalIgnoreCase)) continue;
lines.Add(TrimForDisplay(line, 200));
if (lines.Count >= maxLines) break;
}
return lines;
}
private static string TrimForDisplay(string text, int maxChars)
{
if (string.IsNullOrEmpty(text) || text.Length <= maxChars) return text ?? "";
return text.Substring(0, maxChars) + "...";
}
private string BuildReactTraceHeader()
{
string state = _core != null && _core.IsThinking ? "思考中" : "已思考";
float startTime = _core?.ThinkingStartTime ?? 0f;
float elapsed = _core != null && _core.IsThinking
? Mathf.Max(0f, Time.realtimeSinceStartup - startTime)
: _core?.LastThinkingDuration ?? 0f;
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) : "0.0";
int phaseIndex = _core?.ThinkingPhaseIndex ?? 0;
int phaseTotal = _core?.ThinkingPhaseTotal ?? 0;
return $"{state} ({elapsedText}s · Loop {phaseIndex}/{phaseTotal})";
}
private void DrawReactTracePanel(Rect rect)
{
var originalColor = GUI.color;
var originalFont = Text.Font;
var originalAnchor = Text.Anchor;
float padding = 8f;
Rect headerRect = new Rect(rect.x, rect.y, rect.width, _reactTraceHeaderHeight);
GUI.color = new Color(0.12f, 0.12f, 0.12f, 0.8f);
Widgets.DrawBoxSolid(headerRect, GUI.color);
GUI.color = Color.white;
Text.Font = GameFont.Tiny;
Text.Anchor = TextAnchor.MiddleLeft;
string headerLine = $"{(_reactTraceExpanded ? "v" : ">")} {_reactTraceHeader}";
Widgets.Label(headerRect.ContractedBy(padding), headerLine);
if (Widgets.ButtonInvisible(headerRect))
{
_reactTraceExpanded = !_reactTraceExpanded;
_lastHistoryCount = -1;
_lastUsedWidth = -1f;
}
if (_reactTraceExpanded && _reactTraceLines.Count > 0)
{
float y = headerRect.yMax + 2f;
foreach (string line in _reactTraceLines)
{
float lineHeight = Text.CalcHeight(line, rect.width - padding * 2f) + 2f;
Rect lineRect = new Rect(rect.x + padding, y, rect.width - padding * 2f, lineHeight);
GUI.color = new Color(0.85f, 0.85f, 0.85f, 1f);
Widgets.Label(lineRect, line);
y += lineHeight;
}
}
GUI.color = originalColor;
Text.Font = originalFont;
Text.Anchor = originalAnchor;
}
private void DrawFooter(Rect rect) private void DrawFooter(Rect rect)
{ {
Widgets.DrawBoxSolid(rect, WulaLinkStyles.InputBarColor); Widgets.DrawBoxSolid(rect, WulaLinkStyles.InputBarColor);