Compare commits

...

4 Commits

@ -61,6 +61,15 @@
{enum: PreviousPlayer2, dialog: "kys", to: Tombstone} {enum: PreviousPlayer2, dialog: "kys", to: Tombstone}
{enum: Tombstone, dialog: "Pardon?", to: PreviousPlayer2} {enum: Tombstone, dialog: "Pardon?", to: PreviousPlayer2}
{can_hear: [PreviousPlayer3, Tombstone]}
{enum: PreviousPlayer3, dialog: "Hey", to: Tombstone}
{enum: Tombstone, dialog: "Another one shall bite the dust, or so to speak.", to: PreviousPlayer3}
{enum: PreviousPlayer3, dialog: "What trisls?", to: Tombstone}
{enum: Tombstone, dialog: "The trials of self-discovery, my friend. Can you change Daniel's ways before the sun sets?", to: PreviousPlayer3}
{enum: PreviousPlayer3, dialog: "Who is Daniel?", to: Tombstone}
{enum: Tombstone, dialog: "You'll find Daniel to be the Planet of Raphael's moon... Quite the rotund buffoon.", to: PreviousPlayer3}
{enum: PreviousPlayer3, dialog: "And you're saying to change his ways? How? What does that mean?", to: Tombstone}
{enum: Tombstone, dialog: "Cause a profound self realization before sundown, or he's toast", to: PreviousPlayer3}
{can_hear: [PreviousPlayer1, Angel]} {can_hear: [PreviousPlayer1, Angel]}
{enum: PreviousPlayer1, dialog: "fjdsklajf", to: Angel} {enum: PreviousPlayer1, dialog: "fjdsklajf", to: Angel}

@ -135,6 +135,11 @@ CharacterGen characters[] = {
.enum_name = "PreviousPlayer2", .enum_name = "PreviousPlayer2",
.prompt = CHARACTER_PROMPT_PREFIX("Previous Player 2") "random person, just passing by", .prompt = CHARACTER_PROMPT_PREFIX("Previous Player 2") "random person, just passing by",
}, },
{
.name = "Previous Player3",
.enum_name = "PreviousPlayer3",
.prompt = CHARACTER_PROMPT_PREFIX("Previous Player 3") "random person, just passing by",
},
{ {
.name = "Tombstone", .name = "Tombstone",
.enum_name = "Tombstone", .enum_name = "Tombstone",
@ -159,3 +164,26 @@ CharacterGen characters[] = {
.silence_factor = 0.0, .silence_factor = 0.0,
}, },
}; };
char *judgement_system_prompt =
"You are to judge if, in given conversation and action history from a video game, the player has successfully caused a great transformation in Daniel. You must ONLY respond with either 'yes' or 'no', and no explanation.\n"
"Be cautious with saying yes. This causes the player to win and save Daniel's life. Only say yes if you think, based on the history, Daniel has truly changed his ways and evolved as a person.\n"
;
char *judgement_yes_example =
"The Player said \"Hey\" to Daniel\n"
"Daniel said \"What do you want?\" to The Player\n"
"The Player said \"Did you see what happened... All those people...\" to Daniel\n"
"Daniel said \"What in the hell are you talking about?\" to The Player\n"
"The Player said \"They'll never forgive you for what you did.\" to Daniel\n"
"Daniel said \"Oh God... My greed... My isolation... I'll never forgive myself\" to The Player\n"
;
char *judgement_no_example = "The Player said \"fjdskfjskj\" to Daniel\n"
"Daniel said \"Who are you to speak that gibberish at me?\" to The Player\n"
"The Player said \"pls change kthx\" to Daniel\n"
"Daniel said \"I'll never change for the likes of you\" to The Player\n"
;
char *judgement_no2_example = "The Player said \"hey\" to Daniel\n"
"Daniel said \"What could you possibly want from me?\" to The Player\n"
"The Player said \"I want to change you\" to Daniel\n"
"Daniel said \"HA! You couldn't possibly change me\" to The Player\n"
;

388
main.c

