diff --git a/.vscode/settings.json b/.vscode/settings.json index d4e21e0e..2e1b9b70 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -146,7 +146,8 @@ "bgm.h": "c", "*.tcc": "c", "object_ram.h": "c", - "sf64dma.h": "c" + "sf64dma.h": "c", + "string": "c" }, "C_Cpp_Runner.msvcBatchPath": "" } \ No newline at end of file diff --git a/include/mods.h b/include/mods.h index 232bfb7c..823b36d9 100644 --- a/include/mods.h +++ b/include/mods.h @@ -40,14 +40,21 @@ #define MODS_FPS_COUNTER 0 /** - * Object Ram Watch: + * RAM modifier: + * Press C> while paused to cycle modes + * + * Cheats: + * Use D-Pad up and down to select a cheat + * Use D-Pad left and right to set its value + * Press L to set the value or twice to lock it + * + * Object Ram Editor: * Watch up to seven addresses from the player and object arrays - * Press C> while paused to enable/disable - * Use D-PAD to move the cursor and change values + * Use D-Pad to move the cursor and change values * Press L to edit the highlighted value */ -#define MODS_OBJECT_RAM 0 +#define MODS_RAM_MOD 0 /* ************************* */ @@ -59,8 +66,8 @@ void Map_LevelSelect(void); static void Play_RenderFps(void); #endif -#if MODS_OBJECT_RAM == 1 -void ObjectRam_Update(void); +#if MODS_RAM_MOD == 1 +void RamMod_Update(void); #endif #endif diff --git a/include/sf64level.h b/include/sf64level.h index b2195947..c644f77a 100644 --- a/include/sf64level.h +++ b/include/sf64level.h @@ -16,6 +16,7 @@ typedef enum MissionStatus { /* 0 */ MISSION_COMPLETE, /* 1 */ MISSION_ACCOMPLISHED, /* 2 */ MISSION_WARP, + /* 3 */ MISSION_MAX, } MissionStatus; typedef struct Environment { diff --git a/include/sf64player.h b/include/sf64player.h index 4a1a5de5..cbdc735a 100644 --- a/include/sf64player.h +++ b/include/sf64player.h @@ -48,12 +48,14 @@ typedef enum DrawMode { /* 6 */ DRAW_UNK_VS, /* 7 */ DRAW_UNK_7, /* 8 */ DRAW_ENDING, + /* 9 */ DRAW_MAX, } DrawMode; typedef enum LevelMode { /* 0 */ LEVELMODE_ON_RAILS, /* 1 */ LEVELMODE_ALL_RANGE, /* 2 */ LEVELMODE_UNK_2, + /* 3 */ LEVELMODE_MAX, } LevelMode; typedef enum PlayerState1C8 { @@ -71,6 +73,7 @@ typedef enum PlayerState1C8 { /* 11 */ PLAYERSTATE_1C8_ANDROSS_MOUTH, /* 12 */ PLAYERSTATE_1C8_12, /* 13 */ PLAYERSTATE_1C8_VS_STANDBY, + /* 14 */ PLAYERSTATE_1C8_MAX, } PlayerState1C8; typedef enum PlayerShotStatus { @@ -102,6 +105,7 @@ typedef enum LaserStrength { /* 1 */ LASERS_TWIN, /* 2 */ LASERS_HYPER, /* 3 */ LASERS_UNK_3, + /* 4 */ LASERS_MAX, } LaserStrength; typedef struct PlayerShot { diff --git a/src/engine/fox_game.c b/src/engine/fox_game.c index 6e3ea8bb..a676865a 100644 --- a/src/engine/fox_game.c +++ b/src/engine/fox_game.c @@ -579,8 +579,8 @@ void Game_Update(void) { gFillScreenGreen, gFillScreenBlue, gFillScreenAlpha); } Audio_dummy_80016A50(); -#if MODS_OBJECT_RAM == 1 - ObjectRam_Update(); +#if MODS_RAM_MOD == 1 + RamMod_Update(); #endif #if MODS_FPS_COUNTER == 1 Play_RenderFps(); @@ -591,7 +591,7 @@ void Game_Update(void) { #if MODS_FPS_COUNTER == 1 #include "../mods/fpscounter.c" #endif -#if MODS_OBJECT_RAM == 1 +#if MODS_RAM_MOD == 1 #include "../mods/object_ram.c" #endif diff --git a/src/mods/object_ram.c b/src/mods/object_ram.c index a9078167..9c748b7d 100644 --- a/src/mods/object_ram.c +++ b/src/mods/object_ram.c @@ -10,9 +10,10 @@ static RamEntry oRamEntries[7] = { ORAM_OFF, }; -static u32 selectNum = 0; -static s32 oRamActive = true; -static u32 editMode = EDM_TYPE; +static s32 holdTimer = 0; +static s32 selectNum = 0; +static RamModMode oRamActive = RAMMOD_OFF; +static s32 editMode = EDM_TYPE; static s32 editing = false; static s32 editingValue = false; static fu dataTemp; @@ -272,14 +273,14 @@ void ObjectRam_DrawEntry(RamEntry* entry, s32 num) { SET_DRAW_COLOR(EDM_INDEX) Graphics_Printf("%02d", index); - Graphics_DisplaySmallText(entry->x + 32, y, 1.0f, 1.0f, D_801619A0); + Graphics_DisplaySmallText(x + 32, y, 1.0f, 1.0f, D_801619A0); SET_DRAW_COLOR(EDM_MAX) Graphics_DisplaySmallText(x + 50, y, 1.0f, 1.0f, "."); SET_DRAW_COLOR(EDM_OFFSET) Graphics_Printf("%03X", offset); - Graphics_DisplaySmallText(entry->x + 56, y, 1.0f, 1.0f, D_801619A0); + Graphics_DisplaySmallText(x + 56, y, 1.0f, 1.0f, D_801619A0); SET_DRAW_COLOR(EDM_FORMAT) Graphics_DisplaySmallText(x + 90, y, 1.0f, 1.0f, fmtTypes[entry->fmt]); @@ -321,15 +322,7 @@ void ObjectRam_Update(void) { objArrays[ORAM_Player].ptr = gPlayer; objArrays[ORAM_Scenery360].ptr = gScenery360; objArrays[ORAM_Player].count = gCamCount; - contPress = &gControllerPress[gMainController]; - contHold = &gControllerHold[gMainController]; - if ((gPlayState == PLAY_PAUSE) && (contPress->button & R_CBUTTONS)) { - oRamActive = 1 - oRamActive; - } - if (!oRamActive || (gPlayState <= PLAY_INIT)) { - return; - } if (contPress->button & L_TRIG) { editing ^= 1; } @@ -391,3 +384,312 @@ void ObjectRam_Update(void) { gTexturedLines[99].posBB.z = oRamEntries[selectNum].objPtr->pos.z; } } + +bool sCheatSetup = false; + +s32 sCheatOptions[] = { 2, 5, 1, 2, 1, 4, 2, 1, 2 }; +s32 sCheatStates[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +CheatEntry cheats[CHEAT_MAX]; + +void CheatRam_Select(void) { + if (contPress->button & (U_JPAD | D_JPAD)) { + editMode += (contPress->button & D_JPAD) ? 1 : -1; + editMode = WRAP_MODE(editMode, ARRAY_COUNT(cheats)); + selectNum = cheats[editMode].option; + } else if (contPress->button & (L_JPAD | R_JPAD)) { + selectNum += (contPress->button & R_JPAD) ? 1 : -1; + } + selectNum = WRAP_MODE(selectNum, cheats[editMode].optionMax + 1); + cheats[editMode].option = selectNum; +} + +void CheatRam_RepairWings(void) { + if ((gPlayer[0].wings.rightState <= WINGSTATE_BROKEN) || (gPlayer[0].wings.leftState <= WINGSTATE_BROKEN)) { + func_enmy_80067A40(); + } +} + +void CheatRam_SpawnCheckpoint(void) { + static ItemCheckpoint* checkpoint = NULL; + s32 i; + + if ((gLevelMode != LEVELMODE_ON_RAILS) || (gPlayer[0].state_1C8 != PLAYERSTATE_1C8_ACTIVE)) { + return; + } + + if ((checkpoint != NULL) && (checkpoint->obj.status != OBJ_FREE)) { + return; + } + for (i = 0; i < ARRAY_COUNT(gItems); i++) { + if (gItems[i].obj.status == OBJ_FREE) { + Item_Initialize(&gItems[i]); + gItems[i].obj.status = OBJ_ACTIVE; + gItems[i].obj.id = OBJ_ITEM_CHECKPOINT; + gItems[i].obj.pos.x = gPlayer[0].pos.x + gPlayer[0].vel.x; + gItems[i].obj.pos.y = gPlayer[0].pos.y + gPlayer[0].vel.y; + gItems[i].obj.pos.z = gPlayer[0].trueZpos + gPlayer[0].vel.z; + Object_SetInfo(&gItems[i].info, gItems[i].obj.id); + break; + } + } + if (i == ARRAY_COUNT(gItems)) { + checkpoint = NULL; + } +} + +void CheatRam_Setup(void) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(cheats); i++) { + CheatEntry* cheat = &cheats[i]; + + cheat->mode = i; + switch (cheat->mode) { + case CHEAT_SHIELDS: + cheat->option = cheat->optionMax = 100; + SET_CHEAT_RAM(&cheat->ram[0], &gPlayer[0].shields, Play_GetMaxShields()) + break; + case CHEAT_LASERS: + cheat->option = LASERS_HYPER; + cheat->optionMax = LASERS_MAX - 1; + SET_CHEAT_RAM(&cheat->ram[0], &gLaserStrength[0], cheat->option) + cheat->ram[1].func = CheatRam_RepairWings; + break; + case CHEAT_BOMBS: + cheat->option = cheat->optionMax = 9; + SET_CHEAT_RAM(&cheat->ram[0], &gBombCount[0], cheat->option) + break; + case CHEAT_BOOST: + SET_CHEAT_RAM(&cheat->ram[0], &gPlayer[0].boostMeter, 0) + SET_CHEAT_RAM(&cheat->ram[1], &gPlayer[0].boostCooldown, false) + cheat->action = 1; + break; + case CHEAT_LIVES: + cheat->option = cheat->optionMax = 99; + SET_CHEAT_RAM(&cheat->ram[0], &gLifeCount[0], cheat->option) + break; + case CHEAT_TEAMMATES: + cheat->option = 0; + cheat->optionMax = 7; + SET_CHEAT_RAM(&cheat->ram[0], &gTeamShields[TEAM_ID_FALCO], 255) + SET_CHEAT_RAM(&cheat->ram[1], &gTeamShields[TEAM_ID_SLIPPY], 255) + SET_CHEAT_RAM(&cheat->ram[2], &gTeamShields[TEAM_ID_PEPPY], 255) + break; + case CHEAT_SPEED: + cheat->option = 60; + cheat->optionMax = 100; + cheat->action = 1; + SET_CHEAT_RAM(&cheat->ram[0], &gPlayer[0].baseSpeed, cheat->option) + break; + case CHEAT_HITS: + cheat->option = 500; + cheat->optionMax = 999; + SET_CHEAT_RAM(&cheat->ram[0], &gHitCount, cheat->option); + break; + case CHEAT_CHECKPOINT: + cheat->action = -1; + cheat->ram[0].func = CheatRam_SpawnCheckpoint; + break; + case CHEAT_COMPLETE: + cheat->option = MISSION_COMPLETE; + cheat->optionMax = MISSION_WARP; + cheat->action = 1; + if ((gCurrentLevel == LEVEL_METEO) || (gCurrentLevel == LEVEL_SECTOR_X)) {} + SET_CHEAT_RAM(&cheat->ram[0], &gMissionStatus, cheat->option) + break; + } + } +} + +void CheatRam_SetValues(CheatRamEntry* cheat) { + s32 i; + + for (i = 0; i < 4; i++) { + if (cheat[i].addr != NULL) { + switch (cheat[i].size) { + case 0: + cheat[i].func(); + break; + case 1: + *((u8*) cheat[i].addr) = cheat[i].data.i; + break; + case 2: + *((u16*) cheat[i].addr) = cheat[i].data.i; + break; + case 3: + break; + case 4: + *cheat[i].addr = cheat[i].data; + break; + } + } + } +} + +void CheatRam_UpdateEntry(CheatEntry* cheat) { + s32 teamnum; + s32 i; + s32 press = false; + s32 doublepress = false; + + if (holdTimer == 0) { + holdTimer = 10; + press = true; + } else if (holdTimer != 0) { + doublepress = true; + } + if (cheat->action == 0) { + cheat->hold ^= doublepress; + } else if (cheat->action == 1) { + cheat->hold ^= press; + } + + switch (editMode) { + case CHEAT_SHIELDS: + cheat->ram[0].data.i = (cheat->option / 100.0f) * Play_GetMaxShields(); + break; + case CHEAT_BOOST: + break; + case CHEAT_SPEED: + cheat->ram[0].data.f = cheat->hold ? cheat->option : 40.0f; + break; + case CHEAT_TEAMMATES: + teamnum = (cheat->option % 4) - 1; + for (i = 0; i < 3; i++) { + cheat->ram[i].data.i = (cheat->option > 3) ? -1 : 255; + cheat->ram[i].size = (teamnum == -1) ? 4 : 3; + } + if (teamnum != -1) { + cheat->ram[teamnum].size = 4; + } + break; + case CHEAT_COMPLETE: + if ((gCurrentLevel == LEVEL_CORNERIA) && (gPlayer[0].state_1C8 != PLAYERSTATE_1C8_LEVEL_COMPLETE)) { + if ((gLevelMode == LEVELMODE_ON_RAILS) && (cheat->option == MISSION_COMPLETE)) { + gScenery360 = Memory_Allocate(200 * sizeof(Scenery360)); + gLevelMode = LEVELMODE_ALL_RANGE; + } else if ((gLevelMode == LEVELMODE_ALL_RANGE) && (cheat->option == MISSION_ACCOMPLISHED)) { + gLevelMode = LEVELMODE_ON_RAILS; + } + } + gPlayer[0].state_1C8 = PLAYERSTATE_1C8_LEVEL_COMPLETE; + default: + cheat->ram[0].data.i = cheat->option; + break; + } + + CheatRam_SetValues(cheat->ram); +} + +const char* cheatNames[] = { "SHIELDS", "LASERS", "BOMBS", "BOOST", "LIVES", + "TEAM", "SPEED", "HITS", "CHECKPOINT", "MISSION" }; +const char* laserNames[] = { "SINGLE", "TWIN", "HYPER", "UNUSED" }; +const char* teamNames[] = { "ALL", "FALCO", "SLIPPY", "PEPPY" }; +const char* statusNames[] = { "COMPLETE", "ACCOMPLISHED", "WARP" }; + +void CheatRam_DrawEntry(CheatMode mode, s32 x, s32 y, s32 option) { + switch (mode) { + case CHEAT_SHIELDS: + case CHEAT_HITS: + Graphics_Printf("%s: %3d", cheatNames[mode], option); + break; + case CHEAT_LASERS: + Graphics_Printf("%s: %s", cheatNames[mode], laserNames[option]); + break; + case CHEAT_COMPLETE: + Graphics_Printf("%s: %s", cheatNames[mode], statusNames[option]); + break; + case CHEAT_LIVES: + case CHEAT_BOMBS: + case CHEAT_SPEED: + Graphics_Printf("%s: %d", cheatNames[mode], option); + break; + case CHEAT_CHECKPOINT: + case CHEAT_BOOST: + Graphics_Printf("%s", cheatNames[mode]); + break; + case CHEAT_TEAMMATES: + Graphics_Printf("%s: %s %s", cheatNames[mode], (option > 3) ? "DOWN" : "HEAL", teamNames[option % 4]); + break; + } + Graphics_DisplaySmallText(x, y, 1.0f, 1.0f, D_801619A0); +} + +s32 medalCount[] = { 150, 200, 150, 300, 0, 200, 100, 250, 200, 0, 150, 100, 150, 50, 0, 150, 150, 100, 200 }; + +void CheatRam_Update(void) { + s32 i; + s32 x = 10; + s32 y = 50; + + if (!sCheatSetup) { + CheatRam_Setup(); + selectNum = cheats[editMode].option; + sCheatSetup = true; + } + CheatRam_Select(); + if (holdTimer != 0) { + holdTimer--; + } + if (contPress->button & L_TRIG) { + CheatRam_UpdateEntry(&cheats[editMode]); + } + for (i = 0; i < ARRAY_COUNT(cheats); i++) { + if ((cheats[i].action != -1) && (cheats[i].hold || ((i == editMode) && (holdTimer != 0)))) { + CheatRam_SetValues(cheats[i].ram); + } + } + RCP_SetupDL(&gMasterDisp, SETUPDL_76); + gDPSetPrimColor(gMasterDisp++, 0, 0, 255, 255, 0, 255); + Graphics_DisplaySmallText(x, y, 1.0f, 1.0f, "CHEATS"); + y += 14; + for (i = 0; i < ARRAY_COUNT(cheats); i++) { + if (cheats[i].hold) { + gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 0, 0, 255); + } else if (i == editMode) { + if (holdTimer == 0) { + gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 170, 0, 255); + } else { + gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 85, 0, 255); + } + } else { + gDPSetPrimColor(gMasterDisp++, 0x00, 0x00, 255, 255, 0, 255); + } + + CheatRam_DrawEntry(cheats[i].mode, x, y + 11 * i, cheats[i].option); + } +} + +void RamMod_Update(void) { + contPress = &gControllerPress[gMainController]; + contHold = &gControllerHold[gMainController]; + + if ((gGameState != GSTATE_PLAY) || (gPlayState <= PLAY_INIT)) { + return; + } + + if ((contPress->button & START_BUTTON) && (gPlayer[0].state_1C8 == PLAYERSTATE_1C8_LEVEL_COMPLETE)) { + gPlayer[0].state_1C8 = PLAYERSTATE_1C8_NEXT; + Audio_FadeOutAll(10); + gFadeoutType = 4; + gLeveLClearStatus[gCurrentLevel] = Play_CheckMedalStatus(medalCount[gCurrentLevel]) + 1; + cheats[CHEAT_COMPLETE].hold = false; + } + + if ((gPlayState == PLAY_PAUSE) && (contPress->button & R_CBUTTONS)) { + oRamActive++; + oRamActive = WRAP_MODE(oRamActive, RAMMOD_MAX); + } + + switch (oRamActive) { + case RAMMOD_OFF: + break; + case RAMMOD_CHEAT: + CheatRam_Update(); + break; + case RAMMOD_OBJECT: + ObjectRam_Update(); + break; + } +} diff --git a/src/mods/object_ram.h b/src/mods/object_ram.h index 540919d3..2241a257 100644 --- a/src/mods/object_ram.h +++ b/src/mods/object_ram.h @@ -8,7 +8,7 @@ #define ORAM_OFF \ { 0, 0, 0, 0, 0, 0, 0, 0, 0 } -#define WRAP_MODE(val, max) ((u8) ((val) + (max)) % max) +#define WRAP_MODE(val, max) ((u8) ((val) + (max)) % (max)) #define OBJ_ARRAY_INFO(objarr, name) \ { objarr, sizeof(*objarr), ARRAY_COUNT(objarr), name } @@ -21,17 +21,65 @@ typedef struct ObjArrayInfo { } ObjArrayInfo; typedef struct RamEntry { - u8 type; - u8 index; + s16 type; + s16 index; s16 offset; Object* objPtr; void* dataPtr; - u8 fmt; + s16 fmt; u8 width; u16 x; u16 y; } RamEntry; +typedef enum RamModMode { + RAMMOD_OFF, + RAMMOD_CHEAT, + RAMMOD_OBJECT, + RAMMOD_MAX, +} RamModMode; + +typedef enum CheatMode { + CHEAT_SHIELDS, + CHEAT_LASERS, + CHEAT_BOMBS, + CHEAT_BOOST, + CHEAT_LIVES, + CHEAT_TEAMMATES, + CHEAT_SPEED, + CHEAT_HITS, + CHEAT_CHECKPOINT, + CHEAT_COMPLETE, + CHEAT_MAX, +} CheatMode; + +typedef struct CheatRamEntry { + union { + fu* addr; + void (*func)(void); + }; + s32 size; + fu data; +} CheatRamEntry; + +#define SET_CHEAT_RAM(ram, address, value) \ + { \ + (ram)->addr = (void*)(address); \ + (ram)->size = sizeof(*(address)); \ + (ram)->data.i = (value); \ + } + + + +typedef struct CheatEntry { + CheatMode mode; + CheatRamEntry ram[4]; + s32 option; + s32 optionMax; + s32 action; + bool hold; +} CheatEntry; + typedef enum ObjectRamType { ORAM_NONE, ORAM_Player, diff --git a/src/overlays/ovl_menu/fox_map.c b/src/overlays/ovl_menu/fox_map.c index 7e392b91..1bb2080a 100644 --- a/src/overlays/ovl_menu/fox_map.c +++ b/src/overlays/ovl_menu/fox_map.c @@ -2263,7 +2263,7 @@ void Map_801A116C(void) { Matrix_SetGfxMtx(&gMasterDisp); - wings.rightState = wings.leftState = 2; + wings.rightState = wings.leftState = WINGSTATE_INTACT; // clang-format off wings.unk_18 = wings.unk_1C = wings.unk_20 = wings.unk_24 = wings.unk_04 = wings.unk_08 = wings.unk_0C = wings.unk_10 = wings.unk_28 = 0.0f;