diff --git a/common/deserialization/subtitles/subtitles.cpp b/common/deserialization/subtitles/subtitles.cpp index f55aebe105..5751e12e3d 100644 --- a/common/deserialization/subtitles/subtitles.cpp +++ b/common/deserialization/subtitles/subtitles.cpp @@ -39,25 +39,29 @@ bool write_subtitle_db_to_files(const GameSubtitleDB& db) { } file_contents += fmt::format("\n(\"{}\"", scene_name); if (scene_info.m_kind == SubtitleSceneKind::Hint) { - file_contents += " :hint 0"; + file_contents += " :hint #x0"; } else if (scene_info.m_kind == SubtitleSceneKind::HintNamed) { file_contents += fmt::format(" :hint #x{0:x}", scene_info.m_id); } file_contents += "\n"; - for (const auto& line : scene_info.m_lines) { - // Clear screen entries - if (line.line_utf8.empty()) { - file_contents += fmt::format(" ({})\n", line.frame); - } else { - file_contents += fmt::format(" ({}", line.frame); - if (line.offscreen && scene_info.m_kind == SubtitleSceneKind::Movie) { - file_contents += " :offscreen"; + if (scene_info.m_lines.empty()) { + file_contents += fmt::format(" ()\n"); + } else { + for (const auto& line : scene_info.m_lines) { + // Clear screen entries + if (line.line_utf8.empty()) { + file_contents += fmt::format(" ({})\n", line.frame); + } else { + file_contents += fmt::format(" ({}", line.frame); + if (line.offscreen && scene_info.m_kind == SubtitleSceneKind::Movie) { + file_contents += " :offscreen"; + } + file_contents += fmt::format(" \"{}\"", line.speaker_utf8); + // escape quotes + std::string temp = line.line_utf8; + temp = std::regex_replace(temp, std::regex("\""), "\\\""); + file_contents += fmt::format(" \"{}\")\n", temp); } - file_contents += fmt::format(" \"{}\"", line.speaker_utf8); - // escape quotes - std::string temp = line.line_utf8; - temp = std::regex_replace(temp, std::regex("\""), "\\\""); - file_contents += fmt::format(" \"{}\")\n", temp); } } file_contents += " )\n"; diff --git a/common/nrepl/ReplServer.cpp b/common/nrepl/ReplServer.cpp index d57ab14774..828ef5284f 100644 --- a/common/nrepl/ReplServer.cpp +++ b/common/nrepl/ReplServer.cpp @@ -34,7 +34,13 @@ void ReplServer::post_init() { void ReplServer::ping_response(int socket) { std::string ping = fmt::format("Connected to OpenGOAL v{}.{} nREPL!", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR); - write_to_socket(socket, ping.c_str(), ping.size()); + auto resp = write_to_socket(socket, ping.c_str(), ping.size()); + if (resp == -1) { + fmt::print("[nREPL:{}] Client Disconnected: {}\n", tcp_port, inet_ntoa(addr.sin_addr), + ntohs(addr.sin_port), socket); + close_socket(socket); + client_sockets.erase(socket); + } } std::optional ReplServer::get_msg() { diff --git a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc index 46f154f621..9c154b9590 100644 --- a/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc +++ b/decompiler/config/jak1_ntsc_black_label/type_casts.jsonc @@ -824,51 +824,21 @@ [[157, 162], "a1", "dma-packet"] ], "(top-level-login task-control)": [[165, "v1", "symbol"]], - "(anon-function 227 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 286 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 366 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 367 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 368 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 369 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 380 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 383 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 390 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 393 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 400 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 403 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 435 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 445 task-control)": [ - [17, "v1", "float"] - ], - "(anon-function 455 task-control)": [ - [17, "v1", "float"] - ], + "(anon-function 227 task-control)": [[17, "v1", "float"]], + "(anon-function 286 task-control)": [[17, "v1", "float"]], + "(anon-function 366 task-control)": [[17, "v1", "float"]], + "(anon-function 367 task-control)": [[17, "v1", "float"]], + "(anon-function 368 task-control)": [[17, "v1", "float"]], + "(anon-function 369 task-control)": [[17, "v1", "float"]], + "(anon-function 380 task-control)": [[17, "v1", "float"]], + "(anon-function 383 task-control)": [[17, "v1", "float"]], + "(anon-function 390 task-control)": [[17, "v1", "float"]], + "(anon-function 393 task-control)": [[17, "v1", "float"]], + "(anon-function 400 task-control)": [[17, "v1", "float"]], + "(anon-function 403 task-control)": [[17, "v1", "float"]], + "(anon-function 435 task-control)": [[17, "v1", "float"]], + "(anon-function 445 task-control)": [[17, "v1", "float"]], + "(anon-function 455 task-control)": [[17, "v1", "float"]], "(method 18 game-info)": [ [4, "v1", "symbol"], [5, "v1", "level-load-info"], @@ -5946,18 +5916,14 @@ [67, "v1", "art-joint-anim"] ], - "(method 56 ram-boss)": [ - [58, "v1", "(pointer ram-boss-proj)"] - ], + "(method 56 ram-boss)": [[58, "v1", "(pointer ram-boss-proj)"]], "(code ram-idle)": [ [28, "v1", "art-joint-anim"], [98, "v1", "art-joint-anim"] ], - "(code ram-give-fuel-cell)": [ - [69, "v1", "(pointer snowcam)"] - ], + "(code ram-give-fuel-cell)": [[69, "v1", "(pointer snowcam)"]], "(code snow-bumper-deactivate)": [ [29, "v1", "art-joint-anim"], diff --git a/game/tools/subtitles/subtitle_editor.cpp b/game/tools/subtitles/subtitle_editor.cpp index d8f6d341b4..2028d4b1ed 100644 --- a/game/tools/subtitles/subtitle_editor.cpp +++ b/game/tools/subtitles/subtitle_editor.cpp @@ -43,7 +43,6 @@ SubtitleEditor::SubtitleEditor() : m_repl(8181) { m_subtitle_db = load_subtitle_project(); m_filter = m_filter_placeholder; m_filter_hints = m_filter_placeholder; - m_repl.connect(); } void SubtitleEditor::repl_set_continue_point(const std::string_view& continue_point) { @@ -70,6 +69,16 @@ std::string SubtitleEditor::repl_get_process_string(const std::string_view& enti process_name); } +void SubtitleEditor::repl_play_hint(const std::string_view& hint_name) { + repl_reset_game(); + repl_set_continue_point("village1-hut"); + // TODO - move into water fountain + m_repl.eval( + fmt::format("(level-hint-spawn (game-text-id zero) \"{}\" (the-as entity #f) *entity-pool* " + "(game-task none))", + hint_name)); +} + void SubtitleEditor::repl_execute_cutscene_code(const SubtitleEditorDB::Entry& entry) { // Reset the game first to get to a known state repl_reset_game(); @@ -150,7 +159,24 @@ void SubtitleEditor::draw_window() { draw_edit_options(); draw_repl_options(); - draw_current_cutscene(); + if (!m_current_scene) { + ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, m_selected_text_color); + } + if (ImGui::TreeNode("Currently Selected Cutscene")) { + ImGui::PopStyleColor(); + if (m_current_scene) { + draw_subtitle_options(*m_current_scene, true); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 0, 0, 255)); + ImGui::Text("Select a Scene from Below!"); + ImGui::PopStyleColor(); + } + ImGui::TreePop(); + } else { + ImGui::PopStyleColor(); + } if (ImGui::TreeNode("All Cutscenes")) { ImGui::InputText("New Scene Name", &m_new_scene_name); @@ -183,13 +209,50 @@ void SubtitleEditor::draw_window() { ImGui::TreePop(); } - // TODO - hints + if (ImGui::TreeNode("All Hints")) { + ImGui::InputText("New Scene Name", &m_new_scene_name); + // TODO - make this a dropdown + ImGui::InputText("New Scene ID (hex)", &m_new_scene_id); + ImGui::InputText("New Scene Group", &m_new_scene_group); + ImGui::InputText("Filter", &m_filter, ImGuiInputTextFlags_::ImGuiInputTextFlags_AutoSelectAll); + if (is_scene_in_current_lang(m_new_scene_name)) { + ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); + ImGui::Text("Scene already exists with that name, no!"); + ImGui::PopStyleColor(); + } + if (m_new_scene_group.empty()) { + ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); + ImGui::Text("You must provide a group to sort the scene into!"); + ImGui::PopStyleColor(); + } + if (!is_scene_in_current_lang(m_new_scene_name) && !m_new_scene_group.empty()) { + if (ImGui::Button("Add Scene")) { + GameSubtitleSceneInfo newScene; + newScene.m_name = m_new_scene_name; + if (m_new_scene_id == "0") { + newScene.m_kind = SubtitleSceneKind::Hint; + newScene.m_id = strtoul(m_new_scene_id.c_str(), nullptr, 16); + } else { + newScene.m_kind = SubtitleSceneKind::HintNamed; + newScene.m_id = strtoul(m_new_scene_id.c_str(), nullptr, 16); + } + newScene.m_sorting_group = m_new_scene_group; + m_subtitle_db.m_banks.at(m_current_language)->add_scene(newScene); + m_new_scene_name = ""; + } + } + + draw_all_hint_groups(); + ImGui::TreePop(); + } ImGui::End(); } void SubtitleEditor::draw_edit_options() { if (ImGui::TreeNode("Editing Options")) { + // TODO - validate these / make it a dropdown + // - source of truth is the files ImGui::InputInt("Editing language ID", &m_current_language); ImGui::InputInt("Base language ID", &m_base_language); ImGui::Checkbox("Show missing cutscenes from base", &m_base_show_missing_cutscenes); @@ -252,6 +315,17 @@ void SubtitleEditor::draw_all_cutscene_groups() { } } +void SubtitleEditor::draw_all_hint_groups() { + for (auto& group_name : m_subtitle_db.m_subtitle_groups->m_group_order) { + ImGui::SetNextItemOpen(true); + if (ImGui::TreeNode(group_name.c_str())) { + draw_all_hints(group_name, false); + draw_all_hints(group_name, true); + ImGui::TreePop(); + } + } +} + void SubtitleEditor::draw_all_scenes(std::string group_name, bool base_cutscenes) { auto& scenes = m_subtitle_db.m_banks.at(base_cutscenes ? m_base_language : m_current_language)->m_scenes; @@ -279,6 +353,7 @@ void SubtitleEditor::draw_all_scenes(std::string group_name, bool base_cutscenes if (base_cutscenes) { ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); } + if (ImGui::TreeNode( fmt::format("{}-{}", scene_name, base_cutscenes ? m_base_language : m_current_language) .c_str(), @@ -296,77 +371,7 @@ void SubtitleEditor::draw_all_scenes(std::string group_name, bool base_cutscenes m_subtitle_db.m_banks.at(m_current_language)->add_scene(scene_info); } } - if (!m_repl.is_connected()) { - ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); - ImGui::Text("REPL not connected, can't play!"); - ImGui::PopStyleColor(); - } else if (m_db.count(scene_info.m_name) > 0) { - if (ImGui::Button("Play Scene")) { - if (m_db.count(scene_info.m_name) == 1) { - repl_execute_cutscene_code(m_db[scene_info.m_name]); - } - } - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); - ImGui::TextWrapped("You may have to click twice, load times cause issues"); - ImGui::PopStyleColor(); - ImGui::NewLine(); - } - if (ImGui::BeginCombo("Sorting Group", scene_info.m_sorting_group.c_str())) { - for (size_t i = 0; i < m_subtitle_db.m_subtitle_groups->m_group_order.size(); ++i) { - const bool isSelected = (scene_info.m_sorting_group_idx == (int)i); - if (ImGui::Selectable(m_subtitle_db.m_subtitle_groups->m_group_order[i].c_str(), - isSelected)) { - // Remove from current group - m_subtitle_db.m_subtitle_groups->remove_scene(scene_info.m_sorting_group, - scene_info.m_name); - // Add to new group - scene_info.m_sorting_group_idx = i; - scene_info.m_sorting_group = m_subtitle_db.m_subtitle_groups->m_group_order.at(i); - m_subtitle_db.m_subtitle_groups->add_scene(scene_info.m_sorting_group, - scene_info.m_name); - } - if (isSelected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - for (size_t i = 0; i < scene_info.m_lines.size(); i++) { - auto& subtitleLine = scene_info.m_lines.at(i); - std::string summary; - if (subtitleLine.line_utf8.empty()) { - summary = fmt::format("[{}] Clear Screen", subtitleLine.frame); - } else { - summary = fmt::format("[{}] {} - '{}...'", subtitleLine.frame, subtitleLine.speaker_utf8, - subtitleLine.line_utf8.substr(0, 30)); - } - if (subtitleLine.line_utf8.empty()) { - ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); - } else if (subtitleLine.offscreen) { - ImGui::PushStyleColor(ImGuiCol_Text, m_offscreen_text_color); - } - if (ImGui::TreeNode(fmt::format("{}", i).c_str(), "%s", summary.c_str())) { - if (subtitleLine.line_utf8.empty() || subtitleLine.offscreen) { - ImGui::PopStyleColor(); - } - ImGui::InputInt("Starting Frame", &subtitleLine.frame, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal); - ImGui::InputText("Speaker", &subtitleLine.speaker_utf8, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsUppercase); - ImGui::InputText("Text", &subtitleLine.line_utf8, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsUppercase); - ImGui::Checkbox("Offscreen?", &subtitleLine.offscreen); - ImGui::PushStyleColor(ImGuiCol_Button, m_warning_color); - if (ImGui::Button("Remove Line")) { - scene_info.m_lines.erase(scene_info.m_lines.begin() + i); - } - ImGui::PopStyleColor(); - ImGui::TreePop(); - } else if (subtitleLine.line_utf8.empty() || subtitleLine.offscreen) { - ImGui::PopStyleColor(); - } - } + draw_subtitle_options(scene_info); ImGui::TreePop(); } else if (base_cutscenes || is_current_scene) { ImGui::PopStyleColor(); @@ -374,126 +379,167 @@ void SubtitleEditor::draw_all_scenes(std::string group_name, bool base_cutscenes } } -void SubtitleEditor::draw_current_cutscene() { - if (!m_current_scene) { - ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); - } else { - ImGui::PushStyleColor(ImGuiCol_Text, m_selected_text_color); - } - if (ImGui::TreeNode("Currently Selected Movie")) { - ImGui::PopStyleColor(); - if (m_current_scene) { - if (!m_repl.is_connected()) { - ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); - ImGui::Text("REPL not connected, can't play!"); +void SubtitleEditor::draw_all_hints(std::string group_name, bool base_cutscenes) { + auto& scenes = + m_subtitle_db.m_banks.at(base_cutscenes ? m_base_language : m_current_language)->m_scenes; + auto scenes_in_group = m_subtitle_db.m_subtitle_groups->m_groups[group_name]; + for (auto& scene_name : scenes_in_group) { + if (scenes.count(scene_name) == 0) { + continue; + } + auto& scene_info = scenes[scene_name]; + // Don't duplicate entries + if (base_cutscenes && is_scene_in_current_lang(scene_name)) { + continue; + } + if (scene_info.m_kind != SubtitleSceneKind::Hint && + scene_info.m_kind != SubtitleSceneKind::HintNamed) { + continue; + } + if ((!m_filter.empty() && m_filter != m_filter_placeholder) && + scene_name.find(m_filter) == std::string::npos) { + continue; + } + if (base_cutscenes) { + ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); + } + + if (ImGui::TreeNode( + fmt::format("{}-{}", scene_name, base_cutscenes ? m_base_language : m_current_language) + .c_str(), + "%s", scene_name.c_str())) { + if (base_cutscenes) { ImGui::PopStyleColor(); - } else if (m_db.count(m_current_scene->m_name) > 0) { - if (ImGui::Button("Play Scene")) { - if (m_db.count(m_current_scene->m_name) == 1) { - repl_execute_cutscene_code(m_db[m_current_scene->m_name]); - } - } - ImGui::SameLine(); - ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); - ImGui::TextWrapped("You may have to click twice, load times cause issues"); - ImGui::PopStyleColor(); - ImGui::NewLine(); } - if (ImGui::BeginCombo("Sorting Group", m_current_scene->m_sorting_group.c_str())) { - for (size_t i = 0; i < m_subtitle_db.m_subtitle_groups->m_group_order.size(); ++i) { - const bool isSelected = (m_current_scene->m_sorting_group_idx == (int)i); - if (ImGui::Selectable(m_subtitle_db.m_subtitle_groups->m_group_order[i].c_str(), - isSelected)) { - // Remove from current group - m_subtitle_db.m_subtitle_groups->remove_scene(m_current_scene->m_sorting_group, - m_current_scene->m_name); - // Add to new group - m_current_scene->m_sorting_group_idx = i; - m_current_scene->m_sorting_group = m_subtitle_db.m_subtitle_groups->m_group_order.at(i); - m_subtitle_db.m_subtitle_groups->add_scene(m_current_scene->m_sorting_group, - m_current_scene->m_name); - } - if (isSelected) { - ImGui::SetItemDefaultFocus(); - } - } - ImGui::EndCombo(); - } - ImGui::InputInt("Frame Number", &m_current_scene_frame, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal); - ImGui::InputText("Text", &m_current_scene_text, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsUppercase); - ImGui::InputText("Speaker", &m_current_scene_speaker, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsUppercase); - ImGui::Checkbox("Offscreen", &m_current_scene_offscreen); - bool rendered_text_entry_btn = false; - if (m_current_scene_frame < 0 || m_current_scene_text.empty() || - m_current_scene_speaker.empty()) { - ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); - ImGui::Text("Can't add a new text entry with the current fields!"); - ImGui::PopStyleColor(); - } else { - rendered_text_entry_btn = true; - if (ImGui::Button("Add Text Entry")) { - m_current_scene->add_line(m_current_scene_frame, "", m_current_scene_text, "", - m_current_scene_speaker, m_current_scene_offscreen); + if (base_cutscenes) { + if (ImGui::Button("Copy from Base Language")) { + m_subtitle_db.m_banks.at(m_current_language)->add_scene(scene_info); } } - if (m_current_scene_frame < 0) { - ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); - ImGui::Text("Can't add a clear screen entry with the current fields!"); - ImGui::PopStyleColor(); - } else { - if (rendered_text_entry_btn) { - ImGui::SameLine(); - if (ImGui::Button("Add Clear Screen Entry")) { - m_current_scene->add_line(m_current_scene_frame, "", "", "", "", false); - } - } - } - ImGui::NewLine(); - for (size_t i = 0; i < m_current_scene->m_lines.size(); i++) { - auto& subtitleLine = m_current_scene->m_lines.at(i); - std::string summary; - if (subtitleLine.line_utf8.empty()) { - summary = fmt::format("[{}] Clear Screen", subtitleLine.frame); - } else { - summary = fmt::format("[{}] {} - '{}...'", subtitleLine.frame, subtitleLine.speaker_utf8, - subtitleLine.line_utf8.substr(0, 30)); - } - if (subtitleLine.line_utf8.empty()) { - ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); - } else if (subtitleLine.offscreen) { - ImGui::PushStyleColor(ImGuiCol_Text, m_offscreen_text_color); - } - if (ImGui::TreeNode(fmt::format("{}", i).c_str(), "%s", summary.c_str())) { - if (subtitleLine.line_utf8.empty() || subtitleLine.offscreen) { - ImGui::PopStyleColor(); - } - ImGui::InputInt("Starting Frame", &subtitleLine.frame, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal); - ImGui::InputText("Speaker", &subtitleLine.speaker_utf8, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsUppercase); - ImGui::InputText("Text", &subtitleLine.line_utf8, - ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsUppercase); - ImGui::Checkbox("Offscreen?", &subtitleLine.offscreen); - ImGui::PushStyleColor(ImGuiCol_Button, m_warning_color); - if (ImGui::Button("Remove Line")) { - m_current_scene->m_lines.erase(m_current_scene->m_lines.begin() + i); - } - ImGui::PopStyleColor(); - ImGui::TreePop(); - } else if (subtitleLine.line_utf8.empty() || subtitleLine.offscreen) { - ImGui::PopStyleColor(); - } - } - } else { - ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 0, 0, 255)); - ImGui::Text("Select a Scene from Below!"); + draw_subtitle_options(scene_info); + ImGui::TreePop(); + } else if (base_cutscenes) { ImGui::PopStyleColor(); } - ImGui::TreePop(); - } else { - ImGui::PopStyleColor(); } } + +void SubtitleEditor::draw_subtitle_options(GameSubtitleSceneInfo& scene, bool current_scene) { + if (!m_repl.is_connected()) { + ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); + ImGui::Text("REPL not connected, can't play!"); + ImGui::PopStyleColor(); + } else { + // Cutscenes + if (scene.m_kind == SubtitleSceneKind::Movie && m_db.count(scene.m_name) > 0) { + if (ImGui::Button("Play Scene")) { + repl_execute_cutscene_code(m_db[scene.m_name]); + } + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); + ImGui::TextWrapped("You may have to click twice, load times cause issues"); + ImGui::PopStyleColor(); + ImGui::NewLine(); + } + // Hints + else if (scene.m_kind == SubtitleSceneKind::Hint || + scene.m_kind == SubtitleSceneKind::HintNamed) { + if (ImGui::Button("Play Hint")) { + repl_play_hint(scene.m_name); + } + ImGui::SameLine(); + ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); + ImGui::TextWrapped("You may have to click twice, load times cause issues"); + ImGui::PopStyleColor(); + ImGui::NewLine(); + } + } + if (ImGui::BeginCombo("Sorting Group", scene.m_sorting_group.c_str())) { + for (size_t i = 0; i < m_subtitle_db.m_subtitle_groups->m_group_order.size(); ++i) { + const bool isSelected = (scene.m_sorting_group_idx == (int)i); + if (ImGui::Selectable(m_subtitle_db.m_subtitle_groups->m_group_order[i].c_str(), + isSelected)) { + // Remove from current group + m_subtitle_db.m_subtitle_groups->remove_scene(scene.m_sorting_group, scene.m_name); + // Add to new group + scene.m_sorting_group_idx = i; + scene.m_sorting_group = m_subtitle_db.m_subtitle_groups->m_group_order.at(i); + m_subtitle_db.m_subtitle_groups->add_scene(scene.m_sorting_group, scene.m_name); + } + if (isSelected) { + ImGui::SetItemDefaultFocus(); + } + } + ImGui::EndCombo(); + } + if (current_scene) { + draw_new_cutscene_line_form(); + } + for (size_t i = 0; i < scene.m_lines.size(); i++) { + auto& subtitleLine = scene.m_lines.at(i); + std::string summary; + if (subtitleLine.line_utf8.empty()) { + summary = fmt::format("[{}] Clear Screen", subtitleLine.frame); + } else { + summary = fmt::format("[{}] {} - '{}...'", subtitleLine.frame, subtitleLine.speaker_utf8, + subtitleLine.line_utf8.substr(0, 30)); + } + if (subtitleLine.line_utf8.empty()) { + ImGui::PushStyleColor(ImGuiCol_Text, m_disabled_text_color); + } else if (subtitleLine.offscreen) { + ImGui::PushStyleColor(ImGuiCol_Text, m_offscreen_text_color); + } + if (ImGui::TreeNode(fmt::format("{}", i).c_str(), "%s", summary.c_str())) { + if (subtitleLine.line_utf8.empty() || subtitleLine.offscreen) { + ImGui::PopStyleColor(); + } + ImGui::InputInt("Starting Frame", &subtitleLine.frame, + ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal); + ImGui::InputText("Speaker", &subtitleLine.speaker_utf8); + ImGui::InputText("Text", &subtitleLine.line_utf8); + ImGui::Checkbox("Offscreen?", &subtitleLine.offscreen); + ImGui::PushStyleColor(ImGuiCol_Button, m_warning_color); + if (ImGui::Button("Remove Line")) { + scene.m_lines.erase(scene.m_lines.begin() + i); + } + ImGui::PopStyleColor(); + ImGui::TreePop(); + } else if (subtitleLine.line_utf8.empty() || subtitleLine.offscreen) { + ImGui::PopStyleColor(); + } + } +} + +void SubtitleEditor::draw_new_cutscene_line_form() { + ImGui::InputInt("Frame Number", &m_current_scene_frame, + ImGuiInputTextFlags_::ImGuiInputTextFlags_CharsDecimal); + ImGui::InputText("Text", &m_current_scene_text); + ImGui::InputText("Speaker", &m_current_scene_speaker); + ImGui::Checkbox("Offscreen", &m_current_scene_offscreen); + bool rendered_text_entry_btn = false; + if (m_current_scene_frame < 0 || m_current_scene_text.empty() || + m_current_scene_speaker.empty()) { + ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); + ImGui::Text("Can't add a new text entry with the current fields!"); + ImGui::PopStyleColor(); + } else { + rendered_text_entry_btn = true; + if (ImGui::Button("Add Text Entry")) { + m_current_scene->add_line(m_current_scene_frame, "", m_current_scene_text, "", + m_current_scene_speaker, m_current_scene_offscreen); + } + } + if (m_current_scene_frame < 0) { + ImGui::PushStyleColor(ImGuiCol_Text, m_error_text_color); + ImGui::Text("Can't add a clear screen entry with the current fields!"); + ImGui::PopStyleColor(); + } else { + if (rendered_text_entry_btn) { + ImGui::SameLine(); + if (ImGui::Button("Add Clear Screen Entry")) { + m_current_scene->add_line(m_current_scene_frame, "", "", "", "", false); + } + } + } + ImGui::NewLine(); +} diff --git a/game/tools/subtitles/subtitle_editor.h b/game/tools/subtitles/subtitle_editor.h index bddabaa71c..97528a7570 100644 --- a/game/tools/subtitles/subtitle_editor.h +++ b/game/tools/subtitles/subtitle_editor.h @@ -33,7 +33,10 @@ class SubtitleEditor { void draw_all_cutscene_groups(); void draw_all_scenes(std::string group_name, bool base_cutscenes = false); - void draw_current_cutscene(); + void draw_subtitle_options(GameSubtitleSceneInfo& scene, bool current_scene = false); + void draw_new_cutscene_line_form(); + void draw_all_hint_groups(); + void draw_all_hints(std::string group_name, bool base_cutscenes); GameSubtitleDB m_subtitle_db; std::map m_db = {}; @@ -50,6 +53,7 @@ class SubtitleEditor { std::string m_new_scene_name = ""; std::string m_new_scene_group = ""; + std::string m_new_scene_id = "0"; std::string m_new_scene_group_name = ""; @@ -79,6 +83,7 @@ class SubtitleEditor { const std::string_view& process_name); void repl_execute_cutscene_code(const SubtitleEditorDB::Entry& entry); void repl_rebuild_text(); + void repl_play_hint(const std::string_view& hint_name); bool is_scene_in_current_lang(const std::string& scene_name); };