aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorin1tiate <32779090+in1tiate@users.noreply.github.com>2021-01-19 05:32:11 -0600
committerGitHub <noreply@github.com>2021-01-19 14:32:11 +0300
commit894b2b2a0e6bb2744e92a2d8ed363a39d7ae59b1 (patch)
treed5c0868152ca9958173f5e4f857a2fd98cfce56b
parent21b4aa5072755923f5f555605ef4dc2b01857579 (diff)
Consolidate AOScene, AOMovie, and AOCharMovie into one class, add support for (c) animations, implement emote continuity, add scaling overrides to all layer types, allow for stretch-to-fill images, allow for additional effect configuration (#302)
* Rewrite AOScene and remove the need for AOMovie and AOCharMovie by consolidation * Rename AOScene to AOLayer, apply suggestions to improve functionality * Implement suggested change to allocation * Switch from pointer to field, fix ui_vp_player_char not resetting play_once * Use the variable gif_name instead of the string "gif_name" Oops. * Total rewrite of AOLayer (again) * Add support for (c) animations, do some housekeeping * allow themes to override misc chatboxes * add support for pulling InterfaceLayer elements from theme/misc * mistakes were made * move all frame fx functionality to CharLayer subclass * virtual functions are cool mkay * remove evidence of my incompetence * allow themes to override font design inis under theme/misc * Proper support for theme/misc chatbox, fixes * Fix chatbox dimensions not updating and inline color causing missingnos * rename chat markdown to chat markup * add missing misc overrides * quick hotfix for chatblank and misc overrides * Fix oversight with backgrounds causing them to be culled * Same as last commit but for FG layer * amend comment to explain impossible shenanigans * Adjust ForegroundLayer to take charname rather than miscname, allow for checking in char folder * fix an incredibly embarrassing pathing bug * add scaling overrides for all layer types, parent orphaned viewport elements to the viewport * stupid fix because themes use "custom.png" as a button * switch to .append() * Revert "Merge branch 'aoscene_rewrite' of https://github.com/AttorneyOnline/AO2-Client into aoscene_rewrite" This reverts commit bdeb1bff7639d522031aab3c133a83b0e2a291df, reversing changes made to 125ee63b97a6f6c156e69525d88fddc625e7a978. * switch to .append() (again) * move function call to fix showname length calculation error * fix nonlooping character animations being broken Again * unparent elements from the viewport and do fancy masking arithmetic instead * use override keyword * move scaling override to char.ini, allow stretching, restructure effect property loading * fix some redundancy * unparent chat_arrow from chatbox to prevent accidental masking * at no point do we want a frozen gif to display * overhaul how wtce is handled * oops * also let sounds be pulled from theme miscs * i should probably compile before i push * actually make it work * don't check a default bg * readd 1x1 stretch thing * actually the 1x1 thing was a bad idea * Add missing parenthesis * Use load_image instead of play play is a nonexistent method now * Remote shout_message and usages because it does nothing * Remove multiple redefinitions * Add in missing brackets and indent to fix build I have know idea what this does but it brings fear * fix build error * fix chat arrow and remove duped code * remove more duped code and fix misc themes * only update chat_arrow when needed * consolidate log_chatmessage and display_log_chatmessage Co-authored-by: scatterflower <marisaposs@gameboyprinter.moe> Co-authored-by: Skye Deving <76892045+skyedeving@users.noreply.github.com> Co-authored-by: oldmud0 <oldmud0@users.noreply.github.com>
-rw-r--r--include/aoapplication.h22
-rw-r--r--include/aocharmovie.h125
-rw-r--r--include/aoevidencedisplay.h4
-rw-r--r--include/aolayer.h214
-rw-r--r--include/aomovie.h36
-rw-r--r--include/aoscene.h42
-rw-r--r--include/courtroom.h69
-rw-r--r--src/aocharmovie.cpp332
-rw-r--r--src/aoevidencedisplay.cpp10
-rw-r--r--src/aolayer.cpp577
-rw-r--r--src/aomovie.cpp100
-rw-r--r--src/aoscene.cpp132
-rw-r--r--src/courtroom.cpp514
-rw-r--r--src/path_functions.cpp10
-rw-r--r--src/text_file_functions.cpp155
15 files changed, 1207 insertions, 1135 deletions
diff --git a/include/aoapplication.h b/include/aoapplication.h
index 9b7cef13..c0c3ba37 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/<char-defined>/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 2dda0ec2..00000000
--- a/include/aocharmovie.h
+++ /dev/null
@@ -1,125 +0,0 @@
-#ifndef AOCHARMOVIE_H
-#define AOCHARMOVIE_H
-
-#include <QDebug>
-#include <QElapsedTimer>
-#include <QImageReader>
-#include <QLabel>
-#include <QTimer>
-
-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<QPixmap> movie_frames;
- QVector<int> 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<QVector<QString>> 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 979a754e..ff448c91 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 <QDebug>
@@ -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 00000000..2e0510a9
--- /dev/null
+++ b/include/aolayer.h
@@ -0,0 +1,214 @@
+#ifndef AOLAYER_H
+#define AOLAYER_H
+
+#include <QDebug>
+#include <QElapsedTimer>
+#include <QImageReader>
+#include <QLabel>
+#include <QTimer>
+#include <QBitmap>
+
+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<QString> p_list);
+
+protected:
+ AOApplication *ao_app;
+ QVector<QPixmap> movie_frames;
+ QVector<int> 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<QVector<QString>> 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 eb7f7a53..00000000
--- a/include/aomovie.h
+++ /dev/null
@@ -1,36 +0,0 @@
-#ifndef AOMOVIE_H
-#define AOMOVIE_H
-
-#include <QLabel>
-#include <QMovie>
-
-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 726e2641..00000000
--- a/include/aoscene.h
+++ /dev/null
@@ -1,42 +0,0 @@
-#ifndef AOSCENE_H
-#define AOSCENE_H
-
-#include <QDebug>
-#include <QLabel>
-#include <QMovie>
-
-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 e86330e6..5ad6fa61 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<br>text instead of name: text
+ // True, if the log should display the message like name<br>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 09a4b889..00000000
--- 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<QString> pathlist;
- pathlist = {
- ao_app->get_image_suffix(ao_app->get_character_path(
- p_char, emote_prefix + p_emote)), // Default path
- ao_app->get_image_suffix(ao_app->get_character_path(
- p_char, emote_prefix + "/" +
- p_emote)), // Path check if it's categorized into a folder
- ao_app->get_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<int>(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 2ffea2c9..f6dffd85 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 00000000..de8a451c
--- /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<QString> 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<int>(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<QString> 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<QString> 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<QString> 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<QString> 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 196c1d3e..00000000
--- 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<QString> pathlist;
-
- pathlist = {
- ao_app->get_image_suffix(
- ao_app->get_character_path(p_char, p_image)), // Character folder
- ao_app->get_image_suffix(ao_app->get_base_path() + "misc/" +
- p_custom_theme + "/" + p_image), // Misc path
- ao_app->get_image_suffix(ao_app->get_custom_theme_path(
- p_custom_theme, p_image)), // Custom theme path
- ao_app->get_image_suffix(ao_app->get_theme_path(p_image)), // Theme path
- ao_app->get_image_suffix(
- ao_app->get_default_theme_path(p_image)), // Default theme path
- ao_app->get_image_suffix(
- ao_app->get_theme_path("placeholder")), // Placeholder path
- ao_app->get_image_suffix(ao_app->get_default_theme_path(
- "placeholder")), // Default placeholder path
- };
-
- for (QString path : pathlist) {
- if (file_exists(path)) {
- shout_path = path;
- break;
- }
- }
- }
-
- m_movie->setFileName(shout_path);
-
- if (m_movie->loopCount() == 0)
- play_once = true;
-
- this->show();
- m_movie->start();
- if (m_movie->frameCount() == 0 && duration > 0)
- timer->start(duration);
-}
-
-void AOMovie::stop()
-{
- 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 78d69acd..00000000
--- 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<float>(f_h) /
- static_cast<float>(m_movie->frameRect().height());
- // preserve aspect ratio
- int n_w = static_cast<int>(m_movie->frameRect().width() * scale_factor);
- int n_h = static_cast<int>(m_movie->frameRect().height() * scale_factor);
-
- 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<int>(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 20216716..8f5ee4b6 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<int>(y_modifier * ui_viewport->height());
- ui_vp_legacy_desk->move(0, final_y);
- ui_vp_legacy_desk->hide();
ui_vp_evidence_display->move(0, 0);
ui_vp_evidence_display->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,7 +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"));
+ 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;
+ }
}
// Obtain evidence ID we're trying to work with
@@ -2067,107 +2098,35 @@ void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_show
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;
+ 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 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!");
+ // 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 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!");
+ 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;
- // 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;
- }
- append_ic_text(shout_message, f_displayname, 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;
- // Append the message to the IC chatlogs in client
- append_ic_text(f_evi_name, f_displayname, 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() && 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);
}
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 c6c73a8b..728f5a40 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 43a72ecc..0128f565 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>();
+ QString result = configini->value("log_newline", "false").value<QString>();
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>();
+ QString result = configini->value("log_timestamp", "false").value<QString>();
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;
}