#include "global.h"
#include "bike.h"
#include "event_data.h"
#include "event_object_movement.h"
#include "field_player_avatar.h"
#include "fieldmap.h"
#include "global.fieldmap.h"
#include "metatile_behavior.h"
#include "overworld.h"
#include "rotating_gate.h"
#include "sprite.h"
#include "task.h"
#include "constants/event_objects.h"
#include "constants/event_object_movement_constants.h"
#include "constants/flags.h"

extern void task_add_bump_boulder(u8, u8);
static bool8 ShouldJumpLedge(s16, s16, u8);
static bool8 sub_808B1BC(s16, s16, u8);
static u8 sub_808B164(struct EventObject *, s16, s16, u8, u8);
static u8 sub_808B238(s16, s16, u8);
static void check_acro_bike_metatile(s16, s16, u8, u8 *);
extern void PlayerNotOnBikeCollide(u8);
extern void PlayerNotOnBikeCollideWithFarawayIslandMew(u8);
extern void PlayerRun(u8); 
static void MovePlayerNotOnBike(u8, u16);
static u8 CheckMovementInputNotOnBike(u8);
extern void sub_808C5B0(void);
extern void sub_808C4D8(void);
static u8 CheckForPlayerAvatarCollision(u8);
static u8 EventObjectCB2_NoMovement2();
extern void sub_808C280(struct EventObject *); 
static bool8 TryInterruptEventObjectSpecialAnim(struct EventObject *, u8);
void npc_clear_strange_bits(struct EventObject *);
extern void DoPlayerAvatarTransition(void); 
static bool8 TryDoMetatileBehaviorForcedMovement();
static void MovePlayerAvatarUsingKeypadInput(u8, u16, u16);
static void PlayerAllowForcedMovementIfMovingSameDirection();
static u8 sub_808B028(u8);
static u8 GetForcedMovementByMetatileBehavior();
static void PlayerNotOnBikeNotMoving(u8, u16);
static void PlayerNotOnBikeTurningInPlace(u8, u16);
static void PlayerNotOnBikeMoving(u8, u16);
extern void sub_808C750(u8);

extern void PlayerAvatarTransition_Normal(struct EventObject *a);
extern void PlayerAvatarTransition_MachBike(struct EventObject *a);
extern void PlayerAvatarTransition_AcroBike(struct EventObject *a);
extern void PlayerAvatarTransition_Surfing(struct EventObject *a);
extern void PlayerAvatarTransition_Underwater(struct EventObject *a);
extern void PlayerAvatarTransition_ReturnToField(struct EventObject *a);
extern void PlayerAvatarTransition_Dummy(struct EventObject *a);

extern u8 sub_808C3A4(struct Task *task, struct EventObject *playerObject, struct EventObject *strengthObject);
extern u8 do_boulder_dust(struct Task *task, struct EventObject *playerObject, struct EventObject *strengthObject);
extern u8 sub_808C484(struct Task *task, struct EventObject *playerObject, struct EventObject *strengthObject);

extern u8 sub_808C544(struct Task *task, struct EventObject *eventObject);

extern u8 sub_808C61C(struct Task *task, struct EventObject *eventObject);
extern u8 sub_808C644(struct Task *task, struct EventObject *eventObject);
extern u8 sub_808C6BC(struct Task *task, struct EventObject *eventObject);
extern u8 sub_808C6FC(struct Task *task, struct EventObject *eventObject);

extern u8 fish0(struct Task *task);
extern u8 fish1(struct Task *task);
extern u8 fish2(struct Task *task);
extern u8 fish3(struct Task *task);
extern u8 fish4(struct Task *task);
extern u8 fish5(struct Task *task);
extern u8 fish6(struct Task *task);
extern u8 fish7(struct Task *task);
extern u8 fish8(struct Task *task);
extern u8 fish9(struct Task *task);
extern u8 fishA_wait_for_a_pressed(struct Task *task);
extern u8 fishB(struct Task *task);
extern u8 fishC(struct Task *task);
extern u8 fishD(struct Task *task);
extern u8 fishE(struct Task *task);
extern u8 fishF(struct Task *task);

