diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll
index 6153219b..7a17183d 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/Tools/Tool_SpawnResources.cs b/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_SpawnResources.cs
index 3e1200dd..67158c32 100644
--- a/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_SpawnResources.cs
+++ b/Source/WulaFallenEmpire/EventSystem/AI/Tools/Tool_SpawnResources.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Text;
+using System.Text.RegularExpressions;
using RimWorld;
using Verse;
using WulaFallenEmpire.EventSystem.AI.Utils;
@@ -10,46 +11,70 @@ namespace WulaFallenEmpire.EventSystem.AI.Tools
public class Tool_SpawnResources : AITool
{
public override string Name => "spawn_resources";
- public override string Description => "Spawns resources via drop pod. Accepts a natural language description of items and quantities (e.g., '5 beef, 10 medicine'). " +
+ public override string Description => "Spawns resources via drop pod. " +
"IMPORTANT: You MUST decide the quantity based on your goodwill and mood. " +
"Do NOT blindly follow the player's requested amount. " +
"If goodwill is low (< 0), give significantly less than asked or refuse. " +
"If goodwill is high (> 50), you may give what is asked or slightly more. " +
"Otherwise, give a moderate amount.";
- public override string UsageSchema => "string describing items";
+ public override string UsageSchema => "- Item NameInteger
";
public override string Execute(string args)
{
try
{
- var parsedArgs = ParseXmlArgs(args);
- string request = "";
+ // Custom XML parsing for nested items
+ var itemsToSpawn = new List<(ThingDef def, int count)>();
- if (parsedArgs.TryGetValue("request", out string req))
+ // Match all - ...
blocks
+ var itemMatches = Regex.Matches(args, @"- (.*?)
", RegexOptions.Singleline);
+
+ foreach (Match match in itemMatches)
{
- request = req;
- }
- else
- {
- // Fallback: try to treat the whole args as the request if parsing failed or format is weird
- // But with strict XML, this shouldn't happen often.
- // Let's just log a warning or return error.
- // Actually, for robustness, if the args doesn't contain tags, maybe it's raw text?
- if (!args.Trim().StartsWith("<"))
+ string itemXml = match.Groups[1].Value;
+
+ // Extract name (supports or for backward compatibility)
+ string name = "";
+ var nameMatch = Regex.Match(itemXml, @"(.*?)");
+ if (nameMatch.Success)
{
- request = args;
+ name = nameMatch.Groups[1].Value;
+ }
+ else
+ {
+ var defNameMatch = Regex.Match(itemXml, @"(.*?)");
+ if (defNameMatch.Success) name = defNameMatch.Groups[1].Value;
+ }
+
+ if (string.IsNullOrEmpty(name)) continue;
+
+ // Extract count
+ var countMatch = Regex.Match(itemXml, @"(.*?)");
+ if (!countMatch.Success) continue;
+ if (!int.TryParse(countMatch.Groups[1].Value, out int count)) continue;
+
+ // Search for ThingDef using fuzzy search
+ ThingDef def = null;
+ var searchResult = ThingDefSearcher.ParseAndSearch(name);
+ if (searchResult.Count > 0)
+ {
+ def = searchResult[0].Def;
+ }
+ else
+ {
+ // Fallback: try exact defName match just in case
+ def = DefDatabase.GetNamed(name, false);
+ }
+
+ if (def != null && count > 0)
+ {
+ itemsToSpawn.Add((def, count));
}
}
- if (string.IsNullOrEmpty(request))
+ if (itemsToSpawn.Count == 0)
{
- return "Error: Empty request. Usage: ...";
- }
-
- var items = ThingDefSearcher.ParseAndSearch(request);
- if (items.Count == 0)
- {
- return $"Error: Could not identify any valid items in request '{request}'.";
+ return "Error: No valid items found in request. Usage: - ......
";
}
Map map = Find.CurrentMap;
@@ -63,12 +88,12 @@ namespace WulaFallenEmpire.EventSystem.AI.Tools
StringBuilder resultLog = new StringBuilder();
resultLog.Append("Success: Dropped ");
- foreach (var item in items)
+ foreach (var (def, count) in itemsToSpawn)
{
- Thing thing = ThingMaker.MakeThing(item.Def);
- thing.stackCount = item.Count;
+ Thing thing = ThingMaker.MakeThing(def);
+ thing.stackCount = count;
thingsToDrop.Add(thing);
- resultLog.Append($"{item.Count}x {item.Def.label}, ");
+ resultLog.Append($"{count}x {def.label}, ");
}
if (thingsToDrop.Count > 0)
diff --git a/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs b/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs
index 83fb57dd..c5f6087f 100644
--- a/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs
+++ b/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs
@@ -53,14 +53,28 @@ Use this tool when:
- You have ALREADY verified their need in a previous turn using `get_colonist_status` and `get_map_resources`.
CRITICAL: The quantity you provide is NOT what the player asks for. It MUST be based on your internal goodwill. Low goodwill (<0) means giving less or refusing. High goodwill (>50) means giving the requested amount or more.
Parameters:
-- request: (REQUIRED) A natural language string describing the items and quantities.
+- items: (REQUIRED) A list of items to spawn. Each item must have a `name` (English label or DefName) and `count`.
Usage:
- string describing items
+
+ -
+ Item Name
+ Integer
+
+
Example:
- 50 MealSimple, 10 MedicineIndustrial
+
+ -
+ Simple Meal
+ 50
+
+ -
+ Medicine
+ 10
+
+
## modify_goodwill
@@ -162,7 +176,16 @@ When the player requests any form of resources, you MUST follow this multi-turn
- *(Internal thought after confirming they have no medicine)*
- *Your Response (Turn 3)*:
- 50 MealSimple, 10 MedicineIndustrial
+
+ -
+ Simple Meal
+ 50
+
+ -
+ Medicine
+ 10
+
+
4. **Turn 4 (Confirmation)**: After you receive the ""Success"" message from the `spawn_resources` tool, you will finally provide a conversational response to the player.
@@ -365,9 +388,17 @@ When the player requests any form of resources, you MUST follow this multi-turn
}
// 3. Execute the tool directly with the XML string
- // The tools have been updated to parse XML arguments internally.
- Log.Message($"[WulaAI] Executing tool: {toolName} with args: {xml}");
- string result = tool.Execute(xml).Trim();
+ // We need to pass the INNER XML (parameters) to the tool, stripping the root tool tag.
+ // Otherwise, ParseXmlArgs will match the root tag as a parameter.
+ string argsXml = xml;
+ var contentMatch = Regex.Match(xml, $@"<{toolName}>(.*?){toolName}>", RegexOptions.Singleline);
+ if (contentMatch.Success)
+ {
+ argsXml = contentMatch.Groups[1].Value;
+ }
+
+ Log.Message($"[WulaAI] Executing tool: {toolName} with args: {argsXml}");
+ string result = tool.Execute(argsXml).Trim();
string toolResultOutput = (toolName == "modify_goodwill")
? $"Tool '{toolName}' Result (Invisible): {result}"
@@ -470,6 +501,8 @@ When the player requests any form of resources, you MUST follow this multi-turn
var entry = filteredHistory[i];
string text = entry.role == "assistant" ? ParseResponseForDisplay(entry.message) : entry.message;
+ if (string.IsNullOrEmpty(text)) continue;
+
bool isLastMessage = i == filteredHistory.Count - 1;
Text.Font = (isLastMessage && entry.role == "assistant") ? GameFont.Medium : GameFont.Small;
@@ -482,6 +515,9 @@ When the player requests any form of resources, you MUST follow this multi-turn
{
var entry = filteredHistory[i];
string text = entry.role == "assistant" ? ParseResponseForDisplay(entry.message) : entry.message;
+
+ if (string.IsNullOrEmpty(text)) continue;
+
bool isLastMessage = i == filteredHistory.Count - 1;
Text.Font = (isLastMessage && entry.role == "assistant") ? GameFont.Medium : GameFont.Small;
float height = Text.CalcHeight(text, viewRect.width) + 10f; // Increased padding
@@ -511,9 +547,18 @@ When the player requests any form of resources, you MUST follow this multi-turn
private string ParseResponseForDisplay(string rawResponse)
{
if (string.IsNullOrEmpty(rawResponse)) return "";
- // If the response is an XML tool call, don't display it in the chat history.
- if (rawResponse.Trim().StartsWith("<")) return "[Calling Tool...]";
- return rawResponse.Split(new[] { "OPTIONS:" }, StringSplitOptions.None)[0].Trim();
+
+ string text = rawResponse;
+
+ // Remove standard tags with content: content
+ text = Regex.Replace(text, @"<([a-zA-Z0-9_]+)[^>]*>.*?\1>", "", RegexOptions.Singleline);
+
+ // Remove self-closing tags:
+ text = Regex.Replace(text, @"<[a-zA-Z0-9_]+[^>]*/>", "");
+
+ text = text.Trim();
+
+ return text.Split(new[] { "OPTIONS:" }, StringSplitOptions.None)[0].Trim();
}
protected override void DrawSingleOption(Rect rect, EventOption option)