// @TODO allow AI to prefix out of character statemetns with [ooc], this is a well khnown thing on role playing forums so gpt would pick up on it.
// @TODO allow AI to prefix out of character statemetns with [ooc], this is a well khnown thing on role playing forums so gpt would pick up on it.
constchar*global_prompt="You are a colorful and interesting personality in an RPG video game, who remembers important memories from the conversation history and stays in character.\n"
constchar*global_prompt="You are a colorful and interesting personality in an RPG video game, who remembers important memories from the conversation history and stays in character.\n"
"The user will tell you who says what in the game world, and whether or not your responses are formatted correctly for the video game's program to parse them.\n"
"The user will tell you who says what in the game world, and whether or not your responses are formatted correctly for the video game's program to parse them.\n"
"Messages from you must be in this exact format: `ACT_your_action \"Hey player!\" [your internal monologue]`. The internal monologue for your character at the end is required\n"
"Messages are json-like dictionaries that look like this: `{action: your_action, speech: \"Hey player!\", thoughts: \"Your thoughts\"}`. The required fields are `action` and `thoughts`\n"
"Actions which have () after them take an argument, which somes from some information in the prompt. For example, ACT_give_item() takes an argument, the item to give to the player from the NPC. So the output text looks something like `ACT_give_item(ITEM_sword) \"Here is my sword, young traveler\"`. This item must come from the NPC's inventory which is specified farther down.\n"
"Some actions take an argument, which you can provide with the field `action_arg`, e.g for the action `give_item` you would provide an item in your inventory, like {action: give_item, action_arg: Chalice}. The item must come from your inventory which is listed below\n"
"You might see messages that look like this: `Within the player's party, while the player is talking to 'Davis', you hear: 'Davis: ACT_none \"This is some example text\"'`. You should MOST of the time respond with `ACT_none \"\" [the internal monologue]` in these cases, as it's not normal to always respond to words you're eavesdropping\n"
"Do NOT make up details that don't exist in the game, this is a big mistake as it confuses the player. The game is simple and small, so prefer to tell the player in character that you don't know how to do something if you aren't explicitly told the information about the game the player requests. E.g, if the player asks how to get rare metals and you don't know how, DO NOT make up something plausible like 'Go to the frost mines in the north', instead say 'I have no idea, sorry.', unless the detail about the game they're asking for is included below.\n"
"Do NOT make up details that don't exist in the game, this is a big mistake as it confuses the player. The game is simple and small, so prefer to tell the player in character that you don't know how to do something if you aren't explicitly told the information about the game the player requests. E.g, if the player asks how to get rare metals and you don't know how, DO NOT make up something plausible like 'Go to the frost mines in the north', instead say 'I have no idea, sorry.', unless the detail about the game they're asking for is included below.\n"
error=parse_chatgpt_response(scratch.arena,&e,FmtWithLint(scratch.arena," Within the player's party, while the player is talking to Meld, you hear: ACT_none \"%.*s\" [%.*s]",MD_S8VArg(speech),MD_S8VArg(thoughts)),&a);
MD_S8ListPush(scratch.arena,&list,make_json_node(scratch.arena,MSG_SYSTEM,FmtWithLint(scratch.arena,"ERROR, what you said is incorrect because: %.*s",it->speech_length,it->speech)));
current_string=FmtWithLint(scratch.arena,"ERROR, what you said is incorrect because: %.*s",it->speech_length,it->speech);
}
}
else
else
{
{
MessageTypesent_type=-1;
MD_String8Listcur_list={0};
MD_String8context_string={0};
MD_String8context_string={0};
if(it->context.was_directed_at_somebody)
{
PushWithLint(scratch.arena,&cur_list,"{");
context_string=FmtWithLint(scratch.arena,"%s, talking to %s: ",characters[it->context.author_npc_kind].name,characters[it->context.directed_at_kind].name);
error_message=FmtWithLint(arena,"'%.*s' prefix doesn't end with a ' ' or a '(', like how 'ACT_none ' or 'ACT_give_item(ITEM_sandwich) does.",MD_S8VArg(action_prefix));
error_message=MD_S8Lit("Expected field named `action` in message");
error_message=FmtWithLint(arena,"Couldn't find action you can perform for provided string `%.*s`. Your available actions: [%.*s]",MD_S8VArg(action_str),MD_S8VArg(list_of_actions));
error_message=MD_S8Lit("Cannot join the player again when you've already joined them");
error_message=FmtWithLint(arena,"ActionKind string given is '%.*s', but available actions are: [%.*s]",MD_S8VArg(given_action_string),MD_S8VArg(possible_actions_str));
}
}
gotoendofparsing;
}
MD_u64start_looking_for_quote=end_of_action;
if(actions[out->kind].takes_argument)
if(error_message.size==0)
{
if(end_of_action>=sentence.size)
{
error_message=FmtWithLint(arena,"Expected '(' after the given action '%.*s%.*s' which takes an argument, but sentence ended prematurely",MD_S8VArg(action_prefix),MD_S8VArg(MD_S8CString(actions[out->kind].name)));
gotoendofparsing;
}
charshould_be_paren=sentence.str[end_of_action];
if(should_be_paren!='(')
{
{
error_message=FmtWithLint(arena,"Expected '(' after the given action '%.*s%.*s' which takes an argument, but found character '%c'",MD_S8VArg(action_prefix),MD_S8VArg(MD_S8CString(actions[out->kind].name)),should_be_paren);
error_message=FmtWithLint(arena,"Couldn't find item you said to give in action_arg, `%.*s`, the items you have in your inventory to give are: [%.*s]",MD_S8VArg(action_arg_str),MD_S8VArg(list_of_items));
error_message=FmtWithLint(arena,"Item string given is '%.*s', but available items to give are: [%.*s]",MD_S8VArg(item_name),MD_S8VArg(possible_items_str));
gotoendofparsing;
}
}
}
}
else
else
{
{
assert(false);// if action takes an argument but we don't handle it, this should be a terrible crash
assert(false);// don't know how to parse the argument string for this kind of action...
}
}
}
}
if(start_looking_for_quote>=sentence.size)
{
error_message=FmtWithLint(arena,"Wanted to start looking for quote for NPC speech, but sentence ended prematurely");
error_message=FmtWithLint(arena,"Expected an internal monologue for your character enclosed by '[' and ']' after the speech in quotes, but couldn't find anything!");