#include "global.h"
#include "overworld.h"
#include "main.h"
#include "battle_setup.h"
#include "berry.h"
// #include "cable_club.h"
#include "clock.h"
#include "event_data.h"
#include "field_camera.h"
#include "field_control_avatar.h"
#include "field_effect.h"
#include "field_fadetransition.h"
#include "event_object_movement.h"
#include "field_message_box.h"
#include "field_player_avatar.h"
#include "field_screen_effect.h"
// #include "field_special_scene.h"
#include "field_specials.h"
#include "field_tasks.h"
#include "field_weather.h"
#include "fieldmap.h"
// #include "fldeff_flash.h"
#include "heal_location.h"
#include "link.h"
#include "load_save.h"
#include "main.h"
#include "m4a.h"
#include "constants/maps.h"
#include "map_name_popup.h"
#include "menu.h"
#include "metatile_behavior.h"
#include "new_game.h"
#include "palette.h"
#include "play_time.h"
#include "random.h"
#include "roamer.h"
// #include "rotating_gate.h"
#include "safari_zone.h"
#include "save.h"
#include "script.h"
// #include "script_pokemon_80C4.h"
#include "secret_base.h"
#include "constants/songs.h"
#include "sound.h"
#include "constants/species.h"
#include "start_menu.h"
#include "task.h"
// #include "tileset_anim.h"
#include "time_events.h"
#include "tv.h"
#include "scanline_effect.h"
#include "wild_encounter.h"
#include "bg.h"
#include "money.h"
#include "save_location.h"
#include "constants/abilities.h"
#include "malloc.h"
#include "gpu_regs.h"
#include "link_rfu.h"

// event scripts
extern const u8 EventScript_WhiteOut[];
extern const u8 EventScript_271862[];
extern const u8 EventScript_277513[];
extern const u8 EventScript_TradeRoom_TooBusyToNotice[];
extern const u8 EventScript_TradeRoom_ReadTrainerCard1[];
extern const u8 EventScript_TradeRoom_ReadTrainerCard2[];
extern const u8 gUnknown_08277388[];
extern const u8 gUnknown_082773A3[];
extern const u8 gUnknown_082773BE[];
extern const u8 gUnknown_082773D9[];
extern const u8 gUnknown_0827741D[];
extern const u8 gUnknown_08277432[];
extern const u8 gUnknown_08277447[];
extern const u8 gUnknown_0827745C[];
extern const u8 gUnknown_08277374[];
extern const u8 gUnknown_0827737E[];
extern const u8 gUnknown_082773FF[];
extern const u8 gUnknown_082773F5[];
extern const u8 gUnknown_082774EF[];
extern const u8 gUnknown_08277509[];

// vars
extern const struct MapData *const gMapAttributes[];
extern const struct MapHeader *const *const gMapGroups[];
extern const s32 gMaxFlashLevel;
extern const u16 gUnknown_82EC7C4[];

u16 gUnknown_03005DA8;
MainCallback gFieldCallback;
bool8 (*gUnknown_03005DB0)(void);
u8 gUnknown_03005DB4;
u8 gFieldLinkPlayerCount;

// functions
extern void HealPlayerParty(void);
extern void move_tilemap_camera_to_upper_left_corner(void);
extern void cur_mapheader_run_tileset_funcs_after_some_cpuset(void);
extern void DrawWholeMapView(void);
extern void copy_map_tileset1_tileset2_to_vram(const struct MapData *);
extern void apply_map_tileset1_tileset2_palette(const struct MapData *);
extern void ResetCyclingRoadChallengeData(void);
extern void ApplyNewEncryptionKeyToWord(u32 *word, u32 newKey);
extern void mapheader_run_script_with_tag_x5(void);
extern void ResetFieldTasksArgs(void);
extern void sub_80A0A2C(void);
extern void not_trainer_hill_battle_pyramid(void);
extern void apply_map_tileset2_palette(const struct MapData *);
extern void copy_map_tileset2_to_vram_2(const struct MapData *);
extern void prev_quest_postbuffer_cursor_backup_reset(void);
extern void ShowMapNamePopup(void);
extern bool32 InTrainerHill(void);
extern bool32 sub_808651C(void);
extern bool8 sub_80AF6A4(void);
extern bool8 sub_81A9E6C(void);
extern bool8 sub_80E909C(void);
extern void sub_81AA1D8(void);
extern void c2_change_map(void);
extern void sub_81D5DF8(void);
extern void sub_80EB218(void);
extern void sub_81BE72C(void);
extern void sub_80AF3C8(void);
extern void sub_81971F4(void);
extern void sub_808B578(void);
extern void sub_80AF314(void);
extern void sub_80AF214(void);
extern void sub_80AF188(void);
extern void door_upload_tiles(void);
extern void RotatingGate_InitPuzzleAndGraphics(void);
extern void sub_80AF168(void);
extern void sub_80AF3C8(void);
extern void ExecuteTruckSequence(void);
extern void sub_80A0A38(void);
extern void trainer_hill_map_load_related(void);
extern void sub_8087D74(void);
extern void battle_pyramid_map_load_related(u8);
extern void sub_80B00E8(u8);
extern void sub_80E9238(u8);
extern void sub_81A3908(void);
extern void sub_81AA2F8(void);
extern void sub_8195E10(void);
extern void sub_80EDB44(void);
extern void sub_81D64C0(void);
extern void sub_81BE6AC(void);
extern void sub_8098128(void);
extern void copy_map_tileset1_to_vram(const struct MapData *);
extern void copy_map_tileset2_to_vram(const struct MapData *);
extern void FieldUpdateBgTilemapScroll(void);
extern void TransferTilesetAnimsBuffer(void);
extern bool32 sub_81D5F48(void);
extern u8 GetCurrentTrainerHillMapId(void);
extern bool8 warp0_in_pokecenter(void);
extern void dp13_810BB8C(void);
extern void FieldEffectActiveListClear(void);
extern void SetUpFieldTasks(void);
extern void sub_81BE6B8(void);
extern void sub_80AAFA4(void);
extern void ShowStartMenu(void);
extern void sub_80AEE84(void);
extern void mapldr_default(void);
extern void npc_paltag_set_load(u8);
extern void sub_8088B3C(u16, u16);
extern bool32 sub_800F0B8(void);
extern bool32 sub_8009F3C(void);
extern void sub_8010198(void);
extern u32 sub_800B4DC(void);
extern bool32 sub_80B39D4(u8);
extern const u8* sub_809C2C8(struct MapPosition *a1, u8, u8);
extern u8 *sub_809D0F4(void*);
extern u8 sub_808BD6C(u8);
extern u8 sub_808BD7C(u8);
extern void sub_80979D4(struct Sprite*, u8);

// this file's functions
static void Overworld_ResetStateAfterWhiteOut(void);
static void c2_80567AC(void);
static void CB2_LoadMap2(void);
static void VBlankCB_Field(void);
static void SpriteCB_LinkPlayer(struct Sprite *sprite);
static void ChooseAmbientCrySpecies(void);
static void do_load_map_stuff_loop(u8 *state);
static bool32 map_loading_iteration_3(u8 *state);
static bool32 sub_8086638(u8 *state);
static bool32 load_map_stuff(u8 *state, u32);
static bool32 map_loading_iteration_2_link(u8 *state);
static void mli4_mapscripts_and_other(void);
static void map_loading_lcd_reset(void);
static u8 sub_8087858(u8);
static u16 sub_80871C0(u32 a1);
static void sub_80867C8(void);
static void sub_80867D8(void);
static void sub_8086AE4(void);
static void sub_80869DC(void);
static void sub_8086B14(void);
static void sub_8086AAC(void);
static void sub_8086988(bool32 arg0);
static void sub_8086A80(void);
static void sub_8086A68(void);
static void sub_8086860(void);
static void sub_8086AC8(void);
static void sub_8086B9C(void);
static void sub_8086C40(void);
static void sub_8086C90(void);
static void sub_8086FA0(u16);
static void sub_8086F38(u16*, s32);
static u8 npc_something3(u8 a1, u8 a2);
static u8 LinkPlayerDetectCollision(u8 selfMapObjId, u8 a2, s16 x, s16 y);
static void CreateLinkPlayerSprite(u8 linkPlayerId, u8 gameVersion);
static void sub_8087878(u8 linkPlayerId, u16 *x, u16 *y);
static u8 sub_80878A0(u8 linkPlayerId);
static u8 sub_80878C0(u8 linkPlayerId);
static s32 sub_80878E4(u8 linkPlayerId);
static u8 GetLinkPlayerIdAt(s16 x, s16 y);
static void sub_808796C(u8 linkPlayerId, u8 a2);
static void ZeroMapObject(struct MapObject *mapObj);
static void SpawnLinkPlayerMapObject(u8 linkPlayerId, s16 x, s16 y, u8 a4);
static void InitLinkPlayerMapObjectPos(struct MapObject *mapObj, s16 x, s16 y);
static void sub_80877DC(u8 linkPlayerId, u8 a2);
static void sub_808780C(u8 linkPlayerId);
static u8 sub_8087858(u8 linkPlayerId);
static void sub_8087584(void);
static u32 sub_8087690(void);
static void ZeroLinkPlayerMapObject(struct LinkPlayerMapObject *linkPlayerMapObj);
static const u8 *sub_80873B4(struct UnkStruct_8054FF8 *a1);
static u16 sub_8087480(const u8 *script);
static void sub_8087510(void);
static void sub_808751C(void);
static void sub_8087530(const u8 *script);
static void sub_808754C(void);
static void sub_8087568(const u8 *script);
static void sub_80872D8(s32 linkPlayerId, s32 a2, struct UnkStruct_8054FF8 *a3);
static bool32 sub_8087340(struct UnkStruct_8054FF8 *a1);
static bool32 sub_8087358(struct UnkStruct_8054FF8 *a1);
static u8 *sub_8087370(struct UnkStruct_8054FF8 *a1);
static bool32 sub_8087388(struct UnkStruct_8054FF8 *a1);
static const u8 *sub_80873B4(struct UnkStruct_8054FF8 *a1);
static u16 sub_808711C(u32);
static u16 sub_8087140(u32);
static void sub_808709C(u16 *a1);
static u16 sub_80870B0(u32 a1);
static u16 sub_80870F8(u32 a1);
static u16 sub_8087068(u16 a1);
static void c1_link_related(void);
static void c1_link_related_func_set(u16 (*func)(u32));
static void SetFieldVBlankCallback(void);
static void FieldClearVBlankHBlankCallbacks(void);
static void sub_8085810(void);
static u8 sub_808532C(struct UnkPlayerStruct *playerStruct, u16 a2, u8 a3);
static u8 sub_808538C(struct UnkPlayerStruct *playerStruct, u8 a2, u16 a3, u8 a4);
static u16 cur_mapdata_block_role_at_screen_center_acc_to_sav1(void);

// IWRAM bss vars
IWRAM_DATA static void *sUnknown_03000E0C;
IWRAM_DATA static u8 sUnknown_03000E10[4];
IWRAM_DATA static u16 (*sUnknown_03000E14)(u32);
IWRAM_DATA static u8 sUnknown_03000E18;
IWRAM_DATA static u8 sUnknown_03000E19;
IWRAM_DATA static u32 sUnusedVar;

// EWRAM vars
EWRAM_DATA static u8 sUnknown_020322D8 = 0;
EWRAM_DATA struct WarpData gUnknown_020322DC = {0};
EWRAM_DATA static struct WarpData sWarpDestination = {0};  // new warp position
EWRAM_DATA static struct WarpData sUnknown_020322EC = {0};
EWRAM_DATA static struct WarpData sUnknown_020322F4 = {0};
EWRAM_DATA static u16 sLastMapSectionId = 0;
EWRAM_DATA static struct UnkPlayerStruct sUnknown_02032300 = {0};
EWRAM_DATA static u16 sAmbientCrySpecies = 0;
EWRAM_DATA static bool8 sIsAmbientCryWaterMon = FALSE;
EWRAM_DATA struct LinkPlayerMapObject gLinkPlayerMapObjects[4] = {0};

// const rom data
static const struct WarpData sDummyWarpData =
{
    .mapGroup = -1,
    .mapNum = -1,
    .warpId = -1,
    .x = -1,
    .y = -1,
};