static bool8 (*const gUnknown_084973FC[])(u8) =
{
    MetatileBehavior_IsTrickHouseSlipperyFloor,
    MetatileBehavior_IsIce_2,
    MetatileBehavior_IsWalkSouth,
    MetatileBehavior_IsWalkNorth,
    MetatileBehavior_IsWalkWest,
    MetatileBehavior_IsWalkEast,
    MetatileBehavior_IsSouthwardCurrent,
    MetatileBehavior_IsNorthwardCurrent,
    MetatileBehavior_IsWestwardCurrent,
    MetatileBehavior_IsEastwardCurrent,
    MetatileBehavior_IsSlideSouth,
    MetatileBehavior_IsSlideNorth,
    MetatileBehavior_IsSlideWest,
    MetatileBehavior_IsSlideEast,
    MetatileBehavior_IsWaterfall,
    MetatileBehavior_IsSecretBaseJumpMat,
    MetatileBehavior_IsSecretBaseSpinMat,
    MetatileBehavior_IsMuddySlope,
};

static bool8 (*const gUnknown_08497444[])(void) =
{
    ForcedMovement_None,
    ForcedMovement_Slip,
    ForcedMovement_Slip,
    ForcedMovement_WalkSouth,
    ForcedMovement_WalkNorth,
    ForcedMovement_WalkWest,
    ForcedMovement_WalkEast,
    ForcedMovement_PushedSouthByCurrent,
    ForcedMovement_PushedNorthByCurrent,
    ForcedMovement_PushedWestByCurrent,
    ForcedMovement_PushedEastByCurrent,
    ForcedMovement_SlideSouth,
    ForcedMovement_SlideNorth,
    ForcedMovement_SlideWest,
    ForcedMovement_SlideEast,
    ForcedMovement_PushedSouthByCurrent,
    ForcedMovement_0xBB,
    ForcedMovement_0xBC,
    ForcedMovement_MuddySlope,
};

static void (*const gUnknown_08497490[])(u8, u16) =
{
    PlayerNotOnBikeNotMoving,
    PlayerNotOnBikeTurningInPlace,
    PlayerNotOnBikeMoving,
};

static bool8 (*const gUnknown_0849749C[])(u8) =
{
    MetatileBehavior_IsBumpySlope,
    MetatileBehavior_IsIsolatedVerticalRail,
    MetatileBehavior_IsIsolatedHorizontalRail,
    MetatileBehavior_IsVerticalRail,
    MetatileBehavior_IsHorizontalRail,
};

static const u8 gUnknown_084974B0[] = {9, 10, 11, 12, 13, 0, 0, 0};

void (*const gUnknown_084974B8[])(struct EventObject *) =
{
    PlayerAvatarTransition_Normal,
    PlayerAvatarTransition_MachBike,
    PlayerAvatarTransition_AcroBike,
    PlayerAvatarTransition_Surfing,
    PlayerAvatarTransition_Underwater,
    PlayerAvatarTransition_ReturnToField,
    PlayerAvatarTransition_Dummy,
    PlayerAvatarTransition_Dummy,
};

bool8 (*const gUnknown_084974D8[])(u8) =
{
    MetatileBehavior_IsSouthArrowWarp,
    MetatileBehavior_IsNorthArrowWarp,
    MetatileBehavior_IsWestArrowWarp,
    MetatileBehavior_IsEastArrowWarp,
};

