Skip to content

Commit

Permalink
Added MB_X_Y_STAIR_WARP metatile behaviors (#5278)
Browse files Browse the repository at this point in the history
  • Loading branch information
Bassoonian authored Sep 28, 2024
2 parents 1956376 + 580b9e3 commit 7dad830
Show file tree
Hide file tree
Showing 10 changed files with 349 additions and 14 deletions.
8 changes: 4 additions & 4 deletions include/constants/metatile_behaviors.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,10 +236,10 @@
#define MB_WIRELESS_BOX_RESULTS 0xE8
#define MB_TRAINER_HILL_TIMER 0xE9
#define MB_SKY_PILLAR_CLOSED_DOOR 0xEA
#define MB_UNUSED_EB 0xEB
#define MB_UNUSED_EC 0xEC
#define MB_UNUSED_ED 0xED
#define MB_UNUSED_EE 0xEE
#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
#define MB_UNUSED_EF 0xEF

#define NUM_METATILE_BEHAVIORS 0xF0
Expand Down
2 changes: 2 additions & 0 deletions include/field_screen_effect.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,7 @@ void DoOrbEffect(void);
void FadeOutOrbEffect(void);
void WriteFlashScanlineEffectBuffer(u8 flashLevel);
bool8 IsPlayerStandingStill(void);
void DoStairWarp(u16 metatileBehavior, u16 delay);
bool32 IsDirectionalStairWarpMetatileBehavior(u16 metatileBehavior, u8 playerDirection);

#endif // GUARD_FIELD_SCREEN_EFFECT_H
1 change: 1 addition & 0 deletions include/global.fieldmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ enum
COLLISION_ISOLATED_HORIZONTAL_RAIL,
COLLISION_VERTICAL_RAIL,
COLLISION_HORIZONTAL_RAIL,
COLLISION_STAIR_WARP,
};

// player running states
Expand Down
11 changes: 8 additions & 3 deletions include/metatile_behavior.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,13 @@ bool8 MetatileBehavior_IsQuestionnaire(u8);
bool8 MetatileBehavior_IsLongGrass_Duplicate(u8);
bool8 MetatileBehavior_IsLongGrassSouthEdge(u8);
bool8 MetatileBehavior_IsTrainerHillTimer(u8);
bool32 MetatileBehavior_IsSignpost(u32);
bool32 MetatileBehavior_IsPokemonCenterSign(u32);
bool32 MetatileBehavior_IsPokeMartSign(u32);
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);
bool8 MetatileBehavior_IsSignpost(u32);
bool8 MetatileBehavior_IsPokemonCenterSign(u32);
bool8 MetatileBehavior_IsPokeMartSign(u32);

#endif // GUARD_METATILE_BEHAVIOR_H
1 change: 1 addition & 0 deletions include/overworld.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ extern void (*gFieldCallback)(void);
extern bool8 (*gFieldCallback2)(void);
extern u8 gLocalLinkPlayerId;
extern u8 gFieldLinkPlayerCount;
extern bool8 gExitStairsMovementDisabled;

extern const struct UCoords32 gDirectionToVectors[];

Expand Down
30 changes: 26 additions & 4 deletions src/field_control_avatar.c
Original file line number Diff line number Diff line change
Expand Up @@ -752,17 +752,39 @@ static bool8 CheckStandardWildEncounter(u16 metatileBehavior)
return FALSE;
}

static void StorePlayerStateAndSetupWarp(struct MapPosition *position, s32 warpEventId)
{
StoreInitialPlayerAvatarState();
SetupWarp(&gMapHeader, warpEventId, position);
}

static bool8 TryArrowWarp(struct MapPosition *position, u16 metatileBehavior, u8 direction)
{
s8 warpEventId = GetWarpEventAtMapPosition(&gMapHeader, position);
s32 warpEventId = GetWarpEventAtMapPosition(&gMapHeader, position);
u32 delay;

if (IsArrowWarpMetatileBehavior(metatileBehavior, direction) == TRUE && warpEventId != WARP_ID_NONE)
if (warpEventId == WARP_ID_NONE)
return FALSE;

if (IsArrowWarpMetatileBehavior(metatileBehavior, direction) == TRUE)
{
StoreInitialPlayerAvatarState();
SetupWarp(&gMapHeader, warpEventId, position);
StorePlayerStateAndSetupWarp(position, warpEventId);
DoWarp();
return TRUE;
}
else if (IsDirectionalStairWarpMetatileBehavior(metatileBehavior, direction) == TRUE)
{
delay = 0;
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_BIKE)
{
SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_ON_FOOT);
delay = 12;
}

StorePlayerStateAndSetupWarp(position, warpEventId);
DoStairWarp(metatileBehavior, delay);
return TRUE;
}
return FALSE;
}