static const u8 sUnusedData[] =
{
    0xB0, 0x04, 0x00, 0x00,
    0x10, 0x0E, 0x00, 0x00,
    0xB0, 0x04, 0x00, 0x00,
    0x60, 0x09, 0x00, 0x00,
    0x32, 0x00, 0x00, 0x00,
    0x50, 0x00, 0x00, 0x00,
    0xD4, 0xFF, 0xFF, 0xFF,
    0x2C, 0x00, 0x00, 0x00,
};

const struct UCoords32 gUnknown_08339D64[] =
{
    { 0,  0},
    { 0,  1},
    { 0, -1},
    {-1,  0},
    { 1,  0},
    {-1,  1},
    { 1,  1},
    {-1, -1},
    { 1, -1},
};

static const struct BgTemplate gUnknown_08339DAC[] =
{
    {
        .bg = 0,
        .charBaseIndex = 2,
        .mapBaseIndex = 31,
        .screenSize = 0,
        .paletteMode = 0,
        .priority = 0,
        .baseTile = 0
    },
    {
        .bg = 1,
        .charBaseIndex = 0,
        .mapBaseIndex = 29,
        .screenSize = 0,
        .paletteMode = 0,
        .priority = 1,
        .baseTile = 0
    },
    {
        .bg = 2,
        .charBaseIndex = 0,
        .mapBaseIndex = 28,
        .screenSize = 0,
        .paletteMode = 0,
        .priority = 2,
        .baseTile = 0
    },
    {
        .bg = 3,
        .charBaseIndex = 0,
        .mapBaseIndex = 30,
        .screenSize = 0,
        .paletteMode = 0,
        .priority = 3,
        .baseTile = 0
    }
};

static const struct ScanlineEffectParams gUnknown_08339DBC =
{
    (void *)REG_ADDR_WIN0H,
    ((DMA_ENABLE | DMA_START_HBLANK | DMA_REPEAT | DMA_DEST_RELOAD) << 16) | 1,
    1,
    0,
};

static u8 sub_80879D8(struct LinkPlayerMapObject *, struct MapObject *, u8);
static u8 sub_80879F8(struct LinkPlayerMapObject *, struct MapObject *, u8);
static u8 sub_80879FC(struct LinkPlayerMapObject *, struct MapObject *, u8);

static u8 (*const gUnknown_08339DC8[])(struct LinkPlayerMapObject *, struct MapObject *, u8) =
{
    sub_80879D8,
    sub_80879F8,
    sub_80879FC,
};

static u8 sub_8087A1C(struct LinkPlayerMapObject *, struct MapObject *, u8);
static u8 sub_8087A20(struct LinkPlayerMapObject *, struct MapObject *, u8);
static u8 sub_8087A88(struct LinkPlayerMapObject *, struct MapObject *, u8);

static u8 (*const gUnknown_08339DD4[])(struct LinkPlayerMapObject *, struct MapObject *, u8) =
{
    sub_8087A1C,
    sub_8087A20,
    sub_8087A20,
    sub_8087A20,
    sub_8087A20,
    sub_8087A1C,
    sub_8087A1C,
    sub_8087A88,
    sub_8087A88,
    sub_8087A88,
    sub_8087A88,
};

static void sub_8087AA0(struct LinkPlayerMapObject *, struct MapObject *);
static void sub_8087AA8(struct LinkPlayerMapObject *, struct MapObject *);

static void (*const gUnknown_08339E00[])(struct LinkPlayerMapObject *, struct MapObject *) =
{
    sub_8087AA0,
    sub_8087AA8,
};

// code
void DoWhiteOut(void)
{
    ScriptContext2_RunNewScript(EventScript_WhiteOut);
    SetMoney(&gSaveBlock1Ptr->money, GetMoney(&gSaveBlock1Ptr->money) / 2);
    HealPlayerParty();
    Overworld_ResetStateAfterWhiteOut();
    Overworld_SetWarpDestToLastHealLoc();
    warp_in();
}

void Overworld_ResetStateAfterFly(void)
{
    player_avatar_init_params_reset();
    FlagClear(FLAG_SYS_CYCLING_ROAD);
    FlagClear(FLAG_SYS_CRUISE_MODE);
    FlagClear(FLAG_SYS_SAFARI_MODE);
    FlagClear(FLAG_SYS_USE_STRENGTH);
    FlagClear(FLAG_SYS_USE_FLASH);
}

void Overworld_ResetStateAfterTeleport(void)
{
    player_avatar_init_params_reset();
    FlagClear(FLAG_SYS_CYCLING_ROAD);
    FlagClear(FLAG_SYS_CRUISE_MODE);
    FlagClear(FLAG_SYS_SAFARI_MODE);
    FlagClear(FLAG_SYS_USE_STRENGTH);
    FlagClear(FLAG_SYS_USE_FLASH);
    ScriptContext2_RunNewScript(EventScript_271862);
}

void Overworld_ResetStateAfterDigEscRope(void)
{
    player_avatar_init_params_reset();
    FlagClear(FLAG_SYS_CYCLING_ROAD);
    FlagClear(FLAG_SYS_CRUISE_MODE);
    FlagClear(FLAG_SYS_SAFARI_MODE);
    FlagClear(FLAG_SYS_USE_STRENGTH);
    FlagClear(FLAG_SYS_USE_FLASH);
}

static void Overworld_ResetStateAfterWhiteOut(void)
{
    player_avatar_init_params_reset();
    FlagClear(FLAG_SYS_CYCLING_ROAD);
    FlagClear(FLAG_SYS_CRUISE_MODE);
    FlagClear(FLAG_SYS_SAFARI_MODE);
    FlagClear(FLAG_SYS_USE_STRENGTH);
    FlagClear(FLAG_SYS_USE_FLASH);
    if (VarGet(VAR_0x4039) == 1)
    {
        VarSet(VAR_0x4039, 0);
        VarSet(VAR_0x4037, 0);
    }
}

static void sub_8084788(void)
{
    FlagClear(FLAG_SYS_SAFARI_MODE);
    ChooseAmbientCrySpecies();
    ResetCyclingRoadChallengeData();
    UpdateLocationHistoryForRoamer();
    RoamerMoveToOtherLocationSet();
}

void ResetGameStats(void)
{
    s32 i;

    for (i = 0; i < NUM_GAME_STATS; i++)
        SetGameStat(i, 0);
}

void IncrementGameStat(u8 index)
{
    if (index < NUM_USED_GAME_STATS)
    {
        u32 statVal = GetGameStat(index);
        if (statVal < 0xFFFFFF)
            statVal++;
        else
            statVal = 0xFFFFFF;

        SetGameStat(index, statVal);
    }
}

u32 GetGameStat(u8 index)
{
    if (index >= NUM_USED_GAME_STATS)
        return 0;

    return gSaveBlock1Ptr->gameStats[index] ^ gSaveBlock2Ptr->encryptionKey;
}

void SetGameStat(u8 index, u32 value)
{
    if (index < NUM_USED_GAME_STATS)
        gSaveBlock1Ptr->gameStats[index] = value ^ gSaveBlock2Ptr->encryptionKey;
}

void ApplyNewEncryptionKeyToGameStats(u32 newKey)
{
    u8 i;

    for (i = 0; i < NUM_GAME_STATS; i++)
        ApplyNewEncryptionKeyToWord(&gSaveBlock1Ptr->gameStats[i], newKey);
}

void LoadMapObjTemplatesFromHeader(void)
{
    // Clear map object templates
    CpuFill32(0, gSaveBlock1Ptr->mapObjectTemplates, sizeof(gSaveBlock1Ptr->mapObjectTemplates));

    // Copy map header events to save block
    CpuCopy32(gMapHeader.events->mapObjects,
              gSaveBlock1Ptr->mapObjectTemplates,
              gMapHeader.events->mapObjectCount * sizeof(struct MapObjectTemplate));
}

void LoadSaveblockMapObjScripts(void)
{
    struct MapObjectTemplate *mapHeaderObjTemplates = gMapHeader.events->mapObjects;
    struct MapObjectTemplate *savObjTemplates = gSaveBlock1Ptr->mapObjectTemplates;
    s32 i;

    for (i = 0; i < 64; i++)
        savObjTemplates[i].script = mapHeaderObjTemplates[i].script;
}

void Overworld_SetMapObjTemplateCoords(u8 localId, s16 x, s16 y)
{
    s32 i;
    struct MapObjectTemplate *savObjTemplates = gSaveBlock1Ptr->mapObjectTemplates;

    for (i = 0; i < 64; i++)
    {
        struct MapObjectTemplate *mapObjectTemplate = &savObjTemplates[i];
        if (mapObjectTemplate->localId == localId)
        {
            mapObjectTemplate->x = x;
            mapObjectTemplate->y = y;
            return;
        }
    }
}

void Overworld_SetMapObjTemplateMovementType(u8 localId, u8 movementType)
{
    s32 i;

    struct MapObjectTemplate *savObjTemplates = gSaveBlock1Ptr->mapObjectTemplates;
    for (i = 0; i < 64; i++)
    {
        struct MapObjectTemplate *mapObjectTemplate = &savObjTemplates[i];
        if (mapObjectTemplate->localId == localId)
        {
            mapObjectTemplate->movementType = movementType;
            return;
        }
    }
}

static void mapdata_load_assets_to_gpu_and_full_redraw(void)
{
    move_tilemap_camera_to_upper_left_corner();
    copy_map_tileset1_tileset2_to_vram(gMapHeader.mapData);
    apply_map_tileset1_tileset2_palette(gMapHeader.mapData);
    DrawWholeMapView();
    cur_mapheader_run_tileset_funcs_after_some_cpuset();
}

const struct MapData *get_mapdata_header(void)
{
    u16 mapDataId = gSaveBlock1Ptr->mapDataId;
    if (mapDataId)
        return gMapAttributes[mapDataId - 1];
    return NULL;
}

void ApplyCurrentWarp(void)
{
    gUnknown_020322DC = gSaveBlock1Ptr->location;
    gSaveBlock1Ptr->location = sWarpDestination;
    sUnknown_020322EC = sDummyWarpData;
    sUnknown_020322F4 = sDummyWarpData;
}

void set_warp2_warp3_to_neg_1(void)
{
    sUnknown_020322EC = sDummyWarpData;
    sUnknown_020322F4 = sDummyWarpData;
}

void SetWarpData(struct WarpData *warp, s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y)
{
    warp->mapGroup = mapGroup;
    warp->mapNum = mapNum;
    warp->warpId = warpId;
    warp->x = x;
    warp->y = y;
}

bool32 warp_data_is_not_neg_1(struct WarpData *warp)
{
    if (warp->mapGroup != -1)
        return FALSE;
    else if (warp->mapNum != -1)
        return FALSE;
    else if (warp->warpId != -1)
        return FALSE;
    else if (warp->x != -1)
        return FALSE;
    else if (warp->y != -1)
        return FALSE;
    else
        return TRUE;
}

struct MapHeader const *const Overworld_GetMapHeaderByGroupAndId(u16 mapGroup, u16 mapNum)
{
    return gMapGroups[mapGroup][mapNum];
}

struct MapHeader const *const warp1_get_mapheader(void)
{
    return Overworld_GetMapHeaderByGroupAndId(sWarpDestination.mapGroup, sWarpDestination.mapNum);
}

void set_current_map_header_from_sav1_save_old_name(void)
{
    sLastMapSectionId = gMapHeader.regionMapSectionId;
    gMapHeader = *Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
    gSaveBlock1Ptr->mapDataId = gMapHeader.mapDataId;
    gMapHeader.mapData = get_mapdata_header();
}

void LoadSaveblockMapHeader(void)
{
    gMapHeader = *Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
    gMapHeader.mapData = get_mapdata_header();
}

void update_camera_pos_from_warpid(void)
{
    if (gSaveBlock1Ptr->location.warpId >= 0 && gSaveBlock1Ptr->location.warpId < gMapHeader.events->warpCount)
    {
        gSaveBlock1Ptr->pos.x = gMapHeader.events->warps[gSaveBlock1Ptr->location.warpId].x;
        gSaveBlock1Ptr->pos.y = gMapHeader.events->warps[gSaveBlock1Ptr->location.warpId].y;
    }
    else if (gSaveBlock1Ptr->location.x >= 0 && gSaveBlock1Ptr->location.y >= 0)
    {
        gSaveBlock1Ptr->pos.x = gSaveBlock1Ptr->location.x;
        gSaveBlock1Ptr->pos.y = gSaveBlock1Ptr->location.y;
    }
    else
    {
        gSaveBlock1Ptr->pos.x = gMapHeader.mapData->width / 2;
        gSaveBlock1Ptr->pos.y = gMapHeader.mapData->height / 2;
    }
}

