diff options
Diffstat (limited to 'src')
37 files changed, 8730 insertions, 0 deletions
diff --git a/src/aoapplication.cpp b/src/aoapplication.cpp new file mode 100644 index 00000000..d7c3e662 --- /dev/null +++ b/src/aoapplication.cpp @@ -0,0 +1,187 @@ +#include "aoapplication.h" + +#include "lobby.h" +#include "courtroom.h" +#include "networkmanager.h" +#include "debug_functions.h" + +#include "aooptionsdialog.h" +#include "aocaseannouncerdialog.h" + +AOApplication::AOApplication(int &argc, char **argv) : QApplication(argc, argv) +{ + // Create the QSettings class that points to the config.ini. + configini = new QSettings(get_base_path() + "config.ini", QSettings::IniFormat); + + net_manager = new NetworkManager(this); + discord = new AttorneyOnline::Discord(); + QObject::connect(net_manager, SIGNAL(ms_connect_finished(bool, bool)), + SLOT(ms_connect_finished(bool, bool))); +} + +AOApplication::~AOApplication() +{ + destruct_lobby(); + destruct_courtroom(); + delete discord; +} + +void AOApplication::construct_lobby() +{ + if (lobby_constructed) + { + qDebug() << "W: lobby was attempted constructed when it already exists"; + return; + } + + w_lobby = new Lobby(this); + lobby_constructed = true; + + QRect screenGeometry = QApplication::desktop()->screenGeometry(); + int x = (screenGeometry.width()-w_lobby->width()) / 2; + int y = (screenGeometry.height()-w_lobby->height()) / 2; + w_lobby->move(x, y); + + if (is_discord_enabled()) + discord->state_lobby(); + + w_lobby->show(); +} + +void AOApplication::destruct_lobby() +{ + if(!lobby_constructed) + { + qDebug() << "W: lobby was attempted destructed when it did not exist"; + return; + } + + delete w_lobby; + w_lobby = nullptr; + lobby_constructed = false; +} + +void AOApplication::construct_courtroom() +{ + if (courtroom_constructed) + { + qDebug() << "W: courtroom was attempted constructed when it already exists"; + return; + } + + w_courtroom = new Courtroom(this); + courtroom_constructed = true; + + QRect screenGeometry = QApplication::desktop()->screenGeometry(); + int x = (screenGeometry.width()-w_courtroom->width()) / 2; + int y = (screenGeometry.height()-w_courtroom->height()) / 2; + w_courtroom->move(x, y); +} + +void AOApplication::destruct_courtroom() +{ + if (!courtroom_constructed) + { + qDebug() << "W: courtroom was attempted destructed when it did not exist"; + return; + } + + delete w_courtroom; + w_courtroom = nullptr; + courtroom_constructed = false; +} + +QString AOApplication::get_version_string() +{ + return + QString::number(RELEASE) + "." + + QString::number(MAJOR_VERSION) + "." + + QString::number(MINOR_VERSION); +} + +void AOApplication::reload_theme() +{ + current_theme = read_theme(); +} + +void AOApplication::set_favorite_list() +{ + favorite_list = read_serverlist_txt(); +} + +QString AOApplication::get_current_char() +{ + if (courtroom_constructed) + return w_courtroom->get_current_char(); + else + return ""; +} + +void AOApplication::add_favorite_server(int p_server) +{ + if (p_server < 0 || p_server >= server_list.size()) + return; + + server_type fav_server = server_list.at(p_server); + + QString str_port = QString::number(fav_server.port); + + QString server_line = fav_server.ip + ":" + str_port + ":" + fav_server.name; + + write_to_serverlist_txt(server_line); +} + +void AOApplication::server_disconnected() +{ + if (courtroom_constructed) + { + call_notice("Disconnected from server."); + construct_lobby(); + destruct_courtroom(); + } +} + +void AOApplication::loading_cancelled() +{ + destruct_courtroom(); + + w_lobby->hide_loading_overlay(); +} + +void AOApplication::ms_connect_finished(bool connected, bool will_retry) +{ + if (connected) + { + AOPacket *f_packet = new AOPacket("ALL#%"); + send_ms_packet(f_packet); + } + else + { + 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_ms / 1000.f) + " seconds."); + } + 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."); + } + } +} + +void AOApplication::call_settings_menu() +{ + AOOptionsDialog settings(nullptr, this); + settings.exec(); +} + +void AOApplication::call_announce_menu(Courtroom *court) +{ + AOCaseAnnouncerDialog announcer(nullptr, this, court); + announcer.exec(); +} diff --git a/src/aoblipplayer.cpp b/src/aoblipplayer.cpp new file mode 100644 index 00000000..74757c50 --- /dev/null +++ b/src/aoblipplayer.cpp @@ -0,0 +1,47 @@ +#include "aoblipplayer.h" + +AOBlipPlayer::AOBlipPlayer(QWidget *parent, AOApplication *p_ao_app) +{ + m_parent = parent; + ao_app = p_ao_app; +} + +void AOBlipPlayer::set_blips(QString p_sfx) +{ + QString f_path = 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); + } + + set_volume(m_volume); +} + +void AOBlipPlayer::blip_tick() +{ + int f_cycle = m_cycle++; + + if (m_cycle == 5) + m_cycle = 0; + + HSTREAM f_stream = m_stream_list[f_cycle]; + + if (ao_app->get_audio_output_device() != "default") + BASS_ChannelSetDevice(f_stream, BASS_GetDevice()); + BASS_ChannelPlay(f_stream, false); +} + +void AOBlipPlayer::set_volume(int p_value) +{ + m_volume = p_value; + + float volume = p_value / 100.0f; + + for (int n_stream = 0 ; n_stream < 5 ; ++n_stream) + { + BASS_ChannelSetAttribute(m_stream_list[n_stream], BASS_ATTRIB_VOL, volume); + } +} diff --git a/src/aobutton.cpp b/src/aobutton.cpp new file mode 100644 index 00000000..5be2e678 --- /dev/null +++ b/src/aobutton.cpp @@ -0,0 +1,26 @@ +#include "aobutton.h" + +#include "debug_functions.h" +#include "file_functions.h" + +AOButton::AOButton(QWidget *parent, AOApplication *p_ao_app) : QPushButton(parent) +{ + ao_app = p_ao_app; +} + +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); + + if (file_exists(image_path)) + this->setStyleSheet("border-image:url(\"" + image_path + "\")"); + else + this->setStyleSheet("border-image:url(\"" + default_image_path + "\")"); +} + diff --git a/src/aocaseannouncerdialog.cpp b/src/aocaseannouncerdialog.cpp new file mode 100644 index 00000000..5b82b64f --- /dev/null +++ b/src/aocaseannouncerdialog.cpp @@ -0,0 +1,82 @@ +#include "aocaseannouncerdialog.h" + +AOCaseAnnouncerDialog::AOCaseAnnouncerDialog(QWidget *parent, AOApplication *p_ao_app, Courtroom *p_court) + : QDialog(parent) +{ + ao_app = p_ao_app; + court = p_court; + + setWindowTitle(tr("Case Announcer")); + resize(405, 235); + + ui_announcer_buttons = new QDialogButtonBox(this); + + QSizePolicy sizepolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + sizepolicy.setHorizontalStretch(0); + sizepolicy.setVerticalStretch(0); + sizepolicy.setHeightForWidth(ui_announcer_buttons->sizePolicy().hasHeightForWidth()); + ui_announcer_buttons->setSizePolicy(sizepolicy); + ui_announcer_buttons->setOrientation(Qt::Horizontal); + ui_announcer_buttons->setStandardButtons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); + + QObject::connect(ui_announcer_buttons, SIGNAL(accepted()), this, SLOT(ok_pressed())); + QObject::connect(ui_announcer_buttons, SIGNAL(rejected()), this, SLOT(cancel_pressed())); + + setUpdatesEnabled(false); + + ui_vbox_layout = new QVBoxLayout(this); + + ui_form_layout = new QFormLayout(this); + ui_form_layout->setLabelAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignVCenter); + ui_form_layout->setFormAlignment(Qt::AlignLeading | Qt::AlignLeft | Qt::AlignTop); + ui_form_layout->setContentsMargins(6, 6, 6, 6); + + ui_vbox_layout->addItem(ui_form_layout); + ui_vbox_layout->addWidget(ui_announcer_buttons); + + ui_case_title_label = new QLabel(this); + ui_case_title_label->setText(tr("Case title:")); + + ui_form_layout->setWidget(0, QFormLayout::LabelRole, ui_case_title_label); + + ui_case_title_textbox = new QLineEdit(this); + ui_case_title_textbox->setMaxLength(50); + + ui_form_layout->setWidget(0, QFormLayout::FieldRole, ui_case_title_textbox); + + ui_defense_needed = new QCheckBox(this); + ui_defense_needed->setText(tr("Defense needed")); + ui_prosecutor_needed = new QCheckBox(this); + ui_prosecutor_needed->setText(tr("Prosecution needed")); + ui_judge_needed = new QCheckBox(this); + ui_judge_needed->setText(tr("Judge needed")); + ui_juror_needed = new QCheckBox(this); + ui_juror_needed->setText(tr("Jurors needed")); + ui_steno_needed = new QCheckBox(this); + ui_steno_needed->setText(tr("Stenographer needed")); + + ui_form_layout->setWidget(1, QFormLayout::FieldRole, ui_defense_needed); + ui_form_layout->setWidget(2, QFormLayout::FieldRole, ui_prosecutor_needed); + ui_form_layout->setWidget(3, QFormLayout::FieldRole, ui_judge_needed); + ui_form_layout->setWidget(4, QFormLayout::FieldRole, ui_juror_needed); + ui_form_layout->setWidget(5, QFormLayout::FieldRole, ui_steno_needed); + + setUpdatesEnabled(true); +} + +void AOCaseAnnouncerDialog::ok_pressed() +{ + court->announce_case(ui_case_title_textbox->text(), + ui_defense_needed->isChecked(), + ui_prosecutor_needed->isChecked(), + ui_judge_needed->isChecked(), + ui_juror_needed->isChecked(), + ui_steno_needed->isChecked()); + + done(0); +} + +void AOCaseAnnouncerDialog::cancel_pressed() +{ + done(0); +} diff --git a/src/aocharbutton.cpp b/src/aocharbutton.cpp new file mode 100644 index 00000000..76610270 --- /dev/null +++ b/src/aocharbutton.cpp @@ -0,0 +1,97 @@ +#include "aocharbutton.h" + +#include "file_functions.h" + +AOCharButton::AOCharButton(QWidget *parent, AOApplication *p_ao_app, int x_pos, int y_pos, bool is_taken) : QPushButton(parent) +{ + m_parent = parent; + + ao_app = p_ao_app; + + taken = is_taken; + + this->resize(60, 60); + this->move(x_pos, y_pos); + + ui_taken = new AOImage(this, ao_app); + ui_taken->resize(60, 60); + ui_taken->set_image("char_taken.png"); + ui_taken->setAttribute(Qt::WA_TransparentForMouseEvents); + ui_taken->hide(); + + ui_passworded = new AOImage(this, ao_app); + ui_passworded->resize(60, 60); + ui_passworded->set_image("char_passworded"); + ui_passworded->setAttribute(Qt::WA_TransparentForMouseEvents); + ui_passworded->hide(); + + ui_selector = new AOImage(parent, ao_app); + ui_selector->resize(62, 62); + ui_selector->move(x_pos - 1, y_pos - 1); + ui_selector->set_image("char_selector.png"); + ui_selector->setAttribute(Qt::WA_TransparentForMouseEvents); + ui_selector->hide(); +} + +void AOCharButton::reset() +{ + ui_taken->hide(); + ui_passworded->hide(); + ui_selector->hide(); +} + +void AOCharButton::set_taken(bool is_taken) +{ + taken = is_taken; +} + +void AOCharButton::apply_taken_image() +{ + if (taken) + { + ui_taken->move(0,0); + ui_taken->show(); + } + else + { + ui_taken->hide(); + } +} + +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"); + + this->setText(""); + + if (file_exists(image_path)) + this->setStyleSheet("border-image:url(\"" + image_path + "\")"); + else + { + this->setStyleSheet("border-image:url()"); + this->setText(p_character); + } +} + +void AOCharButton::enterEvent(QEvent * e) +{ + ui_selector->move(this->x() - 1, this->y() - 1); + ui_selector->raise(); + ui_selector->show(); + + setFlat(false); + QPushButton::enterEvent(e); +} + +void AOCharButton::leaveEvent(QEvent * e) +{ + ui_selector->hide(); + QPushButton::leaveEvent(e); +} + + diff --git a/src/aocharmovie.cpp b/src/aocharmovie.cpp new file mode 100644 index 00000000..57487233 --- /dev/null +++ b/src/aocharmovie.cpp @@ -0,0 +1,195 @@ +#include "aocharmovie.h" + +#include "misc_functions.h" +#include "file_functions.h" +#include "aoapplication.h" + +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; + + this->show(); + m_movie->start(); +} + +void AOCharMovie::play_pre(QString p_char, QString p_emote, int duration) +{ + QString gif_path = ao_app->get_character_path(p_char, p_emote); + + m_movie->stop(); + this->clear(); + m_movie->setFileName(gif_path); + m_movie->jumpToFrame(0); + + int full_duration = duration * time_mod; + int real_duration = 0; + + play_once = false; + + for (int n_frame = 0 ; n_frame < m_movie->frameCount() ; ++n_frame) + { + real_duration += m_movie->nextFrameDelay(); + m_movie->jumpToFrame(n_frame + 1); + } + +#ifdef DEBUG_GIF + qDebug() << "full_duration: " << full_duration; + qDebug() << "real_duration: " << real_duration; +#endif + + double percentage_modifier = 100.0; + + if (real_duration != 0 && duration != 0) + { + double modifier = full_duration / static_cast<double>(real_duration); + percentage_modifier = 100 / modifier; + + if (percentage_modifier > 100.0) + percentage_modifier = 100.0; + } + +#ifdef DEBUG_GIF + qDebug() << "% mod: " << percentage_modifier; +#endif + + if (full_duration == 0 || full_duration >= real_duration) + { + play_once = true; + } + else + { + play_once = false; + preanim_timer->start(full_duration); + } + + + m_movie->setSpeed(static_cast<int>(percentage_modifier)); + play(p_char, p_emote, ""); +} + +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)"); +} + +void AOCharMovie::play_idle(QString p_char, QString p_emote) +{ + QString gif_path = ao_app->get_character_path(p_char, "(a)" + p_emote); + + m_movie->stop(); + this->clear(); + m_movie->setFileName(gif_path); + + play_once = false; + m_movie->setSpeed(100); + play(p_char, p_emote, "(a)"); +} + +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(); + preanim_timer->stop(); + this->hide(); +} + +void AOCharMovie::combo_resize(int w, int h) +{ + QSize f_size(w, h); + this->resize(f_size); + m_movie->setScaledSize(f_size); +} + +void AOCharMovie::move(int ax, int ay) +{ + x = ax; + y = ay; + QLabel::move(x, y); +} + +void AOCharMovie::frame_change(int n_frame) +{ + + 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)); + else + this->setPixmap(f_pixmap.scaled(this->width(), this->height(), aspect_ratio, Qt::FastTransformation)); + + QLabel::move(x + (this->width() - this->pixmap()->width())/2, y); + } + + if (m_movie->frameCount() - 1 == n_frame && play_once) + { + preanim_timer->start(m_movie->nextFrameDelay()); + m_movie->stop(); + } +} + +void AOCharMovie::timer_done() +{ + + done(); +} diff --git a/src/aoemotebutton.cpp b/src/aoemotebutton.cpp new file mode 100644 index 00000000..9c1d3889 --- /dev/null +++ b/src/aoemotebutton.cpp @@ -0,0 +1,36 @@ +#include "aoemotebutton.h" + +#include "file_functions.h" + +AOEmoteButton::AOEmoteButton(QWidget *p_parent, AOApplication *p_ao_app, int p_x, int p_y) : QPushButton(p_parent) +{ + parent = p_parent; + ao_app = p_ao_app; + + this->move(p_x, p_y); + this->resize(40, 40); + + connect(this, SIGNAL(clicked()), this, SLOT(on_clicked())); +} + +void AOEmoteButton::set_image(QString p_char, int p_emote, QString suffix) +{ + 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)) + { + this->setText(""); + this->setStyleSheet("border-image:url(\"" + image_path + "\")"); + } + else + { + this->setText(ao_app->get_emote_comment(p_char, p_emote)); + this->setStyleSheet("border-image:url(\"\")"); + } +} + +void AOEmoteButton::on_clicked() +{ + emote_clicked(m_id); +} diff --git a/src/aoevidencebutton.cpp b/src/aoevidencebutton.cpp new file mode 100644 index 00000000..924aeb88 --- /dev/null +++ b/src/aoevidencebutton.cpp @@ -0,0 +1,119 @@ +#include "aoevidencebutton.h" + +#include "file_functions.h" + +AOEvidenceButton::AOEvidenceButton(QWidget *p_parent, AOApplication *p_ao_app, int p_x, int p_y) : 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->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->setAttribute(Qt::WA_TransparentForMouseEvents); + ui_selector->hide(); + + this->move(p_x, p_y); + this->resize(70, 70); + 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)) + { + this->setText(""); + this->setStyleSheet("border-image:url(\"" + image_path + "\")"); + } + else + { + this->setText(p_image); + this->setStyleSheet(""); + } +} + +void AOEvidenceButton::set_theme_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 final_image_path; + + if (file_exists(theme_image_path)) + final_image_path = theme_image_path; + else + final_image_path = default_image_path; + + this->setText(""); + this->setStyleSheet("border-image:url(\"" + final_image_path + "\")"); +} + +void AOEvidenceButton::set_selected(bool p_selected) +{ + if (p_selected) + ui_selected->show(); + else + ui_selected->hide(); +} + +void AOEvidenceButton::on_clicked() +{ + evidence_clicked(m_id); +} + +void AOEvidenceButton::mouseDoubleClickEvent(QMouseEvent *e) +{ + QPushButton::mouseDoubleClickEvent(e); + evidence_double_clicked(m_id); +} + +void AOEvidenceButton::dragLeaveEvent(QMouseEvent *e) +{ + //QWidget::dragLeaveEvent(e); + + qDebug() << "drag leave event"; +} + +void AOEvidenceButton::dragEnterEvent(QMouseEvent *e) +{ + //QWidget::dragEnterEvent(e); + + qDebug() << "drag enter event"; +} + +void AOEvidenceButton::enterEvent(QEvent * e) +{ + ui_selector->show(); + + on_hover(m_id, true); + + setFlat(false); + QPushButton::enterEvent(e); +} + +void AOEvidenceButton::leaveEvent(QEvent * e) +{ + ui_selector->hide(); + + on_hover(m_id, false); + QPushButton::leaveEvent(e); +} diff --git a/src/aoevidencedisplay.cpp b/src/aoevidencedisplay.cpp new file mode 100644 index 00000000..9ec105dd --- /dev/null +++ b/src/aoevidencedisplay.cpp @@ -0,0 +1,96 @@ +#include "aoevidencedisplay.h" + +#include "file_functions.h" +#include "datatypes.h" +#include "misc_functions.h" + +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))); +} + +void AOEvidenceDisplay::show_evidence(QString p_evidence_image, bool is_left_side, int p_volume) +{ + this->reset(); + + 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"; + } + else + { + icon_identifier = "right_evidence_icon"; + gif_name = "evidence_appear_right.gif"; + } + + pos_size_type icon_dimensions = ao_app->get_element_dimensions(icon_identifier, "courtroom_design.ini"); + + 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(); + 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(); + evidence_movie->stop(); + evidence_icon->hide(); + this->clear(); +} + +QLabel* AOEvidenceDisplay::get_evidence_icon() +{ + return evidence_icon; +} + + diff --git a/src/aoimage.cpp b/src/aoimage.cpp new file mode 100644 index 00000000..7bb56bb6 --- /dev/null +++ b/src/aoimage.cpp @@ -0,0 +1,47 @@ +#include "file_functions.h" + +#include "aoimage.h" + +AOImage::AOImage(QWidget *parent, AOApplication *p_ao_app) : QLabel(parent) +{ + m_parent = parent; + ao_app = p_ao_app; +} + +AOImage::~AOImage() +{ + +} + +void 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 final_image_path; + + if (file_exists(theme_image_path)) + final_image_path = theme_image_path; + else + final_image_path = default_image_path; + + QPixmap f_pixmap(final_image_path); + + this->setPixmap(f_pixmap.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio)); +} + +void AOImage::set_image_from_path(QString p_path) +{ + QString default_path = ao_app->get_default_theme_path("chatmed.png"); + + QString final_path; + + if (file_exists(p_path)) + final_path = p_path; + else + final_path = default_path; + + QPixmap f_pixmap(final_path); + + this->setPixmap(f_pixmap.scaled(this->width(), this->height(), Qt::IgnoreAspectRatio)); +} diff --git a/src/aolineedit.cpp b/src/aolineedit.cpp new file mode 100644 index 00000000..f6026e14 --- /dev/null +++ b/src/aolineedit.cpp @@ -0,0 +1,21 @@ +#include "aolineedit.h" + +AOLineEdit::AOLineEdit(QWidget *parent) : QLineEdit(parent) +{ + this->setReadOnly(true); + this->setFrame(false); + + connect(this, SIGNAL(returnPressed()), this, SLOT(on_enter_pressed())); +} + +void AOLineEdit::mouseDoubleClickEvent(QMouseEvent *e) +{ + QLineEdit::mouseDoubleClickEvent(e); + + this->setReadOnly(false); +} + +void AOLineEdit::on_enter_pressed() +{ + this->setReadOnly(true); +} diff --git a/src/aomovie.cpp b/src/aomovie.cpp new file mode 100644 index 00000000..edf5bdb0 --- /dev/null +++ b/src/aomovie.cpp @@ -0,0 +1,90 @@ +#include "aomovie.h" + +#include "file_functions.h" +#include "courtroom.h" +#include "misc_functions.h" + +AOMovie::AOMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) +{ + ao_app = p_ao_app; + + m_movie = new QMovie(); + + this->setMovie(m_movie); + + connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frame_change(int))); +} + +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) +{ + 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); + + this->show(); + m_movie->start(); +} + +void AOMovie::stop() +{ + m_movie->stop(); + this->hide(); +} + +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(); + + //signal connected to courtroom object, let it figure out what to do + done(); + } +} + +void AOMovie::combo_resize(int w, int h) +{ + QSize f_size(w, h); + this->resize(f_size); + m_movie->setScaledSize(f_size); +} diff --git a/src/aomusicplayer.cpp b/src/aomusicplayer.cpp new file mode 100644 index 00000000..997d82d8 --- /dev/null +++ b/src/aomusicplayer.cpp @@ -0,0 +1,34 @@ +#include "aomusicplayer.h" + +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) +{ + BASS_ChannelStop(m_stream); + + 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); + + this->set_volume(m_volume); + + if (ao_app->get_audio_output_device() != "default") + BASS_ChannelSetDevice(m_stream, BASS_GetDevice()); + BASS_ChannelPlay(m_stream, false); +} + +void AOMusicPlayer::set_volume(int p_value) +{ + m_volume = p_value; + float volume = m_volume / 100.0f; + BASS_ChannelSetAttribute(m_stream, BASS_ATTRIB_VOL, volume); +} diff --git a/src/aooptionsdialog.cpp b/src/aooptionsdialog.cpp new file mode 100644 index 00000000..2b8259f5 --- /dev/null +++ b/src/aooptionsdialog.cpp @@ -0,0 +1,517 @@ +#include "aooptionsdialog.h" +#include "aoapplication.h" +#include "bass.h" + +AOOptionsDialog::AOOptionsDialog(QWidget *parent, AOApplication *p_ao_app) : QDialog(parent) +{ + ao_app = p_ao_app; + + // Setting up the basics. + // setAttribute(Qt::WA_DeleteOnClose); + setWindowTitle(tr("Settings")); + resize(398, 320); + + ui_settings_buttons = new QDialogButtonBox(this); + + QSizePolicy sizePolicy1(QSizePolicy::Expanding, QSizePolicy::Fixed); + sizePolicy1.setHorizontalStretch(0); + sizePolicy1.setVerticalStretch(0); + sizePolicy1.setHeightForWidth(ui_settings_buttons->sizePolicy().hasHeightForWidth()); + ui_settings_buttons->setSizePolicy(sizePolicy1); + ui_settings_buttons->setOrientation(Qt::Horizontal); + ui_settings_buttons->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Save); + + QObject::connect(ui_settings_buttons, SIGNAL(accepted()), this, SLOT(save_pressed())); + QObject::connect(ui_settings_buttons, SIGNAL(rejected()), this, SLOT(discard_pressed())); + + // We'll stop updates so that the window won't flicker while it's being made. + setUpdatesEnabled(false); + + // First of all, we want a tabbed dialog, so let's add some layout. + ui_vertical_layout = new QVBoxLayout(this); + ui_settings_tabs = new QTabWidget(this); + + ui_vertical_layout->addWidget(ui_settings_tabs); + ui_vertical_layout->addWidget(ui_settings_buttons); + + // Let's add the tabs one by one. + // First, we'll start with 'Gameplay'. + ui_gameplay_tab = new QWidget(); + 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_gameplay_form = new QFormLayout(ui_form_layout_widget); + ui_gameplay_form->setLabelAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter); + ui_gameplay_form->setFormAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop); + ui_gameplay_form->setContentsMargins(0, 0, 0, 0); + + ui_theme_label = new QLabel(ui_form_layout_widget); + ui_theme_label->setText(tr("Theme:")); + ui_theme_label->setToolTip(tr("Sets the theme used in-game. If the new theme changes " + "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_theme_combobox = new QComboBox(ui_form_layout_widget); + + // Fill the combobox with the names of the themes. + QDirIterator it(p_ao_app->get_base_path() + "themes", QDir::Dirs, QDirIterator::NoIteratorFlags); + while (it.hasNext()) + { + QString actualname = QDir(it.next()).dirName(); + if (actualname != "." && actualname != "..") + ui_theme_combobox->addItem(actualname); + if (actualname == p_ao_app->read_theme()) + ui_theme_combobox->setCurrentIndex(ui_theme_combobox->count()-1); + } + + ui_gameplay_form->setWidget(0, QFormLayout::FieldRole, ui_theme_combobox); + + 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_downwards_lbl = new QLabel(ui_form_layout_widget); + ui_downwards_lbl->setText(tr("Log goes downwards:")); + ui_downwards_lbl->setToolTip(tr("If ticked, new messages will appear at " + "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_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_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_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_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_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_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_showname_lbl = new QLabel(ui_form_layout_widget); + ui_showname_lbl->setText(tr("Custom shownames:")); + ui_showname_lbl->setToolTip(tr("Gives the default value for the in-game 'Custom shownames' " + "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_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_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_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); + + 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_discord_lbl = new QLabel(ui_form_layout_widget); + ui_discord_lbl->setText(tr("Discord:")); + ui_discord_lbl->setToolTip(tr("Allows others on Discord to see what server you are in, " + "what character are you playing, and how long you have " + "been playing for.")); + + ui_gameplay_form->setWidget(9, 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); + + // Here we start the callwords tab. + ui_callwords_tab = new QWidget(); + ui_settings_tabs->addTab(ui_callwords_tab, tr("Callwords")); + + ui_callwords_widget = new QWidget(ui_callwords_tab); + ui_callwords_widget->setGeometry(QRect(10, 10, 361, 211)); + + ui_callwords_layout = new QVBoxLayout(ui_callwords_widget); + ui_callwords_layout->setContentsMargins(0,0,0,0); + + ui_callwords_textbox = new QPlainTextEdit(ui_callwords_widget); + QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(ui_callwords_textbox->sizePolicy().hasHeightForWidth()); + ui_callwords_textbox->setSizePolicy(sizePolicy); + + // Let's fill the callwords text edit with the already present callwords. + ui_callwords_textbox->document()->clear(); + foreach (QString callword, p_ao_app->get_call_words()) { + ui_callwords_textbox->appendPlainText(callword); + } + + ui_callwords_layout->addWidget(ui_callwords_textbox); + + ui_callwords_explain_lbl = new QLabel(ui_callwords_widget); + ui_callwords_explain_lbl->setWordWrap(true); + ui_callwords_explain_lbl->setText(tr("<html><head/><body>Enter as many callwords as you would like. These are case insensitive. Make sure to leave every callword in its own line!<br>Do not leave a line with a space at the end -- you will be alerted everytime someone uses a space in their messages.</body></html>")); + + ui_callwords_layout->addWidget(ui_callwords_explain_lbl); + + // The audio tab. + ui_audio_tab = new QWidget(); + ui_settings_tabs->addTab(ui_audio_tab, tr("Audio")); + + ui_audio_widget = new QWidget(ui_audio_tab); + ui_audio_widget->setGeometry(QRect(10, 10, 361, 211)); + + ui_audio_layout = new QFormLayout(ui_audio_widget); + ui_audio_layout->setLabelAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter); + ui_audio_layout->setFormAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop); + ui_audio_layout->setContentsMargins(0, 0, 0, 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_device_combobox = new QComboBox(ui_audio_widget); + + // Let's fill out the combobox with the available audio devices. + int a = 0; + BASS_DEVICEINFO info; + + if (needs_default_audiodev()) + { + ui_audio_device_combobox->addItem("default"); + } + + 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, ui_audio_device_combobox); + + 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_volume_divider); + + 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_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_music_volume_spinbox); + + 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_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_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_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_blips_volume_spinbox); + + 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_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_bliprate_spinbox = new QSpinBox(ui_audio_widget); + ui_bliprate_spinbox->setValue(p_ao_app->read_blip_rate()); + ui_bliprate_spinbox->setMinimum(1); + + ui_audio_layout->setWidget(6, QFormLayout::FieldRole, ui_bliprate_spinbox); + + 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_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); + + // The casing tab! + ui_casing_tab = new QWidget(); + ui_settings_tabs->addTab(ui_casing_tab, tr("Casing")); + + ui_casing_widget = new QWidget(ui_casing_tab); + ui_casing_widget->setGeometry(QRect(10,10, 361, 211)); + + ui_casing_layout = new QFormLayout(ui_casing_widget); + ui_casing_layout->setLabelAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter); + ui_casing_layout->setFormAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop); + ui_casing_layout->setContentsMargins(0, 0, 0, 0); + + // -- SERVER SUPPORTS CASING + + ui_casing_supported_lbl = new QLabel(ui_casing_widget); + if (ao_app->casing_alerts_enabled) + ui_casing_supported_lbl->setText(tr("This server supports case alerts.")); + else + ui_casing_supported_lbl->setText(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_supported_lbl); + + // -- CASE ANNOUNCEMENTS + + 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_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); + + // -- DEFENSE ANNOUNCEMENTS + + 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_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); + + // -- PROSECUTOR ANNOUNCEMENTS + + 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_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); + + // -- JUDGE ANNOUNCEMENTS + + 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_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); + + // -- JUROR ANNOUNCEMENTS + + 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_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); + + // -- STENO ANNOUNCEMENTS + + 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_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); + + // -- CM ANNOUNCEMENTS + + 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_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); + + // -- CM CASES ANNOUNCEMENTS + + 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_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_cm_cases_textbox); + + // When we're done, we should continue the updates! + setUpdatesEnabled(true); +} + +void AOOptionsDialog::save_pressed() +{ + // Save everything into the config.ini. + QSettings* configini = ao_app->configini; + + configini->setValue("theme", ui_theme_combobox->currentText()); + configini->setValue("log_goes_downwards", ui_downwards_cb->isChecked()); + configini->setValue("log_maximum", ui_length_spinbox->value()); + configini->setValue("default_username", ui_username_textbox->text()); + configini->setValue("show_custom_shownames", ui_showname_cb->isChecked()); + configini->setValue("master", ui_ms_textbox->text()); + configini->setValue("discord", ui_discord_cb->isChecked()); + + QFile* callwordsini = new QFile(ao_app->get_base_path() + "callwords.ini"); + + if (!callwordsini->open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) + { + // Nevermind! + } + else + { + QTextStream out(callwordsini); + out << ui_callwords_textbox->toPlainText(); + callwordsini->close(); + } + + configini->setValue("default_audio_device", ui_audio_device_combobox->currentText()); + configini->setValue("default_music", ui_music_volume_spinbox->value()); + configini->setValue("default_sfx", ui_sfx_volume_spinbox->value()); + 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("casing_enabled", ui_casing_enabled_cb->isChecked()); + configini->setValue("casing_defence_enabled", ui_casing_def_cb->isChecked()); + configini->setValue("casing_prosecution_enabled", ui_casing_pro_cb->isChecked()); + configini->setValue("casing_judge_enabled", ui_casing_jud_cb->isChecked()); + configini->setValue("casing_juror_enabled", ui_casing_jur_cb->isChecked()); + configini->setValue("casing_steno_enabled", ui_casing_steno_cb->isChecked()); + configini->setValue("casing_cm_enabled", ui_casing_cm_cb->isChecked()); + configini->setValue("casing_can_host_cases", ui_casing_cm_cases_textbox->text()); + + callwordsini->close(); + done(0); +} + +void AOOptionsDialog::discard_pressed() +{ + done(0); +} + +#if (defined (_WIN32) || defined (_WIN64)) +bool AOOptionsDialog::needs_default_audiodev() +{ + return true; +} +#elif (defined (LINUX) || defined (__linux__)) +bool AOOptionsDialog::needs_default_audiodev() +{ + return false; +} +#else +#error This operating system is not supported. +#endif diff --git a/src/aopacket.cpp b/src/aopacket.cpp new file mode 100644 index 00000000..b957efea --- /dev/null +++ b/src/aopacket.cpp @@ -0,0 +1,83 @@ +#include "aopacket.h" + +#include "encryption_functions.h" + +AOPacket::AOPacket(QString p_packet_string) +{ + QStringList packet_contents = p_packet_string.split("#"); + + m_header = packet_contents.at(0); + + for(int n_string = 1 ; n_string < packet_contents.size() - 1 ; ++n_string) + { + m_contents.append(packet_contents.at(n_string)); + } +} + +AOPacket::AOPacket(QString p_header, QStringList &p_contents) +{ + m_header = p_header; + m_contents = p_contents; +} + +AOPacket::~AOPacket() +{ + +} + +QString AOPacket::to_string() +{ + QString f_string = m_header; + + for (QString i_string : m_contents) + { + f_string += ("#" + i_string); + } + + f_string += "#%"; + + + if (encrypted) + return "#" + f_string; + else + return f_string; +} + +void AOPacket::encrypt_header(unsigned int p_key) +{ + m_header = fanta_encrypt(m_header, p_key); + + encrypted = true; +} + +void AOPacket::decrypt_header(unsigned int p_key) +{ + m_header = fanta_decrypt(m_header, p_key); + + encrypted = false; +} + +void AOPacket::net_encode() +{ + for (int n_element = 0 ; n_element < m_contents.size() ; ++n_element) + { + QString f_element = m_contents.at(n_element); + f_element.replace("#", "<num>").replace("%", "<percent>").replace("$", "<dollar>").replace("&", "<and>"); + + m_contents.removeAt(n_element); + m_contents.insert(n_element, f_element); + } +} + +void AOPacket::net_decode() +{ + for (int n_element = 0 ; n_element < m_contents.size() ; ++n_element) + { + QString f_element = m_contents.at(n_element); + f_element.replace("<num>", "#").replace("<percent>", "%").replace("<dollar>", "$").replace("<and>", "&"); + + m_contents.removeAt(n_element); + m_contents.insert(n_element, f_element); + } +} + diff --git a/src/aoscene.cpp b/src/aoscene.cpp new file mode 100644 index 00000000..344522b6 --- /dev/null +++ b/src/aoscene.cpp @@ -0,0 +1,76 @@ +#include "aoscene.h" +#include "courtroom.h" +#include "file_functions.h" + +AOScene::AOScene(QWidget *parent, AOApplication *p_ao_app) : QLabel(parent) +{ + m_parent = parent; + ao_app = p_ao_app; + m_movie = new QMovie(this); +} + +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"); + + QPixmap background(background_path); + QPixmap default_bg(default_path); + + int w = this->width(); + int h = this->height(); + + this->clear(); + this->setMovie(nullptr); + + m_movie->stop(); + m_movie->setFileName(animated_background_path); + m_movie->setScaledSize(QSize(w, h)); + + if (m_movie->isValid()) + { + this->setMovie(m_movie); + m_movie->start(); + } + else if (file_exists(background_path)) + { + this->setPixmap(background.scaled(w, h)); + } + else + { + this->setPixmap(default_bg.scaled(w, h)); + } +} + +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); + + QPixmap f_desk; + + if (file_exists(desk_path)) + f_desk.load(desk_path); + else + f_desk.load(default_path); + + 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)); +} diff --git a/src/aosfxplayer.cpp b/src/aosfxplayer.cpp new file mode 100644 index 00000000..7fe7987b --- /dev/null +++ b/src/aosfxplayer.cpp @@ -0,0 +1,51 @@ +#include "aosfxplayer.h" +#include "file_functions.h" + +AOSfxPlayer::AOSfxPlayer(QWidget *parent, AOApplication *p_ao_app) +{ + m_parent = parent; + ao_app = p_ao_app; +} + +void AOSfxPlayer::play(QString p_sfx, QString p_char, QString shout) +{ + BASS_ChannelStop(m_stream); + + QString misc_path = ""; + QString char_path = ""; + QString sound_path = ao_app->get_sounds_path(p_sfx); + + if (shout != "") + misc_path = ao_app->get_base_path() + "misc/" + shout + "/" + p_sfx; + if (p_char != "") + char_path = ao_app->get_character_path(p_char, p_sfx); + + QString f_path; + + if (file_exists(char_path)) + f_path = char_path; + else if (file_exists(misc_path)) + f_path = misc_path; + else + f_path = sound_path; + + m_stream = 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); +} + +void AOSfxPlayer::stop() +{ + BASS_ChannelStop(m_stream); +} + +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); +} diff --git a/src/aotextarea.cpp b/src/aotextarea.cpp new file mode 100644 index 00000000..5e146326 --- /dev/null +++ b/src/aotextarea.cpp @@ -0,0 +1,60 @@ +#include "aotextarea.h" + +AOTextArea::AOTextArea(QWidget *p_parent) : QTextBrowser(p_parent) +{ + +} + +void AOTextArea::append_chatmessage(QString p_name, QString p_message, QString p_colour) +{ + const QTextCursor old_cursor = this->textCursor(); + const int old_scrollbar_value = this->verticalScrollBar()->value(); + const bool is_scrolled_down = old_scrollbar_value == this->verticalScrollBar()->maximum(); + + this->moveCursor(QTextCursor::End); + + this->append(""); + this->insertHtml("<b><font color=" + p_colour + ">" + p_name.toHtmlEscaped() + "</font></b>: "); + + //cheap workarounds ahoy + p_message += " "; + QString result = p_message.toHtmlEscaped().replace("\n", "<br>").replace(omnis_dank_url_regex, "<a href='\\1'>\\1</a>" ); + + this->insertHtml(result); + + this->auto_scroll(old_cursor, old_scrollbar_value, is_scrolled_down); +} + +void AOTextArea::append_error(QString p_message) +{ + const QTextCursor old_cursor = this->textCursor(); + const int old_scrollbar_value = this->verticalScrollBar()->value(); + const bool is_scrolled_down = old_scrollbar_value == this->verticalScrollBar()->maximum(); + + this->moveCursor(QTextCursor::End); + + this->append(""); + + p_message += " "; + QString result = p_message.replace("\n", "<br>").replace(omnis_dank_url_regex, "<a href='\\1'>\\1</a>" ); + + this->insertHtml("<font color='red'>" + result + "</font>"); + + this->auto_scroll(old_cursor, old_scrollbar_value, is_scrolled_down); +} + +void AOTextArea::auto_scroll(QTextCursor old_cursor, int old_scrollbar_value, bool is_scrolled_down) +{ + if (old_cursor.hasSelection() || !is_scrolled_down) + { + // The user has selected text or scrolled away from the bottom: maintain position. + this->setTextCursor(old_cursor); + this->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. + this->moveCursor(QTextCursor::End); + this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum()); + } +} diff --git a/src/aotextedit.cpp b/src/aotextedit.cpp new file mode 100644 index 00000000..30e48b73 --- /dev/null +++ b/src/aotextedit.cpp @@ -0,0 +1,21 @@ +#include "aotextedit.h" + +AOTextEdit::AOTextEdit(QWidget *parent) : QPlainTextEdit(parent) +{ + this->setReadOnly(true); + + //connect(this, SIGNAL(returnPressed()), this, SLOT(on_enter_pressed())); +} + +void AOTextEdit::mouseDoubleClickEvent(QMouseEvent *e) +{ + QPlainTextEdit::mouseDoubleClickEvent(e); + + this->setReadOnly(false); +} + +void AOTextEdit::on_enter_pressed() +{ + this->setReadOnly(true); +} + diff --git a/src/charselect.cpp b/src/charselect.cpp new file mode 100644 index 00000000..8e1b9122 --- /dev/null +++ b/src/charselect.cpp @@ -0,0 +1,268 @@ +#include "courtroom.h" +#include "lobby.h" + +#include "file_functions.h" +#include "debug_functions.h" +#include "hardware_functions.h" + +void Courtroom::construct_char_select() +{ + ui_char_select_background = new AOImage(this, ao_app); + + ui_char_buttons = new QWidget(ui_char_select_background); + + ui_selector = new AOImage(ui_char_select_background, ao_app); + ui_selector->setAttribute(Qt::WA_TransparentForMouseEvents); + ui_selector->resize(62, 62); + + ui_back_to_lobby = new AOButton(ui_char_select_background, ao_app); + + ui_char_password = new QLineEdit(ui_char_select_background); + ui_char_password->setPlaceholderText(tr("Password")); + + ui_char_select_left = new AOButton(ui_char_select_background, ao_app); + ui_char_select_right = new AOButton(ui_char_select_background, ao_app); + + ui_spectator = new AOButton(ui_char_select_background, ao_app); + ui_spectator->setText(tr("Spectator")); + + ui_char_search = new QLineEdit(ui_char_select_background); + ui_char_search->setPlaceholderText(tr("Search")); + ui_char_search->setFocus(); + set_size_and_pos(ui_char_search, "char_search"); + + ui_char_passworded = new QCheckBox(ui_char_select_background); + ui_char_passworded->setText(tr("Passworded")); + set_size_and_pos(ui_char_passworded, "char_passworded"); + + ui_char_taken = new QCheckBox(ui_char_select_background); + ui_char_taken->setText(tr("Taken")); + set_size_and_pos(ui_char_taken, "char_taken"); + + ui_char_taken->setChecked(true); + ui_char_passworded->setChecked(true); + + set_size_and_pos(ui_char_buttons, "char_buttons"); + + connect (char_button_mapper, SIGNAL(mapped(int)), this, SLOT(char_clicked(int))); + connect(ui_back_to_lobby, SIGNAL(clicked()), this, SLOT(on_back_to_lobby_clicked())); + + connect(ui_char_select_left, SIGNAL(clicked()), this, SLOT(on_char_select_left_clicked())); + connect(ui_char_select_right, SIGNAL(clicked()), this, SLOT(on_char_select_right_clicked())); + + connect(ui_spectator, SIGNAL(clicked()), this, SLOT(on_spectator_clicked())); + + connect(ui_char_search, SIGNAL(textEdited(const QString&)), this, SLOT(on_char_search_changed(const QString&))); + connect(ui_char_passworded, SIGNAL(stateChanged(int)), this, SLOT(on_char_passworded_clicked(int))); + connect(ui_char_taken, SIGNAL(stateChanged(int)), this, SLOT(on_char_taken_clicked(int))); +} + +void Courtroom::set_char_select() +{ + QString filename = "courtroom_design.ini"; + + pos_size_type f_charselect = 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!"; + 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"); + + filter_character_list(); + + ui_char_search->setFocus(); +} + +void Courtroom::set_char_select_page() +{ + ui_char_select_background->show(); + + ui_char_select_left->hide(); + ui_char_select_right->hide(); + + for (AOCharButton *i_button : ui_char_button_list) + { + i_button->reset(); + i_button->hide(); + i_button->move(0,0); + } + + int total_pages = ui_char_button_list_filtered.size() / max_chars_on_page; + int chars_on_page = 0; + + if (ui_char_button_list_filtered.size() % max_chars_on_page != 0) + { + ++total_pages; + //i. e. not on the last page + if (total_pages > current_char_page + 1) + chars_on_page = max_chars_on_page; + else + chars_on_page = ui_char_button_list_filtered.size() % max_chars_on_page; + + } + else + chars_on_page = max_chars_on_page; + + if (total_pages > current_char_page + 1) + ui_char_select_right->show(); + + if (current_char_page > 0) + ui_char_select_left->show(); + + put_button_in_place(current_char_page * max_chars_on_page, chars_on_page); +} + +void Courtroom::char_clicked(int n_char) +{ + QString char_ini_path = ao_app->get_character_path(char_list.at(n_char).name, "char.ini"); + + qDebug() << "char_ini_path" << char_ini_path; + + if (!file_exists(char_ini_path)) + { + call_notice("Could not find " + char_ini_path); + return; + } + + if (n_char == m_cid) + { + enter_courtroom(m_cid); + } + else + { + ao_app->send_server_packet(new AOPacket("CC#" + QString::number(ao_app->s_pv) + "#" + QString::number(n_char) + "#" + get_hdid() + "#%")); + } + + ui_ic_chat_name->setPlaceholderText(char_list.at(n_char).name); +} + +void Courtroom::put_button_in_place(int starting, int chars_on_this_page) +{ + if (ui_char_button_list_filtered.size() == 0) + return; + + QPoint f_spacing = ao_app->get_button_spacing("char_button_spacing", "courtroom_design.ini"); + + int x_spacing = f_spacing.x(); + int x_mod_count = 0; + + int y_spacing = f_spacing.y(); + int y_mod_count = 0; + + char_columns = ((ui_char_buttons->width() - button_width) / (x_spacing + button_width)) + 1; + char_rows = ((ui_char_buttons->height() - button_height) / (y_spacing + button_height)) + 1; + + max_chars_on_page = char_columns * char_rows; + + int startout = starting; + for (int n = starting ; n < startout+chars_on_this_page ; ++n) + { + int x_pos = (button_width + x_spacing) * x_mod_count; + int y_pos = (button_height + y_spacing) * y_mod_count; + + ui_char_button_list_filtered.at(n)->move(x_pos, y_pos); + ui_char_button_list_filtered.at(n)->show(); + ui_char_button_list_filtered.at(n)->apply_taken_image(); + + ++x_mod_count; + + if (x_mod_count == char_columns) + { + ++y_mod_count; + x_mod_count = 0; + } + } +} + +void Courtroom::character_loading_finished() +{ + // Zeroeth, we'll clear any leftover characters from previous server visits. + ao_app->generated_chars = 0; + if (ui_char_button_list.size() > 0) + { + foreach (AOCharButton* item, ui_char_button_list) { + delete item; + } + ui_char_button_list.clear(); + } + + // First, we'll make all the character buttons in the very beginning. + // We also hide them all, so they can't be accidentally clicked. + // Later on, we'll be revealing buttons as we need them. + for (int n = 0; n < char_list.size(); n++) + { + AOCharButton* character = new AOCharButton(ui_char_buttons, ao_app, 0, 0, char_list.at(n).taken); + character->reset(); + character->hide(); + character->set_image(char_list.at(n).name); + ui_char_button_list.append(character); + + connect(character, SIGNAL(clicked()), char_button_mapper, SLOT(map())); + char_button_mapper->setMapping(character, ui_char_button_list.size() - 1); + + // This part here serves as a way of showing to the player that the game is still running, it is + // just loading the pictures of the characters. + if (ao_app->lobby_constructed) + { + ao_app->generated_chars++; + int total_loading_size = ao_app->char_list_size * 2 + ao_app->evidence_list_size + ao_app->music_list_size; + int loading_value = int(((ao_app->loaded_chars + ao_app->generated_chars + ao_app->loaded_music + ao_app->loaded_evidence) / static_cast<double>(total_loading_size)) * 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)); + } + } + + filter_character_list(); +} + +void Courtroom::filter_character_list() +{ + ui_char_button_list_filtered.clear(); + for (int i = 0; i < char_list.size(); i++) + { + AOCharButton* current_char = ui_char_button_list.at(i); + + // It seems passwording characters is unimplemented yet? + // Until then, this will stay here, I suppose. + //if (ui_char_passworded->isChecked() && character_is_passworded??) + // continue; + + if (!ui_char_taken->isChecked() && char_list.at(i).taken) + continue; + + if (!char_list.at(i).name.contains(ui_char_search->text(), Qt::CaseInsensitive)) + continue; + + // We only really need to update the fact that a character is taken + // for the buttons that actually appear. + // You'd also update the passwordedness and etc. here later. + current_char->reset(); + current_char->set_taken(char_list.at(i).taken); + + ui_char_button_list_filtered.append(current_char); + } + + current_char_page = 0; + set_char_select_page(); +} + +void Courtroom::on_char_search_changed(const QString& newtext) +{ + filter_character_list(); +} + +void Courtroom::on_char_passworded_clicked(int newstate) +{ + filter_character_list(); +} + +void Courtroom::on_char_taken_clicked(int newstate) +{ + filter_character_list(); +} diff --git a/src/chatlogpiece.cpp b/src/chatlogpiece.cpp new file mode 100644 index 00000000..6c861f01 --- /dev/null +++ b/src/chatlogpiece.cpp @@ -0,0 +1,76 @@ +#include "chatlogpiece.h" + +chatlogpiece::chatlogpiece() +{ + name = "UNKNOWN"; + showname = "UNKNOWN"; + message = "UNKNOWN"; + is_song = false; + datetime = QDateTime::currentDateTime().toUTC(); +} + +chatlogpiece::chatlogpiece(QString p_name, QString p_showname, QString p_message, bool p_song) +{ + name = p_name; + showname = p_showname; + message = p_message; + is_song = p_song; + datetime = QDateTime::currentDateTime().toUTC(); +} + +chatlogpiece::chatlogpiece(QString p_name, QString p_showname, QString p_message, bool p_song, QDateTime p_datetime) +{ + name = p_name; + showname = p_showname; + message = p_message; + is_song = p_song; + datetime = p_datetime.toUTC(); +} + +QString chatlogpiece::get_name() +{ + return name; +} + +QString chatlogpiece::get_showname() +{ + return showname; +} + +QString chatlogpiece::get_message() +{ + return message; +} + +QDateTime chatlogpiece::get_datetime() +{ + return datetime; +} + +bool chatlogpiece::get_is_song() +{ + return is_song; +} + +QString chatlogpiece::get_datetime_as_string() +{ + return datetime.toString(); +} + + +QString chatlogpiece::get_full() +{ + QString full = "["; + + full.append(get_datetime_as_string()); + full.append(" UTC] "); + full.append(get_showname()); + full.append(" ("); + full.append(get_name()); + full.append(")"); + if (is_song) + full.append(" has played a song: "); + full.append(get_message()); + + return full; +} diff --git a/src/courtroom.cpp b/src/courtroom.cpp new file mode 100644 index 00000000..3ed1e745 --- /dev/null +++ b/src/courtroom.cpp @@ -0,0 +1,3513 @@ +#include "courtroom.h" + +Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() +{ + ao_app = p_ao_app; + + // Change the default audio output device to be the one the user has given + // in his config.ini file for now. + int a = 0; + BASS_DEVICEINFO info; + + if (ao_app->get_audio_output_device() == "default") + { + BASS_Init(-1, 48000, BASS_DEVICE_LATENCY, nullptr, nullptr); + load_bass_opus_plugin(); + } + else + { + for (a = 0; BASS_GetDeviceInfo(a, &info); a++) + { + if (ao_app->get_audio_output_device() == info.name) + { + BASS_SetDevice(a); + BASS_Init(a, 48000, BASS_DEVICE_LATENCY, nullptr, nullptr); + load_bass_opus_plugin(); + qDebug() << info.name << "was set as the default audio output device."; + break; + } + } + } + + keepalive_timer = new QTimer(this); + keepalive_timer->start(60000); + + chat_tick_timer = new QTimer(this); + + text_delay_timer = new QTimer(this); + text_delay_timer->setSingleShot(true); + + 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); + + char_button_mapper = new QSignalMapper(this); + + music_player = new AOMusicPlayer(this, ao_app); + music_player->set_volume(0); + sfx_player = new AOSfxPlayer(this, ao_app); + sfx_player->set_volume(0); + objection_player = new AOSfxPlayer(this, ao_app); + sfx_player->set_volume(0); + blip_player = new AOBlipPlayer(this, ao_app); + blip_player->set_volume(0); + + modcall_player = new AOSfxPlayer(this, ao_app); + modcall_player->set_volume(50); + + ui_background = new AOImage(this, ao_app); + + ui_viewport = new QWidget(this); + ui_vp_background = new AOScene(ui_viewport, ao_app); + ui_vp_speedlines = new AOMovie(ui_viewport, ao_app); + ui_vp_speedlines->set_play_once(false); + ui_vp_player_char = new AOCharMovie(ui_viewport, ao_app); + ui_vp_sideplayer_char = new AOCharMovie(ui_viewport, ao_app); + ui_vp_sideplayer_char->hide(); + 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_chatbox = new AOImage(this, ao_app); + ui_vp_showname = new QLabel(ui_vp_chatbox); + ui_vp_message = new QTextEdit(ui_vp_chatbox); + 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_wtce = new AOMovie(this, ao_app); + ui_vp_objection = new AOMovie(this, ao_app); + + ui_ic_chatlog = new QTextEdit(this); + ui_ic_chatlog->setReadOnly(true); + + log_maximum_blocks = ao_app->get_max_log_size(); + log_goes_downwards = ao_app->get_log_goes_downwards(); + + ui_ms_chatlog = new AOTextArea(this); + ui_ms_chatlog->setReadOnly(true); + ui_ms_chatlog->setOpenExternalLinks(true); + ui_ms_chatlog->hide(); + + ui_server_chatlog = new AOTextArea(this); + ui_server_chatlog->setReadOnly(true); + ui_server_chatlog->setOpenExternalLinks(true); + + ui_area_list = new QListWidget(this); + ui_area_list->hide(); + ui_music_list = new QListWidget(this); + + 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->setFrame(false); + ui_ic_chat_message->setPlaceholderText(tr("Message")); + + ui_muted = new AOImage(ui_ic_chat_message, ao_app); + ui_muted->hide(); + + ui_ooc_chat_message = new QLineEdit(this); + ui_ooc_chat_message->setFrame(false); + + ui_ooc_chat_name = new QLineEdit(this); + ui_ooc_chat_name->setFrame(false); + ui_ooc_chat_name->setPlaceholderText("Name"); + ui_ooc_chat_name->setMaxLength(30); + ui_ooc_chat_name->setText(p_ao_app->get_default_username()); + + //ui_area_password = new QLineEdit(this); + //ui_area_password->setFrame(false); + ui_music_search = new QLineEdit(this); + 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); + + 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_defense_bar = new AOImage(this, ao_app); + ui_prosecution_bar = new AOImage(this, ao_app); + + ui_music_label = new QLabel(this); + 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); + + ui_ooc_toggle = new AOButton(this, ao_app); + ui_witness_testimony = new AOButton(this, ao_app); + ui_cross_examination = new AOButton(this, ao_app); + ui_guilty = new AOButton(this, ao_app); + ui_not_guilty = new AOButton(this, ao_app); + + ui_change_character = new AOButton(this, ao_app); + ui_reload_theme = new AOButton(this, ao_app); + ui_call_mod = new AOButton(this, ao_app); + ui_settings = new AOButton(this, ao_app); + ui_announce_casing = new AOButton(this, ao_app); + ui_switch_area_music = new AOButton(this, ao_app); + + ui_pre = new QCheckBox(this); + ui_pre->setText("Pre"); + ui_flip = new QCheckBox(this); + ui_flip->setText("Flip"); + ui_flip->hide(); + ui_guard = new QCheckBox(this); + ui_guard->setText("Guard"); + ui_guard->hide(); + ui_casing = new QCheckBox(this); + ui_casing->setChecked(ao_app->get_casing_enabled()); + ui_casing->setText(tr("Casing")); + ui_casing->hide(); + + ui_showname_enable = new QCheckBox(this); + ui_showname_enable->setChecked(ao_app->get_showname_enabled_by_default()); + ui_showname_enable->setText(tr("Shownames")); + + ui_pre_non_interrupt = new QCheckBox(this); + ui_pre_non_interrupt->setText(tr("No Interrupt")); + ui_pre_non_interrupt->hide(); + + ui_custom_objection = new AOButton(this, ao_app); + ui_realization = new AOButton(this, ao_app); + ui_mute = new AOButton(this, ao_app); + + ui_defense_plus = new AOButton(this, ao_app); + ui_defense_minus = new AOButton(this, ao_app); + + ui_prosecution_plus = new AOButton(this, ao_app); + 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); + ui_music_slider->setValue(ao_app->get_default_music()); + + ui_sfx_slider = new QSlider(Qt::Horizontal, this); + ui_sfx_slider->setRange(0, 100); + ui_sfx_slider->setValue(ao_app->get_default_sfx()); + + ui_blip_slider = new QSlider(Qt::Horizontal, this); + 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_button = new AOButton(this, ao_app); + + ui_evidence_button = new AOButton(this, ao_app); + + construct_evidence(); + + construct_char_select(); + + connect(keepalive_timer, SIGNAL(timeout()), this, SLOT(ping_server())); + + connect(ui_vp_objection, SIGNAL(done()), this, SLOT(objection_done())); + connect(ui_vp_player_char, SIGNAL(done()), this, SLOT(preanim_done())); + + connect(text_delay_timer, SIGNAL(timeout()), this, SLOT(start_chat_ticking())); + connect(sfx_delay_timer, SIGNAL(timeout()), this, SLOT(play_sfx())); + + 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_mute_list, SIGNAL(clicked(QModelIndex)), this, SLOT(on_mute_list_clicked(QModelIndex))); + + connect(ui_ic_chat_message, SIGNAL(returnPressed()), this, SLOT(on_chat_return_pressed())); + + 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_hold_it, SIGNAL(clicked()), this, SLOT(on_hold_it_clicked())); + connect(ui_objection, SIGNAL(clicked()), this, SLOT(on_objection_clicked())); + connect(ui_take_that, SIGNAL(clicked()), this, SLOT(on_take_that_clicked())); + connect(ui_custom_objection, SIGNAL(clicked()), this, SLOT(on_custom_objection_clicked())); + + connect(ui_realization, SIGNAL(clicked()), this, SLOT(on_realization_clicked())); + + connect(ui_mute, SIGNAL(clicked()), this, SLOT(on_mute_clicked())); + + connect(ui_defense_minus, SIGNAL(clicked()), this, SLOT(on_defense_minus_clicked())); + connect(ui_defense_plus, SIGNAL(clicked()), this, SLOT(on_defense_plus_clicked())); + connect(ui_prosecution_minus, SIGNAL(clicked()), this, SLOT(on_prosecution_minus_clicked())); + connect(ui_prosecution_plus, SIGNAL(clicked()), this, SLOT(on_prosecution_plus_clicked())); + + connect(ui_text_color, SIGNAL(currentIndexChanged(int)), this, SLOT(on_text_color_changed(int))); + + connect(ui_music_slider, SIGNAL(valueChanged(int)), this, SLOT(on_music_slider_moved(int))); + connect(ui_sfx_slider, SIGNAL(valueChanged(int)), this, SLOT(on_sfx_slider_moved(int))); + 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())); + + connect(ui_music_search, SIGNAL(textChanged(QString)), this, SLOT(on_music_search_edited(QString))); + + connect(ui_witness_testimony, SIGNAL(clicked()), this, SLOT(on_witness_testimony_clicked())); + connect(ui_cross_examination, SIGNAL(clicked()), this, SLOT(on_cross_examination_clicked())); + connect(ui_guilty, SIGNAL(clicked()), this, SLOT(on_guilty_clicked())); + connect(ui_not_guilty, SIGNAL(clicked()), this, SLOT(on_not_guilty_clicked())); + + connect(ui_change_character, SIGNAL(clicked()), this, SLOT(on_change_character_clicked())); + connect(ui_reload_theme, SIGNAL(clicked()), this, SLOT(on_reload_theme_clicked())); + connect(ui_call_mod, SIGNAL(clicked()), this, SLOT(on_call_mod_clicked())); + connect(ui_settings, SIGNAL(clicked()), this, SLOT(on_settings_clicked())); + connect(ui_announce_casing, SIGNAL(clicked()), this, SLOT(on_announce_casing_clicked())); + connect(ui_switch_area_music, SIGNAL(clicked()), this, SLOT(on_switch_area_music_clicked())); + + connect(ui_pre, SIGNAL(clicked()), this, SLOT(on_pre_clicked())); + connect(ui_flip, SIGNAL(clicked()), this, SLOT(on_flip_clicked())); + connect(ui_guard, SIGNAL(clicked()), this, SLOT(on_guard_clicked())); + connect(ui_casing, SIGNAL(clicked()), this, SLOT(on_casing_clicked())); + + connect(ui_showname_enable, SIGNAL(clicked()), this, SLOT(on_showname_enable_clicked())); + + connect(ui_pair_button, SIGNAL(clicked()), this, SLOT(on_pair_clicked())); + connect(ui_pair_list, SIGNAL(clicked(QModelIndex)), this, SLOT(on_pair_list_clicked(QModelIndex))); + connect(ui_pair_offset_spinbox, SIGNAL(valueChanged(int)), this, SLOT(on_pair_offset_changed(int))); + + connect(ui_evidence_button, SIGNAL(clicked()), this, SLOT(on_evidence_button_clicked())); + + set_widgets(); + + set_char_select(); +} + +void Courtroom::set_mute_list() +{ + mute_map.clear(); + + //maps which characters are muted based on cid, none are muted by default + for (int n_cid = 0 ; n_cid < char_list.size() ; n_cid++) + { + mute_map.insert(n_cid, false); + } + + QStringList sorted_mute_list; + + for (char_type i_char : char_list) + sorted_mute_list.append(i_char.name); + + sorted_mute_list.sort(); + + for (QString i_name : sorted_mute_list) + { + //mute_map.insert(i_name, false); + ui_mute_list->addItem(i_name); + } +} + +void Courtroom::set_pair_list() +{ + QStringList sorted_pair_list; + + for (char_type i_char : char_list) + sorted_pair_list.append(i_char.name); + + sorted_pair_list.sort(); + + for (QString i_name : sorted_pair_list) + { + ui_pair_list->addItem(i_name); + } +} + +void Courtroom::set_widgets() +{ + blip_rate = ao_app->read_blip_rate(); + blank_blip = ao_app->get_blank_blip(); + + QString filename = "courtroom_design.ini"; + + pos_size_type f_courtroom = ao_app->get_element_dimensions("courtroom", filename); + + if (f_courtroom.width < 0 || f_courtroom.height < 0) + { + qDebug() << "W: did not find courtroom width or height in " << filename; + + this->resize(714, 668); + } + else + { + m_courtroom_width = f_courtroom.width; + m_courtroom_height = f_courtroom.height; + + this->resize(f_courtroom.width, f_courtroom.height); + } + + set_fonts(); + + ui_background->move(0, 0); + ui_background->resize(m_courtroom_width, m_courtroom_height); + ui_background->set_image("courtroombackground.png"); + + set_size_and_pos(ui_viewport, "viewport"); + + // If there is a point to it, show all CCCC features. + // We also do this this soon so that set_size_and_pos can hide them all later, if needed. + if (ao_app->cccc_ic_support_enabled) + { + ui_pair_button->show(); + ui_pre_non_interrupt->show(); + ui_showname_enable->show(); + ui_ic_chat_name->show(); + ui_ic_chat_name->setEnabled(true); + } + else + { + ui_pair_button->hide(); + ui_pre_non_interrupt->hide(); + ui_showname_enable->hide(); + ui_ic_chat_name->hide(); + ui_ic_chat_name->setEnabled(false); + } + + if (ao_app->casing_alerts_enabled) + { + ui_announce_casing->show(); + } + else + { + ui_announce_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_speedlines->move(0, 0); + ui_vp_speedlines->combo_resize(ui_viewport->width(), ui_viewport->height()); + + ui_vp_player_char->move(0, 0); + ui_vp_player_char->combo_resize(ui_viewport->width(), ui_viewport->height()); + + ui_vp_sideplayer_char->move(0, 0); + ui_vp_sideplayer_char->combo_resize(ui_viewport->width(), ui_viewport->height()); + + //the AO2 desk element + ui_vp_desk->move(0, 0); + ui_vp_desk->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() + + double y_modifier = 147.0 / 192.0; + int final_y = static_cast<int>(y_modifier * ui_viewport->height()); + ui_vp_legacy_desk->move(0, final_y); + ui_vp_legacy_desk->hide(); + + ui_vp_evidence_display->move(0, 0); + ui_vp_evidence_display->resize(ui_viewport->width(), ui_viewport->height()); + + set_size_and_pos(ui_vp_showname, "showname"); + + 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"); + + 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_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_wtce->move(ui_viewport->x(), ui_viewport->y()); + ui_vp_wtce->combo_resize(ui_viewport->width(), ui_viewport->height()); + + ui_vp_objection->move(ui_viewport->x(), ui_viewport->y()); + ui_vp_objection->combo_resize(ui_viewport->width(), ui_viewport->height()); + + set_size_and_pos(ui_ic_chatlog, "ic_chatlog"); + + set_size_and_pos(ui_ms_chatlog, "ms_chatlog"); + + set_size_and_pos(ui_server_chatlog, "server_chatlog"); + + 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(); + set_size_and_pos(ui_pair_offset_spinbox, "pair_offset_spinbox"); + ui_pair_offset_spinbox->hide(); + set_size_and_pos(ui_pair_button, "pair_button"); + ui_pair_button->set_image("pair_button.png"); + + set_size_and_pos(ui_area_list, "music_list"); + ui_area_list->setStyleSheet("background-color: rgba(0, 0, 0, 0);"); + + set_size_and_pos(ui_music_list, "music_list"); + + if (is_ao2_bg) + { + set_size_and_pos(ui_ic_chat_message, "ao2_ic_chat_message"); + set_size_and_pos(ui_vp_chatbox, "ao2_chatbox"); + set_size_and_pos(ui_ic_chat_name, "ao2_ic_chat_name"); + } + else + { + set_size_and_pos(ui_ic_chat_message, "ic_chat_message"); + set_size_and_pos(ui_vp_chatbox, "chatbox"); + set_size_and_pos(ui_ic_chat_name, "ic_chat_name"); + } + + ui_ic_chat_message->setStyleSheet("QLineEdit{background-color: rgba(100, 100, 100, 255);}"); + ui_ic_chat_name->setStyleSheet("QLineEdit{background-color: rgba(180, 180, 180, 255);}"); + + ui_vp_chatbox->set_image("chatmed.png"); + ui_vp_chatbox->hide(); + + ui_muted->resize(ui_ic_chat_message->width(), ui_ic_chat_message->height()); + ui_muted->set_image("muted.png"); + + set_size_and_pos(ui_ooc_chat_message, "ooc_chat_message"); + ui_ooc_chat_message->setStyleSheet("background-color: rgba(0, 0, 0, 0);"); + + set_size_and_pos(ui_ooc_chat_name, "ooc_chat_name"); + ui_ooc_chat_name->setStyleSheet("background-color: rgba(0, 0, 0, 0);"); + + //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"); + + set_size_and_pos(ui_emote_dropdown, "emote_dropdown"); + set_size_and_pos(ui_pos_dropdown, "pos_dropdown"); + + set_size_and_pos(ui_defense_bar, "defense_bar"); + ui_defense_bar->set_image("defensebar" + QString::number(defense_bar_state) + ".png"); + + set_size_and_pos(ui_prosecution_bar, "prosecution_bar"); + ui_prosecution_bar->set_image("prosecutionbar" + QString::number(prosecution_bar_state) + ".png"); + + set_size_and_pos(ui_music_label, "music_label"); + ui_music_label->setText("Music"); + set_size_and_pos(ui_sfx_label, "sfx_label"); + ui_sfx_label->setText("Sfx"); + 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"); + set_size_and_pos(ui_objection, "objection"); + ui_objection->set_image("objection.png"); + set_size_and_pos(ui_take_that, "take_that"); + ui_take_that->set_image("takethat.png"); + + set_size_and_pos(ui_ooc_toggle, "ooc_toggle"); + ui_ooc_toggle->setText("Server"); + + set_size_and_pos(ui_witness_testimony, "witness_testimony"); + ui_witness_testimony->set_image("witnesstestimony.png"); + set_size_and_pos(ui_cross_examination, "cross_examination"); + ui_cross_examination->set_image("crossexamination.png"); + + set_size_and_pos(ui_guilty, "guilty"); + ui_guilty->set_image("guilty.png"); + set_size_and_pos(ui_not_guilty, "not_guilty"); + ui_not_guilty->set_image("notguilty.png"); + + set_size_and_pos(ui_change_character, "change_character"); + ui_change_character->setText("Change character"); + + set_size_and_pos(ui_reload_theme, "reload_theme"); + ui_reload_theme->setText("Reload theme"); + + set_size_and_pos(ui_call_mod, "call_mod"); + ui_call_mod->setText("Call mod"); + + set_size_and_pos(ui_settings, "settings"); + ui_settings->setText("Settings"); + + set_size_and_pos(ui_announce_casing, "casing_button"); + ui_announce_casing->setText("Casing"); + + set_size_and_pos(ui_switch_area_music, "switch_area_music"); + ui_switch_area_music->setText("A/M"); + + set_size_and_pos(ui_pre, "pre"); + ui_pre->setText("Preanim"); + + set_size_and_pos(ui_pre_non_interrupt, "pre_no_interrupt"); + set_size_and_pos(ui_flip, "flip"); + + set_size_and_pos(ui_guard, "guard"); + + set_size_and_pos(ui_casing, "casing"); + + set_size_and_pos(ui_showname_enable, "showname_enable"); + + set_size_and_pos(ui_custom_objection, "custom_objection"); + ui_custom_objection->set_image("custom.png"); + + set_size_and_pos(ui_realization, "realization"); + ui_realization->set_image("realization.png"); + + set_size_and_pos(ui_mute, "mute_button"); + ui_mute->set_image("mute.png"); + + set_size_and_pos(ui_defense_plus, "defense_plus"); + ui_defense_plus->set_image("defplus.png"); + + set_size_and_pos(ui_defense_minus, "defense_minus"); + ui_defense_minus->set_image("defminus.png"); + + set_size_and_pos(ui_prosecution_plus, "prosecution_plus"); + ui_prosecution_plus->set_image("proplus.png"); + + set_size_and_pos(ui_prosecution_minus, "prosecution_minus"); + ui_prosecution_minus->set_image("prominus.png"); + + set_size_and_pos(ui_text_color, "text_color"); + + 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->hide(); + + set_size_and_pos(ui_back_to_lobby, "back_to_lobby"); + ui_back_to_lobby->setText("Back to Lobby"); + + 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"); + + set_size_and_pos(ui_char_select_right, "char_select_right"); + ui_char_select_right->set_image("arrow_right.png"); + + set_size_and_pos(ui_spectator, "spectator"); +} + +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) +{ + QString design_file = "courtroom_fonts.ini"; + int f_weight = ao_app->get_font_size(p_identifier, design_file); + QString class_name = widget->metaObject()->className(); + + widget->setFont(QFont("Sans", f_weight)); + + QColor f_color = ao_app->get_color(p_identifier + "_color", design_file); + + 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); }"; + + widget->setStyleSheet(style_sheet_string); +} + +void Courtroom::set_window_title(QString p_title) +{ + this->setWindowTitle(p_title); +} + +void Courtroom::set_size_and_pos(QWidget *p_widget, QString p_identifier) +{ + QString filename = "courtroom_design.ini"; + + pos_size_type design_ini_result = ao_app->get_element_dimensions(p_identifier, filename); + + if (design_ini_result.width < 0 || design_ini_result.height < 0) + { + qDebug() << "W: could not find \"" << p_identifier << "\" in " << filename; + p_widget->hide(); + } + else + { + p_widget->move(design_ini_result.x, design_ini_result.y); + p_widget->resize(design_ini_result.width, design_ini_result.height); + } +} + +void Courtroom::set_taken(int n_char, bool p_taken) +{ + if (n_char >= char_list.size()) + { + qDebug() << "W: set_taken attempted to set an index bigger than char_list size"; + return; + } + + char_type f_char; + f_char.name = char_list.at(n_char).name; + f_char.description = char_list.at(n_char).description; + f_char.taken = p_taken; + f_char.evidence_string = char_list.at(n_char).evidence_string; + + char_list.replace(n_char, f_char); +} + +void Courtroom::done_received() +{ + m_cid = -1; + + music_player->set_volume(0); + sfx_player->set_volume(0); + objection_player->set_volume(0); + blip_player->set_volume(0); + + set_char_select_page(); + + set_mute_list(); + set_pair_list(); + + set_char_select(); + + show(); + + ui_spectator->show(); +} + +void Courtroom::set_background(QString p_background) +{ + testimony_in_progress = false; + + 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")); + + if (is_ao2_bg) + { + set_size_and_pos(ui_vp_chatbox, "ao2_chatbox"); + set_size_and_pos(ui_ic_chat_message, "ao2_ic_chat_message"); + } + else + { + set_size_and_pos(ui_vp_chatbox, "chatbox"); + set_size_and_pos(ui_ic_chat_message, "ic_chat_message"); + } +} + +void Courtroom::enter_courtroom(int p_cid) +{ + m_cid = p_cid; + + QString f_char; + + if (m_cid == -1) + { + if (ao_app->is_discord_enabled()) + ao_app->discord->state_spectate(); + f_char = ""; + } + else + { + f_char = ao_app->get_char_name(char_list.at(m_cid).name); + + if (ao_app->is_discord_enabled()) + ao_app->discord->state_character(f_char.toStdString()); + } + + current_char = f_char; + + current_emote_page = 0; + current_emote = 0; + + if (m_cid == -1) + ui_emotes->hide(); + else + ui_emotes->show(); + + set_emote_page(); + set_emote_dropdown(); + + current_evidence_page = 0; + current_evidence = 0; + + set_evidence_page(); + + QString side = ao_app->get_char_side(f_char); + + if (side == "jud") + { + ui_witness_testimony->show(); + ui_cross_examination->show(); + ui_not_guilty->show(); + ui_guilty->show(); + ui_defense_minus->show(); + ui_defense_plus->show(); + ui_prosecution_minus->show(); + ui_prosecution_plus->show(); + } + else + { + ui_witness_testimony->hide(); + ui_cross_examination->hide(); + ui_guilty->hide(); + ui_not_guilty->hide(); + ui_defense_minus->hide(); + ui_defense_plus->hide(); + ui_prosecution_minus->hide(); + ui_prosecution_plus->hide(); + } + + 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"))) + ui_custom_objection->show(); + else + ui_custom_objection->hide(); + + if (ao_app->flipping_enabled) + ui_flip->show(); + else + ui_flip->hide(); + + if (ao_app->casing_alerts_enabled) + ui_casing->show(); + else + ui_casing->hide(); + + list_music(); + list_areas(); + + music_player->set_volume(ui_music_slider->value()); + 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_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(); +} + +void Courtroom::list_music() +{ + ui_music_list->clear(); + music_row_to_number.clear(); + + QString f_file = "courtroom_design.ini"; + + QBrush found_brush(ao_app->get_color("found_song_color", f_file)); + QBrush missing_brush(ao_app->get_color("missing_song_color", f_file)); + + int n_listed_songs = 0; + + 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(".")); + + 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); + + 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); + + ++n_listed_songs; + } + } +} + +void Courtroom::list_areas() +{ + ui_area_list->clear(); + area_row_to_number.clear(); + + QString f_file = "courtroom_design.ini"; + + QBrush free_brush(ao_app->get_color("area_free_color", f_file)); + QBrush lfp_brush(ao_app->get_color("area_lfp_color", f_file)); + QBrush casing_brush(ao_app->get_color("area_casing_color", f_file)); + QBrush recess_brush(ao_app->get_color("area_recess_color", f_file)); + QBrush rp_brush(ao_app->get_color("area_rp_color", f_file)); + QBrush gaming_brush(ao_app->get_color("area_gaming_color", f_file)); + QBrush locked_brush(ao_app->get_color("area_locked_color", f_file)); + + int n_listed_areas = 0; + + 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.append("\n "); + + i_area.append(arup_statuses.at(n_area)); + i_area.append(" | CM: "); + i_area.append(arup_cms.at(n_area)); + + i_area.append("\n "); + + i_area.append(QString::number(arup_players.at(n_area))); + i_area.append(" users | "); + + 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); + + 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); + } + } + else + { + ui_area_list->item(n_listed_areas)->setBackground(free_brush); + } + + ++n_listed_areas; + } + } +} + +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()); +} + +void Courtroom::append_server_chatmessage(QString p_name, QString p_message, QString p_colour) +{ + 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(); + + ui_server_chatlog->append_chatmessage(p_name, p_message, colour); +} + +void Courtroom::on_chat_return_pressed() +{ + if (ui_ic_chat_message->text() == "" || is_muted) + return; + + if ((anim_state < 3 || text_state < 2) && + objection_state == 0) + return; + + //MS# + //deskmod# + //pre-emote# + //character# + //emote# + //message# + //side# + //sfx-name# + //emote_modifier# + //char_id# + //sfx_delay# + //objection_modifier# + //evidence# + //placeholder# + //realization# + //text_color#% + + // Additionally, in our case: + + //showname# + //other_charid# + //self_offset# + //noninterrupting_preanim#% + + QStringList packet_contents; + + QString f_side = ao_app->get_char_side(current_char); + + QString f_desk_mod = "chat"; + + if (ao_app->desk_mod_enabled) + { + f_desk_mod = QString::number(ao_app->get_desk_mod(current_char, current_emote)); + if (f_desk_mod == "-1") + f_desk_mod = "chat"; + } + + packet_contents.append(f_desk_mod); + + packet_contents.append(ao_app->get_pre_emote(current_char, current_emote)); + + packet_contents.append(current_char); + + packet_contents.append(ao_app->get_emote(current_char, current_emote)); + + packet_contents.append(ui_ic_chat_message->text()); + + packet_contents.append(f_side); + + packet_contents.append(ao_app->get_sfx_name(current_char, current_emote)); + + 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) + f_emote_mod = 6; + else + f_emote_mod = 2; + } + } + else if (ui_pre->isChecked() and !ui_pre_non_interrupt->isChecked()) + { + if (f_emote_mod == 0) + f_emote_mod = 1; + else if (f_emote_mod == 5 && ao_app->prezoom_enabled) + f_emote_mod = 4; + } + else + { + if (f_emote_mod == 1) + f_emote_mod = 0; + else if (f_emote_mod == 4) + f_emote_mod = 5; + } + + 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))); + + QString f_obj_state; + + if ((objection_state == 4 && !ao_app->custom_objection_enabled) || + (objection_state < 0)) + f_obj_state = "0"; + else + f_obj_state = QString::number(objection_state); + + packet_contents.append(f_obj_state); + + if (is_presenting_evidence) + //the evidence index is shifted by 1 because 0 is no evidence per legacy standards + //besides, older clients crash if we pass -1 + packet_contents.append(QString::number(current_evidence + 1)); + else + packet_contents.append("0"); + + QString f_flip; + + if (ao_app->flipping_enabled) + { + if (ui_flip->isChecked()) + f_flip = "1"; + else + f_flip = "0"; + } + else + f_flip = QString::number(m_cid); + + packet_contents.append(f_flip); + + packet_contents.append(QString::number(realization_state)); + + QString f_text_color; + + if (text_color < 0) + f_text_color = "0"; + else if (text_color > 8) + f_text_color = "0"; + else + f_text_color = QString::number(text_color); + + packet_contents.append(f_text_color); + + // If the server we're on supports CCCC stuff, we should use it! + if (ao_app->cccc_ic_support_enabled) + { + // If there is a showname entered, use that -- else, just send an empty packet-part. + if (!ui_ic_chat_name->text().isEmpty()) + { + packet_contents.append(ui_ic_chat_name->text()); + } + else + { + packet_contents.append(""); + } + + // 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)); + } + else + { + packet_contents.append("-1"); + packet_contents.append("0"); + } + + // Finally, we send over if we want our pres to not interrupt. + if (ui_pre_non_interrupt->isChecked() && ui_pre->isChecked()) + { + packet_contents.append("1"); + } + else + { + packet_contents.append("0"); + } + } + + ao_app->send_server_packet(new AOPacket("MS", packet_contents)); +} + +void Courtroom::handle_chatmessage(QStringList *p_contents) +{ + // Instead of checking for whether a message has at least chatmessage_size + // amount of packages, we'll check if it has at least 15. + // That was the original chatmessage_size. + if (p_contents->size() < 15) + return; + + for (int n_string = 0 ; n_string < chatmessage_size ; ++n_string) + { + //m_chatmessage[n_string] = p_contents->at(n_string); + + // Note that we have added stuff that vanilla clients and servers simply won't send. + // So now, we have to check if the thing we want even exists amongst the packet's content. + // We also have to check if the server even supports CCCC's IC features, or if it's just japing us. + // Also, don't forget! A size 15 message will have indices from 0 to 14. + if (n_string < p_contents->size() && + (n_string < 15 || ao_app->cccc_ic_support_enabled)) + { + m_chatmessage[n_string] = p_contents->at(n_string); + } + else + { + m_chatmessage[n_string] = ""; + } + } + + int f_char_id = m_chatmessage[CHAR_ID].toInt(); + + 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()) + { + f_showname = ao_app->get_showname(char_list.at(f_char_id).name); + } + else + { + f_showname = m_chatmessage[SHOWNAME]; + } + + + QString f_message = f_showname + ": " + m_chatmessage[MESSAGE] + '\n'; + + if (f_message == previous_ic_message) + return; + + 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() && m_chatmessage[CHAR_ID].toInt() == m_cid) + { + ui_ic_chat_message->clear(); + objection_state = 0; + realization_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"); + } + + chatlogpiece* temp = new chatlogpiece(ao_app->get_showname(char_list.at(f_char_id).name), f_showname, ": " + m_chatmessage[MESSAGE], false); + ic_chatlog_history.append(*temp); + + 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; + + int objection_mod = m_chatmessage[OBJECTION_MOD].toInt(); + QString f_char = m_chatmessage[CHAR_NAME]; + QString f_custom_theme = ao_app->get_char_shouts(f_char); + + //if an objection is used + if (objection_mod <= 4 && objection_mod >= 1) + { + 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); + break; + case 2: + ui_vp_objection->play("objection", f_char, f_custom_theme); + objection_player->play("objection.wav", f_char, f_custom_theme); + break; + case 3: + ui_vp_objection->play("takethat", f_char, f_custom_theme); + objection_player->play("takethat.wav", 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); + break; + default: + qDebug() << "W: Logic error in objection switch statement!"; + } + + int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); + + if (emote_mod == 0) + m_chatmessage[EMOTE_MOD] = 1; + } + else + handle_chatmessage_2(); +} + +void Courtroom::objection_done() +{ + handle_chatmessage_2(); +} + +void Courtroom::handle_chatmessage_2() +{ + ui_vp_speedlines->stop(); + ui_vp_player_char->stop(); + + if (m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked()) + { + QString real_name = char_list.at(m_chatmessage[CHAR_ID].toInt()).name; + + QString f_showname = ao_app->get_showname(real_name); + + ui_vp_showname->setText(f_showname); + } + else + { + 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); + } + + ui_vp_showname->setStyleSheet("QLabel { color : " + get_text_color("_showname").name() + "; }"); + + set_scene(); + set_text_color(); + + // 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; + } + else + { + message_is_centered = false; + } + } + else + { + ui_vp_message->setAlignment(Qt::AlignLeft); + } + + + int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); + + if (ao_app->flipping_enabled && m_chatmessage[FLIP].toInt() == 1) + ui_vp_player_char->set_flipped(true); + else + ui_vp_player_char->set_flipped(false); + + QString side = m_chatmessage[SIDE]; + + // 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(); + 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); + 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(); + } + 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); + else + ui_vp_sideplayer_char->set_flipped(false); + ui_vp_sideplayer_char->play_idle(m_chatmessage[OTHER_NAME], m_chatmessage[OTHER_EMOTE]); + } + else + { + // If the server understands other characters, but there + // 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); + } + } + } + + switch (emote_mod) + { + case 1: case 2: case 6: + play_preanim(false); + break; + case 0: case 5: + if (m_chatmessage[NONINTERRUPTING_PRE].toInt() == 0) + handle_chatmessage_3(); + else + play_preanim(true); + break; + default: + qDebug() << "W: invalid emote mod: " << QString::number(emote_mod); + } +} + +void Courtroom::handle_chatmessage_3() +{ + start_chat_ticking(); + + int f_evi_id = m_chatmessage[EVIDENCE_ID].toInt(); + QString f_side = m_chatmessage[SIDE]; + + 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; + //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()); + } + + int emote_mod = m_chatmessage[EMOTE_MOD].toInt(); + + QString side = m_chatmessage[SIDE]; + + if (emote_mod == 5 || + emote_mod == 6) + { + ui_vp_desk->hide(); + ui_vp_legacy_desk->hide(); + + // Since we're zooming, hide the second character, and centre the first. + ui_vp_sideplayer_char->hide(); + ui_vp_player_char->move(0,0); + + if (side == "pro" || + side == "hlp" || + side == "wit") + ui_vp_speedlines->play("prosecution_speedlines"); + 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; + } + + 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 (f_anim_state == 2) { + ui_vp_player_char->play_talking(f_char, f_emote); + anim_state = 2; + } + else + { + ui_vp_player_char->play_idle(f_char, f_emote); + anim_state = 3; + } + + QString f_message = m_chatmessage[MESSAGE]; + QStringList call_words = ao_app->get_call_words(); + + for (QString word : call_words) + { + if (f_message.contains(word, Qt::CaseInsensitive)) + { + modcall_player->play(ao_app->get_sfx("word_call")); + ao_app->alert(this); + + break; + } + } + +} + +QString Courtroom::filter_ic_text(QString p_text) +{ + // 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 ': ~~'. + + // 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; + 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); + } + + // Text speed modifier. + else if (f_character == "{" and !ic_next_is_not_special) + { + p_text.remove(trick_check_pos,1); + } + else if (f_character == "}" and !ic_next_is_not_special) + { + p_text.remove(trick_check_pos,1); + } + + // 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); + } + } + + // 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 + { + ic_next_is_not_special = 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++; + } + 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); + } + else + { + ic_colour_stack.push(INLINE_GREEN); + p_text.remove(trick_check_pos,1); + } + } + else + { + ic_colour_stack.push(INLINE_GREEN); + p_text.remove(trick_check_pos,1); + } + } + else + { + trick_check_pos++; + ic_next_is_not_special = false; + } + } + + return p_text; +} + +void Courtroom::append_ic_text(QString p_text, QString p_name, bool is_songchange) +{ + QTextCharFormat bold; + QTextCharFormat normal; + QTextCharFormat italics; + bold.setFontWeight(QFont::Bold); + normal.setFontWeight(QFont::Normal); + italics.setFontItalic(true); + 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 (log_goes_downwards) + { + const bool is_scrolled_down = old_scrollbar_value == ui_ic_chatlog->verticalScrollBar()->maximum(); + + ui_ic_chatlog->moveCursor(QTextCursor::End); + + if (!first_message_sent) + { + ui_ic_chatlog->textCursor().insertText(p_name, bold); + first_message_sent = true; + } + else + { + ui_ic_chatlog->textCursor().insertText('\n' + p_name, bold); + } + + if (is_songchange) + { + ui_ic_chatlog->textCursor().insertText(" has played a song: ", normal); + ui_ic_chatlog->textCursor().insertText(p_text + ".", italics); + } + else + { + ui_ic_chatlog->textCursor().insertText(p_text, normal); + } + + // If we got too many blocks in the current log, delete some from the top. + while (ui_ic_chatlog->document()->blockCount() > log_maximum_blocks && log_maximum_blocks > 0) + { + ui_ic_chatlog->moveCursor(QTextCursor::Start); + 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) + { + // The user has selected text or scrolled away from the bottom: maintain position. + ui_ic_chatlog->setTextCursor(old_cursor); + ui_ic_chatlog->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the bottom: scroll to the bottom. + ui_ic_chatlog->moveCursor(QTextCursor::End); + ui_ic_chatlog->verticalScrollBar()->setValue(ui_ic_chatlog->verticalScrollBar()->maximum()); + } + } + else + { + const bool is_scrolled_up = old_scrollbar_value == ui_ic_chatlog->verticalScrollBar()->minimum(); + + ui_ic_chatlog->moveCursor(QTextCursor::Start); + + ui_ic_chatlog->textCursor().insertText(p_name, bold); + + if (is_songchange) + { + ui_ic_chatlog->textCursor().insertText(" has played a song: ", normal); + ui_ic_chatlog->textCursor().insertText(p_text + "." + '\n', italics); + } + else + { + ui_ic_chatlog->textCursor().insertText(p_text + '\n', normal); + } + + // If we got too many blocks in the current log, delete some from the bottom. + while (ui_ic_chatlog->document()->blockCount() > log_maximum_blocks && log_maximum_blocks > 0) + { + ui_ic_chatlog->moveCursor(QTextCursor::End); + 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) + { + // The user has selected text or scrolled away from the top: maintain position. + ui_ic_chatlog->setTextCursor(old_cursor); + ui_ic_chatlog->verticalScrollBar()->setValue(old_scrollbar_value); + } + else + { + // The user hasn't selected any text and the scrollbar is at the top: scroll to the top. + ui_ic_chatlog->moveCursor(QTextCursor::Start); + ui_ic_chatlog->verticalScrollBar()->setValue(ui_ic_chatlog->verticalScrollBar()->minimum()); + } + } +} + +void Courtroom::play_preanim(bool noninterrupting) +{ + QString f_char = m_chatmessage[CHAR_NAME]; + QString f_preanim = m_chatmessage[PRE_EMOTE]; + + //all time values in char.inis are multiplied by a constant(time_mod) to get 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 preanim_duration; + + if (ao2_duration < 0) + preanim_duration = ao_app->get_preanim_duration(f_char, f_preanim); + else + preanim_duration = ao2_duration; + + 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 (noninterrupting) + anim_state = 4; + else + anim_state = 1; + preanim_done(); + qDebug() << "could not find " + anim_to_find; + return; + } + + ui_vp_player_char->play_pre(f_char, f_preanim, preanim_duration); + + if (noninterrupting) + anim_state = 4; + else + anim_state = 1; + + if (text_delay >= 0) + text_delay_timer->start(text_delay); + + if (noninterrupting) + handle_chatmessage_3(); +} + +void Courtroom::preanim_done() +{ + anim_state = 1; + 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 can be called by two logic paths + 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])); + } + + ui_vp_message->clear(); + set_text_color(); + rainbow_counter = 0; + + 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(); + + tick_pos = 0; + blip_pos = 0; + + // 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; + + // 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]); + + QString f_gender = ao_app->get_gender(m_chatmessage[CHAR_NAME]); + + blip_player->set_blips("sfx-blip" + f_gender + ".wav"); + + //means text is currently ticking + text_state = 1; +} + +void Courtroom::chat_tick() +{ + //note: this is called fairly often(every 60 ms when char is talking) + //do not perform heavy operations here + + QString f_message = m_chatmessage[MESSAGE]; + + // 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 (tick_pos >= f_message.size()) + { + text_state = 2; + if (anim_state != 4) + { + anim_state = 3; + ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME], m_chatmessage[EMOTE]); + } + } + + else + { + QString f_character = f_message.at(tick_pos); + f_character = f_character.toHtmlEscaped(); + + if (f_character == " ") + ui_vp_message->insertPlainText(" "); + + // Escape character. + else if (f_character == "\\" and !next_character_is_not_special) + { + next_character_is_not_special = true; + formatting_char = true; + } + + // Text speed modifier. + else if (f_character == "{" and !next_character_is_not_special) + { + // ++, because it INCREASES delay! + current_display_speed++; + formatting_char = true; + } + else if (f_character == "}" and !next_character_is_not_special) + { + 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 + { + next_character_is_not_special = true; + tick_pos--; + } + } + + // 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--; + } + } + + // 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; + } + } + 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; + default: + ui_vp_message->insertHtml(f_character); + 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; + + ui_vp_message->insertHtml("<font color=\"" + html_color + "\">" + f_character + "</font>"); + } + else + ui_vp_message->insertHtml(f_character); + } + + if (message_is_centered) + { + ui_vp_message->setAlignment(Qt::AlignCenter); + } + else + { + ui_vp_message->setAlignment(Qt::AlignLeft); + } + } + + QScrollBar *scroll = ui_vp_message->verticalScrollBar(); + scroll->setValue(scroll->maximum()); + + if(blank_blip) + qDebug() << "blank_blip found true"; + + if (f_message.at(tick_pos) != ' ' || blank_blip) + { + + if (blip_pos % blip_rate == 0 && !formatting_char) + { + blip_pos = 0; + blip_player->blip_tick(); + } + + ++blip_pos; + } + + ++tick_pos; + + // 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; + } + + if (current_display_speed > 6) + { + current_display_speed = 6; + } + + // 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); + } + else + { + chat_tick_timer->start(message_display_speed[current_display_speed]); + } + + } +} + +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 (sfx_name == "1") + return; + + sfx_player->play(ao_app->get_sfx_suffix(sfx_name)); +} + +void Courtroom::set_scene() +{ + 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") + { + f_background = "defenseempty"; + if (is_ao2_bg) + f_desk_image = "defensedesk"; + else + f_desk_image = "bancodefensa"; + } + else if (f_side == "pro") + { + f_background = "prosecutorempty"; + if (is_ao2_bg) + f_desk_image = "prosecutiondesk"; + else + f_desk_image = "bancoacusacion"; + } + else if (f_side == "jud") + { + f_background = "judgestand"; + f_desk_image = "judgedesk"; + } + else if (f_side == "hld") + { + f_background = "helperstand"; + f_desk_image = "helperdesk"; + } + else if (f_side == "hlp") + { + 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")))) + { + 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")))) + { + f_background = "seancestand"; + f_desk_image = "seancedesk"; + } + else + { + if (is_ao2_bg) + f_desk_image = "stand"; + else + f_desk_image = "estrado"; + } + + ui_vp_background->set_image(f_background); + ui_vp_desk->set_image(f_desk_image); + ui_vp_legacy_desk->set_legacy_desk(f_desk_image); + + if (f_desk_mod == "0" || (f_desk_mod != "1" && + (f_side == "jud" || + f_side == "hld" || + f_side == "hlp"))) + { + ui_vp_desk->hide(); + ui_vp_legacy_desk->hide(); + } + else if (is_ao2_bg || (f_side == "jud" || + f_side == "hld" || + f_side == "hlp")) + { + 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) +{ + QString f_list = p_list.replace("|", ":").replace("*", "\n"); + + ui_server_chatlog->append(f_list); +} + +void Courtroom::set_mute(bool p_muted, int p_cid) +{ + if (p_cid != m_cid && p_cid != -1) + return; + + if (p_muted) + ui_muted->show(); + else + { + ui_muted->hide(); + ui_ic_chat_message->setFocus(); + } + + ui_muted->resize(ui_ic_chat_message->width(), ui_ic_chat_message->height()); + ui_muted->set_image("muted.png"); + + is_muted = p_muted; + ui_ic_chat_message->setEnabled(!p_muted); +} + +void Courtroom::set_ban(int p_cid) +{ + if (p_cid != m_cid && p_cid != -1) + return; + + call_notice("You have been banned."); + + ao_app->construct_lobby(); + ao_app->destruct_courtroom(); +} + +void Courtroom::handle_song(QStringList *p_contents) +{ + QStringList f_contents = *p_contents; + + if (f_contents.size() < 2) + 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(".")); + int n_char = f_contents.at(1).toInt(); + + if (n_char < 0 || n_char >= char_list.size()) + { + music_player->play(f_song); + } + 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 (!mute_map.value(n_char)) + { + chatlogpiece* temp = new chatlogpiece(str_char, str_show, f_song, true); + ic_chatlog_history.append(*temp); + + 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); + } + } +} + +void Courtroom::handle_wtce(QString p_wtce, int variant) +{ + QString sfx_file = "courtroom_sounds.ini"; + + //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(); + } + //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; + } + 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; + } + else if (variant == 1) { + sfx_player->play(ao_app->get_sfx("guilty")); + ui_vp_wtce->play("guilty"); + testimony_in_progress = false; + } + } +} + +void Courtroom::set_hp_bar(int p_bar, int p_state) +{ + if (p_state < 0 || p_state > 10) + return; + + if (p_bar == 1) + { + ui_defense_bar->set_image("defensebar" + QString::number(p_state) + ".png"); + defense_bar_state = p_state; + } + else if (p_bar == 2) + { + ui_prosecution_bar->set_image("prosecutionbar" + QString::number(p_state) + ".png"); + prosecution_bar_state = p_state; + } +} + +void Courtroom::toggle_judge_buttons(bool is_on) +{ + if (is_on) + { + ui_witness_testimony->show(); + ui_cross_examination->show(); + ui_guilty->show(); + ui_not_guilty->show(); + ui_defense_minus->show(); + ui_defense_plus->show(); + ui_prosecution_minus->show(); + ui_prosecution_plus->show(); + } + else + { + ui_witness_testimony->hide(); + ui_cross_examination->hide(); + ui_guilty->hide(); + ui_not_guilty->hide(); + ui_defense_minus->hide(); + ui_defense_plus->hide(); + ui_prosecution_minus->hide(); + ui_prosecution_plus->hide(); + } +} + +void Courtroom::mod_called(QString p_ip) +{ + ui_server_chatlog->append(p_ip); + if (ui_guard->isChecked()) + { + modcall_player->play(ao_app->get_sfx("mod_call")); + ao_app->alert(this); + } +} + +void Courtroom::case_called(QString msg, bool def, bool pro, bool jud, bool jur, bool steno) +{ + if (ui_casing->isChecked()) + { + ui_server_chatlog->append(msg); + if ((ao_app->get_casing_defence_enabled() && def) || + (ao_app->get_casing_prosecution_enabled() && pro) || + (ao_app->get_casing_judge_enabled() && jud) || + (ao_app->get_casing_juror_enabled() && jur) || + (ao_app->get_casing_steno_enabled() && steno)) + { + modcall_player->play(ao_app->get_sfx("case_call")); + ao_app->alert(this); + } + } +} + +void Courtroom::on_ooc_return_pressed() +{ + QString ooc_message = ui_ooc_chat_message->text(); + + if (ooc_message == "" || ui_ooc_chat_name->text() == "") + return; + + if (ooc_message.startsWith("/pos")) + { + if (ooc_message == "/pos jud") + { + toggle_judge_buttons(true); + } + else + { + 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"); + return; + } + else if (ooc_message.startsWith("/pair")) + { + ui_ooc_chat_message->clear(); + ooc_message.remove(0,6); + + bool ok; + int whom = ooc_message.toInt(&ok); + if (ok) + { + if (whom > -1) + { + other_charid = whom; + QString msg = "You will now pair up with "; + msg.append(char_list.at(whom).name); + msg.append(" if they also choose your character in return."); + append_server_chatmessage("CLIENT", msg, "1"); + } + else + { + append_server_chatmessage("CLIENT", "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.", "1"); + } + return; + } + else if (ooc_message.startsWith("/offset")) + { + ui_ooc_chat_message->clear(); + ooc_message.remove(0,8); + + bool ok; + int off = ooc_message.toInt(&ok); + if (ok) + { + if (off >= -100 && off <= 100) + { + offset_with_pair = off; + QString msg = "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"); + } + } + else + { + append_server_chatmessage("CLIENT", "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"); + 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"); + ao_app->cccc_ic_support_enabled = true; + ao_app->arup_enabled = true; + ao_app->modcall_reason_enabled = true; + on_reload_theme_clicked(); + ui_ooc_chat_message->clear(); + return; + } + else if (ooc_message.startsWith("/non_int_pre")) + { + if (ui_pre_non_interrupt->isChecked()) + append_server_chatmessage("CLIENT", "Your pre-animations interrupt again.", "1"); + else + append_server_chatmessage("CLIENT", "Your pre-animations will not interrupt text.", "1"); + ui_pre_non_interrupt->setChecked(!ui_pre_non_interrupt->isChecked()); + ui_ooc_chat_message->clear(); + return; + } + else if (ooc_message.startsWith("/save_chatlog")) + { + QFile file("chatlog.txt"); + + if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) + { + append_server_chatmessage("CLIENT", "Couldn't open chatlog.txt to write into.", "1"); + ui_ooc_chat_message->clear(); + return; + } + + QTextStream out(&file); + + foreach (chatlogpiece item, ic_chatlog_history) { + out << item.get_full() << '\n'; + } + + file.close(); + + append_server_chatmessage("CLIENT", "The IC chatlog has been saved.", "1"); + ui_ooc_chat_message->clear(); + return; + } + else if (ooc_message.startsWith("/load_case")) + { + QStringList command = ooc_message.split(" ", QString::SkipEmptyParts); + + QDir casefolder("base/cases"); + if (!casefolder.exists()) + { + 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.", "1"); + ui_ooc_chat_message->clear(); + return; + } + QStringList caseslist = casefolder.entryList(); + caseslist.removeOne("."); + caseslist.removeOne(".."); + caseslist.replaceInStrings(".ini",""); + + 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(", "), "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"); + ui_ooc_chat_message->clear(); + return; + } + + QSettings casefile("base/cases/" + command[1] + ".ini", QSettings::IniFormat); + + QString caseauth = casefile.value("author", "").value<QString>(); + QString casedoc = casefile.value("doc", "").value<QString>(); + QString cmdoc = casefile.value("cmdoc", "").value<QString>(); + QString casestatus = casefile.value("status", "").value<QString>(); + + if (!caseauth.isEmpty()) + append_server_chatmessage("CLIENT", "Case made by " + caseauth + ".", "1"); + if (!casedoc.isEmpty()) + ao_app->send_server_packet(new AOPacket("CT#" + ui_ooc_chat_name->text() + "#/doc " + casedoc + "#%")); + if (!casestatus.isEmpty()) + ao_app->send_server_packet(new AOPacket("CT#" + ui_ooc_chat_name->text() + "#/status " + casestatus + "#%")); + if (!cmdoc.isEmpty()) + append_server_chatmessage("CLIENT", "Navigate to " + cmdoc + " for the CM doc.", "1"); + + for (int i = local_evidence_list.size() - 1; i >= 0; i--) { + ao_app->send_server_packet(new AOPacket("DE#" + QString::number(i) + "#%")); + } + + foreach (QString evi, casefile.childGroups()) { + if (evi == "General") + continue; + + QStringList f_contents; + + f_contents.append(casefile.value(evi + "/name", "UNKNOWN").value<QString>()); + f_contents.append(casefile.value(evi + "/description", "UNKNOWN").value<QString>()); + f_contents.append(casefile.value(evi + "/image", "UNKNOWN.png").value<QString>()); + + ao_app->send_server_packet(new AOPacket("PE", f_contents)); + } + + append_server_chatmessage("CLIENT", "Your case \"" + command[1] + "\" was loaded!", "1"); + ui_ooc_chat_message->clear(); + return; + } + + QStringList packet_contents; + packet_contents.append(ui_ooc_chat_name->text()); + packet_contents.append(ooc_message); + + AOPacket *f_packet = new AOPacket("CT", packet_contents); + + if (server_ooc) + ao_app->send_server_packet(f_packet); + else + ao_app->send_ms_packet(f_packet); + + ui_ooc_chat_message->clear(); + + ui_ooc_chat_message->setFocus(); +} + +void Courtroom::on_ooc_toggle_clicked() +{ + if (server_ooc) + { + ui_ms_chatlog->show(); + ui_server_chatlog->hide(); + ui_ooc_toggle->setText("Master"); + + server_ooc = false; + } + else + { + ui_ms_chatlog->hide(); + ui_server_chatlog->show(); + ui_ooc_toggle->setText("Server"); + + server_ooc = true; + } +} + +void Courtroom::on_music_search_edited(QString p_text) +{ + //preventing compiler warnings + p_text += "a"; + list_music(); + list_areas(); +} + +void Courtroom::on_pos_dropdown_changed(int p_index) +{ + ui_ic_chat_message->setFocus(); + + if (p_index < 0 || p_index > 5) + return; + + toggle_judge_buttons(false); + + QString f_pos; + + 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"; + toggle_judge_buttons(true); + break; + case 4: + f_pos = "hld"; + break; + case 5: + f_pos = "hlp"; + break; + default: + f_pos = ""; + } + + if (f_pos == "" || ui_ooc_chat_name->text() == "") + return; + + ao_app->send_server_packet(new AOPacket("CT#" + ui_ooc_chat_name->text() + "#/pos " + f_pos + "#%")); +} + +void Courtroom::on_mute_list_clicked(QModelIndex p_index) +{ + QListWidgetItem *f_item = ui_mute_list->item(p_index.row()); + QString f_char = f_item->text(); + QString real_char; + + if (f_char.endsWith(" [x]")) + real_char = f_char.left(f_char.size() - 4); + else + real_char = f_char; + + int f_cid = -1; + + for (int n_char = 0 ; n_char < char_list.size() ; n_char++) + { + if (char_list.at(n_char).name == real_char) + f_cid = n_char; + } + + if (f_cid < 0 || f_cid >= char_list.size()) + { + qDebug() << "W: " << real_char << " not present in char_list"; + return; + } + + if (mute_map.value(f_cid)) + { + mute_map.insert(f_cid, false); + f_item->setText(real_char); + } + else + { + mute_map.insert(f_cid, true); + f_item->setText(real_char + " [x]"); + } +} + +void Courtroom::on_pair_list_clicked(QModelIndex p_index) +{ + QListWidgetItem *f_item = ui_pair_list->item(p_index.row()); + QString f_char = f_item->text(); + QString real_char; + + if (f_char.endsWith(" [x]")) + { + real_char = f_char.left(f_char.size() - 4); + f_item->setText(real_char); + } + else + real_char = f_char; + + int f_cid = -1; + + for (int n_char = 0 ; n_char < char_list.size() ; n_char++) + { + if (char_list.at(n_char).name == real_char) + f_cid = n_char; + } + + if (f_cid < 0 || f_cid >= char_list.size()) + { + qDebug() << "W: " << real_char << " not present in char_list"; + return; + } + + other_charid = f_cid; + + // Redo the character list. + QStringList sorted_pair_list; + + for (char_type i_char : char_list) + sorted_pair_list.append(i_char.name); + + sorted_pair_list.sort(); + + for (int i = 0; i < ui_pair_list->count(); i++) { + ui_pair_list->item(i)->setText(sorted_pair_list.at(i)); + } + + f_item->setText(real_char + " [x]"); +} + +void Courtroom::on_music_list_double_clicked(QModelIndex p_model) +{ + if (is_muted) + return; + + QString p_song = music_list.at(music_row_to_number.at(p_model.row())); + + 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); + } +} + +void Courtroom::on_area_list_double_clicked(QModelIndex p_model) +{ + 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); +} + +void Courtroom::on_hold_it_clicked() +{ + if (objection_state == 1) + { + ui_hold_it->set_image("holdit.png"); + 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_hold_it->set_image("holdit_selected.png"); + objection_state = 1; + } + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_objection_clicked() +{ + if (objection_state == 2) + { + ui_objection->set_image("objection.png"); + 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_objection->set_image("objection_selected.png"); + objection_state = 2; + } + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_take_that_clicked() +{ + if (objection_state == 3) + { + ui_take_that->set_image("takethat.png"); + 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_take_that->set_image("takethat_selected.png"); + objection_state = 3; + } + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_custom_objection_clicked() +{ + if (objection_state == 4) + { + ui_custom_objection->set_image("custom.png"); + 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_custom_objection->set_image("custom_selected.png"); + objection_state = 4; + } + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_realization_clicked() +{ + if (realization_state == 0) + { + realization_state = 1; + ui_realization->set_image("realization_pressed.png"); + } + else + { + realization_state = 0; + ui_realization->set_image("realization.png"); + } + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_mute_clicked() +{ + if (ui_mute_list->isHidden()) + { + 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"); + } + else + { + ui_mute_list->hide(); + ui_mute->set_image("mute.png"); + } +} + +void Courtroom::on_pair_clicked() +{ + if (ui_pair_list->isHidden()) + { + ui_pair_list->show(); + ui_pair_offset_spinbox->show(); + ui_mute_list->hide(); + ui_mute->set_image("mute.png"); + ui_pair_button->set_image("pair_button_pressed.png"); + } + else + { + ui_pair_list->hide(); + ui_pair_offset_spinbox->hide(); + ui_pair_button->set_image("pair_button.png"); + } +} + +void Courtroom::on_defense_minus_clicked() +{ + int f_state = defense_bar_state - 1; + + if (f_state >= 0) + ao_app->send_server_packet(new AOPacket("HP#1#" + QString::number(f_state) + "#%")); +} + +void Courtroom::on_defense_plus_clicked() +{ + int f_state = defense_bar_state + 1; + + if (f_state <= 10) + ao_app->send_server_packet(new AOPacket("HP#1#" + QString::number(f_state) + "#%")); +} + +void Courtroom::on_prosecution_minus_clicked() +{ + int f_state = prosecution_bar_state - 1; + + if (f_state >= 0) + ao_app->send_server_packet(new AOPacket("HP#2#" + QString::number(f_state) + "#%")); +} + +void Courtroom::on_prosecution_plus_clicked() +{ + int f_state = prosecution_bar_state + 1; + + if (f_state <= 10) + ao_app->send_server_packet(new AOPacket("HP#2#" + QString::number(f_state) + "#%")); +} + +void Courtroom::on_text_color_changed(int p_color) +{ + text_color = p_color; + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_music_slider_moved(int p_value) +{ + music_player->set_volume(p_value); + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_sfx_slider_moved(int p_value) +{ + sfx_player->set_volume(p_value); + objection_player->set_volume(p_value); + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_blip_slider_moved(int p_value) +{ + blip_player->set_volume(p_value); + ui_ic_chat_message->setFocus(); +} + +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_witness_testimony_clicked() +{ + if (is_muted) + return; + + ao_app->send_server_packet(new AOPacket("RT#testimony1#%")); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_cross_examination_clicked() +{ + if (is_muted) + return; + + ao_app->send_server_packet(new AOPacket("RT#testimony2#%")); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_not_guilty_clicked() +{ + if (is_muted) + return; + + ao_app->send_server_packet(new AOPacket("RT#judgeruling#0#%")); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_guilty_clicked() +{ + if (is_muted) + return; + + ao_app->send_server_packet(new AOPacket("RT#judgeruling#1#%")); + + ui_ic_chat_message->setFocus(); +} + +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); + + set_char_select(); + + ui_char_select_background->show(); + ui_spectator->hide(); +} + +void Courtroom::on_reload_theme_clicked() +{ + ao_app->reload_theme(); + + //to update status on the background + set_background(current_background); + enter_courtroom(m_cid); + + anim_state = 4; + text_state = 3; +} + +void Courtroom::on_back_to_lobby_clicked() +{ + ao_app->construct_lobby(); + ao_app->destruct_courtroom(); +} + +void Courtroom::on_char_select_left_clicked() +{ + --current_char_page; + set_char_select_page(); +} + +void Courtroom::on_char_select_right_clicked() +{ + ++current_char_page; + set_char_select_page(); +} + +void Courtroom::on_spectator_clicked() +{ + enter_courtroom(-1); + + ui_emotes->hide(); + + ui_char_select_background->hide(); +} + +void Courtroom::on_call_mod_clicked() +{ + if (ao_app->modcall_reason_enabled) { + QMessageBox errorBox; + QInputDialog input; + + input.setWindowFlags(Qt::WindowSystemMenuHint); + input.setLabelText("Reason:"); + input.setWindowTitle("Call Moderator"); + auto code = input.exec(); + + if (code != QDialog::Accepted) + return; + + QString text = input.textValue(); + if (text.isEmpty()) { + errorBox.critical(nullptr, "Error", "You must provide a reason."); + return; + } else if (text.length() > 256) { + errorBox.critical(nullptr, "Error", "The message is too long."); + return; + } + + QStringList mod_reason; + mod_reason.append(text); + + ao_app->send_server_packet(new AOPacket("ZZ", mod_reason)); + } else { + ao_app->send_server_packet(new AOPacket("ZZ#%")); + } + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_settings_clicked() +{ + ao_app->call_settings_menu(); +} + +void Courtroom::on_announce_casing_clicked() +{ + ao_app->call_announce_menu(this); +} + +void Courtroom::on_pre_clicked() +{ + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_flip_clicked() +{ + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_guard_clicked() +{ + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_showname_enable_clicked() +{ + ui_ic_chatlog->clear(); + first_message_sent = false; + + 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); + 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); + else + append_ic_text(item.get_message(), item.get_name()); + } + } + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_evidence_button_clicked() +{ + if (ui_evidence->isHidden()) + { + ui_evidence->show(); + ui_evidence_overlay->hide(); + } + else + { + ui_evidence->hide(); + } +} + +void Courtroom::on_switch_area_music_clicked() +{ + if (ui_area_list->isHidden()) + { + ui_area_list->show(); + ui_music_list->hide(); + } + else + { + ui_area_list->hide(); + ui_music_list->show(); + } +} + +void Courtroom::ping_server() +{ + ao_app->send_server_packet(new AOPacket("CH#" + QString::number(m_cid) + "#%")); +} + +void Courtroom::on_casing_clicked() +{ + if (ao_app->casing_alerts_enabled) + { + if (ui_casing->isChecked()) + { + QStringList f_packet; + + f_packet.append(ao_app->get_casing_can_host_cases()); + f_packet.append(QString::number(ao_app->get_casing_cm_enabled())); + f_packet.append(QString::number(ao_app->get_casing_defence_enabled())); + f_packet.append(QString::number(ao_app->get_casing_prosecution_enabled())); + f_packet.append(QString::number(ao_app->get_casing_judge_enabled())); + f_packet.append(QString::number(ao_app->get_casing_juror_enabled())); + f_packet.append(QString::number(ao_app->get_casing_steno_enabled())); + + ao_app->send_server_packet(new AOPacket("SETCASE", f_packet)); + } + else + ao_app->send_server_packet(new AOPacket("SETCASE#\"\"#0#0#0#0#0#0#%")); + } +} + +void Courtroom::announce_case(QString title, bool def, bool pro, bool jud, bool jur, bool steno) +{ + if (ao_app->casing_alerts_enabled) + { + QStringList f_packet; + + f_packet.append(title); + f_packet.append(QString::number(def)); + f_packet.append(QString::number(pro)); + f_packet.append(QString::number(jud)); + f_packet.append(QString::number(jur)); + f_packet.append(QString::number(steno)); + + ao_app->send_server_packet(new AOPacket("CASEA", f_packet)); + } +} + +Courtroom::~Courtroom() +{ + delete music_player; + delete sfx_player; + delete objection_player; + delete blip_player; +} + +#if (defined (_WIN32) || defined (_WIN64)) +void Courtroom::load_bass_opus_plugin() +{ + BASS_PluginLoad("bassopus.dll", 0); +} +#elif (defined (LINUX) || defined (__linux__)) +void Courtroom::load_bass_opus_plugin() +{ + BASS_PluginLoad("libbassopus.so", 0); +} +#else +#error This operating system is unsupported for bass plugins. +#endif diff --git a/src/debug_functions.cpp b/src/debug_functions.cpp new file mode 100644 index 00000000..77f2f35a --- /dev/null +++ b/src/debug_functions.cpp @@ -0,0 +1,25 @@ +#include "debug_functions.h" + +void call_error(QString p_message) +{ + QMessageBox *msgBox = new QMessageBox; + + msgBox->setText("Error: " + p_message); + msgBox->setWindowTitle("Error"); + + + //msgBox->setWindowModality(Qt::NonModal); + msgBox->exec(); +} + +void call_notice(QString p_message) +{ + QMessageBox *msgBox = new QMessageBox; + + msgBox->setText(p_message); + msgBox->setWindowTitle("Notice"); + + + //msgBox->setWindowModality(Qt::NonModal); + msgBox->exec(); +} diff --git a/src/discord_rich_presence.cpp b/src/discord_rich_presence.cpp new file mode 100644 index 00000000..10f5833e --- /dev/null +++ b/src/discord_rich_presence.cpp @@ -0,0 +1,103 @@ +#include "discord_rich_presence.h" + +namespace AttorneyOnline { + +Discord::Discord() +{ + DiscordEventHandlers handlers; + std::memset(&handlers, 0, sizeof(handlers)); + handlers = {}; + handlers.ready = [] { + qInfo() << "Discord RPC ready"; + }; + handlers.disconnected = [](int errorCode, const char* message) { + qInfo() << "Discord RPC disconnected! " << message; + }; + handlers.errored = [](int errorCode, const char* message) { + qWarning() << "Discord RPC errored out! " << message; + }; + qInfo() << "Initializing Discord RPC"; + Discord_Initialize(APPLICATION_ID, &handlers, 1, nullptr); +} + +Discord::~Discord() +{ + Discord_Shutdown(); +} + +void Discord::state_lobby() +{ + DiscordRichPresence presence; + std::memset(&presence, 0, sizeof(presence)); + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + + presence.state = "In Lobby"; + presence.details = "Idle"; + Discord_UpdatePresence(&presence); +} + +void Discord::state_server(std::string name, std::string server_id) +{ + qDebug() << "Discord RPC: Setting server state"; + + DiscordRichPresence presence; + std::memset(&presence, 0, sizeof(presence)); + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + + auto timestamp = static_cast<int64_t>(std::time(nullptr)); + + presence.state = "In a Server"; + presence.details = name.c_str(); + presence.matchSecret = server_id.c_str(); + presence.startTimestamp = this->timestamp; + + this->server_id = server_id; + this->server_name = name; + this->timestamp = timestamp; + Discord_UpdatePresence(&presence); +} + +void Discord::state_character(std::string name) +{ + auto name_internal = QString(name.c_str()).toLower().replace(' ', '_').toStdString(); + auto name_friendly = QString(name.c_str()).replace('_', ' ').toStdString(); + const std::string playing_as = "Playing as " + name_friendly; + qDebug() << "Discord RPC: Setting character state (" << playing_as.c_str() << ")"; + + DiscordRichPresence presence; + std::memset(&presence, 0, sizeof(presence)); + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + presence.details = this->server_name.c_str(); + presence.matchSecret = this->server_id.c_str(); + presence.startTimestamp = this->timestamp; + + presence.state = playing_as.c_str(); + presence.smallImageKey = name_internal.c_str(); + // presence.smallImageText = name_internal.c_str(); + Discord_UpdatePresence(&presence); +} + +void Discord::state_spectate() +{ + qDebug() << "Discord RPC: Setting specator state"; + + DiscordRichPresence presence; + std::memset(&presence, 0, sizeof(presence)); + presence.largeImageKey = "ao2-logo"; + presence.largeImageText = "Objection!"; + presence.instance = 1; + presence.details = this->server_name.c_str(); + presence.matchSecret = this->server_id.c_str(); + presence.startTimestamp = this->timestamp; + + presence.state = "Spectating"; + Discord_UpdatePresence(&presence); +} + +} diff --git a/src/emotes.cpp b/src/emotes.cpp new file mode 100644 index 00000000..b6a217e6 --- /dev/null +++ b/src/emotes.cpp @@ -0,0 +1,174 @@ +#include "courtroom.h" + +#include "aoemotebutton.h" + +void Courtroom::construct_emotes() +{ + ui_emotes = new QWidget(this); + + set_size_and_pos(ui_emotes, "emotes"); + + QPoint f_spacing = ao_app->get_button_spacing("emote_button_spacing", "courtroom_design.ini"); + + const int button_width = 40; + int x_spacing = f_spacing.x(); + int x_mod_count = 0; + + const int button_height = 40; + int y_spacing = f_spacing.y(); + int y_mod_count = 0; + + emote_columns = ((ui_emotes->width() - button_width) / (x_spacing + button_width)) + 1; + emote_rows = ((ui_emotes->height() - button_height) / (y_spacing + button_height)) + 1; + + max_emotes_on_page = emote_columns * emote_rows; + + for (int n = 0 ; n < max_emotes_on_page ; ++n) + { + 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); + + ui_emote_list.append(f_emote); + + f_emote->set_id(n); + + connect(f_emote, SIGNAL(emote_clicked(int)), this, SLOT(on_emote_clicked(int))); + + ++x_mod_count; + + if (x_mod_count == emote_columns) + { + ++y_mod_count; + x_mod_count = 0; + } + } +} + +void Courtroom::set_emote_page() +{ + if (m_cid == -1) + return; + + int total_emotes = ao_app->get_emote_number(current_char); + + ui_emote_left->hide(); + ui_emote_right->hide(); + + for (AOEmoteButton *i_button : ui_emote_list) + { + i_button->hide(); + } + + int total_pages = total_emotes / max_emotes_on_page; + int emotes_on_page = 0; + + if (total_emotes % max_emotes_on_page != 0) + { + ++total_pages; + //i. e. not on the last page + if (total_pages > current_emote_page + 1) + emotes_on_page = max_emotes_on_page; + else + emotes_on_page = total_emotes % max_emotes_on_page; + + } + else + emotes_on_page = max_emotes_on_page; + + if (total_pages > current_emote_page + 1) + ui_emote_right->show(); + + if (current_emote_page > 0) + ui_emote_left->show(); + + for (int n_emote = 0 ; n_emote < emotes_on_page ; ++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"); + else + f_emote->set_image(current_char, n_real_emote, "_off.png"); + + f_emote->show(); + } + +} + +void Courtroom::set_emote_dropdown() +{ + ui_emote_dropdown->clear(); + + int total_emotes = ao_app->get_emote_number(current_char); + QStringList emote_list; + + for (int n = 0 ; n < total_emotes ; ++n) + { + emote_list.append(ao_app->get_emote_comment(current_char, n)); + } + + ui_emote_dropdown->addItems(emote_list); +} + +void Courtroom::select_emote(int p_id) +{ + int min = current_emote_page * max_emotes_on_page; + int max = (max_emotes_on_page - 1) + current_emote_page * max_emotes_on_page; + + 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"); + + int old_emote = current_emote; + + current_emote = 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"); + + 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); + + ui_emote_dropdown->setCurrentIndex(current_emote); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_emote_clicked(int p_id) +{ + select_emote(p_id + max_emotes_on_page * current_emote_page); +} + +void Courtroom::on_emote_left_clicked() +{ + --current_emote_page; + + set_emote_page(); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_emote_right_clicked() +{ + qDebug() << "emote right clicked"; + ++current_emote_page; + + set_emote_page(); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_emote_dropdown_changed(int p_index) +{ + select_emote(p_index); +} diff --git a/src/encryption_functions.cpp b/src/encryption_functions.cpp new file mode 100644 index 00000000..ffbe0cdb --- /dev/null +++ b/src/encryption_functions.cpp @@ -0,0 +1,63 @@ +#include "encryption_functions.h" + +#include "hex_functions.h" + +QString fanta_encrypt(QString temp_input, unsigned int p_key) +{ + //using standard stdlib types is actually easier here because of implicit char<->int conversion + //which in turn makes encryption arithmetic easier + + unsigned int key = p_key; + unsigned int C1 = 53761; + unsigned int C2 = 32618; + + QVector<uint_fast8_t> temp_result; + std::string input = temp_input.toUtf8().constData(); + + for (unsigned int pos = 0 ; pos < input.size() ; ++pos) + { + uint_fast8_t output = input.at(pos) ^ (key >> 8) % 256; + temp_result.append(output); + key = (temp_result.at(pos) + key) * C1 + C2; + } + + std::string result = ""; + + for (uint_fast8_t i_int : temp_result) + { + result += omni::int_to_hex(i_int); + } + + QString final_result = QString::fromStdString(result); + + return final_result; +} + +QString fanta_decrypt(QString temp_input, unsigned int key) +{ + std::string input = temp_input.toUtf8().constData(); + + QVector<unsigned int> unhexed_vector; + + for(unsigned int i=0; i< input.length(); i+=2) + { + std::string byte = input.substr(i,2); + unsigned int hex_int = strtoul(byte.c_str(), nullptr, 16); + unhexed_vector.append(hex_int); + } + + unsigned int C1 = 53761; + unsigned int C2 = 32618; + + std::string result = ""; + + for (int pos = 0 ; pos < unhexed_vector.size() ; ++pos) + { + unsigned char output = unhexed_vector.at(pos) ^ (key >> 8) % 256; + result += output; + key = (unhexed_vector.at(pos) + key) * C1 + C2; + } + + return QString::fromStdString(result); + +} diff --git a/src/evidence.cpp b/src/evidence.cpp new file mode 100644 index 00000000..4e796642 --- /dev/null +++ b/src/evidence.cpp @@ -0,0 +1,345 @@ +#include "courtroom.h" + +void Courtroom::construct_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_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_overlay = new AOImage(ui_evidence, ao_app); + + ui_evidence_delete = new AOButton(ui_evidence_overlay, ao_app); + 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_description = new AOTextEdit(ui_evidence_overlay); + ui_evidence_description->setStyleSheet("background-color: rgba(0, 0, 0, 0);" + "color: white;"); + + set_size_and_pos(ui_evidence, "evidence_background"); + set_size_and_pos(ui_evidence_buttons, "evidence_buttons"); + + QPoint f_spacing = ao_app->get_button_spacing("evidence_button_spacing", "courtroom_design.ini"); + + const int button_width = 70; + int x_spacing = f_spacing.x(); + int x_mod_count = 0; + + const int button_height = 70; + int y_spacing = f_spacing.y(); + int y_mod_count = 0; + + evidence_columns = ((ui_evidence_buttons->width() - button_width) / (x_spacing + button_width)) + 1; + evidence_rows = ((ui_evidence_buttons->height() - button_height) / (y_spacing + button_height)) + 1; + + max_evidence_on_page = evidence_columns * evidence_rows; + + for (int n = 0 ; n < max_evidence_on_page ; ++n) + { + 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); + + ui_evidence_list.append(f_evidence); + + f_evidence->set_id(n); + + connect(f_evidence, SIGNAL(evidence_clicked(int)), this, SLOT(on_evidence_clicked(int))); + connect(f_evidence, SIGNAL(evidence_double_clicked(int)), this, SLOT(on_evidence_double_clicked(int))); + connect(f_evidence, SIGNAL(on_hover(int, bool)), this, SLOT(on_evidence_hover(int, bool))); + + ++x_mod_count; + + if (x_mod_count == evidence_columns) + { + ++y_mod_count; + 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) +{ + local_evidence_list.clear(); + local_evidence_list = p_evi_list; + + set_evidence_page(); +} + +void Courtroom::set_evidence_page() +{ + int total_evidence = local_evidence_list.size(); + + ui_evidence_left->hide(); + ui_evidence_right->hide(); + + for (AOEvidenceButton *i_button : ui_evidence_list) + { + i_button->reset(); + } + + //to account for the "add evidence" button + ++total_evidence; + + int total_pages = total_evidence / max_evidence_on_page; + int evidence_on_page = 0; + + if ((total_evidence % max_evidence_on_page) != 0) + { + ++total_pages; + //i. e. not on the last page + if (total_pages > current_evidence_page + 1) + evidence_on_page = max_evidence_on_page; + else + evidence_on_page = total_evidence % max_evidence_on_page; + + } + else + evidence_on_page = max_evidence_on_page; + + if (total_pages > current_evidence_page + 1) + ui_evidence_right->show(); + + if (current_evidence_page > 0) + ui_evidence_left->show(); + + for (int n_evidence_button = 0 ; n_evidence_button < evidence_on_page ; ++n_evidence_button) + { + int n_real_evidence = n_evidence_button + current_evidence_page * max_evidence_on_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_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); + } + else + f_evidence_button->set_image(""); + + f_evidence_button->show(); + } +} + +void Courtroom::on_evidence_name_edited() +{ + 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); + + ao_app->send_server_packet(new AOPacket("EE", f_contents)); +} + +void Courtroom::on_evidence_image_name_edited() +{ + 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() +{ + QFileDialog dialog(this); + dialog.setFileMode(QFileDialog::ExistingFile); + dialog.setNameFilter(tr("Images (*.png)")); + dialog.setViewMode(QFileDialog::List); + dialog.setDirectory(ao_app->get_base_path() + "evidence"); + + QStringList filenames; + + if (dialog.exec()) + filenames = dialog.selectedFiles(); + + if (filenames.size() != 1) + return; + + QString filename = filenames.at(0); + + QStringList split_filename = filename.split("/"); + + filename = split_filename.at(split_filename.size() - 1); + + ui_evidence_image_name->setText(filename); + + on_evidence_image_name_edited(); +} + +void Courtroom::on_evidence_clicked(int p_id) +{ + ui_evidence_name->setReadOnly(true); + + 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#%")); + return; + } + else if (f_real_id > local_evidence_list.size()) + return; + + ui_evidence_name->setText(local_evidence_list.at(f_real_id).name); + + for (AOEvidenceButton *i_button : ui_evidence_list) + i_button->set_selected(false); + + ui_evidence_list.at(p_id)->set_selected(true); + + current_evidence = f_real_id; + + ui_ic_chat_message->setFocus(); + +} + +void Courtroom::on_evidence_double_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()) + return; + + current_evidence = f_real_id; + + evi_type f_evi = local_evidence_list.at(f_real_id); + + ui_evidence_description->clear(); + ui_evidence_description->appendPlainText(f_evi.description); + + ui_evidence_image_name->setText(f_evi.image); + + ui_evidence_overlay->show(); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_evidence_hover(int p_id, bool p_state) +{ + ui_evidence_name->setReadOnly(true); + int final_id = p_id + max_evidence_on_page * current_evidence_page; + + if (p_state) + { + if (final_id == local_evidence_list.size()) + ui_evidence_name->setText("Add new evidence..."); + else if (final_id < local_evidence_list.size()) + ui_evidence_name->setText(local_evidence_list.at(final_id).name); + } + else if (current_evidence < local_evidence_list.size()) + ui_evidence_name->setText(local_evidence_list.at(current_evidence).name); + else + ui_evidence_name->setText(""); +} + +void Courtroom::on_evidence_left_clicked() +{ + --current_evidence_page; + + set_evidence_page(); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_evidence_right_clicked() +{ + ++current_evidence_page; + + set_evidence_page(); + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_evidence_present_clicked() +{ + if (is_presenting_evidence) + ui_evidence_present->set_image("present_disabled.png"); + else + ui_evidence_present->set_image("present.png"); + + is_presenting_evidence = !is_presenting_evidence; + + ui_ic_chat_message->setFocus(); +} + +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) + "#%")); + + current_evidence = 0; + + ui_ic_chat_message->setFocus(); +} + +void Courtroom::on_evidence_x_clicked() +{ + ui_evidence_description->setReadOnly(true); + ui_evidence_overlay->hide(); + + 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(ui_evidence_description->toPlainText()); + f_contents.append(f_evi.image); + + ao_app->send_server_packet(new AOPacket("EE", f_contents)); + + ui_ic_chat_message->setFocus(); +} + diff --git a/src/file_functions.cpp b/src/file_functions.cpp new file mode 100644 index 00000000..f93ee15d --- /dev/null +++ b/src/file_functions.cpp @@ -0,0 +1,21 @@ +#include "file_functions.h" + +bool file_exists(QString file_path) +{ + QFileInfo check_file(file_path); + + return check_file.exists() && check_file.isFile(); +} + +bool dir_exists(QString dir_path) +{ + QDir check_dir(dir_path); + + return check_dir.exists(); +} + +bool exists(QString p_path) { + QFile file(p_path); + + return file.exists(); +} diff --git a/src/hardware_functions.cpp b/src/hardware_functions.cpp new file mode 100644 index 00000000..29823cdf --- /dev/null +++ b/src/hardware_functions.cpp @@ -0,0 +1,57 @@ +#include "hardware_functions.h" + +#include <QDebug> + +#if (defined (_WIN32) || defined (_WIN64)) +#include <windows.h> + +DWORD dwVolSerial; +BOOL bIsRetrieved; + +QString get_hdid() +{ + bIsRetrieved = GetVolumeInformation(TEXT("C:\\"), NULL, NULL, &dwVolSerial, NULL, NULL, NULL, NULL); + + if (bIsRetrieved) + return QString::number(dwVolSerial, 16); + else + //a totally random string + //what could possibly go wrong + return "gxsps32sa9fnwic92mfbs0"; + +} + +#elif (defined (LINUX) || defined (__linux__)) + +#include <QFile> +#include <QTextStream> + +QString get_hdid() +{ + QFile fstab_file("/etc/fstab"); + if (!fstab_file.open(QIODevice::ReadOnly)) + return "gxcps32sa9fnwic92mfbs0"; + + QTextStream in(&fstab_file); + + while(!in.atEnd()) + { + QString line = in.readLine(); + + if (line.startsWith("UUID")) + { + QStringList line_elements = line.split("="); + + if (line_elements.size() > 1) + return line_elements.at(1).left(23).trimmed(); + } + } + + return "gxcpz32sa9fnwic92mfbs0"; +} + +#else + +#error This operating system is unsupported for hardware functions. + +#endif diff --git a/src/hex_functions.cpp b/src/hex_functions.cpp new file mode 100644 index 00000000..4a58d2b4 --- /dev/null +++ b/src/hex_functions.cpp @@ -0,0 +1,18 @@ +#include "hex_functions.h" + +namespace omni +{ + std::string int_to_hex(unsigned int input) + { + if (input > 255) + return "FF"; + + std::stringstream stream; + stream << std::setfill('0') << std::setw(sizeof(char)*2) + << std::hex << input; + std::string result(stream.str()); + std::transform(result.begin(), result.end(), result.begin(), ::toupper); + + return result; + } +} diff --git a/src/lobby.cpp b/src/lobby.cpp new file mode 100644 index 00000000..4a96e567 --- /dev/null +++ b/src/lobby.cpp @@ -0,0 +1,388 @@ +#include "lobby.h" + +#include "debug_functions.h" +#include "aoapplication.h" +#include "networkmanager.h" +#include "aosfxplayer.h" + +Lobby::Lobby(AOApplication *p_ao_app) : QMainWindow() +{ + ao_app = p_ao_app; + + this->setWindowTitle("Attorney Online 2"); + this->setWindowIcon(QIcon(":/logo.png")); + + ui_background = new AOImage(this, ao_app); + ui_public_servers = new AOButton(this, ao_app); + ui_favorites = new AOButton(this, ao_app); + ui_refresh = new AOButton(this, ao_app); + ui_add_to_fav = new AOButton(this, ao_app); + 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_player_count = new QLabel(this); + ui_description = new AOTextArea(this); + ui_chatbox = new AOTextArea(this); + ui_chatbox->setOpenExternalLinks(true); + ui_chatname = new QLineEdit(this); + ui_chatname->setPlaceholderText("Name"); + ui_chatname->setText(ao_app->get_ooc_name()); + ui_chatmessage = new QLineEdit(this); + ui_loading_background = new AOImage(this, ao_app); + ui_loading_text = new QTextEdit(ui_loading_background); + ui_progress_bar = new QProgressBar(ui_loading_background); + ui_progress_bar->setMinimum(0); + ui_progress_bar->setMaximum(100); + ui_progress_bar->setStyleSheet("QProgressBar{ color: white; }"); + ui_cancel = new AOButton(ui_loading_background, ao_app); + + connect(ui_public_servers, SIGNAL(clicked()), this, SLOT(on_public_servers_clicked())); + connect(ui_favorites, SIGNAL(clicked()), this, SLOT(on_favorites_clicked())); + connect(ui_refresh, SIGNAL(pressed()), this, SLOT(on_refresh_pressed())); + connect(ui_refresh, SIGNAL(released()), this, SLOT(on_refresh_released())); + connect(ui_add_to_fav, SIGNAL(pressed()), this, SLOT(on_add_to_fav_pressed())); + connect(ui_add_to_fav, SIGNAL(released()), this, SLOT(on_add_to_fav_released())); + 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_chatmessage, SIGNAL(returnPressed()), this, SLOT(on_chatfield_return_pressed())); + connect(ui_cancel, SIGNAL(clicked()), ao_app, SLOT(loading_cancelled())); + + ui_connect->setEnabled(false); + + list_servers(); + + set_widgets(); +} + +//sets images, position and size +void Lobby::set_widgets() +{ + ao_app->reload_theme(); + + QString filename = "lobby_design.ini"; + + pos_size_type f_lobby = ao_app->get_element_dimensions("lobby", filename); + + if (f_lobby.width < 0 || f_lobby.height < 0) + { + 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?"); + + this->resize(517, 666); + } + else + { + this->resize(f_lobby.width, f_lobby.height); + } + + set_size_and_pos(ui_background, "lobby"); + ui_background->set_image("lobbybackground.png"); + + set_size_and_pos(ui_public_servers, "public_servers"); + ui_public_servers->set_image("publicservers_selected.png"); + + set_size_and_pos(ui_favorites, "favorites"); + ui_favorites->set_image("favorites.png"); + + set_size_and_pos(ui_refresh, "refresh"); + ui_refresh->set_image("refresh.png"); + + set_size_and_pos(ui_add_to_fav, "add_to_fav"); + ui_add_to_fav->set_image("addtofav.png"); + + set_size_and_pos(ui_connect, "connect"); + ui_connect->set_image("connect.png"); + + set_size_and_pos(ui_version, "version"); + ui_version->setText("Version: " + ao_app->get_version_string()); + + set_size_and_pos(ui_about, "about"); + ui_about->set_image("about.png"); + + 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_player_count, "player_count"); + ui_player_count->setText("Offline"); + ui_player_count->setStyleSheet("font: bold;" + "color: white;" + "qproperty-alignment: AlignCenter;"); + + set_size_and_pos(ui_description, "description"); + ui_description->setReadOnly(true); + ui_description->setStyleSheet("background-color: rgba(0, 0, 0, 0);" + "color: white;"); + + set_size_and_pos(ui_chatbox, "chatbox"); + ui_chatbox->setReadOnly(true); + ui_chatbox->setStyleSheet("QTextBrowser{background-color: rgba(0, 0, 0, 0);}"); + + set_size_and_pos(ui_chatname, "chatname"); + ui_chatname->setStyleSheet("background-color: rgba(0, 0, 0, 0);" + "selection-background-color: rgba(0, 0, 0, 0);"); + + set_size_and_pos(ui_chatmessage, "chatmessage"); + ui_chatmessage->setStyleSheet("background-color: rgba(0, 0, 0, 0);" + "selection-background-color: rgba(0, 0, 0, 0);"); + + ui_loading_background->resize(this->width(), this->height()); + ui_loading_background->set_image("loadingbackground.png"); + + + set_size_and_pos(ui_loading_text, "loading_label"); + ui_loading_text->setFont(QFont("Arial", 20, QFont::Bold)); + ui_loading_text->setReadOnly(true); + ui_loading_text->setAlignment(Qt::AlignCenter); + ui_loading_text->setFrameStyle(QFrame::NoFrame); + ui_loading_text->setStyleSheet("background-color: rgba(0, 0, 0, 0);" + "color: rgba(255, 128, 0, 255);"); + ui_loading_text->append("Loading"); + + set_size_and_pos(ui_progress_bar, "progress_bar"); + set_size_and_pos(ui_cancel, "cancel"); + ui_cancel->setText("Cancel"); + + ui_loading_background->hide(); + +} + +void Lobby::set_size_and_pos(QWidget *p_widget, QString p_identifier) +{ + QString filename = "lobby_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; + p_widget->hide(); + } + else + { + p_widget->move(design_ini_result.x, design_ini_result.y); + p_widget->resize(design_ini_result.width, design_ini_result.height); + } +} + +void Lobby::set_loading_text(QString p_text) +{ + ui_loading_text->clear(); + ui_loading_text->setAlignment(Qt::AlignCenter); + ui_loading_text->append(p_text); +} + +QString Lobby::get_chatlog() +{ + QString return_value = ui_chatbox->toPlainText(); + + return return_value; +} + +int Lobby::get_selected_server() +{ + return ui_server_list->currentRow(); +} + +void Lobby::set_loading_value(int p_value) +{ + ui_progress_bar->setValue(p_value); +} + +void Lobby::on_public_servers_clicked() +{ + ui_public_servers->set_image("publicservers_selected.png"); + ui_favorites->set_image("favorites.png"); + + list_servers(); + + public_servers_selected = true; +} + +void Lobby::on_favorites_clicked() +{ + ui_favorites->set_image("favorites_selected.png"); + ui_public_servers->set_image("publicservers.png"); + + ao_app->set_favorite_list(); + //ao_app->favorite_list = read_serverlist_txt(); + + list_favorites(); + + public_servers_selected = false; +} + +void Lobby::on_refresh_pressed() +{ + ui_refresh->set_image("refresh_pressed.png"); +} + +void Lobby::on_refresh_released() +{ + ui_refresh->set_image("refresh.png"); + + AOPacket *f_packet = new AOPacket("ALL#%"); + + ao_app->send_ms_packet(f_packet); +} + +void Lobby::on_add_to_fav_pressed() +{ + ui_add_to_fav->set_image("addtofav_pressed.png"); +} + +void Lobby::on_add_to_fav_released() +{ + ui_add_to_fav->set_image("addtofav.png"); + + //you cant add favorites from favorites m8 + if (!public_servers_selected) + return; + + ao_app->add_favorite_server(ui_server_list->currentRow()); +} + +void Lobby::on_connect_pressed() +{ + ui_connect->set_image("connect_pressed.png"); +} + +void Lobby::on_connect_released() +{ + ui_connect->set_image("connect.png"); + + AOPacket *f_packet; + + f_packet = new AOPacket("askchaa#%"); + + ao_app->send_server_packet(f_packet); +} + +void Lobby::on_about_clicked() +{ + QString msg = tr("<h2>Attorney Online %1</h2>" + "The courtroom drama simulator" + "<p><b>Source code:</b> " + "<a href='https://github.com/AttorneyOnline/AO2-Client'>" + "https://github.com/AttorneyOnline/AO2-Client</a>" + "<p><b>Major development:</b><br>" + "OmniTroid, stonedDiscord, longbyte1, gameboyprinter, Cerapter" + "<p><b>Special thanks:</b><br>" + "Remy, Iamgoofball, Hibiki, Qubrick (webAO), Ruekasu (UI design), " + "Draxirch (UI design), Unishred, Argoneus (tsuserver), Fiercy, " + "Noevain, Cronnicossy") + .arg(ao_app->get_version_string()); + QMessageBox::about(this, "About", msg); +} + +void Lobby::on_server_list_clicked(QModelIndex p_model) +{ + server_type f_server; + int n_server = p_model.row(); + + if (n_server < 0) + return; + + if (public_servers_selected) + { + QVector<server_type> f_server_list = ao_app->get_server_list(); + + 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 = ao_app->get_favorite_list().at(p_model.row()); + } + + ui_description->clear(); + ui_description->append(f_server.desc); + + ui_description->moveCursor(QTextCursor::Start); + ui_description->ensureCursorVisible(); + + ui_player_count->setText("Offline"); + + ui_connect->setEnabled(false); + + ao_app->net_manager->connect_to_server(f_server); +} + +void Lobby::on_chatfield_return_pressed() +{ + //no you can't send empty messages + if (ui_chatname->text() == "" || ui_chatmessage->text() == "") + return; + + + QString f_header = "CT"; + QStringList f_contents{ui_chatname->text(), ui_chatmessage->text()}; + + AOPacket *f_packet = new AOPacket(f_header, f_contents); + + ao_app->send_ms_packet(f_packet); + + ui_chatmessage->clear(); +} + +void Lobby::list_servers() +{ + public_servers_selected = true; + ui_favorites->set_image("favorites.png"); + ui_public_servers->set_image("publicservers_selected.png"); + + ui_server_list->clear(); + + for (server_type i_server : ao_app->get_server_list()) + { + ui_server_list->addItem(i_server.name); + } +} + +void Lobby::list_favorites() +{ + ui_server_list->clear(); + + for (server_type i_server : ao_app->get_favorite_list()) + { + ui_server_list->addItem(i_server.name); + } +} + +void Lobby::append_chatmessage(QString f_name, QString f_message) +{ + ui_chatbox->append_chatmessage(f_name, f_message, ao_app->get_color("ooc_default_color", "courtroom_design.ini").name()); +} + +void Lobby::append_error(QString f_message) +{ + ui_chatbox->append_error(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); + ui_player_count->setText(f_string); +} + +void Lobby::enable_connect_button() +{ + ui_connect->setEnabled(true); +} + +Lobby::~Lobby() +{ + +} diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 00000000..cf51b0af --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,25 @@ + +#include "aoapplication.h" + +#include "datatypes.h" +#include "networkmanager.h" +#include "lobby.h" +#include "courtroom.h" +#include <QPluginLoader> +#include <QDebug> +Q_IMPORT_PLUGIN(ApngImagePlugin); +int main(int argc, char *argv[]) +{ +#if QT_VERSION > QT_VERSION_CHECK(5, 6, 0) + // High-DPI support is for Qt version >=5.6. + // However, many Linux distros still haven't brought their stable/LTS + // packages up to Qt 5.6, so this is conditional. + AOApplication::setAttribute(Qt::AA_EnableHighDpiScaling); +#endif + + AOApplication main_app(argc, argv); + main_app.construct_lobby(); + main_app.net_manager->connect_to_master(); + main_app.w_lobby->show(); + return main_app.exec(); +} diff --git a/src/misc_functions.cpp b/src/misc_functions.cpp new file mode 100644 index 00000000..23520551 --- /dev/null +++ b/src/misc_functions.cpp @@ -0,0 +1,9 @@ +#include "misc_functions.h" + +void delay(int p_milliseconds) +{ + QTime dieTime = QTime::currentTime().addMSecs(p_milliseconds); + + while(QTime::currentTime() < dieTime) + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); +} diff --git a/src/networkmanager.cpp b/src/networkmanager.cpp new file mode 100644 index 00000000..288a9007 --- /dev/null +++ b/src/networkmanager.cpp @@ -0,0 +1,252 @@ +#include "networkmanager.h" + +#include "datatypes.h" +#include "debug_functions.h" +#include "lobby.h" + +NetworkManager::NetworkManager(AOApplication *parent) : QObject(parent) +{ + ao_app = parent; + + ms_socket = new QTcpSocket(this); + server_socket = new QTcpSocket(this); + + ms_reconnect_timer = new QTimer(this); + ms_reconnect_timer->setSingleShot(true); + QObject::connect(ms_reconnect_timer, SIGNAL(timeout()), this, SLOT(retry_ms_connect())); + + QObject::connect(ms_socket, SIGNAL(readyRead()), this, SLOT(handle_ms_packet())); + QObject::connect(server_socket, SIGNAL(readyRead()), this, SLOT(handle_server_packet())); + QObject::connect(server_socket, SIGNAL(disconnected()), ao_app, SLOT(server_disconnected())); + + QString master_config = ao_app->configini->value("master", "").value<QString>(); + if (master_config != "") + ms_nosrv_hostname = master_config; +} + +NetworkManager::~NetworkManager() +{ + +} + +void NetworkManager::connect_to_master() +{ + ms_socket->close(); + ms_socket->abort(); + +#ifdef MS_FAILOVER_SUPPORTED + perform_srv_lookup(); +#else + connect_to_master_nosrv(); +#endif +} + +void NetworkManager::connect_to_master_nosrv() +{ + QObject::connect(ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_ms_socket_error(QAbstractSocket::SocketError))); + + QObject::connect(ms_socket, SIGNAL(connected()), + this, SLOT(on_ms_nosrv_connect_success())); + ms_socket->connectToHost(ms_nosrv_hostname, ms_port); +} + +void NetworkManager::connect_to_server(server_type p_server) +{ + server_socket->close(); + server_socket->abort(); + + server_socket->connectToHost(p_server.ip, p_server.port); +} + +void NetworkManager::ship_ms_packet(QString p_packet) +{ + if (!ms_socket->isOpen()) + { + retry_ms_connect(); + } + else + { + ms_socket->write(p_packet.toUtf8()); + } +} + +void NetworkManager::ship_server_packet(QString p_packet) +{ + server_socket->write(p_packet.toUtf8()); +} + +void NetworkManager::handle_ms_packet() +{ + char buffer[buffer_max_size]; + std::memset(buffer, 0, buffer_max_size); + ms_socket->read(buffer, buffer_max_size); + + QString in_data = buffer; + + if (!in_data.endsWith("%")) + { + ms_partial_packet = true; + ms_temp_packet += in_data; + return; + } + + else + { + if (ms_partial_packet) + { + in_data = ms_temp_packet + in_data; + ms_temp_packet = ""; + ms_partial_packet = false; + } + } + + QStringList packet_list = in_data.split("%", QString::SplitBehavior(QString::SkipEmptyParts)); + + for (QString packet : packet_list) + { + AOPacket *f_packet = new AOPacket(packet); + + ao_app->ms_packet_received(f_packet); + } +} + + +void NetworkManager::perform_srv_lookup() +{ + #ifdef MS_FAILOVER_SUPPORTED + ms_dns = new QDnsLookup(QDnsLookup::SRV, ms_srv_hostname, this); + + connect(ms_dns, SIGNAL(finished()), this, SLOT(on_srv_lookup())); + ms_dns->lookup(); + #endif +} + +void NetworkManager::on_srv_lookup() +{ + #ifdef MS_FAILOVER_SUPPORTED + bool connected = false; + if (ms_dns->error() != QDnsLookup::NoError) + { + qWarning("SRV lookup of the master server DNS failed."); + ms_dns->deleteLater(); + } + else + { + const auto srv_records = ms_dns->serviceRecords(); + + for (const QDnsServiceRecord &record : srv_records) + { + qDebug() << "Connecting to " << record.target() << ":" << record.port(); + ms_socket->connectToHost(record.target(), record.port()); + QTime timer; + timer.start(); + do + { + ao_app->processEvents(); + if (ms_socket->state() == QAbstractSocket::ConnectedState) + { + connected = true; + break; + } + else if (ms_socket->state() != QAbstractSocket::ConnectingState + && ms_socket->state() != QAbstractSocket::HostLookupState + && ms_socket->error() != -1) + { + qDebug() << ms_socket->error(); + qWarning() << "Error connecting to master server:" << ms_socket->errorString(); + ms_socket->abort(); + ms_socket->close(); + break; + } + } while (timer.elapsed() < timeout_milliseconds); // Very expensive spin-wait loop - it will bring CPU to 100%! + if (connected) + { + // Connect a one-shot signal in case the master server disconnects randomly + QObject::connect(ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_ms_socket_error(QAbstractSocket::SocketError))); + break; + } + else + { + ms_socket->abort(); + ms_socket->close(); + } + } + } + + // Failover to non-SRV connection + if (!connected) + connect_to_master_nosrv(); + else + emit ms_connect_finished(connected, false); + #endif +} + +void NetworkManager::on_ms_nosrv_connect_success() +{ + emit ms_connect_finished(true, false); + + QObject::disconnect(ms_socket, SIGNAL(connected()), + this, SLOT(on_ms_nosrv_connect_success())); + + QObject::connect(ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_ms_socket_error(QAbstractSocket::SocketError))); +} + +void NetworkManager::on_ms_socket_error(QAbstractSocket::SocketError error) +{ + qWarning() << "Master server socket error:" << ms_socket->errorString() + << "(" << error << ")"; + + // Disconnect the one-shot signal - this way, failover connect attempts + // don't trigger a full retry + QObject::disconnect(ms_socket, SIGNAL(error(QAbstractSocket::SocketError)), + this, SLOT(on_ms_socket_error(QAbstractSocket::SocketError))); + + emit ms_connect_finished(false, true); + + ms_reconnect_timer->start(ms_reconnect_delay_ms); +} + +void NetworkManager::retry_ms_connect() +{ + if (!ms_reconnect_timer->isActive() && ms_socket->state() != QAbstractSocket::ConnectingState) + connect_to_master(); +} + +void NetworkManager::handle_server_packet() +{ + char buffer[buffer_max_size]; + std::memset(buffer, 0, buffer_max_size); + server_socket->read(buffer, buffer_max_size); + + QString in_data = buffer; + + if (!in_data.endsWith("%")) + { + partial_packet = true; + temp_packet += in_data; + return; + } + + else + { + if (partial_packet) + { + in_data = temp_packet + in_data; + temp_packet = ""; + partial_packet = false; + } + } + + QStringList packet_list = in_data.split("%", QString::SplitBehavior(QString::SkipEmptyParts)); + + for (QString packet : packet_list) + { + AOPacket *f_packet = new AOPacket(packet); + + ao_app->server_packet_received(f_packet); + } +} + diff --git a/src/packet_distribution.cpp b/src/packet_distribution.cpp new file mode 100644 index 00000000..954062e8 --- /dev/null +++ b/src/packet_distribution.cpp @@ -0,0 +1,707 @@ +#include "aoapplication.h" + +#include "lobby.h" +#include "courtroom.h" +#include "networkmanager.h" +#include "encryption_functions.h" +#include "hardware_functions.h" +#include "debug_functions.h" + +void AOApplication::ms_packet_received(AOPacket *p_packet) +{ + p_packet->net_decode(); + + QString header = p_packet->get_header(); + QStringList f_contents = p_packet->get_contents(); + + if (header != "CHECK") + qDebug() << "R(ms):" << p_packet->to_string(); + + if (header == "ALL") + { + server_list.clear(); + + for (QString i_string : p_packet->get_contents()) + { + server_type f_server; + QStringList sub_contents = i_string.split("&"); + + if (sub_contents.size() < 4) + { + qDebug() << "W: malformed packet"; + continue; + } + + f_server.name = sub_contents.at(0); + f_server.desc = sub_contents.at(1); + f_server.ip = sub_contents.at(2); + f_server.port = sub_contents.at(3).toInt(); + + server_list.append(f_server); + } + + if (lobby_constructed) + { + w_lobby->list_servers(); + } + } + else if (header == "CT") + { + QString f_name, f_message; + + if (f_contents.size() == 1) + { + f_name = ""; + f_message = f_contents.at(0); + } + else if (f_contents.size() >= 2) + { + f_name = f_contents.at(0); + f_message = f_contents.at(1); + } + else + goto end; + + if (lobby_constructed) + { + w_lobby->append_chatmessage(f_name, f_message); + } + if (courtroom_constructed && courtroom_loaded) + { + w_courtroom->append_ms_chatmessage(f_name, f_message); + } + } + else if (header == "AO2CHECK") + { + send_ms_packet(new AOPacket("ID#AO2#" + get_version_string() + "#%")); + send_ms_packet(new AOPacket("HI#" + get_hdid() + "#%")); + + if (f_contents.size() < 1) + goto end; + + QStringList version_contents = f_contents.at(0).split("."); + + if (version_contents.size() < 3) + goto end; + + int f_release = version_contents.at(0).toInt(); + int f_major = version_contents.at(1).toInt(); + int f_minor = version_contents.at(2).toInt(); + + if (get_release() > f_release) + goto end; + else if (get_release() == f_release) + { + if (get_major_version() > f_major) + goto end; + else if (get_major_version() == f_major) + { + if (get_minor_version() >= f_minor) + goto end; + } + } + + 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."); + destruct_courtroom(); + destruct_lobby(); + } + + end: + + delete p_packet; +} + +void AOApplication::server_packet_received(AOPacket *p_packet) +{ + p_packet->net_decode(); + + QString header = p_packet->get_header(); + QStringList f_contents = p_packet->get_contents(); + QString f_packet = p_packet->to_string(); + + if (header != "checkconnection") + qDebug() << "R:" << f_packet; + + if (header == "decryptor") + { + if (f_contents.size() == 0) + goto end; + + //you may ask where 322 comes from. that would be a good question. + s_decryptor = fanta_decrypt(f_contents.at(0), 322).toUInt(); + + //default(legacy) values + encryption_needed = true; + yellow_text_enabled = false; + prezoom_enabled = false; + flipping_enabled = false; + custom_objection_enabled = false; + improved_loading_enabled = false; + desk_mod_enabled = false; + evidence_enabled = false; + cccc_ic_support_enabled = false; + arup_enabled = false; + casing_alerts_enabled = false; + modcall_reason_enabled = false; + + //workaround for tsuserver4 + if (f_contents.at(0) == "NOENCRYPT") + encryption_needed = false; + + QString f_hdid; + f_hdid = get_hdid(); + + AOPacket *hi_packet = new AOPacket("HI#" + f_hdid + "#%"); + send_server_packet(hi_packet); + } + else if (header == "ID") + { + if (f_contents.size() < 2) + goto end; + + s_pv = f_contents.at(0).toInt(); + server_software = f_contents.at(1); + + send_server_packet(new AOPacket("ID#AO2#" + get_version_string() + "#%")); + } + else if (header == "CT") + { + if (f_contents.size() < 2) + goto end; + + if (courtroom_constructed) + { + if (f_contents.size() == 3) + w_courtroom->append_server_chatmessage(f_contents.at(0), f_contents.at(1), f_contents.at(2)); + else + w_courtroom->append_server_chatmessage(f_contents.at(0), f_contents.at(1), "0"); + } + } + else if (header == "FL") + { + if (f_packet.contains("yellowtext",Qt::CaseInsensitive)) + yellow_text_enabled = true; + if (f_packet.contains("flipping",Qt::CaseInsensitive)) + flipping_enabled = true; + if (f_packet.contains("customobjections",Qt::CaseInsensitive)) + custom_objection_enabled = true; + if (f_packet.contains("fastloading",Qt::CaseInsensitive)) + improved_loading_enabled = true; + if (f_packet.contains("noencryption",Qt::CaseInsensitive)) + encryption_needed = false; + if (f_packet.contains("deskmod",Qt::CaseInsensitive)) + desk_mod_enabled = true; + if (f_packet.contains("evidence",Qt::CaseInsensitive)) + evidence_enabled = true; + if (f_packet.contains("cccc_ic_support",Qt::CaseInsensitive)) + cccc_ic_support_enabled = true; + if (f_packet.contains("arup",Qt::CaseInsensitive)) + arup_enabled = true; + if (f_packet.contains("casing_alerts",Qt::CaseInsensitive)) + casing_alerts_enabled = true; + if (f_packet.contains("modcall_reason",Qt::CaseInsensitive)) + modcall_reason_enabled = true; + + w_lobby->enable_connect_button(); + } + else if (header == "PN") + { + if (f_contents.size() < 2) + goto end; + + w_lobby->set_player_count(f_contents.at(0).toInt(), f_contents.at(1).toInt()); + } + else if (header == "SI") + { + if (f_contents.size() != 3) + goto end; + + char_list_size = f_contents.at(0).toInt(); + evidence_list_size = f_contents.at(1).toInt(); + music_list_size = f_contents.at(2).toInt(); + + if (char_list_size < 1 || evidence_list_size < 0 || music_list_size < 0) + goto end; + + loaded_chars = 0; + loaded_evidence = 0; + loaded_music = 0; + generated_chars = 0; + + destruct_courtroom(); + construct_courtroom(); + + courtroom_loaded = false; + + QString window_title = "Attorney Online 2"; + int selected_server = w_lobby->get_selected_server(); + + QString server_address = "", server_name = ""; + if (w_lobby->public_servers_selected) + { + 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); + window_title += ": " + server_name; + } + } + else + { + 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; + window_title += ": " + server_name; + } + } + + w_courtroom->set_window_title(window_title); + + w_lobby->show_loading_overlay(); + w_lobby->set_loading_text("Loading"); + w_lobby->set_loading_value(0); + + AOPacket *f_packet; + + if(improved_loading_enabled) + f_packet = new AOPacket("RC#%"); + else + f_packet = new AOPacket("askchar2#%"); + + send_server_packet(f_packet); + + QCryptographicHash hash(QCryptographicHash::Algorithm::Sha256); + hash.addData(server_address.toUtf8()); + if (is_discord_enabled()) + discord->state_server(server_name.toStdString(), hash.result().toBase64().toStdString()); + } + else if (header == "CI") + { + if (!courtroom_constructed) + goto end; + + for (int n_element = 0 ; n_element < f_contents.size() ; n_element += 2) + { + if (f_contents.at(n_element).toInt() != loaded_chars) + break; + + //this means we are on the last element and checking n + 1 element will be game over so + if (n_element == f_contents.size() - 1) + break; + + QStringList sub_elements = f_contents.at(n_element + 1).split("&"); + if (sub_elements.size() < 2) + break; + + char_type f_char; + f_char.name = sub_elements.at(0); + f_char.description = sub_elements.at(1); + f_char.evidence_string = sub_elements.at(3); + //temporary. the CharsCheck packet sets this properly + f_char.taken = false; + + ++loaded_chars; + + w_lobby->set_loading_text("Loading chars:\n" + QString::number(loaded_chars) + "/" + QString::number(char_list_size)); + + w_courtroom->append_char(f_char); + + int total_loading_size = char_list_size * 2 + evidence_list_size + music_list_size; + int loading_value = int(((loaded_chars + generated_chars + loaded_music + loaded_evidence) / static_cast<double>(total_loading_size)) * 100); + w_lobby->set_loading_value(loading_value); + } + + if (improved_loading_enabled) + send_server_packet(new AOPacket("RE#%")); + else + { + QString next_packet_number = QString::number(((loaded_chars - 1) / 10) + 1); + send_server_packet(new AOPacket("AN#" + next_packet_number + "#%")); + } + + } + else if (header == "EI") + { + if (!courtroom_constructed) + goto end; + + + // +1 because evidence starts at 1 rather than 0 for whatever reason + //enjoy fanta + if (f_contents.at(0).toInt() != loaded_evidence + 1) + goto end; + + if (f_contents.size() < 2) + goto end; + + QStringList sub_elements = f_contents.at(1).split("&"); + if (sub_elements.size() < 4) + goto end; + + evi_type f_evi; + f_evi.name = sub_elements.at(0); + f_evi.description = sub_elements.at(1); + //no idea what the number at position 2 is. probably an identifier? + f_evi.image = sub_elements.at(3); + + ++loaded_evidence; + + w_lobby->set_loading_text("Loading evidence:\n" + QString::number(loaded_evidence) + "/" + QString::number(evidence_list_size)); + + w_courtroom->append_evidence(f_evi); + + int total_loading_size = char_list_size * 2 + evidence_list_size + music_list_size; + int loading_value = int(((loaded_chars + generated_chars + loaded_music + loaded_evidence) / static_cast<double>(total_loading_size)) * 100); + w_lobby->set_loading_value(loading_value); + + QString next_packet_number = QString::number(loaded_evidence); + send_server_packet(new AOPacket("AE#" + next_packet_number + "#%")); + + } + else if (header == "EM") + { + if (!courtroom_constructed) + goto end; + + bool musics_time = false; + int areas = 0; + + for (int n_element = 0 ; n_element < f_contents.size() ; n_element += 2) + { + if (f_contents.at(n_element).toInt() != loaded_music) + break; + + if (n_element == f_contents.size() - 1) + break; + + QString f_music = f_contents.at(n_element + 1); + + ++loaded_music; + + w_lobby->set_loading_text("Loading music:\n" + QString::number(loaded_music) + "/" + QString::number(music_list_size)); + + if (musics_time) + { + w_courtroom->append_music(f_music); + } + else + { + if (f_music.endsWith(".wav") || + f_music.endsWith(".mp3") || + f_music.endsWith(".mp4") || + f_music.endsWith(".ogg") || + f_music.endsWith(".opus")) + { + musics_time = true; + areas--; + w_courtroom->fix_last_area(); + w_courtroom->append_music(f_music); + } + else + { + w_courtroom->append_area(f_music); + areas++; + } + } + + for (int area_n = 0; area_n < areas; area_n++) + { + w_courtroom->arup_append(0, "Unknown", "Unknown", "Unknown"); + } + + int total_loading_size = char_list_size * 2 + evidence_list_size + music_list_size; + int loading_value = int(((loaded_chars + generated_chars + loaded_music + loaded_evidence) / static_cast<double>(total_loading_size)) * 100); + w_lobby->set_loading_value(loading_value); + } + + QString next_packet_number = QString::number(((loaded_music - 1) / 10) + 1); + send_server_packet(new AOPacket("AM#" + next_packet_number + "#%")); + } + else if (header == "CharsCheck") + { + if (!courtroom_constructed) + goto end; + + for (int n_char = 0 ; n_char < f_contents.size() ; ++n_char) + { + if (f_contents.at(n_char) == "-1") + w_courtroom->set_taken(n_char, true); + else + w_courtroom->set_taken(n_char, false); + } + } + + else if (header == "SC") + { + if (!courtroom_constructed) + goto end; + + for (int n_element = 0 ; n_element < f_contents.size() ; ++n_element) + { + QStringList sub_elements = f_contents.at(n_element).split("&"); + + char_type f_char; + f_char.name = sub_elements.at(0); + if (sub_elements.size() >= 2) + f_char.description = sub_elements.at(1); + + //temporary. the CharsCheck packet sets this properly + f_char.taken = false; + + ++loaded_chars; + + w_lobby->set_loading_text("Loading chars:\n" + QString::number(loaded_chars) + "/" + QString::number(char_list_size)); + + w_courtroom->append_char(f_char); + + int total_loading_size = char_list_size * 2 + evidence_list_size + music_list_size; + int loading_value = int(((loaded_chars + generated_chars + loaded_music + loaded_evidence) / static_cast<double>(total_loading_size)) * 100); + w_lobby->set_loading_value(loading_value); + } + + send_server_packet(new AOPacket("RM#%")); + } + else if (header == "SM") + { + if (!courtroom_constructed) + goto end; + + bool musics_time = false; + int areas = 0; + + 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)); + + if (musics_time) + { + w_courtroom->append_music(f_contents.at(n_element)); + } + else + { + if (f_contents.at(n_element).endsWith(".wav") || + f_contents.at(n_element).endsWith(".mp3") || + f_contents.at(n_element).endsWith(".mp4") || + f_contents.at(n_element).endsWith(".ogg") || + f_contents.at(n_element).endsWith(".opus")) + { + musics_time = true; + w_courtroom->fix_last_area(); + w_courtroom->append_music(f_contents.at(n_element)); + areas--; + } + else + { + w_courtroom->append_area(f_contents.at(n_element)); + areas++; + } + } + + for (int area_n = 0; area_n < areas; area_n++) + { + w_courtroom->arup_append(0, "Unknown", "Unknown", "Unknown"); + } + + int total_loading_size = char_list_size * 2 + evidence_list_size + music_list_size; + int loading_value = int(((loaded_chars + generated_chars + loaded_music + loaded_evidence) / static_cast<double>(total_loading_size)) * 100); + w_lobby->set_loading_value(loading_value); + } + + send_server_packet(new AOPacket("RD#%")); + } + else if (header == "DONE") + { + if (!courtroom_constructed) + goto end; + + if (lobby_constructed) + w_courtroom->append_ms_chatmessage("", w_lobby->get_chatlog()); + + w_courtroom->character_loading_finished(); + w_courtroom->done_received(); + + courtroom_loaded = true; + + destruct_lobby(); + } + else if (header == "BN") + { + if (f_contents.size() < 1) + goto end; + + if (courtroom_constructed) + w_courtroom->set_background(f_contents.at(0)); + } + //server accepting char request(CC) packet + else if (header == "PV") + { + if (f_contents.size() < 3) + goto end; + + if (courtroom_constructed) + w_courtroom->enter_courtroom(f_contents.at(2).toInt()); + } + else if (header == "MS") + { + if (courtroom_constructed && courtroom_loaded) + w_courtroom->handle_chatmessage(&p_packet->get_contents()); + } + else if (header == "MC") + { + if (courtroom_constructed && courtroom_loaded) + w_courtroom->handle_song(&p_packet->get_contents()); + } + else if (header == "RT") + { + if (f_contents.size() < 1) + goto end; + if (courtroom_constructed) + { + if (f_contents.size() == 1) + w_courtroom->handle_wtce(f_contents.at(0), 0); + else if (f_contents.size() == 2) { + w_courtroom->handle_wtce(f_contents.at(0), f_contents.at(1).toInt()); + } + } + } + else if (header == "HP") + { + if (courtroom_constructed && f_contents.size() > 1) + w_courtroom->set_hp_bar(f_contents.at(0).toInt(), f_contents.at(1).toInt()); + } + else if (header == "LE") + { + if (courtroom_constructed) + { + QVector<evi_type> f_evi_list; + + for (QString f_string : f_contents) + { + QStringList sub_contents = f_string.split("&"); + + if (sub_contents.size() < 3) + continue; + + evi_type f_evi; + f_evi.name = sub_contents.at(0); + f_evi.description = sub_contents.at(1); + f_evi.image = sub_contents.at(2); + + f_evi_list.append(f_evi); + } + + w_courtroom->set_evidence_list(f_evi_list); + } + } + else if (header == "ARUP") + { + if (courtroom_constructed) + { + int arup_type = f_contents.at(0).toInt(); + for (int n_element = 1 ; n_element < f_contents.size() ; n_element++) + { + w_courtroom->arup_modify(arup_type, n_element - 1, f_contents.at(n_element)); + } + } + } + else if (header == "IL") + { + if (courtroom_constructed && f_contents.size() > 0) + w_courtroom->set_ip_list(f_contents.at(0)); + } + else if (header == "MU") + { + if (courtroom_constructed && f_contents.size() > 0) + w_courtroom->set_mute(true, f_contents.at(0).toInt()); + } + else if (header == "UM") + { + if (courtroom_constructed && f_contents.size() > 0) + w_courtroom->set_mute(false, f_contents.at(0).toInt()); + } + 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)); + 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)); + construct_lobby(); + destruct_courtroom(); + } + + } + else if (header == "BD") + { + call_notice("You are banned on this server."); + } + else if (header == "ZZ") + { + if (courtroom_constructed && f_contents.size() > 0) + w_courtroom->mod_called(f_contents.at(0)); + } + else if (header == "CASEA") + { + if (courtroom_constructed && f_contents.size() > 6) + w_courtroom->case_called(f_contents.at(0), f_contents.at(1) == "1", f_contents.at(2) == "1", f_contents.at(3) == "1", f_contents.at(4) == "1", f_contents.at(5) == "1"); + } + + end: + + delete p_packet; +} + +void AOApplication::send_ms_packet(AOPacket *p_packet) +{ + p_packet->net_encode(); + + QString f_packet = p_packet->to_string(); + + net_manager->ship_ms_packet(f_packet); + + qDebug() << "S(ms):" << f_packet; + + delete p_packet; +} + +void AOApplication::send_server_packet(AOPacket *p_packet, bool encoded) +{ + if (encoded) + p_packet->net_encode(); + + QString f_packet = p_packet->to_string(); + + if (encryption_needed) + { + qDebug() << "S(e):" << f_packet; + + p_packet->encrypt_header(s_decryptor); + f_packet = p_packet->to_string(); + } + else + { + qDebug() << "S:" << f_packet; + } + + net_manager->ship_server_packet(f_packet); + + delete p_packet; +} diff --git a/src/path_functions.cpp b/src/path_functions.cpp new file mode 100644 index 00000000..c51cfde3 --- /dev/null +++ b/src/path_functions.cpp @@ -0,0 +1,162 @@ +#include "aoapplication.h" +#include "courtroom.h" +#include "file_functions.h" + +#include <QDir> +#include <QStandardPaths> +#include <QRegExp> + +#ifdef BASE_OVERRIDE +#include "base_override.h" +#endif + +//this is a quite broad generalization +//the most common OSes(mac and windows) are _usually_ case insensitive +//however, there do exist mac installations with case sensitive filesystems +//in that case, define CASE_SENSITIVE_FILESYSTEM and compile on a mac +#if (defined (LINUX) || defined (__linux__)) +#define CASE_SENSITIVE_FILESYSTEM +#endif + +QString AOApplication::get_base_path() +{ + QString base_path = ""; +#ifdef ANDROID + QString sdcard_storage = getenv("SECONDARY_STORAGE"); + if (dir_exists(sdcard_storage + "/AO2/")){ + base_path = sdcard_storage + "/AO2/"; + } + else { + QString external_storage = getenv("EXTERNAL_STORAGE"); + base_path = external_storage + "/AO2/"; + } +#else + base_path = applicationDirPath() + "/base/"; +#endif + + return base_path; +} + +QString AOApplication::get_data_path() +{ + return get_base_path() + "data/"; +} + +QString AOApplication::get_default_theme_path(QString p_file) +{ + QString path = get_base_path() + "themes/default/" + p_file; +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif +} + +QString AOApplication::get_custom_theme_path(QString p_theme, QString p_file) +{ + QString path = get_base_path() + "themes/" + p_theme + "/" + p_file; +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif +} + +QString AOApplication::get_theme_path(QString p_file) +{ + QString path = get_base_path() + "themes/" + current_theme + "/" + p_file; +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif +} + +QString AOApplication::get_character_path(QString p_char, QString p_file) +{ + QString path = get_base_path() + "characters/" + p_char + "/" + p_file; +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif +} + +QString AOApplication::get_sounds_path(QString p_file) +{ + QString path = get_base_path() + "sounds/general/" + p_file; +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif +} + +QString AOApplication::get_music_path(QString p_song) +{ + QString path = get_base_path() + "sounds/music/" + p_song; +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif +} + +QString AOApplication::get_background_path(QString p_file) +{ + QString path = get_base_path() + "background/" + w_courtroom->get_current_background() + "/" + p_file; + if (courtroom_constructed) { +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif + } + return get_default_background_path(p_file); +} + +QString AOApplication::get_default_background_path(QString p_file) +{ + QString path = get_base_path() + "background/default/" + p_file; +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif +} + +QString AOApplication::get_evidence_path(QString p_file) +{ + QString path = get_base_path() + "evidence/" + p_file; +#ifndef CASE_SENSITIVE_FILESYSTEM + return path; +#else + return get_case_sensitive_path(path); +#endif +} + +QString AOApplication::get_case_sensitive_path(QString p_file) { + //first, check to see if it's actually there (also serves as base case for recursion) + if (exists(p_file)) return p_file; + + QFileInfo file(p_file); + + QString file_basename = file.fileName(); + QString file_parent_dir = get_case_sensitive_path(file.absolutePath()); + + //second, does it exist in the new parent dir? + if (exists(file_parent_dir + "/" + file_basename)) + return file_parent_dir + "/" + file_basename; + + //last resort, dirlist parent dir and find case insensitive match + QRegExp file_rx = QRegExp(file_basename, Qt::CaseInsensitive, QRegExp::FixedString); + QStringList files = QDir(file_parent_dir).entryList(); + + int result = files.indexOf(file_rx); + + if (result != -1) + return file_parent_dir + "/" + files.at(result); + + //if nothing is found, let the caller handle the missing file + return file_parent_dir + "/" + file_basename; +} diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp new file mode 100644 index 00000000..fcb8df2c --- /dev/null +++ b/src/text_file_functions.cpp @@ -0,0 +1,639 @@ +#include "text_file_functions.h" + +QString AOApplication::read_theme() +{ + QString result = configini->value("theme", "default").value<QString>(); + return result; +} + +int AOApplication::read_blip_rate() +{ + int result = configini->value("blip_rate", 1).toInt(); + return result; +} + +QString AOApplication::get_ooc_name() +{ + QString result = configini->value("ooc_name").value<QString>(); + return result; +} + +int AOApplication::get_default_music() +{ + int result = configini->value("default_music", 50).toInt(); + return result; +} + +int AOApplication::get_default_sfx() +{ + int result = configini->value("default_sfx", 50).toInt(); + return result; +} + +int AOApplication::get_default_blip() +{ + int result = configini->value("default_blip", 50).toInt(); + return result; +} + +int AOApplication::get_max_log_size() +{ + int result = configini->value("log_maximum", 200).toInt(); + return result; +} + +bool AOApplication::get_log_goes_downwards() +{ + QString result = configini->value("log_goes_downwards", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::get_showname_enabled_by_default() +{ + QString result = configini->value("show_custom_shownames", "true").value<QString>(); + return result.startsWith("true"); +} + +QString AOApplication::get_default_username() +{ + QString result = configini->value("default_username", "").value<QString>(); + if (result.isEmpty()) + return get_ooc_name(); + else + return result; +} + +QString AOApplication::get_audio_output_device() +{ + QString result = configini->value("default_audio_device", "default").value<QString>(); + return result; +} + +QStringList AOApplication::get_call_words() +{ + QStringList return_value; + + QFile callwords_ini; + + callwords_ini.setFileName(get_base_path() + "callwords.ini"); + + if (!callwords_ini.open(QIODevice::ReadOnly)) + return return_value; + + QTextStream in(&callwords_ini); + + while (!in.atEnd()) + { + QString line = in.readLine(); + return_value.append(line); + } + + return return_value; +} + +void AOApplication::write_to_serverlist_txt(QString p_line) +{ + QFile serverlist_txt; + QString serverlist_txt_path = get_base_path() + "serverlist.txt"; + + serverlist_txt.setFileName(serverlist_txt_path); + + if (!serverlist_txt.open(QIODevice::WriteOnly | QIODevice::Append)) + { + return; + } + + QTextStream out(&serverlist_txt); + + out << "\r\n" << p_line; + + serverlist_txt.close(); +} + +QVector<server_type> AOApplication::read_serverlist_txt() +{ + QVector<server_type> f_server_list; + + QFile serverlist_txt; + QString serverlist_txt_path = get_base_path() + "serverlist.txt"; + + serverlist_txt.setFileName(serverlist_txt_path); + + if (!serverlist_txt.open(QIODevice::ReadOnly)) + { + return f_server_list; + } + + QTextStream in(&serverlist_txt); + + while(!in.atEnd()) + { + QString line = in.readLine(); + server_type f_server; + QStringList line_contents = line.split(":"); + + if (line_contents.size() < 3) + continue; + + f_server.ip = line_contents.at(0); + f_server.port = line_contents.at(1).toInt(); + f_server.name = line_contents.at(2); + f_server.desc = ""; + + f_server_list.append(f_server); + } + + return f_server_list; +} + +QString AOApplication::read_design_ini(QString p_identifier, QString p_design_path) +{ + QSettings settings(p_design_path, QSettings::IniFormat); + QVariant value = settings.value(p_identifier); + if (value.type() == QVariant::StringList) { + return value.toStringList().join(","); + } else { + return value.toString(); + } +} + +QPoint AOApplication::get_button_spacing(QString p_identifier, QString 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); + + QPoint return_value; + + return_value.setX(0); + return_value.setY(0); + + if (f_result == "") + { + f_result = read_design_ini(p_identifier, default_path); + + if (f_result == "") + return return_value; + } + + QStringList sub_line_elements = f_result.split(","); + + if (sub_line_elements.size() < 2) + return return_value; + + return_value.setX(sub_line_elements.at(0).toInt()); + return_value.setY(sub_line_elements.at(1).toInt()); + + return return_value; +} + +pos_size_type AOApplication::get_element_dimensions(QString p_identifier, QString 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); + + pos_size_type return_value; + + return_value.x = 0; + return_value.y = 0; + return_value.width = -1; + return_value.height = -1; + + if (f_result == "") + { + f_result = read_design_ini(p_identifier, default_path); + + if (f_result == "") + return return_value; + } + + QStringList sub_line_elements = f_result.split(","); + + if (sub_line_elements.size() < 4) + return return_value; + + return_value.x = sub_line_elements.at(0).toInt(); + return_value.y = sub_line_elements.at(1).toInt(); + return_value.width = sub_line_elements.at(2).toInt(); + return_value.height = sub_line_elements.at(3).toInt(); + + return return_value; +} + +int AOApplication::get_font_size(QString p_identifier, QString 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); + + if (f_result == "") + { + f_result = read_design_ini(p_identifier, default_path); + + if (f_result == "") + return 10; + } + + return f_result.toInt(); +} + +QColor AOApplication::get_color(QString p_identifier, QString 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); + + QColor return_color(0, 0, 0); + + if (f_result == "") + { + f_result = read_design_ini(p_identifier, default_path); + + if (f_result == "") + return return_color; + } + + QStringList color_list = f_result.split(","); + + if (color_list.size() < 3) + return return_color; + + return_color.setRed(color_list.at(0).toInt()); + return_color.setGreen(color_list.at(1).toInt()); + return_color.setBlue(color_list.at(2).toInt()); + + return return_color; +} + +QColor AOApplication::get_chat_color(QString p_identifier, QString p_chat) +{ + QColor return_color(255, 255, 255); + + if (p_identifier == "_inline_grey") + { + return_color = QColor(187, 187, 187); + } + 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; + } + } + + p_identifier = p_identifier.prepend("c"); + 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); + + if (f_result == "") + return return_color; + } + + QStringList color_list = f_result.split(","); + + if (color_list.size() < 3) + return return_color; + + return_color.setRed(color_list.at(0).toInt()); + return_color.setGreen(color_list.at(1).toInt()); + return_color.setBlue(color_list.at(2).toInt()); + + return return_color; +} + +QString AOApplication::get_sfx(QString p_identifier) +{ + QString design_ini_path = get_theme_path("courtroom_sounds.ini"); + QString default_path = get_default_theme_path("courtroom_sounds.ini"); + QString f_result = read_design_ini(p_identifier, design_ini_path); + + QString return_sfx = ""; + + if (f_result == "") + { + f_result = read_design_ini(p_identifier, default_path); + + if (f_result == "") + return return_sfx; + } + + return_sfx = f_result; + + return return_sfx; +} + +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)) + { + return sound_to_check + ".opus"; + } + else if (file_exists(mp3_check)) + { + return sound_to_check + ".mp3"; + } + return sound_to_check + ".wav"; +} + +QString AOApplication::get_image_suffix(QString path_to_check) +{ + QString apng_check = get_sounds_path(path_to_check + ".apng"); + if (file_exists(apng_check)) + { + return path_to_check + ".apng"; + } + return path_to_check + ".gif"; +} + + +//returns whatever is to the right of "search_line =" within target_tag and terminator_tag, trimmed +//returns the empty string if the search line couldnt be found +QString AOApplication::read_char_ini(QString p_char, QString p_search_line, QString target_tag) +{ + QSettings settings(get_character_path(p_char, "char.ini"), QSettings::IniFormat); + settings.beginGroup(target_tag); + QString value = settings.value(p_search_line).toString(); + settings.endGroup(); + return value; +} + +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; +} + +QString AOApplication::get_showname(QString p_char) +{ + QString f_result = read_char_ini(p_char, "showname", "Options"); + QString f_needed = read_char_ini(p_char, "needs_showname", "Options"); + + if (f_needed.startsWith("false")) + return ""; + if (f_result == "") + return p_char; + else return f_result; +} + +QString AOApplication::get_char_side(QString p_char) +{ + QString f_result = read_char_ini(p_char, "side", "Options"); + + if (f_result == "") + return "wit"; + else return f_result; +} + +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; +} + +QString AOApplication::get_chat(QString p_char) +{ + QString f_result = read_char_ini(p_char, "chat", "Options"); + + //handling the correct order of chat is a bit complicated, we let the caller do it + return f_result; +} + +QString AOApplication::get_char_shouts(QString p_char) +{ + QString f_result = read_char_ini(p_char, "shouts", "Options"); + + return f_result; +} + +int AOApplication::get_preanim_duration(QString p_char, QString p_emote) +{ + QString f_result = read_char_ini(p_char, p_emote, "Time"); + + if (f_result == "") + return -1; + else return f_result.toInt(); +} + +int AOApplication::get_ao2_preanim_duration(QString p_char, QString p_emote) +{ + QString f_result = read_char_ini(p_char, "%" + p_emote, "Time"); + + if (f_result == "") + return -1; + else return f_result.toInt(); +} + +int AOApplication::get_emote_number(QString p_char) +{ + QString f_result = read_char_ini(p_char, "number", "Emotions"); + + if (f_result == "") + return 0; + else return f_result.toInt(); +} + +QString AOApplication::get_emote_comment(QString p_char, int p_emote) +{ + QString f_result = read_char_ini(p_char, QString::number(p_emote + 1), "Emotions"); + + QStringList result_contents = f_result.split("#"); + + if (result_contents.size() < 4) + { + qDebug() << "W: misformatted char.ini: " << p_char << ", " << p_emote; + return "normal"; + } + else return result_contents.at(0); +} + +QString AOApplication::get_pre_emote(QString p_char, int p_emote) +{ + QString f_result = read_char_ini(p_char, QString::number(p_emote + 1), "Emotions"); + + QStringList result_contents = f_result.split("#"); + + if (result_contents.size() < 4) + { + qDebug() << "W: misformatted char.ini: " << p_char << ", " << p_emote; + return ""; + } + else return result_contents.at(1); +} + +QString AOApplication::get_emote(QString p_char, int p_emote) +{ + QString f_result = read_char_ini(p_char, QString::number(p_emote + 1), "Emotions"); + + QStringList result_contents = f_result.split("#"); + + if (result_contents.size() < 4) + { + qDebug() << "W: misformatted char.ini: " << p_char << ", " << p_emote; + return "normal"; + } + else return result_contents.at(2); +} + +int AOApplication::get_emote_mod(QString p_char, int p_emote) +{ + QString f_result = read_char_ini(p_char, QString::number(p_emote + 1), "Emotions"); + + QStringList result_contents = f_result.split("#"); + + if (result_contents.size() < 4) + { + qDebug() << "W: misformatted char.ini: " << p_char << ", " << QString::number(p_emote); + return 0; + } + else return result_contents.at(3).toInt(); +} + +int AOApplication::get_desk_mod(QString p_char, int p_emote) +{ + QString f_result = read_char_ini(p_char, QString::number(p_emote + 1), "Emotions"); + + QStringList result_contents = f_result.split("#"); + + if (result_contents.size() < 5) + return -1; + + QString string_result = result_contents.at(4); + if (string_result == "") + return -1; + + else return string_result.toInt(); +} + +QString AOApplication::get_sfx_name(QString p_char, int p_emote) +{ + QString f_result = read_char_ini(p_char, QString::number(p_emote + 1), "SoundN"); + + if (f_result == "") + return "1"; + else return f_result; +} + +int AOApplication::get_sfx_delay(QString p_char, int p_emote) +{ + QString f_result = read_char_ini(p_char, QString::number(p_emote + 1), "SoundT"); + + if (f_result == "") + return 1; + else return f_result.toInt(); +} + +int AOApplication::get_text_delay(QString p_char, QString p_emote) +{ + QString f_result = read_char_ini(p_char, p_emote, "TextDelay"); + + if (f_result == "") + return -1; + else return f_result.toInt(); +} + +QString AOApplication::get_custom_realization(QString p_char) +{ + QString f_result = read_char_ini(p_char, "realization", "Options"); + + if (f_result == "") + return get_sfx("realization"); + else return f_result; +} + +bool AOApplication::get_blank_blip() +{ + QString result = configini->value("blank_blip", "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::get_casing_enabled() +{ + QString result = configini->value("casing_enabled", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::get_casing_defence_enabled() +{ + QString result = configini->value("casing_defence_enabled", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::get_casing_prosecution_enabled() +{ + QString result = configini->value("casing_prosecution_enabled", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::get_casing_judge_enabled() +{ + QString result = configini->value("casing_judge_enabled", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::get_casing_juror_enabled() +{ + QString result = configini->value("casing_juror_enabled", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::get_casing_steno_enabled() +{ + QString result = configini->value("casing_steno_enabled", "false").value<QString>(); + return result.startsWith("true"); +} + +bool AOApplication::get_casing_cm_enabled() +{ + QString result = configini->value("casing_cm_enabled", "false").value<QString>(); + return result.startsWith("true"); +} + +QString AOApplication::get_casing_can_host_cases() +{ + QString result = configini->value("casing_can_host_cases", "Turnabout Check Your Settings").value<QString>(); + return result; +} |
