summaryrefslogtreecommitdiff
path: root/src/mirage_tower.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mirage_tower.c')
-rw-r--r--src/mirage_tower.c868
1 files changed, 868 insertions, 0 deletions
diff --git a/src/mirage_tower.c b/src/mirage_tower.c
new file mode 100644
index 000000000..a8dd5d9de
--- /dev/null
+++ b/src/mirage_tower.c
@@ -0,0 +1,868 @@
+#include "global.h"
+#include "alloc.h"
+#include "bg.h"
+#include "event_data.h"
+#include "event_object_movement.h"
+#include "field_camera.h"
+#include "fieldmap.h"
+#include "gpu_regs.h"
+#include "menu.h"
+#include "random.h"
+#include "roulette_util.h"
+#include "script.h"
+#include "sound.h"
+#include "sprite.h"
+#include "task.h"
+#include "window.h"
+#include "constants/flags.h"
+#include "constants/maps.h"
+#include "constants/rgb.h"
+#include "constants/songs.h"
+
+struct MirageTowerPulseBlend {
+ u8 taskId;
+ struct PulseBlend pulseBlend;
+};
+
+struct MetatileCoords
+{
+ u8 x;
+ u8 y;
+ u16 metatileId;
+};
+
+struct BgRegOffsets
+{
+ u16 bgHOFS;
+ u16 bgVOFS;
+};
+
+struct Struct203CF10
+{
+ u8 *buffer;
+ u8 currIndex;
+};
+
+struct DynamicSpriteFrameImage
+{
+ u8 *data;
+ u16 size;
+};
+
+struct Struct203CF0C
+{
+ u8 *frameImageTiles;
+ struct DynamicSpriteFrameImage *frameImage;
+ u8 spriteId;
+ u16 *unkC;
+ u16 unk10;
+};
+
+#define MIRAGE_TOWER_GFX_LENGTH (sizeof(sBlankTile_Gfx) + sizeof(sMirageTower_Gfx))
+#define ROOT_FOSSIL_GFX_LENGTH sizeof(sRootFossil_Gfx)
+#define ROOT_FOSSIL_GFX_RANDOMIZER_LENGTH 0x100
+
+// extern data
+extern const struct SpriteSheet gMirageTowerCeilingCrumbleSpriteSheets[];
+extern const s16 sCeilingCrumblePositions[][3];
+
+// static functions
+static void PlayerDescendMirageTower(u8 taskId);
+static void DoScreenShake(u8 taskId);
+static void IncrementCeilingCrumbleFinishedCount(void);
+static void WaitCeilingCrumble(u8 taskId);
+static void FinishCeilingCrumbleTask(u8 taskId);
+static void CreateCeilingCrumbleSprites(void);
+static void MoveCeilingCrumbleSprite(struct Sprite* sprite);
+static void DoMirageTowerDisintegration(u8 taskId);
+static void InitMirageTowerShake(u8 taskId);
+static void DoFossilFallAndSink(u8 taskId);
+static void sub_81BF248(struct Sprite *);
+static void sub_81BF2B8(u8* a, u16 b, u8 c, u8 d, u8 e);
+
+// rodata
+static const u8 sBlankTile_Gfx[32] = {0};
+static const u8 sMirageTower_Gfx[] = INCBIN_U8("graphics/misc/mirage_tower.4bpp");
+static const u16 sMirageTowerTilemap[] = INCBIN_U16("graphics/misc/mirage_tower.bin");
+static const u16 sRootFossil_Pal[] = INCBIN_U16("graphics/misc/fossil.gbapal");
+static const u8 sRootFossil_Gfx[] = INCBIN_U8("graphics/misc/fossil.4bpp");
+static const u8 sMirageTowerCrumbles_Gfx[] = INCBIN_U8("graphics/misc/mirage_tower_crumbles.4bpp");
+static const u16 sMirageTowerCrumbles_Palette[] = INCBIN_U16("graphics/misc/mirage_tower_crumbles.gbapal");
+
+const s16 sCeilingCrumblePositions[][3] =
+{
+ { 0, 10, 65},
+ { 17, 3, 50},
+ {-12, 0, 75},
+ { 10, 15, 90},
+ { 7, 8, 65},
+ {-18, 5, 75},
+ { 22, -10, 55},
+ {-24, -4, 65},
+};
+
+const struct SpriteSheet gMirageTowerCeilingCrumbleSpriteSheets[] =
+{
+ {sMirageTowerCrumbles_Gfx, 0x0080, 4000},
+ {NULL}
+};
+
+static const struct MetatileCoords sInvisibleMirageTowerMetatiles[] =
+{
+ {18, 53, 0x251},
+ {19, 53, 0x251},
+ {20, 53, 0x251},
+ {18, 54, 0x251},
+ {19, 54, 0x251},
+ {20, 54, 0x251},
+ {18, 55, 0x251},
+ {19, 55, 0x251},
+ {20, 55, 0x251},
+ {18, 56, 0x251},
+ {19, 56, 0x251},
+ {20, 56, 0x251},
+ {18, 57, 0x259},
+ {19, 57, 0x259},
+ {20, 57, 0x259},
+ {18, 58, 0x121},
+ {19, 58, 0x121},
+ {20, 58, 0x121},
+};
+
+static const union AnimCmd gSpriteAnim_8617DEC[] =
+{
+ ANIMCMD_FRAME(0, 1),
+ ANIMCMD_END,
+};
+
+static const struct OamData gOamData_8617DF4 =
+{
+ .y = 0,
+ .affineMode = 0,
+ .objMode = 0,
+ .mosaic = 0,
+ .bpp = 0,
+ .shape = 0,
+ .x = 0,
+ .matrixNum = 0,
+ .size = 1,
+ .tileNum = 0,
+ .priority = 0,
+ .paletteNum = 3,
+ .affineParam = 0,
+};
+
+static const union AnimCmd *const gSpriteAnimTable_8617DFC[] =
+{
+ gSpriteAnim_8617DEC,
+};
+
+static const struct SpriteTemplate gUnknown_08617E00 =
+{
+ 0xFFFF, 0xFFFF, &gOamData_8617DF4, gSpriteAnimTable_8617DFC, NULL, gDummySpriteAffineAnimTable, SpriteCallbackDummy
+};
+
+const struct PulseBlendSettings gMirageTowerPulseBlendSettings = {
+ .blendColor = RGB(27, 25, 16),
+ .paletteOffset = 0x61,
+ .numColors = 15,
+ .delay = 5,
+ .numFadeCycles = -1,
+ .maxBlendCoeff = 11,
+ .fadeType = 1,
+ .restorePaletteOnUnload = FALSE,
+ .unk7_7 = 1,
+};
+
+static const union AnimCmd sCeilingCrumble2AnimCmd[] =
+{
+ ANIMCMD_FRAME(0, 12),
+ ANIMCMD_JUMP(0),
+};
+
+static const union AnimCmd *const sCeilingCrumble2AnimCmds[] =
+{
+ sCeilingCrumble2AnimCmd,
+};
+
+static const struct OamData sCeilingCrumble2OamData =
+{
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_NORMAL,
+ .mosaic = 0,
+ .bpp = ST_OAM_4BPP,
+ .shape = ST_OAM_SQUARE,
+ .x = 0,
+ .matrixNum = 0,
+ .size = 0,
+ .tileNum = 0,
+ .priority = 0,
+ .paletteNum = 0,
+ .affineParam = 0,
+};
+
+static const struct SpriteTemplate sCeilingCrumbleSpriteTemplate2 = {
+ .tileTag = 4000,
+ .paletteTag = 0xFFFF,
+ .oam = &sCeilingCrumble2OamData,
+ .anims = sCeilingCrumble2AnimCmds,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = MoveCeilingCrumbleSprite
+};
+
+static const union AnimCmd sCeilingCrumble1AnimCmd[] =
+{
+ ANIMCMD_FRAME(0, 12),
+ ANIMCMD_JUMP(0),
+};
+
+static const union AnimCmd *const sCeilingCrumble1AnimCmds[] =
+{
+ sCeilingCrumble1AnimCmd,
+};
+
+static const struct OamData sCeilingCrumble1OamData =
+{
+ .y = 0,
+ .affineMode = ST_OAM_AFFINE_OFF,
+ .objMode = ST_OAM_OBJ_NORMAL,
+ .mosaic = 0,
+ .bpp = ST_OAM_4BPP,
+ .shape = ST_OAM_SQUARE,
+ .x = 0,
+ .matrixNum = 0,
+ .size = 1,
+ .tileNum = 0,
+ .priority = 0,
+ .paletteNum = 0,
+ .affineParam = 0,
+};
+
+static const struct SpriteTemplate sCeilingCrumbleSpriteTemplate1 = {
+ .tileTag = 4000,
+ .paletteTag = 0xFFFF,
+ .oam = &sCeilingCrumble1OamData,
+ .anims = sCeilingCrumble1AnimCmds,
+ .images = NULL,
+ .affineAnims = gDummySpriteAffineAnimTable,
+ .callback = MoveCeilingCrumbleSprite
+};
+
+EWRAM_DATA static u8* sMirageTowerGfxBuffer = NULL;
+EWRAM_DATA static u8* sMirageTowerTilemapBuffer = NULL;
+EWRAM_DATA static struct Struct203CF0C *sUnknown_0203CF0C = NULL;
+EWRAM_DATA static struct Struct203CF10 *sUnknown_0203CF10 = NULL;
+EWRAM_DATA static struct BgRegOffsets *sBgShakeOffsets = NULL;
+EWRAM_DATA struct MirageTowerPulseBlend *sMirageTowerPulseBlend = NULL;
+
+IWRAM_DATA static u16 gUnknown_030012A8[8];
+
+bool8 IsMirageTowerVisible(void)
+{
+ if (!(gSaveBlock1Ptr->location.mapGroup == MAP_GROUP(ROUTE111) && gSaveBlock1Ptr->location.mapNum == MAP_NUM(ROUTE111)))
+ return FALSE;
+ return FlagGet(FLAG_MIRAGE_TOWER_VISIBLE);
+}
+
+static void UpdateMirageTowerPulseBlend(u8 taskId)
+{
+ UpdatePulseBlend(&sMirageTowerPulseBlend->pulseBlend);
+}
+
+void ClearMirageTowerPulseBlend(void)
+{
+ sMirageTowerPulseBlend = NULL;
+}
+
+void TryStartMirageTowerPulseBlendEffect(void)
+{
+ if (sMirageTowerPulseBlend)
+ {
+ sMirageTowerPulseBlend = NULL;
+ return;
+ }
+
+ if (gSaveBlock1Ptr->location.mapGroup != MAP_GROUP(ROUTE111)
+ || gSaveBlock1Ptr->location.mapNum != MAP_NUM(ROUTE111)
+ || !FlagGet(FLAG_MIRAGE_TOWER_VISIBLE))
+ return;
+
+ sMirageTowerPulseBlend = AllocZeroed(sizeof(*sMirageTowerPulseBlend));
+ InitPulseBlend(&sMirageTowerPulseBlend->pulseBlend);
+ InitPulseBlendPaletteSettings(&sMirageTowerPulseBlend->pulseBlend, &gMirageTowerPulseBlendSettings);
+ MarkUsedPulseBlendPalettes(&sMirageTowerPulseBlend->pulseBlend, 0x1, TRUE);
+ sMirageTowerPulseBlend->taskId = CreateTask(UpdateMirageTowerPulseBlend, 0xFF);
+}
+
+void ClearMirageTowerPulseBlendEffect(void)
+{
+ if (gSaveBlock1Ptr->location.mapGroup != MAP_GROUP(ROUTE111)
+ || gSaveBlock1Ptr->location.mapNum != MAP_NUM(ROUTE111)
+ || !FlagGet(FLAG_MIRAGE_TOWER_VISIBLE)
+ || sMirageTowerPulseBlend == NULL)
+ return;
+
+ if (FuncIsActiveTask(UpdateMirageTowerPulseBlend))
+ DestroyTask(sMirageTowerPulseBlend->taskId);
+
+ UnmarkUsedPulseBlendPalettes(&sMirageTowerPulseBlend->pulseBlend, 0x1, TRUE);
+ UnloadUsedPulseBlendPalettes(&sMirageTowerPulseBlend->pulseBlend, 0x1, TRUE);
+ FREE_AND_SET_NULL(sMirageTowerPulseBlend);
+}
+
+void SetMirageTowerVisibility(void)
+{
+ u16 rand;
+ bool8 visible;
+
+ if (VarGet(VAR_0x40CB))
+ {
+ FlagClear(FLAG_MIRAGE_TOWER_VISIBLE);
+ return;
+ }
+
+ rand = Random();
+ visible = rand & 1;
+ if (FlagGet(FLAG_FORCE_MIRAGE_TOWER_VISIBLE) == TRUE)
+ visible = TRUE;
+
+ if (visible)
+ {
+ FlagSet(FLAG_MIRAGE_TOWER_VISIBLE);
+ TryStartMirageTowerPulseBlendEffect();
+ return;
+ }
+
+ FlagClear(FLAG_MIRAGE_TOWER_VISIBLE);
+}
+
+void StartPlayerDescendMirageTower(void)
+{
+ CreateTask(PlayerDescendMirageTower, 8);
+}
+
+static void PlayerDescendMirageTower(u8 taskId)
+{
+ u8 eventObjectId;
+ struct EventObject *fakePlayerEventObject;
+ struct EventObject *playerEventObject;
+
+ TryGetEventObjectIdByLocalIdAndMap(45, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &eventObjectId);
+ fakePlayerEventObject = &gEventObjects[eventObjectId];
+ gSprites[fakePlayerEventObject->spriteId].pos2.y += 4;
+ playerEventObject = &gEventObjects[gPlayerAvatar.eventObjectId];
+ if ((gSprites[fakePlayerEventObject->spriteId].pos1.y + gSprites[fakePlayerEventObject->spriteId].pos2.y) >=
+ (gSprites[playerEventObject->spriteId].pos1.y + gSprites[playerEventObject->spriteId].pos2.y))
+ {
+ DestroyTask(taskId);
+ EnableBothScriptContexts();
+ }
+}
+
+static void StartScreenShake(u8 yShakeOffset, u8 xShakeOffset, u8 numShakes, u8 shakeDelay)
+{
+ u8 taskId = CreateTask(DoScreenShake, 9);
+ gTasks[taskId].data[0] = xShakeOffset;
+ gTasks[taskId].data[1] = 0;
+ gTasks[taskId].data[2] = numShakes;
+ gTasks[taskId].data[3] = shakeDelay;
+ gTasks[taskId].data[4] = yShakeOffset;
+ SetCameraPanningCallback(NULL);
+ PlaySE(SE_W070);
+}
+
+static void DoScreenShake(u8 taskId)
+{
+ s16 *data;
+
+ data = gTasks[taskId].data;
+ data[1]++;
+ if (data[1] % data[3] == 0)
+ {
+ data[1] = 0;
+ data[2]--;
+ data[0] = -data[0];
+ data[4] = -data[4];
+ SetCameraPanning(data[0], data[4]);
+ if (data[2] == 0)
+ {
+ IncrementCeilingCrumbleFinishedCount();
+ DestroyTask(taskId);
+ InstallCameraPanAheadCallback();
+ }
+ }
+}
+
+static void IncrementCeilingCrumbleFinishedCount(void)
+{
+ u8 taskId = FindTaskIdByFunc(WaitCeilingCrumble);
+ if (taskId != 0xFF)
+ gTasks[taskId].data[0]++;
+}
+
+void DoMirageTowerCeilingCrumble(void)
+{
+ LoadSpriteSheets(gMirageTowerCeilingCrumbleSpriteSheets);
+ CreateCeilingCrumbleSprites();
+ CreateTask(WaitCeilingCrumble, 8);
+ StartScreenShake(2, 1, 16, 3);
+}
+
+static void WaitCeilingCrumble(u8 taskId)
+{
+ u16 *data = gTasks[taskId].data;
+ data[1]++;
+ // Either wait 1000 frames, or until all 16 crumble sprites and the one screen-shake task are completed.
+ if (data[1] == 1000 || data[0] == 17)
+ gTasks[taskId].func = FinishCeilingCrumbleTask;
+}
+
+static void FinishCeilingCrumbleTask(u8 taskId)
+{
+ FreeSpriteTilesByTag(4000);
+ DestroyTask(taskId);
+ EnableBothScriptContexts();
+}
+
+static void CreateCeilingCrumbleSprites(void)
+{
+ u8 i;
+ u8 spriteId;
+
+ for (i = 0; i < 8; i++)
+ {
+ spriteId = CreateSprite(&sCeilingCrumbleSpriteTemplate1, sCeilingCrumblePositions[i][0] + 120, sCeilingCrumblePositions[i][1], 8);
+ gSprites[spriteId].oam.priority = 0;
+ gSprites[spriteId].oam.paletteNum = 0;
+ gSprites[spriteId].data[0] = i;
+ }
+ for (i = 0; i < 8; i++)
+ {
+ spriteId = CreateSprite(&sCeilingCrumbleSpriteTemplate2, sCeilingCrumblePositions[i][0] + 115, sCeilingCrumblePositions[i][1] - 3, 8);
+ gSprites[spriteId].oam.priority = 0;
+ gSprites[spriteId].oam.paletteNum = 0;
+ gSprites[spriteId].data[0] = i;
+ }
+}
+
+static void MoveCeilingCrumbleSprite(struct Sprite* sprite)
+{
+ sprite->data[1] += 2;
+ sprite->pos2.y = sprite->data[1] / 2;
+ if(((sprite->pos1.y) + (sprite->pos2.y)) > sCeilingCrumblePositions[sprite->data[0]][2])
+ {
+ DestroySprite(sprite);
+ IncrementCeilingCrumbleFinishedCount();
+ }
+}
+
+static void SetInvisibleMirageTowerMetatiles(void)
+{
+ u8 i;
+ for (i = 0; i < ARRAY_COUNT(sInvisibleMirageTowerMetatiles); i++)
+ MapGridSetMetatileIdAt(sInvisibleMirageTowerMetatiles[i].x + 7, sInvisibleMirageTowerMetatiles[i].y + 7, sInvisibleMirageTowerMetatiles[i].metatileId);
+ DrawWholeMapView();
+}
+
+void StartMirageTowerDisintegration(void)
+{
+ CreateTask(DoMirageTowerDisintegration, 9);
+}
+
+void StartMirageTowerShake(void)
+{
+ CreateTask(InitMirageTowerShake, 9);
+}
+
+void StartMirageTowerFossilFallAndSink(void)
+{
+ CreateTask(DoFossilFallAndSink, 9);
+}
+
+static void SetBgShakeOffsets(void)
+{
+ SetGpuReg(REG_OFFSET_BG0HOFS, sBgShakeOffsets->bgHOFS);
+ SetGpuReg(REG_OFFSET_BG0VOFS, sBgShakeOffsets->bgVOFS);
+}
+
+static void UpdateBgShake(u8 taskId)
+{
+ if (!gTasks[taskId].data[0])
+ {
+ sBgShakeOffsets->bgHOFS = -sBgShakeOffsets->bgHOFS;
+ gTasks[taskId].data[0] = 2;
+ SetBgShakeOffsets();
+ }
+ else
+ {
+ gTasks[taskId].data[0]--;
+ }
+}
+
+static void InitMirageTowerShake(u8 taskId)
+{
+ u8 zero;
+
+ switch (gTasks[taskId].data[0])
+ {
+ case 0:
+ FreeAllWindowBuffers();
+ SetBgAttribute(0, BG_ATTR_PRIORITY, 2);
+ gTasks[taskId].data[0]++;
+ break;
+ case 1:
+ sMirageTowerGfxBuffer = (u8 *)AllocZeroed(MIRAGE_TOWER_GFX_LENGTH);
+ sMirageTowerTilemapBuffer = (u8 *)AllocZeroed(BG_SCREEN_SIZE);
+ ChangeBgX(0, 0, 0);
+ ChangeBgY(0, 0, 0);
+ gTasks[taskId].data[0]++;
+ break;
+ case 2:
+ CpuSet(sBlankTile_Gfx, sMirageTowerGfxBuffer, MIRAGE_TOWER_GFX_LENGTH / 2);
+ LoadBgTiles(0, sMirageTowerGfxBuffer, MIRAGE_TOWER_GFX_LENGTH, 0);
+ gTasks[taskId].data[0]++;
+ break;
+ case 3:
+ SetBgTilemapBuffer(0, sMirageTowerTilemapBuffer);
+ CopyToBgTilemapBufferRect_ChangePalette(0, &sMirageTowerTilemap, 12, 29, 6, 12, 17);
+ CopyBgTilemapBufferToVram(0);
+ gTasks[taskId].data[0]++;
+ break;
+ case 4:
+ ShowBg(0);
+ gTasks[taskId].data[0]++;
+ break;
+ case 5:
+ SetInvisibleMirageTowerMetatiles();
+ gTasks[taskId].data[0]++;
+ break;
+ case 6:
+ sBgShakeOffsets = Alloc(sizeof(*sBgShakeOffsets));
+ zero = 0;
+ sBgShakeOffsets->bgHOFS = 2;
+ sBgShakeOffsets->bgVOFS = zero;
+ CreateTask(UpdateBgShake, 10);
+ DestroyTask(taskId);
+ EnableBothScriptContexts();
+ break;
+ }
+}
+
+#define OUTER_BUFFER_LENGTH 0x60
+#define INNER_BUFFER_LENGTH 0x30
+static void DoMirageTowerDisintegration(u8 taskId)
+{
+ u8 bgShakeTaskId, j;
+ u16 i;
+ u8 index;
+
+ switch (gTasks[taskId].data[0])
+ {
+ case 1:
+ sUnknown_0203CF10 = AllocZeroed(OUTER_BUFFER_LENGTH * sizeof(struct Struct203CF10));
+ break;
+ case 3:
+ if (gTasks[taskId].data[3] <= (OUTER_BUFFER_LENGTH - 1))
+ {
+ if (gTasks[taskId].data[1] > 1)
+ {
+ index = gTasks[taskId].data[3];
+ sUnknown_0203CF10[index].buffer = Alloc(INNER_BUFFER_LENGTH);
+ for (i = 0; i <= (INNER_BUFFER_LENGTH - 1); i++)
+ sUnknown_0203CF10[index].buffer[i] = i;
+ for (i = 0; i <= (INNER_BUFFER_LENGTH - 1); i++)
+ {
+ u16 rand1, rand2, temp;
+
+ rand1 = Random() % 0x30;
+ rand2 = Random() % 0x30;
+ SWAP(sUnknown_0203CF10[index].buffer[rand2], sUnknown_0203CF10[index].buffer[rand1], temp);
+ }
+ if (gTasks[taskId].data[3] <= (OUTER_BUFFER_LENGTH - 1))
+ gTasks[taskId].data[3]++;
+ gTasks[taskId].data[1] = 0;
+ }
+ gTasks[taskId].data[1]++;
+ }
+ index = gTasks[taskId].data[3];
+ for (i = (u8)(gTasks[taskId].data[2]); i < index; i++)
+ {
+ for (j = 0; j < 1; j++)
+ {
+ sub_81BF2B8(sMirageTowerGfxBuffer,
+ ((((OUTER_BUFFER_LENGTH - 1) - i) * INNER_BUFFER_LENGTH) + sUnknown_0203CF10[i].buffer[(sUnknown_0203CF10[i].currIndex)++]),
+ 0, INNER_BUFFER_LENGTH, 1);
+ }
+ if (sUnknown_0203CF10[i].currIndex > (INNER_BUFFER_LENGTH - 1))
+ {
+ FREE_AND_SET_NULL(sUnknown_0203CF10[i].buffer);
+ gTasks[taskId].data[2]++;
+ if ((i % 2) == 1)
+ sBgShakeOffsets->bgVOFS--;
+ }
+ }
+ LoadBgTiles(0, sMirageTowerGfxBuffer, MIRAGE_TOWER_GFX_LENGTH, 0);
+ if (sUnknown_0203CF10[OUTER_BUFFER_LENGTH - 1].currIndex > (INNER_BUFFER_LENGTH - 1))
+ break;
+ return;
+ case 4:
+ UnsetBgTilemapBuffer(0);
+ bgShakeTaskId = FindTaskIdByFunc(UpdateBgShake);
+ if (bgShakeTaskId != 0xFF)
+ DestroyTask(bgShakeTaskId);
+ sBgShakeOffsets->bgVOFS = sBgShakeOffsets->bgHOFS = 0;
+ SetBgShakeOffsets();
+ break;
+ case 5:
+ FREE_AND_SET_NULL(sBgShakeOffsets);
+ FREE_AND_SET_NULL(sUnknown_0203CF10);
+ FREE_AND_SET_NULL(sMirageTowerGfxBuffer);
+ FREE_AND_SET_NULL(sMirageTowerTilemapBuffer);
+ break;
+ case 6:
+ SetGpuRegBits(REG_OFFSET_BG2CNT, BGCNT_PRIORITY(2));
+ SetGpuRegBits(REG_OFFSET_BG0CNT, BGCNT_PRIORITY(0));
+ SetBgAttribute(0, BG_ATTR_PRIORITY, 0);
+ InitStandardTextBoxWindows();
+ break;
+ case 7:
+ ShowBg(0);
+ break;
+ case 8:
+ DestroyTask(taskId);
+ EnableBothScriptContexts();
+ break;
+ }
+ gTasks[taskId].data[0]++;
+}
+
+static void DoFossilFallAndSink(u8 taskId)
+{
+ u16 i;
+ u8 *buffer;
+
+ switch (gTasks[taskId].data[0])
+ {
+ case 1:
+ sUnknown_0203CF0C = AllocZeroed(sizeof(*sUnknown_0203CF0C));
+ sUnknown_0203CF0C->frameImageTiles = AllocZeroed(ROOT_FOSSIL_GFX_LENGTH);
+ sUnknown_0203CF0C->frameImage = AllocZeroed(sizeof(*sUnknown_0203CF0C->frameImage));
+ sUnknown_0203CF0C->unkC = AllocZeroed(ROOT_FOSSIL_GFX_RANDOMIZER_LENGTH * sizeof(u16));
+ sUnknown_0203CF0C->unk10 = 0;
+ break;
+ case 2:
+ buffer = sUnknown_0203CF0C->frameImageTiles;
+ for (i = 0; i < ROOT_FOSSIL_GFX_LENGTH; i++, buffer++)
+ *buffer = sRootFossil_Gfx[i];
+ break;
+ case 3:
+ sUnknown_0203CF0C->frameImage->data = sUnknown_0203CF0C->frameImageTiles;
+ sUnknown_0203CF0C->frameImage->size = ROOT_FOSSIL_GFX_LENGTH;
+ break;
+ case 4:
+ {
+ struct SpriteTemplate fossilTemplate;
+
+ fossilTemplate = gUnknown_08617E00;
+ fossilTemplate.images = (struct SpriteFrameImage *)(sUnknown_0203CF0C->frameImage);
+ sUnknown_0203CF0C->spriteId = CreateSprite(&fossilTemplate, 128, -16, 1);
+ gSprites[sUnknown_0203CF0C->spriteId].centerToCornerVecX = 0;
+ gSprites[sUnknown_0203CF0C->spriteId].data[0] = gSprites[sUnknown_0203CF0C->spriteId].pos1.x;
+ gSprites[sUnknown_0203CF0C->spriteId].data[1] = 1;
+ }
+ case 5:
+ for (i = 0; i < ROOT_FOSSIL_GFX_RANDOMIZER_LENGTH; i++)
+ sUnknown_0203CF0C->unkC[i] = i;
+ break;
+ case 6:
+ for (i = 0; i < (ROOT_FOSSIL_GFX_RANDOMIZER_LENGTH * sizeof(u16)); i++)
+ {
+ u16 rand1, rand2, temp;
+
+ rand1 = Random() % 0x100;
+ rand2 = Random() % 0x100;
+ SWAP(sUnknown_0203CF0C->unkC[rand2], sUnknown_0203CF0C->unkC[rand1], temp);
+ }
+ gSprites[sUnknown_0203CF0C->spriteId].callback = sub_81BF248;
+ break;
+ case 7:
+ if (gSprites[sUnknown_0203CF0C->spriteId].callback != SpriteCallbackDummy)
+ return;
+ DestroySprite(&gSprites[sUnknown_0203CF0C->spriteId]);
+ FREE_AND_SET_NULL(sUnknown_0203CF0C->unkC);;
+ FREE_AND_SET_NULL(sUnknown_0203CF0C->frameImage);
+ FREE_AND_SET_NULL(sUnknown_0203CF0C->frameImageTiles);
+ FREE_AND_SET_NULL(sUnknown_0203CF0C);
+ break;
+ case 8:
+ EnableBothScriptContexts();
+ break;
+ }
+
+ gTasks[taskId].data[0]++;
+}
+
+static void sub_81BF248(struct Sprite *sprite)
+{
+ if (sUnknown_0203CF0C->unk10 >= (ROOT_FOSSIL_GFX_RANDOMIZER_LENGTH))
+ {
+ sprite->callback = SpriteCallbackDummy;
+ }
+ else if (sprite->pos1.y >= 96)
+ {
+ u8 i;
+ for (i = 0; i < 2; i++)
+ sub_81BF2B8(sUnknown_0203CF0C->frameImageTiles, sUnknown_0203CF0C->unkC[sUnknown_0203CF0C->unk10++], 0, 16, 0);
+
+ StartSpriteAnim(sprite, 0);
+ }
+ else
+ {
+ sprite->pos1.y++;
+ }
+}
+
+#ifdef NONMATCHING
+static void sub_81BF2B8(u8* a, u16 b, u8 c, u8 d, u8 e)
+{
+ u8 r5, r4, r0, r2;
+ u16 var;
+
+ r4 = r5 = b / d;
+ gUnknown_030012A8[0] = r4;
+
+ r0 = r2 = b % d;
+ gUnknown_030012A8[1] = r2;
+
+ r4 &= 7;
+ r2 &= 7;
+ gUnknown_030012A8[2] = r4;
+ gUnknown_030012A8[3] = r2;
+
+ r0 /= 8;
+ r5 /= 8;
+ gUnknown_030012A8[4] = r0;
+ gUnknown_030012A8[5] = r5;
+
+ var = ((d / 8) * (r5 * 64)) + (r0 * 64);
+ gUnknown_030012A8[6] = var;
+
+ var += (r4 * 8) + r2;
+ gUnknown_030012A8[7] = var;
+
+ // This part is non-matching. 99% sure it IS functionally equivalent, though.
+ b = (b & 1) ^ 1;
+ c = (c << ((b) << 2)) | (15 << ((b ^ 1) << 2));
+
+ a[(var / 2) + (e * 32)] &= c;
+}
+
+#else
+NAKED
+static void sub_81BF2B8(u8* a, u16 b, u8 c, u8 d, u8 e)
+{
+ asm_unified("\n\
+ push {r4-r7,lr}\n\
+ mov r7, r10\n\
+ mov r6, r9\n\
+ mov r5, r8\n\
+ push {r5-r7}\n\
+ sub sp, 0x8\n\
+ str r0, [sp]\n\
+ mov r10, r1\n\
+ adds r6, r2, 0\n\
+ mov r8, r3\n\
+ ldr r0, [sp, 0x28]\n\
+ mov r9, r0\n\
+ lsls r1, 16\n\
+ lsrs r1, 16\n\
+ mov r10, r1\n\
+ lsls r6, 24\n\
+ lsrs r6, 24\n\
+ mov r0, r8\n\
+ lsls r0, 24\n\
+ mov r8, r0\n\
+ lsrs r7, r0, 24\n\
+ mov r1, r9\n\
+ lsls r1, 24\n\
+ lsrs r1, 24\n\
+ mov r9, r1\n\
+ mov r0, r10\n\
+ adds r1, r7, 0\n\
+ bl __divsi3\n\
+ adds r5, r0, 0\n\
+ lsls r5, 24\n\
+ lsrs r4, r5, 24\n\
+ ldr r3, =gUnknown_030012A8\n\
+ strh r4, [r3]\n\
+ mov r0, r10\n\
+ adds r1, r7, 0\n\
+ str r3, [sp, 0x4]\n\
+ bl __modsi3\n\
+ lsls r0, 24\n\
+ lsrs r2, r0, 24\n\
+ ldr r3, [sp, 0x4]\n\
+ strh r2, [r3, 0x2]\n\
+ movs r1, 0x7\n\
+ ands r4, r1\n\
+ ands r2, r1\n\
+ strh r4, [r3, 0x4]\n\
+ strh r2, [r3, 0x6]\n\
+ lsrs r0, 27\n\
+ lsrs r5, 27\n\
+ strh r0, [r3, 0x8]\n\
+ strh r5, [r3, 0xA]\n\
+ mov r1, r8\n\
+ lsrs r1, 27\n\
+ lsls r1, 6\n\
+ mov r8, r1\n\
+ mov r1, r8\n\
+ muls r1, r5\n\
+ lsls r0, 6\n\
+ adds r1, r0\n\
+ lsls r1, 16\n\
+ lsrs r1, 16\n\
+ strh r1, [r3, 0xC]\n\
+ lsls r4, 3\n\
+ adds r4, r2\n\
+ adds r1, r4\n\
+ lsls r4, r1, 16\n\
+ lsrs r4, 17\n\
+ strh r1, [r3, 0xE]\n\
+ movs r1, 0x1\n\
+ mov r0, r10\n\
+ ands r1, r0\n\
+ movs r2, 0x1\n\
+ eors r1, r2\n\
+ lsls r0, r1, 2\n\
+ lsls r6, r0\n\
+ eors r1, r2\n\
+ lsls r1, 2\n\
+ movs r0, 0xF\n\
+ lsls r0, r1\n\
+ orrs r6, r0\n\
+ lsls r6, 24\n\
+ lsrs r6, 24\n\
+ mov r1, r9\n\
+ lsls r1, 5\n\
+ mov r9, r1\n\
+ add r9, r4\n\
+ ldr r1, [sp]\n\
+ add r1, r9\n\
+ ldrb r0, [r1]\n\
+ ands r6, r0\n\
+ strb r6, [r1]\n\
+ add sp, 0x8\n\
+ pop {r3-r5}\n\
+ mov r8, r3\n\
+ mov r9, r4\n\
+ mov r10, r5\n\
+ pop {r4-r7}\n\
+ pop {r0}\n\
+ bx r0\n\
+ .pool\n\
+ ");
+}
+#endif // NONMATCHING