void warp_in(void)
{
    ApplyCurrentWarp();
    set_current_map_header_from_sav1_save_old_name();
    update_camera_pos_from_warpid();
}

void Overworld_SetWarpDestination(s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y)
{
    SetWarpData(&sWarpDestination, mapGroup, mapNum, warpId, x, y);
}

void warp1_set_2(s8 mapGroup, s8 mapNum, s8 warpId)
{
    Overworld_SetWarpDestination(mapGroup, mapNum, warpId, -1, -1);
}

void saved_warp2_set(s32 unused, s8 mapGroup, s8 mapNum, s8 warpId)
{
    SetWarpData(&gSaveBlock1Ptr->warp2, mapGroup, mapNum, warpId, gSaveBlock1Ptr->pos.x, gSaveBlock1Ptr->pos.y);
}

void saved_warp2_set_2(s32 unused, s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y)
{
    SetWarpData(&gSaveBlock1Ptr->warp2, mapGroup, mapNum, warpId, x, y);
}

void copy_saved_warp2_bank_and_enter_x_to_warp1(u8 unused)
{
    sWarpDestination = gSaveBlock1Ptr->warp2;
}

void sub_8084CCC(u8 a1)
{
    const struct HealLocation *warp = GetHealLocationPointer(a1);

    if (warp)
        Overworld_SetWarpDestination(warp->group, warp->map, -1, warp->x, warp->y);
}

void Overworld_SetWarpDestToLastHealLoc(void)
{
    sWarpDestination = gSaveBlock1Ptr->lastHealLocation;
}

void Overworld_SetHealLocationWarp(u8 healLocationId)
{
    const struct HealLocation *healLocation = GetHealLocationPointer(healLocationId);

    if (healLocation != NULL)
        SetWarpData(&gSaveBlock1Ptr->lastHealLocation, healLocation->group, healLocation->map, -1, healLocation->x, healLocation->y);
}

void sub_8084D5C(s16 a1, s16 a2)
{
    u8 currMapType = Overworld_GetMapTypeOfSaveblockLocation();
    u8 destMapType = GetMapTypeByGroupAndId(sWarpDestination.mapGroup, sWarpDestination.mapNum);
    if (is_map_type_1_2_3_5_or_6(currMapType) && is_map_type_1_2_3_5_or_6(destMapType) != TRUE)
        sub_8084DD4(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum, -1, a1 - 7, a2 - 6);
}

void sub_8084DD4(s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y)
{
    SetWarpData(&gSaveBlock1Ptr->warp4, mapGroup, mapNum, warpId, x, y);
}

void sub_8084E14(void)
{
    sWarpDestination = gSaveBlock1Ptr->warp4;
}

void sub_8084E2C(s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y)
{
    SetWarpData(&sUnknown_020322EC, mapGroup, mapNum, warpId, x, y);
}

void warp1_set_to_warp2(void)
{
    sWarpDestination = sUnknown_020322EC;
}

void sub_8084E80(s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y)
{
    SetWarpData(&sUnknown_020322F4, mapGroup, mapNum, warpId, x, y);
}

void sub_8084EBC(s16 x, s16 y)
{
    if (warp_data_is_not_neg_1(&sUnknown_020322F4) == TRUE)
        sWarpDestination = gUnknown_020322DC;
    else
        Overworld_SetWarpDestination(sUnknown_020322F4.mapGroup, sUnknown_020322F4.mapNum, -1, x, y);
}

void warp1_set_to_sav1w(void)
{
    sWarpDestination = gSaveBlock1Ptr->warp1;
}

void sub_8084F2C(s8 mapGroup, s8 mapNum, s8 warpId, s8 x, s8 y)
{
    SetWarpData(&gSaveBlock1Ptr->warp1, mapGroup, mapNum, warpId, x, y);
}

void sub_8084F6C(u8 a1)
{
    const struct HealLocation *warp = GetHealLocationPointer(a1);
    if (warp)
        SetWarpData(&gSaveBlock1Ptr->warp1, warp->group, warp->map, -1, warp->x, warp->y);
}

void sub_8084FAC(void)
{
    gSaveBlock1Ptr->warp1 = gSaveBlock1Ptr->warp2;
}

const struct MapConnection *GetMapConnection(u8 dir)
{
    s32 i;
    s32 count = gMapHeader.connections->count;
    const struct MapConnection *connection = gMapHeader.connections->connections;

    if (connection == NULL)
        return NULL;

    for(i = 0; i < count; i++, connection++)
        if (connection->direction == dir)
            return connection;

    return NULL;
}

bool8 sub_8084FF8(u8 dir, u16 x, u16 y)
{
    const struct MapConnection *connection = GetMapConnection(dir);

    if (connection != NULL)
    {
        Overworld_SetWarpDestination(connection->mapGroup, connection->mapNum, -1, x, y);
    }
    else
    {
        mapheader_run_script_with_tag_x6();
        if (warp_data_is_not_neg_1(&sUnknown_020322EC))
            return FALSE;
        warp1_set_to_warp2();
    }
    return TRUE;
}

bool8 sub_8085058(u16 x, u16 y)
{
    return sub_8084FF8(CONNECTION_EMERGE, x, y);
}

bool8 sub_8085078(u16 x, u16 y)
{
    return sub_8084FF8(CONNECTION_DIVE, x, y);
}

void mliX_load_map(u8 mapGroup, u8 mapNum)
{
    s32 paletteIndex;

    Overworld_SetWarpDestination(mapGroup, mapNum, -1, -1, -1);
    if (gMapHeader.regionMapSectionId != 0x3A)
        sub_8085810();

    ApplyCurrentWarp();
    set_current_map_header_from_sav1_save_old_name();
    LoadMapObjTemplatesFromHeader();
    TrySetMapSaveWarpStatus();
    ClearTempFieldEventData();
    ResetCyclingRoadChallengeData();
    prev_quest_postbuffer_cursor_backup_reset();
    TryUpdateRandomTrainerRematches(mapGroup, mapNum);
    DoTimeBasedEvents();
    SetSav1WeatherFromCurrMapHeader();
    ChooseAmbientCrySpecies();
    SetDefaultFlashLevel();
    Overworld_ClearSavedMusic();
    mapheader_run_script_with_tag_x3();
    not_trainer_hill_battle_pyramid();
    copy_map_tileset2_to_vram_2(gMapHeader.mapData);
    apply_map_tileset2_palette(gMapHeader.mapData);

    for (paletteIndex = 6; paletteIndex < 13; paletteIndex++)
        ApplyWeatherGammaShiftToPal(paletteIndex);

    sub_80A0A2C();
    UpdateLocationHistoryForRoamer();
    RoamerMove();
    DoCurrentWeather();
    ResetFieldTasksArgs();
    mapheader_run_script_with_tag_x5();

    if (gMapHeader.regionMapSectionId != 0x3A || gMapHeader.regionMapSectionId != sLastMapSectionId)
        ShowMapNamePopup();
}

static void mli0_load_map(u32 a1)
{
    bool8 v2;
    bool8 indoors;

    set_current_map_header_from_sav1_save_old_name();
    if (!(sUnknown_020322D8 & 1))
    {
        if (gMapHeader.mapDataId == 0x169)
            sub_81AA1D8();
        else if (InTrainerHill())
            sub_81D5DF8();
        else
            LoadMapObjTemplatesFromHeader();
    }

    v2 = is_map_type_1_2_3_5_or_6(gMapHeader.mapType);
    indoors = Overworld_MapTypeIsIndoors(gMapHeader.mapType);

    sub_80EB218();
    TrySetMapSaveWarpStatus();
    ClearTempFieldEventData();
    ResetCyclingRoadChallengeData();
    prev_quest_postbuffer_cursor_backup_reset();
    TryUpdateRandomTrainerRematches(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
    if (a1 != 1)
        DoTimeBasedEvents();
    SetSav1WeatherFromCurrMapHeader();
    ChooseAmbientCrySpecies();
    if (v2)
        FlagClear(FLAG_SYS_USE_FLASH);
    SetDefaultFlashLevel();
    Overworld_ClearSavedMusic();
    mapheader_run_script_with_tag_x3();
    UpdateLocationHistoryForRoamer();
    RoamerMoveToOtherLocationSet();
    if (gMapHeader.mapDataId == 0x169)
        battle_pyramid_map_load_related(0);
    else if (InTrainerHill())
        trainer_hill_map_load_related();
    else
        not_trainer_hill_battle_pyramid();

    if (a1 != 1 && indoors)
    {
        UpdateTVScreensOnMap(gUnknown_03005DC0.width, gUnknown_03005DC0.height);
        sub_80E9238(1);
    }
}

void player_avatar_init_params_reset(void)
{
    sUnknown_02032300.player_field_1 = 1;
    sUnknown_02032300.player_field_0 = 1;
}

void walkrun_find_lowest_active_bit_in_bitfield(void)
{
    sUnknown_02032300.player_field_1 = player_get_direction_lower_nybble();

    if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_MACH_BIKE))
        sUnknown_02032300.player_field_0 = 2;
    else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_ACRO_BIKE))
        sUnknown_02032300.player_field_0 = 4;
    else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING))
        sUnknown_02032300.player_field_0 = 8;
    else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_4))
        sUnknown_02032300.player_field_0 = 16;
    else
        sUnknown_02032300.player_field_0 = 1;
}

static struct UnkPlayerStruct *sub_80852D4(void)
{
    struct UnkPlayerStruct playerStruct;
    u8 mapType = Overworld_GetMapTypeOfSaveblockLocation();
    u16 v2 = cur_mapdata_block_role_at_screen_center_acc_to_sav1();
    u8 v4 = sub_808532C(&sUnknown_02032300, v2, mapType);
    playerStruct.player_field_0 = v4;
    playerStruct.player_field_1 = sub_808538C(&sUnknown_02032300, v4, v2, mapType);
    sUnknown_02032300 = playerStruct;
    return &sUnknown_02032300;
}

static u8 sub_808532C(struct UnkPlayerStruct *playerStruct, u16 a2, u8 a3)
{
    if (a3 != 8 && FlagGet(FLAG_SYS_CRUISE_MODE))
        return 1;
    else if (a3 == 5)
        return 16;
    else if (MetatileBehavior_IsSurfableWaterOrUnderwater(a2) == 1)
        return 8;
    else if (Overworld_IsBikingAllowed() != TRUE)
        return 1;
    else if (playerStruct->player_field_0 == 2)
        return 2;
    else if (playerStruct->player_field_0 != 4)
        return 1;
    else
        return 4;
}

static u8 sub_808538C(struct UnkPlayerStruct *playerStruct, u8 a2, u16 a3, u8 a4)
{
    if (FlagGet(FLAG_SYS_CRUISE_MODE) && a4 == 6)
        return 4;
    else if (MetatileBehavior_IsDeepSouthWarp(a3) == TRUE)
        return 2;
    else if (MetatileBehavior_IsNonAnimDoor(a3) == TRUE || MetatileBehavior_IsDoor(a3) == TRUE)
        return 1;
    else if (MetatileBehavior_IsSouthArrowWarp(a3) == TRUE)
        return 2;
    else if (MetatileBehavior_IsNorthArrowWarp(a3) == TRUE)
        return 1;
    else if (MetatileBehavior_IsWestArrowWarp(a3) == TRUE)
        return 4;
    else if (MetatileBehavior_IsEastArrowWarp(a3) == TRUE)
        return 3;
    else if ((playerStruct->player_field_0 == 16 && a2 == 8)
     || (playerStruct->player_field_0 == 8 && a2 == 16))
        return playerStruct->player_field_1;
    else if (MetatileBehavior_IsLadder(a3) == TRUE)
        return playerStruct->player_field_1;
    else
        return 1;
}

static u16 cur_mapdata_block_role_at_screen_center_acc_to_sav1(void)
{
    return MapGridGetMetatileBehaviorAt(gSaveBlock1Ptr->pos.x + 7, gSaveBlock1Ptr->pos.y + 7);
}

bool32 Overworld_IsBikingAllowed(void)
{
    if (!(gMapHeader.flags & 1))
        return FALSE;
    else
        return TRUE;
}

void SetDefaultFlashLevel(void)
{
    if (!gMapHeader.cave)
        gSaveBlock1Ptr->flashLevel = 0;
    else if (FlagGet(FLAG_SYS_USE_FLASH))
        gSaveBlock1Ptr->flashLevel = 1;
    else
        gSaveBlock1Ptr->flashLevel = gMaxFlashLevel - 1;
}

