#include "global.h" #include "malloc.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 "palette_util.h" #include "script.h" #include "sound.h" #include "sprite.h" #include "task.h" #include "window.h" #include "constants/event_objects.h" #include "constants/rgb.h" #include "constants/songs.h" #include "constants/metatile_labels.h" struct MirageTowerPulseBlend { u8 taskId; struct PulseBlend pulseBlend; }; struct MetatileCoords { u8 x; u8 y; u16 metatileId; }; struct BgRegOffsets { u16 bgHOFS; u16 bgVOFS; }; struct FallAnim_Tower { u8 *disintegrateRand; u8 disintegrateIdx; }; struct FallAnim_Fossil { u8 *frameImageTiles; struct SpriteFrameImage *frameImage; u8 spriteId; u16 *disintegrateRand; u16 disintegrateIdx; }; #define TAG_CEILING_CRUMBLE 4000 #define MIRAGE_TOWER_GFX_LENGTH (sizeof(sBlankTile_Gfx) + sizeof(sMirageTower_Gfx)) #define FOSSIL_DISINTEGRATE_LENGTH 0x100 static const struct SpriteSheet sCeilingCrumbleSpriteSheets[]; static const s16 sCeilingCrumblePositions[][3]; static void PlayerDescendMirageTower(u8); static void DoScreenShake(u8); static void IncrementCeilingCrumbleFinishedCount(void); static void WaitCeilingCrumble(u8); static void FinishCeilingCrumbleTask(u8); static void CreateCeilingCrumbleSprites(void); static void SpriteCB_CeilingCrumble(struct Sprite *); static void DoMirageTowerDisintegration(u8); static void InitMirageTowerShake(u8); static void Task_FossilFallAndSink(u8); static void SpriteCB_FallingFossil(struct Sprite *); static void UpdateDisintegrationEffect(u8 *, u16, u8, u8, u8); 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 sFossil_Pal[] = INCBIN_U16("graphics/object_events/pics/misc/fossil.gbapal"); // Unused static const u8 sFossil_Gfx[] = INCBIN_U8("graphics/object_events/pics/misc/fossil.4bpp"); // Duplicate of gObjectEventPic_Fossil 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"); static 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}, }; static const struct SpriteSheet sCeilingCrumbleSpriteSheets[] = { {sMirageTowerCrumbles_Gfx, sizeof(sMirageTowerCrumbles_Gfx), TAG_CEILING_CRUMBLE}, {} }; static const struct MetatileCoords sInvisibleMirageTowerMetatiles[] = { {18, 53, METATILE_Mauville_DeepSand_Center}, {19, 53, METATILE_Mauville_DeepSand_Center}, {20, 53, METATILE_Mauville_DeepSand_Center}, {18, 54, METATILE_Mauville_DeepSand_Center}, {19, 54, METATILE_Mauville_DeepSand_Center}, {20, 54, METATILE_Mauville_DeepSand_Center}, {18, 55, METATILE_Mauville_DeepSand_Center}, {19, 55, METATILE_Mauville_DeepSand_Center}, {20, 55, METATILE_Mauville_DeepSand_Center}, {18, 56, METATILE_Mauville_DeepSand_Center}, {19, 56, METATILE_Mauville_DeepSand_Center}, {20, 56, METATILE_Mauville_DeepSand_Center}, {18, 57, METATILE_Mauville_DeepSand_BottomMid}, {19, 57, METATILE_Mauville_DeepSand_BottomMid}, {20, 57, METATILE_Mauville_DeepSand_BottomMid}, {18, 58, METATILE_General_SandPit_Center}, {19, 58, METATILE_General_SandPit_Center}, {20, 58, METATILE_General_SandPit_Center}, }; static const union AnimCmd sAnim_FallingFossil[] = { ANIMCMD_FRAME(0, 1), ANIMCMD_END, }; static const struct OamData sOamData_FallingFossil = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(16x16), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(16x16), .tileNum = 0, .priority = 0, .paletteNum = 3, .affineParam = 0, }; static const union AnimCmd *const sAnims_FallingFossil[] = { sAnim_FallingFossil, }; static const struct SpriteTemplate sSpriteTemplate_FallingFossil = { .tileTag = TAG_NONE, .paletteTag = TAG_NONE, .oam = &sOamData_FallingFossil, .anims = sAnims_FallingFossil, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = 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 sAnim_CeilingCrumbleSmall[] = { ANIMCMD_FRAME(0, 12), ANIMCMD_JUMP(0), }; static const union AnimCmd *const sAnims_CeilingCrumbleSmall[] = { sAnim_CeilingCrumbleSmall, }; static const struct OamData sOamData_CeilingCrumbleSmall = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(8x8), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(8x8), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0, }; static const struct SpriteTemplate sSpriteTemplate_CeilingCrumbleSmall = { .tileTag = TAG_CEILING_CRUMBLE, .paletteTag = TAG_NONE, .oam = &sOamData_CeilingCrumbleSmall, .anims = sAnims_CeilingCrumbleSmall, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_CeilingCrumble }; static const union AnimCmd sAnim_CeilingCrumbleLarge[] = { ANIMCMD_FRAME(0, 12), ANIMCMD_JUMP(0), }; static const union AnimCmd *const sAnims_CeilingCrumbleLarge[] = { sAnim_CeilingCrumbleLarge, }; static const struct OamData sOamData_CeilingCrumbleLarge = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = FALSE, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(16x16), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(16x16), .tileNum = 0, .priority = 0, .paletteNum = 0, .affineParam = 0, }; static const struct SpriteTemplate sSpriteTemplate_CeilingCrumbleLarge = { .tileTag = TAG_CEILING_CRUMBLE, .paletteTag = TAG_NONE, .oam = &sOamData_CeilingCrumbleLarge, .anims = sAnims_CeilingCrumbleLarge, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_CeilingCrumble }; EWRAM_DATA static u8 *sMirageTowerGfxBuffer = NULL; EWRAM_DATA static u8 *sMirageTowerTilemapBuffer = NULL; EWRAM_DATA static struct FallAnim_Fossil *sFallingFossil = NULL; EWRAM_DATA static struct FallAnim_Tower *sFallingTower = NULL; EWRAM_DATA static struct BgRegOffsets *sBgShakeOffsets = NULL; EWRAM_DATA static struct MirageTowerPulseBlend *sMirageTowerPulseBlend = NULL; // Holds data about the disintegration effect for Mirage Tower / the unchosen fossil. // Never read, presumably for debugging static u16 sDebug_DisintegrationData[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_MIRAGE_TOWER_STATE)) { // Mirage Tower event has already been completed, hide it 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); } // As the tower disintegrates, a duplicate object event of the player // is created at the top of the tower and moved down to show the player falling static void PlayerDescendMirageTower(u8 taskId) { u8 objectEventId; struct ObjectEvent *fallingPlayer; struct ObjectEvent *player; TryGetObjectEventIdByLocalIdAndMap(LOCALID_ROUTE111_PLAYER_FALLING, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objectEventId); fallingPlayer = &gObjectEvents[objectEventId]; gSprites[fallingPlayer->spriteId].y2 += 4; player = &gObjectEvents[gPlayerAvatar.objectEventId]; if ((gSprites[fallingPlayer->spriteId].y + gSprites[fallingPlayer->spriteId].y2) >= (gSprites[player->spriteId].y + gSprites[player->spriteId].y2)) { DestroyTask(taskId); ScriptContext_Enable(); } } #define tXShakeOffset data[0] #define tTimer data[1] #define tNumShakes data[2] #define tShakeDelay data[3] #define tYShakeOffset data[4] static void StartScreenShake(u8 yShakeOffset, u8 xShakeOffset, u8 numShakes, u8 shakeDelay) { u8 taskId = CreateTask(DoScreenShake, 9); gTasks[taskId].tXShakeOffset = xShakeOffset; gTasks[taskId].tTimer = 0; gTasks[taskId].tNumShakes = numShakes; gTasks[taskId].tShakeDelay = shakeDelay; gTasks[taskId].tYShakeOffset = yShakeOffset; SetCameraPanningCallback(NULL); PlaySE(SE_M_STRENGTH); } static void DoScreenShake(u8 taskId) { s16 *data; data = gTasks[taskId].data; tTimer++; if (tTimer % tShakeDelay == 0) { tTimer = 0; tNumShakes--; tXShakeOffset = -tXShakeOffset; tYShakeOffset = -tYShakeOffset; SetCameraPanning(tXShakeOffset, tYShakeOffset); if (tNumShakes == 0) { IncrementCeilingCrumbleFinishedCount(); DestroyTask(taskId); InstallCameraPanAheadCallback(); } } } #undef tXShakeOffset #undef tTimer #undef tNumShakes #undef tShakeDelay #undef tYShakeOffset static void IncrementCeilingCrumbleFinishedCount(void) { u8 taskId = FindTaskIdByFunc(WaitCeilingCrumble); if (taskId != TASK_NONE) gTasks[taskId].data[0]++; } void DoMirageTowerCeilingCrumble(void) { LoadSpriteSheets(sCeilingCrumbleSpriteSheets); 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(TAG_CEILING_CRUMBLE); DestroyTask(taskId); ScriptContext_Enable(); } static void CreateCeilingCrumbleSprites(void) { u8 i; u8 spriteId; for (i = 0; i < 8; i++) { spriteId = CreateSprite(&sSpriteTemplate_CeilingCrumbleLarge, 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(&sSpriteTemplate_CeilingCrumbleSmall, 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 SpriteCB_CeilingCrumble(struct Sprite *sprite) { sprite->data[1] += 2; sprite->y2 = sprite->data[1] / 2; if(((sprite->y) + (sprite->y2)) > 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 + MAP_OFFSET, sInvisibleMirageTowerMetatiles[i].y + MAP_OFFSET, sInvisibleMirageTowerMetatiles[i].metatileId); DrawWholeMapView(); } void StartMirageTowerDisintegration(void) { CreateTask(DoMirageTowerDisintegration, 9); } void StartMirageTowerShake(void) { CreateTask(InitMirageTowerShake, 9); } void StartMirageTowerFossilFallAndSink(void) { CreateTask(Task_FossilFallAndSink, 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]--; } } #define tState data[0] static void InitMirageTowerShake(u8 taskId) { u8 zero; switch (gTasks[taskId].tState) { case 0: FreeAllWindowBuffers(); SetBgAttribute(0, BG_ATTR_PRIORITY, 2); gTasks[taskId].tState++; break; case 1: sMirageTowerGfxBuffer = (u8 *)AllocZeroed(MIRAGE_TOWER_GFX_LENGTH); sMirageTowerTilemapBuffer = (u8 *)AllocZeroed(BG_SCREEN_SIZE); ChangeBgX(0, 0, BG_COORD_SET); ChangeBgY(0, 0, BG_COORD_SET); gTasks[taskId].tState++; break; case 2: CpuSet(sBlankTile_Gfx, sMirageTowerGfxBuffer, MIRAGE_TOWER_GFX_LENGTH / 2); LoadBgTiles(0, sMirageTowerGfxBuffer, MIRAGE_TOWER_GFX_LENGTH, 0); gTasks[taskId].tState++; break; case 3: SetBgTilemapBuffer(0, sMirageTowerTilemapBuffer); CopyToBgTilemapBufferRect_ChangePalette(0, &sMirageTowerTilemap, 12, 29, 6, 12, 17); CopyBgTilemapBufferToVram(0); gTasks[taskId].tState++; break; case 4: ShowBg(0); gTasks[taskId].tState++; break; case 5: SetInvisibleMirageTowerMetatiles(); gTasks[taskId].tState++; break; case 6: sBgShakeOffsets = Alloc(sizeof(*sBgShakeOffsets)); zero = 0; sBgShakeOffsets->bgHOFS = 2; sBgShakeOffsets->bgVOFS = zero; CreateTask(UpdateBgShake, 10); DestroyTask(taskId); ScriptContext_Enable(); 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].tState) { case 1: sFallingTower = AllocZeroed(OUTER_BUFFER_LENGTH * sizeof(struct FallAnim_Tower)); break; case 3: if (gTasks[taskId].data[3] <= (OUTER_BUFFER_LENGTH - 1)) { if (gTasks[taskId].data[1] > 1) { // Initialize disintegration pattern index = gTasks[taskId].data[3]; sFallingTower[index].disintegrateRand = Alloc(INNER_BUFFER_LENGTH); for (i = 0; i <= (INNER_BUFFER_LENGTH - 1); i++) sFallingTower[index].disintegrateRand[i] = i; // Randomize disintegration pattern for (i = 0; i <= (INNER_BUFFER_LENGTH - 1); i++) { u16 rand1, rand2, temp; rand1 = Random() % INNER_BUFFER_LENGTH; rand2 = Random() % INNER_BUFFER_LENGTH; SWAP(sFallingTower[index].disintegrateRand[rand2], sFallingTower[index].disintegrateRand[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++) { UpdateDisintegrationEffect(sMirageTowerGfxBuffer, (OUTER_BUFFER_LENGTH - 1 - i) * INNER_BUFFER_LENGTH + sFallingTower[i].disintegrateRand[sFallingTower[i].disintegrateIdx++], 0, INNER_BUFFER_LENGTH, 1); } if (sFallingTower[i].disintegrateIdx > (INNER_BUFFER_LENGTH - 1)) { FREE_AND_SET_NULL(sFallingTower[i].disintegrateRand); gTasks[taskId].data[2]++; if ((i % 2) == 1) sBgShakeOffsets->bgVOFS--; } } LoadBgTiles(0, sMirageTowerGfxBuffer, MIRAGE_TOWER_GFX_LENGTH, 0); if (sFallingTower[OUTER_BUFFER_LENGTH - 1].disintegrateIdx > INNER_BUFFER_LENGTH - 1) break; return; case 4: UnsetBgTilemapBuffer(0); bgShakeTaskId = FindTaskIdByFunc(UpdateBgShake); if (bgShakeTaskId != TASK_NONE) DestroyTask(bgShakeTaskId); sBgShakeOffsets->bgVOFS = sBgShakeOffsets->bgHOFS = 0; SetBgShakeOffsets(); break; case 5: FREE_AND_SET_NULL(sBgShakeOffsets); FREE_AND_SET_NULL(sFallingTower); 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); ScriptContext_Enable(); break; } gTasks[taskId].tState++; } static void Task_FossilFallAndSink(u8 taskId) { u16 i; u8 *buffer; switch (gTasks[taskId].tState) { case 1: sFallingFossil = AllocZeroed(sizeof(*sFallingFossil)); sFallingFossil->frameImageTiles = AllocZeroed(sizeof(sFossil_Gfx)); sFallingFossil->frameImage = AllocZeroed(sizeof(*sFallingFossil->frameImage)); sFallingFossil->disintegrateRand = AllocZeroed(FOSSIL_DISINTEGRATE_LENGTH * sizeof(u16)); sFallingFossil->disintegrateIdx = 0; break; case 2: buffer = sFallingFossil->frameImageTiles; for (i = 0; i < sizeof(sFossil_Gfx); i++, buffer++) *buffer = sFossil_Gfx[i]; break; case 3: sFallingFossil->frameImage->data = sFallingFossil->frameImageTiles; sFallingFossil->frameImage->size = sizeof(sFossil_Gfx); break; case 4: { struct SpriteTemplate fossilTemplate = sSpriteTemplate_FallingFossil; fossilTemplate.images = sFallingFossil->frameImage; sFallingFossil->spriteId = CreateSprite(&fossilTemplate, 128, -16, 1); gSprites[sFallingFossil->spriteId].centerToCornerVecX = 0; gSprites[sFallingFossil->spriteId].data[0] = gSprites[sFallingFossil->spriteId].x; gSprites[sFallingFossil->spriteId].data[1] = 1; } case 5: // Initialize disintegration pattern for (i = 0; i < FOSSIL_DISINTEGRATE_LENGTH; i++) sFallingFossil->disintegrateRand[i] = i; break; case 6: // Randomize disintegration pattern for (i = 0; i < FOSSIL_DISINTEGRATE_LENGTH * sizeof(u16); i++) { u16 rand1, rand2, temp; rand1 = Random() % FOSSIL_DISINTEGRATE_LENGTH; rand2 = Random() % FOSSIL_DISINTEGRATE_LENGTH; SWAP(sFallingFossil->disintegrateRand[rand2], sFallingFossil->disintegrateRand[rand1], temp); } gSprites[sFallingFossil->spriteId].callback = SpriteCB_FallingFossil; break; case 7: // Wait for fossil to finish falling / disintegrating if (gSprites[sFallingFossil->spriteId].callback != SpriteCallbackDummy) return; DestroySprite(&gSprites[sFallingFossil->spriteId]); FREE_AND_SET_NULL(sFallingFossil->disintegrateRand);; FREE_AND_SET_NULL(sFallingFossil->frameImage); FREE_AND_SET_NULL(sFallingFossil->frameImageTiles); FREE_AND_SET_NULL(sFallingFossil); break; case 8: ScriptContext_Enable(); break; } gTasks[taskId].tState++; } static void SpriteCB_FallingFossil(struct Sprite *sprite) { if (sFallingFossil->disintegrateIdx >= FOSSIL_DISINTEGRATE_LENGTH) { // End animation sprite->callback = SpriteCallbackDummy; } else if (sprite->y >= 96) { // Fossil has reached the ground, update disintegration animation u8 i; for (i = 0; i < 2; i++) UpdateDisintegrationEffect(sFallingFossil->frameImageTiles, sFallingFossil->disintegrateRand[sFallingFossil->disintegrateIdx++], 0, 16, 0); StartSpriteAnim(sprite, 0); } else { // Fossil is still falling sprite->y++; } } static void UpdateDisintegrationEffect(u8 *tiles, u16 randId, u8 c, u8 size, u8 offset) { u8 heightTiles, height, widthTiles, width; u16 var, baseOffset; u8 col, row; u8 flag, tileMask; height = randId / size; sDebug_DisintegrationData[0] = height; width = randId % size; sDebug_DisintegrationData[1] = width; row = height & 7; col = width & 7; sDebug_DisintegrationData[2] = height & 7; sDebug_DisintegrationData[3] = width & 7; widthTiles = width / 8; heightTiles = height / 8; sDebug_DisintegrationData[4] = width / 8; sDebug_DisintegrationData[5] = height / 8; var = (size / 8) * (heightTiles * 64) + (widthTiles * 64); sDebug_DisintegrationData[6] = var; baseOffset = var + ((row * 8) + col); baseOffset /= 2; sDebug_DisintegrationData[7] = var + ((row * 8) + col); flag = ((randId % 2) ^ 1); tileMask = (c << (flag << 2)) | 15 << (((flag ^ 1) << 2)); tiles[baseOffset + (offset * 32)] &= tileMask; }