zc
This commit is contained in:
7
.claude/settings.local.json
Normal file
7
.claude/settings.local.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"mcp__rimworld-code-rag__rough_search"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
129
AGENTS.md
Normal file
129
AGENTS.md
Normal 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
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,9 +195,9 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
||||
|
||||
// Switch to Small UI Button
|
||||
Rect switchBtnRect = new Rect(0f, 0f, 25f, 25f);
|
||||
if (DrawHeaderButton(switchBtnRect, "-"))
|
||||
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})";
|
||||
}
|
||||
@@ -945,22 +945,22 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text)) return;
|
||||
if (_core == null) return;
|
||||
|
||||
|
||||
if (string.Equals(text.Trim(), "/clear", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_isThinking = false;
|
||||
_options.Clear();
|
||||
_inputText = "";
|
||||
// Core functionality for clear if implemented, or just UI clear
|
||||
// For now, Dialog doesn't manage history, Core does.
|
||||
// Core should handle /clear command via SendUserMessage theoretically,
|
||||
// For now, Dialog doesn't manage history, Core does.
|
||||
// Core should handle /clear command via SendUserMessage theoretically,
|
||||
// or we call a hypothetical _core.ClearHistory().
|
||||
// Based on previous code, SendUserMessage handles /clear logic inside Core.
|
||||
}
|
||||
|
||||
_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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,15 +221,15 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
||||
private void DrawMinimized(Rect rect)
|
||||
{
|
||||
// AI 核心挂件背景
|
||||
Widgets.DrawBoxSolid(rect, new Color(0.1f, 0.1f, 0.1f, 0.9f));
|
||||
Widgets.DrawBoxSolid(rect, new Color(0.1f, 0.1f, 0.1f, 0.9f));
|
||||
GUI.color = WulaLinkStyles.HeaderColor;
|
||||
Widgets.DrawBox(rect, 2);
|
||||
Widgets.DrawBox(rect, 2);
|
||||
GUI.color = Color.white;
|
||||
|
||||
|
||||
// 左侧:大型方形头像
|
||||
float avaSize = rect.height - 16f;
|
||||
Rect avatarRect = new Rect(8f, 8f, avaSize, avaSize);
|
||||
|
||||
|
||||
int expId = _core?.ExpressionId ?? 1;
|
||||
string portraitPath = "Wula/Storyteller/WULA_Legion_TINY";
|
||||
Texture2D portrait = ContentFinder<Texture2D>.Get(portraitPath, false);
|
||||
@@ -245,17 +245,18 @@ 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,12 +919,12 @@ 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);
|
||||
_inputText = "";
|
||||
if (wasFocused) GUI.FocusControl("WulaInput");
|
||||
if (wasFocused) GUI.FocusControl("WulaInput");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user