void Overworld_SetFlashLevel(s32 flashLevel)
{
    if (flashLevel < 0 || flashLevel > gMaxFlashLevel)
        flashLevel = 0;
    gSaveBlock1Ptr->flashLevel = flashLevel;
}

u8 Overworld_GetFlashLevel(void)
{
    return gSaveBlock1Ptr->flashLevel;
}

void sub_8085524(u16 mapDataId)
{
    gSaveBlock1Ptr->mapDataId = mapDataId;
    gMapHeader.mapData = get_mapdata_header();
}

void sub_8085540(u8 var)
{
    sUnknown_020322D8 = var;
}

u8 sub_808554C(void)
{
    return sUnknown_020322D8;
}

static bool16 ShouldLegendaryMusicPlayAtLocation(struct WarpData *warp)
{
    if (!FlagGet(FLAG_SYS_WEATHER_CTRL))
        return FALSE;
    if (warp->mapGroup == 0)
    {
        switch (warp->mapNum)
        {
        case MAP_NUM(LILYCOVE_CITY):
        case MAP_NUM(MOSSDEEP_CITY):
        case MAP_NUM(SOOTOPOLIS_CITY):
        case MAP_NUM(EVER_GRANDE_CITY):
        case MAP_NUM(ROUTE124):
        case MAP_NUM(ROUTE125):
        case MAP_NUM(ROUTE126):
        case MAP_NUM(ROUTE127):
        case MAP_NUM(ROUTE128):
            return TRUE;
        default:
            if (VarGet(VAR_0x405E) < 4)
                return FALSE;
            switch (warp->mapNum)
            {
            case MAP_NUM(ROUTE129):
            case MAP_NUM(ROUTE130):
            case MAP_NUM(ROUTE131):
                return TRUE;
            }
        }
    }
    return FALSE;
}

static bool16 NoMusicInSotopolisWithLegendaries(struct WarpData *warp)
{
    if (VarGet(VAR_0x40CA) != 1)
        return FALSE;
    else if (warp->mapGroup != MAP_GROUP(SOOTOPOLIS_CITY))
        return FALSE;
    else if (warp->mapNum == MAP_NUM(SOOTOPOLIS_CITY))
        return TRUE;
    else
        return FALSE;
}

static bool16 IsInfiltratedWeatherInstitute(struct WarpData *warp)
{
    if (VarGet(VAR_WEATHER_INSTITUTE_STATE))
        return FALSE;
    else if (warp->mapGroup != MAP_GROUP(ROUTE119_WEATHER_INSTITUTE_1F))
        return FALSE;
    else if (warp->mapNum == MAP_NUM(ROUTE119_WEATHER_INSTITUTE_1F)
     || warp->mapNum == MAP_NUM(ROUTE119_WEATHER_INSTITUTE_2F))
        return TRUE;
    else
        return FALSE;
}

static bool16 IsInflitratedSpaceCenter(struct WarpData *warp)
{
    if (VarGet(VAR_0x405D) == 0)
        return FALSE;
    else if (VarGet(VAR_0x405D) > 2)
        return FALSE;
    else if (warp->mapGroup != MAP_GROUP(MOSSDEEP_CITY_SPACE_CENTER_1F))
        return FALSE;
    else if (warp->mapNum == MAP_NUM(MOSSDEEP_CITY_SPACE_CENTER_1F)
     || warp->mapNum == MAP_NUM(MOSSDEEP_CITY_SPACE_CENTER_2F))
        return TRUE;
    return FALSE;
}

u16 GetLocationMusic(struct WarpData *warp)
{
    if (NoMusicInSotopolisWithLegendaries(warp) == TRUE)
        return 0xFFFF;
    else if (ShouldLegendaryMusicPlayAtLocation(warp) == TRUE)
        return MUS_OOAME;
    else if (IsInflitratedSpaceCenter(warp) == TRUE)
        return MUS_MGM0;
    else if (IsInfiltratedWeatherInstitute(warp) == TRUE)
        return MUS_TOZAN;
    else
        return Overworld_GetMapHeaderByGroupAndId(warp->mapGroup, warp->mapNum)->music;
}

u16 GetCurrLocationDefaultMusic(void)
{
    u16 music;

    // Play the desert music only when the sandstorm is active on Route 111.
    if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(ROUTE111)
     && gSaveBlock1Ptr->location.mapNum == MAP_NUM(ROUTE111)
     && GetSav1Weather() == 8)
        return MUS_ASHROAD;

    music = GetLocationMusic(&gSaveBlock1Ptr->location);
    if (music != 0x7FFF)
    {
        return music;
    }
    else
    {
        if (gSaveBlock1Ptr->pos.x < 24)
            return MUS_DOORO_X1;
        else
            return MUS_GRANROAD;
    }
}

u16 GetWarpDestinationMusic(void)
{
    u16 music = GetLocationMusic(&sWarpDestination);
    if (music != 0x7FFF)
    {
        return music;
    }
    else
    {
        if (gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(MAUVILLE_CITY)
         && gSaveBlock1Ptr->location.mapNum == MAP_NUM(MAUVILLE_CITY))
            return MUS_DOORO_X1;
        else
            return MUS_GRANROAD;
    }
}

void Overworld_ResetMapMusic(void)
{
    ResetMapMusic();
}

void Overworld_PlaySpecialMapMusic(void)
{
    u16 music = GetCurrLocationDefaultMusic();

    if (music != MUS_OOAME && music != 0xFFFF)
    {
        if (gSaveBlock1Ptr->savedMusic)
            music = gSaveBlock1Ptr->savedMusic;
        else if (Overworld_GetMapTypeOfSaveblockLocation() == MAP_TYPE_UNDERWATER)
            music = MUS_DEEPDEEP;
        else if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING))
            music = MUS_NAMINORI;
    }

    if (music != GetCurrentMapMusic())
        PlayNewMapMusic(music);
}

void Overworld_SetSavedMusic(u16 songNum)
{
    gSaveBlock1Ptr->savedMusic = songNum;
}

void Overworld_ClearSavedMusic(void)
{
    gSaveBlock1Ptr->savedMusic = 0;
}

static void sub_8085810(void)
{
    if (FlagGet(FLAG_SPECIAL_FLAG_0x4001) != TRUE)
    {
        u16 newMusic = GetWarpDestinationMusic();
        u16 currentMusic = GetCurrentMapMusic();
        if (newMusic != MUS_OOAME && newMusic != 0xFFFF)
        {
            if (currentMusic == MUS_DEEPDEEP || currentMusic == MUS_NAMINORI)
                return;
            if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING))
                newMusic = MUS_NAMINORI;
        }
        if (newMusic != currentMusic)
        {
            if (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_MACH_BIKE | PLAYER_AVATAR_FLAG_ACRO_BIKE))
                FadeOutAndFadeInNewMapMusic(newMusic, 4, 4);
            else
                FadeOutAndPlayNewMapMusic(newMusic, 8);
        }
    }
}

void Overworld_ChangeMusicToDefault(void)
{
    u16 currentMusic = GetCurrentMapMusic();
    if (currentMusic != GetCurrLocationDefaultMusic())
        FadeOutAndPlayNewMapMusic(GetCurrLocationDefaultMusic(), 8);
}

void Overworld_ChangeMusicTo(u16 newMusic)
{
    u16 currentMusic = GetCurrentMapMusic();
    if (currentMusic != newMusic && currentMusic != MUS_OOAME)
        FadeOutAndPlayNewMapMusic(newMusic, 8);
}

u8 GetMapMusicFadeoutSpeed(void)
{
    const struct MapHeader *mapHeader = warp1_get_mapheader();
    if (Overworld_MapTypeIsIndoors(mapHeader->mapType) == TRUE)
        return 2;
    else
        return 4;
}

void music_something(void)
{
    u16 currentMusic = GetCurrentMapMusic();
    u16 warpMusic = GetWarpDestinationMusic();
    if (FlagGet(FLAG_SPECIAL_FLAG_0x4001) != TRUE && warpMusic != GetCurrentMapMusic())
    {
        if (currentMusic == MUS_NAMINORI
            && VarGet(VAR_0x40CA) == 2
            && gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(SOOTOPOLIS_CITY)
            && gSaveBlock1Ptr->location.mapNum == MAP_NUM(SOOTOPOLIS_CITY)
            && sWarpDestination.mapGroup == MAP_GROUP(SOOTOPOLIS_CITY)
            && sWarpDestination.mapNum == MAP_NUM(SOOTOPOLIS_CITY)
            && sWarpDestination.x == 0x1D
            && sWarpDestination.y == 0x35)
            return;
        FadeOutMapMusic(GetMapMusicFadeoutSpeed());
    }
}

bool8 sub_80859A0(void)
{
    return IsNotWaitingForBGMStop();
}

void Overworld_FadeOutMapMusic(void)
{
    FadeOutMapMusic(4);
}

static void PlayAmbientCry(void)
{
    s16 x, y;
    s8 pan;
    s8 volume;

    PlayerGetDestCoords(&x, &y);
    if (sIsAmbientCryWaterMon == TRUE
     && !MetatileBehavior_IsSurfableWaterOrUnderwater(MapGridGetMetatileBehaviorAt(x, y)))
        return;
    pan = (Random() % 88) + 212;
    volume = (Random() % 30) + 50;
    PlayCry2(sAmbientCrySpecies, pan, volume, 1);
}

void UpdateAmbientCry(s16 *state, u16 *delayCounter)
{
    u8 i, monsCount, divBy;

    switch (*state)
    {
    case 0:
        if (sAmbientCrySpecies == SPECIES_NONE)
            *state = 4;
        else
            *state = 1;
        break;
    case 1:
        *delayCounter = (Random() % 2400) + 1200;
        *state = 3;
        break;
    case 2:
        divBy = 1;
        monsCount = CalculatePlayerPartyCount();
        for (i = 0; i < monsCount; i++)
        {
            if (!GetMonData(&gPlayerParty[i], MON_DATA_SANITY_BIT3)
                && GetMonAbility(&gPlayerParty[0]) == ABILITY_SWARM)
            {
                divBy = 2;
                break;
            }
        }
        *delayCounter = ((Random() % 1200) + 1200) / divBy;
        *state = 3;
        break;
    case 3:
        (*delayCounter)--;
        if (*delayCounter == 0)
        {
            PlayAmbientCry();
            *state = 2;
        }
        break;
    case 4:
        break;
    }
}

static void ChooseAmbientCrySpecies(void)
{
    if ((gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(ROUTE130)
     && gSaveBlock1Ptr->location.mapNum == MAP_NUM(ROUTE130))
     && !IsMirageIslandPresent())
    {
        // Only play water pokemon cries on this route
        // when Mirage Island is not present
        sIsAmbientCryWaterMon = TRUE;
        sAmbientCrySpecies = GetLocalWaterMon();
    }
    else
    {
        sAmbientCrySpecies = GetLocalWildMon(&sIsAmbientCryWaterMon);
    }
}

u8 GetMapTypeByGroupAndId(s8 mapGroup, s8 mapNum)
{
    return Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->mapType;
}

u8 GetMapTypeByWarpData(struct WarpData *warp)
{
    return GetMapTypeByGroupAndId(warp->mapGroup, warp->mapNum);
}

u8 Overworld_GetMapTypeOfSaveblockLocation(void)
{
    return GetMapTypeByWarpData(&gSaveBlock1Ptr->location);
}

u8 get_map_light_from_warp0(void)
{
    return GetMapTypeByWarpData(&gUnknown_020322DC);
}

bool8 is_map_type_1_2_3_5_or_6(u8 mapType)
{
    if (mapType == MAP_TYPE_ROUTE
     || mapType == MAP_TYPE_TOWN
     || mapType == MAP_TYPE_UNDERWATER
     || mapType == MAP_TYPE_CITY
     || mapType == MAP_TYPE_6)
        return TRUE;
    else
        return FALSE;
}

bool8 Overworld_MapTypeAllowsTeleportAndFly(u8 mapType)
{
    if (mapType == MAP_TYPE_ROUTE
     || mapType == MAP_TYPE_TOWN
     || mapType == MAP_TYPE_6
     || mapType == MAP_TYPE_CITY)
        return TRUE;
    else
        return FALSE;
}

bool8 Overworld_MapTypeIsIndoors(u8 mapType)
{
    if (mapType == MAP_TYPE_INDOOR
     || mapType == MAP_TYPE_SECRET_BASE)
        return TRUE;
    else
        return FALSE;
}