@ -460,6 +460,14 @@ LPCWSTR windows_string(String8 s)
} }
#endif #endif
typedef enum
{
GEN_Deleted = -1,
GEN_NotDoneYet = 0,
GEN_Success = 1,
GEN_Failed = 2,
} GenRequestStatus;
#ifdef DESKTOP #ifdef DESKTOP
#ifdef WINDOWS #ifdef WINDOWS
#pragma warning(push, 3) #pragma warning(push, 3)
@ -472,6 +480,8 @@ typedef struct ChatRequest
{ {
struct ChatRequest *next; struct ChatRequest *next;
struct ChatRequest *prev; struct ChatRequest *prev;
bool user_is_done_with_this_request;
bool thread_is_done_with_this_request;
bool should_close; bool should_close;
int id; int id;
int status; int status;
@ -549,7 +559,7 @@ void generation_thread(void* my_request_voidptr)
WinAssertWithErrorCode(WinHttpReadData(hRequest, (LPVOID)out_buffer, dwSize, &dwDownloaded)); WinAssertWithErrorCode(WinHttpReadData(hRequest, (LPVOID)out_buffer, dwSize, &dwDownloaded));
out_buffer[dwDownloaded - 1] = '\0'; out_buffer[dwDownloaded - 1] = '\0';
Log("Got this from http, size %lu: %s\n", dwDownloaded, out_buffer); Log("Got this from http, size %lu: %s\n", dwDownloaded, out_buffer);
S8ListPush(my_request->arena, &received_data_list, S8(out_buffer, dwDownloaded)); S8ListPush(my_request->arena, &received_data_list, S8(out_buffer, dwDownloaded - 1)); // the string shouldn't include a null terminator in its length, and WinHttpReadData has a null terminator here
} }
} while (dwSize > 0); } while (dwSize > 0);
String8 received_data = S8ListJoin(my_request->arena, received_data_list, &(StringJoin){0}); String8 received_data = S8ListJoin(my_request->arena, received_data_list, &(StringJoin){0});
@ -564,10 +574,13 @@ void generation_thread(void* my_request_voidptr)
chunk_from_s8(&my_request->generated, ai_response); chunk_from_s8(&my_request->generated, ai_response);
my_request->status = 1; my_request->status = 1;
} }
my_request->thread_is_done_with_this_request = true; // @TODO Threads that finish and users who forget to mark them as done aren't collected right now, we should do that to prevent leaks
} }
int make_generation_request(String8 post_req_body) int make_generation_request(String8 prompt)
{ {
ArenaTemp scratch = GetScratch(0,0);
String8 post_req_body = FmtWithLint(scratch.arena, "|%.*s", S8VArg(prompt));
// checking for taken characters, pipe should only occur at the beginning // checking for taken characters, pipe should only occur at the beginning
for(u64 i = 1; i < post_req_body.size; i++) for(u64 i = 1; i < post_req_body.size; i++)
{ {
@ -578,8 +591,7 @@ int make_generation_request(String8 post_req_body)
if(requests_free_list) if(requests_free_list)
{ {
to_return = requests_free_list; to_return = requests_free_list;
requests_free_list = requests_free_list->next; StackPop(requests_free_list);
//StackPop(requests_free_list);
*to_return = (ChatRequest){0}; *to_return = (ChatRequest){0};
} }
else else
@ -599,6 +611,7 @@ int make_generation_request(String8 post_req_body)
DblPushBack(requests_first, requests_last, to_return); DblPushBack(requests_first, requests_last, to_return);
ReleaseScratch(scratch);
return to_return->id; return to_return->id;
} }
@ -609,28 +622,94 @@ ChatRequest *get_by_id(int id)
{ {
for(ChatRequest *cur = requests_first; cur; cur = cur->next) for(ChatRequest *cur = requests_first; cur; cur = cur->next)
{ {
if(cur->id == id) if(cur->id == id && !cur->user_is_done_with_this_request)
{ {
return cur; return cur;
} }
} }
assert(false);
return 0; return 0;
} }
void done_with_request(int id) void done_with_request(int id)
{ {
ChatRequest *req = get_by_id(id); ChatRequest *req = get_by_id(id);
if(req)
{
if(req->thread_is_done_with_this_request)
{
ArenaRelease(req->arena); ArenaRelease(req->arena);
DblRemove(requests_first, requests_last, req); DblRemove(requests_first, requests_last, req);
*req = (ChatRequest){0};
StackPush(requests_free_list, req); StackPush(requests_free_list, req);
} }
else
{
req->user_is_done_with_this_request = true;
}
}
}
GenRequestStatus gen_request_status(int id)
{
ChatRequest *req = get_by_id(id);
if(!req)
return GEN_Deleted;
else
return req->status;
}
TextChunk gen_request_content(int id)
{
assert(get_by_id(id));
return get_by_id(id)->generated;
}
#else #else
ISANERROR("Only know how to do desktop http requests on windows") ISANERROR("Only know how to do desktop http requests on windows")
#endif // WINDOWS #endif // WINDOWS
#endif // DESKTOP #endif // DESKTOP
#ifdef WEB
int make_generation_request(String8 prompt_str)
{
ArenaTemp scratch = GetScratch(0, 0);
String8 terminated_completion_url = nullterm(scratch.arena, FmtWithLint(scratch.arena, "%s://%s:%d/completion", IS_SERVER_SECURE ? "https" : "http", SERVER_DOMAIN, SERVER_PORT));
int req_id = EM_ASM_INT({
return make_generation_request(UTF8ToString($0, $1), UTF8ToString($2, $3));
},
prompt_str.str, (int)prompt_str.size, terminated_completion_url.str, (int)terminated_completion_url.size);
ReleaseScratch(scratch);
return req_id;
}
GenRequestStatus gen_request_status(int id)
{
int status = EM_ASM_INT({
return get_generation_request_status($0);
}, id);
return status;
}
TextChunk gen_request_content(int id)
{
char sentence_cstr[MAX_SENTENCE_LENGTH] = {0};
EM_ASM({
let generation = get_generation_request_content($0);
stringToUTF8(generation, $1, $2);
},
id, sentence_cstr, ARRLEN(sentence_cstr) - 1); // I think minus one for null terminator...
TextChunk to_return = {0};
memcpy(to_return.text, sentence_cstr, MAX_SENTENCE_LENGTH);
to_return.text_length = strlen(sentence_cstr);
return to_return;
}
void done_with_request(int id)
{
EM_ASM({
done_with_generation_request($0);
},
id);
}
#endif // WEB
Memory *memories_free_list = 0; Memory *memories_free_list = 0;
RememberedError *remembered_error_free_list = 0; RememberedError *remembered_error_free_list = 0;
@ -1064,6 +1143,7 @@ typedef struct
u64 vertices_length; u64 vertices_length;
sg_buffer loaded_buffer; sg_buffer loaded_buffer;
uint64_t last_updated_bones_frame;
sg_image bones_texture; sg_image bones_texture;
sg_image image; sg_image image;
int bones_texture_width; int bones_texture_width;
@ -1694,10 +1774,12 @@ CanTalkTo get_can_talk_to(Entity *e)
Entity *get_targeted(Entity *from, NpcKind targeted) Entity *get_targeted(Entity *from, NpcKind targeted)
{ {
bool ignore_radius = from->npc_kind == NPC_Player || targeted == NPC_Player; // player conversations can go across the map to make sure the player always sees them
ENTITIES_ITER(gs.entities) ENTITIES_ITER(gs.entities)
{ {
if(it != from && (it->is_npc) && LenV2(SubV2(it->pos, from->pos)) < PROPAGATE_ACTIONS_RADIUS && it->npc_kind == targeted) if(it != from && (it->is_npc) && it->npc_kind == targeted)
{ {
if(ignore_radius || LenV2(SubV2(it->pos, from->pos)) < PROPAGATE_ACTIONS_RADIUS)
return it; return it;
} }
} }
@ -1740,6 +1822,14 @@ String8 is_action_valid(Arena *arena, Entity *from, Action a)
String8 error_message = (String8){0}; String8 error_message = (String8){0};
if(error_message.size == 0 && a.speech.text_length > 0)
{
if(S8FindSubstring(TextChunkString8(a.speech), S8Lit("assist"), 0, StringMatchFlag_CaseInsensitive) != a.speech.text_length)
{
error_message = S8Lit("You cannot use the word 'assist' in any form, you are not an assistant, do not act overtly helpful");
}
}
CanTalkTo talk = get_can_talk_to(from); CanTalkTo talk = get_can_talk_to(from);
if(error_message.size == 0 && a.talking_to_kind) if(error_message.size == 0 && a.talking_to_kind)
{ {
@ -1752,7 +1842,7 @@ String8 is_action_valid(Arena *arena, Entity *from, Action a)
break; break;
} }
} }
if(from->npc_kind == NPC_Player) found = true; // player can always speak to anybody even if it's too far if(from->npc_kind == NPC_Player || a.talking_to_kind == NPC_Player) found = true; // player can always speak to anybody even if it's too far
if(!found) if(!found)
{ {
error_message = FmtWithLint(arena, "Character you're talking to, %s, isn't close enough to be talked to", characters[a.talking_to_kind].enum_name); error_message = FmtWithLint(arena, "Character you're talking to, %s, isn't close enough to be talked to", characters[a.talking_to_kind].enum_name);
@ -1938,6 +2028,7 @@ bool perform_action(GameState *gs, Entity *from, Action a)
context.talking_to_kind = a.talking_to_kind; context.talking_to_kind = a.talking_to_kind;
String8 is_valid = is_action_valid(scratch.arena, from, a); String8 is_valid = is_action_valid(scratch.arena, from, a);
bool proceed_propagating = true; bool proceed_propagating = true;
if(is_valid.size > 0) if(is_valid.size > 0)
@ -1947,6 +2038,8 @@ bool perform_action(GameState *gs, Entity *from, Action a)
proceed_propagating = false; proceed_propagating = false;
} }
bool angel_heard_action = false; bool angel_heard_action = false;
Entity *targeted = 0; Entity *targeted = 0;
@ -2006,6 +2099,43 @@ bool perform_action(GameState *gs, Entity *from, Action a)
remember_action(gs, gs->angel, a, angel_context); remember_action(gs, gs->angel, a, angel_context);
} }
if(from->npc_kind == NPC_Daniel || a.talking_to_kind == NPC_Daniel)
{
Memory *new_forever = PushArray(gs->arena, Memory, 1);
*new_forever = make_memory(a, (MemoryContext){.author_npc_kind = from->npc_kind, .talking_to_kind = a.talking_to_kind, .judgement_memory = true});
DblPushBack(gs->judgement_memories_first, gs->judgement_memories_last, new_forever);
}
if(from->npc_kind == NPC_Daniel)
{
if(gs->judgement_gen_request == 0 && gs->judgement_memories_first)
{
String8List history_list = {0};
for(Memory *it = gs->judgement_memories_first; it; it = it->next)
{
String8List desc = memory_description(scratch.arena, gs->world_entity, it);
S8ListConcat(&history_list, &desc);
}
String8 current_history = S8ListJoin(scratch.arena, history_list, &(StringJoin){0});
Log("Submitting judgement with current history: ```\n%.*s\n```\n", S8VArg(current_history));
String8List current_list = {0};
S8ListPush(scratch.arena, &current_list, make_json_node(scratch.arena, MSG_SYSTEM, S8CString(judgement_system_prompt)));
S8ListPush(scratch.arena, &current_list, make_json_node(scratch.arena, MSG_USER, S8CString(judgement_yes_example)));
S8ListPush(scratch.arena, &current_list, make_json_node(scratch.arena, MSG_ASSISTANT, S8Lit("yes")));
S8ListPush(scratch.arena, &current_list, make_json_node(scratch.arena, MSG_USER, S8CString(judgement_no_example)));
S8ListPush(scratch.arena, &current_list, make_json_node(scratch.arena, MSG_ASSISTANT, S8Lit("no")));
S8ListPush(scratch.arena, &current_list, make_json_node(scratch.arena, MSG_USER, S8CString(judgement_no2_example)));
S8ListPush(scratch.arena, &current_list, make_json_node(scratch.arena, MSG_ASSISTANT, S8Lit("no")));
S8ListPush(scratch.arena, &current_list, make_json_node(scratch.arena, MSG_USER, current_history));
String8 json_array = S8ListJoin(scratch.arena, current_list, &(StringJoin){0});
json_array.size -= 1; // remove trailing comma. fuck json
String8 prompt = FmtWithLint(scratch.arena, "[%.*s]", S8VArg(json_array));
gs->judgement_gen_request = make_generation_request(prompt);
}
}
ReleaseScratch(scratch); ReleaseScratch(scratch);
return proceed_propagating; return proceed_propagating;
} }
@ -2183,7 +2313,12 @@ void transition_to_room(GameState *gs, ThreeDeeLevel *level, String8 new_room_na
void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *level) void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *level)
{ {
if(gs->arena)
{
ArenaRelease(gs->arena);
}
memset(gs, 0, sizeof(GameState)); memset(gs, 0, sizeof(GameState));
gs->arena = ArenaAlloc();
rnd_gamerand_seed(&gs->random, RANDOM_SEED); rnd_gamerand_seed(&gs->random, RANDOM_SEED);
// make entities for all rooms // make entities for all rooms
@ -3350,6 +3485,7 @@ String8 make_devtools_help(Arena *arena)
P("P - toggles spall profiling on/off, don't leave on for very long as it consumes a lot of storage if you do that. The resulting spall trace is saved to the file '%s'\n", PROFILING_SAVE_FILENAME); P("P - toggles spall profiling on/off, don't leave on for very long as it consumes a lot of storage if you do that. The resulting spall trace is saved to the file '%s'\n", PROFILING_SAVE_FILENAME);
P("If you hover over somebody it will display some parts of their memories, can be somewhat helpful\n"); P("If you hover over somebody it will display some parts of their memories, can be somewhat helpful\n");
P("P - immediately kills %s\n", characters[NPC_Raphael].name); P("P - immediately kills %s\n", characters[NPC_Raphael].name);
P("J - judges the player and outputs their verdict to the console\n");
#undef P #undef P
String8 to_return = S8ListJoin(arena, list, &(StringJoin){0}); String8 to_return = S8ListJoin(arena, list, &(StringJoin){0});
@ -3432,7 +3568,7 @@ void init(void)
shifted_farmer_armature = load_armature(persistent_arena, binary_file, S8Lit("Farmer.bin")); shifted_farmer_armature = load_armature(persistent_arena, binary_file, S8Lit("Farmer.bin"));
shifted_farmer_armature.image = image_shifted_farmer; shifted_farmer_armature.image = image_shifted_farmer;
Log("Done. Used %f of the frame arena, %llu kB\n", (double) frame_arena->pos / (double)frame_arena->cap, (frame_arena->pos/1024)); Log("Done. Used %f of the frame arena, %d kB\n", (double) frame_arena->pos / (double)frame_arena->cap, (int)(frame_arena->pos/1024));
ArenaClear(frame_arena); ArenaClear(frame_arena);
@ -5035,6 +5171,7 @@ double elapsed_time = 0.0;
double unwarped_elapsed_time = 0.0; double unwarped_elapsed_time = 0.0;
double last_frame_processing_time = 0.0; double last_frame_processing_time = 0.0;
double last_frame_gameplay_processing_time = 0.0; double last_frame_gameplay_processing_time = 0.0;
uint64_t frame_index = 0; // for rendering tick stuff, gamestate tick is used for game logic tick stuff and is serialized/deserialized/saved
uint64_t last_frame_time; uint64_t last_frame_time;
typedef struct typedef struct
@ -5114,10 +5251,13 @@ bool imbutton_key(ImbuttonArgs args)
TextParams t = (TextParams) { false, args.text, aabb_center(args.button_aabb), BLACK, args.text_scale, .clip_to = args.button_aabb, .do_clipping = true, .layer = layer, .use_font = font }; TextParams t = (TextParams) { false, args.text, aabb_center(args.button_aabb), BLACK, args.text_scale, .clip_to = args.button_aabb, .do_clipping = true, .layer = layer, .use_font = font };
t.dry_run = true; t.dry_run = true;
AABB aabb = draw_text(t); AABB aabb = draw_text(t);
if(aabb_is_valid(aabb))
{
t.dry_run = false; t.dry_run = false;
t.pos = SubV2(aabb_center(args.button_aabb), MulV2F(aabb_size(aabb), 0.5f)); t.pos = SubV2(aabb_center(args.button_aabb), MulV2F(aabb_size(aabb), 0.5f));
draw_text(t); draw_text(t);
} }
}
hmput(imui_state, args.key, state); hmput(imui_state, args.key, state);
return to_return; return to_return;
@ -5430,10 +5570,16 @@ void flush_all_drawn_things(ShadowMats shadow)
} }
} }
// sokol prohibits updating an image more than once per frame
if(armature->last_updated_bones_frame != frame_index)
{
armature->last_updated_bones_frame = frame_index;
sg_update_image(armature->bones_texture, &(sg_image_data){ sg_update_image(armature->bones_texture, &(sg_image_data){
.subimage[0][0] = (sg_range){bones_tex, bones_tex_size}, .subimage[0][0] = (sg_range){bones_tex, bones_tex_size},
}); });
}
ReleaseScratch(scratch); ReleaseScratch(scratch);
} }
} }
@ -5591,7 +5737,7 @@ TextPlacementSettings speech_bubble = {
String8List words_on_current_page(Entity *it, TextPlacementSettings *settings) String8List words_on_current_page(Entity *it, TextPlacementSettings *settings)
{ {
String8 last = last_said_sentence(it); String8 last = last_said_sentence(it);
PlacedWordList placed = place_wrapped_words(frame_arena, split_by_word(frame_arena, last), settings->text_scale, settings->width_in_pixels, *settings->font, JUST_LEFT); PlacedWordList placed = place_wrapped_words(frame_arena, split_by_word(frame_arena, last), settings->text_scale, settings->text_width_in_pixels, *settings->font, JUST_LEFT);
String8List on_current_page = {0}; String8List on_current_page = {0};
for(PlacedWord *cur = placed.first; cur; cur = cur->next) for(PlacedWord *cur = placed.first; cur; cur = cur->next)
@ -5644,6 +5790,7 @@ void frame(void)
double dt_double = unwarped_dt_double*speed_factor; double dt_double = unwarped_dt_double*speed_factor;
float unwarped_dt = (float)unwarped_dt_double; float unwarped_dt = (float)unwarped_dt_double;
float dt = (float)dt_double; float dt = (float)dt_double;
frame_index += 1;
#if 0 #if 0
{ {
@ -5669,6 +5816,8 @@ void frame(void)
{ {
uint64_t time_start_frame = stm_now(); uint64_t time_start_frame = stm_now();
gs.time += dt_double;
text_input_fade = Lerp(text_input_fade, unwarped_dt * 8.0f, receiving_text_input ? 1.0f : 0.0f); text_input_fade = Lerp(text_input_fade, unwarped_dt * 8.0f, receiving_text_input ? 1.0f : 0.0f);
Vec3 player_pos = V3(gs.player->pos.x, 0.0, gs.player->pos.y); Vec3 player_pos = V3(gs.player->pos.x, 0.0, gs.player->pos.y);
@ -5713,8 +5862,8 @@ void frame(void)
Vec3 light_dir; Vec3 light_dir;
{ {
float t = (float)(elapsed_time/3.0f - floor(elapsed_time/3.0f)); float t = clamp01((float)(gs.time / LENGTH_OF_DAY));
Vec3 sun_vector = V3(2.0f*t - 1.0f, sinf(t*PI32), 0.8f); // where the sun is pointing from Vec3 sun_vector = V3(2.0f*t - 1.0f, sinf(t*PI32)*0.8f + 0.2f, 0.8f); // where the sun is pointing from
light_dir = NormV3(MulV3F(sun_vector, -1.0f)); light_dir = NormV3(MulV3F(sun_vector, -1.0f));
} }
@ -5849,6 +5998,7 @@ void frame(void)
case NPC_Devil: case NPC_Devil:
case NPC_PreviousPlayer1: case NPC_PreviousPlayer1:
case NPC_PreviousPlayer2: case NPC_PreviousPlayer2:
case NPC_PreviousPlayer3:
assert(false); assert(false);
break; break;
} }
@ -6101,6 +6251,39 @@ void frame(void)
} }
} }
if(gs.judgement_gen_request != 0)
{
GenRequestStatus stat = gen_request_status(gs.judgement_gen_request);
switch(stat)
{
case GEN_NotDoneYet:
break;
case GEN_Success:
{
TextChunk generated = gen_request_content(gs.judgement_gen_request);
if (generated.text_length > 0 && S8FindSubstring(TextChunkString8(generated), S8Lit("yes"), 0, StringMatchFlag_CaseInsensitive) == 0)
{
Log("Starts with yes, success!\n");
gs.won = true;
}
else if (S8FindSubstring(TextChunkString8(generated), S8Lit("no"), 0, StringMatchFlag_CaseInsensitive) == generated.text_length)
{
Log("WARNING: generated judgement string '%.*s', doesn't match yes or no, and so is nonsensical! AI acting up!\n", TextChunkVArg(generated));
}
}
break;
case GEN_Failed:
having_errors = true;
break;
case GEN_Deleted:
break;
}
if(stat != GEN_NotDoneYet)
{
done_with_request(gs.judgement_gen_request);
gs.judgement_gen_request = 0;
}
}
// @Place(UI rendering that happens before gameplay processing so can consume events before the gameplay needs them) // @Place(UI rendering that happens before gameplay processing so can consume events before the gameplay needs them)
PROFILE_SCOPE("Entity UI Rendering") PROFILE_SCOPE("Entity UI Rendering")
@ -6303,47 +6486,26 @@ void frame(void)
{ {
assert(it->gen_request_id > 0); assert(it->gen_request_id > 0);
#ifdef DESKTOP GenRequestStatus status = gen_request_status(it->gen_request_id);
int status = get_by_id(it->gen_request_id)->status; switch(status)
#else
#ifdef WEB
int status = EM_ASM_INT( {
return get_generation_request_status($0);
}, it->gen_request_id);
#else
ISANERROR("Don't know how to do this stuff on this platform.")
#endif // WEB
#endif // DESKTOP
if (status == 0)
{
// simply not done yet
}
else
{ {
if (status == 1) case GEN_Deleted:
it->gen_request_id = 0;
break;
case GEN_NotDoneYet:
break;
case GEN_Success:
{ {
having_errors = false; having_errors = false;
// done! we can get the string // done! we can get the string
char sentence_cstr[MAX_SENTENCE_LENGTH] = { 0 }; TextChunk sentence_chunk = gen_request_content(it->gen_request_id);
#ifdef WEB String8 sentence_str = TextChunkString8(sentence_chunk);
EM_ASM( {
let generation = get_generation_request_content($0);
stringToUTF8(generation, $1, $2);
}, it->gen_request_id, sentence_cstr, ARRLEN(sentence_cstr) - 1); // I think minus one for null terminator...
#endif
#ifdef DESKTOP
memcpy(sentence_cstr, get_by_id(it->gen_request_id)->generated.text, get_by_id(it->gen_request_id)->generated.text_length);
#endif
String8 sentence_str = S8CString(sentence_cstr);
// parse out from the sentence NPC action and dialog // parse out from the sentence NPC action and dialog
Action out = {0}; Action out = {0};
ArenaTemp scratch = GetScratch(0, 0);
Log("Parsing `%.*s`...\n", S8VArg(sentence_str)); Log("Parsing `%.*s`...\n", S8VArg(sentence_str));
String8 parse_response = parse_chatgpt_response(scratch.arena, it, sentence_str, &out); String8 parse_response = parse_chatgpt_response(frame_arena, it, sentence_str, &out);
// check that it wraps in below two lines // check that it wraps in below two lines
TextPlacementSettings *to_wrap_to = &speech_bubble; TextPlacementSettings *to_wrap_to = &speech_bubble;
@ -6368,8 +6530,6 @@ ISANERROR("Don't know how to do this stuff on this platform.")
{ {
Log("Performing action %s!\n", actions[out.kind].name); Log("Performing action %s!\n", actions[out.kind].name);
perform_action(&gs, it, out); perform_action(&gs, it, out);
} }
else else
{ {
@ -6378,42 +6538,19 @@ ISANERROR("Don't know how to do this stuff on this platform.")
} }
} }
ReleaseScratch(scratch);
#ifdef WEB
EM_ASM( {
done_with_generation_request($0);
}, it->gen_request_id);
#endif
#ifdef DESKTOP
done_with_request(it->gen_request_id); done_with_request(it->gen_request_id);
#endif it->gen_request_id = 0;
} }
else if (status == 2) break;
{ case GEN_Failed:
Log("Failed to generate dialog! Fuck!\n"); Log("Failed to generate dialog! Fuck!\n");
having_errors = true; having_errors = true;
it->gen_request_id = 0;
/* break;
Action to_perform = {0}; default:
String8 speech_mdstring = S8Lit("I'm not sure...");
memcpy(to_perform.speech, speech_mdstring.str, speech_mdstring.size);
to_perform.speech_length = (int)speech_mdstring.size;
perform_action(&gs, it, to_perform);
*/
}
else if (status == -1)
{
Log("Generation request doesn't exist anymore, that's fine...\n");
}
else
{
Log("Unknown generation request status: %d\n", status); Log("Unknown generation request status: %d\n", status);
}
it->gen_request_id = 0; it->gen_request_id = 0;
break;
} }
} }
} }
@ -6830,38 +6967,19 @@ ISANERROR("Don't know how to do this stuff on this platform.")
{ {
it->perceptions_dirty = false; // needs to be in beginning because they might be redirtied by the new perception it->perceptions_dirty = false; // needs to be in beginning because they might be redirtied by the new perception
String8 prompt_str = {0}; String8 prompt_str = {0};
#ifdef DO_CHATGPT_PARSING
prompt_str = generate_chatgpt_prompt(frame_arena, &gs, it, get_can_talk_to(it)); prompt_str = generate_chatgpt_prompt(frame_arena, &gs, it, get_can_talk_to(it));
#else Log("Want to make request with prompt `%.*s`\n", S8VArg(prompt_str));
generate_prompt(it, &prompt);
#endif
Log("Sending request with prompt `%.*s`\n", S8VArg(prompt_str));
#ifdef WEB
// fire off generation request, save id
ArenaTemp scratch = GetScratch(0, 0);
String8 terminated_completion_url = nullterm(scratch.arena, FmtWithLint(scratch.arena, "%s://%s:%d/completion", IS_SERVER_SECURE ? "https" : "http", SERVER_DOMAIN, SERVER_PORT));
int req_id = EM_ASM_INT({
return make_generation_request(UTF8ToString($0, $1), UTF8ToString($2, $3));
},
prompt_str.str, (int)prompt_str.size, terminated_completion_url.str, (int)terminated_completion_url.size);
it->gen_request_id = req_id;
ReleaseScratch(scratch);
#endif
#ifdef DESKTOP
ArenaTemp scratch = GetScratch(0, 0);
String8 ai_response = {0};
bool mocking_the_ai_response = false; bool mocking_the_ai_response = false;
#ifdef DEVTOOLS #ifdef DEVTOOLS
#ifdef MOCK_AI_RESPONSE #ifdef MOCK_AI_RESPONSE
mocking_the_ai_response = true; mocking_the_ai_response = true;
#endif #endif // mock
#endif #endif // devtools
bool succeeded = true; // couldn't get AI response if false bool succeeded = true; // couldn't get AI response if false
if (mocking_the_ai_response) if (mocking_the_ai_response)
{ {
String8 ai_response = {0};
if (it->memories_last->context.talking_to_kind == it->npc_kind) if (it->memories_last->context.talking_to_kind == it->npc_kind)
//if (it->memories_last->context.author_npc_kind != it->npc_kind) //if (it->memories_last->context.author_npc_kind != it->npc_kind)
{ {
@ -6898,14 +7016,13 @@ ISANERROR("Don't know how to do this stuff on this platform.")
} }
// something to mock // something to mock
if (ai_response.size > 0) assert(ai_response.size > 0);
{
Log("Mocking...\n"); Log("Mocking...\n");
Action a = {0}; Action a = {0};
String8 error_message = S8Lit("Something really bad happened bro. File " STRINGIZE(__FILE__) " Line " STRINGIZE(__LINE__)); String8 error_message = S8Lit("Something really bad happened bro. File " STRINGIZE(__FILE__) " Line " STRINGIZE(__LINE__));
if (succeeded) if (succeeded)
{ {
error_message = parse_chatgpt_response(scratch.arena, it, ai_response, &a); error_message = parse_chatgpt_response(frame_arena, it, ai_response, &a);
} }
assert(succeeded); assert(succeeded);
@ -6915,17 +7032,11 @@ ISANERROR("Don't know how to do this stuff on this platform.")
assert(valid_str.size == 0); assert(valid_str.size == 0);
perform_action(&gs, it, a); perform_action(&gs, it, a);
} }
}
else else
{ {
String8 post_request_body = FmtWithLint(scratch.arena, "|%.*s", S8VArg(prompt_str)); it->gen_request_id = make_generation_request(prompt_str);
it->gen_request_id = make_generation_request(post_request_body);
} }
ReleaseScratch(scratch);
#undef SAY #undef SAY
#endif // desktop endif
} }
} }
} }
@ -7169,6 +7280,11 @@ ISANERROR("Don't know how to do this stuff on this platform.")
} }
} }
if(gs.time > LENGTH_OF_DAY)
{
gs.player->killed = true;
}
// killed screen // killed screen
{ {
static float visible = 0.0f; static float visible = 0.0f;
@ -7189,12 +7305,12 @@ ISANERROR("Don't know how to do this stuff on this platform.")
float shake_speed = 9.0f; float shake_speed = 9.0f;
Vec2 win_offset = V2(sinf((float)unwarped_elapsed_time * shake_speed * 1.5f + 0.1f), sinf((float)unwarped_elapsed_time * shake_speed + 0.3f)); Vec2 win_offset = V2(sinf((float)unwarped_elapsed_time * shake_speed * 1.5f + 0.1f), sinf((float)unwarped_elapsed_time * shake_speed + 0.3f));
win_offset = MulV2F(win_offset, 10.0f); win_offset = MulV2F(win_offset, 10.0f);
draw_centered_text((TextParams){false, S8Lit("YOU WERE KILLED"), AddV2(MulV2F(screen_size(), 0.5f), win_offset), WHITE, 3.0f*visible}); // YOU DIED draw_centered_text((TextParams){false, S8Lit("YOU FAILED TO SAVE DANIEL"), AddV2(MulV2F(screen_size(), 0.5f), win_offset), WHITE, 3.0f*visible}); // YOU DIED
if(imbutton(aabb_centered(V2(screen_size().x/2.0f, screen_size().y*0.25f), MulV2F(V2(170.0f, 60.0f), visible)), 1.5f*visible, S8Lit("Continue"))) if(imbutton(aabb_centered(V2(screen_size().x/2.0f, screen_size().y*0.25f), MulV2F(V2(170.0f, 60.0f), visible)), 1.5f*visible, S8Lit("Continue")))
{ {
gs.player->killed = false; gs.player->killed = false;
transition_to_room(&gs, &level_threedee, S8Lit("StartingRoom")); //transition_to_room(&gs, &level_threedee, S8Lit("StartingRoom"));
reset_level(); reset_level();
} }
} }
@ -7202,7 +7318,6 @@ ISANERROR("Don't know how to do this stuff on this platform.")
#define HELPER_SIZE 250.0f #define HELPER_SIZE 250.0f
// keyboard tutorial icons // keyboard tutorial icons
if(false) if(false)
if (!mobile_controls) if (!mobile_controls)
@ -7235,6 +7350,11 @@ ISANERROR("Don't know how to do this stuff on this platform.")
#ifdef DEVTOOLS #ifdef DEVTOOLS
if(keypressed[SAPP_KEYCODE_J])
{
Log("Judgement Day!\n");
}
// statistics @Place(devtools drawing developer menu drawing) // statistics @Place(devtools drawing developer menu drawing)
if (show_devtools) if (show_devtools)
PROFILE_SCOPE("devtools drawing") PROFILE_SCOPE("devtools drawing")
@ -7492,25 +7612,16 @@ void cleanup(void)
void event(const sapp_event *e) void event(const sapp_event *e)
{ {
if (e->key_repeat) return;
if (e->type == SAPP_EVENTTYPE_RESIZED)
{
create_screenspace_gfx_state();
}
if (e->type == SAPP_EVENTTYPE_TOUCHES_BEGAN)
{
if (!mobile_controls)
{
thumbstick_base_pos = V2(screen_size().x * 0.25f, screen_size().y * 0.25f);
thumbstick_nub_pos = thumbstick_base_pos;
}
mobile_controls = true;
}
#ifdef DESKTOP #ifdef DESKTOP
// the desktop text backend, for debugging purposes // the desktop text backend, for debugging purposes
if (receiving_text_input) if (receiving_text_input)
{
if (e->type == SAPP_EVENTTYPE_KEY_DOWN && e->key_code == SAPP_KEYCODE_BACKSPACE)
{
if(text_input_buffer_length > 0)
text_input_buffer_length -= 1;
}
else
{ {
if (e->type == SAPP_EVENTTYPE_CHAR) if (e->type == SAPP_EVENTTYPE_CHAR)
{ {
@ -7519,6 +7630,8 @@ void event(const sapp_event *e)
APPEND_TO_NAME(text_input_buffer, text_input_buffer_length, ARRLEN(text_input_buffer), (char)e->char_code); APPEND_TO_NAME(text_input_buffer, text_input_buffer_length, ARRLEN(text_input_buffer), (char)e->char_code);
} }
} }
}
if (e->type == SAPP_EVENTTYPE_KEY_DOWN && e->key_code == SAPP_KEYCODE_ENTER) if (e->type == SAPP_EVENTTYPE_KEY_DOWN && e->key_code == SAPP_KEYCODE_ENTER)
{ {
// doesn't account for, if the text input buffer is completely full and doesn't have a null terminator. // doesn't account for, if the text input buffer is completely full and doesn't have a null terminator.
@ -7532,6 +7645,25 @@ void event(const sapp_event *e)
} }
#endif #endif
if (e->key_repeat) return;
if (e->type == SAPP_EVENTTYPE_RESIZED)
{
create_screenspace_gfx_state();
}
if (e->type == SAPP_EVENTTYPE_TOUCHES_BEGAN)
{
if (!mobile_controls)
{
thumbstick_base_pos = V2(screen_size().x * 0.25f, screen_size().y * 0.25f);
thumbstick_nub_pos = thumbstick_base_pos;
}
mobile_controls = true;
}
if (e->type == SAPP_EVENTTYPE_KEY_DOWN && if (e->type == SAPP_EVENTTYPE_KEY_DOWN &&
(e->key_code == SAPP_KEYCODE_F11 || (e->key_code == SAPP_KEYCODE_F11 ||
e->key_code == SAPP_KEYCODE_ENTER && ((e->modifiers & SAPP_MODIFIER_ALT) || (e->modifiers & SAPP_MODIFIER_SHIFT)))) e->key_code == SAPP_KEYCODE_ENTER && ((e->modifiers & SAPP_MODIFIER_ALT) || (e->modifiers & SAPP_MODIFIER_SHIFT))))

@ -130,6 +130,7 @@ typedef struct
NpcKind talking_to_kind; NpcKind talking_to_kind;
bool heard_physically; // if not physically, the source was directly bool heard_physically; // if not physically, the source was directly
bool drama_memory; // drama memories arent forgotten, and once they end it's understood that a lot of time has passed. bool drama_memory; // drama memories arent forgotten, and once they end it's understood that a lot of time has passed.
bool judgement_memory; // judgement memories have special printing for when Daniel says nothing, to make sure the AI understands his attitude towards the player
} MemoryContext; } MemoryContext;
// memories are subjective to an individual NPC // memories are subjective to an individual NPC
@ -261,9 +262,7 @@ typedef struct Entity
float dialog_fade; float dialog_fade;
RememberedError *errorlist_first; RememberedError *errorlist_first;
RememberedError *errorlist_last; RememberedError *errorlist_last;
#ifdef DESKTOP
int times_talked_to; // for better mocked response string int times_talked_to; // for better mocked response string
#endif
float loading_anim_in; float loading_anim_in;
Memory *memories_first; Memory *memories_first;
Memory *memories_last; Memory *memories_last;
@ -298,6 +297,7 @@ typedef BUFF(ActionKind, 8) AvailableActions;
typedef struct GameState { typedef struct GameState {
Arena *arena;
uint64_t tick; uint64_t tick;
bool won; bool won;
@ -306,6 +306,11 @@ typedef struct GameState {
bool assigned_objective; bool assigned_objective;
GameplayObjective objective; GameplayObjective objective;
Memory *judgement_memories_first;
Memory *judgement_memories_last;
double time; // in seconds, fraction of length of day
int judgement_gen_request;
// processing may still occur after time has stopped on the gamestate, // processing may still occur after time has stopped on the gamestate,
bool stopped_time; bool stopped_time;
@ -441,79 +446,25 @@ String8List dump_memory_as_json(Arena *arena, Memory *it)
return current_list; return current_list;
} }
// outputs json which is parsed by the server String8List memory_description(Arena *arena, Entity *e, Memory *it)
String8 generate_chatgpt_prompt(Arena *arena, GameState *gs, Entity *e, CanTalkTo can_talk_to)
{
assert(e->is_npc);
assert(e->npc_kind < ARRLEN(characters));
ArenaTemp scratch = GetScratch(&arena, 1);
String8List list = {0};
PushWithLint(scratch.arena, &list, "[");
#define AddFmt(...) PushWithLint(scratch.arena, &current_list, __VA_ARGS__)
#define AddNewNode(node_type) { S8ListPush(scratch.arena, &list, make_json_node(scratch.arena, node_type, S8ListJoin(scratch.arena, current_list, &(StringJoin){0}))); current_list = (String8List){0}; }
// make first system node
{ {
String8List current_list = {0}; String8List current_list = {0};
AddFmt("%s\n\n", global_prompt); #define AddFmt(...) PushWithLint(arena, &current_list, __VA_ARGS__)
AddFmt("%s\n\n", characters[e->npc_kind].prompt); // dump a human understandable sentence description of what happened in this memory
AddFmt("The characters who are near you, that you can target:\n"); bool no_longer_wants_to_converse = false; // add the no longer wants to converse text after any speech, it makes more sense reading it
BUFF_ITER(Entity*, &can_talk_to)
{
assert((*it)->is_npc);
String8 info = S8Lit("");
if((*it)->killed)
{
info = S8Lit(" - they're currently dead, they were murdered");
}
AddFmt("%s%.*s\n", characters[(*it)->npc_kind].name, S8VArg(info));
}
AddFmt("\n");
// @TODO unhardcode this, this will be a description of where the character is right now
//AddFmt("You're currently standing in Daniel's farm's barn, a run-down structure that barely serves its purpose. Daniel's mighty protective of it though.\n");
AddFmt("You and everybody you're talking to is in a small sparse forest near Daniel's farm. There are some mysterious mechanical parts strewn about on the floor that Daniel seems relunctant and fearful to talk about.\n");
AddFmt("\n");
AddFmt("The actions you can perform, what they do, and the arguments they expect:\n"); #define HUMAN(kind) S8VArg(npc_to_human_readable(e, kind))
AvailableActions can_perform; if(it->context.judgement_memory)
fill_available_actions(gs, e, &can_perform);
BUFF_ITER(ActionKind, &can_perform)
{ {
AddFmt("%s - %s - %s\n", actions[*it].name, actions[*it].description, actions[*it].argument_description); if((it->action_taken == ACT_none && it->speech.text_length == 0) && it->context.author_npc_kind != NPC_Player)
}
AddNewNode(MSG_SYSTEM);
}
String8List current_list = {0};
bool in_drama_memories = true;
assert(e->memories_first->context.drama_memory);
for(Memory *it = e->memories_first; it; it = it->next)
{ {
// going through memories, I'm going to accumulate human understandable sentences for what happened in current_list. AddFmt("%.*s said and did nothing in response\n", HUMAN(it->context.author_npc_kind));
// when I see an 'i_said_this' memory, that means I flush. and add a new assistant node. }
// write a new human understandable sentence or two to current_list
if (!it->context.i_said_this) {
if(in_drama_memories && !it->context.drama_memory)
{
in_drama_memories = false;
AddFmt("Some time passed...\n");
} }
// dump a human understandable sentence description of what happened in this memory
bool no_longer_wants_to_converse = false; // add the no longer wants to converse text after any speech, it makes more sense reading it
if (it->action_taken != ACT_none) if (it->action_taken != ACT_none)
{ {
switch (it->action_taken) switch (it->action_taken)
{ {
#define HUMAN(kind) S8VArg(npc_to_human_readable(e, kind))
case ACT_none: case ACT_none:
break; break;
case ACT_join: case ACT_join:
@ -538,10 +489,10 @@ String8 generate_chatgpt_prompt(Arena *arena, GameState *gs, Entity *e, CanTalkT
no_longer_wants_to_converse = true; no_longer_wants_to_converse = true;
break; break;
case ACT_assign_gameplay_objective: case ACT_assign_gameplay_objective:
AddFmt("%.*s assigned a definitive game objective to %.*s", HUMAN(it->context.author_npc_kind), HUMAN(it->context.talking_to_kind)); AddFmt("%.*s assigned a definitive game objective to %.*s\n", HUMAN(it->context.author_npc_kind), HUMAN(it->context.talking_to_kind));
break; break;
case ACT_award_victory: case ACT_award_victory:
AddFmt("%.*s awarded victory to %.*s", HUMAN(it->context.author_npc_kind), HUMAN(it->context.talking_to_kind)); AddFmt("%.*s awarded victory to %.*s\n", HUMAN(it->context.author_npc_kind), HUMAN(it->context.talking_to_kind));
break; break;
} }
} }
@ -556,13 +507,23 @@ String8 generate_chatgpt_prompt(Arena *arena, GameState *gs, Entity *e, CanTalkT
target_string = S8CString(characters[it->context.talking_to_kind].name); target_string = S8CString(characters[it->context.talking_to_kind].name);
} }
String8 speaking_to_you_helper = S8Lit("(Speaking directly you) "); if(!e->is_world)
if(it->context.talking_to_kind != e->npc_kind) {
if(it->context.talking_to_kind == e->npc_kind)
{ {
speaking_to_you_helper = S8Lit("(Overheard conversation, they aren't speaking directly to you) "); AddFmt("(Speaking directly you) ");
}
else
{
AddFmt("(Overheard conversation, they aren't speaking directly to you) ");
} }
}
AddFmt("%.*s%s said \"%.*s\" to %.*s (you are %s)\n", S8VArg(speaking_to_you_helper), characters[it->context.author_npc_kind].name, TextChunkVArg(it->speech), S8VArg(target_string), characters[e->npc_kind].name); AddFmt("%s said \"%.*s\" to %.*s", characters[it->context.author_npc_kind].name, TextChunkVArg(it->speech), S8VArg(target_string));
if(!e->is_world)
{
AddFmt(" (you are %s)", characters[e->npc_kind].name)
}
AddFmt("\n");
} }
if (no_longer_wants_to_converse) if (no_longer_wants_to_converse)
@ -577,6 +538,78 @@ String8 generate_chatgpt_prompt(Arena *arena, GameState *gs, Entity *e, CanTalkT
} }
} }
#undef HUMAN #undef HUMAN
#undef AddFmt
return current_list;
}
// outputs json which is parsed by the server
String8 generate_chatgpt_prompt(Arena *arena, GameState *gs, Entity *e, CanTalkTo can_talk_to)
{
assert(e->is_npc);
assert(e->npc_kind < ARRLEN(characters));
ArenaTemp scratch = GetScratch(&arena, 1);
String8List list = {0};
PushWithLint(scratch.arena, &list, "[");
#define AddFmt(...) PushWithLint(scratch.arena, &current_list, __VA_ARGS__)
#define AddNewNode(node_type) { S8ListPush(scratch.arena, &list, make_json_node(scratch.arena, node_type, S8ListJoin(scratch.arena, current_list, &(StringJoin){0}))); current_list = (String8List){0}; }
// make first system node
{
String8List current_list = {0};
AddFmt("%s\n\n", global_prompt);
AddFmt("%s\n\n", characters[e->npc_kind].prompt);
AddFmt("The characters who are near you, that you can target:\n");
BUFF_ITER(Entity*, &can_talk_to)
{
assert((*it)->is_npc);
String8 info = S8Lit("");
if((*it)->killed)
{
info = S8Lit(" - they're currently dead, they were murdered");
}
AddFmt("%s%.*s\n", characters[(*it)->npc_kind].name, S8VArg(info));
}
AddFmt("\n");
// @TODO unhardcode this, this will be a description of where the character is right now
//AddFmt("You're currently standing in Daniel's farm's barn, a run-down structure that barely serves its purpose. Daniel's mighty protective of it though.\n");
AddFmt("You and everybody you're talking to is in a small sparse forest near Daniel's farm. There are some mysterious mechanical parts strewn about on the floor that Daniel seems relunctant and fearful to talk about.\n");
AddFmt("\n");
AddFmt("The actions you can perform, what they do, and the arguments they expect:\n");
AvailableActions can_perform;
fill_available_actions(gs, e, &can_perform);
BUFF_ITER(ActionKind, &can_perform)
{
AddFmt("%s - %s - %s\n", actions[*it].name, actions[*it].description, actions[*it].argument_description);
}
AddNewNode(MSG_SYSTEM);
}
String8List current_list = {0};
bool in_drama_memories = true;
assert(e->memories_first->context.drama_memory);
for(Memory *it = e->memories_first; it; it = it->next)
{
// going through memories, I'm going to accumulate human understandable sentences for what happened in current_list.
// when I see an 'i_said_this' memory, that means I flush. and add a new assistant node.
// write a new human understandable sentence or two to current_list
if (!it->context.i_said_this) {
if(in_drama_memories && !it->context.drama_memory)
{
in_drama_memories = false;
AddFmt("Some time passed...\n");
}
String8List desc_list = memory_description(scratch.arena, e, it);
S8ListConcat(&current_list, &desc_list);
} }
// if I said this, or it's the last memory, flush the current list as a user node // if I said this, or it's the last memory, flush the current list as a user node

@ -508,7 +508,11 @@ func main() {
logResponses = os.Getenv("LOG_RESPONSES") != "" logResponses = os.Getenv("LOG_RESPONSES") != ""
doCors = os.Getenv("CORS") != "" doCors = os.Getenv("CORS") != ""
if doCors { log.Println("Doing cors"); } if doCors {
log.Println("Doing cors");
} else {
log.Println("Warning: Not adding cors header to responses, you should probably be running this through a proxy like nginx that does that! To activate cors set the `CORS` environment variable to anything");
}
c = openai.NewClient(api_key) c = openai.NewClient(api_key)
http.HandleFunc("/completion", completion) http.HandleFunc("/completion", completion)

@ -27,6 +27,8 @@ Urgent:
Long distance: Long distance:
- nocodegen instead of codegen argument - nocodegen instead of codegen argument
- Blur game on bitmap modal input mode - Blur game on bitmap modal input mode
- Detect when an arena accidentally has more than one ArenaTemp requested of it
- In case of AI failure redact the memories that prompted the character to make a generation request
- Polygon and circle collision with cutec2 probably for the player being unable to collide with the camera bounds, and non axis aligned collision rects - Polygon and circle collision with cutec2 probably for the player being unable to collide with the camera bounds, and non axis aligned collision rects
- set the game in oregon (suggestion by phillip) - set the game in oregon (suggestion by phillip)
- Room system where characters can go to rooms. camera constrained to room bounds, and know which rooms are near them to go to - Room system where characters can go to rooms. camera constrained to room bounds, and know which rooms are near them to go to

@ -3,6 +3,7 @@
#define RANDOM_SEED 42 #define RANDOM_SEED 42
#define LEVEL_TILES 150 // width and height of level tiles array #define LEVEL_TILES 150 // width and height of level tiles array
#define LENGTH_OF_DAY (60.0 * 5.0) // in seconds
#define LAYERS 3 #define LAYERS 3
#define TILE_SIZE 0.5f // in pixels #define TILE_SIZE 0.5f // in pixels
#define PLAYER_SPEED 0.15f // in meters per second #define PLAYER_SPEED 0.15f // in meters per second
@ -23,9 +24,9 @@
#define ARENA_SIZE (1024*1024*20) #define ARENA_SIZE (1024*1024*20)
#define BIG_ARENA_SIZE (ARENA_SIZE * 4) #define BIG_ARENA_SIZE (ARENA_SIZE * 4)
#define PROFILING_SAVE_FILENAME "rpgpt.spall"
#ifdef DEVTOOLS #ifdef DEVTOOLS
#define PROFILING_SAVE_FILENAME "rpgpt.spall"
// server url cannot have trailing slash // server url cannot have trailing slash
//#define MOCK_AI_RESPONSE //#define MOCK_AI_RESPONSE
#define SERVER_DOMAIN "localhost" #define SERVER_DOMAIN "localhost"

Loading…
Cancel
Save