#include "Runtime/MP1/CCredits.hpp" #include "Runtime/CArchitectureMessage.hpp" #include "Runtime/CArchitectureQueue.hpp" #include "Runtime/Graphics/CGraphics.hpp" #include "Runtime/Graphics/CMoviePlayer.hpp" #include "Runtime/GuiSys/CRasterFont.hpp" #include "Runtime/GuiSys/CStringTable.hpp" #include "Runtime/GuiSys/CGuiTextSupport.hpp" #include "Runtime/Input/CFinalInput.hpp" #include "Runtime/MP1/CPlayMovie.hpp" #include "Runtime/GameGlobalObjects.hpp" #include "Runtime/CSimplePool.hpp" #include "Runtime/Graphics/CBooRenderer.hpp" namespace metaforce::MP1 { namespace { logvisor::Module Log("CCredits"); } CCredits::CCredits() : CIOWin("Credits") , x18_creditsTable(g_SimplePool->GetObj(g_tweakGui->GetCreditsTable())) , x20_creditsFont(g_SimplePool->GetObj(g_tweakGui->GetJapaneseCreditsFont())) , x54_(g_tweakGui->x30c_) { x18_creditsTable.Lock(); x20_creditsFont.Lock(); } CIOWin::EMessageReturn CCredits::OnMessage(const CArchitectureMessage& msg, CArchitectureQueue& queue) { switch (msg.GetType()) { case EArchMsgType::UserInput: { return ProcessUserInput(MakeMsg::GetParmUserInput(msg).x4_parm); break; case EArchMsgType::TimerTick: { return Update(MakeMsg::GetParmTimerTick(msg).x4_parm, queue); } default: break; } } return EMessageReturn::Normal; } void CCredits::Draw() { if (x14_ != 3) { return; } SCOPED_GRAPHICS_DEBUG_GROUP("CCredits::Draw", zeus::skGreen); DrawVideo(); DrawText(); } CIOWin::EMessageReturn CCredits::Update(float dt, CArchitectureQueue& queue) { switch (x14_) { case 0: { if (!x18_creditsTable || !x20_creditsFont) { return EMessageReturn::Exit; } if (x30_text.empty()) { for (size_t i = 0; i < x18_creditsTable->GetStringCount(); ++i) { x30_text.emplace_back(std::make_unique( g_ResFactory->GetResourceIdByName(g_tweakGui->GetCreditsFont())->id, CGuiTextProperties(true, true, EJustification::Center, EVerticalJustification::Top), g_tweakGui->GetCreditsTextFontColor(), g_tweakGui->GetCreditsTextBorderColor(), zeus::skWhite, g_Viewport.x8_width - 64, 0, g_SimplePool, CGuiWidget::EGuiModelDrawFlags::Alpha), zeus::CVector2i(0, 0)); x30_text.back().first->SetText(x18_creditsTable->GetString(i)); } // auto tmp = std::make_pair(std::make_unique( // g_ResFactory->GetResourceIdByName(g_tweakGui->GetCreditsFont())->id, // CGuiTextProperties(true, true, EJustification::Center, // EVerticalJustification::Top), g_tweakGui->GetCreditsTextFontColor(), // g_tweakGui->GetCreditsTextBorderColor(), zeus::skWhite, g_Viewport.x8_width // - 64, 0, g_SimplePool, CGuiWidget::EGuiModelDrawFlags::Alpha), // zeus::CVector2i(0, 0)); // tmp.first->SetText( // "\n&push;&font=C29C51F1;&main-color=#89D6FF;URDE DEVELOPMENT TEAM&pop;\n" // "&push;&main-color=#89D6FF;LEAD REVERSE ENGINEERING TEAM&pop\n;" // "Jack \"Cirrus\" Andersen\n" // "Phillip \"Antidote\" Stephens\n" // "Luke \"encounter\" Street\n\n" // "&push;&main-color=#89D6FF;C++ COMPLIANCE & CLEANUP&pop;\n" // "Lioncache\n"); // x30_text.insert(x30_text.end() - 1, std::move(tmp)); // x30_text.back().first->SetOutlineColor(g_tweakGui->GetCreditsTextBorderColor()); } for (const auto& [text, offset] : x30_text) { if (!text->GetIsTextSupportFinishedLoading()) { return EMessageReturn::Exit; } } int scaleY = 0; for (auto& [text, offset] : x30_text) { auto bounds = text->GetBounds(); offset.y = (bounds.second.y - bounds.first.y); offset.x = scaleY; text->SetExtentX(g_Viewport.x8_width - 1280); text->SetExtentY((bounds.second.y - bounds.first.y)); scaleY += (bounds.second.y - bounds.first.y); } x4c_ = float(scaleY + g_Viewport.xc_height - 896); // * 0.5f; const float divVal = std::max(g_tweakGui->x310_, g_tweakGui->x30c_); x50_ = x4c_ / (g_tweakGui->x308_ - divVal); x14_ = 1; break; } case 1: { if (!x28_) { x28_ = std::make_unique("Video/creditBG.thp", 0.f, true, true); } x14_ = 2; break; } case 2: { if (!x2c_) { x2c_ = std::make_unique("Audio/ending3.rsf", 0, 0x5d7c00); } if (!x2c_->IsReady()) { return EMessageReturn::Exit; } x2c_->SetVolume(1.f); x2c_->StartMixing(); x14_ = 3; } [[fallthrough]]; case 3: { m_videoFilter.Update(dt); m_textFilter.Update(dt); // if (!x28_->PumpIndexLoad()) // break; x28_->Update(dt); if (x5c_24_) { x5c_28_ = true; if (x5c_27_) { x5c_27_ = false; x58_ = g_tweakGui->x310_ - x58_; } } if (x5c_27_ || x5c_28_) { x58_ = zeus::clamp(0.f, x58_ + dt, g_tweakGui->x310_); if (x58_ == g_tweakGui->x310_) { if (x5c_27_) { x5c_27_ = false; x58_ = 0.f; } else if (x5c_28_) { x5c_25_ = true; } } if (x58_ != 0.f && x5c_28_) { const float volume = zeus::clamp(0.f, 1.f - x58_ / g_tweakGui->x310_, 1.f); x2c_->SetVolume(volume); } } x48_ = std::min(x4c_, (dt * x50_) + x48_); if (x48_ == x4c_ || x5c_24_) { x5c_24_ = true; x54_ = std::max(0.f, x54_ - dt); const float alpha = x54_ / g_tweakGui->x30c_; for (const auto& [text, offset] : x30_text) { zeus::CColor col = zeus::skWhite; col.a() *= alpha; text->SetGeometryColor(col); } if (x54_ <= 0.f) { x5c_26_ = true; } } if (x5c_26_ && x5c_25_) { queue.Push(MakeMsg::CreateCreateIOWin(EArchMsgTarget::IOWinManager, 12, 11, std::make_shared(CPlayMovie::EWhichMovie::AfterCredits))); return EMessageReturn::RemoveIOWinAndExit; } break; } default: break; } return EMessageReturn::Exit; } CIOWin::EMessageReturn CCredits::ProcessUserInput(const CFinalInput& input) { if (input.DA()) { x48_ = zeus::clamp(0.f, x48_ - ((x50_ * input.DeltaTime())), x4c_); } else { float leftY = input.ALeftY(); float offset = 0.f; if (leftY < 0.f) { offset = -leftY; leftY = 0.f; } x48_ = zeus::clamp(0.f, x48_ - (leftY - offset) * 10.f * x50_ * input.DeltaTime(), x4c_); } return EMessageReturn::Exit; } void CCredits::DrawVideo() { /* Correct movie aspect ratio */ float hPad, vPad; if (g_Viewport.aspect >= 1.78f) { hPad = 1.78f / g_Viewport.aspect; vPad = 1.78f / 1.33f; } else { hPad = 1.f; vPad = g_Viewport.aspect / 1.33f; } if (x28_ && x28_->GetIsFullyCached()) { /* Render movie */ x28_->SetFrame({-hPad, vPad, 0.f}, {-hPad, -vPad, 0.f}, {hPad, -vPad, 0.f}, {hPad, vPad, 0.f}); x28_->DrawFrame(); if (x5c_27_ || x5c_28_) { float alpha = x58_ / g_tweakGui->x310_; if (x5c_27_) { alpha = 1.f - alpha; } alpha = zeus::clamp(0.f, alpha, 1.f); zeus::CColor filterCol = zeus::skBlack; filterCol.a() = alpha; m_videoFilter.SetFilter(EFilterType::Blend, EFilterShape::Fullscreen, 1.f, filterCol, {}); m_videoFilter.Draw(); } } } void CCredits::DrawText() { float width = 896.f * g_Viewport.aspect; CGraphics::SetOrtho(0.f, width, 896.f, 0.f, -4096.f, 4096.f); auto region = std::make_pair(zeus::CVector2f{0.f, 0.f}, zeus::CVector2f{width, 896.f}); CGraphics::SetViewPointMatrix(zeus::CTransform()); CGraphics::SetModelMatrix(zeus::CTransform::Translate((width - 1280.f) / 2.f, 0.f, 896.f)); float dVar5 = (x48_ - (region.second.y() - region.first.y())); for (const auto& [text, offset] : x30_text) { if (offset.y + offset.x >= dVar5 && offset.x <= x48_) { DrawText(*text, {0.5f * (region.second.x() - text->GetExtentX()), 0.f, x48_ - offset.x}); } } m_textFilter.SetFilter(EFilterType::Multiply, EFilterShape::CinemaBars, 1.f, zeus::skBlack, {}); m_textFilter.Draw(); } void CCredits::DrawText(CGuiTextSupport& text, const zeus::CVector3f& translation) { // auto region = g_Renderer->SetViewportOrtho(false, -4096, 4096); zeus::CTransform xf = zeus::CTransform::Translate(translation); g_Renderer->SetModelMatrix(xf); text.Render(); } } // namespace metaforce::MP1