u8 sav1_saved_warp2_map_get_name(void)
{
    return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->warp2.mapGroup, gSaveBlock1Ptr->warp2.mapNum)->regionMapSectionId;
}

u8 sav1_map_get_name(void)
{
    return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->regionMapSectionId;
}

u8 GetCurrentMapBattleScene(void)
{
    return Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum)->battleType;
}

static void overworld_bg_setup(void)
{
    InitBgsFromTemplates(0, gUnknown_08339DAC, ARRAY_COUNT(gUnknown_08339DAC));
    SetBgAttribute(1, BG_CTRL_ATTR_PALETTEMODE, 1);
    SetBgAttribute(2, BG_CTRL_ATTR_PALETTEMODE, 1);
    SetBgAttribute(3, BG_CTRL_ATTR_PALETTEMODE, 1);
    gBGTilemapBuffers2 = AllocZeroed(0x800);
    gBGTilemapBuffers1 = AllocZeroed(0x800);
    gBGTilemapBuffers3 = AllocZeroed(0x800);
    SetBgTilemapBuffer(1, gBGTilemapBuffers2);
    SetBgTilemapBuffer(2, gBGTilemapBuffers1);
    SetBgTilemapBuffer(3, gBGTilemapBuffers3);
    sub_81971D0();
}

void overworld_free_bg_tilemaps(void)
{
    sub_81BE72C();
    sub_81971F4();
    if (gBGTilemapBuffers3 != NULL)
        FREE_AND_SET_NULL(gBGTilemapBuffers3);
    if (gBGTilemapBuffers1 != NULL)
        FREE_AND_SET_NULL(gBGTilemapBuffers1);
    if (gBGTilemapBuffers2 != NULL)
        FREE_AND_SET_NULL(gBGTilemapBuffers2);
}

static void ResetSafariZoneFlag_(void)
{
    ResetSafariZoneFlag();
}

bool32 is_c1_link_related_active(void)
{
    if (gMain.callback1 == c1_link_related)
        return TRUE;
    else
        return FALSE;
}

static void DoCB1_Overworld(u16 newKeys, u16 heldKeys)
{
    struct FieldInput inputStruct;

    sub_808B578();
    FieldClearPlayerInput(&inputStruct);
    FieldGetPlayerInput(&inputStruct, newKeys, heldKeys);
    if (!ScriptContext2_IsEnabled())
    {
        if (sub_809C014(&inputStruct) == 1)
        {
            ScriptContext2_Enable();
            HideMapNamePopUpWindow();
        }
        else
        {
            player_step(inputStruct.dpadDirection, newKeys, heldKeys);
        }
    }
}

void CB1_Overworld(void)
{
    if (gMain.callback2 == CB2_Overworld)
        DoCB1_Overworld(gMain.newKeys, gMain.heldKeys);
}

static void OverworldBasic(void)
{
    ScriptContext2_RunScript();
    RunTasks();
    AnimateSprites();
    CameraUpdate();
    UpdateCameraPanning();
    BuildOamBuffer();
    UpdatePaletteFade();
    sub_80A0A38();
    do_scheduled_bg_tilemap_copies_to_vram();
}

// This CB2 is used when starting
void CB2_OverworldBasic(void)
{
    OverworldBasic();
}

void CB2_Overworld(void)
{
    bool32 fading = (gPaletteFade.active != 0);
    if (fading)
        SetVBlankCallback(NULL);
    OverworldBasic();
    if (fading)
        SetFieldVBlankCallback();
}

void SetMainCallback1(MainCallback cb)
{
    gMain.callback1 = cb;
}

void sub_8085E94(void *a0)
{
    sUnknown_03000E0C = a0;
}

static bool8 map_post_load_hook_exec(void)
{
    if (gUnknown_03005DB0 != NULL)
    {
        if (!gUnknown_03005DB0())
        {
            return FALSE;
        }
        else
        {
            gUnknown_03005DB0 = NULL;
            gFieldCallback = NULL;
        }
    }
    else
    {
        if (gFieldCallback != NULL)
            gFieldCallback();
        else
            mapldr_default();

        gFieldCallback = NULL;
    }

    return TRUE;
}

void CB2_NewGame(void)
{
    FieldClearVBlankHBlankCallbacks();
    StopMapMusic();
    ResetSafariZoneFlag_();
    NewGameInitData();
    player_avatar_init_params_reset();
    PlayTimeCounter_Start();
    ScriptContext1_Init();
    ScriptContext2_Disable();
    gFieldCallback = ExecuteTruckSequence;
    gUnknown_03005DB0 = NULL;
    do_load_map_stuff_loop(&gMain.state);
    SetFieldVBlankCallback();
    SetMainCallback1(CB1_Overworld);
    SetMainCallback2(CB2_Overworld);
}

void CB2_WhiteOut(void)
{
    u8 val;

    if (++gMain.state >= 120)
    {
        FieldClearVBlankHBlankCallbacks();
        StopMapMusic();
        ResetSafariZoneFlag_();
        DoWhiteOut();
        player_avatar_init_params_reset();
        ScriptContext1_Init();
        ScriptContext2_Disable();
        gFieldCallback = sub_80AF3C8;
        val = 0;
        do_load_map_stuff_loop(&val);
        SetFieldVBlankCallback();
        SetMainCallback1(CB1_Overworld);
        SetMainCallback2(CB2_Overworld);
    }
}

void CB2_LoadMap(void)
{
    FieldClearVBlankHBlankCallbacks();
    ScriptContext1_Init();
    ScriptContext2_Disable();
    SetMainCallback1(NULL);
    SetMainCallback2(c2_change_map);
    gMain.savedCallback = CB2_LoadMap2;
}

static void CB2_LoadMap2(void)
{
    do_load_map_stuff_loop(&gMain.state);
    SetFieldVBlankCallback();
    SetMainCallback1(CB1_Overworld);
    SetMainCallback2(CB2_Overworld);
}

void sub_8086024(void)
{
    if (!gMain.state)
    {
        FieldClearVBlankHBlankCallbacks();
        ScriptContext1_Init();
        ScriptContext2_Disable();
        SetMainCallback1(NULL);
    }
    if (load_map_stuff(&gMain.state, 1))
    {
        SetFieldVBlankCallback();
        SetMainCallback1(CB1_Overworld);
        SetMainCallback2(CB2_Overworld);
    }
}

void sub_8086074(void)
{
    FieldClearVBlankHBlankCallbacks();
    gFieldCallback = sub_80AF314;
    SetMainCallback2(c2_80567AC);
}

static void c2_80567AC(void)
{
    if (map_loading_iteration_3(&gMain.state))
    {
        SetFieldVBlankCallback();
        SetMainCallback1(c1_link_related);
        sub_8086C2C();
        SetMainCallback2(CB2_Overworld);
    }
}

void CB2_ReturnToField(void)
{
    if (is_c1_link_related_active() == TRUE)
    {
        SetMainCallback2(CB2_ReturnToFieldLink);
    }
    else
    {
        FieldClearVBlankHBlankCallbacks();
        SetMainCallback2(CB2_ReturnToFieldLocal);
    }
}

void CB2_ReturnToFieldLocal(void)
{
    if (sub_8086638(&gMain.state))
    {
        SetFieldVBlankCallback();
        SetMainCallback2(CB2_Overworld);
    }
}

void CB2_ReturnToFieldLink(void)
{
    if (!sub_8087598() && map_loading_iteration_2_link(&gMain.state))
        SetMainCallback2(CB2_Overworld);
}

void c2_8056854(void)
{
    FieldClearVBlankHBlankCallbacks();
    StopMapMusic();
    SetMainCallback1(c1_link_related);
    sub_8086C2C();

    if (gWirelessCommType != 0)
        gFieldCallback = sub_80AF314;
    else
        gFieldCallback = sub_80AF214;

    ScriptContext1_Init();
    ScriptContext2_Disable();
    CB2_ReturnToField();
}

void CB2_ReturnToFieldWithOpenMenu(void)
{
    FieldClearVBlankHBlankCallbacks();
    gUnknown_03005DB0 = sub_80AF6A4;
    CB2_ReturnToField();
}

void sub_80861B0(void)
{
    FieldClearVBlankHBlankCallbacks();
    gFieldCallback = sub_80AF188;
    CB2_ReturnToField();
}

void CB2_ReturnToFieldContinueScript(void)
{
    FieldClearVBlankHBlankCallbacks();
    gFieldCallback = sub_80AF168;
    CB2_ReturnToField();
}

void sub_80861E8(void)
{
    FieldClearVBlankHBlankCallbacks();
    gFieldCallback = sub_80AF3C8;
    CB2_ReturnToField();
}

static void sub_8086204(void)
{
    if ((gMapHeader.flags & 0xF8) == 8 && sub_80E909C() == TRUE)
        ShowMapNamePopup();
    sub_80AF3C8();
}

void CB2_ContinueSavedGame(void)
{
    u8 trainerHillMapId;

    FieldClearVBlankHBlankCallbacks();
    StopMapMusic();
    ResetSafariZoneFlag_();
    if (gSaveFileStatus == 0xFF)
        sub_81A3908();

    LoadSaveblockMapHeader();
    set_warp2_warp3_to_neg_1();
    trainerHillMapId = GetCurrentTrainerHillMapId();
    if (gMapHeader.mapDataId == 0x169)
        sub_81AA2F8();
    else if (trainerHillMapId != 0 && trainerHillMapId != 6)
        sub_81D5F48();
    else
        LoadSaveblockMapObjScripts();

    UnfreezeMapObjects();
    DoTimeBasedEvents();
    sub_8084788();
    if (gMapHeader.mapDataId == 0x169)
        battle_pyramid_map_load_related(1);
    else if (trainerHillMapId != 0)
        trainer_hill_map_load_related();
    else
        sub_8087D74();

    PlayTimeCounter_Start();
    ScriptContext1_Init();
    ScriptContext2_Disable();
    sub_8195E10();
    if (GetSecretBase2Field_9() == 1)
    {
        ClearSecretBase2Field_9();
        warp1_set_to_sav1w();
        warp_in();
        sub_80EDB44();
        SetMainCallback2(CB2_LoadMap);
    }
    else
    {
        sub_80EDB44();
        gFieldCallback = sub_8086204;
        SetMainCallback1(CB1_Overworld);
        CB2_ReturnToField();
    }
}

static void FieldClearVBlankHBlankCallbacks(void)
{
    if (warp0_in_pokecenter() == TRUE)
        CloseLink();

    if (gWirelessCommType != 0)
    {
        EnableInterrupts(INTR_FLAG_VBLANK | INTR_FLAG_VCOUNT | INTR_FLAG_TIMER3 | INTR_FLAG_SERIAL);
        DisableInterrupts(INTR_FLAG_HBLANK);
    }
    else
    {
        u16 savedIme = REG_IME;
        REG_IME = 0;
        REG_IE &= ~INTR_FLAG_HBLANK;
        REG_IE |= INTR_FLAG_VBLANK;
        REG_IME = savedIme;
    }

    SetVBlankCallback(NULL);
    SetHBlankCallback(NULL);
}

static void SetFieldVBlankCallback(void)
{
    SetVBlankCallback(VBlankCB_Field);
}

static void VBlankCB_Field(void)
{
    LoadOam();
    ProcessSpriteCopyRequests();
    ScanlineEffect_InitHBlankDmaTransfer();
    FieldUpdateBgTilemapScroll();
    TransferPlttBuffer();
    TransferTilesetAnimsBuffer();
}

static void sub_80863B0(void)
{
    u8 val;

    if (sub_81A9E6C())
    {
        door_upload_tiles();
        ScanlineEffect_SetParams(gUnknown_08339DBC);
    }
    else if ((val = Overworld_GetFlashLevel()))
    {
        sub_80B00E8(val);
        ScanlineEffect_SetParams(gUnknown_08339DBC);
    }
}

