pokefirered/src/field_effect.c
2025-03-15 01:06:45 +01:00

4036 lines
126 KiB
C

#include "global.h"
#include "gflib.h"
#include "data.h"
#include "decompress.h"
#include "event_data.h"
#include "event_object_movement.h"
#include "field_camera.h"
#include "field_control_avatar.h"
#include "field_effect.h"
#include "field_effect_helpers.h"
#include "field_fadetransition.h"
#include "field_player_avatar.h"
#include "field_weather.h"
#include "fieldmap.h"
#include "help_system.h"
#include "metatile_behavior.h"
#include "menu.h"
#include "overworld.h"
#include "party_menu.h"
#include "pokemon_storage_system.h"
#include "quest_log.h"
#include "rtc.h"
#include "script.h"
#include "special_field_anim.h"
#include "task.h"
#include "trainer_pokemon_sprites.h"
#include "trainer_see.h"
#include "trig.h"
#include "util.h"
#include "constants/event_object_movement.h"
#include "constants/metatile_behaviors.h"
#include "constants/songs.h"
#include "constants/sound.h"
#define subsprite_table(ptr) {.subsprites = ptr, .subspriteCount = (sizeof ptr) / (sizeof(struct Subsprite))}
#define MAX_ACTIVE_FLD_EFFECTS 32
EWRAM_DATA u32 gFieldEffectArguments[8] = {0};
static enum FieldEffect sFieldEffectActiveList[MAX_ACTIVE_FLD_EFFECTS];
static void FieldEffectActiveListAdd(enum FieldEffect fldeff);
static void Task_PokecenterHeal(u8 taskId);
static void SpriteCB_PokeballGlow(struct Sprite *sprite);
static void SpriteCB_PokecenterMonitor(struct Sprite *sprite);
static void SpriteCB_HallOfFameMonitor(struct Sprite *sprite);
static u32 FldEff_Nop(void);
// Unused
static const u16 sNewGameOakObject_Gfx[] = INCBIN_U16("graphics/field_effects/pics/new_game_oak.4bpp");
static const u16 sNewGameOakObject_Pal[] = INCBIN_U16("graphics/field_effects/pics/new_game_oak.gbapal");
static const u16 sPokeballGlow_Gfx[] = INCBIN_U16("graphics/field_effects/pics/pokeball_glow.4bpp");
static const u16 sPokeballGlow_Pal[] = INCBIN_U16("graphics/field_effects/pics/pokeball_glow.gbapal");
static const u16 sPokecenterMonitor_Gfx[] = INCBIN_U16("graphics/field_effects/pics/pokemoncenter_monitor.4bpp");
static const u16 sHofMonitor_Pal[] = INCBIN_U16("graphics/field_effects/pics/hof_monitor.gbapal");
static const u16 sHofMonitor_Gfx[] = INCBIN_U16("graphics/field_effects/pics/hof_monitor.4bpp");
static const u16 sFieldMoveStreaksOutdoors_Gfx[] = INCBIN_U16("graphics/field_effects/pics/field_move_streaks_outdoors.4bpp");
static const u16 sFieldMoveStreaksOutdoors_Pal[] = INCBIN_U16("graphics/field_effects/pics/field_move_streaks_outdoors.gbapal");
static const u16 sFieldMoveStreaksOutdoors_Tilemap[] = INCBIN_U16("graphics/field_effects/pics/field_move_streaks_outdoors.bin");
static const u16 sFieldMoveStreaksIndoors_Gfx[] = INCBIN_U16("graphics/field_effects/pics/field_move_streaks_indoors.4bpp");
static const u16 sFieldMoveStreaksIndoors_Pal[] = INCBIN_U16("graphics/field_effects/pics/field_move_streaks_indoors.gbapal");
static const u16 sFieldMoveStreaksIndoors_Tilemap[] = INCBIN_U16("graphics/field_effects/pics/field_move_streaks_indoors.bin");
static const u16 sRockFragment_TopLeft[] = INCBIN_U16("graphics/field_effects/pics/deoxys_rock_fragment_top_left.4bpp");
static const u16 sRockFragment_TopRight[] = INCBIN_U16("graphics/field_effects/pics/deoxys_rock_fragment_top_right.4bpp");
static const u16 sRockFragment_BottomLeft[] = INCBIN_U16("graphics/field_effects/pics/deoxys_rock_fragment_bottom_left.4bpp");
static const u16 sRockFragment_BottomRight[] = INCBIN_U16("graphics/field_effects/pics/deoxys_rock_fragment_bottom_right.4bpp");
static const u32 (*const sFieldEffectFuncs[FLDEFF_COUNT]) (void) =
{
[FLDEFF_NONE] = FldEff_Nop,
[FLDEFF_EXCLAMATION_MARK_ICON] = FldEff_ExclamationMarkIcon,
[FLDEFF_USE_CUT_ON_GRASS] = FldEff_UseCutOnGrass,
[FLDEFF_USE_CUT_ON_TREE] = FldEff_UseCutOnTree,
[FLDEFF_SHADOW] = FldEff_Shadow,
[FLDEFF_TALL_GRASS] = FldEff_TallGrass,
[FLDEFF_RIPPLE] = FldEff_Ripple,
[FLDEFF_FIELD_MOVE_SHOW_MON] = FldEff_FieldMoveShowMon,
[FLDEFF_ASH] = FldEff_Ash,
[FLDEFF_SURF_BLOB] = FldEff_SurfBlob,
[FLDEFF_USE_SURF] = FldEff_UseSurf,
[FLDEFF_DUST] = FldEff_Dust,
[FLDEFF_USE_SECRET_POWER_CAVE] = FldEff_Nop,
[FLDEFF_JUMP_TALL_GRASS] = FldEff_JumpTallGrass,
[FLDEFF_SAND_FOOTPRINTS] = FldEff_SandFootprints,
[FLDEFF_JUMP_BIG_SPLASH] = FldEff_JumpBigSplash,
[FLDEFF_SPLASH] = FldEff_Splash,
[FLDEFF_JUMP_SMALL_SPLASH] = FldEff_JumpSmallSplash,
[FLDEFF_LONG_GRASS] = FldEff_LongGrass,
[FLDEFF_JUMP_LONG_GRASS] = FldEff_JumpLongGrass,
[FLDEFF_SHAKING_GRASS] = FldEff_ShakingGrass,
[FLDEFF_SHAKING_LONG_GRASS] = FldEff_ShakingGrass2,
[FLDEFF_SAND_HOLE] = FldEff_UnusedSand,
[FLDEFF_UNUSED_WATER_SURFACING] = FldEff_UnusedWaterSurfacing,
[FLDEFF_BERRY_TREE_GROWTH_SPARKLE] = FldEff_BerryTreeGrowthSparkle,
[FLDEFF_DEEP_SAND_FOOTPRINTS] = FldEff_DeepSandFootprints,
[FLDEFF_POKECENTER_HEAL] = FldEff_PokecenterHeal,
[FLDEFF_USE_SECRET_POWER_TREE] = FldEff_Nop,
[FLDEFF_USE_SECRET_POWER_SHRUB] = FldEff_Nop,
[FLDEFF_TREE_DISGUISE] = FldEff_TreeDisguise,
[FLDEFF_MOUNTAIN_DISGUISE] = FldEff_MountainDisguise,
[FLDEFF_NPCFLY_OUT] = FldEff_NpcFlyOut,
[FLDEFF_FLY_OUT] = FldEff_FlyOut,
[FLDEFF_FLY_IN] = FldEff_FlyIn,
[FLDEFF_QUESTION_MARK_ICON_AND_EMOTE] = FldEff_QuestionMarkIcon,
[FLDEFF_FEET_IN_FLOWING_WATER] = FldEff_FeetInFlowingWater,
[FLDEFF_BIKE_TIRE_TRACKS] = FldEff_BikeTireTracks,
[FLDEFF_SAND_DISGUISE] = FldEff_SandDisguise,
[FLDEFF_USE_ROCK_SMASH] = FldEff_UseRockSmash,
[FLDEFF_USE_DIG] = FldEff_UseDig,
[FLDEFF_SAND_PILE] = FldEff_SandPile,
[FLDEFF_USE_STRENGTH] = FldEff_UseStrength,
[FLDEFF_SHORT_GRASS] = FldEff_ShortGrass,
[FLDEFF_HOT_SPRINGS_WATER] = FldEff_HotSpringsWater,
[FLDEFF_USE_WATERFALL] = FldEff_UseWaterfall,
[FLDEFF_USE_DIVE] = FldEff_UseDive,
[FLDEFF_POKEBALL] = FldEff_Pokeball,
[FLDEFF_X_ICON] = FldEff_XIcon,
[FLDEFF_NOP_47] = FldEff_Nop,
[FLDEFF_NOP_48] = FldEff_Nop,
[FLDEFF_POP_OUT_OF_ASH] = FldEff_PopOutOfAsh,
[FLDEFF_LAVARIDGE_GYM_WARP] = FldEff_LavaridgeGymWarp,
[FLDEFF_SWEET_SCENT] = FldEff_SweetScent,
[FLDEFF_SAND_PILLAR] = FldEff_Nop,
[FLDEFF_BUBBLES] = FldEff_Bubbles,
[FLDEFF_SPARKLE] = FldEff_Sparkle,
[FLDEFF_SECRET_POWER_CAVE] = FldEff_Nop,
[FLDEFF_SECRET_POWER_TREE] = FldEff_Nop,
[FLDEFF_SECRET_POWER_SHRUB] = FldEff_Nop,
[FLDEFF_CUT_GRASS] = FldEff_CutGrass,
[FLDEFF_FIELD_MOVE_SHOW_MON_INIT] = FldEff_FieldMoveShowMonInit,
[FLDEFF_USE_FLY_ANCIENT_TOMB] = FldEff_Nop,
[FLDEFF_PCTURN_ON] = FldEff_Nop,
[FLDEFF_HALL_OF_FAME_RECORD] = FldEff_HallOfFameRecord,
[FLDEFF_USE_TELEPORT] = FldEff_UseTeleport,
[FLDEFF_SMILEY_FACE_ICON] = FldEff_SmileyFaceIcon,
[FLDEFF_USE_VS_SEEKER] = FldEff_UseVsSeeker,
[FLDEFF_DOUBLE_EXCL_MARK_ICON] = FldEff_DoubleExclMarkIcon,
[FLDEFF_MOVE_DEOXYS_ROCK] = FldEff_MoveDeoxysRock,
[FLDEFF_DESTROY_DEOXYS_ROCK] = FldEff_DestroyDeoxysRock,
[FLDEFF_PHOTO_FLASH] = FldEff_PhotoFlash,
[FLDEFF_TRACKS_SLITHER] = FldEff_TracksSlither,
[FLDEFF_TRACKS_BUG] = FldEff_TracksBug,
[FLDEFF_TRACKS_SPOT] = FldEff_TracksSpot,
[FLDEFF_SNOW_FOOTPRINTS] = FldEff_SnowFootprints,
[FLDEFF_SNOW_BIKE_TIRE_TRACKS] = FldEff_SnowBikeTireTracks,
[FLDEFF_SNOW_TRACKS_SLITHER] = FldEff_SnowTracksSlither,
[FLDEFF_SNOW_TRACKS_BUG] = FldEff_SnowTracksBug,
[FLDEFF_SNOW_TRACKS_SPOT] = FldEff_SnowTracksSpot,
[FLDEFF_CAVE_DUST] = FldEff_CaveDust,
};
static const struct OamData sNewGameOakOamAttributes = {
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = FALSE,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(64x64),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(64x64),
.tileNum = 0x000,
.priority = 0,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOamData_8x8 = {
.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 = 0x000,
.priority = 0,
.paletteNum = 0,
.affineParam = 0
};
static const struct OamData sOamData_16x16 = {
.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 = 0x000,
.priority = 0,
.paletteNum = 0,
.affineParam = 0
};
static const struct SpriteFrameImage sNewGameOakObjectSpriteFrames[] = {
{sNewGameOakObject_Gfx, 0x800}
};
static const struct SpritePalette sNewGameOakObjectPaletteInfo = {
sNewGameOakObject_Pal, 4102
};
static const union AnimCmd sNewGameOakAnim[] = {
ANIMCMD_FRAME(0, 1),
ANIMCMD_END
};
static const union AnimCmd *const sNewGameOakAnimTable[] = {
sNewGameOakAnim
};
static const struct SpriteTemplate sNewGameOakObjectTemplate = {
.tileTag = TAG_NONE,
.paletteTag = 0x1006,
.oam = &sNewGameOakOamAttributes,
.anims = sNewGameOakAnimTable,
.images = sNewGameOakObjectSpriteFrames,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy
};
const struct SpritePalette gSpritePalette_PokeballGlow = {
sPokeballGlow_Pal, 4103
};
const struct SpritePalette gSpritePalette_HofMonitor = {
sHofMonitor_Pal, 4112
};
static const struct OamData sOamData_32x16 = {
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.mosaic = FALSE,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(32x16),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(32x16),
.tileNum = 0x000,
.priority = 0,
.paletteNum = 0,
.affineParam = 0
};
static const struct SpriteFrameImage sPicTable_PokeballGlow[] = {
{sPokeballGlow_Gfx, 0x20}
};
static const struct SpriteFrameImage sPicTable_PokecenterMonitor[] = {
{sPokecenterMonitor_Gfx + 0x000, 0x100},
{sPokecenterMonitor_Gfx + 0x080, 0x100},
{sPokecenterMonitor_Gfx + 0x100, 0x100},
{sPokecenterMonitor_Gfx + 0x180, 0x100}
};
static const struct SpriteFrameImage sPicTable_HofMonitor[] = {
{sHofMonitor_Gfx + 0x00, 0x80},
{sHofMonitor_Gfx + 0x40, 0x80},
{sHofMonitor_Gfx + 0x80, 0x80},
{sHofMonitor_Gfx + 0xC0, 0x80}
};
// Unused, leftover from RSE
static const struct Subsprite sSubsprites_PokecenterMonitor[] =
{
{
.x = -12,
.y = -8,
.shape = SPRITE_SHAPE(16x8),
.size = SPRITE_SIZE(16x8),
.tileOffset = 0,
.priority = 2
}, {
.x = 4,
.y = -8,
.shape = SPRITE_SHAPE(8x8),
.size = SPRITE_SIZE(8x8),
.tileOffset = 2,
.priority = 2
}, {
.x = -12,
.y = 0,
.shape = SPRITE_SHAPE(16x8),
.size = SPRITE_SIZE(16x8),
.tileOffset = 3,
.priority = 2
}, {
.x = 4,
.y = 0,
.shape = SPRITE_SHAPE(8x8),
.size = SPRITE_SIZE(8x8),
.tileOffset = 5,
.priority = 2
}
};
// Unused, leftover from RSE
static const struct SubspriteTable sSubspriteTable_PokecenterMonitor = subsprite_table(sSubsprites_PokecenterMonitor);
// Unused, leftover from RSE
static const struct Subsprite sSubsprites_HofMonitorBig[] =
{
{
.x = -32,
.y = -8,
.shape = SPRITE_SHAPE(32x8),
.size = SPRITE_SIZE(32x8),
.tileOffset = 0,
.priority = 2
}, {
.x = 0,
.y = -8,
.shape = SPRITE_SHAPE(32x8),
.size = SPRITE_SIZE(32x8),
.tileOffset = 4,
.priority = 2
}, {
.x = -32,
.y = 0,
.shape = SPRITE_SHAPE(32x8),
.size = SPRITE_SIZE(32x8),
.tileOffset = 8,
.priority = 2
}, {
.x = 0,
.y = 0,
.shape = SPRITE_SHAPE(32x8),
.size = SPRITE_SIZE(32x8),
.tileOffset = 12,
.priority = 2
}
};
// Unused, leftover from RSE
static const struct SubspriteTable sSubspriteTable_HofMonitorBig = subsprite_table(sSubsprites_HofMonitorBig);
static const union AnimCmd sAnim_Static[] = {
ANIMCMD_FRAME(0, 1),
ANIMCMD_JUMP(0)
};
static const union AnimCmd sAnim_Flicker[] = {
ANIMCMD_FRAME(1, 5),
ANIMCMD_FRAME(2, 5),
ANIMCMD_FRAME(3, 7),
ANIMCMD_FRAME(2, 5),
ANIMCMD_FRAME(1, 5),
ANIMCMD_FRAME(0, 5),
ANIMCMD_LOOP(3),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_Flicker[] = {
sAnim_Static,
sAnim_Flicker
};
static const union AnimCmd sAnim_HofMonitor[] = {
ANIMCMD_FRAME(3, 8),
ANIMCMD_FRAME(2, 8),
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(2, 8),
ANIMCMD_LOOP(2),
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(0, 8),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_HofMonitor[] = {
sAnim_HofMonitor
};
static const struct SpriteTemplate sSpriteTemplate_PokeballGlow = {
.tileTag = TAG_NONE,
.paletteTag = 4103,
.oam = &sOamData_8x8,
.anims = sAnims_Flicker,
.images = sPicTable_PokeballGlow,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_PokeballGlow
};
static const struct SpriteTemplate sSpriteTemplate_PokecenterMonitor = {
.tileTag = TAG_NONE,
.paletteTag = 4103,
.oam = &sOamData_32x16,
.anims = sAnims_Flicker,
.images = sPicTable_PokecenterMonitor,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_PokecenterMonitor
};
static const struct SpriteTemplate sSpriteTemplate_HofMonitor = {
.tileTag = TAG_NONE,
.paletteTag = 4112,
.oam = &sOamData_16x16,
.anims = sAnims_HofMonitor,
.images = sPicTable_HofMonitor,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_HallOfFameMonitor
};
u32 FieldEffectStart(enum FieldEffect fldeff)
{
FieldEffectActiveListAdd(fldeff);
return sFieldEffectFuncs[fldeff]();
}
static void UNUSED FieldEffectScript_LoadTiles(const struct SpriteSheet * spriteSheet)
{
if (GetSpriteTileStartByTag(spriteSheet->tag) == 0xFFFF)
LoadSpriteSheet(spriteSheet);
}
void ApplyGlobalFieldPaletteTint(u8 paletteIdx)
{
switch (gGlobalFieldTintMode)
{
case 0:
return;
case 1:
TintPalette_GrayScale(&gPlttBufferUnfaded[OBJ_PLTT_ID2(paletteIdx)], 16);
break;
case 2:
TintPalette_SepiaTone(&gPlttBufferUnfaded[OBJ_PLTT_ID2(paletteIdx)], 16);
break;
case 3:
QuestLog_BackUpPalette(OBJ_PLTT_ID2(paletteIdx), 16);
TintPalette_GrayScale(&gPlttBufferUnfaded[OBJ_PLTT_ID2(paletteIdx)], 16);
break;
default:
return;
}
CpuFastCopy(&gPlttBufferUnfaded[OBJ_PLTT_ID2(paletteIdx)], &gPlttBufferFaded[OBJ_PLTT_ID2(paletteIdx)], PLTT_SIZE_4BPP);
}
void FieldEffectScript_LoadFadedPal(const struct SpritePalette * spritePalette)
{
bool32 isTagNew = IndexOfSpritePaletteTag(spritePalette->tag) == 0xFF;
u32 paletteSlot = LoadSpritePalette(spritePalette);
SetPaletteColorMapType(paletteSlot + 16, COLOR_MAP_DARK_CONTRAST);
if (isTagNew)
ApplyGlobalFieldPaletteTint(paletteSlot);
UpdateSpritePaletteWithWeather(paletteSlot, TRUE);
}
void FieldEffectScript_LoadPal(const struct SpritePalette * spritePalette)
{
u8 idx = IndexOfSpritePaletteTag(spritePalette->tag);
LoadSpritePalette(spritePalette);
if (idx != 0xFF)
ApplyGlobalFieldPaletteTint(IndexOfSpritePaletteTag(spritePalette->tag));
}
void FieldEffectFreeGraphicsResources(struct Sprite *sprite)
{
u16 tileStart = sprite->sheetTileStart;
u8 paletteNum = sprite->oam.paletteNum;
DestroySprite(sprite);
FieldEffectFreeTilesIfUnused(tileStart);
FieldEffectFreePaletteIfUnused(paletteNum);
}
void FieldEffectStop(struct Sprite *sprite, enum FieldEffect fldeff)
{
FieldEffectFreeGraphicsResources(sprite);
FieldEffectActiveListRemove(fldeff);
}
void FieldEffectFreeTilesIfUnused(u16 tileStart)
{
u8 i;
u16 tag = GetSpriteTileTagByTileStart(tileStart);
if (tag != TAG_NONE)
{
for (i = 0; i < MAX_SPRITES; i++)
if (gSprites[i].inUse && gSprites[i].usingSheet && tileStart == gSprites[i].sheetTileStart)
return;
FreeSpriteTilesByTag(tag);
}
}
void FieldEffectFreePaletteIfUnused(u8 paletteNum)
{
u8 i;
u16 tag = GetSpritePaletteTagByPaletteNum(paletteNum);
if (tag != TAG_NONE)
{
for (i = 0; i < MAX_SPRITES; i++)
if (gSprites[i].inUse && gSprites[i].oam.paletteNum == paletteNum)
return;
FreeSpritePaletteByTag(tag);
}
}
void FieldEffectActiveListClear(void)
{
u8 i;
for (i = 0; i < MAX_ACTIVE_FLD_EFFECTS; i++)
{
sFieldEffectActiveList[i] = FLDEFF_NONE;
}
}
static void FieldEffectActiveListAdd(enum FieldEffect fldeff)
{
u8 i;
for (i = 0; i < MAX_ACTIVE_FLD_EFFECTS; i++)
{
if (sFieldEffectActiveList[i] == FLDEFF_NONE)
{
sFieldEffectActiveList[i] = fldeff;
return;
}
}
}
void FieldEffectActiveListRemove(enum FieldEffect fldeff)
{
u8 i;
for (i = 0; i < MAX_ACTIVE_FLD_EFFECTS; i++)
{
if (sFieldEffectActiveList[i] == fldeff)
{
sFieldEffectActiveList[i] = FLDEFF_NONE;
return;
}
}
}
bool8 FieldEffectActiveListContains(enum FieldEffect fldeff)
{
u8 i;
for (i = 0; i < MAX_ACTIVE_FLD_EFFECTS; i++)
{
if (sFieldEffectActiveList[i] == fldeff)
{
return TRUE;
}
}
return FALSE;
}
u8 CreateTrainerSprite(u8 trainerSpriteID, s16 x, s16 y, u8 subpriority, u8 *buffer)
{
struct SpriteTemplate spriteTemplate;
LoadCompressedSpritePaletteOverrideBuffer(&gTrainerSprites[trainerSpriteID].palette, buffer);
LoadCompressedSpriteSheetOverrideBuffer(&gTrainerSprites[trainerSpriteID].frontPic, buffer);
spriteTemplate.tileTag = gTrainerSprites[trainerSpriteID].frontPic.tag;
spriteTemplate.paletteTag = gTrainerSprites[trainerSpriteID].palette.tag;
spriteTemplate.oam = &sNewGameOakOamAttributes;
spriteTemplate.anims = gDummySpriteAnimTable;
spriteTemplate.images = NULL;
spriteTemplate.affineAnims = gDummySpriteAffineAnimTable;
spriteTemplate.callback = SpriteCallbackDummy;
return CreateSprite(&spriteTemplate, x, y, subpriority);
}
u8 CreateMonSprite_PicBox(u16 species, s16 x, s16 y, u8 subpriority)
{
u16 spriteId = CreateMonPicSprite(species, FALSE, 0x8000, TRUE, x, y, 0, species);
PreservePaletteInWeather(IndexOfSpritePaletteTag(species) + 0x10);
if (spriteId == 0xFFFF)
return MAX_SPRITES;
else
return spriteId;
}
static u8 CreateMonSprite_FieldMove(u16 species, bool32 isShiny, u32 personality, s16 x, s16 y, u8 subpriority)
{
u16 spriteId = CreateMonPicSprite(species, isShiny, personality, 1, x, y, 0, species);
PreservePaletteInWeather(IndexOfSpritePaletteTag(species) + 0x10);
if (spriteId == 0xFFFF)
return MAX_SPRITES;
else
return spriteId;
}
void FreeResourcesAndDestroySprite(struct Sprite *sprite, u8 spriteId)
{
u8 paletteNum = sprite->oam.paletteNum;
ResetPreservedPalettesInWeather();
if (sprite->oam.affineMode != ST_OAM_AFFINE_OFF)
{
FreeOamMatrix(sprite->oam.matrixNum);
}
FreeAndDestroyMonPicSpriteNoPalette(spriteId);
FieldEffectFreePaletteIfUnused(paletteNum); // Clear palette only if unused, in case follower is using it
}
// r, g, b are between 0 and 16
void MultiplyInvertedPaletteRGBComponents(u16 i, u8 r, u8 g, u8 b)
{
int curRed;
int curGreen;
int curBlue;
u16 outPal;
outPal = gPlttBufferUnfaded[i];
curRed = outPal & 0x1f;
curGreen = (outPal & (0x1f << 5)) >> 5;
curBlue = (outPal & (0x1f << 10)) >> 10;
curRed += (((0x1f - curRed) * r) >> 4);
curGreen += (((0x1f - curGreen) * g) >> 4);
curBlue += (((0x1f - curBlue) * b) >> 4);
outPal = curRed;
outPal |= curGreen << 5;
outPal |= curBlue << 10;
gPlttBufferFaded[i] = outPal;
}
static void PokecenterHealEffect_Init(struct Task *task);
static void PokecenterHealEffect_WaitForBallPlacement(struct Task *task);
static void PokecenterHealEffect_WaitForBallFlashing(struct Task *task);
static void PokecenterHealEffect_WaitForSoundAndEnd(struct Task *task);
static void HallOfFameRecordEffect_Init(struct Task *task);
static void HallOfFameRecordEffect_WaitForBallPlacement(struct Task *task);
static void HallOfFameRecordEffect_WaitForBallFlashing(struct Task *task);
static void HallOfFameRecordEffect_WaitForSoundAndEnd(struct Task *task);
static void Task_HallOfFameRecord(u8 taskId);
static u8 CreateGlowingPokeballsEffect(s16 duration, s16 x, s16 y, bool16 fanfare);
static void SpriteCB_PokeballGlowEffect(struct Sprite *sprite);
static void PokeballGlowEffect_PlaceBalls(struct Sprite *sprite);
static void PokeballGlowEffect_TryPlaySe(struct Sprite *sprite);
static void PokeballGlowEffect_FlashFirstThree(struct Sprite *sprite);
static void PokeballGlowEffect_FlashLast(struct Sprite *sprite);
static void PokeballGlowEffect_WaitAfterFlash(struct Sprite *sprite);
static void PokeballGlowEffect_Dummy(struct Sprite *sprite);
static void PokeballGlowEffect_WaitForSound(struct Sprite *sprite);
static void PokeballGlowEffect_Idle(struct Sprite *sprite);
static u8 CreatePokecenterMonitorSprite(s32 x, s32 y);
static void CreateHofMonitorSprite(s32 x, s32 y);
static void (*const sPokecenterHealEffectFuncs[])(struct Task *) =
{
PokecenterHealEffect_Init,
PokecenterHealEffect_WaitForBallPlacement,
PokecenterHealEffect_WaitForBallFlashing,
PokecenterHealEffect_WaitForSoundAndEnd
};
static void (*const sHallOfFameRecordEffectFuncs[])(struct Task *) =
{
HallOfFameRecordEffect_Init,
HallOfFameRecordEffect_WaitForBallPlacement,
HallOfFameRecordEffect_WaitForBallFlashing,
HallOfFameRecordEffect_WaitForSoundAndEnd
};
static void (*const sPokeballGlowEffectFuncs[])(struct Sprite *) =
{
PokeballGlowEffect_PlaceBalls,
PokeballGlowEffect_TryPlaySe,
PokeballGlowEffect_FlashFirstThree,
PokeballGlowEffect_FlashLast,
PokeballGlowEffect_WaitAfterFlash,
PokeballGlowEffect_Dummy,
PokeballGlowEffect_WaitForSound,
PokeballGlowEffect_Idle
};
// Task data for Task_PokecenterHeal and Task_HallOfFameRecord
#define tState data[0]
#define tNumMons data[1]
#define tFirstBallX data[2]
#define tFirstBallY data[3]
#define tMonitorX data[4]
#define tMonitorY data[5]
#define tGlowEffectSpriteId data[6]
#define tMonitorSpriteId data[7]
// Sprite data for SpriteCB_PokeballGlowEffect
#define sState data[0]
#define sTimer data[1]
#define sCounter data[2]
#define sNumFlashed data[3]
#define sPlayHealSe data[5]
#define sNumMons data[6]
#define sSpriteId data[7]
// Sprite data for SpriteCB_PokeballGlow
#define sGlowEffectSpriteId data[0]
// Sprite data for SpriteCB_PokecenterMonitor
#define sStartFlash data[0]
u32 FldEff_PokecenterHeal(void)
{
u8 nPokemon;
struct Task *task;
FieldEffectScript_LoadFadedPal(&gSpritePalette_PokeballGlow);
FieldEffectScript_LoadFadedPal(&gSpritePalette_GeneralFieldEffect0);
nPokemon = (OW_IGNORE_EGGS_ON_HEAL <= GEN_3) ? CalculatePlayerPartyCount() : CountPartyNonEggMons();
task = &gTasks[CreateTask(Task_PokecenterHeal, 0xFF)];
task->tNumMons = nPokemon;
task->tFirstBallX = 93;
task->tFirstBallY = 36;
task->tMonitorX = 128;
task->tMonitorY = 24;
return FALSE;
}
static void Task_PokecenterHeal(u8 taskId)
{
struct Task *task = &gTasks[taskId];
sPokecenterHealEffectFuncs[task->tState](task);
}
static void PokecenterHealEffect_Init(struct Task *task)
{
task->tState++;
task->tGlowEffectSpriteId = CreateGlowingPokeballsEffect(task->tNumMons, task->tFirstBallX, task->tFirstBallY, TRUE);
task->tMonitorSpriteId = CreatePokecenterMonitorSprite(task->tMonitorX, task->tMonitorY);
}
static void PokecenterHealEffect_WaitForBallPlacement(struct Task *task)
{
if (gSprites[task->tGlowEffectSpriteId].sState >= 2)
{
gSprites[task->tMonitorSpriteId].sStartFlash++;
task->tState++;
}
}
static void PokecenterHealEffect_WaitForBallFlashing(struct Task *task)
{
if (gSprites[task->tGlowEffectSpriteId].sState > 4)
task->tState++;
}
static void PokecenterHealEffect_WaitForSoundAndEnd(struct Task *task)
{
if (gSprites[task->tGlowEffectSpriteId].sState > 6)
{
DestroySprite(&gSprites[task->tGlowEffectSpriteId]);
FieldEffectActiveListRemove(FLDEFF_POKECENTER_HEAL);
DestroyTask(FindTaskIdByFunc(Task_PokecenterHeal));
}
}
u32 FldEff_HallOfFameRecord(void)
{
u8 nPokemon;
struct Task *task;
FieldEffectScript_LoadFadedPal(&gSpritePalette_PokeballGlow);
FieldEffectScript_LoadFadedPal(&gSpritePalette_HofMonitor);
nPokemon = CalculatePlayerPartyCount();
task = &gTasks[CreateTask(Task_HallOfFameRecord, 0xFF)];
task->tNumMons = nPokemon;
task->tFirstBallX = 117;
task->tFirstBallY = 60;
return FALSE;
}
static void Task_HallOfFameRecord(u8 taskId)
{
struct Task *task;
task = &gTasks[taskId];
sHallOfFameRecordEffectFuncs[task->tState](task);
}
static void HallOfFameRecordEffect_Init(struct Task *task)
{
task->tState++;
task->tGlowEffectSpriteId = CreateGlowingPokeballsEffect(task->tNumMons, task->tFirstBallX, task->tFirstBallY, FALSE);
}
static void HallOfFameRecordEffect_WaitForBallPlacement(struct Task *task)
{
if (gSprites[task->tGlowEffectSpriteId].sState > 1)
{
CreateHofMonitorSprite(120, 25);
task->data[15]++; // unused, leftover from RSE
task->tState++;
}
}
static void HallOfFameRecordEffect_WaitForBallFlashing(struct Task *task)
{
if (gSprites[task->tGlowEffectSpriteId].sState > 4)
task->tState++;
}
static void HallOfFameRecordEffect_WaitForSoundAndEnd(struct Task *task)
{
if (gSprites[task->tGlowEffectSpriteId].sState > 6)
{
DestroySprite(&gSprites[task->tGlowEffectSpriteId]);
FieldEffectActiveListRemove(FLDEFF_HALL_OF_FAME_RECORD);
DestroyTask(FindTaskIdByFunc(Task_HallOfFameRecord));
}
}
#undef tState
#undef tNumMons
#undef tFirstBallX
#undef tFirstBallY
#undef tMonitorX
#undef tMonitorY
#undef tGlowEffectSpriteId
#undef tMonitorSpriteId
static u8 CreateGlowingPokeballsEffect(s16 numMons, s16 x, s16 y, bool16 playHealSe)
{
u8 spriteId;
struct Sprite *sprite;
spriteId = CreateInvisibleSprite(SpriteCB_PokeballGlowEffect);
sprite = &gSprites[spriteId];
sprite->x2 = x;
sprite->y2 = y;
sprite->subpriority = 0xFF;
sprite->sPlayHealSe = playHealSe;
sprite->sNumMons = numMons;
sprite->sSpriteId = spriteId;
return spriteId;
}
static void SpriteCB_PokeballGlowEffect(struct Sprite *sprite)
{
sPokeballGlowEffectFuncs[sprite->sState](sprite);
}
static const struct Coords16 sPokeballCoordOffsets[] = {
{0, 0},
{6, 0},
{0, 4},
{6, 4},
{0, 8},
{6, 8}
};
static const u8 sPokeballGlowReds[] = {16, 12, 8, 0};
static const u8 sPokeballGlowGreens[] = {16, 12, 8, 0};
static const u8 sPokeballGlowBlues[] = { 0, 0, 0, 0};
static void PokeballGlowEffect_PlaceBalls(struct Sprite *sprite)
{
u8 spriteId;
if (sprite->sTimer == 0 || (--sprite->sTimer) == 0)
{
sprite->sTimer = 25;
spriteId = CreateSpriteAtEnd(&sSpriteTemplate_PokeballGlow, sPokeballCoordOffsets[sprite->sCounter].x + sprite->x2, sPokeballCoordOffsets[sprite->sCounter].y + sprite->y2, 0xFF);
gSprites[spriteId].oam.priority = 2;
gSprites[spriteId].sGlowEffectSpriteId = sprite->sSpriteId;
sprite->sCounter++;
sprite->sNumMons--;
PlaySE(SE_BALL);
}
if (sprite->sNumMons == 0)
{
sprite->sTimer = 32;
sprite->sState++;
}
}
static void PokeballGlowEffect_TryPlaySe(struct Sprite *sprite)
{
if ((--sprite->sTimer) == 0)
{
sprite->sState++;
sprite->sTimer = 8;
sprite->sCounter = 0;
sprite->sNumFlashed = 0;
if (sprite->sPlayHealSe)
PlayFanfare(MUS_HEAL);
}
}
static void PokeballGlowEffect_FlashFirstThree(struct Sprite *sprite)
{
u8 phase;
if ((--sprite->sTimer) == 0)
{
sprite->sTimer = 8;
sprite->sCounter++;
sprite->sCounter &= 3;
if (sprite->sCounter == 0)
sprite->sNumFlashed++;
}
phase = (sprite->sCounter + 3) & 3;
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 8, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
phase = (sprite->sCounter + 2) & 3;
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 6, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
phase = (sprite->sCounter + 1) & 3;
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 2, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
phase = sprite->sCounter;
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 5, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 3, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
if (sprite->sNumFlashed >= 3)
{
sprite->sState++;
sprite->sTimer = 8;
sprite->sCounter = 0;
}
}
static void PokeballGlowEffect_FlashLast(struct Sprite *sprite)
{
u8 phase;
if ((--sprite->sTimer) == 0)
{
sprite->sTimer = 8;
sprite->sCounter++;
sprite->sCounter &= 3;
if (sprite->sCounter == 3)
{
sprite->sState++;
sprite->sTimer = 30;
}
}
phase = sprite->sCounter;
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 8, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 6, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 2, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 5, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
MultiplyInvertedPaletteRGBComponents(OBJ_PLTT_ID(IndexOfSpritePaletteTag(0x1007)) + 3, sPokeballGlowReds[phase], sPokeballGlowGreens[phase], sPokeballGlowBlues[phase]);
}
static void PokeballGlowEffect_WaitAfterFlash(struct Sprite *sprite)
{
if ((--sprite->sTimer) == 0)
sprite->sState++;
}
static void PokeballGlowEffect_Dummy(struct Sprite *sprite)
{
sprite->sState++;
}
static void PokeballGlowEffect_WaitForSound(struct Sprite *sprite)
{
if (sprite->sPlayHealSe == FALSE || IsFanfareTaskInactive())
sprite->sState++;
}
static void PokeballGlowEffect_Idle(struct Sprite *sprite)
{
}
static void SpriteCB_PokeballGlow(struct Sprite *sprite)
{
if (gSprites[sprite->sGlowEffectSpriteId].sState > 4)
FieldEffectFreeGraphicsResources(sprite);
}
#undef sState
#undef sTimer
#undef sCounter
#undef sNumFlashed
#undef sPlayHealSe
#undef sNumMons
#undef sSpriteId
#undef sGlowEffectSpriteId
static u8 CreatePokecenterMonitorSprite(s32 x, s32 y)
{
u8 spriteId;
struct Sprite *sprite;
spriteId = CreateSpriteAtEnd(&sSpriteTemplate_PokecenterMonitor, x, y, 0);
sprite = &gSprites[spriteId];
sprite->oam.priority = 2;
sprite->invisible = TRUE;
return spriteId;
}
static void SpriteCB_PokecenterMonitor(struct Sprite *sprite)
{
if (sprite->sStartFlash != FALSE)
{
sprite->sStartFlash = FALSE;
sprite->invisible = FALSE;
StartSpriteAnim(sprite, 1);
}
if (sprite->animEnded)
FieldEffectFreeGraphicsResources(sprite);
}
#undef sStartFlash
static void CreateHofMonitorSprite(s32 x, s32 y)
{
CreateSpriteAtEnd(&sSpriteTemplate_HofMonitor, x, y, 0);
}
static void SpriteCB_HallOfFameMonitor(struct Sprite *sprite)
{
if (sprite->animEnded)
FieldEffectFreeGraphicsResources(sprite);
}
static void Task_UseFly(u8 taskId);
static void FieldCallback_FlyIntoMap(void);
static void Task_FlyIntoMap(u8 taskId);
void ReturnToFieldFromFlyMapSelect(void)
{
SetMainCallback2(CB2_ReturnToField);
gFieldCallback = FieldCallback_UseFly;
}
void FieldCallback_UseFly(void)
{
FadeInFromBlack();
CreateTask(Task_UseFly, 0);
LockPlayerFieldControls();
FreezeObjectEvents();
gFieldCallback = NULL;
}
static void Task_UseFly(u8 taskId)
{
struct Task *task;
task = &gTasks[taskId];
if (task->data[0] == 0)
{
if (!IsWeatherNotFadingIn())
return;
gFieldEffectArguments[0] = GetCursorSelectionMonId();
if ((int)gFieldEffectArguments[0] >= PARTY_SIZE)
gFieldEffectArguments[0] = 0;
FieldEffectStart(FLDEFF_FLY_OUT);
task->data[0]++;
}
if (!FieldEffectActiveListContains(FLDEFF_FLY_OUT))
{
Overworld_ResetStateAfterFly();
WarpIntoMap();
SetMainCallback2(CB2_LoadMap);
gFieldCallback = FieldCallback_FlyIntoMap;
DestroyTask(taskId);
}
}
static void FieldCallback_FlyIntoMap(void)
{
Overworld_PlaySpecialMapMusic();
FadeInFromBlack();
CreateTask(Task_FlyIntoMap, 0);
gObjectEvents[gPlayerAvatar.objectEventId].invisible = TRUE;
if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
ObjectEventTurn(&gObjectEvents[gPlayerAvatar.objectEventId], DIR_WEST);
LockPlayerFieldControls();
FreezeObjectEvents();
gFieldCallback = NULL;
}
static void Task_FlyIntoMap(u8 taskId)
{
struct Task *task;
task = &gTasks[taskId];
if (task->data[0] == 0)
{
if (gPaletteFade.active)
return;
FieldEffectStart(FLDEFF_FLY_IN);
task->data[0]++;
}
if (!FieldEffectActiveListContains(FLDEFF_FLY_IN))
{
UnlockPlayerFieldControls();
UnfreezeObjectEvents();
DestroyTask(taskId);
}
}
static void Task_FallWarpFieldEffect(u8 taskId);
static bool8 FallWarpEffect_1(struct Task *task);
static bool8 FallWarpEffect_2(struct Task *task);
static bool8 FallWarpEffect_3(struct Task *task);
static bool8 FallWarpEffect_4(struct Task *task);
static bool8 FallWarpEffect_5(struct Task *task);
static bool8 FallWarpEffect_6(struct Task *task);
static bool8 FallWarpEffect_7(struct Task *task);
static bool8 (*const sFallWarpEffectCBPtrs[])(struct Task *task) = {
FallWarpEffect_1,
FallWarpEffect_2,
FallWarpEffect_3,
FallWarpEffect_4,
FallWarpEffect_5,
FallWarpEffect_6,
FallWarpEffect_7
};
void FieldCB_FallWarpExit(void)
{
Overworld_PlaySpecialMapMusic();
WarpFadeInScreen();
QuestLog_DrawPreviouslyOnQuestHeaderIfInPlaybackMode();
LockPlayerFieldControls();
FreezeObjectEvents();
CreateTask(Task_FallWarpFieldEffect, 0);
gFieldCallback = NULL;
}
static void Task_FallWarpFieldEffect(u8 taskId)
{
struct Task *task = &gTasks[taskId];
while (sFallWarpEffectCBPtrs[task->data[0]](task))
;
}
static bool8 FallWarpEffect_1(struct Task *task)
{
struct ObjectEvent * playerObject;
struct Sprite *playerSprite;
playerObject = &gObjectEvents[gPlayerAvatar.objectEventId];
playerSprite = &gSprites[gPlayerAvatar.spriteId];
CameraObjectReset2();
gObjectEvents[gPlayerAvatar.objectEventId].invisible = TRUE;
gPlayerAvatar.preventStep = TRUE;
ObjectEventSetHeldMovement(playerObject, GetFaceDirectionMovementAction(GetPlayerFacingDirection()));
task->data[4] = playerSprite->subspriteMode;
playerObject->fixedPriority = TRUE;
playerSprite->oam.priority = 1;
playerSprite->subspriteMode = SUBSPRITES_IGNORE_PRIORITY;
task->data[0]++;
return TRUE;
}
static bool8 FallWarpEffect_2(struct Task *task)
{
if (IsWeatherNotFadingIn())
{
task->data[0]++;
}
return FALSE;
}
static bool8 FallWarpEffect_3(struct Task *task)
{
struct Sprite *sprite;
s16 centerToCornerVecY;
sprite = &gSprites[gPlayerAvatar.spriteId];
centerToCornerVecY = -(sprite->centerToCornerVecY << 1);
sprite->y2 = -(sprite->y + sprite->centerToCornerVecY + gSpriteCoordOffsetY + centerToCornerVecY);
task->data[1] = 1;
task->data[2] = 0;
gObjectEvents[gPlayerAvatar.objectEventId].invisible = FALSE;
PlaySE(SE_FALL);
task->data[0]++;
return FALSE;
}
static bool8 FallWarpEffect_4(struct Task *task)
{
struct ObjectEvent * objectEvent;
struct Sprite *sprite;
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->y2 += task->data[1];
if (task->data[1] < 8)
{
task->data[2] += task->data[1];
if (task->data[2] & 0xf)
{
task->data[1] <<= 1;
}
}
if (task->data[3] == 0 && sprite->y2 >= -16)
{
task->data[3]++;
objectEvent->fixedPriority = FALSE;
sprite->subspriteMode = task->data[4];
objectEvent->triggerGroundEffectsOnMove = TRUE;
}
if (sprite->y2 >= 0)
{
PlaySE(SE_M_STRENGTH);
objectEvent->triggerGroundEffectsOnStop = TRUE;
objectEvent->landingJump = TRUE;
sprite->y2 = 0;
task->data[0]++;
}
return FALSE;
}
static bool8 FallWarpEffect_5(struct Task *task)
{
task->data[0]++;
task->data[1] = 4;
task->data[2] = 0;
SetCameraPanningCallback(NULL);
return TRUE;
}
static bool8 FallWarpEffect_6(struct Task *task)
{
SetCameraPanning(0, task->data[1]);
task->data[1] = -task->data[1];
task->data[2]++;
if ((task->data[2] & 3) == 0)
{
task->data[1] >>= 1;
}
if (task->data[1] == 0)
{
task->data[0]++;
}
return FALSE;
}
static bool8 FallWarpEffect_7(struct Task *task)
{
s16 x, y;
gPlayerAvatar.preventStep = FALSE;
UnlockPlayerFieldControls();
CameraObjectReset1();
UnfreezeObjectEvents();
InstallCameraPanAheadCallback();
PlayerGetDestCoords(&x, &y);
if (MetatileBehavior_IsSurfableInSeafoamIslands(MapGridGetMetatileBehaviorAt(x, y)) == TRUE)
{
VarSet(VAR_TEMP_1, 1);
SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_SURFING);
SetHelpContext(HELPCONTEXT_SURFING);
}
DestroyTask(FindTaskIdByFunc(Task_FallWarpFieldEffect));
return FALSE;
}
static void HideFollowerForFieldEffect(void)
{
struct ObjectEvent *followerObj = GetFollowerObject();
if (!followerObj || followerObj->invisible)
return;
ClearObjectEventMovement(followerObj, &gSprites[followerObj->spriteId]);
ObjectEventSetHeldMovement(followerObj, MOVEMENT_ACTION_ENTER_POKEBALL);
}
static void Task_EscalatorWarpFieldEffect(u8 taskId);
static bool8 EscalatorWarpEffect_1(struct Task *task);
static bool8 EscalatorWarpEffect_2(struct Task *task);
static bool8 EscalatorWarpEffect_3(struct Task *task);
static bool8 EscalatorWarpEffect_4(struct Task *task);
static bool8 EscalatorWarpEffect_5(struct Task *task);
static bool8 EscalatorWarpEffect_6(struct Task *task);
static void Escalator_AnimatePlayerGoingDown(struct Task *task);
static void Escalator_AnimatePlayerGoingUp(struct Task *task);
static void Escalator_BeginFadeOutToNewMap(void);
static void Escalator_TransitionToWarpInEffect(void);
static void FieldCB_EscalatorWarpIn(void);
static void Task_EscalatorWarpInFieldEffect(u8 taskId);
static bool8 EscalatorWarpInEffect_1(struct Task *task);
static bool8 EscalatorWarpInEffect_2(struct Task *task);
static bool8 EscalatorWarpInEffect_3(struct Task *task);
static bool8 EscalatorWarpInEffect_4(struct Task *task);
static bool8 EscalatorWarpInEffect_5(struct Task *task);
static bool8 EscalatorWarpInEffect_6(struct Task *task);
static bool8 EscalatorWarpInEffect_7(struct Task *task);
static bool8 (*const sEscalatorWarpFieldEffectFuncs[])(struct Task *task) = {
EscalatorWarpEffect_1,
EscalatorWarpEffect_2,
EscalatorWarpEffect_3,
EscalatorWarpEffect_4,
EscalatorWarpEffect_5,
EscalatorWarpEffect_6
};
void StartEscalatorWarp(u8 metatileBehavior, u8 priority)
{
u8 taskId = CreateTask(Task_EscalatorWarpFieldEffect, priority);
gTasks[taskId].data[1] = 0;
if (metatileBehavior == MB_UP_ESCALATOR)
gTasks[taskId].data[1] = 1;
}
static void Task_EscalatorWarpFieldEffect(u8 taskId)
{
struct Task *task = &gTasks[taskId];
while (sEscalatorWarpFieldEffectFuncs[task->data[0]](task))
;
}
static bool8 EscalatorWarpEffect_1(struct Task *task)
{
FreezeObjectEvents();
CameraObjectReset2();
StartEscalator(task->data[1]);
HideFollowerForFieldEffect(); // Hide follower before warping
QuestLog_OnEscalatorWarp(QL_ESCALATOR_OUT);
task->data[0]++;
return FALSE;
}
static bool8 EscalatorWarpEffect_2(struct Task *task)
{
struct ObjectEvent * objectEvent;
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (!ObjectEventIsMovementOverridden(objectEvent) || ObjectEventClearHeldMovementIfFinished(objectEvent))
{
ObjectEventSetHeldMovement(objectEvent, GetFaceDirectionMovementAction(GetPlayerFacingDirection()));
task->data[0]++;
task->data[2] = 0;
task->data[3] = 0;
if ((u8)task->data[1] == 0)
{
task->data[0] = 4;
}
PlaySE(SE_ESCALATOR);
}
return FALSE;
}
static bool8 EscalatorWarpEffect_3(struct Task *task)
{
Escalator_AnimatePlayerGoingDown(task);
if (task->data[2] > 3)
{
Escalator_BeginFadeOutToNewMap();
task->data[0]++;
}
return FALSE;
}
static bool8 EscalatorWarpEffect_4(struct Task *task)
{
Escalator_AnimatePlayerGoingDown(task);
Escalator_TransitionToWarpInEffect();
return FALSE;
}
static bool8 EscalatorWarpEffect_5(struct Task *task)
{
Escalator_AnimatePlayerGoingUp(task);
if (task->data[2] > 3)
{
Escalator_BeginFadeOutToNewMap();
task->data[0]++;
}
return FALSE;
}
static bool8 EscalatorWarpEffect_6(struct Task *task)
{
Escalator_AnimatePlayerGoingUp(task);
Escalator_TransitionToWarpInEffect();
return FALSE;
}
static void Escalator_AnimatePlayerGoingDown(struct Task *task)
{
struct Sprite *sprite;
sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->x2 = Cos(0x84, task->data[2]);
sprite->y2 = Sin(0x94, task->data[2]);
task->data[3]++;
if (task->data[3] & 1)
{
task->data[2]++;
}
}
static void Escalator_AnimatePlayerGoingUp(struct Task *task)
{
struct Sprite *sprite;
sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->x2 = Cos(0x7c, task->data[2]);
sprite->y2 = Sin(0x76, task->data[2]);
task->data[3]++;
if (task->data[3] & 1)
{
task->data[2]++;
}
}
static void Escalator_BeginFadeOutToNewMap(void)
{
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
}
static void Escalator_TransitionToWarpInEffect(void)
{
if (!gPaletteFade.active && BGMusicStopped() == TRUE)
{
StopEscalator();
WarpIntoMap();
gFieldCallback = FieldCB_EscalatorWarpIn;
SetMainCallback2(CB2_LoadMap);
DestroyTask(FindTaskIdByFunc(Task_EscalatorWarpFieldEffect));
}
}
static bool8 (*const sEscalatorWarpInFieldEffectFuncs[])(struct Task *task) = {
EscalatorWarpInEffect_1,
EscalatorWarpInEffect_2,
EscalatorWarpInEffect_3,
EscalatorWarpInEffect_4,
EscalatorWarpInEffect_5,
EscalatorWarpInEffect_6,
EscalatorWarpInEffect_7
};
static void FieldCB_EscalatorWarpIn(void)
{
Overworld_PlaySpecialMapMusic();
WarpFadeInScreen();
QuestLog_DrawPreviouslyOnQuestHeaderIfInPlaybackMode();
LockPlayerFieldControls();
FreezeObjectEvents();
CreateTask(Task_EscalatorWarpInFieldEffect, 0);
gFieldCallback = NULL;
}
static void Task_EscalatorWarpInFieldEffect(u8 taskId)
{
struct Task *task = &gTasks[taskId];
while (sEscalatorWarpInFieldEffectFuncs[task->data[0]](task))
;
}
static bool8 EscalatorWarpInEffect_1(struct Task *task)
{
struct ObjectEvent * objectEvent;
s16 x;
s16 y;
u8 behavior;
CameraObjectReset2();
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
ObjectEventSetHeldMovement(objectEvent, GetFaceDirectionMovementAction(DIR_EAST));
PlayerGetDestCoords(&x, &y);
behavior = MapGridGetMetatileBehaviorAt(x, y);
task->data[0]++;
task->data[1] = 16;
if (behavior == MB_DOWN_ESCALATOR)
{
behavior = 1;
task->data[0] = 3;
} else
{
behavior = 0;
}
StartEscalator(behavior);
return TRUE;
}
static bool8 EscalatorWarpInEffect_2(struct Task *task)
{
struct Sprite *sprite;
sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->x2 = Cos(0x84, task->data[1]);
sprite->y2 = Sin(0x94, task->data[1]);
task->data[0]++;
return FALSE;
}
static bool8 EscalatorWarpInEffect_3(struct Task *task)
{
struct Sprite *sprite;
sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->x2 = Cos(0x84, task->data[1]);
sprite->y2 = Sin(0x94, task->data[1]);
task->data[2]++;
if (task->data[2] & 1)
{
task->data[1]--;
}
if (task->data[1] == 0)
{
sprite->x2 = 0;
sprite->y2 = 0;
task->data[0] = 5;
}
return FALSE;
}
static bool8 EscalatorWarpInEffect_4(struct Task *task)
{
struct Sprite *sprite;
sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->x2 = Cos(0x7c, task->data[1]);
sprite->y2 = Sin(0x76, task->data[1]);
task->data[0]++;
return FALSE;
}
static bool8 EscalatorWarpInEffect_5(struct Task *task)
{
struct Sprite *sprite;
sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->x2 = Cos(0x7c, task->data[1]);
sprite->y2 = Sin(0x76, task->data[1]);
task->data[2]++;
if (task->data[2] & 1)
{
task->data[1]--;
}
if (task->data[1] == 0)
{
sprite->x2 = 0;
sprite->y2 = 0;
task->data[0]++;
}
return FALSE;
}
static bool8 EscalatorWarpInEffect_6(struct Task *task)
{
if (IsEscalatorMoving())
{
return FALSE;
}
StopEscalator();
task->data[0]++;
return TRUE;
}
static bool8 EscalatorWarpInEffect_7(struct Task *task)
{
struct ObjectEvent * objectEvent;
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (ObjectEventClearHeldMovementIfFinished(objectEvent))
{
CameraObjectReset1();
UnlockPlayerFieldControls();
UnfreezeObjectEvents();
ObjectEventSetHeldMovement(objectEvent, GetWalkNormalMovementAction(DIR_EAST));
DestroyTask(FindTaskIdByFunc(Task_EscalatorWarpInFieldEffect));
QuestLog_OnEscalatorWarp(QL_ESCALATOR_IN);
}
return FALSE;
}
static void Task_UseWaterfall(u8 taskId);
static bool8 waterfall_0_setup(struct Task *task, struct ObjectEvent * playerObj);
static bool8 waterfall_1_do_anim_probably(struct Task *task, struct ObjectEvent * playerObj);
static bool8 waterfall_2_wait_anim_finish_probably(struct Task *task, struct ObjectEvent * playerObj);
static bool8 waterfall_3_move_player_probably(struct Task *task, struct ObjectEvent * playerObj);
static bool8 waterfall_4_wait_player_move_probably(struct Task *task, struct ObjectEvent * playerObj);
static bool8 (*const sUseWaterfallFieldEffectFuncs[])(struct Task *task, struct ObjectEvent * playerObj) = {
waterfall_0_setup,
waterfall_1_do_anim_probably,
waterfall_2_wait_anim_finish_probably,
waterfall_3_move_player_probably,
waterfall_4_wait_player_move_probably
};
u32 FldEff_UseWaterfall(void)
{
u8 taskId = CreateTask(Task_UseWaterfall, 0xFF);
gTasks[taskId].data[1] = gFieldEffectArguments[0];
Task_UseWaterfall(taskId);
return 0;
}
static void Task_UseWaterfall(u8 taskId)
{
while (sUseWaterfallFieldEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId], &gObjectEvents[gPlayerAvatar.objectEventId]))
;
}
static bool8 waterfall_0_setup(struct Task *task, struct ObjectEvent * playerObj)
{
LockPlayerFieldControls();
gPlayerAvatar.preventStep = TRUE;
task->data[0]++;
return FALSE;
}
static bool8 waterfall_1_do_anim_probably(struct Task *task, struct ObjectEvent * playerObj)
{
LockPlayerFieldControls();
if (!ObjectEventIsMovementOverridden(playerObj))
{
ObjectEventClearHeldMovementIfFinished(playerObj);
gFieldEffectArguments[0] = task->data[1];
FieldEffectStart(FLDEFF_FIELD_MOVE_SHOW_MON_INIT);
task->data[0]++;
}
return FALSE;
}
static bool8 waterfall_2_wait_anim_finish_probably(struct Task *task, struct ObjectEvent * playerObj)
{
if (FieldEffectActiveListContains(FLDEFF_FIELD_MOVE_SHOW_MON))
return FALSE;
task->data[0]++;
return TRUE;
}
static bool8 waterfall_3_move_player_probably(struct Task *task, struct ObjectEvent * playerObj)
{
ObjectEventSetHeldMovement(playerObj, GetWalkSlowerMovementAction(DIR_NORTH));
task->data[0]++;
return FALSE;
}
static bool8 waterfall_4_wait_player_move_probably(struct Task *task, struct ObjectEvent * playerObj)
{
if (!ObjectEventClearHeldMovementIfFinished(playerObj))
return FALSE;
if (MetatileBehavior_IsWaterfall(playerObj->currentMetatileBehavior))
{
task->data[0] = 3;
return TRUE;
}
UnlockPlayerFieldControls();
gPlayerAvatar.preventStep = FALSE;
DestroyTask(FindTaskIdByFunc(Task_UseWaterfall));
FieldEffectActiveListRemove(FLDEFF_USE_WATERFALL);
return FALSE;
}
static void Task_UseDive(u8 taskId);
static bool8 DiveFieldEffect_Init(struct Task *task);
static bool8 DiveFieldEffect_ShowMon(struct Task *task);
static bool8 DiveFieldEffect_TryWarp(struct Task *task);
static bool8 (*const sDiveFieldEffectFuncs[])(struct Task *task) =
{
DiveFieldEffect_Init,
DiveFieldEffect_ShowMon,
DiveFieldEffect_TryWarp
};
u32 FldEff_UseDive(void)
{
u8 taskId = CreateTask(Task_UseDive, 0xFF);
gTasks[taskId].data[15] = gFieldEffectArguments[0]; // party index of pokemon with dive
gTasks[taskId].data[14] = gFieldEffectArguments[1]; // unused
Task_UseDive(taskId);
return 0;
}
static void Task_UseDive(u8 taskId)
{
while (sDiveFieldEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId]));
}
static bool8 DiveFieldEffect_Init(struct Task *task)
{
gPlayerAvatar.preventStep = TRUE;
task->data[0]++;
return FALSE;
}
static bool8 DiveFieldEffect_ShowMon(struct Task *task)
{
LockPlayerFieldControls();
gFieldEffectArguments[0] = task->data[15];
FieldEffectStart(FLDEFF_FIELD_MOVE_SHOW_MON_INIT);
task->data[0]++;
return FALSE;
}
static bool8 DiveFieldEffect_TryWarp(struct Task *task)
{
struct MapPosition pos;
PlayerGetDestCoords(&pos.x, &pos.y);
if (!FieldEffectActiveListContains(FLDEFF_FIELD_MOVE_SHOW_MON))
{
dive_warp(&pos, gObjectEvents[gPlayerAvatar.objectEventId].currentMetatileBehavior);
DestroyTask(FindTaskIdByFunc(Task_UseDive));
FieldEffectActiveListRemove(FLDEFF_USE_DIVE);
}
return FALSE;
}
static void Task_LavaridgeGymB1FWarp(u8 taskId);
static bool8 LavaridgeGymB1FWarpEffect_1(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGymB1FWarpEffect_2(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGymB1FWarpEffect_3(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGymB1FWarpEffect_4(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGymB1FWarpEffect_5(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGymB1FWarpEffect_6(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static void FieldCB_LavaridgeGymB1FWarpExit(void);
static void Task_LavaridgeGymB1FWarpExit(u8 taskId);
static bool8 LavaridgeGymB1FWarpExitEffect_1(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGymB1FWarpExitEffect_2(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGymB1FWarpExitEffect_3(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGymB1FWarpExitEffect_4(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 (*const sLavaridgeGymB1FWarpEffectFuncs[])(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite) = {
LavaridgeGymB1FWarpEffect_1,
LavaridgeGymB1FWarpEffect_2,
LavaridgeGymB1FWarpEffect_3,
LavaridgeGymB1FWarpEffect_4,
LavaridgeGymB1FWarpEffect_5,
LavaridgeGymB1FWarpEffect_6
};
void StartLavaridgeGymB1FWarp(u8 priority)
{
CreateTask(Task_LavaridgeGymB1FWarp, priority);
}
static void Task_LavaridgeGymB1FWarp(u8 taskId)
{
while (sLavaridgeGymB1FWarpEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId], &gObjectEvents[gPlayerAvatar.objectEventId], &gSprites[gPlayerAvatar.spriteId]));
}
static bool8 LavaridgeGymB1FWarpEffect_1(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
FreezeObjectEvents();
CameraObjectReset2();
SetCameraPanningCallback(NULL);
gPlayerAvatar.preventStep = TRUE;
objectEvent->fixedPriority = TRUE;
task->data[1] = 1;
task->data[0]++;
return TRUE;
}
static bool8 LavaridgeGymB1FWarpEffect_2(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
SetCameraPanning(0, task->data[1]);
task->data[1] = -task->data[1];
task->data[2]++;
if (task->data[2] > 7)
{
task->data[2] = 0;
task->data[0]++;
}
return FALSE;
}
static bool8 LavaridgeGymB1FWarpEffect_3(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
sprite->y2 = 0;
task->data[3] = 1;
gFieldEffectArguments[0] = objectEvent->currentCoords.x;
gFieldEffectArguments[1] = objectEvent->currentCoords.y;
gFieldEffectArguments[2] = sprite->subpriority - 1;
gFieldEffectArguments[3] = sprite->oam.priority;
FieldEffectStart(FLDEFF_LAVARIDGE_GYM_WARP);
PlaySE(SE_M_EXPLOSION);
task->data[0]++;
return TRUE;
}
static bool8 LavaridgeGymB1FWarpEffect_4(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
s16 centerToCornerVecY;
SetCameraPanning(0, task->data[1]);
if (task->data[1] = -task->data[1], ++task->data[2] <= 17)
{
if (!(task->data[2] & 1) && (task->data[1] <= 3))
{
task->data[1] <<= 1;
}
} else if (!(task->data[2] & 4) && (task->data[1] > 0))
{
task->data[1] >>= 1;
}
if (task->data[2] > 6)
{
centerToCornerVecY = -(sprite->centerToCornerVecY << 1);
if (sprite->y2 > -(sprite->y + sprite->centerToCornerVecY + gSpriteCoordOffsetY + centerToCornerVecY))
{
sprite->y2 -= task->data[3];
if (task->data[3] <= 7)
{
task->data[3]++;
}
} else
{
task->data[4] = 1;
}
}
if (task->data[5] == 0 && sprite->y2 < -0x10)
{
task->data[5]++;
objectEvent->fixedPriority = TRUE;
sprite->oam.priority = 1;
sprite->subspriteMode = SUBSPRITES_IGNORE_PRIORITY;
}
if (task->data[1] == 0 && task->data[4] != 0)
{
task->data[0]++;
}
return FALSE;
}
static bool8 LavaridgeGymB1FWarpEffect_5(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
task->data[0]++;
return FALSE;
}
static bool8 LavaridgeGymB1FWarpEffect_6(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
if (!gPaletteFade.active && BGMusicStopped() == TRUE)
{
WarpIntoMap();
gFieldCallback = FieldCB_LavaridgeGymB1FWarpExit;
SetMainCallback2(CB2_LoadMap);
DestroyTask(FindTaskIdByFunc(Task_LavaridgeGymB1FWarp));
}
return FALSE;
}
static bool8 (*const sLavaridgeGymB1FWarpExitEffectFuncs[])(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite) = {
LavaridgeGymB1FWarpExitEffect_1,
LavaridgeGymB1FWarpExitEffect_2,
LavaridgeGymB1FWarpExitEffect_3,
LavaridgeGymB1FWarpExitEffect_4
};
static void FieldCB_LavaridgeGymB1FWarpExit(void)
{
Overworld_PlaySpecialMapMusic();
WarpFadeInScreen();
QuestLog_DrawPreviouslyOnQuestHeaderIfInPlaybackMode();
LockPlayerFieldControls();
gFieldCallback = NULL;
CreateTask(Task_LavaridgeGymB1FWarpExit, 0);
}
static void Task_LavaridgeGymB1FWarpExit(u8 taskId)
{
while (sLavaridgeGymB1FWarpExitEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId], &gObjectEvents[gPlayerAvatar.objectEventId], &gSprites[gPlayerAvatar.spriteId]));
}
static bool8 LavaridgeGymB1FWarpExitEffect_1(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
CameraObjectReset2();
FreezeObjectEvents();
gPlayerAvatar.preventStep = TRUE;
objectEvent->invisible = TRUE;
task->data[0]++;
return FALSE;
}
static bool8 LavaridgeGymB1FWarpExitEffect_2(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
if (IsWeatherNotFadingIn())
{
gFieldEffectArguments[0] = objectEvent->currentCoords.x;
gFieldEffectArguments[1] = objectEvent->currentCoords.y;
gFieldEffectArguments[2] = sprite->subpriority - 1;
gFieldEffectArguments[3] = sprite->oam.priority;
task->data[1] = FieldEffectStart(FLDEFF_POP_OUT_OF_ASH);
task->data[0]++;
}
return FALSE;
}
static bool8 LavaridgeGymB1FWarpExitEffect_3(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
sprite = &gSprites[task->data[1]];
if (sprite->animCmdIndex > 1)
{
task->data[0]++;
objectEvent->invisible = FALSE;
CameraObjectReset1();
PlaySE(SE_M_DIG);
ObjectEventSetHeldMovement(objectEvent, GetJumpMovementAction(DIR_EAST));
}
return FALSE;
}
static bool8 LavaridgeGymB1FWarpExitEffect_4(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
if (ObjectEventClearHeldMovementIfFinished(objectEvent))
{
gPlayerAvatar.preventStep = FALSE;
UnlockPlayerFieldControls();
UnfreezeObjectEvents();
DestroyTask(FindTaskIdByFunc(Task_LavaridgeGymB1FWarpExit));
}
return FALSE;
}
static void Task_LavaridgeGym1FWarp(u8 taskId);
static bool8 LavaridgeGym1FWarpEffect_1(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGym1FWarpEffect_2(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGym1FWarpEffect_3(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGym1FWarpEffect_4(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 LavaridgeGym1FWarpEffect_5(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite);
static bool8 (*const sLavaridgeGym1FWarpEffectFuncs[])(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite) = {
LavaridgeGym1FWarpEffect_1,
LavaridgeGym1FWarpEffect_2,
LavaridgeGym1FWarpEffect_3,
LavaridgeGym1FWarpEffect_4,
LavaridgeGym1FWarpEffect_5
};
// For the ash puff effect when warping off the B1F ash tiles
u32 FldEff_LavaridgeGymWarp(void)
{
u8 spriteId;
FieldEffectScript_LoadFadedPal(&gSpritePalette_Ash);
SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 8);
spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_AshLaunch, gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]);
gSprites[spriteId].oam.priority = gFieldEffectArguments[3];
gSprites[spriteId].coordOffsetEnabled = TRUE;
return spriteId;
}
void SpriteCB_AshLaunch(struct Sprite *sprite)
{
if (sprite->animEnded)
{
FieldEffectStop(sprite, FLDEFF_LAVARIDGE_GYM_WARP);
}
}
void StartLavaridgeGym1FWarp(u8 priority)
{
CreateTask(Task_LavaridgeGym1FWarp, priority);
}
static void Task_LavaridgeGym1FWarp(u8 taskId)
{
while(sLavaridgeGym1FWarpEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId], &gObjectEvents[gPlayerAvatar.objectEventId], &gSprites[gPlayerAvatar.spriteId]));
}
static bool8 LavaridgeGym1FWarpEffect_1(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
FreezeObjectEvents();
CameraObjectReset2();
gPlayerAvatar.preventStep = TRUE;
objectEvent->fixedPriority = TRUE;
task->data[0]++;
return FALSE;
}
static bool8 LavaridgeGym1FWarpEffect_2(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
if (ObjectEventClearHeldMovementIfFinished(objectEvent))
{
if (task->data[1] > 3)
{
gFieldEffectArguments[0] = objectEvent->currentCoords.x;
gFieldEffectArguments[1] = objectEvent->currentCoords.y;
gFieldEffectArguments[2] = sprite->subpriority - 1;
gFieldEffectArguments[3] = sprite->oam.priority;
task->data[1] = FieldEffectStart(FLDEFF_POP_OUT_OF_ASH);
task->data[0]++;
} else
{
task->data[1]++;
ObjectEventSetHeldMovement(objectEvent, GetWalkInPlaceFasterMovementAction(objectEvent->facingDirection));
PlaySE(SE_LAVARIDGE_FALL_WARP);
}
}
return FALSE;
}
static bool8 LavaridgeGym1FWarpEffect_3(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
if (gSprites[task->data[1]].animCmdIndex == 2)
{
objectEvent->invisible = TRUE;
task->data[0]++;
}
return FALSE;
}
static bool8 LavaridgeGym1FWarpEffect_4(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
if (!FieldEffectActiveListContains(FLDEFF_POP_OUT_OF_ASH))
{
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
task->data[0]++;
}
return FALSE;
}
static bool8 LavaridgeGym1FWarpEffect_5(struct Task *task, struct ObjectEvent * objectEvent, struct Sprite *sprite)
{
if (!gPaletteFade.active && BGMusicStopped() == TRUE)
{
WarpIntoMap();
gFieldCallback = FieldCB_FallWarpExit;
SetMainCallback2(CB2_LoadMap);
DestroyTask(FindTaskIdByFunc(Task_LavaridgeGym1FWarp));
}
return FALSE;
}
u32 FldEff_PopOutOfAsh(void)
{
u8 spriteId;
FieldEffectScript_LoadFadedPal(&gSpritePalette_Ash);
SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 8);
spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_AshPuff, gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]);
gSprites[spriteId].oam.priority = gFieldEffectArguments[3];
gSprites[spriteId].coordOffsetEnabled = TRUE;
return spriteId;
}
void SpriteCB_PopOutOfAsh(struct Sprite *sprite)
{
if (sprite->animEnded)
{
FieldEffectStop(sprite, FLDEFF_POP_OUT_OF_ASH);
}
}
// Task data for Task_EscapeRopeWarpOut
#define tState data[0]
#define tSpinDelay data[1]
#define tNumTurns data[2]
#define tTimer data[3]
#define tOffscreen data[4]
#define tMovingState data[5]
#define tOffsetY data[6]
#define tDirection data[15]
static void Task_EscapeRopeWarpOut(u8 taskId);
static void EscapeRopeWarpOutEffect_Init(struct Task *task);
static void EscapeRopeWarpOutEffect_Spin(struct Task *task);
static u8 SpinObjectEvent(struct ObjectEvent *playerObj, s16 *timer, s16 *numTurns);
static bool32 WarpOutObjectEventUpwards(struct ObjectEvent *playerObj, s16 *movingState, s16 *offsetY);
static void FieldCallback_EscapeRopeExit(void);
static void Task_EscapeRopeWarpIn(u8 taskId);
static void EscapeRopeWarpInEffect_Init(struct Task *task);
static void EscapeRopeWarpInEffect_Spin(struct Task *task);
static void (*const sEscapeRopeWarpOutEffectFuncs[])(struct Task *task) =
{
EscapeRopeWarpOutEffect_Init,
EscapeRopeWarpOutEffect_Spin
};
void StartEscapeRopeFieldEffect(void)
{
LockPlayerFieldControls();
FreezeObjectEvents();
HideFollowerForFieldEffect(); // hide follower before warping
CreateTask(Task_EscapeRopeWarpOut, 80);
}
static void Task_EscapeRopeWarpOut(u8 taskId)
{
sEscapeRopeWarpOutEffectFuncs[gTasks[taskId].tState](&gTasks[taskId]);
}
static void EscapeRopeWarpOutEffect_Init(struct Task *task)
{
task->tState++;
task->data[13] = 64; // unused
task->data[14] = GetPlayerFacingDirection(); // unused
task->tDirection = DIR_NONE;
}
static void EscapeRopeWarpOutEffect_Spin(struct Task *task)
{
struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
s16 *data = task->data;
SpinObjectEvent(playerObj, &task->tSpinDelay, &task->tNumTurns);
if (tTimer < 60)
{
tTimer++;
if (tTimer == 20)
PlaySE(SE_WARP_IN);
}
else if (tOffscreen == FALSE && !WarpOutObjectEventUpwards(playerObj, &task->tMovingState, &task->tOffsetY))
{
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
tOffscreen = TRUE;
}
if (tOffscreen == TRUE && !gPaletteFade.active && BGMusicStopped() == TRUE)
{
SetObjectEventDirection(playerObj, task->tDirection); // always DIR_NONE
SetWarpDestinationToEscapeWarp();
WarpIntoMap();
gFieldCallback = FieldCallback_EscapeRopeExit;
SetMainCallback2(CB2_LoadMap);
DestroyTask(FindTaskIdByFunc(Task_EscapeRopeWarpOut));
}
}
static const u8 sSpinDirections[] =
{
[DIR_NONE] = DIR_SOUTH,
[DIR_SOUTH] = DIR_WEST,
[DIR_NORTH] = DIR_EAST,
[DIR_WEST] = DIR_NORTH,
[DIR_EAST] = DIR_SOUTH,
};
static u8 SpinObjectEvent(struct ObjectEvent *playerObj, s16 *spinDelay, s16 *numTurns)
{
if (!ObjectEventIsMovementOverridden(playerObj) || ObjectEventClearHeldMovementIfFinished(playerObj))
{
if (*spinDelay != 0 && --(*spinDelay) != 0)
return playerObj->facingDirection;
ObjectEventSetHeldMovement(playerObj, GetFaceDirectionMovementAction(sSpinDirections[playerObj->facingDirection]));
if (*numTurns < 12)
(*numTurns)++;
*spinDelay = 12 >> (*numTurns);
return sSpinDirections[playerObj->facingDirection];
}
return playerObj->facingDirection;
}
static bool32 WarpOutObjectEventUpwards(struct ObjectEvent *playerObj, s16 *movingState, s16 *offsetY)
{
struct Sprite *sprite = &gSprites[playerObj->spriteId];
switch (*movingState)
{
case 0:
CameraObjectReset2();
(*movingState)++;
// fallthrough
case 1:
sprite->y2 -= 8;
(*offsetY) -= 8;
if (*offsetY <= -16)
{
playerObj->fixedPriority = TRUE;
sprite->oam.priority = 1;
sprite->subpriority = 0;
sprite->subspriteMode = SUBSPRITES_OFF;
(*movingState)++;
}
break;
case 2:
sprite->y2 -= 8;
(*offsetY) -= 8;
if (*offsetY <= -88)
{
(*movingState)++;
return FALSE;
}
break;
case 3:
return FALSE;
}
return TRUE;
}
#undef tState
#undef tSpinDelay
#undef tNumTurns
#undef tTimer
#undef tOffscreen
#undef tMovingState
#undef tOffsetY
#undef tDirection
// Task data for Task_EscapeRopeWarpIn
#define tState data[0]
#define tMovingState data[1]
#define tOffsetY data[2]
#define tPriority data[3]
#define tSubpriority data[4]
#define tSubspriteMode data[5]
#define tTimer data[6]
#define tSpinEnded data[7]
#define tCurrentDir data[8]
#define tSpinDelay data[9]
#define tNumTurns data[10]
#define tOriginalDir data[15]
static void (*const sEscapeRopeWarpInEffectFuncs[])(struct Task *task) =
{
EscapeRopeWarpInEffect_Init,
EscapeRopeWarpInEffect_Spin
};
static bool32 WarpInObjectEventDownwards(struct ObjectEvent *playerObj, s16 *movingState, s16 *offsetY, s16 *priority, s16 *subpriority, s16 *subspriteMode)
{
struct Sprite *sprite = &gSprites[playerObj->spriteId];
switch (*movingState)
{
case 0:
CameraObjectReset2();
*offsetY = -88;
sprite->y2 -= 88;
*priority = sprite->oam.priority;
*subpriority = sprite->subpriority;
*subspriteMode = sprite->subspriteMode;
playerObj->fixedPriority = TRUE;
sprite->oam.priority = 1;
sprite->subpriority = 0;
sprite->subspriteMode = SUBSPRITES_OFF;
(*movingState)++;
// fallthrough
case 1:
sprite->y2 += 4;
(*offsetY) += 4;
if (*offsetY >= -16)
{
sprite->oam.priority = *priority;
sprite->subpriority = *subpriority;
sprite->subspriteMode = *subspriteMode;
(*movingState)++;
}
break;
case 2:
sprite->y2 += 4;
(*offsetY) += 4;
if (*offsetY >= 0)
{
PlaySE(SE_CLICK);
CameraObjectReset1();
(*movingState)++;
return FALSE;
}
break;
case 3:
return FALSE;
}
return TRUE;
}
static void FieldCallback_EscapeRopeExit(void)
{
Overworld_PlaySpecialMapMusic();
WarpFadeInScreen();
QuestLog_DrawPreviouslyOnQuestHeaderIfInPlaybackMode();
LockPlayerFieldControls();
FreezeObjectEvents();
gFieldCallback = NULL;
gObjectEvents[gPlayerAvatar.objectEventId].invisible = TRUE;
CreateTask(Task_EscapeRopeWarpIn, 0);
}
static void Task_EscapeRopeWarpIn(u8 taskId)
{
sEscapeRopeWarpInEffectFuncs[gTasks[taskId].tState](&gTasks[taskId]);
}
static void EscapeRopeWarpInEffect_Init(struct Task *task)
{
if (IsWeatherNotFadingIn())
{
PlaySE(SE_WARP_OUT);
task->tOriginalDir = GetPlayerFacingDirection();
task->tState++;
}
}
static void EscapeRopeWarpInEffect_Spin(struct Task *task)
{
s16 *data = task->data;
struct ObjectEvent *playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
bool32 moving = WarpInObjectEventDownwards(playerObj, &tMovingState, &tOffsetY, &tPriority, &tSubpriority, &tSubspriteMode);
playerObj->invisible = FALSE;
if (tTimer < 8)
tTimer++;
else if (tSpinEnded == FALSE)
{
tTimer++;
tCurrentDir = SpinObjectEvent(playerObj, &tSpinDelay, &tNumTurns);
if (tTimer >= 50 && tCurrentDir == tOriginalDir)
tSpinEnded = TRUE;
}
if (!moving && tCurrentDir == tOriginalDir && ObjectEventCheckHeldMovementStatus(playerObj) == TRUE)
{
playerObj->invisible = FALSE;
playerObj->fixedPriority = FALSE;
UnlockPlayerFieldControls();
UnfreezeObjectEvents();
DestroyTask(FindTaskIdByFunc(Task_EscapeRopeWarpIn));
}
}
#undef tState
#undef tMovingState
#undef tOffsetY
#undef tPriority
#undef tSubpriority
#undef tSubspriteMode
#undef tTimer
#undef tSpinEnded
#undef tCurrentDir
#undef tSpinDelay
#undef tNumTurns
#undef tOriginalDir
static void Task_DoTeleportFieldEffect(u8 taskId);
static void TeleportFieldEffectTask1(struct Task *task);
static void TeleportFieldEffectTask2(struct Task *task);
static void TeleportFieldEffectTask3(struct Task *task);
static void TeleportFieldEffectTask4(struct Task *task);
static void FieldCallback_TeleportIn(void);
static void Task_DoTeleportInFieldEffect(u8 taskId);
static void TeleportInFieldEffectTask1(struct Task *task);
static void TeleportInFieldEffectTask2(struct Task *task);
static void TeleportInFieldEffectTask3(struct Task *task);
static void (*const sTeleportEffectFuncs[])(struct Task *) = {
TeleportFieldEffectTask1,
TeleportFieldEffectTask2,
TeleportFieldEffectTask3,
TeleportFieldEffectTask4
};
void CreateTeleportFieldEffectTask(void)
{
CreateTask(Task_DoTeleportFieldEffect, 0);
}
static void Task_DoTeleportFieldEffect(u8 taskId)
{
sTeleportEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId]);
}
static void TeleportFieldEffectTask1(struct Task *task)
{
LockPlayerFieldControls();
FreezeObjectEvents();
CameraObjectReset2();
task->data[15] = GetPlayerFacingDirection();
task->data[0]++;
}
static void TeleportFieldEffectTask2(struct Task *task)
{
u8 spinDirections[5] = {
[DIR_NONE] = DIR_SOUTH,
[DIR_SOUTH] = DIR_WEST,
[DIR_WEST] = DIR_NORTH,
[DIR_NORTH] = DIR_EAST,
[DIR_EAST] = DIR_SOUTH
};
struct ObjectEvent * objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (task->data[1] == 0 || (--task->data[1]) == 0)
{
ObjectEventTurn(objectEvent, spinDirections[objectEvent->facingDirection]);
task->data[1] = 8;
task->data[2]++;
}
if (task->data[2] > 7 && task->data[15] == objectEvent->facingDirection)
{
task->data[0]++;
task->data[1] = 4;
task->data[2] = 8;
task->data[3] = 1;
PlaySE(SE_WARP_IN);
}
}
static void TeleportFieldEffectTask3(struct Task *task)
{
u8 spinDirections[5] = {DIR_SOUTH, DIR_WEST, DIR_EAST, DIR_NORTH, DIR_SOUTH};
struct ObjectEvent * objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
struct Sprite *sprite = &gSprites[gPlayerAvatar.spriteId];
if ((--task->data[1]) <= 0)
{
task->data[1] = 4;
ObjectEventTurn(objectEvent, spinDirections[objectEvent->facingDirection]);
}
sprite->y -= task->data[3];
task->data[4] += task->data[3];
if ((--task->data[2]) <= 0 && (task->data[2] = 4, task->data[3] < 8))
{
task->data[3] <<= 1;
}
if (task->data[4] > 8 && (sprite->oam.priority = 1, sprite->subspriteMode != SUBSPRITES_OFF))
{
sprite->subspriteMode = SUBSPRITES_IGNORE_PRIORITY;
}
if (task->data[4] >= 0xa8)
{
task->data[0]++;
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
}
}
static void TeleportFieldEffectTask4(struct Task *task)
{
if (!gPaletteFade.active)
{
if (BGMusicStopped() == TRUE)
{
SetWarpDestinationToLastHealLocation();
WarpIntoMap();
SetMainCallback2(CB2_LoadMap);
gFieldCallback = FieldCallback_TeleportIn;
DestroyTask(FindTaskIdByFunc(Task_DoTeleportFieldEffect));
}
}
}
static void (*const sTeleportInEffectFuncs[])(struct Task *) = {
TeleportInFieldEffectTask1,
TeleportInFieldEffectTask2,
TeleportInFieldEffectTask3
};
static void FieldCallback_TeleportIn(void)
{
Overworld_PlaySpecialMapMusic();
WarpFadeInScreen();
QuestLog_DrawPreviouslyOnQuestHeaderIfInPlaybackMode();
LockPlayerFieldControls();
FreezeObjectEvents();
gFieldCallback = NULL;
gObjectEvents[gPlayerAvatar.objectEventId].invisible = TRUE;
CameraObjectReset2();
CreateTask(Task_DoTeleportInFieldEffect, 0);
}
static void Task_DoTeleportInFieldEffect(u8 taskId)
{
sTeleportInEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId]);
}
static void TeleportInFieldEffectTask1(struct Task *task)
{
struct Sprite *sprite;
s16 centerToCornerVecY;
if (IsWeatherNotFadingIn())
{
sprite = &gSprites[gPlayerAvatar.spriteId];
centerToCornerVecY = -(sprite->centerToCornerVecY << 1);
sprite->y2 = -(sprite->y + sprite->centerToCornerVecY + gSpriteCoordOffsetY + centerToCornerVecY);
gObjectEvents[gPlayerAvatar.objectEventId].invisible = FALSE;
task->data[0]++;
task->data[1] = 8;
task->data[2] = 1;
task->data[14] = sprite->subspriteMode;
task->data[15] = GetPlayerFacingDirection();
PlaySE(SE_WARP_IN);
}
}
static void TeleportInFieldEffectTask2(struct Task *task)
{
u8 spinDirections[5] = {1, 3, 4, 2, 1};
struct ObjectEvent * objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
struct Sprite *sprite = &gSprites[gPlayerAvatar.spriteId];
if ((sprite->y2 += task->data[1]) >= -8)
{
if (task->data[13] == 0)
{
task->data[13]++;
objectEvent->triggerGroundEffectsOnMove = TRUE;
sprite->subspriteMode = task->data[14];
}
} else
{
sprite->oam.priority = 1;
if (sprite->subspriteMode != SUBSPRITES_OFF)
{
sprite->subspriteMode = SUBSPRITES_IGNORE_PRIORITY;
}
}
if (sprite->y2 >= -0x30 && task->data[1] > 1 && !(sprite->y2 & 1))
{
task->data[1]--;
}
if ((--task->data[2]) == 0)
{
task->data[2] = 4;
ObjectEventTurn(objectEvent, spinDirections[objectEvent->facingDirection]);
}
if (sprite->y2 >= 0)
{
sprite->y2 = 0;
task->data[0]++;
task->data[1] = 1;
task->data[2] = 0;
}
}
static void TeleportInFieldEffectTask3(struct Task *task)
{
u8 spinDirections[5] = {1, 3, 4, 2, 1};
struct ObjectEvent * objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if ((--task->data[1]) == 0)
{
ObjectEventTurn(objectEvent, spinDirections[objectEvent->facingDirection]);
task->data[1] = 8;
if ((++task->data[2]) > 4 && task->data[14] == objectEvent->facingDirection)
{
UnlockPlayerFieldControls();
CameraObjectReset1();
UnfreezeObjectEvents();
DestroyTask(FindTaskIdByFunc(Task_DoTeleportInFieldEffect));
}
}
}
static void Task_ShowMon_Outdoors(u8 taskId);
static void ShowMonEffect_Outdoors_1(struct Task *task);
static void ShowMonEffect_Outdoors_2(struct Task *task);
static void ShowMonEffect_Outdoors_3(struct Task *task);
static void ShowMonEffect_Outdoors_4(struct Task *task);
static void ShowMonEffect_Outdoors_5(struct Task *task);
static void ShowMonEffect_Outdoors_6(struct Task *task);
static void ShowMonEffect_Outdoors_7(struct Task *task);
static void VBlankCB_ShowMonEffect_Outdoors(void);
static void LoadFieldMoveStreaksTilemapToVram(u16 screenbase);
static void Task_ShowMon_Indoors(u8 taskId);
static void ShowMonEffect_Indoors_1(struct Task *task);
static void ShowMonEffect_Indoors_2(struct Task *task);
static void ShowMonEffect_Indoors_3(struct Task *task);
static void ShowMonEffect_Indoors_4(struct Task *task);
static void ShowMonEffect_Indoors_5(struct Task *task);
static void ShowMonEffect_Indoors_6(struct Task *task);
static void ShowMonEffect_Indoors_7(struct Task *task);
static void VBlankCB_ShowMonEffect_Indoors(void);
static void AnimateIndoorShowMonBg(struct Task *task);
static bool8 SlideIndoorBannerOnscreen(struct Task *task);
static bool8 SlideIndoorBannerOffscreen(struct Task *task);
static u8 InitFieldMoveMonSprite(u32 species, bool32 isShiny, u32 personality);
static void SpriteCB_FieldMoveMonSlideOnscreen(struct Sprite *sprite);
static void SpriteCB_FieldMoveMonWaitAfterCry(struct Sprite *sprite);
static void SpriteCB_FieldMoveMonSlideOffscreen(struct Sprite *sprite);
static void (*const sShowMonOutdoorsEffectFuncs[])(struct Task *task) = {
ShowMonEffect_Outdoors_1,
ShowMonEffect_Outdoors_2,
ShowMonEffect_Outdoors_3,
ShowMonEffect_Outdoors_4,
ShowMonEffect_Outdoors_5,
ShowMonEffect_Outdoors_6,
ShowMonEffect_Outdoors_7
};
u32 FldEff_FieldMoveShowMon(void)
{
u8 taskId;
if (IsMapTypeOutdoors(GetCurrentMapType()) == TRUE)
taskId = CreateTask(Task_ShowMon_Outdoors, 0xFF);
else
taskId = CreateTask(Task_ShowMon_Indoors, 0xFF);
gTasks[taskId].data[15] = InitFieldMoveMonSprite(gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2]);
return 0;
}
#define SHOW_MON_CRY_NO_DUCKING (1 << 31)
u32 FldEff_FieldMoveShowMonInit(void)
{
u32 noDucking = gFieldEffectArguments[0] & SHOW_MON_CRY_NO_DUCKING;
u8 partyIdx = gFieldEffectArguments[0];
if (gFieldEffectArguments[0] & NOT_IN_PARTY_MASK)
{
gFieldEffectArguments[0] &= ~NOT_IN_PARTY_MASK;
gFieldEffectArguments[1] = FALSE;
gFieldEffectArguments[2] = SHINY_ODDS;
}
else
{
gFieldEffectArguments[0] = GetMonData(&gPlayerParty[partyIdx], MON_DATA_SPECIES);
gFieldEffectArguments[1] = GetMonData(&gPlayerParty[partyIdx], MON_DATA_IS_SHINY, NULL);
gFieldEffectArguments[2] = GetMonData(&gPlayerParty[partyIdx], MON_DATA_PERSONALITY);
}
gFieldEffectArguments[0] |= noDucking;
FieldEffectStart(FLDEFF_FIELD_MOVE_SHOW_MON);
FieldEffectActiveListRemove(FLDEFF_FIELD_MOVE_SHOW_MON_INIT);
return 0;
}
static void Task_ShowMon_Outdoors(u8 taskId)
{
sShowMonOutdoorsEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId]);
}
static void ShowMonEffect_Outdoors_1(struct Task *task)
{
task->data[11] = GetGpuReg(REG_OFFSET_WININ);
task->data[12] = GetGpuReg(REG_OFFSET_WINOUT);
StoreWordInTwoHalfwords((u16 *)&task->data[13], (u32)gMain.vblankCallback);
task->data[1] = WIN_RANGE(0xF0, 0xF1);
task->data[2] = WIN_RANGE(0x50, 0x51);
task->data[3] = WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR;
task->data[4] = WINOUT_WIN01_BG1 | WINOUT_WIN01_BG2 | WINOUT_WIN01_BG3 | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR;
SetGpuReg(REG_OFFSET_WIN0H, task->data[1]);
SetGpuReg(REG_OFFSET_WIN0V, task->data[2]);
SetGpuReg(REG_OFFSET_WININ, task->data[3]);
SetGpuReg(REG_OFFSET_WINOUT, task->data[4]);
SetVBlankCallback(VBlankCB_ShowMonEffect_Outdoors);
task->data[0]++;
}
static void ShowMonEffect_Outdoors_2(struct Task *task)
{
u16 charbase = ((GetGpuReg(REG_OFFSET_BG0CNT) >> 2) << 14);
u16 screenbase = ((GetGpuReg(REG_OFFSET_BG0CNT) >> 8) << 11);
CpuCopy16(sFieldMoveStreaksOutdoors_Gfx, (void *)(VRAM + charbase), 0x200);
CpuFill32(0, (void *)(VRAM + screenbase), 0x800);
LoadPalette(sFieldMoveStreaksOutdoors_Pal, BG_PLTT_ID(15), sizeof(sFieldMoveStreaksOutdoors_Pal));
LoadFieldMoveStreaksTilemapToVram(screenbase);
task->data[0]++;
}
static void ShowMonEffect_Outdoors_3(struct Task *task)
{
s16 win0h_lo;
s16 win0v_lo;
s16 win0v_hi;
task->data[5] -= 16;
win0h_lo = ((u16)task->data[1] >> 8);
win0v_lo = ((u16)task->data[2] >> 8);
win0v_hi = ((u16)task->data[2] & 0xff);
win0h_lo -= 16;
win0v_lo -= 2;
win0v_hi += 2;
if (win0h_lo < 0)
{
win0h_lo = 0;
}
if (win0v_lo < 0x28)
{
win0v_lo = 0x28;
}
if (win0v_hi > 0x78)
{
win0v_hi = 0x78;
}
task->data[1] = WIN_RANGE(win0h_lo, task->data[1] & 0xff);
task->data[2] = WIN_RANGE(win0v_lo, win0v_hi);
if (win0h_lo == 0 && win0v_lo == 0x28 && win0v_hi == 0x78)
{
gSprites[task->data[15]].callback = SpriteCB_FieldMoveMonSlideOnscreen;
task->data[0]++;
}
}
static void ShowMonEffect_Outdoors_4(struct Task *task)
{
task->data[5] -= 16;
if (gSprites[task->data[15]].data[7])
{
task->data[0]++;
}
}
static void ShowMonEffect_Outdoors_5(struct Task *task)
{
s16 win0v_lo;
s16 win0v_hi;
task->data[5] -= 16;
win0v_lo = (task->data[2] >> 8);
win0v_hi = (task->data[2] & 0xff);
win0v_lo += 6;
win0v_hi -= 6;
if (win0v_lo > 0x50)
{
win0v_lo = 0x50;
}
if (win0v_hi < 0x51)
{
win0v_hi = 0x51;
}
task->data[2] = WIN_RANGE(win0v_lo, win0v_hi);
if (win0v_lo == 0x50 && win0v_hi == 0x51)
{
task->data[0]++;
}
}
static void ShowMonEffect_Outdoors_6(struct Task *task)
{
u16 bg0cnt = (GetGpuReg(REG_OFFSET_BG0CNT) >> 8) << 11;
CpuFill32(0, (void *)VRAM + bg0cnt, 0x800);
task->data[1] = WIN_RANGE(0x00, 0xf1);
task->data[2] = WIN_RANGE(0x00, 0xa1);
task->data[3] = task->data[11];
task->data[4] = task->data[12];
task->data[0]++;
}
static void ShowMonEffect_Outdoors_7(struct Task *task)
{
IntrCallback callback;
LoadWordFromTwoHalfwords((u16 *)&task->data[13], (u32 *)&callback);
SetVBlankCallback(callback);
ChangeBgX(0, 0, 0);
ChangeBgY(0, 0, 0);
Menu_LoadStdPal();
FreeResourcesAndDestroySprite(&gSprites[task->data[15]], task->data[15]);
FieldEffectActiveListRemove(FLDEFF_FIELD_MOVE_SHOW_MON);
DestroyTask(FindTaskIdByFunc(Task_ShowMon_Outdoors));
}
static void VBlankCB_ShowMonEffect_Outdoors(void)
{
IntrCallback callback;
struct Task *task = &gTasks[FindTaskIdByFunc(Task_ShowMon_Outdoors)];
LoadWordFromTwoHalfwords((u16 *)&task->data[13], (u32 *)&callback);
callback();
SetGpuReg(REG_OFFSET_WIN0H, task->data[1]);
SetGpuReg(REG_OFFSET_WIN0V, task->data[2]);
SetGpuReg(REG_OFFSET_WININ, task->data[3]);
SetGpuReg(REG_OFFSET_WINOUT, task->data[4]);
SetGpuReg(REG_OFFSET_BG0HOFS, task->data[5]);
SetGpuReg(REG_OFFSET_BG0VOFS, task->data[6]);
}
static void LoadFieldMoveStreaksTilemapToVram(u16 screenbase)
{
u16 i;
u16 *dest;
dest = (u16 *)(VRAM + (10 * 32) + screenbase);
for (i = 0; i < (10 * 32); i++, dest++)
*dest = sFieldMoveStreaksOutdoors_Tilemap[i] | 0xF000;
}
static void (*const sShowMonIndoorsEffectFuncs[])(struct Task *) = {
ShowMonEffect_Indoors_1,
ShowMonEffect_Indoors_2,
ShowMonEffect_Indoors_3,
ShowMonEffect_Indoors_4,
ShowMonEffect_Indoors_5,
ShowMonEffect_Indoors_6,
ShowMonEffect_Indoors_7
};
static void Task_ShowMon_Indoors(u8 taskId)
{
sShowMonIndoorsEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId]);
}
static void ShowMonEffect_Indoors_1(struct Task *task)
{
SetGpuReg(REG_OFFSET_BG0HOFS, task->data[1]);
SetGpuReg(REG_OFFSET_BG0VOFS, task->data[2]);
StoreWordInTwoHalfwords((u16 *)&task->data[13], (u32)gMain.vblankCallback);
SetVBlankCallback(VBlankCB_ShowMonEffect_Indoors);
task->data[0]++;
}
static void ShowMonEffect_Indoors_2(struct Task *task)
{
u16 charbase;
u16 screenbase;
charbase = ((GetGpuReg(REG_OFFSET_BG0CNT) >> 2) << 14);
screenbase = ((GetGpuReg(REG_OFFSET_BG0CNT) >> 8) << 11);
task->data[12] = screenbase;
CpuCopy16(sFieldMoveStreaksIndoors_Gfx, (void *)(VRAM + charbase), 0x80);
CpuFill32(0, (void *)(VRAM + screenbase), 0x800);
LoadPalette(sFieldMoveStreaksIndoors_Pal, BG_PLTT_ID(15), sizeof(sFieldMoveStreaksIndoors_Pal));
task->data[0]++;
}
static void ShowMonEffect_Indoors_3(struct Task *task)
{
if (SlideIndoorBannerOnscreen(task))
{
task->data[5] = GetGpuReg(REG_OFFSET_WININ);
SetGpuReg(REG_OFFSET_WININ, (task->data[5] & 0xFF) | WININ_WIN1_BG0 | WININ_WIN1_OBJ);
SetGpuReg(REG_OFFSET_WIN1H, WIN_RANGE(0x00, 0xf0));
SetGpuReg(REG_OFFSET_WIN1V, WIN_RANGE(0x28, 0x78));
gSprites[task->data[15]].callback = SpriteCB_FieldMoveMonSlideOnscreen;
task->data[0]++;
}
AnimateIndoorShowMonBg(task);
}
static void ShowMonEffect_Indoors_4(struct Task *task)
{
AnimateIndoorShowMonBg(task);
if (gSprites[task->data[15]].data[7])
{
task->data[0]++;
}
}
static void ShowMonEffect_Indoors_5(struct Task *task)
{
AnimateIndoorShowMonBg(task);
task->data[3] = task->data[1] & 7;
task->data[4] = 0;
SetGpuReg(REG_OFFSET_WIN1H, WIN_RANGE(0xff, 0xff));
SetGpuReg(REG_OFFSET_WIN1V, WIN_RANGE(0xff, 0xff));
SetGpuReg(REG_OFFSET_WININ, task->data[5]);
task->data[0]++;
}
static void ShowMonEffect_Indoors_6(struct Task *task)
{
AnimateIndoorShowMonBg(task);
if (SlideIndoorBannerOffscreen(task))
{
task->data[0]++;
}
}
static void ShowMonEffect_Indoors_7(struct Task *task)
{
IntrCallback intrCallback;
u16 charbase;
charbase = (GetGpuReg(REG_OFFSET_BG0CNT) >> 8) << 11;
CpuFill32(0, (void *)VRAM + charbase, 0x800);
LoadWordFromTwoHalfwords((u16 *)&task->data[13], (u32 *)&intrCallback);
SetVBlankCallback(intrCallback);
ChangeBgX(0, 0, 0);
ChangeBgY(0, 0, 0);
Menu_LoadStdPal();
FreeResourcesAndDestroySprite(&gSprites[task->data[15]], task->data[15]);
FieldEffectActiveListRemove(FLDEFF_FIELD_MOVE_SHOW_MON);
DestroyTask(FindTaskIdByFunc(Task_ShowMon_Indoors));
}
static void VBlankCB_ShowMonEffect_Indoors(void)
{
IntrCallback intrCallback;
struct Task *task;
task = &gTasks[FindTaskIdByFunc(Task_ShowMon_Indoors)];
LoadWordFromTwoHalfwords((u16 *)&task->data[13], (u32 *)&intrCallback);
intrCallback();
SetGpuReg(REG_OFFSET_BG0HOFS, task->data[1]);
SetGpuReg(REG_OFFSET_BG0VOFS, task->data[2]);
}
static void AnimateIndoorShowMonBg(struct Task *task)
{
task->data[1] -= 16;
task->data[3] += 16;
}
static bool8 SlideIndoorBannerOnscreen(struct Task *task)
{
u16 i;
u16 srcOffs;
u16 dstOffs;
u16 *dest;
if (task->data[4] >= 32)
{
return TRUE;
}
dstOffs = (task->data[3] >> 3) & 0x1f;
if (dstOffs >= task->data[4])
{
dstOffs = (32 - dstOffs) & 0x1f;
srcOffs = (32 - task->data[4]) & 0x1f;
dest = (u16 *)(VRAM + 0x140 + (u16)task->data[12]);
for (i = 0; i < 10; i++)
{
dest[dstOffs + i * 32] = sFieldMoveStreaksIndoors_Tilemap[srcOffs + i * 32];
dest[dstOffs + i * 32] |= 0xf000;
dest[((dstOffs + 1) & 0x1f) + i * 32] = sFieldMoveStreaksIndoors_Tilemap[((srcOffs + 1) & 0x1f) + i * 32] | 0xf000;
dest[((dstOffs + 1) & 0x1f) + i * 32] |= 0xf000;
}
task->data[4] += 2;
}
return FALSE;
}
static bool8 SlideIndoorBannerOffscreen(struct Task *task)
{
u16 i;
u16 dstOffs;
u16 *dest;
if (task->data[4] >= 32)
{
return TRUE;
}
dstOffs = task->data[3] >> 3;
if (dstOffs >= task->data[4])
{
dstOffs = (task->data[1] >> 3) & 0x1f;
dest = (u16 *)(VRAM + 0x140 + (u16)task->data[12]);
for (i = 0; i < 10; i++)
{
dest[dstOffs + i * 32] = 0xf000;
dest[((dstOffs + 1) & 0x1f) + i * 32] = 0xf000;
}
task->data[4] += 2;
}
return FALSE;
}
static u8 InitFieldMoveMonSprite(u32 species, bool32 isShiny, u32 personality)
{
bool16 playCry;
u8 monSprite;
struct Sprite *sprite;
playCry = (species & SHOW_MON_CRY_NO_DUCKING) >> 16;
species &= ~SHOW_MON_CRY_NO_DUCKING;
monSprite = CreateMonSprite_FieldMove(species, isShiny, personality, 320, 80, 0);
sprite = &gSprites[monSprite];
sprite->callback = SpriteCallbackDummy;
sprite->oam.priority = 0;
sprite->data[0] = species;
sprite->data[6] = playCry;
return monSprite;
}
static void SpriteCB_FieldMoveMonSlideOnscreen(struct Sprite *sprite)
{
if ((sprite->x -= 20) <= 0x78)
{
sprite->x = 0x78;
sprite->data[1] = 30;
sprite->callback = SpriteCB_FieldMoveMonWaitAfterCry;
if (sprite->data[6])
{
PlayCry_NormalNoDucking(sprite->data[0], 0, CRY_VOLUME_RS, CRY_PRIORITY_NORMAL);
}
else
{
PlayCry_Normal(sprite->data[0], 0);
}
}
}
static void SpriteCB_FieldMoveMonWaitAfterCry(struct Sprite *sprite)
{
if ((--sprite->data[1]) == 0)
{
sprite->callback = SpriteCB_FieldMoveMonSlideOffscreen;
}
}
static void SpriteCB_FieldMoveMonSlideOffscreen(struct Sprite *sprite)
{
if (sprite->x < -0x40)
{
sprite->data[7] = 1;
}
else
{
sprite->x -= 20;
}
}
static void Task_FldEffUseSurf(u8 taskId);
static void UseSurfEffect_1(struct Task *task);
static void UseSurfEffect_2(struct Task *task);
static void UseSurfEffect_3(struct Task *task);
static void UseSurfEffect_4(struct Task *task);
static void UseSurfEffect_5(struct Task *task);
static void (*const sUseSurfEffectFuncs[])(struct Task *) = {
UseSurfEffect_1,
UseSurfEffect_2,
UseSurfEffect_3,
UseSurfEffect_4,
UseSurfEffect_5,
};
u32 FldEff_UseSurf(void)
{
u8 taskId = CreateTask(Task_FldEffUseSurf, 0xff);
gTasks[taskId].data[15] = gFieldEffectArguments[0];
Overworld_ClearSavedMusic();
if (Overworld_MusicCanOverrideMapMusic(MUS_SURF))
Overworld_ChangeMusicTo(MUS_SURF);
return FALSE;
}
static void Task_FldEffUseSurf(u8 taskId)
{
sUseSurfEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId]);
}
static void UseSurfEffect_1(struct Task *task)
{
LockPlayerFieldControls();
FreezeObjectEvents();
// Put follower into pokeball before using Surf
HideFollowerForFieldEffect();
gPlayerAvatar.preventStep = TRUE;
SetPlayerAvatarStateMask(PLAYER_AVATAR_FLAG_SURFING);
PlayerGetDestCoords(&task->data[1], &task->data[2]);
MoveCoords(gObjectEvents[gPlayerAvatar.objectEventId].movementDirection, &task->data[1], &task->data[2]);
task->data[0]++;
}
static void UseSurfEffect_2(struct Task *task)
{
struct ObjectEvent * objectEvent;
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (!ObjectEventIsMovementOverridden(objectEvent) || ObjectEventClearHeldMovementIfFinished(objectEvent))
{
StartPlayerAvatarSummonMonForFieldMoveAnim();
ObjectEventSetHeldMovement(objectEvent, MOVEMENT_ACTION_START_ANIM_IN_DIRECTION);
task->data[0]++;
}
}
static void UseSurfEffect_3(struct Task *task)
{
struct ObjectEvent * objectEvent;
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (ObjectEventCheckHeldMovementStatus(objectEvent))
{
gFieldEffectArguments[0] = task->data[15] | SHOW_MON_CRY_NO_DUCKING;
FieldEffectStart(FLDEFF_FIELD_MOVE_SHOW_MON_INIT);
task->data[0]++;
}
}
static void UseSurfEffect_4(struct Task *task)
{
struct ObjectEvent * objectEvent;
if (!FieldEffectActiveListContains(FLDEFF_FIELD_MOVE_SHOW_MON))
{
objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
ObjectEventSetGraphicsId(objectEvent, GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_SURFING));
ObjectEventClearHeldMovementIfFinished(objectEvent);
ObjectEventSetHeldMovement(objectEvent, GetJumpSpecialMovementAction(objectEvent->movementDirection));
gFieldEffectArguments[0] = task->data[1];
gFieldEffectArguments[1] = task->data[2];
gFieldEffectArguments[2] = gPlayerAvatar.objectEventId;
objectEvent->fieldEffectSpriteId = FieldEffectStart(FLDEFF_SURF_BLOB);
task->data[0]++;
}
}
static void UseSurfEffect_5(struct Task *task)
{
struct ObjectEvent *objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
struct ObjectEvent *followerObject = GetFollowerObject();
if (ObjectEventClearHeldMovementIfFinished(objectEvent))
{
gPlayerAvatar.preventStep = FALSE;
gPlayerAvatar.flags &= ~PLAYER_AVATAR_FLAG_CONTROLLABLE;
ObjectEventSetHeldMovement(objectEvent, GetFaceDirectionMovementAction(objectEvent->movementDirection));
if (followerObject)
ObjectEventClearHeldMovementIfFinished(followerObject);
SetSurfBlob_BobState(objectEvent->fieldEffectSpriteId, BOB_PLAYER_AND_MON);
UnfreezeObjectEvents();
UnlockPlayerFieldControls();
FieldEffectActiveListRemove(FLDEFF_USE_SURF);
DestroyTask(FindTaskIdByFunc(Task_FldEffUseSurf));
SetHelpContext(HELPCONTEXT_SURFING);
}
}
static void Task_FldEffUseVsSeeker(u8 taskId);
static void UseVsSeekerEffect_1(struct Task *task);
static void UseVsSeekerEffect_2(struct Task *task);
static void UseVsSeekerEffect_3(struct Task *task);
static void UseVsSeekerEffect_4(struct Task *task);
static void (*const sUseVsSeekerEffectFuncs[])(struct Task *task) = {
UseVsSeekerEffect_1,
UseVsSeekerEffect_2,
UseVsSeekerEffect_3,
UseVsSeekerEffect_4
};
u32 FldEff_UseVsSeeker(void)
{
if (gQuestLogState == QL_STATE_RECORDING)
QuestLogRecordPlayerAvatarGfxTransitionWithDuration(8, 89);
CreateTask(Task_FldEffUseVsSeeker, 0xFF);
return 0;
}
static void Task_FldEffUseVsSeeker(u8 taskId)
{
sUseVsSeekerEffectFuncs[gTasks[taskId].data[0]](&gTasks[taskId]);
}
static void UseVsSeekerEffect_1(struct Task *task)
{
LockPlayerFieldControls();
FreezeObjectEvents();
gPlayerAvatar.preventStep = TRUE;
task->data[0]++;
}
static void UseVsSeekerEffect_2(struct Task *task)
{
struct ObjectEvent * playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
if (!ObjectEventIsMovementOverridden(playerObj) || ObjectEventClearHeldMovementIfFinished(playerObj))
{
StartPlayerAvatarVsSeekerAnim();
ObjectEventSetHeldMovement(playerObj, MOVEMENT_ACTION_START_ANIM_IN_DIRECTION);
task->data[0]++;
}
}
static void UseVsSeekerEffect_3(struct Task *task)
{
struct ObjectEvent * playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
if (ObjectEventClearHeldMovementIfFinished(playerObj))
{
if (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_ACRO_BIKE | PLAYER_AVATAR_FLAG_MACH_BIKE))
ObjectEventSetGraphicsId(playerObj, GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_MACH_BIKE));
else if (gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING)
ObjectEventSetGraphicsId(playerObj, GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_SURFING));
else
ObjectEventSetGraphicsId(playerObj, GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_NORMAL));
ObjectEventForceSetHeldMovement(playerObj, GetFaceDirectionMovementAction(playerObj->facingDirection));
task->data[0]++;
}
}
static void UseVsSeekerEffect_4(struct Task *task)
{
struct ObjectEvent * playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
if (ObjectEventClearHeldMovementIfFinished(playerObj))
{
gPlayerAvatar.preventStep = FALSE;
FieldEffectActiveListRemove(FLDEFF_USE_VS_SEEKER);
DestroyTask(FindTaskIdByFunc(Task_FldEffUseVsSeeker));
}
}
static void SpriteCB_NPCFlyOut(struct Sprite *sprite);
u32 FldEff_NpcFlyOut(void)
{
u8 spriteId = CreateSprite(&gFieldEffectObjectTemplate_Bird, 0x78, 0, 1);
struct Sprite *sprite = &gSprites[spriteId];
sprite->oam.paletteNum = LoadPlayerObjectEventPalette(gSaveBlock2Ptr->playerGender);
sprite->oam.priority = 1;
sprite->callback = SpriteCB_NPCFlyOut;
sprite->data[1] = gFieldEffectArguments[0];
PlaySE(SE_M_FLY);
return spriteId;
}
static void SpriteCB_NPCFlyOut(struct Sprite *sprite)
{
struct Sprite *npcSprite;
sprite->x2 = Cos(sprite->data[2], 0x8c);
sprite->y2 = Sin(sprite->data[2], 0x48);
sprite->data[2] = (sprite->data[2] + 4) & 0xff;
if (sprite->data[0])
{
npcSprite = &gSprites[sprite->data[1]];
npcSprite->coordOffsetEnabled = FALSE;
npcSprite->x = sprite->x + sprite->x2;
npcSprite->y = sprite->y + sprite->y2 - 8;
npcSprite->x2 = 0;
npcSprite->y2 = 0;
}
if (sprite->data[2] >= 0x80)
{
FieldEffectStop(sprite, FLDEFF_NPCFLY_OUT);
}
}
// Task data for Task_FlyOut / Task_FlyIn
#define tState data[0]
#define tMonPartyId data[1]
#define tBirdSpriteId data[1] // re-used
#define tTimer data[2]
#define tAvatarFlags data[15]
static void Task_FlyOut(u8 taskId);
static void FlyOutFieldEffect_FieldMovePose(struct Task *task);
static void FlyOutFieldEffect_ShowMon(struct Task *task);
static void FlyOutFieldEffect_BirdLeaveBall(struct Task *task);
static void FlyOutFieldEffect_WaitBirdLeave(struct Task *task);
static void FlyOutFieldEffect_BirdSwoopDown(struct Task *task);
static void FlyOutFieldEffect_JumpOnBird(struct Task *task);
static void FlyOutFieldEffect_FlyOffWithBird(struct Task *task);
static void FlyOutFieldEffect_WaitFlyOff(struct Task *task);
static void FlyOutFieldEffect_End(struct Task *task);
static u8 CreateFlyBirdSprite(void);
static bool8 GetFlyBirdAnimCompleted(u8 flyBlobSpriteId);
static void StartFlyBirdSwoopDown(u8 flyBlobSpriteId);
static void SetFlyBirdPlayerSpriteId(u8 flyBlobSpriteId, u8 playerSpriteId);
static void SpriteCB_FlyBirdLeaveBall(struct Sprite *sprite);
static void SpriteCB_FlyBirdSwoopDown(struct Sprite *sprite);
static void DoBirdSpriteWithPlayerAffineAnim(struct Sprite *sprite, u8 affineAnimId);
static void SpriteCB_FlyBirdWithPlayer(struct Sprite *sprite);
static void (*const sFlyOutFieldEffectFuncs[])(struct Task *) =
{
FlyOutFieldEffect_FieldMovePose,
FlyOutFieldEffect_ShowMon,
FlyOutFieldEffect_BirdLeaveBall,
FlyOutFieldEffect_WaitBirdLeave,
FlyOutFieldEffect_BirdSwoopDown,
FlyOutFieldEffect_JumpOnBird,
FlyOutFieldEffect_FlyOffWithBird,
FlyOutFieldEffect_WaitFlyOff,
FlyOutFieldEffect_End
};
u32 FldEff_FlyOut(void)
{
u8 taskId = CreateTask(Task_FlyOut, 0xFE);
gTasks[taskId].tMonPartyId = gFieldEffectArguments[0];
return 0;
}
static void Task_FlyOut(u8 taskId)
{
sFlyOutFieldEffectFuncs[gTasks[taskId].tState](&gTasks[taskId]);
}
static void FlyOutFieldEffect_FieldMovePose(struct Task *task)
{
struct ObjectEvent *objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (!ObjectEventIsMovementOverridden(objectEvent) || ObjectEventClearHeldMovementIfFinished(objectEvent))
{
task->tAvatarFlags = gPlayerAvatar.flags;
gPlayerAvatar.preventStep = TRUE;
SetPlayerAvatarStateMask(PLAYER_AVATAR_FLAG_ON_FOOT);
StartPlayerAvatarSummonMonForFieldMoveAnim();
ObjectEventSetHeldMovement(objectEvent, MOVEMENT_ACTION_START_ANIM_IN_DIRECTION);
task->tState++;
}
}
static void FlyOutFieldEffect_ShowMon(struct Task *task)
{
struct ObjectEvent *objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (ObjectEventClearHeldMovementIfFinished(objectEvent))
{
task->tState++;
gFieldEffectArguments[0] = task->tMonPartyId;
FieldEffectStart(FLDEFF_FIELD_MOVE_SHOW_MON_INIT);
}
}
static void FlyOutFieldEffect_BirdLeaveBall(struct Task *task)
{
if (!FieldEffectActiveListContains(FLDEFF_FIELD_MOVE_SHOW_MON))
{
struct ObjectEvent *objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (task->tAvatarFlags & PLAYER_AVATAR_FLAG_SURFING)
{
SetSurfBlob_BobState(objectEvent->fieldEffectSpriteId, BOB_MON_ONLY);
SetSurfBlob_DontSyncAnim(objectEvent->fieldEffectSpriteId, FALSE);
}
task->tBirdSpriteId = CreateFlyBirdSprite();
task->tState++;
}
}
static void FlyOutFieldEffect_WaitBirdLeave(struct Task *task)
{
if (GetFlyBirdAnimCompleted(task->tBirdSpriteId))
{
task->tState++;
task->tTimer = 16;
SetPlayerAvatarTransitionFlags(PLAYER_AVATAR_FLAG_ON_FOOT);
ObjectEventSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], MOVEMENT_ACTION_FACE_LEFT);
}
}
static void FlyOutFieldEffect_BirdSwoopDown(struct Task *task)
{
struct ObjectEvent *objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if ((task->tTimer == 0 || (--task->tTimer) == 0) && ObjectEventClearHeldMovementIfFinished(objectEvent))
{
task->tState++;
PlaySE(SE_M_FLY);
StartFlyBirdSwoopDown(task->tBirdSpriteId);
}
}
static void FlyOutFieldEffect_JumpOnBird(struct Task *task)
{
if ((++task->tTimer) >= 8)
{
struct ObjectEvent *objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
ObjectEventSetGraphicsId(objectEvent, GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_SURFING));
StartSpriteAnim(&gSprites[objectEvent->spriteId], ANIM_GET_ON_OFF_POKEMON_WEST);
objectEvent->inanimate = TRUE;
ObjectEventSetHeldMovement(objectEvent, MOVEMENT_ACTION_JUMP_IN_PLACE_LEFT);
task->tState++;
task->tTimer = 0;
}
}
static void FlyOutFieldEffect_FlyOffWithBird(struct Task *task)
{
if ((++task->tTimer) >= 10)
{
struct ObjectEvent *objectEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
ObjectEventClearHeldMovementIfActive(objectEvent);
objectEvent->inanimate = FALSE;
objectEvent->hasShadow = FALSE;
SetFlyBirdPlayerSpriteId(task->tBirdSpriteId, objectEvent->spriteId);
StartSpriteAnim(&gSprites[task->tBirdSpriteId], gSaveBlock2Ptr->playerGender * 2 + 1);
DoBirdSpriteWithPlayerAffineAnim(&gSprites[task->tBirdSpriteId], 0);
gSprites[task->tBirdSpriteId].callback = SpriteCB_FlyBirdWithPlayer;
CameraObjectReset2();
task->tState++;
}
}
static void FlyOutFieldEffect_WaitFlyOff(struct Task *task)
{
if (GetFlyBirdAnimCompleted(task->tBirdSpriteId))
{
WarpFadeOutScreen();
task->tState++;
}
}
static void FlyOutFieldEffect_End(struct Task *task)
{
if (!gPaletteFade.active)
{
FieldEffectActiveListRemove(FLDEFF_FLY_OUT);
DestroyTask(FindTaskIdByFunc(Task_FlyOut));
}
}
static u8 CreateFlyBirdSprite(void)
{
u8 spriteId;
struct Sprite *sprite;
spriteId = CreateSprite(&gFieldEffectObjectTemplate_Bird, 255, 180, 1);
sprite = &gSprites[spriteId];
sprite->oam.paletteNum = LoadPlayerObjectEventPalette(gSaveBlock2Ptr->playerGender);
sprite->oam.priority = 1;
sprite->callback = SpriteCB_FlyBirdLeaveBall;
return spriteId;
}
// Sprite data for the bird sprite
#define sInitData data[0]
#define sPlayerSpriteId data[6]
#define sAnimCompleted data[7]
static bool8 GetFlyBirdAnimCompleted(u8 spriteId)
{
return gSprites[spriteId].sAnimCompleted;
}
static void StartFlyBirdSwoopDown(u8 spriteId)
{
struct Sprite *sprite;
sprite = &gSprites[spriteId];
sprite->callback = SpriteCB_FlyBirdSwoopDown;
sprite->x = 120;
sprite->y = 0;
sprite->x2 = 0;
sprite->y2 = 0;
memset(&sprite->data[0], 0, 8 * sizeof(u16) /* zero all data cells */);
sprite->data[6] = MAX_SPRITES;
}
static void SetFlyBirdPlayerSpriteId(u8 flyBlobSpriteId, u8 playerSpriteId)
{
gSprites[flyBlobSpriteId].sPlayerSpriteId = playerSpriteId;
}
static const union AffineAnimCmd sAffineAnim_FlyBirdLeaveBall[] =
{
AFFINEANIMCMD_FRAME( 8, 8, -30, 0),
AFFINEANIMCMD_FRAME(28, 28, 0, 30),
AFFINEANIMCMD_END
};
static const union AffineAnimCmd sAffineAnim_FlyBirdReturnToBall[] =
{
AFFINEANIMCMD_FRAME(256, 256, 64, 0),
AFFINEANIMCMD_FRAME(-10, -10, 0, 22),
AFFINEANIMCMD_END
};
static const union AffineAnimCmd *const sAffineAnims_FlyBirdBall[] =
{
sAffineAnim_FlyBirdLeaveBall,
sAffineAnim_FlyBirdReturnToBall
};
static void SpriteCB_FlyBirdLeaveBall(struct Sprite *sprite)
{
if (sprite->sAnimCompleted == FALSE)
{
if (sprite->sInitData == FALSE)
{
sprite->oam.affineMode = ST_OAM_AFFINE_DOUBLE;
sprite->affineAnims = sAffineAnims_FlyBirdBall;
InitSpriteAffineAnim(sprite);
StartSpriteAffineAnim(sprite, 0);
if (gSaveBlock2Ptr->playerGender == MALE)
sprite->x = 128;
else
sprite->x = 118;
sprite->y = -48;
sprite->sInitData++;
sprite->data[1] = 64;
sprite->data[2] = 256;
}
sprite->data[1] += (sprite->data[2] >> 8);
sprite->x2 = Cos(sprite->data[1], 120);
sprite->y2 = Sin(sprite->data[1], 120);
if (sprite->data[2] < 2048)
sprite->data[2] += 96;
if (sprite->data[1] > 129)
{
sprite->sAnimCompleted++;
sprite->oam.affineMode = ST_OAM_AFFINE_OFF;
FreeOamMatrix(sprite->oam.matrixNum);
CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, ST_OAM_AFFINE_OFF);
}
}
}
static void SpriteCB_FlyBirdSwoopDown(struct Sprite *sprite)
{
sprite->x2 = Cos(sprite->data[2], 140);
sprite->y2 = Sin(sprite->data[2], 72);
sprite->data[2] = (sprite->data[2] + 4) & 0xFF;
if (sprite->sPlayerSpriteId != MAX_SPRITES)
{
struct Sprite *playerSprite = &gSprites[sprite->sPlayerSpriteId];
playerSprite->coordOffsetEnabled = FALSE;
playerSprite->x = sprite->x + sprite->x2;
playerSprite->y = sprite->y + sprite->y2 - 8;
playerSprite->x2 = 0;
playerSprite->y2 = 0;
}
if (sprite->data[2] >= 128)
sprite->sAnimCompleted = TRUE;
}
static void SpriteCB_FlyBirdReturnToBall(struct Sprite *sprite)
{
if (sprite->sAnimCompleted == FALSE)
{
if (sprite->sInitData == FALSE)
{
sprite->oam.affineMode = ST_OAM_AFFINE_DOUBLE;
sprite->affineAnims = sAffineAnims_FlyBirdBall;
InitSpriteAffineAnim(sprite);
StartSpriteAffineAnim(sprite, 1);
if (gSaveBlock2Ptr->playerGender == MALE)
sprite->x = 112;
else
sprite->x = 100;
sprite->y = -32;
sprite->sInitData++;
sprite->data[1] = 240;
sprite->data[2] = 2048;
sprite->data[4] = 128;
}
sprite->data[1] += sprite->data[2] >> 8;
sprite->data[3] += sprite->data[2] >> 8;
sprite->data[1] &= 0xFF;
sprite->x2 = Cos(sprite->data[1], 32);
sprite->y2 = Sin(sprite->data[1], 120);
if (sprite->data[2] > 256)
sprite->data[2] -= sprite->data[4];
if (sprite->data[4] < 256)
sprite->data[4] += 24;
if (sprite->data[2] < 256)
sprite->data[2] = 256;
if (sprite->data[3] >= 60)
{
sprite->sAnimCompleted++;
sprite->oam.affineMode = ST_OAM_AFFINE_OFF;
FreeOamMatrix(sprite->oam.matrixNum);
sprite->invisible = TRUE;
}
}
}
static void StartFlyBirdReturnToBall(u8 spriteId)
{
StartFlyBirdSwoopDown(spriteId);
gSprites[spriteId].callback = SpriteCB_FlyBirdReturnToBall;
}
static void Task_FlyIn(u8 taskId);
static void FlyInFieldEffect_BirdSwoopDown(struct Task *task);
static void FlyInFieldEffect_FlyInWithBird(struct Task *task);
static void FlyInFieldEffect_JumpOffBird(struct Task *task);
static void FlyInFieldEffect_FieldMovePose(struct Task *task);
static void FlyInFieldEffect_BirdReturnToBall(struct Task *task);
static void FlyInFieldEffect_WaitBirdReturn(struct Task *task);
static void FlyInFieldEffect_End(struct Task *task);
static void TryChangeBirdSprite(struct Sprite *sprite);
static void (*const sFlyInFieldEffectFuncs[])(struct Task *task) =
{
FlyInFieldEffect_BirdSwoopDown,
FlyInFieldEffect_FlyInWithBird,
FlyInFieldEffect_JumpOffBird,
FlyInFieldEffect_FieldMovePose,
FlyInFieldEffect_BirdReturnToBall,
FlyInFieldEffect_WaitBirdReturn,
FlyInFieldEffect_End
};
u32 FldEff_FlyIn(void)
{
CreateTask(Task_FlyIn, 0xFE);
return 0;
}
static void Task_FlyIn(u8 taskId)
{
sFlyInFieldEffectFuncs[gTasks[taskId].tState](&gTasks[taskId]);
}
static void FlyInFieldEffect_BirdSwoopDown(struct Task *task)
{
struct ObjectEvent *playerObj;
playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
if (!ObjectEventIsMovementOverridden(playerObj) || ObjectEventClearHeldMovementIfFinished(playerObj))
{
task->tState++;
task->tTimer = 33;
task->tAvatarFlags = gPlayerAvatar.flags;
gPlayerAvatar.preventStep = TRUE;
SetPlayerAvatarStateMask(PLAYER_AVATAR_FLAG_ON_FOOT);
if (task->tAvatarFlags & PLAYER_AVATAR_FLAG_SURFING)
SetSurfBlob_BobState(playerObj->fieldEffectSpriteId, BOB_NONE);
ObjectEventSetGraphicsId(playerObj, GetPlayerAvatarGraphicsIdByStateId(PLAYER_AVATAR_STATE_SURFING));
CameraObjectReset2();
ObjectEventTurn(playerObj, DIR_WEST);
StartSpriteAnim(&gSprites[playerObj->spriteId], ANIM_GET_ON_OFF_POKEMON_WEST);
playerObj->invisible = FALSE;
task->tBirdSpriteId = CreateFlyBirdSprite();
StartFlyBirdSwoopDown(task->tBirdSpriteId);
SetFlyBirdPlayerSpriteId(task->tBirdSpriteId, playerObj->spriteId);
StartSpriteAnim(&gSprites[task->tBirdSpriteId], gSaveBlock2Ptr->playerGender * 2 + 2);
DoBirdSpriteWithPlayerAffineAnim(&gSprites[task->tBirdSpriteId], 1);
gSprites[task->tBirdSpriteId].callback = SpriteCB_FlyBirdWithPlayer;
}
}
static void FlyInFieldEffect_FlyInWithBird(struct Task *task)
{
struct ObjectEvent *playerObj;
struct Sprite *playerSprite;
TryChangeBirdSprite(&gSprites[task->tBirdSpriteId]);
if (task->tTimer == 0 || (--task->tTimer) == 0)
{
playerObj= &gObjectEvents[gPlayerAvatar.objectEventId];
playerSprite = &gSprites[playerObj->spriteId];
SetFlyBirdPlayerSpriteId(task->tBirdSpriteId, MAX_SPRITES);
playerSprite->x += playerSprite->x2;
playerSprite->y += playerSprite->y2;
playerSprite->x2 = 0;
playerSprite->y2 = 0;
task->tState++;
task->tTimer = 0;
}
}
static void FlyInFieldEffect_JumpOffBird(struct Task *task)
{
s16 yOffsets[18] = {-2, -4, -5, -6, -7, -8, -8, -8, -7, -7, -6, -5, -3, -2, 0, 2, 4, 8};
struct Sprite *sprite = &gSprites[gPlayerAvatar.spriteId];
sprite->y2 = yOffsets[task->tTimer];
if ((++task->tTimer) >= 18)
task->tState++;
}
static void FlyInFieldEffect_FieldMovePose(struct Task *task)
{
struct ObjectEvent *playerObj;
struct Sprite *playerSprite;
if (GetFlyBirdAnimCompleted(task->tBirdSpriteId))
{
playerObj= &gObjectEvents[gPlayerAvatar.objectEventId];
playerSprite = &gSprites[playerObj->spriteId];
playerObj->inanimate = FALSE;
MoveObjectEventToMapCoords(playerObj, playerObj->currentCoords.x, playerObj->currentCoords.y);
playerSprite->x2 = 0;
playerSprite->y2 = 0;
playerSprite->coordOffsetEnabled = TRUE;
StartPlayerAvatarSummonMonForFieldMoveAnim();
ObjectEventSetHeldMovement(playerObj, MOVEMENT_ACTION_START_ANIM_IN_DIRECTION);
task->tState++;
}
}
static void FlyInFieldEffect_BirdReturnToBall(struct Task *task)
{
if (ObjectEventClearHeldMovementIfFinished(&gObjectEvents[gPlayerAvatar.objectEventId]))
{
task->tState++;
StartFlyBirdReturnToBall(task->tBirdSpriteId);
}
}
static void FlyInFieldEffect_WaitBirdReturn(struct Task *task)
{
if (GetFlyBirdAnimCompleted(task->tBirdSpriteId))
{
DestroySprite(&gSprites[task->tBirdSpriteId]);
task->tState++;
task->data[1] = 16;
}
}
static void FlyInFieldEffect_End(struct Task *task)
{
struct ObjectEvent *playerObj;
u8 state;
if ((--task->data[1]) == 0)
{
playerObj = &gObjectEvents[gPlayerAvatar.objectEventId];
state = PLAYER_AVATAR_STATE_NORMAL;
if (task->tAvatarFlags & PLAYER_AVATAR_FLAG_SURFING)
{
state = PLAYER_AVATAR_STATE_SURFING;
SetSurfBlob_BobState(playerObj->fieldEffectSpriteId, BOB_PLAYER_AND_MON);
}
ObjectEventSetGraphicsId(playerObj, GetPlayerAvatarGraphicsIdByStateId(state));
ObjectEventTurn(playerObj, DIR_SOUTH);
gPlayerAvatar.flags = task->tAvatarFlags;
gPlayerAvatar.preventStep = FALSE;
FieldEffectActiveListRemove(FLDEFF_FLY_IN);
DestroyTask(FindTaskIdByFunc(Task_FlyIn));
}
}
#undef tState
#undef tMonPartyId
#undef tBirdSpriteId
#undef tTimer
#undef tAvatarFlags
static const union AffineAnimCmd sAffineAnim_FlyBirdOutOfMap[] =
{
AFFINEANIMCMD_FRAME(24, 24, 0, 1),
AFFINEANIMCMD_JUMP(0)
};
static const union AffineAnimCmd sAffineAnim_FlyBirdIntoMap[] =
{
AFFINEANIMCMD_FRAME(512, 512, 0, 1),
AFFINEANIMCMD_FRAME(-16, -16, 0, 1),
AFFINEANIMCMD_JUMP(1)
};
static const union AffineAnimCmd *const sAffineAnims_FlyBirdWithPlayer[] =
{
sAffineAnim_FlyBirdOutOfMap,
sAffineAnim_FlyBirdIntoMap
};
static void DoBirdSpriteWithPlayerAffineAnim(struct Sprite *sprite, u8 affineAnimId)
{
sprite->oam.affineMode = ST_OAM_AFFINE_DOUBLE;
sprite->affineAnims = sAffineAnims_FlyBirdWithPlayer;
InitSpriteAffineAnim(sprite);
StartSpriteAffineAnim(sprite, affineAnimId);
}
static void SpriteCB_FlyBirdWithPlayer(struct Sprite *sprite)
{
sprite->x2 = Cos(sprite->data[2], 180);
sprite->y2 = Sin(sprite->data[2], 72);
sprite->data[2] = (sprite->data[2] + 2) & 0xFF;
if (sprite->sPlayerSpriteId != MAX_SPRITES)
{
struct Sprite *playerSprite;
playerSprite = &gSprites[sprite->sPlayerSpriteId];
playerSprite->coordOffsetEnabled = FALSE;
playerSprite->x = sprite->x + sprite->x2;
playerSprite->y = sprite->y + sprite->y2 - 8;
playerSprite->x2 = 0;
playerSprite->y2 = 0;
}
if (sprite->data[2] >= 128)
{
sprite->sAnimCompleted = TRUE;
sprite->oam.affineMode = ST_OAM_AFFINE_OFF;
FreeOamMatrix(sprite->oam.matrixNum);
CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, ST_OAM_AFFINE_OFF);
}
}
#undef sInitData
#undef sPlayerSpriteId
#undef sAnimCompleted
static void TryChangeBirdSprite(struct Sprite *sprite)
{
if (sprite->oam.affineMode != ST_OAM_AFFINE_OFF)
{
if (gOamMatrices[sprite->oam.matrixNum].a == 0x100 || gOamMatrices[sprite->oam.matrixNum].d == 0x100)
{
sprite->oam.affineMode = ST_OAM_AFFINE_OFF;
FreeOamMatrix(sprite->oam.matrixNum);
CalcCenterToCornerVec(sprite, sprite->oam.shape, sprite->oam.size, ST_OAM_AFFINE_OFF);
StartSpriteAnim(sprite, 0);
sprite->callback = SpriteCB_FlyBirdSwoopDown;
}
}
}
static void Task_MoveDeoxysRock_Step(u8 taskId);
u32 FldEff_MoveDeoxysRock(void)
{
u8 taskId;
u8 objectEventIdBuffer;
s32 x;
s32 y;
struct ObjectEvent * objectEvent;
if (!TryGetObjectEventIdByLocalIdAndMap(gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2], &objectEventIdBuffer))
{
objectEvent = &gObjectEvents[objectEventIdBuffer];
x = objectEvent->currentCoords.x - 7;
y = objectEvent->currentCoords.y - 7;
x = (gFieldEffectArguments[3] - x) * 16;
y = (gFieldEffectArguments[4] - y) * 16;
ShiftObjectEventCoords(objectEvent, gFieldEffectArguments[3] + 7, gFieldEffectArguments[4] + 7);
taskId = CreateTask(Task_MoveDeoxysRock_Step, 0x50);
gTasks[taskId].data[1] = objectEvent->spriteId;
gTasks[taskId].data[2] = gSprites[objectEvent->spriteId].x + x;
gTasks[taskId].data[3] = gSprites[objectEvent->spriteId].y + y;
gTasks[taskId].data[8] = gFieldEffectArguments[5];
gTasks[taskId].data[9] = objectEventIdBuffer;
}
return FALSE;
}
static void Task_MoveDeoxysRock_Step(u8 taskId)
{
s16 *data = gTasks[taskId].data;
struct Sprite *sprite = &gSprites[data[1]];
struct ObjectEvent * objectEvent;
switch (data[0])
{
case 0:
data[4] = sprite->x << 4;
data[5] = sprite->y << 4;
// UB: Possible divide by zero
data[6] = SAFE_DIV(((data[2] << 4) - data[4]), data[8]);
data[7] = SAFE_DIV(((data[3] << 4) - data[5]), data[8]);
data[0]++;
// fallthrough
case 1:
if (data[8] != 0)
{
data[8]--;
data[4] += data[6];
data[5] += data[7];
sprite->x = data[4] >> 4;
sprite->y = data[5] >> 4;
}
else
{
objectEvent = &gObjectEvents[data[9]];
sprite->x = data[2];
sprite->y = data[3];
ShiftStillObjectEventCoords(objectEvent);
objectEvent->triggerGroundEffectsOnStop = TRUE;
FieldEffectActiveListRemove(FLDEFF_MOVE_DEOXYS_ROCK);
DestroyTask(taskId);
}
break;
}
}
u32 FldEff_CaveDust(void)
{
u8 spriteId;
FieldEffectScript_LoadFadedPal(&gSpritePalette_CaveDust);
SetSpritePosToOffsetMapCoords((s16 *)&gFieldEffectArguments[0], (s16 *)&gFieldEffectArguments[1], 8, 8);
spriteId = CreateSpriteAtEnd(&gFieldEffectObjectTemplate_CaveDust, gFieldEffectArguments[0], gFieldEffectArguments[1], 0xFF);
if (spriteId != MAX_SPRITES)
{
gSprites[spriteId].coordOffsetEnabled = TRUE;
gSprites[spriteId].data[0] = 22;
}
return spriteId;
}
static void Task_DestroyDeoxysRock(u8 taskId);
static void DestroyDeoxysRockEffect_CameraShake(s16 *data, u8 taskId);
static void DestroyDeoxysRockEffect_RockFragments(s16 *data, u8 taskId);
static void DestroyDeoxysRockEffect_WaitAndEnd(s16 *data, u8 taskId);
static void CreateDeoxysRockFragments(struct Sprite *sprite);
static void SpriteCB_DeoxysRockFragment(struct Sprite *sprite);
static void (*const sDestroyDeoxysRockEffectFuncs[])(s16 *data, u8 taskId) =
{
DestroyDeoxysRockEffect_CameraShake,
DestroyDeoxysRockEffect_RockFragments,
DestroyDeoxysRockEffect_WaitAndEnd
};
static const struct SpriteFrameImage sImages_DeoxysRockFragment[] =
{
{sRockFragment_TopLeft, 0x20},
{sRockFragment_TopRight, 0x20},
{sRockFragment_BottomLeft, 0x20},
{sRockFragment_BottomRight, 0x20}
};
static const union AnimCmd sAnim_RockFragment_TopLeft[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sAnim_RockFragment_TopRight[] =
{
ANIMCMD_FRAME(1, 0),
ANIMCMD_END
};
static const union AnimCmd sAnim_RockFragment_BottomLeft[] =
{
ANIMCMD_FRAME(2, 0),
ANIMCMD_END
};
static const union AnimCmd sAnim_RockFragment_BottomRight[] =
{
ANIMCMD_FRAME(3, 0),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_DeoxysRockFragment[] =
{
sAnim_RockFragment_TopLeft,
sAnim_RockFragment_TopRight,
sAnim_RockFragment_BottomLeft,
sAnim_RockFragment_BottomRight
};
static const struct SpriteTemplate sSpriteTemplate_DeoxysRockFragment =
{
.tileTag = TAG_NONE,
.paletteTag = 4371,
.oam = &sOamData_8x8,
.anims = sAnims_DeoxysRockFragment,
.images = sImages_DeoxysRockFragment,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_DeoxysRockFragment
};
// Task data for Task_DestroyDeoxysRock
#define tState data[1]
#define tObjectEventId data[2]
#define tTimer data[3]
#define tCameraTaskId data[5]
#define tLocalId data[6]
#define tMapNum data[7]
#define tMapGroup data[8]
u32 FldEff_DestroyDeoxysRock(void)
{
u8 taskId;
u8 objectEventId;
if (!TryGetObjectEventIdByLocalIdAndMap(gFieldEffectArguments[0], gFieldEffectArguments[1], gFieldEffectArguments[2], &objectEventId))
{
taskId = CreateTask(Task_DestroyDeoxysRock, 80);
gTasks[taskId].data[2] = objectEventId;
gTasks[taskId].tLocalId = gFieldEffectArguments[0];
gTasks[taskId].tMapNum = gFieldEffectArguments[1];
gTasks[taskId].tMapGroup = gFieldEffectArguments[2];
}
else
FieldEffectActiveListRemove(FLDEFF_DESTROY_DEOXYS_ROCK);
return FALSE;
}
// Task data for Task_DeoxysRockCameraShake
#define tShakeDelay data[0]
#define tShakeUp data[1]
#define tShake data[5]
#define tEndDelay data[6]
#define tEnding data[7]
static void Task_DeoxysRockCameraShake(u8 taskId)
{
s16 *data = gTasks[taskId].data;
if (data[7] != 0)
{
if (++data[6] > 20)
{
data[6] = 0;
if (data[5] != 0)
data[5]--;
}
}
else
data[5] = 4;
if (++data[0] > 1)
{
data[0] = 0;
if (++data[1] & 1)
SetCameraPanning(0, -data[5]);
else
SetCameraPanning(0, data[5]);
}
UpdateCameraPanning();
if (data[5] == 0)
DestroyTask(taskId);
}
static void StartEndingDeoxysRockCameraShake(u8 taskId)
{
gTasks[taskId].data[7] = 1;
}
#undef tShakeDelay
#undef tShakeUp
#undef tShake
#undef tEndDelay
#undef tEnding
static void Task_DestroyDeoxysRock(u8 taskId)
{
s16 *data = gTasks[taskId].data;
InstallCameraPanAheadCallback();
SetCameraPanningCallback(NULL);
sDestroyDeoxysRockEffectFuncs[tState](data, taskId);
}
static void DestroyDeoxysRockEffect_CameraShake(s16 *data, u8 taskId)
{
u8 newTaskId = CreateTask(Task_DeoxysRockCameraShake, 90);
PlaySE(SE_THUNDER2);
tCameraTaskId = newTaskId;
tState++;
}
static void DestroyDeoxysRockEffect_RockFragments(s16 *data, u8 taskId)
{
if (++tTimer > 120)
{
struct Sprite *sprite = &gSprites[gObjectEvents[tObjectEventId].spriteId];
gObjectEvents[tObjectEventId].invisible = TRUE;
BlendPalettes(PALETTES_BG, 0x10, RGB_WHITE);
BeginNormalPaletteFade(PALETTES_BG, 0, 0x10, 0, RGB_WHITE);
CreateDeoxysRockFragments(sprite);
PlaySE(SE_THUNDER);
StartEndingDeoxysRockCameraShake(tCameraTaskId);
tTimer = 0;
tState++;
}
}
static void DestroyDeoxysRockEffect_WaitAndEnd(s16 *data, u8 taskId)
{
if (!gPaletteFade.active && !FuncIsActiveTask(Task_DeoxysRockCameraShake))
{
InstallCameraPanAheadCallback();
RemoveObjectEventByLocalIdAndMap(tLocalId, tMapNum, tMapGroup);
FieldEffectActiveListRemove(FLDEFF_DESTROY_DEOXYS_ROCK);
DestroyTask(taskId);
}
}
#undef tState
#undef tObjectEventId
#undef tTimer
#undef tCameraTaskId
#undef tLocalId
#undef tMapNum
#undef tMapGroup
static void CreateDeoxysRockFragments(struct Sprite *sprite)
{
int i;
int xPos = (s16)gTotalCameraPixelOffsetX + sprite->x + sprite->x2;
int yPos = (s16)gTotalCameraPixelOffsetY + sprite->y + sprite->y2 - 4;
for (i = 0; i < 4; i++)
{
u8 spriteId = CreateSprite(&sSpriteTemplate_DeoxysRockFragment, xPos, yPos, 0);
if (spriteId != MAX_SPRITES)
{
StartSpriteAnim(&gSprites[spriteId], i);
gSprites[spriteId].data[0] = i;
gSprites[spriteId].oam.paletteNum = sprite->oam.paletteNum;
}
}
}
static void SpriteCB_DeoxysRockFragment(struct Sprite *sprite)
{
switch (sprite->data[0])
{
case 0:
sprite->x -= 16;
sprite->y -= 12;
break;
case 1:
sprite->x += 16;
sprite->y -= 12;
break;
case 2:
sprite->x -= 16;
sprite->y += 12;
break;
case 3:
sprite->x += 16;
sprite->y += 12;
break;
}
if (sprite->x < -4 || sprite->x > DISPLAY_WIDTH + 4 || sprite->y < -4 || sprite->y > DISPLAY_HEIGHT + 4)
DestroySprite(sprite);
}
static void Task_PhotoFlash(u8 taskId)
{
if (!gPaletteFade.active)
{
FieldEffectActiveListRemove(FLDEFF_PHOTO_FLASH);
DestroyTask(taskId);
}
}
u32 FldEff_PhotoFlash(void)
{
BlendPalettes(PALETTES_ALL, 0x10, RGB_WHITE);
BeginNormalPaletteFade(PALETTES_ALL, -1, 0x0F, 0x00, RGB_WHITE);
CreateTask(Task_PhotoFlash, 90);
return 0;
}
static u32 FldEff_Nop()
{
return 0;
}