-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Add In‐Cart Rumble
voloved edited this page Nov 14, 2023
·
1 revision
By devolov. Credit to Citrus Bolt. Also Credit to Evan Bowman.
Goal: Allow the GBA to rumble during certain field moves, sound effects, you mon fainting, and when the ball shakes when attempting to catch.
----------------------------- include/gba/io_reg.h -----------------------------
index 64075c092..c948ea6a8 100644
@@ -776,5 +776,9 @@
#define WAITCNT_AGB (0 << 15)
#define WAITCNT_CGB (1 << 15)
+#define GPIO_PORT_DATA (*(vu16 *)0x80000C4)
+#define GPIO_PORT_DIRECTION (*(vu16 *)0x80000C6)
+#define GPIO_PORT_READ_ENABLE (*(vu16 *)0x80000C8)
+
#endif // GUARD_GBA_IO_REG_H
------------------------------- include/global.h -------------------------------
index da7eabb8a..e6ee34c2b 100644
@@ -499,9 +499,10 @@ struct SaveBlock2
u16 optionsBattleSceneOff:1; // whether battle animations are disabled
u16 regionMapZoom:1; // whether the map is zoomed in
/*0x18*/ struct Pokedex pokedex;
/*0x90*/ u16 lastUsedBall;
- /*0x92*/ u8 filler_90[0x6];
+ /*0x92*/ u8 optionsRumble; // Set this to true to allow rumbling
+ /*0x93*/ u8 filler_90[0x5];
/*0x98*/ struct Time localTimeOffset;
/*0xA0*/ struct Time lastBerryTreeUpdate;
/*0xA8*/ u32 gcnLinkFlags; // Read by Pokemon Colosseum/XD
/*0xAC*/ u32 encryptionKey;
------------------------------- include/rumble.h -------------------------------
new file mode 100755
index 000000000..90c2b8852
@@ -0,0 +1,18 @@
+#ifndef GUARD_RUMBLE_H
+#define GUARD_RUMBLE_H
+
+enum{
+ RUMBLE_TYPE_OFF,
+ RUMBLE_TYPE_CONT,
+ RUMBLE_TYPE_TIMED
+};
+
+void RumbleFrameUpdate();
+bool32 SetTimedRumble(u8 deciseconds);
+bool32 RumbleStart(void);
+bool32 RumbleStop(void);
+u32 GetRumbleState(void);
+
+
+#endif // GUARD_RUMBLE_H
+
--------------------------- src/battle_anim_throw.c ---------------------------
index a23a03d97..36e2b32a6 100755
@@ -18,8 +18,9 @@
#include "task.h"
#include "trig.h"
#include "util.h"
#include "data.h"
+#include "rumble.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/songs.h"
#include "constants/rgb.h"
@@ -1137,8 +1138,9 @@ static void SpriteCB_Ball_Wobble(struct Sprite *sprite)
StartSpriteAffineAnim(sprite, BALL_ROTATE_RIGHT);
gBattleSpritesDataPtr->animationData->ballSubpx = 0;
sprite->callback = SpriteCB_Ball_Wobble_Step;
PlaySE(SE_BALL);
+ RumbleStart();
}
}
#undef sTimer
@@ -1299,8 +1301,10 @@ static void SpriteCB_Ball_Wobble_Step(struct Sprite *sprite)
else
StartSpriteAffineAnim(sprite, BALL_ROTATE_RIGHT);
PlaySE(SE_BALL);
+ RumbleStart();
}
break;
}
}
@@ -1344,8 +1348,9 @@ static void SpriteCB_Ball_Capture_Step(struct Sprite *sprite)
sprite->sTimer++;
if (sprite->sTimer == 40)
{
PlaySE(SE_RG_BALL_CLICK);
+ SetTimedRumble(1);
BlendPalettes(0x10000 << sprite->oam.paletteNum, 6, RGB(0, 0, 0));
MakeCaptureStars(sprite);
}
else if (sprite->sTimer == 60)
------------------------- src/battle_script_commands.c -------------------------
index dadbddd07..00f0e1f15 100644
@@ -55,8 +55,9 @@
#include "constants/trainers.h"
#include "constants/flags.h"
#include "constants/battle.h"
#include "debug.h"
+#include "rumble.h"
extern const u8 *const gBattleScriptsForMoveEffects[];
#define DEFENDER_IS_PROTECTED ((gProtectStructs[gBattlerTarget].protected) && (gBattleMoves[gCurrentMove].flags & FLAG_PROTECT_AFFECTED))
@@ -3374,8 +3375,9 @@ static void Cmd_tryfaintmon(void)
BattleScriptPush(gBattlescriptCurrInstr + 7);
gBattlescriptCurrInstr = BS_ptr;
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
{
+ SetTimedRumble(10);
gHitMarker |= HITMARKER_PLAYER_FAINTED;
if (gBattleResults.playerFaintCounter < 255)
gBattleResults.playerFaintCounter++;
AdjustFriendshipOnBattleFaint(gActiveBattler);
-------------------------- src/field_player_avatar.c --------------------------
index aee08e54b..d19d16f85 100644
@@ -20,8 +20,9 @@
#include "strings.h"
#include "task.h"
#include "tv.h"
#include "wild_encounter.h"
+#include "rumble.h"
#include "constants/abilities.h"
#include "constants/event_objects.h"
#include "constants/event_object_movement.h"
#include "constants/field_effects.h"
@@ -1566,8 +1567,9 @@ static bool8 PushBoulder_Move(struct Task *task, struct ObjectEvent *player, str
gFieldEffectArguments[2] = boulder->previousElevation;
gFieldEffectArguments[3] = gSprites[boulder->spriteId].oam.priority;
FieldEffectStart(FLDEFF_DUST);
PlaySE(SE_M_STRENGTH);
+ RumbleStart();
task->tState++;
}
return FALSE;
}
@@ -1643,8 +1645,9 @@ static bool8 PlayerAvatar_SecretBaseMatSpinStep0(struct Task *task, struct Objec
task->data[1] = objectEvent->movementDirection;
gPlayerAvatar.preventStep = TRUE;
LockPlayerFieldControls();
PlaySE(SE_WARP_IN);
+ RumbleStart();
return TRUE;
}
static bool8 PlayerAvatar_SecretBaseMatSpinStep1(struct Task *task, struct ObjectEvent *objectEvent)
------------------------------- src/fldeff_cut.c -------------------------------
index 0fd9263e5..952bd4c65 100644
@@ -16,8 +16,9 @@
#include "sound.h"
#include "sprite.h"
#include "task.h"
#include "trig.h"
+#include "rumble.h"
#include "constants/abilities.h"
#include "constants/event_objects.h"
#include "constants/field_effects.h"
#include "constants/songs.h"
@@ -318,8 +319,9 @@ bool8 FldEff_CutGrass(void)
s16 x, y;
u8 i = 0;
PlaySE(SE_M_CUT);
+ RumbleStart();
PlayerGetDestCoords(&gPlayerFacingPosition.x, &gPlayerFacingPosition.y);
for (i = 0; i < CUT_HYPER_AREA; i++)
{
if (sHyperCutTiles[i] == TRUE)
@@ -641,7 +643,8 @@ void FixLongGrassMetatilesWindowBottom(s16 x, s16 y)
static void StartCutTreeFieldEffect(void)
{
PlaySE(SE_M_CUT);
+ RumbleStart();
FieldEffectActiveListRemove(FLDEFF_USE_CUT_ON_TREE);
ScriptContext_Enable();
}
---------------------------- src/fldeff_rocksmash.c ----------------------------
index 99314b6ac..b5db48251 100644
@@ -12,8 +12,9 @@
#include "script.h"
#include "sound.h"
#include "sprite.h"
#include "task.h"
+#include "rumble.h"
#include "constants/event_object_movement.h"
#include "constants/event_objects.h"
#include "constants/field_effects.h"
#include "constants/map_types.h"
@@ -163,7 +164,8 @@ bool8 FldEff_UseRockSmash(void)
// The actual rock smashing is handled by EventScript_SmashRock, so this function does very little
static void FieldMove_RockSmash(void)
{
PlaySE(SE_M_ROCK_THROW);
+ RumbleStart();
FieldEffectActiveListRemove(FLDEFF_USE_ROCK_SMASH);
ScriptContext_Enable();
}
---------------------------------- src/m4a.c ----------------------------------
index 7774d09cb..68e5aea0e 100644
@@ -1,6 +1,8 @@
#include <string.h>
#include "gba/m4a_internal.h"
+#include "rumble.h"
+#include "constants/songs.h"
extern const u8 gCgb3Vol[];
#define BSS_CODE __attribute__((section(".bss.code")))
@@ -110,8 +112,29 @@ void m4aSongNumStart(u16 n)
const struct Song *songTable = gSongTable;
const struct Song *song = &songTable[n];
const struct MusicPlayer *mplay = &mplayTable[song->ms];
+ switch (n)
+ {
+ case SE_LEDGE:
+ case SE_BIKE_BELL:
+ case SE_BIKE_HOP:
+ case SE_ICE_BREAK:
+ case SE_ICE_CRACK:
+ case SE_TRUCK_MOVE:
+ case SE_TRUCK_STOP:
+ case SE_TRUCK_UNLOAD:
+ case SE_TRUCK_DOOR:
+ case SE_ITEMFINDER:
+ case SE_BREAKABLE_DOOR:
+ case SE_FIELD_POISON:
+ case SE_M_SELF_DESTRUCT:
+ case SE_M_EXPLOSION:
+ case SE_RG_SS_ANNE_HORN:
+ case SE_POKENAV_CALL:
+ RumbleStart();
+ }
+
MPlayStart(mplay->info, song->header);
}
void m4aSongNumStartOrChange(u16 n)
---------------------------------- src/main.c ----------------------------------
index e7a7003ae..33aa7c4bc 100644
@@ -25,8 +25,9 @@
#include "trainer_hill.h"
#include "constants/rgb.h"
#include "palette.h"
#include "event_data.h"
+#include "rumble.h"
static void VBlankIntr(void);
static void HBlankIntr(void);
static void VCountIntr(void);
@@ -130,8 +131,11 @@ void AgbMain()
#endif
#endif
for (;;)
{
+ if (gSaveBlock2Ptr->optionsRumble)
+ RumbleFrameUpdate();
+
ReadKeys();
if (gSoftResetDisabled == FALSE
&& JOY_HELD_RAW(A_BUTTON)
@@ -390,8 +394,11 @@ static void VBlankIntr(void)
Random();
UpdateWirelessStatusIndicatorSprite();
+ if (!IsSEPlaying())
+ RumbleStop();
+
INTR_CHECK |= INTR_FLAG_VBLANK;
gMain.intrCheck |= INTR_FLAG_VBLANK;
}
--------------------------------- src/rumble.c ---------------------------------
new file mode 100755
index 000000000..aea07424d
@@ -0,0 +1,69 @@
+#include "global.h"
+#include "rumble.h"
+#include "main.h"
+#include "sound.h"
+
+static EWRAM_DATA u32 sRumbleState = 0;
+static EWRAM_DATA u8 sRumbleType = RUMBLE_TYPE_OFF;
+static EWRAM_DATA u32 stopRumbleVblankCounter = 0;
+
+static u16 const sNintendoHandshakeData[] = {0x494E, 0x544E, 0x4E45, 0x4F44, 0x8000};
+static void SetRumbleState(u32 state);
+
+enum {
+ RUMBLE_ON = 0x40000026,
+ RUMBLE_OFF = 0x40000004,
+ RUMBLE_HARD_STOP = 0x40000015
+};
+
+void RumbleFrameUpdate()
+{
+ REG_SIOCNT &= ~1;
+ REG_SIOCNT |= SIO_START;
+ if(gMain.vblankCounter1 == stopRumbleVblankCounter && sRumbleType == RUMBLE_TYPE_TIMED)
+ SetRumbleState(RUMBLE_OFF);
+}
+
+static void SetRumbleState(u32 state)
+{
+ sRumbleState = state;
+ if (sRumbleState != RUMBLE_ON)
+ sRumbleType = RUMBLE_TYPE_OFF;
+
+ if (gSaveBlock2Ptr->optionsRumble) {
+ GPIO_PORT_DIRECTION = 1 << 3;
+ GPIO_PORT_DATA = (sRumbleState == RUMBLE_ON) << 3;
+ }
+}
+
+bool32 RumbleStart(void)
+{
+ if (sRumbleType == RUMBLE_TYPE_TIMED)
+ return FALSE;
+ sRumbleType = RUMBLE_TYPE_CONT;
+ SetRumbleState(RUMBLE_ON);
+ return TRUE;
+}
+
+bool32 RumbleStop(void)
+{
+ if (sRumbleType != RUMBLE_TYPE_CONT)
+ return FALSE;
+ SetRumbleState(RUMBLE_OFF);
+ return TRUE;
+}
+
+bool32 SetTimedRumble(u8 deciseconds)
+{
+ if (sRumbleType == RUMBLE_TYPE_CONT)
+ return FALSE;
+ sRumbleType = RUMBLE_TYPE_TIMED;
+ stopRumbleVblankCounter = gMain.vblankCounter1 + (6 * deciseconds);
+ SetRumbleState(RUMBLE_ON);
+ return TRUE;
+}
+
+u32 GetRumbleState(void)
+{
+ return sRumbleState;
+}
+
--------------------------------- src/siirtc.c ---------------------------------
index 0e598f717..b658b9e77 100644
@@ -59,12 +59,8 @@
#define DIR_2_OUT 4
#define DIR_ALL_IN (DIR_0_IN | DIR_1_IN | DIR_2_IN)
#define DIR_ALL_OUT (DIR_0_OUT | DIR_1_OUT | DIR_2_OUT)
-#define GPIO_PORT_DATA (*(vu16 *)0x80000C4)
-#define GPIO_PORT_DIRECTION (*(vu16 *)0x80000C6)
-#define GPIO_PORT_READ_ENABLE (*(vu16 *)0x80000C8)
-
extern vu16 GPIOPortDirection;
static u16 sDummy; // unused variable
static bool8 sLocked;
-------------------------------- sym_ewram.txt --------------------------------
index b7513dba9..afdd24a9d 100644
@@ -151,4 +151,5 @@
.include "src/debug.o"
.include "src/battle_controller_player.o"
.include "src/mugshot.o"
.include "src/tm_case.o"
+ .include "src/rumble.o"