diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index dee74753..e837ec46 100644
Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ
diff --git a/Source/WulaFallenEmpire/EventSystem/AI/AIIntelligenceCore.cs b/Source/WulaFallenEmpire/EventSystem/AI/AIIntelligenceCore.cs
index 027cdf3d..6f9e0dc6 100644
--- a/Source/WulaFallenEmpire/EventSystem/AI/AIIntelligenceCore.cs
+++ b/Source/WulaFallenEmpire/EventSystem/AI/AIIntelligenceCore.cs
@@ -248,7 +248,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
return;
}
- // 附加选中对象的上下文信息
+ // 闄勫姞閫変腑瀵硅薄鐨勪笂涓嬫枃淇℃伅
string messageWithContext = BuildUserMessageWithContext(text);
_history.Add(("user", messageWithContext));
PersistHistory();
@@ -317,23 +317,18 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
_tools.Add(new Tool_SendReinforcement());
_tools.Add(new Tool_GetColonistStatus());
_tools.Add(new Tool_GetMapResources());
+ _tools.Add(new Tool_GetAvailablePrefabs());
_tools.Add(new Tool_GetMapPawns());
_tools.Add(new Tool_GetRecentNotifications());
_tools.Add(new Tool_CallBombardment());
_tools.Add(new Tool_SearchThingDef());
_tools.Add(new Tool_SearchPawnKind());
+ _tools.Add(new Tool_CallPrefabAirdrop());
- // Agent 工具 - 纯视觉操作 (移除了 GetGameState, DesignateMine, DraftPawn)
+ // Agent 宸ュ叿 - 淇濈暀鐢婚潰鍒嗘瀽鎴浘鑳藉姏锛岀Щ闄ゆ墍鏈夋ā鎷熸搷浣滃伐鍏
if (WulaFallenEmpireMod.settings?.enableVlmFeatures == true)
{
_tools.Add(new Tool_AnalyzeScreen());
- _tools.Add(new Tool_VisualClick());
- _tools.Add(new Tool_VisualScroll());
- _tools.Add(new Tool_VisualTypeText());
- _tools.Add(new Tool_VisualDrag());
- _tools.Add(new Tool_VisualHotkey());
- _tools.Add(new Tool_VisualWait());
- _tools.Add(new Tool_VisualDeleteText());
}
}
@@ -482,6 +477,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
"- send_reinforcement\n" +
"- call_bombardment\n" +
"- modify_goodwill\n" +
+ "- call_prefab_airdrop\n" +
"If no action is required, output exactly: .\n" +
"Query tools exist but are disabled in this phase (not listed here).\n"
: string.Empty;
@@ -497,8 +493,8 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
string actionWhitelist = phase == RequestPhase.ActionTools
? "ACTION PHASE VALID TAGS ONLY:\n" +
- ", , , , , , , , , , , \n" +
- "INVALID EXAMPLES (do NOT use now): , JSON, Markdown Code Blocks\n"
+ ", , , , , \n" +
+ "INVALID EXAMPLES (do NOT use now): , \n"
: string.Empty;
return string.Join("\n\n", new[]
@@ -571,7 +567,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
"Rules:\n" +
"- You MUST NOT write any natural language to the user in this phase.\n" +
"- Output XML tool calls only, or exactly: .\n" +
- "- ONLY action tools are accepted in this phase (spawn_resources, send_reinforcement, call_bombardment, modify_goodwill, visual_click, visual_scroll, visual_type_text, visual_drag, visual_hotkey, visual_wait, visual_delete_text).\n" +
+ "- ONLY action tools are accepted in this phase (spawn_resources, send_reinforcement, call_bombardment, modify_goodwill, call_prefab_airdrop).\n" +
"- Query tools (get_*/search_*) will be ignored.\n" +
"- Prefer action tools (spawn_resources, send_reinforcement, call_bombardment, modify_goodwill).\n" +
"- Avoid queries unless absolutely required.\n" +
@@ -643,13 +639,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
toolName == "send_reinforcement" ||
toolName == "call_bombardment" ||
toolName == "modify_goodwill" ||
- toolName == "visual_click" ||
- toolName == "visual_scroll" ||
- toolName == "visual_type_text" ||
- toolName == "visual_drag" ||
- toolName == "visual_hotkey" ||
- toolName == "visual_wait" ||
- toolName == "visual_delete_text";
+ toolName == "call_prefab_airdrop";
}
private static bool IsQueryToolName(string toolName)
@@ -839,15 +829,6 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
}
}
- private bool CheckVisualIntent(string message)
- {
- if (string.IsNullOrEmpty(message)) return false;
- string[] keywords = new string[] {
- "屏幕", "画面", "截图", "看", "找", "显示", // CN
- "screen", "screenshot", "image", "view", "look", "see", "find", "visual", "scan" // EN
- };
- return keywords.Any(k => message.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0);
- }
private async Task RunPhasedRequestAsync()
{
if (_isThinking) return;
@@ -880,7 +861,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
if (settings.enableVlmFeatures && settings.showThinkingProcess)
{
// Optional: We can still say "Analyzing data link..."
- AddAssistantMessage("[P.I.A] 正在分析数据链路...");
+ AddAssistantMessage("[P.I.A] 姝e湪鍒嗘瀽鏁版嵁閾捐矾...");
}
var queryPhase = RequestPhase.QueryTools;
@@ -914,7 +895,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
base64Image = queryResult.CapturedImage;
if (settings.showThinkingProcess)
{
- AddAssistantMessage("[P.I.A] 视觉传感器已激活,图像已捕获...");
+ AddAssistantMessage("[P.I.A] 瑙嗚浼犳劅鍣ㄥ凡婵娲伙紝鍥惧儚宸叉崟鑾...");
}
}
@@ -965,7 +946,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
if (settings.showThinkingProcess)
{
- AddAssistantMessage("[P.I.A] 正在计算最优战术方案...");
+ AddAssistantMessage("[P.I.A] 姝e湪璁$畻鏈浼樻垬鏈柟妗...");
}
var actionPhase = RequestPhase.ActionTools;
@@ -998,19 +979,14 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
"Preserve the intent of the previous output.\n" +
"If the previous output indicates no action is needed or refuses action, output exactly: .\n" +
"Do NOT invent new actions.\n" +
- "Output VALID XML tool calls only. No natural language, no commentary.\nIf the previous output contains JSON (including JSON code blocks or inline {\"point\": [x,y]}), convert it to XML.\n- For a point coordinate, output: .......\n- If coordinates are larger than 1 (e.g., 0-1000), normalize by dividing by 1000.\nIgnore any non-XML text.\n" +
- "Allowed tags: , , , , , , , .\n" +
+ "Output VALID XML tool calls only. No natural language, no commentary.\nIgnore any non-XML text.\n" +
+ "Allowed tags: , , , , , .\n" +
"\nAction tool XML formats:\n" +
"- - DefNameInt
\n" +
"- PawnKindDef: Count, ...\n" +
"- DefNameIntInt\n" +
"- Int\n" +
- "- FloatFloat\n" +
- "- Int\n" +
- "- String\n" +
- "- 0-10-10-10-1\n" +
- "- String (e.g. 'enter', 'esc', 'space')\n" +
- "- Float\n" +
+ "- DefNameIntInt\n" +
"\nPrevious output:\n" + TrimForPrompt(actionResponse, 600);
string fixedResponse = await client.GetChatCompletionAsync(fixInstruction, actionContext, maxTokens: 2048, temperature: 0.1f);
bool fixedHasXml = !string.IsNullOrEmpty(fixedResponse) && IsXmlToolCall(fixedResponse);
@@ -1073,19 +1049,14 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
"Preserve the intent of the previous output.\n" +
"If the previous output indicates no action is needed or refuses action, output exactly: .\n" +
"Do NOT invent new actions.\n" +
- "Output VALID XML tool calls only. No natural language, no commentary.\nIf the previous output contains JSON (including JSON code blocks or inline {\"point\": [x,y]}), convert it to XML.\n- For a point coordinate, output: .......\n- If coordinates are larger than 1 (e.g., 0-1000), normalize by dividing by 1000.\nIgnore any non-XML text.\n" +
- "Allowed tags: , , , , , , , .\n" +
+ "Output VALID XML tool calls only. No natural language, no commentary.\nIgnore any non-XML text.\n" +
+ "Allowed tags: , , , , , .\n" +
"\nAction tool XML formats:\n" +
"- - DefNameInt
\n" +
"- PawnKindDef: Count, ...\n" +
"- DefNameIntInt\n" +
"- Int\n" +
- "- FloatFloat\n" +
- "- Int\n" +
- "- String\n" +
- "- 0-10-10-10-1\n" +
- "- String\n" +
- "- Float\n" +
+ "- DefNameIntInt\n" +
"\nPrevious output:\n" + TrimForPrompt(retryActionResponse, 600);
string retryFixedResponse = await client.GetChatCompletionAsync(retryFixInstruction, retryActionContext, maxTokens: 2048, temperature: 0.1f);
bool retryFixedHasXml = !string.IsNullOrEmpty(retryFixedResponse) && IsXmlToolCall(retryFixedResponse);
@@ -1151,7 +1122,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
if (settings.showThinkingProcess)
{
- AddAssistantMessage("[P.I.A] 正在汇总战报并建立通讯记录...");
+ AddAssistantMessage("[P.I.A] 姝e湪姹囨绘垬鎶ュ苟寤虹珛閫氳璁板綍...");
}
// VISUAL CONTEXT FOR REPLY: Pass the image so the AI can describe what it sees.
@@ -1212,52 +1183,6 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
string guidance = "ToolRunner Guidance: Reply to the player in natural language only. Do NOT output any XML. You may include [EXPR:n] to set expression (n=1-6).";
var matches = Regex.Matches(xml ?? "", @"<([a-zA-Z0-9_]+)(?:>.*?\1>|/>)", RegexOptions.Singleline);
-
- // GEMINI 3 JSON FALLBACK (Restored & Fixed)
- // If no XML found, try to parse JSON markdown block commonly output by Gemini 3 Flash Preview
- // Uses a 0-1000 coordinate system typically.
- if (matches.Count == 0 && (xml ?? "").Contains("```json"))
- {
- try
- {
- var jsonMatch = Regex.Match(xml, @"```json\s*(\[.*?\])\s*```", RegexOptions.Singleline);
- if (jsonMatch.Success)
- {
- string jsonArr = jsonMatch.Groups[1].Value;
- // Regex to extract objects with "point" (and optional "action")
- // Matches: { "point": [123, 456] }
- // Note: In verbatim string @"""", double quotes are escaped as "". Not \".
- var pointMatches = Regex.Matches(jsonArr, @"\{.*?\""point\""\s*:\s*\[\s*([\d\.]+)\s*,\s*([\d\.]+)\s*\].*?\}", RegexOptions.Singleline);
-
- if (pointMatches.Count > 0)
- {
- StringBuilder synthesizedXml = new StringBuilder();
- foreach (Match pm in pointMatches)
- {
- if (float.TryParse(pm.Groups[1].Value, out float xVal) && float.TryParse(pm.Groups[2].Value, out float yVal))
- {
- // Gemini uses 0-1000 scale usually
- if (xVal > 1 || yVal > 1)
- {
- xVal /= 1000.0f;
- yVal /= 1000.0f;
- }
- synthesizedXml.Append($"{xVal}{yVal}");
- }
- }
- if (synthesizedXml.Length > 0)
- {
- xml = synthesizedXml.ToString() + "\n" + xml; // Prepend to process downstream
- matches = Regex.Matches(xml, @"<([a-zA-Z0-9_]+)(?:>.*?\1>|/>)", RegexOptions.Singleline);
- }
- }
- }
- }
- catch (Exception ex)
- {
- WulaLog.Debug($"[JSON Fallback Error] {ex.Message}");
- }
- }
if (matches.Count == 0 || (matches.Count == 1 && matches[0].Groups[1].Value.Equals("no_action", StringComparison.OrdinalIgnoreCase)))
{
diff --git a/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_CallPrefabAirdrop.cs b/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_CallPrefabAirdrop.cs
new file mode 100644
index 00000000..e4247507
--- /dev/null
+++ b/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_CallPrefabAirdrop.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using RimWorld;
+using UnityEngine;
+using Verse;
+
+namespace WulaFallenEmpire.EventSystem.AI.Tools
+{
+ public class Tool_CallPrefabAirdrop : AITool
+ {
+ public override string Name => "call_prefab_airdrop";
+ public override string Description => "Calls a large prefab building airdrop at the specified coordinates. " +
+ "You must specify the prefabDefName (e.g., 'WULA_NewColonyBase') and the coordinates (x, z). " +
+ "TIP: Use the 'get_available_prefabs' tool first to see which structures are available. " +
+ "The default skyfaller animation is 'WULA_Prefab_Incoming'.";
+ public override string UsageSchema => "DefName of the prefabOptional, default is WULA_Prefab_Incomingintint";
+
+ public override string Execute(string args)
+ {
+ try
+ {
+ var parsed = ParseXmlArgs(args);
+
+ if (!parsed.TryGetValue("prefabDefName", out string prefabDefName) || string.IsNullOrWhiteSpace(prefabDefName))
+ {
+ return "Error: Missing . Example: WULA_NewColonyBase";
+ }
+
+ if (!parsed.TryGetValue("x", out string xStr) || !int.TryParse(xStr, out int x) ||
+ !parsed.TryGetValue("z", out string zStr) || !int.TryParse(zStr, out int z))
+ {
+ return "Error: Missing or invalid target coordinates. Provide and .";
+ }
+
+ string skyfallerDefName = parsed.TryGetValue("skyfallerDef", out string sd) && !string.IsNullOrWhiteSpace(sd)
+ ? sd.Trim()
+ : "WULA_Prefab_Incoming";
+
+ Map map = Find.CurrentMap;
+ if (map == null) return "Error: No active map.";
+
+ IntVec3 targetCell = new IntVec3(x, 0, z);
+ if (!targetCell.InBounds(map)) return $"Error: Target {targetCell} is out of bounds.";
+
+ // Check if prefab exists
+ PrefabDef prefabDef = DefDatabase.GetNamed(prefabDefName, false);
+ if (prefabDef == null)
+ {
+ return $"Error: PrefabDef '{prefabDefName}' not found.";
+ }
+
+ // Check if skyfaller exists
+ ThingDef skyfallerDef = DefDatabase.GetNamed(skyfallerDefName, false);
+ if (skyfallerDef == null)
+ {
+ return $"Error: Skyfaller ThingDef '{skyfallerDefName}' not found.";
+ }
+
+ // Spawning must happen on main thread
+ string resultMessage = $"Success: Scheduled airdrop for '{prefabDefName}' at {targetCell} using {skyfallerDefName}.";
+
+ // We use a closure to capture the parameters
+ string pDef = prefabDefName;
+ ThingDef sDef = skyfallerDef;
+ IntVec3 cell = targetCell;
+ Map targetMap = map;
+
+ LongEventHandler.ExecuteWhenFinished(() =>
+ {
+ try
+ {
+ var skyfaller = (Skyfaller_PrefabSpawner)SkyfallerMaker.MakeSkyfaller(sDef);
+ skyfaller.prefabDefName = pDef;
+ GenSpawn.Spawn(skyfaller, cell, targetMap);
+ WulaLog.Debug($"[WulaAI] Prefab airdrop spawned: {pDef} at {cell}");
+ }
+ catch (Exception ex)
+ {
+ WulaLog.Debug($"[WulaAI] Failed to spawn prefab airdrop on main thread: {ex.Message}");
+ }
+ });
+
+ return resultMessage;
+ }
+ catch (Exception ex)
+ {
+ return $"Error: {ex.Message}";
+ }
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_GetAvailablePrefabs.cs b/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_GetAvailablePrefabs.cs
new file mode 100644
index 00000000..ba72f370
--- /dev/null
+++ b/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_GetAvailablePrefabs.cs
@@ -0,0 +1,68 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using RimWorld;
+using Verse;
+
+namespace WulaFallenEmpire.EventSystem.AI.Tools
+{
+ public class Tool_GetAvailablePrefabs : AITool
+ {
+ public override string Name => "get_available_prefabs";
+ public override string Description => "Returns a list of available building prefabs (blueprints) that can be summoned. " +
+ "Use this to find the correct 'prefabDefName' for the 'call_prefab_airdrop' tool.";
+ public override string UsageSchema => "";
+
+ public override string Execute(string args)
+ {
+ try
+ {
+ var prefabs = DefDatabase.AllDefs.ToList();
+ if (prefabs.Count == 0)
+ {
+ return "No prefabs found in the database.";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.AppendLine($"Found {prefabs.Count} available prefabs:");
+
+ // Group by prefix to help AI categorize
+ var wulaPrefabs = prefabs.Where(p => p.defName.StartsWith("WULA_", StringComparison.OrdinalIgnoreCase)).ToList();
+ var otherPrefabs = prefabs.Where(p => !p.defName.StartsWith("WULA_", StringComparison.OrdinalIgnoreCase)).ToList();
+
+ if (wulaPrefabs.Count > 0)
+ {
+ sb.AppendLine("\n[Wula Empire Specialized Prefabs]:");
+ foreach (var p in wulaPrefabs)
+ {
+ string label = !string.IsNullOrEmpty(p.label) ? $" ({p.label})" : "";
+ sb.AppendLine($"- {p.defName}{label}, Size: {p.size}");
+ }
+ }
+
+ if (otherPrefabs.Count > 0)
+ {
+ sb.AppendLine("\n[Generic/Other Prefabs]:");
+ // Limit generic ones to avoid token bloat
+ var genericToShow = otherPrefabs.Take(20).ToList();
+ foreach (var p in genericToShow)
+ {
+ string label = !string.IsNullOrEmpty(p.label) ? $" ({p.label})" : "";
+ sb.AppendLine($"- {p.defName}{label}, Size: {p.size}");
+ }
+ if (otherPrefabs.Count > 20)
+ {
+ sb.AppendLine($"- ... and {otherPrefabs.Count - 20} more generic prefabs.");
+ }
+ }
+
+ return sb.ToString();
+ }
+ catch (Exception ex)
+ {
+ return $"Error: {ex.Message}";
+ }
+ }
+ }
+}
diff --git a/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_VisualClick.cs b/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_VisualClick.cs
deleted file mode 100644
index 9a43db5d..00000000
--- a/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_VisualClick.cs
+++ /dev/null
@@ -1,167 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using UnityEngine;
-using WulaFallenEmpire.EventSystem.AI.Agent;
-
-namespace WulaFallenEmpire.EventSystem.AI.Tools
-{
- ///
- /// 瑙嗚鐐瑰嚮宸ュ叿 - 浣跨敤 VLM 鍒嗘瀽灞忓箷鍚庢ā鎷熼紶鏍囩偣鍑
- /// 閫傜敤浜庡師鐗 API 鏃犳硶鐩存帴鎿嶄綔鐨 mod UI 鍏冪礌
- ///
- public class Tool_VisualClick : AITool
- {
- public override string Name => "visual_click";
-
- public override string Description =>
- "鍦ㄦ寚瀹氱殑灞忓箷浣嶇疆鎵ц榧犳爣鐐瑰嚮銆傚潗鏍囦娇鐢ㄦ瘮渚嬪 (0-1)锛(0,0) 鏄乏涓婅锛(1,1) 鏄彸涓嬭銆" +
- "閫傜敤浜庣偣鍑绘棤娉曢氳繃 API 鎿嶄綔鐨 mod 鎸夐挳鎴 UI 鍏冪礌銆傚厛浣跨敤 analyze_screen 鑾峰彇鐩爣浣嶇疆鍒嗘瀽銆";
-
- public override string UsageSchema =>
- "0-1涔嬮棿鐨刋姣斾緥0-1涔嬮棿鐨刌姣斾緥鍙夛紝true涓哄彸閿";
-
- public override Task ExecuteAsync(string args)
- {
- try
- {
- var argsDict = ParseXmlArgs(args);
-
- // 瑙f瀽 X 鍧愭爣
- if (!argsDict.TryGetValue("x", out string xStr) || !float.TryParse(xStr, out float x))
- {
- return Task.FromResult("Error: 缂哄皯鏈夋晥鐨 x 鍧愭爣 (0-1涔嬮棿鐨勬瘮渚嬪)");
- }
-
- // 瑙f瀽 Y 鍧愭爣
- if (!argsDict.TryGetValue("y", out string yStr) || !float.TryParse(yStr, out float y))
- {
- return Task.FromResult("Error: 缂哄皯鏈夋晥鐨 y 鍧愭爣 (0-1涔嬮棿鐨勬瘮渚嬪)");
- }
-
- // 楠岃瘉鑼冨洿
- if (x < 0 || x > 1 || y < 0 || y > 1)
- {
- return Task.FromResult($"Error: 鍧愭爣 ({x}, {y}) 瓒呭嚭鑼冨洿锛屽繀椤诲湪 0-1 涔嬮棿");
- }
-
- // 瑙f瀽鍙抽敭閫夐」
- bool rightClick = false;
- if (argsDict.TryGetValue("right_click", out string rightStr))
- {
- rightClick = rightStr.ToLowerInvariant() == "true" || rightStr == "1";
- }
-
- // 鎵ц鐐瑰嚮
- bool success = Agent.MouseSimulator.ClickAtProportional(x, y, rightClick);
-
- if (success)
- {
- string clickType = rightClick ? "鍙抽敭" : "宸﹂敭";
- int screenX = Mathf.RoundToInt(x * Screen.width);
- int screenY = Mathf.RoundToInt(y * Screen.height);
-
- WulaLog.Debug($"[Tool_VisualClick] {clickType}鐐瑰嚮 ({x:F3}, {y:F3}) -> 灞忓箷 ({screenX}, {screenY})");
- return Task.FromResult($"Success: 宸插湪灞忓箷浣嶇疆 ({screenX}, {screenY}) 鎵ц{clickType}鐐瑰嚮");
- }
- else
- {
- return Task.FromResult("Error: 鐐瑰嚮鎿嶄綔澶辫触");
- }
- }
- catch (Exception ex)
- {
- WulaLog.Debug($"[Tool_VisualClick] Error: {ex}");
- return Task.FromResult($"Error: 鐐瑰嚮鎿嶄綔澶辫触 - {ex.Message}");
- }
- }
- }
-
- ///
- /// 瑙嗚杈撳叆鏂囨湰宸ュ叿 - 鍦ㄥ綋鍓嶇劍鐐逛綅缃緭鍏ユ枃鏈
- ///
- public class Tool_VisualTypeText : AITool
- {
- public override string Name => "visual_type_text";
-
- public override string Description =>
- "鍦ㄥ綋鍓嶇劍鐐逛綅缃緭鍏ユ枃鏈傞傜敤浜庨渶瑕佹枃鏈緭鍏ョ殑瀵硅瘽妗嗘垨杈撳叆妗嗐傚簲鍏堢敤 visual_click 鐐瑰嚮杈撳叆妗嗚幏鍙栫劍鐐广";
-
- public override string UsageSchema =>
- "瑕佽緭鍏ョ殑鏂囨湰";
-
- public override Task ExecuteAsync(string args)
- {
- try
- {
- var argsDict = ParseXmlArgs(args);
-
- if (!argsDict.TryGetValue("text", out string text) || string.IsNullOrEmpty(text))
- {
- return Task.FromResult("Error: 缂哄皯瑕佽緭鍏ョ殑鏂囨湰");
- }
-
- // 鑾峰彇褰撳墠榧犳爣浣嶇疆
- var pos = MouseSimulator.GetCurrentPosition();
-
- float propX = Mathf.Clamp01((float)pos.x / Screen.width);
- float propY = Mathf.Clamp01((float)pos.y / Screen.height);
-
- WulaLog.Debug($"[VisualTypeText] Current Pos: ({pos.x}, {pos.y}) -> Proportional: ({propX:F3}, {propY:F3})");
-
- return Task.FromResult(VisualInteractionTools.TypeText(propX, propY, text));
- }
- catch (Exception ex)
- {
- WulaLog.Debug($"[Tool_VisualTypeText] Error: {ex}");
- return Task.FromResult($"Error: 杈撳叆鏂囨湰澶辫触 - {ex.Message}");
- }
- }
- }
-
- ///
- /// 瑙嗚婊氬姩宸ュ叿 - 鍦ㄥ綋鍓嶄綅缃粴鍔ㄩ紶鏍囨粴杞
- ///
- public class Tool_VisualScroll : AITool
- {
- public override string Name => "visual_scroll";
-
- public override string Description =>
- "鍦ㄥ綋鍓嶉紶鏍囦綅缃粴鍔ㄣ傚彲閫夊厛绉诲姩鍒版寚瀹氫綅缃啀婊氬姩銆俤elta 姝f暟鍚戜笂婊氬姩锛岃礋鏁板悜涓嬫粴鍔ㄣ";
-
- public override string UsageSchema =>
- "婊氬姩閲忥紝姝f暟鍚戜笂璐熸暟鍚戜笅鍙夛紝0-1 X鍧愭爣鍙夛紝0-1 Y鍧愭爣";
-
- public override Task ExecuteAsync(string args)
- {
- try
- {
- var argsDict = ParseXmlArgs(args);
-
- if (!argsDict.TryGetValue("delta", out string deltaStr) || !int.TryParse(deltaStr, out int delta))
- {
- return Task.FromResult("Error: 缂哄皯鏈夋晥鐨 delta 鍊");
- }
-
- // 鍙夛細鍏堢Щ鍔ㄥ埌鎸囧畾浣嶇疆
- if (argsDict.TryGetValue("x", out string xStr) && argsDict.TryGetValue("y", out string yStr))
- {
- if (float.TryParse(xStr, out float x) && float.TryParse(yStr, out float y))
- {
- Agent.MouseSimulator.MoveToProportional(x, y);
- System.Threading.Thread.Sleep(10);
- }
- }
-
- Agent.MouseSimulator.Scroll(delta);
-
- string direction = delta > 0 ? "鍚戜笂" : "鍚戜笅";
- return Task.FromResult($"Success: 宸瞷direction}婊氬姩 {Math.Abs(delta)} 鍗曚綅");
- }
- catch (Exception ex)
- {
- WulaLog.Debug($"[Tool_VisualScroll] Error: {ex}");
- return Task.FromResult($"Error: 婊氬姩鎿嶄綔澶辫触 - {ex.Message}");
- }
- }
- }
-}
diff --git a/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_VisualUtils.cs b/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_VisualUtils.cs
deleted file mode 100644
index d652bde1..00000000
--- a/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_VisualUtils.cs
+++ /dev/null
@@ -1,130 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Threading.Tasks;
-using UnityEngine;
-using WulaFallenEmpire.EventSystem.AI.Agent;
-
-namespace WulaFallenEmpire.EventSystem.AI.Tools
-{
- public abstract class VisualToolBase : AITool
- {
- protected bool GetFloat(Dictionary dict, string key, out float result)
- {
- result = 0f;
- if (dict.TryGetValue(key, out string val) && float.TryParse(val, out result))
- return true;
- return false;
- }
-
- public abstract override Task ExecuteAsync(string args);
- }
-
- ///
- /// 瑙嗚鎷栨嫿宸ュ叿
- ///
- public class Tool_VisualDrag : VisualToolBase
- {
- public override string Name => "visual_drag";
- public override string Description => "浠庤捣濮嬪潗鏍囨嫋鎷藉埌缁撴潫鍧愭爣銆傞傜敤浜庢閫夊崟浣嶃佹嫋鍔ㄦ粦鍧楁垨鍦板浘銆";
- public override string UsageSchema => "0-10-10-10-1绉(榛樿0.5)";
-
- public override Task ExecuteAsync(string args)
- {
- try
- {
- var dict = ParseXmlArgs(args);
- if (!GetFloat(dict, "start_x", out float sx) || !GetFloat(dict, "start_y", out float sy) ||
- !GetFloat(dict, "end_x", out float ex) || !GetFloat(dict, "end_y", out float ey))
- return Task.FromResult("Error: 缂哄皯鏈夋晥鐨勫潗鏍囧弬鏁 (0-1)");
-
- float duration = 0.5f;
- if (GetFloat(dict, "duration", out float d)) duration = d;
-
- return Task.FromResult(VisualInteractionTools.MouseDrag(sx, sy, ex, ey, duration));
- }
- catch (Exception ex) { return Task.FromResult($"Error: {ex.Message}"); }
- }
- }
-
- ///
- /// 瑙嗚蹇嵎閿伐鍏 (閫氱敤)
- ///
- public class Tool_VisualHotkey : VisualToolBase
- {
- public override string Name => "visual_hotkey";
- public override string Description => "鍦ㄦ寚瀹氫綅缃偣鍑伙紙鍙夛級骞舵寜涓嬪揩鎹烽敭銆傛敮鎸佺粍鍚堥敭濡 'ctrl+c', 'alt+f4', 鍗曢敭濡 'enter', 'esc', 'r', 'space'銆";
- public override string UsageSchema => "蹇嵎閿鍙鍙";
-
- public override Task ExecuteAsync(string args)
- {
- try
- {
- var dict = ParseXmlArgs(args);
- string key = dict.ContainsKey("key") ? dict["key"] : "";
- if (string.IsNullOrEmpty(key)) return Task.FromResult("Error: 缂哄皯 key 鍙傛暟");
-
- // 濡傛灉鎻愪緵浜嗗潗鏍囷紝鍏堢偣鍑
- if (GetFloat(dict, "x", out float x) && GetFloat(dict, "y", out float y))
- {
- return Task.FromResult(VisualInteractionTools.PressHotkey(x, y, key));
- }
- else
- {
- // 鍦ㄥ綋鍓嶄綅缃洿鎺ユ寜閿
- var pos = MouseSimulator.GetCurrentPosition();
- float propX = Mathf.Clamp01((float)pos.x / Screen.width);
- float propY = Mathf.Clamp01(1.0f - ((float)pos.y / Screen.height));
- return Task.FromResult(VisualInteractionTools.PressHotkey(propX, propY, key));
- }
- }
- catch (Exception ex) { return Task.FromResult($"Error: {ex.Message}"); }
- }
- }
-
- ///
- /// 瑙嗚绛夊緟宸ュ叿
- ///
- public class Tool_VisualWait : VisualToolBase
- {
- public override string Name => "visual_wait";
- public override string Description => "绛夊緟鎸囧畾鏃堕棿銆傜敤浜庣瓑寰匲I鍔ㄧ敾鎴栧姞杞姐";
- public override string UsageSchema => "绉掓暟";
-
- public override Task ExecuteAsync(string args)
- {
- try
- {
- var dict = ParseXmlArgs(args);
- if (!GetFloat(dict, "seconds", out float seconds)) return Task.FromResult("Error: 缂哄皯 seconds 鍙傛暟");
- return Task.FromResult(VisualInteractionTools.Wait(seconds));
- }
- catch (Exception ex) { return Task.FromResult($"Error: {ex.Message}"); }
- }
- }
-
- ///
- /// 瑙嗚鍒犻櫎鏂囨湰宸ュ叿
- ///
- public class Tool_VisualDeleteText : VisualToolBase
- {
- public override string Name => "visual_delete_text";
- public override string Description => "鐐瑰嚮鎸囧畾浣嶇疆骞舵寜 Backspace 鍒犻櫎鎸囧畾鏁伴噺鐨勫瓧绗︺傜敤浜庢竻绌鸿緭鍏ユ銆";
- public override string UsageSchema => "0-10-1瀛楃鏁(榛樿1)";
-
- public override Task ExecuteAsync(string args)
- {
- try
- {
- var dict = ParseXmlArgs(args);
- if (!GetFloat(dict, "x", out float x) || !GetFloat(dict, "y", out float y))
- return Task.FromResult("Error: 缂哄皯鏈夋晥鐨勫潗鏍囧弬鏁");
-
- int count = 1;
- if (dict.TryGetValue("count", out string cStr) && int.TryParse(cStr, out int c)) count = c;
-
- return Task.FromResult(VisualInteractionTools.DeleteText(x, y, count));
- }
- catch (Exception ex) { return Task.FromResult($"Error: {ex.Message}"); }
- }
- }
-}