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>
This commit is contained in:
in1tiate 2021-01-19 05:32:11 -06:00 committed by GitHub
parent 21b4aa5072
commit 894b2b2a0e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1210 additions and 1138 deletions

View File

@ -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.
// ======

View File

@ -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

View File

@ -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;

214
include/aolayer.h Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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();
}

View File

@ -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()

577
src/aolayer.cpp Normal file
View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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,95 +2079,17 @@ void Courtroom::log_chatmessage(QString f_message, int f_char_id, QString f_show
}
break;
}
log_ic_text(f_char, f_displayname, shout_message, tr("shouts"));
}
// Obtain evidence ID we're trying to work with
int f_evi_id = m_chatmessage[EVIDENCE_ID].toInt();
// If the evidence ID is in the valid range
if (f_evi_id > 0 && f_evi_id <= local_evidence_list.size()) {
// Obtain the evidence name
QString f_evi_name = local_evidence_list.at(f_evi_id - 1).name;
// Add the message to the logs file
log_ic_text(f_showname, f_displayname, f_evi_name,
tr("has presented evidence"));
}
}
// If our current message is a blankpost, the chat log isn't empty, the chat log's last message is a blank post, and the blankpost's showname is the same as ours
if (f_message.isEmpty() && !ic_chatlog_history.isEmpty() && ic_chatlog_history.last().get_message().isEmpty() && ic_chatlog_history.last().get_showname() == f_displayname)
return; // Skip adding message
// Add the message to the logs file
log_ic_text(f_showname, f_displayname, f_message, "",
f_color);
}
void Courtroom::display_log_chatmessage(QString f_message, int f_char_id, QString f_showname, int f_color)
{
// Display name will use the showname
QString f_displayname = f_showname;
if (f_char_id != -1) {
// Grab the char.ini showname
f_showname = ao_app->get_showname(char_list.at(f_char_id).name);
// If custom serversided shownames are not enabled
if (!ui_showname_enable->isChecked()) {
// Set the display name to the char.ini showname
f_displayname = f_showname;
}
}
// If display name is just whitespace, use the char.ini showname.
if (f_displayname.trimmed().isEmpty())
f_displayname = f_showname;
if (log_ic_actions) {
// Check if a custom objection is in use
int objection_mod = 0;
QString custom_objection = "";
if (m_chatmessage[OBJECTION_MOD].contains("4&")) {
objection_mod = 4;
custom_objection = m_chatmessage[OBJECTION_MOD].split(
"4&")[1]; // takes the name of custom objection.
}
else {
objection_mod = m_chatmessage[OBJECTION_MOD].toInt();
}
QString f_char = m_chatmessage[CHAR_NAME];
QString f_custom_theme = ao_app->get_char_shouts(f_char);
if (objection_mod <= 4 && objection_mod >= 1) {
QString shout_message;
switch (objection_mod) {
case 1:
shout_message = ao_app->read_char_ini(f_char, "holdit_message", "Shouts");
if (shout_message == "")
shout_message = tr("HOLD IT!");
break;
case 2:
shout_message = ao_app->read_char_ini(f_char, "objection_message", "Shouts");
if (shout_message == "")
shout_message = tr("OBJECTION!");
break;
case 3:
shout_message = ao_app->read_char_ini(f_char, "takethat_message", "Shouts");
if (shout_message == "")
shout_message = tr("TAKE THAT!");
break;
// case 4 is AO2 only
case 4:
if (custom_objection != "") {
shout_message = ao_app->read_char_ini(f_char, custom_objection.split('.')[0] + "_message", "Shouts");
if (shout_message == "")
shout_message = custom_objection.split('.')[0];
}
else {
shout_message = ao_app->read_char_ini(f_char, "custom_message", "Shouts");
if (shout_message == "")
shout_message = tr("CUSTOM OBJECTION!");
}
break;
switch (f_log_mode) {
case IO_ONLY:
log_ic_text(f_char, f_displayname, shout_message, tr("shouts"));
break;
case DISPLAY_AND_IO:
log_ic_text(f_char, f_displayname, shout_message, tr("shouts"));
[[fallthrough]];
case DISPLAY_ONLY:
append_ic_text(shout_message, f_displayname, tr("shouts"));
break;
}
append_ic_text(shout_message, f_displayname, tr("shouts"));
}
// Obtain evidence ID we're trying to work with
@ -2155,19 +2098,35 @@ void Courtroom::display_log_chatmessage(QString f_message, int f_char_id, QStrin
if (f_evi_id > 0 && f_evi_id <= local_evidence_list.size()) {
// Obtain the evidence name
QString f_evi_name = local_evidence_list.at(f_evi_id - 1).name;
// Append the message to the IC chatlogs in client
append_ic_text(f_evi_name, f_displayname, tr("has presented evidence"));
switch (f_log_mode) {
case IO_ONLY:
log_ic_text(f_showname, f_displayname, f_evi_name, tr("has presented evidence"));
break;
case DISPLAY_AND_IO:
log_ic_text(f_showname, f_displayname, f_evi_name, tr("has presented evidence"));
[[fallthrough]];
case DISPLAY_ONLY:
append_ic_text(f_evi_name, f_displayname, tr("has presented evidence"));
break;
}
}
}
// If our current message is a blankpost, the chat log isn't empty, the chat log's last message is a blank post, and the blankpost's showname is the same as ours
if (f_message.isEmpty() && last_ic_message == f_displayname + ":")
return; // Skip adding message
last_ic_message = f_displayname + ":" + f_message;
// Append the message to the IC chatlogs in client
append_ic_text(f_message, f_displayname, "",
f_color);
// If the chat message isn't a blankpost, or the chatlog history is empty, or its last message isn't a blankpost
if (!f_message.isEmpty() ||
ic_chatlog_history.isEmpty() || ic_chatlog_history.last().get_message() != "") {
switch (f_log_mode) {
case IO_ONLY:
log_ic_text(f_showname, f_displayname, f_message, "",f_color);
break;
case DISPLAY_AND_IO:
log_ic_text(f_showname, f_displayname, f_message, "",f_color);
[[fallthrough]];
case DISPLAY_ONLY:
append_ic_text(f_message, f_displayname, "",f_color);
break;
}
}
}
bool Courtroom::handle_objection()
@ -2184,64 +2143,75 @@ bool Courtroom::handle_objection()
objection_mod = m_chatmessage[OBJECTION_MOD].toInt();
}
QString f_char = m_chatmessage[CHAR_NAME];
QString f_custom_theme = ao_app->get_char_shouts(f_char);
if (is_ao2_bg) {
set_size_and_pos(ui_vp_chatbox, "ao2_chatbox", m_chatmessage[CHAR_NAME]);
}
else {
set_size_and_pos(ui_vp_chatbox, "chatbox", m_chatmessage[CHAR_NAME]);
}
set_size_and_pos(ui_vp_showname, "showname", m_chatmessage[CHAR_NAME]);
set_size_and_pos(ui_vp_message, "message", m_chatmessage[CHAR_NAME]);
ui_vp_message->move(ui_vp_message->x() + ui_vp_chatbox->x(),
ui_vp_message->y() + ui_vp_chatbox->y());
ui_vp_message->setTextInteractionFlags(Qt::NoTextInteraction);
// if an objection is used
if (objection_mod <= 4 && objection_mod >= 1) {
QString shout_message;
ui_vp_objection->set_static_duration(shout_static_time);
ui_vp_objection->set_max_duration(shout_max_time);
QString filename;
switch (objection_mod) {
case 1:
ui_vp_objection->play("holdit_bubble", f_char, f_custom_theme, 724);
objection_player->play("holdit", f_char, f_custom_theme);
shout_message = ao_app->read_char_ini(f_char, "holdit_message", "Shouts");
if (shout_message == "")
shout_message = tr("HOLD IT!");
filename = "holdit_bubble";
objection_player->play("holdit", m_chatmessage[CHAR_NAME],
ao_app->get_char_shouts(m_chatmessage[CHAR_NAME]));
break;
case 2:
ui_vp_objection->play("objection_bubble", f_char, f_custom_theme, 724);
objection_player->play("objection", f_char, f_custom_theme);
shout_message = ao_app->read_char_ini(f_char, "objection_message", "Shouts");
if (shout_message == "")
shout_message = tr("OBJECTION!");
filename = "objection_bubble";
objection_player->play("objection", m_chatmessage[CHAR_NAME],
ao_app->get_char_shouts(m_chatmessage[CHAR_NAME]));
if (ao_app->objection_stop_music())
music_player->stop();
break;
case 3:
ui_vp_objection->play("takethat_bubble", f_char, f_custom_theme, 724);
objection_player->play("takethat", f_char, f_custom_theme);
shout_message = ao_app->read_char_ini(f_char, "takethat_message", "Shouts");
if (shout_message == "")
shout_message = tr("TAKE THAT!");
filename = "takethat_bubble";
objection_player->play("takethat", m_chatmessage[CHAR_NAME],
ao_app->get_char_shouts(m_chatmessage[CHAR_NAME]));
break;
// case 4 is AO2 only
case 4:
if (custom_objection != "") {
ui_vp_objection->play("custom_objections/" + custom_objection, f_char,
f_custom_theme, shout_stay_time);
objection_player->play("custom_objections/" +
custom_objection.split('.')[0],
f_char, f_custom_theme);
shout_message = ao_app->read_char_ini(f_char, custom_objection.split('.')[0] + "_message", "Shouts");
if (shout_message == "")
shout_message = custom_objection.split('.')[0];
filename = "custom_objections/" + custom_objection;
objection_player->play(
"custom_objections/" + custom_objection.split('.')[0],
m_chatmessage[CHAR_NAME],
ao_app->get_char_shouts(m_chatmessage[CHAR_NAME]));
}
else {
ui_vp_objection->play("custom", f_char, f_custom_theme,
shout_stay_time);
objection_player->play("custom", f_char, f_custom_theme);
shout_message = ao_app->read_char_ini(f_char, "custom_message", "Shouts");
if (shout_message == "")
shout_message = tr("CUSTOM OBJECTION!");
filename = "custom";
objection_player->play(
"custom", m_chatmessage[CHAR_NAME],
ao_app->get_char_shouts(m_chatmessage[CHAR_NAME]));
}
break;
break;
m_chatmessage[EMOTE_MOD] = 1;
}
ui_vp_objection->load_image(
filename, m_chatmessage[CHAR_NAME],
ao_app->get_char_shouts(m_chatmessage[CHAR_NAME]));
sfx_player->clear(); // Objection played! Cut all sfx.
return true;
}
display_character();
return false;
}
void Courtroom::effect_done()
{
ui_vp_effect->stop();
ui_vp_wtce->stop();
}
void Courtroom::display_character()
{
// Stop all previously playing animations, effects etc.
@ -2333,26 +2303,6 @@ void Courtroom::display_pair_character(QString other_charid, QString other_offse
ui_vp_sideplayer_char->move(ui_viewport->width() * offset_x / 100,
ui_viewport->height() * offset_y / 100);
// Split the charid according to the ^ to determine if we have "ordering" info
QStringList args = other_charid.split("^");
if (args.size() >
1) // This ugly workaround is so we don't make an extra packet just
// for this purpose. Rewrite pairing when?
{
// Change the order of appearance based on the pair order variable
int order = args.at(1).toInt();
switch (order) {
case 0: // Our character is in front
ui_vp_sideplayer_char->stackUnder(ui_vp_player_char);
break;
case 1: // Our character is behind
ui_vp_player_char->stackUnder(ui_vp_sideplayer_char);
break;
default:
break;
}
}
// Flip the pair character
if (ao_app->flipping_enabled && m_chatmessage[OTHER_FLIP].toInt() == 1)
ui_vp_sideplayer_char->set_flipped(true);
@ -2360,10 +2310,11 @@ void Courtroom::display_pair_character(QString other_charid, QString other_offse
ui_vp_sideplayer_char->set_flipped(false);
// Play the other pair character's idle animation
ui_vp_sideplayer_char->play_idle(m_chatmessage[OTHER_NAME],
m_chatmessage[OTHER_EMOTE]);
QString filename = "(a)" + m_chatmessage[OTHER_EMOTE];
ui_vp_sideplayer_char->load_image(filename, m_chatmessage[OTHER_NAME],
0, false);
}
}
}
}
void Courtroom::handle_emote_mod(int emote_mod, bool p_immediate)
@ -2494,7 +2445,11 @@ void Courtroom::do_flash()
QString f_char = m_chatmessage[CHAR_NAME];
QString f_custom_theme = ao_app->get_char_shouts(f_char);
ui_vp_effect->play("realizationflash", f_char, f_custom_theme, 60);
ui_vp_effect->stretch = true;
ui_vp_effect->set_static_duration(60);
ui_vp_effect->set_max_duration(60);
ui_vp_effect->load_image(
ao_app->get_effect("realization", f_char, f_custom_theme), false);
}
void Courtroom::do_effect(QString fx_name, QString fx_sound, QString p_char,
@ -2511,12 +2466,17 @@ void Courtroom::do_effect(QString fx_name, QString fx_sound, QString p_char,
// Only check if effects are disabled after playing the sound if it exists
if (!ao_app->is_effects_enabled())
return;
ui_vp_effect->transform_mode = ao_app->get_scaling(
ao_app->get_effect_property(fx_name, p_char, "scaling"));
ui_vp_effect->stretch =
ao_app->get_effect_property(fx_name, p_char, "stretch")
.startsWith("true");
ui_vp_effect->set_play_once(
false); // The effects themselves dictate whether or not they're looping.
// Static effects will linger.
ui_vp_effect->play(effect); // It will set_play_once to true if the filepath
// provided is not designed to loop more than once
ui_vp_effect->set_static_duration(0);
ui_vp_effect->set_max_duration(0);
ui_vp_effect->load_image(effect, false);
}
void Courtroom::play_char_sfx(QString sfx_name)
@ -2530,7 +2490,7 @@ void Courtroom::initialize_chatbox()
if (f_charid >= 0 && f_charid < char_list.size() &&
(m_chatmessage[SHOWNAME].isEmpty() || !ui_showname_enable->isChecked())) {
QString real_name = char_list.at(f_charid).name;
ui_vp_player_char->set_static_duration(0);
QString f_showname = ao_app->get_showname(real_name);
ui_vp_showname->setText(f_showname);
@ -2562,23 +2522,28 @@ void Courtroom::initialize_chatbox()
QString chatbox = ao_app->get_chat(customchar);
if (chatbox != "" && ao_app->is_customchat_enabled()) {
chatbox_path = ao_app->get_base_path() + "misc/" + chatbox + "/chat";
if (!ui_vp_chatbox->set_chatbox(chatbox_path))
ui_vp_chatbox->set_chatbox(chatbox_path + "box");
chatbox_path = ao_app->get_theme_path("misc/" + chatbox + "/chat");
if (!ui_vp_chatbox->set_chatbox(chatbox_path)) {
chatbox_path = ao_app->get_base_path() + "misc/" + chatbox + "/chat";
if (!ui_vp_chatbox->set_chatbox(chatbox_path))
ui_vp_chatbox->set_chatbox(chatbox_path + "box");
}
}
// This should probably be called only if any change from the last chat
// arrow was actually detected.
pos_size_type design_ini_result = ao_app->get_element_dimensions(
"chat_arrow", "courtroom_design.ini", customchar);
if (design_ini_result.width < 0 || design_ini_result.height < 0) {
qDebug() << "W: could not find \"chat_arrow\" in courtroom_design.ini";
ui_vp_chat_arrow->hide();
}
else {
ui_vp_chat_arrow->move(design_ini_result.x, design_ini_result.y);
ui_vp_chat_arrow->combo_resize(design_ini_result.width,
design_ini_result.height);
if (current_misc != last_misc) {
pos_size_type design_ini_result = ao_app->get_element_dimensions(
"chat_arrow", "courtroom_design.ini", customchar);
if (design_ini_result.width < 0 || design_ini_result.height < 0) {
qDebug() << "W: could not find \"chat_arrow\" in courtroom_design.ini";
ui_vp_chat_arrow->hide();
}
else {
ui_vp_chat_arrow->move(design_ini_result.x + ui_vp_chatbox->x(), design_ini_result.y + ui_vp_chatbox->y());
ui_vp_chat_arrow->combo_resize(design_ini_result.width,
design_ini_result.height);
}
}
pos_size_type default_width = ao_app->get_element_dimensions(
@ -2686,39 +2651,41 @@ void Courtroom::handle_ic_speaking()
if (emote_mod == 5 || emote_mod == 6) {
// Hide the desks
ui_vp_desk->hide();
ui_vp_legacy_desk->hide();
// Obtain character information for our character
QString f_char = m_chatmessage[CHAR_NAME];
QString f_custom_theme = ao_app->get_char_shouts(f_char);
QString filename;
// I still hate this hardcoding. If we're on pos pro, hlp and wit, use prosecution_speedlines. Otherwise, defense_speedlines.
if (side == "pro" || side == "hlp" || side == "wit")
ui_vp_speedlines->play("prosecution_speedlines", f_char, f_custom_theme);
filename = "prosecution_speedlines";
else
ui_vp_speedlines->play("defense_speedlines", f_char, f_custom_theme);
filename = "defense_speedlines";
ui_vp_speedlines->load_image(filename, m_chatmessage[CHAR_NAME]);
}
// Check if this is a talking color (white text, etc.)
color_is_talking =
color_markdown_talking_list.at(m_chatmessage[TEXT_COLOR].toInt());
QString filename;
// If color is talking, and our state isn't already talking
if (color_is_talking && text_state == 1 &&
anim_state < 2)
{
// Stop the previous animation and play the talking animation
ui_vp_player_char->stop();
ui_vp_player_char->play_talking(m_chatmessage[CHAR_NAME],
m_chatmessage[EMOTE]);
ui_vp_player_char->set_play_once(false);
filename = "(b)" + m_chatmessage[EMOTE];
ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, false);
// Set the anim state accordingly
anim_state = 2;
}
else if (anim_state < 3)
else if (anim_state < 3 &&
anim_state != 3) // Set it to idle as we're not on that already
{
// Stop the previous animation and play the idle animation
ui_vp_player_char->stop();
ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME],
m_chatmessage[EMOTE]);
ui_vp_player_char->set_play_once(false);
filename = "(a)" + m_chatmessage[EMOTE];
ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0, false);
// Set the anim state accordingly
anim_state = 3;
}
@ -3117,7 +3084,6 @@ void Courtroom::play_preanim(bool immediate)
{
QString f_char = m_chatmessage[CHAR_NAME];
QString f_preanim = m_chatmessage[PRE_EMOTE];
// all time values in char.inis are multiplied by a constant(time_mod) to get
// the actual time
int ao2_duration = ao_app->get_ao2_preanim_duration(f_char, f_preanim);
@ -3143,8 +3109,9 @@ void Courtroom::play_preanim(bool immediate)
qDebug() << "W: could not find " + anim_to_find;
return;
}
ui_vp_player_char->play_pre(f_char, f_preanim, preanim_duration);
ui_vp_player_char->set_static_duration(preanim_duration);
ui_vp_player_char->set_play_once(true);
ui_vp_player_char->load_image(f_preanim, f_char, preanim_duration, true);
if (immediate)
anim_state = 4;
@ -3161,6 +3128,7 @@ void Courtroom::play_preanim(bool immediate)
void Courtroom::preanim_done()
{
anim_state = 1;
qDebug() << "preanim over, anim_state set to 1";
handle_ic_speaking();
}
@ -3232,6 +3200,8 @@ void Courtroom::start_chat_ticking()
// means text is currently ticking
text_state = 1;
c_played = false;
}
void Courtroom::chat_tick()
@ -3243,13 +3213,30 @@ void Courtroom::chat_tick()
// Due to our new text speed system, we always need to stop the timer now.
chat_tick_timer->stop();
ui_vp_player_char->set_static_duration(0);
QString filename;
if (tick_pos >= f_message.size()) {
text_state = 2;
if (anim_state < 3) {
anim_state = 3;
ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME],
m_chatmessage[EMOTE]);
QStringList c_paths = {
ao_app->get_image_suffix(ao_app->get_character_path(m_chatmessage[CHAR_NAME], "(c)" + m_chatmessage[EMOTE])),
ao_app->get_image_suffix(ao_app->get_character_path(m_chatmessage[CHAR_NAME], "(c)/" + m_chatmessage[EMOTE]))
};
// if there is a (c) animation for this emote and we haven't played it already
if (file_exists(ui_vp_player_char->find_image(c_paths)) &&(!c_played)) {
anim_state = 5;
ui_vp_player_char->set_play_once(true);
filename = "(c)" + m_chatmessage[EMOTE];
c_played = true;
}
else {
anim_state = 3;
ui_vp_player_char->set_play_once(false);
filename = "(a)" + m_chatmessage[EMOTE];
}
ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0,
false);
}
QString f_char;
QString f_custom_theme;
@ -3257,7 +3244,11 @@ void Courtroom::chat_tick()
f_char = m_chatmessage[CHAR_NAME];
f_custom_theme = ao_app->get_chat(f_char);
}
QString f_message_filtered = filter_ic_text(f_message, true, -1, m_chatmessage[TEXT_COLOR].toInt());
ui_vp_chat_arrow->load_image("chat_arrow",f_custom_theme); // Chat stopped being processed, indicate that.
additive_previous =
additive_previous +
filter_ic_text(f_message, true, -1, m_chatmessage[TEXT_COLOR].toInt());
QString f_message_filtered = filter_ic_text(f_message, true, -1, m_chatmessage[TEXT_COLOR].toInt());
for (int c = 0; c < max_colors; ++c) {
f_message_filtered = f_message_filtered.replace("$c" + QString::number(c), char_color_rgb_list.at(c).name(QColor::HexRgb));
}
@ -3453,16 +3444,20 @@ void Courtroom::chat_tick()
// to avoid interrupting a non-interrupted preanim)
{
ui_vp_player_char->stop();
ui_vp_player_char->play_talking(m_chatmessage[CHAR_NAME],
m_chatmessage[EMOTE]);
ui_vp_player_char->set_play_once(false);
filename = "(b)" + m_chatmessage[EMOTE];
ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0,
false);
anim_state = 2;
}
else if (!color_is_talking && anim_state < 3 &&
anim_state != 3) // Set it to idle as we're not on that already
{
ui_vp_player_char->stop();
ui_vp_player_char->play_idle(m_chatmessage[CHAR_NAME],
m_chatmessage[EMOTE]);
ui_vp_player_char->set_play_once(false);
filename = "(a)" + m_chatmessage[EMOTE];
ui_vp_player_char->load_image(filename, m_chatmessage[CHAR_NAME], 0,
false);
anim_state = 3;
}
// Continue ticking
@ -3547,19 +3542,15 @@ void Courtroom::set_scene(QString f_desk_mod, QString f_side)
f_background = f_side;
f_desk_image = f_side + "_overlay";
}
ui_vp_background->set_image(f_background);
ui_vp_desk->set_image(f_desk_image);
ui_vp_legacy_desk->set_legacy_desk(f_desk_image);
ui_vp_background->load_image(f_background);
ui_vp_desk->load_image(f_desk_image);
if (f_desk_mod == "0" ||
(f_desk_mod != "1" &&
(f_side == "jud" || f_side == "hld" || f_side == "hlp"))) {
ui_vp_desk->hide();
ui_vp_legacy_desk->hide();
}
else {
ui_vp_legacy_desk->hide();
ui_vp_desk->show();
}
}
@ -3707,31 +3698,40 @@ void Courtroom::handle_song(QStringList *p_contents)
void Courtroom::handle_wtce(QString p_wtce, int variant)
{
QString sfx_file = "courtroom_sounds.ini";
QString sfx_name;
QString filename;
ui_vp_wtce->set_static_duration(wtce_static_time);
ui_vp_wtce->set_max_duration(wtce_max_time);
// witness testimony
if (p_wtce == "testimony1") {
sfx_player->play(ao_app->get_sfx("witness_testimony"));
ui_vp_wtce->play("witnesstestimony", "", "", 1500);
ui_vp_testimony->play("testimony");
sfx_name = "witness_testimony";
filename = "witnesstestimony";
ui_vp_testimony->load_image("testimony", "");
}
// cross examination
else if (p_wtce == "testimony2") {
sfx_player->play(ao_app->get_sfx("cross_examination"));
ui_vp_wtce->play("crossexamination", "", "", 1500);
sfx_name = "cross_examination";
filename = "crossexamination";
ui_vp_testimony->stop();
}
else if (p_wtce == "judgeruling") {
ui_vp_wtce->set_static_duration(verdict_static_time);
ui_vp_wtce->set_max_duration(verdict_max_time);
if (variant == 0) {
sfx_player->play(ao_app->get_sfx("not_guilty"));
ui_vp_wtce->play("notguilty", "", "", 3000);
sfx_name = "not_guilty";
filename = "notguilty";
ui_vp_testimony->stop();
}
else if (variant == 1) {
sfx_player->play(ao_app->get_sfx("guilty"));
ui_vp_wtce->play("guilty", "", "", 3000);
sfx_name = "guilty";
filename = "guilty";
ui_vp_testimony->stop();
}
}
QString bg_misc = ao_app->read_design_ini("misc", ao_app->get_background_path("design.ini"));
sfx_player->play(ao_app->get_sfx(sfx_name, bg_misc));
ui_vp_wtce->load_image(filename, "", bg_misc);
ui_vp_wtce->set_play_once(true);
}
void Courtroom::set_hp_bar(int p_bar, int p_state)
@ -4954,18 +4954,18 @@ void Courtroom::set_text_color_dropdown()
QColor color =
ao_app->get_chat_color("c" + QString::number(c), current_char);
color_rgb_list.append(color);
color_markdown_start_list.append(ao_app->get_chat_markdown(
color_markdown_start_list.append(ao_app->get_chat_markup(
"c" + QString::number(c) + "_start", current_char));
color_markdown_end_list.append(ao_app->get_chat_markdown(
color_markdown_end_list.append(ao_app->get_chat_markup(
"c" + QString::number(c) + "_end", current_char));
color_markdown_remove_list.append(
ao_app->get_chat_markdown("c" + QString::number(c) + "_remove",
current_char) == "1");
ao_app->get_chat_markup("c" + QString::number(c) + "_remove",
current_char) == "1");
color_markdown_talking_list.append(
ao_app->get_chat_markdown("c" + QString::number(c) + "_talking",
current_char) != "0");
ao_app->get_chat_markup("c" + QString::number(c) + "_talking",
current_char) != "0");
QString color_name = ao_app->get_chat_markdown(
QString color_name = ao_app->get_chat_markup(
"c" + QString::number(c) + "_name", current_char);
if (color_name.isEmpty()) // Not defined
{

View File

@ -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;

View File

@ -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;
}