const u8 gUnknown_084974E8[][2] =
{
    {EVENT_OBJ_GFX_RIVAL_BRENDAN_NORMAL,     EVENT_OBJ_GFX_RIVAL_MAY_NORMAL},
    {EVENT_OBJ_GFX_RIVAL_BRENDAN_MACH_BIKE,  EVENT_OBJ_GFX_RIVAL_MAY_MACH_BIKE},
    {EVENT_OBJ_GFX_RIVAL_BRENDAN_ACRO_BIKE,  EVENT_OBJ_GFX_RIVAL_MAY_ACRO_BIKE},
    {EVENT_OBJ_GFX_RIVAL_BRENDAN_SURFING,    EVENT_OBJ_GFX_RIVAL_MAY_SURFING},
    {EVENT_OBJ_GFX_BRENDAN_UNDERWATER,       EVENT_OBJ_GFX_MAY_UNDERWATER},
    {EVENT_OBJ_GFX_RIVAL_BRENDAN_FIELD_MOVE, EVENT_OBJ_GFX_RIVAL_MAY_FIELD_MOVE},
    {EVENT_OBJ_GFX_BRENDAN_FISHING,          EVENT_OBJ_GFX_MAY_FISHING},
    {EVENT_OBJ_GFX_BRENDAN_WATERING,         EVENT_OBJ_GFX_MAY_WATERING}
};

const u8 gUnknown_084974F8[][2] =
{
    {EVENT_OBJ_GFX_BRENDAN_NORMAL,     EVENT_OBJ_GFX_MAY_NORMAL},
    {EVENT_OBJ_GFX_BRENDAN_MACH_BIKE,  EVENT_OBJ_GFX_MAY_MACH_BIKE},
    {EVENT_OBJ_GFX_BRENDAN_ACRO_BIKE,  EVENT_OBJ_GFX_MAY_ACRO_BIKE},
    {EVENT_OBJ_GFX_BRENDAN_SURFING,    EVENT_OBJ_GFX_MAY_SURFING},
    {EVENT_OBJ_GFX_BRENDAN_UNDERWATER, EVENT_OBJ_GFX_MAY_UNDERWATER},
    {EVENT_OBJ_GFX_BRENDAN_FIELD_MOVE, EVENT_OBJ_GFX_MAY_FIELD_MOVE},
    {EVENT_OBJ_GFX_BRENDAN_FISHING,    EVENT_OBJ_GFX_MAY_FISHING},
    {EVENT_OBJ_GFX_BRENDAN_WATERING,   EVENT_OBJ_GFX_MAY_WATERING},
};

const u8 gUnknown_08497508[] = {EVENT_OBJ_GFX_RED, EVENT_OBJ_GFX_LEAF};

const u8 gUnknown_0849750A[] = {EVENT_OBJ_GFX_LINK_RS_BRENDAN, EVENT_OBJ_GFX_LINK_RS_MAY};

const u8 gUnknown_0849750C[2][5][2] =
{
    //male
    {
        {EVENT_OBJ_GFX_BRENDAN_NORMAL, 1},
        {EVENT_OBJ_GFX_BRENDAN_MACH_BIKE, 2},
        {EVENT_OBJ_GFX_BRENDAN_ACRO_BIKE, 4},
        {EVENT_OBJ_GFX_BRENDAN_SURFING, 8},
        {EVENT_OBJ_GFX_BRENDAN_UNDERWATER, 16},
    },
    //female
    {
        {EVENT_OBJ_GFX_MAY_NORMAL, 1},
        {EVENT_OBJ_GFX_MAY_MACH_BIKE, 2},
        {EVENT_OBJ_GFX_MAY_ACRO_BIKE, 4},
        {EVENT_OBJ_GFX_MAY_SURFING, 8},
        {EVENT_OBJ_GFX_MAY_UNDERWATER, 16},
    }
};

bool8 (*const gUnknown_08497520[])(u8) =  //Duplicate of sArrowWarpMetatileBehaviorChecks
{
    MetatileBehavior_IsSouthArrowWarp,
    MetatileBehavior_IsNorthArrowWarp,
    MetatileBehavior_IsWestArrowWarp,
    MetatileBehavior_IsEastArrowWarp,
};

u8 (*const gUnknown_08497530[])(struct Task *, struct EventObject *, struct EventObject *) =
{
    sub_808C3A4,
    do_boulder_dust,
    sub_808C484,
};