Expand Down
8 changes: 8 additions & 0 deletions src/field_player_avatar.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "field_camera.h"
#include "field_effect.h"
#include "field_effect_helpers.h"
#include "field_screen_effect.h"
#include "field_player_avatar.h"
#include "fieldmap.h"
#include "menu.h"
Expand Down Expand Up @@ -634,6 +635,10 @@ static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys)
PlayerNotOnBikeCollideWithFarawayIslandMew(direction);
return;
}
else if (collision == COLLISION_STAIR_WARP)
{
PlayerFaceDirection(direction);
}
else
{
u8 adjustedCollision = collision - COLLISION_STOP_SURFING;
Expand Down Expand Up @@ -670,6 +675,9 @@ static u8 CheckForPlayerAvatarCollision(u8 direction)

x = playerObjEvent->currentCoords.x;
y = playerObjEvent->currentCoords.y;
if (IsDirectionalStairWarpMetatileBehavior(MapGridGetMetatileBehaviorAt(x, y), direction))
return COLLISION_STAIR_WARP;

MoveCoords(direction, &x, &y);
return CheckForObjectEventCollision(playerObjEvent, x, y, direction, MapGridGetMetatileBehaviorAt(x, y));
}
Expand Down
246 changes: 246 additions & 0 deletions src/field_screen_effect.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ static void Task_WarpAndLoadMap(u8 taskId);
static void Task_DoDoorWarp(u8 taskId);
static void Task_EnableScriptAfterMusicFade(u8 taskId);

static void ExitStairsMovement(s16*, s16*, s16*, s16*, s16*);
static void GetStairsMovementDirection(u32, s16*, s16*);
static void Task_ExitStairs(u8);
static bool8 WaitStairExitMovementFinished(s16*, s16*, s16*, s16*, s16*);
static void UpdateStairsMovement(s16, s16, s16*, s16*, s16*);
static void Task_StairWarp(u8);
static void ForceStairsMovement(u32, s16*, s16*);

// data[0] is used universally by tasks in this file as a state for switches
#define tState data[0]

Expand Down Expand Up @@ -268,10 +276,14 @@ static void SetUpWarpExitTask(void)
behavior = MapGridGetMetatileBehaviorAt(x, y);
if (MetatileBehavior_IsDoor(behavior) == TRUE)
func = Task_ExitDoor;
else if (MetatileBehavior_IsDirectionalStairWarp(behavior) == TRUE && !gExitStairsMovementDisabled)
func = Task_ExitStairs;
else if (MetatileBehavior_IsNonAnimDoor(behavior) == TRUE)
func = Task_ExitNonAnimDoor;
else
func = Task_ExitNonDoor;

gExitStairsMovementDisabled = FALSE;
CreateTask(func, 10);
}

Expand Down Expand Up @@ -1388,3 +1400,237 @@ void FieldCB_RushInjuredPokemonToCenter(void)
taskId = CreateTask(Task_RushInjuredPokemonToCenter, 10);
gTasks[taskId].tState = FRLG_WHITEOUT_ENTER_MSG_SCREEN;
}

static void GetStairsMovementDirection(u32 metatileBehavior, s16 *speedX, s16 *speedY)
{
if (MetatileBehavior_IsDirectionalUpRightStairWarp(metatileBehavior))
{
*speedX = 16;
*speedY = -10;
}
else if (MetatileBehavior_IsDirectionalUpLeftStairWarp(metatileBehavior))
{
*speedX = -17;
*speedY = -10;
}
else if (MetatileBehavior_IsDirectionalDownRightStairWarp(metatileBehavior))
{
*speedX = 17;
*speedY = 3;
}
else if (MetatileBehavior_IsDirectionalDownLeftStairWarp(metatileBehavior))
{
*speedX = -17;
*speedY = 3;
}
else
{
*speedX = 0;
*speedY = 0;
}
}

static bool8 WaitStairExitMovementFinished(s16 *speedX, s16 *speedY, s16 *offsetX, s16 *offsetY, s16 *timer)
{
struct Sprite *sprite = &gSprites[gPlayerAvatar.spriteId];
if (*timer != 0)
{
*offsetX += *speedX;
*offsetY += *speedY;
sprite->x2 = *offsetX >> 5;
sprite->y2 = *offsetY >> 5;
(*timer)--;
return TRUE;
}
else
{
sprite->x2 = 0;
sprite->y2 = 0;
return FALSE;
}
}

static void ExitStairsMovement(s16 *speedX, s16 *speedY, s16 *offsetX, s16 *offsetY, s16 *timer)
{
s16 x, y;
u32 metatileBehavior;
s32 direction;
struct Sprite *sprite;

PlayerGetDestCoords(&x, &y);
metatileBehavior = MapGridGetMetatileBehaviorAt(x, y);
if (MetatileBehavior_IsDirectionalDownRightStairWarp(metatileBehavior) || MetatileBehavior_IsDirectionalUpRightStairWarp(metatileBehavior))
direction = DIR_WEST;
else
direction = DIR_EAST;

ObjectEventForceSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], GetWalkInPlaceSlowMovementAction(direction));
GetStairsMovementDirection(metatileBehavior, speedX, speedY);
*offsetX = *speedX * 16;
*offsetY = *speedY * 16;
*timer = 16;
sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->x2 = *offsetX >> 5;
sprite->y2 = *offsetY >> 5;
*speedX *= -1;
*speedY *= -1;
}