static bool32 map_loading_iteration_3(u8 *state)
{
    switch (*state)
    {
    case 0:
        overworld_bg_setup();
        ScriptContext1_Init();
        ScriptContext2_Disable();
        sub_80867C8();
        sub_80867D8();
        (*state)++;
        break;
    case 1:
        mli0_load_map(1);
        (*state)++;
        break;
    case 2:
        sub_8086988(TRUE);
        (*state)++;
        break;
    case 3:
        sub_8086AE4();
        sub_80869DC();
        sub_8086B14();
        sub_8086AAC();
        (*state)++;
        break;
    case 4:
        sub_80863B0();
        map_loading_lcd_reset();
        sub_8197200();
        (*state)++;
        break;
    case 5:
        move_tilemap_camera_to_upper_left_corner();
        (*state)++;
        break;
    case 6:
        copy_map_tileset1_to_vram(gMapHeader.mapData);
        (*state)++;
        break;
    case 7:
        copy_map_tileset2_to_vram(gMapHeader.mapData);
        (*state)++;
        break;
    case 8:
        if (free_temp_tile_data_buffers_if_possible() != TRUE)
        {
            apply_map_tileset1_tileset2_palette(gMapHeader.mapData);
            (*state)++;
        }
        break;
    case 9:
        DrawWholeMapView();
        (*state)++;
        break;
    case 10:
        cur_mapheader_run_tileset_funcs_after_some_cpuset();
        (*state)++;
        break;
    case 11:
        if (gWirelessCommType != 0)
        {
            sub_800E0E8();
            CreateWirelessStatusIndicatorSprite(0, 0);
        }
        (*state)++;
        break;
    case 12:
        if (map_post_load_hook_exec())
            (*state)++;
        break;
    case 13:
        return TRUE;
    }

    return FALSE;
}

static bool32 load_map_stuff(u8 *state, u32 a2)
{
    switch (*state)
    {
    case 0:
        FieldClearVBlankHBlankCallbacks();
        mli0_load_map(a2);
        (*state)++;
        break;
    case 1:
        sub_80867C8();
        sub_80867D8();
        (*state)++;
        break;
    case 2:
        sub_8086988(a2);
        (*state)++;
        break;
    case 3:
        mli4_mapscripts_and_other();
        sub_8086A80();
        (*state)++;
        break;
    case 4:
        sub_80863B0();
        map_loading_lcd_reset();
        sub_8197200();
        (*state)++;
        break;
    case 5:
        move_tilemap_camera_to_upper_left_corner();
        (*state)++;
        break;
    case 6:
        copy_map_tileset1_to_vram(gMapHeader.mapData);
        (*state)++;
        break;
    case 7:
        copy_map_tileset2_to_vram(gMapHeader.mapData);
        (*state)++;
        break;
    case 8:
        if (free_temp_tile_data_buffers_if_possible() != TRUE)
        {
            apply_map_tileset1_tileset2_palette(gMapHeader.mapData);
            (*state)++;
        }
        break;
    case 9:
        DrawWholeMapView();
        (*state)++;
        break;
    case 10:
        cur_mapheader_run_tileset_funcs_after_some_cpuset();
        (*state)++;
        break;
    case 11:
        if ((gMapHeader.flags & 0xF8) == 8 && sub_80E909C() == 1)
            ShowMapNamePopup();
        (*state)++;
        break;
    case 12:
        if (map_post_load_hook_exec())
            (*state)++;
        break;
    case 13:
        return TRUE;
    }

    return FALSE;
}

static bool32 sub_8086638(u8 *state)
{
    switch (*state)
    {
    case 0:
        sub_80867C8();
        sub_80867D8();
        sub_8086988(0);
        sub_8086A68();
        sub_8086A80();
        (*state)++;
        break;
    case 1:
        sub_8086860();
        sub_81D64C0();
        (*state)++;
        break;
    case 2:
        if (map_post_load_hook_exec())
            (*state)++;
        break;
    case 3:
        return TRUE;
    }

    return FALSE;
}

static bool32 map_loading_iteration_2_link(u8 *state)
{
    switch (*state)
    {
    case 0:
        FieldClearVBlankHBlankCallbacks();
        sub_80867C8();
        sub_80867D8();
        (*state)++;
        break;
    case 1:
        sub_8086988(1);
        (*state)++;
        break;
    case 2:
        sub_8086B9C();
        sub_8086A68();
        sub_8086AC8();
        (*state)++;
        break;
    case 3:
        sub_80863B0();
        map_loading_lcd_reset();
        sub_8197200();
        (*state)++;
        break;
    case 4:
        move_tilemap_camera_to_upper_left_corner();
        (*state)++;
        break;
    case 5:
        copy_map_tileset1_to_vram(gMapHeader.mapData);
        (*state)++;
        break;
    case 6:
        copy_map_tileset2_to_vram(gMapHeader.mapData);
        (*state)++;
        break;
    case 7:
        if (free_temp_tile_data_buffers_if_possible() != TRUE)
        {
            apply_map_tileset1_tileset2_palette(gMapHeader.mapData);
            (*state)++;
        }
        break;
    case 8:
        DrawWholeMapView();
        (*state)++;
        break;
    case 9:
        cur_mapheader_run_tileset_funcs_after_some_cpuset();
        (*state)++;
        break;
    case 11:
        if (gWirelessCommType != 0)
        {
            sub_800E0E8();
            CreateWirelessStatusIndicatorSprite(0, 0);
        }
        (*state)++;
        break;
    case 12:
        if (map_post_load_hook_exec())
            (*state)++;
        break;
    case 10:
        (*state)++;
        break;
    case 13:
        SetFieldVBlankCallback();
        (*state)++;
        return TRUE;
    }

    return FALSE;
}

static void do_load_map_stuff_loop(u8 *state)
{
    while (!load_map_stuff(state, 0));
}

static void sub_80867C8(void)
{
    sub_81BE6AC();
    MoveSaveBlocks_ResetHeap();
}

static void sub_80867D8(void)
{
    SetGpuReg(REG_OFFSET_DISPCNT, 0);
    ScanlineEffect_Stop();

    DmaClear16(3, PLTT + 2, PLTT_SIZE - 2);
    DmaFillLarge16(3, 0, (void *)(VRAM + 0x0), 0x18000, 0x1000);
    ResetOamRange(0, 128);
    LoadOam();
}

static void sub_8086860(void)
{
    sub_80863B0();
    map_loading_lcd_reset();
    sub_8197200();
    mapdata_load_assets_to_gpu_and_full_redraw();
}

static void map_loading_lcd_reset(void)
{
    clear_scheduled_bg_copies_to_vram();
    reset_temp_tile_data_buffers();
    SetGpuReg(REG_OFFSET_MOSAIC, 0);
    SetGpuReg(REG_OFFSET_WININ, 0x1F1F);
    SetGpuReg(REG_OFFSET_WINOUT, 0x101);
    SetGpuReg(REG_OFFSET_WIN0H, 0xFF);
    SetGpuReg(REG_OFFSET_WIN0V, 0xFF);
    SetGpuReg(REG_OFFSET_WIN1H, 0xFFFF);
    SetGpuReg(REG_OFFSET_WIN1V, 0xFFFF);
    SetGpuReg(REG_OFFSET_BLDCNT, gUnknown_82EC7C4[1] | gUnknown_82EC7C4[2] | gUnknown_82EC7C4[3]
                               | BLDCNT_TGT2_OBJ | BLDCNT_EFFECT_BLEND);
    SetGpuReg(REG_OFFSET_BLDALPHA, BLDALPHA_BLEND(13, 7));
    overworld_bg_setup();
    schedule_bg_copy_tilemap_to_vram(1);
    schedule_bg_copy_tilemap_to_vram(2);
    schedule_bg_copy_tilemap_to_vram(3);
    ChangeBgX(0, 0, 0);
    ChangeBgY(0, 0, 0);
    ChangeBgX(1, 0, 0);
    ChangeBgY(1, 0, 0);
    ChangeBgX(2, 0, 0);
    ChangeBgY(2, 0, 0);
    ChangeBgX(3, 0, 0);
    ChangeBgY(3, 0, 0);
    SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_WIN0_ON | DISPCNT_WIN1_ON
                                | DISPCNT_OBJ_1D_MAP | DISPCNT_HBLANK_INTERVAL);
    ShowBg(0);
    ShowBg(1);
    ShowBg(2);
    ShowBg(3);
    sub_8098128();
}

static void sub_8086988(u32 a1)
{
    ResetTasks();
    ResetSpriteData();
    ResetPaletteFade();
    ScanlineEffect_Clear();
    dp13_810BB8C();
    ResetCameraUpdateInfo();
    InstallCameraPanAheadCallback();
    if (!a1)
        npc_paltag_set_load(0);
    else
        npc_paltag_set_load(1);

    FieldEffectActiveListClear();
    sub_80AAFA4();
    sub_80AEE84();
    if (!a1)
        SetUpFieldTasks();
    mapheader_run_script_with_tag_x5();
    sub_81BE6B8();
}

static void sub_80869DC(void)
{
    gUnknown_03005DEC = 0;
    gUnknown_03005DE8 = 0;
    sub_808D438();
    SpawnFieldObjectsInView(0, 0);
    mapheader_run_first_tag4_script_list_match();
}

static void mli4_mapscripts_and_other(void)
{
    s16 x, y;
    struct UnkPlayerStruct *player;

    gUnknown_03005DEC = 0;
    gUnknown_03005DE8 = 0;
    sub_808D438();
    sav1_camera_get_focus_coords(&x, &y);
    player = sub_80852D4();
    InitPlayerAvatar(x, y, player->player_field_1, gSaveBlock2Ptr->playerGender);
    SetPlayerAvatarTransitionFlags(player->player_field_0);
    player_avatar_init_params_reset();
    SpawnFieldObjectsInView(0, 0);
    mapheader_run_first_tag4_script_list_match();
}

static void sub_8086A68(void)
{
    sub_808E16C(0, 0);
    RotatingGate_InitPuzzleAndGraphics();
    mapheader_run_script_with_tag_x7();
}

static void sub_8086A80(void)
{
    gMapObjects[gPlayerAvatar.mapObjectId].trackedByCamera = 1;
    InitCameraUpdateCallback(gPlayerAvatar.spriteId);
}

static void sub_8086AAC(void)
{
    InitCameraUpdateCallback(sub_8087858(gUnknown_03005DB4));
}

static void sub_8086AC8(void)
{
    InitCameraUpdateCallback(sub_8087858(gUnknown_03005DB4));
}

static void sub_8086AE4(void)
{
    u16 x, y;
    sav1_camera_get_focus_coords(&x, &y);
    sub_8088B3C(x + gUnknown_03005DB4, y);
}

static void sub_8086B14(void)
{
    u16 i;
    u16 x, y;

    sav1_camera_get_focus_coords(&x, &y);
    x -= gUnknown_03005DB4;

    for (i = 0; i < gFieldLinkPlayerCount; i++)
    {
        SpawnLinkPlayerMapObject(i, i + x, y, gLinkPlayers[i].gender);
        CreateLinkPlayerSprite(i, gLinkPlayers[i].version);
    }

    sub_8086C40();
}

static void sub_8086B9C(void)
{
    u16 i;
    for (i = 0; i < gFieldLinkPlayerCount; i++)
        CreateLinkPlayerSprite(i, gLinkPlayers[i].version);
}

static void c1_link_related(void)
{
    if (gWirelessCommType == 0 || !sub_800F0B8() || !sub_8009F3C())
    {
        u8 var = gUnknown_03005DB4;
        sub_8086F38(gLinkPartnersHeldKeys, var);
        sub_8086FA0(sUnknown_03000E14(var));
        sub_8086C40();
    }
}

void sub_8086C2C(void)
{
    sub_8086C90();
    c1_link_related_func_set(sub_80870B0);
}

static void sub_8086C40(void)
{
    sub_808709C(gLinkPartnersHeldKeys);
}

static void c1_link_related_func_set(u16 (*func)(u32))
{
    sUnknown_03000E19 = 0;
    sUnknown_03000E14 = func;
}

static void sub_8086C64(void)
{
    if (gWirelessCommType != 0 && ++sUnknown_03000E19 > 60)
        sub_8010198();
}

static void sub_8086C90(void)
{
    s32 i;
    for (i = 0; i < 4; i++)
        sUnknown_03000E10[i] = 0x80;
}

static bool32 sub_8086CA8(u16 a1)
{
    s32 i;
    s32 count = gFieldLinkPlayerCount;

    for (i = 0; i < count; i++)
        if (sUnknown_03000E10[i] != a1)
            return FALSE;
    return TRUE;
}

static bool32 sub_8086CE0(u16 a1)
{
    s32 i;
    s32 count = gFieldLinkPlayerCount;

    for (i = 0; i < count; i++)
        if (sUnknown_03000E10[i] == a1)
            return TRUE;
    return FALSE;
}

