This commit is contained in:
2026-01-16 21:31:48 +08:00
parent 1b36fb789a
commit 04673fc967
7 changed files with 179 additions and 34 deletions

View File

@@ -0,0 +1,7 @@
{
"permissions": {
"allow": [
"mcp__rimworld-code-rag__rough_search"
]
}
}

129
AGENTS.md Normal file
View File

@@ -0,0 +1,129 @@
# AGENTS.md - WulaFallenEmpire RimWorld Mod
## Build Commands
### Primary Build Command
```bash
dotnet build "C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire\WulaFallenEmpire.csproj"
```
### Clean Build
```bash
dotnet clean "C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire\WulaFallenEmpire.csproj"
dotnet build "C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire\WulaFallenEmpire.csproj"
```
### Output Location
- Debug builds: `C:\Steam\steamapps\common\RimWorld\Mods\3516260226\1.6\1.6\Assemblies\`
- Release builds: Same as Debug (optimized)
### Testing
This project does not have automated unit tests. Manual testing is done through RimWorld gameplay.
## Project Structure
```
C:\Steam\steamapps\common\RimWorld\Mods\3516260226\
├── Source\WulaFallenEmpire\ # C# source code
├── 1.6\1.6\ # RimWorld 1.6 mod files
│ ├── Assemblies\ # Compiled DLL output
│ ├── Defs\ # XML definitions
│ ├── Languages\ # Translation files
│ ├── Patches\ # XML patch operations
│ └── Textures\ # Visual assets
└── LoadFolders.xml # Mod loading configuration
```
## Code Style Guidelines
### Imports & Formatting
- Group RimWorld imports: `Verse`, `RimWorld`, `Verse.Sound`, `UnityEngine`
- Group mod imports after RimWorld imports
- 4-space indentation, curly braces on new lines
- Use `var` when type is obvious, explicit types when clarity matters
- C# 11.0, .NET Framework 4.8
### Naming Conventions
- Classes/Methods/Properties: PascalCase (e.g., `WulaFallenEmpireMod`, `TryCastShot`)
- Fields: camelCase (e.g., `explosionShotCounter`), private: `_scrollPosition`
- Harmony patches: `Patch_` prefix (e.g., `Patch_CaravanFormingUtility_AllSendablePawns`)
### Harmony Patches
```csharp
[HarmonyPatch(typeof(RimWorld.Planet.CaravanFormingUtility), "AllSendablePawns")]
public static class Patch_CaravanFormingUtility_AllSendablePawns
{
[HarmonyPostfix]
public static void Postfix(Map map, ref List<Pawn> __result)
{
WulaLog.Debug("[WULA] Patch executed");
}
}
```
### DefOf Pattern
```csharp
[DefOf]
public static class ThingDefOf_WULA
{
public static ThingDef WULA_MaintenancePod;
static ThingDefOf_WULA() => DefOfHelper.EnsureInitializedInCtor(typeof(ThingDefOf_WULA));
}
```
### Mod Initialization
```csharp
[StaticConstructorOnStartup]
public class WulaFallenEmpireMod : Mod
{
public WulaFallenEmpireMod(ModContentPack content) : base(content)
{
new Harmony("tourswen.wulafallenempire").PatchAll(Assembly.GetExecutingAssembly());
}
}
```
### Error Handling
Check null before access, use `WulaLog.Debug()` for logging (controlled by mod setting).
### Signing Convention
沐雪写的代码会加上可爱的署名注释,例如:
```csharp
// ✨ 沐雪写的哦~
```
## Important Rules
### Knowledge Base Usage
When working on RimWorld modding, ALWAYS use the `rimworld-knowledge-base` tool to:
- Search for correct class names, method signatures, and enum values
- Verify game mechanics and API usage
- Access decompiled RimWorld 1.6 source code
- **Do not rely on external memory or searches**
### Critical Paths
- Local C# Knowledge Base: `C:\Steam\steamapps\common\RimWorld\dll1.6`
- Mod Project: `C:\Steam\steamapps\common\RimWorld\Mods\3516260226`
- C# Project: `C:\Steam\steamapps\common\RimWorld\Mods\3516260226\Source\WulaFallenEmpire`
### Project File Sync
When renaming, moving, or deleting C# files, **MUST** update `.csproj` file's `<Compile Include="..." />` entries.
### Dependencies
- RimWorld Assembly-CSharp
- UnityEngine modules (Core, IMGUIModule, etc.)
- Harmony (0Harmony.dll)
- AlienRace (AlienRace.dll)
## Additional Notes
### Logging
Use `WulaLog.Debug(string message)` for all debug output. Controlled by mod setting `enableDebugLogs`. Independent of DevMode.
### Serialization
Use `Scribe_Values.Look()` for primitive types, `Scribe_Collections.Look()` for collections in `ExposeData()` methods.
### Comments
- Use Chinese comments for Chinese-language code
- Use English comments for general API documentation
- XML documentation (`///`) for public APIs

View File