u8 (*const gUnknown_0849753C[])(struct Task *, struct EventObject *) =
{
    sub_808C544,
};

u8 (*const gUnknown_08497540[])(struct Task *, struct EventObject *) =
{
    sub_808C61C,
    sub_808C644,
    sub_808C6BC,
    sub_808C6FC,
};

const u8 gUnknown_08497550[] = {3, 4, 2, 1};

const u8 gUnknown_08497554[] = {16, 16, 17, 18, 19};

u8 (*const gUnknown_0849755C[])(struct Task *) =
{
    fish0,
    fish1,
    fish2,
    fish3,
    fish4,
    fish5,
    fish6,
    fish7,
    fish8,
    fish9,
    fishA_wait_for_a_pressed,
    fishB,
    fishC,
    fishD,
    fishE,
    fishF,
};

const u16 gUnknown_0849759C[] = {1, 1, 1};

const u16 gUnknown_084975A2[] = {1, 3, 6};

const u8 gUnknown_084975A8[] = _("·");

const u16 gUnknown_084975AA[] = {36, 33, 30};

const u16 gUnknown_084975B0[] =
{
    0, 0,
    0x28, 10,
    0x46, 30,
};

const u8 gUnknown_084975BC[] = {0x01, 0x03, 0x04, 0x02, 0x01, 0x00, 0x00, 0x00};

// .text
void MovementType_Player(struct Sprite *sprite)
{
	UpdateEventObjectCurrentMovement(&gEventObjects[sprite->data[0]], sprite, EventObjectCB2_NoMovement2);
}

static u8 EventObjectCB2_NoMovement2()
{
	return 0;
}

void player_step(u8 direction, u16 newKeys, u16 heldKeys)
{
    struct EventObject *playerEventObj = &gEventObjects[gPlayerAvatar.eventObjectId];

    sub_808C280(playerEventObj);
    if (gPlayerAvatar.preventStep == FALSE)
    {
        Bike_TryAcroBikeHistoryUpdate(newKeys, heldKeys);
        if (TryInterruptEventObjectSpecialAnim(playerEventObj, direction) == 0)
        {
            npc_clear_strange_bits(playerEventObj);
            DoPlayerAvatarTransition();
            if (TryDoMetatileBehaviorForcedMovement() == 0)
            {
                MovePlayerAvatarUsingKeypadInput(direction, newKeys, heldKeys);
                PlayerAllowForcedMovementIfMovingSameDirection();
            }
        }
    }
}

static bool8 TryInterruptEventObjectSpecialAnim(struct EventObject *playerEventObj, u8 direction)
{
    #ifdef NONMATCHING
        u8 r5 = direction;
        u8 r6 = direction;
    #else
        u8 r5 = direction;
        register u8 r6 asm("r6") = direction;
    #endif
    //a very bad HACK 

    if (EventObjectIsMovementOverridden(playerEventObj)
     && !EventObjectClearHeldMovementIfFinished(playerEventObj))
    {
        u8 heldMovementActionId = EventObjectGetHeldMovementActionId(playerEventObj);
        if (heldMovementActionId > MOVEMENT_ACTION_WALK_FAST_RIGHT && heldMovementActionId < MOVEMENT_ACTION_WALK_IN_PLACE_NORMAL_DOWN)
        {
            if (direction == DIR_NONE)
            {
                return TRUE;
            }

            if (playerEventObj->movementDirection != r5)
            {
                EventObjectClearHeldMovement(playerEventObj);
                return FALSE;
            }

            if (!sub_808B028(r6))
            {
                EventObjectClearHeldMovement(playerEventObj);
                return FALSE;
            }
        }

        return TRUE;
    }

    return FALSE;
}

void npc_clear_strange_bits(struct EventObject *eventObj)
{
    eventObj->inanimate = 0;
    eventObj->disableAnim = 0;
    eventObj->facingDirectionLocked = 0;
    gPlayerAvatar.flags &= ~PLAYER_AVATAR_FLAG_DASH;
}