static void sub_8086D18(u32 a1, u16 a2, struct UnkStruct_8054FF8 *a3, u16 *a4)
{
    const u8 *script;

    if (sUnknown_03000E10[a1] == 0x80)
    {
        script = sub_8087370(a3);
        if (script)
        {
            *a4 = sub_8087480(script);
            sUnknown_03000E10[a1] = 0x81;
            if (a3->b)
            {
                c1_link_related_func_set(sub_80870F8);
                sub_8087530(script);
            }
            return;
        }
        if (sub_8086CE0(0x83) == 1)
        {
            sUnknown_03000E10[a1] = 0x81;
            if (a3->b)
            {
                c1_link_related_func_set(sub_80870F8);
                sub_8087584();
            }
            return;
        }
        switch (a2)
        {
        case 24:
            if (sub_8087358(a3))
            {
                sUnknown_03000E10[a1] = 0x81;
                if (a3->b)
                {
                    c1_link_related_func_set(sub_80870F8);
                    sub_808751C();
                }
            }
            break;
        case 18:
            if (sub_8087388(a3) == TRUE)
            {
                sUnknown_03000E10[a1] = 0x81;
                if (a3->b)
                {
                    c1_link_related_func_set(sub_80870F8);
                    sub_808754C();
                }
            }
            break;
        case 25:
            script = sub_80873B4(a3);
            if (script)
            {
                sUnknown_03000E10[a1] = 0x81;
                if (a3->b)
                {
                    c1_link_related_func_set(sub_80870F8);
                    sub_8087568(script);
                }
            }
            break;
        case 27:
            if (sub_8087340(a3))
            {
                sUnknown_03000E10[a1] = 0x81;
                if (a3->b)
                {
                    c1_link_related_func_set(sub_808711C);
                    sub_8087510();
                }
            }
            break;
        case 28:
            if (sub_8087340(a3))
            {
                sUnknown_03000E10[a1] = 0x81;
                if (a3->b)
                {
                    c1_link_related_func_set(sub_8087140);
                    sub_8087510();
                }
            }
            break;
        }
    }

    switch (a2)
    {
    case 23:
        sUnknown_03000E10[a1] = 0x83;
        break;
    case 22:
        sUnknown_03000E10[a1] = 0x82;
        break;
    case 26:
        sUnknown_03000E10[a1] = 0x80;
        if (a3->b)
            c1_link_related_func_set(sub_80870B0);
        break;
    case 29:
        if (sUnknown_03000E10[a1] == 0x82)
            sUnknown_03000E10[a1] = 0x81;
        break;
    }
}

static void sub_8086F38(u16 *a1, s32 a2)
{
    struct UnkStruct_8054FF8 st;
    s32 i;

    for (i = 0; i < 4; i++)
    {
        u8 v5 = a1[i];
        u16 v8 = 0;
        sub_80872D8(i, a2, &st);
        sub_8086D18(i, v5, &st, &v8);
        if (sUnknown_03000E10[i] == 0x80)
            v8 = sub_8087068(v5);
        sub_808796C(i, v8);
    }
}

static void sub_8086FA0(u16 a1)
{
    if (a1 >= 17 && a1 < 30)
        gUnknown_03005DA8 = a1;
    else
        gUnknown_03005DA8 = 17;

    if (gWirelessCommType != 0
        && sub_8087690() > 1
        && is_c1_link_related_active() == TRUE
        && sub_8009F3C() == TRUE)
    {
        switch (a1)
        {
        case 17:
        case 18:
        case 19:
        case 20:
        case 21:
        case 24:
        case 25:
            gUnknown_03005DA8 = 0;
            break;
        }
    }
}

static u16 sub_808700C(u32 a1)
{
    if (gMain.heldKeys & DPAD_UP)
        return 19;
    else if (gMain.heldKeys & DPAD_DOWN)
        return 18;
    else if (gMain.heldKeys & DPAD_LEFT)
        return 20;
    else if (gMain.heldKeys & DPAD_RIGHT)
        return 21;
    else if (gMain.newKeys & START_BUTTON)
        return 24;
    else if (gMain.newKeys & A_BUTTON)
        return 25;
    else
        return 17;
}

static u16 sub_8087068(u16 a1)
{
    switch (a1)
    {
    case 21:
        return 4;
    case 20:
        return 3;
    case 19:
        return 1;
    case 18:
        return 2;
    default:
        return 0;
    }
}

static void sub_808709C(u16 *a1)
{
    s32 i;
    for (i = 0; i < 4; i++)
        a1[i] = 17;
}

static u16 sub_80870B0(u32 a1)
{
    if (ScriptContext2_IsEnabled() == 1)
        return 17;
    if (sub_800B4DC() > 4)
        return 27;
    if (sub_8087690() <= 4)
        return sub_808700C(a1);
    return 28;
}

static u16 sub_80870EC(u32 a1)
{
    sub_8086C64();
    return 17;
}

static u16 sub_80870F8(u32 a1)
{
    u16 retVal;
    if (ScriptContext2_IsEnabled() == TRUE)
    {
        retVal = 17;
    }
    else
    {
        retVal = 26;
        c1_link_related_func_set(sub_80870EC);
    }
    return retVal;
}

static u16 sub_808711C(u32 a1)
{
    u16 retVal;
    if (sub_800B4DC() > 2)
    {
        retVal = 17;
    }
    else
    {
        retVal = 26;
        ScriptContext2_Disable();
        c1_link_related_func_set(sub_80870EC);
    }
    return retVal;
}

static u16 sub_8087140(u32 a1)
{
    u16 retVal;
    if (sub_8087690() > 2)
    {
        retVal = 17;
    }
    else
    {
        retVal = 26;
        ScriptContext2_Disable();
        c1_link_related_func_set(sub_80870EC);
    }
    return retVal;
}

static u16 sub_8087164(u32 a1)
{
    sub_8086C64();
    return 17;
}

static u16 sub_8087170(u32 linkPlayerId)
{
    if (sUnknown_03000E10[linkPlayerId] == 0x82)
    {
        if (gMain.newKeys & B_BUTTON)
        {
            c1_link_related_func_set(sub_8087164);
            return 29;
        }
        else
        {
            return 17;
        }
    }
    else
    {
        sub_8086C64();
        return 17;
    }
}

static u16 sub_80871AC(u32 a1)
{
    c1_link_related_func_set(sub_8087170);
    return 22;
}

static u16 sub_80871C0(u32 a1)
{
    return 17;
}

static u16 sub_80871C4(u32 a1)
{
    if (sUnknown_03000E10[a1] != 0x83)
        sub_8086C64();
    if (sub_8086CA8(0x83) == TRUE)
    {
        ScriptContext1_SetupScript(EventScript_277513);
        c1_link_related_func_set(sub_80871C0);
    }
    return 17;
}

static u16 sub_80871FC(u32 a1)
{
    c1_link_related_func_set(sub_80871C4);
    return 23;
}

static u16 sub_8087210(u32 a1)
{
    return 17;
}

u32 sub_8087214(void)
{
    if (sub_8086CE0(0x83) == TRUE)
        return 2;
    if (sUnknown_03000E14 == sub_8087170 && sUnknown_03000E10[gUnknown_03005DB4] != 0x82)
        return 0;
    if (sUnknown_03000E14 == sub_8087164 && sUnknown_03000E10[gUnknown_03005DB4] == 0x81)
        return 2;
    if (sub_8086CA8(0x82) != 0)
        return 1;
    return 0;
}

bool32 sub_808727C(void)
{
    return sub_8086CE0(0x83);
}

u16 sub_8087288(void)
{
    c1_link_related_func_set(sub_80871AC);
    return 0;
}

u16 sub_808729C(void)
{
    c1_link_related_func_set(sub_80870F8);
    return 0;
}

u16 sub_80872B0(void)
{
    c1_link_related_func_set(sub_80871FC);
    return 0;
}

u16 sub_80872C4(void)
{
    c1_link_related_func_set(sub_8087210);
    return 0;
}

static void sub_80872D8(s32 linkPlayerId, s32 a2, struct UnkStruct_8054FF8 *a3)
{
    s16 x, y;

    a3->a = linkPlayerId;
    a3->b = (linkPlayerId == a2) ? 1 : 0;
    a3->c = gLinkPlayerMapObjects[linkPlayerId].mode;
    a3->d = sub_80878A0(linkPlayerId);
    sub_8087878(linkPlayerId, &x, &y);
    a3->sub.x = x;
    a3->sub.y = y;
    a3->sub.height = sub_80878C0(linkPlayerId);
    a3->field_C = MapGridGetMetatileBehaviorAt(x, y);
}

static bool32 sub_8087340(struct UnkStruct_8054FF8 *a1)
{
    u8 v1 = a1->c;
    if (v1 == 2 || v1 == 0)
        return TRUE;
    else
        return FALSE;
}

static bool32 sub_8087358(struct UnkStruct_8054FF8 *a1)
{
    u8 v1 = a1->c;
    if (v1 == 2 || v1 == 0)
        return TRUE;
    else
        return FALSE;
}

static u8 *sub_8087370(struct UnkStruct_8054FF8 *a1)
{
    if (a1->c != 2)
        return 0;
    return sub_809D0F4(&a1->sub);
}

static bool32 sub_8087388(struct UnkStruct_8054FF8 *a1)
{
    if (a1->c != 2 && a1->c != 0)
        return FALSE;
    else if (!MetatileBehavior_IsSouthArrowWarp(a1->field_C))
        return FALSE;
    else if (a1->d != 1)
        return FALSE;
    else
        return TRUE;
}

static const u8 *sub_80873B4(struct UnkStruct_8054FF8 *a1)
{
    struct MapPosition unkStruct;
    u8 linkPlayerId;

    if (a1->c && a1->c != 2)
        return 0;

    unkStruct = a1->sub;
    unkStruct.x += gUnknown_08339D64[a1->d].x;
    unkStruct.y += gUnknown_08339D64[a1->d].y;
    unkStruct.height = 0;
    linkPlayerId = GetLinkPlayerIdAt(unkStruct.x, unkStruct.y);

    if (linkPlayerId != 4)
    {
        if (!a1->b)
            return EventScript_TradeRoom_TooBusyToNotice;
        else if (sUnknown_03000E10[linkPlayerId] != 0x80)
            return EventScript_TradeRoom_TooBusyToNotice;
        else if (!sub_80B39D4(linkPlayerId))
            return EventScript_TradeRoom_ReadTrainerCard1;
        else
            return EventScript_TradeRoom_ReadTrainerCard2;
    }

    return sub_809C2C8(&unkStruct, a1->field_C, a1->d);
}

static u16 sub_8087480(const u8 *script)
{
    if (script == gUnknown_08277388)
        return 10;
    else if (script == gUnknown_082773A3)
        return 9;
    else if (script == gUnknown_082773BE)
        return 10;
    else if (script == gUnknown_082773D9)
        return 9;
    else if (script == gUnknown_0827741D)
        return 10;
    else if (script == gUnknown_08277432)
        return 9;
    else if (script == gUnknown_08277447)
        return 10;
    else if (script == gUnknown_0827745C)
        return 9;
    else if (script == gUnknown_08277374)
        return 10;
    else if (script == gUnknown_0827737E)
        return 9;
    else if (script == gUnknown_082773F5)
        return 10;
    else if (script == gUnknown_082773FF)
        return 9;
    else
        return 0;
}

static void sub_8087510(void)
{
    ScriptContext2_Enable();
}

static void sub_808751C(void)
{
    PlaySE(SE_WIN_OPEN);
    ShowStartMenu();
    ScriptContext2_Enable();
}

static void sub_8087530(const u8 *script)
{
    PlaySE(SE_SELECT);
    ScriptContext1_SetupScript(script);
    ScriptContext2_Enable();
}

static void sub_808754C(void)
{
    PlaySE(SE_WIN_OPEN);
    ScriptContext1_SetupScript(gUnknown_082774EF);
    ScriptContext2_Enable();
}

static void sub_8087568(const u8 *script)
{
    PlaySE(SE_SELECT);
    ScriptContext1_SetupScript(script);
    ScriptContext2_Enable();
}

static void sub_8087584(void)
{
    ScriptContext1_SetupScript(gUnknown_08277509);
    ScriptContext2_Enable();
}

bool32 sub_8087598(void)
{
    if (!is_c1_link_related_active())
        return 0;
    if (sub_800B4DC() >= 3)
        sUnknown_03000E18 = 1;
    else
        sUnknown_03000E18 = 0;
    return sUnknown_03000E18;
}

