diff --git a/base/themes b/base/themes index c9acb90..7c036c0 160000 --- a/base/themes +++ b/base/themes @@ -1 +1 @@ -Subproject commit c9acb90137361ea8c29bc4cf2bf530d14b52b23a +Subproject commit 7c036c09ddfadc2680a2c5dbfa188dc98142a427 diff --git a/include/aoimage.h b/include/aoimage.h index 70ff1fc..b75eee1 100644 --- a/include/aoimage.h +++ b/include/aoimage.h @@ -11,7 +11,7 @@ class AOImage : public QLabel { public: - AOImage(QWidget *parent, AOApplication *p_ao_app); + AOImage(QWidget *parent, AOApplication *p_ao_app, bool make_static = false); ~AOImage(); QWidget *m_parent; @@ -20,6 +20,8 @@ public: QString path; + bool is_static = false; + bool set_image(QString p_image, QString p_misc = ""); void set_size_and_pos(QString identifier); }; diff --git a/include/aolayer.h b/include/aolayer.h index 1984b77..f42642c 100644 --- a/include/aolayer.h +++ b/include/aolayer.h @@ -52,6 +52,7 @@ public: bool force_continuous = false; Qt::TransformationMode transform_mode = Qt::FastTransformation; // transformation mode to use for this image bool stretch = false; // Should we stretch/squash this image to fill the screen? + bool masked = true; // Set a mask to the dimensions of the widget? // Set the movie's image to provided paths, preparing for playback. void start_playback(QString p_image); diff --git a/include/courtroom.h b/include/courtroom.h index 9e0ae58..9da465a 100644 --- a/include/courtroom.h +++ b/include/courtroom.h @@ -235,7 +235,7 @@ public: DISPLAY_AND_IO }; // Log the message contents and information such as evidence presenting etc. into the log file, the IC log, or both. - void log_chatmessage(QString f_message, int f_char_id, QString f_showname = "", int f_color = 0, LogMode f_log_mode=IO_ONLY); + void log_chatmessage(QString f_message, int f_char_id, QString f_showname = "", QString f_char = "", QString f_objection_mod = "", int f_evi_id = 0, int f_color = 0, LogMode f_log_mode=IO_ONLY); // Log the message contents and information such as evidence presenting etc. into the IC logs void handle_callwords(); @@ -755,7 +755,6 @@ private: QVector ui_char_button_list; QVector ui_char_button_list_filtered; - AOImage *ui_selector; AOButton *ui_back_to_lobby; diff --git a/include/demoserver.h b/include/demoserver.h index 0d4f0e4..e5d5712 100644 --- a/include/demoserver.h +++ b/include/demoserver.h @@ -10,6 +10,7 @@ #include #include #include +#include class DemoServer : public QObject { diff --git a/src/aocharbutton.cpp b/src/aocharbutton.cpp index 3b384f3..5b48e50 100644 --- a/src/aocharbutton.cpp +++ b/src/aocharbutton.cpp @@ -15,19 +15,19 @@ AOCharButton::AOCharButton(QWidget *parent, AOApplication *p_ao_app, int x_pos, this->resize(60, 60); this->move(x_pos, y_pos); - ui_taken = new AOImage(this, ao_app); + ui_taken = new AOImage(this, ao_app, true); ui_taken->resize(60, 60); ui_taken->set_image("char_taken"); ui_taken->setAttribute(Qt::WA_TransparentForMouseEvents); ui_taken->hide(); - ui_passworded = new AOImage(this, ao_app); + ui_passworded = new AOImage(this, ao_app, true); ui_passworded->resize(60, 60); ui_passworded->set_image("char_passworded"); ui_passworded->setAttribute(Qt::WA_TransparentForMouseEvents); ui_passworded->hide(); - ui_selector = new AOImage(parent, ao_app); + ui_selector = new AOImage(parent, ao_app, true); ui_selector->resize(62, 62); ui_selector->move(x_pos - 1, y_pos - 1); ui_selector->set_image("char_selector"); diff --git a/src/aoevidencebutton.cpp b/src/aoevidencebutton.cpp index aea903a..fee7327 100644 --- a/src/aoevidencebutton.cpp +++ b/src/aoevidencebutton.cpp @@ -9,14 +9,14 @@ AOEvidenceButton::AOEvidenceButton(QWidget *p_parent, AOApplication *p_ao_app, ao_app = p_ao_app; m_parent = p_parent; - ui_selected = new AOImage(this, ao_app); + ui_selected = new AOImage(this, ao_app, true); ui_selected->resize(p_w, p_h); // ui_selected->move(p_x, p_y); ui_selected->set_image("evidence_selected"); ui_selected->setAttribute(Qt::WA_TransparentForMouseEvents); ui_selected->hide(); - ui_selector = new AOImage(this, ao_app); + ui_selector = new AOImage(this, ao_app, true); ui_selector->resize(p_w, p_h); // ui_selector->move(p_x - 1, p_y - 1); ui_selector->set_image("evidence_selector"); diff --git a/src/aoimage.cpp b/src/aoimage.cpp index e1bd8b8..656528e 100644 --- a/src/aoimage.cpp +++ b/src/aoimage.cpp @@ -4,18 +4,22 @@ #include -AOImage::AOImage(QWidget *parent, AOApplication *p_ao_app) : QLabel(parent) +AOImage::AOImage(QWidget *parent, AOApplication *p_ao_app, bool make_static) : QLabel(parent) { m_parent = parent; ao_app = p_ao_app; - movie = new QMovie(); - connect(movie, &QMovie::frameChanged, [=]{ - QPixmap f_pixmap = movie->currentPixmap(); - f_pixmap = - f_pixmap.scaled(this->size(), Qt::IgnoreAspectRatio); - this->setPixmap(f_pixmap); - this->setMask(f_pixmap.mask()); - }); + is_static = make_static; + if (!is_static) // Only create the QMovie if we're non-static + { + movie = new QMovie(this); + connect(movie, &QMovie::frameChanged, [=]{ + QPixmap f_pixmap = movie->currentPixmap(); + f_pixmap = + f_pixmap.scaled(this->size(), Qt::IgnoreAspectRatio); + this->setPixmap(f_pixmap); + this->setMask(f_pixmap.mask()); + }); + } } AOImage::~AOImage() {} @@ -23,7 +27,7 @@ AOImage::~AOImage() {} bool AOImage::set_image(QString p_path, QString p_misc) { // Check if the user wants animated themes - if (ao_app->get_animated_theme()) + if (!is_static && ao_app->get_animated_theme()) // We want an animated image p_path = ao_app->get_image(p_path, ao_app->current_theme, ao_app->get_subtheme(), ao_app->default_theme, p_misc); else @@ -35,12 +39,14 @@ bool AOImage::set_image(QString p_path, QString p_misc) return false; } path = p_path; - movie->stop(); - movie->setFileName(path); - if (ao_app->get_animated_theme() && movie->frameCount() > 1) { - movie->start(); + if (!is_static) { + movie->stop(); + movie->setFileName(path); + if (ao_app->get_animated_theme() && movie->frameCount() > 1) { + movie->start(); + } } - else { + if (is_static || !ao_app->get_animated_theme() || movie->frameCount() <= 1) { QPixmap f_pixmap(path); f_pixmap = diff --git a/src/aolayer.cpp b/src/aolayer.cpp index 18213ce..795274f 100644 --- a/src/aolayer.cpp +++ b/src/aolayer.cpp @@ -98,9 +98,10 @@ void AOLayer::center_pixmap(QPixmap f_pixmap) { x + (f_w - f_pixmap.width()) / 2, y + (f_h - f_pixmap.height())); // Always center horizontally, always put // at the bottom vertically - this->setMask( - QRegion((f_pixmap.width() - f_w) / 2, (f_pixmap.height() - f_h) / 2, f_w, - f_h)); // make sure we don't escape the area we've been given + if (masked) + this->setMask( + QRegion((f_pixmap.width() - f_w) / 2, (f_pixmap.height() - f_h) / 2, f_w, + f_h)); // make sure we don't escape the area we've been given } void AOLayer::combo_resize(int w, int h) @@ -196,20 +197,20 @@ void CharLayer::load_image(QString p_filename, QString p_charname, << " continuous: " << continuous; #endif QStringList pathlist = { - ao_app->get_image_suffix(ao_app->get_character_path( - p_charname, prefix + current_emote)), // Default path - ao_app->get_image_suffix(ao_app->get_character_path( + ao_app->get_character_path( + p_charname, prefix + current_emote), // Default path + ao_app->get_character_path( p_charname, - prefix + "/" + current_emote)), // Path check if it's categorized + prefix + "/" + current_emote), // Path check if it's categorized // into a folder - ao_app->get_image_suffix(ao_app->get_character_path( + ao_app->get_character_path( p_charname, - current_emote)), // Just use the non-prefixed image, animated or not - ao_app->get_image_suffix( - ao_app->get_theme_path("placeholder")), // Theme placeholder path - ao_app->get_image_suffix(ao_app->get_theme_path( - "placeholder", ao_app->default_theme))}; // Default theme placeholder path - start_playback(find_image(pathlist)); + current_emote), // Just use the non-prefixed image, animated or not + current_emote, // The path by itself after the above fail + ao_app->get_theme_path("placeholder"), // Theme placeholder path + ao_app->get_theme_path( + "placeholder", ao_app->default_theme)}; // Default theme placeholder path + start_playback(ao_app->get_image_path(pathlist)); } void SplashLayer::load_image(QString p_filename, QString p_charname, diff --git a/src/charselect.cpp b/src/charselect.cpp index d35f0ca..820abf5 100644 --- a/src/charselect.cpp +++ b/src/charselect.cpp @@ -21,10 +21,6 @@ void Courtroom::construct_char_select() ui_char_buttons = new QWidget(ui_char_select_background); - ui_selector = new AOImage(ui_char_select_background, ao_app); - ui_selector->setAttribute(Qt::WA_TransparentForMouseEvents); - ui_selector->resize(62, 62); - ui_back_to_lobby = new AOButton(ui_char_select_background, ao_app); ui_char_password = new QLineEdit(ui_char_select_background); diff --git a/src/courtroom.cpp b/src/courtroom.cpp index 7ea39df..8bbaaa9 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -46,7 +46,9 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_vp_background = new BackgroundLayer(ui_viewport, ao_app); ui_vp_speedlines = new SplashLayer(ui_viewport, ao_app); ui_vp_player_char = new CharLayer(ui_viewport, ao_app); + ui_vp_player_char->masked = false; ui_vp_sideplayer_char = new CharLayer(ui_viewport, ao_app); + ui_vp_sideplayer_char->masked = false; ui_vp_sideplayer_char->hide(); ui_vp_desk = new BackgroundLayer(ui_viewport, ao_app); @@ -976,9 +978,6 @@ void Courtroom::set_widgets() set_size_and_pos(ui_sfx_slider, "sfx_slider"); set_size_and_pos(ui_blip_slider, "blip_slider"); - ui_selector->set_image("char_selector"); - ui_selector->hide(); - set_size_and_pos(ui_back_to_lobby, "back_to_lobby"); ui_back_to_lobby->setText(tr("Back to Lobby")); ui_back_to_lobby->setToolTip(tr("Return back to the server list.")); @@ -1645,6 +1644,11 @@ void Courtroom::append_server_chatmessage(QString p_name, QString p_message, ui_server_chatlog->append_chatmessage(p_name, p_message, color); + + if (ao_app->get_auto_logging_enabled() && !ao_app->log_filename.isEmpty()) { + QString full = "[OOC][" + QDateTime::currentDateTime().toUTC().toString() + "] " + p_name + ": " + p_message; + ao_app->append_to_file(full, ao_app->log_filename, true); + } } void Courtroom::on_authentication_state_received(int p_state) @@ -1667,7 +1671,7 @@ void Courtroom::on_chat_return_pressed() if (is_muted) return; - if ((anim_state < 3 || text_state < 2) && objection_state == 0) + if (text_state < 2 && objection_state == 0) return; ui_ic_chat_message->blockSignals(true); @@ -1980,7 +1984,7 @@ void Courtroom::chatmessage_enqueue(QStringList p_contents) if (SHOWNAME < p_contents.size()) showname = p_contents[SHOWNAME]; - log_chatmessage(p_contents[MESSAGE], f_char_id, showname, p_contents[TEXT_COLOR].toInt(), log_mode); + log_chatmessage(p_contents[MESSAGE], f_char_id, showname, p_contents[CHAR_NAME], p_contents[OBJECTION_MOD], p_contents[EVIDENCE_ID].toInt(), p_contents[TEXT_COLOR].toInt(), log_mode); // Send this boi into the queue chatmessage_queue.enqueue(p_contents); @@ -2014,7 +2018,7 @@ void Courtroom::skip_chatmessage_queue() while (!chatmessage_queue.isEmpty()) { QStringList p_contents = chatmessage_queue.dequeue(); - log_chatmessage(p_contents[MESSAGE], p_contents[CHAR_ID].toInt(), p_contents[SHOWNAME], p_contents[TEXT_COLOR].toInt(), DISPLAY_ONLY); + log_chatmessage(p_contents[MESSAGE], p_contents[CHAR_ID].toInt(), p_contents[SHOWNAME], p_contents[CHAR_NAME], p_contents[OBJECTION_MOD], p_contents[EVIDENCE_ID].toInt(), p_contents[TEXT_COLOR].toInt(), DISPLAY_ONLY); } } @@ -2037,7 +2041,7 @@ void Courtroom::unpack_chatmessage(QStringList p_contents) if (!ao_app->is_desyncrhonized_logs_enabled()) { // We have logs displaying as soon as we reach the message in our queue, which is a less confusing but also less accurate experience for the user. - log_chatmessage(m_chatmessage[MESSAGE], m_chatmessage[CHAR_ID].toInt(), m_chatmessage[SHOWNAME], m_chatmessage[TEXT_COLOR].toInt(), DISPLAY_ONLY); + log_chatmessage(m_chatmessage[MESSAGE], m_chatmessage[CHAR_ID].toInt(), m_chatmessage[SHOWNAME], m_chatmessage[CHAR_NAME], m_chatmessage[OBJECTION_MOD], m_chatmessage[EVIDENCE_ID].toInt(), m_chatmessage[TEXT_COLOR].toInt(), DISPLAY_ONLY); } // Process the callwords for this message @@ -2056,18 +2060,13 @@ void Courtroom::unpack_chatmessage(QStringList p_contents) handle_ic_message(); } -void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_showname, int f_color, LogMode f_log_mode) +void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_showname, QString f_char, QString f_objection_mod, int f_evi_id, int f_color, LogMode f_log_mode) { // Display name will use the showname QString f_displayname = f_showname; if (f_char_id != -1) { // Grab the char.ini showname f_showname = ao_app->get_showname(char_list.at(f_char_id).name); - // If custom serversided shownames are not enabled - if (!ui_showname_enable->isChecked()) { - // Set the display name to the char.ini showname - f_displayname = f_showname; - } } // If display name is just whitespace, use the char.ini showname. if (f_displayname.trimmed().isEmpty()) @@ -2077,16 +2076,15 @@ void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_show // Check if a custom objection is in use int objection_mod = 0; QString custom_objection = ""; - if (m_chatmessage[OBJECTION_MOD].contains("4&")) { + if (f_objection_mod.contains("4&")) { objection_mod = 4; - custom_objection = m_chatmessage[OBJECTION_MOD].split( + custom_objection = f_objection_mod.split( "4&")[1]; // takes the name of custom objection. } else { - objection_mod = m_chatmessage[OBJECTION_MOD].toInt(); + objection_mod = f_objection_mod.toInt(); } - QString f_char = m_chatmessage[CHAR_NAME]; QString f_custom_theme = ao_app->get_chat(f_char); if (objection_mod <= 4 && objection_mod >= 1) { QString shout_message; @@ -2133,8 +2131,6 @@ void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_show } } - // Obtain evidence ID we're trying to work with - int f_evi_id = m_chatmessage[EVIDENCE_ID].toInt(); // If the evidence ID is in the valid range if (f_evi_id > 0 && f_evi_id <= local_evidence_list.size()) { // Obtain the evidence name @@ -2170,6 +2166,8 @@ void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_show append_ic_text(f_message, f_displayname, "",f_color); break; } + if (!ui_showname_enable->isChecked()) + regenerate_ic_chatlog(); } bool Courtroom::handle_objection() @@ -2186,8 +2184,6 @@ bool Courtroom::handle_objection() objection_mod = m_chatmessage[OBJECTION_MOD].toInt(); } - - // if an objection is used if (objection_mod <= 4 && objection_mod >= 1) { ui_vp_objection->set_static_duration(shout_static_time); @@ -2231,8 +2227,10 @@ bool Courtroom::handle_objection() filename, m_chatmessage[CHAR_NAME], ao_app->get_chat(m_chatmessage[CHAR_NAME])); sfx_player->clear(); // Objection played! Cut all sfx. + ui_vp_player_char->set_play_once(true); return true; } + if (m_chatmessage[EMOTE] != "") display_character(); return false; } @@ -2391,22 +2389,31 @@ void Courtroom::handle_ic_message() // Update the chatbox information initialize_chatbox(); - // Display our own character - display_character(); - - // Reset the pair character - ui_vp_sideplayer_char->stop(); - ui_vp_sideplayer_char->move(0, 0); - - // If the emote_mod is not zooming int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); - if (emote_mod != 5 && emote_mod != 6) { - // Display the pair character - display_pair_character(m_chatmessage[OTHER_CHARID], m_chatmessage[OTHER_OFFSET]); - } + bool immediate = m_chatmessage[IMMEDIATE].toInt() == 1; + if (m_chatmessage[EMOTE] != "") { + // Display our own character + display_character(); - // Parse the emote_mod part of the chat message - handle_emote_mod(m_chatmessage[EMOTE_MOD].toInt(), m_chatmessage[IMMEDIATE].toInt() == 1); + // Reset the pair character + ui_vp_sideplayer_char->stop(); + ui_vp_sideplayer_char->move(0, 0); + + // If the emote_mod is not zooming + if (emote_mod != 5 && emote_mod != 6) { + // Display the pair character + display_pair_character(m_chatmessage[OTHER_CHARID], m_chatmessage[OTHER_OFFSET]); + } + + // Parse the emote_mod part of the chat message + handle_emote_mod(emote_mod, immediate); + } + else + { + start_chat_ticking(); + if (emote_mod == 1 || emote_mod == 2 || emote_mod == 6 || immediate) + play_sfx(); + } // if we have instant objections disabled, and queue is not empty, check if next message after this is an objection. if (!ao_app->is_instant_objection_enabled() && chatmessage_queue.size() > 0) @@ -2523,7 +2530,6 @@ void Courtroom::initialize_chatbox() if (f_charid >= 0 && f_charid < char_list.size() && (m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked())) { QString real_name = char_list.at(f_charid).name; - ui_vp_player_char->set_static_duration(0); QString f_showname = ao_app->get_showname(real_name); ui_vp_showname->setText(f_showname); @@ -2671,8 +2677,6 @@ void Courtroom::display_evidence_image() void Courtroom::handle_ic_speaking() { - // Display the evidence - display_evidence_image(); QString side = m_chatmessage[SIDE]; int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); // emote_mod 5 is zoom and emote_mod 6 is zoom w/ preanim. @@ -3182,6 +3186,9 @@ void Courtroom::play_preanim(bool immediate) void Courtroom::preanim_done() { + // Currently, someone's talking over us mid-preanim... + if (anim_state != 1 && anim_state != 4) + return; anim_state = 1; switch(m_chatmessage[DESK_MOD].toInt()) { case 4: @@ -3211,6 +3218,9 @@ void Courtroom::start_chat_ticking() if (text_state != 0) return; + // Display the evidence + display_evidence_image(); + if (m_chatmessage[EFFECTS] != "") { QStringList fx_list = m_chatmessage[EFFECTS].split("|"); QString fx = fx_list[0]; @@ -3242,6 +3252,10 @@ void Courtroom::start_chat_ticking() ui_vp_chatbox->show(); ui_vp_message->show(); } + else { + ui_vp_chatbox->hide(); + ui_vp_message->hide(); + } // If we're not already waiting on the next message, start the timer. We could be overriden if there's an objection planned. int delay = ao_app->stay_time(); if (delay > 0 && !text_queue_timer->isActive()) @@ -3299,26 +3313,31 @@ void Courtroom::chat_tick() if (tick_pos >= f_message.size()) { text_state = 2; - if (anim_state < 3) { - QStringList c_paths = { - ao_app->get_image_suffix(ao_app->get_character_path(m_chatmessage[CHAR_NAME], "(c)" + m_chatmessage[EMOTE])), - ao_app->get_image_suffix(ao_app->get_character_path(m_chatmessage[CHAR_NAME], "(c)/" + m_chatmessage[EMOTE])) - }; - // if there is a (c) animation for this emote and we haven't played it already - if (file_exists(ui_vp_player_char->find_image(c_paths)) &&(!c_played)) { - anim_state = 5; - ui_vp_player_char->set_play_once(true); - filename = "(c)" + m_chatmessage[EMOTE]; - c_played = true; + // Check if we're a narrator msg + if (m_chatmessage[EMOTE] != "") { + if (anim_state < 3) { + QStringList c_paths = { + ao_app->get_image_suffix(ao_app->get_character_path(m_chatmessage[CHAR_NAME], "(c)" + m_chatmessage[EMOTE])), + ao_app->get_image_suffix(ao_app->get_character_path(m_chatmessage[CHAR_NAME], "(c)/" + m_chatmessage[EMOTE])) + }; + // if there is a (c) animation for this emote and we haven't played it already + if (file_exists(ui_vp_player_char->find_image(c_paths)) &&(!c_played)) { + anim_state = 5; + ui_vp_player_char->set_play_once(true); + filename = "(c)" + m_chatmessage[EMOTE]; + c_played = true; + } + else { + anim_state = 3; + ui_vp_player_char->set_play_once(false); + filename = "(a)" + m_chatmessage[EMOTE]; + } + ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, + false); } - else { - anim_state = 3; - ui_vp_player_char->set_play_once(false); - filename = "(a)" + m_chatmessage[EMOTE]; - } - ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, - false); } + else // We're a narrator msg + anim_state = 3; QString f_char; QString f_custom_theme; if (ao_app->is_customchat_enabled()) { @@ -3361,21 +3380,8 @@ void Courtroom::chat_tick() // Alignment characters if (tick_pos < 2) { - if (f_rest.startsWith("~~")) { - tick_pos = f_rest.indexOf("~~"); - f_rest.remove(tick_pos, 2); + if (f_rest.startsWith("~~") || f_rest.startsWith("~>") || f_rest.startsWith("<>")) tick_pos += 2; - } - else if (f_rest.startsWith("~>")) { - tick_pos = f_rest.indexOf("~>"); - f_rest.remove(tick_pos, 2); - tick_pos += 2; - } - else if (f_rest.startsWith("<>")) { - tick_pos = f_rest.indexOf("<>"); - f_rest.remove(tick_pos, 2); - tick_pos += 2; - } } f_rest.remove(0, tick_pos); QTextBoundaryFinder tbf(QTextBoundaryFinder::Grapheme, f_rest); @@ -3519,28 +3525,30 @@ void Courtroom::chat_tick() msg_delay = qMin(max_delay, msg_delay * punctuation_modifier); } - // If this color is talking - if (color_is_talking && anim_state != 2 && - anim_state < - 4) // Set it to talking as we're not on that already (though we have - // to avoid interrupting a non-interrupted preanim) - { - ui_vp_player_char->stop(); - ui_vp_player_char->set_play_once(false); - filename = "(b)" + m_chatmessage[EMOTE]; - ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, - false); - anim_state = 2; - } - else if (!color_is_talking && anim_state < 3 && - anim_state != 3) // Set it to idle as we're not on that already - { - ui_vp_player_char->stop(); - ui_vp_player_char->set_play_once(false); - filename = "(a)" + m_chatmessage[EMOTE]; - ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, - false); - anim_state = 3; + if (m_chatmessage[EMOTE] != "") { + // If this color is talking + if (color_is_talking && anim_state != 2 && + anim_state < + 4) // Set it to talking as we're not on that already (though we have + // to avoid interrupting a non-interrupted preanim) + { + ui_vp_player_char->stop(); + ui_vp_player_char->set_play_once(false); + filename = "(b)" + m_chatmessage[EMOTE]; + ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, + false); + anim_state = 2; + } + else if (!color_is_talking && anim_state < 3 && + anim_state != 3) // Set it to idle as we're not on that already + { + ui_vp_player_char->stop(); + ui_vp_player_char->set_play_once(false); + filename = "(a)" + m_chatmessage[EMOTE]; + ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, + false); + anim_state = 3; + } } // Continue ticking chat_tick_timer->start(msg_delay); @@ -3891,14 +3899,8 @@ void Courtroom::case_called(QString msg, bool def, bool pro, bool jud, bool jur, { if (ui_casing->isChecked()) { ui_server_chatlog->append(msg); - if ((ao_app->get_casing_defence_enabled() && def) || - (ao_app->get_casing_prosecution_enabled() && pro) || - (ao_app->get_casing_judge_enabled() && jud) || - (ao_app->get_casing_juror_enabled() && jur) || - (ao_app->get_casing_steno_enabled() && steno)) { - modcall_player->play(ao_app->get_court_sfx("case_call")); - ao_app->alert(this); - } + modcall_player->play(ao_app->get_court_sfx("case_call")); + ao_app->alert(this); } } @@ -5260,8 +5262,6 @@ void Courtroom::on_reload_theme_clicked() enter_courtroom(); gen_char_rgb_list(ao_app->get_chat(current_char)); - anim_state = 4; - text_state = 3; objection_custom = ""; // to update status on the background diff --git a/src/demoserver.cpp b/src/demoserver.cpp index b9721bf..5a0d579 100644 --- a/src/demoserver.cpp +++ b/src/demoserver.cpp @@ -275,6 +275,56 @@ void DemoServer::load_demo(QString filename) demo_data.enqueue(line); line = demo_stream.readLine(); } + demo_file.flush(); + demo_file.close(); + + // No-shenanigans 2.9.0 demo file with the dreaded demo desync bug detected https://github.com/AttorneyOnline/AO2-Client/pull/496 + // If we don't start with the SC packet this means user-edited weirdo shenanigans. Don't screw around with those. + if (demo_data.head().startsWith("SC#") && demo_data.last().startsWith("wait#")) { + qDebug() << "Loaded a broken pre-2.9.1 demo file, with the wait desync issue!"; + QMessageBox *msgBox = new QMessageBox; + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->setTextFormat(Qt::RichText); + msgBox->setText("This appears to be a broken pre-2.9.1 demo file with the wait desync issue!
Do you want to correct this file? If you refuse, this demo will be desynchronized!"); + msgBox->setWindowTitle("Pre-2.9.1 demo detected!"); + msgBox->setStandardButtons(QMessageBox::NoButton); + QTimer::singleShot(2000, msgBox, std::bind(&QMessageBox::setStandardButtons,msgBox,QMessageBox::Yes|QMessageBox::No)); + int ret = msgBox->exec(); + QQueue p_demo_data; + switch (ret) { + case QMessageBox::Yes: + qDebug() << "Making a backup of the broken demo..."; + QFile::copy(filename, filename + ".backup"); + while (!demo_data.isEmpty()) { + QString current_packet = demo_data.dequeue(); + // TODO: faster way of doing this, maybe with QtConcurrent's MapReduce methods? + if (!current_packet.startsWith("SC#") && current_packet.startsWith("wait#")) { + p_demo_data.insert(qMax(1, p_demo_data.size()-1), current_packet); + continue; + } + p_demo_data.enqueue(current_packet); + } + if (demo_file.open(QIODevice::WriteOnly | QIODevice::Text | + QIODevice::Truncate)) { + QTextStream out(&demo_file); + out.setCodec("UTF-8"); + out << p_demo_data.dequeue(); + for (QString line : p_demo_data) { + out << "\n" << line; + } + demo_file.flush(); + demo_file.close(); + } + load_demo(filename); + break; + case QMessageBox::No: + // No was clicked + break; + default: + // should never be reached + break; + } + } } void DemoServer::reset_all_timers() @@ -301,22 +351,26 @@ void DemoServer::playback() if (current_packet.startsWith("MS#")) elapsed_time = 0; - while (!current_packet.startsWith("wait") && !demo_data.isEmpty()) { + while (!current_packet.startsWith("wait#")) { client_sock->write(current_packet.toUtf8()); + if (demo_data.isEmpty()) + break; current_packet = demo_data.dequeue(); } if (!demo_data.isEmpty()) { AOPacket wait_packet = AOPacket(current_packet); int duration = wait_packet.get_contents().at(0).toInt(); - if (max_wait != -1 && duration + elapsed_time > max_wait) { - duration = qMax(0, max_wait - elapsed_time); - // Skip the difference on the timers - emit skip_timers(wait_packet.get_contents().at(0).toInt() - duration); - } - else if (timer->interval() != 0 && duration + elapsed_time > timer->interval()) { - duration = qMax(0, timer->interval() - elapsed_time); + if (max_wait != -1) { + if (duration + elapsed_time > max_wait) { + duration = qMax(0, max_wait - elapsed_time); + // Skip the difference on the timers emit skip_timers(wait_packet.get_contents().at(0).toInt() - duration); + } + else if (timer->interval() != 0 && duration + elapsed_time > timer->interval()) { + duration = qMax(0, timer->interval() - elapsed_time); + emit skip_timers(wait_packet.get_contents().at(0).toInt() - duration); + } } elapsed_time += duration; timer->start(duration); diff --git a/src/packet_distribution.cpp b/src/packet_distribution.cpp index cf51002..dc76ec1 100644 --- a/src/packet_distribution.cpp +++ b/src/packet_distribution.cpp @@ -308,7 +308,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet) this->log_filename = QDateTime::currentDateTime().toUTC().toString( "'logs/" + server_name.remove(QRegExp("[\\\\/:*?\"<>|\']")) + "/'yyyy-MM-dd hh-mm-ss t'.log'"); - this->write_to_file("Joined server " + server_name + " on address " + + this->write_to_file("Joined server " + server_name + " hosted on address " + server_address + " on " + QDateTime::currentDateTime().toUTC().toString(), log_filename, true); diff --git a/src/path_functions.cpp b/src/path_functions.cpp index 5107349..dbf1cc4 100644 --- a/src/path_functions.cpp +++ b/src/path_functions.cpp @@ -105,7 +105,6 @@ QString AOApplication::get_evidence_path(QString p_file) QStringList AOApplication::get_asset_paths(QString p_element, QString p_theme, QString p_subtheme, QString p_default_theme, QString p_misc, QString p_character, QString p_placeholder) { QStringList pathlist; - pathlist += p_element; // The path by itself if (p_character != "") pathlist += get_character_path(p_character, p_element); // Character folder if (p_misc != "" && p_theme != "" && p_subtheme != "") @@ -120,6 +119,7 @@ QStringList AOApplication::get_asset_paths(QString p_element, QString p_theme, Q pathlist += get_theme_path(p_element, p_theme); // Theme path if (p_default_theme != "") pathlist += get_theme_path(p_element, p_default_theme); // Default theme path + pathlist += p_element; // The path by itself if (p_placeholder != "" && p_theme != "") pathlist += get_theme_path(p_placeholder, p_theme); // Placeholder path if (p_placeholder != "" && p_default_theme != "") @@ -173,6 +173,7 @@ QString AOApplication::get_config_value(QString p_identifier, QString p_config, p = get_case_sensitive_path(p); if (file_exists(p)) { QSettings settings(p, QSettings::IniFormat); + settings.setIniCodec("UTF-8"); QVariant value = settings.value(p_identifier); if (value.type() == QVariant::StringList) { // qDebug() << "got" << p << "is a string list, returning" << value.toStringList().join(","); diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp index a671d61..af4c232 100644 --- a/src/text_file_functions.cpp +++ b/src/text_file_functions.cpp @@ -277,6 +277,7 @@ QString AOApplication::read_design_ini(QString p_identifier, QString p_design_path) { QSettings settings(p_design_path, QSettings::IniFormat); + settings.setIniCodec("UTF-8"); QVariant value = settings.value(p_identifier); if (value.type() == QVariant::StringList) { return value.toStringList().join(","); @@ -435,7 +436,7 @@ QString AOApplication::get_chat_markup(QString p_identifier, QString p_chat) // New Chadly method QString value = get_config_value(p_identifier, "chat_config.ini", current_theme, get_subtheme(), default_theme, p_chat); if (!value.isEmpty()) - return value.toLatin1(); + return value.toUtf8(); // Backwards ass compatibility QStringList backwards_paths{get_theme_path("misc/" + p_chat + "/config.ini"), @@ -446,7 +447,7 @@ QString AOApplication::get_chat_markup(QString p_identifier, QString p_chat) for (const QString &p : backwards_paths) { QString value = read_design_ini(p_identifier, p); if (!value.isEmpty()) { - return value.toLatin1(); + return value.toUtf8(); } } @@ -521,6 +522,7 @@ QString AOApplication::read_char_ini(QString p_char, QString p_search_line, QSettings settings(get_character_path(p_char, "char.ini"), QSettings::IniFormat); settings.beginGroup(target_tag); + settings.setIniCodec("UTF-8"); QString value = settings.value(p_search_line).value(); settings.endGroup(); return value; @@ -541,6 +543,7 @@ QStringList AOApplication::read_ini_tags(QString p_path, QString target_tag) { QStringList r_values; QSettings settings(p_path, QSettings::IniFormat); + settings.setIniCodec("UTF-8"); if (!target_tag.isEmpty()) settings.beginGroup(target_tag); QStringList keys = settings.allKeys();