diff --git a/DataSpec/DNACommon/Tweaks/ITweakGui.hpp b/DataSpec/DNACommon/Tweaks/ITweakGui.hpp index 2c1d31af7..6f8fc7950 100644 --- a/DataSpec/DNACommon/Tweaks/ITweakGui.hpp +++ b/DataSpec/DNACommon/Tweaks/ITweakGui.hpp @@ -125,6 +125,11 @@ struct ITweakGui : ITweak { virtual float GetHudLightAttMulConstant() const = 0; virtual float GetHudLightAttMulLinear() const = 0; virtual float GetHudLightAttMulQuadratic() const = 0; + virtual std::string_view GetCreditsTable() const = 0; + virtual std::string_view GetCreditsFont() const =0; + virtual std::string_view GetJapaneseCreditsFont() const=0; + virtual const zeus::CColor& GetCreditsTextFontColor() const=0; + virtual const zeus::CColor& GetCreditsTextBorderColor() const=0; static float FaceReflectionDistanceDebugValueToActualValue(float v) { return 0.015f * v + 0.2f; } static float FaceReflectionHeightDebugValueToActualValue(float v) { return 0.005f * v - 0.05f; } diff --git a/DataSpec/DNAMP1/Tweaks/CTweakGui.hpp b/DataSpec/DNAMP1/Tweaks/CTweakGui.hpp index 109eae1e9..557b97527 100644 --- a/DataSpec/DNAMP1/Tweaks/CTweakGui.hpp +++ b/DataSpec/DNAMP1/Tweaks/CTweakGui.hpp @@ -160,9 +160,9 @@ struct CTweakGui final : ITweakGui { Value x2c0_hudLightAttMulQuadratic; Value m_scanSpeedsCount; Vector x2c4_scanSpeeds; - String<-1> x2d0_; - String<-1> x2e0_; - String<-1> x2f0_; + String<-1> x2d0_creditsTable; + String<-1> x2e0_creditsFont; + String<-1> x2f0_japaneseCreditsFont; DNAColor x300_; DNAColor x304_; Value x308_; @@ -301,6 +301,11 @@ struct CTweakGui final : ITweakGui { float GetHudLightAttMulConstant() const override { return x2b8_hudLightAttMulConstant; } float GetHudLightAttMulLinear() const override { return x2bc_hudLightAttMulLinear; } float GetHudLightAttMulQuadratic() const override { return x2c0_hudLightAttMulQuadratic; } + std::string_view GetCreditsTable() const override { return x2d0_creditsTable; } + std::string_view GetCreditsFont() const override { return x2e0_creditsFont; } + std::string_view GetJapaneseCreditsFont() const override { return x2f0_japaneseCreditsFont; } + const zeus::CColor& GetCreditsTextFontColor() const override { return x300_; } + const zeus::CColor& GetCreditsTextBorderColor() const override { return x304_; } float GetScanSpeed(int idx) const override { if (idx < 0 || size_t(idx) >= x2c4_scanSpeeds.size()) diff --git a/Runtime/GuiSys/CGuiTextSupport.hpp b/Runtime/GuiSys/CGuiTextSupport.hpp index 8e7cc85d6..d11bbe1fc 100644 --- a/Runtime/GuiSys/CGuiTextSupport.hpp +++ b/Runtime/GuiSys/CGuiTextSupport.hpp @@ -129,6 +129,17 @@ public: void SetVerticalJustification(EVerticalJustification j); void SetImageBaseline(bool b); bool GetIsTextSupportFinishedLoading(); + float GetCurTimeMod900() const { return x10_curTimeMod900; } + s32 GetExtentX() const { return x34_extentX; } + void SetExtentX(s32 extent) { + x34_extentX = extent; + ClearRenderBuffer(); + } + s32 GetExtentY() const { return x38_extentY; } + void SetExtentY(s32 extent) { + x38_extentY = extent; + ClearRenderBuffer(); + } float GetCurTime() const { return x3c_curTime; } void SetCurTime(float t) { x3c_curTime = t; } std::u16string_view GetString() const { return x0_string; } diff --git a/Runtime/GuiSys/CInstruction.cpp b/Runtime/GuiSys/CInstruction.cpp index cdc5578c3..f134ce695 100644 --- a/Runtime/GuiSys/CInstruction.cpp +++ b/Runtime/GuiSys/CInstruction.cpp @@ -162,7 +162,13 @@ void CLineSpacingInstruction::Invoke(CFontRenderState& state, CTextRenderBuffer* void CLineSpacingInstruction::PageInvoke(CFontRenderState& state, CTextRenderBuffer* buf) const { Invoke(state, buf); } -void CPopStateInstruction::Invoke(CFontRenderState& state, CTextRenderBuffer* buf) const { state.PopState(); } +void CPopStateInstruction::Invoke(CFontRenderState& state, CTextRenderBuffer* buf) const { + const auto& oldFont = state.GetFont(); + state.PopState(); + if (oldFont.GetObj() != state.GetFont().GetObj()) { + buf->AddFontChange(state.GetFont()); + } +} void CPopStateInstruction::PageInvoke(CFontRenderState& state, CTextRenderBuffer* buf) const { Invoke(state, buf); } diff --git a/Runtime/GuiSys/CSaveableState.hpp b/Runtime/GuiSys/CSaveableState.hpp index 2809a2347..a7eb1c130 100644 --- a/Runtime/GuiSys/CSaveableState.hpp +++ b/Runtime/GuiSys/CSaveableState.hpp @@ -35,6 +35,7 @@ protected: public: CSaveableState() : x54_colors(3, zeus::skBlack), x64_colorOverrides(16) {} + const TLockedToken& GetFont() const { return x48_font; } bool IsFinishedLoading() const; }; diff --git a/Runtime/GuiSys/CTextExecuteBuffer.cpp b/Runtime/GuiSys/CTextExecuteBuffer.cpp index 6cac32e35..946202818 100644 --- a/Runtime/GuiSys/CTextExecuteBuffer.cpp +++ b/Runtime/GuiSys/CTextExecuteBuffer.cpp @@ -229,7 +229,7 @@ void CTextExecuteBuffer::MoveWordLTR() { ->get()); // Dunno what's up with this in the original; seems fine without - // x0_instList.emplace(xa8_curWordIt, std::make_shared()); + x0_instList.emplace(xa8_curWordIt, std::make_shared()); ++xa0_curBlock->x34_lineCount; } @@ -263,7 +263,7 @@ void CTextExecuteBuffer::TerminateLine() { } void CTextExecuteBuffer::TerminateLineLTR() { - if (!xa4_curLine->xc_curY /*&& x18_textState.IsFinishedLoading()*/) { + if (!xa4_curLine->xc_curY && x18_textState.IsFinishedLoading()) { xa4_curLine->xc_curY = std::max(xa4_curLine->GetHeight(), x18_textState.x48_font->GetCarriageAdvance()); } @@ -280,7 +280,7 @@ void CTextExecuteBuffer::AddPopState() { x18_textState = xc4_stateStack.back(); xc4_stateStack.pop_back(); - if (!xa4_curLine->x8_curX) { + if (xa4_curLine->x8_curX == 0) { xa4_curLine->x28_just = x18_textState.x80_just; xa4_curLine->x2c_vjust = x18_textState.x84_vjust; } diff --git a/Runtime/MP1/CCredits.cpp b/Runtime/MP1/CCredits.cpp index 9b896cea7..b961200e6 100644 --- a/Runtime/MP1/CCredits.cpp +++ b/Runtime/MP1/CCredits.cpp @@ -1,17 +1,252 @@ #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 { - -CCredits::CCredits() : CIOWin("Credits") {} +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)); + x30_text.back().first->SetOutlineColor(g_tweakGui->GetCreditsTextBorderColor()); + } + + 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 - 64); + text->SetExtentY((bounds.second.y - bounds.first.y)); + scaleY += (bounds.second.y - bounds.first.y); + } + + x4c_ = float(scaleY + g_Viewport.xc_height); // * 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_28_ = 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() { + auto region = g_Renderer->SetViewportOrtho(false, -4096, 4096); + 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 diff --git a/Runtime/MP1/CCredits.hpp b/Runtime/MP1/CCredits.hpp index d5efabd56..97c0da6cb 100644 --- a/Runtime/MP1/CCredits.hpp +++ b/Runtime/MP1/CCredits.hpp @@ -1,15 +1,53 @@ #pragma once #include "Runtime/CIOWin.hpp" +#include "Runtime/CToken.hpp" +#include "Runtime/Audio/CStaticAudioPlayer.hpp" +#include "Runtime/Camera/CCameraFilter.hpp" -namespace metaforce::MP1 { +#include "zeus/CVector2i.hpp" +namespace metaforce { +class CGuiTextSupport; +class CStringTable; +class CRasterFont; +class CMoviePlayer; +struct CFinalInput; + +namespace MP1 { class CCredits : public CIOWin { + u32 x14_ = 0; + TLockedToken x18_creditsTable; + TLockedToken x20_creditsFont; + std::unique_ptr x28_; + std::unique_ptr x2c_; + std::vector, zeus::CVector2i>> x30_text; + int x44_textSupport = 0; + float x48_ = 0.f; + float x4c_ = 0.f; + float x50_ = 8.f; + float x54_; + float x58_ = 0.f; + bool x5c_24_ : 1 = false; + bool x5c_25_ : 1 = false; + bool x5c_26_ : 1 = false; + bool x5c_27_ : 1 = true; + bool x5c_28_ : 1 = false; + void DrawVideo(); + void DrawText(); + static void DrawText(CGuiTextSupport&, const zeus::CVector3f& translation); + + CCameraFilterPassPoly m_videoFilter; + CCameraFilterPassPoly m_textFilter; + public: CCredits(); EMessageReturn OnMessage(const CArchitectureMessage&, CArchitectureQueue&) override; bool GetIsContinueDraw() const override { return false; } void Draw() override; -}; -} // namespace metaforce::MP1 + EMessageReturn Update(float, CArchitectureQueue& queue); + EMessageReturn ProcessUserInput(const CFinalInput& input); +}; +} // namespace MP1 +} // namespace metaforce diff --git a/Runtime/MP1/MP1.cpp b/Runtime/MP1/MP1.cpp index 120b95aa3..58e87b29a 100644 --- a/Runtime/MP1/MP1.cpp +++ b/Runtime/MP1/MP1.cpp @@ -70,6 +70,9 @@ #include #include + +#include "Runtime/MP1/CCredits.hpp" + #include namespace metaforce::MP1 { @@ -106,6 +109,9 @@ CGameArchitectureSupport::CGameArchitectureSupport(CMain& parent, boo::IAudioVoi CStreamAudioManager::SetMusicVolume(0x7f); m->ResetGameState(); + std::shared_ptr credits = std::make_shared(); + x58_ioWinManager.AddIOWin(credits, 1000, 10000); +/* if (!g_tweakGame->GetSplashScreensDisabled()) { std::shared_ptr splash = std::make_shared(CSplashScreen::ESplashScreen::Nintendo); x58_ioWinManager.AddIOWin(splash, 1000, 10000); @@ -125,6 +131,7 @@ CGameArchitectureSupport::CGameArchitectureSupport(CMain& parent, boo::IAudioVoi g_GuiSys = &x44_guiSys; g_GameState->GameOptions().EnsureSettings(); + */ } void CGameArchitectureSupport::UpdateTicks(float dt) {