Finally implement frame-specific effects such as screenshake, realization flash, sound effects, etc.
Fix screenshake animation modifying the default positions of shook elements Fix aomovie sometimes not playing the last frame and causing lagspikes due to the delay() method
This commit is contained in:
parent
4db1140074
commit
a2f9df4042
@ -271,6 +271,18 @@ public:
|
||||
//Returns the sfx of p_char's p_emote
|
||||
QString get_sfx_name(QString p_char, int p_emote);
|
||||
|
||||
//Returns if the sfx is defined as looping in char.ini
|
||||
QString get_sfx_looping(QString p_char, QString p_sfx);
|
||||
|
||||
//Returns if an emote has a frame specific SFX for it
|
||||
QString get_sfx_frame(QString p_char, QString p_emote, int n_frame);
|
||||
|
||||
//Returns if an emote has a frame specific SFX for it
|
||||
QString get_flash_frame(QString p_char, QString p_emote, int n_frame);
|
||||
|
||||
//Returns if an emote has a frame specific SFX for it
|
||||
QString get_screenshake_frame(QString p_char, QString p_emote, int n_frame);
|
||||
|
||||
//Not in use
|
||||
int get_sfx_delay(QString p_char, int p_emote);
|
||||
|
||||
|
@ -32,6 +32,9 @@ public:
|
||||
//Play an (a)normal.gif - style animation (not talking)
|
||||
void play_idle(QString p_char, QString p_emote);
|
||||
|
||||
//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);
|
||||
|
||||
@ -61,6 +64,12 @@ private:
|
||||
|
||||
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;
|
||||
@ -83,6 +92,9 @@ private:
|
||||
|
||||
signals:
|
||||
void done();
|
||||
void shake();
|
||||
void flash();
|
||||
void play_sfx(QString sfx);
|
||||
|
||||
private slots:
|
||||
void preanim_done();
|
||||
|
@ -15,7 +15,6 @@ public:
|
||||
AOMovie(QWidget *p_parent, AOApplication *p_ao_app);
|
||||
|
||||
void set_play_once(bool p_play_once);
|
||||
void start_timer(int delay);
|
||||
void play(QString p_gif, QString p_char = "", QString p_custom_theme = "", int default_duration = 0);
|
||||
void combo_resize(int w, int h);
|
||||
void stop();
|
||||
|
@ -219,8 +219,6 @@ public:
|
||||
void announce_case(QString title, bool def, bool pro, bool jud, bool jur, bool steno);
|
||||
|
||||
void check_connection_received();
|
||||
void do_screenshake();
|
||||
void doRealization();
|
||||
|
||||
~Courtroom();
|
||||
|
||||
@ -239,7 +237,7 @@ private:
|
||||
bool first_message_sent = false;
|
||||
int maximumMessages = 0;
|
||||
|
||||
QParallelAnimationGroup *screenshake_animation_group;
|
||||
QParallelAnimationGroup *screenshake_animation_group = new QParallelAnimationGroup;
|
||||
|
||||
// This is for inline message-colouring.
|
||||
|
||||
@ -557,6 +555,9 @@ private:
|
||||
public slots:
|
||||
void objection_done();
|
||||
void preanim_done();
|
||||
void do_screenshake();
|
||||
void do_flash();
|
||||
void play_char_sfx(QString sfx_name);
|
||||
|
||||
void mod_called(QString p_ip);
|
||||
|
||||
|
@ -47,6 +47,7 @@ void AOCharMovie::load_image(QString p_char, QString p_emote, QString emote_pref
|
||||
preanim_timer->stop();
|
||||
movie_frames.clear();
|
||||
movie_delays.clear();
|
||||
movie_effects.clear();
|
||||
|
||||
m_reader->setFileName(emote_path);
|
||||
QPixmap f_pixmap = this->get_pixmap(m_reader->read());
|
||||
@ -62,6 +63,32 @@ void AOCharMovie::load_image(QString p_char, QString p_emote, QString emote_pref
|
||||
movie_frames.append(f_pixmap);
|
||||
movie_delays.append(f_delay);
|
||||
}
|
||||
|
||||
movie_effects.resize(max_frames);
|
||||
for (int e_frame = 0; e_frame < max_frames; ++e_frame)
|
||||
{
|
||||
qDebug() << p_char << p_emote << e_frame;
|
||||
QString effect = ao_app->get_screenshake_frame(p_char, emote_prefix + p_emote, e_frame);
|
||||
if (effect != "")
|
||||
{
|
||||
movie_effects[e_frame].append("shake");
|
||||
qDebug() << e_frame << "shake";
|
||||
}
|
||||
|
||||
effect = ao_app->get_flash_frame(p_char, emote_prefix + p_emote, e_frame);
|
||||
if (effect != "")
|
||||
{
|
||||
movie_effects[e_frame].append("flash");
|
||||
qDebug() << e_frame << "flash";
|
||||
}
|
||||
|
||||
effect = ao_app->get_sfx_frame(p_char, emote_prefix + p_emote, e_frame);
|
||||
if (effect != "")
|
||||
{
|
||||
movie_effects[e_frame].append("sfx^"+effect);
|
||||
qDebug() << e_frame << effect;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG_CHARMOVIE
|
||||
qDebug() << max_frames << "Setting image to " << emote_path << "Time taken to process image:" << actual_time.elapsed();
|
||||
|
||||
@ -71,6 +98,7 @@ void AOCharMovie::load_image(QString p_char, QString p_emote, QString emote_pref
|
||||
|
||||
void AOCharMovie::play()
|
||||
{
|
||||
play_frame_effect(frame);
|
||||
if (max_frames <= 1)
|
||||
return;
|
||||
ticker->start(this->get_frame_delay(movie_delays[frame]));
|
||||
@ -101,6 +129,34 @@ void AOCharMovie::play_idle(QString p_char, QString p_emote)
|
||||
play();
|
||||
}
|
||||
|
||||
void AOCharMovie::play_frame_effect(int frame)
|
||||
{
|
||||
if(frame < max_frames)
|
||||
{
|
||||
foreach (QString effect, movie_effects[frame])
|
||||
{
|
||||
if(effect == "shake")
|
||||
{
|
||||
shake();
|
||||
qDebug() << "Attempting to play shake on frame" << frame;
|
||||
}
|
||||
|
||||
if(effect == "flash")
|
||||
{
|
||||
flash();
|
||||
qDebug() << "Attempting to play flash on frame" << frame;
|
||||
}
|
||||
|
||||
if(effect.startsWith("sfx^"))
|
||||
{
|
||||
QString sfx = effect.section("^", 1);
|
||||
play_sfx(sfx);
|
||||
qDebug() << "Attempting to play sfx" << sfx << "on frame" << frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -182,6 +238,7 @@ void AOCharMovie::movie_ticker()
|
||||
#endif
|
||||
|
||||
this->set_frame(movie_frames[frame]);
|
||||
play_frame_effect(frame);
|
||||
ticker->setInterval(this->get_frame_delay(movie_delays[frame]));
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@ AOMovie::AOMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent)
|
||||
this->setMovie(m_movie);
|
||||
|
||||
timer = new QTimer(this);
|
||||
timer->setTimerType(Qt::PreciseTimer);
|
||||
timer->setSingleShot(true);
|
||||
|
||||
connect(m_movie, SIGNAL(frameChanged(int)), this, SLOT(frame_change(int)));
|
||||
@ -24,11 +25,6 @@ void AOMovie::set_play_once(bool p_play_once)
|
||||
play_once = p_play_once;
|
||||
}
|
||||
|
||||
void AOMovie::start_timer(int delay)
|
||||
{
|
||||
timer->start(delay);
|
||||
}
|
||||
|
||||
void AOMovie::play(QString p_image, QString p_char, QString p_custom_theme, int duration)
|
||||
{
|
||||
m_movie->stop();
|
||||
@ -65,7 +61,7 @@ void AOMovie::play(QString p_image, QString p_char, QString p_custom_theme, int
|
||||
this->show();
|
||||
m_movie->start();
|
||||
if (m_movie->frameCount() == 0 && duration > 0)
|
||||
this->start_timer(duration);
|
||||
timer->start(duration);
|
||||
}
|
||||
|
||||
void AOMovie::stop()
|
||||
@ -81,17 +77,13 @@ void AOMovie::frame_change(int n_frame)
|
||||
if (m_movie->frameCount() == 0 || n_frame < (m_movie->frameCount() - 1) || !play_once)
|
||||
return;
|
||||
//we need this or else the last frame wont show
|
||||
delay(m_movie->nextFrameDelay());
|
||||
|
||||
this->stop();
|
||||
|
||||
//signal connected to courtroom object, let it figure out what to do
|
||||
done();
|
||||
timer->start(m_movie->nextFrameDelay());
|
||||
}
|
||||
|
||||
void AOMovie::timer_done()
|
||||
{
|
||||
this->stop();
|
||||
//signal connected to courtroom object, let it figure out what to do
|
||||
done();
|
||||
}
|
||||
|
||||
|
@ -265,6 +265,9 @@ Courtroom::Courtroom(AOApplication *p_ao_app) : QMainWindow()
|
||||
|
||||
connect(ui_vp_objection, SIGNAL(done()), this, SLOT(objection_done()));
|
||||
connect(ui_vp_player_char, SIGNAL(done()), this, SLOT(preanim_done()));
|
||||
connect(ui_vp_player_char, SIGNAL(shake()), this, SLOT(do_screenshake()));
|
||||
connect(ui_vp_player_char, SIGNAL(flash()), this, SLOT(do_flash()));
|
||||
connect(ui_vp_player_char, SIGNAL(play_sfx(QString)), this, SLOT(play_char_sfx(QString)));
|
||||
|
||||
connect(text_delay_timer, SIGNAL(timeout()), this, SLOT(start_chat_ticking()));
|
||||
connect(sfx_delay_timer, SIGNAL(timeout()), this, SLOT(play_sfx()));
|
||||
@ -1655,47 +1658,58 @@ void Courtroom::handle_chatmessage_2()
|
||||
|
||||
void Courtroom::do_screenshake()
|
||||
{
|
||||
//Code below causes segfault, do not uncomment unless you know what you're doing.
|
||||
// if (screenshake_animation_group != nullptr && screenshake_animation_group->state() == QAbstractAnimation::Running)
|
||||
// screenshake_animation_group->setCurrentTime(screenshake_animation_group->duration()); //Force it to finish and delete itself
|
||||
//This way, the animation is reset in such a way that last played screenshake would return to its "final frame" properly.
|
||||
//This properly resets all UI elements without having to bother keeping track of "origin" positions.
|
||||
//Works great wit the chat text being detached from the chat box!
|
||||
screenshake_animation_group->setCurrentTime(screenshake_animation_group->duration());
|
||||
screenshake_animation_group->clear();
|
||||
|
||||
screenshake_animation_group = new QParallelAnimationGroup;
|
||||
QList<QWidget *> affected_list = {
|
||||
ui_vp_background,
|
||||
ui_vp_player_char,
|
||||
ui_vp_sideplayer_char,
|
||||
ui_vp_chatbox
|
||||
};
|
||||
|
||||
QList<QWidget *> affected_list = {
|
||||
ui_vp_background,
|
||||
ui_vp_player_char,
|
||||
ui_vp_sideplayer_char,
|
||||
ui_vp_chatbox
|
||||
};
|
||||
int i = 0;
|
||||
//I would prefer if this was its own "shake" function to be honest.
|
||||
foreach (QWidget* ui_element, affected_list)
|
||||
{
|
||||
qDebug() << ++i;
|
||||
QPropertyAnimation *screenshake_animation = new QPropertyAnimation(ui_element, "pos", this);
|
||||
QPoint pos_default = QPoint(ui_element->x(), ui_element->y());
|
||||
|
||||
int i = 0;
|
||||
//I would prefer if this was its own "shake" function to be honest.
|
||||
foreach (QWidget* ui_element, affected_list)
|
||||
int duration = 300; //How long does the screenshake last
|
||||
int frequency = 20; //How often in ms is there a "jolt" frame
|
||||
int maxframes = duration/frequency;
|
||||
int max_x = 7; //Max deviation from origin on x axis
|
||||
int max_y = 7; //Max deviation from origin on y axis
|
||||
screenshake_animation->setDuration(duration);
|
||||
for (int frame=0; frame < maxframes; frame++)
|
||||
{
|
||||
qDebug() << ++i;
|
||||
QPropertyAnimation *screenshake_animation = new QPropertyAnimation(ui_element, "pos", this);
|
||||
QPoint pos_default = QPoint(ui_element->x(), ui_element->y());
|
||||
|
||||
int duration = 300; //How long does the screenshake last
|
||||
int frequency = 20; //How often in ms is there a "jolt" frame
|
||||
int maxframes = duration/frequency;
|
||||
int max_x = 7; //Max deviation from origin on x axis
|
||||
int max_y = 7; //Max deviation from origin on y axis
|
||||
screenshake_animation->setDuration(duration);
|
||||
for (int frame=0; frame < maxframes; frame++)
|
||||
{
|
||||
double fraction = double(frame*frequency)/duration;
|
||||
quint32 rng = QRandomGenerator::global()->generate();
|
||||
int rand_x = int(rng) % max_x;
|
||||
int rand_y = int(rng+100) % max_y;
|
||||
screenshake_animation->setKeyValueAt(fraction, QPoint(pos_default.x() + rand_x, pos_default.y() + rand_y));
|
||||
}
|
||||
screenshake_animation->setEndValue(pos_default);
|
||||
screenshake_animation->setEasingCurve(QEasingCurve::Linear);
|
||||
screenshake_animation_group->addAnimation(screenshake_animation);
|
||||
double fraction = double(frame*frequency)/duration;
|
||||
quint32 rng = QRandomGenerator::global()->generate();
|
||||
int rand_x = int(rng) % max_x;
|
||||
int rand_y = int(rng+100) % max_y;
|
||||
screenshake_animation->setKeyValueAt(fraction, QPoint(pos_default.x() + rand_x, pos_default.y() + rand_y));
|
||||
}
|
||||
screenshake_animation->setEndValue(pos_default);
|
||||
screenshake_animation->setEasingCurve(QEasingCurve::Linear);
|
||||
screenshake_animation_group->addAnimation(screenshake_animation);
|
||||
}
|
||||
|
||||
screenshake_animation_group->start(QAbstractAnimation::DeletionPolicy::DeleteWhenStopped);
|
||||
screenshake_animation_group->start();
|
||||
}
|
||||
|
||||
void Courtroom::do_flash()
|
||||
{
|
||||
ui_vp_realization->play("realizationflash", "", "", 60);
|
||||
}
|
||||
|
||||
void Courtroom::play_char_sfx(QString sfx_name)
|
||||
{
|
||||
sfx_player->play(ao_app->get_sfx_suffix(sfx_name));
|
||||
sfx_player->set_looping(ao_app->get_sfx_looping(current_char, sfx_name)!="0");
|
||||
}
|
||||
|
||||
void Courtroom::handle_chatmessage_3()
|
||||
@ -1829,6 +1843,16 @@ QString Courtroom::filter_ic_text(QString p_text)
|
||||
p_text.remove(trick_check_pos,1);
|
||||
}
|
||||
|
||||
else if (f_character == "$" and !ic_next_is_not_special)
|
||||
{
|
||||
p_text.remove(trick_check_pos,1);
|
||||
}
|
||||
|
||||
else if (f_character == "@" and !ic_next_is_not_special)
|
||||
{
|
||||
p_text.remove(trick_check_pos,1);
|
||||
}
|
||||
|
||||
// Orange inline colourisation.
|
||||
else if (f_character == "|" and !ic_next_is_not_special)
|
||||
{
|
||||
@ -2089,7 +2113,7 @@ void Courtroom::start_chat_ticking()
|
||||
|
||||
if (m_chatmessage[REALIZATION] == "1")
|
||||
{
|
||||
ui_vp_realization->play("realizationflash", "", "", 60);
|
||||
this->do_flash();
|
||||
sfx_player->play(ao_app->get_custom_realization(m_chatmessage[CHAR_NAME]));
|
||||
}
|
||||
|
||||
@ -2208,6 +2232,18 @@ void Courtroom::chat_tick()
|
||||
formatting_char = true;
|
||||
}
|
||||
|
||||
else if (f_character == "@" and !next_character_is_not_special)
|
||||
{
|
||||
this->do_screenshake();
|
||||
formatting_char = true;
|
||||
}
|
||||
|
||||
else if (f_character == "$" and !next_character_is_not_special)
|
||||
{
|
||||
this->do_flash();
|
||||
formatting_char = true;
|
||||
}
|
||||
|
||||
// Orange inline colourisation.
|
||||
else if (f_character == "|" and !next_character_is_not_special)
|
||||
{
|
||||
|
@ -578,6 +578,42 @@ int AOApplication::get_sfx_delay(QString p_char, int p_emote)
|
||||
else return f_result.toInt();
|
||||
}
|
||||
|
||||
QString AOApplication::get_sfx_looping(QString p_char, QString p_sfx)
|
||||
{
|
||||
QString f_result = read_char_ini(p_char, p_sfx, "SoundL");
|
||||
|
||||
if (f_result == "")
|
||||
return "0";
|
||||
else return f_result;
|
||||
}
|
||||
|
||||
QString AOApplication::get_sfx_frame(QString p_char, QString p_emote, int n_frame)
|
||||
{
|
||||
QString f_result = read_char_ini(p_char, QString::number(n_frame), p_emote.append("_FrameSFX"));
|
||||
|
||||
if (f_result == "")
|
||||
return "";
|
||||
else return f_result;
|
||||
}
|
||||
|
||||
QString AOApplication::get_screenshake_frame(QString p_char, QString p_emote, int n_frame)
|
||||
{
|
||||
QString f_result = read_char_ini(p_char, QString::number(n_frame), p_emote.append("_FrameScreenshake"));
|
||||
|
||||
if (f_result == "")
|
||||
return "";
|
||||
else return f_result;
|
||||
}
|
||||
|
||||
QString AOApplication::get_flash_frame(QString p_char, QString p_emote, int n_frame)
|
||||
{
|
||||
QString f_result = read_char_ini(p_char, QString::number(n_frame), p_emote.append("_FrameRealization"));
|
||||
|
||||
if (f_result == "")
|
||||
return "";
|
||||
else return f_result;
|
||||
}
|
||||
|
||||
int AOApplication::get_text_delay(QString p_char, QString p_emote)
|
||||
{
|
||||
QString f_result = read_char_ini(p_char, p_emote, "TextDelay");
|
||||
|
Loading…
Reference in New Issue
Block a user