Skip to content

Commit

Permalink
Animate Window_Message during transitions
Browse files Browse the repository at this point in the history
Emulates the RPG_RT behavior for EasyRPG#1706 Tests 17 & 18
  • Loading branch information
mateofio committed Mar 3, 2020
1 parent c856538 commit e399c33
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 29 deletions.
6 changes: 6 additions & 0 deletions src/game_message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,12 @@ int Game_Message::WordWrap(const std::string& line, const int limit, const std::
return line_count;
}

void Game_Message::AdvanceAnimationFrames(int frames) {
if (window) {
window->AdvanceAnimationFrames(frames);
}
}

void Game_Message::Update() {
if (window) {
window->Update();
Expand Down
3 changes: 3 additions & 0 deletions src/game_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ namespace Game_Message {

void Update();

/** Calls AdvanceAnimationFrames() on the Window_Message */
void AdvanceAnimationFrames(int frames);

/** Reset the face graphic. */
void ClearFace();

Expand Down
57 changes: 37 additions & 20 deletions src/scene_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ void Scene_Map::Start2(MapUpdateAsyncContext actx) {
// in order to make async logic work properly.
auto& transition = Transition::instance();
if (transition.IsErased()) {
transition.InitShow(Transition::TransitionFadeIn, this);
InitTransitionShow(Transition::TransitionFadeIn);
}

// Call any requested scenes when transition is done.
Expand Down Expand Up @@ -160,7 +160,7 @@ void Scene_Map::TransitionIn(SceneType prev_scene) {
}

if (prev_scene == Scene::Battle) {
transition.InitShow(Game_System::GetTransition(Game_System::Transition_EndBattleShow), this);
InitTransitionShow(Game_System::GetTransition(Game_System::Transition_EndBattleShow));
return;
}

Expand All @@ -182,7 +182,7 @@ void Scene_Map::TransitionOut(SceneType next_scene) {
}

if (next_scene == Scene::Debug) {
transition.InitErase(Transition::TransitionCutOut, this);
InitTransitionErase(Transition::TransitionCutOut);
return;
}

Expand All @@ -191,22 +191,23 @@ void Scene_Map::TransitionOut(SceneType next_scene) {
auto tt = Game_System::GetTransition(Game_System::Transition_BeginBattleErase);
if (tt == Transition::TransitionNone) {
// If transition type is none, RPG_RT flashes and then waits 40 frames before starting the battle.
transition.InitErase(Transition::TransitionCutOut, this, 40);
InitTransitionErase(Transition::TransitionCutOut, 40);
} else {
transition.InitErase(tt, this);
InitTransitionErase(tt);
}
transition.PrependFlashes(31, 31, 31, 31, 10, 2);
} else {
// If screen is already erased, RPG_RT does nothing for 40 frames.
transition.InitErase(Transition::TransitionNone, this, 40);
InitTransitionErase(Transition::TransitionNone, 40);
}
return;
}
if (next_scene == Scene::Gameover) {
transition.InitErase(Transition::TransitionFadeOut, this);
InitTransitionErase(Transition::TransitionFadeOut);
return;
}
Scene::TransitionOut(next_scene);

InitTransitionErase(Transition::TransitionFadeOut, 6);
}

void Scene_Map::DrawBackground(Bitmap& dst) {
Expand Down Expand Up @@ -309,7 +310,7 @@ void Scene_Map::StartPendingTeleport(TeleportParams tp) {
request->Start();

if (!transition.IsErased() && tp.erase_screen) {
transition.InitErase(Game_System::GetTransition(Game_System::Transition_TeleportErase), this);
InitTransitionErase(Game_System::GetTransition(Game_System::Transition_TeleportErase));
}

AsyncNext([=]() { FinishPendingTeleport(tp); });
Expand Down Expand Up @@ -347,9 +348,9 @@ void Scene_Map::FinishPendingTeleport2(MapUpdateAsyncContext actx, TeleportParam

// This logic was tested against RPG_RT and works this way ...
if (tp.use_default_transition_in && transition.IsErased()) {
transition.InitShow(Transition::TransitionFadeIn, this);
InitTransitionShow(Transition::TransitionFadeIn);
} else if (!tp.use_default_transition_in && !screen_erased_by_event) {
transition.InitShow(Game_System::GetTransition(Game_System::Transition_TeleportShow), this);
InitTransitionShow(Game_System::GetTransition(Game_System::Transition_TeleportShow));
}

// Call any requested scenes when transition is done.
Expand Down Expand Up @@ -411,16 +412,14 @@ void Scene_Map::OnAsyncSuspend(F&& f, AsyncOp aop, bool is_preupdate) {
return;
}

auto& transition = Transition::instance();

if (aop.GetType() == AsyncOp::eEraseScreen) {
auto tt = static_cast<Transition::Type>(aop.GetTransitionType());
if (tt == Transition::TransitionNone) {
// Emulates an RPG_RT bug where instantaneous transitions cause a
// 30 frame pause and then make the screen black.
transition.InitErase(Transition::TransitionCutOut, this, 30);
InitTransitionErase(Transition::TransitionCutOut, 30);
} else {
transition.InitErase(tt, this);
InitTransitionErase(tt);
}

if (!is_preupdate) {
Expand All @@ -431,7 +430,7 @@ void Scene_Map::OnAsyncSuspend(F&& f, AsyncOp aop, bool is_preupdate) {

if (aop.GetType() == AsyncOp::eShowScreen) {
auto tt = static_cast<Transition::Type>(aop.GetTransitionType());
transition.InitShow(tt, this);
InitTransitionShow(tt);
screen_erased_by_event = false;
}

Expand All @@ -442,7 +441,7 @@ void Scene_Map::OnAsyncSuspend(F&& f, AsyncOp aop, bool is_preupdate) {

Game_System::BgmFade(800);

transition.InitErase(Transition::TransitionFadeOut, Scene::instance.get());
InitTransitionErase(Transition::TransitionFadeOut);

AsyncNext([=]() { StartInn(); });
return;
Expand Down Expand Up @@ -480,9 +479,7 @@ void Scene_Map::FinishInn() {
// was issued previously.
screen_erased_by_event = false;

auto& transition = Transition::instance();

transition.InitShow(Transition::TransitionFadeIn, Scene::instance.get());
InitTransitionShow(Transition::TransitionFadeIn);
Game_System::BgmPlay(music_before_inn);

// Full heal
Expand All @@ -501,3 +498,23 @@ void Scene_Map::UpdateInn() {
FinishInn();
}
}

void Scene_Map::InitTransitionShow(Transition::Type tt, int frames) {
// In rare cases where a message and a transition are triggered on the same frame, RPG_RT
// will still animate the message box open/close and the transition effect will show
// the resulting message box state. This can affect interpreter timing so we try to emulate it.
// See #1706 Tests 17 and 18
auto cb = [this](int frames) {
message_window->AdvanceAnimationFrames(frames);
};
Transition::instance().InitShow(tt, this, frames, cb);
}

void Scene_Map::InitTransitionErase(Transition::Type tt, int frames) {
// See comments in InitTransitionShow
auto cb = [this](int frames) {
message_window->AdvanceAnimationFrames(frames);
};
Transition::instance().InitErase(tt, this, frames, cb);
}

4 changes: 4 additions & 0 deletions src/scene_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include "window_message.h"
#include "window_varlist.h"
#include "game_map.h"
#include "transition.h"

/**
* Scene Map class.
Expand Down Expand Up @@ -84,6 +85,9 @@ class Scene_Map: public Scene {
void UpdateInn();
void FinishInn();

void InitTransitionShow(Transition::Type tt, int frames = -1);
void InitTransitionErase(Transition::Type tt, int frames = -1);

template <typename F> void AsyncNext(F&& f);
template <typename F> void OnAsyncSuspend(F&& f, AsyncOp aop, bool is_preupdate);

Expand Down
8 changes: 7 additions & 1 deletion src/transition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "drawable.h"
#include "drawable_mgr.h"
#include "output.h"
#include "game_message.h"

int Transition::GetDefaultFrames(Transition::Type type)
{
Expand Down Expand Up @@ -66,7 +67,7 @@ void Transition::PrependFlashes(int r, int g, int b, int p, int duration, int it
flash_iterations = std::max(1, iterations);
}

void Transition::Init(Type type, Scene *linked_scene, int duration, bool erase) {
void Transition::Init(Type type, Scene *linked_scene, int duration, bool erase, const TransitionCallback& cb) {
if (duration < 0) {
duration = GetDefaultFrames(type);
}
Expand All @@ -86,6 +87,11 @@ void Transition::Init(Type type, Scene *linked_scene, int duration, bool erase)
return;
}

// Allow visual game logic to animate to the end of transition.
if (cb) {
cb(duration);
}

// FIXME: Break this dependency on DisplayUI
if (screen_erased) {
screen1 = Bitmap::Create(DisplayUi->GetWidth(), DisplayUi->GetHeight(), Color(0, 0, 0, 255));
Expand Down
18 changes: 11 additions & 7 deletions src/transition.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,23 +86,27 @@ class Transition : public Drawable {
/** @return the transition singleton */
static Transition& instance();

using TransitionCallback = std::function<void(int)>;

/**
* Initiate a ShowScreen transition
*
* @param type transition type.
* @param linked_scene scene transitioning, it should be either the scene summoning (this) or the current instance (Scene::instance.get())
* @param duration transition duration, set to -1 to use the default number of frames.
* @param cb callback to call with number of frames of transition before it occurs.
*/
void InitShow(Type type, Scene *linked_scene, int duration = -1);
void InitShow(Type type, Scene *linked_scene, int duration = -1, const TransitionCallback& cb = {});

/**
* Initiate an EraseScreen transition
*
* @param type transition type.
* @param linked_scene scene transitioning, it should be either the scene summoning (this) or the current instance (Scene::instance.get())
* @param duration transition duration, set to -1 to use the default number of frames.
* @param cb callback to call with number of frames of transition before it occurs.
*/
void InitErase(Type type, Scene *linked_scene, int duration = -1);
void InitErase(Type type, Scene *linked_scene, int duration = -1, const TransitionCallback& cb = {});

void PrependFlashes(int r, int g, int b, int power, int duration, int iterations);

Expand All @@ -114,7 +118,7 @@ class Transition : public Drawable {

private:
Transition();
void Init(Type type, Scene *linked_scene, int duration, bool erase);
void Init(Type type, Scene *linked_scene, int duration, bool erase, const TransitionCallback& cb);

const uint32_t size_random_blocks = 4;

Expand Down Expand Up @@ -153,12 +157,12 @@ inline Transition& Transition::instance() {
return transition;
}

inline void Transition::InitShow(Type type, Scene *linked_scene, int duration) {
Init(type, linked_scene, duration, false);
inline void Transition::InitShow(Type type, Scene *linked_scene, int duration, const TransitionCallback& cb) {
Init(type, linked_scene, duration, false, cb);
}

inline void Transition::InitErase(Type type, Scene *linked_scene, int duration) {
Init(type, linked_scene, duration, true);
inline void Transition::InitErase(Type type, Scene *linked_scene, int duration, const TransitionCallback& cb) {
Init(type, linked_scene, duration, true, cb);
}

inline bool Transition::IsActive() {
Expand Down
15 changes: 15 additions & 0 deletions src/window_message.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,21 @@ void Window_Message::ResetWindow() {

}

void Window_Message::AdvanceAnimationFrames(int frames) {
if (frames == 0) {
return;
}

close_started_this_frame = false;

while (frames > 0) {
bool was_closing = IsClosing();
Window::Update();
close_finished_this_frame = was_closing && !IsClosing();
--frames;
}
}

void Window_Message::Update() {
if (IsOpening()) { DebugLog("%d: MSG OPENING"); }
if (IsClosing()) { DebugLog("%d: MSG CLOSING"); }
Expand Down
10 changes: 9 additions & 1 deletion src/window_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,11 +88,19 @@ class Window_Message: public Window_Selectable {

void Update() override;

/**
* Force the window animations to advance by a number of frames.
* This is exercised by some edge cases with transitions
*
* @params frames number of frames of the transition.
*/
void AdvanceAnimationFrames(int frames);

/**
* Continues outputting more text. Also handles the
* CommandCode parsing.
*/
virtual void UpdateMessage();
void UpdateMessage();

/**
* Stub. For choice.
Expand Down

0 comments on commit e399c33

Please sign in to comment.