bool32 sub_80875C8(void)
{
    u8 temp;

    if (sub_800B4DC() < 2)
        return FALSE;
    else if (is_c1_link_related_active() != TRUE)
        return FALSE;
    else if (sub_8009F3C() != TRUE)
        return FALSE;
    else if (sUnknown_03000E14 == sub_808711C)
        return TRUE;
    else if (sUnknown_03000E14 != sub_80870F8)
        return FALSE;

    temp = sUnknown_03000E18;
    sUnknown_03000E18 = 0;

    if (temp == TRUE)
        return TRUE;
    else if (gPaletteFade.active && gPaletteFade.softwareFadeFinishing)
        return TRUE;
    else
        return FALSE;
}

bool32 sub_8087634(void)
{
    if (sub_8087690() < 2)
        return FALSE;
    else if (is_c1_link_related_active() != TRUE)
        return FALSE;
    else if (sub_8009F3C() != TRUE)
        return FALSE;
    else if (sUnknown_03000E14 == sub_8087140)
        return TRUE;
    else
        return FALSE;
}

bool32 sub_808766C(void)
{
    if (gWirelessCommType != 0)
        return FALSE;
    else if (!sub_8009F3C())
        return FALSE;
    else
        return TRUE;
}

static u32 sub_8087690(void)
{
    if (gWirelessCommType != 0)
        return gUnknown_03005000.unk_9e8.unk_232;
    else
        return gLink.sendQueue.count;
}

static void ZeroLinkPlayerMapObject(struct LinkPlayerMapObject *linkPlayerMapObj)
{
    memset(linkPlayerMapObj, 0, sizeof(struct LinkPlayerMapObject));
}

void ZeroAllLinkPlayerMapObjects(void)
{
    memset(gLinkPlayerMapObjects, 0, sizeof(gLinkPlayerMapObjects));
}

static void ZeroMapObject(struct MapObject *mapObj)
{
    memset(mapObj, 0, sizeof(struct MapObject));
}

static void SpawnLinkPlayerMapObject(u8 linkPlayerId, s16 x, s16 y, u8 a4)
{
    u8 mapObjId = sub_808D4F4();
    struct LinkPlayerMapObject *linkPlayerMapObj = &gLinkPlayerMapObjects[linkPlayerId];
    struct MapObject *mapObj = &gMapObjects[mapObjId];

    ZeroLinkPlayerMapObject(linkPlayerMapObj);
    ZeroMapObject(mapObj);

    linkPlayerMapObj->active = 1;
    linkPlayerMapObj->linkPlayerId = linkPlayerId;
    linkPlayerMapObj->mapObjId = mapObjId;
    linkPlayerMapObj->mode = 0;

    mapObj->active = 1;
    mapObj->singleMovementActive = a4;
    mapObj->range.as_byte = 2;
    mapObj->spriteId = 64;

    InitLinkPlayerMapObjectPos(mapObj, x, y);
}

static void InitLinkPlayerMapObjectPos(struct MapObject *mapObj, s16 x, s16 y)
{
    mapObj->currentCoords.x = x;
    mapObj->currentCoords.y = y;
    mapObj->previousCoords.x = x;
    mapObj->previousCoords.y = y;
    sub_8093038(x, y, &mapObj->initialCoords.x, &mapObj->initialCoords.y);
    mapObj->initialCoords.x += 8;
    FieldObjectUpdateZCoord(mapObj);
}

static void sub_80877DC(u8 linkPlayerId, u8 a2)
{
    if (gLinkPlayerMapObjects[linkPlayerId].active)
    {
        u8 mapObjId = gLinkPlayerMapObjects[linkPlayerId].mapObjId;
        struct MapObject *mapObj = &gMapObjects[mapObjId];
        mapObj->range.as_byte = a2;
    }
}

static void sub_808780C(u8 linkPlayerId)
{
    struct LinkPlayerMapObject *linkPlayerMapObj = &gLinkPlayerMapObjects[linkPlayerId];
    u8 mapObjId = linkPlayerMapObj->mapObjId;
    struct MapObject *mapObj = &gMapObjects[mapObjId];
    if (mapObj->spriteId != 64 )
        DestroySprite(&gSprites[mapObj->spriteId]);
    linkPlayerMapObj->active = 0;
    mapObj->active = 0;
}

static u8 sub_8087858(u8 linkPlayerId)
{
    u8 mapObjId = gLinkPlayerMapObjects[linkPlayerId].mapObjId;
    struct MapObject *mapObj = &gMapObjects[mapObjId];
    return mapObj->spriteId;
}

static void sub_8087878(u8 linkPlayerId, u16 *x, u16 *y)
{
    u8 mapObjId = gLinkPlayerMapObjects[linkPlayerId].mapObjId;
    struct MapObject *mapObj = &gMapObjects[mapObjId];
    *x = mapObj->currentCoords.x;
    *y = mapObj->currentCoords.y;
}

static u8 sub_80878A0(u8 linkPlayerId)
{
    u8 mapObjId = gLinkPlayerMapObjects[linkPlayerId].mapObjId;
    struct MapObject *mapObj = &gMapObjects[mapObjId];
    return mapObj->range.as_byte;
}

static u8 sub_80878C0(u8 linkPlayerId)
{
    u8 mapObjId = gLinkPlayerMapObjects[linkPlayerId].mapObjId;
    struct MapObject *mapObj = &gMapObjects[mapObjId];
    return mapObj->currentElevation;
}

static s32 sub_80878E4(u8 linkPlayerId)
{
    u8 mapObjId = gLinkPlayerMapObjects[linkPlayerId].mapObjId;
    struct MapObject *mapObj = &gMapObjects[mapObjId];
    return 16 - (s8)mapObj->directionSequenceIndex;
}

static u8 GetLinkPlayerIdAt(s16 x, s16 y)
{
    u8 i;
    for (i = 0; i < 4; i++)
    {
        if (gLinkPlayerMapObjects[i].active
         && (gLinkPlayerMapObjects[i].mode == 0 || gLinkPlayerMapObjects[i].mode == 2))
        {
            struct MapObject *mapObj = &gMapObjects[gLinkPlayerMapObjects[i].mapObjId];
            if (mapObj->currentCoords.x == x && mapObj->currentCoords.y == y)
                return i;
        }
    }
    return 4;
}

static void sub_808796C(u8 linkPlayerId, u8 a2)
{
    struct LinkPlayerMapObject *linkPlayerMapObj = &gLinkPlayerMapObjects[linkPlayerId];
    u8 mapObjId = linkPlayerMapObj->mapObjId;
    struct MapObject *mapObj = &gMapObjects[mapObjId];

    if (linkPlayerMapObj->active)
    {
        if (a2 > 10)
            mapObj->triggerGroundEffectsOnMove = 1;
        else
            gUnknown_08339E00[gUnknown_08339DC8[linkPlayerMapObj->mode](linkPlayerMapObj, mapObj, a2)](linkPlayerMapObj, mapObj);
    }
}

static u8 sub_80879D8(struct LinkPlayerMapObject *linkPlayerMapObj, struct MapObject *mapObj, u8 a3)
{
    return gUnknown_08339DD4[a3](linkPlayerMapObj, mapObj, a3);
}

static u8 sub_80879F8(struct LinkPlayerMapObject *linkPlayerMapObj, struct MapObject *mapObj, u8 a3)
{
    return 1;
}

static u8 sub_80879FC(struct LinkPlayerMapObject *linkPlayerMapObj, struct MapObject *mapObj, u8 a3)
{
    return gUnknown_08339DD4[a3](linkPlayerMapObj, mapObj, a3);
}

static u8 sub_8087A1C(struct LinkPlayerMapObject *linkPlayerMapObj, struct MapObject *mapObj, u8 a3)
{
    return 0;
}

static u8 sub_8087A20(struct LinkPlayerMapObject *linkPlayerMapObj, struct MapObject *mapObj, u8 a3)
{
    s16 x, y;

    mapObj->range.as_byte = npc_something3(a3, mapObj->range.as_byte);
    FieldObjectMoveDestCoords(mapObj, mapObj->range.as_byte, &x, &y);

    if (LinkPlayerDetectCollision(linkPlayerMapObj->mapObjId, mapObj->range.as_byte, x, y))
    {
        return 0;
    }
    else
    {
        mapObj->directionSequenceIndex = 16;
        npc_coords_shift(mapObj, x, y);
        FieldObjectUpdateZCoord(mapObj);
        return 1;
    }
}

static u8 sub_8087A88(struct LinkPlayerMapObject *linkPlayerMapObj, struct MapObject *mapObj, u8 a3)
{
    mapObj->range.as_byte = npc_something3(a3, mapObj->range.as_byte);
    return 0;
}

static void sub_8087AA0(struct LinkPlayerMapObject *linkPlayerMapObj, struct MapObject *mapObj)
{
    linkPlayerMapObj->mode = 0;
}

static void sub_8087AA8(struct LinkPlayerMapObject *linkPlayerMapObj, struct MapObject *mapObj)
{
    mapObj->directionSequenceIndex--;
    linkPlayerMapObj->mode = 1;
    MoveCoords(mapObj->range.as_byte, &mapObj->initialCoords.x, &mapObj->initialCoords.y);
    if (!mapObj->directionSequenceIndex)
    {
        npc_coords_shift_still(mapObj);
        linkPlayerMapObj->mode = 2;
    }
}

static u8 npc_something3(u8 a1, u8 a2)
{
    switch (a1 - 1)
    {
    case 0:
    case 6:
        return 2;
    case 1:
    case 7:
        return 1;
    case 2:
    case 8:
        return 3;
    case 3:
    case 9:
        return 4;
    }
    return a2;
}

static u8 LinkPlayerDetectCollision(u8 selfMapObjId, u8 a2, s16 x, s16 y)
{
    u8 i;
    for (i = 0; i < 16; i++)
    {
        if (i != selfMapObjId)
        {
            if ((gMapObjects[i].currentCoords.x == x && gMapObjects[i].currentCoords.y == y)
             || (gMapObjects[i].previousCoords.x == x && gMapObjects[i].previousCoords.y == y))
            {
                return 1;
            }
        }
    }
    return MapGridIsImpassableAt(x, y);
}

static void CreateLinkPlayerSprite(u8 linkPlayerId, u8 gameVersion)
{
    struct LinkPlayerMapObject *linkPlayerMapObj = &gLinkPlayerMapObjects[linkPlayerId];
    u8 mapObjId = linkPlayerMapObj->mapObjId;
    struct MapObject *mapObj = &gMapObjects[mapObjId];
    struct Sprite *sprite;

    if (linkPlayerMapObj->active)
    {
        switch (gameVersion)
        {
        case VERSION_FIRE_RED:
        case VERSION_LEAF_GREEN:
            mapObj->spriteId = AddPseudoFieldObject(sub_808BD6C(mapObj->singleMovementActive), SpriteCB_LinkPlayer, 0, 0, 0);
            break;
        case VERSION_RUBY:
        case VERSION_SAPPHIRE:
            mapObj->spriteId = AddPseudoFieldObject(sub_808BD7C(mapObj->singleMovementActive), SpriteCB_LinkPlayer, 0, 0, 0);
            break;
        case VERSION_EMERALD:
            mapObj->spriteId = AddPseudoFieldObject(GetRivalAvatarGraphicsIdByStateIdAndGender(0, mapObj->singleMovementActive), SpriteCB_LinkPlayer, 0, 0, 0);
            break;
        }

        sprite = &gSprites[mapObj->spriteId];
        sprite->coordOffsetEnabled = TRUE;
        sprite->data[0] = linkPlayerId;
        mapObj->triggerGroundEffectsOnMove = 0;
    }
}

static void SpriteCB_LinkPlayer(struct Sprite *sprite)
{
    struct LinkPlayerMapObject *linkPlayerMapObj = &gLinkPlayerMapObjects[sprite->data[0]];
    struct MapObject *mapObj = &gMapObjects[linkPlayerMapObj->mapObjId];
    sprite->pos1.x = mapObj->initialCoords.x;
    sprite->pos1.y = mapObj->initialCoords.y;
    SetObjectSubpriorityByZCoord(mapObj->previousElevation, sprite, 1);
    sprite->oam.priority = ZCoordToPriority(mapObj->previousElevation);

	if (!linkPlayerMapObj->mode)
        StartSpriteAnim(sprite, FieldObjectDirectionToImageAnimId(mapObj->range.as_byte));
    else
        StartSpriteAnimIfDifferent(sprite, get_go_image_anim_num(mapObj->range.as_byte));

	sub_80979D4(sprite, 0);
    if (mapObj->triggerGroundEffectsOnMove)
    {
        sprite->invisible = ((sprite->data[7] & 4) >> 2);
        sprite->data[7]++;
    }
}