static void MovePlayerAvatarUsingKeypadInput(u8 direction, u16 newKeys, u16 heldKeys)
{
    if ((gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_MACH_BIKE)
     || (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_ACRO_BIKE))
        MovePlayerOnBike(direction, newKeys, heldKeys);
    else
        MovePlayerNotOnBike(direction, heldKeys);
}

static void PlayerAllowForcedMovementIfMovingSameDirection(void)
{
    if (gPlayerAvatar.runningState == MOVING)
        gPlayerAvatar.flags &= ~PLAYER_AVATAR_FLAG_5;
}

static bool8 TryDoMetatileBehaviorForcedMovement()
{
	return gUnknown_08497444[GetForcedMovementByMetatileBehavior()]();
}

static u8 GetForcedMovementByMetatileBehavior(void)
{
    u8 i;

    if (!(gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_5))
    {
        u8 metatileBehavior = gEventObjects[gPlayerAvatar.eventObjectId].currentMetatileBehavior;

        for (i = 0; i < 18; i++)
        {
            if (gUnknown_084973FC[i](metatileBehavior))
                return i + 1;
        }
    }
    return 0;
}

bool8 ForcedMovement_None(void)
{
    if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_6)
    {
        struct EventObject *playerEventObj = &gEventObjects[gPlayerAvatar.eventObjectId];

        playerEventObj->facingDirectionLocked = 0;
        playerEventObj->enableAnim = 1;
        SetEventObjectDirection(playerEventObj, playerEventObj->facingDirection);
        gPlayerAvatar.flags &= ~PLAYER_AVATAR_FLAG_6;
    }
    return FALSE;
}

static u8 DoForcedMovement(u8 direction, void (*b)(u8))
{
    struct PlayerAvatar *playerAvatar = &gPlayerAvatar;
    u8 collisionType = CheckForPlayerAvatarCollision(direction);

    playerAvatar->flags |= PLAYER_AVATAR_FLAG_6;
    if (collisionType != 0)
    {
        ForcedMovement_None();
        if (collisionType <= 4)
        {
            return 0;
        }
        else
        {
            if (collisionType == COLLISION_LEDGE_JUMP)
                PlayerJumpLedge(direction);
            playerAvatar->flags |= PLAYER_AVATAR_FLAG_6;
            playerAvatar->runningState = MOVING;
            return 1;
        }
    }
    else
    {
        playerAvatar->runningState = MOVING;
        b(direction);
        return 1;
    }
}

u8 DoForcedMovementInCurrentDirection(void (*a)(u8))
{
    struct EventObject *playerEventObj = &gEventObjects[gPlayerAvatar.eventObjectId];

    playerEventObj->disableAnim = 1;
    return DoForcedMovement(playerEventObj->movementDirection, a);
}

bool8 ForcedMovement_Slip(void)
{
    return DoForcedMovementInCurrentDirection(PlayerGoSpeed2);
}

bool8 ForcedMovement_WalkSouth(void)
{
    return DoForcedMovement(DIR_SOUTH, PlayerGoSpeed1);
}

bool8 ForcedMovement_WalkNorth(void)
{
    return DoForcedMovement(DIR_NORTH, PlayerGoSpeed1);
}

bool8 ForcedMovement_WalkWest(void)
{
    return DoForcedMovement(DIR_WEST, PlayerGoSpeed1);
}

bool8 ForcedMovement_WalkEast(void)
{
    return DoForcedMovement(DIR_EAST, PlayerGoSpeed1);
}

bool8 ForcedMovement_PushedSouthByCurrent(void)
{
    return DoForcedMovement(DIR_SOUTH, PlayerGoSpeed3);
}

bool8 ForcedMovement_PushedNorthByCurrent(void)
{
    return DoForcedMovement(DIR_NORTH, PlayerGoSpeed3);
}

bool8 ForcedMovement_PushedWestByCurrent(void)
{
    return DoForcedMovement(DIR_WEST, PlayerGoSpeed3);
}

