From b4dcddab19aa6eae6cbc61dcd910389bada116b2 Mon Sep 17 00:00:00 2001 From: Cameron Reikes Date: Sat, 12 Aug 2023 17:27:00 -0700 Subject: [PATCH] Delete old panel drawing, support scrolling through multiple pages of bubble dialog. Rewrite of text anim in --- main.c | 532 ++++++++++++--------------------------------------- makeprompt.h | 10 +- tuning.h | 2 + 3 files changed, 133 insertions(+), 411 deletions(-) diff --git a/main.c b/main.c index dd75a15..03d90fd 100644 --- a/main.c +++ b/main.c @@ -1357,16 +1357,6 @@ float flycam_speed = 1.0f; Mat4 view = {0}; Mat4 projection = {0}; -typedef struct ListOfEntities { - struct ListOfEntities *next; - struct ListOfEntities *prev; - EntityRef referring_to; -} ListOfEntities; - -ListOfEntities *unread_first = 0; -ListOfEntities *unread_last = 0; -ListOfEntities *unread_free_list = 0; - Vec4 IsPoint(Vec3 point) { return V4(point.x, point.y, point.z, 1.0f); @@ -1609,8 +1599,11 @@ void remember_action(GameState *gs, Entity *to_modify, Action a, MemoryContext c if(context.i_said_this) { - to_modify->words_said = 0; - to_modify->word_anim_in = 0; + to_modify->undismissed_action = true; + to_modify->undismissed_action_tick = gs->tick; + to_modify->characters_of_word_animated = 0.0f; + to_modify->words_said_on_page = 0; + to_modify->cur_page_index = 0; } } @@ -1789,23 +1782,6 @@ bool perform_action(GameState *gs, Entity *from, Action a) { MD_ArenaTemp scratch = MD_GetScratch(0, 0); - if(!from->is_character && a.speech.text_length > 0) - { - ListOfEntities *new_unread = 0; - if(unread_free_list) - { - new_unread = unread_free_list; - MD_StackPop(unread_free_list); - *new_unread = (ListOfEntities){0}; - } - else - { - new_unread = MD_PushArray(persistent_arena, ListOfEntities, 1); - } - new_unread->referring_to = frome(from); - MD_DblPushBack(unread_first, unread_last, new_unread); - } - MemoryContext context = {0}; context.author_npc_kind = from->npc_kind; @@ -2167,7 +2143,7 @@ void initialize_gamestate_from_threedee_level(GameState *gs, ThreeDeeLevel *leve remember_action(gs, it, no_speak, no_speak_context); } - it->words_said = 999; // prevent the animating in sound effects of words said in drama document + it->undismissed_action = false; // prevent the animating in sound effects of words said in drama document found = true; break; @@ -2309,8 +2285,13 @@ void ser_entity(SerState *ser, Entity *e) } ser_float(ser, &e->dialog_panel_opacity); - ser_int(ser, &e->words_said); - ser_float(ser, &e->word_anim_in); + + ser_bool(ser, &e->undismissed_action); + ser_uint64_t(ser, &e->undismissed_action_tick); + ser_float(ser, &e->characters_of_word_animated); + ser_int(ser, &e->words_said_on_page); + ser_int(ser, &e->cur_page_index); + ser_NpcKind(ser, &e->npc_kind); ser_int(ser, &e->gen_request_id); ser_Vec2(ser, &e->target_goto); @@ -4486,12 +4467,11 @@ float get_vertical_dist_between_lines(LoadedFont for_font, float text_scale) return for_font.font_line_advance*text_scale*0.9f; } -PlacedWordList place_wrapped_words(MD_Arena *arena, MD_String8 text, float text_scale, float maximum_width, LoadedFont for_font) +PlacedWordList place_wrapped_words(MD_Arena *arena, MD_String8List words, float text_scale, float maximum_width, LoadedFont for_font) { PlacedWordList to_return = {0}; MD_ArenaTemp scratch = MD_GetScratch(&arena, 1); - MD_String8List words = split_by_word(scratch.arena, text); Vec2 at_position = V2(0.0, 0.0); Vec2 cur = at_position; float space_size = character_width(for_font, (int)' ', text_scale); @@ -4573,91 +4553,6 @@ typedef struct bool was_last_said; } DialogElement; -MD_String8List last_said_without_unsaid_words(MD_Arena *arena, Entity *it) -{ - MD_ArenaTemp scratch = MD_GetScratch(&arena, 1); - MD_String8 sentence = last_said_sentence(it); - if(sentence.size == 0) - { - return (MD_String8List){0}; - } - MD_String8List by_word = split_by_word(scratch.arena, sentence); - MD_String8Node *cur = by_word.first; - MD_String8List without_unsaid_words = {0}; - for(int i = 0; i < by_word.node_count; i++) - { - if(i >= it->words_said) - { - break; - } - else - { - assert(cur); - MD_S8ListPush(arena, &without_unsaid_words, cur->string); - cur = cur->next; - } - } - MD_ReleaseScratch(scratch); - return without_unsaid_words; -} - -// Some perceptions can have multiple dialog elements. -// Like item give perceptions that have an action with both dialog -// and an argument. So worst case every perception has 2 dialog -// elements right now is why it's *2 -typedef BUFF(DialogElement, REMEMBERED_MEMORIES*2) Dialog; -Dialog get_dialog_elems(Entity *talking_to, bool character_names) -{ - MD_ArenaTemp scratch = MD_GetScratch(0, 0); - assert(talking_to->is_npc); - Dialog to_return = { 0 }; - for(Memory *it = talking_to->memories_first; it; it = it->next) - { - if(!it->context.dont_show_to_player) - { - if(it->speech.text_length > 0) - { - DialogElement new_element = { .who_said_it = it->context.author_npc_kind }; - - MD_String8 my_speech = TextChunkString8(it->speech); - if(last_said_sentence(talking_to).str == it->speech.text) - { - new_element.was_last_said = true; - my_speech = MD_S8ListJoin(scratch.arena, last_said_without_unsaid_words(scratch.arena, talking_to), &(MD_StringJoin){.mid = MD_S8Lit(" ")}); - } - - MD_String8 name_string = {0}; - if(it->context.talking_to_kind != NPC_nobody) - { - name_string = FmtWithLint(scratch.arena, "%s to %s", characters[it->context.author_npc_kind].name, characters[it->context.talking_to_kind].name); - } - else - { - name_string = FmtWithLint(scratch.arena, "%s", characters[it->context.author_npc_kind].name); - } - - MD_String8 dialog_speech = FmtWithLint(scratch.arena, "%.*s: %.*s", MD_S8VArg(name_string), MD_S8VArg(my_speech)); - - memcpy(new_element.speech, dialog_speech.str, dialog_speech.size); - new_element.speech_length = (int)dialog_speech.size; - - if(it->context.author_npc_kind == NPC_Player) - { - new_element.kind = DELEM_PLAYER; - } - else - { - new_element.kind = DELEM_NPC; - } - - BUFF_APPEND(&to_return, new_element); - } - } - } - MD_ReleaseScratch(scratch); - return to_return; -} - // trail is buffer of vec2s Vec2 get_point_along_trail(BuffRef trail, float along) { @@ -4707,106 +4602,6 @@ float get_total_trail_len(BuffRef trail) Vec2 mouse_pos = { 0 }; // in screen space -void draw_dialog_panel(Entity *talking_to, float alpha) -{ - MD_ArenaTemp scratch = MD_GetScratch(0, 0); - - float panel_width = 250.0f; - float panel_height = 150.0f; - float panel_vert_offset = 30.0f; - - - AABB dialog_panel = (AABB) { - .upper_left = AddV2(talking_to->pos, V2(-panel_width / 2.0f, panel_vert_offset + panel_height)), - .lower_right = AddV2(talking_to->pos, V2(panel_width / 2.0f, panel_vert_offset)), - }; - AABB constrict_to = (AABB){0}; - dialog_panel.upper_left.x = fmaxf(constrict_to.upper_left.x, dialog_panel.upper_left.x); - dialog_panel.lower_right.y = fmaxf(constrict_to.lower_right.y, dialog_panel.lower_right.y); - dialog_panel.upper_left.y = fminf(constrict_to.upper_left.y, dialog_panel.upper_left.y); - dialog_panel.lower_right.x = fminf(constrict_to.lower_right.x, dialog_panel.lower_right.x); - - if (aabb_is_valid(dialog_panel)) - { - Quad dialog_quad = quad_aabb(dialog_panel); - float line_width = 2.0f; - Quad panel_quad = dialog_quad; - { - float inset = line_width; - panel_quad.ul = AddV2(panel_quad.ul, V2(inset, -inset)); - panel_quad.ll = AddV2(panel_quad.ll, V2(inset, inset)); - panel_quad.lr = AddV2(panel_quad.lr, V2(-inset, inset)); - panel_quad.ur = AddV2(panel_quad.ur, V2(-inset, -inset)); - } - colorquad(panel_quad, (Color) { 1.0f, 1.0f, 1.0f, 0.7f*alpha }); - Color line_color = (Color) { 0, 0, 0, alpha }; - line(AddV2(dialog_quad.ul, V2(-line_width, 0.0)), AddV2(dialog_quad.ur, V2(line_width, 0.0)), line_width, line_color); - line(dialog_quad.ur, dialog_quad.lr, line_width, line_color); - line(AddV2(dialog_quad.lr, V2(line_width, 0.0)), AddV2(dialog_quad.ll, V2(-line_width, 0.0)), line_width, line_color); - line(dialog_quad.ll, dialog_quad.ul, line_width, line_color); - - float padding = 7.5f; - dialog_panel.upper_left = AddV2(dialog_panel.upper_left, V2(padding, -padding)); - dialog_panel.lower_right = AddV2(dialog_panel.lower_right, V2(-padding, padding)); - - if (aabb_is_valid(dialog_panel)) - { - float new_line_height = dialog_panel.lower_right.Y; - - Dialog dialog = get_dialog_elems(talking_to, false); - if (dialog.cur_index > 0) - { - for (int i = dialog.cur_index - 1; i >= 0; i--) - { - DialogElement *it = &dialog.data[i]; - { - Color color; - // decide color - { - if (it->kind == DELEM_PLAYER) - { - color = BLACK; - } - else if (it->kind == DELEM_NPC) - { - color = colhex(0x345e22); - } - else if (it->kind == DELEM_ACTION_DESCRIPTION) - { - color = colhex(0xb5910e); - } - else - { - assert(false); - } - } - - color = blendalpha(color, alpha); - const float text_scale = 0.5f; - PlacedWordList wrapped = place_wrapped_words(scratch.arena, MD_S8(it->speech, it->speech_length), text_scale, dialog_panel.lower_right.x - dialog_panel.upper_left.x, BUBBLE_FONT); - float line_vertical_offset = -wrapped.last->lower_left_corner.y; - translate_words_by(wrapped, V2(0.0, line_vertical_offset)); - translate_words_by(wrapped, V2(dialog_panel.upper_left.x, new_line_height)); - new_line_height += line_vertical_offset + BUBBLE_FONT.font_line_advance * text_scale; - - AABB no_clip_curly_things = dialog_panel; - no_clip_curly_things.lower_right.y -= padding; - for(PlacedWord *cur = wrapped.first; cur; cur = cur->next) - { - draw_text((TextParams){false, cur->text, cur->lower_left_corner, color, text_scale, .clip_to = no_clip_curly_things, .do_clipping = true,}); - } - } - } - } - - dbgrect(dialog_panel); - } - } - - MD_ReleaseScratch(scratch); -} - - #define ROLL_KEY SAPP_KEYCODE_LEFT_SHIFT double elapsed_time = 0.0; double unwarped_elapsed_time = 0.0; @@ -5454,6 +5249,39 @@ void flush_all_drawn_things(Vec3 light_dir, Vec3 cam_pos, Vec3 cam_facing, Vec3 } } +// Unsaid words are still there, so you gotta handle the animation homie +MD_String8List words_on_current_page(Entity *it) +{ + MD_String8 last = last_said_sentence(it); + PlacedWordList placed = place_wrapped_words(frame_arena, split_by_word(frame_arena, last), BUBBLE_TEXT_SCALE, BUBBLE_TEXT_WIDTH_PIXELS, BUBBLE_FONT); + + MD_String8List on_current_page = {0}; + for(PlacedWord *cur = placed.first; cur; cur = cur->next) + { + if(cur->line_index / BUBBLE_LINES_PER_PAGE == it->cur_page_index) + MD_S8ListPush(frame_arena, &on_current_page, cur->text); + } + + return on_current_page; + + //return place_wrapped_words(frame_arena, on_current_page, text_scale, aabb_size(placing_text_in).x, default_font); +} + +MD_String8List words_on_current_page_without_unsaid(Entity *it) +{ + MD_String8List all_words = words_on_current_page(it); + int index = 0; + MD_String8List to_return = {0}; + for(MD_String8Node *cur = all_words.first; cur; cur = cur->next) + { + if(index > it->words_said_on_page) + break; + MD_S8ListPush(frame_arena, &to_return, cur->string); + index += 1; + } + return to_return; +} + void frame(void) { static float speed_factor = 1.0f; @@ -5785,16 +5613,32 @@ void frame(void) } #endif + Entity *cur_unread_entity = 0; + uint64_t earliest_unread_time = gs.tick; + ENTITIES_ITER(gs.entities) + { + if(it->is_npc && it->undismissed_action && it->undismissed_action_tick < earliest_unread_time) + { + earliest_unread_time = it->undismissed_action_tick; + cur_unread_entity = it; + } + } + + // @Place(UI rendering that happens before gameplay processing so can consume events before the gameplay needs them) PROFILE_SCOPE("Entity UI Rendering") { - while(unread_first && !gete(unread_first->referring_to)) - MD_DblRemove(unread_first, unread_last, unread_first); + ENTITIES_ITER(gs.entities) { if (it->is_npc) { + if(it->undismissed_action) + { + assert(it->undismissed_action_tick <= gs.tick); // no future undismissed actions + } + const float text_scale = BUBBLE_TEXT_SCALE; float dist = LenV2(SubV2(it->pos, gs.player->pos)); float bubble_factor = 1.0f - clamp01(dist / 6.0f); @@ -5805,7 +5649,8 @@ void frame(void) Vec2 bubble_center = AddV2(screen_pos, V2(-10.0f, 55.0f)); float dialog_alpha = clamp01(bubble_factor * it->dialog_fade); bool unread = false; - if (unread_first && gete(unread_first->referring_to) == it) + + if (cur_unread_entity == it) { dialog_alpha = 1.0f; unread = true; @@ -5816,6 +5661,7 @@ void frame(void) blendalpha(WHITE, dialog_alpha), .layer = LAYER_UI_FG, }); + MD_String8List words_to_say = words_on_current_page(it); if (unread) { draw_quad((DrawParams){ @@ -5824,9 +5670,30 @@ void frame(void) blendalpha(WHITE, 0.8f), .layer = LAYER_UI_FG, }); + if (interact) { - MD_DblRemove(unread_first, unread_last, unread_first); + if(it->words_said_on_page < words_to_say.node_count) + { + // still saying stuff + it->words_said_on_page = (int)words_to_say.node_count; + } + else + { + it->cur_page_index += 1; + if(words_on_current_page(it).node_count == 0) + { + // don't reset words_said_on_page because, even when the action is dismissed, the text for the last + // page of dialog should still linger + it->undismissed_action = false; + it->cur_page_index -= 1; + } + else + { + it->characters_of_word_animated = 0.0f; + it->words_said_on_page = 0; + } + } interact = false; } } @@ -5840,11 +5707,12 @@ void frame(void) AABB placing_text_in = aabb_centered(AddV2(bubble_center, V2(0, 10.0f)), V2(BUBBLE_TEXT_WIDTH_PIXELS, size.y * 0.15f)); dbgrect(placing_text_in); - MD_String8List last = last_said_without_unsaid_words(frame_arena, it); - if(last.node_count != 0) + MD_String8List to_draw = words_on_current_page_without_unsaid(it); + if(to_draw.node_count != 0) { + PlacedWordList placed = place_wrapped_words(frame_arena, to_draw, text_scale, aabb_size(placing_text_in).x, default_font); + // also called on npc response to see if it fits in the right amount of bubbles, if not tells AI how many words it has to trim its response by - PlacedWordList placed = place_wrapped_words(frame_arena, MD_S8ListJoin(frame_arena, last, &(MD_StringJoin){.mid = MD_S8Lit(" ")}), text_scale, aabb_size(placing_text_in).x, default_font); // translate_words_by(placed, V2(placing_text_in.upper_left.x, placing_text_in.lower_right.y)); translate_words_by(placed, AddV2(placing_text_in.upper_left, V2(0, -get_vertical_dist_between_lines(default_font, text_scale)))); for (PlacedWord *cur = placed.first; cur; cur = cur->next) @@ -5864,8 +5732,11 @@ void frame(void) static Entity *interacting_with = 0; // used by rendering to figure out who to draw dialog box on static bool player_in_combat = false; + + + float speed_target = 1.0f; - gs.stopped_time = unread_first != 0; + gs.stopped_time = cur_unread_entity != 0; if(gs.stopped_time) speed_target = 0.0f; // pausing the game speed_factor = Lerp(speed_factor, unwarped_dt*10.0f, speed_target); @@ -5987,11 +5858,11 @@ ISANERROR("Don't know how to do this stuff on this platform.") MD_String8 parse_response = parse_chatgpt_response(scratch.arena, it, sentence_str, &out); // check that it wraps in below two lines - PlacedWordList placed = place_wrapped_words(frame_arena, TextChunkString8(out.speech), BUBBLE_TEXT_SCALE, BUBBLE_TEXT_WIDTH_PIXELS, BUBBLE_FONT); + PlacedWordList placed = place_wrapped_words(frame_arena, split_by_word(frame_arena, TextChunkString8(out.speech)), BUBBLE_TEXT_SCALE, BUBBLE_TEXT_WIDTH_PIXELS, BUBBLE_FONT); int words_over_limit = 0; for(PlacedWord *cur = placed.first; cur; cur = cur->next) { - if(cur->line_index > 1) // the max number of lines of text on a bubble + if(cur->line_index >= BUBBLE_LINES_PER_PAGE*AI_MAX_BUBBLE_PAGES_IN_OUTPUT) // the max number of lines of text on a bubble { words_over_limit += 1; } @@ -6072,40 +5943,29 @@ ISANERROR("Don't know how to do this stuff on this platform.") { MD_ArenaTemp scratch = MD_GetScratch(0, 0); - MD_String8List split = split_by_word(scratch.arena, last_said_sentence(it)); - if(it->words_said <= split.node_count) + MD_String8List to_say = words_on_current_page(it); + MD_String8List to_say_without_unsaid = words_on_current_page_without_unsaid(it); + if(to_say.node_count > 0 && it->words_said_on_page < to_say.node_count) { - it->word_anim_in += CHARACTERS_PER_SEC * unwarped_dt; - int characters_in_animating_word = 0; - MD_String8Node *cur = split.first; - for(int i = 0; i < it->words_said + 1; i++) - { - if(cur) - { - if(i >= it->words_said - 1) - { - characters_in_animating_word = (int)cur->string.size; - break; - } - cur = cur->next; - } - } - if((int)it->word_anim_in + 1 > characters_in_animating_word) + if(cur_unread_entity == it) { - it->words_said += 1; - if(it->words_said < split.node_count) + it->characters_of_word_animated += CHARACTERS_PER_SEC * unwarped_dt; + int characters_in_animating_word = (int)to_say_without_unsaid.last->string.size; + if((int)it->characters_of_word_animated + 1 > characters_in_animating_word) { - it->word_anim_in = 0; + it->words_said_on_page += 1; + it->characters_of_word_animated = 0.0f; + + float dist = LenV2(SubV2(it->pos, gs.player->pos)); + float volume = Lerp(-0.6f, clamp01(dist / 70.0f), -1.0f); + AudioSample * possible_grunts[] = { + &sound_grunt_0, + &sound_grunt_1, + &sound_grunt_2, + &sound_grunt_3, + }; + play_audio(possible_grunts[rand() % ARRLEN(possible_grunts)], volume); } - float dist = LenV2(SubV2(it->pos, gs.player->pos)); - float volume = Lerp(-0.6f, clamp01(dist / 70.0f), -1.0f); - AudioSample * possible_grunts[] = { - &sound_grunt_0, - &sound_grunt_1, - &sound_grunt_2, - &sound_grunt_3, - }; - play_audio(possible_grunts[rand() % ARRLEN(possible_grunts)], volume); } } @@ -6520,13 +6380,16 @@ ISANERROR("Don't know how to do this stuff on this platform.") if (mocking_the_ai_response) { if (it->memories_last->context.talking_to_kind == it->npc_kind) + //if (it->memories_last->context.author_npc_kind != it->npc_kind) { const char *action = "none"; char *rigged_dialog[] = { "Repeated amounts of testing dialog overwhelmingly in support of the mulaney brothers", }; char *next_dialog = rigged_dialog[it->times_talked_to % ARRLEN(rigged_dialog)]; - ai_response = MD_S8Fmt(frame_arena, "{\"target\": \"%s\", \"action\": \"%s\", \"speech\": \"%s\"}", characters[it->memories_last->context.author_npc_kind].name, action, next_dialog); + char *target = characters[it->memories_last->context.author_npc_kind].name; + target = characters[NPC_Player].name; + ai_response = FmtWithLint(frame_arena, "{\"target\": \"%s\", \"action\": \"%s\", \"speech\": \"%s\"}", target, action, next_dialog); #ifdef DESKTOP it->times_talked_to += 1; #endif @@ -6823,155 +6686,6 @@ ISANERROR("Don't know how to do this stuff on this platform.") draw_centered_text((TextParams){false, MD_S8Lit("The AI server is having technical difficulties..."), text_center, WHITE, 1.0f }); } - if(false) - PROFILE_SCOPE("dialog menu") // big dialog panel draw big dialog panel - { - static float on_screen = 0.0f; - Entity *talking_to = gete(gs.player->talking_to); - on_screen = Lerp(on_screen, unwarped_dt*9.0f, talking_to ? 1.0f : 0.0f); - { - float panel_width = screen_size().x * 0.4f * on_screen; - AABB panel_aabb = (AABB) { .upper_left = V2(0.0f, screen_size().y), .lower_right = V2(panel_width, 0.0f) }; - float alpha = 1.0f; - - if (aabb_is_valid(panel_aabb)) - { - if (!item_grid_state.open && pressed.mouse_down && !has_point(panel_aabb, mouse_pos)) - { - gs.player->state = CHARACTER_IDLE; - } - draw_quad((DrawParams) { quad_aabb(panel_aabb), IMG(image_white_square), blendalpha(BLACK, 0.7f) }); - - // apply padding - float padding = 0.1f * screen_size().y; - panel_width -= padding * 2.0f; - panel_aabb.upper_left = AddV2(panel_aabb.upper_left, V2(padding, -padding)); - panel_aabb.lower_right = AddV2(panel_aabb.lower_right, V2(-padding, padding)); - - // draw button - float space_btwn_buttons = 20.0f; - float text_scale = 1.0f; - const float num_buttons = 2.0f; - Vec2 button_size = V2( - (panel_width - (num_buttons - 1.0f)*space_btwn_buttons) / num_buttons, - (panel_aabb.upper_left.y - panel_aabb.lower_right.y)*0.2f - ); - float button_grid_width = button_size.x*num_buttons + space_btwn_buttons * (num_buttons - 1.0f); - Vec2 cur_upper_left = V2((panel_aabb.upper_left.x + panel_aabb.lower_right.x) / 2.0f - button_grid_width / 2.0f, panel_aabb.lower_right.y + button_size.y); - if(receiving_text_input && pressed.speak_shortcut) - { - end_text_input(""); - pressed.speak_shortcut = false; - } - if (imbutton_key(aabb_at(cur_upper_left, button_size), text_scale, MD_S8Lit("Speak"), __LINE__, unwarped_dt, receiving_text_input) || (talking_to && pressed.speak_shortcut)) - { - begin_text_input(); - } - - - // draw keyboard hint - { - Vec2 keyboard_helper_at = V2(cur_upper_left.x + button_size.x*0.5f, cur_upper_left.y - button_size.y*0.75f); - draw_quad((DrawParams){ centered_quad(keyboard_helper_at, V2(40.0f, 40.0f)), IMG(image_white_square), blendalpha(GREY, 0.4f)}); - draw_centered_text((TextParams){false, MD_S8Lit("S"), keyboard_helper_at, BLACK, 1.5f}); - } - - cur_upper_left.x += button_size.x + space_btwn_buttons; - - if(item_grid_state.open && pressed.give_shortcut) - { - pressed.give_shortcut = false; - item_grid_state.open = false; - } - - if (imbutton_key(aabb_at(cur_upper_left, button_size), text_scale, MD_S8Lit("Give Item"), __LINE__, unwarped_dt, item_grid_state.open) || (talking_to && pressed.give_shortcut)) - { - item_grid_state = (ItemgridState){.open = true, .for_giving = true}; - } - - - // draw keyboard hint - { - Vec2 keyboard_helper_at = V2(cur_upper_left.x + button_size.x*0.5f, cur_upper_left.y - button_size.y*0.75f); - draw_quad((DrawParams){ centered_quad(keyboard_helper_at, V2(40.0f, 40.0f)), IMG(image_white_square), blendalpha(GREY, 0.4f)}); - draw_centered_text((TextParams){ false, MD_S8Lit("G"), keyboard_helper_at, BLACK, 1.5f}); - } - - const float dialog_text_scale = 1.0f; - float button_grid_height = button_size.y; - AABB dialog_panel = panel_aabb; - dialog_panel.lower_right.y += button_grid_height + 20.0f; // a little bit of padding because the buttons go up - float new_line_height = dialog_panel.lower_right.y; - - // talking to dialog text - if (talking_to && aabb_is_valid(dialog_panel)) - { - MD_ArenaTemp scratch = MD_GetScratch(0, 0); - Dialog dialog = get_dialog_elems(talking_to, true); - { - for (int i = dialog.cur_index - 1; i >= 0; i--) - { - DialogElement *it = &dialog.data[i]; - { - Color color; - if (it->kind == DELEM_PLAYER) - { - color = WHITE; - } - else if (it->kind == DELEM_NPC) - { - color = colhex(0x34e05c); - } - else if (it->kind == DELEM_ACTION_DESCRIPTION) - { - color = colhex(0xebc334); - } - else - { - assert(false); - } - color = blendalpha(color, alpha); - - const float text_scale = 1.0f; - PlacedWordList wrapped = place_wrapped_words(scratch.arena, MD_S8(it->speech, it->speech_length), text_scale, dialog_panel.lower_right.x - dialog_panel.upper_left.x, default_font); - float line_vertical_offset = -wrapped.last->lower_left_corner.y; - translate_words_by(wrapped, V2(0.0, line_vertical_offset)); - translate_words_by(wrapped, V2(dialog_panel.upper_left.x, new_line_height)); - new_line_height += line_vertical_offset + default_font.font_line_advance * text_scale; - - for(PlacedWord *cur = wrapped.first; cur; cur = cur->next) - { - float this_text_scale = text_scale; - if(it->was_last_said && cur->next == 0) - { - this_text_scale *= clamp01(talking_to->word_anim_in / (float)cur->text.size); - } - AABB clipping_aabb = dialog_panel; - clipping_aabb.lower_right.y -= 50.0f; - dbgrect(clipping_aabb); - draw_text((TextParams){ false, cur->text, cur->lower_left_corner, color, this_text_scale, .clip_to = clipping_aabb, .do_clipping = true,}); - } - - if(i != 0) - { - float separator_height = 40.0f; // how much vertical space the whole separation, including padding, takes - float line_height = 1.0f; - Vec2 line_from = AddV2(wrapped.first->lower_left_corner, V2(0, default_font.font_line_advance*text_scale + separator_height/2.0f)); - Vec2 line_to = AddV2(line_from, V2(aabb_size(dialog_panel).x, 0)); - draw_quad((DrawParams){ line_quad(line_from, line_to, line_height), IMG(image_white_square), blendalpha(WHITE, 0.6f), .clip_to = dialog_panel, .do_clipping = true}); - - new_line_height += separator_height; - } - - } - } - } - MD_ReleaseScratch(scratch); - } - } - } - } - // win screen { static float visible = 0.0f; diff --git a/makeprompt.h b/makeprompt.h index 90df891..ba30f91 100644 --- a/makeprompt.h +++ b/makeprompt.h @@ -243,8 +243,14 @@ typedef struct Entity Memory *memories_last; Memory *memories_added_while_time_stopped; float dialog_panel_opacity; - int words_said; - float word_anim_in; // in characters, the fraction a word is animated in is this over its length. + + // last_said_sentence(entity) contains the dialog the player has yet to see + bool undismissed_action; + uint64_t undismissed_action_tick; + float characters_of_word_animated; + int words_said_on_page; + int cur_page_index; + PathCacheHandle cached_path; int gen_request_id; Vec2 target_goto; diff --git a/tuning.h b/tuning.h index 1d66b51..b80c3db 100644 --- a/tuning.h +++ b/tuning.h @@ -20,6 +20,8 @@ #define BUBBLE_TEXT_WIDTH_PIXELS (BUBBLE_WIDTH_PIXELS*0.8f) #define BUBBLE_TEXT_SCALE 1.0f #define BUBBLE_FONT default_font +#define BUBBLE_LINES_PER_PAGE 2 +#define AI_MAX_BUBBLE_PAGES_IN_OUTPUT 2 #define ARENA_SIZE (1024*1024*10) #define BIG_ARENA_SIZE (ARENA_SIZE * 8)