Extend the AI response system to parse JSON responses, extracting both text and commands. If the AI responds with valid JSON, extract and execute commands (like doEmote). Otherwise, proceed with current plain text behavior.
New function getCommandContext():String
- Returns information about available commands the AI can use
- Include 15 valid emotes:
happy,angry,love,sad,joy,blush,devious,shock,terrified,homesick,mad,oreally,ill,hmph,snowSplat
Modified buildPrompt()
- Add command context to the prompt so AI knows available commands
New function parseAiResponse(response:String, aiPlayer:GlobalPlayerInstance):String
- Try to parse response as JSON using
haxe.Json.parse() - If valid JSON, extract:
textfield: the response text to speakcommandfield: if present, contains{type: "doEmote", emote: "emoteName"}
- If not valid JSON, return original response
- If command is
doEmote, callaiPlayer.doEmote(Emote.emoteName)
JSON Format expected from AI:
{
"text": "Response text here",
"command": {
"type": "doEmote",
"emote": "happy"
}
}Or plain text (current behavior):
Just a normal response text
In AiHandler.respondToPlayerAsync():
- Call
parseAiResponse(response, fromPlayer)BEFOREsendResponseInChunks() - Pass the parsed text (extracted from JSON if applicable) to
sendResponseInChunks() - This ensures commands are executed and text is extracted before chunking/sending
Flow:
ChatResponse()returns raw responseparseAiResponse(response, fromPlayer)- extracts text, executes commandssendResponseInChunks(fromPlayer, parsedText, onSuccess)- handles the extracted text- Callback in
AiBase.hxjust callsmyPlayer.say(response)(text already parsed)
- Add new function after
checkIfShouldDoCommand():
private static function getCommandContext():String {
return "You can also respond with a JSON command object to perform actions:
{ \"text\": \"your response\", \"command\": { \"type\": \"doEmote\", \"emote\": \"emoteName\" } }
Available emotes: happy, angry, love, sad, joy, blush, devious, shock, terrified, homesick, mad, oreally, ill, hmph, snowSplat";
}-
Modify
buildPrompt()to include command context before the user message. -
Add new function for parsing and executing commands:
private static function parseAiResponse(response:String, aiPlayer:GlobalPlayerInstance):String {
var text = response;
try {
var json = haxe.Json.parse(response);
if (Reflect.hasField(json, "text")) {
text = json.text;
}
if (Reflect.hasField(json, "command")) {
var command = json.command;
if (Reflect.hasField(command, "type") && command.type == "doEmote") {
var emoteName = command.emote;
var emoteId = getEmoteId(emoteName);
if (emoteId >= 0) {
aiPlayer.doEmote(emoteId);
}
}
}
} catch(e:Dynamic) {
// Not valid JSON, use original response
}
return text;
}- Add helper function to get emote ID by name:
private static function getEmoteId(emoteName:String):Int {
switch(emoteName.toLowerCase()) {
case "happy": return 0;
case "mad": return 1;
case "angry": return 2;
case "sad": return 3;
case "devious": return 4;
case "joy": return 5;
case "blush": return 6;
case "snowSplat": return 8;
case "oreally": return 14;
case "ill": return 10;
case "hmph": return 12;
case "shock": return 15;
case "love": return 13;
case "terrified": return 27;
case "homesick": return 28;
default: return -1;
}
}- Modify
respondToPlayerAsync()inAiHandler.hxto callparseAiResponsebeforesendResponseInChunks:
// Spawn a new thread to call the LLM without blocking the main thread
Thread.create(function() {
// Call ChatResponse directly with the pre-built prompt
var response = ChatResponse(fullPrompt);
response = response.split("\n").join(" ");
// Log the conversation to file (thread-safe)
logToFile(fullPrompt, response);
// Add to chat memory only if call succeeded
if (response != null) {
fromSoul.addChatEntry(toPlayer, message, response);
}
// Parse response to extract text and execute commands BEFORE sending
var parsedText = parseAiResponse(response, fromPlayer);
// Execute the callback with the parsed text
sendResponseInChunks(fromPlayer, parsedText, onSuccess);
});- Simplify the
onSuccesscallback inAiBase.hx:4961to just say the response:
AiHandler.respondToPlayerAsync(aiPlayer, player, text, function(response:String) {
if (response != null) {
myPlayer.say(response);
// ... rest of existing code
}
});| File | Changes |
|---|---|
AiHandler.hx |
Add getCommandContext(), parseAiResponse(), getEmoteId() functions; modify buildPrompt() to include command context; call parseAiResponse() before sendResponseInChunks() in respondToPlayerAsync() |
AiBase.hx |
Simplify respondToPlayerAsync() callback - just calls myPlayer.say(response) since text is already parsed |