bool8 ForcedMovement_PushedEastByCurrent(void)
{
    return DoForcedMovement(DIR_EAST, PlayerGoSpeed3);
}

u8 ForcedMovement_Slide(u8 direction, void (*b)(u8))
{
    struct EventObject *playerEventObj = &gEventObjects[gPlayerAvatar.eventObjectId];

    playerEventObj->disableAnim = 1;
    playerEventObj->facingDirectionLocked = 1;
    return DoForcedMovement(direction, b);
}

bool8 ForcedMovement_SlideSouth(void)
{
    return ForcedMovement_Slide(DIR_SOUTH, PlayerGoSpeed2);
}

bool8 ForcedMovement_SlideNorth(void)
{
    return ForcedMovement_Slide(DIR_NORTH, PlayerGoSpeed2);
}

bool8 ForcedMovement_SlideWest(void)
{
    return ForcedMovement_Slide(DIR_WEST, PlayerGoSpeed2);
}

bool8 ForcedMovement_SlideEast(void)
{
    return ForcedMovement_Slide(DIR_EAST, PlayerGoSpeed2);
}

bool8 ForcedMovement_0xBB(void)
{
    sub_808C4D8();
    return TRUE;
}

bool8 ForcedMovement_0xBC(void)
{
    sub_808C5B0();
    return TRUE;
}

bool8 ForcedMovement_MuddySlope(void)
{
    struct EventObject *playerEventObj = &gEventObjects[gPlayerAvatar.eventObjectId];

    if (playerEventObj->movementDirection != DIR_NORTH || GetPlayerSpeed() <= 3)
    {
        Bike_UpdateBikeCounterSpeed(0);
        playerEventObj->facingDirectionLocked = 1;
        return DoForcedMovement(1, PlayerGoSpeed2);
    }
    else
    {
        return FALSE;
    }
}

static void MovePlayerNotOnBike(u8 direction, u16 heldKeys)
{
    gUnknown_08497490[CheckMovementInputNotOnBike(direction)](direction, heldKeys);
}

static u8 CheckMovementInputNotOnBike(u8 direction)
{
    if (direction == DIR_NONE)
    {
        gPlayerAvatar.runningState = NOT_MOVING;
        return 0;
    }
    else if (direction != GetPlayerMovementDirection() && gPlayerAvatar.runningState != MOVING)
    {
        gPlayerAvatar.runningState = TURN_DIRECTION;
        return 1;
    }
    else
    {
        gPlayerAvatar.runningState = MOVING;
        return 2;
    }
}

static void PlayerNotOnBikeNotMoving(u8 direction, u16 heldKeys)
{
    PlayerFaceDirection(GetPlayerFacingDirection());
}

static void PlayerNotOnBikeTurningInPlace(u8 direction, u16 heldKeys)
{
    PlayerTurnInPlace(direction);
}

static void PlayerNotOnBikeMoving(u8 direction, u16 heldKeys)
{
    u8 r0 = CheckForPlayerAvatarCollision(direction);

    if (r0 != 0)
    {
        if (r0 == 6)
        {
            PlayerJumpLedge(direction);
            return;
        }
        else if (r0 == 4 && IsPlayerCollidingWithFarawayIslandMew(direction) != 0)
        {
            PlayerNotOnBikeCollideWithFarawayIslandMew(direction);
            return;
        }
        else
        {
            u8 r4 = r0 - 5;

            if (r4 > 3)
            {
                PlayerNotOnBikeCollide(direction);
                return;
            }
            else
            {
                return;
            }
        }
    }

    if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
    {
	// speed 2 is fast, same speed as running
        PlayerGoSpeed2(direction);
        return;
    }

    if (!(gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_4) && (heldKeys & B_BUTTON) && FlagGet(FLAG_SYS_B_DASH)
     && IsRunningDisallowed(gEventObjects[gPlayerAvatar.eventObjectId].currentMetatileBehavior) == 0)
    {
        PlayerRun(direction);
        gPlayerAvatar.flags |= PLAYER_AVATAR_FLAG_DASH;
        return;
    }
    else
    {
        PlayerGoSpeed1(direction);
    }
}

