void Window_Message::ApplyTextInsertingCommands() { text_index = text.end(); end = text.end(); // Contains already substitued \N actors to prevent endless recursion std::vector<int> replaced_actors; int actor_replacement_start = std::distance(text.begin(), end); if (!text.empty()) { // Move on first valid char --text_index; // Apply commands that insert text while (std::distance(text_index, text.begin()) <= -1) { char ch = tolower(*text_index--); switch (ch) { case 'n': case 'v': { if (*text_index != escape_char) { continue; } ++text_index; auto start_code = text_index - 1; bool success; int parsed_num; std::u32string command_result = Utils::DecodeUTF32(ParseCommandCode(success, parsed_num)); if (start_code < text.begin() + actor_replacement_start) { replaced_actors.clear(); } if (!success || std::find(replaced_actors.begin(), replaced_actors.end(), parsed_num) != replaced_actors.end()) { text_index = start_code; continue; } if (ch == 'n') { replaced_actors.push_back(parsed_num); actor_replacement_start = std::min<int>(std::distance(text.begin(), start_code), actor_replacement_start); } text.replace(start_code, text_index + 1, command_result); // Start from the beginning, the inserted text might add new commands text_index = text.end(); end = text.end(); actor_replacement_start = std::min<int> (std::distance(text.begin(), end), actor_replacement_start); // Move on first valid char --text_index; break; } default: break; } } } }
void Window_Message::StartMessageProcessing() { contents->Clear(); text.clear(); for (const std::string& line : Game_Message::texts) { text.append(Utils::DecodeUTF32(line)).append(1, U'\n'); } Game_Message::texts.clear(); item_max = min(4, Game_Message::choice_max); text_index = text.end(); end = text.end(); if (!text.empty()) { // Move on first valid char --text_index; // Apply commands that insert text while (std::distance(text_index, text.begin()) <= -1) { switch (tolower(*text_index--)) { case 'n': case 'v': { if (*text_index != escape_char) { continue; } ++text_index; auto start_code = text_index - 1; bool success; std::u32string command_result = Utils::DecodeUTF32(ParseCommandCode(success)); if (!success) { text_index = start_code - 2; continue; } text.replace(start_code, text_index + 1, command_result); // Start from the beginning, the inserted text might add new commands text_index = text.end(); end = text.end(); // Move on first valid char --text_index; break; } default: break; } } } text_index = text.begin(); InsertNewPage(); }
std::string Window_Message::ParseCommandCode(int call_depth) { int parameter; bool is_valid; // sub_code is used by chained arguments like \v[\v[1]] // In that case sub_code contains the result from \v[1] int sub_code = -1; uint32_t cmd_char = *text_index; if (std::distance(text_index, end) > 3 && *boost::next(text_index, 2) == '\\' && tolower(*boost::next(text_index, 3)) == 'v') { ++(++(++text_index)); // The result is an int value, str-to-int is safe in this case std::stringstream ss; ss << ParseCommandCode(++call_depth); ss >> sub_code; }
void Window_Message::UpdateMessage() { // Message Box Show Message rendering loop // Contains at what frame the sleep is over static int sleep_until = -1; if (sleep_until > -1) { if (Graphics::GetFrameCount() >= sleep_until) { // Sleep over sleep_until = -1; } else { return; } } bool instant_speed = false; int loop_count = 0; int loop_max = speed_table[speed_modifier] == 0 ? 2 : 1; while (instant_speed || loop_count < loop_max) { // It's assumed that speed_modifier is between 0 and 20 ++speed_frame_counter; if (speed_table[speed_modifier] != 0 && speed_table[speed_modifier] != speed_frame_counter) { break; } speed_frame_counter = 0; ++loop_count; if (text_index == end) { FinishMessageProcessing(); break; } else if (line_count == 4) { pause = true; new_page_after_pause = true; break; } if (*text_index == '\n') { instant_speed = false; InsertNewLine(); if (pause) { break; } } else if (*text_index == '\f') { instant_speed = false; ++text_index; if (*text_index == '\n') { ++text_index; } if (text_index != end) { pause = true; new_page_after_pause = true; } break; } else if (*text_index == '\\' && std::distance(text_index, end) > 1) { // Special message codes ++text_index; std::string command_result; switch (tolower(*text_index)) { case 'c': case 'n': case 's': case 'v': // These commands support indirect access via \v[] command_result = ParseCommandCode(); contents->TextDraw(contents_x, contents_y, text_color, command_result); contents_x += contents->GetFont()->GetSize(command_result).width; break; case '\\': // Show Backslash contents->TextDraw(contents_x, contents_y, text_color, std::string("\\")); contents_x += contents->GetFont()->GetSize("\\").width; break; case '_': // Insert half size space contents_x += contents->GetFont()->GetSize(" ").width / 2; break; case '$': // Show Gold Window gold_window->SetY(y == 0 ? 240 - 32 : 0); gold_window->Refresh(); gold_window->SetOpenAnimation(5); gold_window->SetVisible(true); break; case '!': // Text pause pause = true; break; case '^': // Force message close // The close happens at the end of the message, not where // the ^ is encoutered kill_message = true; break; case '>': // Instant speed start instant_speed = true; break; case '<': // Instant speed stop instant_speed = false; break; case '.': // 1/4 second sleep sleep_until = Graphics::GetFrameCount() + 60 / 4; ++text_index; return; break; case '|': // Second sleep sleep_until = Graphics::GetFrameCount() + 60; ++text_index; return; break; default:; } } else if (*text_index == '$' && std::distance(text_index, end) > 1 && std::isalpha(*boost::next(text_index))) { // ExFont contents->TextDraw(contents_x, contents_y, text_color, std::string(text_index.base(), boost::next(text_index, 2).base())); contents_x += 12; ++text_index; } else { std::string const glyph(text_index.base(), boost::next(text_index).base()); contents->TextDraw(contents_x, contents_y, text_color, glyph); contents_x += contents->GetFont()->GetSize(glyph).width; } ++text_index; } loop_count = 0; }