From cddcf89f035f5abef942695427109e009f37a06a Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Mon, 8 Sep 2025 21:50:48 -0500 Subject: [PATCH 01/10] separate PlayerSpinState and PlayerData from PlayerStatus --- .gitignore | 1 + lib/libpm-jp.a | 2 ++ lib/libpm-us.a | 2 ++ src/fp.c | 14 +++++------ src/fp/player/fp_player.c | 50 ++++++++++++++++++------------------- src/fp/player/items.c | 6 ++--- src/fp/practice/trainer.c | 2 +- src/menu/menu_item_button.c | 8 +++--- src/pm64.h | 37 +++++++++++++++++---------- src/types.h | 10 ++++++++ 10 files changed, 79 insertions(+), 53 deletions(-) diff --git a/.gitignore b/.gitignore index 7f2152a0..02ab81e7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ venv/ *.bin .ninja_* build.ninja +CMakeLists.txt romc diff --git a/lib/libpm-jp.a b/lib/libpm-jp.a index 0017c803..ebe7fdc7 100644 --- a/lib/libpm-jp.a +++ b/lib/libpm-jp.a @@ -30,6 +30,8 @@ pm_gPopupState = 0x8010D800; pm_gPartnerStatus = 0x8010ED70; pm_gUiStatus = 0x8010F118; pm_gPlayerStatus = 0x8010F188; +pm_gPlayerSpinState = 0x8010F410; +pm_gPlayerData = 0x8010F450; pm_gHudElementSizes = 0x8015406C; pm_MusicCurrentVolume = 0x8015EA66; pm_gActionCommandStatus = 0x8029FED0; diff --git a/lib/libpm-us.a b/lib/libpm-us.a index 5da428e0..89ce6807 100644 --- a/lib/libpm-us.a +++ b/lib/libpm-us.a @@ -30,6 +30,8 @@ pm_gPopupState = 0x8010D640; pm_gPartnerStatus = 0x8010EBB0; pm_gUiStatus = 0x8010EF58; pm_gPlayerStatus = 0x8010EFC8; +pm_gPlayerSpinState = 0x8010F250; +pm_gPlayerData = 0x8010F290; pm_gHudElementSizes = 0x8014EFCC; pm_MusicCurrentVolume = 0x80159AE6; pm_gActionCommandStatus = 0x8029FBE0; diff --git a/src/fp.c b/src/fp.c index cb2ee45e..889c04dd 100644 --- a/src/fp.c +++ b/src/fp.c @@ -299,26 +299,26 @@ void fpDrawTimer(struct GfxFont *font, s32 cellWidth, s32 cellHeight, u8 menuAlp void fpUpdateCheats(void) { pm_gGameStatus.debugEnemyContact = settings->cheatEnemyContact; if (CHEAT_ACTIVE(CHEAT_HP)) { - pm_gPlayerStatus.playerData.curHP = pm_gPlayerStatus.playerData.curMaxHP; + pm_gPlayerData.curHP = pm_gPlayerData.curMaxHP; } if (CHEAT_ACTIVE(CHEAT_FP)) { - pm_gPlayerStatus.playerData.curFP = pm_gPlayerStatus.playerData.curMaxFP; + pm_gPlayerData.curFP = pm_gPlayerData.curMaxFP; } if (CHEAT_ACTIVE(CHEAT_ATTACK)) { pm_gBattleStatus.merleeAttackBoost = 127; } if (CHEAT_ACTIVE(CHEAT_COINS)) { - pm_gPlayerStatus.playerData.coins = 999; + pm_gPlayerData.coins = 999; } if (CHEAT_ACTIVE(CHEAT_STAR_POWER)) { - pm_gPlayerStatus.playerData.starPowerFullBars = pm_gPlayerStatus.playerData.maxStarPower; - pm_gPlayerStatus.playerData.starPowerPartialBars = 0; + pm_gPlayerData.starPowerFullBars = pm_gPlayerData.maxStarPower; + pm_gPlayerData.starPowerPartialBars = 0; } if (CHEAT_ACTIVE(CHEAT_STAR_PIECES)) { - pm_gPlayerStatus.playerData.starPieces = 160; + pm_gPlayerData.starPieces = 160; } if (CHEAT_ACTIVE(CHEAT_PERIL)) { - pm_gPlayerStatus.playerData.curHP = 1; + pm_gPlayerData.curHP = 1; } if (CHEAT_ACTIVE(CHEAT_AUTO_MASH)) { if (pm_gGameStatus.isBattle == 1) { diff --git a/src/fp/player/fp_player.c b/src/fp/player/fp_player.c index 399707a8..61c0bef7 100644 --- a/src/fp/player/fp_player.c +++ b/src/fp/player/fp_player.c @@ -50,8 +50,8 @@ static struct GfxTexture **getItemTextureList(void) { } static s32 maxHpProc(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { - s8 *curMaxHP = &pm_gPlayerStatus.playerData.curMaxHP; - s8 *hardMaxHP = &pm_gPlayerStatus.playerData.hardMaxHP; + s8 *curMaxHP = &pm_gPlayerData.curMaxHP; + s8 *hardMaxHP = &pm_gPlayerData.hardMaxHP; if (reason == MENU_CALLBACK_THINK_INACTIVE) { if (menuIntinputGet(item) != *curMaxHP) { menuIntinputSet(item, *curMaxHP); @@ -69,8 +69,8 @@ static s32 maxHpProc(struct MenuItem *item, enum MenuCallbackReason reason, void } static s32 maxFpProc(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { - s8 *curMaxFP = &pm_gPlayerStatus.playerData.curMaxFP; - s8 *hardMaxFP = &pm_gPlayerStatus.playerData.hardMaxFP; + s8 *curMaxFP = &pm_gPlayerData.curMaxFP; + s8 *hardMaxFP = &pm_gPlayerData.hardMaxFP; if (reason == MENU_CALLBACK_THINK_INACTIVE) { if (menuIntinputGet(item) != *curMaxFP) { menuIntinputSet(item, *curMaxFP); @@ -89,16 +89,16 @@ static s32 maxFpProc(struct MenuItem *item, enum MenuCallbackReason reason, void static s32 currentPartnerProc(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { if (reason == MENU_CALLBACK_THINK_INACTIVE) { - menuOptionSet(item, partnerOrder[pm_gPlayerStatus.playerData.currentPartner]); + menuOptionSet(item, partnerOrder[pm_gPlayerData.currentPartner]); } else if (reason == MENU_CALLBACK_DEACTIVATE) { - pm_gPlayerStatus.playerData.currentPartner = partnerOrder[menuOptionGet(item)]; + pm_gPlayerData.currentPartner = partnerOrder[menuOptionGet(item)]; } return 0; } static s32 bootsProc(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { u32 trackedLevel = (u32)data; - s8 *bootsUpgrade = &pm_gPlayerStatus.playerData.bootsLevel; + s8 *bootsUpgrade = &pm_gPlayerData.bootsLevel; if (reason == MENU_CALLBACK_SWITCH_ON) { *bootsUpgrade = trackedLevel; } else if (reason == MENU_CALLBACK_THINK) { @@ -109,7 +109,7 @@ static s32 bootsProc(struct MenuItem *item, enum MenuCallbackReason reason, void static s32 hammerProc(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { u32 trackedLevel = (u32)data; - s8 *hammerUpgrade = &pm_gPlayerStatus.playerData.hammerLevel; + s8 *hammerUpgrade = &pm_gPlayerData.hammerLevel; if (reason == MENU_CALLBACK_SWITCH_ON) { *hammerUpgrade = trackedLevel; } else if (reason == MENU_CALLBACK_SWITCH_OFF) { @@ -149,8 +149,8 @@ static s32 ultraRankProc(struct MenuItem *item, enum MenuCallbackReason reason, } static s32 starSpiritSwitchProc(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { - s8 *ssSaved = &pm_gPlayerStatus.playerData.maxStarPower; - s16 *starPower = &pm_gPlayerStatus.playerData.starPower; + s8 *ssSaved = &pm_gPlayerData.maxStarPower; + s16 *starPower = &pm_gPlayerData.starPower; s32 ssIndex = (u32)data; if (reason == MENU_CALLBACK_SWITCH_ON) { *ssSaved = ssIndex; @@ -252,7 +252,7 @@ static void createStatsMenu(struct Menu *menu) { s32 hpX = 1; s32 hpY = 2; menuAddStaticIcon(menu, hpX, hpY, texHeart, 0, 0xFFFFFF, 1.0f); - item = menuAddIntinput(menu, hpX + 2, hpY, 10, 2, menuByteModProc, &pm_gPlayerStatus.playerData.curHP); + item = menuAddIntinput(menu, hpX + 2, hpY, 10, 2, menuByteModProc, &pm_gPlayerData.curHP); item->tooltip = strHp; menuAddStatic(menu, hpX + 4, hpY, "/", 0xC0C0C0); item = menuAddIntinput(menu, hpX + 5, hpY, 10, 2, maxHpProc, NULL); @@ -261,7 +261,7 @@ static void createStatsMenu(struct Menu *menu) { s32 fpX = 1; s32 fpY = 4; menuAddStaticIcon(menu, fpX, fpY, texFlower, 0, 0xFFFFFF, 1.0f); - item = menuAddIntinput(menu, fpX + 2, fpY, 10, 2, menuByteModProc, &pm_gPlayerStatus.playerData.curFP); + item = menuAddIntinput(menu, fpX + 2, fpY, 10, 2, menuByteModProc, &pm_gPlayerData.curFP); item->tooltip = strFp; menuAddStatic(menu, fpX + 4, fpY, "/", 0xC0C0C0); item = menuAddIntinput(menu, fpX + 5, fpY, 10, 2, maxFpProc, NULL); @@ -270,40 +270,40 @@ static void createStatsMenu(struct Menu *menu) { s32 bpX = 1; s32 bpY = 6; menuAddStaticIcon(menu, bpX, bpY, texBpIcon, 0, 0xFFFFFF, 1.0f); - item = menuAddIntinput(menu, bpX + 2, bpY, 10, 2, menuByteModProc, &pm_gPlayerStatus.playerData.maxBP); + item = menuAddIntinput(menu, bpX + 2, bpY, 10, 2, menuByteModProc, &pm_gPlayerData.maxBP); item->tooltip = strBp; s32 coinX = 10; s32 coinY = 2; menuAddStaticIcon(menu, coinX, coinY, texCoin, 0, 0xFFFFFF, 1.0f); - item = menuAddIntinput(menu, coinX + 2, coinY, 10, 3, menuHalfwordModProc, &pm_gPlayerStatus.playerData.coins); + item = menuAddIntinput(menu, coinX + 2, coinY, 10, 3, menuHalfwordModProc, &pm_gPlayerData.coins); item->tooltip = strCoins; s32 starPieceX = 10; s32 starPieceY = 4; menuAddStaticIcon(menu, starPieceX, starPieceY, texStarPiece, 0, 0xFFFFFF, 1.0f); item = menuAddIntinput(menu, starPieceX + 2, starPieceY, 10, 3, menuByteModProc, - &pm_gPlayerStatus.playerData.starPieces); + &pm_gPlayerData.starPieces); item->tooltip = strStarPieces; s32 levelX = 17; s32 levelY = 2; menuAddStaticIcon(menu, levelX, levelY, texMarioHead, 0, 0xFFFFFF, 1.0f); - item = menuAddIntinput(menu, levelX + 2, levelY, 10, 2, menuByteModProc, &pm_gPlayerStatus.playerData.level); + item = menuAddIntinput(menu, levelX + 2, levelY, 10, 2, menuByteModProc, &pm_gPlayerData.level); item->tooltip = strLevel; s32 starPointX = 17; s32 starPointY = 4; menuAddStaticIcon(menu, starPointX, starPointY, texStarPoint, 0, 0xFFFFFF, 1.0f); item = menuAddIntinput(menu, starPointX + 2, starPointY, 10, 2, menuByteModProc, - &pm_gPlayerStatus.playerData.starPoints); + &pm_gPlayerData.starPoints); item->tooltip = strStarPoints; s32 actionCommandX = 23; s32 actionCommandY = 2; item = menuAddSwitch(menu, actionCommandX, actionCommandY, texLuckyStar, 0, 0, 0xFFFFFF, texLuckyStar, 0, 1, 0xFFFFFF, - 0.7f, FALSE, menuByteSwitchToggleProc, &pm_gPlayerStatus.playerData.hasActionCommands); + 0.7f, FALSE, menuByteSwitchToggleProc, &pm_gPlayerData.hasActionCommands); item->tooltip = strActionCommands; } @@ -348,19 +348,19 @@ static void createPartyMenu(struct Menu *menu) { partners[i] = menuAddSwitch(menu, partnerX, partnerY, texPartner, i + 1, 0, 0xFFFFFF, texPartner, i + 1, 1, 0xFFFFFF, scale, FALSE, menuByteSwitchToggleProc, - &pm_gPlayerStatus.playerData.partners[partnerOrder[i + 1]].enabled); + &pm_gPlayerData.partners[partnerOrder[i + 1]].enabled); partners[i]->tooltip = strPartnerNames[i]; // super tex superRanks[i] = menuAddSwitch(menu, partnerX + 2, partnerY, texRank, 0, 0, 0xFFFFFF, texRank, 0, 1, 0xFFFFFF, scale, FALSE, - superRankProc, &pm_gPlayerStatus.playerData.partners[partnerOrder[i + 1]]); + superRankProc, &pm_gPlayerData.partners[partnerOrder[i + 1]]); superRanks[i]->tooltip = strSuperRank; // ultra tex ultraRanks[i] = menuAddSwitch(menu, partnerX + 3, partnerY, texRank, 0, 0, 0xFFFFFF, texRank, 0, 1, 0xFFFFFF, scale, FALSE, - ultraRankProc, &pm_gPlayerStatus.playerData.partners[partnerOrder[i + 1]]); + ultraRankProc, &pm_gPlayerData.partners[partnerOrder[i + 1]]); ultraRanks[i]->tooltip = strUltraRank; } menuItemAddChainLink(activeItem, partners[0], MENU_NAVIGATE_DOWN); @@ -399,7 +399,7 @@ static void createStarSpiritMenu(struct Menu *menu) { s8 beamPalettes[] = {1, 0, 0}; u32 beamColors[] = {0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; struct MenuItem *item = menuAddCycle(menu, ssX, ssY, 3, beamTextures, beamTiles, beamPalettes, beamColors, scale, - FALSE, menuByteCycleProc, &pm_gPlayerStatus.playerData.starBeamLevel); + FALSE, menuByteCycleProc, &pm_gPlayerData.starBeamLevel); item->tooltip = strStarPeachBeam; menuItemCreateChain(starSpirits, 8, MENU_NAVIGATE_RIGHT, FALSE, FALSE); menuItemCreateChain(starSpirits, 8, MENU_NAVIGATE_LEFT, FALSE, TRUE); @@ -438,13 +438,13 @@ static void createMerleeMenu(struct Menu *menu) { "+3 DEF\0" "EXP x2\0" "Coins x2\0", - menuByteOptionmodProc, &pm_gPlayerStatus.playerData.merleeSpellType); + menuByteOptionmodProc, &pm_gPlayerData.merleeSpellType); menuAddStatic(menu, 0, yValue, "casts remaining", 0xC0C0C0); - menuAddIntinput(menu, 16, yValue++, 10, 2, menuByteModProc, &pm_gPlayerStatus.playerData.merleeCastsRemaining); + menuAddIntinput(menu, 16, yValue++, 10, 2, menuByteModProc, &pm_gPlayerData.merleeCastsRemaining); menuAddStatic(menu, 0, yValue, "turns remaining", 0xC0C0C0); - menuAddIntinput(menu, 16, yValue++, 10, 3, menuHalfwordModProc, &pm_gPlayerStatus.playerData.merleeTurnCount); + menuAddIntinput(menu, 16, yValue++, 10, 3, menuHalfwordModProc, &pm_gPlayerData.merleeTurnCount); } struct Menu *createPlayerMenu(void) { diff --git a/src/fp/player/items.c b/src/fp/player/items.c index c292ed92..033c95a9 100644 --- a/src/fp/player/items.c +++ b/src/fp/player/items.c @@ -442,7 +442,7 @@ static s16 *itemSlotToUpdate; static s32 badgeProcSwitch(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { u32 badgeId = (u32)data; - s16 *badgeList = pm_gPlayerStatus.playerData.badges; + s16 *badgeList = pm_gPlayerData.badges; if (reason == MENU_CALLBACK_SWITCH_ON) { for (u16 i = 0; i < 128; i++) { if (badgeList[i] == 0) { @@ -457,8 +457,8 @@ static s32 badgeProcSwitch(struct MenuItem *item, enum MenuCallbackReason reason } } for (u16 i = 0; i < 64; i++) { - if (pm_gPlayerStatus.playerData.equippedBadges[i] == badgeId) { - pm_gPlayerStatus.playerData.equippedBadges[i] = 0; + if (pm_gPlayerData.equippedBadges[i] == badgeId) { + pm_gPlayerData.equippedBadges[i] = 0; } } } else if (reason == MENU_CALLBACK_THINK) { diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index cbf34a88..d180d557 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -520,7 +520,7 @@ static void updateBlockTrainer(void) { } // Either goombario or mario attacking - if ((pm_gBattleSubState == 3 && pm_gPlayerStatus.playerData.currentPartner == PARTNER_GOOMBARIO) || + if ((pm_gBattleSubState == 3 && pm_gPlayerData.currentPartner == PARTNER_GOOMBARIO) || pm_gBattleSubState == 4) { if (pm_gActionCommandStatus.state == 10 && pm_gGameStatus.pressedButtons[0].a) { acLastAPress = pm_gGameStatus.frameCounter; diff --git a/src/menu/menu_item_button.c b/src/menu/menu_item_button.c index a0d16b0f..1f60646a 100644 --- a/src/menu/menu_item_button.c +++ b/src/menu/menu_item_button.c @@ -28,11 +28,11 @@ static s16 getEmptyIcon(enum ItemType type) { static s16 *getItemSlot(enum ItemType type, s16 itemIndex) { switch (type) { - case ITEM_TYPE_NORMAL: return &pm_gPlayerStatus.playerData.invItems[itemIndex]; - case ITEM_TYPE_KEY: return &pm_gPlayerStatus.playerData.keyItems[itemIndex]; - case ITEM_TYPE_STORED: return &pm_gPlayerStatus.playerData.storedItems[itemIndex]; + case ITEM_TYPE_NORMAL: return &pm_gPlayerData.invItems[itemIndex]; + case ITEM_TYPE_KEY: return &pm_gPlayerData.keyItems[itemIndex]; + case ITEM_TYPE_STORED: return &pm_gPlayerData.storedItems[itemIndex]; case ITEM_TYPE_BADGE: - default: return &pm_gPlayerStatus.playerData.badges[itemIndex]; + default: return &pm_gPlayerData.badges[itemIndex]; } } diff --git a/src/pm64.h b/src/pm64.h index 5e9a6aca..8cd7e7ff 100644 --- a/src/pm64.h +++ b/src/pm64.h @@ -432,19 +432,28 @@ typedef struct pm_PlayerStatus { /* 0x168 */ s32 stickAxisXBuffer[10]; /* 0x190 */ s32 stickAxisYBuffer[10]; /* 0x1B8 */ s32 inputBufPos; - /* 0x1BC */ char unk_0x1BC[0xCC]; - /* 0x288 */ u8 spinCooldownTimer; /*4 frames at the end of spin*/ - /* 0x289 */ char unk_0x289[0x02]; - /* 0x28B */ u8 spinTimer; - /* 0x28C */ char unk_0x28C[0x20]; - /* 0x2AC */ f32 spinSpeed; - /* 0x2B0 */ char unk_0x2B0[0x04]; - /* 0x2B4 */ char unk_0x2B4[0x01]; - /* 0x2B5 */ u8 spinDuration; - /* 0x2B6 */ char unk_0x228[0x02]; - /* 0x2B8 */ char unk_0x2B8[0x10]; - /* 0x2C8 */ pm_PlayerData playerData; -} pm_PlayerStatus; // size = 0x6F0 + /* 0x1BC */ char unk_0x1BC[196]; + /* 0x280 */ s8 poundImpactDelay; // governs period of immobility after landing a ground pound + /* 0x281 */ char unk_281[7]; +} pm_PlayerStatus; // size = 0x288 + +typedef struct pm_PlayerSpinState { + /* 0x00 */ s8 stopSoundTimer; + /* 0x01 */ s8 hasBufferedSpin; + /* 0x02 */ s8 hitWallTime; // incremented while blocked by a wall + /* 0x03 */ s8 spinCountdown; + /* 0x04 */ s32 prevActionState; + /* 0x08 */ Vec2i bufferedStickAxis; + /* 0x10 */ f32 spinDirectionMagnitude; + /* 0x14 */ Vec2f spinDirection; + /* 0x1C */ f32 inputMagnitude; + /* 0x20 */ f32 spinRate; + /* 0x24 */ f32 speedScale; + /* 0x28 */ f32 frictionScale; + /* 0x2C */ s16 initialSpinTime; + /* 0x2E */ s16 fullSpeedSpinTime; + /* 0x30 */ s32 spinSoundID; +} pm_PlayerSpinState; // size = 0x34 typedef struct pm_SaveMetadata { /* 0x00 */ s32 timePlayed; @@ -1381,6 +1390,8 @@ extern_data s32 pm_gPopupState; extern_data pm_PartnerStatus pm_gPartnerStatus; extern_data pm_UiStatus pm_gUiStatus; extern_data pm_PlayerStatus pm_gPlayerStatus; +extern_data pm_PlayerSpinState pm_gPlayerSpinState; +extern_data pm_PlayerData pm_gPlayerData; extern_data pm_HudElementSize pm_gHudElementSizes[26]; extern_data s16 pm_MusicCurrentVolume; extern_data pm_ActionCommandStatus pm_gActionCommandStatus; diff --git a/src/types.h b/src/types.h index ac74024b..e029cb3e 100644 --- a/src/types.h +++ b/src/types.h @@ -46,6 +46,16 @@ typedef struct { /* 0x04 */ s16 z; } Vec3s; // size = 0x06 +typedef struct Vec2i { + /* 0x00 */ s32 x; + /* 0x04 */ s32 y; +} Vec2i; // size = 0x08 + +typedef struct Vec2f { + /* 0x00 */ f32 x; + /* 0x04 */ f32 y; +} Vec2f; // size = 0x08 + typedef f32 Matrix4f[4][4]; // size = 0x40 typedef struct Matrix4s { From d725f5baeac74c1d1f7c58b60437305bbbde569c Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Wed, 26 Nov 2025 02:59:25 -0600 Subject: [PATCH 02/10] implement initial spin trainer --- src/enums.h | 106 ++++++++++++++++++++++++++ src/fp/player/fp_player.c | 21 ++---- src/fp/practice/trainer.c | 153 +++++++++++++++++++++++++++++++++++++- src/fp/practice/trainer.h | 1 + src/pm64.h | 8 +- 5 files changed, 270 insertions(+), 19 deletions(-) diff --git a/src/enums.h b/src/enums.h index f285f662..5cb28c81 100644 --- a/src/enums.h +++ b/src/enums.h @@ -1,6 +1,65 @@ #ifndef ENUMS_H #define ENUMS_H +enum Abilities { + ABILITY_DODGE_MASTER = 0x00000000, + ABILITY_UNUSED = 0x00000001, + ABILITY_SPIKE_SHIELD = 0x00000002, + ABILITY_FIRST_ATTACK = 0x00000003, + ABILITY_HP_PLUS = 0x00000004, + ABILITY_DOUBLE_DIP = 0x00000005, + ABILITY_MYSTERY_SCROLL = 0x00000006, + ABILITY_FIRE_SHIELD = 0x00000007, + ABILITY_PRETTY_LUCKY = 0x00000008, + ABILITY_HP_DRAIN = 0x00000009, + ABILITY_ALL_OR_NOTHING = 0x0000000A, + ABILITY_SLOW_GO = 0x0000000B, + ABILITY_FP_PLUS = 0x0000000C, + ABILITY_ICE_POWER = 0x0000000D, + ABILITY_FEELING_FINE = 0x0000000E, + ABILITY_ATTACK_FX = 0x0000000F, + ABILITY_MONEY_MONEY = 0x00000010, + ABILITY_CHILL_OUT = 0x00000011, + ABILITY_HAPPY_HEART = 0x00000012, + ABILITY_ZAP_TAP = 0x00000013, + ABILITY_MEGA_RUSH = 0x00000014, + ABILITY_BERSERKER = 0x00000015, + ABILITY_RIGHT_ON = 0x00000016, + ABILITY_RUNAWAY_PAY = 0x00000017, + ABILITY_FLOWER_SAVER = 0x00000018, + ABILITY_PAY_OFF = 0x00000019, + ABILITY_QUICK_CHANGE = 0x0000001A, + ABILITY_DEFEND_PLUS = 0x0000001B, + ABILITY_POWER_PLUS = 0x0000001C, + ABILITY_REFUND = 0x0000001D, + ABILITY_POWER_RUSH = 0x0000001E, + ABILITY_CRAZY_HEART = 0x0000001F, + ABILITY_LAST_STAND = 0x00000020, + ABILITY_CLOSE_CALL = 0x00000021, + ABILITY_P_UP_D_DOWN = 0x00000022, + ABILITY_LUCKY_DAY = 0x00000023, + ABILITY_MEGA_HP_DRAIN = 0x00000024, + ABILITY_P_DOWN_D_UP = 0x00000025, + ABILITY_FLOWER_FANATIC = 0x00000026, + ABILITY_SPEEDY_SPIN = 0x00000027, + ABILITY_SPIN_ATTACK = 0x00000028, + ABILITY_I_SPY = 0x00000029, + ABILITY_BUMP_ATTACK = 0x0000002A, + ABILITY_HEART_FINDER = 0x0000002B, + ABILITY_FLOWER_FINDER = 0x0000002C, + ABILITY_DIZZY_ATTACK = 0x0000002D, + ABILITY_FINAL_GOOMPA = 0x0000002E, + ABILITY_FINAL_BOBOMB = 0x0000002F, + ABILITY_DEEP_FOCUS = 0x00000030, + ABILITY_SUPER_FOCUS = 0x00000031, + ABILITY_KAIDEN = 0x00000032, + ABILITY_DAMAGE_DODGE = 0x00000033, + ABILITY_HAPPY_FLOWER = 0x00000034, + ABILITY_GROUP_FOCUS = 0x00000035, + ABILITY_PEEKABOO = 0x00000036, + ABILITY_HEALTHY_HEALTHY = 0x00000037, +}; + enum ActionStates { ACTION_STATE_IDLE, ACTION_STATE_WALK, @@ -881,4 +940,51 @@ enum PlayerStatusFlags { PS_FLAG_ACTION_STATE_CHANGED = 0x80000000, }; +enum PlayerStatusAnimFlags { + /* Whether Mario is in the process of using Watt (but isn't necessarily holding them yet) */ + PA_FLAG_USING_WATT = 0x00000001, + /* Whether Watt is actually in Mario's hands at the moment */ + PA_FLAG_WATT_IN_HANDS = 0x00000002, + PA_FLAG_INTERRUPT_USE_PARTNER = + 0x00000004, ///< forces actions with bow, parakarry, watt, and lakilester to end (sushie not tested) + PA_FLAG_FORCE_USE_PARTNER = 0x00000008, ///< triggers partner use when set + PA_FLAG_INTERACT_PROMPT_AVAILABLE = 0x00000010, ///< ! prompt + PA_FLAG_SPEECH_PROMPT_AVAILABLE = 0x00000020, ///< (...) prompt + PA_FLAG_PULSE_STONE_VISIBLE = 0x00000040, ///< The pulse stone icon is being shown + PA_FLAG_USING_PULSE_STONE = 0x00000080, + PA_FLAG_ISPY_VISIBLE = 0x00000100, ///< The I Spy icon is being shown + PA_FLAG_RAISED_ARMS = 0x00000200, ///< Sets action state to ACTION_STATE_RAISE_ARMS on idle + PA_FLAG_SHIVERING = 0x00000400, + PA_FLAG_OPENED_HIDDEN_PANEL = 0x00000800, + PA_FLAG_USING_PEACH_PHYSICS = 0x00001000, + PA_FLAG_INVISIBLE = 0x00002000, + PA_FLAG_8BIT_MARIO = 0x00004000, + PA_FLAG_NPC_COLLIDED = 0x00008000, + PA_FLAG_SPINNING = 0x00010000, + /* Began an encounter by spinning into an enemy with the Dizzy Attack badge on */ + PA_FLAG_DIZZY_ATTACK_ENCOUNTER = 0x00020000, + PA_FLAG_INTERRUPT_SPIN = 0x00040000, + /* When Mario is in a transition to a new map, either through a loading zone or pipe */ + PA_FLAG_CHANGING_MAP = 0x00100000, + /* Occurs after PA_FLAG_FORCE_USE_PARTNER. Some partners - namely Bow and Lakilester, unset this immediately. + Not sure why - seems like it might contribute to being unable to *stop* using your partner during a cutscene. */ + PA_FLAG_PARTNER_USAGE_FORCED = 0x00200000, + PA_FLAG_RIDING_PARTNER = 0x00400000, + PA_FLAG_ABORT_PUSHING_BLOCK = 0x00800000, + /* Changes how Mario is rendered. Seems to be intended to make Mario's depth render properly when using Bow behind a + * switch (two translucent objects on top of eachother), but it doesn't actually work. */ + PA_FLAG_MAP_HAS_SWITCH = 0x01000000, + /* Usually, if Mario falls for too long, he eventually gets reset to his last safe position. This prevents that. + * Used by some scripts. */ + PA_FLAG_NO_OOB_RESPAWN = 0x10000000, + /* This allows dismounting from Lakilester, even if in a precarious situation (like over spikes, lava, or water). */ + PA_FLAG_DISMOUNTING_ALLOWED = 0x20000000, + /* This flag is set when partner usage was interrupted by a script, and it prevents menu sounds (like the error + * sound) from playing for script-initiated player actions */ + PA_FLAG_FORCED_PARTNER_ABILITY_END = 0x40000000, + /* This one's really weird. Seems to have something to do with the direction Mario is facing, but I'm not sure what + * it's actually supposed to be achieving. */ + PA_FLAG_80000000 = 0x80000000, +}; + #endif diff --git a/src/fp/player/fp_player.c b/src/fp/player/fp_player.c index 61c0bef7..5286ef45 100644 --- a/src/fp/player/fp_player.c +++ b/src/fp/player/fp_player.c @@ -282,8 +282,7 @@ static void createStatsMenu(struct Menu *menu) { s32 starPieceX = 10; s32 starPieceY = 4; menuAddStaticIcon(menu, starPieceX, starPieceY, texStarPiece, 0, 0xFFFFFF, 1.0f); - item = menuAddIntinput(menu, starPieceX + 2, starPieceY, 10, 3, menuByteModProc, - &pm_gPlayerData.starPieces); + item = menuAddIntinput(menu, starPieceX + 2, starPieceY, 10, 3, menuByteModProc, &pm_gPlayerData.starPieces); item->tooltip = strStarPieces; s32 levelX = 17; @@ -295,15 +294,13 @@ static void createStatsMenu(struct Menu *menu) { s32 starPointX = 17; s32 starPointY = 4; menuAddStaticIcon(menu, starPointX, starPointY, texStarPoint, 0, 0xFFFFFF, 1.0f); - item = menuAddIntinput(menu, starPointX + 2, starPointY, 10, 2, menuByteModProc, - &pm_gPlayerData.starPoints); + item = menuAddIntinput(menu, starPointX + 2, starPointY, 10, 2, menuByteModProc, &pm_gPlayerData.starPoints); item->tooltip = strStarPoints; s32 actionCommandX = 23; s32 actionCommandY = 2; - item = - menuAddSwitch(menu, actionCommandX, actionCommandY, texLuckyStar, 0, 0, 0xFFFFFF, texLuckyStar, 0, 1, 0xFFFFFF, - 0.7f, FALSE, menuByteSwitchToggleProc, &pm_gPlayerData.hasActionCommands); + item = menuAddSwitch(menu, actionCommandX, actionCommandY, texLuckyStar, 0, 0, 0xFFFFFF, texLuckyStar, 0, 1, + 0xFFFFFF, 0.7f, FALSE, menuByteSwitchToggleProc, &pm_gPlayerData.hasActionCommands); item->tooltip = strActionCommands; } @@ -352,15 +349,13 @@ static void createPartyMenu(struct Menu *menu) { partners[i]->tooltip = strPartnerNames[i]; // super tex - superRanks[i] = - menuAddSwitch(menu, partnerX + 2, partnerY, texRank, 0, 0, 0xFFFFFF, texRank, 0, 1, 0xFFFFFF, scale, FALSE, - superRankProc, &pm_gPlayerData.partners[partnerOrder[i + 1]]); + superRanks[i] = menuAddSwitch(menu, partnerX + 2, partnerY, texRank, 0, 0, 0xFFFFFF, texRank, 0, 1, 0xFFFFFF, + scale, FALSE, superRankProc, &pm_gPlayerData.partners[partnerOrder[i + 1]]); superRanks[i]->tooltip = strSuperRank; // ultra tex - ultraRanks[i] = - menuAddSwitch(menu, partnerX + 3, partnerY, texRank, 0, 0, 0xFFFFFF, texRank, 0, 1, 0xFFFFFF, scale, FALSE, - ultraRankProc, &pm_gPlayerData.partners[partnerOrder[i + 1]]); + ultraRanks[i] = menuAddSwitch(menu, partnerX + 3, partnerY, texRank, 0, 0, 0xFFFFFF, texRank, 0, 1, 0xFFFFFF, + scale, FALSE, ultraRankProc, &pm_gPlayerData.partners[partnerOrder[i + 1]]); ultraRanks[i]->tooltip = strUltraRank; } menuItemAddChainLink(activeItem, partners[0], MENU_NAVIGATE_DOWN); diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index d180d557..995036ec 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -5,6 +5,8 @@ #include "sys/resource.h" #include "sys/settings.h" #include +#include +#include enum BowserVariant { BOWSER_VARIANT_HALLWAY = 0xC1, @@ -76,6 +78,17 @@ static u16 acLastValidFrame = 0; static u16 clippyFramesSinceBattle = 0; static u8 clippyStatus = 0; +// spin trainer vars +static enum ActionStates spinPrevActionState = ACTION_STATE_IDLE; +static bool spinPrevCanSpin = FALSE; +static s8 spinCanceled = FALSE; +static s8 spinEndTiming = 0; +static bool spinIsJumping = FALSE; +static u16 spinJumpStart = 0; +static s16 spinJumpLast = 1; +static u16 spinDelayStart = 0; +static u16 spinDelayLast = 0; + extern void setACEHook(void); s32 getMatrixTotal(void) { @@ -166,6 +179,129 @@ static s32 lzsDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) return 1; } +static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { + enum ActionStates actionState = pm_gPlayerStatus.actionState; + pm_PlayerSpinState *spinState = &pm_gPlayerSpinState; + + // end timing and jump length + if (spinPrevActionState == ACTION_STATE_SPIN) { + if (actionState == ACTION_STATE_JUMP || actionState == ACTION_STATE_HAMMER) { + spinCanceled = FALSE; + s8 perfectEndTiming = 7; + if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { + perfectEndTiming = 8; + } + spinEndTiming = spinState->spinCountdown - perfectEndTiming; + + if (pm_gPlayerStatus.actionState == ACTION_STATE_JUMP) { + spinIsJumping = TRUE; + spinJumpStart = pm_gGameStatus.frameCounter; + } + } else if (actionState != ACTION_STATE_SPIN) { + spinCanceled = TRUE; + } + } else if (spinIsJumping && (!pm_gGameStatus.currentButtons[0].a || actionState != ACTION_STATE_JUMP)) { + spinIsJumping = FALSE; + spinJumpLast = pm_gGameStatus.frameCounter - spinJumpStart; + } + + // delay between spins + bool canSpin = !(pm_gPlayerStatus.flags & (PS_FLAG_NO_STATIC_COLLISION | PS_FLAG_CUTSCENE_MOVEMENT | + PS_FLAG_INPUT_DISABLED | PS_FLAG_PAUSED) || + pm_gPlayerStatus.animFlags & (PA_FLAG_USING_WATT | PA_FLAG_SPINNING) || + pm_is_ability_active(ABILITY_SLOW_GO)) && + actionState < ACTION_STATE_JUMP; + if (canSpin && !spinPrevCanSpin) { + spinDelayStart = pm_gGameStatus.frameCounter; + if (!(actionState == ACTION_STATE_RUN && pm_gPlayerStatus.prevActionState == ACTION_STATE_WALK && + spinPrevActionState == ACTION_STATE_IDLE)) { + // note that there are a lot of rare edge cases here that just aren't worth handling + spinDelayStart += 1; + } + } + if (actionState == ACTION_STATE_SPIN && spinState->initialSpinTime - spinState->spinCountdown == 1) { + // start of new spin + if (!spinPrevCanSpin) { + spinDelayLast = 0; + } else { + spinDelayLast = pm_gGameStatus.frameCounter - spinDelayStart; + } + } + spinPrevActionState = actionState; + spinPrevCanSpin = canSpin; + + // begin drawing + chHeight += 1; + s32 menuY = 0; + s32 menuXOffset = x + chWidth * 13; + u32 colorGreen = GPACK_RGB24A8(0x00FF00, 0xFF); + u32 colorYellow = GPACK_RGB24A8(0xFFFF00, 0xFF); + u32 colorRed = GPACK_RGB24A8(0xFF0000, 0xFF); + gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + + gfxPrintf(font, x, y + chHeight * menuY++, "end timing:"); + gfxPrintf(font, x, y + chHeight * menuY++, "jump frames:"); + gfxPrintf(font, x, y + chHeight * menuY++, "start delay:"); + menuY = 0; + + // end timing + if (spinCanceled) { + gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "canceled"); + } else if (spinEndTiming == 0) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "good"); + } else if (spinEndTiming > 0) { + if (spinEndTiming == 1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d early", spinEndTiming); + } else if (spinEndTiming < 0) { + if (spinEndTiming == -1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + if (spinEndTiming > -8) { + gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d late", abs(spinEndTiming)); + } else { + gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "8+ late"); + } + } + + // jump frames + if (spinCanceled) { + gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "canceled"); + } else { + if (spinJumpLast == 1) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + } else if (spinJumpLast == 2) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d", spinJumpLast); + } + + // start delay + if (spinDelayLast == 0) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + } else if (spinDelayLast == 1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d", spinDelayLast); +} + +static s32 spinDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) { + spinDraw(drawParams->x, drawParams->y, drawParams->font, menuGetCellWidth(item->owner, TRUE), + menuGetCellHeight(item->owner, TRUE), drawParams->color, drawParams->alpha); + return 1; +} + static void issDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); @@ -680,6 +816,7 @@ void trainerUpdate(void) { void trainerDrawPinned(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { switch (settings->pinnedTrainer) { case TRAINER_LZS: lzsDraw(x, y, font, chWidth, chHeight, color, alpha); break; + case TRAINER_SPIN: spinDraw(x, y, font, chWidth, chHeight, color, alpha); break; case TRAINER_ISS: issDraw(x, y, font, chWidth, chHeight, color, alpha); break; case TRAINER_ACE: aceDraw(x, y, font, chWidth, chHeight, color, alpha); break; } @@ -687,12 +824,14 @@ void trainerDrawPinned(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHe void createTrainerMenu(struct Menu *menu) { static struct Menu lzsMenu; + static struct Menu spinMenu; static struct Menu issMenu; static struct Menu aceMenu; /* initialize menu */ menuInit(menu, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); menuInit(&lzsMenu, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); + menuInit(&spinMenu, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); menuInit(&issMenu, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); menuInit(&aceMenu, MENU_NOVALUE, MENU_NOVALUE, MENU_NOVALUE); @@ -731,8 +870,10 @@ void createTrainerMenu(struct Menu *menu) { menuAddCheckbox(menu, xOffset, y++, menuByteCheckboxProc, &settings->trainerHammerCancelsEnabled); menuAddStatic(menu, 0, y, "clippy", 0xC0C0C0); - struct MenuItem *lastOption = - menuAddCheckbox(menu, xOffset, y++, menuByteCheckboxProc, &settings->trainerClippyEnabled); + menuAddCheckbox(menu, xOffset, y++, menuByteCheckboxProc, &settings->trainerClippyEnabled); + + menuAddStatic(menu, 0, y, "spin", 0xC0C0C0); + struct MenuItem *lastOption = menuAddSubmenuIcon(menu, xOffset, y++, &spinMenu, wrench, 0, 0, 1.0f); #if PM64_VERSION == JP menuAddStatic(menu, 0, y, "ice staircase skip", 0xC0C0C0); menuAddSubmenuIcon(menu, xOffset, y++, &issMenu, wrench, 0, 0, 1.0f); @@ -755,6 +896,14 @@ void createTrainerMenu(struct Menu *menu) { y++; menuAddStaticCustom(&lzsMenu, 0, y++, lzsDrawProc, NULL, 0xC0C0C0); + /*build spin menu*/ + y = 0; + spinMenu.selector = menuAddSubmenu(&spinMenu, 0, y++, NULL, "return"); + menuAddStatic(&spinMenu, 0, y, "pin", 0xC0C0C0); + menuAddCheckbox(&spinMenu, 4, y++, pinnedTrainerProc, (void *)TRAINER_SPIN); + y++; + menuAddStaticCustom(&spinMenu, 0, y++, spinDrawProc, NULL, 0xC0C0C0); + /*build iss menu*/ y = 0; issMenu.selector = menuAddSubmenu(&issMenu, 0, y++, NULL, "return"); diff --git a/src/fp/practice/trainer.h b/src/fp/practice/trainer.h index e3c994c0..f01ee56f 100644 --- a/src/fp/practice/trainer.h +++ b/src/fp/practice/trainer.h @@ -4,6 +4,7 @@ enum PinnedTrainer { TRAINER_LZS, + TRAINER_SPIN, TRAINER_ISS, TRAINER_ACE, }; diff --git a/src/pm64.h b/src/pm64.h index 8cd7e7ff..c1271e21 100644 --- a/src/pm64.h +++ b/src/pm64.h @@ -403,9 +403,9 @@ typedef struct pm_PlayerStatus { /* 0x0AC */ char unk_0xAC[4]; /* 0x0B0 */ s16 colliderHeight; /* 0x0B2 */ s16 colliderDiameter; - /* 0x0B4 */ u8 actionState; - /* 0x0B5 */ u8 prevActionState; - /* 0x0B6 */ s8 fallState; /*also used as sleep state in Peach idle action*/ + /* 0x0B4 */ s8 actionState; + /* 0x0B5 */ s8 prevActionState; + /* 0x0B6 */ s8 actionSubstate; /* 0x0B7 */ char unk_B7; /* 0x0B8 */ u32 anim; /* 0x0BC */ u16 unk_BC; @@ -1443,7 +1443,7 @@ s32 pm_player_raycast_general(s32 mode, f32 startX, f32 startY, f32 startZ, f32 f32 *hitY, f32 *hitZ, f32 *hitDepth, f32 *hitNx, f32 *hitNy, f32 *hitNz); void pm_disable_player_input(void); void pm_update_player_input(void); -s32 pm_is_ability_active(s32 arg0); +s32 pm_is_ability_active(enum Abilities ability); void pm_hide_popup_menu(void); void pm_destroy_popup_menu(void); void pm_state_render_frontUI(void); From c7a27dda256eb05b3e594bef7e51fc118ab67318 Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Fri, 28 Nov 2025 00:32:32 -0600 Subject: [PATCH 03/10] add timing bar --- src/fp/practice/trainer.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index 995036ec..894da7aa 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -294,6 +294,45 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig gfxModeSet(GFX_MODE_COLOR, colorRed); } gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d", spinDelayLast); + + // draw bar + u32 colorBackground = GPACK_RGB24A8(0x000000, alpha); + u32 colorEmpty = GPACK_RGB24A8(0x000095, alpha); + u32 colorFull = GPACK_RGB24A8(0x00E486, alpha); + u8 barMult = 3; + s8 spinFrame = 25 - pm_gPlayerSpinState.spinCountdown; + u16 barWidth = 25; + u16 barGoal = 18; + if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { + spinFrame = 30 - pm_gPlayerSpinState.spinCountdown; + barWidth = 30; + barGoal = 22; + } + barWidth *= barMult; + barGoal *= barMult; + u16 barX = SCREEN_WIDTH / 2 - barWidth / 2; + u16 barGoalX = barX + barGoal; + u16 barFullX = barX + spinFrame * barMult; + u16 barYTop = SCREEN_HEIGHT - 75; + u16 barYBottom = barYTop + 10; + + gfxModeReplace(GFX_MODE_COMBINE, G_CC_MODE(G_CC_PRIMITIVE, G_CC_PRIMITIVE)); + // background + gfxModeSet(GFX_MODE_COLOR, colorBackground); + gfxDisp(gsSPScisTextureRectangle(qs102(barX - 1), qs102(barYTop - 1), qs102(barX + barWidth + 1), + qs102(barYBottom + 1), 0, 0, 0, 0, 0)); + // empty bar + gfxModeSet(GFX_MODE_COLOR, colorEmpty); + gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(barYTop), qs102(barX + barWidth), qs102(barYBottom), 0, 0, 0, 0, + 0)); + // full bar + gfxModeSet(GFX_MODE_COLOR, colorFull); + gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(barYTop), qs102(barFullX), qs102(barYBottom), 0, 0, 0, 0, 0)); + // goal marker + gfxModeSet(GFX_MODE_COLOR, colorYellow); + gfxDisp(gsSPScisTextureRectangle(qs102(barGoalX - 1), qs102(barYTop), qs102(barGoalX + 1), qs102(barYBottom), 0, 0, + 0, 0, 0)); + gfxModePop(GFX_MODE_COMBINE); } static s32 spinDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) { From 0fabc781cb69f7d465965022006f9a0ab3da50e3 Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Sat, 29 Nov 2025 00:03:58 -0600 Subject: [PATCH 04/10] use images for spin trainer --- src/fp/practice/trainer.c | 170 +++++++++++++++++++++----------------- src/pm64.h | 2 + src/sys/resource.c | 17 ++-- src/sys/resource.h | 1 + 4 files changed, 108 insertions(+), 82 deletions(-) diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index 894da7aa..ff245955 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -88,6 +88,8 @@ static u16 spinJumpStart = 0; static s16 spinJumpLast = 1; static u16 spinDelayStart = 0; static u16 spinDelayLast = 0; +static struct GfxTexture *spinJumpTex = NULL; +static struct GfxTexture *spinDelayTex = NULL; extern void setACEHook(void); @@ -182,13 +184,14 @@ static s32 lzsDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { enum ActionStates actionState = pm_gPlayerStatus.actionState; pm_PlayerSpinState *spinState = &pm_gPlayerSpinState; + bool isSpeedy = pm_is_ability_active(ABILITY_SPEEDY_SPIN); // end timing and jump length if (spinPrevActionState == ACTION_STATE_SPIN) { if (actionState == ACTION_STATE_JUMP || actionState == ACTION_STATE_HAMMER) { spinCanceled = FALSE; s8 perfectEndTiming = 7; - if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { + if (isSpeedy) { perfectEndTiming = 8; } spinEndTiming = spinState->spinCountdown - perfectEndTiming; @@ -208,8 +211,7 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig // delay between spins bool canSpin = !(pm_gPlayerStatus.flags & (PS_FLAG_NO_STATIC_COLLISION | PS_FLAG_CUTSCENE_MOVEMENT | PS_FLAG_INPUT_DISABLED | PS_FLAG_PAUSED) || - pm_gPlayerStatus.animFlags & (PA_FLAG_USING_WATT | PA_FLAG_SPINNING) || - pm_is_ability_active(ABILITY_SLOW_GO)) && + pm_gPlayerStatus.animFlags & (PA_FLAG_USING_WATT | PA_FLAG_SPINNING)) && actionState < ACTION_STATE_JUMP; if (canSpin && !spinPrevCanSpin) { spinDelayStart = pm_gGameStatus.frameCounter; @@ -231,108 +233,120 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig spinPrevCanSpin = canSpin; // begin drawing - chHeight += 1; - s32 menuY = 0; - s32 menuXOffset = x + chWidth * 13; + u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, 0xFF); + u32 colorBlackT = GPACK_RGB24A8(0x000000, 0x60); u32 colorGreen = GPACK_RGB24A8(0x00FF00, 0xFF); u32 colorYellow = GPACK_RGB24A8(0xFFFF00, 0xFF); u32 colorRed = GPACK_RGB24A8(0xFF0000, 0xFF); - gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); - - gfxPrintf(font, x, y + chHeight * menuY++, "end timing:"); - gfxPrintf(font, x, y + chHeight * menuY++, "jump frames:"); - gfxPrintf(font, x, y + chHeight * menuY++, "start delay:"); - menuY = 0; - - // end timing - if (spinCanceled) { - gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "canceled"); - } else if (spinEndTiming == 0) { - gfxModeSet(GFX_MODE_COLOR, colorGreen); - gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "good"); - } else if (spinEndTiming > 0) { - if (spinEndTiming == 1) { - gfxModeSet(GFX_MODE_COLOR, colorYellow); - } else { - gfxModeSet(GFX_MODE_COLOR, colorRed); - } - gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d early", spinEndTiming); - } else if (spinEndTiming < 0) { - if (spinEndTiming == -1) { - gfxModeSet(GFX_MODE_COLOR, colorYellow); - } else { - gfxModeSet(GFX_MODE_COLOR, colorRed); - } - if (spinEndTiming > -8) { - gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d late", abs(spinEndTiming)); - } else { - gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "8+ late"); - } - } - - // jump frames - if (spinCanceled) { - gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); - gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "canceled"); - } else { - if (spinJumpLast == 1) { - gfxModeSet(GFX_MODE_COLOR, colorGreen); - } else if (spinJumpLast == 2) { - gfxModeSet(GFX_MODE_COLOR, colorYellow); - } else { - gfxModeSet(GFX_MODE_COLOR, colorRed); - } - gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d", spinJumpLast); - } - - // start delay - if (spinDelayLast == 0) { - gfxModeSet(GFX_MODE_COLOR, colorGreen); - } else if (spinDelayLast == 1) { - gfxModeSet(GFX_MODE_COLOR, colorYellow); - } else { - gfxModeSet(GFX_MODE_COLOR, colorRed); - } - gfxPrintf(font, menuXOffset, y + chHeight * menuY++, "%d", spinDelayLast); - - // draw bar u32 colorBackground = GPACK_RGB24A8(0x000000, alpha); u32 colorEmpty = GPACK_RGB24A8(0x000095, alpha); u32 colorFull = GPACK_RGB24A8(0x00E486, alpha); + u8 barMult = 3; s8 spinFrame = 25 - pm_gPlayerSpinState.spinCountdown; u16 barWidth = 25; u16 barGoal = 18; - if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { + if (isSpeedy) { spinFrame = 30 - pm_gPlayerSpinState.spinCountdown; barWidth = 30; barGoal = 22; } barWidth *= barMult; barGoal *= barMult; - u16 barX = SCREEN_WIDTH / 2 - barWidth / 2; + u16 barX = x - barWidth / 2; u16 barGoalX = barX + barGoal; u16 barFullX = barX + spinFrame * barMult; - u16 barYTop = SCREEN_HEIGHT - 75; - u16 barYBottom = barYTop + 10; + u16 barYBottom = y + 10; + + s32 iconY = y + 12; + s32 textY = iconY + 11; + s32 jumpIconX = x - 30; + s32 jumpTextX = jumpIconX + 17; + s32 delayIconX = x + 2; + s32 delayTextX = delayIconX + 17; + // draw background + gfxModeSet(GFX_MODE_COLOR, colorBlackT); gfxModeReplace(GFX_MODE_COMBINE, G_CC_MODE(G_CC_PRIMITIVE, G_CC_PRIMITIVE)); + gfxDisp(gsSPScisTextureRectangle(qs102(barX - 4), qs102(y - 4), qs102(barX + barWidth + 4), qs102(y + 30), 0, 0, 0, + 0, 0)); + + // draw bar // background gfxModeSet(GFX_MODE_COLOR, colorBackground); - gfxDisp(gsSPScisTextureRectangle(qs102(barX - 1), qs102(barYTop - 1), qs102(barX + barWidth + 1), - qs102(barYBottom + 1), 0, 0, 0, 0, 0)); + gfxDisp(gsSPScisTextureRectangle(qs102(barX - 1), qs102(y - 1), qs102(barX + barWidth + 1), qs102(barYBottom + 1), + 0, 0, 0, 0, 0)); // empty bar gfxModeSet(GFX_MODE_COLOR, colorEmpty); - gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(barYTop), qs102(barX + barWidth), qs102(barYBottom), 0, 0, 0, 0, - 0)); + gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(y), qs102(barX + barWidth), qs102(barYBottom), 0, 0, 0, 0, 0)); // full bar gfxModeSet(GFX_MODE_COLOR, colorFull); - gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(barYTop), qs102(barFullX), qs102(barYBottom), 0, 0, 0, 0, 0)); + gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(y), qs102(barFullX), qs102(barYBottom), 0, 0, 0, 0, 0)); // goal marker gfxModeSet(GFX_MODE_COLOR, colorYellow); - gfxDisp(gsSPScisTextureRectangle(qs102(barGoalX - 1), qs102(barYTop), qs102(barGoalX + 1), qs102(barYBottom), 0, 0, - 0, 0, 0)); + gfxDisp( + gsSPScisTextureRectangle(qs102(barGoalX - 1), qs102(y), qs102(barGoalX + 1), qs102(barYBottom), 0, 0, 0, 0, 0)); gfxModePop(GFX_MODE_COMBINE); + + gfxModeSet(GFX_MODE_COLOR, colorWhite); + gfxModeReplace(GFX_MODE_DROPSHADOW, 0); + struct GfxSprite jumpSprite = {spinJumpTex, 0, 0, jumpIconX, iconY, 0.5f, 0.5f}; + struct GfxSprite delaySprite = {spinDelayTex, 0, 0, delayIconX, iconY, 1.f, 1.f}; + gfxSpriteDraw(&jumpSprite); + gfxSpriteDraw(&delaySprite); + gfxModePop(GFX_MODE_DROPSHADOW); + + // end timing + // if (spinCanceled) { + // gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + // gfxPrintf(font, timingX, textY, " -"); + // } else if (spinEndTiming == 0) { + // gfxModeSet(GFX_MODE_COLOR, colorGreen); + // gfxPrintf(font, timingX, textY, " 0"); + // } else if (spinEndTiming > 0) { + // if (spinEndTiming == 1) { + // gfxModeSet(GFX_MODE_COLOR, colorYellow); + // } else { + // gfxModeSet(GFX_MODE_COLOR, colorRed); + // } + // gfxPrintf(font, timingX, textY, "-%d", spinEndTiming); + // } else if (spinEndTiming < 0) { + // if (spinEndTiming == -1) { + // gfxModeSet(GFX_MODE_COLOR, colorYellow); + // } else { + // gfxModeSet(GFX_MODE_COLOR, colorRed); + // } + // gfxPrintf(font, timingX, textY, "+%d", abs(spinEndTiming)); + // } + + // jump frames + if (spinCanceled) { + gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + gfxPrintf(font, jumpTextX, textY, "-"); + } else { + if (spinJumpLast == 1) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + } else if (spinJumpLast == 2) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, jumpTextX, textY, "%d", spinJumpLast); + } + + // start delay + if (spinDelayLast == 0) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + } else if (spinDelayLast == 1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + if (spinDelayLast < 100) { + gfxPrintf(font, delayTextX, textY, "%d", spinDelayLast); + } else { + gfxPrintf(font, delayTextX, textY, "99"); + } } static s32 spinDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) { @@ -855,7 +869,7 @@ void trainerUpdate(void) { void trainerDrawPinned(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { switch (settings->pinnedTrainer) { case TRAINER_LZS: lzsDraw(x, y, font, chWidth, chHeight, color, alpha); break; - case TRAINER_SPIN: spinDraw(x, y, font, chWidth, chHeight, color, alpha); break; + case TRAINER_SPIN: spinDraw(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 65, font, chWidth, chHeight, color, alpha); break; case TRAINER_ISS: issDraw(x, y, font, chWidth, chHeight, color, alpha); break; case TRAINER_ACE: aceDraw(x, y, font, chWidth, chHeight, color, alpha); break; } @@ -942,6 +956,8 @@ void createTrainerMenu(struct Menu *menu) { menuAddCheckbox(&spinMenu, 4, y++, pinnedTrainerProc, (void *)TRAINER_SPIN); y++; menuAddStaticCustom(&spinMenu, 0, y++, spinDrawProc, NULL, 0xC0C0C0); + spinJumpTex = resourceLoadPmiconGlobal(ICON_MENU_BOOTS_1, 1); + spinDelayTex = resourceGet(RES_PMICON_CLOCK); /*build iss menu*/ y = 0; diff --git a/src/pm64.h b/src/pm64.h index c1271e21..a19f97c0 100644 --- a/src/pm64.h +++ b/src/pm64.h @@ -19,12 +19,14 @@ #define ICONS_PARTNERS_ROM_START 0x97890 #define ICONS_STAR_SPIRITS_ROM_START 0x963B0 #define ICONS_BP_ROM_START 0x134520 +#define ICONS_CLOCK_ROM_START 0x134660 #else #define SCRIPTS_GLOBAL_START 0x80104b40 #define ICONS_ITEMS_ROM_START 0x1D4720 #define ICONS_PARTNERS_ROM_START 0x97A20 #define ICONS_STAR_SPIRITS_ROM_START 0x96540 #define ICONS_BP_ROM_START 0x13C820 +#define ICONS_CLOCK_ROM_START 0x13C960 #endif #define SCRIPT_BOWSER_HALLWAY_TAKE_TURN 0x80222B48 diff --git a/src/sys/resource.c b/src/sys/resource.c index 6b86d49e..bbfcc7da 100644 --- a/src/sys/resource.c +++ b/src/sys/resource.c @@ -217,6 +217,13 @@ static void *rcPmiconBp(void) { return gfxTextureLoad(&td, NULL); } +static void *rcPmiconClock(void) { + struct GfxTexdesc td = { + G_IM_FMT_CI, G_IM_SIZ_4b, 0, 16, 16, 1, 1, ICONS_CLOCK_ROM_START, VSIZE(16, 16, G_IM_SIZ_4b, 1, 1), 1, + }; + return gfxTextureLoad(&td, NULL); +} + static void *rcIconCheck(void) { return resourceLoadGrcTexture("check_icons"); } @@ -270,11 +277,11 @@ static void rdFontGeneric(void *data) { /* resource management tables */ static void *(*resCtor[RES_MAX])(void) = { - rcFontFipps, rcFontNotalot35, rcFontOrigamimommy, rcFontPcsenior, rcFontPixelintv, - rcFontPressstart2p, rcFontSmwtextnc, rcFontWerdnasreturn, rcFontPixelzim, rcPmiconPartner, - rcPmiconStarSpirits, rcPmiconBp, rcIconCheck, rcIconButtons, rcIconPause, - rcIconMacro, rcIconMovie, rcIconArrow, rcIconFile, rcIconSave, - rcIconOsk, rcTextureCrosshair, rcTextureControlStick, + rcFontFipps, rcFontNotalot35, rcFontOrigamimommy, rcFontPcsenior, rcFontPixelintv, + rcFontPressstart2p, rcFontSmwtextnc, rcFontWerdnasreturn, rcFontPixelzim, rcPmiconPartner, + rcPmiconStarSpirits, rcPmiconBp, rcPmiconClock, rcIconCheck, rcIconButtons, + rcIconPause, rcIconMacro, rcIconMovie, rcIconArrow, rcIconFile, + rcIconSave, rcIconOsk, rcTextureCrosshair, rcTextureControlStick, }; static void (*resDtor[RES_MAX])() = { diff --git a/src/sys/resource.h b/src/sys/resource.h index e7eb574b..cdfe22f9 100644 --- a/src/sys/resource.h +++ b/src/sys/resource.h @@ -16,6 +16,7 @@ enum ResourceId { RES_PMICON_PARTNERS, RES_PMICON_STAR_SPIRITS, RES_PMICON_BP, + RES_PMICON_CLOCK, RES_ICON_CHECK, RES_ICON_BUTTONS, RES_ICON_PAUSE, From dd4348046aed10f6a762a7e4b7d6ee810902ab17 Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Sat, 29 Nov 2025 01:13:43 -0600 Subject: [PATCH 05/10] improve spin trainer menu --- src/fp.c | 4 + src/fp.h | 1 + src/fp/practice/trainer.c | 244 ++++++++++++++++++++++++++------------ src/fp/practice/trainer.h | 1 + src/sys/settings.c | 2 + src/sys/settings.h | 5 +- 6 files changed, 178 insertions(+), 79 deletions(-) diff --git a/src/fp.c b/src/fp.c index 889c04dd..2fa221f7 100644 --- a/src/fp.c +++ b/src/fp.c @@ -559,6 +559,10 @@ void fpDraw(void) { trainerDrawPinned(settings->trainerX, settings->trainerY, font, cellWidth, cellHeight, 0xC0C0C0, menuAlpha); } + if (fp.spinTrainerMoving || (settings->trainerSpinBarEnabled && !fp.menuActive)) { + trainerDrawSpinBar(settings->trainerSpinX, settings->trainerSpinY, font, 0xC0C0C0, menuAlpha); + } + if (!fp.versionShown) { fpDrawVersion(font, cellWidth, cellHeight, menuAlpha); } diff --git a/src/fp.h b/src/fp.h index a0435948..c168fc6a 100644 --- a/src/fp.h +++ b/src/fp.h @@ -42,6 +42,7 @@ typedef struct { s32 cpuCounterFreq; bool timerMoving; bool trainerMoving; + bool spinTrainerMoving; bool menuActive; struct LogEntry log[SETTINGS_LOG_MAX]; Vec3f savedPos; diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index ff245955..91e01864 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -181,58 +181,16 @@ static s32 lzsDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) return 1; } -static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { - enum ActionStates actionState = pm_gPlayerStatus.actionState; - pm_PlayerSpinState *spinState = &pm_gPlayerSpinState; - bool isSpeedy = pm_is_ability_active(ABILITY_SPEEDY_SPIN); - - // end timing and jump length - if (spinPrevActionState == ACTION_STATE_SPIN) { - if (actionState == ACTION_STATE_JUMP || actionState == ACTION_STATE_HAMMER) { - spinCanceled = FALSE; - s8 perfectEndTiming = 7; - if (isSpeedy) { - perfectEndTiming = 8; - } - spinEndTiming = spinState->spinCountdown - perfectEndTiming; - - if (pm_gPlayerStatus.actionState == ACTION_STATE_JUMP) { - spinIsJumping = TRUE; - spinJumpStart = pm_gGameStatus.frameCounter; - } - } else if (actionState != ACTION_STATE_SPIN) { - spinCanceled = TRUE; - } - } else if (spinIsJumping && (!pm_gGameStatus.currentButtons[0].a || actionState != ACTION_STATE_JUMP)) { - spinIsJumping = FALSE; - spinJumpLast = pm_gGameStatus.frameCounter - spinJumpStart; - } - - // delay between spins - bool canSpin = !(pm_gPlayerStatus.flags & (PS_FLAG_NO_STATIC_COLLISION | PS_FLAG_CUTSCENE_MOVEMENT | - PS_FLAG_INPUT_DISABLED | PS_FLAG_PAUSED) || - pm_gPlayerStatus.animFlags & (PA_FLAG_USING_WATT | PA_FLAG_SPINNING)) && - actionState < ACTION_STATE_JUMP; - if (canSpin && !spinPrevCanSpin) { - spinDelayStart = pm_gGameStatus.frameCounter; - if (!(actionState == ACTION_STATE_RUN && pm_gPlayerStatus.prevActionState == ACTION_STATE_WALK && - spinPrevActionState == ACTION_STATE_IDLE)) { - // note that there are a lot of rare edge cases here that just aren't worth handling - spinDelayStart += 1; - } - } - if (actionState == ACTION_STATE_SPIN && spinState->initialSpinTime - spinState->spinCountdown == 1) { - // start of new spin - if (!spinPrevCanSpin) { - spinDelayLast = 0; - } else { - spinDelayLast = pm_gGameStatus.frameCounter - spinDelayStart; - } +static s32 spinVisualPositionProc(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { + if (reason == MENU_CALLBACK_ACTIVATE) { + fp.spinTrainerMoving = TRUE; + } else if (reason == MENU_CALLBACK_DEACTIVATE) { + fp.spinTrainerMoving = FALSE; } - spinPrevActionState = actionState; - spinPrevCanSpin = canSpin; + return menuGenericPositionProc(item, reason, &settings->trainerSpinX); +} - // begin drawing +static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, 0xFF); u32 colorBlackT = GPACK_RGB24A8(0x000000, 0x60); u32 colorGreen = GPACK_RGB24A8(0x00FF00, 0xFF); @@ -246,7 +204,7 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig s8 spinFrame = 25 - pm_gPlayerSpinState.spinCountdown; u16 barWidth = 25; u16 barGoal = 18; - if (isSpeedy) { + if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { spinFrame = 30 - pm_gPlayerSpinState.spinCountdown; barWidth = 30; barGoal = 22; @@ -265,29 +223,6 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig s32 delayIconX = x + 2; s32 delayTextX = delayIconX + 17; - // draw background - gfxModeSet(GFX_MODE_COLOR, colorBlackT); - gfxModeReplace(GFX_MODE_COMBINE, G_CC_MODE(G_CC_PRIMITIVE, G_CC_PRIMITIVE)); - gfxDisp(gsSPScisTextureRectangle(qs102(barX - 4), qs102(y - 4), qs102(barX + barWidth + 4), qs102(y + 30), 0, 0, 0, - 0, 0)); - - // draw bar - // background - gfxModeSet(GFX_MODE_COLOR, colorBackground); - gfxDisp(gsSPScisTextureRectangle(qs102(barX - 1), qs102(y - 1), qs102(barX + barWidth + 1), qs102(barYBottom + 1), - 0, 0, 0, 0, 0)); - // empty bar - gfxModeSet(GFX_MODE_COLOR, colorEmpty); - gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(y), qs102(barX + barWidth), qs102(barYBottom), 0, 0, 0, 0, 0)); - // full bar - gfxModeSet(GFX_MODE_COLOR, colorFull); - gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(y), qs102(barFullX), qs102(barYBottom), 0, 0, 0, 0, 0)); - // goal marker - gfxModeSet(GFX_MODE_COLOR, colorYellow); - gfxDisp( - gsSPScisTextureRectangle(qs102(barGoalX - 1), qs102(y), qs102(barGoalX + 1), qs102(barYBottom), 0, 0, 0, 0, 0)); - gfxModePop(GFX_MODE_COMBINE); - gfxModeSet(GFX_MODE_COLOR, colorWhite); gfxModeReplace(GFX_MODE_DROPSHADOW, 0); struct GfxSprite jumpSprite = {spinJumpTex, 0, 0, jumpIconX, iconY, 0.5f, 0.5f}; @@ -640,6 +575,58 @@ static void updateLzsTrainer(void) { } } +static void updateSpinTrainer(void) { + enum ActionStates actionState = pm_gPlayerStatus.actionState; + pm_PlayerSpinState *spinState = &pm_gPlayerSpinState; + bool isSpeedy = pm_is_ability_active(ABILITY_SPEEDY_SPIN); + + // end timing and jump length + if (spinPrevActionState == ACTION_STATE_SPIN) { + if (actionState == ACTION_STATE_JUMP || actionState == ACTION_STATE_HAMMER) { + spinCanceled = FALSE; + s8 perfectEndTiming = 7; + if (isSpeedy) { + perfectEndTiming = 8; + } + spinEndTiming = spinState->spinCountdown - perfectEndTiming; + + if (pm_gPlayerStatus.actionState == ACTION_STATE_JUMP) { + spinIsJumping = TRUE; + spinJumpStart = pm_gGameStatus.frameCounter; + } + } else if (actionState != ACTION_STATE_SPIN) { + spinCanceled = TRUE; + } + } else if (spinIsJumping && (!pm_gGameStatus.currentButtons[0].a || actionState != ACTION_STATE_JUMP)) { + spinIsJumping = FALSE; + spinJumpLast = pm_gGameStatus.frameCounter - spinJumpStart; + } + + // delay between spins + bool canSpin = !(pm_gPlayerStatus.flags & (PS_FLAG_NO_STATIC_COLLISION | PS_FLAG_CUTSCENE_MOVEMENT | + PS_FLAG_INPUT_DISABLED | PS_FLAG_PAUSED) || + pm_gPlayerStatus.animFlags & (PA_FLAG_USING_WATT | PA_FLAG_SPINNING)) && + actionState < ACTION_STATE_JUMP; + if (canSpin && !spinPrevCanSpin) { + spinDelayStart = pm_gGameStatus.frameCounter; + if (!(actionState == ACTION_STATE_RUN && pm_gPlayerStatus.prevActionState == ACTION_STATE_WALK && + spinPrevActionState == ACTION_STATE_IDLE)) { + // note that there are a lot of rare edge cases here that just aren't worth handling + spinDelayStart += 1; + } + } + if (actionState == ACTION_STATE_SPIN && spinState->initialSpinTime - spinState->spinCountdown == 1) { + // start of new spin + if (!spinPrevCanSpin) { + spinDelayLast = 0; + } else { + spinDelayLast = pm_gGameStatus.frameCounter - spinDelayStart; + } + } + spinPrevActionState = actionState; + spinPrevCanSpin = canSpin; +} + static void blockCheckSuccessOrEarly(void) { const u32 mashWindow = 10; u32 blockWindow = 3; @@ -857,9 +844,105 @@ static void updateHammerCancelTrainer(void) { } } +void trainerDrawSpinBar(s32 x, s32 y, struct GfxFont *font, u32 color, u8 alpha) { + PRINTF("%d, %d, %d, %d\n", x, y, settings->trainerSpinX, settings->trainerSpinY); + u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, 0xFF); + u32 colorBlackT = GPACK_RGB24A8(0x000000, 0x60); + u32 colorGreen = GPACK_RGB24A8(0x00FF00, 0xFF); + u32 colorYellow = GPACK_RGB24A8(0xFFFF00, 0xFF); + u32 colorRed = GPACK_RGB24A8(0xFF0000, 0xFF); + u32 colorBackground = GPACK_RGB24A8(0x000000, alpha); + u32 colorEmpty = GPACK_RGB24A8(0x000095, alpha); + u32 colorFull = GPACK_RGB24A8(0x00E486, alpha); + + u8 barMult = 3; + s8 spinFrame = 25 - pm_gPlayerSpinState.spinCountdown; + u16 barWidth = 25; + u16 barGoal = 18; + if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { + spinFrame = 30 - pm_gPlayerSpinState.spinCountdown; + barWidth = 30; + barGoal = 22; + } + barWidth *= barMult; + barGoal *= barMult; + u16 barX = x - barWidth / 2; + u16 barGoalX = barX + barGoal; + u16 barFullX = barX + spinFrame * barMult; + u16 barYBottom = y + 10; + + s32 iconY = y + 12; + s32 textY = iconY + 11; + s32 jumpIconX = x - 30; + s32 jumpTextX = jumpIconX + 17; + s32 delayIconX = x + 2; + s32 delayTextX = delayIconX + 17; + + // draw background + gfxModeSet(GFX_MODE_COLOR, colorBlackT); + gfxModeReplace(GFX_MODE_COMBINE, G_CC_MODE(G_CC_PRIMITIVE, G_CC_PRIMITIVE)); + gfxDisp(gsSPScisTextureRectangle(qs102(barX - 4), qs102(y - 4), qs102(barX + barWidth + 4), qs102(y + 30), 0, 0, 0, + 0, 0)); + + // draw bar + // background + gfxModeSet(GFX_MODE_COLOR, colorBackground); + gfxDisp(gsSPScisTextureRectangle(qs102(barX - 1), qs102(y - 1), qs102(barX + barWidth + 1), qs102(barYBottom + 1), + 0, 0, 0, 0, 0)); + // empty bar + gfxModeSet(GFX_MODE_COLOR, colorEmpty); + gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(y), qs102(barX + barWidth), qs102(barYBottom), 0, 0, 0, 0, 0)); + // full bar + gfxModeSet(GFX_MODE_COLOR, colorFull); + gfxDisp(gsSPScisTextureRectangle(qs102(barX), qs102(y), qs102(barFullX), qs102(barYBottom), 0, 0, 0, 0, 0)); + // goal marker + gfxModeSet(GFX_MODE_COLOR, colorYellow); + gfxDisp( + gsSPScisTextureRectangle(qs102(barGoalX - 1), qs102(y), qs102(barGoalX + 1), qs102(barYBottom), 0, 0, 0, 0, 0)); + gfxModePop(GFX_MODE_COMBINE); + + gfxModeSet(GFX_MODE_COLOR, colorWhite); + gfxModeReplace(GFX_MODE_DROPSHADOW, 0); + struct GfxSprite jumpSprite = {spinJumpTex, 0, 0, jumpIconX, iconY, 0.5f, 0.5f}; + struct GfxSprite delaySprite = {spinDelayTex, 0, 0, delayIconX, iconY, 1.f, 1.f}; + gfxSpriteDraw(&jumpSprite); + gfxSpriteDraw(&delaySprite); + gfxModePop(GFX_MODE_DROPSHADOW); + + // jump frames + if (spinCanceled) { + gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + gfxPrintf(font, jumpTextX, textY, "-"); + } else { + if (spinJumpLast == 1) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + } else if (spinJumpLast == 2) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, jumpTextX, textY, "%d", spinJumpLast); + } + + // start delay + if (spinDelayLast == 0) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + } else if (spinDelayLast == 1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + if (spinDelayLast < 100) { + gfxPrintf(font, delayTextX, textY, "%d", spinDelayLast); + } else { + gfxPrintf(font, delayTextX, textY, "99"); + } +} + void trainerUpdate(void) { updateBowserBlockTrainer(); updateLzsTrainer(); + updateSpinTrainer(); updateBlockTrainer(); updateClippyTrainer(); updateQuickJumpTrainer(); @@ -869,7 +952,7 @@ void trainerUpdate(void) { void trainerDrawPinned(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { switch (settings->pinnedTrainer) { case TRAINER_LZS: lzsDraw(x, y, font, chWidth, chHeight, color, alpha); break; - case TRAINER_SPIN: spinDraw(SCREEN_WIDTH / 2, SCREEN_HEIGHT - 65, font, chWidth, chHeight, color, alpha); break; + case TRAINER_SPIN: spinDraw(x, y, font, chWidth, chHeight, color, alpha); break; case TRAINER_ISS: issDraw(x, y, font, chWidth, chHeight, color, alpha); break; case TRAINER_ACE: aceDraw(x, y, font, chWidth, chHeight, color, alpha); break; } @@ -951,13 +1034,18 @@ void createTrainerMenu(struct Menu *menu) { /*build spin menu*/ y = 0; + xOffset = 7; + spinJumpTex = resourceLoadPmiconGlobal(ICON_MENU_BOOTS_1, 1); + spinDelayTex = resourceGet(RES_PMICON_CLOCK); spinMenu.selector = menuAddSubmenu(&spinMenu, 0, y++, NULL, "return"); menuAddStatic(&spinMenu, 0, y, "pin", 0xC0C0C0); - menuAddCheckbox(&spinMenu, 4, y++, pinnedTrainerProc, (void *)TRAINER_SPIN); + menuAddCheckbox(&spinMenu, xOffset, y++, pinnedTrainerProc, (void *)TRAINER_SPIN); + menuAddStatic(&spinMenu, 0, y, "visual", 0xC0C0C0); + menuAddCheckbox(&spinMenu, xOffset, y, menuByteCheckboxProc, &settings->trainerSpinBarEnabled); + menuAddPositioning(&spinMenu, xOffset + 2, y++, spinVisualPositionProc, NULL); + y++; menuAddStaticCustom(&spinMenu, 0, y++, spinDrawProc, NULL, 0xC0C0C0); - spinJumpTex = resourceLoadPmiconGlobal(ICON_MENU_BOOTS_1, 1); - spinDelayTex = resourceGet(RES_PMICON_CLOCK); /*build iss menu*/ y = 0; diff --git a/src/fp/practice/trainer.h b/src/fp/practice/trainer.h index f01ee56f..a4a2fb9b 100644 --- a/src/fp/practice/trainer.h +++ b/src/fp/practice/trainer.h @@ -9,6 +9,7 @@ enum PinnedTrainer { TRAINER_ACE, }; +void trainerDrawSpinBar(s32 x, s32 y, struct GfxFont *font, u32 color, u8 alpha); void trainerUpdate(void); void trainerDrawPinned(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha); void createTrainerMenu(struct Menu *menu); diff --git a/src/sys/settings.c b/src/sys/settings.c index 977aba85..a54aa885 100644 --- a/src/sys/settings.c +++ b/src/sys/settings.c @@ -60,6 +60,8 @@ void settingsLoadDefault(void) { d->timerY = 68; d->trainerX = 16; d->trainerY = 68; + d->trainerSpinX = SCREEN_WIDTH / 2; + d->trainerSpinY = SCREEN_HEIGHT - 65; d->binds[COMMAND_MENU] = bindMake(2, BUTTON_R, BUTTON_D_UP); d->binds[COMMAND_RETURN] = bindMake(2, BUTTON_R, BUTTON_D_LEFT); d->binds[COMMAND_LEVITATE] = bindMake(1, BUTTON_D_UP); diff --git a/src/sys/settings.h b/src/sys/settings.h index f5bd7ae3..864579b7 100644 --- a/src/sys/settings.h +++ b/src/sys/settings.h @@ -7,7 +7,7 @@ #define SETTINGS_SAVE_FILE_SIZE 0x1380 #define SETTINGS_PROFILE_MAX 4 -#define SETTINGS_VERSION 8 +#define SETTINGS_VERSION 9 #define SETTINGS_FIO_PAGE 7 #define SETTINGS_WATCHES_MAX 18 @@ -55,6 +55,8 @@ struct SettingsData { s16 timerY; s16 trainerX; s16 trainerY; + s16 trainerSpinX; + s16 trainerSpinY; s16 watchX[SETTINGS_WATCHES_MAX]; s16 watchY[SETTINGS_WATCHES_MAX]; u16 binds[SETTINGS_BIND_MAX]; @@ -76,6 +78,7 @@ struct SettingsData { u8 trainerClippyEnabled; u8 trainerQuickJumpsEnabled; u8 trainerHammerCancelsEnabled; + u8 trainerSpinBarEnabled; u8 trainerDisplayPinned; u8 battleDebug; u8 quickLaunch; From e32d845c0e32ecec1112f23980b5ab339702080f Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Sat, 29 Nov 2025 02:24:58 -0600 Subject: [PATCH 06/10] make text version of spin trainer --- src/fp.c | 2 +- src/fp/practice/trainer.c | 108 +++++++++++++++++--------------------- 2 files changed, 48 insertions(+), 62 deletions(-) diff --git a/src/fp.c b/src/fp.c index 2fa221f7..cf63fed9 100644 --- a/src/fp.c +++ b/src/fp.c @@ -559,7 +559,7 @@ void fpDraw(void) { trainerDrawPinned(settings->trainerX, settings->trainerY, font, cellWidth, cellHeight, 0xC0C0C0, menuAlpha); } - if (fp.spinTrainerMoving || (settings->trainerSpinBarEnabled && !fp.menuActive)) { + if (fp.spinTrainerMoving || (settings->trainerSpinBarEnabled && !fp.menuActive && pm_gGameStatus.isBattle == 0)) { trainerDrawSpinBar(settings->trainerSpinX, settings->trainerSpinY, font, 0xC0C0C0, menuAlpha); } diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index 91e01864..584e0fb2 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -88,6 +88,7 @@ static u16 spinJumpStart = 0; static s16 spinJumpLast = 1; static u16 spinDelayStart = 0; static u16 spinDelayLast = 0; +static struct GfxTexture *spinAButtonTex = NULL; static struct GfxTexture *spinJumpTex = NULL; static struct GfxTexture *spinDelayTex = NULL; @@ -191,73 +192,58 @@ static s32 spinVisualPositionProc(struct MenuItem *item, enum MenuCallbackReason } static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { + if (pm_gGameStatus.isBattle != 0) { + return; + } + u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, 0xFF); - u32 colorBlackT = GPACK_RGB24A8(0x000000, 0x60); u32 colorGreen = GPACK_RGB24A8(0x00FF00, 0xFF); u32 colorYellow = GPACK_RGB24A8(0xFFFF00, 0xFF); u32 colorRed = GPACK_RGB24A8(0xFF0000, 0xFF); - u32 colorBackground = GPACK_RGB24A8(0x000000, alpha); - u32 colorEmpty = GPACK_RGB24A8(0x000095, alpha); - u32 colorFull = GPACK_RGB24A8(0x00E486, alpha); - - u8 barMult = 3; - s8 spinFrame = 25 - pm_gPlayerSpinState.spinCountdown; - u16 barWidth = 25; - u16 barGoal = 18; - if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { - spinFrame = 30 - pm_gPlayerSpinState.spinCountdown; - barWidth = 30; - barGoal = 22; - } - barWidth *= barMult; - barGoal *= barMult; - u16 barX = x - barWidth / 2; - u16 barGoalX = barX + barGoal; - u16 barFullX = barX + spinFrame * barMult; - u16 barYBottom = y + 10; - - s32 iconY = y + 12; - s32 textY = iconY + 11; - s32 jumpIconX = x - 30; - s32 jumpTextX = jumpIconX + 17; - s32 delayIconX = x + 2; - s32 delayTextX = delayIconX + 17; gfxModeSet(GFX_MODE_COLOR, colorWhite); gfxModeReplace(GFX_MODE_DROPSHADOW, 0); - struct GfxSprite jumpSprite = {spinJumpTex, 0, 0, jumpIconX, iconY, 0.5f, 0.5f}; - struct GfxSprite delaySprite = {spinDelayTex, 0, 0, delayIconX, iconY, 1.f, 1.f}; + s32 endTimingY = y - 9; + s32 jumpY = endTimingY + 11; + s32 delayY = jumpY + 15; + struct GfxSprite aButtonSprite = {spinAButtonTex, 0, 0, x, endTimingY, 0.3f, 0.3f}; + struct GfxSprite jumpSprite = {spinJumpTex, 0, 0, x, jumpY, 0.5f, 0.5f}; + struct GfxSprite delaySprite = {spinDelayTex, 0, 0, x, delayY, 1.f, 1.f}; + gfxSpriteDraw(&aButtonSprite); gfxSpriteDraw(&jumpSprite); gfxSpriteDraw(&delaySprite); gfxModePop(GFX_MODE_DROPSHADOW); + s32 textX = x + 17; + s32 textY = y; // end timing - // if (spinCanceled) { - // gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); - // gfxPrintf(font, timingX, textY, " -"); - // } else if (spinEndTiming == 0) { - // gfxModeSet(GFX_MODE_COLOR, colorGreen); - // gfxPrintf(font, timingX, textY, " 0"); - // } else if (spinEndTiming > 0) { - // if (spinEndTiming == 1) { - // gfxModeSet(GFX_MODE_COLOR, colorYellow); - // } else { - // gfxModeSet(GFX_MODE_COLOR, colorRed); - // } - // gfxPrintf(font, timingX, textY, "-%d", spinEndTiming); - // } else if (spinEndTiming < 0) { - // if (spinEndTiming == -1) { - // gfxModeSet(GFX_MODE_COLOR, colorYellow); - // } else { - // gfxModeSet(GFX_MODE_COLOR, colorRed); - // } - // gfxPrintf(font, timingX, textY, "+%d", abs(spinEndTiming)); - // } + if (spinCanceled) { + gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + gfxPrintf(font, textX, textY, "-"); + } else if (spinEndTiming == 0) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + gfxPrintf(font, textX, textY, "0"); + } else if (spinEndTiming > 0) { + if (spinEndTiming == 1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, textX, textY, "%d early", spinEndTiming); + } else if (spinEndTiming < 0) { + if (spinEndTiming == -1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, textX, textY, "%d late", abs(spinEndTiming)); + } // jump frames + textY += 14; if (spinCanceled) { gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); - gfxPrintf(font, jumpTextX, textY, "-"); + gfxPrintf(font, textX, textY, "-"); } else { if (spinJumpLast == 1) { gfxModeSet(GFX_MODE_COLOR, colorGreen); @@ -266,10 +252,11 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig } else { gfxModeSet(GFX_MODE_COLOR, colorRed); } - gfxPrintf(font, jumpTextX, textY, "%d", spinJumpLast); + gfxPrintf(font, textX, textY, "%d", spinJumpLast); } // start delay + textY += 14; if (spinDelayLast == 0) { gfxModeSet(GFX_MODE_COLOR, colorGreen); } else if (spinDelayLast == 1) { @@ -277,11 +264,7 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig } else { gfxModeSet(GFX_MODE_COLOR, colorRed); } - if (spinDelayLast < 100) { - gfxPrintf(font, delayTextX, textY, "%d", spinDelayLast); - } else { - gfxPrintf(font, delayTextX, textY, "99"); - } + gfxPrintf(font, textX, textY, "%d", spinDelayLast); } static s32 spinDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) { @@ -576,16 +559,20 @@ static void updateLzsTrainer(void) { } static void updateSpinTrainer(void) { + if (!(pm_gGameStatus.isBattle != 1 && + (settings->trainerSpinBarEnabled || settings->pinnedTrainer == TRAINER_SPIN))) { + return; + } + enum ActionStates actionState = pm_gPlayerStatus.actionState; pm_PlayerSpinState *spinState = &pm_gPlayerSpinState; - bool isSpeedy = pm_is_ability_active(ABILITY_SPEEDY_SPIN); // end timing and jump length if (spinPrevActionState == ACTION_STATE_SPIN) { if (actionState == ACTION_STATE_JUMP || actionState == ACTION_STATE_HAMMER) { spinCanceled = FALSE; s8 perfectEndTiming = 7; - if (isSpeedy) { + if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { perfectEndTiming = 8; } spinEndTiming = spinState->spinCountdown - perfectEndTiming; @@ -845,7 +832,6 @@ static void updateHammerCancelTrainer(void) { } void trainerDrawSpinBar(s32 x, s32 y, struct GfxFont *font, u32 color, u8 alpha) { - PRINTF("%d, %d, %d, %d\n", x, y, settings->trainerSpinX, settings->trainerSpinY); u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, 0xFF); u32 colorBlackT = GPACK_RGB24A8(0x000000, 0x60); u32 colorGreen = GPACK_RGB24A8(0x00FF00, 0xFF); @@ -1035,6 +1021,7 @@ void createTrainerMenu(struct Menu *menu) { /*build spin menu*/ y = 0; xOffset = 7; + spinAButtonTex = resourceLoadPmiconGlobal(ICON_A_BUTTON, 1); spinJumpTex = resourceLoadPmiconGlobal(ICON_MENU_BOOTS_1, 1); spinDelayTex = resourceGet(RES_PMICON_CLOCK); spinMenu.selector = menuAddSubmenu(&spinMenu, 0, y++, NULL, "return"); @@ -1043,7 +1030,6 @@ void createTrainerMenu(struct Menu *menu) { menuAddStatic(&spinMenu, 0, y, "visual", 0xC0C0C0); menuAddCheckbox(&spinMenu, xOffset, y, menuByteCheckboxProc, &settings->trainerSpinBarEnabled); menuAddPositioning(&spinMenu, xOffset + 2, y++, spinVisualPositionProc, NULL); - y++; menuAddStaticCustom(&spinMenu, 0, y++, spinDrawProc, NULL, 0xC0C0C0); From 729e0bbe8f152a5cb7a4dc46e637c3ba23668964 Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:06:48 -0600 Subject: [PATCH 07/10] add z buffer timing to spin trainer --- src/fp/practice/trainer.c | 51 ++++++++++++++++++++++++++++++--------- src/pm64.h | 10 +++++--- src/sys/resource.c | 17 +++++++++---- src/sys/resource.h | 1 + 4 files changed, 58 insertions(+), 21 deletions(-) diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index 584e0fb2..5037d8e8 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -88,9 +88,12 @@ static u16 spinJumpStart = 0; static s16 spinJumpLast = 1; static u16 spinDelayStart = 0; static u16 spinDelayLast = 0; +static s8 spinBufferAttempt = 0; +static s8 spinBufferLast = 0; static struct GfxTexture *spinAButtonTex = NULL; static struct GfxTexture *spinJumpTex = NULL; static struct GfxTexture *spinDelayTex = NULL; +static struct GfxTexture *spinZButtonTex = NULL; extern void setACEHook(void); @@ -196,33 +199,37 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig return; } - u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, 0xFF); - u32 colorGreen = GPACK_RGB24A8(0x00FF00, 0xFF); - u32 colorYellow = GPACK_RGB24A8(0xFFFF00, 0xFF); - u32 colorRed = GPACK_RGB24A8(0xFF0000, 0xFF); + u32 colorInput = GPACK_RGB24A8(color, alpha); + u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, alpha); + u32 colorGreen = GPACK_RGB24A8(0x00FF00, alpha); + u32 colorYellow = GPACK_RGB24A8(0xFFFF00, alpha); + u32 colorRed = GPACK_RGB24A8(0xFF0000, alpha); gfxModeSet(GFX_MODE_COLOR, colorWhite); gfxModeReplace(GFX_MODE_DROPSHADOW, 0); s32 endTimingY = y - 9; s32 jumpY = endTimingY + 11; s32 delayY = jumpY + 15; + s32 bufferY = delayY + 16; struct GfxSprite aButtonSprite = {spinAButtonTex, 0, 0, x, endTimingY, 0.3f, 0.3f}; struct GfxSprite jumpSprite = {spinJumpTex, 0, 0, x, jumpY, 0.5f, 0.5f}; struct GfxSprite delaySprite = {spinDelayTex, 0, 0, x, delayY, 1.f, 1.f}; + struct GfxSprite zButtonSprite = {spinZButtonTex, 0, 0, x + 2, bufferY, 1.f, 1.f}; gfxSpriteDraw(&aButtonSprite); gfxSpriteDraw(&jumpSprite); gfxSpriteDraw(&delaySprite); + gfxSpriteDraw(&zButtonSprite); gfxModePop(GFX_MODE_DROPSHADOW); s32 textX = x + 17; s32 textY = y; // end timing if (spinCanceled) { - gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + gfxModeSet(GFX_MODE_COLOR, colorInput); gfxPrintf(font, textX, textY, "-"); } else if (spinEndTiming == 0) { gfxModeSet(GFX_MODE_COLOR, colorGreen); - gfxPrintf(font, textX, textY, "0"); + gfxPrintf(font, textX, textY, "good"); } else if (spinEndTiming > 0) { if (spinEndTiming == 1) { gfxModeSet(GFX_MODE_COLOR, colorYellow); @@ -242,7 +249,7 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig // jump frames textY += 14; if (spinCanceled) { - gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + gfxModeSet(GFX_MODE_COLOR, colorInput); gfxPrintf(font, textX, textY, "-"); } else { if (spinJumpLast == 1) { @@ -265,6 +272,19 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig gfxModeSet(GFX_MODE_COLOR, colorRed); } gfxPrintf(font, textX, textY, "%d", spinDelayLast); + + // z buffer offset + textY += 14; + if (spinBufferLast == -1) { + gfxModeSet(GFX_MODE_COLOR, colorInput); + gfxPrintf(font, textX, textY, "-"); + } else if (spinBufferLast < 10) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + gfxPrintf(font, textX, textY, "good"); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + gfxPrintf(font, textX, textY, "%d early", spinBufferLast - 9); + } } static s32 spinDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) { @@ -571,6 +591,7 @@ static void updateSpinTrainer(void) { if (spinPrevActionState == ACTION_STATE_SPIN) { if (actionState == ACTION_STATE_JUMP || actionState == ACTION_STATE_HAMMER) { spinCanceled = FALSE; + spinBufferLast = spinBufferAttempt; s8 perfectEndTiming = 7; if (pm_is_ability_active(ABILITY_SPEEDY_SPIN)) { perfectEndTiming = 8; @@ -609,7 +630,11 @@ static void updateSpinTrainer(void) { } else { spinDelayLast = pm_gGameStatus.frameCounter - spinDelayStart; } + spinBufferAttempt = -1; + } else if (actionState == ACTION_STATE_SPIN && pm_gGameStatus.pressedButtons[0].z) { + spinBufferAttempt = spinState->spinCountdown; } + spinPrevActionState = actionState; spinPrevCanSpin = canSpin; } @@ -832,11 +857,12 @@ static void updateHammerCancelTrainer(void) { } void trainerDrawSpinBar(s32 x, s32 y, struct GfxFont *font, u32 color, u8 alpha) { - u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, 0xFF); + u32 colorInput = GPACK_RGB24A8(color, alpha); + u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, alpha); u32 colorBlackT = GPACK_RGB24A8(0x000000, 0x60); - u32 colorGreen = GPACK_RGB24A8(0x00FF00, 0xFF); - u32 colorYellow = GPACK_RGB24A8(0xFFFF00, 0xFF); - u32 colorRed = GPACK_RGB24A8(0xFF0000, 0xFF); + u32 colorGreen = GPACK_RGB24A8(0x00FF00, alpha); + u32 colorYellow = GPACK_RGB24A8(0xFFFF00, alpha); + u32 colorRed = GPACK_RGB24A8(0xFF0000, alpha); u32 colorBackground = GPACK_RGB24A8(0x000000, alpha); u32 colorEmpty = GPACK_RGB24A8(0x000095, alpha); u32 colorFull = GPACK_RGB24A8(0x00E486, alpha); @@ -897,7 +923,7 @@ void trainerDrawSpinBar(s32 x, s32 y, struct GfxFont *font, u32 color, u8 alpha) // jump frames if (spinCanceled) { - gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); + gfxModeSet(GFX_MODE_COLOR, colorInput); gfxPrintf(font, jumpTextX, textY, "-"); } else { if (spinJumpLast == 1) { @@ -1024,6 +1050,7 @@ void createTrainerMenu(struct Menu *menu) { spinAButtonTex = resourceLoadPmiconGlobal(ICON_A_BUTTON, 1); spinJumpTex = resourceLoadPmiconGlobal(ICON_MENU_BOOTS_1, 1); spinDelayTex = resourceGet(RES_PMICON_CLOCK); + spinZButtonTex = resourceGet(RES_PMICON_Z_BUTTON); spinMenu.selector = menuAddSubmenu(&spinMenu, 0, y++, NULL, "return"); menuAddStatic(&spinMenu, 0, y, "pin", 0xC0C0C0); menuAddCheckbox(&spinMenu, xOffset, y++, pinnedTrainerProc, (void *)TRAINER_SPIN); diff --git a/src/pm64.h b/src/pm64.h index a19f97c0..d5908200 100644 --- a/src/pm64.h +++ b/src/pm64.h @@ -15,18 +15,20 @@ #if PM64_VERSION == US #define SCRIPTS_GLOBAL_START 0x801049B0 -#define ICONS_ITEMS_ROM_START 0x1CC310 -#define ICONS_PARTNERS_ROM_START 0x97890 #define ICONS_STAR_SPIRITS_ROM_START 0x963B0 +#define ICONS_PARTNERS_ROM_START 0x97890 #define ICONS_BP_ROM_START 0x134520 #define ICONS_CLOCK_ROM_START 0x134660 +#define ICONS_Z_BUTTON_ROM_START 0x1B8A60 +#define ICONS_ITEMS_ROM_START 0x1CC310 #else #define SCRIPTS_GLOBAL_START 0x80104b40 -#define ICONS_ITEMS_ROM_START 0x1D4720 -#define ICONS_PARTNERS_ROM_START 0x97A20 #define ICONS_STAR_SPIRITS_ROM_START 0x96540 +#define ICONS_PARTNERS_ROM_START 0x97A20 #define ICONS_BP_ROM_START 0x13C820 #define ICONS_CLOCK_ROM_START 0x13C960 +#define ICONS_Z_BUTTON_ROM_START 0x1C0E70 +#define ICONS_ITEMS_ROM_START 0x1D4720 #endif #define SCRIPT_BOWSER_HALLWAY_TAKE_TURN 0x80222B48 diff --git a/src/sys/resource.c b/src/sys/resource.c index bbfcc7da..ce2c01e4 100644 --- a/src/sys/resource.c +++ b/src/sys/resource.c @@ -224,6 +224,13 @@ static void *rcPmiconClock(void) { return gfxTextureLoad(&td, NULL); } +static void *rcPmiconZButton(void) { + struct GfxTexdesc td = { + G_IM_FMT_CI, G_IM_SIZ_4b, 0, 16, 16, 1, 1, ICONS_Z_BUTTON_ROM_START, VSIZE(16, 16, G_IM_SIZ_4b, 1, 1), 1, + }; + return gfxTextureLoad(&td, NULL); +} + static void *rcIconCheck(void) { return resourceLoadGrcTexture("check_icons"); } @@ -277,11 +284,11 @@ static void rdFontGeneric(void *data) { /* resource management tables */ static void *(*resCtor[RES_MAX])(void) = { - rcFontFipps, rcFontNotalot35, rcFontOrigamimommy, rcFontPcsenior, rcFontPixelintv, - rcFontPressstart2p, rcFontSmwtextnc, rcFontWerdnasreturn, rcFontPixelzim, rcPmiconPartner, - rcPmiconStarSpirits, rcPmiconBp, rcPmiconClock, rcIconCheck, rcIconButtons, - rcIconPause, rcIconMacro, rcIconMovie, rcIconArrow, rcIconFile, - rcIconSave, rcIconOsk, rcTextureCrosshair, rcTextureControlStick, + rcFontFipps, rcFontNotalot35, rcFontOrigamimommy, rcFontPcsenior, rcFontPixelintv, + rcFontPressstart2p, rcFontSmwtextnc, rcFontWerdnasreturn, rcFontPixelzim, rcPmiconPartner, + rcPmiconStarSpirits, rcPmiconBp, rcPmiconClock, rcPmiconZButton, rcIconCheck, + rcIconButtons, rcIconPause, rcIconMacro, rcIconMovie, rcIconArrow, + rcIconFile, rcIconSave, rcIconOsk, rcTextureCrosshair, rcTextureControlStick, }; static void (*resDtor[RES_MAX])() = { diff --git a/src/sys/resource.h b/src/sys/resource.h index cdfe22f9..7ebf761e 100644 --- a/src/sys/resource.h +++ b/src/sys/resource.h @@ -17,6 +17,7 @@ enum ResourceId { RES_PMICON_STAR_SPIRITS, RES_PMICON_BP, RES_PMICON_CLOCK, + RES_PMICON_Z_BUTTON, RES_ICON_CHECK, RES_ICON_BUTTONS, RES_ICON_PAUSE, From 7e493d8f5a9f6704cd8a74a854900f3e5759a53b Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Sat, 29 Nov 2025 04:57:35 -0600 Subject: [PATCH 08/10] check for buffered spin at the end all spins --- src/fp/practice/trainer.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index 5037d8e8..bc0cef8d 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -604,6 +604,7 @@ static void updateSpinTrainer(void) { } } else if (actionState != ACTION_STATE_SPIN) { spinCanceled = TRUE; + spinBufferLast = spinBufferAttempt; } } else if (spinIsJumping && (!pm_gGameStatus.currentButtons[0].a || actionState != ACTION_STATE_JUMP)) { spinIsJumping = FALSE; From 0eaf79ba2e8f806a3f5eb947dc6c21f74d4e3957 Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Mon, 1 Dec 2025 02:17:57 -0600 Subject: [PATCH 09/10] add background and indiv. toggles for spin details --- src/fp/practice/trainer.c | 227 ++++++++++++++++++++++++-------------- src/sys/settings.c | 5 + src/sys/settings.h | 4 + 3 files changed, 152 insertions(+), 84 deletions(-) diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index bc0cef8d..4bcaf65c 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -91,8 +91,8 @@ static u16 spinDelayLast = 0; static s8 spinBufferAttempt = 0; static s8 spinBufferLast = 0; static struct GfxTexture *spinAButtonTex = NULL; -static struct GfxTexture *spinJumpTex = NULL; -static struct GfxTexture *spinDelayTex = NULL; +static struct GfxTexture *spinBootTex = NULL; +static struct GfxTexture *spinClockTex = NULL; static struct GfxTexture *spinZButtonTex = NULL; extern void setACEHook(void); @@ -172,6 +172,15 @@ static s32 pinnedTrainerProc(struct MenuItem *item, enum MenuCallbackReason reas return 0; } +static s32 trainerPositionProc(struct MenuItem *item, enum MenuCallbackReason reason, void *data) { + if (reason == MENU_CALLBACK_ACTIVATE) { + fp.trainerMoving = TRUE; + } else if (reason == MENU_CALLBACK_DEACTIVATE) { + fp.trainerMoving = FALSE; + } + return menuGenericPositionProc(item, reason, &settings->trainerX); +} + static void lzsDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); s32 menuY = 0; @@ -200,99 +209,142 @@ static void spinDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeig } u32 colorInput = GPACK_RGB24A8(color, alpha); + u32 colorBlackT = GPACK_RGB24A8(0x000000, 0x60); u32 colorWhite = GPACK_RGB24A8(0xFFFFFF, alpha); u32 colorGreen = GPACK_RGB24A8(0x00FF00, alpha); u32 colorYellow = GPACK_RGB24A8(0xFFFF00, alpha); u32 colorRed = GPACK_RGB24A8(0xFF0000, alpha); - gfxModeSet(GFX_MODE_COLOR, colorWhite); - gfxModeReplace(GFX_MODE_DROPSHADOW, 0); - s32 endTimingY = y - 9; - s32 jumpY = endTimingY + 11; - s32 delayY = jumpY + 15; - s32 bufferY = delayY + 16; - struct GfxSprite aButtonSprite = {spinAButtonTex, 0, 0, x, endTimingY, 0.3f, 0.3f}; - struct GfxSprite jumpSprite = {spinJumpTex, 0, 0, x, jumpY, 0.5f, 0.5f}; - struct GfxSprite delaySprite = {spinDelayTex, 0, 0, x, delayY, 1.f, 1.f}; - struct GfxSprite zButtonSprite = {spinZButtonTex, 0, 0, x + 2, bufferY, 1.f, 1.f}; - gfxSpriteDraw(&aButtonSprite); - gfxSpriteDraw(&jumpSprite); - gfxSpriteDraw(&delaySprite); - gfxSpriteDraw(&zButtonSprite); - gfxModePop(GFX_MODE_DROPSHADOW); + // black background + s32 bgY = y - 11; + s32 bgHeight = 0; + u8 count = 0; + if (settings->trainerSpinJumpTiming) { + count++; + bgHeight += 14; + } + if (settings->trainerSpinJumpLength) { + count++; + bgHeight += 14; + } + if (settings->trainerSpinStartDelay) { + if (count == 0) { + bgY -= 1; + bgHeight += 15; + } else { + bgHeight += 14; + } + } + if (settings->trainerSpinZBuffer) { + bgHeight += 16; + } + if (bgHeight > 0) { + bgHeight += 3; + } + gfxModeSet(GFX_MODE_COLOR, colorBlackT); + gfxModeReplace(GFX_MODE_COMBINE, G_CC_MODE(G_CC_PRIMITIVE, G_CC_PRIMITIVE)); + gfxDisp(gsSPScisTextureRectangle(qs102(x - 2), qs102(bgY), qs102(x + 86), qs102(bgY + bgHeight), 0, 0, 0, 0, 0)); + gfxModePop(GFX_MODE_COMBINE); s32 textX = x + 17; s32 textY = y; - // end timing - if (spinCanceled) { - gfxModeSet(GFX_MODE_COLOR, colorInput); - gfxPrintf(font, textX, textY, "-"); - } else if (spinEndTiming == 0) { - gfxModeSet(GFX_MODE_COLOR, colorGreen); - gfxPrintf(font, textX, textY, "good"); - } else if (spinEndTiming > 0) { - if (spinEndTiming == 1) { - gfxModeSet(GFX_MODE_COLOR, colorYellow); - } else { - gfxModeSet(GFX_MODE_COLOR, colorRed); + s32 iconY = y; + // jump (and hammer) timing + if (settings->trainerSpinJumpTiming) { + iconY = textY - 9; + gfxModeSet(GFX_MODE_COLOR, colorWhite); + gfxModeReplace(GFX_MODE_DROPSHADOW, 0); + struct GfxSprite aButtonSprite = {spinAButtonTex, 0, 0, x, iconY, 0.3f, 0.3f}; + gfxSpriteDraw(&aButtonSprite); + gfxModePop(GFX_MODE_DROPSHADOW); + if (spinCanceled) { + gfxModeSet(GFX_MODE_COLOR, colorInput); + gfxPrintf(font, textX, textY, "-"); + } else if (spinEndTiming == 0) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + gfxPrintf(font, textX, textY, "good"); + } else if (spinEndTiming > 0) { + if (spinEndTiming == 1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, textX, textY, "%d early", spinEndTiming); + } else if (spinEndTiming < 0) { + if (spinEndTiming == -1) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, textX, textY, "%d late", abs(spinEndTiming)); } - gfxPrintf(font, textX, textY, "%d early", spinEndTiming); - } else if (spinEndTiming < 0) { - if (spinEndTiming == -1) { - gfxModeSet(GFX_MODE_COLOR, colorYellow); + textY += 14; + } + + // jump length + if (settings->trainerSpinJumpLength) { + iconY = textY - 11; + gfxModeSet(GFX_MODE_COLOR, colorWhite); + gfxModeReplace(GFX_MODE_DROPSHADOW, 0); + struct GfxSprite bootSprite = {spinBootTex, 0, 0, x, iconY, 0.5f, 0.5f}; + gfxSpriteDraw(&bootSprite); + gfxModePop(GFX_MODE_DROPSHADOW); + if (spinCanceled) { + gfxModeSet(GFX_MODE_COLOR, colorInput); + gfxPrintf(font, textX, textY, "-"); } else { - gfxModeSet(GFX_MODE_COLOR, colorRed); + if (spinJumpLast == 1) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + } else if (spinJumpLast == 2) { + gfxModeSet(GFX_MODE_COLOR, colorYellow); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + } + gfxPrintf(font, textX, textY, "%d", spinJumpLast); } - gfxPrintf(font, textX, textY, "%d late", abs(spinEndTiming)); + textY += 14; } - // jump frames - textY += 14; - if (spinCanceled) { - gfxModeSet(GFX_MODE_COLOR, colorInput); - gfxPrintf(font, textX, textY, "-"); - } else { - if (spinJumpLast == 1) { + // start delay + if (settings->trainerSpinStartDelay) { + iconY = textY - 11; + gfxModeSet(GFX_MODE_COLOR, colorWhite); + gfxModeReplace(GFX_MODE_DROPSHADOW, 0); + struct GfxSprite clockSprite = {spinClockTex, 0, 0, x, iconY, 1.f, 1.f}; + gfxSpriteDraw(&clockSprite); + gfxModePop(GFX_MODE_DROPSHADOW); + if (spinDelayLast == 0) { gfxModeSet(GFX_MODE_COLOR, colorGreen); - } else if (spinJumpLast == 2) { + } else if (spinDelayLast == 1) { gfxModeSet(GFX_MODE_COLOR, colorYellow); } else { gfxModeSet(GFX_MODE_COLOR, colorRed); } - gfxPrintf(font, textX, textY, "%d", spinJumpLast); + gfxPrintf(font, textX, textY, "%d", spinDelayLast); + textY += 14; } - // start delay - textY += 14; - if (spinDelayLast == 0) { - gfxModeSet(GFX_MODE_COLOR, colorGreen); - } else if (spinDelayLast == 1) { - gfxModeSet(GFX_MODE_COLOR, colorYellow); - } else { - gfxModeSet(GFX_MODE_COLOR, colorRed); - } - gfxPrintf(font, textX, textY, "%d", spinDelayLast); - // z buffer offset - textY += 14; - if (spinBufferLast == -1) { - gfxModeSet(GFX_MODE_COLOR, colorInput); - gfxPrintf(font, textX, textY, "-"); - } else if (spinBufferLast < 10) { - gfxModeSet(GFX_MODE_COLOR, colorGreen); - gfxPrintf(font, textX, textY, "good"); - } else { - gfxModeSet(GFX_MODE_COLOR, colorRed); - gfxPrintf(font, textX, textY, "%d early", spinBufferLast - 9); + if (settings->trainerSpinZBuffer) { + iconY = textY - 9; + gfxModeSet(GFX_MODE_COLOR, colorWhite); + gfxModeReplace(GFX_MODE_DROPSHADOW, 0); + struct GfxSprite zButtonSprite = {spinZButtonTex, 0, 0, x + 2, iconY, 1.f, 1.f}; + gfxSpriteDraw(&zButtonSprite); + gfxModePop(GFX_MODE_DROPSHADOW); + if (spinBufferLast == -1) { + gfxModeSet(GFX_MODE_COLOR, colorInput); + gfxPrintf(font, textX, textY, "-"); + } else if (spinBufferLast < 10) { + gfxModeSet(GFX_MODE_COLOR, colorGreen); + gfxPrintf(font, textX, textY, "good"); + } else { + gfxModeSet(GFX_MODE_COLOR, colorRed); + gfxPrintf(font, textX, textY, "%d early", spinBufferLast - 9); + } } } -static s32 spinDrawProc(struct MenuItem *item, struct MenuDrawParams *drawParams) { - spinDraw(drawParams->x, drawParams->y, drawParams->font, menuGetCellWidth(item->owner, TRUE), - menuGetCellHeight(item->owner, TRUE), drawParams->color, drawParams->alpha); - return 1; -} - static void issDraw(s32 x, s32 y, struct GfxFont *font, s32 chWidth, s32 chHeight, u32 color, u8 alpha) { gfxModeSet(GFX_MODE_COLOR, GPACK_RGB24A8(color, alpha)); @@ -916,13 +968,13 @@ void trainerDrawSpinBar(s32 x, s32 y, struct GfxFont *font, u32 color, u8 alpha) gfxModeSet(GFX_MODE_COLOR, colorWhite); gfxModeReplace(GFX_MODE_DROPSHADOW, 0); - struct GfxSprite jumpSprite = {spinJumpTex, 0, 0, jumpIconX, iconY, 0.5f, 0.5f}; - struct GfxSprite delaySprite = {spinDelayTex, 0, 0, delayIconX, iconY, 1.f, 1.f}; - gfxSpriteDraw(&jumpSprite); - gfxSpriteDraw(&delaySprite); + struct GfxSprite bootSprite = {spinBootTex, 0, 0, jumpIconX, iconY, 0.5f, 0.5f}; + struct GfxSprite clockSprite = {spinClockTex, 0, 0, delayIconX, iconY, 1.f, 1.f}; + gfxSpriteDraw(&bootSprite); + gfxSpriteDraw(&clockSprite); gfxModePop(GFX_MODE_DROPSHADOW); - // jump frames + // jump length if (spinCanceled) { gfxModeSet(GFX_MODE_COLOR, colorInput); gfxPrintf(font, jumpTextX, textY, "-"); @@ -1047,19 +1099,26 @@ void createTrainerMenu(struct Menu *menu) { /*build spin menu*/ y = 0; - xOffset = 7; + xOffset = 15; spinAButtonTex = resourceLoadPmiconGlobal(ICON_A_BUTTON, 1); - spinJumpTex = resourceLoadPmiconGlobal(ICON_MENU_BOOTS_1, 1); - spinDelayTex = resourceGet(RES_PMICON_CLOCK); + spinBootTex = resourceLoadPmiconGlobal(ICON_MENU_BOOTS_1, 1); + spinClockTex = resourceGet(RES_PMICON_CLOCK); spinZButtonTex = resourceGet(RES_PMICON_Z_BUTTON); spinMenu.selector = menuAddSubmenu(&spinMenu, 0, y++, NULL, "return"); - menuAddStatic(&spinMenu, 0, y, "pin", 0xC0C0C0); - menuAddCheckbox(&spinMenu, xOffset, y++, pinnedTrainerProc, (void *)TRAINER_SPIN); - menuAddStatic(&spinMenu, 0, y, "visual", 0xC0C0C0); + menuAddStatic(&spinMenu, 0, y, "timing bar", 0xC0C0C0); menuAddCheckbox(&spinMenu, xOffset, y, menuByteCheckboxProc, &settings->trainerSpinBarEnabled); menuAddPositioning(&spinMenu, xOffset + 2, y++, spinVisualPositionProc, NULL); - y++; - menuAddStaticCustom(&spinMenu, 0, y++, spinDrawProc, NULL, 0xC0C0C0); + menuAddStatic(&spinMenu, 0, y, "detailed stats", 0xC0C0C0); + menuAddCheckbox(&spinMenu, xOffset, y, pinnedTrainerProc, (void *)TRAINER_SPIN); + menuAddPositioning(&spinMenu, xOffset + 2, y++, trainerPositionProc, NULL); + menuAddStatic(&spinMenu, 1, y, "jump timing", 0xC0C0C0); + menuAddCheckbox(&spinMenu, xOffset, y++, menuByteCheckboxProc, &settings->trainerSpinJumpTiming); + menuAddStatic(&spinMenu, 1, y, "jump length", 0xC0C0C0); + menuAddCheckbox(&spinMenu, xOffset, y++, menuByteCheckboxProc, &settings->trainerSpinJumpLength); + menuAddStatic(&spinMenu, 1, y, "start delay", 0xC0C0C0); + menuAddCheckbox(&spinMenu, xOffset, y++, menuByteCheckboxProc, &settings->trainerSpinStartDelay); + menuAddStatic(&spinMenu, 1, y, "z buffer", 0xC0C0C0); + menuAddCheckbox(&spinMenu, xOffset, y++, menuByteCheckboxProc, &settings->trainerSpinZBuffer); /*build iss menu*/ y = 0; diff --git a/src/sys/settings.c b/src/sys/settings.c index a54aa885..653e6af3 100644 --- a/src/sys/settings.c +++ b/src/sys/settings.c @@ -49,6 +49,11 @@ void settingsLoadDefault(void) { d->trainerBowserEnabled = 0; d->trainerClippyEnabled = 0; d->trainerLzsEnabled = 0; + d->trainerSpinBarEnabled = 0; + d->trainerSpinJumpTiming = 1; + d->trainerSpinJumpLength = 1; + d->trainerSpinStartDelay = 1; + d->trainerSpinZBuffer = 1; d->trainerDisplayPinned = 0; d->menuX = 16; d->menuY = 60; diff --git a/src/sys/settings.h b/src/sys/settings.h index 864579b7..347e1429 100644 --- a/src/sys/settings.h +++ b/src/sys/settings.h @@ -79,6 +79,10 @@ struct SettingsData { u8 trainerQuickJumpsEnabled; u8 trainerHammerCancelsEnabled; u8 trainerSpinBarEnabled; + u8 trainerSpinJumpTiming; + u8 trainerSpinJumpLength; + u8 trainerSpinStartDelay; + u8 trainerSpinZBuffer; u8 trainerDisplayPinned; u8 battleDebug; u8 quickLaunch; From ea36e6ad9edec78192ed6d1313657c097a9a9e71 Mon Sep 17 00:00:00 2001 From: JCog <42006114+JCog@users.noreply.github.com> Date: Sun, 7 Dec 2025 17:00:08 -0600 Subject: [PATCH 10/10] fix imports --- src/fp/practice/trainer.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fp/practice/trainer.c b/src/fp/practice/trainer.c index 4bcaf65c..c6f2fd84 100644 --- a/src/fp/practice/trainer.c +++ b/src/fp/practice/trainer.c @@ -5,7 +5,6 @@ #include "sys/resource.h" #include "sys/settings.h" #include -#include #include enum BowserVariant {