diff --git a/source/ngc/gameinput.cpp b/source/ngc/gameinput.cpp index 30d65f7..bb7fa38 100644 --- a/source/ngc/gameinput.cpp +++ b/source/ngc/gameinput.cpp @@ -1900,6 +1900,16 @@ u32 MetroidZeroInput(unsigned short pad) u32 J = StandardMovement(pad); u32 jp = PAD_ButtonsHeld(pad); u8 BallState = CPUReadByte(0x30015df); // 0 = stand, 1 = crouch, 2 = ball + u16 Health = CPUReadByte(0x3001536); // 0 = stand, 1 = crouch, 2 = ball + static u16 OldHealth = 0; + + static int RumbleCount = 0; + // Rumble when they lose health! + if (Health < OldHealth) + if (RumbleCount < 20) + RumbleCount = 20; + OldHealth = Health; + static int Morph = 0; static int AimCount = 0; static int MissileCount = 0; @@ -1911,9 +1921,15 @@ u32 MetroidZeroInput(unsigned short pad) WPADData * wp = WPAD_Data(pad); - // Visor + if (wp->btns_h & WPAD_BUTTON_UP) + J |= VBA_SPEED; + if (wp->btns_h & WPAD_BUTTON_LEFT) + J |= VBA_BUTTON_L; + if (wp->btns_h & WPAD_BUTTON_RIGHT) + J |= VBA_BUTTON_R; + // Visor doesn't exist, use as start button if (wp->btns_h & WPAD_BUTTON_MINUS) - ; + J |= VBA_BUTTON_START; // Hyper (toggle super missiles) if (wp->btns_h & WPAD_BUTTON_PLUS) J |= VBA_BUTTON_SELECT; @@ -1969,7 +1985,7 @@ u32 MetroidZeroInput(unsigned short pad) } // Morph Bdall - if ((wp->exp.type == WPAD_EXP_NUNCHUK) && (wp->btns_h & WPAD_NUNCHUK_BUTTON_C)) + if ((wp->exp.type == WPAD_EXP_NUNCHUK) && (wp->btns_d & WPAD_NUNCHUK_BUTTON_C)) { if (BallState == 2) { // ball @@ -2049,6 +2065,227 @@ u32 MetroidZeroInput(unsigned short pad) break; } + switch (Morph) + { + case 1: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_DOWN; + Morph = 2; + break; + case 2: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + Morph = 3; + break; + case 3: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_DOWN; + Morph = 0; + break; + case -1: + case -2: + case -3: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_UP; + Morph--; + break; + case -4: + case -5: + case -6: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + Morph--; + break; + case -7: + case -8: + case -9: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_UP; + Morph--; + break; + case -10: + Morph = 0; + break; + } + + // Menu + if (jp & PAD_BUTTON_START) + J |= VBA_BUTTON_START; + + if (RumbleCount>0) + { + WPAD_Rumble(pad, 1); + RumbleCount--; + } + else WPAD_Rumble(pad, 0); + + if ((J & 48) == 48) + J &= ~16; + if ((J & 192) == 192) + J &= ~128; + + return J; +} + +u32 MetroidFusionInput(unsigned short pad) +{ + u32 J = StandardMovement(pad); + u32 jp = PAD_ButtonsHeld(pad); + u8 BallState = CPUReadByte(0x3001329); // 0 = stand, 2 = crouch, 5 = ball + u16 Health = CPUReadHalfWord(0x3001310); + static u16 OldHealth = 0; + + static int RumbleCount = 0; + // Rumble when they lose health! + if (Health < OldHealth) + if (RumbleCount < 20) + RumbleCount = 20; + OldHealth = Health; + + static int Morph = 0; + static int AimCount = 0; + static int MissileCount = 0; + + if (BallState == 5) // Can't exit morph ball without pressing C! + J &= ~VBA_UP; + if (BallState == 2) // Can't enter morph ball without pressing C! + J &= ~VBA_DOWN; + + WPADData * wp = WPAD_Data(pad); + + if (wp->btns_h & WPAD_BUTTON_LEFT) + J |= VBA_BUTTON_L; + if (wp->btns_h & WPAD_BUTTON_RIGHT) + J |= VBA_BUTTON_R; + + // Visor doesn't exist, just use as select button + if (wp->btns_h & WPAD_BUTTON_MINUS) + J |= VBA_BUTTON_SELECT; + // Hyper isn't available, just use start button as start + if (wp->btns_h & WPAD_BUTTON_PLUS) + J |= VBA_BUTTON_START; + // Map + if (wp->btns_h & WPAD_BUTTON_1) + J |= VBA_BUTTON_START; + // Hint + if (wp->btns_h & WPAD_BUTTON_2) + J |= VBA_BUTTON_R; + // Z-Target + if ((wp->exp.type == WPAD_EXP_NUNCHUK) && (wp->btns_h & WPAD_NUNCHUK_BUTTON_Z)) + J |= VBA_SPEED; + + // Jump + if (wp->btns_h & WPAD_BUTTON_B && BallState!=5) + J |= VBA_BUTTON_A; + else if (BallState==5 && fabs(wp->gforce.y)> 1.5) + J |= VBA_BUTTON_A; + // Fire + if (wp->btns_h & WPAD_BUTTON_A) + J |= VBA_BUTTON_B; + // Aim + if (wp->orient.pitch < -45 && BallState !=5) + { + J |= VBA_UP; + AimCount = 0; + } + else if (wp->orient.pitch < -22 && BallState !=5) + { + if (AimCount>=0) AimCount = -1; + } + else if (wp->orient.pitch> 45 && BallState !=5) + { + if (AimCount<10) AimCount=10; + } + else if (wp->orient.pitch> 22 && BallState !=5) + { + if (AimCount<=0 || AimCount>=10) AimCount = 1; + } + else + { + AimCount=0; + } + + // Morph Bdall + if ((wp->exp.type == WPAD_EXP_NUNCHUK) && (wp->btns_d & WPAD_NUNCHUK_BUTTON_C)) + { + if (BallState == 5) + { // ball + Morph = -1; + } + else if (BallState == 2) + { + Morph = 2; + } + else + { + Morph = 1; + } + } + // Missile + if (wp->btns_h & WPAD_BUTTON_DOWN) + { + MissileCount = 1; + } + + switch (AimCount) + { + case 1: + J &= ~(VBA_UP | VBA_DOWN | VBA_BUTTON_L); + J |= VBA_BUTTON_L; + AimCount++; + break; + case 2: + J &= ~(VBA_UP | VBA_DOWN | VBA_BUTTON_L); + J |= VBA_BUTTON_L | VBA_DOWN; + AimCount++; + break; + case 3: + J |= VBA_BUTTON_L; + break; + case -1: + J &= ~(VBA_UP | VBA_DOWN | VBA_BUTTON_L); + J |= 0; + AimCount--; + break; + case -2: + J &= ~(VBA_UP | VBA_DOWN | VBA_BUTTON_L); + J |= VBA_BUTTON_L; + AimCount--; + break; + case -3: + J |= VBA_BUTTON_L; + break; + + case 10: + case 11: + J |= VBA_BUTTON_A; + AimCount++; + break; + case 12: + case 13: + J |= VBA_DOWN; + AimCount++; + break; + case 14: + J |= VBA_DOWN; + break; + } + + switch (MissileCount) + { + case 1: + case 2: + J |= VBA_BUTTON_R; + MissileCount++; + break; + case 3: + case 4: + J |= VBA_BUTTON_R | VBA_BUTTON_B; + MissileCount++; + break; + case 5: + MissileCount = 0; + break; + } + switch (Morph) { case 1: @@ -2091,6 +2328,321 @@ u32 MetroidZeroInput(unsigned short pad) if (jp & PAD_BUTTON_START) J |= VBA_BUTTON_START; + if (RumbleCount>0) + { + WPAD_Rumble(pad, 1); + RumbleCount--; + } + else WPAD_Rumble(pad, 0); + + if ((J & 48) == 48) + J &= ~16; + if ((J & 192) == 192) + J &= ~128; + + return J; +} + +u32 Metroid1Input(unsigned short pad) +{ + u32 J = StandardMovement(pad); + u32 jp = PAD_ButtonsHeld(pad); + u8 BallState = CPUReadByte(0x3007500); // 3 = ball, other = stand + u8 MissileState = CPUReadByte(0x300730E); // 1 = missile, 0 = beam + u16 Health = CPUReadHalfWord(0x3007306); // Binary Coded Decimal + static u16 OldHealth = 0; + + static int RumbleCount = 0; + // Rumble when they lose health! + if (Health < OldHealth) + if (RumbleCount < 20) + RumbleCount = 20; + OldHealth = Health; + + static int Morph = 0; + + if (BallState == 3) // Can't exit morph ball without pressing C! + J &= ~VBA_UP; + if (BallState != 3) // Can't enter morph ball without pressing C! + J &= ~VBA_DOWN; + + WPADData * wp = WPAD_Data(pad); + + // No visors + if (wp->btns_h & WPAD_BUTTON_MINUS) + J |= VBA_BUTTON_SELECT; + // Start button just pauses + if (wp->btns_h & WPAD_BUTTON_PLUS) + J |= VBA_BUTTON_START; + // Map (doesn't exist) + if (wp->btns_h & WPAD_BUTTON_1) + J |= VBA_BUTTON_START; + // Hint (doesn't exist) + if (wp->btns_h & WPAD_BUTTON_2) + J |= VBA_SPEED; + // Z-Target + if ((wp->exp.type == WPAD_EXP_NUNCHUK) && (wp->btns_h & WPAD_NUNCHUK_BUTTON_Z)) + ; + + // Jump + if (wp->btns_h & WPAD_BUTTON_B && BallState!=5) + J |= VBA_BUTTON_A; + else if (BallState==5 && fabs(wp->gforce.y)> 1.5) + J |= VBA_BUTTON_A; + // Fire + if (wp->btns_h & WPAD_BUTTON_A) { + if (MissileState) + J |= VBA_BUTTON_SELECT; + else + J |= VBA_BUTTON_B; + } + + // Aim + if (wp->orient.pitch < -45 && BallState !=3) + J |= VBA_UP; + + // Morph Ball + if ((wp->exp.type == WPAD_EXP_NUNCHUK) && (wp->btns_d & WPAD_NUNCHUK_BUTTON_C)) + { + if (BallState == 3) + { // ball + Morph = -1; + } + else + { + Morph = 2; + } + } + // Missile + if (wp->btns_h & WPAD_BUTTON_DOWN) + { + if (!(MissileState)) + J |= VBA_BUTTON_SELECT; + else + J |= VBA_BUTTON_B; + } + if (wp->btns_u & WPAD_BUTTON_DOWN) + { + if (MissileState) + J |= VBA_BUTTON_SELECT; + } + + switch (Morph) + { + case 1: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_DOWN; + Morph = 2; + break; + case 2: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + Morph = 3; + break; + case 3: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_DOWN; + Morph = 0; + break; + case -1: + case -2: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_UP; + Morph--; + break; + case -3: + case -4: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + Morph--; + break; + case -5: + case -6: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_UP; + Morph--; + break; + case -7: + Morph = 0; + break; + } + + // Menu + if (jp & PAD_BUTTON_START) + J |= VBA_BUTTON_START; + + if (RumbleCount>0) + { + WPAD_Rumble(pad, 1); + RumbleCount--; + } + else WPAD_Rumble(pad, 0); + + if ((J & 48) == 48) + J &= ~16; + if ((J & 192) == 192) + J &= ~128; + + return J; +} + +u32 Metroid2Input(unsigned short pad) +{ + u32 J = StandardMovement(pad); + u32 jp = PAD_ButtonsHeld(pad); + u8 BallState = gbReadMemory(0xD020); // 4 = crouch, 5 = ball, other = stand + u8 MissileState = gbReadMemory(0xD04D); // 8 = missile hatch open, 0 = missile closed + u8 Health = gbReadMemory(0xD051); // Binary Coded Decimal + static u8 OldHealth = 0; + + static int RumbleCount = 0; + // Rumble when they lose (or gain) health! (since I'm not checking energy tanks) + if (Health != OldHealth) + if (RumbleCount < 20) + RumbleCount = 20; + OldHealth = Health; + + static int Morph = 0; + static int AimCount = 0; + + if (BallState == 5) // Can't exit morph ball without pressing C! + J &= ~VBA_UP; + if (BallState == 4) // Can't enter morph ball without pressing C! + J &= ~VBA_DOWN; + + WPADData * wp = WPAD_Data(pad); + + // No visors + if (wp->btns_h & WPAD_BUTTON_MINUS) + J |= VBA_BUTTON_SELECT; + // Start button just pauses + if (wp->btns_h & WPAD_BUTTON_PLUS) + J |= VBA_BUTTON_START; + // Map (doesn't exist) + if (wp->btns_h & WPAD_BUTTON_1) + J |= VBA_BUTTON_START; + // Hint (doesn't exist) + if (wp->btns_h & WPAD_BUTTON_2) + J |= VBA_SPEED; + // Z-Target + if ((wp->exp.type == WPAD_EXP_NUNCHUK) && (wp->btns_h & WPAD_NUNCHUK_BUTTON_Z)) + ; + + // Jump + if (wp->btns_h & WPAD_BUTTON_B && BallState!=5) + J |= VBA_BUTTON_A; + else if (BallState==5 && fabs(wp->gforce.y)> 1.5) + J |= VBA_BUTTON_A; + // Fire + if (wp->btns_h & WPAD_BUTTON_A) { + if (MissileState & 8) + J |= VBA_BUTTON_SELECT; + else + J |= VBA_BUTTON_B; + } + + // Aim + /*if (CursorValid && CursorY < 96 && BallState!=2) + J |= VBA_UP; + else if (CursorValid && CursorY < 192 && BallState!=2) + J |= VBA_BUTTON_L; + else if (CursorValid && CursorY < 288 && BallState!=2) + J |= 0; + else if (CursorValid && CursorY < 384 && BallState!=2) + J |= VBA_BUTTON_L & VBA_DOWN; + else if (CursorValid && BallState==0) + J |= VBA_DOWN;*/ + if (wp->orient.pitch < -45 && BallState !=5) + { + J |= VBA_UP; + AimCount = 0; + } + else if (wp->orient.pitch> 45 && BallState !=5 && BallState !=4) + { + //if (AimCount<10) AimCount=10; + } + else + { + AimCount=0; + } + + // Morph Ball + if ((wp->exp.type == WPAD_EXP_NUNCHUK) && (wp->btns_d & WPAD_NUNCHUK_BUTTON_C)) + { + if (BallState == 5) + { // ball + Morph = -1; + } + else if (BallState == 4) + { + Morph = 2; + } + else + { + Morph = 1; + } + } + // Missile + if (wp->btns_h & WPAD_BUTTON_DOWN) + { + if (!(MissileState & 8)) + J |= VBA_BUTTON_SELECT; + else + J |= VBA_BUTTON_B; + } + if (wp->btns_u & WPAD_BUTTON_DOWN) + { + if (MissileState & 8) + J |= VBA_BUTTON_SELECT; + } + + switch (Morph) + { + case 1: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_DOWN; + Morph = 2; + break; + case 2: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + Morph = 3; + break; + case 3: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_DOWN; + Morph = 0; + break; + case -1: + case -2: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_UP; + Morph--; + break; + case -3: + case -4: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + Morph--; + break; + case -5: + case -6: + J &= ~(VBA_UP | VBA_DOWN | VBA_LEFT | VBA_RIGHT | VBA_BUTTON_L); + J |= VBA_UP; + Morph--; + break; + case -7: + Morph = 0; + break; + } + + // Menu + if (jp & PAD_BUTTON_START) + J |= VBA_BUTTON_START; + + if (RumbleCount>0) + { + WPAD_Rumble(pad, 1); + RumbleCount--; + } + else WPAD_Rumble(pad, 0); + if ((J & 48) == 48) J &= ~16; if ((J & 192) == 192) diff --git a/source/ngc/gameinput.h b/source/ngc/gameinput.h index 7745935..2e6a68b 100644 --- a/source/ngc/gameinput.h +++ b/source/ngc/gameinput.h @@ -26,6 +26,9 @@ #define MINISHCAP gid('B','Z','M') #define METROID0 gid('B','M','X') +#define METROID1 gid('F','M','R') +#define METROID2 0xFF0009 +#define METROID4 gid('A','M','T') #define MARIO1CLASSIC gid('F','S','M') #define MARIO1DX gid('A','H','Y') @@ -103,6 +106,9 @@ u32 LinksAwakeningInput(unsigned short pad); u32 OracleOfAgesInput(unsigned short pad); u32 MinishCapInput(unsigned short pad); u32 MetroidZeroInput(unsigned short pad); +u32 Metroid1Input(unsigned short pad); +u32 Metroid2Input(unsigned short pad); +u32 MetroidFusionInput(unsigned short pad); u32 TMNTInput(unsigned short pad); u32 HarryPotter1Input(unsigned short pad); u32 HarryPotter1GBCInput(unsigned short pad); diff --git a/source/ngc/input.cpp b/source/ngc/input.cpp index 14e5b76..2238da1 100644 --- a/source/ngc/input.cpp +++ b/source/ngc/input.cpp @@ -523,6 +523,12 @@ static u32 DecodeJoy(unsigned short pad) // Metroid case METROID0: return MetroidZeroInput(pad); + case METROID1: + return Metroid1Input(pad); + case METROID2: + return Metroid2Input(pad); + case METROID4: + return MetroidFusionInput(pad); // TMNT case TMNT: diff --git a/source/ngc/vbasupport.cpp b/source/ngc/vbasupport.cpp index 3786e05..1530c0d 100644 --- a/source/ngc/vbasupport.cpp +++ b/source/ngc/vbasupport.cpp @@ -704,6 +704,8 @@ static void gbApplyPerImagePreferences() RomIdCode = MARIOLAND1; else if (strcmp(title, "MARIOLAND2") == 0) RomIdCode = MARIOLAND2; + else if (strcmp(title, "METROID2") == 0) + RomIdCode = METROID2; } } @@ -873,9 +875,14 @@ bool LoadVBAROM(int method) cartridgeType = 2; else if(utilIsGBImage(zippedFilename)) cartridgeType = 1; + else { + WaitPrompt("Rom must be 1st file in zip, or unzipped!"); + return false; + } } else // loading the file failed { + WaitPrompt("Empty or invalid zip file!"); return false; } } @@ -883,7 +890,8 @@ bool LoadVBAROM(int method) // leave before we do anything if(cartridgeType != 1 && cartridgeType != 2) { - WaitPrompt("Unknown game image!"); + // Not zip gba agb gbc cgb sgb gb mb bin elf or dmg! + WaitPrompt("Invalid filename extension! Not a rom?"); return false; }