Files
dusklight/src/d/actor/d_a_title.cpp
T
Ash 2da6590657 feat: FPS Limiter (#1446)
* Add interpolation frame rate cap

* wip: reworked framelimiter

Based on my testing this is a bit more stable in frametimes.

* wip: efficiency improvement + windows build fix

Significantly improve efficiency by using a hybrid approach.

* wip: UI changes

* wip: end frame AFTER limiting

* wip: remove unused include

* wip: minor ui code change

Makes it easier to remove/add presets

* Simplify Limiter UI

- Change enableFrameInterpolation to an enum with off/capped/unlimited values
- Simplify the UI to use 2 settings (unlock framerate + a max value entry)

* wip: slight limiter simplification

* wip: implement review suggestions

* wip: fix syntax error

* wip: revert enum order + replace old checks

* Fix compile error

---------

Co-authored-by: SailorSnoW <sailorsnow@pm.me>
Co-authored-by: Loïs <49660929+SailorSnoW@users.noreply.github.com>
Co-authored-by: SuperDude88 <82904174+SuperDude88@users.noreply.github.com>
Co-authored-by: Luke Street <luke@street.dev>
2026-05-17 20:11:32 -06:00

474 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "d/dolzel_rel.h" // IWYU pragma: keep
#include "JSystem/J2DGraph/J2DScreen.h"
#include "JSystem/J2DGraph/J2DTextBox.h"
#include "JSystem/JKernel/JKRExpHeap.h"
#include "JSystem/JKernel/JKRMemArchive.h"
#include "d/actor/d_a_title.h"
#include "d/d_com_inf_game.h"
#include "d/d_demo.h"
#include "d/d_menu_collect.h"
#include "d/d_pane_class_alpha.h"
#include "d/d_s_logo.h"
#include "d/d_s_play.h"
#include "dusk/version.hpp"
#include "f_op/f_op_msg_mng.h"
#include "f_op/f_op_overlap_mng.h"
#include "f_op/f_op_scene_mng.h"
#include "m_Do/m_Do_Reset.h"
#include "m_Do/m_Do_controller_pad.h"
#include "m_Do/m_Do_graphic.h"
#ifdef TARGET_PC
#include "dusk/frame_interpolation.h"
#include "dusk/settings.h"
#endif
class daTit_HIO_c : public JORReflexible {
public:
daTit_HIO_c();
virtual ~daTit_HIO_c() {}
void genMessage(JORMContext*);
/* 0x04 */ s8 id;
/* 0x08 */ f32 mPSScaleX;
/* 0x0C */ f32 mPSScaleY;
/* 0x10 */ f32 mPSPosX;
/* 0x14 */ f32 mPSPosY;
#if DEBUG
/* 0x18 */ u8 unk_0x18[0x48 - 0x18];
#endif
/* 0x18 */ u8 mAppear;
/* 0x19 */ u8 mArrow;
/* 0x1A */ u8 field_0x1a;
};
static daTit_HIO_c g_daTitHIO;
static u8 const lit_3772[12] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
#if TARGET_PC
using namespace dusk::version;
#define l_arcName versionSelect<const char*>({{GameVersion::GcnPal, "TitlePal"}}, "Title")
#elif VERSION == VERSION_GCN_PAL
static char const l_arcName[] = "TitlePal";
#else
static char const l_arcName[] = "Title";
#endif
daTit_HIO_c::daTit_HIO_c() {
mPSScaleX = 1.0f;
mPSScaleY = 1.0f;
#if TARGET_PC || VERSION == VERSION_GCN_PAL
switch (OSGetLanguage()) {
case OS_LANGUAGE_ENGLISH:
case OS_LANGUAGE_GERMAN:
case OS_LANGUAGE_SPANISH:
case OS_LANGUAGE_ITALIAN:
case OS_LANGUAGE_DUTCH:
mPSPosX = 303.0f;
break;
case OS_LANGUAGE_FRENCH:
mPSPosX = FB_WIDTH / 2;
break;
}
#else
mPSPosX = 303.0f;
#endif
mPSPosY = 347.0f;
mAppear = 15;
mArrow = 60;
field_0x1a = 15;
}
#if DEBUG
void daTit_HIO_c::genMessage(JORMContext* mctx) {
mctx->genLabel("\n======= PRESS START ========", 0);
mctx->genSlider("Scale ", &mPSScaleX, 0.1f, 100.0f);
mctx->genSlider("Scale ", &mPSScaleY, 0.1f, 100.0f);
mctx->genSlider("Pos ", &mPSPosX, 0.0f, 1000.0f);
mctx->genSlider("Pos ", &mPSPosY, 0.0f, 1000.0f);
mctx->genLabel("\n======= ", 0);
mctx->genSlider("出現", &mAppear, 0, 255);
mctx->genSlider("矢印", &mArrow, 0, 255);
}
#endif
int daTitle_c::CreateHeap() {
J3DModelData* modelData = (J3DModelData*)dComIfG_getObjectRes(l_arcName, 10);
JUT_ASSERT(258, modelData);
mpModel = mDoExt_J3DModel__create(modelData, 0x80000, 0x11000285);
if (mpModel == NULL) {
return 0;
}
int res = mBck.init((J3DAnmTransform*)dComIfG_getObjectRes(l_arcName, 7), 1, 0, 2.0f, 0, -1, false);
JUT_ASSERT(276, res == 1);
res = mBpk.init(modelData, (J3DAnmColor*)dComIfG_getObjectRes(l_arcName, 13), 1, 0, 2.0f, 0, -1);
JUT_ASSERT(283, res == 1);
res = mBrk.init(modelData, (J3DAnmTevRegKey*)dComIfG_getObjectRes(l_arcName, 16), 1, 0, 2.0f, 0, -1);
JUT_ASSERT(290, res == 1);
res = mBtk.init(modelData, (J3DAnmTextureSRTKey*)dComIfG_getObjectRes(l_arcName, 19), 1, 0, 2.0f, 0, -1);
JUT_ASSERT(297, res == 1);
return 1;
}
static procFunc daTitleProc[6] = {
&daTitle_c::loadWait_proc, &daTitle_c::logoDispWait, &daTitle_c::logoDispAnm,
&daTitle_c::keyWait, &daTitle_c::nextScene_proc, &daTitle_c::fastLogoDisp,
};
int daTitle_c::create() {
fopAcM_ct(this, daTitle_c);
int phase_state = dComIfG_resLoad(&mPhaseReq, l_arcName);
if (phase_state != cPhs_COMPLEATE_e) {
return phase_state;
}
if (!fopAcM_entrySolidHeap(this, createHeapCallBack, 0x4000)) {
return cPhs_ERROR_e;
}
mpMount = mDoDvdThd_mountArchive_c::create("/res/Layout/Title2D.arc", 0, NULL);
mIsDispLogo = 0;
field_0x5f9 = 0;
m2DHeap = JKRCreateExpHeap(0x8000, mDoExt_getGameHeap(), false);
JUT_ASSERT(345, m2DHeap != NULL);
JKRHEAP_NAME(m2DHeap, "daTitle_c::m2DHeap");
loadWait_init();
g_daTitHIO.id = mDoHIO_CREATE_CHILD("タイトルロゴ", &g_daTitHIO);
return phase_state;
}
int daTitle_c::createHeapCallBack(fopAc_ac_c* actor) {
daTitle_c* i_this = (daTitle_c*)actor;
return i_this->CreateHeap();
}
int daTitle_c::Execute() {
#if PLATFORM_WII || PLATFORM_SHIELD
mDoGph_gInf_c::resetDimming();
#endif
if (fopOvlpM_IsPeek()) {
return 1;
}
#ifdef TARGET_PC
if (!dusk::frame_interp::is_enabled()) {
#endif
dMenu_Collect3D_c::setViewPortOffsetY(0.0f);
#ifdef TARGET_PC
}
#endif
if (mDoRst::isReset()) {
return 1;
}
(this->*daTitleProc[mProcID])();
KeyWaitAnm();
#if VERSION == VERSION_SHIELD_DEBUG
KeyWaitPosMove();
#endif
return 1;
}
void daTitle_c::KeyWaitAnm() {
if (field_0x5f9 != 0) {
if (field_0x604 == 0) {
if (field_0x5fa != 0) {
field_0x600->alphaAnime(g_daTitHIO.mArrow, 0, 255, 0);
} else {
field_0x600->alphaAnimeLoop(g_daTitHIO.mArrow, 255, 128, 0);
}
if (field_0x600->getAlpha() == 255) {
if (field_0x5fa != 0) {
field_0x5fa = 0;
}
field_0x604 = g_daTitHIO.field_0x1a;
}
}
if (field_0x604 != 0) {
field_0x604--;
}
}
}
#if VERSION == VERSION_SHIELD_DEBUG
void daTitle_c::KeyWaitPosMove() {
J2DPane* pane = mTitle.Scr->search(MULTI_CHAR('n_all'));
pane->translate(g_daTitHIO.mPSPosX, g_daTitHIO.mPSPosY);
pane->scale(g_daTitHIO.mPSScaleX, g_daTitHIO.mPSScaleY);
}
#endif
void daTitle_c::loadWait_init() {
mProcID = 0;
}
void daTitle_c::loadWait_proc() {
if (mpMount->sync()) {
mpHeap = mDoExt_setCurrentHeap(m2DHeap);
mpFont = mDoExt_getMesgFont();
mTitle.Scr = JKR_NEW J2DScreen();
JUT_ASSERT(529, mTitle.Scr != NULL);
mTitle.Scr->setPriority("zelda_press_start.blo", 0x100000, mpMount->getArchive());
J2DTextBox* text[7];
text[0] = (J2DTextBox*)mTitle.Scr->search(MULTI_CHAR('t_s_00'));
text[1] = (J2DTextBox*)mTitle.Scr->search(MULTI_CHAR('t_s_01'));
text[2] = (J2DTextBox*)mTitle.Scr->search(MULTI_CHAR('t_s_02'));
text[3] = (J2DTextBox*)mTitle.Scr->search(MULTI_CHAR('t_s_03'));
text[4] = (J2DTextBox*)mTitle.Scr->search(MULTI_CHAR('t_s_04'));
text[5] = (J2DTextBox*)mTitle.Scr->search(MULTI_CHAR('t_s_05'));
text[6] = (J2DTextBox*)mTitle.Scr->search('t_o');
for (int i = 0; i < 7; i++) {
text[i]->setFont(mpFont);
text[i]->setString(0x80, "");
char* msg = text[i]->getStringPtr();
fopMsgM_messageGet(msg, 100);
}
field_0x600 = JKR_NEW CPaneMgrAlpha(mTitle.Scr, MULTI_CHAR('n_all'), 2, NULL);
field_0x600->setAlpha(0);
J2DPane* pane = mTitle.Scr->search(MULTI_CHAR('n_all'));
pane->translate(g_daTitHIO.mPSPosX, g_daTitHIO.mPSPosY);
pane->scale(g_daTitHIO.mPSScaleX, g_daTitHIO.mPSScaleY);
JKRSetCurrentHeap(mpHeap);
logoDispWaitInit();
}
}
void daTitle_c::logoDispWaitInit() {
mProcID = 1;
}
void daTitle_c::logoDispWait() {
if (mDoCPd_c::getTrigA(PAD_1) || mDoCPd_c::getTrigStart(PAD_1)) {
fastLogoDispInit();
} else if (getDemoPrm() == 1) {
logoDispAnmInit();
}
}
void daTitle_c::logoDispAnmInit() {
mBck.setPlaySpeed(1.0f);
mBpk.setPlaySpeed(1.0f);
mBrk.setPlaySpeed(1.0f);
mBtk.setPlaySpeed(1.0f);
mIsDispLogo = 1;
mProcID = 2;
}
void daTitle_c::logoDispAnm() {
mBck.play();
mBpk.play();
mBrk.play();
mBtk.play();
if (mBrk.isStop() && mBtk.isStop() && mBck.isStop() && mBpk.isStop()) {
field_0x600->alphaAnimeStart(0);
field_0x604 = 0;
field_0x5f9 = 1;
field_0x5fa = 1;
keyWaitInit();
}
}
void daTitle_c::keyWaitInit() {
mProcID = 3;
}
void daTitle_c::keyWait() {
if (mDoCPd_c::getTrigA(PAD_1) || mDoCPd_c::getTrigStart(PAD_1)) {
mDoAud_seStart(Z2SE_TITLE_ENTER, NULL, 0, 0);
nextScene_init();
}
}
void daTitle_c::nextScene_init() {
mProcID = 4;
}
void daTitle_c::nextScene_proc() {
scene_class* playScene;
if (!fopOvlpM_IsPeek() && !mDoRst::isReset()) {
playScene = fopScnM_SearchByID(dStage_roomControl_c::getProcID());
JUT_ASSERT(706, playScene != NULL);
#if DEBUG
if (!dScnLogo_c::isOpeningCut())
#endif
{
fopScnM_ChangeReq(playScene, fpcNm_NAME_SCENE_e, 0, 5);
}
#if DEBUG
else {
fopScnM_ChangeReq(playScene, fpcNm_MENU_SCENE_e, 0, 5);
dComIfGs_init();
dComIfG_playerStatusD();
}
#endif
#if VERSION != VERSION_SHIELD_DEBUG
mDoGph_gInf_c::setFadeColor(*(JUtility::TColor*)&g_blackColor);
#endif
}
}
void daTitle_c::fastLogoDispInit() {
mBck.setFrame(mBck.getEndFrame() - 1.0f);
mBpk.setFrame(mBpk.getEndFrame() - 1.0f);
mBrk.setFrame(mBrk.getEndFrame() - 1.0f);
mBtk.setFrame(mBtk.getEndFrame() - 1.0f);
field_0x600->alphaAnimeStart(0);
field_0x604 = 0;
mWaitTimer = 30;
mProcID = 5;
#ifdef TARGET_PC
if (dusk::frame_interp::is_enabled()) {
dusk::frame_interp::request_presentation_sync();
}
#endif
}
void daTitle_c::fastLogoDisp() {
if (mWaitTimer != 0) {
mWaitTimer--;
return;
}
field_0x5f9 = 1;
field_0x5fa = 1;
mIsDispLogo = 1;
keyWaitInit();
}
int daTitle_c::getDemoPrm() {
dDemo_actor_c* demoActor = dDemo_c::getActor(demoActorID);
dDemo_prm_c* prm;
if (demoActor != NULL && demoActor->checkEnable(1) && (prm = demoActor->getPrm())) {
JStudio::stb::TParseData_fixed<49> parser(prm->getData());
TValueIterator_raw<u8> iter = parser.begin();
return *iter;
}
return -1;
}
int daTitle_c::Draw() {
J3DModelData* modelData = mpModel->getModelData();
cMtx_trans(mpModel->getBaseTRMtx(), IREG_F(7), IREG_F(8), IREG_F(9) + -430.0f);
mpModel->getBaseScale()->x = -1.0f;
mBck.entry(modelData);
mBpk.entry(modelData);
mBrk.entry(modelData);
mBtk.entry(modelData);
dComIfGd_setListItem3D();
mDoExt_modelUpdateDL(mpModel);
dComIfGd_setList();
if (mIsDispLogo) {
dComIfGd_set2DOpaTop(&mTitle);
}
return 1;
}
int daTitle_c::Delete() {
mDoHIO_DELETE_CHILD(g_daTitHIO.id);
dComIfG_resDelete(&mPhaseReq, l_arcName);
JKR_DELETE(mTitle.Scr);
JKR_DELETE(field_0x600);
mpMount->getArchive()->removeResourceAll();
JKRUnmountArchive(mpMount->getArchive());
mpMount->destroy();
if (m2DHeap != NULL) {
m2DHeap->destroy();
}
return 1;
}
static int daTitle_Draw(daTitle_c* i_this) {
return i_this->Draw();
}
static int daTitle_Execute(daTitle_c* i_this) {
return i_this->Execute();
}
static int daTitle_Delete(daTitle_c* i_this) {
fpc_ProcID id = fopAcM_GetID(i_this);
return i_this->Delete();
}
static int daTitle_Create(fopAc_ac_c* i_this) {
daTitle_c* a_this = (daTitle_c*)i_this;
fpc_ProcID id = fopAcM_GetID(i_this);
return a_this->create();
}
void dDlst_daTitle_c::draw() {
J2DGrafContext* ctx = dComIfGp_getCurrentGrafPort();
Scr->draw(0.0f, 0.0f, ctx);
}
static actor_method_class l_daTitle_Method = {
(process_method_func)daTitle_Create,
(process_method_func)daTitle_Delete,
(process_method_func)daTitle_Execute,
(process_method_func)NULL,
(process_method_func)daTitle_Draw,
};
actor_process_profile_definition g_profile_TITLE = {
/* Layer ID */ fpcLy_CURRENT_e,
/* List ID */ 7,
/* List Prio */ fpcPi_CURRENT_e,
/* Proc Name */ fpcNm_TITLE_e,
/* Proc SubMtd */ &g_fpcLf_Method.base,
/* Size */ sizeof(daTitle_c),
/* Size Other */ 0,
/* Parameters */ 0,
/* Leaf SubMtd */ &g_fopAc_Method.base,
/* Draw Prio */ fpcDwPi_TITLE_e,
/* Actor SubMtd */ &l_daTitle_Method,
/* Status */ fopAcStts_UNK_0x40000_e | fopAcStts_UNK_0x4000_e,
/* Group */ fopAc_ACTOR_e,
/* Cull Type */ fopAc_CULLBOX_CUSTOM_e,
};