diff --git a/asm/MetroidPrime/CCredits.s b/asm/MetroidPrime/CCredits.s index 38e7884d..39aa185f 100644 --- a/asm/MetroidPrime/CCredits.s +++ b/asm/MetroidPrime/CCredits.s @@ -475,8 +475,8 @@ lbl_80025468: /* 80025484 000223E4 38 21 00 20 */ addi r1, r1, 0x20 /* 80025488 000223E8 4E 80 00 20 */ blr -.global sub_8002548c -sub_8002548c: +.global DrawVideo__8CCreditsCFv +DrawVideo__8CCreditsCFv: /* 8002548C 000223EC 94 21 FF C0 */ stwu r1, -0x40(r1) /* 80025490 000223F0 7C 08 02 A6 */ mflr r0 /* 80025494 000223F4 90 01 00 44 */ stw r0, 0x44(r1) @@ -489,7 +489,7 @@ sub_8002548c: /* 800254B0 00022410 80 63 00 28 */ lwz r3, 0x28(r3) /* 800254B4 00022414 28 03 00 00 */ cmplwi r3, 0 /* 800254B8 00022418 41 82 00 A0 */ beq lbl_80025558 -/* 800254BC 0002241C 48 00 2A 89 */ bl sub_80027f44 +/* 800254BC 0002241C 48 00 2A 89 */ bl DrawVideo__12CMoviePlayerCFv /* 800254C0 00022420 54 60 06 3F */ clrlwi. r0, r3, 0x18 /* 800254C4 00022424 41 82 00 94 */ beq lbl_80025558 /* 800254C8 00022428 88 7F 00 5C */ lbz r3, 0x5c(r31) @@ -541,8 +541,8 @@ lbl_80025558: /* 80025574 000224D4 38 21 00 40 */ addi r1, r1, 0x40 /* 80025578 000224D8 4E 80 00 20 */ blr -.global sub_8002557c -sub_8002557c: +.global DrawText__8CCreditsCFv +DrawText__8CCreditsCFv: /* 8002557C 000224DC 94 21 FF 70 */ stwu r1, -0x90(r1) /* 80025580 000224E0 7C 08 02 A6 */ mflr r0 /* 80025584 000224E4 90 01 00 94 */ stw r0, 0x94(r1) @@ -658,9 +658,9 @@ Draw__8CCreditsCFv: /* 80025724 00022684 80 03 00 14 */ lwz r0, 0x14(r3) /* 80025728 00022688 2C 00 00 03 */ cmpwi r0, 3 /* 8002572C 0002268C 40 82 00 10 */ bne lbl_8002573C -/* 80025730 00022690 4B FF FD 5D */ bl sub_8002548c +/* 80025730 00022690 4B FF FD 5D */ bl DrawVideo__8CCreditsCFv /* 80025734 00022694 7F E3 FB 78 */ mr r3, r31 -/* 80025738 00022698 4B FF FE 45 */ bl sub_8002557c +/* 80025738 00022698 4B FF FE 45 */ bl DrawText__8CCreditsCFv lbl_8002573C: /* 8002573C 0002269C 80 01 00 14 */ lwz r0, 0x14(r1) /* 80025740 000226A0 83 E1 00 0C */ lwz r31, 0xc(r1) @@ -1927,7 +1927,7 @@ sub_8002692c: /* 80026938 00023898 80 63 00 38 */ lwz r3, 0x38(r3) /* 8002693C 0002389C 28 03 00 00 */ cmplwi r3, 0 /* 80026940 000238A0 41 82 00 08 */ beq lbl_80026948 -/* 80026944 000238A4 48 00 16 01 */ bl sub_80027f44 +/* 80026944 000238A4 48 00 16 01 */ bl DrawVideo__12CMoviePlayerCFv lbl_80026948: /* 80026948 000238A8 80 01 00 14 */ lwz r0, 0x14(r1) /* 8002694C 000238AC 7C 08 03 A6 */ mtlr r0 @@ -3474,8 +3474,8 @@ lbl_80027F30: /* 80027F3C 00024E9C 38 21 00 20 */ addi r1, r1, 0x20 /* 80027F40 00024EA0 4E 80 00 20 */ blr -.global sub_80027f44 -sub_80027f44: +.global DrawVideo__12CMoviePlayerCFv +DrawVideo__12CMoviePlayerCFv: /* 80027F44 00024EA4 94 21 FF 60 */ stwu r1, -0xa0(r1) /* 80027F48 00024EA8 7C 08 02 A6 */ mflr r0 /* 80027F4C 00024EAC 90 01 00 A4 */ stw r0, 0xa4(r1) diff --git a/configure.py b/configure.py index 311f08d2..9cff61b1 100755 --- a/configure.py +++ b/configure.py @@ -29,7 +29,7 @@ LIBS = [ ["MetroidPrime/CInputGenerator", False], ["MetroidPrime/CMainFlow", False], "MetroidPrime/CMFGame", - "MetroidPrime/CCredits", + ["MetroidPrime/CCredits", False], "MetroidPrime/CSplashScreen", ["MetroidPrime/CAnimData", False], "MetroidPrime/Factories/CCharacterFactory", diff --git a/include/GuiSys/CGuiTextSupport.hpp b/include/GuiSys/CGuiTextSupport.hpp index c612f451..9c65d2a0 100644 --- a/include/GuiSys/CGuiTextSupport.hpp +++ b/include/GuiSys/CGuiTextSupport.hpp @@ -2,25 +2,86 @@ #define _CGUITEXTSUPPORT #include "rstl/string.hpp" +#include "rstl/vector.hpp" class CColor; class CGuiFrame; class CTextExecuteBuffer; class CTextParser; +enum EJustification { + kJ_Left = 0, + kJ_Center, + kJ_Right, + kJ_Full, + kJ_NLeft, + kJ_NCenter, + kJ_NRight, + kJ_LeftMono, + kJ_CenterMono, + kJ_RightMono, +}; + +enum EVerticalJustification { + kVJ_Top = 0, + kVJ_Center, + kVJ_Bottom, + kVJ_Full, + kVJ_NTop, + kVJ_NCenter, + kVJ_NBottom, + kVJ_TopMono, + kVJ_CenterMono, + kVJ_RightMono, +}; + +enum ETextDirection { + kTD_Horizontal, + kTD_Vertical, +}; + +class CGuiTextProperties { + friend class CGuiTextSupport; + +public: + CGuiTextProperties(bool wordWrap, bool horizontal, EJustification justification, + EVerticalJustification vertJustification, + const rstl::vector< rstl::pair< CAssetId, CAssetId > >* txtrMap = nullptr) + : x0_wordWrap(wordWrap) + , x1_horizontal(horizontal) + , x4_justification(justification) + , x8_vertJustification(vertJustification) + , xc_txtrMap(txtrMap) {} + +private: + bool x0_wordWrap; + bool x1_horizontal; + EJustification x4_justification; + EVerticalJustification x8_vertJustification; + const rstl::vector< rstl::pair< CAssetId, CAssetId > >* xc_txtrMap; +}; + class CGuiTextSupport { static CTextExecuteBuffer* gpExecBuf; static CTextParser* gpTextParser; public: + CGuiTextSupport(CAssetId fontId, const CGuiTextProperties& props, const CColor& fontCol, + const CColor& outlineCol, const CColor& geomCol, int extX, int extY, + CSimplePool* store, int /*CGuiWidget::EGuiModelDrawFlags*/ drawFlags); + void SetText(const rstl::string&, bool multipage = false); void SetOutlineColor(const CColor& col); void SetFontColor(const CColor& col); + void Render() const; static void Initialize(CTextExecuteBuffer* buf, CTextParser* parser) { gpExecBuf = buf; gpTextParser = parser; } + +private: + char x0_pad[0x30c]; }; #endif // _CGUITEXTSUPPORT diff --git a/include/Kyoto/Graphics/CMoviePlayer.hpp b/include/Kyoto/Graphics/CMoviePlayer.hpp index caa71068..c03e8447 100644 --- a/include/Kyoto/Graphics/CMoviePlayer.hpp +++ b/include/Kyoto/Graphics/CMoviePlayer.hpp @@ -6,6 +6,15 @@ class CMoviePlayer { public: static void SetSfxVolume(uchar); + + CMoviePlayer(const char*, float, bool, bool); + ~CMoviePlayer(); + + void Update(float); + bool DrawVideo() const; + + private: + char x0_pad[0x100]; }; #endif // _CMOVIEPLAYER diff --git a/include/Kyoto/Input/CFinalInput.hpp b/include/Kyoto/Input/CFinalInput.hpp index c60da2ff..5a0cc261 100644 --- a/include/Kyoto/Input/CFinalInput.hpp +++ b/include/Kyoto/Input/CFinalInput.hpp @@ -42,6 +42,8 @@ public: CFinalInput ScaleAnalogueSticks(float leftDiv, float rightDiv) const; + float DeltaTime() const { return x0_dt; } + float ALAUp() const { return xc_anaLeftY > 0.f ? xc_anaLeftY : 0.f; } float ALADown() const { return xc_anaLeftY < 0.f ? -xc_anaLeftY : 0.f; } diff --git a/include/Kyoto/Text/CStringTable.hpp b/include/Kyoto/Text/CStringTable.hpp index 730ebb39..deeead15 100644 --- a/include/Kyoto/Text/CStringTable.hpp +++ b/include/Kyoto/Text/CStringTable.hpp @@ -14,6 +14,7 @@ class CStringTable { public: CStringTable(CInputStream& in); const wchar_t* GetString(int idx) const; + int GetStringCount() const { return x0_stringCount; } }; extern CStringTable* gpStringTable; diff --git a/include/MetroidPrime/CCredits.hpp b/include/MetroidPrime/CCredits.hpp index 289199b1..98395284 100644 --- a/include/MetroidPrime/CCredits.hpp +++ b/include/MetroidPrime/CCredits.hpp @@ -3,13 +3,53 @@ #include "MetroidPrime/CIOWin.hpp" +#include "Kyoto/TToken.hpp" +#include "Kyoto/Math/CVector2i.hpp" + +#include "rstl/single_ptr.hpp" + +class CFinalInput; +class CStringTable; +class CRasterFont; +class CMoviePlayer; +class CStaticAudioPlayer; +class CGuiTextSupport; +class CVector3f; + class CCredits : public CIOWin { public: CCredits(); - EMessageReturn OnMessage(const CArchitectureMessage& msg, CArchitectureQueue& queue); + ~CCredits() override; + + EMessageReturn OnMessage(const CArchitectureMessage&, CArchitectureQueue&) override; + bool GetIsContinueDraw() const override; + void Draw() const override; + + EMessageReturn Update(float, CArchitectureQueue& queue); + EMessageReturn ProcessUserInput(const CFinalInput& input); private: - char data[0x4c]; + int x14_; // = 0; + TLockedToken< CStringTable > x18_creditsTable; + TLockedToken< CRasterFont > x20_creditsFont; + rstl::single_ptr< CMoviePlayer > x28_moviePlayer; + rstl::single_ptr< CStaticAudioPlayer > x2c_; + rstl::vector< rstl::pair< rstl::single_ptr< CGuiTextSupport >, 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() const; + void DrawText() const; + static void DrawText(CGuiTextSupport&, const CVector3f& translation); }; #endif // _CCREDITS diff --git a/include/MetroidPrime/Tweaks/CTweakGui.hpp b/include/MetroidPrime/Tweaks/CTweakGui.hpp index 81bbded8..e830da99 100644 --- a/include/MetroidPrime/Tweaks/CTweakGui.hpp +++ b/include/MetroidPrime/Tweaks/CTweakGui.hpp @@ -154,7 +154,9 @@ public: const rstl::string& GetJapaneseCreditsFont() const { return x2f0_japaneseCreditsFont; } const CColor& GetCreditsTextFontColor() const { return x300_; } const CColor& GetCreditsTextBorderColor() const { return x304_; } - + float GetCredits_x30c() const { return x30c_; } + float GetCredits_x310() const { return x310_; } + static float FaceReflectionDistanceDebugValueToActualValue(float v); static float FaceReflectionHeightDebugValueToActualValue(float v); static float FaceReflectionAspectDebugValueToActualValue(float v); diff --git a/src/MetroidPrime/CCredits.cpp b/src/MetroidPrime/CCredits.cpp new file mode 100644 index 00000000..2350a2ef --- /dev/null +++ b/src/MetroidPrime/CCredits.cpp @@ -0,0 +1,251 @@ +#include "MetroidPrime/CCredits.hpp" + +#include "MetroidPrime/CArchitectureMessage.hpp" +#include "MetroidPrime/Cameras/CCameraFilterPass.hpp" +#include "MetroidPrime/Decode.hpp" +#include "MetroidPrime/Tweaks/CTweakGui.hpp" + +#include "GuiSys/CGuiTextSupport.hpp" +#include "GuiSys/CGuiWidget.hpp" + + +#include "Kyoto/CResFactory.hpp" +#include "Kyoto/Graphics/CGraphics.hpp" +#include "Kyoto/Graphics/CMoviePlayer.hpp" +#include "Kyoto/Text/CRasterFont.hpp" +#include "Kyoto/Text/CStringTable.hpp" + +#include "MetaRender/CCubeRenderer.hpp" + +#include "rstl/math.hpp" + +bool CCredits::GetIsContinueDraw() const { return false; } + +CCredits::CCredits() +: CIOWin("Credits") +, x18_creditsTable(gpSimplePool->GetObj(gpTweakGui->GetCreditsTable().data())) +, x20_creditsFont(gpSimplePool->GetObj(gpTweakGui->GetJapaneseCreditsFont().data())) +, x54_(gpTweakGui->GetCredits_x30c()) { + // x18_creditsTable.Lock(); + // x20_creditsFont.Lock(); +} + +CCredits::~CCredits() {} + +CIOWin::EMessageReturn CCredits::OnMessage(const CArchitectureMessage& msg, + CArchitectureQueue& queue) { + switch (msg.GetType()) { + case kAM_UserInput: { + return ProcessUserInput(MakeMsg::GetParmUserInput(msg).GetUserInput()); + break; + case kAM_TimerTick: { + return Update(MakeMsg::GetParmTimerTick(msg).GetReal(), queue); + } + default: + break; + } + } + return kMR_Normal; +} + +CIOWin::EMessageReturn CCredits::Update(float dt, CArchitectureQueue& queue) { + switch (x14_) { + case 0: { + if (!x18_creditsTable.IsLoaded() || !x20_creditsFont.IsLoaded()) { + return kMR_Exit; + } + if (x30_text.empty()) { + for (int i = 0; i < x18_creditsTable->GetStringCount(); ++i) { + x30_text.push_back(rstl::pair< rstl::single_ptr< CGuiTextSupport >, CVector2i >( + new CGuiTextSupport( + gpResourceFactory->GetResourceIdByName(gpTweakGui->GetCreditsFont().data())->id, + CGuiTextProperties(true, true, kJ_Center, kVJ_Top), + gpTweakGui->GetCreditsTextFontColor(), gpTweakGui->GetCreditsTextBorderColor(), + CColor::White(), CGraphics::GetViewport().mWidth - 64, 0, gpSimplePool, + CGuiWidget::kGMDF_Alpha), + CVector2i(0, 0))); + // x30_text.back().first->SetText(x18_creditsTable->GetString(i)); + } + + // auto tmp = std::make_pair(std::make_unique( + // gpResourceFactory->GetResourceIdByName(gpTweakGui->GetCreditsFont())->id, + // CGuiTextProperties(true, true, kJ_Center, + // EVerticalJustification::Top), + // gpTweakGui->GetCreditsTextFontColor(), + // gpTweakGui->GetCreditsTextBorderColor(), + // CColor::White(), g_Viewport.x8_width + // - 64, 0, gpSimplePool, + // CGuiWidget::EGuiModelDrawFlags::Alpha), + // 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(gpTweakGui->GetCreditsTextBorderColor()); + } + + // for (const auto& [text, offset] : x30_text) { + // if (!text->GetIsTextSupportFinishedLoading()) { + // return kMR_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(CGraphics::GetViewportWidth() - 1280); + // text->SetExtentY((bounds.second.y - bounds.first.y)); + // scaleY += (bounds.second.y - bounds.first.y); + // } + + // x4c_ = float(scaleY + CGraphics::GetViewportHeight() - 896); // * 0.5f; + // const float divVal = rstl::max_val(gpTweakGui->x310_, gpTweakGui->x30c_); + // x50_ = x4c_ / (gpTweakGui->x308_ - divVal); + x14_ = 1; + break; + } + case 1: { + if (x28_moviePlayer.null()) { + x28_moviePlayer = new CMoviePlayer("Video/creditBG.thp", 0.f, true, true); + } + x14_ = 2; + break; + } + case 2: { + // if (x2c_.null()) { + // x2c_ = new CStaticAudioPlayer("Audio/ending3.rsf", 0, 0x5d7c00); + // } + // if (!x2c_->IsReady()) { + // return kMR_Exit; + // } + // x2c_->SetVolume(1.f); + // x2c_->StartMixing(); + x14_ = 3; + } + // [[fallthrough]]; + case 3: { + // if (!x28_moviePlayer->PumpIndexLoad()) + // break; + x28_moviePlayer->Update(dt); + if (x5c_24_) { + x5c_28_ = true; + if (x5c_27_) { + x5c_27_ = false; + x58_ = gpTweakGui->GetCredits_x310() - x58_; + } + } + if (x5c_27_ || x5c_28_) { + x58_ = CMath::Clamp(0.f, x58_ + dt, gpTweakGui->GetCredits_x310()); + if (x58_ == gpTweakGui->GetCredits_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 = CMath::Clamp(0.f, 1.f - x58_ / gpTweakGui->GetCredits_x310(), 1.f); + // x2c_->SetVolume(volume); + } + } + x48_ = rstl::min_val(x4c_, (dt * x50_) + x48_); + + if (x48_ == x4c_ || x5c_24_) { + x5c_24_ = true; + x54_ = rstl::max_val(0.f, x54_ - dt); + const float alpha = x54_ / gpTweakGui->GetCredits_x30c(); + // for (const auto& [text, offset] : x30_text) { + // CColor col = CColor::White(); + // 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 >(CPlayMovie::EWhichMovie::AfterCredits))); + return kMR_RemoveIOWinAndExit; + } + break; + } + default: + break; + } + return kMR_Exit; +} + +CIOWin::EMessageReturn CCredits::ProcessUserInput(const CFinalInput& input) { + if (input.DA()) { + x48_ = CMath::Clamp(0.f, x48_ - ((x50_ * input.DeltaTime())), x4c_); + } else { + float leftY = input.ALAUp(); + float offset = 0.f; + if (leftY < 0.f) { + offset = -leftY; + leftY = 0.f; + } + x48_ = CMath::Clamp(0.f, x48_ - (leftY - offset) * 10.f * x50_ * input.DeltaTime(), x4c_); + } + return kMR_Exit; +} + +void CCredits::DrawText(CGuiTextSupport& text, const CVector3f& translation) { + CGraphics::SetCullMode(kCM_None); + gpRender->SetViewportOrtho(true, -4096.f, 4096.f); + gpRender->SetModelMatrix(CTransform4f::Translate(translation)); + gpRender->SetDepthReadWrite(false, false); + text.Render(); +} + +void CCredits::Draw() const { + if (x14_ != 3) { + return; + } + DrawVideo(); + DrawText(); +} + +void CCredits::DrawText() const { + // float width = 896.f * CGraphics::GetViewportAspect(); + // CGraphics::SetOrtho(0.f, width, 896.f, 0.f, -4096.f, 4096.f); + // auto region = std::make_pair(CVector2f{0.f, 0.f}, CVector2f{width, 896.f}); + // CGraphics::SetViewPointMatrix(CTransform4f()); + // CGraphics::SetModelMatrix(CTransform4f::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}); + // } + // } + // CCameraFilterPass::DrawFilter(EFilterType::Multiply, EFilterShape::CinemaBars, skBlack, + // nullptr, + // 1.f); +} + +void CCredits::DrawVideo() const { + /* Render movie */ + if (x28_moviePlayer.get() && x28_moviePlayer->DrawVideo() && (x5c_27_ || x5c_28_)) { + float alpha = x58_ / gpTweakGui->GetCredits_x310(); + if (x5c_27_) { + alpha = 1.f - alpha; + } + + alpha = CMath::Clamp(0.f, alpha, 1.f); + CColor filterCol = CColor::Black().WithAlphaOf(alpha); + CCameraFilterPass::DrawFilter(CCameraFilterPass::kFT_Blend, CCameraFilterPass::kFS_Fullscreen, + filterCol, nullptr, 1.f); + } +}