atrooney-online-2/src/aocharmovie.cpp
Crystalwarrior 9451822e09 Fix move func making characters slowly move to the left every frame on animated chars which are sized differently from viewport
Add a new get_qfont function
Fix message box font being different from every other font due to incorrect font loading scheme
Fix ui_evidence_save and ui_evidence_load tooltips being wrong
2019-10-03 23:17:21 +03:00

312 lines
8.4 KiB
C++

#include "aocharmovie.h"
#include "misc_functions.h"
#include "file_functions.h"
#include "aoapplication.h"
AOCharMovie::AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent)
{
ao_app = p_ao_app;
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_character_path(p_char, p_emote + ".png"), //Non-animated path if emote_prefix fails
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();
m_reader->setFileName(emote_path);
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::SmoothTransformation;
f_pixmap = f_pixmap.scaledToHeight(f_h, transform_mode);
this->resize(f_pixmap.size());
QLabel::move(x + (f_w - f_pixmap.width())/2, y + (f_h - f_pixmap.height())); //Always center horizontally, always put at the bottom vertically
return f_pixmap;
}
void AOCharMovie::set_frame(QPixmap f_pixmap)
{
this->setPixmap(f_pixmap);
QLabel::move(x + (this->width() - this->pixmap()->width())/2, y);
}
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();
}