#define tState data[0]
#define tSpeedX data[1]
#define tSpeedY data[2]
#define tOffsetX data[3]
#define tOffsetY data[4]
#define tTimer data[5]

static void Task_ExitStairs(u8 taskId)
{
s16 * data = gTasks[taskId].data;
switch (tState)
{
default:
if (WaitForWeatherFadeIn() == TRUE)
{
CameraObjectReset();
UnlockPlayerFieldControls();
DestroyTask(taskId);
}
break;
case 0:
Overworld_PlaySpecialMapMusic();
WarpFadeInScreen();
LockPlayerFieldControls();
ExitStairsMovement(&tSpeedX, &tSpeedY, &tOffsetX, &tOffsetY, &tTimer);
tState++;
break;
case 1:
if (!WaitStairExitMovementFinished(&tSpeedX, &tSpeedY, &tOffsetX, &tOffsetY, &tTimer))
tState++;
break;
}
}

static void ForceStairsMovement(u32 metatileBehavior, s16 *speedX, s16 *speedY)
{
ObjectEventForceSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], GetWalkInPlaceNormalMovementAction(GetPlayerFacingDirection()));
GetStairsMovementDirection(metatileBehavior, speedX, speedY);
}
#undef tSpeedX
#undef tSpeedY
#undef tOffsetX
#undef tOffsetY
#undef tTimer

#define tMetatileBehavior data[1]
#define tSpeedX data[2]
#define tSpeedY data[3]
#define tOffsetX data[4]
#define tOffsetY data[5]
#define tTimer data[6]
#define tDelay data[15]

static void UpdateStairsMovement(s16 speedX, s16 speedY, s16 *offsetX, s16 *offsetY, s16 *timer)
{
struct Sprite *playerSprite = &gSprites[gPlayerAvatar.spriteId];
struct ObjectEvent *playerObjectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];

if (speedY > 0 || *timer > 6)
*offsetY += speedY;

*offsetX += speedX;
(*timer)++;
playerSprite->x2 = *offsetX >> 5;
playerSprite->y2 = *offsetY >> 5;
if (playerObjectEvent->heldMovementFinished)
ObjectEventForceSetHeldMovement(playerObjectEvent, GetWalkInPlaceNormalMovementAction(GetPlayerFacingDirection()));
}

static void Task_StairWarp(u8 taskId)
{
s16 * data = gTasks[taskId].data;
struct ObjectEvent *playerObjectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
struct Sprite *playerSprite = &gSprites[gPlayerAvatar.spriteId];

switch (tState)
{
case 0:
LockPlayerFieldControls();
FreezeObjectEvents();
CameraObjectFreeze();
tState++;
break;
case 1:
if (!ObjectEventIsMovementOverridden(playerObjectEvent) || ObjectEventClearHeldMovementIfFinished(playerObjectEvent))
{
if (tDelay != 0)
{
tDelay--;
}
else
{
TryFadeOutOldMapMusic();
PlayRainStoppingSoundEffect();
playerSprite->oam.priority = 1;
ForceStairsMovement(tMetatileBehavior, &tSpeedX, &tSpeedY);
PlaySE(SE_EXIT);
tState++;
}
}
break;
case 2:
UpdateStairsMovement(tSpeedX, tSpeedY, &tOffsetX, &tOffsetY, &tTimer);
tDelay++;
if (tDelay >= 12)
{
WarpFadeOutScreen();
tState++;
}
break;
case 3:
UpdateStairsMovement(tSpeedX, tSpeedY, &tOffsetX, &tOffsetY, &tTimer);
if (!PaletteFadeActive() && BGMusicStopped())
tState++;
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].tMetatileBehavior = metatileBehavior;
gTasks[taskId].tDelay = delay;
Task_StairWarp(taskId);
}

#undef tMetatileBehavior
#undef tSpeedX
#undef tSpeedY
#undef tOffsetX
#undef tOffsetY
#undef tTimer
#undef tDelay

bool32 IsDirectionalStairWarpMetatileBehavior(u16 metatileBehavior, u8 playerDirection)
{
if (playerDirection == DIR_WEST)
{
if (MetatileBehavior_IsDirectionalUpLeftStairWarp(metatileBehavior))
return TRUE;
if (MetatileBehavior_IsDirectionalDownLeftStairWarp(metatileBehavior))
return TRUE;
}
else if (playerDirection == DIR_EAST)
{
if (MetatileBehavior_IsDirectionalUpRightStairWarp(metatileBehavior))
return TRUE;
if (MetatileBehavior_IsDirectionalDownRightStairWarp(metatileBehavior))
return TRUE;
}
return FALSE;
}
Loading

0 comments on commit 7dad830

Please sign in to comment.