summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorghoulslash <41651341+ghoulslash@users.noreply.github.com>2020-07-20 12:31:45 -0600
committerghoulslash <41651341+ghoulslash@users.noreply.github.com>2020-07-20 12:31:45 -0600
commit2e5fc4069c27e42c020a35096e069319bbeb6c9f (patch)
treefcf9858d94848dbe3f883e4d83137b355447671b
parent6775505cbd8203a021cf01033db39f4d42df7328 (diff)
Created Stair Warps (markdown)
-rw-r--r--Stair-Warps.md392
1 files changed, 392 insertions, 0 deletions
diff --git a/Stair-Warps.md b/Stair-Warps.md
new file mode 100644
index 0000000..a890ef9
--- /dev/null
+++ b/Stair-Warps.md
@@ -0,0 +1,392 @@
+## Stair Warps
+
+Credit to ghoulslash. The easy implementation is to pull from the [repo](https://github.com/ghoulslash/pokeemerald/tree/stair_warps)
+
+FRLG include a stair warp effect where the player walks up/down stairs at the beginning/end of the warp sequence. This ports that feature from FRLG to emerald:
+
+<a href="https://imgur.com/q9SzXmW"><img src="https://i.imgur.com/q9SzXmW.gif" title="source: imgur.com" /></a>
+
+### Add new metatile behaviors
+**1.** First, open [include/constants/metatile_behaviors.h](../blob/master/include/constants/metatile_behaviors.h). Replace any 4 unused metatile behaviors (I chose MB_UNUSED_EB through MB_UNUSED_EE):
+```c
+#define MB_UP_RIGHT_STAIR_WARP 0xEB
+#define MB_UP_LEFT_STAIR_WARP 0xEC
+#define MB_DOWN_RIGHT_STAIR_WARP 0xED
+#define MB_DOWN_LEFT_STAIR_WARP 0xEE
+```
+
+**2.** Next, open [src/metatile_behavior.c](../blob/master/src/metatile_behavior.c). Replace the elements in `sTileBitAttributes` for your metatile behaviours:
+```c
+ [MB_UP_RIGHT_STAIR_WARP] = TILE_ATTRIBUTES(FALSE, FALSE, FALSE),
+ [MB_UP_LEFT_STAIR_WARP] = TILE_ATTRIBUTES(FALSE, FALSE, FALSE),
+ [MB_DOWN_RIGHT_STAIR_WARP] = TILE_ATTRIBUTES(FALSE, FALSE, FALSE),
+ [MB_DOWN_LEFT_STAIR_WARP] = TILE_ATTRIBUTES(FALSE, FALSE, FALSE),
+```
+
+**3.** At the bottom of the file, add the following 5 functions:
+```c
+bool8 MetatileBehavior_IsDirectionalUpRightStairWarp(u8 metatileBehavior)
+{
+ if(metatileBehavior == MB_UP_RIGHT_STAIR_WARP)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+bool8 MetatileBehavior_IsDirectionalUpLeftStairWarp(u8 metatileBehavior)
+{
+ if (metatileBehavior == MB_UP_LEFT_STAIR_WARP)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+bool8 MetatileBehavior_IsDirectionalDownRightStairWarp(u8 metatileBehavior)
+{
+ if (metatileBehavior == MB_DOWN_RIGHT_STAIR_WARP)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+bool8 MetatileBehavior_IsDirectionalDownLeftStairWarp(u8 metatileBehavior)
+{
+ if (metatileBehavior == MB_DOWN_LEFT_STAIR_WARP)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+bool8 MetatileBehavior_IsDirectionalStairWarp(u8 metatileBehavior)
+{
+ if (metatileBehavior >= MB_UP_RIGHT_STAIR_WARP && metatileBehavior <= MB_DOWN_LEFT_STAIR_WARP)
+ return TRUE;
+ else
+ return FALSE;
+}
+```
+
+### Globally define the metatile behavior functions
+Open [include/metatile_behavior.h](../blob/master/include/metatile_behavior.h). Add the following to the bottom:
+```c
+bool8 MetatileBehavior_IsDirectionalUpRightStairWarp(u8 metatileBehavior);
+bool8 MetatileBehavior_IsDirectionalUpLeftStairWarp(u8 metatileBehavior);
+bool8 MetatileBehavior_IsDirectionalDownRightStairWarp(u8 metatileBehavior);
+bool8 MetatileBehavior_IsDirectionalDownLeftStairWarp(u8 metatileBehavior);
+bool8 MetatileBehavior_IsDirectionalStairWarp(u8 metatileBehavior);
+```
+
+### Add a new stair warp collision
+This allows us to ignore the impassable tiles when we try to warp on the stairs. Open [include/global.fieldmap.h](../blob/master/include/global.fieldmap.h). Add `COLLISION_STAIR_WARP` after the enum define `COLLISION_HORIZONTAL_RAIL,`
+
+### Add the collision exclusion
+Open [src/field_player/avatar.c](../blob/master/src/field_player_avatar.c).
+
+**1.** First, let's add `#include "field_screen_effect.h"` to the top of the file.
+
+**2.** Next, find `static u8 CheckForPlayerAvatarCollision(u8 direction)`. Before the line, `MoveCoords(direction, &x, &y);`, add the following two lines:
+```c
+ if (IsDirectionalStairWarpMetatileBehavior(MapGridGetMetatileBehaviorAt(x, y), direction))
+ return COLLISION_STAIR_WARP;
+```
+
+**3.** Finally, find `static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys)`. Replace the `if (collision)` code block with:
+```c
+ if (collision)
+ {
+ if (collision == COLLISION_LEDGE_JUMP)
+ {
+ PlayerJumpLedge(direction);
+ return;
+ }
+ else if (collision == COLLISION_OBJECT_EVENT && IsPlayerCollidingWithFarawayIslandMew(direction))
+ {
+ PlayerNotOnBikeCollideWithFarawayIslandMew(direction);
+ return;
+ }
+ else if (collision == COLLISION_STAIR_WARP)
+ {
+ PlayerFaceDirection(direction);
+ }
+ else
+ {
+ u8 adjustedCollision = collision - COLLISION_STOP_SURFING;
+ if (adjustedCollision > 3)
+ PlayerNotOnBikeCollide(direction);
+ return;
+ }
+ }
+```
+
+### Add the warp arrow
+Open [src/field_control_avatar.c](../blob/master/src/field_control_avatar.c) and find the function `TryArrowWarp`. Replace everything with the following:
+```c
+static bool8 TryArrowWarp(struct MapPosition *position, u16 metatileBehavior, u8 direction)
+{
+ s8 warpEventId = GetWarpEventAtMapPosition(&gMapHeader, position);
+ u16 delay;
+
+ if (warpEventId != -1)
+ {
+ if (IsArrowWarpMetatileBehavior(metatileBehavior, direction) == TRUE)
+ {
+ StoreInitialPlayerAvatarState();
+ SetupWarp(&gMapHeader, warpEventId, position);
+ DoWarp();
+ return TRUE;
+ }
+ else if (IsDirectionalStairWarpMetatileBehavior(metatileBehavior, direction) == TRUE)
+ {
+ delay = 0;
+ if (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_MACH_BIKE | PLAYER_AVATAR_FLAG_ACRO_BIKE))
+ {
+ SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_ON_FOOT);
+ delay = 12;
+ }
+
+ StoreInitialPlayerAvatarState();
+ SetupWarp(&gMapHeader, warpEventId, position);
+ DoStairWarp(metatileBehavior, delay);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+```
+
+### Add `DoStairWarp`
+This is the meat of the code implementation. Let's start by opening [src/field_screen_effect.h](../blob/master/src/field_screen_effect.c).
+
+**1.** Find the function `static void SetUpWarpExitTask(void)`. Before `else if (MetatileBehavior_IsNonAnimDoor(behavior) == TRUE)`, Add the following:
+```c
+ else if (MetatileBehavior_IsDirectionalStairWarp(behavior) == TRUE)
+ func = Task_ExitStairs;
+```
+
+**2.** Also, add `static void Task_ExitStairs(u8 taskId);` to the top of the file underneath `static void Task_EnableScriptAfterMusicFade(u8 taskId);`.
+
+**3.** At the bottom of the file, let's add a bunch of functions:
+```c
+static void GetStairsMovementDirection(u8 a0, s16 *a1, s16 *a2)
+{
+ if (MetatileBehavior_IsDirectionalUpRightStairWarp(a0))
+ {
+ *a1 = 16;
+ *a2 = -10;
+ }
+ else if (MetatileBehavior_IsDirectionalUpLeftStairWarp(a0))
+ {
+ *a1 = -17;
+ *a2 = -10;
+ }
+ else if (MetatileBehavior_IsDirectionalDownRightStairWarp(a0))
+ {
+ *a1 = 17;
+ *a2 = 3;
+ }
+ else if (MetatileBehavior_IsDirectionalDownLeftStairWarp(a0))
+ {
+ *a1 = -17;
+ *a2 = 3;
+ }
+ else
+ {
+ *a1 = 0;
+ *a2 = 0;
+ }
+}
+
+static bool8 WaitStairExitMovementFinished(s16 *a0, s16 *a1, s16 *a2, s16 *a3, s16 *a4)
+{
+ struct Sprite *sprite;
+ sprite = &gSprites[gPlayerAvatar.spriteId];
+ if (*a4 != 0)
+ {
+ *a2 += *a0;
+ *a3 += *a1;
+ sprite->pos2.x = *a2 >> 5;
+ sprite->pos2.y = *a3 >> 5;
+ (*a4)--;
+ return TRUE;
+ }
+ else
+ {
+ sprite->pos2.x = 0;
+ sprite->pos2.y = 0;
+ return FALSE;
+ }
+}
+
+static void ExitStairsMovement(s16 *a0, s16 *a1, s16 *a2, s16 *a3, s16 *a4)
+{
+ s16 x, y;
+ u8 behavior;
+ s32 r1;
+ struct Sprite *sprite;
+
+ PlayerGetDestCoords(&x, &y);
+ behavior = MapGridGetMetatileBehaviorAt(x, y);
+ if (MetatileBehavior_IsDirectionalDownRightStairWarp(behavior) || MetatileBehavior_IsDirectionalUpRightStairWarp(behavior))
+ r1 = 3;
+ else
+ r1 = 4;
+
+ ObjectEventForceSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], GetWalkInPlaceSlowMovementAction(r1));
+ GetStairsMovementDirection(behavior, a0, a1);
+ *a2 = *a0 * 16;
+ *a3 = *a1 * 16;
+ *a4 = 16;
+ sprite = &gSprites[gPlayerAvatar.spriteId];
+ sprite->pos2.x = *a2 >> 5;
+ sprite->pos2.y = *a3 >> 5;
+ *a0 *= -1;
+ *a1 *= -1;
+}
+
+static void Task_ExitStairs(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ switch (data[0])
+ {
+ default:
+ if (WaitForWeatherFadeIn() == TRUE)
+ {
+ CameraObjectReset1();
+ ScriptContext2_Disable();
+ DestroyTask(taskId);
+ }
+ break;
+ case 0:
+ Overworld_PlaySpecialMapMusic();
+ WarpFadeInScreen();
+ ScriptContext2_Enable();
+ ExitStairsMovement(&data[1], &data[2], &data[3], &data[4], &data[5]);
+ data[0]++;
+ break;
+ case 1:
+ if (!WaitStairExitMovementFinished(&data[1], &data[2], &data[3], &data[4], &data[5]))
+ data[0]++;
+ break;
+ }
+}
+
+bool8 IsDirectionalStairWarpMetatileBehavior(u16 metatileBehavior, u8 playerDirection)
+{
+ switch (playerDirection)
+ {
+ case DIR_WEST:
+ if (MetatileBehavior_IsDirectionalUpLeftStairWarp(metatileBehavior))
+ return TRUE;
+ if (MetatileBehavior_IsDirectionalDownLeftStairWarp(metatileBehavior))
+ return TRUE;
+ break;
+ case DIR_EAST:
+ if (MetatileBehavior_IsDirectionalUpRightStairWarp(metatileBehavior))
+ return TRUE;
+ if (MetatileBehavior_IsDirectionalDownRightStairWarp(metatileBehavior))
+ return TRUE;
+ break;
+ }
+ return FALSE;
+}
+
+static void ForceStairsMovement(u16 a0, s16 *a1, s16 *a2)
+{
+ ObjectEventForceSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], GetWalkInPlaceNormalMovementAction(GetPlayerFacingDirection()));
+ GetStairsMovementDirection(a0, a1, a2);
+}
+
+static void UpdateStairsMovement(s16 a0, s16 a1, s16 *a2, s16 *a3, s16 *a4)
+{
+ struct Sprite *playerSpr = &gSprites[gPlayerAvatar.spriteId];
+ struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
+
+ if (a1 > 0 || *a4 > 6)
+ *a3 += a1;
+
+ *a2 += a0;
+ (*a4)++;
+ playerSpr->pos2.x = *a2 >> 5;
+ playerSpr->pos2.y = *a3 >> 5;
+ if (playerObj->heldMovementFinished)
+ ObjectEventForceSetHeldMovement(playerObj, GetWalkInPlaceNormalMovementAction(GetPlayerFacingDirection()));
+}
+
+static void Task_StairWarp(u8 taskId)
+{
+ s16 * data = gTasks[taskId].data;
+ struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
+ struct Sprite *playerSpr = &gSprites[gPlayerAvatar.spriteId];
+
+ switch (data[0])
+ {
+ case 0:
+ ScriptContext2_Enable();
+ FreezeObjectEvents();
+ CameraObjectReset2();
+ data[0]++;
+ break;
+ case 1:
+ if (!ObjectEventIsMovementOverridden(playerObj) || ObjectEventClearHeldMovementIfFinished(playerObj))
+ {
+ if (data[15] != 0)
+ data[15]--;
+ else
+ {
+ TryFadeOutOldMapMusic();
+ PlayRainStoppingSoundEffect();
+ playerSpr->oam.priority = 1;
+ ForceStairsMovement(data[1], &data[2], &data[3]);
+ PlaySE(SE_KAIDAN);
+ data[0]++;
+ }
+ }
+ break;
+ case 2:
+ UpdateStairsMovement(data[2], data[3], &data[4], &data[5], &data[6]);
+ data[15]++;
+ if (data[15] >= 12)
+ {
+ WarpFadeOutScreen();
+ data[0]++;
+ }
+ break;
+ case 3:
+ UpdateStairsMovement(data[2], data[3], &data[4], &data[5], &data[6]);
+ if (!PaletteFadeActive() && BGMusicStopped())
+ data[0]++;
+ break;
+ default:
+ gFieldCallback = FieldCB_DefaultWarpExit;
+ WarpIntoMap();
+ SetMainCallback2(CB2_LoadMap);
+ DestroyTask(taskId);
+ break;
+ }
+}
+
+void DoStairWarp(u16 metatileBehavior, u16 delay)
+{
+ u8 taskId = CreateTask(Task_StairWarp, 10);
+ gTasks[taskId].data[1] = metatileBehavior;
+ gTasks[taskId].data[15] = delay;
+ Task_StairWarp(taskId);
+}
+```
+
+### Globally define two stair warp functions
+Open [include/field_screen_effect.h](../blob/master/include/field_screen_effect.h). At the bottom, add the following:
+```c
+void DoStairWarp(u16 metatileBehavior, u16 delay);
+bool8 IsDirectionalStairWarpMetatileBehavior(u16 metatileBehavior, u8 playerDirection);
+```
+
+
+### Adjust the player's movement direction on stair warps
+Open [src/overworld.c](../blob/master/src/overworld.c) and find the function `GetAdjustedInitialDirection`. Before the line `else if ((playerStruct->transitionFlags == PLAYER_AVATAR_FLAG_UNDERWATER && (etc...)`, add the following:
+```c
+ else if (MetatileBehavior_IsDirectionalUpRightStairWarp(metatileBehavior) == TRUE || MetatileBehavior_IsDirectionalDownRightStairWarp(metatileBehavior) == TRUE)
+ return DIR_WEST;
+ else if (MetatileBehavior_IsDirectionalUpLeftStairWarp(metatileBehavior) == TRUE || MetatileBehavior_IsDirectionalDownLeftStairWarp(metatileBehavior) == TRUE)
+ return DIR_EAST;
+```