diff --git a/include/config/config_rom.h b/include/config/config_rom.h index a5927c3d..db15706b 100644 --- a/include/config/config_rom.h +++ b/include/config/config_rom.h @@ -10,12 +10,32 @@ */ #define INTERNAL_ROM_NAME "HackerSM64 " +/** + * Force the game to delete any existing save data originating from a different hack. This requires INTERNAL_ROM_NAME to be unique to work properly. + * It is recommended to enable this if any significant changes to the save file are made that could cause issues with this or other hacks. + * NOTE: Using save editors with this define will likely just end up wiping your save, since SM64 specific save editors most likely use hardcoded save magic. + */ +// #define UNIQUE_SAVE_DATA + /** * Enables Rumble Pak Support. * Currently not recommended, as it may cause random crashes. */ // #define ENABLE_RUMBLE (1 || VERSION_SH) +/** + * The maximum number of supported players/controllers. 1-4. + * This will save performance if the player has extra unused controllers plugged in. + * NOTE: Default is 2, maximum is 4. + * NOTE: This needs to be at least 2 for now for gamecube controller swap to work. + */ +#define MAX_NUM_PLAYERS 2 + +/** + * Informs supported emulators to default to gamecube controller inputs. + */ +// #define USE_GAMECUBE_CONTROLLER + /** * Screen Size Defines. */ @@ -29,18 +49,6 @@ #define BORDER_HEIGHT_CONSOLE 0 #define BORDER_HEIGHT_EMULATOR 0 -/** - * Force the game to delete any existing save data originating from a different hack. This requires INTERNAL_ROM_NAME to be unique to work properly. - * It is recommended to enable this if any significant changes to the save file are made that could cause issues with this or other hacks. - * NOTE: Using save editors with this define will likely just end up wiping your save, since SM64 specific save editors most likely use hardcoded save magic. - */ -// #define UNIQUE_SAVE_DATA - -/** - * Informs supported emulators to default to gamecube controller inputs. - */ -// #define USE_GAMECUBE_CONTROLLER - /** * RCVI hack. Increases performance on emulator, and does nothing on console. * Might break on some emulators. Use at your own risk, and don't use it unless you actually need the extra performance. diff --git a/include/types.h b/include/types.h index cd9dcd57..dac51488 100644 --- a/include/types.h +++ b/include/types.h @@ -33,20 +33,18 @@ struct Config { }; struct Controller { - /*0x00*/ s16 rawStickX; // - /*0x02*/ s16 rawStickY; // - /*0x04*/ f32 stickX; // [-64, 64] positive is right - /*0x08*/ f32 stickY; // [-64, 64] positive is up - /*0x0C*/ f32 stickMag; // distance from center [0, 64] - /*0x10*/ u16 buttonDown; - /*0x12*/ u16 buttonPressed; - /*0x14*/ u16 buttonReleased; - /*0x18*/ OSContStatus *statusData; - /*0x1C*/ OSContPadEx *controllerData; -#if ENABLE_RUMBLE - /*0x20*/ s32 port; -#endif -}; + /*0x00*/ s16 rawStickX; // Analog stick [-80, 80] positive is right. Used for menus. + /*0x02*/ s16 rawStickY; // Analog stick [-80, 80] positive is up. Used for menus. + /*0x04*/ f32 stickX; // Analog stick [-64, 64] positive is right. Used for gameplay. + /*0x08*/ f32 stickY; // Analog stick [-64, 64] positive is up. Used for gameplay. + /*0x0C*/ f32 stickMag; // Analog stick distance from center [0, 64]. Used for gameplay. + /*0x10*/ u16 buttonDown; // Buttons held down on the current frame. + /*0x12*/ u16 buttonPressed; // Buttons pressed on the current frame but not held on the previous frame. + /*0x14*/ u16 buttonReleased; // Burrons released on the current frame and held on the previous frame. + /*0x18*/ OSContStatus* statusData; // Pointer to the controller status data in gControllerStatuses. + /*0x1C*/ OSContPadEx* controllerData; // Pointer to the raw input data in gControllerPads. + /*0x20*/ s32 port; // The port index this controller is plugged into [0, 3]. +}; /*0x24*/ // -- Booleans -- diff --git a/src/game/game_init.c b/src/game/game_init.c index 67e760fc..db071f56 100644 --- a/src/game/game_init.c +++ b/src/game/game_init.c @@ -39,11 +39,10 @@ u8 *gGfxPoolEnd; struct GfxPool *gGfxPool; // OS Controllers -struct Controller gControllers[4]; -OSContStatus gControllerStatuses[4]; -OSContPadEx gControllerPads[4]; -u8 gControllerBits; -s8 gGamecubeControllerPort = -1; // HackerSM64: This is set to -1 if there's no GC controller, 0 if there's one in the first port and 1 if there's one in the second port. +struct Controller gControllers[MAX_NUM_PLAYERS]; +OSContStatus gControllerStatuses[MAXCONTROLLERS]; +OSContPadEx gControllerPads[MAXCONTROLLERS]; +u8 gControllerBits = 0b0000; u8 gIsConsole = TRUE; // Needs to be initialized before audio_reset_session is called u8 gCacheEmulated = TRUE; u8 gBorderHeight; @@ -86,11 +85,11 @@ u16 sRenderingFramebuffer = 0; // Goddard Vblank Function Caller void (*gGoddardVblankCallback)(void) = NULL; -// Defined controller slots -struct Controller *gPlayer1Controller = &gControllers[0]; -struct Controller *gPlayer2Controller = &gControllers[1]; -struct Controller *gPlayer3Controller = &gControllers[2]; -struct Controller *gPlayer4Controller = &gControllers[3]; +// Defined controller slots. Anything above MAX_NUM_PLAYERS will be unused. +struct Controller* const gPlayer1Controller = &gControllers[0]; +struct Controller* const gPlayer2Controller = &gControllers[1]; +struct Controller* const gPlayer3Controller = &gControllers[2]; +struct Controller* const gPlayer4Controller = &gControllers[3]; // Title Screen Demo Handler struct DemoInput *gCurrDemoInput = NULL; @@ -492,36 +491,25 @@ UNUSED static void record_demo(void) { */ void run_demo_inputs(void) { // Eliminate the unused bits. - gControllers[0].controllerData->button &= VALID_BUTTONS; + gPlayer1Controller->controllerData->button &= VALID_BUTTONS; // Check if a demo inputs list exists and if so, // run the active demo input list. if (gCurrDemoInput != NULL) { - // Clear player 2's inputs if they exist. Player 2's controller - // cannot be used to influence a demo. At some point, Nintendo - // may have planned for there to be a demo where 2 players moved - // around instead of just one, so clearing player 2's influence from - // the demo had to have been necessary to perform this. Co-op mode, perhaps? - if (gControllers[1].controllerData != NULL) { - gControllers[1].controllerData->stick_x = 0; - gControllers[1].controllerData->stick_y = 0; - gControllers[1].controllerData->button = 0; - } - // The timer variable being 0 at the current input means the demo is over. // Set the button to the END_DEMO mask to end the demo. if (gCurrDemoInput->timer == 0) { - gControllers[0].controllerData->stick_x = 0; - gControllers[0].controllerData->stick_y = 0; - gControllers[0].controllerData->button = END_DEMO; + gPlayer1Controller->controllerData->stick_x = 0; + gPlayer1Controller->controllerData->stick_y = 0; + gPlayer1Controller->controllerData->button = END_DEMO; } else { // Backup the start button if it is pressed, since we don't want the // demo input to override the mask where start may have been pressed. - u16 startPushed = gControllers[0].controllerData->button & START_BUTTON; + u16 startPushed = (gPlayer1Controller->controllerData->button & START_BUTTON); // Perform the demo inputs by assigning the current button mask and the stick inputs. - gControllers[0].controllerData->stick_x = gCurrDemoInput->rawStickX; - gControllers[0].controllerData->stick_y = gCurrDemoInput->rawStickY; + gPlayer1Controller->controllerData->stick_x = gCurrDemoInput->rawStickX; + gPlayer1Controller->controllerData->stick_y = gCurrDemoInput->rawStickY; // To assign the demo input, the button information is stored in // an 8-bit mask rather than a 16-bit mask. this is because only @@ -530,11 +518,11 @@ void run_demo_inputs(void) { // upper 4 bits (A, B, Z, and Start) and shift then left by 8 to // match the correct input mask. We then add this to the masked // lower 4 bits to get the correct button mask. - gControllers[0].controllerData->button = + gPlayer1Controller->controllerData->button = ((gCurrDemoInput->buttonMask & 0xF0) << 8) + ((gCurrDemoInput->buttonMask & 0xF)); // If start was pushed, put it into the demo sequence being input to end the demo. - gControllers[0].controllerData->button |= startPushed; + gPlayer1Controller->controllerData->button |= startPushed; // Run the current demo input's timer down. if it hits 0, advance the demo input list. if (--gCurrDemoInput->timer == 0) { @@ -604,38 +592,39 @@ void read_controller_inputs(s32 threadID) { run_demo_inputs(); #endif - for (i = 0; i < 4; i++) { - struct Controller *controller = &gControllers[i]; + for (i = 0; i < MAX_NUM_PLAYERS; i++) { + struct Controller* controller = &gControllers[i]; + OSContPadEx* controllerData = controller->controllerData; // if we're receiving inputs, update the controller struct with the new button info. if (controller->controllerData != NULL) { // HackerSM64: Swaps Z and L, only on console, and only when playing with a GameCube controller. - if (gIsConsole && i == gGamecubeControllerPort) { - u32 oldButton = controller->controllerData->button; + if (__osControllerTypes[controller->port] == CONT_TYPE_GCN) { + u32 oldButton = controllerData->button; u32 newButton = oldButton & ~(Z_TRIG | L_TRIG); if (oldButton & Z_TRIG) { newButton |= L_TRIG; } - if (controller->controllerData->l_trig > 85) { // How far the player has to press the L trigger for it to be considered a Z press. 64 is about 25%. 127 would be about 50%. + if (controllerData->l_trig > 85) { // How far the player has to press the L trigger for it to be considered a Z press. 64 is about 25%. 127 would be about 50%. newButton |= Z_TRIG; } - controller->controllerData->button = newButton; + controllerData->button = newButton; } - controller->rawStickX = controller->controllerData->stick_x; - controller->rawStickY = controller->controllerData->stick_y; - controller->buttonPressed = ~controller->buttonDown & controller->controllerData->button; - controller->buttonReleased = ~controller->controllerData->button & controller->buttonDown; + controller->rawStickX = controllerData->stick_x; + controller->rawStickY = controllerData->stick_y; + controller->buttonPressed = (~controller->buttonDown & controllerData->button); + controller->buttonReleased = (~controllerData->button & controller->buttonDown); // 0.5x A presses are a good meme - controller->buttonDown = controller->controllerData->button; + controller->buttonDown = controllerData->button; adjust_analog_stick(controller); } else { // otherwise, if the controllerData is NULL, 0 out all of the inputs. - controller->rawStickX = 0; - controller->rawStickY = 0; - controller->buttonPressed = 0; - controller->buttonReleased = 0; - controller->buttonDown = 0; - controller->stickX = 0; - controller->stickY = 0; - controller->stickMag = 0; + controller->rawStickX = 0x0000; + controller->rawStickY = 0x0000; + controller->buttonPressed = 0x0000; + controller->buttonReleased = 0x0000; + controller->buttonDown = 0x0000; + controller->stickX = 0.0f; + controller->stickY = 0.0f; + controller->stickMag = 0.0f; } } } @@ -644,7 +633,9 @@ void read_controller_inputs(s32 threadID) { * Initialize the controller structs to point at the OSCont information. */ void init_controllers(void) { - s16 port, cont; + struct Controller* controller = NULL; + int port, cont = 0; + int lastUsedPort = -1; // Set controller 1 to point to the set of status/pads for input 1 and // init the controllers. @@ -664,29 +655,43 @@ void init_controllers(void) { #endif // Loop over the 4 ports and link the controller structs to the appropriate status and pad. - for (cont = 0, port = 0; port < 4 && cont < 4; port++) { + for (port = 0; port < MAXCONTROLLERS; port++) { + if (cont >= MAX_NUM_PLAYERS) { + break; + } + // Is controller plugged in? if (gControllerBits & (1 << port)) { // The game allows you to have just 1 controller plugged // into any port in order to play the game. this was probably // so if any of the ports didn't work, you can have controllers // plugged into any of them and it will work. -#if ENABLE_RUMBLE - gControllers[cont].port = port; + controller = &gControllers[cont]; + controller->statusData = &gControllerStatuses[port]; + controller->controllerData = &gControllerPads[port]; + controller->port = port; + + lastUsedPort = port; + + cont++; + } + } + +#if (MAX_NUM_PLAYERS >= 2) + //! Some flashcarts (eg. ED64p) don't let you start a ROM with a GameCube controller in port 1, + // so if port 1 is an N64 controller and port 2 is a GC controller, swap them. + if (gIsConsole) { + if (__osControllerTypes[0] == CONT_TYPE_N64 + && __osControllerTypes[1] == CONT_TYPE_GCN) { + struct Controller temp = gControllers[0]; + gControllers[0] = gControllers[1]; + gControllers[1] = temp; + } + } #endif - gControllers[cont].statusData = &gControllerStatuses[port]; - gControllers[cont++].controllerData = &gControllerPads[port]; - } - } - if ((__osControllerTypes[1] == CONT_TYPE_GCN) && (gIsConsole)) { - gGamecubeControllerPort = 1; - gPlayer1Controller = &gControllers[1]; - } else { - if (__osControllerTypes[0] == CONT_TYPE_GCN) { - gGamecubeControllerPort = 0; - } - gPlayer1Controller = &gControllers[0]; - } + + // Disable the ports after the last used one. + osContSetCh(lastUsedPort + 1); } // Game thread core diff --git a/src/game/game_init.h b/src/game/game_init.h index 354a85ac..8e97343b 100644 --- a/src/game/game_init.h +++ b/src/game/game_init.h @@ -30,9 +30,9 @@ enum ZBmodes { CLEAR_ZBUFFER = 1, }; -extern struct Controller gControllers[4]; -extern OSContStatus gControllerStatuses[4]; -extern OSContPadEx gControllerPads[4]; +extern struct Controller gControllers[MAX_NUM_PLAYERS]; +extern OSContStatus gControllerStatuses[MAXCONTROLLERS]; +extern OSContPadEx gControllerPads[MAXCONTROLLERS]; extern OSMesgQueue gGameVblankQueue; extern OSMesgQueue gGfxVblankQueue; extern OSMesg gGameMesgBuf[1]; @@ -47,7 +47,6 @@ extern Gfx *gDisplayListHead; extern u8 *gGfxPoolEnd; extern struct GfxPool *gGfxPool; extern u8 gControllerBits; -extern s8 gGamecubeControllerPort; extern u8 gIsConsole; extern u8 gCacheEmulated; extern u8 gBorderHeight; @@ -64,10 +63,10 @@ extern s8 gSramProbe; #endif extern void (*gGoddardVblankCallback)(void); -extern struct Controller *gPlayer1Controller; -extern struct Controller *gPlayer2Controller; -extern struct Controller *gPlayer3Controller; -extern struct Controller *gPlayer4Controller; +extern struct Controller* const gPlayer1Controller; +extern struct Controller* const gPlayer2Controller; +extern struct Controller* const gPlayer3Controller; +extern struct Controller* const gPlayer4Controller; extern struct DemoInput *gCurrDemoInput; extern u16 gDemoInputListID; extern struct DemoInput gRecordedDemoInput; diff --git a/src/game/mario.c b/src/game/mario.c index ebf3bb26..b58157c6 100644 --- a/src/game/mario.c +++ b/src/game/mario.c @@ -1873,12 +1873,8 @@ void init_mario_from_save_file(void) { gMarioState->spawnInfo = &gPlayerSpawnInfos[0]; gMarioState->statusForCamera = &gPlayerCameraState[0]; gMarioState->marioBodyState = &gBodyStates[0]; + gMarioState->controller = &gControllers[0]; gMarioState->animList = &gMarioAnimsBuf; - if (gIsConsole && __osControllerTypes[1] == CONT_TYPE_GCN) { - gMarioState->controller = &gControllers[1]; - } else { - gMarioState->controller = &gControllers[0]; - } gMarioState->numCoins = 0; gMarioState->numStars = save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_MIN - 1, COURSE_MAX - 1);