@@ -141,25 +141,32 @@ For each function call, return a JSON object within <tool_call></tool_call> tags
if (_overlayWindowOpen && !string.IsNullOrEmpty(_overlayWindowEventDefName))
{
string eventDefNameToRestore = _overlayWindowEventDefName;
float savedX = _overlayWindowX;
float savedY = _overlayWindowY;
LongEventHandler.ExecuteWhenFinished(() =>
{
try
{
var existingWindow = Find.WindowStack?.Windows?.OfType<WulaFallenEmpire.EventSystem.AI.UI.Overlay_WulaLink>().FirstOrDefault();
// Additional safety checks for load scenarios
if (Find.WindowStack == null || Find.World == null)
{
WulaLog.Debug("[WulaAI] Skipping overlay restore: game not fully loaded.");
return;
}
var existingWindow = Find.WindowStack.Windows?.OfType<WulaFallenEmpire.EventSystem.AI.UI.Overlay_WulaLink>().FirstOrDefault();
if (existingWindow == null)
{
var eventDef = DefDatabase<EventDef>.GetNamedSilentFail(eventDefNameToRestore);
if (eventDef != null)
{
var newWindow = new WulaFallenEmpire.EventSystem.AI.UI.Overlay_WulaLink(eventDef);
if (savedX >= 0f && savedY >= 0f)
{
newWindow.SetInitialPosition(savedX, savedY);
}
Find.WindowStack.Add(newWindow);
newWindow.ToggleMinimize(); // Start minimized
// Force position after everything else
if (_overlayWindowX >= 0f && _overlayWindowY >= 0f)
{
newWindow.windowRect.x = _overlayWindowX;
newWindow.windowRect.y = _overlayWindowY;
}
}
}
}

View File

@@ -197,7 +197,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
Rect switchBtnRect = new Rect(0f, 0f, 25f, 25f);
if (DrawHeaderButton(switchBtnRect, "-"))
{
if (def != null)
if (def != null && Find.WindowStack != null)
{
var existing = Find.WindowStack.WindowOfType<Overlay_WulaLink>();
if (existing != null) existing.Expand();
@@ -210,7 +210,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
Rect personalityBtnRect = new Rect(0f, 30f, 25f, 25f);
if (DrawHeaderButton(personalityBtnRect, "P"))
{
Find.WindowStack.Add(new Dialog_ExtraPersonalityPrompt());
Find.WindowStack?.Add(new Dialog_ExtraPersonalityPrompt());
}
float margin = 15f;
@@ -854,7 +854,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
private string BuildThinkingStatus()
{
if (_core == null) return "Thinking...";
float elapsedSeconds = Mathf.Max(0f, Time.realtimeSinceStartup - _core.ThinkingStartTime);
float elapsedSeconds = Mathf.Max(0f, Time.realtimeSinceStartup - (_core.ThinkingStartTime));
string elapsedText = elapsedSeconds.ToString("0.0", CultureInfo.InvariantCulture);
return $"P.I.A is thinking... ({elapsedText}s Loop {_core.ThinkingPhaseIndex})";
}
@@ -960,7 +960,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
_scrollToBottom = true;
_core.SendUserMessage(text);
_history = _core.GetHistorySnapshot();
_history = _core.GetHistorySnapshot() ?? new List<(string role, string message)>();
}
private void DrawConversationOptions(Rect rect, List<EventOption> options)

View File

@@ -129,14 +129,14 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
public void Expand()
{
if (_isMinimized) ToggleMinimize();
Find.WindowStack.Notify_ManuallySetFocus(this);
Find.WindowStack?.Notify_ManuallySetFocus(this);
}
public override void PreOpen()
{
base.PreOpen();
// Connect to Core
_core = Find.World.GetComponent<AIIntelligenceCore>();
// Connect to Core - with null safety for load scenarios
_core = Find.World?.GetComponent<AIIntelligenceCore>();
if (_core != null)
{
_core.InitializeConversation(_eventDefName);
@@ -167,7 +167,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
{
_unreadCount++;
// Spawn Notification Bubble
Find.WindowStack.Add(new Overlay_WulaLink_Notification(msg));
Find.WindowStack?.Add(new Overlay_WulaLink_Notification(msg));
}
}
@@ -246,16 +246,17 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
float rightContentX = avatarRect.xMax + 12f;
float btnWidth = 30f;
// Status Info
string status = _core.IsThinking ? "Thinking..." : "Standby";
Color statusColor = _core.IsThinking ? Color.yellow : Color.green;
// Status Info - with null safety
bool isThinking = _core?.IsThinking ?? false;
string status = isThinking ? "Thinking..." : "Standby";
Color statusColor = isThinking ? Color.yellow : Color.green;
// 绘制状态文字
Rect textRect = new Rect(rightContentX, 0, rect.width - rightContentX - btnWidth - 5f, rect.height);
Text.Anchor = TextAnchor.MiddleLeft;
Text.Font = GameFont.Small;
GUI.color = statusColor;
Widgets.Label(textRect, _core.IsThinking ? BuildThinkingStatus() : "Standby");
Widgets.Label(textRect, isThinking ? BuildThinkingStatus() : "Standby");
GUI.color = Color.white;
// 右侧:小巧的展开按钮
@@ -290,13 +291,14 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
Widgets.DrawLineHorizontal(rect.x, rect.y, rect.width);
string contextInfo = "Context: None";
if (Find.Selector.SingleSelectedThing != null)
var selector = Find.Selector;
if (selector?.SingleSelectedThing != null)
{
contextInfo = $"Context: [{Find.Selector.SingleSelectedThing.LabelCap}]";
contextInfo = $"Context: [{selector.SingleSelectedThing.LabelCap}]";
}
else if (Find.Selector.SelectedObjects.Count > 1)
else if (selector?.SelectedObjects?.Count > 1)
{
contextInfo = $"Context: {Find.Selector.SelectedObjects.Count} objects selected";
contextInfo = $"Context: {selector.SelectedObjects.Count} objects selected";
}
Text.Anchor = TextAnchor.MiddleLeft;
@@ -917,7 +919,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
bool sendClicked = DrawCustomButton(btnRect, ">", !string.IsNullOrWhiteSpace(_inputText));
if (sendClicked || enterPressed)
{
if (!string.IsNullOrWhiteSpace(_inputText))
if (!string.IsNullOrWhiteSpace(_inputText) && _core != null)
{
bool wasFocused = GUI.GetNameOfFocusedControl() == "WulaInput";
_core.SendUserMessage(_inputText);