diff options
Diffstat (limited to 'src/aocharmovie.cpp')
| -rw-r--r-- | src/aocharmovie.cpp | 431 |
1 files changed, 247 insertions, 184 deletions
diff --git a/src/aocharmovie.cpp b/src/aocharmovie.cpp index 4252923a..252aab5d 100644 --- a/src/aocharmovie.cpp +++ b/src/aocharmovie.cpp @@ -8,248 +8,276 @@ AOCharMovie::AOCharMovie(QWidget *p_parent, AOApplication *p_ao_app) : QLabel(p_parent) { ao_app = p_ao_app; - m_movie = new QMovie(this); preanim_timer = new QTimer(this); - ticker = new QTimer(this); preanim_timer->setSingleShot(true); - ticker->setSingleShot(true); + + ticker = new QTimer(this); + ticker->setTimerType(Qt::PreciseTimer); + ticker->setSingleShot(false); connect(ticker, SIGNAL(timeout()), this, SLOT(movie_ticker())); - this->setUpdatesEnabled(true); + + // connect(m_movie, SIGNAL(frameChanged(int)), this, + // SLOT(frame_change(int))); + connect(preanim_timer, SIGNAL(timeout()), this, SLOT(preanim_done())); } -void AOCharMovie::play(QString p_char, QString p_emote, QString emote_prefix) +void AOCharMovie::load_image(QString p_char, QString p_emote, + QString emote_prefix) { - apng = false; - - QString original_path = - ao_app->get_character_path(p_char, emote_prefix + p_emote + ".gif"); - QString alt_path = - ao_app->get_character_path(p_char, emote_prefix + p_emote + ".png"); - QString apng_path = - ao_app->get_character_path(p_char, emote_prefix + p_emote + ".apng"); - QString alt_path_still = ao_app->get_character_path(p_char, p_emote + ".png"); - - QString placeholder_path = ao_app->get_theme_path("placeholder.gif"); - QString placeholder_default_path = - ao_app->get_default_theme_path("placeholder.gif"); - QString gif_path; - current_emote = emote_prefix + p_emote; - current_char = p_char; - if (file_exists(apng_path)) { - gif_path = apng_path; - apng = true; +#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_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; + } } - else if (file_exists(original_path)) - gif_path = original_path; - else if (file_exists(alt_path)) - gif_path = alt_path; - else if (file_exists(alt_path_still)) - gif_path = alt_path_still; - else if (file_exists(placeholder_path)) - gif_path = placeholder_path; - else - gif_path = placeholder_default_path; - last_path = gif_path; - delete m_movie; - m_movie = new QMovie(this); - m_movie->stop(); + this->clear(); - m_movie->setFileName(gif_path); - m_movie->jumpToFrame(0); - this->LoadImageWithStupidMethodForFlipSupport(m_movie->currentImage()); + 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); + 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(); - this->play_frame_sfx(); - // if the frame count is 0 (i.e. it's a static PNG) don't try to play the next - // frame, ya goofus - if (m_movie->frameCount() != 0) { - ticker->start(m_movie->nextFrameDelay()); + if (max_frames > 1) { + movie_frames.append(f_pixmap); + movie_delays.append(f_delay); } -} -void AOCharMovie::play_frame_sfx() -{ - int current_frame = m_movie->currentFrameNumber(); - QString sfx_to_play = - ao_app->get_frame_sfx_name(current_char, current_emote, current_frame); - QString screenshake_to_play = - ao_app->get_screenshake_frame(current_char, current_emote, current_frame); - QString realization_to_play = - ao_app->get_realization_frame(current_char, current_emote, current_frame); - if (sfx_to_play != "" && !use_networked_framehell) { - frame_specific_sfx_player->play(ao_app->get_sfx_suffix(sfx_to_play)); - } - else if (use_networked_framehell) { - this->sfx_two_network_boogaloo(); - } - if (screenshake_to_play != "" && !use_networked_framehell) { - mycourtroom->doScreenShake(); - } - else if (use_networked_framehell) { - this->screenshake_two_network_boogaloo(); - } - if (realization_to_play != "" && !use_networked_framehell) { - mycourtroom->doRealization(); - } - else if (use_networked_framehell) { - this->realization_two_network_boogaloo(); - } -} + m_char = p_char; + m_emote = emote_prefix + p_emote; -void AOCharMovie::realization_two_network_boogaloo() -{ - int current_frame = m_movie->currentFrameNumber(); - QStringList realizationList = this->frame_realization_hellstring.split("^"); - for (int i = 0; i < realizationList.length(); i++) { - QString screenshakeList = realizationList.at(i); - QStringList extra_garbage = screenshakeList.split("|"); - if (extra_garbage.at(0) != current_emote) { - continue; - } - for (int ii = 1; ii < extra_garbage.length(); ii++) { - QString levels_of_garbage = extra_garbage.at(ii); - QStringList that_shouldnt_be_possible = levels_of_garbage.split("="); - if (that_shouldnt_be_possible.at(0).toInt() == current_frame && - that_shouldnt_be_possible.at(1) != "") { - mycourtroom->doRealization(); - } - } - } + 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::screenshake_two_network_boogaloo() +void AOCharMovie::load_effects() { - int current_frame = m_movie->currentFrameNumber(); - QStringList realizationList = this->frame_screenshake_hellstring.split("^"); - for (int i = 0; i < realizationList.length(); i++) { - QString screenshakeList = realizationList.at(i); - QStringList extra_garbage = screenshakeList.split("|"); - if (extra_garbage.at(0) != current_emote) { - continue; - } - for (int ii = 1; ii < extra_garbage.length(); ii++) { - QString levels_of_garbage = extra_garbage.at(ii); - QStringList that_shouldnt_be_possible = levels_of_garbage.split("="); - if (that_shouldnt_be_possible.at(0).toInt() == current_frame && - that_shouldnt_be_possible.at(1) != "") { - mycourtroom->doScreenShake(); - } + 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"); } - } -} -void AOCharMovie::sfx_two_network_boogaloo() -{ - int current_frame = m_movie->currentFrameNumber(); - QStringList realizationList = this->frame_sfx_hellstring.split("^"); - for (int i = 0; i < realizationList.length(); i++) { - QString screenshakeList = realizationList.at(i); - QStringList extra_garbage = screenshakeList.split("|"); - if (extra_garbage.at(0) != current_emote) { - continue; + effect = ao_app->get_flash_frame(m_char, m_emote, e_frame); + if (effect != "") { + movie_effects[e_frame].append("flash"); } - for (int ii = 1; ii < extra_garbage.length(); ii++) { - QString levels_of_garbage = extra_garbage.at(ii); - QStringList that_shouldnt_be_possible = levels_of_garbage.split("="); - if (that_shouldnt_be_possible.at(0).toInt() == current_frame && - that_shouldnt_be_possible.at(1) != "") { - frame_specific_sfx_player->play( - ao_app->get_sfx_suffix(that_shouldnt_be_possible.at(1))); - } + + effect = ao_app->get_sfx_frame(m_char, m_emote, e_frame); + if (effect != "") { + movie_effects[e_frame].append("sfx^" + effect); } } } -void AOCharMovie::movie_ticker() +void AOCharMovie::load_network_effects() { - if (m_movie->currentFrameNumber() == m_movie->frameCount() - 1) { - delete m_movie; - m_movie = new QMovie(this); - m_movie->stop(); - this->clear(); - m_movie->setFileName(last_path); - m_movie->jumpToFrame(0); - if (play_once) { - timer_done(); - } - } - else { - m_movie->jumpToNextFrame(); - } - this->LoadImageWithStupidMethodForFlipSupport(m_movie->currentImage()); - // imagine if QT had sane stuff like "mirror on QMovie" or "resize the image - // on QT" or "interface with the current QMovie image" or anything else + movie_effects.clear(); + movie_effects.resize(max_frames); + // Order is important!!! + QStringList effects_list = {"shake", "flash", "sfx^"}; - this->play_frame_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()); - if (m_movie->frameCount() == 0) { - return; - } - else if (!apng) { - ticker->start(m_movie->nextFrameDelay()); + 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::LoadImageWithStupidMethodForFlipSupport(QImage image) +void AOCharMovie::play() { - 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.size().width() > f_pixmap.size().height()) - aspect_ratio = Qt::KeepAspectRatioByExpanding; - - if (f_pixmap.size().width() > this->size().width() || - f_pixmap.size().height() > this->size().height()) - this->setPixmap(f_pixmap.scaled(this->width(), this->height(), aspect_ratio, - Qt::SmoothTransformation)); + play_frame_effect(frame); + if (max_frames <= 1) { + if (play_once) + ticker->start(60); + } else - this->setPixmap(f_pixmap.scaled(this->width(), this->height(), aspect_ratio, - Qt::FastTransformation)); - - QLabel::move(x + (this->width() - this->pixmap()->width()) / 2, y); + ticker->start(this->get_frame_delay(movie_delays[frame])); } void AOCharMovie::play_pre(QString p_char, QString p_emote, int duration) { - QString gif_path = ao_app->get_character_path(p_char, p_emote); - - m_movie->stop(); - m_movie->setFileName(gif_path); - m_movie->jumpToFrame(0); + 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; - play(p_char, p_emote, ""); + 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; - play(p_char, p_emote, "(b)"); + load_image(p_char, p_emote, "(b)"); + play(); } void AOCharMovie::play_idle(QString p_char, QString p_emote) { play_once = false; - play(p_char, p_emote, "(a)"); + 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 - m_movie->stop(); - frame_specific_sfx_player->stop(); + 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); - m_movie->setScaledSize(this->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; @@ -257,4 +285,39 @@ void AOCharMovie::move(int ax, int ay) QLabel::move(x, y); } -void AOCharMovie::timer_done() { done(); } +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(); +} |
