diff --git a/include/aoapplication.h b/include/aoapplication.h index 9b7cef1..c0c3ba3 100644 --- a/include/aoapplication.h +++ b/include/aoapplication.h @@ -130,6 +130,7 @@ public: QString get_default_theme_path(QString p_file); QString get_custom_theme_path(QString p_theme, QString p_file); QString get_character_path(QString p_char, QString p_file); + QString get_misc_path(QString p_misc, QString p_file); QString get_sounds_path(QString p_file); QString get_music_path(QString p_song); QString get_background_path(QString p_file); @@ -301,14 +302,14 @@ public: // Returns the color with p_identifier from p_file QColor get_color(QString p_identifier, QString p_file); - // Returns the markdown symbol used for specified p_identifier such as colors - QString get_chat_markdown(QString p_identifier, QString p_file); + // Returns the markup symbol used for specified p_identifier such as colors + QString get_chat_markup(QString p_identifier, QString p_file); // Returns the color from the misc folder. QColor get_chat_color(QString p_identifier, QString p_chat); // Returns the sfx with p_identifier from sounds.ini in the current theme path - QString get_sfx(QString p_identifier); + QString get_sfx(QString p_identifier, QString p_misc="default"); // Figure out if we can opus this or if we should fall back to wav QString get_sfx_suffix(QString sound_to_check); @@ -380,9 +381,9 @@ public: // t QString get_effect(QString effect, QString p_char, QString p_folder); - // Return the effect sound associated with the fx_name in the - // misc/effects//sounds.ini, or theme/effects/sounds.ini. - QString get_effect_sound(QString fx_name, QString p_char); + // Return p_property of fx_name. If p_property is "sound", return + // the value associated with fx_name, otherwise use fx_name + '_' + p_property. + QString get_effect_property(QString fx_name, QString p_char, QString p_property); // Returns the custom realisation used by the character. QString get_custom_realization(QString p_char); @@ -432,6 +433,15 @@ public: // Returns p_char's blips (previously called their "gender") QString get_blips(QString p_char); + // Get a property of a given emote, or get it from "options" if emote doesn't have it + QString get_emote_property(QString p_char, QString p_emote, QString p_property); + + // Return a transformation mode from a string ("smooth" for smooth, anything else for fast) + Qt::TransformationMode get_scaling(QString p_scaling); + + // Returns the scaling type for p_miscname + Qt::TransformationMode get_misc_scaling(QString p_miscname); + // ====== // These are all casing-related settings. // ====== diff --git a/include/aocharmovie.h b/include/aocharmovie.h deleted file mode 100644 index 2dda0ec..0000000 --- a/include/aocharmovie.h +++ /dev/null @@ -1,125 +0,0 @@ -#ifndef AOCHARMOVIE_H -#define AOCHARMOVIE_H - -#include -#include -#include -#include -#include - -class AOApplication; - -class AOCharMovie : public QLabel { - Q_OBJECT - -public: - AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app); - - // Play a hat.gif - style preanimation - void play_pre(QString p_char, QString p_emote, int duration); - - // Play a (b)normal.gif - style animation (talking) - void play_talking(QString p_char, QString p_emote); - - // Play an (a)normal.gif - style animation (not talking) - void play_idle(QString p_char, QString p_emote); - - // Stop the movie, clearing the image - void stop(); - - // Set the m_flipped variable to true/false - void set_flipped(bool p_flipped) { m_flipped = p_flipped; } - - // Set the movie's playback speed (between 10% and 1000%) - void set_speed(int modifier) { speed = qMax(10, qMin(modifier, 1000)); } - - // Move the label itself around - void move(int ax, int ay); - - // This is somewhat pointless now as there's no "QMovie" object to resize, aka - // no "combo" to speak of - void combo_resize(int w, int h); - - // Return the frame delay adjusted for speed - int get_frame_delay(int delay); - - QStringList network_strings; - - QString m_char; - QString m_emote; - -private: - AOApplication *ao_app; - - QVector movie_frames; - QVector movie_delays; - - // Effects such as sfx, screenshakes and realization flashes are stored in - // here. QString entry format: "sfx^[sfx_name]", "shake", "flash". The program - // uses the QVector index as reference. - QVector> movie_effects; - - QTimer *preanim_timer; - QTimer *ticker; - QString last_path; - QImageReader *m_reader = new QImageReader(); - - QElapsedTimer actual_time; - - // Usually used to turn seconds into milliseconds such as for [Time] tag in - // char.ini - const int time_mod = 60; - - // These are the X and Y values before they are fixed based on the sprite's - // width. - int x = 0; - int y = 0; - // These are the width and height values before they are fixed based on the - // sprite's width. - int f_w = 0; - int f_h = 0; - - int frame = 0; - int max_frames = 0; - - int speed = 100; - - bool m_flipped = false; - bool play_once = true; - - // Set the movie's image to provided paths, preparing for playback. - void load_image(QString p_char, QString p_emote, QString emote_prefix); - - // Start playback of the movie (if animated). - void play(); - - // Play a frame-specific effect, if there's any defined for that specific - // frame. - void play_frame_effect(int frame); - - // Retreive a pixmap adjused for mirroring/aspect ratio shenanigans from a - // provided QImage - QPixmap get_pixmap(QImage image); - - // Set the movie's frame to provided pixmap - void set_frame(QPixmap f_pixmap); - - // Initialize the frame-specific effects from the char.ini - void load_effects(); - - // Initialize the frame-specific effects from the provided network_strings, - // this is only initialized if network_strings has size more than 0. - void load_network_effects(); - -signals: - void done(); - void shake(); - void flash(); - void play_sfx(QString sfx); - -private slots: - void preanim_done(); - void movie_ticker(); -}; - -#endif // AOCHARMOVIE_H diff --git a/include/aoevidencedisplay.h b/include/aoevidencedisplay.h index 979a754..ff448c9 100644 --- a/include/aoevidencedisplay.h +++ b/include/aoevidencedisplay.h @@ -2,7 +2,7 @@ #define AOEVIDENCEDISPLAY_H #include "aoapplication.h" -#include "aomovie.h" +#include "aolayer.h" #include "aosfxplayer.h" #include @@ -21,7 +21,7 @@ public: private: AOApplication *ao_app; - AOMovie *evidence_movie; + InterfaceLayer *evidence_movie; QLabel *evidence_icon; AOSfxPlayer *sfx_player; diff --git a/include/aolayer.h b/include/aolayer.h new file mode 100644 index 0000000..2e0510a --- /dev/null +++ b/include/aolayer.h @@ -0,0 +1,214 @@ +#ifndef AOLAYER_H +#define AOLAYER_H + +#include +#include +#include +#include +#include +#include + +class AOApplication; + +class AOLayer : public QLabel { + Q_OBJECT + +public: + AOLayer(QWidget *p_parent, AOApplication *p_ao_app); + + QString filename; // file name without extension, i.e. "witnesstestimony" + int static_duration; // time in ms for static images to be displayed, if + // applicable. set to 0 for infinite + int max_duration; // maximum duration in ms, image will be culled if it is + // exceeded. set this to 0 for infinite duration + bool play_once = false; // Whether to loop this animation or not + bool cull_image = true; // if we're done playing this animation, should we + // hide it? also controls durational culling + Qt::TransformationMode transform_mode = Qt::FastTransformation; // transformation mode to use for this image + bool stretch = false; // Should we stretch/squash this image to fill the screen? + + // Set the movie's image to provided paths, preparing for playback. + void start_playback(QString p_image); + + void set_play_once(bool p_play_once); + void set_cull_image(bool p_cull_image); + void set_static_duration(int p_static_duration); + void set_max_duration(int p_max_duration); + + // Stop the movie, clearing the image + void stop(); + + // Set the m_flipped variable to true/false + void set_flipped(bool p_flipped) { m_flipped = p_flipped; } + + // Set the movie's playback speed (between 10% and 1000%) + void set_speed(int modifier) { speed = qMax(10, qMin(modifier, 1000)); } + + // Move the label itself around + void move(int ax, int ay); + + // This is somewhat pointless now as there's no "QMovie" object to resize, aka + // no "combo" to speak of + void combo_resize(int w, int h); + + // Return the frame delay adjusted for speed + int get_frame_delay(int delay); + + // iterate through a list of paths and return the first entry that exists. if + // none exist, return NULL (safe because we check again for existence later) + QString find_image(QList p_list); + +protected: + AOApplication *ao_app; + QVector movie_frames; + QVector movie_delays; + + QTimer *preanim_timer; + QTimer *shfx_timer; + QTimer *ticker; + QString last_path; + QImageReader m_reader; + + QElapsedTimer actual_time; + + // Usually used to turn seconds into milliseconds such as for [Time] tag in + // char.ini + const int tick_ms = 60; + + // These are the X and Y values before they are fixed based on the sprite's + // width. + int x = 0; + int y = 0; + // These are the width and height values before they are fixed based on the + // sprite's width. + int f_w = 0; + int f_h = 0; + + int frame = 0; + int max_frames = 0; + int last_max_frames = 0; + + int speed = 100; + + bool m_flipped = false; + // Are we loading this from the same frame we left off on? TODO: actually make + // this work + bool continuous = false; + // Whether or not to forcibly bypass the simple check done by start_playback + // and use the existent value of continuous instead + bool force_continuous = false; + + int duration = 0; + + // Start playback of the movie (if animated). + void play(); + + // Freeze the movie at the current frame. + void freeze(); + + // Retreive a pixmap adjused for mirroring/aspect ratio shenanigans from a + // provided QImage + QPixmap get_pixmap(QImage image); + + // Set the movie's frame to provided pixmap + void set_frame(QPixmap f_pixmap); + +signals: + void done(); + +protected slots: + virtual void preanim_done(); + void shfx_timer_done(); + virtual void movie_ticker(); +}; + +class BackgroundLayer : public AOLayer { + Q_OBJECT +public: + BackgroundLayer(QWidget *p_parent, AOApplication *p_ao_app); + void load_image(QString p_filename); +}; + +class ForegroundLayer : public AOLayer { + Q_OBJECT +public: + ForegroundLayer(QWidget *p_parent, AOApplication *p_ao_app); + QString miscname; //'misc' folder to search. we fetch this based on p_charname below + void load_image(QString p_filename, QString p_charname); +}; + +class CharLayer : public AOLayer { + Q_OBJECT +public: + CharLayer(QWidget *p_parent, AOApplication *p_ao_app); + QString current_emote = ""; // name of the emote we're using + bool is_preanim; // equivalent to the old play_once, if true we don't want + // to loop this + QString prefix = ""; // prefix, left blank if it's a preanim + + void load_image(QString p_filename, QString p_charname, int p_duration, bool p_is_preanim); + void play(); // overloaded so we can play effects + + // networked frame fx string + QStringList network_strings; + +private: + QString last_char; // name of the last character we used + QString last_emote; // name of the last animation we used + QString last_prefix; // prefix of the last animation we played + bool was_preanim = false; // whether is_preanim was true last time + + // Effects such as sfx, screenshakes and realization flashes are stored in + // here. QString entry format: "sfx^[sfx_name]", "shake", "flash". The program + // uses the QVector index as reference. + QVector> movie_effects; + + // used for effect loading + QString m_char = ""; + QString m_emote = ""; + + // overloaded for effects reasons + void start_playback(QString p_image); + + // Initialize the frame-specific effects from the char.ini + void load_effects(); + + // Initialize the frame-specific effects from the provided network_strings, + // this is only initialized if network_strings has size more than 0. + void load_network_effects(); + + // Play a frame-specific effect, if there's any defined for that specific + // frame. + void play_frame_effect(int p_frame); + +private slots: + void preanim_done() override; // overridden so we don't accidentally cull characters + void movie_ticker() override; // overridden so we can play effects + +signals: + void shake(); + void flash(); + void play_sfx(QString sfx); +}; + +class InterjectionLayer : public AOLayer { + Q_OBJECT +public: + InterjectionLayer(QWidget *p_parent, AOApplication *p_ao_app); + void load_image(QString p_filename, QString p_charname, QString p_miscname); +}; + +class EffectLayer : public AOLayer { + Q_OBJECT +public: + EffectLayer(QWidget *p_parent, AOApplication *p_ao_app); + void load_image(QString p_filename, bool p_looping); +}; + +class InterfaceLayer : public AOLayer { + Q_OBJECT +public: + InterfaceLayer(QWidget *p_parent, AOApplication *p_ao_app); + void load_image(QString p_filename, QString p_miscname); +}; +#endif // AOLAYER_H diff --git a/include/aomovie.h b/include/aomovie.h deleted file mode 100644 index eb7f7a5..0000000 --- a/include/aomovie.h +++ /dev/null @@ -1,36 +0,0 @@ -#ifndef AOMOVIE_H -#define AOMOVIE_H - -#include -#include - -class Courtroom; -class AOApplication; - -class AOMovie : public QLabel { - Q_OBJECT - -public: - AOMovie(QWidget *p_parent, AOApplication *p_ao_app); - - void set_play_once(bool p_play_once); - void play(QString p_image, QString p_char = "", QString p_custom_theme = "", - int default_duration = 0); - void combo_resize(int w, int h); - void stop(); - -private: - QMovie *m_movie; - AOApplication *ao_app; - QTimer *timer; - bool play_once = true; - -signals: - void done(); - -private slots: - void frame_change(int n_frame); - void timer_done(); -}; - -#endif // AOMOVIE_H diff --git a/include/aoscene.h b/include/aoscene.h deleted file mode 100644 index 726e264..0000000 --- a/include/aoscene.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef AOSCENE_H -#define AOSCENE_H - -#include -#include -#include - -class Courtroom; -class AOApplication; - -class AOScene : public QLabel { - Q_OBJECT -public: - explicit AOScene(QWidget *parent, AOApplication *p_ao_app); - - void set_image(QString p_image); - void set_legacy_desk(QString p_image); - - // Move the label itself around - void move(int ax, int ay); - - // This is somewhat pointless now as there's no "QMovie" object to resize, aka - // no "combo" to speak of - void combo_resize(int w, int h); - -private: - QWidget *m_parent; - QMovie *m_movie; - AOApplication *ao_app; - QString last_image; - - // These are the X and Y values before they are fixed based on the sprite's - // width. - int x = 0; - int y = 0; - // These are the width and height values before they are fixed based on the - // sprite's width. - int f_w = 0; - int f_h = 0; -}; - -#endif // AOSCENE_H diff --git a/include/courtroom.h b/include/courtroom.h index e86330e..5ad6fa6 100644 --- a/include/courtroom.h +++ b/include/courtroom.h @@ -5,17 +5,15 @@ #include "aoblipplayer.h" #include "aobutton.h" #include "aocharbutton.h" -#include "aocharmovie.h" #include "aoemotebutton.h" #include "aoevidencebutton.h" #include "aoevidencedisplay.h" #include "aoimage.h" +#include "aolayer.h" #include "aolineedit.h" -#include "aomovie.h" #include "aomusicplayer.h" #include "aooptionsdialog.h" #include "aopacket.h" -#include "aoscene.h" #include "aosfxplayer.h" #include "aotextarea.h" #include "aotextedit.h" @@ -151,6 +149,9 @@ public: // reads theme inis and sets size and pos based on the identifier void set_size_and_pos(QWidget *p_widget, QString p_identifier); + // reads theme and char inis and sets size and pos based on the identifier + void set_size_and_pos(QWidget *p_widget, QString p_identifier, QString p_char); + // reads theme inis and returns the size and pos as defined by it QPoint get_theme_pos(QString p_identifier); @@ -224,11 +225,13 @@ public: // Parse the chat message packet and unpack it into the m_chatmessage[ITEM] format void unpack_chatmessage(QStringList p_contents); - // Log the message contents and information such as evidence presenting etc. into the log file - void log_chatmessage(QString f_message, int f_char_id, QString f_showname = "", int f_color = 0); - - // Display the message contents and information such as evidence presenting etc. in the IC logs - void display_log_chatmessage(QString f_message, int f_char_id, QString f_showname = "", int f_color = 0); + enum LogMode { + IO_ONLY, + DISPLAY_ONLY, + DISPLAY_AND_IO + }; + // Log the message contents and information such as evidence presenting etc. into the log file, the IC log, or both. + void log_chatmessage(QString f_message, int f_char_id, QString f_showname = "", int f_color = 0, LogMode f_log_mode=IO_ONLY); // Log the message contents and information such as evidence presenting etc. into the IC logs void handle_callwords(); @@ -264,7 +267,8 @@ public: QString filter_ic_text(QString p_text, bool colorize = false, int pos = -1, int default_color = 0); - void log_ic_text(QString p_name, QString p_showname, QString p_message, QString p_action="", int p_color=0); + void log_ic_text(QString p_name, QString p_showname, QString p_message, + QString p_action = "", int p_color = 0); // adds text to the IC chatlog. p_name first as bold then p_text then a newlin // this function keeps the chatlog scrolled to the top unless there's text @@ -380,7 +384,8 @@ private: // True, if log should display colors. bool log_colors = true; - // True, if the log should display the message like name
text instead of name: text + // True, if the log should display the message like name
text instead of + // name: text bool log_newline = false; // True, if the log should include RP actions like interjections, showing evidence, etc. @@ -408,16 +413,21 @@ private: const int time_mod = 40; // the amount of time non-animated objection/hold it/takethat images stay - // onscreen for in ms - const int shout_stay_time = 724; + // onscreen for in ms, and the maximum amount of time any interjections are + // allowed to play + const int shout_static_time = 724; + const int shout_max_time = 1500; // the amount of time non-animated guilty/not guilty images stay onscreen for - // in ms - const int verdict_stay_time = 3000; + // in ms, and the maximum amount of time g/ng images are allowed to play + const int verdict_static_time = 3000; + const int verdict_max_time = 4000; // the amount of time non-animated witness testimony/cross-examination images - // stay onscreen for in ms - const int wtce_stay_time = 1500; + // stay onscreen for in ms, and the maximum time any wt/ce image is allowed to + // play + const int wtce_static_time = 1500; + const int wtce_max_time = 4000; // characters we consider punctuation const QString punctuation_chars = ".,?!:;"; @@ -441,7 +451,7 @@ private: bool is_muted = false; // state of animation, 0 = objecting, 1 = preanim, 2 = talking, 3 = idle, 4 = - // noniterrupting preanim + // noniterrupting preanim, 5 = (c) animation int anim_state = 3; // whether or not current color is a talking one @@ -512,6 +522,7 @@ private: // is the message we're about to send supposed to present evidence? bool is_presenting_evidence = false; + bool c_played = false; // whether we've played a (c)-style postanimation yet // have we already presented evidence for this message? bool evidence_presented = false; @@ -579,21 +590,20 @@ private: AOImage *ui_background; QWidget *ui_viewport; - AOScene *ui_vp_background; - AOMovie *ui_vp_speedlines; - AOCharMovie *ui_vp_player_char; - AOCharMovie *ui_vp_sideplayer_char; - AOScene *ui_vp_desk; - AOScene *ui_vp_legacy_desk; + BackgroundLayer *ui_vp_background; + ForegroundLayer *ui_vp_speedlines; + CharLayer *ui_vp_player_char; + CharLayer *ui_vp_sideplayer_char; + BackgroundLayer *ui_vp_desk; AOEvidenceDisplay *ui_vp_evidence_display; AOImage *ui_vp_chatbox; QLabel *ui_vp_showname; - AOMovie *ui_vp_chat_arrow; + InterfaceLayer *ui_vp_chat_arrow; QTextEdit *ui_vp_message; - AOMovie *ui_vp_effect; - AOMovie *ui_vp_testimony; - AOMovie *ui_vp_wtce; - AOMovie *ui_vp_objection; + EffectLayer *ui_vp_effect; + InterfaceLayer *ui_vp_testimony; + InterjectionLayer *ui_vp_wtce; + InterjectionLayer *ui_vp_objection; QTextEdit *ui_ic_chatlog; @@ -605,7 +615,7 @@ private: QTreeWidget *ui_music_list; ScrollText *ui_music_name; - AOMovie *ui_music_display; + InterfaceLayer *ui_music_display; AOButton *ui_pair_button; QListWidget *ui_pair_list; @@ -759,6 +769,7 @@ private: void regenerate_ic_chatlog(); public slots: void objection_done(); + void effect_done(); void preanim_done(); void do_screenshake(); void do_flash(); diff --git a/src/aocharmovie.cpp b/src/aocharmovie.cpp deleted file mode 100644 index 09a4b88..0000000 --- a/src/aocharmovie.cpp +++ /dev/null @@ -1,332 +0,0 @@ -#include "aocharmovie.h" - -#include "aoapplication.h" -#include "file_functions.h" -#include "misc_functions.h" - -AOCharMovie::AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app) - : QLabel(p_parent) -{ - ao_app = p_ao_app; - preanim_timer = new QTimer(this); - preanim_timer->setSingleShot(true); - - ticker = new QTimer(this); - ticker->setTimerType(Qt::PreciseTimer); - ticker->setSingleShot(false); - connect(ticker, SIGNAL(timeout()), this, SLOT(movie_ticker())); - - // connect(m_movie, SIGNAL(frameChanged(int)), this, - // SLOT(frame_change(int))); - connect(preanim_timer, SIGNAL(timeout()), this, SLOT(preanim_done())); -} - -void AOCharMovie::load_image(QString p_char, QString p_emote, - QString emote_prefix) -{ -#ifdef DEBUG_CHARMOVIE - actual_time.restart(); -#endif - QString emote_path; - QList pathlist; - pathlist = { - ao_app->get_image_suffix(ao_app->get_character_path( - p_char, emote_prefix + p_emote)), // Default path - ao_app->get_image_suffix(ao_app->get_character_path( - p_char, emote_prefix + "/" + - p_emote)), // Path check if it's categorized into a folder - ao_app->get_image_suffix(ao_app->get_character_path( - p_char, p_emote)), // Just use the non-prefixed image, animated or not - ao_app->get_image_suffix( - ao_app->get_theme_path("placeholder")), // Theme placeholder path - ao_app->get_image_suffix(ao_app->get_default_theme_path( - "placeholder")), // Default theme placeholder path - }; - - for (QString path : pathlist) { - if (file_exists(path)) { - emote_path = path; - break; - } - } - - this->clear(); - ticker->stop(); - preanim_timer->stop(); - movie_frames.clear(); - movie_delays.clear(); - movie_effects.clear(); - - if (!file_exists(emote_path)) - return; - - m_reader->setFileName(emote_path); - - // set format to apng if png supports animation - if (emote_path.endsWith("png")) { - m_reader->setFormat("apng"); - if (!m_reader->supportsAnimation()) { - m_reader->setFormat("png"); - } - } - - QPixmap f_pixmap = this->get_pixmap(m_reader->read()); - int f_delay = m_reader->nextImageDelay(); - - frame = 0; - max_frames = m_reader->imageCount(); - - this->set_frame(f_pixmap); - this->show(); - if (max_frames > 1) { - movie_frames.append(f_pixmap); - movie_delays.append(f_delay); - } - - m_char = p_char; - m_emote = emote_prefix + p_emote; - - if (network_strings.size() > 0) // our FX overwritten by networked ones - this->load_network_effects(); - else // Use default ini FX - this->load_effects(); -#ifdef DEBUG_CHARMOVIE - qDebug() << max_frames << "Setting image to " << emote_path - << "Time taken to process image:" << actual_time.elapsed(); - - actual_time.restart(); -#endif -} - -void AOCharMovie::load_effects() -{ - movie_effects.clear(); - movie_effects.resize(max_frames); - for (int e_frame = 0; e_frame < max_frames; ++e_frame) { - QString effect = ao_app->get_screenshake_frame(m_char, m_emote, e_frame); - if (effect != "") { - movie_effects[e_frame].append("shake"); - } - - effect = ao_app->get_flash_frame(m_char, m_emote, e_frame); - if (effect != "") { - movie_effects[e_frame].append("flash"); - } - - effect = ao_app->get_sfx_frame(m_char, m_emote, e_frame); - if (effect != "") { - movie_effects[e_frame].append("sfx^" + effect); - } - } -} - -void AOCharMovie::load_network_effects() -{ - movie_effects.clear(); - movie_effects.resize(max_frames); - // Order is important!!! - QStringList effects_list = {"shake", "flash", "sfx^"}; - - // Determines which list is smaller - effects_list or network_strings - and - // uses it as basis for the loop. This way, incomplete network_strings would - // still be parsed, and excess/unaccounted for networked information is - // omitted. - int effects_size = qMin(effects_list.size(), network_strings.size()); - - for (int i = 0; i < effects_size; ++i) { - QString netstring = network_strings.at(i); - QStringList emote_splits = netstring.split("^"); - foreach (QString emote, emote_splits) { - QStringList parsed = emote.split("|"); - if (parsed.size() <= 0 || parsed.at(0) != m_emote) - continue; - foreach (QString frame_data, parsed) { - QStringList frame_split = frame_data.split("="); - if (frame_split.size() <= - 1) // We might still be hanging at the emote itself (entry 0). - continue; - int f_frame = frame_split.at(0).toInt(); - if (f_frame >= max_frames) { - qDebug() << "Warning: out of bounds" << effects_list[i] << "frame" - << f_frame << "out of" << max_frames << "for" << m_char - << m_emote; - continue; - } - QString f_data = frame_split.at(1); - if (f_data != "") { - QString effect = effects_list[i]; - if (effect == "sfx^") // Currently the only frame result that feeds us - // data, let's yank it in. - effect += f_data; - qDebug() << effect << f_data << "frame" << f_frame << "for" << m_char - << m_emote; - movie_effects[f_frame].append(effect); - } - } - } - } -} - -void AOCharMovie::play() -{ - play_frame_effect(frame); - if (max_frames <= 1) { - if (play_once) - ticker->start(60); - } - else - ticker->start(this->get_frame_delay(movie_delays[frame])); -} - -void AOCharMovie::play_pre(QString p_char, QString p_emote, int duration) -{ - load_image(p_char, p_emote, ""); - // As much as I'd like to screw around with [Time] durations modifying the - // animation speed, I don't think I can reliably do that, not without looping - // through all frames in the image at least - which causes lag. So for now it - // simply ends the preanimation early instead. - play_once = true; - if (duration > - 0) // It's -1 if there's no definition in [Time] for it. In which case, it - // will let the animation run out in full. Duration 0 does the same. - preanim_timer->start(duration * - time_mod); // This timer will not fire if the animation - // finishes earlier than that - play(); -} - -void AOCharMovie::play_talking(QString p_char, QString p_emote) -{ - play_once = false; - load_image(p_char, p_emote, "(b)"); - play(); -} - -void AOCharMovie::play_idle(QString p_char, QString p_emote) -{ - play_once = false; - load_image(p_char, p_emote, "(a)"); - play(); -} - -void AOCharMovie::play_frame_effect(int frame) -{ - if (frame < max_frames) { - foreach (QString effect, movie_effects[frame]) { - if (effect == "shake") { - shake(); -#ifdef DEBUG_CHARMOVIE - qDebug() << "Attempting to play shake on frame" << frame; -#endif - } - - if (effect == "flash") { - flash(); -#ifdef DEBUG_CHARMOVIE - qDebug() << "Attempting to play flash on frame" << frame; -#endif - } - - if (effect.startsWith("sfx^")) { - QString sfx = effect.section("^", 1); - play_sfx(sfx); -#ifdef DEBUG_CHARMOVIE - qDebug() << "Attempting to play sfx" << sfx << "on frame" << frame; -#endif - } - } - } -} - -void AOCharMovie::stop() -{ - // for all intents and purposes, stopping is the same as hiding. at no point - // do we want a frozen gif to display - ticker->stop(); - preanim_timer->stop(); - this->hide(); -} - -QPixmap AOCharMovie::get_pixmap(QImage image) -{ - QPixmap f_pixmap; - if (m_flipped) - f_pixmap = QPixmap::fromImage(image.mirrored(true, false)); - else - f_pixmap = QPixmap::fromImage(image); - // auto aspect_ratio = Qt::KeepAspectRatio; - auto transform_mode = Qt::FastTransformation; - if (f_pixmap.height() > f_h) // We are downscaling, use anti-aliasing. - transform_mode = Qt::SmoothTransformation; - - f_pixmap = f_pixmap.scaledToHeight(f_h, transform_mode); - this->resize(f_pixmap.size()); - - return f_pixmap; -} - -void AOCharMovie::set_frame(QPixmap f_pixmap) -{ - this->setPixmap(f_pixmap); - QLabel::move( - x + (f_w - f_pixmap.width()) / 2, - y + (f_h - f_pixmap.height())); // Always center horizontally, always put - // at the bottom vertically -} - -void AOCharMovie::combo_resize(int w, int h) -{ - QSize f_size(w, h); - f_w = w; - f_h = h; - this->resize(f_size); -} - -int AOCharMovie::get_frame_delay(int delay) -{ - return static_cast(double(delay) * double(speed / 100)); -} - -void AOCharMovie::move(int ax, int ay) -{ - x = ax; - y = ay; - QLabel::move(x, y); -} - -void AOCharMovie::movie_ticker() -{ - ++frame; - if (frame >= max_frames) { - if (play_once) { - preanim_done(); - return; - } - else - frame = 0; - } - // qint64 difference = elapsed - movie_delays[frame]; - if (frame >= movie_frames.size()) { - m_reader->jumpToImage(frame); - movie_frames.resize(frame + 1); - movie_frames[frame] = this->get_pixmap(m_reader->read()); - movie_delays.resize(frame + 1); - movie_delays[frame] = m_reader->nextImageDelay(); - } - -#ifdef DEBUG_CHARMOVIE - qDebug() << frame << movie_delays[frame] - << "actual time taken from last frame:" << actual_time.restart(); -#endif - - this->set_frame(movie_frames[frame]); - play_frame_effect(frame); - ticker->setInterval(this->get_frame_delay(movie_delays[frame])); -} - -void AOCharMovie::preanim_done() -{ - ticker->stop(); - preanim_timer->stop(); - done(); -} diff --git a/src/aoevidencedisplay.cpp b/src/aoevidencedisplay.cpp index 2ffea2c..f6dffd8 100644 --- a/src/aoevidencedisplay.cpp +++ b/src/aoevidencedisplay.cpp @@ -11,7 +11,7 @@ AOEvidenceDisplay::AOEvidenceDisplay(QWidget *p_parent, AOApplication *p_ao_app) evidence_icon = new QLabel(this); sfx_player = new AOSfxPlayer(this, ao_app); - evidence_movie = new AOMovie(this, ao_app); + evidence_movie = new InterfaceLayer(this, ao_app); connect(evidence_movie, SIGNAL(done()), this, SLOT(show_done())); } @@ -46,9 +46,11 @@ void AOEvidenceDisplay::show_evidence(QString p_evidence_image, evidence_icon->setPixmap(f_pixmap); evidence_icon->resize(f_pixmap.size()); evidence_icon->move(icon_dimensions.x, icon_dimensions.y); - - evidence_movie->play(gif_name); - sfx_player->play(ao_app->get_sfx("evidence_present")); + evidence_movie->static_duration = 320; + evidence_movie->max_duration = 1000; + evidence_movie->set_play_once(true); + evidence_movie->load_image(gif_name, ""); + sfx_player->play(ao_app->get_sfx("evidence_present", "default")); } void AOEvidenceDisplay::reset() diff --git a/src/aolayer.cpp b/src/aolayer.cpp new file mode 100644 index 0000000..de8a451 --- /dev/null +++ b/src/aolayer.cpp @@ -0,0 +1,577 @@ +#include "aolayer.h" + +#include "aoapplication.h" +#include "file_functions.h" +#include "misc_functions.h" + +AOLayer::AOLayer(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) +{ + ao_app = p_ao_app; + + // used for culling images when their max_duration is exceeded + shfx_timer = new QTimer(this); + shfx_timer->setTimerType(Qt::PreciseTimer); + shfx_timer->setSingleShot(true); + connect(shfx_timer, SIGNAL(timeout()), this, SLOT(shfx_timer_done())); + + ticker = new QTimer(this); + ticker->setTimerType(Qt::PreciseTimer); + ticker->setSingleShot(false); + connect(ticker, SIGNAL(timeout()), this, SLOT(movie_ticker())); + + preanim_timer = new QTimer(this); + preanim_timer->setSingleShot(true); + connect(preanim_timer, SIGNAL(timeout()), this, SLOT(preanim_done())); +} + +BackgroundLayer::BackgroundLayer(QWidget *p_parent, AOApplication *p_ao_app) + : AOLayer(p_parent, p_ao_app) +{ +} +ForegroundLayer::ForegroundLayer(QWidget *p_parent, AOApplication *p_ao_app) + : AOLayer(p_parent, p_ao_app) +{ +} +CharLayer::CharLayer(QWidget *p_parent, AOApplication *p_ao_app) + : AOLayer(p_parent, p_ao_app) +{ +} +EffectLayer::EffectLayer(QWidget *p_parent, AOApplication *p_ao_app) + : AOLayer(p_parent, p_ao_app) +{ +} +InterjectionLayer::InterjectionLayer(QWidget *p_parent, AOApplication *p_ao_app) + : AOLayer(p_parent, p_ao_app) +{ +} +InterfaceLayer::InterfaceLayer(QWidget *p_parent, AOApplication *p_ao_app) + : AOLayer(p_parent, p_ao_app) +{ +} + +QString AOLayer::find_image(QList p_list) +{ + QString image_path; + for (QString path : p_list) { +#ifdef DEBUG_MOVIE + qDebug() << "checking path " << path; +#endif + if (file_exists(path)) { + image_path = path; +#ifdef DEBUG_MOVIE + qDebug() << "found path " << path; +#endif + break; + } + } + return image_path; +} + +QPixmap AOLayer::get_pixmap(QImage image) +{ + QPixmap f_pixmap; + if (m_flipped) + f_pixmap = QPixmap::fromImage(image.mirrored(true, false)); + else + f_pixmap = QPixmap::fromImage(image); + // auto aspect_ratio = Qt::KeepAspectRatio; + if (f_pixmap.height() > f_h) // We are downscaling, use anti-aliasing. + transform_mode = Qt::SmoothTransformation; + if (stretch) + f_pixmap = f_pixmap.scaled(f_w, f_h); + else + f_pixmap = f_pixmap.scaledToHeight(f_h, transform_mode); + this->resize(f_pixmap.size()); + + return f_pixmap; +} + +void AOLayer::set_frame(QPixmap f_pixmap) +{ + this->setPixmap(f_pixmap); + QLabel::move( + x + (f_w - f_pixmap.width()) / 2, + y + (f_h - f_pixmap.height())); // Always center horizontally, always put + // at the bottom vertically + this->setMask( + QRegion((f_pixmap.width() - f_w) / 2, (f_pixmap.height() - f_h) / 2, f_w, + f_h)); // make sure we don't escape the area we've been given +} + +void AOLayer::combo_resize(int w, int h) +{ + QSize f_size(w, h); + f_w = w; + f_h = h; + this->resize(f_size); +} + +int AOLayer::get_frame_delay(int delay) +{ + return static_cast(double(delay) * double(speed / 100)); +} + +void AOLayer::move(int ax, int ay) +{ + x = ax; + y = ay; + QLabel::move(x, y); +} + +void BackgroundLayer::load_image(QString p_filename) +{ + play_once = false; + cull_image = false; + QString design_path = ao_app->get_background_path("design.ini"); + transform_mode = + ao_app->get_scaling(ao_app->read_design_ini("scaling", design_path)); + stretch = ao_app->read_design_ini("stretch", design_path).startsWith("true"); + qDebug() << "[BackgroundLayer] BG loaded: " << p_filename; + start_playback(ao_app->get_image_suffix(ao_app->get_background_path(p_filename))); +} + +void ForegroundLayer::load_image(QString p_filename, QString p_charname) +{ + play_once = false; + cull_image = false; + miscname = ao_app->get_char_shouts(p_charname); + qDebug() << "[ForegroundLayer] FG loaded: " << p_filename; + QList pathlist = { + ao_app->get_image_suffix(ao_app->get_character_path( + p_charname, p_filename)), // first check the character folder + ao_app->get_image_suffix(ao_app->get_theme_path( + "misc/" + miscname + "/" + + p_filename)), // then check our theme's misc directory + ao_app->get_image_suffix(ao_app->get_misc_path( + miscname, p_filename)), // then check our global misc folder + ao_app->get_image_suffix( + ao_app->get_theme_path(p_filename)), // then check the user's theme + ao_app->get_image_suffix(ao_app->get_default_theme_path( + p_filename))}; // and finally check the default theme + start_playback(find_image(pathlist)); +} + +void CharLayer::load_image(QString p_filename, QString p_charname, + int p_duration, bool p_is_preanim) +{ + duration = p_duration; + cull_image = false; + force_continuous = false; + transform_mode = ao_app->get_scaling( + ao_app->get_emote_property(p_charname, p_filename, "scaling")); + stretch = ao_app->get_emote_property(p_charname, p_filename, "stretch") + .startsWith(true); + if ((p_charname == last_char) && + ((p_filename == last_emote) || + (p_filename.mid(3, -1) == last_emote.mid(3, -1))) && + (!is_preanim) && (!was_preanim)) { + continuous = true; + force_continuous = true; + } + else { + continuous = false; + force_continuous = true; + } + prefix = ""; + current_emote = p_filename; + was_preanim = is_preanim; + m_char = p_charname; + m_emote = current_emote; + last_char = p_charname; + last_emote = current_emote; + last_prefix = prefix; + is_preanim = p_is_preanim; + if ((p_filename.left(3) == "(a)") || (p_filename.left(3) == "(b)")) { + prefix = p_filename.left(3); + current_emote = p_filename.mid(3, -1); + } + else if ((duration > 0) || (p_filename.left(3) == "(c)")) { + if (p_filename.left(3) == "(c)") { + prefix = "(c)"; + current_emote = p_filename.mid(3, -1); + } + is_preanim = true; + play_once = true; + preanim_timer->start(duration * tick_ms); + } + qDebug() << "[CharLayer] anim loaded: prefix " << prefix << " filename " + << current_emote << " from character: " << p_charname + << " continuous: " << continuous; + QList pathlist = { + ao_app->get_image_suffix(ao_app->get_character_path( + p_charname, prefix + current_emote)), // Default path + ao_app->get_image_suffix(ao_app->get_character_path( + p_charname, + prefix + "/" + current_emote)), // Path check if it's categorized + // into a folder + ao_app->get_image_suffix(ao_app->get_character_path( + p_charname, + current_emote)), // Just use the non-prefixed image, animated or not + ao_app->get_image_suffix( + ao_app->get_theme_path("placeholder")), // Theme placeholder path + ao_app->get_image_suffix(ao_app->get_default_theme_path( + "placeholder"))}; // Default theme placeholder path + this->start_playback(find_image(pathlist)); +} + +void InterjectionLayer::load_image(QString p_filename, QString p_charname, + QString p_miscname) +{ + continuous = false; + force_continuous = true; + play_once = true; + transform_mode = ao_app->get_misc_scaling(p_miscname); + QList pathlist = { + ao_app->get_image_suffix(ao_app->get_character_path( + p_charname, p_filename)), // Character folder + ao_app->get_image_suffix(ao_app->get_theme_path( + "misc/" + p_miscname + "/" + p_filename)), // Theme misc path + ao_app->get_image_suffix( + ao_app->get_misc_path(p_miscname, p_filename)), // Misc path + ao_app->get_image_suffix( + ao_app->get_theme_path(p_filename)), // Theme path + ao_app->get_image_suffix( + ao_app->get_default_theme_path(p_filename)), // Default theme path + ao_app->get_image_suffix( + ao_app->get_theme_path("placeholder")), // Placeholder path + ao_app->get_image_suffix(ao_app->get_default_theme_path( + "placeholder")), // Default placeholder path + }; + QString final_image = find_image(pathlist); + if (final_image == ao_app->get_theme_path("custom.png") || + final_image == ao_app->get_default_theme_path("custom.png") || + final_image == ao_app->get_theme_path("witnesstestimony.png") || + final_image == ao_app->get_default_theme_path("witnesstestimony.png") || + final_image == ao_app->get_theme_path("crossexamination.png") || + final_image == ao_app->get_default_theme_path("crossexamination.png")) + // stupid exceptions because themes are stupid + final_image = find_image( + {ao_app->get_image_suffix(ao_app->get_theme_path("placeholder")), + ao_app->get_image_suffix(ao_app->get_default_theme_path("placeholder"))}); + start_playback(final_image); +} + +void EffectLayer::load_image(QString p_filename, bool p_looping) +{ + if (p_looping) + play_once = false; + else + play_once = true; + continuous = false; + force_continuous = true; + start_playback(p_filename); // handled in its own file before we see it +} + +void InterfaceLayer::load_image(QString p_filename, QString p_miscname) +{ + transform_mode = ao_app->get_misc_scaling(p_miscname); + QList pathlist = { + ao_app->get_image_suffix(ao_app->get_theme_path( + "misc/" + p_miscname + "/" + + p_filename)), // first check our theme's misc directory + ao_app->get_image_suffix(ao_app->get_misc_path( + p_miscname, p_filename)), // then check our global misc folder + ao_app->get_image_suffix(ao_app->get_theme_path( + p_filename)), // then check the user's theme for a default image + ao_app->get_image_suffix(ao_app->get_default_theme_path( + p_filename))}; // and finally check the default theme + start_playback(find_image(pathlist)); +} + +void CharLayer::start_playback(QString p_image) +{ + movie_effects.clear(); + AOLayer::start_playback(p_image); + if (network_strings.size() > 0) // our FX overwritten by networked ones + load_network_effects(); + else // Use default ini FX + load_effects(); +} + +void AOLayer::start_playback(QString p_image) +{ +#ifdef DEBUG_MOVIE + actual_time.restart(); +#endif + this->clear(); + freeze(); + movie_frames.clear(); + movie_delays.clear(); + + if (!file_exists(p_image)) + return; + + QString scaling_override = + ao_app->read_design_ini("scaling", p_image + ".ini"); + if (scaling_override != "") + transform_mode = ao_app->get_scaling(scaling_override); + QString stretch_override = + ao_app->read_design_ini("stretch", p_image + ".ini"); + if (stretch_override != "") + stretch = stretch_override.startsWith("true"); + + qDebug() << "stretch:" << stretch << "filename:" << p_image; + m_reader.setFileName(p_image); + if (m_reader.loopCount() == 0) + play_once = true; + if ((last_path == p_image) && (!force_continuous)) + continuous = true; + else if ((last_path != p_image) && !force_continuous) + continuous = false; + if (!continuous) + frame = 0; + force_continuous = false; + last_max_frames = max_frames; + max_frames = m_reader.imageCount(); + if (((continuous) && (max_frames != last_max_frames)) || max_frames == 0) { + frame = 0; + continuous = false; + } + // CANTFIX: this causes a slight hitch + // The correct way of doing this would be to use QImageReader::jumpToImage() + // and populate missing data in the movie ticker when it's needed. This is + // unforunately completely impossible, because QImageReader::jumpToImage() is + // not implemented in any image format AO2 is equipped to use. Instead, the + // default behavior is used - that is, absolutely nothing. + if (continuous) { + for (int i = frame; i--;) { + if (i <= -1) + break; + QPixmap l_pixmap = this->get_pixmap(m_reader.read()); + int l_delay = m_reader.nextImageDelay(); + movie_frames.append(l_pixmap); + movie_delays.append(l_delay); + // qDebug() << "appending delay of " << l_delay; + } + } + // qDebug() << "CONT: " << continuous << " MAX: " << max_frames + // << " LAST MAX: " << last_max_frames << " FRAME: " << frame; + QPixmap f_pixmap = this->get_pixmap(m_reader.read()); + int f_delay = m_reader.nextImageDelay(); + + this->set_frame(f_pixmap); + this->show(); + if (max_frames > 1) { + movie_frames.append(f_pixmap); + movie_delays.append(f_delay); + } + else if (max_frames <= 1) { + duration = static_duration; + play_once = false; +#ifdef DEBUG_MOVIE + qDebug() << "max_frames is <= 1, using static duration"; +#endif + } + if (duration > 0 && cull_image == true) + shfx_timer->start(duration); + play(); +#ifdef DEBUG_MOVIE + qDebug() << max_frames << "Setting image to " << image_path + << "Time taken to process image:" << actual_time.elapsed(); + + actual_time.restart(); +#endif +} + +void CharLayer::play() +{ + play_frame_effect(frame); + AOLayer::play(); +} + +void AOLayer::play() +{ + if (max_frames <= 1) { + if (play_once) + ticker->start(tick_ms); + else + this->freeze(); + } + else + ticker->start(this->get_frame_delay(movie_delays[frame])); +} + +void AOLayer::set_play_once(bool p_play_once) { play_once = p_play_once; } +void AOLayer::set_cull_image(bool p_cull_image) { cull_image = p_cull_image; } +void AOLayer::set_static_duration(int p_static_duration) +{ + static_duration = p_static_duration; +} +void AOLayer::set_max_duration(int p_max_duration) +{ + max_duration = p_max_duration; +} + +void CharLayer::load_effects() +{ + movie_effects.clear(); + movie_effects.resize(max_frames); + for (int e_frame = 0; e_frame < max_frames; ++e_frame) { + QString effect = ao_app->get_screenshake_frame(m_char, m_emote, e_frame); + if (effect != "") { + movie_effects[e_frame].append("shake"); + } + + effect = ao_app->get_flash_frame(m_char, m_emote, e_frame); + if (effect != "") { + movie_effects[e_frame].append("flash"); + } + + effect = ao_app->get_sfx_frame(m_char, m_emote, e_frame); + if (effect != "") { + movie_effects[e_frame].append("sfx^" + effect); + } + } +} + +void CharLayer::load_network_effects() +{ + movie_effects.clear(); + movie_effects.resize(max_frames); + // Order is important!!! + QStringList effects_list = {"shake", "flash", "sfx^"}; + + // Determines which list is smaller - effects_list or network_strings - and + // uses it as basis for the loop. This way, incomplete network_strings would + // still be parsed, and excess/unaccounted for networked information is + // omitted. + int effects_size = qMin(effects_list.size(), network_strings.size()); + + for (int i = 0; i < effects_size; ++i) { + QString netstring = network_strings.at(i); + QStringList emote_splits = netstring.split("^"); + for (const QString &emote : emote_splits) { + QStringList parsed = emote.split("|"); + if (parsed.size() <= 0 || parsed.at(0) != m_emote) + continue; + foreach (QString frame_data, parsed) { + QStringList frame_split = frame_data.split("="); + if (frame_split.size() <= + 1) // We might still be hanging at the emote itself (entry 0). + continue; + int f_frame = frame_split.at(0).toInt(); + if (f_frame >= max_frames || f_frame < 0) { + qDebug() << "Warning: out of bounds" << effects_list[i] << "frame" + << f_frame << "out of" << max_frames << "for" << m_emote; + continue; + } + QString f_data = frame_split.at(1); + if (f_data != "") { + QString effect = effects_list[i]; + if (effect == "sfx^") // Currently the only frame result that feeds us + // data, let's yank it in. + effect += f_data; + qDebug() << effect << f_data << "frame" << f_frame << "for" + << m_emote; + movie_effects[f_frame].append(effect); + } + } + } + } +} + +void CharLayer::play_frame_effect(int p_frame) +{ + if (p_frame < max_frames) { + foreach (QString effect, movie_effects[p_frame]) { + if (effect == "shake") { + shake(); +#ifdef DEBUG_MOVIE + qDebug() << "Attempting to play shake on frame" << frame; +#endif + } + + if (effect == "flash") { + flash(); +#ifdef DEBUG_MOVIE + qDebug() << "Attempting to play flash on frame" << frame; +#endif + } + + if (effect.startsWith("sfx^")) { + QString sfx = effect.section("^", 1); + play_sfx(sfx); +#ifdef DEBUG_MOVIE + qDebug() << "Attempting to play sfx" << sfx << "on frame" << frame; +#endif + } + } + } +} + +void AOLayer::stop() +{ + // for all intents and purposes, stopping is the same as hiding. at no point + // do we want a frozen gif to display + this->freeze(); + this->hide(); +} + +void AOLayer::freeze() +{ + // aT nO pOiNt Do We WaNt A fRoZeN gIf To DiSpLaY + ticker->stop(); + preanim_timer->stop(); + shfx_timer->stop(); +} + +void CharLayer::movie_ticker() +{ + AOLayer::movie_ticker(); + play_frame_effect(frame); +} + +void AOLayer::movie_ticker() +{ + ++frame; + if ((frame >= max_frames) && (max_frames > 1)) { + if (play_once) { + if (cull_image) + this->stop(); + else + this->freeze(); + preanim_done(); + return; + } + else + frame = 0; + } + // qint64 difference = elapsed - movie_delays[frame]; + if (frame >= movie_frames.size()) { + movie_frames.append(this->get_pixmap(m_reader.read())); + movie_delays.append(m_reader.nextImageDelay()); + } + +#ifdef DEBUG_MOVIE + qDebug() << frame << movie_delays[frame] + << "actual time taken from last frame:" << actual_time.restart(); +#endif + + this->set_frame(movie_frames[frame]); + ticker->setInterval(this->get_frame_delay(movie_delays[frame])); +} + +void CharLayer::preanim_done() +{ + if (is_preanim) + AOLayer::preanim_done(); + else + return; +} + +void AOLayer::preanim_done() +{ + ticker->stop(); + preanim_timer->stop(); + done(); +} + +void AOLayer::shfx_timer_done() +{ + this->stop(); +#ifdef DEBUG_MOVIE + qDebug() << "shfx timer signaled done"; +#endif + // signal connected to courtroom object, let it figure out what to do + done(); +} diff --git a/src/aomovie.cpp b/src/aomovie.cpp deleted file mode 100644 index 196c1d3..0000000 --- a/src/aomovie.cpp +++ /dev/null @@ -1,100 +0,0 @@ -#include "aomovie.h" - -#include "courtroom.h" -#include "file_functions.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(); - m_movie->setCacheMode(QMovie::CacheAll); - - this->setMovie(m_movie); - - timer = new QTimer(this); - timer->setTimerType(Qt::PreciseTimer); - timer->setSingleShot(true); - - connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frame_change(int))); - connect(timer, SIGNAL(timeout()), this, SLOT(timer_done())); -} - -void AOMovie::set_play_once(bool p_play_once) { play_once = p_play_once; } - -void AOMovie::play(QString p_image, QString p_char, QString p_custom_theme, - int duration) -{ - m_movie->stop(); - - QString shout_path = p_image; - if (!file_exists(p_image)) { - QList pathlist; - - pathlist = { - ao_app->get_image_suffix( - ao_app->get_character_path(p_char, p_image)), // Character folder - ao_app->get_image_suffix(ao_app->get_base_path() + "misc/" + - p_custom_theme + "/" + p_image), // Misc path - ao_app->get_image_suffix(ao_app->get_custom_theme_path( - p_custom_theme, p_image)), // Custom theme path - ao_app->get_image_suffix(ao_app->get_theme_path(p_image)), // Theme path - ao_app->get_image_suffix( - ao_app->get_default_theme_path(p_image)), // Default theme path - ao_app->get_image_suffix( - ao_app->get_theme_path("placeholder")), // Placeholder path - ao_app->get_image_suffix(ao_app->get_default_theme_path( - "placeholder")), // Default placeholder path - }; - - for (QString path : pathlist) { - if (file_exists(path)) { - shout_path = path; - break; - } - } - } - - m_movie->setFileName(shout_path); - - if (m_movie->loopCount() == 0) - play_once = true; - - this->show(); - m_movie->start(); - if (m_movie->frameCount() == 0 && duration > 0) - timer->start(duration); -} - -void AOMovie::stop() -{ - m_movie->stop(); - this->hide(); -} - -void AOMovie::frame_change(int n_frame) -{ - // If it's a "static movie" (only one frame - png image), we can't change - // frames - ignore this function (use timer instead). If the frame didn't reach - // the last frame or the movie is continuous, don't stop the movie. - if (m_movie->frameCount() == 0 || n_frame < (m_movie->frameCount() - 1) || - !play_once) - return; - // we need this or else the last frame wont show - timer->start(m_movie->nextFrameDelay()); -} - -void AOMovie::timer_done() -{ - this->stop(); - // signal connected to courtroom object, let it figure out what to do - done(); -} - -void AOMovie::combo_resize(int w, int h) -{ - QSize f_size(w, h); - this->resize(f_size); - m_movie->setScaledSize(f_size); -} diff --git a/src/aoscene.cpp b/src/aoscene.cpp deleted file mode 100644 index 78d69ac..0000000 --- a/src/aoscene.cpp +++ /dev/null @@ -1,132 +0,0 @@ -#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); - m_movie->setCacheMode(QMovie::CacheAll); - last_image = ""; -} - -void AOScene::set_image(QString p_image) -{ - QString background_path = - ao_app->get_image_suffix(ao_app->get_background_path(p_image)); - if (!file_exists(background_path)) // If image is missing, clear current image - { - this->clear(); - this->setMovie(nullptr); - - m_movie->stop(); - last_image = ""; - return; - } - - if (!file_exists(background_path) || background_path != last_image) - { - this->clear(); - this->setMovie(nullptr); - - m_movie->stop(); - m_movie->setFileName(background_path); - } - - if (m_movie->isValid() && m_movie->frameCount() > 1) { - m_movie->jumpToNextFrame(); - float scale_factor = static_cast(f_h) / - static_cast(m_movie->frameRect().height()); - // preserve aspect ratio - int n_w = static_cast(m_movie->frameRect().width() * scale_factor); - int n_h = static_cast(m_movie->frameRect().height() * scale_factor); - - m_movie->setScaledSize(QSize(n_w, n_h)); - this->resize(m_movie->scaledSize()); - if (!file_exists(background_path) || background_path != last_image) - { - this->setMovie(m_movie); - m_movie->start(); - } - QLabel::move(x + (f_w - n_w) / 2, y + (f_h - n_h) / 2); // Center - } - else { - QPixmap background(background_path); - auto transform_mode = Qt::FastTransformation; - if (background.height() > f_h) // We are downscaling, use anti-aliasing. - transform_mode = Qt::SmoothTransformation; - - background = background.scaledToHeight(f_h, transform_mode); - this->resize(background.size()); - this->setPixmap(background); - QLabel::move( - x + (f_w - background.width()) / 2, - y + (f_h - background.height()) / - 2); // Always center horizontally, always center vertically - } - last_image = background_path; -} - -void AOScene::set_legacy_desk(QString p_image) -{ - - QString desk_path = - ao_app->get_image_suffix(ao_app->get_background_path(p_image)); - if (!file_exists(desk_path)) // If image is missing, clear current image - { - this->clear(); - this->setMovie(nullptr); - - m_movie->stop(); - last_image = ""; - return; - } - - if (file_exists(desk_path) && desk_path == last_image) - return; - - QPixmap f_desk(desk_path); - - // vanilla desks vary in both width and height. in order to make that work - // with viewport rescaling, some INTENSE math is needed. - int vp_width = m_parent->width(); - int vp_height = m_parent->height(); - - double h_modifier = vp_height / 192; - - int final_h = static_cast(h_modifier * f_desk.height()); - - this->clear(); - this->setMovie(nullptr); - - m_movie->stop(); - m_movie->setFileName(desk_path); - - m_movie->setScaledSize(QSize(vp_width, final_h)); - - if (m_movie->isValid() && m_movie->frameCount() > 1) { - this->setMovie(m_movie); - m_movie->start(); - } - else { - this->resize(vp_width, final_h); - this->setPixmap(f_desk.scaled(vp_width, final_h)); - } - last_image = desk_path; -} - -void AOScene::combo_resize(int w, int h) -{ - QSize f_size(w, h); - f_w = w; - f_h = h; - this->resize(f_size); -} - -void AOScene::move(int ax, int ay) -{ - x = ax; - y = ay; - QLabel::move(x, y); -} diff --git a/src/courtroom.cpp b/src/courtroom.cpp index 2021671..8f5ee4b 100644 --- a/src/courtroom.cpp +++ b/src/courtroom.cpp @@ -43,21 +43,22 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() 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_background = new BackgroundLayer(ui_viewport, ao_app); + ui_vp_speedlines = new ForegroundLayer(ui_viewport, ao_app); + ui_vp_player_char = new CharLayer(ui_viewport, ao_app); + ui_vp_sideplayer_char = new CharLayer(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_desk = new BackgroundLayer(ui_viewport, ao_app); + + ui_vp_effect = new EffectLayer(this, ao_app); + ui_vp_effect->setAttribute(Qt::WA_TransparentForMouseEvents); ui_vp_evidence_display = new AOEvidenceDisplay(ui_viewport, ao_app); ui_vp_chatbox = new AOImage(this, ao_app); ui_vp_showname = new QLabel(ui_vp_chatbox); ui_vp_showname->setAlignment(Qt::AlignLeft); - ui_vp_chat_arrow = new AOMovie(ui_vp_chatbox, ao_app); + ui_vp_chat_arrow = new InterfaceLayer(this, ao_app); ui_vp_chat_arrow->set_play_once(false); ui_vp_message = new QTextEdit(this); @@ -66,14 +67,13 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_vp_message->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); ui_vp_message->setReadOnly(true); - ui_vp_testimony = new AOMovie(this, ao_app); + ui_vp_testimony = new InterfaceLayer(this, ao_app); ui_vp_testimony->set_play_once(false); ui_vp_testimony->setAttribute(Qt::WA_TransparentForMouseEvents); - ui_vp_effect = new AOMovie(this, ao_app); - ui_vp_effect->setAttribute(Qt::WA_TransparentForMouseEvents); - ui_vp_wtce = new AOMovie(this, ao_app); + ui_vp_wtce = new InterjectionLayer(this, ao_app); + ui_vp_wtce->set_play_once(true); ui_vp_wtce->setAttribute(Qt::WA_TransparentForMouseEvents); - ui_vp_objection = new AOMovie(this, ao_app); + ui_vp_objection = new InterjectionLayer(this, ao_app); ui_vp_objection->setAttribute(Qt::WA_TransparentForMouseEvents); ui_ic_chatlog = new QTextEdit(this); @@ -113,7 +113,7 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() ui_music_list->setUniformRowHeights(true); - ui_music_display = new AOMovie(this, ao_app); + ui_music_display = new InterfaceLayer(this, ao_app); ui_music_display->set_play_once(false); ui_music_display->setAttribute(Qt::WA_TransparentForMouseEvents); @@ -271,6 +271,8 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow() connect(keepalive_timer, SIGNAL(timeout()), this, SLOT(ping_server())); connect(ui_vp_objection, SIGNAL(done()), this, SLOT(objection_done())); + connect(ui_vp_effect, SIGNAL(done()), this, SLOT(effect_done())); + connect(ui_vp_wtce, SIGNAL(done()), this, SLOT(effect_done())); connect(ui_vp_player_char, SIGNAL(done()), this, SLOT(preanim_done())); connect(ui_vp_player_char, SIGNAL(shake()), this, SLOT(do_screenshake())); connect(ui_vp_player_char, SIGNAL(flash()), this, SLOT(do_flash())); @@ -532,13 +534,8 @@ void Courtroom::set_widgets() ui_vp_desk->move(0, 0); ui_vp_desk->combo_resize(ui_viewport->width(), ui_viewport->height()); - // the size of the ui_vp_legacy_desk element relies on various factors and is - // set in set_scene() - double y_modifier = 147.0 / 192.0; int final_y = static_cast(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->combo_resize(ui_viewport->width(), @@ -553,11 +550,14 @@ void Courtroom::set_widgets() ui_vp_chat_arrow->hide(); } else { - ui_vp_chat_arrow->move(design_ini_result.x, design_ini_result.y); - ui_vp_chat_arrow->combo_resize(design_ini_result.width, - design_ini_result.height); + ui_vp_chat_arrow->move(design_ini_result.x + ui_vp_chatbox->x(), design_ini_result.y + ui_vp_chatbox->y()); + ui_vp_chat_arrow->combo_resize(design_ini_result.width, design_ini_result.height); } + // layering shenanigans with ui_vp_chatbox prevent us from doing the sensible + // thing, which is to parent these to ui_viewport. instead, AOLayer handles + // masking so we don't overlap parts of the UI, and they become free floating + // widgets. ui_vp_testimony->move(ui_viewport->x(), ui_viewport->y()); ui_vp_testimony->combo_resize(ui_viewport->width(), ui_viewport->height()); @@ -659,18 +659,16 @@ void Courtroom::set_widgets() ui_music_display->combo_resize(design_ini_result.width, design_ini_result.height); } - - ui_music_display->play("music_display"); - ui_music_display->set_play_once(false); + ui_music_display->load_image("music_display", ""); 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_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_vp_chatbox, "chatbox"); set_size_and_pos(ui_ic_chat_name, "ic_chat_name"); } @@ -1120,6 +1118,24 @@ void Courtroom::set_size_and_pos(QWidget *p_widget, QString p_identifier) } } +void Courtroom::set_size_and_pos(QWidget *p_widget, QString p_identifier, + QString p_char) +{ + QString filename = "courtroom_design.ini"; + + pos_size_type design_ini_result = + ao_app->get_element_dimensions(p_identifier, filename, p_char); + + 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()) { @@ -1219,11 +1235,11 @@ void Courtroom::set_background(QString p_background, bool display) is_ao2_bg = true; if (is_ao2_bg) { - set_size_and_pos(ui_vp_chatbox, "ao2_chatbox"); + // 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_vp_chatbox, "chatbox"); set_size_and_pos(ui_ic_chat_message, "ic_chat_message"); } @@ -1403,6 +1419,12 @@ void Courtroom::update_character(int p_cid) } } } + if (is_ao2_bg) { + set_size_and_pos(ui_vp_chatbox, "ao2_chatbox", f_char); + } + else { + set_size_and_pos(ui_vp_chatbox, "chatbox", f_char); + } if (m_cid != -1) // there is no name at char_list -1, and we crash if we try // to find one @@ -1838,7 +1860,8 @@ void Courtroom::on_chat_return_pressed() packet_contents.append(ui_additive->isChecked() ? "1" : "0"); } if (ao_app->effects_enabled) { - QString fx_sound = ao_app->get_effect_sound(effect, current_char); + QString fx_sound = + ao_app->get_effect_property(effect, current_char, "sound"); QString p_effect = ao_app->read_char_ini(current_char, "effects", "Options"); packet_contents.append(effect + "|" + p_effect + "|" + fx_sound); @@ -1919,12 +1942,9 @@ void Courtroom::chatmessage_enqueue(QStringList p_contents) } // Record the log I/O, log files should be accurate. - log_chatmessage(p_contents[MESSAGE], f_char_id, p_contents[SHOWNAME], p_contents[TEXT_COLOR].toInt()); - if (ao_app->is_desyncrhonized_logs_enabled()) { - // Display the logs immediately. - display_log_chatmessage(p_contents[MESSAGE], f_char_id, p_contents[SHOWNAME], p_contents[TEXT_COLOR].toInt()); - } - + // If desynced logs are on, display the log IC immediately. + LogMode log_mode = ao_app->is_desyncrhonized_logs_enabled() ? DISPLAY_AND_IO : IO_ONLY; + log_chatmessage(p_contents[MESSAGE], f_char_id, p_contents[SHOWNAME], p_contents[TEXT_COLOR].toInt(), log_mode); // Send this boi into the queue chatmessage_queue.enqueue(p_contents); @@ -1939,11 +1959,12 @@ void Courtroom::chatmessage_enqueue(QStringList p_contents) void Courtroom::chatmessage_dequeue() { // Chat stopped being processed, indicate that the user can post their message now. - QString f_char = m_chatmessage[CHAR_NAME]; - QString f_custom_theme = ao_app->get_char_shouts(f_char); - ui_vp_chat_arrow->play( - "chat_arrow", f_char, - f_custom_theme); + QString f_custom_theme; + if (ao_app->is_customchat_enabled()) { + QString f_char = m_chatmessage[CHAR_NAME]; + f_custom_theme = ao_app->get_chat(f_char); + } + ui_vp_chat_arrow->load_image("chat_arrow", f_custom_theme); // Nothing to parse in the queue if (chatmessage_queue.isEmpty()) @@ -1975,7 +1996,7 @@ void Courtroom::unpack_chatmessage(QStringList p_contents) if (!ao_app->is_desyncrhonized_logs_enabled()) { // We have logs displaying as soon as we reach the message in our queue, which is a less confusing but also less accurate experience for the user. - display_log_chatmessage(m_chatmessage[MESSAGE], m_chatmessage[CHAR_ID].toInt(), m_chatmessage[SHOWNAME], m_chatmessage[TEXT_COLOR].toInt()); + log_chatmessage(m_chatmessage[MESSAGE], m_chatmessage[CHAR_ID].toInt(), m_chatmessage[SHOWNAME], m_chatmessage[TEXT_COLOR].toInt(), DISPLAY_ONLY); } // Process the callwords for this message @@ -1994,7 +2015,7 @@ void Courtroom::unpack_chatmessage(QStringList p_contents) handle_ic_message(); } -void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_showname, int f_color) +void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_showname, int f_color, LogMode f_log_mode) { // Display name will use the showname QString f_displayname = f_showname; @@ -2058,95 +2079,17 @@ void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_show } break; } - log_ic_text(f_char, f_displayname, shout_message, tr("shouts")); - } - - // Obtain evidence ID we're trying to work with - int f_evi_id = m_chatmessage[EVIDENCE_ID].toInt(); - // If the evidence ID is in the valid range - if (f_evi_id > 0 && f_evi_id <= local_evidence_list.size()) { - // Obtain the evidence name - QString f_evi_name = local_evidence_list.at(f_evi_id - 1).name; - // Add the message to the logs file - log_ic_text(f_showname, f_displayname, f_evi_name, - tr("has presented evidence")); - } - } - - // If our current message is a blankpost, the chat log isn't empty, the chat log's last message is a blank post, and the blankpost's showname is the same as ours - if (f_message.isEmpty() && !ic_chatlog_history.isEmpty() && ic_chatlog_history.last().get_message().isEmpty() && ic_chatlog_history.last().get_showname() == f_displayname) - return; // Skip adding message - - // Add the message to the logs file - log_ic_text(f_showname, f_displayname, f_message, "", - f_color); -} - -void Courtroom::display_log_chatmessage(QString f_message, int f_char_id, QString f_showname, int f_color) -{ - // Display name will use the showname - QString f_displayname = f_showname; - if (f_char_id != -1) { - // Grab the char.ini showname - f_showname = ao_app->get_showname(char_list.at(f_char_id).name); - // If custom serversided shownames are not enabled - if (!ui_showname_enable->isChecked()) { - // Set the display name to the char.ini showname - f_displayname = f_showname; - } - } - // If display name is just whitespace, use the char.ini showname. - if (f_displayname.trimmed().isEmpty()) - f_displayname = f_showname; - - if (log_ic_actions) { - // Check if a custom objection is in use - int objection_mod = 0; - QString custom_objection = ""; - if (m_chatmessage[OBJECTION_MOD].contains("4&")) { - objection_mod = 4; - custom_objection = m_chatmessage[OBJECTION_MOD].split( - "4&")[1]; // takes the name of custom objection. - } - else { - 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 (objection_mod <= 4 && objection_mod >= 1) { - QString shout_message; - switch (objection_mod) { - case 1: - shout_message = ao_app->read_char_ini(f_char, "holdit_message", "Shouts"); - if (shout_message == "") - shout_message = tr("HOLD IT!"); - break; - case 2: - shout_message = ao_app->read_char_ini(f_char, "objection_message", "Shouts"); - if (shout_message == "") - shout_message = tr("OBJECTION!"); - break; - case 3: - shout_message = ao_app->read_char_ini(f_char, "takethat_message", "Shouts"); - if (shout_message == "") - shout_message = tr("TAKE THAT!"); - break; - // case 4 is AO2 only - case 4: - if (custom_objection != "") { - shout_message = ao_app->read_char_ini(f_char, custom_objection.split('.')[0] + "_message", "Shouts"); - if (shout_message == "") - shout_message = custom_objection.split('.')[0]; - } - else { - shout_message = ao_app->read_char_ini(f_char, "custom_message", "Shouts"); - if (shout_message == "") - shout_message = tr("CUSTOM OBJECTION!"); - } - break; + switch (f_log_mode) { + case IO_ONLY: + log_ic_text(f_char, f_displayname, shout_message, tr("shouts")); + break; + case DISPLAY_AND_IO: + log_ic_text(f_char, f_displayname, shout_message, tr("shouts")); + [[fallthrough]]; + case DISPLAY_ONLY: + append_ic_text(shout_message, f_displayname, tr("shouts")); + break; } - append_ic_text(shout_message, f_displayname, tr("shouts")); } // Obtain evidence ID we're trying to work with @@ -2155,19 +2098,35 @@ void Courtroom::display_log_chatmessage(QString f_message, int f_char_id, QStrin if (f_evi_id > 0 && f_evi_id <= local_evidence_list.size()) { // Obtain the evidence name QString f_evi_name = local_evidence_list.at(f_evi_id - 1).name; - // Append the message to the IC chatlogs in client - append_ic_text(f_evi_name, f_displayname, tr("has presented evidence")); + switch (f_log_mode) { + case IO_ONLY: + log_ic_text(f_showname, f_displayname, f_evi_name, tr("has presented evidence")); + break; + case DISPLAY_AND_IO: + log_ic_text(f_showname, f_displayname, f_evi_name, tr("has presented evidence")); + [[fallthrough]]; + case DISPLAY_ONLY: + append_ic_text(f_evi_name, f_displayname, tr("has presented evidence")); + break; + } } } - // If our current message is a blankpost, the chat log isn't empty, the chat log's last message is a blank post, and the blankpost's showname is the same as ours - if (f_message.isEmpty() && last_ic_message == f_displayname + ":") - return; // Skip adding message - - last_ic_message = f_displayname + ":" + f_message; - // Append the message to the IC chatlogs in client - append_ic_text(f_message, f_displayname, "", - f_color); + // If the chat message isn't a blankpost, or the chatlog history is empty, or its last message isn't a blankpost + if (!f_message.isEmpty() || + ic_chatlog_history.isEmpty() || ic_chatlog_history.last().get_message() != "") { + switch (f_log_mode) { + case IO_ONLY: + log_ic_text(f_showname, f_displayname, f_message, "",f_color); + break; + case DISPLAY_AND_IO: + log_ic_text(f_showname, f_displayname, f_message, "",f_color); + [[fallthrough]]; + case DISPLAY_ONLY: + append_ic_text(f_message, f_displayname, "",f_color); + break; + } + } } bool Courtroom::handle_objection() @@ -2184,64 +2143,75 @@ bool Courtroom::handle_objection() 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 (is_ao2_bg) { + set_size_and_pos(ui_vp_chatbox, "ao2_chatbox", m_chatmessage[CHAR_NAME]); + } + else { + set_size_and_pos(ui_vp_chatbox, "chatbox", m_chatmessage[CHAR_NAME]); + } + set_size_and_pos(ui_vp_showname, "showname", m_chatmessage[CHAR_NAME]); + set_size_and_pos(ui_vp_message, "message", m_chatmessage[CHAR_NAME]); + ui_vp_message->move(ui_vp_message->x() + ui_vp_chatbox->x(), + ui_vp_message->y() + ui_vp_chatbox->y()); + ui_vp_message->setTextInteractionFlags(Qt::NoTextInteraction); // if an objection is used if (objection_mod <= 4 && objection_mod >= 1) { - QString shout_message; + ui_vp_objection->set_static_duration(shout_static_time); + ui_vp_objection->set_max_duration(shout_max_time); + QString filename; switch (objection_mod) { case 1: - ui_vp_objection->play("holdit_bubble", f_char, f_custom_theme, 724); - objection_player->play("holdit", f_char, f_custom_theme); - shout_message = ao_app->read_char_ini(f_char, "holdit_message", "Shouts"); - if (shout_message == "") - shout_message = tr("HOLD IT!"); + filename = "holdit_bubble"; + objection_player->play("holdit", m_chatmessage[CHAR_NAME], + ao_app->get_char_shouts(m_chatmessage[CHAR_NAME])); break; case 2: - ui_vp_objection->play("objection_bubble", f_char, f_custom_theme, 724); - objection_player->play("objection", f_char, f_custom_theme); - shout_message = ao_app->read_char_ini(f_char, "objection_message", "Shouts"); - if (shout_message == "") - shout_message = tr("OBJECTION!"); + filename = "objection_bubble"; + objection_player->play("objection", m_chatmessage[CHAR_NAME], + ao_app->get_char_shouts(m_chatmessage[CHAR_NAME])); if (ao_app->objection_stop_music()) music_player->stop(); break; case 3: - ui_vp_objection->play("takethat_bubble", f_char, f_custom_theme, 724); - objection_player->play("takethat", f_char, f_custom_theme); - shout_message = ao_app->read_char_ini(f_char, "takethat_message", "Shouts"); - if (shout_message == "") - shout_message = tr("TAKE THAT!"); + filename = "takethat_bubble"; + objection_player->play("takethat", m_chatmessage[CHAR_NAME], + ao_app->get_char_shouts(m_chatmessage[CHAR_NAME])); break; // case 4 is AO2 only case 4: if (custom_objection != "") { - ui_vp_objection->play("custom_objections/" + custom_objection, f_char, - f_custom_theme, shout_stay_time); - objection_player->play("custom_objections/" + - custom_objection.split('.')[0], - f_char, f_custom_theme); - shout_message = ao_app->read_char_ini(f_char, custom_objection.split('.')[0] + "_message", "Shouts"); - if (shout_message == "") - shout_message = custom_objection.split('.')[0]; + filename = "custom_objections/" + custom_objection; + objection_player->play( + "custom_objections/" + custom_objection.split('.')[0], + m_chatmessage[CHAR_NAME], + ao_app->get_char_shouts(m_chatmessage[CHAR_NAME])); } else { - ui_vp_objection->play("custom", f_char, f_custom_theme, - shout_stay_time); - objection_player->play("custom", f_char, f_custom_theme); - shout_message = ao_app->read_char_ini(f_char, "custom_message", "Shouts"); - if (shout_message == "") - shout_message = tr("CUSTOM OBJECTION!"); + filename = "custom"; + objection_player->play( + "custom", m_chatmessage[CHAR_NAME], + ao_app->get_char_shouts(m_chatmessage[CHAR_NAME])); } - break; + break; + m_chatmessage[EMOTE_MOD] = 1; } + ui_vp_objection->load_image( + filename, m_chatmessage[CHAR_NAME], + ao_app->get_char_shouts(m_chatmessage[CHAR_NAME])); sfx_player->clear(); // Objection played! Cut all sfx. return true; } + display_character(); return false; } +void Courtroom::effect_done() +{ + ui_vp_effect->stop(); + ui_vp_wtce->stop(); +} + void Courtroom::display_character() { // Stop all previously playing animations, effects etc. @@ -2333,26 +2303,6 @@ void Courtroom::display_pair_character(QString other_charid, QString other_offse ui_vp_sideplayer_char->move(ui_viewport->width() * offset_x / 100, ui_viewport->height() * offset_y / 100); - // Split the charid according to the ^ to determine if we have "ordering" info - QStringList args = other_charid.split("^"); - if (args.size() > - 1) // This ugly workaround is so we don't make an extra packet just - // for this purpose. Rewrite pairing when? - { - // Change the order of appearance based on the pair order variable - int order = args.at(1).toInt(); - switch (order) { - case 0: // Our character is in front - ui_vp_sideplayer_char->stackUnder(ui_vp_player_char); - break; - case 1: // Our character is behind - ui_vp_player_char->stackUnder(ui_vp_sideplayer_char); - break; - default: - break; - } - } - // Flip the pair character if (ao_app->flipping_enabled && m_chatmessage[OTHER_FLIP].toInt() == 1) ui_vp_sideplayer_char->set_flipped(true); @@ -2360,10 +2310,11 @@ void Courtroom::display_pair_character(QString other_charid, QString other_offse ui_vp_sideplayer_char->set_flipped(false); // Play the other pair character's idle animation - ui_vp_sideplayer_char->play_idle(m_chatmessage[OTHER_NAME], - m_chatmessage[OTHER_EMOTE]); + QString filename = "(a)" + m_chatmessage[OTHER_EMOTE]; + ui_vp_sideplayer_char->load_image(filename, m_chatmessage[OTHER_NAME], + 0, false); + } } - } } void Courtroom::handle_emote_mod(int emote_mod, bool p_immediate) @@ -2494,7 +2445,11 @@ void Courtroom::do_flash() QString f_char = m_chatmessage[CHAR_NAME]; QString f_custom_theme = ao_app->get_char_shouts(f_char); - ui_vp_effect->play("realizationflash", f_char, f_custom_theme, 60); + ui_vp_effect->stretch = true; + ui_vp_effect->set_static_duration(60); + ui_vp_effect->set_max_duration(60); + ui_vp_effect->load_image( + ao_app->get_effect("realization", f_char, f_custom_theme), false); } void Courtroom::do_effect(QString fx_name, QString fx_sound, QString p_char, @@ -2511,12 +2466,17 @@ void Courtroom::do_effect(QString fx_name, QString fx_sound, QString p_char, // Only check if effects are disabled after playing the sound if it exists if (!ao_app->is_effects_enabled()) return; - + ui_vp_effect->transform_mode = ao_app->get_scaling( + ao_app->get_effect_property(fx_name, p_char, "scaling")); + ui_vp_effect->stretch = + ao_app->get_effect_property(fx_name, p_char, "stretch") + .startsWith("true"); ui_vp_effect->set_play_once( false); // The effects themselves dictate whether or not they're looping. // Static effects will linger. - ui_vp_effect->play(effect); // It will set_play_once to true if the filepath - // provided is not designed to loop more than once + ui_vp_effect->set_static_duration(0); + ui_vp_effect->set_max_duration(0); + ui_vp_effect->load_image(effect, false); } void Courtroom::play_char_sfx(QString sfx_name) @@ -2530,7 +2490,7 @@ void Courtroom::initialize_chatbox() if (f_charid >= 0 && f_charid < char_list.size() && (m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked())) { QString real_name = char_list.at(f_charid).name; - + ui_vp_player_char->set_static_duration(0); QString f_showname = ao_app->get_showname(real_name); ui_vp_showname->setText(f_showname); @@ -2562,23 +2522,28 @@ void Courtroom::initialize_chatbox() QString chatbox = ao_app->get_chat(customchar); if (chatbox != "" && ao_app->is_customchat_enabled()) { - chatbox_path = ao_app->get_base_path() + "misc/" + chatbox + "/chat"; - if (!ui_vp_chatbox->set_chatbox(chatbox_path)) - ui_vp_chatbox->set_chatbox(chatbox_path + "box"); + chatbox_path = ao_app->get_theme_path("misc/" + chatbox + "/chat"); + if (!ui_vp_chatbox->set_chatbox(chatbox_path)) { + chatbox_path = ao_app->get_base_path() + "misc/" + chatbox + "/chat"; + if (!ui_vp_chatbox->set_chatbox(chatbox_path)) + ui_vp_chatbox->set_chatbox(chatbox_path + "box"); + } } // This should probably be called only if any change from the last chat // arrow was actually detected. - pos_size_type design_ini_result = ao_app->get_element_dimensions( - "chat_arrow", "courtroom_design.ini", customchar); - if (design_ini_result.width < 0 || design_ini_result.height < 0) { - qDebug() << "W: could not find \"chat_arrow\" in courtroom_design.ini"; - ui_vp_chat_arrow->hide(); - } - else { - ui_vp_chat_arrow->move(design_ini_result.x, design_ini_result.y); - ui_vp_chat_arrow->combo_resize(design_ini_result.width, - design_ini_result.height); + if (current_misc != last_misc) { + pos_size_type design_ini_result = ao_app->get_element_dimensions( + "chat_arrow", "courtroom_design.ini", customchar); + if (design_ini_result.width < 0 || design_ini_result.height < 0) { + qDebug() << "W: could not find \"chat_arrow\" in courtroom_design.ini"; + ui_vp_chat_arrow->hide(); + } + else { + ui_vp_chat_arrow->move(design_ini_result.x + ui_vp_chatbox->x(), design_ini_result.y + ui_vp_chatbox->y()); + ui_vp_chat_arrow->combo_resize(design_ini_result.width, + design_ini_result.height); + } } pos_size_type default_width = ao_app->get_element_dimensions( @@ -2686,39 +2651,41 @@ void Courtroom::handle_ic_speaking() if (emote_mod == 5 || emote_mod == 6) { // Hide the desks ui_vp_desk->hide(); - ui_vp_legacy_desk->hide(); // Obtain character information for our character - QString f_char = m_chatmessage[CHAR_NAME]; - QString f_custom_theme = ao_app->get_char_shouts(f_char); + QString filename; // I still hate this hardcoding. If we're on pos pro, hlp and wit, use prosecution_speedlines. Otherwise, defense_speedlines. if (side == "pro" || side == "hlp" || side == "wit") - ui_vp_speedlines->play("prosecution_speedlines", f_char, f_custom_theme); + filename = "prosecution_speedlines"; else - ui_vp_speedlines->play("defense_speedlines", f_char, f_custom_theme); + filename = "defense_speedlines"; + ui_vp_speedlines->load_image(filename, m_chatmessage[CHAR_NAME]); } // Check if this is a talking color (white text, etc.) color_is_talking = color_markdown_talking_list.at(m_chatmessage[TEXT_COLOR].toInt()); - + QString filename; // If color is talking, and our state isn't already talking if (color_is_talking && text_state == 1 && anim_state < 2) { // Stop the previous animation and play the talking animation ui_vp_player_char->stop(); - ui_vp_player_char->play_talking(m_chatmessage[CHAR_NAME], - m_chatmessage[EMOTE]); + ui_vp_player_char->set_play_once(false); + filename = "(b)" + m_chatmessage[EMOTE]; + ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, false); // Set the anim state accordingly anim_state = 2; } - else if (anim_state < 3) + else if (anim_state < 3 && + anim_state != 3) // Set it to idle as we're not on that already { // Stop the previous animation and play the idle animation ui_vp_player_char->stop(); - ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME], - m_chatmessage[EMOTE]); + ui_vp_player_char->set_play_once(false); + filename = "(a)" + m_chatmessage[EMOTE]; + ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, false); // Set the anim state accordingly anim_state = 3; } @@ -3117,7 +3084,6 @@ void Courtroom::play_preanim(bool immediate) { 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); @@ -3143,8 +3109,9 @@ void Courtroom::play_preanim(bool immediate) qDebug() << "W: could not find " + anim_to_find; return; } - - ui_vp_player_char->play_pre(f_char, f_preanim, preanim_duration); + ui_vp_player_char->set_static_duration(preanim_duration); + ui_vp_player_char->set_play_once(true); + ui_vp_player_char->load_image(f_preanim, f_char, preanim_duration, true); if (immediate) anim_state = 4; @@ -3161,6 +3128,7 @@ void Courtroom::play_preanim(bool immediate) void Courtroom::preanim_done() { anim_state = 1; + qDebug() << "preanim over, anim_state set to 1"; handle_ic_speaking(); } @@ -3232,6 +3200,8 @@ void Courtroom::start_chat_ticking() // means text is currently ticking text_state = 1; + + c_played = false; } void Courtroom::chat_tick() @@ -3243,13 +3213,30 @@ void Courtroom::chat_tick() // Due to our new text speed system, we always need to stop the timer now. chat_tick_timer->stop(); + ui_vp_player_char->set_static_duration(0); + QString filename; if (tick_pos >= f_message.size()) { text_state = 2; if (anim_state < 3) { - anim_state = 3; - ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME], - m_chatmessage[EMOTE]); + QStringList c_paths = { + ao_app->get_image_suffix(ao_app->get_character_path(m_chatmessage[CHAR_NAME], "(c)" + m_chatmessage[EMOTE])), + ao_app->get_image_suffix(ao_app->get_character_path(m_chatmessage[CHAR_NAME], "(c)/" + m_chatmessage[EMOTE])) + }; + // if there is a (c) animation for this emote and we haven't played it already + if (file_exists(ui_vp_player_char->find_image(c_paths)) &&(!c_played)) { + anim_state = 5; + ui_vp_player_char->set_play_once(true); + filename = "(c)" + m_chatmessage[EMOTE]; + c_played = true; + } + else { + anim_state = 3; + ui_vp_player_char->set_play_once(false); + filename = "(a)" + m_chatmessage[EMOTE]; + } + ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, + false); } QString f_char; QString f_custom_theme; @@ -3257,7 +3244,11 @@ void Courtroom::chat_tick() f_char = m_chatmessage[CHAR_NAME]; f_custom_theme = ao_app->get_chat(f_char); } - QString f_message_filtered = filter_ic_text(f_message, true, -1, m_chatmessage[TEXT_COLOR].toInt()); + ui_vp_chat_arrow->load_image("chat_arrow",f_custom_theme); // Chat stopped being processed, indicate that. + additive_previous = + additive_previous + + filter_ic_text(f_message, true, -1, m_chatmessage[TEXT_COLOR].toInt()); + QString f_message_filtered = filter_ic_text(f_message, true, -1, m_chatmessage[TEXT_COLOR].toInt()); for (int c = 0; c < max_colors; ++c) { f_message_filtered = f_message_filtered.replace("$c" + QString::number(c), char_color_rgb_list.at(c).name(QColor::HexRgb)); } @@ -3453,16 +3444,20 @@ void Courtroom::chat_tick() // to avoid interrupting a non-interrupted preanim) { ui_vp_player_char->stop(); - ui_vp_player_char->play_talking(m_chatmessage[CHAR_NAME], - m_chatmessage[EMOTE]); + ui_vp_player_char->set_play_once(false); + filename = "(b)" + m_chatmessage[EMOTE]; + ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, + false); anim_state = 2; } else if (!color_is_talking && anim_state < 3 && anim_state != 3) // Set it to idle as we're not on that already { ui_vp_player_char->stop(); - ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME], - m_chatmessage[EMOTE]); + ui_vp_player_char->set_play_once(false); + filename = "(a)" + m_chatmessage[EMOTE]; + ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, + false); anim_state = 3; } // Continue ticking @@ -3547,19 +3542,15 @@ void Courtroom::set_scene(QString f_desk_mod, QString f_side) f_background = f_side; f_desk_image = f_side + "_overlay"; } - - 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); + ui_vp_background->load_image(f_background); + ui_vp_desk->load_image(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 { - ui_vp_legacy_desk->hide(); ui_vp_desk->show(); } } @@ -3707,31 +3698,40 @@ void Courtroom::handle_song(QStringList *p_contents) void Courtroom::handle_wtce(QString p_wtce, int variant) { QString sfx_file = "courtroom_sounds.ini"; - + QString sfx_name; + QString filename; + ui_vp_wtce->set_static_duration(wtce_static_time); + ui_vp_wtce->set_max_duration(wtce_max_time); // witness testimony if (p_wtce == "testimony1") { - sfx_player->play(ao_app->get_sfx("witness_testimony")); - ui_vp_wtce->play("witnesstestimony", "", "", 1500); - ui_vp_testimony->play("testimony"); + sfx_name = "witness_testimony"; + filename = "witnesstestimony"; + ui_vp_testimony->load_image("testimony", ""); } // cross examination else if (p_wtce == "testimony2") { - sfx_player->play(ao_app->get_sfx("cross_examination")); - ui_vp_wtce->play("crossexamination", "", "", 1500); + sfx_name = "cross_examination"; + filename = "crossexamination"; ui_vp_testimony->stop(); } else if (p_wtce == "judgeruling") { + ui_vp_wtce->set_static_duration(verdict_static_time); + ui_vp_wtce->set_max_duration(verdict_max_time); if (variant == 0) { - sfx_player->play(ao_app->get_sfx("not_guilty")); - ui_vp_wtce->play("notguilty", "", "", 3000); + sfx_name = "not_guilty"; + filename = "notguilty"; ui_vp_testimony->stop(); } else if (variant == 1) { - sfx_player->play(ao_app->get_sfx("guilty")); - ui_vp_wtce->play("guilty", "", "", 3000); + sfx_name = "guilty"; + filename = "guilty"; ui_vp_testimony->stop(); } } + QString bg_misc = ao_app->read_design_ini("misc", ao_app->get_background_path("design.ini")); + sfx_player->play(ao_app->get_sfx(sfx_name, bg_misc)); + ui_vp_wtce->load_image(filename, "", bg_misc); + ui_vp_wtce->set_play_once(true); } void Courtroom::set_hp_bar(int p_bar, int p_state) @@ -4954,18 +4954,18 @@ void Courtroom::set_text_color_dropdown() QColor color = ao_app->get_chat_color("c" + QString::number(c), current_char); color_rgb_list.append(color); - color_markdown_start_list.append(ao_app->get_chat_markdown( + color_markdown_start_list.append(ao_app->get_chat_markup( "c" + QString::number(c) + "_start", current_char)); - color_markdown_end_list.append(ao_app->get_chat_markdown( + color_markdown_end_list.append(ao_app->get_chat_markup( "c" + QString::number(c) + "_end", current_char)); color_markdown_remove_list.append( - ao_app->get_chat_markdown("c" + QString::number(c) + "_remove", - current_char) == "1"); + ao_app->get_chat_markup("c" + QString::number(c) + "_remove", + current_char) == "1"); color_markdown_talking_list.append( - ao_app->get_chat_markdown("c" + QString::number(c) + "_talking", - current_char) != "0"); + ao_app->get_chat_markup("c" + QString::number(c) + "_talking", + current_char) != "0"); - QString color_name = ao_app->get_chat_markdown( + QString color_name = ao_app->get_chat_markup( "c" + QString::number(c) + "_name", current_char); if (color_name.isEmpty()) // Not defined { diff --git a/src/path_functions.cpp b/src/path_functions.cpp index c6c73a8..728f5a4 100644 --- a/src/path_functions.cpp +++ b/src/path_functions.cpp @@ -65,6 +65,16 @@ QString AOApplication::get_character_path(QString p_char, QString p_file) return get_case_sensitive_path(path); } +QString AOApplication::get_misc_path(QString p_misc, QString p_file) +{ + QString path = get_base_path() + "misc/" + p_misc + "/" + 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; diff --git a/src/text_file_functions.cpp b/src/text_file_functions.cpp index 43a72ec..0128f56 100644 --- a/src/text_file_functions.cpp +++ b/src/text_file_functions.cpp @@ -67,8 +67,7 @@ bool AOApplication::get_log_goes_downwards() bool AOApplication::get_log_newline() { - QString result = - configini->value("log_newline", "false").value(); + QString result = configini->value("log_newline", "false").value(); return result.startsWith("true"); } @@ -80,8 +79,7 @@ int AOApplication::get_log_margin() bool AOApplication::get_log_timestamp() { - QString result = - configini->value("log_timestamp", "false").value(); + QString result = configini->value("log_timestamp", "false").value(); return result.startsWith("true"); } @@ -282,6 +280,13 @@ QString AOApplication::read_design_ini(QString p_identifier, } } +Qt::TransformationMode AOApplication::get_scaling(QString p_scaling) +{ + if (p_scaling == "smooth") + return Qt::SmoothTransformation; + return Qt::FastTransformation; +} + QPoint AOApplication::get_button_spacing(QString p_identifier, QString p_file) { QString design_ini_path = get_theme_path(p_file); @@ -315,28 +320,12 @@ pos_size_type AOApplication::get_element_dimensions(QString p_identifier, QString p_file, QString p_char) { - QString char_ini_path = - get_base_path() + "misc/" + get_chat(p_char) + "/" + p_file; - QString design_ini_path = get_theme_path(p_file); - QString default_path = get_default_theme_path(p_file); - QString f_result = read_design_ini(p_identifier, char_ini_path); - 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, design_ini_path); - if (f_result == "") { - f_result = read_design_ini(p_identifier, default_path); - - if (f_result == "") - return return_value; - } - } + QString f_result = get_design_element(p_identifier, p_file, p_char); QStringList sub_line_elements = f_result.split(","); @@ -353,17 +342,16 @@ pos_size_type AOApplication::get_element_dimensions(QString p_identifier, QString AOApplication::get_design_element(QString p_identifier, QString p_file, QString p_char) { - QString char_ini_path = - get_base_path() + "misc/" + get_chat(p_char) + "/" + p_file; - QString design_ini_path = get_theme_path(p_file); - QString default_path = get_default_theme_path(p_file); - QString f_result = read_design_ini(p_identifier, char_ini_path); - if (f_result == "") { - f_result = read_design_ini(p_identifier, design_ini_path); - if (f_result == "") - f_result = read_design_ini(p_identifier, default_path); + QStringList paths{get_theme_path("misc/" + get_chat(p_char) + "/" + + p_file), // user theme overrides base/misc + get_base_path() + "misc/" + get_chat(p_char) + "/" + p_file, + get_theme_path(p_file), get_default_theme_path(p_file)}; + for (const QString &path : paths) { + QString value = read_design_ini(p_identifier, path); + if (!value.isEmpty()) + return value; } - return f_result; + return ""; } QString AOApplication::get_font_name(QString p_identifier, QString p_file) { @@ -483,34 +471,30 @@ QString AOApplication::get_tagged_stylesheet(QString target_tag, QString p_file) return f_text; } -QString AOApplication::get_chat_markdown(QString p_identifier, QString p_chat) +QString AOApplication::get_chat_markup(QString p_identifier, QString p_chat) { - QString design_ini_path = - get_base_path() + "misc/" + get_chat(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); + QStringList paths{get_theme_path("misc/" + get_chat(p_chat) + "/config.ini"), + get_base_path() + "misc/" + get_chat(p_chat) + + "/config.ini", + get_base_path() + "misc/default/config.ini", + get_theme_path("misc/default/config.ini")}; - if (f_result == "") - f_result = read_design_ini(p_identifier, default_path); + for (const QString &path : paths) { + QString value = read_design_ini(p_identifier, path); + if (!value.isEmpty()) { + return value.toLatin1(); + } + } - return f_result.toLatin1(); + return ""; } QColor AOApplication::get_chat_color(QString p_identifier, QString p_chat) { QColor return_color(255, 255, 255); - - QString design_ini_path = - get_base_path() + "misc/" + get_chat(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; - } + QString f_result = get_chat_markup(p_identifier, p_chat); + if (f_result == "") + return return_color; QStringList color_list = f_result.split(","); @@ -524,23 +508,21 @@ QColor AOApplication::get_chat_color(QString p_identifier, QString p_chat) return return_color; } -QString AOApplication::get_sfx(QString p_identifier) +QString AOApplication::get_sfx(QString p_identifier, QString p_misc) { - 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); + QStringList paths{get_theme_path("misc/" + p_misc + "/courtroom_sounds.ini"), + get_misc_path(p_misc, "courtroom_sounds.ini"), + get_theme_path("courtroom_sounds.ini"), + get_default_theme_path("courtroom_sounds.ini")}; QString return_sfx = ""; - if (f_result == "") { - f_result = read_design_ini(p_identifier, default_path); - - if (f_result == "") - return return_sfx; + for (const QString &path : paths) { + QString value = read_design_ini(p_identifier, path); + if (!value.isEmpty()) { + return value.toLatin1(); + } } - - return_sfx = f_result; - return return_sfx; } @@ -667,6 +649,31 @@ QString AOApplication::get_blips(QString p_char) return f_result; } +QString AOApplication::get_emote_property(QString p_char, QString p_emote, + QString p_property) +{ + QString f_result = + read_char_ini(p_char, p_emote, p_property); // per-emote override + if (f_result == "") + f_result = read_char_ini(p_char, p_property, + "Options"); // global for this character + return f_result; +} + +Qt::TransformationMode AOApplication::get_misc_scaling(QString p_miscname) +{ + if (p_miscname != "") { + QString misc_transform_mode = read_design_ini( + "scaling", get_theme_path("misc/" + p_miscname + "/config.ini")); + if (misc_transform_mode == "") + misc_transform_mode = + read_design_ini("scaling", get_misc_path(p_miscname, "config.ini")); + if (misc_transform_mode == "smooth") + return Qt::SmoothTransformation; + } + return Qt::FastTransformation; +} + QString AOApplication::get_category(QString p_char) { QString f_result = read_char_ini(p_char, "category", "Options"); @@ -904,7 +911,7 @@ QStringList AOApplication::get_theme_effects() QStringList lines = read_file(p_path).split("\n"); foreach (QString effect, lines) { - effect = effect.split("=")[0].trimmed(); + effect = effect.split("=")[0].trimmed().split("_")[0]; if (!effect.isEmpty() && !effects.contains(effect)) effects.append(effect); } @@ -922,7 +929,7 @@ QStringList AOApplication::get_effects(QString p_char) QStringList lines = read_file(p_path).split("\n"); foreach (QString effect, lines) { - effect = effect.split("=")[0].trimmed(); + effect = effect.split("=")[0].trimmed().split("_")[0]; if (!effect.isEmpty() && !effects.contains(effect)) effects.append(effect); } @@ -957,25 +964,33 @@ QString AOApplication::get_effect(QString effect, QString p_char, return p_path; } -QString AOApplication::get_effect_sound(QString fx_name, QString p_char) +QString AOApplication::get_effect_property(QString fx_name, QString p_char, + QString p_property) { + QString f_property; + if (p_property == "sound") + f_property = fx_name; + else + f_property = fx_name + "_" + p_property; QString p_effect = read_char_ini(p_char, "effects", "Options"); QString p_path = get_base_path() + "misc/" + p_effect + "/effects.ini"; QString design_ini_path = get_theme_path("effects/effects.ini"); QString default_path = get_default_theme_path("effects/effects.ini"); - QString f_result = read_design_ini(fx_name, p_path); + QString f_result = read_design_ini(f_property, p_path); if (f_result == "") { - f_result = read_design_ini(fx_name, design_ini_path); + f_result = read_design_ini(f_property, design_ini_path); if (f_result == "") { - f_result = read_design_ini(fx_name, default_path); + f_result = read_design_ini(f_property, default_path); } } - if (fx_name == "realization") { + if (fx_name == "realization" && p_property == "sound") { f_result = get_custom_realization(p_char); } + qDebug() << "got" << f_property << "of" << fx_name << "==" << f_result; + return f_result; }