diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..0e1e98c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Version:** + - OS: (e.g. Windows 10) + - Version or branch: (e.g. 2.8.5, master, ui-files, etc.) + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/.travis.yml b/.travis.yml index 48bc2b5..802ace9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,6 @@ language: cpp os: osx +osx_image: xcode12 addons: homebrew: update: true diff --git a/Attorney_Online.pro b/Attorney_Online.pro index d2c2062..5ee3f40 100644 --- a/Attorney_Online.pro +++ b/Attorney_Online.pro @@ -14,6 +14,10 @@ SOURCES += $$files($$PWD/src/*.cpp) HEADERS += $$files($$PWD/include/*.h) LIBS += -L$$PWD/lib +QMAKE_LFLAGS += -Wl,-rpath,"'\$$ORIGIN/lib'" + +# Uncomment for verbose network logging +# DEFINES += DEBUG_NETWORK # Uncomment to enable Discord Rich Presence # DEFINES += DISCORD @@ -31,7 +35,7 @@ LIBS += -lbassopus macx:LIBS += -framework CoreFoundation -framework Foundation -framework CoreServices -CONFIG += c++14 +CONFIG += c++17 RESOURCES += resources.qrc diff --git a/include/aoapplication.h b/include/aoapplication.h index 458a884..b17ac4a 100644 --- a/include/aoapplication.h +++ b/include/aoapplication.h @@ -67,14 +67,10 @@ public: /////////////////server metadata////////////////// - unsigned int s_decryptor = 5; - bool encryption_needed = true; - bool yellow_text_enabled = false; bool prezoom_enabled = false; bool flipping_enabled = false; bool custom_objection_enabled = false; - bool improved_loading_enabled = false; bool desk_mod_enabled = false; bool evidence_enabled = false; bool cccc_ic_support_enabled = false; @@ -84,6 +80,8 @@ public: bool looping_sfx_support_enabled = false; bool additive_enabled = false; bool effects_enabled = false; + bool y_offset_enabled = false; + bool expanded_desk_mods_enabled = false; ///////////////loading info/////////////////// @@ -227,6 +225,9 @@ public: // Returns whether the log should have a timestamp. bool get_log_timestamp(); + // Returns whether to log IC actions. + bool get_log_ic_actions(); + // Returns the username the user may have set in config.ini. QString get_default_username(); @@ -407,8 +408,8 @@ public: // Returns the desk modifier for p_char's p_emote int get_desk_mod(QString p_char, int p_emote); - // Returns p_char's gender - QString get_gender(QString p_char); + // Returns p_char's blips (previously called their "gender") + QString get_blips(QString p_char); // ====== // These are all casing-related settings. diff --git a/include/aoblipplayer.h b/include/aoblipplayer.h index 4d3b5f1..a11e29d 100644 --- a/include/aoblipplayer.h +++ b/include/aoblipplayer.h @@ -22,12 +22,9 @@ public: int m_cycle = 0; private: - const int max_blip_ms = 60; - QWidget *m_parent; AOApplication *ao_app; qreal m_volume; - QElapsedTimer delay; void set_volume_internal(qreal p_volume); diff --git a/include/aooptionsdialog.h b/include/aooptionsdialog.h index fe99626..2e2d7b8 100644 --- a/include/aooptionsdialog.h +++ b/include/aooptionsdialog.h @@ -52,6 +52,8 @@ private: QSpinBox *ui_log_margin_spinbox; QLabel *ui_log_timestamp_lbl; QCheckBox *ui_log_timestamp_cb; + QLabel *ui_log_ic_actions_lbl; + QCheckBox *ui_log_ic_actions_cb; QFrame *ui_log_names_divider; QLineEdit *ui_username_textbox; QLabel *ui_username_lbl; diff --git a/include/aopacket.h b/include/aopacket.h index 4097be8..6d1deba 100644 --- a/include/aopacket.h +++ b/include/aopacket.h @@ -8,21 +8,16 @@ class AOPacket { public: AOPacket(QString p_packet_string); - AOPacket(QString header, QStringList &p_contents); - ~AOPacket(); + AOPacket(QString header, QStringList &p_contents) : m_header(header), m_contents(p_contents){} QString get_header() { return m_header; } QStringList &get_contents() { return m_contents; } QString to_string(); - void encrypt_header(unsigned int p_key); - void decrypt_header(unsigned int p_key); - void net_encode(); void net_decode(); private: - bool encrypted = false; QString m_header; QStringList m_contents; diff --git a/include/aosfxplayer.h b/include/aosfxplayer.h index 0a5fffa..7597636 100644 --- a/include/aosfxplayer.h +++ b/include/aosfxplayer.h @@ -16,8 +16,7 @@ public: void clear(); void loop_clear(); - void play(QString p_sfx, QString p_char = "", QString shout = "", - int channel = -1); + void play(QString p_sfx, QString p_char = "", QString shout = ""); void stop(int channel = -1); void set_volume(qreal p_volume); void set_looping(bool toggle, int channel = -1); diff --git a/include/courtroom.h b/include/courtroom.h index 72b4989..e0db719 100644 --- a/include/courtroom.h +++ b/include/courtroom.h @@ -59,6 +59,7 @@ #include #include +#include #include class AOApplication; @@ -175,6 +176,9 @@ public: // sets desk and bg based on pos in chatmessage void set_scene(QString f_desk_mod, QString f_side); + // sets ui_vp_player_char according to SELF_OFFSET, only a function bc it's used with desk_mod 4 and 5 + void set_self_offset(QString p_list); + // takes in serverD-formatted IP list as prints a converted version to server // OOC admittedly poorly named void set_ip_list(QString p_list); @@ -226,14 +230,14 @@ public: // selected // or the user isn't already scrolled to the top void append_ic_text(QString p_text, QString p_name = "", QString action = "", - int color = 0); + int color = 0, QDateTime timestamp = QDateTime::currentDateTime()); // prints who played the song to IC chat and plays said song(if found on local // filesystem) takes in a list where the first element is the song name and // the second is the char id of who played it void handle_song(QStringList *p_contents); - void play_preanim(bool noninterrupting); + void play_preanim(bool immediate); // plays the witness testimony or cross examination animation based on // argument @@ -259,6 +263,8 @@ public: void set_clock_visibility(bool visible); qint64 pong(); + // Truncates text so it fits within theme-specified boundaries and sets the tooltip to the full string + void truncate_label_text(QWidget* p_widget, QString p_identifier); ~Courtroom(); private: @@ -284,14 +290,17 @@ private: bool message_is_centered = false; int current_display_speed = 3; - int message_display_speed[7] = {0, 10, 25, 40, 50, 70, 90}; + int message_display_speed[7] = {5, 10, 25, 40, 50, 70, 90}; // The character ID of the character this user wants to appear alongside with. int other_charid = -1; - // The offset this user has given if they want to appear alongside someone. + // The horizontal offset this user has given if they want to appear alongside someone. int char_offset = 0; + // The vertical offset this user has given. + int char_vert_offset = 0; + // 0 = in front, 1 = behind int pair_order = 0; @@ -325,7 +334,7 @@ private: int real_tick_pos = 0; // used to determine how often blips sound int blip_ticker = 0; - int blip_rate = 1; + int blip_rate = 2; int rainbow_counter = 0; bool rainbow_appended = false; bool blank_blip = false; @@ -342,6 +351,9 @@ private: // True, if the log should display the message like name
text instead of name: text bool log_newline = false; + // True, if the log should include RP actions like interjections, showing evidence, etc. + bool log_ic_actions = true; + // Margin in pixels between log entries for the IC log. int log_margin = 0; @@ -408,6 +420,11 @@ private: int objection_state = 0; QString objection_custom = ""; + struct CustomObjection { + QString name; + QString filename; + }; + QList custom_objections_list; int realization_state = 0; int screenshake_state = 0; int text_color = 0; @@ -422,6 +439,17 @@ private: // List of associated RGB colors for this color index QVector color_rgb_list; + // Same as above but populated from misc/default's config + QVector default_color_rgb_list; + + // Get a color index from an arbitrary misc config + void gen_char_rgb_list(QString p_char); + QVector char_color_rgb_list; + + // Misc we used for the last message, and the one we're using now. Used to avoid loading assets when it's not needed + QString current_misc; + QString last_misc; + // List of markdown start characters, their index is tied to the color index QStringList color_markdown_start_list; @@ -438,8 +466,12 @@ private: // List of all currently available pos QStringList pos_dropdown_list; + // is the message we're about to send supposed to present evidence? bool is_presenting_evidence = false; + // have we already presented evidence for this message? + bool evidence_presented = false; + QString effect = ""; // Music effect flags we want to send to server when we play music @@ -536,6 +568,7 @@ private: AOButton *ui_pair_button; QListWidget *ui_pair_list; QSpinBox *ui_pair_offset_spinbox; + QSpinBox *ui_pair_vert_offset_spinbox; QComboBox *ui_pair_order_dropdown; @@ -595,7 +628,7 @@ private: QCheckBox *ui_guard; QCheckBox *ui_casing; - QCheckBox *ui_pre_non_interrupt; + QCheckBox *ui_immediate; QCheckBox *ui_showname_enable; AOButton *ui_custom_objection; @@ -717,6 +750,7 @@ private slots: void music_random(); void music_list_expand_all(); void music_list_collapse_all(); + void music_stop(); void on_area_list_double_clicked(QTreeWidgetItem *p_item, int column); void select_emote(int p_id); @@ -790,6 +824,7 @@ private slots: void on_log_limit_changed(int value); void on_pair_offset_changed(int value); + void on_pair_vert_offset_changed(int value); void on_ooc_toggle_clicked(); diff --git a/include/datatypes.h b/include/datatypes.h index 21ade04..ebf2e38 100644 --- a/include/datatypes.h +++ b/include/datatypes.h @@ -91,7 +91,7 @@ enum CHAT_MESSAGE { SELF_OFFSET, OTHER_OFFSET, OTHER_FLIP, - NONINTERRUPTING_PRE, + IMMEDIATE, LOOPING_SFX, SCREENSHAKE, FRAME_SCREENSHAKE, diff --git a/include/encryption_functions.h b/include/encryption_functions.h deleted file mode 100644 index b70e8e6..0000000 --- a/include/encryption_functions.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef ENCRYPTION_FUNCTIONS_H -#define ENCRYPTION_FUNCTIONS_H - -#include - -#include -#include -#include -#include -#include - -QString fanta_encrypt(QString p_input, unsigned int key); -QString fanta_decrypt(QString p_input, unsigned int key); - -#endif // ENCRYPTION_FUNCTIONS_H diff --git a/include/hex_functions.h b/include/hex_functions.h deleted file mode 100644 index d178ba1..0000000 --- a/include/hex_functions.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef HEX_OPERATIONS_H -#define HEX_OPERATIONS_H - -#include -#include -#include -#include -#include -#include -#include - -namespace omni { -std::string int_to_hex(unsigned int input); -} - -#endif // HEX_OPERATIONS_H diff --git a/include/lobby.h b/include/lobby.h index 42f5297..4278534 100644 --- a/include/lobby.h +++ b/include/lobby.h @@ -46,6 +46,7 @@ public: void set_loading_value(int p_value); bool public_servers_selected = true; + bool doubleclicked = false; ~Lobby(); diff --git a/scripts/configure_ubuntu.sh b/scripts/configure_ubuntu.sh index 280dfdf..b010d36 100755 --- a/scripts/configure_ubuntu.sh +++ b/scripts/configure_ubuntu.sh @@ -20,10 +20,13 @@ cd tmp #get the bass prebuilt curl http://www.un4seen.com/files/bass24-linux.zip -o bass_linux.zip +curl http://www.un4seen.com/files/bassopus24-linux.zip -o bassopus_linux.zip unzip bass_linux.zip +unzip bassopus_linux.zip cp x64/libbass.so ../../lib +cp x64/libbassopus.so ../../lib #get the discord-rpc prebuilt curl -L https://github.com/discordapp/discord-rpc/releases/download/v3.4.0/discord-rpc-linux.zip -o discord_rpc_linux.zip diff --git a/src/aoblipplayer.cpp b/src/aoblipplayer.cpp index 5b4d625..6607d46 100644 --- a/src/aoblipplayer.cpp +++ b/src/aoblipplayer.cpp @@ -26,10 +26,6 @@ void AOBlipPlayer::set_blips(QString p_sfx) void AOBlipPlayer::blip_tick() { - if (delay.isValid() && delay.elapsed() < max_blip_ms) - return; - - delay.start(); int f_cycle = m_cycle++; if (m_cycle == 5) diff --git a/src/aocaseannouncerdialog.cpp b/src/aocaseannouncerdialog.cpp index d91433a..aea6405 100644 --- a/src/aocaseannouncerdialog.cpp +++ b/src/aocaseannouncerdialog.cpp @@ -3,7 +3,7 @@ AOCaseAnnouncerDialog::AOCaseAnnouncerDialog(QWidget *parent, AOApplication *p_ao_app, Courtroom *p_court) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint) { ao_app = p_ao_app; court = p_court; diff --git a/src/aocharmovie.cpp b/src/aocharmovie.cpp index 252aab5..09a4b88 100644 --- a/src/aocharmovie.cpp +++ b/src/aocharmovie.cpp @@ -35,8 +35,8 @@ void AOCharMovie::load_image(QString p_char, QString p_emote, ao_app->get_image_suffix(ao_app->get_character_path( p_char, emote_prefix + "/" + p_emote)), // Path check if it's categorized into a folder - ao_app->get_character_path( - p_char, p_emote + ".png"), // Non-animated path if emote_prefix fails + ao_app->get_image_suffix(ao_app->get_character_path( + p_char, p_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_default_theme_path( @@ -61,6 +61,15 @@ void AOCharMovie::load_image(QString p_char, QString p_emote, return; m_reader->setFileName(emote_path); + + // set format to apng if png supports animation + if (emote_path.endsWith("png")) { + m_reader->setFormat("apng"); + if (!m_reader->supportsAnimation()) { + m_reader->setFormat("png"); + } + } + QPixmap f_pixmap = this->get_pixmap(m_reader->read()); int f_delay = m_reader->nextImageDelay(); diff --git a/src/aoemotebutton.cpp b/src/aoemotebutton.cpp index 9053560..652010d 100644 --- a/src/aoemotebutton.cpp +++ b/src/aoemotebutton.cpp @@ -27,6 +27,7 @@ void AOEmoteButton::set_image(QString p_image, QString p_emote_comment) } else if (p_image.contains("_on") && file_exists(tmp_p_image.replace("_on", "_off"))) { QImage tmpImage(tmp_p_image); + tmpImage = tmpImage.convertToFormat(QImage::Format_ARGB32); QPoint p1, p2; p2.setY(tmpImage.height()); diff --git a/src/aoimage.cpp b/src/aoimage.cpp index 2663ba0..3977c97 100644 --- a/src/aoimage.cpp +++ b/src/aoimage.cpp @@ -2,6 +2,8 @@ #include "aoimage.h" +#include + AOImage::AOImage(QWidget *parent, AOApplication *p_ao_app) : QLabel(parent) { m_parent = parent; @@ -29,9 +31,10 @@ bool AOImage::set_image(QString p_image) } QPixmap f_pixmap(final_image_path); - - this->setPixmap( - f_pixmap.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio)); + f_pixmap = + f_pixmap.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio); + this->setPixmap(f_pixmap); + this->setMask(f_pixmap.mask()); return true; } @@ -45,7 +48,9 @@ bool AOImage::set_chatbox(QString p_path) QPixmap f_pixmap(p_path); - this->setPixmap( - f_pixmap.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio)); + f_pixmap = + f_pixmap.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio); + this->setPixmap(f_pixmap); + this->setMask(f_pixmap.mask()); return true; } diff --git a/src/aomovie.cpp b/src/aomovie.cpp index ac94921..196c1d3 100644 --- a/src/aomovie.cpp +++ b/src/aomovie.cpp @@ -9,6 +9,7 @@ AOMovie::AOMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) ao_app = p_ao_app; m_movie = new QMovie(); + m_movie->setCacheMode(QMovie::CacheAll); this->setMovie(m_movie); diff --git a/src/aomusicplayer.cpp b/src/aomusicplayer.cpp index 0c50d50..6219edf 100644 --- a/src/aomusicplayer.cpp +++ b/src/aomusicplayer.cpp @@ -23,14 +23,26 @@ void AOMusicPlayer::play(QString p_song, int channel, bool loop, unsigned int flags = BASS_STREAM_PRESCAN | BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE; - if (loop) + unsigned int streaming_flags = BASS_STREAM_AUTOFREE; + if (loop) { flags |= BASS_SAMPLE_LOOP; + streaming_flags |= BASS_SAMPLE_LOOP; + } DWORD newstream; - if (f_path.endsWith(".opus")) - newstream = BASS_OPUS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags); - else - newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags); + if (f_path.startsWith("http")) { + if (f_path.endsWith(".opus")) + newstream = BASS_OPUS_StreamCreateURL(f_path.toStdString().c_str(), 0, streaming_flags, nullptr, 0); + else + newstream = BASS_StreamCreateURL(f_path.toStdString().c_str(), 0, streaming_flags, nullptr, 0); + + } else { + if (f_path.endsWith(".opus")) + newstream = BASS_OPUS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags); + else + newstream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, flags); + } + if (ao_app->get_audio_output_device() != "default") BASS_ChannelSetDevice(m_stream_list[channel], BASS_GetDevice()); diff --git a/src/aooptionsdialog.cpp b/src/aooptionsdialog.cpp index 6fc4d03..314e982 100644 --- a/src/aooptionsdialog.cpp +++ b/src/aooptionsdialog.cpp @@ -3,7 +3,7 @@ #include "bass.h" AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) - : QDialog(parent) + : QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint) { ao_app = p_ao_app; @@ -41,7 +41,7 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) // Let's add the tabs one by one. // First, we'll start with 'Gameplay'. - ui_gameplay_tab = new QWidget(); + ui_gameplay_tab = new QWidget(this); ui_gameplay_tab->setSizePolicy(sizePolicy1); ui_settings_tabs->addTab(ui_gameplay_tab, tr("Gameplay")); ui_form_layout_widget = new QWidget(ui_gameplay_tab); @@ -163,6 +163,19 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_log_timestamp_cb); + row += 1; + ui_log_ic_actions_lbl = new QLabel(ui_form_layout_widget); + ui_log_ic_actions_lbl->setText(tr("Log IC actions:")); + ui_log_ic_actions_lbl->setToolTip( + tr("If ticked, log will show IC actions such as shouting and presenting evidence.")); + + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_log_ic_actions_lbl); + + ui_log_ic_actions_cb = new QCheckBox(ui_form_layout_widget); + ui_log_ic_actions_cb->setChecked(p_ao_app->get_log_ic_actions()); + + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_log_ic_actions_cb); + row += 1; ui_log_names_divider = new QFrame(ui_form_layout_widget); ui_log_names_divider->setFrameShape(QFrame::HLine); @@ -372,14 +385,14 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_customchat_cb); - QScrollArea *scroll = new QScrollArea; + QScrollArea *scroll = new QScrollArea(this); scroll->setWidget(ui_form_layout_widget); ui_gameplay_tab->setLayout(new QVBoxLayout); ui_gameplay_tab->layout()->addWidget(scroll); ui_gameplay_tab->show(); // Here we start the callwords tab. - ui_callwords_tab = new QWidget(); + ui_callwords_tab = new QWidget(this); ui_settings_tabs->addTab(ui_callwords_tab, tr("Callwords")); ui_callwords_widget = new QWidget(ui_callwords_tab); @@ -416,7 +429,7 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_callwords_layout->addWidget(ui_callwords_explain_lbl); // The audio tab. - ui_audio_tab = new QWidget(); + ui_audio_tab = new QWidget(this); ui_settings_tabs->addTab(ui_audio_tab, tr("Audio")); ui_audio_widget = new QWidget(ui_audio_tab); @@ -577,7 +590,7 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_objectmusic_cb); // The casing tab! - ui_casing_tab = new QWidget(); + ui_casing_tab = new QWidget(this); ui_settings_tabs->addTab(ui_casing_tab, tr("Casing")); ui_casing_widget = new QWidget(ui_casing_tab); @@ -756,12 +769,16 @@ void AOOptionsDialog::save_pressed() // Save everything into the config.ini. QSettings *configini = ao_app->configini; + const bool audioChanged = ui_audio_device_combobox->currentText() != + ao_app->get_audio_output_device(); + configini->setValue("theme", ui_theme_combobox->currentText()); configini->setValue("log_goes_downwards", ui_downwards_cb->isChecked()); configini->setValue("log_maximum", ui_length_spinbox->value()); configini->setValue("log_newline", ui_log_newline_cb->isChecked()); configini->setValue("log_margin", ui_log_margin_spinbox->value()); configini->setValue("log_timestamp", ui_log_timestamp_cb->isChecked()); + configini->setValue("log_ic_actions", ui_log_ic_actions_cb->isChecked()); configini->setValue("default_username", ui_username_textbox->text()); configini->setValue("show_custom_shownames", ui_showname_cb->isChecked()); configini->setValue("master", ui_ms_textbox->text()); @@ -806,7 +823,9 @@ void AOOptionsDialog::save_pressed() configini->setValue("casing_can_host_cases", ui_casing_cm_cases_textbox->text()); - ao_app->initBASS(); + if (audioChanged) + ao_app->initBASS(); + callwordsini->close(); done(0); } diff --git a/src/aopacket.cpp b/src/aopacket.cpp index 6afd39e..bb6ac73 100644 --- a/src/aopacket.cpp +++ b/src/aopacket.cpp @@ -1,80 +1,30 @@ #include "aopacket.h" -#include "encryption_functions.h" - AOPacket::AOPacket(QString p_packet_string) { QStringList packet_contents = p_packet_string.split("#"); - m_header = packet_contents.at(0); - - for (int n_string = 1; n_string < packet_contents.size() - 1; ++n_string) { - m_contents.append(packet_contents.at(n_string)); - } + m_header = packet_contents.first(); + m_contents = packet_contents.mid(1, packet_contents.size()-2); // trims % } -AOPacket::AOPacket(QString p_header, QStringList &p_contents) -{ - m_header = p_header; - m_contents = p_contents; -} - -AOPacket::~AOPacket() {} - QString AOPacket::to_string() { - QString f_string = m_header; - - for (QString i_string : m_contents) { - f_string += ("#" + i_string); - } - - f_string += "#%"; - - if (encrypted) - return "#" + f_string; - else - return f_string; -} - -void AOPacket::encrypt_header(unsigned int p_key) -{ - m_header = fanta_encrypt(m_header, p_key); - - encrypted = true; -} - -void AOPacket::decrypt_header(unsigned int p_key) -{ - m_header = fanta_decrypt(m_header, p_key); - - encrypted = false; + return m_header + "#" + m_contents.join("#") + "#%"; } void AOPacket::net_encode() { - for (int n_element = 0; n_element < m_contents.size(); ++n_element) { - QString f_element = m_contents.at(n_element); - f_element.replace("#", "") - .replace("%", "") - .replace("$", "") - .replace("&", ""); - - m_contents.removeAt(n_element); - m_contents.insert(n_element, f_element); - } + m_contents.replaceInStrings("#", "") + .replaceInStrings("%", "") + .replaceInStrings("$", "") + .replaceInStrings("&", ""); } void AOPacket::net_decode() { - for (int n_element = 0; n_element < m_contents.size(); ++n_element) { - QString f_element = m_contents.at(n_element); - f_element.replace("", "#") - .replace("", "%") - .replace("", "$") - .replace("", "&"); - - m_contents.removeAt(n_element); - m_contents.insert(n_element, f_element); - } + m_contents.replaceInStrings("", "#") + .replaceInStrings("", "%") + .replaceInStrings("", "$") + .replaceInStrings("", "&"); } diff --git a/src/aoscene.cpp b/src/aoscene.cpp index 594013a..78d69ac 100644 --- a/src/aoscene.cpp +++ b/src/aoscene.cpp @@ -7,6 +7,7 @@ AOScene::AOScene(QWidget *parent, AOApplication *p_ao_app) : QLabel(parent) m_parent = parent; ao_app = p_ao_app; m_movie = new QMovie(this); + m_movie->setCacheMode(QMovie::CacheAll); last_image = ""; } diff --git a/src/aosfxplayer.cpp b/src/aosfxplayer.cpp index 8c4f3c8..13de04f 100644 --- a/src/aosfxplayer.cpp +++ b/src/aosfxplayer.cpp @@ -24,24 +24,30 @@ void AOSfxPlayer::loop_clear() set_volume_internal(m_volume); } -void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout, - int channel) +void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout) { - if (channel == -1) { - if (BASS_ChannelIsActive(m_stream_list[channel]) == BASS_ACTIVE_PLAYING) - m_channel = (m_channel + 1) % m_channelmax; - channel = m_channel; + for (int i = 0; i < m_channelmax; ++i) { + if (BASS_ChannelIsActive(m_stream_list[i]) == BASS_ACTIVE_PLAYING) + m_channel = (i + 1) % m_channelmax; + else { + m_channel = i; + break; + } } - BASS_ChannelStop(m_stream_list[channel]); - QString misc_path = ""; QString char_path = ""; + QString theme_path = ""; QString sound_path = ao_app->get_sfx_suffix(ao_app->get_sounds_path(p_sfx)); - if (shout != "") + if (shout != "") { misc_path = ao_app->get_sfx_suffix(ao_app->get_base_path() + "misc/" + shout + "/" + p_sfx); + theme_path = ao_app->get_sfx_suffix(ao_app->get_theme_path(p_sfx)); + if (!file_exists(theme_path)) + theme_path = + ao_app->get_sfx_suffix(ao_app->get_default_theme_path(p_sfx)); + } if (p_char != "") char_path = ao_app->get_sfx_suffix(ao_app->get_character_path(p_char, p_sfx)); @@ -52,15 +58,17 @@ void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout, f_path = char_path; else if (file_exists(misc_path)) f_path = misc_path; + else if (shout != "" && file_exists(theme_path)) // only check here for shouts + f_path = theme_path; else f_path = sound_path; if (f_path.endsWith(".opus")) - m_stream_list[channel] = BASS_OPUS_StreamCreateFile( + m_stream_list[m_channel] = BASS_OPUS_StreamCreateFile( FALSE, f_path.utf16(), 0, 0, BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE); else - m_stream_list[channel] = BASS_StreamCreateFile( + m_stream_list[m_channel] = BASS_StreamCreateFile( FALSE, f_path.utf16(), 0, 0, BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE); @@ -74,7 +82,7 @@ void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout, } BASS_ChannelPlay(m_stream_list[m_channel], false); - BASS_ChannelSetSync(m_stream_list[channel], BASS_SYNC_DEV_FAIL, 0, + BASS_ChannelSetSync(m_stream_list[m_channel], BASS_SYNC_DEV_FAIL, 0, ao_app->BASSreset, 0); } diff --git a/src/charselect.cpp b/src/charselect.cpp index 8e0aab5..33cc517 100644 --- a/src/charselect.cpp +++ b/src/charselect.cpp @@ -7,6 +7,8 @@ void Courtroom::construct_char_select() { + this->setWindowFlags( (this->windowFlags() | Qt::CustomizeWindowHint) & ~Qt::WindowMaximizeButtonHint); + ui_char_select_background = new AOImage(this, ao_app); ui_char_buttons = new QWidget(ui_char_select_background); @@ -60,6 +62,9 @@ void Courtroom::construct_char_select() SLOT(on_char_passworded_clicked())); connect(ui_char_taken, SIGNAL(stateChanged(int)), this, SLOT(on_char_taken_clicked())); + + truncate_label_text(ui_char_taken, "char_taken"); + truncate_label_text(ui_char_passworded, "char_passworded"); } void Courtroom::set_char_select() @@ -72,10 +77,10 @@ void Courtroom::set_char_select() if (f_charselect.width < 0 || f_charselect.height < 0) { qDebug() << "W: did not find char_select width or height in " "courtroom_design.ini!"; - this->resize(714, 668); + this->setFixedSize(714, 668); } else - this->resize(f_charselect.width, f_charselect.height); + this->setFixedSize(f_charselect.width, f_charselect.height); ui_char_select_background->resize(f_charselect.width, f_charselect.height); ui_char_select_background->set_image("charselect_background"); @@ -131,7 +136,7 @@ void Courtroom::char_clicked(int n_char) qDebug() << "char_ini_path" << char_ini_path; if (!file_exists(char_ini_path)) { - call_notice("Could not find " + char_ini_path); + call_error("Could not find " + char_ini_path); return; } } @@ -146,8 +151,6 @@ void Courtroom::char_clicked(int n_char) else update_character(n_char); - enter_courtroom(); - if (n_char != -1) ui_ic_chat_name->setPlaceholderText(char_list.at(n_char).name); } diff --git a/src/courtroom.cpp b/src/courtroom.cpp index c2fcf9b..962c04f 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -3,6 +3,10 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() { ao_app = p_ao_app; + + this->setWindowFlags((this->windowFlags() | Qt::CustomizeWindowHint) & + ~Qt::WindowMaximizeButtonHint); + ao_app->initBASS(); qsrand(static_cast(QDateTime::currentMSecsSinceEpoch() / 1000)); @@ -210,9 +214,9 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_showname_enable->setChecked(ao_app->get_showname_enabled_by_default()); ui_showname_enable->setText(tr("Shownames")); - ui_pre_non_interrupt = new QCheckBox(this); - ui_pre_non_interrupt->setText(tr("No Interrupt")); - ui_pre_non_interrupt->hide(); + ui_immediate = new QCheckBox(this); + ui_immediate->setText(tr("No Interrupt")); + ui_immediate->hide(); ui_custom_objection = new AOButton(this, ao_app); ui_custom_objection->setContextMenuPolicy(Qt::CustomContextMenu); @@ -246,7 +250,10 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_pair_list = new QListWidget(this); ui_pair_offset_spinbox = new QSpinBox(this); ui_pair_offset_spinbox->setRange(-100, 100); - ui_pair_offset_spinbox->setSuffix(tr("% offset")); + ui_pair_offset_spinbox->setSuffix(tr("% x offset")); + ui_pair_vert_offset_spinbox = new QSpinBox(this); + ui_pair_vert_offset_spinbox->setRange(-100, 100); + ui_pair_vert_offset_spinbox->setSuffix(tr("% y offset")); ui_pair_order_dropdown = new QComboBox(this); ui_pair_order_dropdown->addItem(tr("To front")); @@ -390,6 +397,8 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() SLOT(on_pair_list_clicked(QModelIndex))); connect(ui_pair_offset_spinbox, SIGNAL(valueChanged(int)), this, SLOT(on_pair_offset_changed(int))); + connect(ui_pair_vert_offset_spinbox, SIGNAL(valueChanged(int)), this, + SLOT(on_pair_vert_offset_changed(int))); connect(ui_pair_order_dropdown, SIGNAL(currentIndexChanged(int)), this, SLOT(on_pair_order_dropdown_changed(int))); @@ -450,13 +459,13 @@ void Courtroom::set_widgets() if (f_courtroom.width < 0 || f_courtroom.height < 0) { qDebug() << "W: did not find courtroom width or height in " << filename; - this->resize(714, 668); + this->setFixedSize(714, 668); } else { m_courtroom_width = f_courtroom.width; m_courtroom_height = f_courtroom.height; - this->resize(f_courtroom.width, f_courtroom.height); + this->setFixedSize(f_courtroom.width, f_courtroom.height); } set_fonts(); @@ -472,14 +481,14 @@ void Courtroom::set_widgets() // if needed. if (ao_app->cccc_ic_support_enabled) { ui_pair_button->show(); - ui_pre_non_interrupt->show(); + ui_immediate->show(); ui_showname_enable->show(); ui_ic_chat_name->show(); ui_ic_chat_name->setEnabled(true); } else { ui_pair_button->hide(); - ui_pre_non_interrupt->hide(); + ui_immediate->hide(); ui_showname_enable->hide(); ui_ic_chat_name->hide(); ui_ic_chat_name->setEnabled(false); @@ -556,7 +565,11 @@ void Courtroom::set_widgets() log_maximum_blocks = ao_app->get_max_log_size(); - bool regenerate = log_goes_downwards != ao_app->get_log_goes_downwards() || log_colors != ao_app->is_colorlog_enabled() || log_newline != ao_app->get_log_newline() || log_margin != ao_app->get_log_margin() || log_timestamp != ao_app->get_log_timestamp(); + bool regenerate = log_goes_downwards != ao_app->get_log_goes_downwards() || + log_colors != ao_app->is_colorlog_enabled() || + log_newline != ao_app->get_log_newline() || + log_margin != ao_app->get_log_margin() || + log_timestamp != ao_app->get_log_timestamp(); log_goes_downwards = ao_app->get_log_goes_downwards(); log_colors = ao_app->is_colorlog_enabled(); log_newline = ao_app->get_log_newline(); @@ -567,7 +580,8 @@ void Courtroom::set_widgets() set_size_and_pos(ui_ic_chatlog, "ic_chatlog"); ui_ic_chatlog->setFrameShape(QFrame::NoFrame); - ui_ic_chatlog->setPlaceholderText(log_goes_downwards ? "▼ Log goes down ▼" : "▲ Log goes up ▲"); + ui_ic_chatlog->setPlaceholderText(log_goes_downwards ? "▼ Log goes down ▼" + : "▲ Log goes up ▲"); set_size_and_pos(ui_ms_chatlog, "ms_chatlog"); ui_ms_chatlog->setFrameShape(QFrame::NoFrame); @@ -585,12 +599,20 @@ void Courtroom::set_widgets() set_size_and_pos(ui_pair_offset_spinbox, "pair_offset_spinbox"); ui_pair_offset_spinbox->hide(); ui_pair_offset_spinbox->setToolTip( - tr("Change the percentage offset of your character's position from the " + tr("Change the horizontal percentage offset of your character's position " + "from the " + "center of the screen.")); + + set_size_and_pos(ui_pair_vert_offset_spinbox, "pair_vert_offset_spinbox"); + ui_pair_vert_offset_spinbox->hide(); + ui_pair_vert_offset_spinbox->setToolTip( + tr("Change the vertical percentage offset of your character's position " + "from the " "center of the screen.")); ui_pair_order_dropdown->hide(); set_size_and_pos(ui_pair_order_dropdown, "pair_order_dropdown"); - ui_pair_offset_spinbox->setToolTip( + ui_pair_order_dropdown->setToolTip( tr("Change the order of appearance for your character.")); set_size_and_pos(ui_pair_button, "pair_button"); @@ -684,7 +706,7 @@ void Courtroom::set_widgets() tr("Set an 'iniswap', or an alternative character folder to refer to " "from your current character.\n" "Edit by typing and pressing Enter, [X] to remove. This saves to your " - "base/characters//iniswaps.ini")); + "base/iniswaps.ini")); set_size_and_pos(ui_iniswap_remove, "iniswap_remove"); ui_iniswap_remove->setText("X"); @@ -706,11 +728,11 @@ void Courtroom::set_widgets() set_size_and_pos(ui_sfx_remove, "sfx_remove"); ui_sfx_remove->setText("X"); ui_sfx_remove->set_image("evidencex"); - ui_sfx_remove->setToolTip( - tr("Remove the currently selected iniswap from the list and return to " - "the original character folder.")); + ui_sfx_remove->setToolTip(tr("Remove the currently selected sound effect.")); ui_sfx_remove->hide(); + set_iniswap_dropdown(); + set_size_and_pos(ui_effects_dropdown, "effects_dropdown"); ui_effects_dropdown->setInsertPolicy(QComboBox::InsertAtBottom); ui_effects_dropdown->setToolTip( @@ -823,11 +845,23 @@ void Courtroom::set_widgets() ui_pre->setToolTip( tr("Play a single-shot animation as defined by the emote when checked.")); - set_size_and_pos(ui_pre_non_interrupt, "pre_no_interrupt"); - ui_pre_non_interrupt->setToolTip( + ui_immediate->setToolTip( tr("If preanim is checked, display the input text immediately as the " "animation plays concurrently.")); + design_ini_result = + ao_app->get_element_dimensions("immediate", "courtroom_design.ini"); + // If we don't have new-style naming, fall back to the old method + if (design_ini_result.width < 0 || design_ini_result.height < 0) { + set_size_and_pos(ui_immediate, "pre_no_interrupt"); + truncate_label_text(ui_immediate, "pre_no_interrupt"); + } + else {// Adopt the based new method instead + set_size_and_pos(ui_immediate, "immediate"); + truncate_label_text(ui_immediate, "immediate"); + } + + set_size_and_pos(ui_flip, "flip"); ui_flip->setToolTip(tr("Mirror your character's emotes when checked.")); @@ -922,6 +956,17 @@ void Courtroom::set_widgets() ui_spectator->setToolTip(tr("Become a spectator. You won't be able to " "interact with the in-character screen.")); + // QCheckBox + truncate_label_text(ui_guard, "guard"); + truncate_label_text(ui_pre, "pre"); + truncate_label_text(ui_flip, "flip"); + truncate_label_text(ui_showname_enable, "showname_enable"); + + // QLabel + truncate_label_text(ui_music_label, "music_label"); + truncate_label_text(ui_sfx_label, "sfx_label"); + truncate_label_text(ui_blip_label, "blip_label"); + free_brush = QBrush(ao_app->get_color("area_free_color", "courtroom_design.ini")); lfp_brush = @@ -1133,14 +1178,18 @@ void Courtroom::set_background(QString p_background, bool display) // Populate the dropdown list with all pos that exist on this bg QStringList pos_list = {}; for (QString key : default_pos.keys()) { - if (file_exists( - ao_app->get_image_suffix(ao_app->get_background_path(key)))) { + if (file_exists(ao_app->get_image_suffix( + ao_app->get_background_path(default_pos[key]))) || // if we have 2.8-style positions, e.g. def.png, wit.webp, hld.apng + file_exists( + ao_app->get_image_suffix(ao_app->get_background_path(key)))) { // if we have pre-2.8-style positions, e.g. defenseempty.png pos_list.append(default_pos[key]); } } - - // TODO: search through extra/custom pos and add them to the pos dropdown as - // well + for (QString pos : ao_app->read_design_ini("positions", ao_app->get_background_path("design.ini")).split(",")) { + if (file_exists(ao_app->get_image_suffix(ao_app->get_background_path(pos)))) { + pos_list.append(pos); + } + } set_pos_dropdown(pos_list); @@ -1287,10 +1336,16 @@ void Courtroom::update_character(int p_cid) if (ao_app->custom_objection_enabled) // if setting is enabled { custom_obj_menu->clear(); + custom_objections_list.clear(); if (file_exists(ao_app->get_image_suffix( ao_app->get_character_path(current_char, "custom")))) { ui_custom_objection->show(); - QAction *action = custom_obj_menu->addAction("Default"); + QString custom_name = ao_app->read_char_ini(f_char, "custom_name", "Shouts"); + QAction *action; + if (custom_name != "") + action = custom_obj_menu->addAction(custom_name); + else + action = custom_obj_menu->addAction("Default"); custom_obj_menu->setDefaultAction(action); objection_custom = ""; } @@ -1305,11 +1360,23 @@ void Courtroom::update_character(int p_cid) << "*.webp", QDir::Files); for (const QString &filename : custom_obj) { - QAction *action = custom_obj_menu->addAction(filename); + CustomObjection custom_objection; + custom_objection.filename = filename; + QString custom_name = ao_app->read_char_ini(f_char, filename.split(".")[0] + "_name", "Shouts"); + QAction *action; + if (custom_name != "") { + custom_objection.name = custom_name; + action = custom_obj_menu->addAction(custom_name); + } + else { + custom_objection.name = filename.split(".")[0]; + action = custom_obj_menu->addAction(filename.split(".")[0]); + } if (custom_obj_menu->defaultAction() == nullptr) { custom_obj_menu->setDefaultAction(action); - objection_custom = action->text(); + objection_custom = custom_objection.filename; } + custom_objections_list.append(custom_objection); } } } @@ -1320,6 +1387,9 @@ void Courtroom::update_character(int p_cid) ui_char_select_background->hide(); ui_ic_chat_message->setEnabled(m_cid != -1); ui_ic_chat_message->setFocus(); + // have to call these to make sure sfx and blips don't get accidentally muted forever when we change characters + sfx_player->set_volume(ui_sfx_slider->value()); + blip_player->set_volume(ui_blip_slider->value()); } void Courtroom::enter_courtroom() @@ -1532,6 +1602,9 @@ void Courtroom::on_chat_return_pressed() if ((anim_state < 3 || text_state < 2) && objection_state == 0) return; + ui_ic_chat_message->blockSignals(true); + QTimer::singleShot(600, this, + [=] { ui_ic_chat_message->blockSignals(false); }); // MS# // deskmod# // pre-emote# @@ -1554,7 +1627,7 @@ void Courtroom::on_chat_return_pressed() // showname# // other_charid# // self_offset# - // noninterrupting_preanim#% + // immediate_preanim#% QStringList packet_contents; @@ -1566,6 +1639,12 @@ void Courtroom::on_chat_return_pressed() if (ao_app->desk_mod_enabled) { f_desk_mod = QString::number(ao_app->get_desk_mod(current_char, current_emote)); + if (!ao_app->expanded_desk_mods_enabled) { + if (f_desk_mod == "2" || f_desk_mod == "4") + f_desk_mod = "0"; + else if (f_desk_mod == "3" || f_desk_mod == "5") + f_desk_mod = "1"; + } if (f_desk_mod == "-1") f_desk_mod = "chat"; } @@ -1583,7 +1662,7 @@ void Courtroom::on_chat_return_pressed() packet_contents.append(current_side); packet_contents.append(get_char_sfx()); - if (ui_pre->isChecked() && !ao_app->is_stickysounds_enabled()) { + if (ui_pre->isChecked() && !ao_app->is_stickysounds_enabled() && ui_sfx_dropdown->currentIndex() > 1) { ui_sfx_dropdown->blockSignals(true); ui_sfx_dropdown->setCurrentIndex(0); ui_sfx_dropdown->blockSignals(false); @@ -1593,15 +1672,13 @@ void Courtroom::on_chat_return_pressed() int f_emote_mod = ao_app->get_emote_mod(current_char, current_emote); // needed or else legacy won't understand what we're saying - if (objection_state > 0) { - if (ui_pre->isChecked()) { - if (f_emote_mod == 4 || f_emote_mod == 5) - f_emote_mod = 6; - else - f_emote_mod = 2; - } + if (objection_state > 0 && ui_pre->isChecked()) { + if (f_emote_mod == 4 || f_emote_mod == 5) + f_emote_mod = 6; + else + f_emote_mod = 2; } - else if (ui_pre->isChecked() && !ui_pre_non_interrupt->isChecked()) { + else if (ui_pre->isChecked() && !ui_immediate->isChecked()) { if (f_emote_mod == 0) f_emote_mod = 1; else if (f_emote_mod == 5 && ao_app->prezoom_enabled) @@ -1691,10 +1768,13 @@ void Courtroom::on_chat_return_pressed() packet_contents.append("-1"); } // Send the offset as it's gonna be used regardless - packet_contents.append(QString::number(char_offset)); + if(ao_app->y_offset_enabled) + packet_contents.append(QString::number(char_offset) + "&" + QString::number(char_vert_offset)); + else + packet_contents.append(QString::number(char_offset)); // Finally, we send over if we want our pres to not interrupt. - if (ui_pre_non_interrupt->isChecked() && ui_pre->isChecked()) { + if (ui_immediate->isChecked() && ui_pre->isChecked()) { packet_contents.append("1"); } else { @@ -1759,6 +1839,7 @@ void Courtroom::reset_ic() ui_vp_chat_arrow->stop(); text_state = 0; anim_state = 0; + evidence_presented = false; ui_vp_objection->stop(); chat_tick_timer->stop(); ui_vp_evidence_display->reset(); @@ -1792,6 +1873,7 @@ void Courtroom::handle_chatmessage(QStringList *p_contents) if (p_contents->size() < MS_MINIMUM) return; + int prev_char_id = m_chatmessage[CHAR_ID].toInt(); for (int n_string = 0; n_string < MS_MAXIMUM; ++n_string) { // Note that we have added stuff that vanilla clients and servers simply // won't send. So now, we have to check if the thing we want even exists @@ -1859,34 +1941,36 @@ void Courtroom::handle_chatmessage(QStringList *p_contents) { m_chatmessage[MESSAGE] = ""; // Turn it into true blankpost } - - if (!m_chatmessage[MESSAGE].isEmpty() || ic_chatlog_history.isEmpty() || ic_chatlog_history.last().get_message() != "") - { - log_ic_text(f_charname, f_displayname, m_chatmessage[MESSAGE], "", - m_chatmessage[TEXT_COLOR].toInt()); - append_ic_text(m_chatmessage[MESSAGE], f_displayname, "", - m_chatmessage[TEXT_COLOR].toInt()); - } - + QString f_char = m_chatmessage[CHAR_NAME]; QString f_custom_theme = ao_app->get_char_shouts(f_char); // if an objection is used if (objection_mod <= 4 && objection_mod >= 1) { + QString shout_message; switch (objection_mod) { case 1: ui_vp_objection->play("holdit_bubble", f_char, f_custom_theme, 724); objection_player->play("holdit", f_char, f_custom_theme); + shout_message = ao_app->read_char_ini(f_char, "holdit_message", "Shouts"); + if (shout_message == "") + shout_message = tr("HOLD IT!"); break; case 2: ui_vp_objection->play("objection_bubble", f_char, f_custom_theme, 724); objection_player->play("objection", f_char, f_custom_theme); + shout_message = ao_app->read_char_ini(f_char, "objection_message", "Shouts"); + if (shout_message == "") + shout_message = tr("OBJECTION!"); if (ao_app->objection_stop_music()) music_player->stop(); break; case 3: ui_vp_objection->play("takethat_bubble", f_char, f_custom_theme, 724); objection_player->play("takethat", f_char, f_custom_theme); + shout_message = ao_app->read_char_ini(f_char, "takethat_message", "Shouts"); + if (shout_message == "") + shout_message = tr("TAKE THAT!"); break; // case 4 is AO2 only case 4: @@ -1896,19 +1980,36 @@ void Courtroom::handle_chatmessage(QStringList *p_contents) objection_player->play("custom_objections/" + custom_objection.split('.')[0], f_char, f_custom_theme); + shout_message = ao_app->read_char_ini(f_char, custom_objection.split('.')[0] + "_message", "Shouts"); + if (shout_message == "") + shout_message = custom_objection.split('.')[0]; } else { ui_vp_objection->play("custom", f_char, f_custom_theme, shout_stay_time); objection_player->play("custom", f_char, f_custom_theme); + shout_message = ao_app->read_char_ini(f_char, "custom_message", "Shouts"); + if (shout_message == "") + shout_message = tr("CUSTOM OBJECTION!"); } m_chatmessage[EMOTE_MOD] = 1; break; } + log_ic_text(f_char, f_displayname, shout_message, + tr("shouts"),2); + append_ic_text(shout_message, f_displayname, tr("shouts")); sfx_player->clear(); // Objection played! Cut all sfx. } else handle_chatmessage_2(); + + if (!m_chatmessage[MESSAGE].isEmpty() || ic_chatlog_history.isEmpty() || + ic_chatlog_history.last().get_message() != "") { + log_ic_text(f_charname, f_displayname, m_chatmessage[MESSAGE], "", + m_chatmessage[TEXT_COLOR].toInt()); + append_ic_text(m_chatmessage[MESSAGE], f_displayname, "", + m_chatmessage[TEXT_COLOR].toInt()); + } } void Courtroom::objection_done() { handle_chatmessage_2(); } @@ -2048,8 +2149,6 @@ void Courtroom::handle_chatmessage_2() f_pointsize = chatsize; set_font(ui_vp_message, "", "message", customchar, font_name, f_pointsize); - set_scene(m_chatmessage[DESK_MOD], m_chatmessage[SIDE]); - int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); // Deal with invalid emote modifiers if (emote_mod != 0 && emote_mod != 1 && emote_mod != 2 && emote_mod != 5 && @@ -2081,10 +2180,20 @@ void Courtroom::handle_chatmessage_2() if (got_other_charid > -1) { // If there is, show them! ui_vp_sideplayer_char->show(); - - int other_offset = m_chatmessage[OTHER_OFFSET].toInt(); + QStringList other_offsets = m_chatmessage[OTHER_OFFSET].split("&"); + int other_offset; + int other_offset_v; + if (other_offsets.length() <= 1) { + other_offset = m_chatmessage[OTHER_OFFSET].toInt(); + other_offset_v = 0; + } + else { + other_offset = other_offsets[0].toInt(); + other_offset_v = other_offsets[1].toInt(); + } ui_vp_sideplayer_char->move(ui_viewport->width() * other_offset / 100, - 0); + ui_viewport->height() * other_offset_v / + 100); QStringList args = m_chatmessage[OTHER_CHARID].split("^"); if (args.size() > @@ -2123,13 +2232,32 @@ void Courtroom::handle_chatmessage_2() } // Set ourselves according to SELF_OFFSET - bool ok; - int self_offset = m_chatmessage[SELF_OFFSET].toInt(&ok); - if (ok) - ui_vp_player_char->move(ui_viewport->width() * self_offset / 100, 0); + QStringList self_offsets = m_chatmessage[SELF_OFFSET].split("&"); + int self_offset = self_offsets[0].toInt(); + int self_offset_v; + if (self_offsets.length() <= 1) + self_offset_v = 0; else - ui_vp_player_char->move(0, 0); + self_offset_v = self_offsets[1].toInt(); + ui_vp_player_char->move(ui_viewport->width() * self_offset / 100, + ui_viewport->height() * self_offset_v / 100); + switch(m_chatmessage[DESK_MOD].toInt()) { + case 4: + ui_vp_sideplayer_char->hide(); + ui_vp_player_char->move(0, 0); + [[fallthrough]]; + case 2: + set_scene("0", m_chatmessage[SIDE]); + break; + case 5: + case 3: + set_scene("1", m_chatmessage[SIDE]); + break; + default: + set_scene(m_chatmessage[DESK_MOD], m_chatmessage[SIDE]); + break; + } switch (emote_mod) { case 1: case 2: @@ -2138,7 +2266,7 @@ void Courtroom::handle_chatmessage_2() break; case 0: case 5: - if (m_chatmessage[NONINTERRUPTING_PRE].toInt() == 0) + if (m_chatmessage[IMMEDIATE].toInt() == 0) handle_chatmessage_3(); else play_preanim(true); @@ -2251,7 +2379,8 @@ void Courtroom::handle_chatmessage_3() .isEmpty()) // Pure whitespace showname, get outta here. f_showname = m_chatmessage[CHAR_NAME]; - if (f_evi_id > 0 && f_evi_id <= local_evidence_list.size()) { + if (f_evi_id > 0 && f_evi_id <= local_evidence_list.size() && + !evidence_presented) { // shifted by 1 because 0 is no evidence per legacy standards QString f_image = local_evidence_list.at(f_evi_id - 1).image; QString f_evi_name = local_evidence_list.at(f_evi_id - 1).name; @@ -2260,16 +2389,38 @@ void Courtroom::handle_chatmessage_3() f_side == "jud" || f_side == "jur"); ui_vp_evidence_display->show_evidence(f_image, is_left_side, ui_sfx_slider->value()); - - log_ic_text(m_chatmessage[CHAR_NAME], m_chatmessage[SHOWNAME], f_evi_name, tr("has presented evidence"), - m_chatmessage[TEXT_COLOR].toInt()); - append_ic_text(f_evi_name, f_showname, tr("has presented evidence")); + if (log_ic_actions) { + log_ic_text(m_chatmessage[CHAR_NAME], m_chatmessage[SHOWNAME], f_evi_name, + tr("has presented evidence"), + m_chatmessage[TEXT_COLOR].toInt()); + append_ic_text(f_evi_name, f_showname, tr("has presented evidence")); + } + evidence_presented = true; // we're done presenting evidence, and we + // don't want to do it twice } int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); QString side = m_chatmessage[SIDE]; + switch(m_chatmessage[DESK_MOD].toInt()) { + case 4: + set_self_offset(m_chatmessage[SELF_OFFSET]); + [[fallthrough]]; + case 2: + set_scene("1", m_chatmessage[SIDE]); + break; + case 5: + ui_vp_sideplayer_char->hide(); + ui_vp_player_char->move(0, 0); + [[fallthrough]]; + case 3: + set_scene("0", m_chatmessage[SIDE]); + break; + default: + set_scene(m_chatmessage[DESK_MOD], m_chatmessage[SIDE]); + break; + } if (emote_mod == 5 || emote_mod == 6) { ui_vp_desk->hide(); ui_vp_legacy_desk->hide(); @@ -2358,9 +2509,7 @@ QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, // If html is enabled, prepare this text to be all ready for it. if (html) { ic_color_stack.push(default_color); - QString appendage = ""; + QString appendage = ""; if (!align.isEmpty()) appendage.prepend("
"); @@ -2479,8 +2628,7 @@ QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, if (!ic_color_stack.empty()) appendage += - ""; if (is_end && !skip) { @@ -2568,10 +2716,9 @@ QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, } void Courtroom::log_ic_text(QString p_name, QString p_showname, - QString p_message, QString p_action, int p_color) + QString p_message, QString p_action, int p_color) { - chatlogpiece log_entry(p_name, p_showname, p_message, p_action, - p_color); + chatlogpiece log_entry(p_name, p_showname, p_message, p_action, p_color); ic_chatlog_history.append(log_entry); if (ao_app->get_auto_logging_enabled()) ao_app->append_to_file(log_entry.get_full(), ao_app->log_filename, true); @@ -2583,7 +2730,7 @@ void Courtroom::log_ic_text(QString p_name, QString p_showname, } void Courtroom::append_ic_text(QString p_text, QString p_name, QString p_action, - int color) + int color, QDateTime timestamp) { QTextCharFormat bold; QTextCharFormat normal; @@ -2596,9 +2743,12 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, QString p_action, const QTextCursor old_cursor = ui_ic_chatlog->textCursor(); const int old_scrollbar_value = ui_ic_chatlog->verticalScrollBar()->value(); const bool need_newline = !ui_ic_chatlog->document()->isEmpty(); - const int scrollbar_target_value = log_goes_downwards ? ui_ic_chatlog->verticalScrollBar()->maximum() : ui_ic_chatlog->verticalScrollBar()->minimum(); + const int scrollbar_target_value = + log_goes_downwards ? ui_ic_chatlog->verticalScrollBar()->maximum() + : ui_ic_chatlog->verticalScrollBar()->minimum(); - ui_ic_chatlog->moveCursor(log_goes_downwards ? QTextCursor::End : QTextCursor::Start); + ui_ic_chatlog->moveCursor(log_goes_downwards ? QTextCursor::End + : QTextCursor::Start); // Only prepend with newline if log goes downwards if (log_goes_downwards && need_newline) { @@ -2606,18 +2756,37 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, QString p_action, } // Timestamp if we're doing that meme - if (log_timestamp) - ui_ic_chatlog->textCursor().insertText("[" + QDateTime::currentDateTime().toString("h:mm:ss AP") + "] ", normal); + if (log_timestamp) { + if (timestamp.isValid()) { + ui_ic_chatlog->textCursor().insertText( + "[" + timestamp.toString("h:mm:ss AP") + "] ", normal); + } else { + qDebug() << "could not insert invalid timestamp"; + } + } // Format the name of the actor ui_ic_chatlog->textCursor().insertText(p_name, bold); + // Special case for stopping the music + if (p_action == tr("has stopped the music")) { + ui_ic_chatlog->textCursor().insertText(" " + p_action + ".", normal); + } + // Make shout text bold + else if (p_action == tr("shouts") && log_ic_actions) { + ui_ic_chatlog->textCursor().insertText(" " + p_action + " ", normal); + if (log_colors) + ui_ic_chatlog->textCursor().insertHtml("" + filter_ic_text(p_text, true, -1, 0) + ""); + else + ui_ic_chatlog->textCursor().insertText(" " + p_text, italics); + } // If action not blank: - if (p_action != "") { + else if (p_action != "" && log_ic_actions) { // Format the action in normal ui_ic_chatlog->textCursor().insertText(" " + p_action, normal); if (log_newline) - // For some reason, we're forced to use
instead of the more sensible \n. - // Why? Because \n is treated as a new Block instead of a soft newline within a paragraph! + // For some reason, we're forced to use
instead of the more sensible + // \n. Why? Because \n is treated as a new Block instead of a soft newline + // within a paragraph! ui_ic_chatlog->textCursor().insertHtml("
"); else ui_ic_chatlog->textCursor().insertText(": ", normal); @@ -2626,14 +2795,21 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, QString p_action, } else { if (log_newline) - // For some reason, we're forced to use
instead of the more sensible \n. - // Why? Because \n is treated as a new Block instead of a soft newline within a paragraph! + // For some reason, we're forced to use
instead of the more sensible + // \n. Why? Because \n is treated as a new Block instead of a soft newline + // within a paragraph! ui_ic_chatlog->textCursor().insertHtml("
"); else ui_ic_chatlog->textCursor().insertText(": ", normal); // Format the result according to html - if (log_colors) - ui_ic_chatlog->textCursor().insertHtml(filter_ic_text(p_text, true, -1, color)); + if (log_colors) { + QString p_text_filtered = filter_ic_text(p_text, true, -1, color); + p_text_filtered = p_text_filtered.replace("$c0", ao_app->get_color("ic_chatlog_color", "courtroom_fonts.ini").name(QColor::HexRgb)); + for (int c = 1; c < max_colors; ++c) { + p_text_filtered = p_text_filtered.replace("$c" + QString::number(c), default_color_rgb_list.at(c).name(QColor::HexRgb)); + } + ui_ic_chatlog->textCursor().insertHtml(p_text_filtered); + } else ui_ic_chatlog->textCursor().insertText(filter_ic_text(p_text, false), normal); } @@ -2646,7 +2822,8 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, QString p_action, // If we got too many blocks in the current log, delete some. while (ui_ic_chatlog->document()->blockCount() > log_maximum_blocks && log_maximum_blocks > 0) { - ui_ic_chatlog->moveCursor(log_goes_downwards ? QTextCursor::Start : QTextCursor::End); + ui_ic_chatlog->moveCursor(log_goes_downwards ? QTextCursor::Start + : QTextCursor::End); ui_ic_chatlog->textCursor().select(QTextCursor::BlockUnderCursor); ui_ic_chatlog->textCursor().removeSelectedText(); if (log_goes_downwards) @@ -2656,7 +2833,8 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, QString p_action, } // Finally, scroll the scrollbar to the correct position. - if (old_cursor.hasSelection() || old_scrollbar_value != scrollbar_target_value) { + if (old_cursor.hasSelection() || + old_scrollbar_value != scrollbar_target_value) { // The user has selected text or scrolled away from the bottom: maintain // position. ui_ic_chatlog->setTextCursor(old_cursor); @@ -2665,13 +2843,14 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, QString p_action, else { // The user hasn't selected any text and the scrollbar is at the bottom: // scroll to the bottom. - ui_ic_chatlog->moveCursor(log_goes_downwards ? QTextCursor::End : QTextCursor::Start); + ui_ic_chatlog->moveCursor(log_goes_downwards ? QTextCursor::End + : QTextCursor::Start); ui_ic_chatlog->verticalScrollBar()->setValue( - ui_ic_chatlog->verticalScrollBar()->maximum()); + log_goes_downwards ? ui_ic_chatlog->verticalScrollBar()->maximum() : 0); } } -void Courtroom::play_preanim(bool noninterrupting) +void Courtroom::play_preanim(bool immediate) { QString f_char = m_chatmessage[CHAR_NAME]; QString f_preanim = m_chatmessage[PRE_EMOTE]; @@ -2693,7 +2872,7 @@ void Courtroom::play_preanim(bool noninterrupting) QString anim_to_find = ao_app->get_image_suffix(ao_app->get_character_path(f_char, f_preanim)); if (!file_exists(anim_to_find)) { - if (noninterrupting) + if (immediate) anim_state = 4; else anim_state = 1; @@ -2704,7 +2883,7 @@ void Courtroom::play_preanim(bool noninterrupting) ui_vp_player_char->play_pre(f_char, f_preanim, preanim_duration); - if (noninterrupting) + if (immediate) anim_state = 4; else anim_state = 1; @@ -2712,7 +2891,7 @@ void Courtroom::play_preanim(bool noninterrupting) if (text_delay >= 0) text_delay_timer->start(text_delay); - if (noninterrupting) + if (immediate) handle_chatmessage_3(); } @@ -2780,9 +2959,13 @@ void Courtroom::start_chat_ticking() current_display_speed = 3; chat_tick_timer->start(0); // Display the first char right away - QString f_gender = ao_app->get_gender(m_chatmessage[CHAR_NAME]); + last_misc = current_misc; + current_misc = ao_app->get_char_shouts(m_chatmessage[CHAR_NAME]); + if (last_misc != current_misc) + gen_char_rgb_list(m_chatmessage[CHAR_NAME]); - blip_player->set_blips(f_gender); + QString f_blips = ao_app->get_blips(m_chatmessage[CHAR_NAME]); + blip_player->set_blips(f_blips); // means text is currently ticking text_state = 1; @@ -2814,9 +2997,11 @@ void Courtroom::chat_tick() ui_vp_chat_arrow->play( "chat_arrow", f_char, f_custom_theme); // Chat stopped being processed, indicate that. - additive_previous = - additive_previous + - filter_ic_text(f_message, true, -1, m_chatmessage[TEXT_COLOR].toInt()); + QString f_message_filtered = filter_ic_text(f_message, true, -1, m_chatmessage[TEXT_COLOR].toInt()); + for (int c = 0; c < max_colors; ++c) { + f_message_filtered = f_message_filtered.replace("$c" + QString::number(c), char_color_rgb_list.at(c).name(QColor::HexRgb)); + } + additive_previous = additive_previous + f_message_filtered; real_tick_pos = ui_vp_message->toPlainText().size(); return; } @@ -2929,9 +3114,11 @@ void Courtroom::chat_tick() else { int msg_delay = message_display_speed[current_display_speed]; // Do the colors, gradual showing, etc. in here - ui_vp_message->setHtml(additive_previous + - filter_ic_text(f_message, true, tick_pos, - m_chatmessage[TEXT_COLOR].toInt())); + QString f_message_filtered = filter_ic_text(f_message, true, tick_pos, m_chatmessage[TEXT_COLOR].toInt()); + for (int c = 0; c < max_colors; ++c) { + f_message_filtered = f_message_filtered.replace("$c" + QString::number(c), char_color_rgb_list.at(c).name(QColor::HexRgb)); + } + ui_vp_message->setHtml(additive_previous + f_message_filtered); // This should always be done AFTER setHtml. Scroll the chat window with the // text. @@ -2944,17 +3131,42 @@ void Courtroom::chat_tick() ui_vp_message->ensureCursorVisible(); - // Blip player and real tick pos ticker - if (!formatting_char && (f_character != ' ' || blank_blip)) { - if (blip_ticker % blip_rate == 0) { + // We blip every "blip rate" letters. + // Here's an example with blank_blip being false and blip_rate being 2: + // I am you + // ! ! ! ! + // where ! is the blip sound + int b_rate = blip_rate; + // Earrape prevention without using timers, this method is more consistent. + if (msg_delay != 0 && msg_delay <= 25) { + // The default blip speed is 40ms, and if current msg_delay is 25ms, + // the formula will result in the blip rate of: + // 40/25 = 1.6 = 2 + // And if it's faster than that: + // 40/10 = 4 + b_rate = + qMax(b_rate, qRound(static_cast(message_display_speed[3]) / + msg_delay)); + } + if (blip_ticker % b_rate == 0) { + // ignoring white space unless blank_blip is enabled. + if (!formatting_char && (f_character != ' ' || blank_blip)) { blip_player->blip_tick(); + ++blip_ticker; } + } + else { + // Don't fully ignore whitespace still, keep ticking until + // we reached the need to play a blip sound - we also just + // need to wait for a letter to play it on. ++blip_ticker; } - // Punctuation delayer - if (punctuation_chars.contains(f_character)) { - msg_delay *= punctuation_modifier; + // Punctuation delayer, only kicks in on speed ticks less than }} + if (current_display_speed > 1 && punctuation_chars.contains(f_character)) { + // Making the user have to wait any longer than 150ms per letter is + // downright unreasonable + msg_delay = qMin(150, msg_delay * punctuation_modifier); } // If this color is talking @@ -3001,8 +3213,16 @@ void Courtroom::play_sfx() void Courtroom::set_scene(QString f_desk_mod, QString f_side) { // witness is default if pos is invalid - QString f_background = "witnessempty"; - QString f_desk_image = "stand"; + QString f_background; + QString f_desk_image; + if (file_exists(ao_app->get_image_suffix(ao_app->get_background_path("witnessempty")))) { + f_background = "witnessempty"; + f_desk_image = "stand"; + } + else { + f_background = "wit"; + f_desk_image = "wit_overlay"; + } if (f_side == "def" && file_exists(ao_app->get_image_suffix( ao_app->get_background_path("defenseempty")))) { @@ -3067,6 +3287,17 @@ void Courtroom::set_scene(QString f_desk_mod, QString f_side) } } +void Courtroom::set_self_offset(QString p_list) { + QStringList self_offsets = p_list.split("&"); + int self_offset = self_offsets[0].toInt(); + int self_offset_v; + if (self_offsets.length() <= 1) + self_offset_v = 0; + else + self_offset_v = self_offsets[1].toInt(); + ui_vp_player_char->move(ui_viewport->width() * self_offset / 100, ui_viewport->height() * self_offset_v / 100); +} + void Courtroom::set_ip_list(QString p_list) { QString f_list = p_list.replace("|", ":").replace("*", "\n"); @@ -3141,14 +3372,19 @@ void Courtroom::handle_song(QStringList *p_contents) { effect_flags = p_contents->at(5).toInt(); } - music_player->play(f_song, channel, looping, effect_flags); - if (channel == 0) - ui_music_name->setText(f_song_clear); + if (f_song == "~stop.mp3") + ui_music_name->setText(tr("None")); + else if (channel == 0) { + if (file_exists(ao_app->get_sfx_suffix(ao_app->get_music_path(f_song)))) + ui_music_name->setText(f_song_clear); + else + ui_music_name->setText(tr("[MISSING] %1").arg(f_song_clear)); + } } else { QString str_char = char_list.at(n_char).name; - QString str_show = char_list.at(n_char).name; + QString str_show = ao_app->get_showname(str_char); if (p_contents->length() > 2) { if (p_contents->at(2) != "") { @@ -3170,13 +3406,25 @@ void Courtroom::handle_song(QStringList *p_contents) } if (!mute_map.value(n_char)) { - log_ic_text(str_char, str_show, f_song, tr("has played a song"), - m_chatmessage[TEXT_COLOR].toInt()); - append_ic_text(f_song_clear, str_show, tr("has played a song")); - + if (f_song == "~stop.mp3") { + log_ic_text(str_char, str_show, "", tr("has stopped the music"), + m_chatmessage[TEXT_COLOR].toInt()); + append_ic_text("", str_show, tr("has stopped the music")); + } + else { + log_ic_text(str_char, str_show, f_song, tr("has played a song"), + m_chatmessage[TEXT_COLOR].toInt()); + append_ic_text(f_song_clear, str_show, tr("has played a song")); + } music_player->play(f_song, channel, looping, effect_flags); - if (channel == 0) - ui_music_name->setText(f_song_clear); + if (f_song == "~stop.mp3") + ui_music_name->setText(tr("None")); + else if (channel == 0) { + if (file_exists(ao_app->get_sfx_suffix(ao_app->get_music_path(f_song)))) + ui_music_name->setText(f_song_clear); + else + ui_music_name->setText(tr("[MISSING] %1").arg(f_song_clear)); + } } } } @@ -3334,9 +3582,8 @@ void Courtroom::on_ooc_return_pressed() if (ok) { if (off >= -100 && off <= 100) { char_offset = off; - QString msg = tr("You have set your offset to "); - msg.append(QString::number(off)); - msg.append("%."); + QString msg = + tr("You have set your offset to %1%%.").arg(QString::number(off)); append_server_chatmessage("CLIENT", msg, "1"); } else { @@ -3350,6 +3597,31 @@ void Courtroom::on_ooc_return_pressed() } return; } + else if (ooc_message.startsWith("/voffset")) { + ui_ooc_chat_message->clear(); + ooc_message.remove(0, 8); + + bool ok; + int off = ooc_message.toInt(&ok); + if (ok) { + if (off >= -100 && off <= 100) { + char_vert_offset = off; + QString msg = tr("You have set your vertical offset to %1%%.") + .arg(QString::number(off)); + append_server_chatmessage("CLIENT", msg, "1"); + } + else { + append_server_chatmessage( + "CLIENT", + tr("Your vertical offset must be between -100% and 100%!"), "1"); + } + } + else { + append_server_chatmessage( + "CLIENT", tr("That vertical offset does not look like one."), "1"); + } + return; + } else if (ooc_message.startsWith("/switch_am")) { append_server_chatmessage( "CLIENT", tr("You switched your music and area list."), "1"); @@ -3371,13 +3643,13 @@ void Courtroom::on_ooc_return_pressed() return; } else if (ooc_message.startsWith("/non_int_pre")) { - if (ui_pre_non_interrupt->isChecked()) + if (ui_immediate->isChecked()) append_server_chatmessage( "CLIENT", tr("Your pre-animations interrupt again."), "1"); else append_server_chatmessage( "CLIENT", tr("Your pre-animations will not interrupt text."), "1"); - ui_pre_non_interrupt->setChecked(!ui_pre_non_interrupt->isChecked()); + ui_immediate->setChecked(!ui_immediate->isChecked()); ui_ooc_chat_message->clear(); return; } @@ -3458,9 +3730,10 @@ void Courtroom::on_ooc_return_pressed() if (!caseauth.isEmpty()) append_server_chatmessage(tr("CLIENT"), tr("Case made by %1.").arg(caseauth), "1"); - if (!casedoc.isEmpty()) - ao_app->send_server_packet(new AOPacket("CT#" + ui_ooc_chat_name->text() + - "#/doc " + casedoc + "#%")); + if (!casedoc.isEmpty()) { + QStringList f_contents = {ui_ooc_chat_name->text(), "/doc " + casedoc}; + ao_app->send_server_packet(new AOPacket("CT", f_contents)); + } if (!casestatus.isEmpty()) ao_app->send_server_packet(new AOPacket("CT#" + ui_ooc_chat_name->text() + "#/status " + casestatus + "#%")); @@ -3473,7 +3746,15 @@ void Courtroom::on_ooc_return_pressed() new AOPacket("DE#" + QString::number(i) + "#%")); } - foreach (QString evi, casefile.childGroups()) { + // sort the case_evidence numerically + QStringList case_evidence = casefile.childGroups(); + std::sort(case_evidence.begin(), case_evidence.end(), + [] (const QString &a, const QString &b) { + return a.toInt() < b.toInt(); + }); + + // load evidence + foreach (QString evi, case_evidence) { if (evi == "General") continue; @@ -3648,7 +3929,7 @@ void Courtroom::on_music_search_return_pressed() void Courtroom::on_pos_dropdown_changed(int p_index) { - if (p_index < 0 || p_index > 7) + if (p_index < 0) return; toggle_judge_buttons(false); @@ -3676,7 +3957,8 @@ void Courtroom::set_iniswap_dropdown() return; } QStringList iniswaps = ao_app->get_list_file( - ao_app->get_character_path(char_list.at(m_cid).name, "iniswaps.ini")); + ao_app->get_character_path(char_list.at(m_cid).name, "iniswaps.ini")) + ao_app->get_list_file(ao_app->get_base_path() + "iniswaps.ini"); + iniswaps.removeDuplicates(); iniswaps.prepend(char_list.at(m_cid).name); if (iniswaps.size() <= 0) { ui_iniswap_dropdown->hide(); @@ -3706,14 +3988,15 @@ void Courtroom::on_iniswap_dropdown_changed(int p_index) ao_app->set_char_ini(char_list.at(m_cid).name, iniswap, "name", "Options"); QStringList swaplist; + QStringList defswaplist = ao_app->get_list_file(ao_app->get_character_path(char_list.at(m_cid).name, "iniswaps.ini")); for (int i = 0; i < ui_iniswap_dropdown->count(); ++i) { QString entry = ui_iniswap_dropdown->itemText(i); - if (!swaplist.contains(entry) && entry != char_list.at(m_cid).name) + if (!swaplist.contains(entry) && entry != char_list.at(m_cid).name && !defswaplist.contains(entry)) swaplist.append(entry); } ao_app->write_to_file( swaplist.join("\n"), - ao_app->get_character_path(char_list.at(m_cid).name, "iniswaps.ini")); + ao_app->get_base_path() + "iniswaps.ini"); ui_iniswap_dropdown->blockSignals(true); ui_iniswap_dropdown->setCurrentIndex(p_index); ui_iniswap_dropdown->blockSignals(false); @@ -3728,6 +4011,7 @@ void Courtroom::on_iniswap_context_menu_requested(const QPoint &pos) { QMenu *menu = ui_iniswap_dropdown->lineEdit()->createStandardContextMenu(); + menu->setAttribute(Qt::WA_DeleteOnClose); menu->addSeparator(); if (file_exists(ao_app->get_character_path(current_char, "char.ini"))) menu->addAction(QString("Edit " + current_char + "/char.ini"), this, @@ -3787,6 +4071,7 @@ void Courtroom::set_sfx_dropdown() ui_sfx_remove->hide(); return; } + soundlist.prepend("Nothing"); soundlist.prepend("Default"); ui_sfx_dropdown->show(); @@ -3801,9 +4086,9 @@ void Courtroom::on_sfx_dropdown_changed(int p_index) ui_ic_chat_message->setFocus(); QStringList soundlist; - for (int i = 0; i < ui_sfx_dropdown->count(); ++i) { + for (int i = 2; i < ui_sfx_dropdown->count(); ++i) { QString entry = ui_sfx_dropdown->itemText(i); - if (!soundlist.contains(entry) && entry != "Default") + if (!soundlist.contains(entry)) soundlist.append(entry); } @@ -3825,7 +4110,7 @@ void Courtroom::on_sfx_dropdown_changed(int p_index) ui_sfx_dropdown->blockSignals(true); ui_sfx_dropdown->setCurrentIndex(p_index); ui_sfx_dropdown->blockSignals(false); - if (p_index != 0) + if (p_index > 1) ui_sfx_remove->show(); else ui_sfx_remove->hide(); @@ -3835,6 +4120,7 @@ void Courtroom::on_sfx_context_menu_requested(const QPoint &pos) { QMenu *menu = ui_sfx_dropdown->lineEdit()->createStandardContextMenu(); + menu->setAttribute(Qt::WA_DeleteOnClose); menu->addSeparator(); if (file_exists(ao_app->get_character_path(current_char, "soundlist.ini"))) menu->addAction(QString("Edit " + current_char + "/soundlist.ini"), this, @@ -3842,7 +4128,7 @@ void Courtroom::on_sfx_context_menu_requested(const QPoint &pos) else menu->addAction(QString("Edit theme's character_soundlist.ini"), this, SLOT(on_sfx_edit_requested())); - if (ui_sfx_dropdown->currentIndex() != 0) + if (ui_sfx_dropdown->currentIndex() > 1) menu->addAction(QString("Remove " + ui_sfx_dropdown->itemText( ui_sfx_dropdown->currentIndex())), this, SLOT(on_sfx_remove_clicked())); @@ -3870,7 +4156,7 @@ void Courtroom::on_sfx_remove_clicked() // client will crash return; } - if (ui_sfx_dropdown->itemText(ui_sfx_dropdown->currentIndex()) != "Default") { + if (ui_sfx_dropdown->currentIndex() > 1) { ui_sfx_dropdown->removeItem(ui_sfx_dropdown->currentIndex()); on_sfx_dropdown_changed(0); // Reset back to original } @@ -3891,7 +4177,7 @@ void Courtroom::set_effects_dropdown() return; } - effectslist.prepend("None"); + effectslist.prepend(tr("None")); ui_effects_dropdown->show(); ui_effects_dropdown->addItems(effectslist); @@ -3922,8 +4208,9 @@ void Courtroom::set_effects_dropdown() void Courtroom::on_effects_context_menu_requested(const QPoint &pos) { - QMenu *menu = new QMenu(); + QMenu *menu = new QMenu(this); + menu->setAttribute(Qt::WA_DeleteOnClose); if (!ao_app->read_char_ini(current_char, "effects", "Options").isEmpty()) menu->addAction( QString("Open misc/" + @@ -3976,6 +4263,8 @@ bool Courtroom::effects_dropdown_find_and_set(QString effect) QString Courtroom::get_char_sfx() { QString sfx = ui_sfx_dropdown->itemText(ui_sfx_dropdown->currentIndex()); + if (sfx == "Nothing") + return "1"; if (sfx != "" && sfx != "Default") return sfx; return ao_app->get_sfx_name(current_char, current_emote); @@ -4069,10 +4358,10 @@ void Courtroom::on_music_list_double_clicked(QTreeWidgetItem *p_item, { if (is_muted) return; - + if (p_item->parent() == nullptr) // i.e. we've clicked a category + return; column = 1; // Column 1 is always the metadata (which we want) QString p_song = p_item->text(column); - QStringList packet_contents; packet_contents.append(p_song); packet_contents.append(QString::number(m_cid)); @@ -4086,8 +4375,9 @@ void Courtroom::on_music_list_double_clicked(QTreeWidgetItem *p_item, void Courtroom::on_music_list_context_menu_requested(const QPoint &pos) { - QMenu *menu = new QMenu(); - + QMenu *menu = new QMenu(this); + menu->setAttribute(Qt::WA_DeleteOnClose); + menu->addAction(QString(tr("Stop Current Song")), this, SLOT(music_stop())); menu->addAction(QString(tr("Play Random Song")), this, SLOT(music_random())); menu->addSeparator(); menu->addAction(QString(tr("Expand All Categories")), this, @@ -4148,11 +4438,14 @@ void Courtroom::music_random() QTreeWidgetItemIterator::NotHidden | QTreeWidgetItemIterator::NoChildren); while (*it) { - clist += (*it); + if ((*it)->parent()->isExpanded()) { + clist += (*it); + } ++it; } - int i = qrand() % clist.length(); - on_music_list_double_clicked(clist.at(i), 1); + if (clist.length() == 0) + return; + on_music_list_double_clicked(clist.at(qrand() % clist.length()), 1); } void Courtroom::music_list_expand_all() { ui_music_list->expandAll(); } @@ -4165,6 +4458,22 @@ void Courtroom::music_list_collapse_all() ui_music_list->setCurrentItem(current); } +void Courtroom::music_stop() +{ // send a fake music packet with a nonexistent song + if (is_muted) // this requires a special exception for "~stop.mp3" in + return; // tsuserver3, as it will otherwise reject songs not on + QStringList packet_contents; // its music list + packet_contents.append( + "~stop.mp3"); // this is our fake song, playing it triggers special code + packet_contents.append(QString::number(m_cid)); + if ((!ui_ic_chat_name->text().isEmpty() && ao_app->cccc_ic_support_enabled) || + ao_app->effects_enabled) + packet_contents.append(ui_ic_chat_name->text()); + if (ao_app->effects_enabled) + packet_contents.append(QString::number(music_flags)); + ao_app->send_server_packet(new AOPacket("MC", packet_contents), false); +} + void Courtroom::on_area_list_double_clicked(QTreeWidgetItem *p_item, int column) { column = 0; // The metadata @@ -4258,10 +4567,16 @@ void Courtroom::show_custom_objection_menu(const QPoint &pos) ui_take_that->set_image("takethat"); ui_hold_it->set_image("holdit"); ui_custom_objection->set_image("custom_selected"); - if (selecteditem->text() == "Default") + if (selecteditem->text() == ao_app->read_char_ini(current_char, "custom_name", "Shouts") || selecteditem->text() == "Default") objection_custom = ""; - else - objection_custom = selecteditem->text(); + else { + foreach (CustomObjection custom_objection, custom_objections_list) { + if (custom_objection.name == selecteditem->text()) { + objection_custom = custom_objection.filename; + break; + } + } + } objection_state = 4; custom_obj_menu->setDefaultAction(selecteditem); } @@ -4306,6 +4621,7 @@ void Courtroom::on_mute_clicked() ui_mute_list->show(); ui_pair_list->hide(); ui_pair_offset_spinbox->hide(); + ui_pair_vert_offset_spinbox->hide(); ui_pair_order_dropdown->hide(); ui_pair_button->set_image("pair_button"); ui_mute->set_image("mute_pressed"); @@ -4321,6 +4637,8 @@ void Courtroom::on_pair_clicked() if (ui_pair_list->isHidden()) { ui_pair_list->show(); ui_pair_offset_spinbox->show(); + if(ao_app->y_offset_enabled) + ui_pair_vert_offset_spinbox->show(); ui_pair_order_dropdown->show(); ui_mute_list->hide(); ui_mute->set_image("mute"); @@ -4329,6 +4647,7 @@ void Courtroom::on_pair_clicked() else { ui_pair_list->hide(); ui_pair_offset_spinbox->hide(); + ui_pair_vert_offset_spinbox->hide(); ui_pair_order_dropdown->hide(); ui_pair_button->set_image("pair_button"); } @@ -4383,6 +4702,7 @@ void Courtroom::set_text_color_dropdown() // Clear the stored optimization information color_rgb_list.clear(); + default_color_rgb_list.clear(); color_markdown_start_list.clear(); color_markdown_end_list.clear(); color_markdown_remove_list.clear(); @@ -4419,6 +4739,18 @@ void Courtroom::set_text_color_dropdown() ui_text_color->setItemIcon(ui_text_color->count() - 1, QIcon(pixmap)); color_row_to_number.append(c); } + for (int c = 0; c < max_colors; ++c) { + QColor color = ao_app->get_chat_color("c" + QString::number(c), "default"); + default_color_rgb_list.append(color); + } +} + +void Courtroom::gen_char_rgb_list(QString p_char) { + char_color_rgb_list.clear(); + for (int c = 0; c < max_colors; ++c) { + QColor color = ao_app->get_chat_color("c" + QString::number(c), p_char); + char_color_rgb_list.append(color); + } } void Courtroom::on_text_color_changed(int p_color) @@ -4485,6 +4817,11 @@ void Courtroom::on_log_limit_changed(int value) { log_maximum_blocks = value; } void Courtroom::on_pair_offset_changed(int value) { char_offset = value; } +void Courtroom::on_pair_vert_offset_changed(int value) +{ + char_vert_offset = value; +} + void Courtroom::on_witness_testimony_clicked() { if (is_muted) @@ -4640,8 +4977,11 @@ void Courtroom::regenerate_ic_chatlog() { ui_ic_chatlog->clear(); foreach (chatlogpiece item, ic_chatlog_history) { - append_ic_text(item.get_message(), ui_showname_enable->isChecked() ? item.get_showname() : item.get_name(), - item.get_action(), item.get_chat_color()); + append_ic_text(item.get_message(), + ui_showname_enable->isChecked() ? item.get_showname() + : item.get_name(), + item.get_action(), item.get_chat_color(), + item.get_datetime().toLocalTime()); } } @@ -4756,6 +5096,75 @@ void Courtroom::set_clock_visibility(bool visible) ui_clock->setVisible(visible); } +void Courtroom::truncate_label_text(QWidget *p_widget, QString p_identifier) +{ + QString filename = "courtroom_design.ini"; + pos_size_type design_ini_result = + ao_app->get_element_dimensions(p_identifier, filename); + // Get the width of the element as defined by the current theme + + // Cast to make sure we're working with one of the two supported widget types + QLabel *p_label = qobject_cast(p_widget); + QCheckBox *p_checkbox = qobject_cast(p_widget); + + if (p_checkbox == nullptr && + p_label == + nullptr) { // i.e. the given p_widget isn't a QLabel or a QCheckBox + qWarning() << "W: Tried to truncate an unsupported widget:" << p_identifier; + return; + } + // translate the text for the widget we're working with so we truncate the right string + QString label_text_tr = + QCoreApplication::translate(p_widget->metaObject()->className(), "%1") + .arg((p_label != nullptr ? p_label->text() : p_checkbox->text())); + int label_theme_width = + (p_label != nullptr + ? design_ini_result.width + : design_ini_result.width - + 18); // 18 is the width of a checkbox on win10 + 5px of + // padding, TODO: fetch the actual size + int label_px_width = + p_widget->fontMetrics().boundingRect(label_text_tr).width(); // pixel width of our translated text + p_widget->setToolTip(label_text_tr + "\n" + p_widget->toolTip()); + // qInfo() << "I: Width of label text: " << label_px_width << "px. Theme's + // width: " << label_theme_width << "px."; + + // we can't do much with a 0-width widget, and there's no need to truncate if + // the theme gives us enough space + if (label_theme_width <= 0 || label_px_width < label_theme_width) { + qInfo() << "I: Truncation aborted for label text " << label_text_tr + << ", either theme width <= 0 or label width < theme width."; + return; + } + + QString truncated_label = label_text_tr; + int truncated_px_width = label_px_width; + while (truncated_px_width > label_theme_width && truncated_label != "…") { + truncated_label.chop(2); + truncated_label.append("…"); + // qInfo() << "I: Attempted to truncate label to string: " << + // truncated_label; + truncated_px_width = + p_widget->fontMetrics().boundingRect(truncated_label).width(); + } + if (truncated_label == "…") { + // Safeguard against edge case where label text is shorter in px than '…', + // causing an infinite loop. Additionally, having just an ellipse for a + // label looks strange, so we don't set the new label. + qWarning() << "W: Potential infinite loop prevented: Label text " + << label_text_tr + << "truncated to '…', so truncation was aborted."; + return; + } + if (p_label != nullptr) + p_label->setText(truncated_label); + else if (p_checkbox != nullptr) + p_checkbox->setText(truncated_label); + qInfo() << "I: Truncated label text from " << label_text_tr << " (" + << label_px_width << "px ) to " << truncated_label << " (" + << truncated_px_width << "px )"; +} + Courtroom::~Courtroom() { delete music_player; diff --git a/src/debug_functions.cpp b/src/debug_functions.cpp index b832164..1613a7d 100644 --- a/src/debug_functions.cpp +++ b/src/debug_functions.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include "debug_functions.h" @@ -7,6 +9,7 @@ void call_error(QString p_message) { QMessageBox *msgBox = new QMessageBox; + msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setText(QCoreApplication::translate("debug_functions", "Error: %1") .arg(p_message)); msgBox->setWindowTitle( @@ -20,10 +23,15 @@ void call_notice(QString p_message) { QMessageBox *msgBox = new QMessageBox; + msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setText(p_message); msgBox->setWindowTitle( QCoreApplication::translate("debug_functions", "Notice")); - // msgBox->setWindowModality(Qt::NonModal); - msgBox->exec(); + msgBox->setStandardButtons(QMessageBox::NoButton); + + QTimer::singleShot(3000, msgBox, std::bind(&QMessageBox::setStandardButtons,msgBox,QMessageBox::Ok)); + + msgBox->exec(); + } diff --git a/src/encryption_functions.cpp b/src/encryption_functions.cpp deleted file mode 100644 index 6669fe1..0000000 --- a/src/encryption_functions.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "encryption_functions.h" - -#include "hex_functions.h" - -QString fanta_encrypt(QString temp_input, unsigned int p_key) -{ - // using standard stdlib types is actually easier here because of implicit - // char<->int conversion which in turn makes encryption arithmetic easier - - unsigned int key = p_key; - unsigned int C1 = 53761; - unsigned int C2 = 32618; - - QVector temp_result; - std::string input = temp_input.toUtf8().constData(); - - for (unsigned int pos = 0; pos < input.size(); ++pos) { - uint_fast8_t output = input.at(pos) ^ (key >> 8) % 256; - temp_result.append(output); - key = (temp_result.at(pos) + key) * C1 + C2; - } - - std::string result = ""; - - for (uint_fast8_t i_int : temp_result) { - result += omni::int_to_hex(i_int); - } - - QString final_result = QString::fromStdString(result); - - return final_result; -} - -QString fanta_decrypt(QString temp_input, unsigned int key) -{ - std::string input = temp_input.toUtf8().constData(); - - QVector unhexed_vector; - - for (unsigned int i = 0; i < input.length(); i += 2) { - std::string byte = input.substr(i, 2); - unsigned int hex_int = strtoul(byte.c_str(), nullptr, 16); - unhexed_vector.append(hex_int); - } - - unsigned int C1 = 53761; - unsigned int C2 = 32618; - - std::string result = ""; - - for (int pos = 0; pos < unhexed_vector.size(); ++pos) { - unsigned char output = unhexed_vector.at(pos) ^ (key >> 8) % 256; - result += output; - key = (unhexed_vector.at(pos) + key) * C1 + C2; - } - - return QString::fromStdString(result); -} diff --git a/src/evidence.cpp b/src/evidence.cpp index a8f5913..b97607b 100644 --- a/src/evidence.cpp +++ b/src/evidence.cpp @@ -258,8 +258,9 @@ void Courtroom::set_evidence_list(QVector &p_evi_list) else if (compare_evidence_changed( old_list.at(current_evidence), local_evidence_list.at(current_evidence))) { - QMessageBox *msgBox = new QMessageBox; + QMessageBox *msgBox = new QMessageBox(this); + msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setText(tr("The piece of evidence you've been editing has changed.")); msgBox->setInformativeText(tr("Do you wish to keep your changes?")); msgBox->setDetailedText(tr( @@ -552,7 +553,8 @@ void Courtroom::on_evidence_x_clicked() evidence_close(); return; } - QMessageBox *msgBox = new QMessageBox; + QMessageBox *msgBox = new QMessageBox(this); + msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setText(tr("Evidence has been modified.")); msgBox->setInformativeText(tr("Do you want to save your changes?")); msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | @@ -655,7 +657,8 @@ void Courtroom::on_evidence_transfer_clicked() private_evidence_list.append(f_evi); } - QMessageBox *msgBox = new QMessageBox; + QMessageBox *msgBox = new QMessageBox(this); + msgBox->setAttribute(Qt::WA_DeleteOnClose); msgBox->setText(tr("\"%1\" has been transferred.").arg(name)); msgBox->setStandardButtons(QMessageBox::Ok); msgBox->setDefaultButton(QMessageBox::Ok); diff --git a/src/hex_functions.cpp b/src/hex_functions.cpp deleted file mode 100644 index 1e35718..0000000 --- a/src/hex_functions.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "hex_functions.h" - -namespace omni { -std::string int_to_hex(unsigned int input) -{ - if (input > 255) - return "FF"; - - std::stringstream stream; - stream << std::setfill('0') << std::setw(sizeof(char) * 2) << std::hex - << input; - std::string result(stream.str()); - std::transform(result.begin(), result.end(), result.begin(), ::toupper); - - return result; -} -} // namespace omni diff --git a/src/lobby.cpp b/src/lobby.cpp index 093b0f7..954c30a 100644 --- a/src/lobby.cpp +++ b/src/lobby.cpp @@ -13,6 +13,7 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() this->setWindowTitle(tr("Attorney Online 2")); this->setWindowIcon(QIcon(":/logo.png")); + this->setWindowFlags( (this->windowFlags() | Qt::CustomizeWindowHint) & ~Qt::WindowMaximizeButtonHint); ui_background = new AOImage(this, ao_app); ui_public_servers = new AOButton(this, ao_app); @@ -97,10 +98,10 @@ void Lobby::set_widgets() "Did you download all resources correctly from tiny.cc/getao, " "including the large 'base' folder?")); - this->resize(517, 666); + this->setFixedSize(517, 666); } else { - this->resize(f_lobby.width, f_lobby.height); + this->setFixedSize(f_lobby.width, f_lobby.height); } set_size_and_pos(ui_background, "lobby"); @@ -282,7 +283,10 @@ QString Lobby::get_chatlog() int Lobby::get_selected_server() { - return ui_server_list->currentItem()->text(0).toInt(); + if (auto item = ui_server_list->currentItem()) { + return item->text(0).toInt(); + } + return -1; } void Lobby::set_loading_value(int p_value) @@ -332,12 +336,12 @@ void Lobby::on_add_to_fav_pressed() void Lobby::on_add_to_fav_released() { ui_add_to_fav->set_image("addtofav"); - - // you cant add favorites from favorites m8 - if (!public_servers_selected) - return; - - ao_app->add_favorite_server(get_selected_server()); + if (public_servers_selected) { + int selection = get_selected_server(); + if (selection > -1) { + ao_app->add_favorite_server(selection); + } + } } void Lobby::on_connect_pressed() { ui_connect->set_image("connect_pressed"); } @@ -355,7 +359,7 @@ void Lobby::on_connect_released() void Lobby::on_about_clicked() { - const bool hasApng = QImageReader::supportedImageFormats().contains("APNG"); + const bool hasApng = QImageReader::supportedImageFormats().contains("apng"); QString msg = tr("

Attorney Online %1

" @@ -441,8 +445,9 @@ void Lobby::on_server_list_clicked(QTreeWidgetItem *p_item, int column) // doubleclicked on an item in the serverlist so we'll connect right away void Lobby::on_server_list_doubleclicked(QTreeWidgetItem *p_item, int column) { + doubleclicked = true; on_server_list_clicked(p_item, column); - on_connect_released(); + //on_connect_released(); } void Lobby::on_server_search_edited(QString p_text) diff --git a/src/main.cpp b/src/main.cpp index 364377b..ce8b1dd 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -21,6 +21,8 @@ int main(int argc, char *argv[]) AOApplication main_app(argc, argv); + AOApplication::addLibraryPath(AOApplication::applicationDirPath() + "/lib"); + QSettings *configini = main_app.configini; QPluginLoader apngPlugin("qapng"); diff --git a/src/packet_distribution.cpp b/src/packet_distribution.cpp index 17e57bf..0bc3814 100644 --- a/src/packet_distribution.cpp +++ b/src/packet_distribution.cpp @@ -2,7 +2,6 @@ #include "courtroom.h" #include "debug_functions.h" -#include "encryption_functions.h" #include "hardware_functions.h" #include "lobby.h" #include "networkmanager.h" @@ -120,16 +119,11 @@ void AOApplication::server_packet_received(AOPacket *p_packet) if (f_contents.size() == 0) goto end; - // you may ask where 322 comes from. that would be a good question. - s_decryptor = fanta_decrypt(f_contents.at(0), 322).toUInt(); - // default(legacy) values - encryption_needed = true; yellow_text_enabled = false; prezoom_enabled = false; flipping_enabled = false; custom_objection_enabled = false; - improved_loading_enabled = false; desk_mod_enabled = false; evidence_enabled = false; cccc_ic_support_enabled = false; @@ -139,10 +133,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet) looping_sfx_support_enabled = false; additive_enabled = false; effects_enabled = false; - - // workaround for tsuserver4 - if (f_contents.at(0) == "NOENCRYPT") - encryption_needed = false; + y_offset_enabled = false; QString f_hdid; f_hdid = get_hdid(); @@ -176,12 +167,10 @@ void AOApplication::server_packet_received(AOPacket *p_packet) } } else if (header == "FL") { - // encryption_needed = true; yellow_text_enabled = false; prezoom_enabled = false; flipping_enabled = false; custom_objection_enabled = false; - improved_loading_enabled = false; desk_mod_enabled = false; evidence_enabled = false; cccc_ic_support_enabled = false; @@ -191,6 +180,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet) looping_sfx_support_enabled = false; additive_enabled = false; effects_enabled = false; + expanded_desk_mods_enabled = false; if (f_packet.contains("yellowtext", Qt::CaseInsensitive)) yellow_text_enabled = true; if (f_packet.contains("prezoom", Qt::CaseInsensitive)) @@ -199,10 +189,6 @@ void AOApplication::server_packet_received(AOPacket *p_packet) flipping_enabled = true; if (f_packet.contains("customobjections", Qt::CaseInsensitive)) custom_objection_enabled = true; - if (f_packet.contains("fastloading", Qt::CaseInsensitive)) - improved_loading_enabled = true; - if (f_packet.contains("noencryption", Qt::CaseInsensitive)) - encryption_needed = false; if (f_packet.contains("deskmod", Qt::CaseInsensitive)) desk_mod_enabled = true; if (f_packet.contains("evidence", Qt::CaseInsensitive)) @@ -221,6 +207,10 @@ void AOApplication::server_packet_received(AOPacket *p_packet) additive_enabled = true; if (f_packet.contains("effects", Qt::CaseInsensitive)) effects_enabled = true; + if (f_packet.contains("y_offset", Qt::CaseInsensitive)) + y_offset_enabled = true; + if (f_packet.contains("expanded_desk_mods", Qt::CaseInsensitive)) + expanded_desk_mods_enabled = true; } else if (header == "PN") { if (f_contents.size() < 2) @@ -228,6 +218,11 @@ void AOApplication::server_packet_received(AOPacket *p_packet) w_lobby->set_player_count(f_contents.at(0).toInt(), f_contents.at(1).toInt()); + + if (w_lobby->doubleclicked) { + send_server_packet(new AOPacket("askchaa#%")); + w_lobby->doubleclicked = false; + } } else if (header == "SI") { if (f_contents.size() != 3) @@ -283,11 +278,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet) AOPacket *f_packet; - if (improved_loading_enabled) - f_packet = new AOPacket("RC#%"); - else - f_packet = new AOPacket("askchar2#%"); - + f_packet = new AOPacket("RC#%"); send_server_packet(f_packet); // Remove any characters not accepted in folder names for the server_name @@ -308,152 +299,6 @@ void AOApplication::server_packet_received(AOPacket *p_packet) discord->state_server(server_name.toStdString(), hash.result().toBase64().toStdString()); } - else if (header == "CI") { - if (!courtroom_constructed) - goto end; - - for (int n_element = 0; n_element < f_contents.size(); n_element += 2) { - if (f_contents.at(n_element).toInt() != loaded_chars) - break; - - // this means we are on the last element and checking n + 1 element will - // be game over so - if (n_element == f_contents.size() - 1) - break; - - QStringList sub_elements = f_contents.at(n_element + 1).split("&"); - if (sub_elements.size() < 2) - break; - - char_type f_char; - f_char.name = sub_elements.at(0); - f_char.description = sub_elements.at(1); - f_char.evidence_string = sub_elements.at(3); - // temporary. the CharsCheck packet sets this properly - f_char.taken = false; - - ++loaded_chars; - - w_lobby->set_loading_text(tr("Loading chars:\n%1/%2") - .arg(QString::number(loaded_chars)) - .arg(QString::number(char_list_size))); - - w_courtroom->append_char(f_char); - - int total_loading_size = - char_list_size * 2 + evidence_list_size + music_list_size; - int loading_value = int( - ((loaded_chars + generated_chars + loaded_music + loaded_evidence) / - static_cast(total_loading_size)) * - 100); - w_lobby->set_loading_value(loading_value); - } - - if (improved_loading_enabled) - send_server_packet(new AOPacket("RE#%")); - else { - QString next_packet_number = - QString::number(((loaded_chars - 1) / 10) + 1); - send_server_packet(new AOPacket("AN#" + next_packet_number + "#%")); - } - } - else if (header == "EI") { - if (!courtroom_constructed) - goto end; - - // +1 because evidence starts at 1 rather than 0 for whatever reason - // enjoy fanta - if (f_contents.at(0).toInt() != loaded_evidence + 1) - goto end; - - if (f_contents.size() < 2) - goto end; - - QStringList sub_elements = f_contents.at(1).split("&"); - if (sub_elements.size() < 4) - goto end; - - evi_type f_evi; - f_evi.name = sub_elements.at(0); - f_evi.description = sub_elements.at(1); - // no idea what the number at position 2 is. probably an identifier? - f_evi.image = sub_elements.at(3); - - ++loaded_evidence; - - w_lobby->set_loading_text(tr("Loading evidence:\n%1/%2") - .arg(QString::number(loaded_evidence)) - .arg(QString::number(evidence_list_size))); - - w_courtroom->append_evidence(f_evi); - - int total_loading_size = - char_list_size * 2 + evidence_list_size + music_list_size; - int loading_value = - int(((loaded_chars + generated_chars + loaded_music + loaded_evidence) / - static_cast(total_loading_size)) * - 100); - w_lobby->set_loading_value(loading_value); - - QString next_packet_number = QString::number(loaded_evidence); - send_server_packet(new AOPacket("AE#" + next_packet_number + "#%")); - } - else if (header == "EM") { - if (!courtroom_constructed) - goto end; - - bool musics_time = false; - int areas = 0; - - for (int n_element = 0; n_element < f_contents.size(); n_element += 2) { - if (f_contents.at(n_element).toInt() != loaded_music) - break; - - if (n_element == f_contents.size() - 1) - break; - - QString f_music = f_contents.at(n_element + 1); - - ++loaded_music; - - w_lobby->set_loading_text(tr("Loading music:\n%1/%2") - .arg(QString::number(loaded_music)) - .arg(QString::number(music_list_size))); - - if (musics_time) { - w_courtroom->append_music(f_music); - } - else { - if (f_music.endsWith(".wav") || f_music.endsWith(".mp3") || - f_music.endsWith(".mp4") || f_music.endsWith(".ogg") || - f_music.endsWith(".opus")) { - musics_time = true; - areas--; - w_courtroom->fix_last_area(); - w_courtroom->append_music(f_music); - } - else { - w_courtroom->append_area(f_music); - areas++; - } - } - - for (int area_n = 0; area_n < areas; area_n++) { - w_courtroom->arup_append(0, "Unknown", "Unknown", "Unknown"); - } - - int total_loading_size = - char_list_size * 2 + evidence_list_size + music_list_size; - int loading_value = int( - ((loaded_chars + generated_chars + loaded_music + loaded_evidence) / - static_cast(total_loading_size)) * - 100); - w_lobby->set_loading_value(loading_value); - } - - QString next_packet_number = QString::number(((loaded_music - 1) / 10) + 1); - send_server_packet(new AOPacket("AM#" + next_packet_number + "#%")); - } else if (header == "CharsCheck") { if (!courtroom_constructed) goto end; @@ -621,6 +466,8 @@ void AOApplication::server_packet_received(AOPacket *p_packet) if (f_contents.size() < 3) goto end; + w_courtroom->enter_courtroom(); + if (courtroom_constructed) w_courtroom->update_character(f_contents.at(2).toInt()); } @@ -691,6 +538,11 @@ void AOApplication::server_packet_received(AOPacket *p_packet) if (courtroom_constructed && f_contents.size() > 0) w_courtroom->set_mute(false, f_contents.at(0).toInt()); } + else if (header == "BB") { + if (courtroom_constructed && f_contents.size() >= 1) { + call_notice(f_contents.at(0)); + } + } else if (header == "KK") { if (courtroom_constructed && f_contents.size() >= 1) { call_notice(tr("You have been kicked from the server.\nReason: %1") @@ -803,19 +655,9 @@ void AOApplication::send_server_packet(AOPacket *p_packet, bool encoded) QString f_packet = p_packet->to_string(); - if (encryption_needed) { -#ifdef DEBUG_NETWORK - qDebug() << "S(e):" << f_packet; -#endif - - p_packet->encrypt_header(s_decryptor); - f_packet = p_packet->to_string(); - } - else { #ifdef DEBUG_NETWORK qDebug() << "S:" << f_packet; #endif - } net_manager->ship_server_packet(f_packet); diff --git a/src/path_functions.cpp b/src/path_functions.cpp index 10c8ae5..c6c73a8 100644 --- a/src/path_functions.cpp +++ b/src/path_functions.cpp @@ -44,61 +44,40 @@ QString AOApplication::get_data_path() { return get_base_path() + "data/"; } QString AOApplication::get_default_theme_path(QString p_file) { QString path = get_base_path() + "themes/default/" + p_file; -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } QString AOApplication::get_custom_theme_path(QString p_theme, QString p_file) { QString path = get_base_path() + "themes/" + p_theme + "/" + p_file; -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } QString AOApplication::get_theme_path(QString p_file) { QString path = get_base_path() + "themes/" + current_theme + "/" + p_file; -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } QString AOApplication::get_character_path(QString p_char, QString p_file) { QString path = get_base_path() + "characters/" + p_char + "/" + p_file; -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } QString AOApplication::get_sounds_path(QString p_file) { QString path = get_base_path() + "sounds/general/" + p_file; -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } QString AOApplication::get_music_path(QString p_song) { + if (p_song.startsWith("http")) { + return p_song; // url + } QString path = get_base_path() + "sounds/music/" + p_song; -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } QString AOApplication::get_background_path(QString p_file) @@ -106,11 +85,7 @@ QString AOApplication::get_background_path(QString p_file) QString path = get_base_path() + "background/" + w_courtroom->get_current_background() + "/" + p_file; if (courtroom_constructed) { -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } return get_default_background_path(p_file); } @@ -118,33 +93,30 @@ QString AOApplication::get_background_path(QString p_file) QString AOApplication::get_default_background_path(QString p_file) { QString path = get_base_path() + "background/default/" + p_file; -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } QString AOApplication::get_evidence_path(QString p_file) { QString path = get_base_path() + "evidence/" + p_file; -#ifndef CASE_SENSITIVE_FILESYSTEM - return path; -#else return get_case_sensitive_path(path); -#endif } QString AOApplication::get_case_sensitive_path(QString p_file) { + QFileInfo file(p_file); + QString file_basename = file.fileName(); + + // no path traversal above base folder + if (!(file.absolutePath().startsWith(get_base_path()))) + return get_base_path() + file_basename; + + #ifdef CASE_SENSITIVE_FILESYSTEM // first, check to see if it's actually there (also serves as base case for // recursion) if (exists(p_file)) return p_file; - QFileInfo file(p_file); - - QString file_basename = file.fileName(); QString file_parent_dir = get_case_sensitive_path(file.absolutePath()); // second, does it exist in the new parent dir? @@ -163,4 +135,7 @@ QString AOApplication::get_case_sensitive_path(QString p_file) // if nothing is found, let the caller handle the missing file return file_parent_dir + "/" + file_basename; +#else + return p_file; +#endif } diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp index 8247fd8..3524d87 100644 --- a/src/text_file_functions.cpp +++ b/src/text_file_functions.cpp @@ -73,6 +73,13 @@ bool AOApplication::get_log_timestamp() return result.startsWith("true"); } +bool AOApplication::get_log_ic_actions() +{ + QString result = + configini->value("log_ic_actions", "true").value(); + return result.startsWith("true"); +} + bool AOApplication::get_showname_enabled_by_default() { QString result = @@ -618,12 +625,15 @@ QString AOApplication::get_char_side(QString p_char) return f_result; } -QString AOApplication::get_gender(QString p_char) +QString AOApplication::get_blips(QString p_char) { - QString f_result = read_char_ini(p_char, "gender", "Options"); + QString f_result = read_char_ini(p_char, "blips", "Options"); - if (f_result == "") - f_result = "male"; + if (f_result == "") { + f_result = read_char_ini(p_char, "gender", "Options"); // not very PC, FanatSors + if (f_result == "") + f_result = "male"; + } if (!file_exists(get_sfx_suffix(get_sounds_path(f_result)))) { if (file_exists(get_sfx_suffix(get_sounds_path("../blips/" + f_result)))) @@ -636,6 +646,8 @@ QString AOApplication::get_gender(QString p_char) QString AOApplication::get_chat(QString p_char) { + if (p_char == "default") + return "default"; QString f_result = read_char_ini(p_char, "chat", "Options"); // handling the correct order of chat is a bit complicated, we let the caller