pokeemerald/src/mirage_tower.c

787 lines
23 KiB
C

#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;
}