static u8 CheckForPlayerAvatarCollision(u8 direction)
{
    s16 x, y;
    struct EventObject *playerEventObj = &gEventObjects[gPlayerAvatar.eventObjectId];

    x = playerEventObj->currentCoords.x;
    y = playerEventObj->currentCoords.y;
    MoveCoords(direction, &x, &y);
    return CheckForEventObjectCollision(playerEventObj, x, y, direction, MapGridGetMetatileBehaviorAt(x, y));
}

static u8 sub_808B028(u8 direction)
{
    s16 x, y;
    struct EventObject *playerEventObj = &gEventObjects[gPlayerAvatar.eventObjectId];

    x = playerEventObj->currentCoords.x;
    y = playerEventObj->currentCoords.y;
    MoveCoords(direction, &x, &y);
    return sub_808B164(playerEventObj, x, y, direction, MapGridGetMetatileBehaviorAt(x, y));
}

u8 CheckForEventObjectCollision(struct EventObject *a, s16 x, s16 y, u8 direction, u8 e)
{
    u8 collision;

    collision = GetCollisionAtCoords(a, x, y, direction);
    if (collision == 3 && sub_808B1BC(x, y, direction))
        return 5;
    if (ShouldJumpLedge(x, y, direction))
    {
        IncrementGameStat(GAME_STAT_JUMPED_DOWN_LEDGES);
        return COLLISION_LEDGE_JUMP;
    }
    if (collision == 4 && sub_808B238(x, y, direction))
        return 7;

    if (collision == 0)
    {
        if (CheckForRotatingGatePuzzleCollision(direction, x, y))
            return 8;
        check_acro_bike_metatile(x, y, e, &collision);
    }
    return collision;
}

static u8 sub_808B164(struct EventObject *a, s16 x, s16 y, u8 direction, u8 e)
{
    u8 collision = GetCollisionAtCoords(a, x, y, direction);

    if (collision == 0)
    {
        if (CheckForRotatingGatePuzzleCollisionWithoutAnimation(direction, x, y) != 0)
            return 8;
        check_acro_bike_metatile(x, y, e, &collision);
    }
    return collision;
}

static bool8 sub_808B1BC(s16 x, s16 y, u8 direction)
{
    if ((gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
     && MapGridGetZCoordAt(x, y) == 3
     && GetEventObjectIdByXYZ(x, y, 3) == 16)
    {
        sub_808C750(direction);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

static bool8 ShouldJumpLedge(s16 x, s16 y, u8 z)
{
    if (GetLedgeJumpDirection(x, y, z) != 0)
        return TRUE;
    else
        return FALSE;
}

static u8 sub_808B238(s16 x, s16 y, u8 direction)
{
    if (FlagGet(FLAG_SYS_USE_STRENGTH))
    {
        u8 eventObjectId = GetEventObjectIdByXY(x, y);

            if (eventObjectId != 16 && gEventObjects[eventObjectId].graphicsId == 0x57)
            {
                x = gEventObjects[eventObjectId].currentCoords.x;
                y = gEventObjects[eventObjectId].currentCoords.y;
                MoveCoords(direction, &x, &y);
                if (GetCollisionAtCoords(&gEventObjects[eventObjectId], x, y, direction) == 0
                 && MetatileBehavior_IsNonAnimDoor(MapGridGetMetatileBehaviorAt(x, y)) == 0)
                {
                    task_add_bump_boulder(eventObjectId, direction);
                    return 1;
                }
            }
    }
    return 0;
}

static void check_acro_bike_metatile(s16 unused1, s16 unused2, u8 c, u8 *d)
{
    u8 i;

    for (i = 0; i < 5; i++)
    {
        if (gUnknown_0849749C[i](c))
        {
            *d = gUnknown_084974B0[i];
            return;
        }
    }
}
