diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/aoapplication.cpp | 23 | ||||
| -rw-r--r-- | src/aoblipplayer.cpp | 22 | ||||
| -rw-r--r-- | src/aobutton.cpp | 25 | ||||
| -rw-r--r-- | src/aocharbutton.cpp | 19 | ||||
| -rw-r--r-- | src/aocharmovie.cpp | 370 | ||||
| -rw-r--r-- | src/aoemotebutton.cpp | 33 | ||||
| -rw-r--r-- | src/aoevidencebutton.cpp | 51 | ||||
| -rw-r--r-- | src/aoevidencedisplay.cpp | 64 | ||||
| -rw-r--r-- | src/aoimage.cpp | 33 | ||||
| -rw-r--r-- | src/aolineedit.cpp | 21 | ||||
| -rw-r--r-- | src/aomovie.cpp | 102 | ||||
| -rw-r--r-- | src/aomusicplayer.cpp | 167 | ||||
| -rw-r--r-- | src/aooptionsdialog.cpp | 331 | ||||
| -rw-r--r-- | src/aoscene.cpp | 118 | ||||
| -rw-r--r-- | src/aosfxplayer.cpp | 104 | ||||
| -rw-r--r-- | src/aotextarea.cpp | 12 | ||||
| -rw-r--r-- | src/charselect.cpp | 22 | ||||
| -rw-r--r-- | src/chatlogpiece.cpp | 14 | ||||
| -rw-r--r-- | src/courtroom.cpp | 3269 | ||||
| -rw-r--r-- | src/debug_functions.cpp | 12 | ||||
| -rw-r--r-- | src/emotes.cpp | 63 | ||||
| -rw-r--r-- | src/evidence.cpp | 564 | ||||
| -rw-r--r-- | src/hardware_functions.cpp | 21 | ||||
| -rw-r--r-- | src/lobby.cpp | 275 | ||||
| -rw-r--r-- | src/main.cpp | 29 | ||||
| -rw-r--r-- | src/packet_distribution.cpp | 128 | ||||
| -rw-r--r-- | src/scrolltext.cpp | 125 | ||||
| -rw-r--r-- | src/text_file_functions.cpp | 577 |
28 files changed, 4799 insertions, 1795 deletions
diff --git a/src/aoapplication.cpp b/src/aoapplication.cpp index 9bed218..e1e9e7f 100644 --- a/src/aoapplication.cpp +++ b/src/aoapplication.cpp @@ -126,7 +126,7 @@ void AOApplication::add_favorite_server(int p_server) void AOApplication::server_disconnected() { if (courtroom_constructed) { - call_notice("Disconnected from server."); + call_notice(tr("Disconnected from server.")); construct_lobby(); destruct_courtroom(); } @@ -149,18 +149,19 @@ void AOApplication::ms_connect_finished(bool connected, bool will_retry) if (will_retry) { if (lobby_constructed) w_lobby->append_error( - "Error connecting to master server. Will try again in " + - QString::number(net_manager->ms_reconnect_delay) + " seconds."); + tr("Error connecting to master server. Will try again in %1 " + "seconds.") + .arg(QString::number(net_manager->ms_reconnect_delay))); } else { - call_error("There was an error connecting to the master server.\n" - "We deploy multiple master servers to mitigate any possible " - "downtime, " - "but the client appears to have exhausted all possible " - "methods of finding " - "and connecting to one.\n" - "Please check your Internet connection and firewall, and " - "please try again."); + call_error(tr("There was an error connecting to the master server.\n" + "We deploy multiple master servers to mitigate any " + "possible downtime, " + "but the client appears to have exhausted all possible " + "methods of finding " + "and connecting to one.\n" + "Please check your Internet connection and firewall, and " + "please try again.")); } } } diff --git a/src/aoblipplayer.cpp b/src/aoblipplayer.cpp index 0355414..39158fd 100644 --- a/src/aoblipplayer.cpp +++ b/src/aoblipplayer.cpp @@ -9,13 +9,17 @@ AOBlipPlayer::AOBlipPlayer(QWidget *parent, AOApplication *p_ao_app) void AOBlipPlayer::set_blips(QString p_sfx) { - QString f_path = ao_app->get_sounds_path(p_sfx); + QString f_path = ao_app->get_sfx_suffix(ao_app->get_sounds_path(p_sfx)); for (int n_stream = 0; n_stream < 5; ++n_stream) { BASS_StreamFree(m_stream_list[n_stream]); - m_stream_list[n_stream] = BASS_StreamCreateFile( - FALSE, f_path.utf16(), 0, 0, BASS_UNICODE | BASS_ASYNCFILE); + if (f_path.endsWith(".opus")) + m_stream_list[n_stream] = BASS_OPUS_StreamCreateFile( + FALSE, f_path.utf16(), 0, 0, BASS_UNICODE | BASS_ASYNCFILE); + else + m_stream_list[n_stream] = BASS_StreamCreateFile( + FALSE, f_path.utf16(), 0, 0, BASS_UNICODE | BASS_ASYNCFILE); } set_volume(m_volume); @@ -23,6 +27,10 @@ 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) @@ -38,7 +46,9 @@ void AOBlipPlayer::set_volume(int p_value) { m_volume = p_value; - float volume = p_value / 100.0f; +void AOBlipPlayer::set_volume_internal(qreal p_value) +{ + float volume = static_cast<float>(p_value); for (int n_stream = 0; n_stream < 5; ++n_stream) { BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume); @@ -88,5 +98,7 @@ void AOBlipPlayer::set_blips(QString p_sfx) {} void AOBlipPlayer::blip_tick() {} -void AOBlipPlayer::set_volume(int p_value) {} +void AOBlipPlayer::set_volume(qreal p_value) {} + +void AOBlipPlayer::set_volume_internal(qreal p_value) {} #endif diff --git a/src/aobutton.cpp b/src/aobutton.cpp index fee946a..dc2d881 100644 --- a/src/aobutton.cpp +++ b/src/aobutton.cpp @@ -13,11 +13,26 @@ AOButton::~AOButton() {} void AOButton::set_image(QString p_image) { - QString image_path = ao_app->get_theme_path(p_image); - QString default_image_path = ao_app->get_default_theme_path(p_image); + QString image_path = + ao_app->get_static_image_suffix(ao_app->get_theme_path(p_image)); + QString default_image_path = + ao_app->get_static_image_suffix(ao_app->get_default_theme_path(p_image)); - if (file_exists(image_path)) - this->setStyleSheet("border-image:url(\"" + image_path + "\")"); + if (file_exists(image_path)) { + this->setText(""); + this->setStyleSheet("QPushButton { border-image: url(\"" + image_path + + "\") 0 0 0 0 stretch stretch; }" + "QToolTip { background-image: url(); color: #000000; " + "background-color: #ffffff; border: 0px; }"); + } + else if (file_exists(default_image_path)) { + this->setText(""); + this->setStyleSheet("QPushButton { border-image: url(\"" + + default_image_path + + "\"); }" + "QToolTip { background-image: url(); color: #000000; " + "background-color: #ffffff; border: 0px; }"); + } else - this->setStyleSheet("border-image:url(\"" + default_image_path + "\")"); + return; } diff --git a/src/aocharbutton.cpp b/src/aocharbutton.cpp index 6c27e3e..844c959 100644 --- a/src/aocharbutton.cpp +++ b/src/aocharbutton.cpp @@ -17,7 +17,7 @@ AOCharButton::AOCharButton(QWidget *parent, AOApplication *p_ao_app, int x_pos, ui_taken = new AOImage(this, ao_app); ui_taken->resize(60, 60); - ui_taken->set_image("char_taken.png"); + ui_taken->set_image("char_taken"); ui_taken->setAttribute(Qt::WA_TransparentForMouseEvents); ui_taken->hide(); @@ -30,7 +30,7 @@ AOCharButton::AOCharButton(QWidget *parent, AOApplication *p_ao_app, int x_pos, ui_selector = new AOImage(parent, ao_app); ui_selector->resize(62, 62); ui_selector->move(x_pos - 1, y_pos - 1); - ui_selector->set_image("char_selector.png"); + ui_selector->set_image("char_selector"); ui_selector->setAttribute(Qt::WA_TransparentForMouseEvents); ui_selector->hide(); } @@ -59,14 +59,21 @@ void AOCharButton::set_passworded() { ui_passworded->show(); } void AOCharButton::set_image(QString p_character) { - QString image_path = ao_app->get_character_path(p_character, "char_icon.png"); + QString image_path = ao_app->get_static_image_suffix( + ao_app->get_character_path(p_character, "char_icon")); this->setText(""); - if (file_exists(image_path)) - this->setStyleSheet("border-image:url(\"" + image_path + "\")"); + if (file_exists(image_path)) { + this->setStyleSheet("QPushButton { border-image: url(\"" + image_path + + "\") 0 0 0 0 stretch stretch; }" + "QToolTip { background-image: url(); color: #000000; " + "background-color: #ffffff; border: 0px; }"); + } else { - this->setStyleSheet("border-image:url()"); + this->setStyleSheet("QPushButton { border-image: url(); }" + "QToolTip { background-image: url(); color: #000000; " + "background-color: #ffffff; border: 0px; }"); this->setText(p_character); } } diff --git a/src/aocharmovie.cpp b/src/aocharmovie.cpp index 3e0a4de..252aab5 100644 --- a/src/aocharmovie.cpp +++ b/src/aocharmovie.cpp @@ -8,150 +8,274 @@ AOCharMovie::AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) { ao_app = p_ao_app; - - m_movie = new QMovie(this); - preanim_timer = new QTimer(this); preanim_timer->setSingleShot(true); - connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frame_change(int))); - connect(preanim_timer, SIGNAL(timeout()), this, SLOT(timer_done())); -} - -void AOCharMovie::play(QString p_char, QString p_emote, QString emote_prefix) -{ - QString original_path = - ao_app->get_character_path(p_char, emote_prefix + p_emote + ".gif"); - QString alt_path = ao_app->get_character_path(p_char, p_emote + ".png"); - QString apng_path = - ao_app->get_character_path(p_char, emote_prefix + p_emote + ".apng"); - QString placeholder_path = ao_app->get_theme_path("placeholder.gif"); - QString placeholder_default_path = - ao_app->get_default_theme_path("placeholder.gif"); - QString gif_path; - - if (file_exists(apng_path)) - gif_path = apng_path; - else if (file_exists(original_path)) - gif_path = original_path; - else if (file_exists(alt_path)) - gif_path = alt_path; - else if (file_exists(placeholder_path)) - gif_path = placeholder_path; - else - gif_path = placeholder_default_path; - - m_movie->stop(); - m_movie->setFileName(gif_path); - - QImageReader *reader = new QImageReader(gif_path); - - movie_frames.clear(); - QImage f_image = reader->read(); - while (!f_image.isNull()) { - if (m_flipped) - movie_frames.append(f_image.mirrored(true, false)); - else - movie_frames.append(f_image); - f_image = reader->read(); - } - - delete reader; + ticker = new QTimer(this); + ticker->setTimerType(Qt::PreciseTimer); + ticker->setSingleShot(false); + connect(ticker, SIGNAL(timeout()), this, SLOT(movie_ticker())); - this->show(); - m_movie->start(); + // connect(m_movie, SIGNAL(frameChanged(int)), this, + // SLOT(frame_change(int))); + connect(preanim_timer, SIGNAL(timeout()), this, SLOT(preanim_done())); } -void AOCharMovie::play_pre(QString p_char, QString p_emote, int duration) +void AOCharMovie::load_image(QString p_char, QString p_emote, + QString emote_prefix) { - QString gif_path = ao_app->get_character_path(p_char, p_emote); +#ifdef DEBUG_CHARMOVIE + actual_time.restart(); +#endif + QString emote_path; + QList<QString> pathlist; + pathlist = { + ao_app->get_image_suffix(ao_app->get_character_path( + p_char, emote_prefix + p_emote)), // Default path + 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_theme_path("placeholder")), // Theme placeholder path + ao_app->get_image_suffix(ao_app->get_default_theme_path( + "placeholder")), // Default theme placeholder path + }; + + for (QString path : pathlist) { + if (file_exists(path)) { + emote_path = path; + break; + } + } - m_movie->stop(); this->clear(); - m_movie->setFileName(gif_path); - m_movie->jumpToFrame(0); + ticker->stop(); + preanim_timer->stop(); + movie_frames.clear(); + movie_delays.clear(); + movie_effects.clear(); - int full_duration = duration * time_mod; - int real_duration = 0; + if (!file_exists(emote_path)) + return; - play_once = false; + m_reader->setFileName(emote_path); + QPixmap f_pixmap = this->get_pixmap(m_reader->read()); + int f_delay = m_reader->nextImageDelay(); + + frame = 0; + max_frames = m_reader->imageCount(); - for (int n_frame = 0; n_frame < m_movie->frameCount(); ++n_frame) { - real_duration += m_movie->nextFrameDelay(); - m_movie->jumpToFrame(n_frame + 1); + this->set_frame(f_pixmap); + this->show(); + if (max_frames > 1) { + movie_frames.append(f_pixmap); + movie_delays.append(f_delay); } -#ifdef DEBUG_GIF - qDebug() << "full_duration: " << full_duration; - qDebug() << "real_duration: " << real_duration; -#endif + m_char = p_char; + m_emote = emote_prefix + p_emote; - double percentage_modifier = 100.0; + if (network_strings.size() > 0) // our FX overwritten by networked ones + this->load_network_effects(); + else // Use default ini FX + this->load_effects(); +#ifdef DEBUG_CHARMOVIE + qDebug() << max_frames << "Setting image to " << emote_path + << "Time taken to process image:" << actual_time.elapsed(); - if (real_duration != 0 && duration != 0) { - double modifier = full_duration / static_cast<double>(real_duration); - percentage_modifier = 100 / modifier; + actual_time.restart(); +#endif +} - if (percentage_modifier > 100.0) - percentage_modifier = 100.0; +void AOCharMovie::load_effects() +{ + movie_effects.clear(); + movie_effects.resize(max_frames); + for (int e_frame = 0; e_frame < max_frames; ++e_frame) { + QString effect = ao_app->get_screenshake_frame(m_char, m_emote, e_frame); + if (effect != "") { + movie_effects[e_frame].append("shake"); + } + + effect = ao_app->get_flash_frame(m_char, m_emote, e_frame); + if (effect != "") { + movie_effects[e_frame].append("flash"); + } + + effect = ao_app->get_sfx_frame(m_char, m_emote, e_frame); + if (effect != "") { + movie_effects[e_frame].append("sfx^" + effect); + } } +} -#ifdef DEBUG_GIF - qDebug() << "% mod: " << percentage_modifier; -#endif - - if (full_duration == 0 || full_duration >= real_duration) { - play_once = true; +void AOCharMovie::load_network_effects() +{ + movie_effects.clear(); + movie_effects.resize(max_frames); + // Order is important!!! + QStringList effects_list = {"shake", "flash", "sfx^"}; + + // Determines which list is smaller - effects_list or network_strings - and + // uses it as basis for the loop. This way, incomplete network_strings would + // still be parsed, and excess/unaccounted for networked information is + // omitted. + int effects_size = qMin(effects_list.size(), network_strings.size()); + + for (int i = 0; i < effects_size; ++i) { + QString netstring = network_strings.at(i); + QStringList emote_splits = netstring.split("^"); + foreach (QString emote, emote_splits) { + QStringList parsed = emote.split("|"); + if (parsed.size() <= 0 || parsed.at(0) != m_emote) + continue; + foreach (QString frame_data, parsed) { + QStringList frame_split = frame_data.split("="); + if (frame_split.size() <= + 1) // We might still be hanging at the emote itself (entry 0). + continue; + int f_frame = frame_split.at(0).toInt(); + if (f_frame >= max_frames) { + qDebug() << "Warning: out of bounds" << effects_list[i] << "frame" + << f_frame << "out of" << max_frames << "for" << m_char + << m_emote; + continue; + } + QString f_data = frame_split.at(1); + if (f_data != "") { + QString effect = effects_list[i]; + if (effect == "sfx^") // Currently the only frame result that feeds us + // data, let's yank it in. + effect += f_data; + qDebug() << effect << f_data << "frame" << f_frame << "for" << m_char + << m_emote; + movie_effects[f_frame].append(effect); + } + } + } } - else { - play_once = false; - preanim_timer->start(full_duration); +} + +void AOCharMovie::play() +{ + play_frame_effect(frame); + if (max_frames <= 1) { + if (play_once) + ticker->start(60); } + else + ticker->start(this->get_frame_delay(movie_delays[frame])); +} - m_movie->setSpeed(static_cast<int>(percentage_modifier)); - play(p_char, p_emote, ""); +void AOCharMovie::play_pre(QString p_char, QString p_emote, int duration) +{ + load_image(p_char, p_emote, ""); + // As much as I'd like to screw around with [Time] durations modifying the + // animation speed, I don't think I can reliably do that, not without looping + // through all frames in the image at least - which causes lag. So for now it + // simply ends the preanimation early instead. + play_once = true; + if (duration > + 0) // It's -1 if there's no definition in [Time] for it. In which case, it + // will let the animation run out in full. Duration 0 does the same. + preanim_timer->start(duration * + time_mod); // This timer will not fire if the animation + // finishes earlier than that + play(); } void AOCharMovie::play_talking(QString p_char, QString p_emote) { - QString gif_path = ao_app->get_character_path(p_char, "(b)" + p_emote); - - m_movie->stop(); - this->clear(); - m_movie->setFileName(gif_path); - play_once = false; - m_movie->setSpeed(100); - play(p_char, p_emote, "(b)"); + load_image(p_char, p_emote, "(b)"); + play(); } void AOCharMovie::play_idle(QString p_char, QString p_emote) { - QString gif_path = ao_app->get_character_path(p_char, "(a)" + p_emote); + play_once = false; + load_image(p_char, p_emote, "(a)"); + play(); +} - m_movie->stop(); - this->clear(); - m_movie->setFileName(gif_path); +void AOCharMovie::play_frame_effect(int frame) +{ + if (frame < max_frames) { + foreach (QString effect, movie_effects[frame]) { + if (effect == "shake") { + shake(); +#ifdef DEBUG_CHARMOVIE + qDebug() << "Attempting to play shake on frame" << frame; +#endif + } - play_once = false; - m_movie->setSpeed(100); - play(p_char, p_emote, "(a)"); + if (effect == "flash") { + flash(); +#ifdef DEBUG_CHARMOVIE + qDebug() << "Attempting to play flash on frame" << frame; +#endif + } + + if (effect.startsWith("sfx^")) { + QString sfx = effect.section("^", 1); + play_sfx(sfx); +#ifdef DEBUG_CHARMOVIE + qDebug() << "Attempting to play sfx" << sfx << "on frame" << frame; +#endif + } + } + } } void AOCharMovie::stop() { // for all intents and purposes, stopping is the same as hiding. at no point // do we want a frozen gif to display - m_movie->stop(); + ticker->stop(); preanim_timer->stop(); this->hide(); } +QPixmap AOCharMovie::get_pixmap(QImage image) +{ + QPixmap f_pixmap; + if (m_flipped) + f_pixmap = QPixmap::fromImage(image.mirrored(true, false)); + else + f_pixmap = QPixmap::fromImage(image); + // auto aspect_ratio = Qt::KeepAspectRatio; + auto transform_mode = Qt::FastTransformation; + if (f_pixmap.height() > f_h) // We are downscaling, use anti-aliasing. + transform_mode = Qt::SmoothTransformation; + + f_pixmap = f_pixmap.scaledToHeight(f_h, transform_mode); + this->resize(f_pixmap.size()); + + return f_pixmap; +} + +void AOCharMovie::set_frame(QPixmap f_pixmap) +{ + this->setPixmap(f_pixmap); + QLabel::move( + x + (f_w - f_pixmap.width()) / 2, + y + (f_h - f_pixmap.height())); // Always center horizontally, always put + // at the bottom vertically +} + void AOCharMovie::combo_resize(int w, int h) { QSize f_size(w, h); + f_w = w; + f_h = h; this->resize(f_size); - m_movie->setScaledSize(f_size); +} + +int AOCharMovie::get_frame_delay(int delay) +{ + return static_cast<int>(double(delay) * double(speed / 100)); } void AOCharMovie::move(int ax, int ay) @@ -161,31 +285,39 @@ void AOCharMovie::move(int ax, int ay) QLabel::move(x, y); } -void AOCharMovie::frame_change(int n_frame) +void AOCharMovie::movie_ticker() { - - if (movie_frames.size() > n_frame) { - QPixmap f_pixmap = QPixmap::fromImage(movie_frames.at(n_frame)); - auto aspect_ratio = Qt::KeepAspectRatio; - - if (f_pixmap.size().width() > f_pixmap.size().height()) - aspect_ratio = Qt::KeepAspectRatioByExpanding; - - if (f_pixmap.size().width() > this->size().width() || - f_pixmap.size().height() > this->size().height()) - this->setPixmap(f_pixmap.scaled(this->width(), this->height(), - aspect_ratio, Qt::SmoothTransformation)); + ++frame; + if (frame >= max_frames) { + if (play_once) { + preanim_done(); + return; + } else - this->setPixmap(f_pixmap.scaled(this->width(), this->height(), - aspect_ratio, Qt::FastTransformation)); - - QLabel::move(x + (this->width() - this->pixmap()->width()) / 2, y); + frame = 0; } - - if (m_movie->frameCount() - 1 == n_frame && play_once) { - preanim_timer->start(m_movie->nextFrameDelay()); - m_movie->stop(); + // qint64 difference = elapsed - movie_delays[frame]; + if (frame >= movie_frames.size()) { + m_reader->jumpToImage(frame); + movie_frames.resize(frame + 1); + movie_frames[frame] = this->get_pixmap(m_reader->read()); + movie_delays.resize(frame + 1); + movie_delays[frame] = m_reader->nextImageDelay(); } + +#ifdef DEBUG_CHARMOVIE + qDebug() << frame << movie_delays[frame] + << "actual time taken from last frame:" << actual_time.restart(); +#endif + + this->set_frame(movie_frames[frame]); + play_frame_effect(frame); + ticker->setInterval(this->get_frame_delay(movie_delays[frame])); } -void AOCharMovie::timer_done() { done(); } +void AOCharMovie::preanim_done() +{ + ticker->stop(); + preanim_timer->stop(); + done(); +} diff --git a/src/aoemotebutton.cpp b/src/aoemotebutton.cpp index 29329c9..abec526 100644 --- a/src/aoemotebutton.cpp +++ b/src/aoemotebutton.cpp @@ -3,32 +3,43 @@ #include "file_functions.h" AOEmoteButton::AOEmoteButton(QWidget *p_parent, AOApplication *p_ao_app, - int p_x, int p_y) + int p_x, int p_y, int p_w, int p_h) : QPushButton(p_parent) { parent = p_parent; ao_app = p_ao_app; this->move(p_x, p_y); - this->resize(40, 40); + this->resize(p_w, p_h); connect(this, SIGNAL(clicked()), this, SLOT(on_clicked())); } -void AOEmoteButton::set_image(QString p_char, int p_emote, QString suffix) +void AOEmoteButton::set_image(QString p_image, QString p_emote_comment) { - QString emotion_number = QString::number(p_emote + 1); - QString image_path = ao_app->get_character_path( - p_char, "emotions/button" + emotion_number + suffix); - - if (file_exists(image_path)) { + if (file_exists(p_image)) { this->setText(""); - this->setStyleSheet("border-image:url(\"" + image_path + "\")"); + this->setStyleSheet( + "QPushButton { border-image: url(\"" + p_image + + "\") 0 0 0 0 stretch stretch; }" + "QToolTip { color: #000000; background-color: #ffffff; border: 0px; }"); } else { - this->setText(ao_app->get_emote_comment(p_char, p_emote)); - this->setStyleSheet("border-image:url(\"\")"); + this->setText(p_emote_comment); + this->setStyleSheet("QPushButton { border-image: url(); }" + "QToolTip { background-image: url(); color: #000000; " + "background-color: #ffffff; border: 0px; }"); } } +void AOEmoteButton::set_char_image(QString p_char, int p_emote, QString suffix) +{ + QString emotion_number = QString::number(p_emote + 1); + QString image_path = + ao_app->get_static_image_suffix(ao_app->get_character_path( + p_char, "emotions/button" + emotion_number + suffix)); + + this->set_image(image_path, ao_app->get_emote_comment(p_char, p_emote)); +} + void AOEmoteButton::on_clicked() { emote_clicked(m_id); } diff --git a/src/aoevidencebutton.cpp b/src/aoevidencebutton.cpp index a94519e..f85a223 100644 --- a/src/aoevidencebutton.cpp +++ b/src/aoevidencebutton.cpp @@ -3,51 +3,55 @@ #include "file_functions.h" AOEvidenceButton::AOEvidenceButton(QWidget *p_parent, AOApplication *p_ao_app, - int p_x, int p_y) + int p_x, int p_y, int p_w, int p_h) : QPushButton(p_parent) { ao_app = p_ao_app; m_parent = p_parent; - ui_selected = new AOImage(p_parent, ao_app); - ui_selected->resize(70, 70); - ui_selected->move(p_x, p_y); - ui_selected->set_image("evidence_selected.png"); + ui_selected = new AOImage(this, ao_app); + ui_selected->resize(p_w, p_h); + // ui_selected->move(p_x, p_y); + ui_selected->set_image("evidence_selected"); ui_selected->setAttribute(Qt::WA_TransparentForMouseEvents); ui_selected->hide(); - ui_selector = new AOImage(p_parent, ao_app); - ui_selector->resize(71, 71); - ui_selector->move(p_x - 1, p_y - 1); - ui_selector->set_image("evidence_selector.png"); + ui_selector = new AOImage(this, ao_app); + ui_selector->resize(p_w, p_h); + // ui_selector->move(p_x - 1, p_y - 1); + ui_selector->set_image("evidence_selector"); ui_selector->setAttribute(Qt::WA_TransparentForMouseEvents); ui_selector->hide(); this->move(p_x, p_y); - this->resize(70, 70); - this->setAcceptDrops(true); + this->resize(p_w, p_h); + // this->setAcceptDrops(true); connect(this, SIGNAL(clicked()), this, SLOT(on_clicked())); } -void AOEvidenceButton::reset() -{ - this->hide(); - ui_selected->hide(); - ui_selector->hide(); -} - void AOEvidenceButton::set_image(QString p_image) { QString image_path = ao_app->get_evidence_path(p_image); - - if (file_exists(image_path)) { + if (file_exists(p_image)) { + this->setText(""); + this->setStyleSheet( + "QPushButton { border-image: url(\"" + p_image + + "\") 0 0 0 0 stretch stretch; }" + "QToolTip { color: #000000; background-color: #ffffff; border: 0px; }"); + } + else if (file_exists(image_path)) { this->setText(""); - this->setStyleSheet("border-image:url(\"" + image_path + "\")"); + this->setStyleSheet( + "QPushButton { border-image: url(\"" + image_path + + "\") 0 0 0 0 stretch stretch; }" + "QToolTip { color: #000000; background-color: #ffffff; border: 0px; }"); } else { this->setText(p_image); - this->setStyleSheet(""); + this->setStyleSheet("QPushButton { border-image: url(); }" + "QToolTip { background-image: url(); color: #000000; " + "background-color: #ffffff; border: 0px; }"); } } @@ -63,8 +67,7 @@ void AOEvidenceButton::set_theme_image(QString p_image) else final_image_path = default_image_path; - this->setText(""); - this->setStyleSheet("border-image:url(\"" + final_image_path + "\")"); + this->set_image(final_image_path); } void AOEvidenceButton::set_selected(bool p_selected) diff --git a/src/aoevidencedisplay.cpp b/src/aoevidencedisplay.cpp index 9dd062b..2ffea2c 100644 --- a/src/aoevidencedisplay.cpp +++ b/src/aoevidencedisplay.cpp @@ -8,13 +8,12 @@ AOEvidenceDisplay::AOEvidenceDisplay(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) { ao_app = p_ao_app; - - evidence_movie = new QMovie(this); evidence_icon = new QLabel(this); sfx_player = new AOSfxPlayer(this, ao_app); - connect(evidence_movie, SIGNAL(frameChanged(int)), this, - SLOT(frame_change(int))); + evidence_movie = new AOMovie(this, ao_app); + + connect(evidence_movie, SIGNAL(done()), this, SLOT(show_done())); } void AOEvidenceDisplay::show_evidence(QString p_evidence_image, @@ -24,64 +23,34 @@ void AOEvidenceDisplay::show_evidence(QString p_evidence_image, sfx_player->set_volume(p_volume); - QString f_evidence_path = ao_app->get_evidence_path(p_evidence_image); - - QPixmap f_pixmap(f_evidence_path); - QString final_gif_path; QString gif_name; QString icon_identifier; if (is_left_side) { icon_identifier = "left_evidence_icon"; - gif_name = "evidence_appear_left.gif"; + gif_name = "evidence_appear_left"; } else { icon_identifier = "right_evidence_icon"; - gif_name = "evidence_appear_right.gif"; + gif_name = "evidence_appear_right"; } + QString f_evidence_path = ao_app->get_evidence_path(p_evidence_image); + QPixmap f_pixmap(f_evidence_path); + pos_size_type icon_dimensions = ao_app->get_element_dimensions(icon_identifier, "courtroom_design.ini"); + f_pixmap = f_pixmap.scaled(icon_dimensions.width, icon_dimensions.height); + evidence_icon->setPixmap(f_pixmap); + evidence_icon->resize(f_pixmap.size()); evidence_icon->move(icon_dimensions.x, icon_dimensions.y); - evidence_icon->resize(icon_dimensions.width, icon_dimensions.height); - - evidence_icon->setPixmap(f_pixmap.scaled( - evidence_icon->width(), evidence_icon->height(), Qt::IgnoreAspectRatio)); - - QString f_default_gif_path = ao_app->get_default_theme_path(gif_name); - QString f_gif_path = ao_app->get_theme_path(gif_name); - - if (file_exists(f_gif_path)) - final_gif_path = f_gif_path; - else - final_gif_path = f_default_gif_path; - - evidence_movie->setFileName(final_gif_path); - if (evidence_movie->frameCount() < 1) - return; - - this->setMovie(evidence_movie); - - evidence_movie->start(); + evidence_movie->play(gif_name); sfx_player->play(ao_app->get_sfx("evidence_present")); } -void AOEvidenceDisplay::frame_change(int p_frame) -{ - if (p_frame == (evidence_movie->frameCount() - 1)) { - // we need this or else the last frame wont show - delay(evidence_movie->nextFrameDelay()); - - evidence_movie->stop(); - this->clear(); - - evidence_icon->show(); - } -} - void AOEvidenceDisplay::reset() { sfx_player->stop(); @@ -90,4 +59,13 @@ void AOEvidenceDisplay::reset() this->clear(); } +void AOEvidenceDisplay::show_done() { evidence_icon->show(); } + QLabel *AOEvidenceDisplay::get_evidence_icon() { return evidence_icon; } + +void AOEvidenceDisplay::combo_resize(int w, int h) +{ + QSize f_size(w, h); + this->resize(f_size); + evidence_movie->combo_resize(w, h); +} diff --git a/src/aoimage.cpp b/src/aoimage.cpp index ffdf25a..2663ba0 100644 --- a/src/aoimage.cpp +++ b/src/aoimage.cpp @@ -10,37 +10,42 @@ AOImage::AOImage(QWidget *parent, AOApplication *p_ao_app) : QLabel(parent) AOImage::~AOImage() {} -void AOImage::set_image(QString p_image) +bool AOImage::set_image(QString p_image) { - QString theme_image_path = ao_app->get_theme_path(p_image); - QString default_image_path = ao_app->get_default_theme_path(p_image); + QString theme_image_path = + ao_app->get_static_image_suffix(ao_app->get_theme_path(p_image)); + QString default_image_path = + ao_app->get_static_image_suffix(ao_app->get_default_theme_path(p_image)); QString final_image_path; if (file_exists(theme_image_path)) final_image_path = theme_image_path; - else + else if (file_exists(default_image_path)) final_image_path = default_image_path; + else { + qDebug() << "Warning: Image" << p_image << "not found! Can't set!"; + return false; + } QPixmap f_pixmap(final_image_path); this->setPixmap( f_pixmap.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio)); + return true; } -void AOImage::set_image_from_path(QString p_path) +bool AOImage::set_chatbox(QString p_path) { - QString default_path = ao_app->get_default_theme_path("chatmed.png"); + p_path = ao_app->get_static_image_suffix(p_path); + if (!file_exists(p_path)) { + qDebug() << "Warning: Chatbox" << p_path << "not found! Can't set!"; + return false; + } - QString final_path; - - if (file_exists(p_path)) - final_path = p_path; - else - final_path = default_path; - - QPixmap f_pixmap(final_path); + QPixmap f_pixmap(p_path); this->setPixmap( f_pixmap.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio)); + return true; } diff --git a/src/aolineedit.cpp b/src/aolineedit.cpp index 211d9f7..f98d95f 100644 --- a/src/aolineedit.cpp +++ b/src/aolineedit.cpp @@ -1,18 +1,19 @@ #include "aolineedit.h" -AOLineEdit::AOLineEdit(QWidget *parent) : QLineEdit(parent) -{ - this->setReadOnly(true); - this->setFrame(false); - - connect(this, SIGNAL(returnPressed()), this, SLOT(on_enter_pressed())); -} +AOLineEdit::AOLineEdit(QWidget *parent) : QLineEdit(parent) {} void AOLineEdit::mouseDoubleClickEvent(QMouseEvent *e) { QLineEdit::mouseDoubleClickEvent(e); - this->setReadOnly(false); + double_clicked(); +} +void AOLineEdit::focusOutEvent(QFocusEvent *ev) +{ + int start = selectionStart(); + int len = selectionEnd() - start; // We're not using selectionLength because + // Linux build doesn't run qt5.10 + QLineEdit::focusOutEvent(ev); + if (p_selection && start != -1 && len != -1) + this->setSelection(start, len); } - -void AOLineEdit::on_enter_pressed() { this->setReadOnly(true); } diff --git a/src/aomovie.cpp b/src/aomovie.cpp index 8642688..ac94921 100644 --- a/src/aomovie.cpp +++ b/src/aomovie.cpp @@ -12,56 +12,58 @@ AOMovie::AOMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) this->setMovie(m_movie); + timer = new QTimer(this); + timer->setTimerType(Qt::PreciseTimer); + timer->setSingleShot(true); + connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frame_change(int))); + connect(timer, SIGNAL(timeout()), this, SLOT(timer_done())); } void AOMovie::set_play_once(bool p_play_once) { play_once = p_play_once; } -void AOMovie::play(QString p_gif, QString p_char, QString p_custom_theme) +void AOMovie::play(QString p_image, QString p_char, QString p_custom_theme, + int duration) { m_movie->stop(); - QString gif_path; - - QString custom_path; - if (p_gif == "custom") - custom_path = - ao_app->get_image_suffix(ao_app->get_character_path(p_char, p_gif)); - else - custom_path = ao_app->get_image_suffix( - ao_app->get_character_path(p_char, p_gif + "_bubble")); - - QString misc_path = ao_app->get_base_path() + "misc/" + p_custom_theme + "/" + - p_gif + "_bubble.gif"; - QString custom_theme_path = - ao_app->get_custom_theme_path(p_custom_theme, p_gif + ".gif"); - QString theme_path = ao_app->get_theme_path(p_gif + ".gif"); - QString default_theme_path = ao_app->get_default_theme_path(p_gif + ".gif"); - QString placeholder_path = ao_app->get_theme_path("placeholder.gif"); - QString default_placeholder_path = - ao_app->get_default_theme_path("placeholder.gif"); - - if (file_exists(custom_path)) - gif_path = custom_path; - else if (file_exists(misc_path)) - gif_path = misc_path; - else if (file_exists(custom_theme_path)) - gif_path = custom_theme_path; - else if (file_exists(theme_path)) - gif_path = theme_path; - else if (file_exists(default_theme_path)) - gif_path = default_theme_path; - else if (file_exists(placeholder_path)) - gif_path = placeholder_path; - else if (file_exists(default_placeholder_path)) - gif_path = default_placeholder_path; - else - gif_path = ""; - - m_movie->setFileName(gif_path); + QString shout_path = p_image; + if (!file_exists(p_image)) { + QList<QString> pathlist; + + pathlist = { + ao_app->get_image_suffix( + ao_app->get_character_path(p_char, p_image)), // Character folder + ao_app->get_image_suffix(ao_app->get_base_path() + "misc/" + + p_custom_theme + "/" + p_image), // Misc path + ao_app->get_image_suffix(ao_app->get_custom_theme_path( + p_custom_theme, p_image)), // Custom theme path + ao_app->get_image_suffix(ao_app->get_theme_path(p_image)), // Theme path + ao_app->get_image_suffix( + ao_app->get_default_theme_path(p_image)), // Default theme path + ao_app->get_image_suffix( + ao_app->get_theme_path("placeholder")), // Placeholder path + ao_app->get_image_suffix(ao_app->get_default_theme_path( + "placeholder")), // Default placeholder path + }; + + for (QString path : pathlist) { + if (file_exists(path)) { + shout_path = path; + break; + } + } + } + + m_movie->setFileName(shout_path); + + if (m_movie->loopCount() == 0) + play_once = true; this->show(); m_movie->start(); + if (m_movie->frameCount() == 0 && duration > 0) + timer->start(duration); } void AOMovie::stop() @@ -72,15 +74,21 @@ void AOMovie::stop() void AOMovie::frame_change(int n_frame) { - if (n_frame == (m_movie->frameCount() - 1) && play_once) { - // we need this or else the last frame wont show - delay(m_movie->nextFrameDelay()); - - this->stop(); + // If it's a "static movie" (only one frame - png image), we can't change + // frames - ignore this function (use timer instead). If the frame didn't reach + // the last frame or the movie is continuous, don't stop the movie. + if (m_movie->frameCount() == 0 || n_frame < (m_movie->frameCount() - 1) || + !play_once) + return; + // we need this or else the last frame wont show + timer->start(m_movie->nextFrameDelay()); +} - // signal connected to courtroom object, let it figure out what to do - done(); - } +void AOMovie::timer_done() +{ + this->stop(); + // signal connected to courtroom object, let it figure out what to do + done(); } void AOMovie::combo_resize(int w, int h) diff --git a/src/aomusicplayer.cpp b/src/aomusicplayer.cpp index e61555a..bc1ab23 100644 --- a/src/aomusicplayer.cpp +++ b/src/aomusicplayer.cpp @@ -1,36 +1,173 @@ #include "aomusicplayer.h" -#if defined(BASSAUDIO) +#ifdef BASSAUDIO AOMusicPlayer::AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app) { m_parent = parent; ao_app = p_ao_app; } -AOMusicPlayer::~AOMusicPlayer() { BASS_ChannelStop(m_stream); } - -void AOMusicPlayer::play(QString p_song) +AOMusicPlayer::~AOMusicPlayer() { - BASS_ChannelStop(m_stream); + for (int n_stream = 0; n_stream < m_channelmax; ++n_stream) { + BASS_ChannelStop(m_stream_list[n_stream]); + } +} +void AOMusicPlayer::play(QString p_song, int channel, bool loop, + int effect_flags) +{ + channel = channel % m_channelmax; + if (channel < 0) // wtf? + return; QString f_path = ao_app->get_music_path(p_song); - m_stream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, - BASS_STREAM_AUTOFREE | BASS_UNICODE | - BASS_ASYNCFILE); + unsigned int flags = BASS_STREAM_PRESCAN | BASS_STREAM_AUTOFREE | + BASS_UNICODE | BASS_ASYNCFILE; + if (loop) + flags |= BASS_SAMPLE_LOOP; - this->set_volume(m_volume); + 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 (ao_app->get_audio_output_device() != "default") - BASS_ChannelSetDevice(m_stream, BASS_GetDevice()); - BASS_ChannelPlay(m_stream, false); + BASS_ChannelSetDevice(m_stream_list[channel], BASS_GetDevice()); + + QString d_path = f_path + ".txt"; + + loop_start = 0; + loop_end = BASS_ChannelGetLength(newstream, BASS_POS_BYTE); + if (loop && file_exists(d_path)) // Contains loop/etc. information file + { + QStringList lines = ao_app->read_file(d_path).split("\n"); + foreach (QString line, lines) { + QStringList args = line.split("="); + if (args.size() < 2) + continue; + QString arg = args[0].trimmed(); + + float sample_rate; + BASS_ChannelGetAttribute(newstream, BASS_ATTRIB_FREQ, &sample_rate); + + // Grab number of bytes for sample size + int sample_size = 16 / 8; + + // number of channels (stereo/mono) + int num_channels = 2; + + // Calculate the bytes for loop_start/loop_end to use with the sync proc + QWORD bytes = static_cast<QWORD>(args[1].trimmed().toFloat() * + sample_size * num_channels); + if (arg == "loop_start") + loop_start = bytes; + else if (arg == "loop_length") + loop_end = loop_start + bytes; + else if (arg == "loop_end") + loop_end = bytes; + } + qDebug() << "Found data file for song" << p_song << "length" + << BASS_ChannelGetLength(newstream, BASS_POS_BYTE) << "loop start" + << loop_start << "loop end" << loop_end; + } + + if (BASS_ChannelIsActive(m_stream_list[channel]) == BASS_ACTIVE_PLAYING) { + DWORD oldstream = m_stream_list[channel]; + + if (effect_flags & SYNC_POS) { + BASS_ChannelLock(oldstream, true); + // Sync it with the new sample + BASS_ChannelSetPosition(newstream, + BASS_ChannelGetPosition(oldstream, BASS_POS_BYTE), + BASS_POS_BYTE); + BASS_ChannelLock(oldstream, false); + } + + if (effect_flags & FADE_OUT) { + // Fade out the other sample and stop it (due to -1) + BASS_ChannelSlideAttribute(oldstream, BASS_ATTRIB_VOL | BASS_SLIDE_LOG, + -1, 4000); + } + else + BASS_ChannelStop( + oldstream); // Stop the sample since we don't need it anymore + } + else + BASS_ChannelStop(m_stream_list[channel]); + + m_stream_list[channel] = newstream; + BASS_ChannelPlay(m_stream_list[channel], false); + if (effect_flags & FADE_IN) { + // Fade in our sample + BASS_ChannelSetAttribute(newstream, BASS_ATTRIB_VOL, 0); + BASS_ChannelSlideAttribute(newstream, BASS_ATTRIB_VOL, + static_cast<float>(m_volume[channel] / 100.0f), + 1000); + } + else + this->set_volume(m_volume[channel], channel); + + this->set_looping(loop); // Have to do this here due to any + // crossfading-related changes, etc. } -void AOMusicPlayer::set_volume(int p_value) +void AOMusicPlayer::stop(int channel) { - m_volume = p_value; - float volume = m_volume / 100.0f; - BASS_ChannelSetAttribute(m_stream, BASS_ATTRIB_VOL, volume); + BASS_ChannelStop(m_stream_list[channel]); +} + +void AOMusicPlayer::set_volume(int p_value, int channel) +{ + m_volume[channel] = p_value; + float volume = m_volume[channel] / 100.0f; + if (channel < 0) { + for (int n_stream = 0; n_stream < m_channelmax; ++n_stream) { + BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, + volume); + } + } + else { + BASS_ChannelSetAttribute(m_stream_list[channel], BASS_ATTRIB_VOL, volume); + } +} + +void CALLBACK loopProc(HSYNC handle, DWORD channel, DWORD data, void *user) +{ + QWORD loop_start = *(static_cast<unsigned *>(user)); + BASS_ChannelLock(channel, true); + BASS_ChannelSetPosition(channel, loop_start, BASS_POS_BYTE); + BASS_ChannelLock(channel, false); +} + +void AOMusicPlayer::set_looping(bool toggle, int channel) +{ + m_looping = toggle; + if (!m_looping) { + if (BASS_ChannelFlags(m_stream_list[channel], 0, 0) & BASS_SAMPLE_LOOP) + BASS_ChannelFlags(m_stream_list[channel], 0, + BASS_SAMPLE_LOOP); // remove the LOOP flag + BASS_ChannelRemoveSync(m_stream_list[channel], loop_sync[channel]); + loop_sync[channel] = 0; + } + else { + BASS_ChannelFlags(m_stream_list[channel], BASS_SAMPLE_LOOP, + BASS_SAMPLE_LOOP); // set the LOOP flag + if (loop_sync[channel] != 0) { + BASS_ChannelRemoveSync(m_stream_list[channel], + loop_sync[channel]); // remove the sync + loop_sync[channel] = 0; + } + if (loop_start > 0) { + if (loop_end == 0) + loop_end = BASS_ChannelGetLength(m_stream_list[channel], BASS_POS_BYTE); + if (loop_end > 0) // Don't loop zero length songs even if we're asked to + loop_sync[channel] = BASS_ChannelSetSync( + m_stream_list[channel], BASS_SYNC_POS | BASS_SYNC_MIXTIME, loop_end, + loopProc, &loop_start); + } + } } #elif defined(QTAUDIO) AOMusicPlayer::AOMusicPlayer(QWidget *parent, AOApplication *p_ao_app) diff --git a/src/aooptionsdialog.cpp b/src/aooptionsdialog.cpp index 5653fd9..e911963 100644 --- a/src/aooptionsdialog.cpp +++ b/src/aooptionsdialog.cpp @@ -14,7 +14,7 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_settings_buttons = new QDialogButtonBox(this); - QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Fixed); + QSizePolicy sizePolicy1(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); sizePolicy1.setHorizontalStretch(0); sizePolicy1.setVerticalStretch(0); sizePolicy1.setHeightForWidth( @@ -42,10 +42,11 @@ 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->setSizePolicy(sizePolicy1); ui_settings_tabs->addTab(ui_gameplay_tab, tr("Gameplay")); - ui_form_layout_widget = new QWidget(ui_gameplay_tab); - ui_form_layout_widget->setGeometry(QRect(10, 10, 361, 211)); + ui_form_layout_widget->setGeometry(QRect(10, 10, 361, 361)); + ui_form_layout_widget->setSizePolicy(sizePolicy1); ui_gameplay_form = new QFormLayout(ui_form_layout_widget); ui_gameplay_form->setLabelAlignment(Qt::AlignLeading | Qt::AlignLeft | @@ -53,6 +54,9 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_gameplay_form->setFormAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); ui_gameplay_form->setContentsMargins(0, 0, 0, 0); + ui_gameplay_form->setSpacing(2); + + int row = 0; ui_theme_label = new QLabel(ui_form_layout_widget); ui_theme_label->setText(tr("Theme:")); @@ -61,8 +65,7 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) "the lobby's look as well, you'll need to reload the " "lobby for the changes to take effect, such as by joining " "a server and leaving it.")); - ui_gameplay_form->setWidget(0, QFormLayout::LabelRole, ui_theme_label); - + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_theme_label); ui_theme_combobox = new QComboBox(ui_form_layout_widget); // Fill the combobox with the names of the themes. @@ -76,15 +79,18 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_theme_combobox->setCurrentIndex(ui_theme_combobox->count() - 1); } - ui_gameplay_form->setWidget(0, QFormLayout::FieldRole, ui_theme_combobox); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_theme_combobox); + row += 1; ui_theme_log_divider = new QFrame(ui_form_layout_widget); ui_theme_log_divider->setMidLineWidth(0); ui_theme_log_divider->setFrameShape(QFrame::HLine); ui_theme_log_divider->setFrameShadow(QFrame::Sunken); - ui_gameplay_form->setWidget(1, QFormLayout::FieldRole, ui_theme_log_divider); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, + ui_theme_log_divider); + row += 1; ui_downwards_lbl = new QLabel(ui_form_layout_widget); ui_downwards_lbl->setText(tr("Log goes downwards:")); ui_downwards_lbl->setToolTip( @@ -92,47 +98,52 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) "the bottom (like the OOC chatlog). The traditional " "(AO1) behaviour is equivalent to this being unticked.")); - ui_gameplay_form->setWidget(2, QFormLayout::LabelRole, ui_downwards_lbl); + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_downwards_lbl); ui_downwards_cb = new QCheckBox(ui_form_layout_widget); ui_downwards_cb->setChecked(p_ao_app->get_log_goes_downwards()); - ui_gameplay_form->setWidget(2, QFormLayout::FieldRole, ui_downwards_cb); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_downwards_cb); + row += 1; ui_length_lbl = new QLabel(ui_form_layout_widget); ui_length_lbl->setText(tr("Log length:")); ui_length_lbl->setToolTip(tr( "The amount of messages the IC chatlog will keep before " "deleting older messages. A value of 0 or below counts as 'infinite'.")); - ui_gameplay_form->setWidget(3, QFormLayout::LabelRole, ui_length_lbl); + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_length_lbl); ui_length_spinbox = new QSpinBox(ui_form_layout_widget); ui_length_spinbox->setMaximum(10000); ui_length_spinbox->setValue(p_ao_app->get_max_log_size()); - ui_gameplay_form->setWidget(3, QFormLayout::FieldRole, ui_length_spinbox); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_length_spinbox); + row += 1; ui_log_names_divider = new QFrame(ui_form_layout_widget); ui_log_names_divider->setFrameShape(QFrame::HLine); ui_log_names_divider->setFrameShadow(QFrame::Sunken); - ui_gameplay_form->setWidget(4, QFormLayout::FieldRole, ui_log_names_divider); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, + ui_log_names_divider); + row += 1; ui_username_lbl = new QLabel(ui_form_layout_widget); ui_username_lbl->setText(tr("Default username:")); ui_username_lbl->setToolTip( tr("Your OOC name will be automatically set to this value " "when you join a server.")); - ui_gameplay_form->setWidget(5, QFormLayout::LabelRole, ui_username_lbl); + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_username_lbl); ui_username_textbox = new QLineEdit(ui_form_layout_widget); ui_username_textbox->setMaxLength(30); ui_username_textbox->setText(p_ao_app->get_default_username()); - ui_gameplay_form->setWidget(5, QFormLayout::FieldRole, ui_username_textbox); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_username_textbox); + row += 1; ui_showname_lbl = new QLabel(ui_form_layout_widget); ui_showname_lbl->setText(tr("Custom shownames:")); ui_showname_lbl->setToolTip( @@ -140,33 +151,36 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) "tickbox, which in turn determines whether the client should " "display custom in-character names.")); - ui_gameplay_form->setWidget(6, QFormLayout::LabelRole, ui_showname_lbl); + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_showname_lbl); ui_showname_cb = new QCheckBox(ui_form_layout_widget); ui_showname_cb->setChecked(p_ao_app->get_showname_enabled_by_default()); - ui_gameplay_form->setWidget(6, QFormLayout::FieldRole, ui_showname_cb); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_showname_cb); + row += 1; ui_net_divider = new QFrame(ui_form_layout_widget); ui_net_divider->setFrameShape(QFrame::HLine); ui_net_divider->setFrameShadow(QFrame::Sunken); - ui_gameplay_form->setWidget(7, QFormLayout::FieldRole, ui_net_divider); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_net_divider); + row += 1; ui_ms_lbl = new QLabel(ui_form_layout_widget); ui_ms_lbl->setText(tr("Backup MS:")); ui_ms_lbl->setToolTip( tr("If the built-in server lookups fail, the game will try the " "address given here and use it as a backup master server address.")); - ui_gameplay_form->setWidget(8, QFormLayout::LabelRole, ui_ms_lbl); + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_ms_lbl); QSettings *configini = ao_app->configini; ui_ms_textbox = new QLineEdit(ui_form_layout_widget); ui_ms_textbox->setText(configini->value("master", "").value<QString>()); - ui_gameplay_form->setWidget(8, QFormLayout::FieldRole, ui_ms_textbox); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_ms_textbox); + row += 1; ui_discord_lbl = new QLabel(ui_form_layout_widget); ui_discord_lbl->setText(tr("Discord:")); ui_discord_lbl->setToolTip( @@ -174,12 +188,136 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) "what character are you playing, and how long you have " "been playing for.")); - ui_gameplay_form->setWidget(9, QFormLayout::LabelRole, ui_discord_lbl); + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_discord_lbl); ui_discord_cb = new QCheckBox(ui_form_layout_widget); ui_discord_cb->setChecked(ao_app->is_discord_enabled()); - ui_gameplay_form->setWidget(9, QFormLayout::FieldRole, ui_discord_cb); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_discord_cb); + + row += 1; + ui_language_label = new QLabel(ui_form_layout_widget); + ui_language_label->setText(tr("Language:")); + ui_language_label->setToolTip( + tr("Sets the language if you don't want to use your system language.")); + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_language_label); + + ui_language_combobox = new QComboBox(ui_form_layout_widget); + ui_language_combobox->addItem( + configini->value("language", " ").value<QString>() + + " - Keep current setting"); + ui_language_combobox->addItem(" - Default"); + ui_language_combobox->addItem("en - English"); + ui_language_combobox->addItem("de - Deutsch"); + ui_language_combobox->addItem("es - Español"); + ui_language_combobox->addItem("jp - 日本語"); + ui_language_combobox->addItem("ru - Русский"); + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, + ui_language_combobox); + + row += 1; + ui_shake_lbl = new QLabel(ui_form_layout_widget); + ui_shake_lbl->setText(tr("Allow Screenshake:")); + ui_shake_lbl->setToolTip( + tr("Allows screenshaking. Disable this if you have concerns or issues " + "with photosensitivity and/or seizures.")); + + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_shake_lbl); + + ui_shake_cb = new QCheckBox(ui_form_layout_widget); + ui_shake_cb->setChecked(ao_app->is_shake_enabled()); + + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_shake_cb); + + row += 1; + ui_effects_lbl = new QLabel(ui_form_layout_widget); + ui_effects_lbl->setText(tr("Allow Effects:")); + ui_effects_lbl->setToolTip( + tr("Allows screen effects. Disable this if you have concerns or issues " + "with photosensitivity and/or seizures.")); + + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_effects_lbl); + + ui_effects_cb = new QCheckBox(ui_form_layout_widget); + ui_effects_cb->setChecked(ao_app->is_effects_enabled()); + + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_effects_cb); + + row += 1; + ui_framenetwork_lbl = new QLabel(ui_form_layout_widget); + ui_framenetwork_lbl->setText(tr("Network Frame Effects:")); + ui_framenetwork_lbl->setToolTip(tr( + "Send screen-shaking, flashes and sounds as defined in the char.ini over " + "the network. Only works for servers that support this functionality.")); + + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_framenetwork_lbl); + + ui_framenetwork_cb = new QCheckBox(ui_form_layout_widget); + ui_framenetwork_cb->setChecked(ao_app->is_frame_network_enabled()); + + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_framenetwork_cb); + + row += 1; + ui_colorlog_lbl = new QLabel(ui_form_layout_widget); + ui_colorlog_lbl->setText(tr("Colors in IC Log:")); + ui_colorlog_lbl->setToolTip( + tr("Use the markup colors in the server IC chatlog.")); + + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_colorlog_lbl); + + ui_colorlog_cb = new QCheckBox(ui_form_layout_widget); + ui_colorlog_cb->setChecked(ao_app->is_colorlog_enabled()); + + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_colorlog_cb); + + row += 1; + ui_stickysounds_lbl = new QLabel(ui_form_layout_widget); + ui_stickysounds_lbl->setText(tr("Sticky Sounds:")); + ui_stickysounds_lbl->setToolTip( + tr("Turn this on to prevent the sound dropdown from clearing the sound " + "after playing it.")); + + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_stickysounds_lbl); + + ui_stickysounds_cb = new QCheckBox(ui_form_layout_widget); + ui_stickysounds_cb->setChecked(ao_app->is_stickysounds_enabled()); + + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_stickysounds_cb); + + row += 1; + ui_stickyeffects_lbl = new QLabel(ui_form_layout_widget); + ui_stickyeffects_lbl->setText(tr("Sticky Effects:")); + ui_stickyeffects_lbl->setToolTip( + tr("Turn this on to prevent the effects dropdown from clearing the " + "effect after playing it.")); + + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, + ui_stickyeffects_lbl); + + ui_stickyeffects_cb = new QCheckBox(ui_form_layout_widget); + ui_stickyeffects_cb->setChecked(ao_app->is_stickyeffects_enabled()); + + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_stickyeffects_cb); + + row += 1; + ui_stickypres_lbl = new QLabel(ui_form_layout_widget); + ui_stickypres_lbl->setText(tr("Sticky Preanims:")); + ui_stickypres_lbl->setToolTip( + tr("Turn this on to prevent preanimation checkbox from clearing after " + "playing the emote.")); + + ui_gameplay_form->setWidget(row, QFormLayout::LabelRole, ui_stickypres_lbl); + + ui_stickypres_cb = new QCheckBox(ui_form_layout_widget); + ui_stickypres_cb->setChecked(ao_app->is_stickypres_enabled()); + + ui_gameplay_form->setWidget(row, QFormLayout::FieldRole, ui_stickypres_cb); + + QScrollArea *scroll = new QScrollArea; + 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(); @@ -218,8 +356,7 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_callwords_layout->addWidget(ui_callwords_explain_lbl); -// The audio tab. -#ifdef BASSAUDIO + // The audio tab. ui_audio_tab = new QWidget(); ui_settings_tabs->addTab(ui_audio_tab, tr("Audio")); @@ -232,117 +369,163 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_audio_layout->setFormAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); ui_audio_layout->setContentsMargins(0, 0, 0, 0); + row = 0; ui_audio_device_lbl = new QLabel(ui_audio_widget); ui_audio_device_lbl->setText(tr("Audio device:")); ui_audio_device_lbl->setToolTip(tr("Sets the audio device for all sounds.")); - ui_audio_layout->setWidget(0, QFormLayout::LabelRole, ui_audio_device_lbl); + ui_audio_layout->setWidget(row, QFormLayout::LabelRole, ui_audio_device_lbl); ui_audio_device_combobox = new QComboBox(ui_audio_widget); // Let's fill out the combobox with the available audio devices. Or don't if // there is no audio int a = 0; - BASS_DEVICEINFO info; - if (needs_default_audiodev()) { + ui_audio_device_combobox->addItem("default"); } - +#ifdef BASSAUDIO + BASS_DEVICEINFO info; for (a = 0; BASS_GetDeviceInfo(a, &info); a++) { ui_audio_device_combobox->addItem(info.name); if (p_ao_app->get_audio_output_device() == info.name) ui_audio_device_combobox->setCurrentIndex( ui_audio_device_combobox->count() - 1); } - - ui_audio_layout->setWidget(0, QFormLayout::FieldRole, +#elif defined QTAUDIO + foreach (const QAudioDeviceInfo &deviceInfo, + QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) { + ui_audio_device_combobox->addItem(deviceInfo.deviceName()); + if (p_ao_app->get_audio_output_device() == deviceInfo.deviceName()) + ui_audio_device_combobox->setCurrentIndex( + ui_audio_device_combobox->count() - 1); + } +#endif + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_audio_device_combobox); + row += 1; ui_audio_volume_divider = new QFrame(ui_audio_widget); ui_audio_volume_divider->setFrameShape(QFrame::HLine); ui_audio_volume_divider->setFrameShadow(QFrame::Sunken); - ui_audio_layout->setWidget(1, QFormLayout::FieldRole, + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_audio_volume_divider); + row += 1; ui_music_volume_lbl = new QLabel(ui_audio_widget); ui_music_volume_lbl->setText(tr("Music:")); ui_music_volume_lbl->setToolTip(tr("Sets the music's default volume.")); - ui_audio_layout->setWidget(2, QFormLayout::LabelRole, ui_music_volume_lbl); + ui_audio_layout->setWidget(row, QFormLayout::LabelRole, ui_music_volume_lbl); ui_music_volume_spinbox = new QSpinBox(ui_audio_widget); ui_music_volume_spinbox->setValue(p_ao_app->get_default_music()); ui_music_volume_spinbox->setMaximum(100); ui_music_volume_spinbox->setSuffix("%"); - ui_audio_layout->setWidget(2, QFormLayout::FieldRole, + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_music_volume_spinbox); + row += 1; ui_sfx_volume_lbl = new QLabel(ui_audio_widget); ui_sfx_volume_lbl->setText(tr("SFX:")); ui_sfx_volume_lbl->setToolTip( tr("Sets the SFX's default volume. " "Interjections and actual sound effects count as 'SFX'.")); - - ui_audio_layout->setWidget(3, QFormLayout::LabelRole, ui_sfx_volume_lbl); + ui_audio_layout->setWidget(row, QFormLayout::LabelRole, ui_sfx_volume_lbl); ui_sfx_volume_spinbox = new QSpinBox(ui_audio_widget); ui_sfx_volume_spinbox->setValue(p_ao_app->get_default_sfx()); ui_sfx_volume_spinbox->setMaximum(100); ui_sfx_volume_spinbox->setSuffix("%"); - ui_audio_layout->setWidget(3, QFormLayout::FieldRole, ui_sfx_volume_spinbox); + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, + ui_sfx_volume_spinbox); + row += 1; ui_blips_volume_lbl = new QLabel(ui_audio_widget); ui_blips_volume_lbl->setText(tr("Blips:")); ui_blips_volume_lbl->setToolTip( tr("Sets the volume of the blips, the talking sound effects.")); - ui_audio_layout->setWidget(4, QFormLayout::LabelRole, ui_blips_volume_lbl); + ui_audio_layout->setWidget(row, QFormLayout::LabelRole, ui_blips_volume_lbl); ui_blips_volume_spinbox = new QSpinBox(ui_audio_widget); ui_blips_volume_spinbox->setValue(p_ao_app->get_default_blip()); ui_blips_volume_spinbox->setMaximum(100); ui_blips_volume_spinbox->setSuffix("%"); - ui_audio_layout->setWidget(4, QFormLayout::FieldRole, + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_blips_volume_spinbox); + row += 1; ui_volume_blip_divider = new QFrame(ui_audio_widget); ui_volume_blip_divider->setFrameShape(QFrame::HLine); ui_volume_blip_divider->setFrameShadow(QFrame::Sunken); - ui_audio_layout->setWidget(5, QFormLayout::FieldRole, ui_volume_blip_divider); + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, + ui_volume_blip_divider); + row += 1; ui_bliprate_lbl = new QLabel(ui_audio_widget); ui_bliprate_lbl->setText(tr("Blip rate:")); ui_bliprate_lbl->setToolTip( tr("Sets the delay between playing the blip sounds.")); - ui_audio_layout->setWidget(6, QFormLayout::LabelRole, ui_bliprate_lbl); + ui_audio_layout->setWidget(row, QFormLayout::LabelRole, ui_bliprate_lbl); ui_bliprate_spinbox = new QSpinBox(ui_audio_widget); ui_bliprate_spinbox->setValue(p_ao_app->read_blip_rate()); ui_bliprate_spinbox->setMinimum(1); + ui_bliprate_spinbox->setToolTip( + tr("Play a blip sound \"once per every X symbols\", where " + "X is the blip rate.")); - ui_audio_layout->setWidget(6, QFormLayout::FieldRole, ui_bliprate_spinbox); + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_bliprate_spinbox); + row += 1; ui_blank_blips_lbl = new QLabel(ui_audio_widget); ui_blank_blips_lbl->setText(tr("Blank blips:")); ui_blank_blips_lbl->setToolTip( tr("If true, the game will play a blip sound even " "when a space is 'being said'.")); - ui_audio_layout->setWidget(7, QFormLayout::LabelRole, ui_blank_blips_lbl); + ui_audio_layout->setWidget(row, QFormLayout::LabelRole, ui_blank_blips_lbl); ui_blank_blips_cb = new QCheckBox(ui_audio_widget); ui_blank_blips_cb->setChecked(p_ao_app->get_blank_blip()); - ui_audio_layout->setWidget(7, QFormLayout::FieldRole, ui_blank_blips_cb); -#endif + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_blank_blips_cb); + + row += 1; + ui_loopsfx_lbl = new QLabel(ui_audio_widget); + ui_loopsfx_lbl->setText(tr("Enable Looping SFX:")); + ui_loopsfx_lbl->setToolTip(tr("If true, the game will allow looping sound " + "effects to play on preanimations.")); + + ui_audio_layout->setWidget(row, QFormLayout::LabelRole, ui_loopsfx_lbl); + + ui_loopsfx_cb = new QCheckBox(ui_audio_widget); + ui_loopsfx_cb->setChecked(p_ao_app->get_looping_sfx()); + + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_loopsfx_cb); + + row += 1; + ui_objectmusic_lbl = new QLabel(ui_audio_widget); + ui_objectmusic_lbl->setText(tr("Kill Music On Objection:")); + ui_objectmusic_lbl->setToolTip( + tr("If true, AO2 will stop the music for you when you or someone else " + "does 'Objection!'.")); + + ui_audio_layout->setWidget(row, QFormLayout::LabelRole, ui_objectmusic_lbl); + + ui_objectmusic_cb = new QCheckBox(ui_audio_widget); + ui_objectmusic_cb->setChecked(p_ao_app->objection_stop_music()); + + ui_audio_layout->setWidget(row, QFormLayout::FieldRole, ui_objectmusic_cb); // The casing tab! ui_casing_tab = new QWidget(); @@ -357,6 +540,7 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) ui_casing_layout->setFormAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); ui_casing_layout->setContentsMargins(0, 0, 0, 0); + row = 0; // -- SERVER SUPPORTS CASING @@ -368,126 +552,136 @@ AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) tr("This server does not support case alerts.")); ui_casing_supported_lbl->setToolTip(tr("Pretty self-explanatory.")); - ui_casing_layout->setWidget(0, QFormLayout::FieldRole, + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_casing_supported_lbl); // -- CASE ANNOUNCEMENTS + row += 1; ui_casing_enabled_lbl = new QLabel(ui_casing_widget); ui_casing_enabled_lbl->setText(tr("Casing:")); ui_casing_enabled_lbl->setToolTip( tr("If checked, you will get alerts about case " "announcements.")); - ui_casing_layout->setWidget(1, QFormLayout::LabelRole, ui_casing_enabled_lbl); + ui_casing_layout->setWidget(row, QFormLayout::LabelRole, + ui_casing_enabled_lbl); ui_casing_enabled_cb = new QCheckBox(ui_casing_widget); ui_casing_enabled_cb->setChecked(ao_app->get_casing_enabled()); - ui_casing_layout->setWidget(1, QFormLayout::FieldRole, ui_casing_enabled_cb); + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, + ui_casing_enabled_cb); // -- DEFENSE ANNOUNCEMENTS + row += 1; ui_casing_def_lbl = new QLabel(ui_casing_widget); ui_casing_def_lbl->setText(tr("Defense:")); ui_casing_def_lbl->setToolTip(tr("If checked, you will get alerts about case " "announcements if a defense spot is open.")); - ui_casing_layout->setWidget(2, QFormLayout::LabelRole, ui_casing_def_lbl); + ui_casing_layout->setWidget(row, QFormLayout::LabelRole, ui_casing_def_lbl); ui_casing_def_cb = new QCheckBox(ui_casing_widget); ui_casing_def_cb->setChecked(ao_app->get_casing_defence_enabled()); - ui_casing_layout->setWidget(2, QFormLayout::FieldRole, ui_casing_def_cb); + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_casing_def_cb); // -- PROSECUTOR ANNOUNCEMENTS + row += 1; ui_casing_pro_lbl = new QLabel(ui_casing_widget); ui_casing_pro_lbl->setText(tr("Prosecution:")); ui_casing_pro_lbl->setToolTip( tr("If checked, you will get alerts about case " "announcements if a prosecutor spot is open.")); - ui_casing_layout->setWidget(3, QFormLayout::LabelRole, ui_casing_pro_lbl); + ui_casing_layout->setWidget(row, QFormLayout::LabelRole, ui_casing_pro_lbl); ui_casing_pro_cb = new QCheckBox(ui_casing_widget); ui_casing_pro_cb->setChecked(ao_app->get_casing_prosecution_enabled()); - ui_casing_layout->setWidget(3, QFormLayout::FieldRole, ui_casing_pro_cb); + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_casing_pro_cb); // -- JUDGE ANNOUNCEMENTS + row += 1; ui_casing_jud_lbl = new QLabel(ui_casing_widget); ui_casing_jud_lbl->setText(tr("Judge:")); ui_casing_jud_lbl->setToolTip(tr("If checked, you will get alerts about case " "announcements if the judge spot is open.")); - ui_casing_layout->setWidget(4, QFormLayout::LabelRole, ui_casing_jud_lbl); + ui_casing_layout->setWidget(row, QFormLayout::LabelRole, ui_casing_jud_lbl); ui_casing_jud_cb = new QCheckBox(ui_casing_widget); ui_casing_jud_cb->setChecked(ao_app->get_casing_judge_enabled()); - ui_casing_layout->setWidget(4, QFormLayout::FieldRole, ui_casing_jud_cb); + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_casing_jud_cb); // -- JUROR ANNOUNCEMENTS + row += 1; ui_casing_jur_lbl = new QLabel(ui_casing_widget); ui_casing_jur_lbl->setText(tr("Juror:")); ui_casing_jur_lbl->setToolTip(tr("If checked, you will get alerts about case " "announcements if a juror spot is open.")); - ui_casing_layout->setWidget(5, QFormLayout::LabelRole, ui_casing_jur_lbl); + ui_casing_layout->setWidget(row, QFormLayout::LabelRole, ui_casing_jur_lbl); ui_casing_jur_cb = new QCheckBox(ui_casing_widget); ui_casing_jur_cb->setChecked(ao_app->get_casing_juror_enabled()); - ui_casing_layout->setWidget(5, QFormLayout::FieldRole, ui_casing_jur_cb); + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_casing_jur_cb); // -- STENO ANNOUNCEMENTS + row += 1; ui_casing_steno_lbl = new QLabel(ui_casing_widget); ui_casing_steno_lbl->setText(tr("Stenographer:")); ui_casing_steno_lbl->setToolTip( tr("If checked, you will get alerts about case " "announcements if a stenographer spot is open.")); - ui_casing_layout->setWidget(6, QFormLayout::LabelRole, ui_casing_steno_lbl); + ui_casing_layout->setWidget(row, QFormLayout::LabelRole, ui_casing_steno_lbl); ui_casing_steno_cb = new QCheckBox(ui_casing_widget); ui_casing_steno_cb->setChecked(ao_app->get_casing_steno_enabled()); - ui_casing_layout->setWidget(6, QFormLayout::FieldRole, ui_casing_steno_cb); + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_casing_steno_cb); // -- CM ANNOUNCEMENTS + row += 1; ui_casing_cm_lbl = new QLabel(ui_casing_widget); ui_casing_cm_lbl->setText(tr("CM:")); ui_casing_cm_lbl->setToolTip( tr("If checked, you will appear amongst the potential " "CMs on the server.")); - ui_casing_layout->setWidget(7, QFormLayout::LabelRole, ui_casing_cm_lbl); + ui_casing_layout->setWidget(row, QFormLayout::LabelRole, ui_casing_cm_lbl); ui_casing_cm_cb = new QCheckBox(ui_casing_widget); ui_casing_cm_cb->setChecked(ao_app->get_casing_cm_enabled()); - ui_casing_layout->setWidget(7, QFormLayout::FieldRole, ui_casing_cm_cb); + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_casing_cm_cb); // -- CM CASES ANNOUNCEMENTS + row += 1; ui_casing_cm_cases_lbl = new QLabel(ui_casing_widget); ui_casing_cm_cases_lbl->setText(tr("Hosting cases:")); ui_casing_cm_cases_lbl->setToolTip( tr("If you're a CM, enter what cases you are " "willing to host.")); - ui_casing_layout->setWidget(8, QFormLayout::LabelRole, + ui_casing_layout->setWidget(row, QFormLayout::LabelRole, ui_casing_cm_cases_lbl); ui_casing_cm_cases_textbox = new QLineEdit(ui_casing_widget); ui_casing_cm_cases_textbox->setText(ao_app->get_casing_can_host_cases()); - ui_casing_layout->setWidget(8, QFormLayout::FieldRole, + ui_casing_layout->setWidget(row, QFormLayout::FieldRole, ui_casing_cm_cases_textbox); // When we're done, we should continue the updates! @@ -506,14 +700,19 @@ void AOOptionsDialog::save_pressed() configini->setValue("show_custom_shownames", ui_showname_cb->isChecked()); configini->setValue("master", ui_ms_textbox->text()); configini->setValue("discord", ui_discord_cb->isChecked()); + configini->setValue("language", ui_language_combobox->currentText().left(2)); + configini->setValue("shake", ui_shake_cb->isChecked()); + configini->setValue("effects", ui_effects_cb->isChecked()); + configini->setValue("framenetwork", ui_framenetwork_cb->isChecked()); + configini->setValue("colorlog", ui_colorlog_cb->isChecked()); + configini->setValue("stickysounds", ui_stickysounds_cb->isChecked()); + configini->setValue("stickyeffects", ui_stickyeffects_cb->isChecked()); + configini->setValue("stickypres", ui_stickypres_cb->isChecked()); QFile *callwordsini = new QFile(ao_app->get_base_path() + "callwords.ini"); - if (!callwordsini->open(QIODevice::WriteOnly | QIODevice::Truncate | - QIODevice::Text)) { - // Nevermind! - } - else { + if (callwordsini->open(QIODevice::WriteOnly | QIODevice::Truncate | + QIODevice::Text)) { QTextStream out(callwordsini); out << ui_callwords_textbox->toPlainText(); callwordsini->close(); @@ -526,6 +725,8 @@ void AOOptionsDialog::save_pressed() configini->setValue("default_blip", ui_blips_volume_spinbox->value()); configini->setValue("blip_rate", ui_bliprate_spinbox->value()); configini->setValue("blank_blip", ui_blank_blips_cb->isChecked()); + configini->setValue("looping_sfx", ui_loopsfx_cb->isChecked()); + configini->setValue("objection_stop_music", ui_objectmusic_cb->isChecked()); configini->setValue("casing_enabled", ui_casing_enabled_cb->isChecked()); configini->setValue("casing_defence_enabled", ui_casing_def_cb->isChecked()); diff --git a/src/aoscene.cpp b/src/aoscene.cpp index aad6c10..094d7a5 100644 --- a/src/aoscene.cpp +++ b/src/aoscene.cpp @@ -7,68 +7,122 @@ AOScene::AOScene(QWidget *parent, AOApplication *p_ao_app) : QLabel(parent) m_parent = parent; ao_app = p_ao_app; m_movie = new QMovie(this); + last_image = ""; } void AOScene::set_image(QString p_image) { - QString background_path = ao_app->get_background_path(p_image + ".png"); - QString animated_background_path = - ao_app->get_background_path(p_image + ".gif"); - QString default_path = ao_app->get_default_background_path(p_image + ".png"); + QString background_path = + ao_app->get_image_suffix(ao_app->get_background_path(p_image)); + if (!file_exists(background_path)) // If image is missing, clear current image + { + this->clear(); + this->setMovie(nullptr); - QPixmap background(background_path); - QPixmap default_bg(default_path); + m_movie->stop(); + last_image = ""; + return; + } - int w = this->width(); - int h = this->height(); + if (file_exists(background_path) && background_path == last_image) + return; this->clear(); this->setMovie(nullptr); m_movie->stop(); - m_movie->setFileName(animated_background_path); - m_movie->setScaledSize(QSize(w, h)); + m_movie->setFileName(background_path); + + if (m_movie->isValid() && m_movie->frameCount() > 1) { + m_movie->jumpToNextFrame(); + float scale_factor = static_cast<float>(f_h) / + static_cast<float>(m_movie->frameRect().height()); + // preserve aspect ratio + int n_w = static_cast<int>(m_movie->frameRect().width() * scale_factor); + int n_h = static_cast<int>(m_movie->frameRect().height() * scale_factor); - if (m_movie->isValid()) { + m_movie->setScaledSize(QSize(n_w, n_h)); + this->resize(m_movie->scaledSize()); this->setMovie(m_movie); + QLabel::move(x + (f_w - n_w) / 2, y + (f_h - n_h) / 2); // Center m_movie->start(); } - else if (file_exists(background_path)) { - this->setPixmap(background.scaled(w, h)); - } else { - this->setPixmap(default_bg.scaled(w, h)); + QPixmap background(background_path); + auto transform_mode = Qt::FastTransformation; + if (background.height() > f_h) // We are downscaling, use anti-aliasing. + transform_mode = Qt::SmoothTransformation; + + background = background.scaledToHeight(f_h, transform_mode); + this->resize(background.size()); + this->setPixmap(background); + QLabel::move( + x + (f_w - background.width()) / 2, + y + (f_h - background.height()) / + 2); // Always center horizontally, always center vertically } + last_image = background_path; } void AOScene::set_legacy_desk(QString p_image) { - // vanilla desks vary in both width and height. in order to make that work - // with viewport rescaling, some INTENSE math is needed. - QString desk_path = ao_app->get_background_path(p_image); - QString default_path = ao_app->get_default_background_path(p_image); + QString desk_path = + ao_app->get_image_suffix(ao_app->get_background_path(p_image)); + if (!file_exists(desk_path)) // If image is missing, clear current image + { + this->clear(); + this->setMovie(nullptr); + + m_movie->stop(); + last_image = ""; + return; + } - QPixmap f_desk; + if (file_exists(desk_path) && desk_path == last_image) + return; - if (file_exists(desk_path)) - f_desk.load(desk_path); - else - f_desk.load(default_path); + QPixmap f_desk(desk_path); + // vanilla desks vary in both width and height. in order to make that work + // with viewport rescaling, some INTENSE math is needed. int vp_width = m_parent->width(); int vp_height = m_parent->height(); - // double y_modifier = 147 / 192; - // double w_modifier = vp_width / 256; double h_modifier = vp_height / 192; - // int final_y = y_modifier * vp_height; - // int final_w = w_modifier * f_desk.width(); int final_h = static_cast<int>(h_modifier * f_desk.height()); - // this->resize(final_w, final_h); - // this->setPixmap(f_desk.scaled(final_w, final_h)); - this->resize(vp_width, final_h); - this->setPixmap(f_desk.scaled(vp_width, final_h)); + this->clear(); + this->setMovie(nullptr); + + m_movie->stop(); + m_movie->setFileName(desk_path); + + m_movie->setScaledSize(QSize(vp_width, final_h)); + + if (m_movie->isValid() && m_movie->frameCount() > 1) { + this->setMovie(m_movie); + m_movie->start(); + } + else { + this->resize(vp_width, final_h); + this->setPixmap(f_desk.scaled(vp_width, final_h)); + } + last_image = desk_path; +} + +void AOScene::combo_resize(int w, int h) +{ + QSize f_size(w, h); + f_w = w; + f_h = h; + this->resize(f_size); +} + +void AOScene::move(int ax, int ay) +{ + x = ax; + y = ay; + QLabel::move(x, y); } diff --git a/src/aosfxplayer.cpp b/src/aosfxplayer.cpp index 543da5c..6db6f37 100644 --- a/src/aosfxplayer.cpp +++ b/src/aosfxplayer.cpp @@ -8,18 +8,44 @@ AOSfxPlayer::AOSfxPlayer(QWidget *parent, AOApplication *p_ao_app) ao_app = p_ao_app; } -void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout) +void AOSfxPlayer::clear() { - BASS_ChannelStop(m_stream); + for (int n_stream = 0; n_stream < m_channelmax; ++n_stream) { + BASS_ChannelStop(m_stream_list[n_stream]); + } + set_volume_internal(m_volume); +} + +void AOSfxPlayer::loop_clear() +{ + for (int n_stream = 0; n_stream < m_channelmax; ++n_stream) { + if ((BASS_ChannelFlags(m_stream_list[n_stream], 0, 0) & BASS_SAMPLE_LOOP)) + BASS_ChannelStop(m_stream_list[n_stream]); + } + set_volume_internal(m_volume); +} + +void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout, + int channel) +{ + if (channel == -1) { + if (BASS_ChannelIsActive(m_stream_list[channel]) == BASS_ACTIVE_PLAYING) + m_channel = (m_channel + 1) % m_channelmax; + channel = m_channel; + } + + BASS_ChannelStop(m_stream_list[channel]); QString misc_path = ""; QString char_path = ""; - QString sound_path = ao_app->get_sounds_path(p_sfx); + QString sound_path = ao_app->get_sfx_suffix(ao_app->get_sounds_path(p_sfx)); if (shout != "") - misc_path = ao_app->get_base_path() + "misc/" + shout + "/" + p_sfx; + misc_path = ao_app->get_sfx_suffix(ao_app->get_base_path() + "misc/" + + shout + "/" + p_sfx); if (p_char != "") - char_path = ao_app->get_character_path(p_char, p_sfx); + char_path = + ao_app->get_sfx_suffix(ao_app->get_character_path(p_char, p_sfx)); QString f_path; @@ -30,24 +56,60 @@ void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout) else f_path = sound_path; - m_stream = BASS_StreamCreateFile(FALSE, f_path.utf16(), 0, 0, - BASS_STREAM_AUTOFREE | BASS_UNICODE | - BASS_ASYNCFILE); + if (f_path.endsWith(".opus")) + m_stream_list[channel] = BASS_OPUS_StreamCreateFile( + FALSE, f_path.utf16(), 0, 0, + BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE); + else + m_stream_list[channel] = BASS_StreamCreateFile( + FALSE, f_path.utf16(), 0, 0, + BASS_STREAM_AUTOFREE | BASS_UNICODE | BASS_ASYNCFILE); set_volume(m_volume); if (ao_app->get_audio_output_device() != "default") - BASS_ChannelSetDevice(m_stream, BASS_GetDevice()); - BASS_ChannelPlay(m_stream, false); + BASS_ChannelSetDevice(m_stream_list[m_channel], BASS_GetDevice()); + BASS_ChannelPlay(m_stream_list[m_channel], false); +} + +void AOSfxPlayer::stop(int channel) +{ + if (channel == -1) { + channel = m_channel; + } + BASS_ChannelStop(m_stream_list[channel]); } -void AOSfxPlayer::stop() { BASS_ChannelStop(m_stream); } +void AOSfxPlayer::set_volume(qreal p_value) +{ + m_volume = p_value / 100; + set_volume_internal(m_volume); +} void AOSfxPlayer::set_volume(int p_value) { - m_volume = p_value; - float volume = p_value / 100.0f; - BASS_ChannelSetAttribute(m_stream, BASS_ATTRIB_VOL, volume); + float volume = static_cast<float>(p_value); + for (int n_stream = 0; n_stream < m_channelmax; ++n_stream) { + BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume); + } +} + +void AOSfxPlayer::set_looping(bool toggle, int channel) +{ + if (channel == -1) { + channel = m_channel; + } + m_looping = toggle; + if (BASS_ChannelFlags(m_stream_list[channel], 0, 0) & BASS_SAMPLE_LOOP) { + if (m_looping == false) + BASS_ChannelFlags(m_stream_list[channel], 0, + BASS_SAMPLE_LOOP); // remove the LOOP flag + } + else { + if (m_looping == true) + BASS_ChannelFlags(m_stream_list[channel], BASS_SAMPLE_LOOP, + BASS_SAMPLE_LOOP); // set the LOOP flag + } } #elif defined(QTAUDIO) // Using Qt's QSoundEffect class AOSfxPlayer::AOSfxPlayer(QWidget *parent, AOApplication *p_ao_app) @@ -82,7 +144,7 @@ void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout) { m_sfx.setSource(QUrl::fromLocalFile(f_path)); - set_volume(m_volume); + set_volume_internal(m_volume); m_sfx.play(); } @@ -90,7 +152,13 @@ void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout) void AOSfxPlayer::stop() { m_sfx.stop(); } -void AOSfxPlayer::set_volume(int p_value) +void AOSfxPlayer::set_volume(qreal p_value) +{ + m_volume = p_value / 100; + set_volume_internal(m_volume); +} + +void AOSfxPlayer::set_volume_internal(qreal p_value) { m_volume = p_value; m_sfx.setVolume(m_volume); @@ -106,5 +174,7 @@ void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout) {} void AOSfxPlayer::stop() {} -void AOSfxPlayer::set_volume(int p_value) {} +void AOSfxPlayer::set_volume(qreal p_value) {} + +void AOSfxPlayer::set_volume_internal(qreal p_value) {} #endif diff --git a/src/aotextarea.cpp b/src/aotextarea.cpp index ebc75e2..52e32f5 100644 --- a/src/aotextarea.cpp +++ b/src/aotextarea.cpp @@ -2,6 +2,14 @@ AOTextArea::AOTextArea(QWidget *p_parent) : QTextBrowser(p_parent) {} +void AOTextArea::append_linked(QString p_message) +{ + QString result = p_message.toHtmlEscaped() + .replace("\n", "<br>") + .replace(url_parser_regex, "<a href='\\1'>\\1</a>"); + this->insertHtml(result); +} + void AOTextArea::append_chatmessage(QString p_name, QString p_message, QString p_colour) { @@ -20,7 +28,7 @@ void AOTextArea::append_chatmessage(QString p_name, QString p_message, p_message += " "; QString result = p_message.toHtmlEscaped() .replace("\n", "<br>") - .replace(omnis_dank_url_regex, "<a href='\\1'>\\1</a>"); + .replace(url_parser_regex, "<a href='\\1'>\\1</a>"); this->insertHtml(result); @@ -40,7 +48,7 @@ void AOTextArea::append_error(QString p_message) p_message += " "; QString result = p_message.replace("\n", "<br>") - .replace(omnis_dank_url_regex, "<a href='\\1'>\\1</a>"); + .replace(url_parser_regex, "<a href='\\1'>\\1</a>"); this->insertHtml("<font color='red'>" + result + "</font>"); diff --git a/src/charselect.cpp b/src/charselect.cpp index c207d57..de9748b 100644 --- a/src/charselect.cpp +++ b/src/charselect.cpp @@ -70,15 +70,15 @@ void Courtroom::set_char_select() ao_app->get_element_dimensions("char_select", filename); if (f_charselect.width < 0 || f_charselect.height < 0) { - qDebug() - << "W: did not find courtroom width or height in courtroom_design.ini!"; + qDebug() << "W: did not find char_select width or height in " + "courtroom_design.ini!"; this->resize(714, 668); } else this->resize(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.png"); + ui_char_select_background->set_image("charselect_background"); filter_character_list(); @@ -133,14 +133,17 @@ void Courtroom::char_clicked(int n_char) return; } - if (n_char == m_cid) { - enter_courtroom(m_cid); - } - else { + if (n_char != m_cid) { + ao_app->send_server_packet( + new AOPacket("PW#" + ui_char_password->text() + "#%")); ao_app->send_server_packet( new AOPacket("CC#" + QString::number(ao_app->s_pv) + "#" + QString::number(n_char) + "#" + get_hdid() + "#%")); } + else + update_character(n_char); + + enter_courtroom(); ui_ic_chat_name->setPlaceholderText(char_list.at(n_char).name); } @@ -225,8 +228,9 @@ void Courtroom::character_loading_finished() 100); ao_app->w_lobby->set_loading_value(loading_value); ao_app->w_lobby->set_loading_text( - "Generating chars:\n" + QString::number(ao_app->generated_chars) + - "/" + QString::number(ao_app->char_list_size)); + tr("Generating chars:\n%1/%2") + .arg(QString::number(ao_app->generated_chars)) + .arg(QString::number(ao_app->char_list_size))); } } diff --git a/src/chatlogpiece.cpp b/src/chatlogpiece.cpp index be3be4a..34a2fea 100644 --- a/src/chatlogpiece.cpp +++ b/src/chatlogpiece.cpp @@ -5,7 +5,7 @@ chatlogpiece::chatlogpiece() name = "UNKNOWN"; showname = "UNKNOWN"; message = "UNKNOWN"; - is_song = false; + p_is_song = false; datetime = QDateTime::currentDateTime().toUTC(); } @@ -15,7 +15,7 @@ chatlogpiece::chatlogpiece(QString p_name, QString p_showname, name = p_name; showname = p_showname; message = p_message; - is_song = p_song; + p_is_song = p_song; datetime = QDateTime::currentDateTime().toUTC(); } @@ -25,7 +25,7 @@ chatlogpiece::chatlogpiece(QString p_name, QString p_showname, name = p_name; showname = p_showname; message = p_message; - is_song = p_song; + p_is_song = p_song; datetime = p_datetime.toUTC(); } @@ -37,7 +37,7 @@ QString chatlogpiece::get_message() { return message; } QDateTime chatlogpiece::get_datetime() { return datetime; } -bool chatlogpiece::get_is_song() { return is_song; } +bool chatlogpiece::is_song() { return p_is_song; } QString chatlogpiece::get_datetime_as_string() { return datetime.toString(); } @@ -46,13 +46,15 @@ QString chatlogpiece::get_full() QString full = "["; full.append(get_datetime_as_string()); - full.append(" UTC] "); + full.append("] "); full.append(get_showname()); full.append(" ("); full.append(get_name()); full.append(")"); - if (is_song) + if (p_is_song) full.append(" has played a song: "); + else + full.append(": "); full.append(get_message()); return full; diff --git a/src/courtroom.cpp b/src/courtroom.cpp index 25b4826..bcd3b47 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -1,4 +1,4 @@ -#include "courtroom.h" +#include "courtroom.h" Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() { @@ -25,8 +25,23 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() } } } +#elif defined QTAUDIO + + if (ao_app->get_audio_output_device() != "default") { + foreach (const QAudioDeviceInfo &deviceInfo, + QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) { + if (ao_app->get_audio_output_device() == deviceInfo.deviceName()) { + ao_app->QtAudioDevice = deviceInfo; + qDebug() << deviceInfo.deviceName() + << "was set as the default audio output device."; + break; + } + } + } #endif + qsrand(static_cast<uint>(QDateTime::currentMSecsSinceEpoch() / 1000)); + keepalive_timer = new QTimer(this); keepalive_timer->start(60000); @@ -38,15 +53,6 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() sfx_delay_timer = new QTimer(this); sfx_delay_timer->setSingleShot(true); - realization_timer = new QTimer(this); - realization_timer->setSingleShot(true); - - testimony_show_timer = new QTimer(this); - testimony_show_timer->setSingleShot(true); - - testimony_hide_timer = new QTimer(this); - testimony_hide_timer->setSingleShot(true); - music_player = new AOMusicPlayer(this, ao_app); music_player->set_volume(0); @@ -74,20 +80,29 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_vp_desk = new AOScene(ui_viewport, ao_app); ui_vp_legacy_desk = new AOScene(ui_viewport, ao_app); - ui_vp_evidence_display = new AOEvidenceDisplay(this, ao_app); + ui_vp_evidence_display = new AOEvidenceDisplay(ui_viewport, ao_app); ui_vp_chatbox = new AOImage(this, ao_app); ui_vp_showname = new QLabel(ui_vp_chatbox); - ui_vp_message = new QTextEdit(ui_vp_chatbox); + ui_vp_showname->setAlignment(Qt::AlignHCenter); + ui_vp_chat_arrow = new AOMovie(ui_vp_chatbox, ao_app); + ui_vp_chat_arrow->set_play_once(false); + + ui_vp_message = new QTextEdit(this); ui_vp_message->setFrameStyle(QFrame::NoFrame); ui_vp_message->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui_vp_message->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui_vp_message->setReadOnly(true); - ui_vp_testimony = new AOImage(this, ao_app); - ui_vp_realization = new AOImage(this, ao_app); + ui_vp_testimony = new AOMovie(this, ao_app); + ui_vp_testimony->set_play_once(false); + ui_vp_testimony->setAttribute(Qt::WA_TransparentForMouseEvents); + ui_vp_effect = new AOMovie(this, ao_app); + ui_vp_effect->setAttribute(Qt::WA_TransparentForMouseEvents); ui_vp_wtce = new AOMovie(this, ao_app); + ui_vp_wtce->setAttribute(Qt::WA_TransparentForMouseEvents); ui_vp_objection = new AOMovie(this, ao_app); + ui_vp_objection->setAttribute(Qt::WA_TransparentForMouseEvents); ui_ic_chatlog = new QTextEdit(this); ui_ic_chatlog->setReadOnly(true); @@ -104,17 +119,42 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_server_chatlog->setReadOnly(true); ui_server_chatlog->setOpenExternalLinks(true); - ui_area_list = new QListWidget(this); + ui_area_list = new QTreeWidget(this); + ui_area_list->setColumnCount(2); + ui_area_list->hideColumn(0); + ui_area_list->setHeaderHidden(true); + ui_area_list->header()->setStretchLastSection(false); + ui_area_list->header()->setSectionResizeMode(QHeaderView::ResizeToContents); ui_area_list->hide(); - ui_music_list = new QListWidget(this); + + ui_music_list = new QTreeWidget(this); + ui_music_list->setColumnCount(2); + ui_music_list->hideColumn(1); + ui_music_list->setHeaderHidden(true); + ui_music_list->header()->setStretchLastSection(false); + ui_music_list->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + ui_music_list->setContextMenuPolicy(Qt::CustomContextMenu); + + ui_music_display = new AOMovie(this, ao_app); + ui_music_display->set_play_once(false); + ui_music_display->setAttribute(Qt::WA_TransparentForMouseEvents); + + ui_music_name = new ScrollText(ui_music_display); + ui_music_name->setText(tr("None")); + ui_music_name->setAttribute(Qt::WA_TransparentForMouseEvents); ui_ic_chat_name = new QLineEdit(this); ui_ic_chat_name->setFrame(false); ui_ic_chat_name->setPlaceholderText(tr("Showname")); - ui_ic_chat_message = new QLineEdit(this); + ui_ic_chat_message = new AOLineEdit(this); ui_ic_chat_message->setFrame(false); ui_ic_chat_message->setPlaceholderText(tr("Message")); + ui_ic_chat_message->preserve_selection(true); + // ui_ic_chat_message->setValidator(new QRegExpValidator(QRegExp("^\\S+(?: + // \\S+)*$"), ui_ic_chat_message)); + // todo: filter out \n from showing up as that commonly breaks the chatlog and + // can be spammed to hell ui_muted = new AOImage(ui_ic_chat_message, ao_app); ui_muted->hide(); @@ -134,21 +174,20 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_music_search->setFrame(false); ui_music_search->setPlaceholderText(tr("Search")); - construct_emotes(); - - ui_emote_left = new AOButton(this, ao_app); - ui_emote_right = new AOButton(this, ao_app); + initialize_emotes(); - ui_emote_dropdown = new QComboBox(this); ui_pos_dropdown = new QComboBox(this); - ui_pos_dropdown->addItem("wit"); - ui_pos_dropdown->addItem("def"); - ui_pos_dropdown->addItem("pro"); - ui_pos_dropdown->addItem("jud"); - ui_pos_dropdown->addItem("hld"); - ui_pos_dropdown->addItem("hlp"); - ui_pos_dropdown->addItem("jur"); - ui_pos_dropdown->addItem("sea"); + + ui_iniswap_dropdown = new QComboBox(this); + ui_iniswap_dropdown->setContextMenuPolicy(Qt::CustomContextMenu); + ui_iniswap_remove = new AOButton(this, ao_app); + + ui_sfx_dropdown = new QComboBox(this); + ui_sfx_dropdown->setContextMenuPolicy(Qt::CustomContextMenu); + ui_sfx_remove = new AOButton(this, ao_app); + + ui_effects_dropdown = new QComboBox(this); + ui_effects_dropdown->setContextMenuPolicy(Qt::CustomContextMenu); ui_defense_bar = new AOImage(this, ao_app); ui_prosecution_bar = new AOImage(this, ao_app); @@ -157,8 +196,6 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_sfx_label = new QLabel(this); ui_blip_label = new QLabel(this); - ui_log_limit_label = new QLabel(this); - ui_hold_it = new AOButton(this, ao_app); ui_objection = new AOButton(this, ao_app); ui_take_that = new AOButton(this, ao_app); @@ -187,6 +224,10 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_guard->setText("Guard"); ui_guard->hide(); + ui_additive = new QCheckBox(this); + ui_additive->setText(tr("Additive")); + ui_additive->hide(); + ui_casing = new QCheckBox(this); ui_casing->setChecked(ao_app->get_casing_enabled()); ui_casing->setText(tr("Casing")); @@ -202,6 +243,7 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_custom_objection = new AOButton(this, ao_app); ui_realization = new AOButton(this, ao_app); + ui_screenshake = new AOButton(this, ao_app); ui_mute = new AOButton(this, ao_app); ui_defense_plus = new AOButton(this, ao_app); @@ -211,15 +253,6 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_prosecution_minus = new AOButton(this, ao_app); ui_text_color = new QComboBox(this); - ui_text_color->addItem("White"); - ui_text_color->addItem("Green"); - ui_text_color->addItem("Red"); - ui_text_color->addItem("Orange"); - ui_text_color->addItem("Blue"); - ui_text_color->addItem("Yellow"); - ui_text_color->addItem("Rainbow"); - ui_text_color->addItem("Pink"); - ui_text_color->addItem("Cyan"); ui_music_slider = new QSlider(Qt::Horizontal, this); ui_music_slider->setRange(0, 100); @@ -233,21 +266,22 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_blip_slider->setRange(0, 100); ui_blip_slider->setValue(ao_app->get_default_blip()); - ui_log_limit_spinbox = new QSpinBox(this); - ui_log_limit_spinbox->setRange(0, 10000); - ui_log_limit_spinbox->setValue(ao_app->get_max_log_size()); - ui_mute_list = new QListWidget(this); 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("% offset"); + ui_pair_offset_spinbox->setSuffix(tr("% offset")); + + ui_pair_order_dropdown = new QComboBox(this); + ui_pair_order_dropdown->addItem("To front"); + ui_pair_order_dropdown->addItem("To behind"); + ui_pair_button = new AOButton(this, ao_app); ui_evidence_button = new AOButton(this, ao_app); - construct_evidence(); + initialize_evidence(); construct_char_select(); @@ -255,6 +289,10 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() connect(ui_vp_objection, SIGNAL(done()), this, SLOT(objection_done())); connect(ui_vp_player_char, SIGNAL(done()), this, SLOT(preanim_done())); + connect(ui_vp_player_char, SIGNAL(shake()), this, SLOT(do_screenshake())); + connect(ui_vp_player_char, SIGNAL(flash()), this, SLOT(do_flash())); + connect(ui_vp_player_char, SIGNAL(play_sfx(QString)), this, + SLOT(play_char_sfx(QString))); connect(text_delay_timer, SIGNAL(timeout()), this, SLOT(start_chat_ticking())); @@ -262,23 +300,28 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() connect(chat_tick_timer, SIGNAL(timeout()), this, SLOT(chat_tick())); - connect(realization_timer, SIGNAL(timeout()), this, SLOT(realization_done())); - - connect(testimony_show_timer, SIGNAL(timeout()), this, - SLOT(hide_testimony())); - connect(testimony_hide_timer, SIGNAL(timeout()), this, - SLOT(show_testimony())); - - connect(ui_emote_left, SIGNAL(clicked()), this, - SLOT(on_emote_left_clicked())); - connect(ui_emote_right, SIGNAL(clicked()), this, - SLOT(on_emote_right_clicked())); - - connect(ui_emote_dropdown, SIGNAL(activated(int)), this, - SLOT(on_emote_dropdown_changed(int))); connect(ui_pos_dropdown, SIGNAL(currentIndexChanged(int)), this, SLOT(on_pos_dropdown_changed(int))); + connect(ui_iniswap_dropdown, SIGNAL(currentIndexChanged(int)), this, + SLOT(on_iniswap_dropdown_changed(int))); + connect(ui_iniswap_dropdown, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(on_iniswap_context_menu_requested(QPoint))); + connect(ui_iniswap_remove, SIGNAL(clicked()), this, + SLOT(on_iniswap_remove_clicked())); + + connect(ui_sfx_dropdown, SIGNAL(currentIndexChanged(int)), this, + SLOT(on_sfx_dropdown_changed(int))); + connect(ui_sfx_dropdown, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(on_sfx_context_menu_requested(QPoint))); + connect(ui_sfx_remove, SIGNAL(clicked()), this, + SLOT(on_sfx_remove_clicked())); + + connect(ui_effects_dropdown, SIGNAL(currentIndexChanged(int)), this, + SLOT(on_effects_dropdown_changed(int))); + connect(ui_effects_dropdown, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(on_effects_context_menu_requested(QPoint))); + connect(ui_mute_list, SIGNAL(clicked(QModelIndex)), this, SLOT(on_mute_list_clicked(QModelIndex))); @@ -288,10 +331,13 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() connect(ui_ooc_chat_message, SIGNAL(returnPressed()), this, SLOT(on_ooc_return_pressed())); - connect(ui_music_list, SIGNAL(doubleClicked(QModelIndex)), this, - SLOT(on_music_list_double_clicked(QModelIndex))); - connect(ui_area_list, SIGNAL(doubleClicked(QModelIndex)), this, - SLOT(on_area_list_double_clicked(QModelIndex))); + connect(ui_music_list, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), + this, SLOT(on_music_list_double_clicked(QTreeWidgetItem *, int))); + connect(ui_music_list, SIGNAL(customContextMenuRequested(QPoint)), this, + SLOT(on_music_list_context_menu_requested(QPoint))); + + connect(ui_area_list, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, + SLOT(on_area_list_double_clicked(QTreeWidgetItem *, int))); connect(ui_hold_it, SIGNAL(clicked()), this, SLOT(on_hold_it_clicked())); connect(ui_objection, SIGNAL(clicked()), this, SLOT(on_objection_clicked())); @@ -301,6 +347,8 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() connect(ui_realization, SIGNAL(clicked()), this, SLOT(on_realization_clicked())); + connect(ui_screenshake, SIGNAL(clicked()), this, + SLOT(on_screenshake_clicked())); connect(ui_mute, SIGNAL(clicked()), this, SLOT(on_mute_clicked())); @@ -323,9 +371,6 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() connect(ui_blip_slider, SIGNAL(valueChanged(int)), this, SLOT(on_blip_slider_moved(int))); - connect(ui_log_limit_spinbox, SIGNAL(valueChanged(int)), this, - SLOT(on_log_limit_changed(int))); - connect(ui_ooc_toggle, SIGNAL(clicked()), this, SLOT(on_ooc_toggle_clicked())); @@ -353,6 +398,7 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() connect(ui_pre, SIGNAL(clicked()), this, SLOT(on_pre_clicked())); connect(ui_flip, SIGNAL(clicked()), this, SLOT(on_flip_clicked())); + connect(ui_additive, SIGNAL(clicked()), this, SLOT(on_additive_clicked())); connect(ui_guard, SIGNAL(clicked()), this, SLOT(on_guard_clicked())); connect(ui_casing, SIGNAL(clicked()), this, SLOT(on_casing_clicked())); @@ -364,6 +410,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_order_dropdown, SIGNAL(currentIndexChanged(int)), this, + SLOT(on_pair_order_dropdown_changed(int))); connect(ui_evidence_button, SIGNAL(clicked()), this, SLOT(on_evidence_button_clicked())); @@ -435,7 +483,7 @@ void Courtroom::set_widgets() ui_background->move(0, 0); ui_background->resize(m_courtroom_width, m_courtroom_height); - ui_background->set_image("courtroombackground.png"); + ui_background->set_image("courtroombackground"); set_size_and_pos(ui_viewport, "viewport"); @@ -459,20 +507,20 @@ void Courtroom::set_widgets() if (ao_app->casing_alerts_enabled) { ui_announce_casing->show(); + ui_casing->show(); } else { ui_announce_casing->hide(); + ui_casing->hide(); } // We also show the non-server-dependent client additions. // Once again, if the theme can't display it, set_move_and_pos will catch // them. ui_settings->show(); - ui_log_limit_label->show(); - ui_log_limit_spinbox->show(); ui_vp_background->move(0, 0); - ui_vp_background->resize(ui_viewport->width(), ui_viewport->height()); + ui_vp_background->combo_resize(ui_viewport->width(), ui_viewport->height()); ui_vp_speedlines->move(0, 0); ui_vp_speedlines->combo_resize(ui_viewport->width(), ui_viewport->height()); @@ -486,7 +534,7 @@ void Courtroom::set_widgets() // the AO2 desk element ui_vp_desk->move(0, 0); - ui_vp_desk->resize(ui_viewport->width(), ui_viewport->height()); + ui_vp_desk->combo_resize(ui_viewport->width(), ui_viewport->height()); // the size of the ui_vp_legacy_desk element relies on various factors and is // set in set_scene() @@ -497,24 +545,28 @@ void Courtroom::set_widgets() ui_vp_legacy_desk->hide(); ui_vp_evidence_display->move(0, 0); - ui_vp_evidence_display->resize(ui_viewport->width(), ui_viewport->height()); + ui_vp_evidence_display->combo_resize(ui_viewport->width(), + ui_viewport->height()); - set_size_and_pos(ui_vp_showname, "showname"); + ui_vp_chat_arrow->move(0, 0); + pos_size_type design_ini_result = + ao_app->get_element_dimensions("chat_arrow", "courtroom_design.ini"); - set_size_and_pos(ui_vp_message, "message"); - ui_vp_message->setTextInteractionFlags(Qt::NoTextInteraction); - ui_vp_message->setStyleSheet("background-color: rgba(0, 0, 0, 0);" - "color: white"); + if (design_ini_result.width < 0 || design_ini_result.height < 0) { + qDebug() << "W: could not find \"chat_arrow\" in courtroom_design.ini"; + ui_vp_chat_arrow->hide(); + } + else { + ui_vp_chat_arrow->move(design_ini_result.x, design_ini_result.y); + ui_vp_chat_arrow->combo_resize(design_ini_result.width, + design_ini_result.height); + } ui_vp_testimony->move(ui_viewport->x(), ui_viewport->y()); - ui_vp_testimony->resize(ui_viewport->width(), ui_viewport->height()); - ui_vp_testimony->set_image("testimony.png"); - ui_vp_testimony->hide(); + ui_vp_testimony->combo_resize(ui_viewport->width(), ui_viewport->height()); - ui_vp_realization->move(ui_viewport->x(), ui_viewport->y()); - ui_vp_realization->resize(ui_viewport->width(), ui_viewport->height()); - ui_vp_realization->set_image("realizationflash.png"); - ui_vp_realization->hide(); + ui_vp_effect->move(ui_viewport->x(), ui_viewport->y()); + ui_vp_effect->combo_resize(ui_viewport->width(), ui_viewport->height()); ui_vp_wtce->move(ui_viewport->x(), ui_viewport->y()); ui_vp_wtce->combo_resize(ui_viewport->width(), ui_viewport->height()); @@ -523,25 +575,60 @@ void Courtroom::set_widgets() ui_vp_objection->combo_resize(ui_viewport->width(), ui_viewport->height()); set_size_and_pos(ui_ic_chatlog, "ic_chatlog"); + ui_ic_chatlog->setFrameShape(QFrame::NoFrame); set_size_and_pos(ui_ms_chatlog, "ms_chatlog"); + ui_ms_chatlog->setFrameShape(QFrame::NoFrame); set_size_and_pos(ui_server_chatlog, "server_chatlog"); + ui_server_chatlog->setFrameShape(QFrame::NoFrame); set_size_and_pos(ui_mute_list, "mute_list"); ui_mute_list->hide(); set_size_and_pos(ui_pair_list, "pair_list"); ui_pair_list->hide(); + ui_pair_list->setToolTip(tr("Select a character you wish to pair with.")); + 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 " + "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( + tr("Change the order of appearance for your character.")); + set_size_and_pos(ui_pair_button, "pair_button"); - ui_pair_button->set_image("pair_button.png"); + ui_pair_button->set_image("pair_button"); + ui_pair_button->setToolTip( + tr("Display the list of characters to pair with.")); set_size_and_pos(ui_area_list, "music_list"); - ui_area_list->setStyleSheet("background-color: rgba(0, 0, 0, 0);"); + ui_area_list->header()->setMinimumSectionSize(ui_area_list->width()); set_size_and_pos(ui_music_list, "music_list"); + ui_music_list->header()->setMinimumSectionSize(ui_music_list->width()); + + set_size_and_pos(ui_music_name, "music_name"); + + ui_music_display->move(0, 0); + design_ini_result = + ao_app->get_element_dimensions("music_display", "courtroom_design.ini"); + + if (design_ini_result.width < 0 || design_ini_result.height < 0) { + qDebug() << "W: could not find \"music_name\" in courtroom_design.ini"; + ui_music_display->hide(); + } + else { + ui_music_display->move(design_ini_result.x, design_ini_result.y); + ui_music_display->combo_resize(design_ini_result.width, + design_ini_result.height); + } + + ui_music_display->play("music_display"); if (is_ao2_bg) { set_size_and_pos(ui_ic_chat_message, "ao2_ic_chat_message"); @@ -559,11 +646,23 @@ void Courtroom::set_widgets() ui_ic_chat_name->setStyleSheet( "QLineEdit{background-color: rgba(180, 180, 180, 255);}"); - ui_vp_chatbox->set_image("chatmed.png"); + ui_vp_chatbox->set_image("chatblank"); ui_vp_chatbox->hide(); + set_size_and_pos(ui_vp_showname, "showname"); + + set_size_and_pos(ui_vp_message, "message"); + ui_vp_message->hide(); + + // We detached the text as parent from the chatbox so it doesn't get affected + // by the screenshake. + ui_vp_message->move(ui_vp_message->x() + ui_vp_chatbox->x(), + ui_vp_message->y() + ui_vp_chatbox->y()); + ui_vp_message->setTextInteractionFlags(Qt::NoTextInteraction); + ui_muted->resize(ui_ic_chat_message->width(), ui_ic_chat_message->height()); - ui_muted->set_image("muted.png"); + ui_muted->set_image("muted"); + ui_muted->setToolTip(tr("Oops, you're muted!")); set_size_and_pos(ui_ooc_chat_message, "ooc_chat_message"); ui_ooc_chat_message->setStyleSheet("background-color: rgba(0, 0, 0, 0);"); @@ -573,25 +672,71 @@ void Courtroom::set_widgets() // set_size_and_pos(ui_area_password, "area_password"); set_size_and_pos(ui_music_search, "music_search"); - - set_size_and_pos(ui_emotes, "emotes"); - - set_size_and_pos(ui_emote_left, "emote_left"); - ui_emote_left->set_image("arrow_left.png"); - - set_size_and_pos(ui_emote_right, "emote_right"); - ui_emote_right->set_image("arrow_right.png"); + ui_music_search->setStyleSheet("background-color: rgba(0, 0, 0, 0);"); set_size_and_pos(ui_emote_dropdown, "emote_dropdown"); + ui_emote_dropdown->setToolTip( + tr("Set your character's emote to play on your next message.")); + set_size_and_pos(ui_pos_dropdown, "pos_dropdown"); + ui_pos_dropdown->setToolTip( + tr("Set your character's supplementary background.")); + + set_size_and_pos(ui_iniswap_dropdown, "iniswap_dropdown"); + ui_iniswap_dropdown->setEditable(true); + ui_iniswap_dropdown->setInsertPolicy(QComboBox::InsertAtBottom); + ui_iniswap_dropdown->setToolTip( + 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/<charname>/iniswaps.ini")); + + set_size_and_pos(ui_iniswap_remove, "iniswap_remove"); + ui_iniswap_remove->setText("X"); + ui_iniswap_remove->set_image("evidencex"); + ui_iniswap_remove->setToolTip( + tr("Remove the currently selected iniswap from the list and return to " + "the original character folder.")); + ui_iniswap_remove->hide(); + + set_size_and_pos(ui_sfx_dropdown, "sfx_dropdown"); + ui_sfx_dropdown->setEditable(true); + ui_sfx_dropdown->setInsertPolicy(QComboBox::InsertAtBottom); + ui_sfx_dropdown->setToolTip( + tr("Set a sound effect to play on your next 'Preanim'. Leaving it on " + "Default will use the emote-defined sound (if any).\n" + "Edit by typing and pressing Enter, [X] to remove. This saves to your " + "base/characters/<charname>/soundlist.ini")); + + 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->hide(); + + set_size_and_pos(ui_effects_dropdown, "effects_dropdown"); + ui_effects_dropdown->setInsertPolicy(QComboBox::InsertAtBottom); + ui_effects_dropdown->setToolTip( + tr("Choose an effect to play on your next spoken message.\n" + "The effects are defined in your theme/effects/effects.ini. Your " + "character can define custom effects by\n" + "char.ini [Options] category, effects = 'miscname' where it referes " + "to misc/<miscname>/effects.ini to read the effects.")); + // Todo: recode this entire fucking system with these dumbass goddamn ini's + // why is everything so specifically coded for all these purposes is ABSTRACT + // CODING not a thing now huh what the FUCK why do I gotta do this pleASE FOR + // THE LOVE OF GOD SPARE ME FROM THIS FRESH HELL btw i still love coding. + QPoint p_point = ao_app->get_button_spacing("effects_icon_size", filename); + ui_effects_dropdown->setIconSize(QSize(p_point.x(), p_point.y())); set_size_and_pos(ui_defense_bar, "defense_bar"); - ui_defense_bar->set_image("defensebar" + QString::number(defense_bar_state) + - ".png"); + ui_defense_bar->set_image("defensebar" + QString::number(defense_bar_state)); set_size_and_pos(ui_prosecution_bar, "prosecution_bar"); - ui_prosecution_bar->set_image( - "prosecutionbar" + QString::number(prosecution_bar_state) + ".png"); + ui_prosecution_bar->set_image("prosecutionbar" + + QString::number(prosecution_bar_state)); set_size_and_pos(ui_music_label, "music_label"); ui_music_label->setText("Music"); @@ -600,183 +745,272 @@ void Courtroom::set_widgets() set_size_and_pos(ui_blip_label, "blip_label"); ui_blip_label->setText("Blips"); - set_size_and_pos(ui_log_limit_label, "log_limit_label"); - ui_log_limit_label->setText("Log limit"); - set_size_and_pos(ui_hold_it, "hold_it"); - ui_hold_it->set_image("holdit.png"); + ui_hold_it->setText(tr("Hold It!")); + ui_hold_it->setToolTip(tr("When this is turned on, your next in-character " + "message will be a shout!")); + ui_hold_it->set_image("holdit"); + set_size_and_pos(ui_objection, "objection"); - ui_objection->set_image("objection.png"); + ui_objection->setText(tr("Objection!")); + ui_objection->setToolTip(tr("When this is turned on, your next in-character " + "message will be a shout!")); + ui_objection->set_image("objection"); + set_size_and_pos(ui_take_that, "take_that"); - ui_take_that->set_image("takethat.png"); + ui_take_that->setText(tr("Take That!")); + ui_take_that->setToolTip(tr("When this is turned on, your next in-character " + "message will be a shout!")); + ui_take_that->set_image("takethat"); set_size_and_pos(ui_ooc_toggle, "ooc_toggle"); - ui_ooc_toggle->setText("Server"); + ui_ooc_toggle->setText(tr("Server")); + ui_ooc_toggle->setToolTip( + tr("Toggle between server chat and global AO2 chat.")); set_size_and_pos(ui_witness_testimony, "witness_testimony"); - ui_witness_testimony->set_image("witnesstestimony.png"); + ui_witness_testimony->set_image("witnesstestimony"); + ui_witness_testimony->setToolTip(tr("This will display the animation in the " + "viewport as soon as it is pressed.")); set_size_and_pos(ui_cross_examination, "cross_examination"); - ui_cross_examination->set_image("crossexamination.png"); + ui_cross_examination->set_image("crossexamination"); + ui_cross_examination->setToolTip(tr("This will display the animation in the " + "viewport as soon as it is pressed.")); set_size_and_pos(ui_guilty, "guilty"); - ui_guilty->set_image("guilty.png"); + ui_guilty->setText(tr("Guilty!")); + ui_guilty->set_image("guilty"); + ui_guilty->setToolTip(tr("This will display the animation in the viewport as " + "soon as it is pressed.")); set_size_and_pos(ui_not_guilty, "not_guilty"); - ui_not_guilty->set_image("notguilty.png"); + ui_not_guilty->set_image("notguilty"); + ui_not_guilty->setToolTip(tr("This will display the animation in the " + "viewport as soon as it is pressed.")); set_size_and_pos(ui_change_character, "change_character"); - ui_change_character->setText("Change character"); + ui_change_character->setText(tr("Change character")); + ui_change_character->set_image("change_character"); + ui_change_character->setToolTip( + tr("Bring up the Character Select Screen and change your character.")); set_size_and_pos(ui_reload_theme, "reload_theme"); - ui_reload_theme->setText("Reload theme"); + ui_reload_theme->setText(tr("Reload theme")); + ui_reload_theme->set_image("reload_theme"); + ui_reload_theme->setToolTip( + tr("Refresh the theme and update all of the ui elements to match.")); set_size_and_pos(ui_call_mod, "call_mod"); - ui_call_mod->setText("Call mod"); + ui_call_mod->setText(tr("Call mod")); + ui_call_mod->set_image("call_mod"); + ui_call_mod->setToolTip( + tr("Request the attention of the current server's moderator.")); set_size_and_pos(ui_settings, "settings"); - ui_settings->setText("Settings"); + ui_settings->setText(tr("Settings")); + ui_settings->set_image("settings"); + ui_settings->setToolTip( + tr("Allows you to change various aspects of the client.")); set_size_and_pos(ui_announce_casing, "casing_button"); - ui_announce_casing->setText("Casing"); + ui_announce_casing->setText(tr("Casing")); + ui_announce_casing->set_image("casing_button"); + ui_announce_casing->setToolTip( + tr("An interface to help you announce a case (you have to be a CM first " + "to be able to announce cases)")); set_size_and_pos(ui_switch_area_music, "switch_area_music"); - ui_switch_area_music->setText("A/M"); + ui_switch_area_music->setText(tr("A/M")); + ui_switch_area_music->set_image("switch_area_music"); + ui_switch_area_music->setToolTip(tr("Switch between Areas and Music lists")); set_size_and_pos(ui_pre, "pre"); - ui_pre->setText("Preanim"); + ui_pre->setText(tr("Preanim")); + 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( + tr("If preanim is checked, display the input text immediately as the " + "animation plays concurrently.")); + set_size_and_pos(ui_flip, "flip"); + ui_flip->setToolTip(tr("Mirror your character's emotes when checked.")); + + set_size_and_pos(ui_additive, "additive"); + ui_additive->setToolTip( + tr("Add text to your last spoken message when checked.")); set_size_and_pos(ui_guard, "guard"); + ui_guard->setToolTip( + tr("Do not listen to mod calls when checked, preventing them from " + "playing sounds or focusing attention on the window.")); set_size_and_pos(ui_casing, "casing"); + ui_casing->setToolTip(tr("Lets you receive case alerts when enabled.\n" + "(You can set your preferences in the Settings!)")); set_size_and_pos(ui_showname_enable, "showname_enable"); + ui_showname_enable->setToolTip( + tr("Display customized shownames for all users when checked.")); set_size_and_pos(ui_custom_objection, "custom_objection"); - ui_custom_objection->set_image("custom.png"); + ui_custom_objection->setText(tr("Custom Shout!")); + ui_custom_objection->set_image("custom"); + ui_custom_objection->setToolTip( + tr("This will display the custom character-defined animation in the " + "viewport as soon as it is pressed.\n" + "To make one, your character's folder must contain " + "custom.[webp/apng/gif/png] and custom.[wav/ogg/opus] sound effect")); set_size_and_pos(ui_realization, "realization"); - ui_realization->set_image("realization.png"); + ui_realization->set_image("realization"); + ui_realization->setToolTip( + tr("Play realization sound and animation in the viewport on the next " + "spoken message when checked.")); + + set_size_and_pos(ui_screenshake, "screenshake"); + ui_screenshake->set_image("screenshake"); + ui_screenshake->setToolTip( + tr("Shake the screen on next spoken message when checked.")); set_size_and_pos(ui_mute, "mute_button"); - ui_mute->set_image("mute.png"); + ui_mute->setText("Mute"); + ui_mute->set_image("mute"); + ui_mute->setToolTip( + tr("Display the list of character folders you wish to mute.")); set_size_and_pos(ui_defense_plus, "defense_plus"); - ui_defense_plus->set_image("defplus.png"); + ui_defense_plus->set_image("defplus"); + ui_defense_plus->setToolTip(tr("Increase the health bar.")); set_size_and_pos(ui_defense_minus, "defense_minus"); - ui_defense_minus->set_image("defminus.png"); + ui_defense_minus->set_image("defminus"); + ui_defense_minus->setToolTip(tr("Decrease the health bar.")); set_size_and_pos(ui_prosecution_plus, "prosecution_plus"); - ui_prosecution_plus->set_image("proplus.png"); + ui_prosecution_plus->set_image("proplus"); + ui_prosecution_plus->setToolTip(tr("Increase the health bar.")); set_size_and_pos(ui_prosecution_minus, "prosecution_minus"); - ui_prosecution_minus->set_image("prominus.png"); + ui_prosecution_minus->set_image("prominus"); + ui_prosecution_minus->setToolTip(tr("Decrease the health bar.")); set_size_and_pos(ui_text_color, "text_color"); + ui_text_color->setToolTip( + tr("Change the text color of the spoken message.\n" + "You can also select a part of your currently typed message and use " + "the dropdown to change its color!")); + set_text_color_dropdown(); set_size_and_pos(ui_music_slider, "music_slider"); set_size_and_pos(ui_sfx_slider, "sfx_slider"); set_size_and_pos(ui_blip_slider, "blip_slider"); - set_size_and_pos(ui_log_limit_spinbox, "log_limit_spinbox"); - - set_size_and_pos(ui_evidence_button, "evidence_button"); - ui_evidence_button->set_image("evidencebutton.png"); - - set_size_and_pos(ui_evidence, "evidence_background"); - ui_evidence->set_image("evidencebackground.png"); - - set_size_and_pos(ui_evidence_name, "evidence_name"); - - set_size_and_pos(ui_evidence_buttons, "evidence_buttons"); - - set_size_and_pos(ui_evidence_left, "evidence_left"); - ui_evidence_left->set_image("arrow_left.png"); - - set_size_and_pos(ui_evidence_right, "evidence_right"); - ui_evidence_right->set_image("arrow_right.png"); - - set_size_and_pos(ui_evidence_present, "evidence_present"); - ui_evidence_present->set_image("present_disabled.png"); - - set_size_and_pos(ui_evidence_overlay, "evidence_overlay"); - ui_evidence_overlay->set_image("evidenceoverlay.png"); - - set_size_and_pos(ui_evidence_delete, "evidence_delete"); - ui_evidence_delete->set_image("deleteevidence.png"); - - set_size_and_pos(ui_evidence_image_name, "evidence_image_name"); - - set_size_and_pos(ui_evidence_image_button, "evidence_image_button"); - - set_size_and_pos(ui_evidence_x, "evidence_x"); - ui_evidence_x->set_image("evidencex.png"); - - set_size_and_pos(ui_evidence_description, "evidence_description"); - - ui_selector->set_image("char_selector.png"); + ui_selector->set_image("char_selector"); ui_selector->hide(); set_size_and_pos(ui_back_to_lobby, "back_to_lobby"); - ui_back_to_lobby->setText("Back to Lobby"); + ui_back_to_lobby->setText(tr("Back to Lobby")); + ui_back_to_lobby->setToolTip(tr("Return back to the server list.")); set_size_and_pos(ui_char_password, "char_password"); set_size_and_pos(ui_char_buttons, "char_buttons"); set_size_and_pos(ui_char_select_left, "char_select_left"); - ui_char_select_left->set_image("arrow_left.png"); + ui_char_select_left->set_image("arrow_left"); set_size_and_pos(ui_char_select_right, "char_select_right"); - ui_char_select_right->set_image("arrow_right.png"); + ui_char_select_right->set_image("arrow_right"); set_size_and_pos(ui_spectator, "spectator"); + ui_spectator->setToolTip(tr("Become a spectator. You won't be able to " + "interact with the in-character screen.")); + + refresh_evidence(); } void Courtroom::set_fonts() { - set_font(ui_vp_showname, "showname"); - set_font(ui_vp_message, "message"); - set_font(ui_ic_chatlog, "ic_chatlog"); - set_font(ui_ms_chatlog, "ms_chatlog"); - set_font(ui_server_chatlog, "server_chatlog"); - set_font(ui_music_list, "music_list"); - set_font(ui_area_list, "music_list"); - - // Set color of labels and checkboxes - const QString design_file = "courtroom_fonts.ini"; - QColor f_color = ao_app->get_color("label_color", design_file); - QString color_string = "color: rgba(" + QString::number(f_color.red()) + - ", " + QString::number(f_color.green()) + ", " + - QString::number(f_color.blue()) + ", 255); }"; - QString style_sheet_string = "QLabel {" + color_string + - "}" - "QCheckBox {" + - color_string + "}"; - setStyleSheet(style_sheet_string); -} - -void Courtroom::set_font(QWidget *widget, QString p_identifier) + set_font(ui_vp_showname, "", "showname"); + set_font(ui_vp_message, "", "message"); + set_font(ui_ic_chatlog, "", "ic_chatlog"); + set_font(ui_ms_chatlog, "", "ms_chatlog"); + set_font(ui_server_chatlog, "", "server_chatlog"); + set_font(ui_music_list, "", "music_list"); + set_font(ui_area_list, "", "area_list"); + set_font(ui_music_name, "", "music_name"); + + set_dropdowns(); +} + +void Courtroom::set_font(QWidget *widget, QString class_name, + QString p_identifier) { QString design_file = "courtroom_fonts.ini"; - int f_weight = ao_app->get_font_size(p_identifier, design_file); - QString class_name = widget->metaObject()->className(); + int f_pointsize = ao_app->get_font_size(p_identifier, design_file); + QString font_name = + ao_app->get_font_name(p_identifier + "_font", design_file); + QColor f_color = ao_app->get_color(p_identifier + "_color", design_file); + bool bold = ao_app->get_font_size(p_identifier + "_bold", design_file) == + 1; // is the font bold or not? + bool antialias = + ao_app->get_font_size(p_identifier + "_sharp", design_file) != + 1; // is the font anti-aliased or not? + + this->set_qfont(widget, class_name, + get_qfont(font_name, f_pointsize, antialias), f_color, bold); +} + +QFont Courtroom::get_qfont(QString font_name, int f_pointsize, bool antialias) +{ + QFont font; + if (font_name.isEmpty()) + font_name = "Arial"; - QString fontt = ao_app->get_font_name(p_identifier + "_font", design_file); - widget->setFont(QFont(fontt, f_weight)); + QFont::StyleStrategy style_strategy = QFont::PreferDefault; + if (!antialias) + style_strategy = QFont::NoAntialias; - QColor f_color = ao_app->get_color(p_identifier + "_color", design_file); + font = QFont(font_name, f_pointsize); + font.setStyleHint(QFont::SansSerif, style_strategy); + return font; +} + +void Courtroom::set_qfont(QWidget *widget, QString class_name, QFont font, + QColor f_color, bool bold) +{ + if (class_name.isEmpty()) + class_name = widget->metaObject()->className(); + + font.setBold(bold); + widget->setFont(font); QString style_sheet_string = class_name + " { background-color: rgba(0, 0, 0, 0);\n" + "color: rgba(" + QString::number(f_color.red()) + ", " + QString::number(f_color.green()) + - ", " + QString::number(f_color.blue()) + ", 255); }"; - + ", " + QString::number(f_color.blue()) + ", 255);}"; widget->setStyleSheet(style_sheet_string); } +void Courtroom::set_dropdown(QWidget *widget) +{ + QString f_file = "courtroom_stylesheets.css"; + QString style_sheet_string = ao_app->get_stylesheet(f_file); + if (style_sheet_string != "") + widget->setStyleSheet(style_sheet_string); +} + +void Courtroom::set_dropdowns() +{ + set_dropdown( + this); // EXPERIMENTAL - Read the style-sheet as-is for maximum memeage + // set_dropdown(ui_text_color, "[TEXT COLOR]"); + // set_dropdown(ui_pos_dropdown, "[POS DROPDOWN]"); + // set_dropdown(ui_emote_dropdown, "[EMOTE DROPDOWN]"); + // set_dropdown(ui_mute_list, "[MUTE LIST]"); +} + void Courtroom::set_window_title(QString p_title) { this->setWindowTitle(p_title); @@ -816,6 +1050,22 @@ void Courtroom::set_taken(int n_char, bool p_taken) char_list.replace(n_char, f_char); } +QPoint Courtroom::get_theme_pos(QString p_identifier) +{ + QString filename = "courtroom_design.ini"; + + pos_size_type design_ini_result = + ao_app->get_element_dimensions(p_identifier, filename); + + if (design_ini_result.width < 0 || design_ini_result.height < 0) { + qDebug() << "W: could not find \"" << p_identifier << "\" in " << filename; + return QPoint(0, 0); + } + else { + return QPoint(design_ini_result.x, design_ini_result.y); + } +} + void Courtroom::done_received() { m_cid = -1; @@ -837,15 +1087,38 @@ void Courtroom::done_received() ui_spectator->show(); } -void Courtroom::set_background(QString p_background) +void Courtroom::set_background(QString p_background, bool display) { - testimony_in_progress = false; - + ui_vp_testimony->stop(); current_background = p_background; - is_ao2_bg = file_exists(ao_app->get_background_path("defensedesk.png")) && - file_exists(ao_app->get_background_path("prosecutiondesk.png")) && - file_exists(ao_app->get_background_path("stand.png")); + // welcome to hardcode central may I take your order of regularly scheduled + // CBT + QMap<QString, QString> default_pos; + default_pos["defenseempty"] = "def"; + default_pos["helperstand"] = "hld"; + default_pos["prosecutorempty"] = "pro"; + default_pos["prohelperstand"] = "hlp"; + default_pos["witnessempty"] = "wit"; + default_pos["judgestand"] = "jud"; + default_pos["jurystand"] = "jur"; + default_pos["seancestand"] = "sea"; + + // 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_static_image_suffix( + ao_app->get_background_path(key)))) { + pos_list.append(default_pos[key]); + } + } + + // TODO: search through extra/custom pos and add them to the pos dropdown as + // well + + set_pos_dropdown(pos_list); + + is_ao2_bg = true; if (is_ao2_bg) { set_size_and_pos(ui_vp_chatbox, "ao2_chatbox"); @@ -855,10 +1128,74 @@ void Courtroom::set_background(QString p_background) set_size_and_pos(ui_vp_chatbox, "chatbox"); set_size_and_pos(ui_ic_chat_message, "ic_chat_message"); } + + if (display) { + ui_vp_speedlines->stop(); + ui_vp_player_char->stop(); + + ui_vp_sideplayer_char->stop(); + ui_vp_effect->stop(); + ui_vp_message->hide(); + ui_vp_chatbox->hide(); + + // Stop the chat arrow from animating + ui_vp_chat_arrow->stop(); + + text_state = 2; + anim_state = 3; + ui_vp_objection->stop(); + chat_tick_timer->stop(); + ui_vp_evidence_display->reset(); + set_scene( + QString::number(ao_app->get_desk_mod(current_char, current_emote)), + current_side); + } } -void Courtroom::enter_courtroom(int p_cid) +void Courtroom::set_side(QString p_side) { + if (p_side == "") + current_side = ao_app->get_char_side(current_char); + else + current_side = p_side; + + for (int i = 0; i < ui_pos_dropdown->count(); ++i) { + QString pos = ui_pos_dropdown->itemText(i); + if (pos == current_side) { + // Block the signals to prevent setCurrentIndex from triggering a pos + // change + ui_pos_dropdown->blockSignals(true); + + // Set the index on dropdown ui element to let you know what pos you're on + // right now + ui_pos_dropdown->setCurrentIndex(i); + + // Unblock the signals so the element can be used for setting pos again + ui_pos_dropdown->blockSignals(false); + + // alright we dun, jobs done here boyos + break; + } + } +} + +void Courtroom::set_pos_dropdown(QStringList pos_dropdowns) +{ + // Block the signals to prevent setCurrentIndex from triggering a pos change + ui_pos_dropdown->blockSignals(true); + pos_dropdown_list = pos_dropdowns; + ui_pos_dropdown->clear(); + ui_pos_dropdown->addItems(pos_dropdown_list); + // Unblock the signals so the element can be used for setting pos again + ui_pos_dropdown->blockSignals(false); + + qDebug() << pos_dropdown_list; +} + +void Courtroom::update_character(int p_cid) +{ + bool newchar = m_cid != p_cid; + m_cid = p_cid; QString f_char; @@ -876,6 +1213,7 @@ void Courtroom::enter_courtroom(int p_cid) } current_char = f_char; + current_side = ao_app->get_char_side(current_char); current_emote_page = 0; current_emote = 0; @@ -885,17 +1223,18 @@ void Courtroom::enter_courtroom(int p_cid) else ui_emotes->show(); + refresh_emotes(); set_emote_page(); set_emote_dropdown(); - current_evidence_page = 0; - current_evidence = 0; - - set_evidence_page(); + set_sfx_dropdown(); + set_effects_dropdown(); - QString side = ao_app->get_char_side(f_char); + qDebug() << "update_character called"; + if (newchar) // Avoid infinite loop of death and suffering + set_iniswap_dropdown(); - if (side == "jud") { + if (current_side == "jud") { ui_witness_testimony->show(); ui_cross_examination->show(); ui_not_guilty->show(); @@ -917,18 +1256,36 @@ void Courtroom::enter_courtroom(int p_cid) } if (ao_app->custom_objection_enabled && - (file_exists(ao_app->get_character_path(current_char, "custom.gif")) || - file_exists(ao_app->get_character_path(current_char, "custom.apng"))) && - file_exists(ao_app->get_character_path(current_char, "custom.wav"))) + file_exists(ao_app->get_image_suffix( + ao_app->get_character_path(current_char, "custom")))) ui_custom_objection->show(); else ui_custom_objection->hide(); + ui_char_select_background->hide(); + ui_ic_chat_message->setEnabled(m_cid != -1); + ui_ic_chat_message->setFocus(); +} + +void Courtroom::enter_courtroom() +{ + set_widgets(); + + current_evidence_page = 0; + current_evidence = 0; + + set_evidence_page(); + if (ao_app->flipping_enabled) ui_flip->show(); else ui_flip->hide(); + if (ao_app->additive_enabled) + ui_additive->show(); + else + ui_additive->hide(); + if (ao_app->casing_alerts_enabled) ui_casing->show(); else @@ -937,27 +1294,24 @@ void Courtroom::enter_courtroom(int p_cid) list_music(); list_areas(); - music_player->set_volume(ui_music_slider->value()); + music_player->set_volume(ui_music_slider->value(), 0); // set music + // Set the ambience and other misc. music layers + for (int i = 1; i < music_player->m_channelmax; ++i) { + music_player->set_volume(ui_sfx_slider->value(), i); + } sfx_player->set_volume(ui_sfx_slider->value()); objection_player->set_volume(ui_sfx_slider->value()); blip_player->set_volume(ui_blip_slider->value()); - testimony_in_progress = false; - - set_widgets(); - + ui_vp_testimony->stop(); // ui_server_chatlog->setHtml(ui_server_chatlog->toHtml()); - - ui_char_select_background->hide(); - - ui_ic_chat_message->setEnabled(m_cid != -1); - ui_ic_chat_message->setFocus(); } +// Todo: multithread this due to some servers having large as hell music list void Courtroom::list_music() { ui_music_list->clear(); - music_row_to_number.clear(); + // ui_music_search->setText(""); QString f_file = "courtroom_design.ini"; @@ -966,31 +1320,48 @@ void Courtroom::list_music() int n_listed_songs = 0; + QTreeWidgetItem *parent = nullptr; for (int n_song = 0; n_song < music_list.size(); ++n_song) { QString i_song = music_list.at(n_song); - QString i_song_listname = i_song; - i_song_listname = i_song_listname.left(i_song_listname.lastIndexOf(".")); + QString i_song_listname = i_song.left(i_song.lastIndexOf(".")); + i_song_listname = i_song_listname.right( + i_song_listname.length() - (i_song_listname.lastIndexOf("/") + 1)); + + QTreeWidgetItem *treeItem; + if (i_song_listname != i_song && + parent != nullptr) // not a category, parent exists + treeItem = new QTreeWidgetItem(parent); + else + treeItem = new QTreeWidgetItem(ui_music_list); + treeItem->setText(0, i_song_listname); + treeItem->setText(1, i_song); - if (i_song.toLower().contains(ui_music_search->text().toLower())) { - ui_music_list->addItem(i_song_listname); - music_row_to_number.append(n_song); + QString song_path = ao_app->get_music_path(i_song); - QString song_path = ao_app->get_music_path(i_song); + if (file_exists(song_path)) + treeItem->setBackground(0, found_brush); + else + treeItem->setBackground(0, missing_brush); - if (file_exists(song_path)) - ui_music_list->item(n_listed_songs)->setBackground(found_brush); - else - ui_music_list->item(n_listed_songs)->setBackground(missing_brush); + if (i_song_listname == + i_song) // Not supposed to be a song to begin with - a category? + parent = treeItem; + ++n_listed_songs; + } - ++n_listed_songs; - } + ui_music_list->expandAll(); // Needs to somehow remember which categories were + // expanded/collapsed if the music list didn't + // change since last time + if (ui_music_search->text() != "") { + on_music_search_edited(ui_music_search->text()); } } +// Todo: multithread this due to some servers having large as hell area list void Courtroom::list_areas() { ui_area_list->clear(); - area_row_to_number.clear(); + // ui_music_search->setText(""); QString f_file = "courtroom_design.ini"; @@ -1006,13 +1377,11 @@ void Courtroom::list_areas() for (int n_area = 0; n_area < area_list.size(); ++n_area) { QString i_area = ""; - i_area.append("["); - i_area.append(QString::number(n_area)); - i_area.append("] "); - i_area.append(area_list.at(n_area)); if (ao_app->arup_enabled) { + i_area.prepend("[" + QString::number(n_area) + "] "); // Give it the index + i_area.append("\n "); i_area.append(arup_statuses.at(n_area)); @@ -1027,35 +1396,38 @@ void Courtroom::list_areas() i_area.append(arup_locks.at(n_area)); } - if (i_area.toLower().contains(ui_music_search->text().toLower())) { - ui_area_list->addItem(i_area); - area_row_to_number.append(n_area); + QTreeWidgetItem *treeItem = new QTreeWidgetItem(ui_area_list); + treeItem->setText(0, area_list.at(n_area)); + treeItem->setText(1, i_area); - if (ao_app->arup_enabled) { - // Colouring logic here. - ui_area_list->item(n_listed_areas)->setBackground(free_brush); - if (arup_locks.at(n_area) == "LOCKED") { - ui_area_list->item(n_listed_areas)->setBackground(locked_brush); - } - else { - if (arup_statuses.at(n_area) == "LOOKING-FOR-PLAYERS") - ui_area_list->item(n_listed_areas)->setBackground(lfp_brush); - else if (arup_statuses.at(n_area) == "CASING") - ui_area_list->item(n_listed_areas)->setBackground(casing_brush); - else if (arup_statuses.at(n_area) == "RECESS") - ui_area_list->item(n_listed_areas)->setBackground(recess_brush); - else if (arup_statuses.at(n_area) == "RP") - ui_area_list->item(n_listed_areas)->setBackground(rp_brush); - else if (arup_statuses.at(n_area) == "GAMING") - ui_area_list->item(n_listed_areas)->setBackground(gaming_brush); - } + if (ao_app->arup_enabled) { + // Coloring logic here. + treeItem->setBackground(1, free_brush); + if (arup_locks.at(n_area) == "LOCKED") { + treeItem->setBackground(1, locked_brush); } else { - ui_area_list->item(n_listed_areas)->setBackground(free_brush); + if (arup_statuses.at(n_area) == "LOOKING-FOR-PLAYERS") + treeItem->setBackground(1, lfp_brush); + else if (arup_statuses.at(n_area) == "CASING") + treeItem->setBackground(1, casing_brush); + else if (arup_statuses.at(n_area) == "RECESS") + treeItem->setBackground(1, recess_brush); + else if (arup_statuses.at(n_area) == "RP") + treeItem->setBackground(1, rp_brush); + else if (arup_statuses.at(n_area) == "GAMING") + treeItem->setBackground(1, gaming_brush); } - - ++n_listed_areas; } + else { + treeItem->setBackground(1, free_brush); + } + + ++n_listed_areas; + } + + if (ui_music_search->text() != "") { + on_music_search_edited(ui_music_search->text()); } } @@ -1063,22 +1435,29 @@ void Courtroom::append_ms_chatmessage(QString f_name, QString f_message) { ui_ms_chatlog->append_chatmessage( f_name, f_message, - ao_app->get_color("ooc_default_color", "courtroom_design.ini").name()); + ao_app->get_color("ms_chatlog_sender_color", "courtroom_fonts.ini") + .name()); } void Courtroom::append_server_chatmessage(QString p_name, QString p_message, - QString p_colour) + QString p_color) { - QString colour = "#000000"; - - if (p_colour == "0") - colour = - ao_app->get_color("ooc_default_color", "courtroom_design.ini").name(); - if (p_colour == "1") - colour = - ao_app->get_color("ooc_server_color", "courtroom_design.ini").name(); + QString color = "#000000"; + + if (p_color == "0") + color = ao_app->get_color("ms_chatlog_sender_color", "courtroom_fonts.ini") + .name(); + if (p_color == "1") + color = + ao_app->get_color("server_chatlog_sender_color", "courtroom_fonts.ini") + .name(); + if (p_message == "Logged in as a moderator.") { + ui_guard->show(); + append_server_chatmessage( + "CLIENT", "You were granted the Disable Modcalls button.", "1"); + } - ui_server_chatlog->append_chatmessage(p_name, p_message, colour); + ui_server_chatlog->append_chatmessage(p_name, p_message, color); } void Courtroom::on_chat_return_pressed() @@ -1115,7 +1494,8 @@ void Courtroom::on_chat_return_pressed() QStringList packet_contents; - QString f_side = ao_app->get_char_side(current_char); + if (current_side == "") + current_side = ao_app->get_char_side(current_char); QString f_desk_mod = "chat"; @@ -1136,16 +1516,22 @@ void Courtroom::on_chat_return_pressed() packet_contents.append(ui_ic_chat_message->text()); - packet_contents.append(f_side); + packet_contents.append(current_side); - packet_contents.append(ao_app->get_sfx_name(current_char, current_emote)); + packet_contents.append(get_char_sfx()); + if (ui_pre->isChecked() && !ao_app->is_stickysounds_enabled()) { + ui_sfx_dropdown->blockSignals(true); + ui_sfx_dropdown->setCurrentIndex(0); + ui_sfx_dropdown->blockSignals(false); + ui_sfx_remove->hide(); + } 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 == 5) + if (f_emote_mod == 4 || f_emote_mod == 5) f_emote_mod = 6; else f_emote_mod = 2; @@ -1167,8 +1553,7 @@ void Courtroom::on_chat_return_pressed() packet_contents.append(QString::number(f_emote_mod)); packet_contents.append(QString::number(m_cid)); - packet_contents.append( - QString::number(ao_app->get_sfx_delay(current_char, current_emote))); + packet_contents.append(QString::number(get_char_sfx_delay())); QString f_obj_state; @@ -1206,7 +1591,7 @@ void Courtroom::on_chat_return_pressed() if (text_color < 0) f_text_color = "0"; - else if (text_color > 8) + else if (text_color > max_colors) f_text_color = "0"; else f_text_color = QString::number(text_color); @@ -1227,13 +1612,17 @@ void Courtroom::on_chat_return_pressed() // Similarly, we send over whom we're paired with, unless we have chosen // ourselves. Or a charid of -1 or lower, through some means. if (other_charid > -1 && other_charid != m_cid) { - packet_contents.append(QString::number(other_charid)); - packet_contents.append(QString::number(offset_with_pair)); + QString packet = QString::number(other_charid); + if (ao_app->effects_enabled) // Only servers with effects enabled will + // support pair reordering + packet += "^" + QString::number(pair_order); + packet_contents.append(packet); } else { packet_contents.append("-1"); - packet_contents.append("0"); } + // Send the offset as it's gonna be used regardless + 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()) { @@ -1244,6 +1633,55 @@ void Courtroom::on_chat_return_pressed() } } + // If the server we're on supports Looping SFX and Screenshake, use it if the + // emote uses it. + if (ao_app->looping_sfx_support_enabled) { + packet_contents.append( + ao_app->get_sfx_looping(current_char, current_emote)); + packet_contents.append(QString::number(screenshake_state)); + + QString pre_emote = ao_app->get_pre_emote(current_char, current_emote); + QString emote = ao_app->get_emote(current_char, current_emote); + QStringList emotes_to_check = {pre_emote, "(b)" + emote, "(a)" + emote}; + QStringList effects_to_check = {"_FrameScreenshake", "_FrameRealization", + "_FrameSFX"}; + + foreach (QString f_effect, effects_to_check) { + QString packet; + foreach (QString f_emote, emotes_to_check) { + packet += f_emote; + if (ao_app->is_frame_network_enabled()) { + QString sfx_frames = + ao_app + ->read_ini_tags( + ao_app->get_character_path(current_char, "char.ini"), + f_emote.append(f_effect)) + .join("|"); + if (sfx_frames != "") + packet += "|" + sfx_frames; + } + packet += "^"; + } + packet_contents.append(packet); + } + } + + if (ao_app->additive_enabled) { + packet_contents.append(ui_additive->isChecked() ? "1" : "0"); + } + if (ao_app->effects_enabled) { + QString fx_sound = ao_app->get_effect_sound(effect, current_char); + QString p_effect = + ao_app->read_char_ini(current_char, "effects", "Options"); + packet_contents.append(effect + "|" + p_effect + "|" + fx_sound); + if (!ao_app->is_stickyeffects_enabled()) { + ui_effects_dropdown->blockSignals(true); + ui_effects_dropdown->setCurrentIndex(0); + ui_effects_dropdown->blockSignals(false); + effect = ""; + } + } + ao_app->send_server_packet(new AOPacket("MS", packet_contents)); } @@ -1274,63 +1712,89 @@ void Courtroom::handle_chatmessage(QStringList *p_contents) int f_char_id = m_chatmessage[CHAR_ID].toInt(); - if (f_char_id < 0 || f_char_id >= char_list.size()) + if (f_char_id >= 0 && f_char_id >= char_list.size()) return; if (mute_map.value(m_chatmessage[CHAR_ID].toInt())) return; QString f_showname; - if (m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked()) { + if (f_char_id > 0 && + (m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked())) { f_showname = ao_app->get_showname(char_list.at(f_char_id).name); } else { f_showname = m_chatmessage[SHOWNAME]; } + if (f_showname.trimmed() + .isEmpty()) // Pure whitespace showname, get outta here. + f_showname = m_chatmessage[CHAR_NAME]; + QString f_message = f_showname + ": " + m_chatmessage[MESSAGE] + '\n'; + // Remove undesired newline chars + m_chatmessage[MESSAGE].remove("\n"); + chatmessage_is_empty = + m_chatmessage[MESSAGE] == " " || m_chatmessage[MESSAGE] == ""; - if (f_message == previous_ic_message) + if (f_char_id >= 0 && !chatmessage_is_empty && + f_message == previous_ic_message) // Not a system message return; + if (f_char_id <= -1) + previous_ic_message = + ""; // System messages don't care about repeating themselves + else + previous_ic_message = f_message; + + // Stop the chat arrow from animating + ui_vp_chat_arrow->stop(); + text_state = 0; anim_state = 0; ui_vp_objection->stop(); - ui_vp_player_char->stop(); chat_tick_timer->stop(); ui_vp_evidence_display->reset(); - chatmessage_is_empty = - m_chatmessage[MESSAGE] == " " || m_chatmessage[MESSAGE] == ""; - - if (m_chatmessage[MESSAGE] == ui_ic_chat_message->text() && + // Hey, our message showed up! Cool! + if (m_chatmessage[MESSAGE] == ui_ic_chat_message->text().remove("\n") && m_chatmessage[CHAR_ID].toInt() == m_cid) { ui_ic_chat_message->clear(); + if (ui_additive->isChecked()) + ui_ic_chat_message->insert(" "); objection_state = 0; realization_state = 0; + screenshake_state = 0; is_presenting_evidence = false; - ui_pre->setChecked(false); - ui_hold_it->set_image("holdit.png"); - ui_objection->set_image("objection.png"); - ui_take_that->set_image("takethat.png"); - ui_custom_objection->set_image("custom.png"); - ui_realization->set_image("realization.png"); - ui_evidence_present->set_image("present_disabled.png"); + if (!ao_app->is_stickypres_enabled()) + ui_pre->setChecked(false); + ui_hold_it->set_image("holdit"); + ui_objection->set_image("objection"); + ui_take_that->set_image("takethat"); + ui_custom_objection->set_image("custom"); + ui_realization->set_image("realization"); + ui_screenshake->set_image("screenshake"); + ui_evidence_present->set_image("present"); } + // Let the server handle actually checking if they're allowed to do this. + is_additive = m_chatmessage[ADDITIVE].toInt() == 1; + + QString f_charname = ""; + if (f_char_id >= 0) + f_charname = ao_app->get_showname(char_list.at(f_char_id).name); + chatlogpiece *temp = - new chatlogpiece(ao_app->get_showname(char_list.at(f_char_id).name), - f_showname, ": " + m_chatmessage[MESSAGE], false); + new chatlogpiece(f_charname, f_showname, m_chatmessage[MESSAGE], false); ic_chatlog_history.append(*temp); + ao_app->append_to_file(temp->get_full(), ao_app->log_filename, true); while (ic_chatlog_history.size() > log_maximum_blocks && log_maximum_blocks > 0) { ic_chatlog_history.removeFirst(); } - append_ic_text(": " + m_chatmessage[MESSAGE], f_showname); - - previous_ic_message = f_message; + append_ic_text(m_chatmessage[MESSAGE], f_showname); int objection_mod = m_chatmessage[OBJECTION_MOD].toInt(); QString f_char = m_chatmessage[CHAR_NAME]; @@ -1340,26 +1804,28 @@ void Courtroom::handle_chatmessage(QStringList *p_contents) if (objection_mod <= 4 && objection_mod >= 1) { switch (objection_mod) { case 1: - ui_vp_objection->play("holdit", f_char, f_custom_theme); - objection_player->play("holdit.wav", f_char, f_custom_theme); + ui_vp_objection->play("holdit_bubble", f_char, f_custom_theme, 724); + objection_player->play("holdit", f_char, f_custom_theme); break; case 2: - ui_vp_objection->play("objection", f_char, f_custom_theme); - objection_player->play("objection.wav", f_char, f_custom_theme); + ui_vp_objection->play("objection_bubble", f_char, f_custom_theme, 724); + objection_player->play("objection", f_char, f_custom_theme); + if (ao_app->objection_stop_music()) + music_player->stop(); break; case 3: - ui_vp_objection->play("takethat", f_char, f_custom_theme); - objection_player->play("takethat.wav", f_char, f_custom_theme); + ui_vp_objection->play("takethat_bubble", f_char, f_custom_theme, 724); + objection_player->play("takethat", f_char, f_custom_theme); break; // case 4 is AO2 only case 4: - ui_vp_objection->play("custom", f_char, f_custom_theme); - objection_player->play("custom.wav", f_char, f_custom_theme); + ui_vp_objection->play("custom", f_char, f_custom_theme, 724); + objection_player->play("custom", f_char, f_custom_theme); break; default: qDebug() << "W: Logic error in objection switch statement!"; } - + sfx_player->clear(); // Objection played! Cut all sfx. int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); if (emote_mod == 0) @@ -1375,9 +1841,25 @@ void Courtroom::handle_chatmessage_2() { ui_vp_speedlines->stop(); ui_vp_player_char->stop(); + ui_vp_effect->stop(); + // Clear all looping sfx to prevent obnoxiousness + sfx_player->loop_clear(); + + if (!m_chatmessage[FRAME_SFX].isEmpty() && + ao_app->is_frame_network_enabled()) { + // ORDER IS IMPORTANT!! + QStringList netstrings = {m_chatmessage[FRAME_SCREENSHAKE], + m_chatmessage[FRAME_REALIZATION], + m_chatmessage[FRAME_SFX]}; + ui_vp_player_char->network_strings = netstrings; + } + else + ui_vp_player_char->network_strings.clear(); - if (m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked()) { - QString real_name = char_list.at(m_chatmessage[CHAR_ID].toInt()).name; + int f_charid = m_chatmessage[CHAR_ID].toInt(); + if (f_charid >= 0 && + (m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked())) { + QString real_name = char_list.at(f_charid).name; QString f_showname = ao_app->get_showname(real_name); @@ -1387,38 +1869,92 @@ void Courtroom::handle_chatmessage_2() ui_vp_showname->setText(m_chatmessage[SHOWNAME]); } - ui_vp_message->clear(); - ui_vp_chatbox->hide(); - - QString chatbox = ao_app->get_chat(m_chatmessage[CHAR_NAME]); - - if (chatbox == "") - ui_vp_chatbox->set_image("chatmed.png"); - else { - QString chatbox_path = - ao_app->get_base_path() + "misc/" + chatbox + "/chatbox.png"; - ui_vp_chatbox->set_image_from_path(chatbox_path); + if (ui_vp_showname->text().trimmed().isEmpty()) // Whitespace showname + { + ui_vp_chatbox->set_image("chatblank"); } + else // Aw yeah dude do some showname resizing magic + { + if (!ui_vp_chatbox->set_image("chat")) + ui_vp_chatbox->set_image("chatbox"); - ui_vp_showname->setStyleSheet( - "QLabel { color : " + get_text_color("_showname").name() + "; }"); + QFontMetrics fm(ui_vp_showname->font()); + int fm_width = fm.horizontalAdvance(ui_vp_showname->text()); - set_scene(); - set_text_color(); + QString chatbox_path = ao_app->get_theme_path("chat"); + QString chatbox = ao_app->get_chat(m_chatmessage[CHAR_NAME]); + if (chatbox != "") { + chatbox_path = ao_app->get_base_path() + "misc/" + chatbox + "/chat"; + if (!ui_vp_chatbox->set_chatbox(chatbox_path)) + ui_vp_chatbox->set_chatbox(chatbox_path + "box"); - // Check if the message needs to be centered. - QString f_message = m_chatmessage[MESSAGE]; - if (f_message.size() >= 2) { - if (f_message.startsWith("~~")) { - message_is_centered = true; + pos_size_type design_ini_result = ao_app->get_element_dimensions( + "chat_arrow", "courtroom_design.ini", m_chatmessage[CHAR_NAME]); + if (design_ini_result.width < 0 || design_ini_result.height < 0) { + qDebug() << "W: could not find \"chat_arrow\" in courtroom_design.ini"; + ui_vp_chat_arrow->hide(); + } + else { + ui_vp_chat_arrow->move(design_ini_result.x, design_ini_result.y); + ui_vp_chat_arrow->combo_resize(design_ini_result.width, + design_ini_result.height); + } } - else { - message_is_centered = false; + + pos_size_type default_width = ao_app->get_element_dimensions( + "showname", "courtroom_design.ini", m_chatmessage[CHAR_NAME]); + int extra_width = + ao_app + ->get_design_element("showname_extra_width", "courtroom_design.ini", + m_chatmessage[CHAR_NAME]) + .toInt(); + + if (extra_width > 0) { + if (fm_width > default_width.width && + ui_vp_chatbox->set_chatbox( + chatbox_path + + "med")) // This text be big. Let's do some shenanigans. + { + ui_vp_showname->resize(default_width.width + extra_width, + ui_vp_showname->height()); + if (fm_width > ui_vp_showname->width() && + ui_vp_chatbox->set_chatbox(chatbox_path + + "big")) // Biggest possible size for us. + { + ui_vp_showname->resize( + static_cast<int>(default_width.width + (extra_width * 2)), + ui_vp_showname->height()); + } + } + else + ui_vp_showname->resize(default_width.width, ui_vp_showname->height()); } } - else { - ui_vp_message->setAlignment(Qt::AlignLeft); - } + + ui_vp_message->hide(); + ui_vp_chatbox->hide(); + + // todo: put this in its own function or update + QString design_file = "courtroom_fonts.ini"; + int f_pointsize = ao_app->get_font_size("message", design_file); + QString font_name = ao_app->get_font_name("message_font", design_file); + QColor f_color = ao_app->get_color("message_color", design_file); + bool bold = ao_app->get_font_size("message_bold", design_file) == + 1; // is the font bold or not? + bool antialias = ao_app->get_font_size("message_sharp", design_file) != + 1; // is the font anti-aliased or not? + + QString chatfont = ao_app->get_chat_font(m_chatmessage[CHAR_NAME]); + if (chatfont != "") + font_name = chatfont; + + int chatsize = ao_app->get_chat_size(m_chatmessage[CHAR_NAME]); + if (chatsize != -1) + f_pointsize = chatsize; + this->set_qfont(ui_vp_message, "", + get_qfont(font_name, f_pointsize, antialias), f_color, bold); + + set_scene(m_chatmessage[DESK_MOD], m_chatmessage[SIDE]); int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); @@ -1431,113 +1967,41 @@ void Courtroom::handle_chatmessage_2() // Making the second character appear. if (m_chatmessage[OTHER_CHARID].isEmpty()) { - // If there is no second character, hide 'em, and center the first. - ui_vp_sideplayer_char->hide(); + // If there is no second character, hide 'em + ui_vp_sideplayer_char->stop(); ui_vp_sideplayer_char->move(0, 0); - - ui_vp_player_char->move(0, 0); } else { bool ok; - int got_other_charid = m_chatmessage[OTHER_CHARID].toInt(&ok); + int got_other_charid = m_chatmessage[OTHER_CHARID].split("^")[0].toInt(&ok); if (ok) { if (got_other_charid > -1) { // If there is, show them! ui_vp_sideplayer_char->show(); - // Depending on where we are, we offset the characters, and reorder - // their stacking. - if (side == "def") { - // We also move the character down depending on how far the are to the - // right. - int hor_offset = m_chatmessage[SELF_OFFSET].toInt(); - int vert_offset = 0; - if (hor_offset > 0) { - vert_offset = hor_offset / 10; - } - ui_vp_player_char->move(ui_viewport->width() * hor_offset / 100, - ui_viewport->height() * vert_offset / 100); - - // We do the same with the second character. - int hor2_offset = m_chatmessage[OTHER_OFFSET].toInt(); - int vert2_offset = 0; - if (hor2_offset > 0) { - vert2_offset = hor2_offset / 10; - } - ui_vp_sideplayer_char->move(ui_viewport->width() * hor2_offset / 100, - ui_viewport->height() * vert2_offset / - 100); - - // Finally, we reorder them based on who is more to the left. - // The person more to the left is more in the front. - if (hor2_offset >= hor_offset) { - ui_vp_sideplayer_char->raise(); - ui_vp_player_char->raise(); - } - else { - ui_vp_player_char->raise(); - ui_vp_sideplayer_char->raise(); - } - ui_vp_desk->raise(); - ui_vp_legacy_desk->raise(); - } - else if (side == "pro") { - // Almost the same thing happens here, but in reverse. - int hor_offset = m_chatmessage[SELF_OFFSET].toInt(); - int vert_offset = 0; - if (hor_offset < 0) { - // We don't want to RAISE the char off the floor. - vert_offset = -1 * hor_offset / 10; - } - ui_vp_player_char->move(ui_viewport->width() * hor_offset / 100, - ui_viewport->height() * vert_offset / 100); - - // We do the same with the second character. - int hor2_offset = m_chatmessage[OTHER_OFFSET].toInt(); - int vert2_offset = 0; - if (hor2_offset < 0) { - vert2_offset = -1 * hor2_offset / 10; - } - ui_vp_sideplayer_char->move(ui_viewport->width() * hor2_offset / 100, - ui_viewport->height() * vert2_offset / - 100); - - // Finally, we reorder them based on who is more to the right. - if (hor2_offset <= hor_offset) { - ui_vp_sideplayer_char->raise(); - ui_vp_player_char->raise(); - } - else { - ui_vp_player_char->raise(); - ui_vp_sideplayer_char->raise(); - } - ui_vp_desk->raise(); - ui_vp_legacy_desk->raise(); - } - else { - // In every other case, the person more to the left is on top. - // These cases also don't move the characters down. - int hor_offset = m_chatmessage[SELF_OFFSET].toInt(); - ui_vp_player_char->move(ui_viewport->width() * hor_offset / 100, 0); - - // We do the same with the second character. - int hor2_offset = m_chatmessage[OTHER_OFFSET].toInt(); - ui_vp_sideplayer_char->move(ui_viewport->width() * hor2_offset / 100, - 0); - - // Finally, we reorder them based on who is more to the left. - // The person more to the left is more in the front. - if (hor2_offset >= hor_offset) { - ui_vp_sideplayer_char->raise(); - ui_vp_player_char->raise(); - } - else { - ui_vp_player_char->raise(); - ui_vp_sideplayer_char->raise(); + int other_offset = m_chatmessage[OTHER_OFFSET].toInt(); + ui_vp_sideplayer_char->move(ui_viewport->width() * other_offset / 100, + 0); + + QStringList args = m_chatmessage[OTHER_CHARID].split("^"); + if (args.size() > + 1) // This ugly workaround is so we don't make an extra packet just + // for this purpose. Rewrite pairing when? + { + // Change the order of appearance based on the pair order variable + int order = args.at(1).toInt(); + switch (order) { + case 0: + ui_vp_sideplayer_char->stackUnder(ui_vp_player_char); + break; + case 1: + ui_vp_player_char->stackUnder(ui_vp_sideplayer_char); + break; + default: + break; } - ui_vp_desk->raise(); - ui_vp_legacy_desk->raise(); } + // We should probably also play the other character's idle emote. if (ao_app->flipping_enabled && m_chatmessage[OTHER_FLIP].toInt() == 1) ui_vp_sideplayer_char->set_flipped(true); @@ -1551,11 +2015,17 @@ void Courtroom::handle_chatmessage_2() // really is no second character, hide 'em, and center the first. ui_vp_sideplayer_char->hide(); ui_vp_sideplayer_char->move(0, 0); - - ui_vp_player_char->move(0, 0); } } } + // 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); + else + ui_vp_player_char->move(0, 0); switch (emote_mod) { case 1: @@ -1575,6 +2045,91 @@ void Courtroom::handle_chatmessage_2() } } +void Courtroom::do_screenshake() +{ + if (!ao_app->is_shake_enabled()) + return; + + // This way, the animation is reset in such a way that last played screenshake + // would return to its "final frame" properly. This properly resets all UI + // elements without having to bother keeping track of "origin" positions. + // Works great wit the chat text being detached from the chat box! + screenshake_animation_group->setCurrentTime( + screenshake_animation_group->duration()); + screenshake_animation_group->clear(); + + QList<QWidget *> affected_list = {ui_vp_background, ui_vp_player_char, + ui_vp_sideplayer_char, ui_vp_chatbox}; + + // I would prefer if this was its own "shake" function to be honest. + foreach (QWidget *ui_element, affected_list) { + QPropertyAnimation *screenshake_animation = + new QPropertyAnimation(ui_element, "pos", this); + QPoint pos_default = QPoint(ui_element->x(), ui_element->y()); + + int duration = 300; // How long does the screenshake last + int frequency = 20; // How often in ms is there a "jolt" frame + int maxframes = duration / frequency; + int max_x = 7; // Max deviation from origin on x axis + int max_y = 7; // Max deviation from origin on y axis + screenshake_animation->setDuration(duration); + for (int frame = 0; frame < maxframes; frame++) { + double fraction = double(frame * frequency) / duration; + int rng = qrand(); // QRandomGenerator::global()->generate(); + int rand_x = max_x - (int(rng) % (max_x * 2)); + int rand_y = max_y - (int(rng + 100) % (max_y * 2)); + screenshake_animation->setKeyValueAt( + fraction, QPoint(pos_default.x() + rand_x, pos_default.y() + rand_y)); + } + screenshake_animation->setEndValue(pos_default); + screenshake_animation->setEasingCurve(QEasingCurve::Linear); + screenshake_animation_group->addAnimation(screenshake_animation); + } + + screenshake_animation_group->start(); +} + +void Courtroom::do_flash() +{ + if (!ao_app->is_effects_enabled()) + return; + + QString f_char = m_chatmessage[CHAR_NAME]; + QString f_custom_theme = ao_app->get_char_shouts(f_char); + ui_vp_effect->play("realizationflash", f_char, f_custom_theme, 60); +} + +void Courtroom::do_effect(QString fx_name, QString fx_sound, QString p_char, + QString p_folder) +{ + + QString effect = ao_app->get_effect(fx_name, p_char, p_folder); + if (effect == "") + return; + + if (fx_sound != "") + sfx_player->play(fx_sound); + + // Only check if effects are disabled after playing the sound if it exists + if (!ao_app->is_effects_enabled()) + return; + + ui_vp_effect->set_play_once( + false); // The effects themselves dictate whether or not they're looping. + // Static effects will linger. + ui_vp_effect->play(effect); // It will set_play_once to true if the filepath + // provided is not designed to loop more than once +} + +void Courtroom::play_char_sfx(QString sfx_name) +{ + sfx_player->play(sfx_name); + // sfx_player->set_looping(false); + // if (ao_app->get_looping_sfx()) + // sfx_player->set_looping( + // ao_app->get_sfx_looping(current_char, current_emote) == "1"); +} + void Courtroom::handle_chatmessage_3() { start_chat_ticking(); @@ -1582,14 +2137,29 @@ void Courtroom::handle_chatmessage_3() int f_evi_id = m_chatmessage[EVIDENCE_ID].toInt(); QString f_side = m_chatmessage[SIDE]; + QString f_showname; + int f_char_id = m_chatmessage[CHAR_ID].toInt(); + if (f_char_id > 0 && + (m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked())) { + f_showname = ao_app->get_showname(char_list.at(f_char_id).name); + } + else { + f_showname = m_chatmessage[SHOWNAME]; + } + if (f_showname.trimmed() + .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()) { // 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_name = local_evidence_list.at(f_evi_id - 1).name; // def jud and hlp should display the evidence icon on the RIGHT side bool is_left_side = !(f_side == "def" || f_side == "hlp" || f_side == "jud" || f_side == "jur"); ui_vp_evidence_display->show_evidence(f_image, is_left_side, ui_sfx_slider->value()); + append_ic_text(f_name, f_showname, "has presented evidence"); } int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); @@ -1604,40 +2174,31 @@ void Courtroom::handle_chatmessage_3() ui_vp_sideplayer_char->hide(); ui_vp_player_char->move(0, 0); + QString f_char = m_chatmessage[CHAR_NAME]; + QString f_custom_theme = ao_app->get_char_shouts(f_char); if (side == "pro" || side == "hlp" || side == "wit") - ui_vp_speedlines->play("prosecution_speedlines"); + ui_vp_speedlines->play("prosecution_speedlines", f_char, f_custom_theme); else - ui_vp_speedlines->play("defense_speedlines"); - } - - int f_anim_state = 0; - // BLUE is from an enum in datatypes.h - bool text_is_blue = m_chatmessage[TEXT_COLOR].toInt() == BLUE; - - if (!text_is_blue && text_state == 1) { - // talking - f_anim_state = 2; - entire_message_is_blue = false; - } - else { - // idle - f_anim_state = 3; - entire_message_is_blue = true; + ui_vp_speedlines->play("defense_speedlines", f_char, f_custom_theme); } - if (f_anim_state <= anim_state) - return; - - ui_vp_player_char->stop(); - QString f_char = m_chatmessage[CHAR_NAME]; - QString f_emote = m_chatmessage[EMOTE]; + // If this color is talking + color_is_talking = + color_markdown_talking_list.at(m_chatmessage[TEXT_COLOR].toInt()); - if (f_anim_state == 2) { - ui_vp_player_char->play_talking(f_char, f_emote); + if (color_is_talking && text_state == 1 && + anim_state < 2) // Set it to talking as we're not on that already + { + ui_vp_player_char->stop(); + ui_vp_player_char->play_talking(m_chatmessage[CHAR_NAME], + m_chatmessage[EMOTE]); anim_state = 2; } - else { - ui_vp_player_char->play_idle(f_char, f_emote); + else if (anim_state < 3) // Set it to idle as we're not on that already + { + ui_vp_player_char->stop(); + ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME], + m_chatmessage[EMOTE]); anim_state = 3; } @@ -1654,121 +2215,250 @@ void Courtroom::handle_chatmessage_3() } } -QString Courtroom::filter_ic_text(QString p_text) +QString Courtroom::filter_ic_text(QString p_text, bool html, int target_pos, + int default_color) { - // Get rid of centering. - if (p_text.startsWith(": ~~")) { - // Don't forget, the p_text part actually everything after the name! - // Hence why we check for ': ~~'. + QString p_text_escaped; - // Let's remove those two tildes, then. - // : _ ~ ~ - // 0 1 2 3 - p_text.remove(2, 2); - } - - // Get rid of the inline-colouring. - // I know, I know, excessive code duplication. - // Nobody looks in here, I'm fine. - int trick_check_pos = 0; + int check_pos = 0; + int check_pos_escaped = 0; bool ic_next_is_not_special = false; - QString f_character = p_text.at(trick_check_pos); - std::stack<INLINE_COLOURS> ic_colour_stack; - while (trick_check_pos < p_text.size()) { - f_character = p_text.at(trick_check_pos); - - // Escape character. - if (f_character == "\\" and !ic_next_is_not_special) { - ic_next_is_not_special = true; - p_text.remove(trick_check_pos, 1); + std::stack<int> ic_color_stack; + + // Text alignment shenanigans. Could make a dropdown for this later, too! + QString align; + if (p_text.trimmed().startsWith("~~")) { + p_text.remove(p_text.indexOf("~~"), 2); + if (target_pos != -1) { + target_pos = qMax(0, target_pos - 2); } - - // Text speed modifier. - else if (f_character == "{" and !ic_next_is_not_special) { - p_text.remove(trick_check_pos, 1); + align = "center"; + } + else if (p_text.trimmed().startsWith("~>")) { + p_text.remove(p_text.indexOf("~>"), 2); + if (target_pos != -1) { + target_pos = qMax(0, target_pos - 2); } - else if (f_character == "}" and !ic_next_is_not_special) { - p_text.remove(trick_check_pos, 1); + align = "right"; + } + else if (p_text.trimmed().startsWith("<>")) { + p_text.remove(p_text.indexOf("<>"), 2); + if (target_pos != -1) { + target_pos = qMax(0, target_pos - 2); } + align = "justify"; + } - // Orange inline colourisation. - else if (f_character == "|" and !ic_next_is_not_special) { - if (!ic_colour_stack.empty()) { - if (ic_colour_stack.top() == INLINE_ORANGE) { - ic_colour_stack.pop(); - p_text.remove(trick_check_pos, 1); - } - else { - ic_colour_stack.push(INLINE_ORANGE); - p_text.remove(trick_check_pos, 1); - } - } - else { - ic_colour_stack.push(INLINE_ORANGE); - p_text.remove(trick_check_pos, 1); - } - } + // If html is enabled, prepare this text to be all ready for it. + if (html) { + ic_color_stack.push(default_color); + QString appendage = "<font color=\"" + + color_rgb_list.at(default_color).name(QColor::HexRgb) + + "\">"; - // Blue inline colourisation. - else if (f_character == "(" and !ic_next_is_not_special) { - ic_colour_stack.push(INLINE_BLUE); - trick_check_pos++; - } - else if (f_character == ")" and !ic_next_is_not_special and - !ic_colour_stack.empty()) { - if (ic_colour_stack.top() == INLINE_BLUE) { - ic_colour_stack.pop(); - trick_check_pos++; - } - else { + if (!align.isEmpty()) + appendage.prepend("<div align=" + align + ">"); + + p_text_escaped.insert(check_pos_escaped, appendage); + check_pos_escaped += appendage.size(); + } + + // Current issue: does not properly escape html stuff. + // Solution: probably parse p_text and export into a different string + // separately, perform some mumbo jumbo to properly adjust string indexes. + while (check_pos < p_text.size()) { + QString f_rest = p_text.right(p_text.size() - check_pos); + QTextBoundaryFinder tbf(QTextBoundaryFinder::Grapheme, f_rest); + QString f_character; + int f_char_length; + + tbf.toNextBoundary(); + + if (tbf.position() == -1) + f_character = f_rest; + else + f_character = f_rest.left(tbf.position()); + + // if (f_character == "&") //oh shit it's probably an escaped html + // { + // //Skip escaped chars like you would graphemes + // QRegularExpression re("&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-f]{1,6});", + // QRegularExpression::CaseInsensitiveOption); QRegularExpressionMatch + // match = re.match(f_rest); if (match.hasMatch()) //OH SHIT IT IS, + // PANIC, PANIC + // { + // f_character = match.captured(0); //Phew, we solved the big problem + // here. + // } + // } + + if (html) + f_character = f_character.toHtmlEscaped(); + + f_char_length = f_character.length(); + + bool color_update = false; + bool is_end = false; + bool skip = false; + + if (!ic_next_is_not_special) { + if (f_character == "\\") { ic_next_is_not_special = true; + skip = true; } - } - - // Grey inline colourisation. - else if (f_character == "[" and !ic_next_is_not_special) { - ic_colour_stack.push(INLINE_GREY); - trick_check_pos++; - } - else if (f_character == "]" and !ic_next_is_not_special and - !ic_colour_stack.empty()) { - if (ic_colour_stack.top() == INLINE_GREY) { - ic_colour_stack.pop(); - trick_check_pos++; + // Nothing related to colors here + else if (f_character == "{" || + f_character == + "}") //|| f_character == "@" || f_character == "$") + { + skip = true; } + // Parse markdown colors else { - ic_next_is_not_special = true; - } - } - - // Green inline colourisation. - else if (f_character == "`" and !ic_next_is_not_special) { - if (!ic_colour_stack.empty()) { - if (ic_colour_stack.top() == INLINE_GREEN) { - ic_colour_stack.pop(); - p_text.remove(trick_check_pos, 1); + for (int c = 0; c < max_colors; ++c) { + // Clear the stored optimization information + QString markdown_start = color_markdown_start_list.at(c); + QString markdown_end = color_markdown_end_list.at(c); + if (html) { + markdown_start = markdown_start.toHtmlEscaped(); + markdown_end = markdown_end.toHtmlEscaped(); + } + bool markdown_remove = color_markdown_remove_list.at(c); + if (markdown_start.isEmpty()) // Not defined + continue; + + if (markdown_end.isEmpty() || + markdown_end == markdown_start) //"toggle switch" type + { + if (f_character == markdown_start) { + if (html) { + if (!ic_color_stack.empty() && ic_color_stack.top() == c && + default_color != c) { + ic_color_stack.pop(); // Cease our coloring + is_end = true; + } + else { + ic_color_stack.push(c); // Begin our coloring + } + color_update = true; + } + skip = markdown_remove; + break; // Prevent it from looping forward for whatever reason + } + } + else if (f_character == markdown_start || + (f_character == markdown_end && !ic_color_stack.empty() && + ic_color_stack.top() == c)) { + if (html) { + if (f_character == markdown_end) { + ic_color_stack.pop(); // Cease our coloring + is_end = true; + } + else if (f_character == markdown_start) { + ic_color_stack.push(c); // Begin our coloring + } + color_update = true; + } + skip = markdown_remove; + break; // Prevent it from looping forward for whatever reason + } } - else { - ic_colour_stack.push(INLINE_GREEN); - p_text.remove(trick_check_pos, 1); + // Parse the newest color stack + if (color_update && (target_pos <= -1 || check_pos < target_pos)) { + if (!ic_next_is_not_special) { + QString appendage = "</font>"; + + if (!ic_color_stack.empty()) + appendage += + "<font color=\"" + + color_rgb_list.at(ic_color_stack.top()).name(QColor::HexRgb) + + "\">"; + + if (is_end && !skip) { + p_text_escaped.insert(check_pos_escaped, + f_character); // Add that char right now + check_pos_escaped += + f_char_length; // So the closing char is captured too + skip = true; + } + p_text_escaped.insert(check_pos_escaped, appendage); + check_pos_escaped += appendage.size(); + } } } - else { - ic_colour_stack.push(INLINE_GREEN); - p_text.remove(trick_check_pos, 1); - } } else { - trick_check_pos++; + if (f_character == "n") // \n, that's a line break son + { + QString appendage = "<br/>"; + if (!html) { + // actual newline commented out + // appendage = "\n"; + // size = 1; //yeah guess what \n is a "single character" + // apparently + appendage = "\\n "; // visual representation of a newline + } + p_text_escaped.insert(check_pos_escaped, appendage); + check_pos_escaped += appendage.size(); + skip = true; + } + if (f_character == "s" || f_character == "f") // screenshake/flash + skip = true; + ic_next_is_not_special = false; } + + // Make all chars we're not supposed to see invisible + if (target_pos > -1 && check_pos == target_pos) { + QString appendage = ""; + if (!ic_color_stack.empty()) { + if (!is_end) // Was our last coloring char ending the color stack or nah + { + // God forgive me for my transgressions but I have refactored this + // whole thing about 25 times and having to refactor it again to more + // elegantly support this will finally make me go insane. + color_is_talking = + color_markdown_talking_list.at(ic_color_stack.top()); + } + + // Clean it up, we're done here + while (!ic_color_stack.empty()) + ic_color_stack.pop(); + + appendage += "</font>"; + } + ic_color_stack.push( + -1); // Dummy colorstack push for maximum </font> appendage + appendage += "<font color=\"#00000000\">"; + p_text_escaped.insert(check_pos_escaped, appendage); + check_pos_escaped += appendage.size(); + } + if (!skip) { + p_text_escaped.insert(check_pos_escaped, f_character); + check_pos_escaped += f_char_length; + } + check_pos += 1; } - return p_text; + if (!ic_color_stack.empty() && html) { + p_text_escaped.append("</font>"); + } + + if (html) { + // Example: https://regex101.com/r/oL4nM9/37 - this replaces + // excessive/trailing/etc. whitespace with non-breaking space. I WOULD use + // white-space: pre; stylesheet tag, but for whataver reason it doesn't work + // no matter where I try it. If somoene else can get that piece of HTML + // memery to work, please do. + p_text_escaped.replace(QRegularExpression("^\\s|(?<=\\s)\\s"), " "); + if (!align.isEmpty()) + p_text_escaped.append("</div>"); + } + + return p_text_escaped; } -void Courtroom::append_ic_text(QString p_text, QString p_name, - bool is_songchange) +void Courtroom::append_ic_text(QString p_text, QString p_name, QString p_action) { QTextCharFormat bold; QTextCharFormat normal; @@ -1779,8 +2469,9 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, const QTextCursor old_cursor = ui_ic_chatlog->textCursor(); const int old_scrollbar_value = ui_ic_chatlog->verticalScrollBar()->value(); - if (!is_songchange) - p_text = filter_ic_text(p_text); + if (p_action == "") + p_text = filter_ic_text(p_text, ao_app->is_colorlog_enabled(), -1, + m_chatmessage[TEXT_COLOR].toInt()); if (log_goes_downwards) { const bool is_scrolled_down = @@ -1796,12 +2487,13 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, ui_ic_chatlog->textCursor().insertText('\n' + p_name, bold); } - if (is_songchange) { - ui_ic_chatlog->textCursor().insertText(" has played a song: ", normal); + if (p_action != "") { + ui_ic_chatlog->textCursor().insertText(" " + p_action + ": ", normal); ui_ic_chatlog->textCursor().insertText(p_text + ".", italics); } else { - ui_ic_chatlog->textCursor().insertText(p_text, normal); + ui_ic_chatlog->textCursor().insertText(": ", normal); + ui_ic_chatlog->textCursor().insertHtml(p_text); } // If we got too many blocks in the current log, delete some from the top. @@ -1811,8 +2503,6 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, ui_ic_chatlog->textCursor().select(QTextCursor::BlockUnderCursor); ui_ic_chatlog->textCursor().removeSelectedText(); ui_ic_chatlog->textCursor().deleteChar(); - // qDebug() << ui_ic_chatlog->document()->blockCount() << " < " << - // log_maximum_blocks; } if (old_cursor.hasSelection() || !is_scrolled_down) { @@ -1837,11 +2527,12 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, ui_ic_chatlog->textCursor().insertText(p_name, bold); - if (is_songchange) { - ui_ic_chatlog->textCursor().insertText(" has played a song: ", normal); + if (p_action != "") { + ui_ic_chatlog->textCursor().insertText(" " + p_action + ": ", normal); ui_ic_chatlog->textCursor().insertText(p_text + "." + '\n', italics); } else { + ui_ic_chatlog->textCursor().insertText(": ", normal); ui_ic_chatlog->textCursor().insertText(p_text + '\n', normal); } @@ -1853,8 +2544,6 @@ void Courtroom::append_ic_text(QString p_text, QString p_name, ui_ic_chatlog->textCursor().select(QTextCursor::BlockUnderCursor); ui_ic_chatlog->textCursor().removeSelectedText(); ui_ic_chatlog->textCursor().deletePreviousChar(); - // qDebug() << ui_ic_chatlog->document()->blockCount() << " < " << - // log_maximum_blocks; } if (old_cursor.hasSelection() || !is_scrolled_up) { @@ -1882,7 +2571,7 @@ void Courtroom::play_preanim(bool noninterrupting) // the actual time int ao2_duration = ao_app->get_ao2_preanim_duration(f_char, f_preanim); int text_delay = ao_app->get_text_delay(f_char, f_preanim) * time_mod; - int sfx_delay = m_chatmessage[SFX_DELAY].toInt() * 60; + int sfx_delay = m_chatmessage[SFX_DELAY].toInt() * time_mod; int preanim_duration; @@ -1894,7 +2583,7 @@ void Courtroom::play_preanim(bool noninterrupting) sfx_delay_timer->start(sfx_delay); QString anim_to_find = ao_app->get_image_suffix(ao_app->get_character_path(f_char, f_preanim)); - if (!file_exists(anim_to_find) || preanim_duration < 0) { + if (!file_exists(anim_to_find)) { if (noninterrupting) anim_state = 4; else @@ -1924,8 +2613,6 @@ void Courtroom::preanim_done() handle_chatmessage_3(); } -void Courtroom::realization_done() { ui_vp_realization->hide(); } - void Courtroom::start_chat_ticking() { // we need to ensure that the text isn't already ticking because this function @@ -1933,44 +2620,56 @@ void Courtroom::start_chat_ticking() if (text_state != 0) return; - if (m_chatmessage[REALIZATION] == "1") { - realization_timer->start(60); - ui_vp_realization->show(); - sfx_player->play(ao_app->get_custom_realization(m_chatmessage[CHAR_NAME])); - } + if (m_chatmessage[EFFECTS] != "") { + QStringList fx_list = m_chatmessage[EFFECTS].split("|"); + QString fx = fx_list[0]; + QString fx_sound; + QString fx_folder; - ui_vp_message->clear(); - set_text_color(); - rainbow_counter = 0; + if (fx_list.length() > 1) + fx_sound = fx_list[1]; + if (fx_list.length() > 2) { + fx_folder = fx_list[1]; + fx_sound = fx_list[2]; + } + + this->do_effect(fx, fx_sound, m_chatmessage[CHAR_NAME], fx_folder); + } + else if (m_chatmessage[REALIZATION] == "1") { + this->do_flash(); + sfx_player->play(ao_app->get_custom_realization(m_chatmessage[CHAR_NAME])); + } if (chatmessage_is_empty) { // since the message is empty, it's technically done ticking text_state = 2; return; } - // At this point, we'd do well to clear the inline colour stack. - // This stops it from flowing into next messages. - while (!inline_colour_stack.empty()) { - inline_colour_stack.pop(); - } - ui_vp_chatbox->show(); + ui_vp_message->show(); - tick_pos = 0; - blip_pos = 0; + if (!is_additive) { + ui_vp_message->clear(); + real_tick_pos = 0; + additive_previous = ""; + } - // Just in case we somehow got inline blue text left over from a previous - // message, let's set it to false. - inline_blue_depth = 0; + tick_pos = 0; + blip_ticker = 0; // At the start of every new message, we set the text speed to the default. current_display_speed = 3; - chat_tick_timer->start(message_display_speed[current_display_speed]); + chat_tick_timer->start(0); // Display the first char right away QString f_gender = ao_app->get_gender(m_chatmessage[CHAR_NAME]); - blip_player->set_blips(ao_app->get_sfx_suffix("sfx-blip" + f_gender)); + blip_player->set_blips(f_gender); + + int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); // text meme bonanza + if ((emote_mod == 0 || emote_mod == 5) && m_chatmessage[SCREENSHAKE] == "1") { + this->do_screenshake(); + } // means text is currently ticking text_state = 1; @@ -1978,362 +2677,261 @@ void Courtroom::start_chat_ticking() void Courtroom::chat_tick() { - // note: this is called fairly often(every 60 ms when char is talking) + // note: this is called fairly often // do not perform heavy operations here QString f_message = m_chatmessage[MESSAGE]; - f_message.remove(0, tick_pos); // Due to our new text speed system, we always need to stop the timer now. chat_tick_timer->stop(); - // Stops blips from playing when we have a formatting option. - bool formatting_char = false; - - // If previously, we have detected that the message is centered, now - // is the time to remove those two tildes at the start. - if (message_is_centered) { - f_message.remove(0, 2); - } - - if (f_message.size() == 0) { + if (tick_pos >= f_message.size()) { text_state = 2; - if (anim_state != 4) { + if (anim_state < 3) { anim_state = 3; ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME], m_chatmessage[EMOTE]); } + QString f_char = m_chatmessage[CHAR_NAME]; + QString f_custom_theme = ao_app->get_chat(f_char); + 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()); + real_tick_pos = ui_vp_message->toPlainText().size(); + return; } - else { - QTextBoundaryFinder tbf(QTextBoundaryFinder::Grapheme, f_message); - QString f_character; - int f_char_length; + // Stops blips from playing when we have a formatting option. + bool formatting_char = false; - tbf.toNextBoundary(); + QString f_rest = f_message; - if (tbf.position() == -1) - f_character = f_message; - else - f_character = f_message.left(tbf.position()); + // Alignment characters + if (tick_pos < 2) { + if (f_rest.startsWith("~~")) { + tick_pos = f_rest.indexOf("~~"); + f_rest.remove(tick_pos, 2); + tick_pos += 2; + } + else if (f_rest.startsWith("~>")) { + tick_pos = f_rest.indexOf("~>"); + f_rest.remove(tick_pos, 2); + tick_pos += 2; + } + else if (f_rest.startsWith("<>")) { + tick_pos = f_rest.indexOf("<>"); + f_rest.remove(tick_pos, 2); + tick_pos += 2; + } + } + f_rest.remove(0, tick_pos); + QTextBoundaryFinder tbf(QTextBoundaryFinder::Grapheme, f_rest); + QString f_character; + int f_char_length; - f_char_length = f_character.length(); - f_character = f_character.toHtmlEscaped(); + tbf.toNextBoundary(); + + if (tbf.position() == -1) + f_character = f_rest; + else + f_character = f_rest.left(tbf.position()); - if (f_character == " ") - ui_vp_message->insertPlainText(" "); + f_char_length = f_character.length(); + tick_pos += f_char_length; - // Escape character. - else if (f_character == "\\" and !next_character_is_not_special) { + // Escape character. + if (!next_character_is_not_special) { + if (f_character == "\\") { next_character_is_not_special = true; formatting_char = true; } // Text speed modifier. - else if (f_character == "{" and !next_character_is_not_special) { + else if (f_character == "{") { // ++, because it INCREASES delay! current_display_speed++; formatting_char = true; } - else if (f_character == "}" and !next_character_is_not_special) { + else if (f_character == "}") { current_display_speed--; formatting_char = true; } - // Orange inline colourisation. - else if (f_character == "|" and !next_character_is_not_special) { - if (!inline_colour_stack.empty()) { - if (inline_colour_stack.top() == INLINE_ORANGE) { - inline_colour_stack.pop(); - } - else { - inline_colour_stack.push(INLINE_ORANGE); - } - } - else { - inline_colour_stack.push(INLINE_ORANGE); - } - formatting_char = true; - } - - // Blue inline colourisation. - else if (f_character == "(" and !next_character_is_not_special) { - inline_colour_stack.push(INLINE_BLUE); - ui_vp_message->insertHtml("<font color=\"" + - get_text_color(QString::number(BLUE)).name() + - "\">" + f_character + "</font>"); - - // Increase how deep we are in inline blues. - inline_blue_depth++; - - // Here, we check if the entire message is blue. - // If it isn't, we stop talking. - if (!entire_message_is_blue and anim_state != 4) { - QString f_char = m_chatmessage[CHAR_NAME]; - QString f_emote = m_chatmessage[EMOTE]; - ui_vp_player_char->play_idle(f_char, f_emote); - } - } - else if (f_character == ")" and !next_character_is_not_special and - !inline_colour_stack.empty()) { - if (inline_colour_stack.top() == INLINE_BLUE) { - inline_colour_stack.pop(); - ui_vp_message->insertHtml("<font color=\"" + - get_text_color(QString::number(BLUE)).name() + - "\">" + f_character + "</font>"); - - // Decrease how deep we are in inline blues. - // Just in case, we do a check if we're above zero, but we should be. - if (inline_blue_depth > 0) { - inline_blue_depth--; - // Here, we check if the entire message is blue. - // If it isn't, we start talking if we have completely climbed out of - // inline blues. - if (!entire_message_is_blue) { - // We should only go back to talking if we're out of inline blues, - // not during a non. int. pre, and not on the last character. - if (inline_blue_depth == 0 and anim_state != 4 and - !(tick_pos + 1 >= f_message.size())) { - QString f_char = m_chatmessage[CHAR_NAME]; - QString f_emote = m_chatmessage[EMOTE]; - ui_vp_player_char->play_talking(f_char, f_emote); - } - } + else { + // Parse markdown colors + for (int c = 0; c < max_colors; ++c) { + QString markdown_start = color_markdown_start_list.at(c); + QString markdown_end = color_markdown_end_list.at(c); + bool markdown_remove = color_markdown_remove_list.at(c); + if (markdown_start.isEmpty()) + continue; + + if (f_character == markdown_start || f_character == markdown_end) { + if (markdown_remove) + formatting_char = true; + break; } } - else { - next_character_is_not_special = true; - tick_pos -= f_char_length; - } } - - // Grey inline colourisation. - else if (f_character == "[" and !next_character_is_not_special) { - inline_colour_stack.push(INLINE_GREY); - ui_vp_message->insertHtml("<font color=\"" + - get_text_color("_inline_grey").name() + "\">" + - f_character + "</font>"); - } - else if (f_character == "]" and !next_character_is_not_special and - !inline_colour_stack.empty()) { - if (inline_colour_stack.top() == INLINE_GREY) { - inline_colour_stack.pop(); - ui_vp_message->insertHtml("<font color=\"" + - get_text_color("_inline_grey").name() + - "\">" + f_character + "</font>"); - } - else { - next_character_is_not_special = true; - tick_pos -= f_char_length; - } + } + else { + if (f_character == "n") + formatting_char = true; // it's a newline + if (f_character == "s") // Screenshake. + { + this->do_screenshake(); + formatting_char = true; } - - // Green inline colourisation. - else if (f_character == "`" and !next_character_is_not_special) { - if (!inline_colour_stack.empty()) { - if (inline_colour_stack.top() == INLINE_GREEN) { - inline_colour_stack.pop(); - formatting_char = true; - } - else { - inline_colour_stack.push(INLINE_GREEN); - formatting_char = true; - } - } - else { - inline_colour_stack.push(INLINE_GREEN); - formatting_char = true; - } + if (f_character == "f") // Flash. + { + this->do_flash(); + formatting_char = true; } - else { - next_character_is_not_special = false; - if (!inline_colour_stack.empty()) { - switch (inline_colour_stack.top()) { - case INLINE_ORANGE: - ui_vp_message->insertHtml( - "<font color=\"" + - get_text_color(QString::number(ORANGE)).name() + "\">" + - f_character + "</font>"); - break; - case INLINE_BLUE: - ui_vp_message->insertHtml( - "<font color=\"" + get_text_color(QString::number(BLUE)).name() + - "\">" + f_character + "</font>"); - break; - case INLINE_GREEN: - ui_vp_message->insertHtml( - "<font color=\"" + get_text_color(QString::number(GREEN)).name() + - "\">" + f_character + "</font>"); - break; - case INLINE_GREY: - ui_vp_message->insertHtml("<font color=\"" + - get_text_color("_inline_grey").name() + - "\">" + f_character + "</font>"); - break; - } - } - else { - if (m_chatmessage[TEXT_COLOR].toInt() == RAINBOW) { - QString html_color; - - switch (rainbow_counter) { - case 0: - html_color = get_text_color(QString::number(RED)).name(); - break; - case 1: - html_color = get_text_color(QString::number(ORANGE)).name(); - break; - case 2: - html_color = get_text_color(QString::number(YELLOW)).name(); - break; - case 3: - html_color = get_text_color(QString::number(GREEN)).name(); - break; - default: - html_color = get_text_color(QString::number(BLUE)).name(); - rainbow_counter = -1; - } - - ++rainbow_counter; + next_character_is_not_special = false; + } - ui_vp_message->insertHtml("<font color=\"" + html_color + "\">" + - f_character + "</font>"); - } - else - ui_vp_message->insertHtml(f_character); - } + if ((message_display_speed[current_display_speed] <= 0 && + tick_pos < f_message.size() - 1) || + formatting_char) { + chat_tick_timer->start(0); // Don't bother rendering anything out as we're + // doing the SPEED. (there's latency otherwise) + if (!formatting_char || f_character == "n" || f_character == "f" || + f_character == "s") + real_tick_pos += f_char_length; // Adjust the tick position for the + // scrollbar convenience + } + 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())); - if (message_is_centered) { - ui_vp_message->setAlignment(Qt::AlignCenter); - } - else { - ui_vp_message->setAlignment(Qt::AlignLeft); - } - } + // This should always be done AFTER setHtml. Scroll the chat window with the + // text. - QScrollBar *scroll = ui_vp_message->verticalScrollBar(); - scroll->setValue(scroll->maximum()); + // Make the cursor follow the message + QTextCursor cursor = ui_vp_message->textCursor(); + cursor.setPosition(real_tick_pos); + ui_vp_message->setTextCursor(cursor); + real_tick_pos += f_char_length; - if (blank_blip) - qDebug() << "blank_blip found true"; + ui_vp_message->ensureCursorVisible(); - if (f_character != ' ' || blank_blip) { + // Keep the speed at bay. + if (current_display_speed < 0) + current_display_speed = 0; + else if (current_display_speed > 6) + current_display_speed = 6; - if (blip_pos % blip_rate == 0 && !formatting_char) { - blip_pos = 0; + // Blip player and real tick pos ticker + if (!formatting_char && (f_character != ' ' || blank_blip)) { + if (blip_ticker % blip_rate == 0) { blip_player->blip_tick(); } - - ++blip_pos; - } - - tick_pos += f_char_length; - - // Restart the timer, but according to the newly set speeds, if there were - // any. Keep the speed at bay. - if (current_display_speed < 0) { - current_display_speed = 0; + ++blip_ticker; } - if (current_display_speed > 6) { - current_display_speed = 6; + // Punctuation delayer + if (punctuation_chars.contains(f_character)) { + msg_delay *= punctuation_modifier; } - // If we had a formatting char, we shouldn't wait so long again, as it won't - // appear! - if (formatting_char) { - chat_tick_timer->start(1); + // If this color is talking + if (color_is_talking && anim_state != 2 && + anim_state < + 4) // Set it to talking as we're not on that already (though we have + // to avoid interrupting a non-interrupted preanim) + { + ui_vp_player_char->stop(); + ui_vp_player_char->play_talking(m_chatmessage[CHAR_NAME], + m_chatmessage[EMOTE]); + anim_state = 2; } - else { - chat_tick_timer->start(message_display_speed[current_display_speed]); + else if (!color_is_talking && anim_state < 3 && + anim_state != 3) // Set it to idle as we're not on that already + { + ui_vp_player_char->stop(); + ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME], + m_chatmessage[EMOTE]); + anim_state = 3; } + // Continue ticking + chat_tick_timer->start(msg_delay); } } -void Courtroom::show_testimony() -{ - if (!testimony_in_progress || m_chatmessage[SIDE] != "wit") - return; - - ui_vp_testimony->show(); - - testimony_show_timer->start(testimony_show_time); -} - -void Courtroom::hide_testimony() -{ - ui_vp_testimony->hide(); - - if (!testimony_in_progress) - return; - - testimony_hide_timer->start(testimony_hide_time); -} - void Courtroom::play_sfx() { QString sfx_name = m_chatmessage[SFX_NAME]; - + if (m_chatmessage[SCREENSHAKE] == + "1") // Screenshake dependant on preanim sfx delay meme + { + this->do_screenshake(); + } if (sfx_name == "1") return; - sfx_player->play(ao_app->get_sfx_suffix(sfx_name)); + sfx_player->play(sfx_name); + if (ao_app->get_looping_sfx()) + sfx_player->set_looping( + ao_app->get_sfx_looping(current_char, current_emote) == "1"); } -void Courtroom::set_scene() +void Courtroom::set_scene(QString f_desk_mod, QString f_side) { - if (testimony_in_progress) - show_testimony(); - // witness is default if pos is invalid QString f_background = "witnessempty"; QString f_desk_image = "stand"; - QString f_desk_mod = m_chatmessage[DESK_MOD]; - QString f_side = m_chatmessage[SIDE]; - if (f_side == "def") { + if (f_side == "def" && file_exists(ao_app->get_image_suffix( + ao_app->get_background_path("defenseempty")))) { f_background = "defenseempty"; - if (is_ao2_bg) - f_desk_image = "defensedesk"; - else - f_desk_image = "bancodefensa"; + f_desk_image = "defensedesk"; } - else if (f_side == "pro") { + else if (f_side == "pro" && + file_exists(ao_app->get_image_suffix( + ao_app->get_background_path("prosecutorempty")))) { f_background = "prosecutorempty"; - if (is_ao2_bg) - f_desk_image = "prosecutiondesk"; - else - f_desk_image = "bancoacusacion"; + f_desk_image = "prosecutiondesk"; } - else if (f_side == "jud") { + else if (f_side == "jud" && file_exists(ao_app->get_image_suffix( + ao_app->get_background_path("judgestand")))) { f_background = "judgestand"; f_desk_image = "judgedesk"; } - else if (f_side == "hld") { + else if (f_side == "hld" && + file_exists(ao_app->get_image_suffix( + ao_app->get_background_path("helperstand")))) { f_background = "helperstand"; f_desk_image = "helperdesk"; } - else if (f_side == "hlp") { + else if (f_side == "hlp" && + file_exists(ao_app->get_image_suffix( + ao_app->get_background_path("prohelperstand")))) { f_background = "prohelperstand"; f_desk_image = "prohelperdesk"; } - else if (f_side == "jur" && - (file_exists(ao_app->get_background_path("jurystand.png")) || - file_exists(ao_app->get_background_path("jurystand.gif")))) { + else if (f_side == "jur" && file_exists(ao_app->get_image_suffix( + ao_app->get_background_path("jurystand")))) { f_background = "jurystand"; f_desk_image = "jurydesk"; } else if (f_side == "sea" && - (file_exists(ao_app->get_background_path("seancestand.png")) || - file_exists(ao_app->get_background_path("seancestand.gif")))) { + file_exists(ao_app->get_image_suffix( + ao_app->get_background_path("seancestand")))) { f_background = "seancestand"; f_desk_image = "seancedesk"; } - else { - if (is_ao2_bg) - f_desk_image = "stand"; - else - f_desk_image = "estrado"; + + if (file_exists(ao_app->get_image_suffix( + ao_app->get_background_path(f_side)))) // Unique pos path + { + f_background = f_side; + f_desk_image = f_side + "_overlay"; } ui_vp_background->set_image(f_background); @@ -2346,47 +2944,10 @@ void Courtroom::set_scene() ui_vp_desk->hide(); ui_vp_legacy_desk->hide(); } - else if (is_ao2_bg || - (f_side == "jud" || f_side == "hld" || f_side == "hlp")) { + else { ui_vp_legacy_desk->hide(); ui_vp_desk->show(); } - else { - if (f_side == "wit") { - ui_vp_desk->show(); - ui_vp_legacy_desk->hide(); - } - else { - ui_vp_desk->hide(); - ui_vp_legacy_desk->show(); - } - } -} - -void Courtroom::set_text_color() -{ - QColor textcolor = ao_app->get_chat_color( - m_chatmessage[TEXT_COLOR], ao_app->get_chat(m_chatmessage[CHAR_NAME])); - - ui_vp_message->setTextBackgroundColor(QColor(0, 0, 0, 0)); - ui_vp_message->setTextColor(textcolor); - - QString style = "background-color: rgba(0, 0, 0, 0);"; - style.append("color: rgb("); - style.append(QString::number(textcolor.red())); - style.append(", "); - style.append(QString::number(textcolor.green())); - style.append(", "); - style.append(QString::number(textcolor.blue())); - style.append(")"); - - ui_vp_message->setStyleSheet(style); -} - -QColor Courtroom::get_text_color(QString color) -{ - return ao_app->get_chat_color(color, - ao_app->get_chat(m_chatmessage[CHAR_NAME])); } void Courtroom::set_ip_list(QString p_list) @@ -2409,7 +2970,7 @@ void Courtroom::set_mute(bool p_muted, int p_cid) } ui_muted->resize(ui_ic_chat_message->width(), ui_ic_chat_message->height()); - ui_muted->set_image("muted.png"); + ui_muted->set_image("muted"); is_muted = p_muted; ui_ic_chat_message->setEnabled(!p_muted); @@ -2434,32 +2995,74 @@ void Courtroom::handle_song(QStringList *p_contents) return; QString f_song = f_contents.at(0); - QString f_song_clear = f_song; - f_song_clear = f_song_clear.left(f_song_clear.lastIndexOf(".")); + QString f_song_clear = f_song.left(f_song.lastIndexOf(".")); + f_song_clear = f_song_clear.right(f_song_clear.length() - + (f_song_clear.lastIndexOf("/") + 1)); int n_char = f_contents.at(1).toInt(); + bool looping = true; + int channel = 0; + int effect_flags = 0; if (n_char < 0 || n_char >= char_list.size()) { - music_player->play(f_song); + int channel = 0; + if (p_contents->length() > 3 && p_contents->at(3) != "-1") + looping = false; + + if (p_contents->length() > + 4) // eyyy we want to change this song's CHANNEL huh + channel = p_contents->at(4).toInt(); // let the music player handle it if + // it's bigger than the channel list + + if (p_contents->length() > 5) // Flags provided to us by server such as Fade + // In, Fade Out, Sync Pos etc. + { + 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); } else { QString str_char = char_list.at(n_char).name; QString str_show = char_list.at(n_char).name; if (p_contents->length() > 2) { - str_show = p_contents->at(2); + if (p_contents->at(2) != "") { + str_show = p_contents->at(2); + } + } + if (p_contents->length() > 3 && p_contents->at(3) != "-1") { + // I am really confused why "-1" is "loop this song" and why anything else + // passes as "don't loop" (if we even have this length) but alright + looping = false; + } + if (p_contents->length() > + 4) // eyyy we want to change this song's CHANNEL huh + channel = p_contents->at(4).toInt(); // let the music player handle it if + // it's bigger than the channel list + + if (p_contents->length() > 5) // Flags provided to us by server such as Fade + // In, Fade Out, Sync Pos etc. + { + effect_flags = p_contents->at(5).toInt(); } if (!mute_map.value(n_char)) { chatlogpiece *temp = new chatlogpiece(str_char, str_show, f_song, true); ic_chatlog_history.append(*temp); + ao_app->append_to_file(temp->get_full(), ao_app->log_filename, true); while (ic_chatlog_history.size() > log_maximum_blocks && log_maximum_blocks > 0) { ic_chatlog_history.removeFirst(); } - append_ic_text(f_song_clear, str_show, true); - music_player->play(f_song); + append_ic_text(f_song_clear, str_show, "has played a song"); + + music_player->play(f_song, channel, looping, effect_flags); + if (channel == 0) + ui_music_name->setText(f_song_clear); } } } @@ -2471,26 +3074,25 @@ void Courtroom::handle_wtce(QString p_wtce, int variant) // witness testimony if (p_wtce == "testimony1") { sfx_player->play(ao_app->get_sfx("witness_testimony")); - ui_vp_wtce->play("witnesstestimony"); - testimony_in_progress = true; - show_testimony(); + ui_vp_wtce->play("witnesstestimony", "", "", 1500); + ui_vp_testimony->play("testimony"); } // cross examination else if (p_wtce == "testimony2") { sfx_player->play(ao_app->get_sfx("cross_examination")); - ui_vp_wtce->play("crossexamination"); - testimony_in_progress = false; + ui_vp_wtce->play("crossexamination", "", "", 1500); + ui_vp_testimony->stop(); } else if (p_wtce == "judgeruling") { if (variant == 0) { sfx_player->play(ao_app->get_sfx("not_guilty")); - ui_vp_wtce->play("notguilty"); - testimony_in_progress = false; + ui_vp_wtce->play("notguilty", "", "", 3000); + ui_vp_testimony->stop(); } else if (variant == 1) { sfx_player->play(ao_app->get_sfx("guilty")); - ui_vp_wtce->play("guilty"); - testimony_in_progress = false; + ui_vp_wtce->play("guilty", "", "", 3000); + ui_vp_testimony->stop(); } } } @@ -2501,12 +3103,11 @@ void Courtroom::set_hp_bar(int p_bar, int p_state) return; if (p_bar == 1) { - ui_defense_bar->set_image("defensebar" + QString::number(p_state) + ".png"); + ui_defense_bar->set_image("defensebar" + QString::number(p_state)); defense_bar_state = p_state; } else if (p_bar == 2) { - ui_prosecution_bar->set_image("prosecutionbar" + QString::number(p_state) + - ".png"); + ui_prosecution_bar->set_image("prosecutionbar" + QString::number(p_state)); prosecution_bar_state = p_state; } } @@ -2538,7 +3139,7 @@ void Courtroom::toggle_judge_buttons(bool is_on) void Courtroom::mod_called(QString p_ip) { ui_server_chatlog->append(p_ip); - if (ui_guard->isChecked()) { + if (!ui_guard->isChecked()) { modcall_player->play(ao_app->get_sfx("mod_call")); ao_app->alert(this); } @@ -2575,24 +3176,11 @@ void Courtroom::on_ooc_return_pressed() toggle_judge_buttons(false); } } - else if (ooc_message.startsWith("/login")) { - ui_guard->show(); - append_server_chatmessage("CLIENT", "You were granted the Guard button.", - "1"); - } - else if (ooc_message.startsWith("/rainbow") && ao_app->yellow_text_enabled && - !rainbow_appended) { - // ui_text_color->addItem("Rainbow"); - ui_ooc_chat_message->clear(); - // rainbow_appended = true; - append_server_chatmessage("CLIENT", "This does nohing, but there you go.", - "1"); - return; - } else if (ooc_message.startsWith("/settings")) { ui_ooc_chat_message->clear(); ao_app->call_settings_menu(); - append_server_chatmessage("CLIENT", "You opened the settings menu.", "1"); + append_server_chatmessage("CLIENT", tr("You opened the settings menu."), + "1"); return; } else if (ooc_message.startsWith("/pair")) { @@ -2611,14 +3199,14 @@ void Courtroom::on_ooc_return_pressed() } else { other_charid = -1; - append_server_chatmessage("CLIENT", - "You are no longer paired with anyone.", "1"); + append_server_chatmessage( + "CLIENT", tr("You are no longer paired with anyone."), "1"); } } else { append_server_chatmessage("CLIENT", - "Are you sure you typed that well? The char ID " - "could not be recognised.", + tr("Are you sure you typed that well? The char " + "ID could not be recognised."), "1"); } return; @@ -2631,36 +3219,36 @@ void Courtroom::on_ooc_return_pressed() int off = ooc_message.toInt(&ok); if (ok) { if (off >= -100 && off <= 100) { - offset_with_pair = off; - QString msg = "You have set your offset to "; + char_offset = off; + QString msg = tr("You have set your offset to "); msg.append(QString::number(off)); msg.append("%."); append_server_chatmessage("CLIENT", msg, "1"); } else { append_server_chatmessage( - "CLIENT", "Your offset must be between -100% and 100%!", "1"); + "CLIENT", tr("Your offset must be between -100% and 100%!"), "1"); } } else { - append_server_chatmessage("CLIENT", "That offset does not look like one.", - "1"); + append_server_chatmessage("CLIENT", + tr("That offset does not look like one."), "1"); } return; } else if (ooc_message.startsWith("/switch_am")) { - append_server_chatmessage("CLIENT", - "You switched your music and area list.", "1"); + append_server_chatmessage( + "CLIENT", tr("You switched your music and area list."), "1"); on_switch_area_music_clicked(); ui_ooc_chat_message->clear(); return; } else if (ooc_message.startsWith("/enable_blocks")) { - append_server_chatmessage( - "CLIENT", - "You have forcefully enabled features that the server may not support. " - "You may not be able to talk IC, or worse, because of this.", - "1"); + append_server_chatmessage("CLIENT", + tr("You have forcefully enabled features that " + "the server may not support. You may not be " + "able to talk IC, or worse, because of this."), + "1"); ao_app->cccc_ic_support_enabled = true; ao_app->arup_enabled = true; ao_app->modcall_reason_enabled = true; @@ -2670,11 +3258,11 @@ void Courtroom::on_ooc_return_pressed() } else if (ooc_message.startsWith("/non_int_pre")) { if (ui_pre_non_interrupt->isChecked()) - append_server_chatmessage("CLIENT", - "Your pre-animations interrupt again.", "1"); + append_server_chatmessage( + "CLIENT", tr("Your pre-animations interrupt again."), "1"); else append_server_chatmessage( - "CLIENT", "Your pre-animations will not interrupt text.", "1"); + "CLIENT", tr("Your pre-animations will not interrupt text."), "1"); ui_pre_non_interrupt->setChecked(!ui_pre_non_interrupt->isChecked()); ui_ooc_chat_message->clear(); return; @@ -2685,7 +3273,7 @@ void Courtroom::on_ooc_return_pressed() if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { append_server_chatmessage( - "CLIENT", "Couldn't open chatlog.txt to write into.", "1"); + "CLIENT", tr("Couldn't open chatlog.txt to write into."), "1"); ui_ooc_chat_message->clear(); return; } @@ -2698,7 +3286,8 @@ void Courtroom::on_ooc_return_pressed() file.close(); - append_server_chatmessage("CLIENT", "The IC chatlog has been saved.", "1"); + append_server_chatmessage("CLIENT", tr("The IC chatlog has been saved."), + "1"); ui_ooc_chat_message->clear(); return; } @@ -2710,9 +3299,9 @@ void Courtroom::on_ooc_return_pressed() QDir::current().mkdir("base/" + casefolder.dirName()); append_server_chatmessage( "CLIENT", - "You don't have a `base/cases/` folder! It was just made for you, " - "but seeing as it WAS just made for you, it's likely the case file " - "you're looking for can't be found in there.", + tr("You don't have a `base/cases/` folder! It was just made for you, " + "but seeing as it WAS just made for you, it's likely the case " + "file you're looking for can't be found in there."), "1"); ui_ooc_chat_message->clear(); return; @@ -2725,20 +3314,21 @@ void Courtroom::on_ooc_return_pressed() if (command.size() < 2) { append_server_chatmessage( "CLIENT", - "You need to give a filename to load (extension not needed)! Make " - "sure that it is in the `base/cases/` folder, and that it is a " - "correctly formatted ini.\nCases you can load: " + - caseslist.join(", "), + tr("You need to give a filename to load (extension not needed)! Make " + "sure that it is in the `base/cases/` folder, and that it is a " + "correctly formatted ini.\nCases you can load: %1") + .arg(caseslist.join(", ")), "1"); ui_ooc_chat_message->clear(); return; } if (command.size() > 2) { - append_server_chatmessage("CLIENT", - "Too many arguments to load a case! You only " - "need one filename, without extension.", - "1"); + append_server_chatmessage( + "CLIENT", + tr("Too many arguments to load a case! You only need one filename, " + "without extension."), + "1"); ui_ooc_chat_message->clear(); return; } @@ -2752,7 +3342,7 @@ void Courtroom::on_ooc_return_pressed() QString casestatus = casefile.value("status", "").value<QString>(); if (!caseauth.isEmpty()) - append_server_chatmessage("CLIENT", "Case made by " + caseauth + ".", + append_server_chatmessage("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() + @@ -2762,7 +3352,7 @@ void Courtroom::on_ooc_return_pressed() "#/status " + casestatus + "#%")); if (!cmdoc.isEmpty()) append_server_chatmessage( - "CLIENT", "Navigate to " + cmdoc + " for the CM doc.", "1"); + "CLIENT", tr("Navigate to %1 for the CM doc.").arg(cmdoc), "1"); for (int i = local_evidence_list.size() - 1; i >= 0; i--) { ao_app->send_server_packet( @@ -2786,7 +3376,7 @@ void Courtroom::on_ooc_return_pressed() } append_server_chatmessage( - "CLIENT", "Your case \"" + command[1] + "\" was loaded!", "1"); + "CLIENT", tr("Your case \"%1\" was loaded!").arg(command[1]), "1"); ui_ooc_chat_message->clear(); return; } @@ -2798,9 +3388,9 @@ void Courtroom::on_ooc_return_pressed() QDir::current().mkdir("base/" + casefolder.dirName()); append_server_chatmessage( "CLIENT", - "You don't have a `base/cases/` folder! It was just made for you, " - "but seeing as it WAS just made for you, it's likely that you " - "somehow deleted it.", + tr("You don't have a `base/cases/` folder! It was just made for you, " + "but seeing as it WAS just made for you, it's likely that you " + "somehow deleted it."), "1"); ui_ooc_chat_message->clear(); return; @@ -2813,8 +3403,8 @@ void Courtroom::on_ooc_return_pressed() if (command.size() < 3) { append_server_chatmessage( "CLIENT", - "You need to give a filename to save (extension not needed) and the " - "courtroom status!", + tr("You need to give a filename to save (extension not needed) and " + "the courtroom status!"), "1"); ui_ooc_chat_message->clear(); return; @@ -2823,8 +3413,8 @@ void Courtroom::on_ooc_return_pressed() if (command.size() > 3) { append_server_chatmessage( "CLIENT", - "Too many arguments to save a case! You only need a filename without " - "extension and the courtroom status!", + tr("Too many arguments to save a case! You only need a filename " + "without extension and the courtroom status!"), "1"); ui_ooc_chat_message->clear(); return; @@ -2836,7 +3426,7 @@ void Courtroom::on_ooc_return_pressed() casefile.setValue("doc", ""); casefile.setValue("status", command[2]); casefile.sync(); - for (int i = local_evidence_list.size() - 1; i >= 0; i--) { + for (int i = 0; i < local_evidence_list.size(); i++) { QString clean_evidence_dsc = local_evidence_list[i].description.replace( QRegularExpression("<owner = ...>..."), ""); clean_evidence_dsc = clean_evidence_dsc.replace( @@ -2850,7 +3440,7 @@ void Courtroom::on_ooc_return_pressed() } casefile.sync(); append_server_chatmessage( - "CLIENT", "Succesfully saved, edit doc and cmdoc link on the ini!", + "CLIENT", tr("Succesfully saved, edit doc and cmdoc link on the ini!"), "1"); ui_ooc_chat_message->clear(); return; @@ -2877,67 +3467,405 @@ void Courtroom::on_ooc_toggle_clicked() if (server_ooc) { ui_ms_chatlog->show(); ui_server_chatlog->hide(); - ui_ooc_toggle->setText("Master"); + ui_ooc_toggle->setText(tr("Master")); server_ooc = false; } else { ui_ms_chatlog->hide(); ui_server_chatlog->show(); - ui_ooc_toggle->setText("Server"); + ui_ooc_toggle->setText(tr("Server")); server_ooc = true; } } +// Todo: multithread this due to some servers having large as hell music list void Courtroom::on_music_search_edited(QString p_text) { - // preventing compiler warnings - p_text += "a"; - list_music(); - list_areas(); + // Iterate through all QTreeWidgetItem items + if (!ui_music_list->isHidden()) { + QTreeWidgetItemIterator it(ui_music_list); + while (*it) { + (*it)->setHidden(p_text != ""); + ++it; + } + } + + if (!ui_area_list->isHidden()) { + QTreeWidgetItemIterator ait(ui_area_list); + while (*ait) { + (*ait)->setHidden(p_text != ""); + ++ait; + } + } + + if (p_text != "") { + if (!ui_music_list->isHidden()) { + // Search in metadata + QList<QTreeWidgetItem *> clist = ui_music_list->findItems( + ui_music_search->text(), Qt::MatchContains | Qt::MatchRecursive, 1); + foreach (QTreeWidgetItem *item, clist) { + if (item->parent() != nullptr) // So the category shows up too + item->parent()->setHidden(false); + item->setHidden(false); + } + } + + if (!ui_area_list->isHidden()) { + // Search in metadata + QList<QTreeWidgetItem *> alist = ui_area_list->findItems( + ui_music_search->text(), Qt::MatchContains | Qt::MatchRecursive, 1); + foreach (QTreeWidgetItem *item, alist) { + if (item->parent() != nullptr) // So the category shows up too + item->parent()->setHidden(false); + item->setHidden(false); + } + } + } } void Courtroom::on_pos_dropdown_changed(int p_index) { - ui_ic_chat_message->setFocus(); - - if (p_index < 0 || p_index > 5) + if (p_index < 0 || p_index > 7) return; toggle_judge_buttons(false); - QString f_pos; + QString f_pos = ui_pos_dropdown->itemText(p_index); - switch (p_index) { - case 0: - f_pos = "wit"; - break; - case 1: - f_pos = "def"; - break; - case 2: - f_pos = "pro"; - break; - case 3: - f_pos = "jud"; + if (f_pos == "") + return; + + if (f_pos == "jud") toggle_judge_buttons(true); - break; - case 4: - f_pos = "hld"; - break; - case 5: - f_pos = "hlp"; - break; - default: - f_pos = ""; + + // YEAH SENDING LIKE 20 PACKETS IF THE USER SCROLLS THROUGH, GREAT IDEA + // how about this instead + set_side(f_pos); +} + +void Courtroom::set_iniswap_dropdown() +{ + ui_iniswap_dropdown->blockSignals(true); + ui_iniswap_dropdown->clear(); + if (m_cid == -1) { + ui_iniswap_dropdown->hide(); + ui_iniswap_remove->hide(); + return; + } + QStringList iniswaps = ao_app->get_list_file( + ao_app->get_character_path(char_list.at(m_cid).name, "iniswaps.ini")); + iniswaps.prepend(char_list.at(m_cid).name); + if (iniswaps.size() <= 0) { + ui_iniswap_dropdown->hide(); + ui_iniswap_remove->hide(); + return; } + ui_iniswap_dropdown->show(); + ui_iniswap_dropdown->addItems(iniswaps); - if (f_pos == "" || ui_ooc_chat_name->text() == "") + for (int i = 0; i < iniswaps.size(); ++i) { + if (iniswaps.at(i) == current_char) { + ui_iniswap_dropdown->setCurrentIndex(i); + if (i != 0) + ui_iniswap_remove->show(); + else + ui_iniswap_remove->hide(); + break; + } + } + ui_iniswap_dropdown->blockSignals(false); +} + +void Courtroom::on_iniswap_dropdown_changed(int p_index) +{ + ui_ic_chat_message->setFocus(); + QString iniswap = ui_iniswap_dropdown->itemText(p_index); + ao_app->set_char_ini(char_list.at(m_cid).name, iniswap, "name", "Options"); + + QStringList swaplist; + 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) + swaplist.append(entry); + } + ao_app->write_to_file( + swaplist.join("\n"), + ao_app->get_character_path(char_list.at(m_cid).name, "iniswaps.ini")); + ui_iniswap_dropdown->blockSignals(true); + ui_iniswap_dropdown->setCurrentIndex(p_index); + ui_iniswap_dropdown->blockSignals(false); + update_character(m_cid); + if (p_index != 0) + ui_iniswap_remove->show(); + else + ui_iniswap_remove->hide(); +} + +void Courtroom::on_iniswap_context_menu_requested(const QPoint &pos) +{ + QMenu *menu = ui_iniswap_dropdown->lineEdit()->createStandardContextMenu(); + + menu->addSeparator(); + if (file_exists(ao_app->get_character_path(current_char, "char.ini"))) + menu->addAction(QString("Edit " + current_char + "/char.ini"), this, + SLOT(on_iniswap_edit_requested())); + if (ui_iniswap_dropdown->itemText(ui_iniswap_dropdown->currentIndex()) != + char_list.at(m_cid).name) + menu->addAction(QString("Remove " + current_char), this, + SLOT(on_iniswap_remove_clicked())); + menu->popup(ui_iniswap_dropdown->mapToGlobal(pos)); +} +void Courtroom::on_iniswap_edit_requested() +{ + QString p_path = ao_app->get_character_path(current_char, "char.ini"); + if (!file_exists(p_path)) return; + QDesktopServices::openUrl(QUrl::fromLocalFile(p_path)); +} - ao_app->send_server_packet( - new AOPacket("CT#" + ui_ooc_chat_name->text() + "#/pos " + f_pos + "#%")); +void Courtroom::on_iniswap_remove_clicked() +{ + if (ui_iniswap_dropdown->count() <= 0) { + ui_iniswap_remove->hide(); // We're not supposed to see it. Do this or the + // client will crash + return; + } + if (ui_iniswap_dropdown->itemText(ui_iniswap_dropdown->currentIndex()) != + char_list.at(m_cid).name) { + ui_iniswap_dropdown->removeItem(ui_iniswap_dropdown->currentIndex()); + on_iniswap_dropdown_changed(0); // Reset back to original + update_character(m_cid); + } +} + +void Courtroom::set_sfx_dropdown() +{ + ui_sfx_dropdown->blockSignals(true); + ui_sfx_dropdown->clear(); + if (m_cid == -1) { + ui_sfx_dropdown->hide(); + ui_sfx_remove->hide(); + return; + } + QStringList soundlist = ao_app->get_list_file( + ao_app->get_character_path(current_char, "soundlist.ini")); + + if (soundlist.size() <= 0) { + soundlist = ao_app->get_list_file( + ao_app->get_theme_path("character_soundlist.ini")); + if (soundlist.size() <= 0) { + soundlist = ao_app->get_list_file( + ao_app->get_default_theme_path("character_soundlist.ini")); + } + } + + if (soundlist.size() <= 0) { + ui_sfx_dropdown->hide(); + ui_sfx_remove->hide(); + return; + } + soundlist.prepend("Default"); + + ui_sfx_dropdown->show(); + ui_sfx_dropdown->addItems(soundlist); + ui_sfx_dropdown->setCurrentIndex(0); + ui_sfx_remove->hide(); + ui_sfx_dropdown->blockSignals(false); +} + +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) { + QString entry = ui_sfx_dropdown->itemText(i); + if (!soundlist.contains(entry) && entry != "Default") + soundlist.append(entry); + } + + QStringList defaultlist = + ao_app->get_list_file(ao_app->get_theme_path("character_soundlist.ini")); + if (defaultlist.size() <= 0) { + defaultlist = ao_app->get_list_file( + ao_app->get_default_theme_path("character_soundlist.ini")); + } + + if (defaultlist.size() > 0 && + defaultlist.toSet().subtract(soundlist.toSet()).size() > + 0) // There's a difference from the default configuration + ao_app->write_to_file( + soundlist.join("\n"), + ao_app->get_character_path(current_char, + "soundlist.ini")); // Create a new sound list + + ui_sfx_dropdown->blockSignals(true); + ui_sfx_dropdown->setCurrentIndex(p_index); + ui_sfx_dropdown->blockSignals(false); + if (p_index != 0) + ui_sfx_remove->show(); + else + ui_sfx_remove->hide(); +} + +void Courtroom::on_sfx_context_menu_requested(const QPoint &pos) +{ + QMenu *menu = ui_sfx_dropdown->lineEdit()->createStandardContextMenu(); + + menu->addSeparator(); + if (file_exists(ao_app->get_character_path(current_char, "soundlist.ini"))) + menu->addAction(QString("Edit " + current_char + "/soundlist.ini"), this, + SLOT(on_sfx_edit_requested())); + else + menu->addAction(QString("Edit theme's character_soundlist.ini"), this, + SLOT(on_sfx_edit_requested())); + if (ui_sfx_dropdown->currentIndex() != 0) + menu->addAction(QString("Remove " + ui_sfx_dropdown->itemText( + ui_sfx_dropdown->currentIndex())), + this, SLOT(on_sfx_remove_clicked())); + menu->popup(ui_sfx_dropdown->mapToGlobal(pos)); +} +void Courtroom::on_sfx_edit_requested() +{ + QString p_path = ao_app->get_character_path(current_char, "soundlist.ini"); + if (!file_exists(p_path)) { + p_path = ao_app->get_theme_path("character_soundlist.ini"); + if (!file_exists(p_path)) { + p_path = ao_app->get_default_theme_path("character_soundlist.ini"); + if (!file_exists(p_path)) { + return; + } + } + } + QDesktopServices::openUrl(QUrl::fromLocalFile(p_path)); +} + +void Courtroom::on_sfx_remove_clicked() +{ + if (ui_sfx_dropdown->count() <= 0) { + ui_sfx_remove->hide(); // We're not supposed to see it. Do this or the + // client will crash + return; + } + if (ui_sfx_dropdown->itemText(ui_sfx_dropdown->currentIndex()) != "Default") { + ui_sfx_dropdown->removeItem(ui_sfx_dropdown->currentIndex()); + on_sfx_dropdown_changed(0); // Reset back to original + } +} + +void Courtroom::set_effects_dropdown() +{ + ui_effects_dropdown->blockSignals(true); + ui_effects_dropdown->clear(); + if (m_cid == -1) { + ui_effects_dropdown->hide(); + return; + } + QStringList effectslist = ao_app->get_effects(current_char); + + if (effectslist.size() <= 0) { + ui_effects_dropdown->hide(); + return; + } + + effectslist.prepend("None"); + + ui_effects_dropdown->show(); + ui_effects_dropdown->addItems(effectslist); + + // ICON-MAKING HELL + QString p_effect = ao_app->read_char_ini(current_char, "effects", "Options"); + QString custom_path = + ao_app->get_base_path() + "misc/" + p_effect + "/icons/"; + QString theme_path = ao_app->get_theme_path("effects/icons/"); + QString default_path = ao_app->get_default_theme_path("effects/icons/"); + for (int i = 0; i < ui_effects_dropdown->count(); ++i) { + QString entry = ui_effects_dropdown->itemText(i); + QString iconpath = ao_app->get_static_image_suffix(custom_path + entry); + if (!file_exists(iconpath)) { + iconpath = ao_app->get_static_image_suffix(theme_path + entry); + if (!file_exists(iconpath)) { + iconpath = ao_app->get_static_image_suffix(default_path + entry); + if (!file_exists(iconpath)) + continue; + } + } + ui_effects_dropdown->setItemIcon(i, QIcon(iconpath)); + } + + ui_effects_dropdown->setCurrentIndex(0); + ui_effects_dropdown->blockSignals(false); +} + +void Courtroom::on_effects_context_menu_requested(const QPoint &pos) +{ + QMenu *menu = new QMenu(); + + if (!ao_app->read_char_ini(current_char, "effects", "Options").isEmpty()) + menu->addAction( + QString("Open misc/" + + ao_app->read_char_ini(current_char, "effects", "Options") + + " folder"), + this, SLOT(on_character_effects_edit_requested())); + menu->addAction(QString("Open theme's effects folder"), this, + SLOT(on_effects_edit_requested())); + menu->popup(ui_effects_dropdown->mapToGlobal(pos)); +} +void Courtroom::on_effects_edit_requested() +{ + QString p_path = ao_app->get_theme_path("effects/"); + if (!dir_exists(p_path)) { + p_path = ao_app->get_default_theme_path("effects/"); + if (!dir_exists(p_path)) { + return; + } + } + QDesktopServices::openUrl(QUrl::fromLocalFile(p_path)); +} +void Courtroom::on_character_effects_edit_requested() +{ + QString p_effect = ao_app->read_char_ini(current_char, "effects", "Options"); + QString p_path = ao_app->get_base_path() + "misc/" + p_effect + "/"; + if (!dir_exists(p_path)) + return; + + QDesktopServices::openUrl(QUrl::fromLocalFile(p_path)); +} + +void Courtroom::on_effects_dropdown_changed(int p_index) +{ + effect = ui_effects_dropdown->itemText(p_index); + ui_ic_chat_message->setFocus(); +} + +bool Courtroom::effects_dropdown_find_and_set(QString effect) +{ + for (int i = 0; i < ui_effects_dropdown->count(); ++i) { + QString entry = ui_effects_dropdown->itemText(i); + if (entry == effect) { + ui_effects_dropdown->setCurrentIndex(i); + return true; + } + } + return false; +} + +QString Courtroom::get_char_sfx() +{ + QString sfx = ui_sfx_dropdown->itemText(ui_sfx_dropdown->currentIndex()); + if (sfx != "" && sfx != "Default") + return sfx; + return ao_app->get_sfx_name(current_char, current_emote); +} + +int Courtroom::get_char_sfx_delay() +{ + // QString sfx = ui_sfx_dropdown->itemText(ui_sfx_dropdown->currentIndex()); + // if (sfx != "" && sfx != "Default") + // return 0; //todo: a way to define this + return ao_app->get_sfx_delay(current_char, current_emote); } void Courtroom::on_mute_list_clicked(QModelIndex p_index) @@ -3015,46 +3943,115 @@ void Courtroom::on_pair_list_clicked(QModelIndex p_index) } } -void Courtroom::on_music_list_double_clicked(QModelIndex p_model) +void Courtroom::on_music_list_double_clicked(QTreeWidgetItem *p_item, + int column) { if (is_muted) return; - QString p_song = music_list.at(music_row_to_number.at(p_model.row())); + column = 1; // Column 1 is always the metadata (which we want) + QString p_song = p_item->text(column); - if (!ui_ic_chat_name->text().isEmpty() && ao_app->cccc_ic_support_enabled) { - ao_app->send_server_packet(new AOPacket("MC#" + p_song + "#" + - QString::number(m_cid) + "#" + - ui_ic_chat_name->text() + "#%"), - false); - } - else { - ao_app->send_server_packet( - new AOPacket("MC#" + p_song + "#" + QString::number(m_cid) + "#%"), - false); - } + QStringList packet_contents; + packet_contents.append(p_song); + 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(QModelIndex p_model) +void Courtroom::on_music_list_context_menu_requested(const QPoint &pos) { - QString p_area = area_list.at(area_row_to_number.at(p_model.row())); - ao_app->send_server_packet( - new AOPacket("MC#" + p_area + "#" + QString::number(m_cid) + "#%"), - false); + QMenu *menu = new QMenu(); + + menu->addAction(QString("Expand All Categories"), this, + SLOT(music_list_expand_all())); + menu->addAction(QString("Collapse All Categories"), this, + SLOT(music_list_collapse_all())); + menu->addSeparator(); + + menu->addAction(new QAction("Fade Out Previous", this)); + menu->actions().back()->setCheckable(true); + menu->actions().back()->setChecked(music_flags & FADE_OUT); + connect(menu->actions().back(), SIGNAL(toggled(bool)), this, + SLOT(music_fade_out(bool))); + + menu->addAction(new QAction("Fade In", this)); + menu->actions().back()->setCheckable(true); + menu->actions().back()->setChecked(music_flags & FADE_IN); + connect(menu->actions().back(), SIGNAL(toggled(bool)), this, + SLOT(music_fade_in(bool))); + + menu->addAction(new QAction("Synchronize", this)); + menu->actions().back()->setCheckable(true); + menu->actions().back()->setChecked(music_flags & SYNC_POS); + connect(menu->actions().back(), SIGNAL(toggled(bool)), this, + SLOT(music_synchronize(bool))); + + menu->popup(ui_music_list->mapToGlobal(pos)); +} + +void Courtroom::music_fade_out(bool toggle) +{ + if (toggle) + music_flags |= FADE_OUT; + else + music_flags &= ~FADE_OUT; +} + +void Courtroom::music_fade_in(bool toggle) +{ + if (toggle) + music_flags |= FADE_IN; + else + music_flags &= ~FADE_IN; +} + +void Courtroom::music_synchronize(bool toggle) +{ + if (toggle) + music_flags |= SYNC_POS; + else + music_flags &= ~SYNC_POS; +} + +void Courtroom::music_list_expand_all() { ui_music_list->expandAll(); } +void Courtroom::music_list_collapse_all() +{ + ui_music_list->collapseAll(); + QTreeWidgetItem *current = ui_music_list->selectedItems()[0]; + if (current->parent() != nullptr) + current = current->parent(); + ui_music_list->setCurrentItem(current); +} + +void Courtroom::on_area_list_double_clicked(QTreeWidgetItem *p_item, int column) +{ + column = 0; // The metadata + QString p_area = p_item->text(0); + + QStringList packet_contents; + packet_contents.append(p_area); + packet_contents.append(QString::number(m_cid)); + qDebug() << packet_contents; + ao_app->send_server_packet(new AOPacket("MC", packet_contents), false); } void Courtroom::on_hold_it_clicked() { if (objection_state == 1) { - ui_hold_it->set_image("holdit.png"); + ui_hold_it->set_image("holdit"); objection_state = 0; } else { - ui_objection->set_image("objection.png"); - ui_take_that->set_image("takethat.png"); - ui_custom_objection->set_image("custom.png"); + ui_objection->set_image("objection"); + ui_take_that->set_image("takethat"); + ui_custom_objection->set_image("custom"); - ui_hold_it->set_image("holdit_selected.png"); + ui_hold_it->set_image("holdit_selected"); objection_state = 1; } @@ -3064,15 +4061,15 @@ void Courtroom::on_hold_it_clicked() void Courtroom::on_objection_clicked() { if (objection_state == 2) { - ui_objection->set_image("objection.png"); + ui_objection->set_image("objection"); objection_state = 0; } else { - ui_hold_it->set_image("holdit.png"); - ui_take_that->set_image("takethat.png"); - ui_custom_objection->set_image("custom.png"); + ui_hold_it->set_image("holdit"); + ui_take_that->set_image("takethat"); + ui_custom_objection->set_image("custom"); - ui_objection->set_image("objection_selected.png"); + ui_objection->set_image("objection_selected"); objection_state = 2; } @@ -3082,15 +4079,15 @@ void Courtroom::on_objection_clicked() void Courtroom::on_take_that_clicked() { if (objection_state == 3) { - ui_take_that->set_image("takethat.png"); + ui_take_that->set_image("takethat"); objection_state = 0; } else { - ui_objection->set_image("objection.png"); - ui_hold_it->set_image("holdit.png"); - ui_custom_objection->set_image("custom.png"); + ui_objection->set_image("objection"); + ui_hold_it->set_image("holdit"); + ui_custom_objection->set_image("custom"); - ui_take_that->set_image("takethat_selected.png"); + ui_take_that->set_image("takethat_selected"); objection_state = 3; } @@ -3100,15 +4097,15 @@ void Courtroom::on_take_that_clicked() void Courtroom::on_custom_objection_clicked() { if (objection_state == 4) { - ui_custom_objection->set_image("custom.png"); + ui_custom_objection->set_image("custom"); objection_state = 0; } else { - ui_objection->set_image("objection.png"); - ui_take_that->set_image("takethat.png"); - ui_hold_it->set_image("holdit.png"); + ui_objection->set_image("objection"); + ui_take_that->set_image("takethat"); + ui_hold_it->set_image("holdit"); - ui_custom_objection->set_image("custom_selected.png"); + ui_custom_objection->set_image("custom_selected"); objection_state = 4; } @@ -3119,11 +4116,30 @@ void Courtroom::on_realization_clicked() { if (realization_state == 0) { realization_state = 1; - ui_realization->set_image("realization_pressed.png"); + if (effects_dropdown_find_and_set("realization")) + on_effects_dropdown_changed(ui_effects_dropdown->currentIndex()); + + ui_realization->set_image("realization_pressed"); } else { realization_state = 0; - ui_realization->set_image("realization.png"); + ui_effects_dropdown->setCurrentIndex(0); + on_effects_dropdown_changed(ui_effects_dropdown->currentIndex()); + ui_realization->set_image("realization"); + } + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_screenshake_clicked() +{ + if (screenshake_state == 0) { + screenshake_state = 1; + ui_screenshake->set_image("screenshake_pressed"); + } + else { + screenshake_state = 0; + ui_screenshake->set_image("screenshake"); } ui_ic_chat_message->setFocus(); @@ -3135,12 +4151,13 @@ void Courtroom::on_mute_clicked() ui_mute_list->show(); ui_pair_list->hide(); ui_pair_offset_spinbox->hide(); - ui_pair_button->set_image("pair_button.png"); - ui_mute->set_image("mute_pressed.png"); + ui_pair_order_dropdown->hide(); + ui_pair_button->set_image("pair_button"); + ui_mute->set_image("mute_pressed"); } else { ui_mute_list->hide(); - ui_mute->set_image("mute.png"); + ui_mute->set_image("mute"); } } @@ -3149,17 +4166,24 @@ void Courtroom::on_pair_clicked() if (ui_pair_list->isHidden()) { ui_pair_list->show(); ui_pair_offset_spinbox->show(); + ui_pair_order_dropdown->show(); ui_mute_list->hide(); - ui_mute->set_image("mute.png"); - ui_pair_button->set_image("pair_button_pressed.png"); + ui_mute->set_image("mute"); + ui_pair_button->set_image("pair_button_pressed"); } else { ui_pair_list->hide(); ui_pair_offset_spinbox->hide(); - ui_pair_button->set_image("pair_button.png"); + ui_pair_order_dropdown->hide(); + ui_pair_button->set_image("pair_button"); } } +void Courtroom::on_pair_order_dropdown_changed(int p_index) +{ + pair_order = p_index; +} + void Courtroom::on_defense_minus_clicked() { int f_state = defense_bar_state - 1; @@ -3196,21 +4220,96 @@ void Courtroom::on_prosecution_plus_clicked() new AOPacket("HP#2#" + QString::number(f_state) + "#%")); } +void Courtroom::set_text_color_dropdown() +{ + // Clear the lists + ui_text_color->clear(); + color_row_to_number.clear(); + + // Clear the stored optimization information + color_rgb_list.clear(); + color_markdown_start_list.clear(); + color_markdown_end_list.clear(); + color_markdown_remove_list.clear(); + color_markdown_talking_list.clear(); + + // Update markdown colors. TODO: make a loading function that only loads the + // config file once instead of several times + for (int c = 0; c < max_colors; ++c) { + QColor color = ao_app->get_chat_color(QString::number(c), current_char); + color_rgb_list.append(color); + color_markdown_start_list.append(ao_app->get_chat_markdown( + "c" + QString::number(c) + "_start", current_char)); + color_markdown_end_list.append(ao_app->get_chat_markdown( + "c" + QString::number(c) + "_end", current_char)); + color_markdown_remove_list.append( + ao_app->get_chat_markdown("c" + QString::number(c) + "_remove", + current_char) == "1"); + color_markdown_talking_list.append( + ao_app->get_chat_markdown("c" + QString::number(c) + "_talking", + current_char) == "1"); + + QString color_name = ao_app->get_chat_markdown( + "c" + QString::number(c) + "_name", current_char); + if (color_name.isEmpty()) // Not defined + { + if (c > 0) + continue; + color_name = tr("Default"); + } + ui_text_color->addItem(color_name); + QPixmap pixmap(16, 16); + pixmap.fill(color); + ui_text_color->setItemIcon(ui_text_color->count() - 1, QIcon(pixmap)); + color_row_to_number.append(c); + } +} + void Courtroom::on_text_color_changed(int p_color) { - text_color = p_color; + if (ui_ic_chat_message->selectionStart() != -1) // We have a selection! + { + int c = color_row_to_number.at(p_color); + QString markdown_start = color_markdown_start_list.at(c); + if (markdown_start.isEmpty()) { + qDebug() << "W: Color list dropdown selected a non-existent markdown " + "start character"; + return; + } + QString markdown_end = color_markdown_end_list.at(c); + if (markdown_end.isEmpty()) + markdown_end = markdown_start; + int start = ui_ic_chat_message->selectionStart(); + int end = ui_ic_chat_message->selectionEnd() + 1; + ui_ic_chat_message->setCursorPosition(start); + ui_ic_chat_message->insert(markdown_start); + ui_ic_chat_message->setCursorPosition(end); + ui_ic_chat_message->insert(markdown_end); + // ui_ic_chat_message->end(false); + ui_text_color->setCurrentIndex(0); + } + else { + if (p_color != -1 && p_color < color_row_to_number.size()) + text_color = color_row_to_number.at(p_color); + else + text_color = 0; + } ui_ic_chat_message->setFocus(); } void Courtroom::on_music_slider_moved(int p_value) { - music_player->set_volume(p_value); + music_player->set_volume(p_value, 0); // Set volume on music layer ui_ic_chat_message->setFocus(); } void Courtroom::on_sfx_slider_moved(int p_value) { sfx_player->set_volume(p_value); + // Set the ambience and other misc. music layers + for (int i = 1; i < music_player->m_channelmax; ++i) { + music_player->set_volume(p_value, i); + } objection_player->set_volume(p_value); ui_ic_chat_message->setFocus(); } @@ -3223,7 +4322,7 @@ void Courtroom::on_blip_slider_moved(int p_value) void Courtroom::on_log_limit_changed(int value) { log_maximum_blocks = value; } -void Courtroom::on_pair_offset_changed(int value) { offset_with_pair = value; } +void Courtroom::on_pair_offset_changed(int value) { char_offset = value; } void Courtroom::on_witness_testimony_clicked() { @@ -3267,8 +4366,6 @@ void Courtroom::on_guilty_clicked() void Courtroom::on_change_character_clicked() { - music_player->set_volume(0); - sfx_player->set_volume(0); sfx_player->set_volume(0); blip_player->set_volume(0); @@ -3282,12 +4379,14 @@ void Courtroom::on_reload_theme_clicked() { ao_app->reload_theme(); - // to update status on the background - set_background(current_background); - enter_courtroom(m_cid); + enter_courtroom(); + update_character(m_cid); anim_state = 4; text_state = 3; + + // to update status on the background + set_background(current_background); } void Courtroom::on_back_to_lobby_clicked() @@ -3308,14 +4407,7 @@ void Courtroom::on_char_select_right_clicked() set_char_select_page(); } -void Courtroom::on_spectator_clicked() -{ - enter_courtroom(-1); - - ui_emotes->hide(); - - ui_char_select_background->hide(); -} +void Courtroom::on_spectator_clicked() { update_character(-1); } void Courtroom::on_call_mod_clicked() { @@ -3337,7 +4429,7 @@ void Courtroom::on_call_mod_clicked() return; } else if (text.length() > 256) { - errorBox.critical(nullptr, "Error", "The message is too long."); + errorBox.critical(nullptr, tr("Error"), tr("The message is too long.")); return; } @@ -3364,6 +4456,17 @@ void Courtroom::on_pre_clicked() { ui_ic_chat_message->setFocus(); } void Courtroom::on_flip_clicked() { ui_ic_chat_message->setFocus(); } +void Courtroom::on_additive_clicked() +{ + if (ui_additive->isChecked()) { + ui_ic_chat_message->home(false); // move cursor to the start of the message + ui_ic_chat_message->insert(" "); // preface the message by whitespace + ui_ic_chat_message->end(false); // move cursor to the end of the message + // without selecting anything + } + ui_ic_chat_message->setFocus(); +} + void Courtroom::on_guard_clicked() { ui_ic_chat_message->setFocus(); } void Courtroom::on_showname_enable_clicked() @@ -3373,14 +4476,16 @@ void Courtroom::on_showname_enable_clicked() foreach (chatlogpiece item, ic_chatlog_history) { if (ui_showname_enable->isChecked()) { - if (item.get_is_song()) - append_ic_text(item.get_message(), item.get_showname(), true); + if (item.is_song()) + append_ic_text(item.get_message(), item.get_showname(), + "has played a song"); else append_ic_text(item.get_message(), item.get_showname()); } else { - if (item.get_is_song()) - append_ic_text(item.get_message(), item.get_name(), true); + if (item.is_song()) + append_ic_text(item.get_message(), item.get_name(), + "has played a song"); else append_ic_text(item.get_message(), item.get_name()); } @@ -3402,6 +4507,8 @@ void Courtroom::on_evidence_button_clicked() void Courtroom::on_switch_area_music_clicked() { + ui_music_search->setText(""); + on_music_search_edited(ui_music_search->text()); if (ui_area_list->isHidden()) { ui_area_list->show(); ui_music_list->hide(); diff --git a/src/debug_functions.cpp b/src/debug_functions.cpp index f33a2c6..b832164 100644 --- a/src/debug_functions.cpp +++ b/src/debug_functions.cpp @@ -1,11 +1,16 @@ +#include <QCoreApplication> +#include <QMessageBox> + #include "debug_functions.h" void call_error(QString p_message) { QMessageBox *msgBox = new QMessageBox; - msgBox->setText("Error: " + p_message); - msgBox->setWindowTitle("Error"); + msgBox->setText(QCoreApplication::translate("debug_functions", "Error: %1") + .arg(p_message)); + msgBox->setWindowTitle( + QCoreApplication::translate("debug_functions", "Error")); // msgBox->setWindowModality(Qt::NonModal); msgBox->exec(); @@ -16,7 +21,8 @@ void call_notice(QString p_message) QMessageBox *msgBox = new QMessageBox; msgBox->setText(p_message); - msgBox->setWindowTitle("Notice"); + msgBox->setWindowTitle( + QCoreApplication::translate("debug_functions", "Notice")); // msgBox->setWindowModality(Qt::NonModal); msgBox->exec(); diff --git a/src/emotes.cpp b/src/emotes.cpp index bbe2f5c..8de5524 100644 --- a/src/emotes.cpp +++ b/src/emotes.cpp @@ -2,20 +2,48 @@ #include "aoemotebutton.h" -void Courtroom::construct_emotes() +void Courtroom::initialize_emotes() { ui_emotes = new QWidget(this); + ui_emote_left = new AOButton(this, ao_app); + ui_emote_right = new AOButton(this, ao_app); + + ui_emote_dropdown = new QComboBox(this); + + connect(ui_emote_left, SIGNAL(clicked()), this, + SLOT(on_emote_left_clicked())); + connect(ui_emote_right, SIGNAL(clicked()), this, + SLOT(on_emote_right_clicked())); + + connect(ui_emote_dropdown, SIGNAL(activated(int)), this, + SLOT(on_emote_dropdown_changed(int))); +} + +void Courtroom::refresh_emotes() +{ + // Should properly refresh the emote list + qDeleteAll(ui_emote_list.begin(), ui_emote_list.end()); + ui_emote_list.clear(); + set_size_and_pos(ui_emotes, "emotes"); + set_size_and_pos(ui_emote_left, "emote_left"); + ui_emote_left->set_image("arrow_left"); + + set_size_and_pos(ui_emote_right, "emote_right"); + ui_emote_right->set_image("arrow_right"); + QPoint f_spacing = ao_app->get_button_spacing("emote_button_spacing", "courtroom_design.ini"); + QPoint p_point = + ao_app->get_button_spacing("emote_button_size", "courtroom_design.ini"); - const int button_width = 40; + const int button_width = p_point.x(); int x_spacing = f_spacing.x(); int x_mod_count = 0; - const int button_height = 40; + const int button_height = p_point.y(); int y_spacing = f_spacing.y(); int y_mod_count = 0; @@ -30,7 +58,8 @@ void Courtroom::construct_emotes() int x_pos = (button_width + x_spacing) * x_mod_count; int y_pos = (button_height + y_spacing) * y_mod_count; - AOEmoteButton *f_emote = new AOEmoteButton(ui_emotes, ao_app, x_pos, y_pos); + AOEmoteButton *f_emote = new AOEmoteButton(ui_emotes, ao_app, x_pos, y_pos, + button_width, button_height); ui_emote_list.append(f_emote); @@ -82,16 +111,19 @@ void Courtroom::set_emote_page() if (current_emote_page > 0) ui_emote_left->show(); - for (int n_emote = 0; n_emote < emotes_on_page; ++n_emote) { + for (int n_emote = 0; + n_emote < emotes_on_page && n_emote < ui_emote_list.size(); ++n_emote) { int n_real_emote = n_emote + current_emote_page * max_emotes_on_page; AOEmoteButton *f_emote = ui_emote_list.at(n_emote); if (n_real_emote == current_emote) - f_emote->set_image(current_char, n_real_emote, "_on.png"); + f_emote->set_char_image(current_char, n_real_emote, "_on"); else - f_emote->set_image(current_char, n_real_emote, "_off.png"); + f_emote->set_char_image(current_char, n_real_emote, "_off"); f_emote->show(); + f_emote->setToolTip(QString::number(n_real_emote + 1) + ": " + + ao_app->get_emote_comment(current_char, n_real_emote)); } } @@ -103,7 +135,8 @@ void Courtroom::set_emote_dropdown() QStringList emote_list; for (int n = 0; n < total_emotes; ++n) { - emote_list.append(ao_app->get_emote_comment(current_char, n)); + emote_list.append(QString::number(n + 1) + ": " + + ao_app->get_emote_comment(current_char, n)); } ui_emote_dropdown->addItems(emote_list); @@ -116,7 +149,7 @@ void Courtroom::select_emote(int p_id) if (current_emote >= min && current_emote <= max) ui_emote_list.at(current_emote % max_emotes_on_page) - ->set_image(current_char, current_emote, "_off.png"); + ->set_char_image(current_char, current_emote, "_off"); int old_emote = current_emote; @@ -124,17 +157,19 @@ void Courtroom::select_emote(int p_id) if (current_emote >= min && current_emote <= max) ui_emote_list.at(current_emote % max_emotes_on_page) - ->set_image(current_char, current_emote, "_on.png"); + ->set_char_image(current_char, current_emote, "_on"); int emote_mod = ao_app->get_emote_mod(current_char, current_emote); if (old_emote == current_emote) { ui_pre->setChecked(!ui_pre->isChecked()); } - else if (emote_mod == 1) - ui_pre->setChecked(true); - else - ui_pre->setChecked(false); + else if (!ao_app->is_stickypres_enabled()) { + if (emote_mod == 1 || emote_mod == 4) + ui_pre->setChecked(true); + else + ui_pre->setChecked(false); + } ui_emote_dropdown->setCurrentIndex(current_emote); diff --git a/src/evidence.cpp b/src/evidence.cpp index 128156c..dffe848 100644 --- a/src/evidence.cpp +++ b/src/evidence.cpp @@ -1,45 +1,194 @@ #include "courtroom.h" -void Courtroom::construct_evidence() +void Courtroom::initialize_evidence() { ui_evidence = new AOImage(this, ao_app); // ui_evidence_name = new QLabel(ui_evidence); ui_evidence_name = new AOLineEdit(ui_evidence); ui_evidence_name->setAlignment(Qt::AlignCenter); - ui_evidence_name->setFont(QFont("Arial", 14, QFont::Bold)); - ui_evidence_name->setStyleSheet("background-color: rgba(0, 0, 0, 0);" - "color: rgba(255, 128, 0, 255);"); + ui_evidence_name->setFrame(false); ui_evidence_buttons = new QWidget(ui_evidence); ui_evidence_left = new AOButton(ui_evidence, ao_app); ui_evidence_right = new AOButton(ui_evidence, ao_app); ui_evidence_present = new AOButton(ui_evidence, ao_app); + ui_evidence_present->setToolTip(tr("Present this piece of evidence to " + "everyone on your next spoken message")); + + ui_evidence_switch = new AOButton(ui_evidence, ao_app); + ui_evidence_transfer = new AOButton(ui_evidence, ao_app); + + ui_evidence_save = new AOButton(ui_evidence, ao_app); + ui_evidence_save->setToolTip(tr("Save evidence to an .ini file.")); + ui_evidence_load = new AOButton(ui_evidence, ao_app); + ui_evidence_load->setToolTip(tr("Load evidence from an .ini file.")); ui_evidence_overlay = new AOImage(ui_evidence, ao_app); ui_evidence_delete = new AOButton(ui_evidence_overlay, ao_app); + ui_evidence_delete->setToolTip(tr("Destroy this piece of evidence")); ui_evidence_image_name = new AOLineEdit(ui_evidence_overlay); ui_evidence_image_button = new AOButton(ui_evidence_overlay, ao_app); ui_evidence_image_button->setText("Choose.."); ui_evidence_x = new AOButton(ui_evidence_overlay, ao_app); + ui_evidence_x->setToolTip( + tr("Close the evidence display/editing overlay.\n" + "You will be prompted if there's any unsaved changes.")); + ui_evidence_ok = new AOButton(ui_evidence_overlay, ao_app); + ui_evidence_ok->setToolTip(tr("Save any changes made to this piece of " + "evidence and send them to server.")); ui_evidence_description = new AOTextEdit(ui_evidence_overlay); ui_evidence_description->setStyleSheet("background-color: rgba(0, 0, 0, 0);" "color: white;"); + ui_evidence_description->setFrameStyle(QFrame::NoFrame); + ui_evidence_description->setToolTip( + "Double-click to edit. Press [X] to update your changes."); + + connect(ui_evidence_name, SIGNAL(returnPressed()), this, + SLOT(on_evidence_name_edited())); + connect(ui_evidence_name, SIGNAL(double_clicked()), this, + SLOT(on_evidence_name_double_clicked())); + connect(ui_evidence_left, SIGNAL(clicked()), this, + SLOT(on_evidence_left_clicked())); + connect(ui_evidence_right, SIGNAL(clicked()), this, + SLOT(on_evidence_right_clicked())); + connect(ui_evidence_present, SIGNAL(clicked()), this, + SLOT(on_evidence_present_clicked())); + connect(ui_evidence_switch, SIGNAL(clicked()), this, + SLOT(on_evidence_switch_clicked())); + connect(ui_evidence_transfer, SIGNAL(clicked()), this, + SLOT(on_evidence_transfer_clicked())); + connect(ui_evidence_save, SIGNAL(clicked()), this, + SLOT(on_evidence_save_clicked())); + connect(ui_evidence_load, SIGNAL(clicked()), this, + SLOT(on_evidence_load_clicked())); + + connect(ui_evidence_delete, SIGNAL(clicked()), this, + SLOT(on_evidence_delete_clicked())); + connect(ui_evidence_image_name, SIGNAL(returnPressed()), this, + SLOT(on_evidence_image_name_edited())); + connect(ui_evidence_image_name, SIGNAL(double_clicked()), this, + SLOT(on_evidence_image_name_double_clicked())); + connect(ui_evidence_image_button, SIGNAL(clicked()), this, + SLOT(on_evidence_image_button_clicked())); + connect(ui_evidence_x, SIGNAL(clicked()), this, + SLOT(on_evidence_x_clicked())); + connect(ui_evidence_ok, SIGNAL(clicked()), this, + SLOT(on_evidence_ok_clicked())); + + connect(ui_evidence_name, SIGNAL(textChanged(QString)), this, + SLOT(on_evidence_edited())); + connect(ui_evidence_image_name, SIGNAL(textChanged(QString)), this, + SLOT(on_evidence_edited())); + connect(ui_evidence_description, SIGNAL(textChanged()), this, + SLOT(on_evidence_edited())); + + ui_evidence->hide(); +} + +void Courtroom::refresh_evidence() +{ + set_font(ui_evidence_name, "", "evidence_name"); + set_font(ui_evidence_image_name, "", "evidence_image_name"); + set_font(ui_evidence_description, "", "evidence_description"); + + // Should properly refresh the evidence list + qDeleteAll(ui_evidence_list.begin(), ui_evidence_list.end()); + ui_evidence_list.clear(); + + set_size_and_pos(ui_evidence_button, "evidence_button"); + ui_evidence_button->set_image("evidence_button"); + ui_evidence_button->setToolTip(tr("Bring up the Evidence screen.")); set_size_and_pos(ui_evidence, "evidence_background"); + if (current_evidence_global) + ui_evidence->set_image("evidence_background"); + else + ui_evidence->set_image("evidence_background_private"); + + set_size_and_pos(ui_evidence_name, "evidence_name"); + set_size_and_pos(ui_evidence_buttons, "evidence_buttons"); + set_size_and_pos(ui_evidence_left, "evidence_left"); + ui_evidence_left->set_image("arrow_left"); + + set_size_and_pos(ui_evidence_right, "evidence_right"); + ui_evidence_right->set_image("arrow_right"); + + set_size_and_pos(ui_evidence_present, "evidence_present"); + ui_evidence_present->set_image("present"); + + set_size_and_pos(ui_evidence_overlay, "evidence_overlay"); + if (current_evidence_global) + ui_evidence_overlay->set_image("evidence_overlay"); + else + ui_evidence_overlay->set_image("evidence_overlay_private"); + + set_size_and_pos(ui_evidence_delete, "evidence_delete"); + ui_evidence_delete->set_image("evidence_delete"); + + set_size_and_pos(ui_evidence_image_name, "evidence_image_name"); + + set_size_and_pos(ui_evidence_image_button, "evidence_image_button"); + + set_size_and_pos(ui_evidence_x, "evidence_x"); + ui_evidence_x->set_image("evidence_x"); + + set_size_and_pos(ui_evidence_ok, "evidence_ok"); + ui_evidence_ok->set_image("evidence_ok"); + + set_size_and_pos(ui_evidence_switch, "evidence_switch"); + if (current_evidence_global) { + ui_evidence_switch->set_image("evidence_global"); + ui_evidence_switch->setToolTip(tr("Switch evidence to private inventory.")); + } + else { + ui_evidence_switch->set_image("evidence_private"); + ui_evidence_switch->setToolTip(tr("Switch evidence to global inventory.")); + } + + set_size_and_pos(ui_evidence_transfer, "evidence_transfer"); + if (current_evidence_global) { + ui_evidence_transfer->set_image("evidence_transfer"); + ui_evidence_transfer->setToolTip( + tr("Transfer evidence to private inventory.")); + } + else { + ui_evidence_transfer->set_image("evidence_transfer_private"); + ui_evidence_transfer->setToolTip( + tr("Transfer evidence to global inventory.")); + } + + set_size_and_pos(ui_evidence_save, "evidence_save"); + ui_evidence_save->set_image("evidence_save"); + if (current_evidence_global) + ui_evidence_save->hide(); + else + ui_evidence_save->show(); + + set_size_and_pos(ui_evidence_load, "evidence_load"); + ui_evidence_load->set_image("evidence_load"); + if (current_evidence_global) + ui_evidence_load->hide(); + else + ui_evidence_load->show(); + + set_size_and_pos(ui_evidence_description, "evidence_description"); + QPoint f_spacing = ao_app->get_button_spacing("evidence_button_spacing", "courtroom_design.ini"); + QPoint p_point = ao_app->get_button_spacing("evidence_button_size", + "courtroom_design.ini"); - const int button_width = 70; + const int button_width = p_point.x(); int x_spacing = f_spacing.x(); int x_mod_count = 0; - const int button_height = 70; + const int button_height = p_point.y(); int y_spacing = f_spacing.y(); int y_mod_count = 0; @@ -56,8 +205,8 @@ void Courtroom::construct_evidence() int x_pos = (button_width + x_spacing) * x_mod_count; int y_pos = (button_height + y_spacing) * y_mod_count; - AOEvidenceButton *f_evidence = - new AOEvidenceButton(ui_evidence_buttons, ao_app, x_pos, y_pos); + AOEvidenceButton *f_evidence = new AOEvidenceButton( + ui_evidence_buttons, ao_app, x_pos, y_pos, button_width, button_height); ui_evidence_list.append(f_evidence); @@ -77,33 +226,65 @@ void Courtroom::construct_evidence() x_mod_count = 0; } } - - connect(ui_evidence_name, SIGNAL(returnPressed()), this, - SLOT(on_evidence_name_edited())); - connect(ui_evidence_left, SIGNAL(clicked()), this, - SLOT(on_evidence_left_clicked())); - connect(ui_evidence_right, SIGNAL(clicked()), this, - SLOT(on_evidence_right_clicked())); - connect(ui_evidence_present, SIGNAL(clicked()), this, - SLOT(on_evidence_present_clicked())); - connect(ui_evidence_delete, SIGNAL(clicked()), this, - SLOT(on_evidence_delete_clicked())); - connect(ui_evidence_image_name, SIGNAL(returnPressed()), this, - SLOT(on_evidence_image_name_edited())); - connect(ui_evidence_image_button, SIGNAL(clicked()), this, - SLOT(on_evidence_image_button_clicked())); - connect(ui_evidence_x, SIGNAL(clicked()), this, - SLOT(on_evidence_x_clicked())); - - ui_evidence->hide(); } void Courtroom::set_evidence_list(QVector<evi_type> &p_evi_list) { + global_evidence_list = p_evi_list; + if (!current_evidence_global) + return; // We're on private evidence editing, wait for user to do their + // thing + + QVector<evi_type> old_list = local_evidence_list; local_evidence_list.clear(); local_evidence_list = p_evi_list; set_evidence_page(); + + if (ui_evidence_overlay + ->isVisible()) // Update the currently edited evidence for this user + { + if (current_evidence >= local_evidence_list.size()) { + evidence_close(); + ui_evidence_name->setText(""); + } + else if (ui_evidence_description->isReadOnly()) // We haven't double clicked + // to edit it or anything + { + on_evidence_double_clicked(current_evidence); + } + // Todo: make a function that compares two pieces of evidence for any + // differences + else if (compare_evidence_changed( + old_list.at(current_evidence), + local_evidence_list.at(current_evidence))) { + QMessageBox *msgBox = new QMessageBox; + + msgBox->setText("The piece of evidence you've been editing has changed."); + msgBox->setInformativeText("Do you wish to keep your changes?"); + msgBox->setDetailedText( + "Name: " + local_evidence_list.at(current_evidence).name + + "\nImage: " + local_evidence_list.at(current_evidence).image + + "\nDescription:\n" + + local_evidence_list.at(current_evidence).description); + msgBox->setStandardButtons(QMessageBox::Yes | QMessageBox::No); + msgBox->setDefaultButton(QMessageBox::LastButton); + // msgBox->setWindowModality(Qt::NonModal); + int ret = msgBox->exec(); + switch (ret) { + case QMessageBox::Yes: + // "Keep changes" + break; + case QMessageBox::No: + // "Discard changes and keep theirs" + on_evidence_double_clicked(current_evidence); + break; + default: + // should never be reached + break; + } + } + } } void Courtroom::set_evidence_page() @@ -114,7 +295,7 @@ void Courtroom::set_evidence_page() ui_evidence_right->hide(); for (AOEvidenceButton *i_button : ui_evidence_list) { - i_button->reset(); + i_button->hide(); } // to account for the "add evidence" button @@ -147,17 +328,21 @@ void Courtroom::set_evidence_page() AOEvidenceButton *f_evidence_button = ui_evidence_list.at(n_evidence_button); - // ie. the add evidence button - if (n_real_evidence == (total_evidence - 1)) + f_evidence_button->set_selected(false); + f_evidence_button->setToolTip(""); + if (n_real_evidence == (total_evidence - 1)) { f_evidence_button->set_theme_image("addevidence.png"); + } else if (n_real_evidence < (total_evidence - 1)) { f_evidence_button->set_image( local_evidence_list.at(n_real_evidence).image); if (n_real_evidence == current_evidence) f_evidence_button->set_selected(true); - else - f_evidence_button->set_selected(false); + + f_evidence_button->setToolTip( + QString::number(n_real_evidence + 1) + ": " + + local_evidence_list.at(n_real_evidence).name); } else f_evidence_button->set_image(""); @@ -168,36 +353,31 @@ void Courtroom::set_evidence_page() void Courtroom::on_evidence_name_edited() { + ui_evidence_name->setReadOnly(true); if (current_evidence >= local_evidence_list.size()) return; +} - QStringList f_contents; - - evi_type f_evi = local_evidence_list.at(current_evidence); - - f_contents.append(QString::number(current_evidence)); - f_contents.append(ui_evidence_name->text()); - f_contents.append(f_evi.description); - f_contents.append(f_evi.image); +void Courtroom::on_evidence_name_double_clicked() +{ + if (ui_evidence_overlay->isVisible()) { + ui_evidence_name->setReadOnly(false); + } + else { + ui_evidence_name->setReadOnly(true); + } +} - ao_app->send_server_packet(new AOPacket("EE", f_contents)); +void Courtroom::on_evidence_image_name_double_clicked() +{ + ui_evidence_image_name->setReadOnly(false); } void Courtroom::on_evidence_image_name_edited() { + ui_evidence_image_name->setReadOnly(true); if (current_evidence >= local_evidence_list.size()) return; - - QStringList f_contents; - - evi_type f_evi = local_evidence_list.at(current_evidence); - - f_contents.append(QString::number(current_evidence)); - f_contents.append(f_evi.name); - f_contents.append(f_evi.description); - f_contents.append(ui_evidence_image_name->text()); - - ao_app->send_server_packet(new AOPacket("EE", f_contents)); } void Courtroom::on_evidence_image_button_clicked() @@ -230,8 +410,19 @@ void Courtroom::on_evidence_clicked(int p_id) int f_real_id = p_id + max_evidence_on_page * current_evidence_page; if (f_real_id == local_evidence_list.size()) { - ao_app->send_server_packet( - new AOPacket("PE#<name>#<description>#empty.png#%")); + if (current_evidence_global) + ao_app->send_server_packet( + new AOPacket("PE#<name>#<description>#empty.png#%")); + else { + evi_type f_evi; + f_evi.name = "<name>"; + f_evi.description = "<description>"; + f_evi.image = "empty.png"; + + local_evidence_list.append(f_evi); + private_evidence_list = local_evidence_list; + set_evidence_page(); + } return; } else if (f_real_id > local_evidence_list.size()) @@ -246,7 +437,7 @@ void Courtroom::on_evidence_clicked(int p_id) current_evidence = f_real_id; - ui_ic_chat_message->setFocus(); + // ui_ic_chat_message->setFocus(); } void Courtroom::on_evidence_double_clicked(int p_id) @@ -262,10 +453,18 @@ void Courtroom::on_evidence_double_clicked(int p_id) ui_evidence_description->clear(); ui_evidence_description->appendPlainText(f_evi.description); + ui_evidence_description->setReadOnly(true); + ui_evidence_description->setToolTip("Double-click to edit..."); + ui_evidence_name->setText(f_evi.name); + ui_evidence_name->setReadOnly(true); + ui_evidence_name->setToolTip("Double-click to edit..."); ui_evidence_image_name->setText(f_evi.image); + ui_evidence_image_name->setReadOnly(true); + ui_evidence_image_name->setToolTip("Double-click to edit..."); ui_evidence_overlay->show(); + ui_evidence_ok->hide(); ui_ic_chat_message->setFocus(); } @@ -307,10 +506,15 @@ void Courtroom::on_evidence_right_clicked() void Courtroom::on_evidence_present_clicked() { + if (!current_evidence_global) { + ui_evidence_present->hide(); + is_presenting_evidence = false; + return; // otherwise we get force-disconnected + } if (is_presenting_evidence) - ui_evidence_present->set_image("present_disabled.png"); + ui_evidence_present->set_image("present"); else - ui_evidence_present->set_image("present.png"); + ui_evidence_present->set_image("present_disabled"); is_presenting_evidence = !is_presenting_evidence; @@ -319,11 +523,15 @@ void Courtroom::on_evidence_present_clicked() void Courtroom::on_evidence_delete_clicked() { - ui_evidence_description->setReadOnly(true); - ui_evidence_overlay->hide(); - - ao_app->send_server_packet( - new AOPacket("DE#" + QString::number(current_evidence) + "#%")); + evidence_close(); + if (current_evidence_global) + ao_app->send_server_packet( + new AOPacket("DE#" + QString::number(current_evidence) + "#%")); + else { + local_evidence_list.remove(current_evidence); + private_evidence_list = local_evidence_list; + set_evidence_page(); + } current_evidence = 0; @@ -332,22 +540,238 @@ void Courtroom::on_evidence_delete_clicked() void Courtroom::on_evidence_x_clicked() { + if (current_evidence >= + local_evidence_list.size()) // Should never happen but you never know. + return; + + evi_type fake_evidence; + fake_evidence.name = ui_evidence_name->text(); + fake_evidence.description = ui_evidence_description->toPlainText(); + fake_evidence.image = ui_evidence_image_name->text(); + if (!compare_evidence_changed(fake_evidence, + local_evidence_list.at(current_evidence))) { + evidence_close(); + return; + } + QMessageBox *msgBox = new QMessageBox; + msgBox->setText("Evidence has been modified."); + msgBox->setInformativeText("Do you want to save your changes?"); + msgBox->setStandardButtons(QMessageBox::Save | QMessageBox::Discard | + QMessageBox::Cancel); + msgBox->setDefaultButton(QMessageBox::Save); + int ret = msgBox->exec(); + switch (ret) { + case QMessageBox::Save: + evidence_close(); + on_evidence_ok_clicked(); + break; + case QMessageBox::Discard: + evidence_close(); + break; + case QMessageBox::Cancel: + // Cancel was clicked, do nothing + break; + default: + // should never be reached + break; + } +} + +void Courtroom::on_evidence_ok_clicked() +{ + ui_evidence_name->setReadOnly(true); ui_evidence_description->setReadOnly(true); - ui_evidence_overlay->hide(); + ui_evidence_image_name->setReadOnly(true); + if (current_evidence < local_evidence_list.size()) { + evi_type f_evi = local_evidence_list.at(current_evidence); + if (current_evidence_global) { + QStringList f_contents; + f_contents.append(QString::number(current_evidence)); + f_contents.append(ui_evidence_name->text()); + f_contents.append(ui_evidence_description->toPlainText()); + f_contents.append(ui_evidence_image_name->text()); + + ao_app->send_server_packet(new AOPacket("EE", f_contents)); + } + else { + f_evi.name = ui_evidence_name->text(); + f_evi.description = ui_evidence_description->toPlainText(); + f_evi.image = ui_evidence_image_name->text(); + local_evidence_list.replace(current_evidence, f_evi); + private_evidence_list = local_evidence_list; + ui_evidence_ok->hide(); + set_evidence_page(); + } + } +} + +void Courtroom::on_evidence_switch_clicked() +{ + evidence_close(); + evidence_switch(!current_evidence_global); + if (current_evidence_global) { + ui_evidence_switch->set_image("evidence_global"); + ui_evidence->set_image("evidence_background"); + ui_evidence_overlay->set_image("evidence_overlay"); + ui_evidence_transfer->set_image("evidence_transfer"); + ui_evidence_transfer->setToolTip( + tr("Transfer evidence to private inventory.")); + ui_evidence_switch->setToolTip( + tr("Current evidence is global. Click to switch to private.")); + } + else { + ui_evidence_switch->set_image("evidence_private"); + ui_evidence->set_image("evidence_background_private"); + ui_evidence_overlay->set_image("evidence_overlay_private"); + ui_evidence_transfer->set_image("evidence_transfer_private"); + ui_evidence_transfer->setToolTip( + tr("Transfer evidence to global inventory.")); + ui_evidence_switch->setToolTip( + tr("Current evidence is private. Click to switch to global.")); + } +} +void Courtroom::on_evidence_transfer_clicked() +{ if (current_evidence >= local_evidence_list.size()) return; - QStringList f_contents; + QString name; + if (!current_evidence_global) // Transfer private evidence to global + { + evi_type f_evi = local_evidence_list.at(current_evidence); - evi_type f_evi = local_evidence_list.at(current_evidence); + QStringList f_contents; + f_contents.append(f_evi.name); + f_contents.append(f_evi.description); + f_contents.append(f_evi.image); - f_contents.append(QString::number(current_evidence)); - f_contents.append(f_evi.name); - f_contents.append(ui_evidence_description->toPlainText()); - f_contents.append(f_evi.image); + name = f_evi.name; + ao_app->send_server_packet(new AOPacket("PE", f_contents)); + } + else // Transfer global evidence to private + { + evi_type f_evi = local_evidence_list.at(current_evidence); + name = f_evi.name; + private_evidence_list.append(f_evi); + } + + QMessageBox *msgBox = new QMessageBox; + msgBox->setText("\"" + name + "\" has been transferred."); + msgBox->setStandardButtons(QMessageBox::Ok); + msgBox->setDefaultButton(QMessageBox::Ok); + msgBox->exec(); +} - ao_app->send_server_packet(new AOPacket("EE", f_contents)); +void Courtroom::on_evidence_edited() +{ + if (current_evidence >= + local_evidence_list.size()) // Should never happen but you never know. + return; + evi_type fake_evidence; + fake_evidence.name = ui_evidence_name->text(); + fake_evidence.description = ui_evidence_description->toPlainText(); + fake_evidence.image = ui_evidence_image_name->text(); + if (compare_evidence_changed(fake_evidence, + local_evidence_list.at(current_evidence))) + ui_evidence_ok->show(); + else + ui_evidence_ok->hide(); +} +void Courtroom::evidence_close() +{ + ui_evidence_description->setReadOnly(true); + ui_evidence_description->setToolTip(""); + ui_evidence_name->setReadOnly(true); + ui_evidence_name->setToolTip(""); + ui_evidence_image_name->setReadOnly(true); + ui_evidence_image_name->setToolTip(""); + ui_evidence_overlay->hide(); ui_ic_chat_message->setFocus(); } + +void Courtroom::evidence_switch(bool global) +{ + current_evidence_global = global; + is_presenting_evidence = false; + ui_evidence_present->set_image("present"); + local_evidence_list.clear(); + if (current_evidence_global) { + local_evidence_list = global_evidence_list; + ui_evidence_present->show(); + ui_evidence_save->hide(); + ui_evidence_load->hide(); + } + else { + local_evidence_list = private_evidence_list; + ui_evidence_present->hide(); + ui_evidence_save->show(); + ui_evidence_load->show(); + } + set_evidence_page(); +} + +void Courtroom::on_evidence_save_clicked() +{ + if (current_evidence_global) + return; // Don't allow saving/loading operations when in global inventory + // mode for now + + QString p_path = QFileDialog::getSaveFileName( + this, tr("Save Inventory"), "base/inventories/", tr("Ini Files (*.ini)")); + if (p_path.isEmpty()) + return; + + evidence_close(); + ui_evidence_name->setText(""); + + QSettings inventory(p_path, QSettings::IniFormat); + inventory.clear(); + for (int i = 0; i < local_evidence_list.size(); i++) { + inventory.beginGroup(QString::number(i)); + inventory.setValue("name", local_evidence_list[i].name); + inventory.setValue("description", local_evidence_list[i].description); + inventory.setValue("image", local_evidence_list[i].image); + inventory.endGroup(); + } + inventory.sync(); +} + +void Courtroom::on_evidence_load_clicked() +{ + if (current_evidence_global) + return; // Don't allow saving/loading operations when in global inventory + // mode for now + + QString p_path = QFileDialog::getOpenFileName( + this, tr("Open Inventory"), "base/inventories/", tr("Ini Files (*.ini)")); + if (p_path.isEmpty()) + return; + + evidence_close(); + ui_evidence_name->setText(""); + + QSettings inventory(p_path, QSettings::IniFormat); + local_evidence_list.clear(); + foreach (QString evi, inventory.childGroups()) { + if (evi == "General") + continue; + + evi_type f_evi; + f_evi.name = inventory.value(evi + "/name", "UNKNOWN").value<QString>(); + f_evi.description = + inventory.value(evi + "/description", "UNKNOWN").value<QString>(); + f_evi.image = + inventory.value(evi + "/image", "UNKNOWN.png").value<QString>(); + local_evidence_list.append(f_evi); + } + private_evidence_list = local_evidence_list; + set_evidence_page(); +} + +bool Courtroom::compare_evidence_changed(evi_type evi_a, evi_type evi_b) +{ + return evi_a.name != evi_b.name || evi_a.image != evi_b.image || + evi_a.description != evi_b.description; +} diff --git a/src/hardware_functions.cpp b/src/hardware_functions.cpp index 4dbc438..c898aef 100644 --- a/src/hardware_functions.cpp +++ b/src/hardware_functions.cpp @@ -51,8 +51,25 @@ QString get_hdid() #elif defined __APPLE__ QString get_hdid() { - // hdids are broken at this point anyways - return "just a mac passing by"; + CFStringRef serial; + char buffer[64] = {0}; + QString hdid; + io_service_t platformExpert = IOServiceGetMatchingService( + kIOMasterPortDefault, IOServiceMatching("IOPlatformExpertDevice")); + if (platformExpert) { + CFTypeRef serialNumberAsCFString = IORegistryEntryCreateCFProperty( + platformExpert, CFSTR(kIOPlatformSerialNumberKey), kCFAllocatorDefault, + 0); + if (serialNumberAsCFString) { + serial = (CFStringRef)serialNumberAsCFString; + } + if (CFStringGetCString(serial, buffer, 64, kCFStringEncodingUTF8)) { + hdid = buffer; + } + + IOObjectRelease(platformExpert); + } + return hdid; } #else diff --git a/src/lobby.cpp b/src/lobby.cpp index 79a37be..81e4086 100644 --- a/src/lobby.cpp +++ b/src/lobby.cpp @@ -20,9 +20,19 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() ui_connect = new AOButton(this, ao_app); ui_version = new QLabel(this); ui_about = new AOButton(this, ao_app); - ui_server_list = new QListWidget(this); + ui_settings = new AOButton(this, ao_app); + + ui_server_list = new QTreeWidget(this); + ui_server_list->setHeaderLabels({"#", "Name"}); //, "Players"}); + ui_server_list->hideColumn(0); + + ui_server_search = new QLineEdit(this); + ui_server_search->setFrame(false); + ui_server_search->setPlaceholderText(tr("Search")); + ui_player_count = new QLabel(this); ui_description = new AOTextArea(this); + ui_description->setOpenExternalLinks(true); ui_chatbox = new AOTextArea(this); ui_chatbox->setOpenExternalLinks(true); ui_chatname = new QLineEdit(this); @@ -49,8 +59,13 @@ Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() connect(ui_connect, SIGNAL(pressed()), this, SLOT(on_connect_pressed())); connect(ui_connect, SIGNAL(released()), this, SLOT(on_connect_released())); connect(ui_about, SIGNAL(clicked()), this, SLOT(on_about_clicked())); - connect(ui_server_list, SIGNAL(clicked(QModelIndex)), this, - SLOT(on_server_list_clicked(QModelIndex))); + connect(ui_settings, SIGNAL(clicked()), this, SLOT(on_settings_clicked())); + connect(ui_server_list, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, + SLOT(on_server_list_clicked(QTreeWidgetItem *, int))); + connect(ui_server_list, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), + this, SLOT(on_server_list_doubleclicked(QTreeWidgetItem *, int))); + connect(ui_server_search, SIGNAL(textChanged(QString)), this, + SLOT(on_server_search_edited(QString))); connect(ui_chatmessage, SIGNAL(returnPressed()), this, SLOT(on_chatfield_return_pressed())); connect(ui_cancel, SIGNAL(clicked()), ao_app, SLOT(loading_cancelled())); @@ -75,9 +90,10 @@ void Lobby::set_widgets() qDebug() << "W: did not find lobby width or height in " << filename; // Most common symptom of bad config files and missing assets. - call_notice("It doesn't look like your client is set up correctly.\n" - "Did you download all resources correctly from tiny.cc/getao, " - "including the large 'base' folder?"); + call_notice( + tr("It doesn't look like your client is set up correctly.\n" + "Did you download all resources correctly from tiny.cc/getao, " + "including the large 'base' folder?")); this->resize(517, 666); } @@ -86,33 +102,42 @@ void Lobby::set_widgets() } set_size_and_pos(ui_background, "lobby"); - ui_background->set_image("lobbybackground.png"); + ui_background->set_image("lobbybackground"); set_size_and_pos(ui_public_servers, "public_servers"); - ui_public_servers->set_image("publicservers_selected.png"); + ui_public_servers->set_image("publicservers_selected"); set_size_and_pos(ui_favorites, "favorites"); - ui_favorites->set_image("favorites.png"); + ui_favorites->set_image("favorites"); set_size_and_pos(ui_refresh, "refresh"); - ui_refresh->set_image("refresh.png"); + ui_refresh->set_image("refresh"); set_size_and_pos(ui_add_to_fav, "add_to_fav"); - ui_add_to_fav->set_image("addtofav.png"); + ui_add_to_fav->set_image("addtofav"); set_size_and_pos(ui_connect, "connect"); - ui_connect->set_image("connect.png"); + ui_connect->set_image("connect"); set_size_and_pos(ui_version, "version"); - ui_version->setText("Version: " + ao_app->get_version_string()); + ui_version->setText(tr("Version: KFO%1").arg(ao_app->get_version_string())); set_size_and_pos(ui_about, "about"); - ui_about->set_image("about.png"); + ui_about->set_image("about"); + + set_size_and_pos(ui_settings, "settings"); + ui_settings->setText(tr("Settings")); + ui_settings->set_image("settings"); + ui_settings->setToolTip( + tr("Allows you to change various aspects of the client.")); set_size_and_pos(ui_server_list, "server_list"); ui_server_list->setStyleSheet("background-color: rgba(0, 0, 0, 0);" "font: bold;"); + set_size_and_pos(ui_server_search, "server_search"); + ui_server_search->setStyleSheet("background-color: rgba(0, 0, 0, 0);"); + set_size_and_pos(ui_player_count, "player_count"); ui_player_count->setText("Offline"); ui_player_count->setStyleSheet("font: bold;" @@ -139,7 +164,7 @@ void Lobby::set_widgets() "selection-background-color: rgba(0, 0, 0, 0);"); ui_loading_background->resize(this->width(), this->height()); - ui_loading_background->set_image("loadingbackground.png"); + ui_loading_background->set_image("loadingbackground"); set_size_and_pos(ui_loading_text, "loading_label"); ui_loading_text->setFont(QFont("Arial", 20, QFont::Bold)); @@ -155,6 +180,9 @@ void Lobby::set_widgets() ui_cancel->setText("Cancel"); ui_loading_background->hide(); + + set_fonts(); + set_stylesheets(); } void Lobby::set_size_and_pos(QWidget *p_widget, QString p_identifier) @@ -174,6 +202,71 @@ void Lobby::set_size_and_pos(QWidget *p_widget, QString p_identifier) } } +void Lobby::set_fonts() +{ + set_font(ui_player_count, "player_count"); + set_font(ui_description, "description"); + set_font(ui_chatbox, "chatbox"); + set_font(ui_chatname, "chatname"); + set_font(ui_chatmessage, "chatmessage"); + set_font(ui_loading_text, "loading_text"); + set_font(ui_server_list, "server_list"); +} + +void Lobby::set_stylesheet(QWidget *widget, QString target_tag) +{ + QString f_file = "lobby_stylesheets.css"; + QString style_sheet_string = + ao_app->get_tagged_stylesheet(target_tag, f_file); + if (style_sheet_string != "") + widget->setStyleSheet(style_sheet_string); +} + +void Lobby::set_stylesheets() +{ + set_stylesheet(ui_player_count, "[PLAYER COUNT]"); + set_stylesheet(ui_description, "[DESCRIPTION]"); + set_stylesheet(ui_chatbox, "[CHAT BOX]"); + set_stylesheet(ui_chatname, "[CHAT NAME]"); + set_stylesheet(ui_chatmessage, "[CHAT MESSAGE]"); + set_stylesheet(ui_loading_text, "[LOADING TEXT]"); + set_stylesheet(ui_server_list, "[SERVER LIST]"); +} + +void Lobby::set_font(QWidget *widget, QString p_identifier) +{ + QString design_file = "lobby_fonts.ini"; + int f_weight = ao_app->get_font_size(p_identifier, design_file); + QString class_name = widget->metaObject()->className(); + QString font_name = + ao_app->get_font_name("font_" + p_identifier, design_file); + QFont font(font_name, f_weight); + bool use = ao_app->get_font_size("use_custom_fonts", design_file) == 1; + if (use) { + widget->setFont(font); + QColor f_color = ao_app->get_color(p_identifier + "_color", design_file); + bool bold = ao_app->get_font_size(p_identifier + "_bold", design_file) == + 1; // is the font bold or not? + bool center = + ao_app->get_font_size(p_identifier + "_center", design_file) == + 1; // should it be centered? + QString is_bold = ""; + if (bold) + is_bold = "bold"; + QString is_center = ""; + if (center) + is_center = "qproperty-alignment: AlignCenter;"; + QString style_sheet_string = + class_name + " { background-color: rgba(0, 0, 0, 0);\n" + + "color: rgba(" + QString::number(f_color.red()) + ", " + + QString::number(f_color.green()) + ", " + + QString::number(f_color.blue()) + ", 255);\n" + is_center + "\n" + + "font: " + is_bold + "; }"; + widget->setStyleSheet(style_sheet_string); + } + return; +} + void Lobby::set_loading_text(QString p_text) { ui_loading_text->clear(); @@ -188,7 +281,10 @@ QString Lobby::get_chatlog() return return_value; } -int Lobby::get_selected_server() { return ui_server_list->currentRow(); } +int Lobby::get_selected_server() +{ + return ui_server_list->currentItem()->text(0).toInt(); +} void Lobby::set_loading_value(int p_value) { @@ -197,8 +293,8 @@ void Lobby::set_loading_value(int p_value) void Lobby::on_public_servers_clicked() { - ui_public_servers->set_image("publicservers_selected.png"); - ui_favorites->set_image("favorites.png"); + ui_public_servers->set_image("publicservers_selected"); + ui_favorites->set_image("favorites"); list_servers(); @@ -207,8 +303,8 @@ void Lobby::on_public_servers_clicked() void Lobby::on_favorites_clicked() { - ui_favorites->set_image("favorites_selected.png"); - ui_public_servers->set_image("publicservers.png"); + ui_favorites->set_image("favorites_selected"); + ui_public_servers->set_image("publicservers"); ao_app->set_favorite_list(); // ao_app->favorite_list = read_serverlist_txt(); @@ -218,14 +314,11 @@ void Lobby::on_favorites_clicked() public_servers_selected = false; } -void Lobby::on_refresh_pressed() -{ - ui_refresh->set_image("refresh_pressed.png"); -} +void Lobby::on_refresh_pressed() { ui_refresh->set_image("refresh_pressed"); } void Lobby::on_refresh_released() { - ui_refresh->set_image("refresh.png"); + ui_refresh->set_image("refresh"); AOPacket *f_packet = new AOPacket("ALL#%"); @@ -234,28 +327,25 @@ void Lobby::on_refresh_released() void Lobby::on_add_to_fav_pressed() { - ui_add_to_fav->set_image("addtofav_pressed.png"); + ui_add_to_fav->set_image("addtofav_pressed"); } void Lobby::on_add_to_fav_released() { - ui_add_to_fav->set_image("addtofav.png"); + ui_add_to_fav->set_image("addtofav"); // you cant add favorites from favorites m8 if (!public_servers_selected) return; - ao_app->add_favorite_server(ui_server_list->currentRow()); + ao_app->add_favorite_server(get_selected_server()); } -void Lobby::on_connect_pressed() -{ - ui_connect->set_image("connect_pressed.png"); -} +void Lobby::on_connect_pressed() { ui_connect->set_image("connect_pressed"); } void Lobby::on_connect_released() { - ui_connect->set_image("connect.png"); + ui_connect->set_image("connect"); AOPacket *f_packet; @@ -274,48 +364,91 @@ void Lobby::on_about_clicked() "https://github.com/AttorneyOnline/AO2-Client</a>" "<p><b>Major development:</b><br>" "OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter" + "<p><b>2.8 Major Release development:</b><br>" + "Crystalwarrior, Iamgoofball" + "<p><b>2.8 Quality Assurance:</b><br>" + "WillDean, Captain N, Mr M, Riel, Seimmet, Fury McFlurry," + "CedricDewitt, Chewable Tablets, Fantos, Futugaze," + "Geck, Minx, Pandae, Sierra, CrazyJC, CaseyMayCazy," + "GreenBowers, Robotic Overlord, Veritas, Gin-Gi" "<p><b>Special thanks:</b><br>" "Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), " "Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, " - "Noevain, Cronnicossy") + "Noevain, Cronnicossy, the AO2 community, server hosts, game masters," + "case makers, content creators and players!") .arg(ao_app->get_version_string()); QMessageBox::about(this, "About", msg); } -void Lobby::on_server_list_clicked(QModelIndex p_model) +void Lobby::on_settings_clicked() { ao_app->call_settings_menu(); } + +// clicked on an item in the serverlist +void Lobby::on_server_list_clicked(QTreeWidgetItem *p_item, int column) { - server_type f_server; - int n_server = p_model.row(); + column = 0; + if (p_item->text(column).toInt() != last_index) { + server_type f_server; + int n_server = p_item->text(column).toInt(); + last_index = n_server; - if (n_server < 0) - return; + if (n_server < 0) + return; - if (public_servers_selected) { - QVector<server_type> f_server_list = ao_app->get_server_list(); + if (public_servers_selected) { + QVector<server_type> f_server_list = ao_app->get_server_list(); - if (n_server >= f_server_list.size()) - return; + if (n_server >= f_server_list.size()) + return; - f_server = f_server_list.at(p_model.row()); - } - else { - if (n_server >= ao_app->get_favorite_list().size()) - return; + f_server = f_server_list.at(n_server); + } + else { + if (n_server >= ao_app->get_favorite_list().size()) + return; - f_server = ao_app->get_favorite_list().at(p_model.row()); - } + f_server = ao_app->get_favorite_list().at(n_server); + } - ui_description->clear(); - ui_description->append(f_server.desc); + ui_description->clear(); + ui_description->append_linked(f_server.desc); - ui_description->moveCursor(QTextCursor::Start); - ui_description->ensureCursorVisible(); + ui_description->moveCursor(QTextCursor::Start); + ui_description->ensureCursorVisible(); - ui_player_count->setText("Offline"); + ui_player_count->setText(tr("Offline")); - ui_connect->setEnabled(false); + ui_connect->setEnabled(false); - ao_app->net_manager->connect_to_server(f_server); + ao_app->net_manager->connect_to_server(f_server); + } +} + +// doubleclicked on an item in the serverlist so we'll connect right away +void Lobby::on_server_list_doubleclicked(QTreeWidgetItem *p_item, int column) +{ + on_server_list_clicked(p_item, column); + on_connect_released(); +} + +void Lobby::on_server_search_edited(QString p_text) +{ + // Iterate through all QTreeWidgetItem items + QTreeWidgetItemIterator it(ui_server_list); + while (*it) { + (*it)->setHidden(p_text != ""); + ++it; + } + + if (p_text != "") { + // Search in metadata + QList<QTreeWidgetItem *> clist = ui_server_list->findItems( + ui_server_search->text(), Qt::MatchContains | Qt::MatchRecursive, 1); + foreach (QTreeWidgetItem *item, clist) { + if (item->parent() != nullptr) // So the category shows up too + item->parent()->setHidden(false); + item->setHidden(false); + } + } } void Lobby::on_chatfield_return_pressed() @@ -337,23 +470,38 @@ void Lobby::on_chatfield_return_pressed() void Lobby::list_servers() { public_servers_selected = true; - ui_favorites->set_image("favorites.png"); - ui_public_servers->set_image("publicservers_selected.png"); + ui_favorites->set_image("favorites"); + ui_public_servers->set_image("publicservers_selected"); + ui_server_list->setSortingEnabled(false); ui_server_list->clear(); + ui_server_search->setText(""); + + int i = 0; for (server_type i_server : ao_app->get_server_list()) { - ui_server_list->addItem(i_server.name); + QTreeWidgetItem *treeItem = new QTreeWidgetItem(ui_server_list); + treeItem->setText(0, QString::number(i)); + treeItem->setText(1, i_server.name); + i++; } + ui_server_list->setSortingEnabled(true); } void Lobby::list_favorites() { + ui_server_list->setSortingEnabled(false); ui_server_list->clear(); + int i = 0; for (server_type i_server : ao_app->get_favorite_list()) { - ui_server_list->addItem(i_server.name); + QTreeWidgetItem *treeItem = new QTreeWidgetItem(ui_server_list); + treeItem->setText(0, QString::number(i)); + treeItem->setText(1, i_server.name); + // treeItem->setText(2, "-"); + i++; } + ui_server_list->setSortingEnabled(true); } void Lobby::append_chatmessage(QString f_name, QString f_message) @@ -370,8 +518,9 @@ void Lobby::append_error(QString f_message) void Lobby::set_player_count(int players_online, int max_players) { - QString f_string = "Online: " + QString::number(players_online) + "/" + - QString::number(max_players); + QString f_string = tr("Online: %1/%2") + .arg(QString::number(players_online)) + .arg(QString::number(max_players)); ui_player_count->setText(f_string); } diff --git a/src/main.cpp b/src/main.cpp index 1caa394..364377b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,7 +6,10 @@ #include "lobby.h" #include "networkmanager.h" #include <QDebug> +#include <QLibraryInfo> #include <QPluginLoader> +#include <QTranslator> + int main(int argc, char *argv[]) { #if QT_VERSION > QT_VERSION_CHECK(5, 6, 0) @@ -17,6 +20,32 @@ int main(int argc, char *argv[]) #endif AOApplication main_app(argc, argv); + + QSettings *configini = main_app.configini; + + QPluginLoader apngPlugin("qapng"); + if (!apngPlugin.load()) + qCritical() << "QApng plugin could not be loaded"; + + QPluginLoader webpPlugin("qwebp"); + if (!webpPlugin.load()) + qCritical() << "QWebp plugin could not be loaded"; + + QString p_language = + configini->value("language", QLocale::system().name()).toString(); + if (p_language == " " || p_language == "") + p_language = QLocale::system().name(); + + QTranslator qtTranslator; + qtTranslator.load("qt_" + p_language, + QLibraryInfo::location(QLibraryInfo::TranslationsPath)); + main_app.installTranslator(&qtTranslator); + + QTranslator appTranslator; + qDebug() << ":/resource/translations/ao_" + p_language; + appTranslator.load("ao_" + p_language, ":/resource/translations/"); + main_app.installTranslator(&appTranslator); + main_app.construct_lobby(); main_app.net_manager->connect_to_master(); main_app.w_lobby->show(); diff --git a/src/packet_distribution.cpp b/src/packet_distribution.cpp index baba0ea..a7bdbab 100644 --- a/src/packet_distribution.cpp +++ b/src/packet_distribution.cpp @@ -91,14 +91,9 @@ void AOApplication::ms_packet_received(AOPacket *p_packet) } } - call_notice("Outdated version! Your version: " + get_version_string() + - "\nPlease go to aceattorneyonline.com to update."); - destruct_courtroom(); - destruct_lobby(); - } - else if (header == "DOOM") { - call_notice("You have been exiled from AO." - "Have a nice day."); + call_notice(tr("Outdated version! Your version: %1\n" + "Please go to aceattorneyonline.com to update.") + .arg(get_version_string())); destruct_courtroom(); destruct_lobby(); } @@ -141,6 +136,9 @@ void AOApplication::server_packet_received(AOPacket *p_packet) arup_enabled = false; casing_alerts_enabled = false; modcall_reason_enabled = false; + looping_sfx_support_enabled = false; + additive_enabled = false; + effects_enabled = false; // workaround for tsuserver4 if (f_contents.at(0) == "NOENCRYPT") @@ -177,6 +175,8 @@ void AOApplication::server_packet_received(AOPacket *p_packet) else if (header == "FL") { if (f_packet.contains("yellowtext", Qt::CaseInsensitive)) yellow_text_enabled = true; + if (f_packet.contains("prezoom", Qt::CaseInsensitive)) + prezoom_enabled = true; if (f_packet.contains("flipping", Qt::CaseInsensitive)) flipping_enabled = true; if (f_packet.contains("customobjections", Qt::CaseInsensitive)) @@ -197,8 +197,12 @@ void AOApplication::server_packet_received(AOPacket *p_packet) casing_alerts_enabled = true; if (f_packet.contains("modcall_reason", Qt::CaseInsensitive)) modcall_reason_enabled = true; - - w_lobby->enable_connect_button(); + if (f_packet.contains("looping_sfx", Qt::CaseInsensitive)) + looping_sfx_support_enabled = true; + if (f_packet.contains("additive", Qt::CaseInsensitive)) + additive_enabled = true; + if (f_packet.contains("effects", Qt::CaseInsensitive)) + effects_enabled = true; } else if (header == "PN") { if (f_contents.size() < 2) @@ -236,7 +240,9 @@ void AOApplication::server_packet_received(AOPacket *p_packet) if (selected_server >= 0 && selected_server < server_list.size()) { auto info = server_list.at(selected_server); server_name = info.name; - server_address = QString("%1:%2").arg(info.ip, info.port); + server_address = + QString("%1:%2").arg(info.ip, QString::number(info.port)); + qDebug() << server_address; window_title += ": " + server_name; } } @@ -244,7 +250,9 @@ void AOApplication::server_packet_received(AOPacket *p_packet) if (selected_server >= 0 && selected_server < favorite_list.size()) { auto info = favorite_list.at(selected_server); server_name = info.name; - server_address = info.ip + info.port; + server_address = + QString("%1:%2").arg(info.ip, QString::number(info.port)); + qDebug() << server_address; window_title += ": " + server_name; } } @@ -264,6 +272,15 @@ void AOApplication::server_packet_received(AOPacket *p_packet) send_server_packet(f_packet); + // Remove any characters not accepted in folder names for the server_name + // here + this->log_filename = QDateTime::currentDateTime().toUTC().toString( + "'logs/" + server_name.remove(QRegExp("[\\\\/:*?\"<>|\']")) + + "/'ddd MMMM yyyy hh.mm.ss t'.log'"); + this->write_to_file("Joined server " + server_name + " on address " + + server_address + " on " + + QDateTime::currentDateTime().toUTC().toString(), + log_filename, true); QCryptographicHash hash(QCryptographicHash::Algorithm::Sha256); hash.addData(server_address.toUtf8()); if (is_discord_enabled()) @@ -343,9 +360,9 @@ void AOApplication::server_packet_received(AOPacket *p_packet) ++loaded_evidence; - w_lobby->set_loading_text("Loading evidence:\n" + - QString::number(loaded_evidence) + "/" + - QString::number(evidence_list_size)); + 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); @@ -378,9 +395,9 @@ void AOApplication::server_packet_received(AOPacket *p_packet) ++loaded_music; - w_lobby->set_loading_text("Loading music:\n" + - QString::number(loaded_music) + "/" + - QString::number(music_list_size)); + 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); @@ -445,9 +462,9 @@ void AOApplication::server_packet_received(AOPacket *p_packet) ++loaded_chars; - w_lobby->set_loading_text("Loading chars:\n" + - QString::number(loaded_chars) + "/" + - QString::number(char_list_size)); + 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); @@ -472,9 +489,9 @@ void AOApplication::server_packet_received(AOPacket *p_packet) for (int n_element = 0; n_element < f_contents.size(); ++n_element) { ++loaded_music; - w_lobby->set_loading_text("Loading music:\n" + - QString::number(loaded_music) + "/" + - QString::number(music_list_size)); + 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_contents.at(n_element)); @@ -511,6 +528,32 @@ void AOApplication::server_packet_received(AOPacket *p_packet) send_server_packet(new AOPacket("RD#%")); } + else if (header == "FM") // Fetch music ONLY + { + if (!courtroom_constructed) + goto end; + + w_courtroom->clear_music(); + + for (int n_element = 0; n_element < f_contents.size(); ++n_element) { + w_courtroom->append_music(f_contents.at(n_element)); + } + + w_courtroom->list_music(); + } + else if (header == "FA") // Fetch areas ONLY + { + if (!courtroom_constructed) + goto end; + + w_courtroom->clear_areas(); + + for (int n_element = 0; n_element < f_contents.size(); ++n_element) { + w_courtroom->append_area(f_contents.at(n_element)); + } + + w_courtroom->list_areas(); + } else if (header == "DONE") { if (!courtroom_constructed) goto end; @@ -529,8 +572,28 @@ void AOApplication::server_packet_received(AOPacket *p_packet) if (f_contents.size() < 1) goto end; - if (courtroom_constructed) - w_courtroom->set_background(f_contents.at(0)); + if (courtroom_constructed) { + if (f_contents.size() >= + 2) // We have a pos included in the background packet! + w_courtroom->set_side(f_contents.at(1)); + w_courtroom->set_background(f_contents.at(0), f_contents.size() >= 2); + } + } + else if (header == "SP") { + if (f_contents.size() < 1) + goto end; + + if (courtroom_constructed) // We were sent a "set position" packet + { + w_courtroom->set_side(f_contents.at(0)); + } + } + else if (header == "SD") // Send pos dropdown + { + if (f_contents.size() < 1) + goto end; + + w_courtroom->set_pos_dropdown(f_contents.at(0).split("*")); } // server accepting char request(CC) packet else if (header == "PV") { @@ -538,7 +601,7 @@ void AOApplication::server_packet_received(AOPacket *p_packet) goto end; if (courtroom_constructed) - w_courtroom->enter_courtroom(f_contents.at(2).toInt()); + w_courtroom->update_character(f_contents.at(2).toInt()); } else if (header == "MS") { if (courtroom_constructed && courtroom_loaded) @@ -608,22 +671,23 @@ void AOApplication::server_packet_received(AOPacket *p_packet) } else if (header == "KK") { if (courtroom_constructed && f_contents.size() >= 1) { - call_notice("You have been kicked from the server.\nReason: " + - f_contents.at(0)); + call_notice(tr("You have been kicked from the server.\nReason: %1") + .arg(f_contents.at(0))); construct_lobby(); destruct_courtroom(); } } else if (header == "KB") { if (courtroom_constructed && f_contents.size() >= 1) { - call_notice("You have been banned from the server.\nReason: " + - f_contents.at(0)); + call_notice(tr("You have been banned from the server.\nReason: %1") + .arg(f_contents.at(0))); construct_lobby(); destruct_courtroom(); } } else if (header == "BD") { - call_notice("You are banned on this server.\nReason: " + f_contents.at(0)); + call_notice( + tr("You are banned on this server.\nReason: %1").arg(f_contents.at(0))); } else if (header == "ZZ") { if (courtroom_constructed && f_contents.size() > 0) diff --git a/src/scrolltext.cpp b/src/scrolltext.cpp new file mode 100644 index 0000000..afd0aab --- /dev/null +++ b/src/scrolltext.cpp @@ -0,0 +1,125 @@ +#include "scrolltext.h" + +ScrollText::ScrollText(QWidget *parent) : QWidget(parent), scrollPos(0) +{ + staticText.setTextFormat(Qt::PlainText); + + // setFixedHeight(fontMetrics().height()*2); //The theme sets this + leftMargin = height() / 3; + + setSeparator(" --- "); + + connect(&timer, SIGNAL(timeout()), this, SLOT(timer_timeout())); + timer.setInterval(50); +} + +QString ScrollText::text() const { return _text; } + +void ScrollText::setText(QString text) +{ + _text = text; + updateText(); + update(); +} + +QString ScrollText::separator() const { return _separator; } + +void ScrollText::setSeparator(QString separator) +{ + _separator = separator; + updateText(); + update(); +} + +void ScrollText::updateText() +{ + timer.stop(); + + singleTextWidth = fontMetrics().horizontalAdvance(_text); + scrollEnabled = (singleTextWidth > width() - leftMargin * 2); + + if (scrollEnabled) { + scrollPos = -64; + staticText.setText(_text + _separator); + timer.start(); + } + else + staticText.setText(_text); + + staticText.prepare(QTransform(), font()); + wholeTextSize = QSize(fontMetrics().horizontalAdvance(staticText.text()), + fontMetrics().height()); +} + +void ScrollText::paintEvent(QPaintEvent *) +{ + QPainter p(this); + + if (scrollEnabled) { + buffer.fill(qRgba(0, 0, 0, 0)); + QPainter pb(&buffer); + pb.setPen(p.pen()); + pb.setFont(p.font()); + + int x = qMin(-scrollPos, 0) + leftMargin; + while (x < width()) { + pb.drawStaticText(QPointF(x, (height() - wholeTextSize.height()) / 2), + staticText); + x += wholeTextSize.width(); + } + + // Apply Alpha Channel + pb.setCompositionMode(QPainter::CompositionMode_DestinationIn); + pb.setClipRect(width() - 15, 0, 15, height()); + pb.drawImage(0, 0, alphaChannel); + pb.setClipRect(0, 0, 15, height()); + // initial situation: don't apply alpha channel in the left half of the + // image at all; apply it more and more until scrollPos gets positive + if (scrollPos < 0) + pb.setOpacity(static_cast<qreal>((qMax(-8, scrollPos) + 8) / 8.0)); + pb.drawImage(0, 0, alphaChannel); + + // pb.end(); + p.drawImage(0, 0, buffer); + } + else { + p.drawStaticText( + QPointF(leftMargin, (height() - wholeTextSize.height()) / 2), + staticText); + } +} + +void ScrollText::resizeEvent(QResizeEvent *) +{ + // When the widget is resized, we need to update the alpha channel. + + alphaChannel = QImage(size(), QImage::Format_ARGB32_Premultiplied); + buffer = QImage(size(), QImage::Format_ARGB32_Premultiplied); + + // Create Alpha Channel: + if (width() > 64) { + // create first scanline + QRgb *scanline1 = reinterpret_cast<QRgb *>(alphaChannel.scanLine(0)); + for (int x = 1; x < 16; ++x) + scanline1[x - 1] = scanline1[width() - x] = qRgba(0, 0, 0, x << 4); + for (int x = 15; x < width() - 15; ++x) + scanline1[x] = qRgb(0, 0, 0); + // copy scanline to the other ones + for (int y = 1; y < height(); ++y) + memcpy(alphaChannel.scanLine(y), scanline1, + static_cast<uint>(width() * 4)); + } + else + alphaChannel.fill(qRgb(0, 0, 0)); + + // Update scrolling state + bool newScrollEnabled = (singleTextWidth > width() - leftMargin); + if (newScrollEnabled != scrollEnabled) + updateText(); +} + +void ScrollText::timer_timeout() +{ + scrollPos = (scrollPos + 2) % wholeTextSize.width(); + update(); +} diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp index 18f0e4c..1689cd3 100644 --- a/src/text_file_functions.cpp +++ b/src/text_file_functions.cpp @@ -8,7 +8,7 @@ QString AOApplication::read_theme() int AOApplication::read_blip_rate() { - int result = configini->value("blip_rate", 1).toInt(); + int result = configini->value("blip_rate", 2).toInt(); if (result < 1) return 1; @@ -49,7 +49,7 @@ int AOApplication::get_max_log_size() bool AOApplication::get_log_goes_downwards() { QString result = - configini->value("log_goes_downwards", "false").value<QString>(); + configini->value("log_goes_downwards", "true").value<QString>(); return result.startsWith("true"); } @@ -78,16 +78,21 @@ QString AOApplication::get_audio_output_device() QStringList AOApplication::get_call_words() { + return get_list_file(get_base_path() + "callwords.ini"); +} + +QStringList AOApplication::get_list_file(QString p_file) +{ QStringList return_value; - QFile callwords_ini; + QFile p_ini; - callwords_ini.setFileName(get_base_path() + "callwords.ini"); + p_ini.setFileName(p_file); - if (!callwords_ini.open(QIODevice::ReadOnly)) + if (!p_ini.open(QIODevice::ReadOnly)) return return_value; - QTextStream in(&callwords_ini); + QTextStream in(&p_ini); while (!in.atEnd()) { QString line = in.readLine(); @@ -97,6 +102,71 @@ QStringList AOApplication::get_call_words() return return_value; } +QString AOApplication::read_file(QString filename) +{ + QFile f_log(filename); + + if (!f_log.open(QIODevice::ReadOnly | QIODevice::Text)) { + qDebug() << "Couldn't open" << filename; + return ""; + } + + QTextStream in(&f_log); + QString text = in.readAll(); + f_log.close(); + return text; +} + +bool AOApplication::write_to_file(QString p_text, QString p_file, bool make_dir) +{ + QString path = QFileInfo(p_file).path(); + if (make_dir) { + // Create the dir if it doesn't exist yet + QDir dir(path); + if (!dir.exists()) + if (!dir.mkpath(".")) + return false; + } + + QFile f_log(p_file); + if (f_log.open(QIODevice::WriteOnly | QIODevice::Text | + QIODevice::Truncate)) { + QTextStream out(&f_log); + + out << p_text; + + f_log.flush(); + f_log.close(); + return true; + } + return false; +} + +bool AOApplication::append_to_file(QString p_text, QString p_file, + bool make_dir) +{ + QString path = QFileInfo(p_file).path(); + // Create the dir if it doesn't exist yet + if (make_dir) { + QDir dir(path); + if (!dir.exists()) + if (!dir.mkpath(".")) + return false; + } + + QFile f_log(p_file); + if (f_log.open(QIODevice::WriteOnly | QIODevice::Append)) { + QTextStream out(&f_log); + + out << "\r\n" << p_text; + + f_log.flush(); + f_log.close(); + return true; + } + return false; +} + void AOApplication::write_to_serverlist_txt(QString p_line) { QFile serverlist_txt; @@ -192,11 +262,14 @@ QPoint AOApplication::get_button_spacing(QString p_identifier, QString p_file) } pos_size_type AOApplication::get_element_dimensions(QString p_identifier, - QString p_file) + QString p_file, + QString p_char) { + QString char_ini_path = + get_base_path() + "misc/" + get_chat(p_char) + "/" + p_file; QString design_ini_path = get_theme_path(p_file); QString default_path = get_default_theme_path(p_file); - QString f_result = read_design_ini(p_identifier, design_ini_path); + QString f_result = read_design_ini(p_identifier, char_ini_path); pos_size_type return_value; @@ -206,10 +279,13 @@ pos_size_type AOApplication::get_element_dimensions(QString p_identifier, return_value.height = -1; if (f_result == "") { - f_result = read_design_ini(p_identifier, default_path); + f_result = read_design_ini(p_identifier, design_ini_path); + if (f_result == "") { + f_result = read_design_ini(p_identifier, default_path); - if (f_result == "") - return return_value; + if (f_result == "") + return return_value; + } } QStringList sub_line_elements = f_result.split(","); @@ -224,6 +300,21 @@ pos_size_type AOApplication::get_element_dimensions(QString p_identifier, return return_value; } +QString AOApplication::get_design_element(QString p_identifier, QString p_file, + QString p_char) +{ + QString char_ini_path = + get_base_path() + "misc/" + get_chat(p_char) + "/" + p_file; + QString design_ini_path = get_theme_path(p_file); + QString default_path = get_default_theme_path(p_file); + QString f_result = read_design_ini(p_identifier, char_ini_path); + if (f_result == "") { + f_result = read_design_ini(p_identifier, design_ini_path); + if (f_result == "") + f_result = read_design_ini(p_identifier, default_path); + } + return f_result; +} QString AOApplication::get_font_name(QString p_identifier, QString p_file) { QString design_ini_path = get_theme_path(p_file); @@ -232,7 +323,7 @@ QString AOApplication::get_font_name(QString p_identifier, QString p_file) if (f_result == "") { f_result = read_design_ini(p_identifier, default_path); if (f_result == "") - return "Sans"; + return ""; } return f_result; } @@ -279,49 +370,121 @@ QColor AOApplication::get_color(QString p_identifier, QString p_file) return return_color; } -QColor AOApplication::get_chat_color(QString p_identifier, QString p_chat) +QString AOApplication::get_stylesheet(QString p_file) { - QColor return_color(255, 255, 255); + QString design_ini_path = get_theme_path(p_file); + QString default_path = get_default_theme_path(p_file); - if (p_identifier == "_inline_grey") { - return_color = QColor(187, 187, 187); + QFile design_ini; + + design_ini.setFileName(design_ini_path); + + if (!design_ini.open(QIODevice::ReadOnly)) { + design_ini.setFileName(default_path); + if (!design_ini.open(QIODevice::ReadOnly)) + return ""; } - else { - switch (p_identifier.toInt()) { - case 1: - return_color = QColor(0, 255, 0); - break; - case 2: - return_color = QColor(255, 0, 0); - break; - case 3: - return_color = QColor(255, 165, 0); - break; - case 4: - return_color = QColor(45, 150, 255); - break; - case 5: - return_color = QColor(255, 255, 0); - break; - case 7: - return_color = QColor(255, 192, 203); - break; - case 8: - return_color = QColor(0, 255, 255); - break; - case 0: - case 6: // 6 is rainbow. - default: - return_color = QColor(255, 255, 255); - break; + + QTextStream in(&design_ini); + + QString f_text; + + while (!in.atEnd()) { + f_text.append(in.readLine()); + } + + design_ini.close(); + return f_text; +} + +QString AOApplication::get_tagged_stylesheet(QString target_tag, QString p_file) +{ + QString design_ini_path = get_theme_path(p_file); + + QFile design_ini; + + design_ini.setFileName(design_ini_path); + + if (!design_ini.open(QIODevice::ReadOnly)) + return ""; + + QTextStream in(&design_ini); + + QString f_text; + + bool tag_found = false; + + while (!in.atEnd()) { + QString line = in.readLine(); + + if (line.startsWith(target_tag, Qt::CaseInsensitive)) { + tag_found = true; + continue; + } + + if (tag_found) { + if ((line.startsWith("[") && line.endsWith("]"))) + break; + f_text.append(line); } } - p_identifier = p_identifier.prepend("c"); + design_ini.close(); + return f_text; +} + +QString AOApplication::get_chat_markdown(QString p_identifier, QString p_chat) +{ QString design_ini_path = get_base_path() + "misc/" + p_chat + "/config.ini"; QString default_path = get_base_path() + "misc/default/config.ini"; QString f_result = read_design_ini(p_identifier, design_ini_path); + if (f_result == "") + f_result = read_design_ini(p_identifier, default_path); + + return f_result.toLatin1(); +} + +QColor AOApplication::get_chat_color(QString p_identifier, QString p_chat) +{ + QColor return_color(255, 255, 255); + + switch (p_identifier.toInt()) { + case 0: // White + return_color = QColor(255, 255, 255); + break; + case 1: // Green + return_color = QColor(0, 255, 0); + break; + case 2: // Red + return_color = QColor(255, 0, 0); + break; + case 3: // Orange + return_color = QColor(255, 165, 0); + break; + case 4: // Blue + return_color = QColor(45, 150, 255); + break; + case 5: // Yellow + return_color = QColor(255, 255, 0); + break; + case 6: // Pink + return_color = QColor(255, 192, 203); + break; + case 7: // Cyan + return_color = QColor(0, 255, 255); + break; + case 8: // Grey + return_color = QColor(187, 187, 187); + break; + default: + return_color = QColor(255, 255, 255); + break; + } + QString design_ini_path = get_base_path() + "misc/" + p_chat + "/config.ini"; + QString default_path = get_base_path() + "misc/default/config.ini"; + QString f_result = read_design_ini("c" + p_identifier, design_ini_path); + if (f_result == "") { f_result = read_design_ini(p_identifier, default_path); @@ -363,24 +526,35 @@ QString AOApplication::get_sfx(QString p_identifier) QString AOApplication::get_sfx_suffix(QString sound_to_check) { - QString mp3_check = get_sounds_path(sound_to_check + ".mp3"); - QString opus_check = get_sounds_path(sound_to_check + ".opus"); - if (file_exists(opus_check)) { + if (file_exists(sound_to_check)) + return sound_to_check; + if (file_exists(sound_to_check + ".opus")) return sound_to_check + ".opus"; - } - else if (file_exists(mp3_check)) { + if (file_exists(sound_to_check + ".ogg")) + return sound_to_check + ".ogg"; + if (file_exists(sound_to_check + ".mp3")) return sound_to_check + ".mp3"; - } + if (file_exists(sound_to_check + ".mp4")) + return sound_to_check + ".mp4"; return sound_to_check + ".wav"; } QString AOApplication::get_image_suffix(QString path_to_check) { - QString apng_check = path_to_check + ".apng"; - if (file_exists(apng_check)) { - return apng_check; - } - return path_to_check + ".gif"; + if (file_exists(path_to_check)) + return path_to_check; + if (file_exists(path_to_check + ".webp")) + return path_to_check + ".webp"; + if (file_exists(path_to_check + ".apng")) + return path_to_check + ".apng"; + if (file_exists(path_to_check + ".gif")) + return path_to_check + ".gif"; + return path_to_check + ".png"; +} + +QString AOApplication::get_static_image_suffix(QString path_to_check) +{ + return path_to_check + ".png"; } // returns whatever is to the right of "search_line =" within target_tag and @@ -397,14 +571,40 @@ QString AOApplication::read_char_ini(QString p_char, QString p_search_line, return value; } +void AOApplication::set_char_ini(QString p_char, QString value, + QString p_search_line, QString target_tag) +{ + QSettings settings(get_character_path(p_char, "char.ini"), + QSettings::IniFormat); + settings.beginGroup(target_tag); + settings.setValue(p_search_line, value); + settings.endGroup(); +} + +// returns all the values of target_tag +QStringList AOApplication::read_ini_tags(QString p_path, QString target_tag) +{ + QStringList r_values; + QSettings settings(p_path, QSettings::IniFormat); + if (!target_tag.isEmpty()) + settings.beginGroup(target_tag); + QStringList keys = settings.allKeys(); + foreach (QString key, keys) { + QString value = settings.value(key).toString(); + r_values << key + "=" + value; + } + if (!settings.group().isEmpty()) + settings.endGroup(); + return r_values; +} + QString AOApplication::get_char_name(QString p_char) { QString f_result = read_char_ini(p_char, "name", "Options"); if (f_result == "") return p_char; - else - return f_result; + return f_result; } QString AOApplication::get_showname(QString p_char) @@ -416,8 +616,7 @@ QString AOApplication::get_showname(QString p_char) return ""; if (f_result == "") return p_char; - else - return f_result; + return f_result; } QString AOApplication::get_char_side(QString p_char) @@ -426,8 +625,7 @@ QString AOApplication::get_char_side(QString p_char) if (f_result == "") return "wit"; - else - return f_result; + return f_result; } QString AOApplication::get_gender(QString p_char) @@ -435,9 +633,15 @@ QString AOApplication::get_gender(QString p_char) QString f_result = read_char_ini(p_char, "gender", "Options"); if (f_result == "") - return "male"; - else - return f_result; + return "sfx-blipmale"; + + if (!file_exists(get_sfx_suffix(get_sounds_path(f_result)))) { + if (file_exists(get_sfx_suffix(get_sounds_path("../blips/" + f_result)))) + return "../blips/" + f_result; // Return the cool kids variant + + return "sfx-blip" + f_result; // Return legacy variant + } + return f_result; } QString AOApplication::get_chat(QString p_char) @@ -449,13 +653,28 @@ QString AOApplication::get_chat(QString p_char) return f_result; } +QString AOApplication::get_chat_font(QString p_char) +{ + QString f_result = read_char_ini(p_char, "chat_font", "Options"); + + return f_result; +} + +int AOApplication::get_chat_size(QString p_char) +{ + QString f_result = read_char_ini(p_char, "chat_size", "Options"); + + if (f_result == "") + return -1; + return f_result.toInt(); +} + QString AOApplication::get_char_shouts(QString p_char) { QString f_result = read_char_ini(p_char, "shouts", "Options"); if (f_result == "") return "default"; - else - return f_result; + return f_result; } int AOApplication::get_preanim_duration(QString p_char, QString p_emote) @@ -464,8 +683,7 @@ int AOApplication::get_preanim_duration(QString p_char, QString p_emote) if (f_result == "") return -1; - else - return f_result.toInt(); + return f_result.toInt(); } int AOApplication::get_ao2_preanim_duration(QString p_char, QString p_emote) @@ -474,8 +692,7 @@ int AOApplication::get_ao2_preanim_duration(QString p_char, QString p_emote) if (f_result == "") return -1; - else - return f_result.toInt(); + return f_result.toInt(); } int AOApplication::get_emote_number(QString p_char) @@ -484,8 +701,7 @@ int AOApplication::get_emote_number(QString p_char) if (f_result == "") return 0; - else - return f_result.toInt(); + return f_result.toInt(); } QString AOApplication::get_emote_comment(QString p_char, int p_emote) @@ -499,8 +715,7 @@ QString AOApplication::get_emote_comment(QString p_char, int p_emote) qDebug() << "W: misformatted char.ini: " << p_char << ", " << p_emote; return "normal"; } - else - return result_contents.at(0); + return result_contents.at(0); } QString AOApplication::get_pre_emote(QString p_char, int p_emote) @@ -514,8 +729,7 @@ QString AOApplication::get_pre_emote(QString p_char, int p_emote) qDebug() << "W: misformatted char.ini: " << p_char << ", " << p_emote; return ""; } - else - return result_contents.at(1); + return result_contents.at(1); } QString AOApplication::get_emote(QString p_char, int p_emote) @@ -529,8 +743,7 @@ QString AOApplication::get_emote(QString p_char, int p_emote) qDebug() << "W: misformatted char.ini: " << p_char << ", " << p_emote; return "normal"; } - else - return result_contents.at(2); + return result_contents.at(2); } int AOApplication::get_emote_mod(QString p_char, int p_emote) @@ -545,8 +758,7 @@ int AOApplication::get_emote_mod(QString p_char, int p_emote) << QString::number(p_emote); return 0; } - else - return result_contents.at(3).toInt(); + return result_contents.at(3).toInt(); } int AOApplication::get_desk_mod(QString p_char, int p_emote) @@ -563,8 +775,7 @@ int AOApplication::get_desk_mod(QString p_char, int p_emote) if (string_result == "") return -1; - else - return string_result.toInt(); + return string_result.toInt(); } QString AOApplication::get_sfx_name(QString p_char, int p_emote) @@ -574,8 +785,14 @@ QString AOApplication::get_sfx_name(QString p_char, int p_emote) if (f_result == "") return "1"; - else - return f_result; + return f_result; +} + +QString AOApplication::get_emote_blip(QString p_char, int p_emote) +{ + QString f_result = + read_char_ini(p_char, QString::number(p_emote + 1), "SoundB"); + return f_result; } int AOApplication::get_sfx_delay(QString p_char, int p_emote) @@ -585,8 +802,52 @@ int AOApplication::get_sfx_delay(QString p_char, int p_emote) if (f_result == "") return 1; + return f_result.toInt(); +} + +QString AOApplication::get_sfx_looping(QString p_char, int p_emote) +{ + QString f_result = + read_char_ini(p_char, QString::number(p_emote + 1), "SoundL"); + + qDebug() << f_result; + if (f_result == "") + return "0"; else - return f_result.toInt(); + return f_result; +} + +QString AOApplication::get_sfx_frame(QString p_char, QString p_emote, + int n_frame) +{ + QString f_result = read_char_ini(p_char, QString::number(n_frame), + p_emote.append("_FrameSFX")); + + if (f_result == "") + return ""; + return f_result; +} + +QString AOApplication::get_screenshake_frame(QString p_char, QString p_emote, + int n_frame) +{ + QString f_result = read_char_ini(p_char, QString::number(n_frame), + p_emote.append("_FrameScreenshake")); + + if (f_result == "") + return ""; + return f_result; +} + +QString AOApplication::get_flash_frame(QString p_char, QString p_emote, + int n_frame) +{ + QString f_result = read_char_ini(p_char, QString::number(n_frame), + p_emote.append("_FrameRealization")); + + if (f_result == "") + return ""; + return f_result; } int AOApplication::get_text_delay(QString p_char, QString p_emote) @@ -595,8 +856,91 @@ int AOApplication::get_text_delay(QString p_char, QString p_emote) if (f_result == "") return -1; - else - return f_result.toInt(); + return f_result.toInt(); +} + +QStringList AOApplication::get_theme_effects() +{ + QString p_path = get_theme_path("effects/effects.ini"); + QString default_path = get_default_theme_path("effects/effects.ini"); + + QStringList effects; + if (!file_exists(p_path)) { + p_path = default_path; + if (!file_exists(p_path)) + return effects; + } + + QStringList lines = read_file(p_path).split("\n"); + foreach (QString effect, lines) { + effect = effect.split("=")[0].trimmed(); + if (!effect.isEmpty() && !effects.contains(effect)) + effects.append(effect); + } + return effects; +} + +QStringList AOApplication::get_effects(QString p_char) +{ + QString p_effect = read_char_ini(p_char, "effects", "Options"); + QString p_path = get_base_path() + "misc/" + p_effect + "/effects.ini"; + + QStringList effects = get_theme_effects(); + if (!file_exists(p_path)) + return effects; + + QStringList lines = read_file(p_path).split("\n"); + foreach (QString effect, lines) { + effect = effect.split("=")[0].trimmed(); + if (!effect.isEmpty() && !effects.contains(effect)) + effects.append(effect); + } + + return effects; +} + +QString AOApplication::get_effect(QString effect, QString p_char, + QString p_folder) +{ + QString p_effect = p_folder; + if (p_folder == "") + p_effect = read_char_ini(p_char, "effects", "Options"); + + QString p_path = + get_image_suffix(get_base_path() + "misc/" + p_effect + "/" + effect); + QString design_ini_path = + get_image_suffix(get_theme_path("effects/" + effect)); + QString default_path = + get_image_suffix(get_default_theme_path("effects/" + effect)); + + if (!file_exists(p_path)) { + p_path = design_ini_path; + if (!file_exists(p_path)) { + p_path = default_path; + if (!file_exists(p_path)) { + return ""; + } + } + } + + return p_path; +} + +QString AOApplication::get_effect_sound(QString fx_name, QString p_char) +{ + QString p_effect = read_char_ini(p_char, "effects", "Options"); + QString p_path = get_base_path() + "misc/" + p_effect + "/effects.ini"; + QString design_ini_path = get_theme_path("effects/effects.ini"); + QString default_path = get_default_theme_path("effects/effects.ini"); + + QString f_result = read_design_ini(fx_name, p_path); + if (f_result == "") { + f_result = read_design_ini(fx_name, design_ini_path); + if (f_result == "") { + f_result = read_design_ini(fx_name, default_path); + } + } + return f_result; } QString AOApplication::get_custom_realization(QString p_char) @@ -606,7 +950,7 @@ QString AOApplication::get_custom_realization(QString p_char) if (f_result == "") return get_sfx("realization"); else - return f_result; + return get_sfx_suffix(get_sounds_path(f_result)); } bool AOApplication::get_blank_blip() @@ -615,12 +959,67 @@ bool AOApplication::get_blank_blip() return result.startsWith("true"); } +bool AOApplication::get_looping_sfx() +{ + QString result = configini->value("looping_sfx", "true").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::objection_stop_music() +{ + QString result = + configini->value("objection_stop_music", "false").value<QString>(); + return result.startsWith("true"); +} + bool AOApplication::is_discord_enabled() { QString result = configini->value("discord", "true").value<QString>(); return result.startsWith("true"); } +bool AOApplication::is_shake_enabled() +{ + QString result = configini->value("shake", "true").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::is_effects_enabled() +{ + QString result = configini->value("effects", "true").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::is_frame_network_enabled() +{ + QString result = configini->value("framenetwork", "true").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::is_colorlog_enabled() +{ + QString result = configini->value("colorlog", "true").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::is_stickysounds_enabled() +{ + QString result = configini->value("stickysounds", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::is_stickyeffects_enabled() +{ + QString result = configini->value("stickyeffects", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::is_stickypres_enabled() +{ + QString result = configini->value("stickypres", "false").value<QString>(); + return result.startsWith("true"); +} + bool AOApplication::get_casing_enabled() { QString result = configini->value("casing_enabled", "false").value<QString>(); |
