tumbledemerald-legacy/src/fldeff_misc.c

1321 lines
37 KiB
C

#include "global.h"
#include "gpu_regs.h"
#include "palette.h"
#include "script.h"
#include "sound.h"
#include "task.h"
#include "strings.h"
#include "party_menu.h"
#include "fieldmap.h"
#include "field_effect.h"
#include "field_camera.h"
#include "field_player_avatar.h"
#include "fldeff.h"
#include "fldeff_misc.h"
#include "secret_base.h"
#include "event_data.h"
#include "event_scripts.h"
#include "event_object_movement.h"
#include "metatile_behavior.h"
#include "string_util.h"
#include "constants/field_effects.h"
#include "constants/metatile_behaviors.h"
#include "constants/metatile_labels.h"
#include "constants/songs.h"
EWRAM_DATA struct MapPosition gPlayerFacingPosition = {0};
static void Task_ComputerScreenOpenEffect(u8);
static void Task_ComputerScreenCloseEffect(u8);
static void CreateComputerScreenEffectTask(TaskFunc, u16, u16, u8);
static void Task_SecretBasePCTurnOn(u8);
static void Task_PopSecretBaseBalloon(u8);
static void DoBalloonSoundEffect(s16);
static void Task_WateringBerryTreeAnim_Start(u8);
static void Task_WateringBerryTreeAnim_Continue(u8);
static void Task_WateringBerryTreeAnim_End(u8);
static void FieldCallback_SecretBaseCave(void);
static void SpriteCB_CaveEntranceInit(struct Sprite *);
static void SpriteCB_CaveEntranceOpen(struct Sprite *);
static void SpriteCB_CaveEntranceEnd(struct Sprite *);
static void StartSecretBaseCaveFieldEffect(void);
static void FieldCallback_SecretBaseTree(void);
static void SpriteCB_TreeEntranceInit(struct Sprite *);
static void SpriteCB_TreeEntranceOpen(struct Sprite *);
static void SpriteCB_TreeEntranceEnd(struct Sprite *);
static void StartSecretBaseTreeFieldEffect(void);
static void FieldCallback_SecretBaseShrub(void);
static void SpriteCB_ShrubEntranceInit(struct Sprite *);
static void SpriteCB_ShrubEntranceOpen(struct Sprite *);
static void SpriteCB_ShrubEntranceEnd(struct Sprite *);
static void StartSecretBaseShrubFieldEffect(void);
static void SpriteCB_SandPillar_BreakTop(struct Sprite *);
static void SpriteCB_SandPillar_BreakBase(struct Sprite *);
static void SpriteCB_SandPillar_End(struct Sprite *);
static const u8 sSecretPowerCave_Gfx[] = INCBIN_U8("graphics/field_effects/pics/secret_power_cave.4bpp");
static const u8 sFiller[32] = {0};
static const u16 sSecretPowerCave_Pal[] = INCBIN_U16("graphics/field_effects/palettes/secret_power_cave.gbapal");
static const u8 sSecretPowerShrub_Gfx[] = INCBIN_U8("graphics/field_effects/pics/secret_power_shrub.4bpp");
static const u8 sSecretPowerTree_Gfx[] = INCBIN_U8("graphics/field_effects/pics/secret_power_tree.4bpp");
static const u16 sSecretPowerPlant_Pal[] = INCBIN_U16("graphics/field_effects/palettes/secret_power_plant.gbapal");
// TODO: These should also be combined into a single image, not matching for some reason
static const u8 sSandPillar0_Gfx[] = INCBIN_U8("graphics/field_effects/pics/sand_pillar/0.4bpp");
static const u8 sSandPillar1_Gfx[] = INCBIN_U8("graphics/field_effects/pics/sand_pillar/1.4bpp");
static const u8 sSandPillar2_Gfx[] = INCBIN_U8("graphics/field_effects/pics/sand_pillar/2.4bpp");
static const struct OamData sOam_SecretPower =
{
.y = 0,
.x = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x16),
.size = SPRITE_SIZE(16x16),
.priority = 2,
};
static const union AnimCmd sAnim_SecretPowerCave[] =
{
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(2, 8),
ANIMCMD_FRAME(3, 8),
ANIMCMD_FRAME(4, 8),
ANIMCMD_END,
};
static const union AnimCmd sAnim_VineDropLeft[] =
{
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(2, 8),
ANIMCMD_FRAME(3, 8),
ANIMCMD_FRAME(4, 8),
ANIMCMD_END,
};
static const union AnimCmd sAnim_VineRiseLeft[] =
{
ANIMCMD_FRAME(4, 8),
ANIMCMD_FRAME(3, 8),
ANIMCMD_FRAME(2, 8),
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(0, 8),
ANIMCMD_END,
};
static const union AnimCmd sAnim_VineDropRight[] =
{
ANIMCMD_FRAME(0, 8, .hFlip = TRUE),
ANIMCMD_FRAME(1, 8, .hFlip = TRUE),
ANIMCMD_FRAME(2, 8, .hFlip = TRUE),
ANIMCMD_FRAME(3, 8, .hFlip = TRUE),
ANIMCMD_FRAME(4, 8, .hFlip = TRUE),
ANIMCMD_END,
};
static const union AnimCmd sAnim_VineRiseRight[] =
{
ANIMCMD_FRAME(4, 8, .hFlip = TRUE),
ANIMCMD_FRAME(3, 8, .hFlip = TRUE),
ANIMCMD_FRAME(2, 8, .hFlip = TRUE),
ANIMCMD_FRAME(1, 8, .hFlip = TRUE),
ANIMCMD_FRAME(0, 8, .hFlip = TRUE),
ANIMCMD_END,
};
static const union AnimCmd sAnim_SecretPowerShrub[] =
{
ANIMCMD_FRAME(0, 8),
ANIMCMD_FRAME(1, 8),
ANIMCMD_FRAME(2, 8),
ANIMCMD_FRAME(3, 8),
ANIMCMD_FRAME(4, 8),
ANIMCMD_END,
};
static const union AnimCmd *const sAnimTable_SecretPowerCave[] =
{
sAnim_SecretPowerCave,
};
static const union AnimCmd *const sAnimTable_SecretPowerTree[] =
{
sAnim_VineDropLeft,
sAnim_VineRiseLeft,
sAnim_VineDropRight,
sAnim_VineRiseRight,
};
static const union AnimCmd *const sAnimTable_SecretPowerShrub[] =
{
sAnim_SecretPowerShrub,
};
static const struct SpriteFrameImage sPicTable_SecretPowerCave[] =
{
overworld_frame(sSecretPowerCave_Gfx, 2, 2, 0),
overworld_frame(sSecretPowerCave_Gfx, 2, 2, 1),
overworld_frame(sSecretPowerCave_Gfx, 2, 2, 2),
overworld_frame(sSecretPowerCave_Gfx, 2, 2, 3),
overworld_frame(sSecretPowerCave_Gfx, 2, 2, 4),
};
static const struct SpriteFrameImage sPicTable_SecretPowerTree[] =
{
overworld_frame(sSecretPowerTree_Gfx, 2, 2, 0),
overworld_frame(sSecretPowerTree_Gfx, 2, 2, 1),
overworld_frame(sSecretPowerTree_Gfx, 2, 2, 2),
overworld_frame(sSecretPowerTree_Gfx, 2, 2, 3),
overworld_frame(sSecretPowerTree_Gfx, 2, 2, 4),
// 6th frame exists but isnt accessed, the tree vine metatile is used instead
};
static const struct SpriteFrameImage sPicTable_SecretPowerShrub[] =
{
overworld_frame(sSecretPowerShrub_Gfx, 2, 2, 0),
overworld_frame(sSecretPowerShrub_Gfx, 2, 2, 1),
overworld_frame(sSecretPowerShrub_Gfx, 2, 2, 2),
overworld_frame(sSecretPowerShrub_Gfx, 2, 2, 3),
overworld_frame(sSecretPowerShrub_Gfx, 2, 2, 4),
};
static const struct SpriteTemplate sSpriteTemplate_SecretPowerCave =
{
.tileTag = TAG_NONE,
.paletteTag = FLDEFF_PAL_TAG_SECRET_POWER_TREE,
.oam = &sOam_SecretPower,
.anims = sAnimTable_SecretPowerCave,
.images = sPicTable_SecretPowerCave,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_CaveEntranceInit,
};
static const struct SpriteTemplate sSpriteTemplate_SecretPowerTree =
{
.tileTag = TAG_NONE,
.paletteTag = FLDEFF_PAL_TAG_SECRET_POWER_PLANT,
.oam = &sOam_SecretPower,
.anims = sAnimTable_SecretPowerTree,
.images = sPicTable_SecretPowerTree,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_TreeEntranceInit,
};
static const struct SpriteTemplate sSpriteTemplate_SecretPowerShrub =
{
.tileTag = TAG_NONE,
.paletteTag = FLDEFF_PAL_TAG_SECRET_POWER_PLANT,
.oam = &sOam_SecretPower,
.anims = sAnimTable_SecretPowerShrub,
.images = sPicTable_SecretPowerShrub,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_ShrubEntranceInit,
};
const struct SpritePalette gSpritePalette_SecretPower_Cave = {sSecretPowerCave_Pal, FLDEFF_PAL_TAG_SECRET_POWER_TREE};
const struct SpritePalette gSpritePalette_SecretPower_Plant = {sSecretPowerPlant_Pal, FLDEFF_PAL_TAG_SECRET_POWER_PLANT};
static const struct OamData sOam_SandPillar =
{
.x = 0,
.y = 0,
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.bpp = ST_OAM_4BPP,
.shape = SPRITE_SHAPE(16x32),
.size = SPRITE_SIZE(16x32),
.priority = 2,
};
static const union AnimCmd sAnim_SandPillar[] =
{
ANIMCMD_FRAME(0, 6),
ANIMCMD_FRAME(1, 6),
ANIMCMD_FRAME(2, 6),
ANIMCMD_END,
};
static const union AnimCmd *const sAnimTable_SandPillar[] =
{
sAnim_SandPillar,
};
static const struct SpriteFrameImage sPicTable_SandPillar[] =
{
{sSandPillar0_Gfx, sizeof(sSandPillar0_Gfx)},
{sSandPillar1_Gfx, sizeof(sSandPillar1_Gfx)},
{sSandPillar2_Gfx, sizeof(sSandPillar2_Gfx)},
};
static const struct SpriteTemplate sSpriteTemplate_SandPillar =
{
.tileTag = TAG_NONE,
.paletteTag = FLDEFF_PAL_TAG_SAND_PILLAR,
.oam = &sOam_SandPillar,
.anims = sAnimTable_SandPillar,
.images = sPicTable_SandPillar,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_SandPillar_BreakTop,
};
const struct SpritePalette gSpritePalette_SandPillar = {gTilesetPalettes_SecretBase[5], FLDEFF_PAL_TAG_SAND_PILLAR};
static const u8 sRecordMixLights_Gfx[] = INCBIN_U8("graphics/field_effects/pics/record_mix_lights.4bpp");
static const u16 sRecordMixLights_Pal[] = INCBIN_U16("graphics/field_effects/palettes/record_mix_lights.gbapal");
static const struct SpriteFrameImage sPicTable_RecordMixLights[] =
{
overworld_frame(sRecordMixLights_Gfx, 4, 1, 0),
overworld_frame(sRecordMixLights_Gfx, 4, 1, 1),
overworld_frame(sRecordMixLights_Gfx, 4, 1, 2),
};
static const struct SpritePalette sSpritePalette_RecordMixLights = {sRecordMixLights_Pal, 0x1000};
static const union AnimCmd sAnim_RecordMixLights[] =
{
ANIMCMD_FRAME(0, 30),
ANIMCMD_FRAME(1, 30),
ANIMCMD_FRAME(2, 30),
ANIMCMD_JUMP(0),
};
static const union AnimCmd *const sAnimTable_RecordMixLights[] =
{
sAnim_RecordMixLights,
};
static const struct SpriteTemplate sSpriteTemplate_RecordMixLights =
{
.tileTag = TAG_NONE,
.paletteTag = 0x1000,
.oam = &gObjectEventBaseOam_32x8,
.anims = sAnimTable_RecordMixLights,
.images = sPicTable_RecordMixLights,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy,
};
// For accessing pokemon storage PC or the Hall of Fame PC
void ComputerScreenOpenEffect(u16 increment, u16 unused, u8 priority)
{
CreateComputerScreenEffectTask(Task_ComputerScreenOpenEffect, increment, unused, priority);
}
void ComputerScreenCloseEffect(u16 increment, u16 unused, u8 priority)
{
CreateComputerScreenEffectTask(Task_ComputerScreenCloseEffect, increment, unused, priority);
}
bool8 IsComputerScreenOpenEffectActive(void)
{
return FuncIsActiveTask(Task_ComputerScreenOpenEffect);
}
bool8 IsComputerScreenCloseEffectActive(void)
{
return FuncIsActiveTask(Task_ComputerScreenCloseEffect);
}
#define tState data[0]
#define tHorzIncrement data[1]
#define tVertIncrement data[2]
#define tWinLeft data[3]
#define tWinRight data[4]
#define tWinTop data[5]
#define tWinBottom data[6]
#define tBlendCnt data[7]
#define tBlendY data[8]
static void CreateComputerScreenEffectTask(void (*taskfunc) (u8), u16 increment, u16 unused, u8 priority)
{
u8 taskId = CreateTask(taskfunc, priority);
gTasks[taskId].tState = 0;
gTasks[taskId].tHorzIncrement = increment == 0 ? 16 : increment;
gTasks[taskId].tVertIncrement = increment == 0 ? 20 : increment;
gTasks[taskId].func(taskId);
}
static void Task_ComputerScreenOpenEffect(u8 taskId)
{
struct Task *task = &gTasks[taskId];
switch (task->tState)
{
case 0:
task->tWinLeft = DISPLAY_WIDTH / 2;
task->tWinRight = DISPLAY_WIDTH / 2;
task->tWinTop = DISPLAY_HEIGHT / 2;
task->tWinBottom = DISPLAY_HEIGHT / 2 + 1;
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON);
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(task->tWinLeft, task->tWinRight));
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(task->tWinTop, task->tWinBottom));
SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR);
SetGpuReg(REG_OFFSET_WINOUT, 0);
break;
case 1:
task->tBlendCnt = GetGpuReg(REG_OFFSET_BLDCNT);
task->tBlendY = GetGpuReg(REG_OFFSET_BLDY);
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_ALL | BLDCNT_EFFECT_LIGHTEN);
SetGpuReg(REG_OFFSET_BLDY, 16);
break;
case 2:
task->tWinLeft -= task->tHorzIncrement;
task->tWinRight += task->tHorzIncrement;
if (task->tWinLeft < 1 || task->tWinRight > DISPLAY_WIDTH - 1)
{
task->tWinLeft = 0;
task->tWinRight = DISPLAY_WIDTH;
SetGpuReg(REG_OFFSET_BLDY, 0);
SetGpuReg(REG_OFFSET_BLDCNT, task->tBlendCnt);
BlendPalettes(PALETTES_ALL, 0, 0);
gPlttBufferFaded[0] = 0;
}
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(task->tWinLeft, task->tWinRight));
if (task->tWinLeft != 0)
return;
break;
case 3:
task->tWinTop -= task->tVertIncrement;
task->tWinBottom += task->tVertIncrement;
if (task->tWinTop < 1 || task->tWinBottom > DISPLAY_HEIGHT - 1)
{
task->tWinTop = 0;
task->tWinBottom = DISPLAY_HEIGHT;
ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON);
}
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(task->tWinTop, task->tWinBottom));
if (task->tWinTop != 0)
return;
break;
default:
SetGpuReg(REG_OFFSET_BLDCNT, task->tBlendCnt);
DestroyTask(taskId);
return;
}
task->tState++;
}
static void Task_ComputerScreenCloseEffect(u8 taskId)
{
struct Task *task = &gTasks[taskId];
switch (task->tState)
{
case 0:
gPlttBufferFaded[0] = 0;
break;
case 1:
task->tWinLeft = 0;
task->tWinRight = DISPLAY_WIDTH;
task->tWinTop = 0;
task->tWinBottom = DISPLAY_HEIGHT;
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON);
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(task->tWinLeft, task->tWinRight));
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(task->tWinTop, task->tWinBottom));
SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR);
SetGpuReg(REG_OFFSET_WINOUT, 0);
break;
case 2:
task->tWinTop += task->tVertIncrement;
task->tWinBottom -= task->tVertIncrement;
if (task->tWinTop >= DISPLAY_HEIGHT / 2 || task->tWinBottom <= DISPLAY_HEIGHT / 2 + 1)
{
task->tWinTop = DISPLAY_HEIGHT / 2;
task->tWinBottom = DISPLAY_HEIGHT / 2 + 1;
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_ALL | BLDCNT_EFFECT_LIGHTEN);
SetGpuReg(REG_OFFSET_BLDY, 16);
}
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(task->tWinTop, task->tWinBottom));
if (task->tWinTop != DISPLAY_HEIGHT / 2)
return;
break;
case 3:
task->tWinLeft += task->tHorzIncrement;
task->tWinRight -= task->tHorzIncrement;
if (task->tWinLeft >= DISPLAY_WIDTH / 2 || task->tWinRight <= DISPLAY_WIDTH / 2)
{
task->tWinLeft = DISPLAY_WIDTH / 2;
task->tWinRight = DISPLAY_WIDTH / 2;
BlendPalettes(PALETTES_ALL, 16, 0);
gPlttBufferFaded[0] = 0;
}
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(task->tWinLeft, task->tWinRight));
if (task->tWinLeft != DISPLAY_WIDTH / 2)
return;
break;
default:
ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON);
SetGpuReg(REG_OFFSET_BLDY, 0);
SetGpuReg(REG_OFFSET_BLDCNT, 0);
DestroyTask(taskId);
return;
}
task->tState++;
}
#undef tState
#undef tHorzIncrement
#undef tVertIncrement
#undef tWinLeft
#undef tWinRight
#undef tWinTop
#undef tWinBottom
#undef tBlendCnt
#undef tBlendY
static void SetCurrentSecretBase(void)
{
SetCurSecretBaseIdFromPosition(&gPlayerFacingPosition, gMapHeader.events);
TrySetCurSecretBaseIndex();
}
static void AdjustSecretPowerSpritePixelOffsets(void)
{
if (gPlayerAvatar.flags & (PLAYER_AVATAR_FLAG_MACH_BIKE | PLAYER_AVATAR_FLAG_ACRO_BIKE))
{
switch (gFieldEffectArguments[1])
{
case DIR_SOUTH:
gFieldEffectArguments[5] = 16;
gFieldEffectArguments[6] = 40;
break;
case DIR_NORTH:
gFieldEffectArguments[5] = 16;
gFieldEffectArguments[6] = 8;
break;
case DIR_WEST:
gFieldEffectArguments[5] = -8;
gFieldEffectArguments[6] = 24;
break;
case DIR_EAST:
gFieldEffectArguments[5] = 24;
gFieldEffectArguments[6] = 24;
break;
}
}
else
{
switch (gFieldEffectArguments[1])
{
case DIR_SOUTH:
gFieldEffectArguments[5] = 8;
gFieldEffectArguments[6] = 40;
break;
case DIR_NORTH:
gFieldEffectArguments[5] = 8;
gFieldEffectArguments[6] = 8;
break;
case DIR_WEST:
gFieldEffectArguments[5] = -8;
gFieldEffectArguments[6] = 24;
break;
case DIR_EAST:
gFieldEffectArguments[5] = 24;
gFieldEffectArguments[6] = 24;
break;
}
}
}
bool8 SetUpFieldMove_SecretPower(void)
{
u8 mb;
CheckPlayerHasSecretBase();
if (gSpecialVar_Result == 1 || GetPlayerFacingDirection() != DIR_NORTH)
return FALSE;
GetXYCoordsOneStepInFrontOfPlayer(&gPlayerFacingPosition.x, &gPlayerFacingPosition.y);
mb = MapGridGetMetatileBehaviorAt(gPlayerFacingPosition.x, gPlayerFacingPosition.y);
if (MetatileBehavior_IsSecretBaseCave(mb) == TRUE)
{
SetCurrentSecretBase();
gFieldCallback2 = FieldCallback_PrepareFadeInFromMenu;
gPostMenuFieldCallback = FieldCallback_SecretBaseCave;
return TRUE;
}
if (MetatileBehavior_IsSecretBaseTree(mb) == TRUE)
{
SetCurrentSecretBase();
gFieldCallback2 = FieldCallback_PrepareFadeInFromMenu;
gPostMenuFieldCallback = FieldCallback_SecretBaseTree;
return TRUE;
}
if (MetatileBehavior_IsSecretBaseShrub(mb) == TRUE)
{
SetCurrentSecretBase();
gFieldCallback2 = FieldCallback_PrepareFadeInFromMenu;
gPostMenuFieldCallback = FieldCallback_SecretBaseShrub;
return TRUE;
}
return FALSE;
}
static void FieldCallback_SecretBaseCave(void)
{
gFieldEffectArguments[0] = GetCursorSelectionMonId();
ScriptContext1_SetupScript(SecretBase_EventScript_CaveUseSecretPower);
}
bool8 FldEff_UseSecretPowerCave(void)
{
u8 taskId = CreateFieldMoveTask();
gTasks[taskId].data[8] = (u32)StartSecretBaseCaveFieldEffect >> 16;
gTasks[taskId].data[9] = (u32)StartSecretBaseCaveFieldEffect;
return FALSE;
}
static void StartSecretBaseCaveFieldEffect(void)
{
FieldEffectActiveListRemove(FLDEFF_USE_SECRET_POWER_CAVE);
FieldEffectStart(FLDEFF_SECRET_POWER_CAVE);
}
bool8 FldEff_SecretPowerCave(void)
{
AdjustSecretPowerSpritePixelOffsets();
CreateSprite(&sSpriteTemplate_SecretPowerCave,
gSprites[gPlayerAvatar.spriteId].oam.x + gFieldEffectArguments[5],
gSprites[gPlayerAvatar.spriteId].oam.y + gFieldEffectArguments[6],
148);
return FALSE;
}
static void SpriteCB_CaveEntranceInit(struct Sprite *sprite)
{
PlaySE(SE_M_ROCK_THROW);
sprite->data[0] = 0;
sprite->callback = SpriteCB_CaveEntranceOpen;
}
static void SpriteCB_CaveEntranceOpen(struct Sprite *sprite)
{
if (sprite->data[0] < 40)
{
if (++sprite->data[0] == 20)
ToggleSecretBaseEntranceMetatile();
}
else
{
sprite->data[0] = 0;
sprite->callback = SpriteCB_CaveEntranceEnd;
}
}
static void SpriteCB_CaveEntranceEnd(struct Sprite *sprite)
{
FieldEffectStop(sprite, FLDEFF_SECRET_POWER_CAVE);
EnableBothScriptContexts();
}
static void FieldCallback_SecretBaseTree(void)
{
gFieldEffectArguments[0] = GetCursorSelectionMonId();
ScriptContext1_SetupScript(SecretBase_EventScript_TreeUseSecretPower);
}
bool8 FldEff_UseSecretPowerTree(void)
{
u8 taskId = CreateFieldMoveTask();
gTasks[taskId].data[8] = (u32)StartSecretBaseTreeFieldEffect >> 16;
gTasks[taskId].data[9] = (u32)StartSecretBaseTreeFieldEffect;
return FALSE;
}
static void StartSecretBaseTreeFieldEffect(void)
{
FieldEffectActiveListRemove(FLDEFF_USE_SECRET_POWER_TREE);
FieldEffectStart(FLDEFF_SECRET_POWER_TREE);
}
bool8 FldEff_SecretPowerTree(void)
{
s16 mb = MapGridGetMetatileBehaviorAt(gPlayerFacingPosition.x, gPlayerFacingPosition.y) & 0xFFF;
if (mb == MB_SECRET_BASE_SPOT_TREE_LEFT)
gFieldEffectArguments[7] = 0;
if (mb == MB_SECRET_BASE_SPOT_TREE_RIGHT)
gFieldEffectArguments[7] = 2;
AdjustSecretPowerSpritePixelOffsets();
CreateSprite(&sSpriteTemplate_SecretPowerTree,
gSprites[gPlayerAvatar.spriteId].oam.x + gFieldEffectArguments[5],
gSprites[gPlayerAvatar.spriteId].oam.y + gFieldEffectArguments[6],
148);
if (gFieldEffectArguments[7] == 1 || gFieldEffectArguments[7] == 3)
ToggleSecretBaseEntranceMetatile();
return FALSE;
}
static void SpriteCB_TreeEntranceInit(struct Sprite *sprite)
{
PlaySE(SE_M_SCRATCH);
sprite->animNum = gFieldEffectArguments[7];
sprite->data[0] = 0;
sprite->callback = SpriteCB_TreeEntranceOpen;
}
static void SpriteCB_TreeEntranceOpen(struct Sprite *sprite)
{
sprite->data[0]++;
if (sprite->data[0] >= 40)
{
if (gFieldEffectArguments[7] == 0 || gFieldEffectArguments[7] == 2)
ToggleSecretBaseEntranceMetatile();
sprite->data[0] = 0;
sprite->callback = SpriteCB_TreeEntranceEnd;
}
}
static void SpriteCB_TreeEntranceEnd(struct Sprite *sprite)
{
FieldEffectStop(sprite, FLDEFF_SECRET_POWER_TREE);
EnableBothScriptContexts();
}
static void FieldCallback_SecretBaseShrub(void)
{
gFieldEffectArguments[0] = GetCursorSelectionMonId();
ScriptContext1_SetupScript(SecretBase_EventScript_ShrubUseSecretPower);
}
bool8 FldEff_UseSecretPowerShrub(void)
{
u8 taskId = CreateFieldMoveTask();
gTasks[taskId].data[8] = (u32)StartSecretBaseShrubFieldEffect >> 16;
gTasks[taskId].data[9] = (u32)StartSecretBaseShrubFieldEffect;
return FALSE;
}
static void StartSecretBaseShrubFieldEffect(void)
{
FieldEffectActiveListRemove(FLDEFF_USE_SECRET_POWER_SHRUB);
FieldEffectStart(FLDEFF_SECRET_POWER_SHRUB);
}
bool8 FldEff_SecretPowerShrub(void)
{
AdjustSecretPowerSpritePixelOffsets();
CreateSprite(&sSpriteTemplate_SecretPowerShrub,
gSprites[gPlayerAvatar.spriteId].oam.x + gFieldEffectArguments[5],
gSprites[gPlayerAvatar.spriteId].oam.y + gFieldEffectArguments[6],
148);
return FALSE;
}
static void SpriteCB_ShrubEntranceInit(struct Sprite *sprite)
{
PlaySE(SE_M_POISON_POWDER);
sprite->data[0] = 0;
sprite->callback = SpriteCB_ShrubEntranceOpen;
}
static void SpriteCB_ShrubEntranceOpen(struct Sprite *sprite)
{
if (sprite->data[0] < 40)
{
sprite->data[0]++;
if (sprite->data[0] == 20)
ToggleSecretBaseEntranceMetatile();
}
else
{
sprite->data[0] = 0;
sprite->callback = SpriteCB_ShrubEntranceEnd;
}
}
static void SpriteCB_ShrubEntranceEnd(struct Sprite *sprite)
{
FieldEffectStop(sprite, FLDEFF_SECRET_POWER_SHRUB);
EnableBothScriptContexts();
}
#define tX data[0]
#define tY data[1]
#define tState data[2]
bool8 FldEff_SecretBasePCTurnOn(void)
{
s16 x, y;
u8 taskId;
GetXYCoordsOneStepInFrontOfPlayer(&x, &y);
taskId = CreateTask(Task_SecretBasePCTurnOn, 0);
gTasks[taskId].tX = x;
gTasks[taskId].tY = y;
gTasks[taskId].tState = 0;
return FALSE;
}
static void Task_SecretBasePCTurnOn(u8 taskId)
{
s16 *data = gTasks[taskId].data;
switch (tState)
{
case 4:
case 12:
MapGridSetMetatileIdAt(tX, tY, METATILE_SecretBase_PC_On);
CurrentMapDrawMetatileAt(tX, tY);
break;
case 8:
case 16:
MapGridSetMetatileIdAt(tX, tY, METATILE_SecretBase_PC);
CurrentMapDrawMetatileAt(tX, tY);
break;
case 20:
MapGridSetMetatileIdAt(tX, tY, METATILE_SecretBase_PC_On);
CurrentMapDrawMetatileAt(tX, tY);
FieldEffectActiveListRemove(FLDEFF_PCTURN_ON);
EnableBothScriptContexts();
DestroyTask(taskId);
return;
}
tState++;
}
#undef tX
#undef tY
#undef tState
void DoSecretBasePCTurnOffEffect(void)
{
s16 x, y;
GetXYCoordsOneStepInFrontOfPlayer(&x, &y);
PlaySE(SE_PC_OFF);
if (!VarGet(VAR_CURRENT_SECRET_BASE))
MapGridSetMetatileIdAt(x, y, METATILE_SecretBase_PC | METATILE_COLLISION_MASK);
else
MapGridSetMetatileIdAt(x, y, METATILE_SecretBase_RegisterPC | METATILE_COLLISION_MASK);
CurrentMapDrawMetatileAt(x, y);
}
void PopSecretBaseBalloon(s16 metatileId, s16 x, s16 y)
{
u8 taskId = CreateTask(Task_PopSecretBaseBalloon, 0);
gTasks[taskId].data[0] = metatileId;
gTasks[taskId].data[1] = x;
gTasks[taskId].data[2] = y;
gTasks[taskId].data[3] = 0;
gTasks[taskId].data[4] = 1;
}
static void Task_PopSecretBaseBalloon(u8 taskId)
{
s16 *data = gTasks[taskId].data;
if (data[3] == 6)
data[3] = 0;
else
data[3]++;
if (data[3] == 0)
{
if (data[4] == 2)
DoBalloonSoundEffect(data[0]);
MapGridSetMetatileIdAt(data[1], data[2], data[0] + data[4]);
CurrentMapDrawMetatileAt(data[1], data[2]);
if (data[4] == 3)
DestroyTask(taskId);
else
data[4]++;
}
}
static void DoBalloonSoundEffect(s16 metatileId)
{
switch (metatileId)
{
case METATILE_SecretBase_RedBalloon:
PlaySE(SE_BALLOON_RED);
break;
case METATILE_SecretBase_BlueBalloon:
PlaySE(SE_BALLOON_BLUE);
break;
case METATILE_SecretBase_YellowBalloon:
PlaySE(SE_BALLOON_YELLOW);
break;
case METATILE_SecretBase_MudBall:
PlaySE(SE_MUD_BALL);
break;
}
}
bool8 FldEff_Nop47(void)
{
return FALSE;
}
bool8 FldEff_Nop48(void)
{
return FALSE;
}
static void DoSecretBaseBreakableDoorEffect(s16 x, s16 y)
{
PlaySE(SE_BREAKABLE_DOOR);
MapGridSetMetatileIdAt(x, y, METATILE_SecretBase_BreakableDoor_BottomOpen);
MapGridSetMetatileIdAt(x, y - 1, METATILE_SecretBase_BreakableDoor_TopOpen);
CurrentMapDrawMetatileAt(x, y);
CurrentMapDrawMetatileAt(x, y - 1);
}
static void Task_ShatterSecretBaseBreakableDoor(u8 taskId)
{
if (gTasks[taskId].data[0] == 7)
{
DoSecretBaseBreakableDoorEffect(gTasks[taskId].data[1], gTasks[taskId].data[2]);
DestroyTask(taskId);
}
else
{
gTasks[taskId].data[0]++;
}
}
void ShatterSecretBaseBreakableDoor(s16 x, s16 y)
{
u8 dir = GetPlayerFacingDirection();
if (dir == DIR_SOUTH)
{
DoSecretBaseBreakableDoorEffect(x, y);
}
else if (dir == DIR_NORTH)
{
u8 taskId = CreateTask(Task_ShatterSecretBaseBreakableDoor, 5);
gTasks[taskId].data[0] = 0;
gTasks[taskId].data[1] = x;
gTasks[taskId].data[2] = y;
}
}
#define tMetatileID data[0]
static void Task_SecretBaseMusicNoteMatSound(u8 taskId)
{
if (gTasks[taskId].data[1] == 7)
{
switch (gTasks[taskId].tMetatileID)
{
case METATILE_SecretBase_NoteMat_C_Low:
PlaySE(SE_NOTE_C);
break;
case METATILE_SecretBase_NoteMat_D:
PlaySE(SE_NOTE_D);
break;
case METATILE_SecretBase_NoteMat_E:
PlaySE(SE_NOTE_E);
break;
case METATILE_SecretBase_NoteMat_F:
PlaySE(SE_NOTE_F);
break;
case METATILE_SecretBase_NoteMat_G:
PlaySE(SE_NOTE_G);
break;
case METATILE_SecretBase_NoteMat_A:
PlaySE(SE_NOTE_A);
break;
case METATILE_SecretBase_NoteMat_B:
PlaySE(SE_NOTE_B);
break;
case METATILE_SecretBase_NoteMat_C_High:
PlaySE(SE_NOTE_C_HIGH);
break;
}
DestroyTask(taskId);
}
else
{
gTasks[taskId].data[1]++;
}
}
void PlaySecretBaseMusicNoteMatSound(s16 metatileId)
{
u8 taskId = CreateTask(Task_SecretBaseMusicNoteMatSound, 5);
gTasks[taskId].tMetatileID = metatileId;
gTasks[taskId].data[1] = 0;
}
#undef tMetatileID
static void SpriteCB_GlitterMatSparkle(struct Sprite *sprite)
{
sprite->data[0]++;
if (sprite->data[0] == 8)
PlaySE(SE_M_HEAL_BELL);
if (sprite->data[0] >= 32)
DestroySprite(sprite);
}
void DoSecretBaseGlitterMatSparkle(void)
{
s16 x = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.x;
s16 y = gObjectEvents[gPlayerAvatar.objectEventId].currentCoords.y;
u8 spriteId;
SetSpritePosToOffsetMapCoords(&x, &y, 8, 4);
spriteId = CreateSpriteAtEnd(gFieldEffectObjectTemplatePointers[FLDEFFOBJ_SPARKLE], x, y, 0);
if (spriteId != MAX_SPRITES)
{
gSprites[spriteId].coordOffsetEnabled = TRUE;
gSprites[spriteId].oam.priority = 1;
gSprites[spriteId].oam.paletteNum = 5;
gSprites[spriteId].callback = SpriteCB_GlitterMatSparkle;
gSprites[spriteId].data[0] = 0;
}
}
bool8 FldEff_SandPillar(void)
{
s16 x, y;
ScriptContext2_Enable();
GetXYCoordsOneStepInFrontOfPlayer(&x, &y);
gFieldEffectArguments[5] = x;
gFieldEffectArguments[6] = y;
switch (GetPlayerFacingDirection())
{
case DIR_SOUTH:
CreateSprite(&sSpriteTemplate_SandPillar,
gSprites[gPlayerAvatar.spriteId].oam.x + 8,
gSprites[gPlayerAvatar.spriteId].oam.y + 32,
0);
break;
case DIR_NORTH:
CreateSprite(&sSpriteTemplate_SandPillar,
gSprites[gPlayerAvatar.spriteId].oam.x + 8,
gSprites[gPlayerAvatar.spriteId].oam.y,
148);
break;
case DIR_WEST:
CreateSprite(&sSpriteTemplate_SandPillar,
gSprites[gPlayerAvatar.spriteId].oam.x - 8,
gSprites[gPlayerAvatar.spriteId].oam.y + 16,
148);
break;
case DIR_EAST:
CreateSprite(&sSpriteTemplate_SandPillar,
gSprites[gPlayerAvatar.spriteId].oam.x + 24,
gSprites[gPlayerAvatar.spriteId].oam.y + 16,
148);
break;
}
return FALSE;
}
static void SpriteCB_SandPillar_BreakTop(struct Sprite *sprite)
{
PlaySE(SE_M_ROCK_THROW);
if (MapGridGetMetatileIdAt(gFieldEffectArguments[5], gFieldEffectArguments[6] - 1) == METATILE_SecretBase_SandOrnament_TopWall)
MapGridSetMetatileIdAt(gFieldEffectArguments[5], gFieldEffectArguments[6] - 1, METATILE_SecretBase_Wall_TopMid | METATILE_COLLISION_MASK);
else
MapGridSetMetatileIdAt(gFieldEffectArguments[5], gFieldEffectArguments[6] - 1, METATILE_SecretBase_SandOrnament_BrokenTop);
MapGridSetMetatileIdAt(gFieldEffectArguments[5], gFieldEffectArguments[6], METATILE_SecretBase_Ground);
CurrentMapDrawMetatileAt(gFieldEffectArguments[5], gFieldEffectArguments[6] - 1);
CurrentMapDrawMetatileAt(gFieldEffectArguments[5], gFieldEffectArguments[6]);
sprite->data[0] = 0;
sprite->callback = SpriteCB_SandPillar_BreakBase;
}
static void SpriteCB_SandPillar_BreakBase(struct Sprite *sprite)
{
if (sprite->data[0] < 18)
{
sprite->data[0]++;
}
else
{
MapGridSetMetatileIdAt(gFieldEffectArguments[5], gFieldEffectArguments[6], METATILE_SecretBase_SandOrnament_BrokenBase | METATILE_COLLISION_MASK);
CurrentMapDrawMetatileAt(gFieldEffectArguments[5], gFieldEffectArguments[6]);
sprite->data[0] = 0;
sprite->callback = SpriteCB_SandPillar_End;
}
}
static void SpriteCB_SandPillar_End(struct Sprite *sprite)
{
FieldEffectStop(sprite, FLDEFF_SAND_PILLAR);
EnableBothScriptContexts();
}
void InteractWithShieldOrTVDecoration(void)
{
s16 x, y;
s32 metatileId;
GetXYCoordsOneStepInFrontOfPlayer(&x, &y);
metatileId = MapGridGetMetatileIdAt(x, y);
switch (metatileId)
{
case METATILE_SecretBase_GoldShield_Base1:
ConvertIntToDecimalStringN(gStringVar1, 100, STR_CONV_MODE_LEFT_ALIGN, 3);
StringCopy(gStringVar2, gText_Gold);
gSpecialVar_Result = 0;
if (!VarGet(VAR_CURRENT_SECRET_BASE))
return;
VarSet(VAR_SECRET_BASE_LOW_TV_FLAGS, VarGet(VAR_SECRET_BASE_LOW_TV_FLAGS) | SECRET_BASE_USED_GOLD_SHIELD);
break;
case METATILE_SecretBase_SilverShield_Base1:
ConvertIntToDecimalStringN(gStringVar1, 50, STR_CONV_MODE_LEFT_ALIGN, 2);
StringCopy(gStringVar2, gText_Silver);
gSpecialVar_Result = 0;
if (!VarGet(VAR_CURRENT_SECRET_BASE))
return;
VarSet(VAR_SECRET_BASE_LOW_TV_FLAGS, VarGet(VAR_SECRET_BASE_LOW_TV_FLAGS) | SECRET_BASE_USED_SILVER_SHIELD);
break;
case METATILE_SecretBase_TV:
gSpecialVar_Result = 1;
if (!VarGet(VAR_CURRENT_SECRET_BASE))
return;
VarSet(VAR_SECRET_BASE_LOW_TV_FLAGS, VarGet(VAR_SECRET_BASE_LOW_TV_FLAGS) | SECRET_BASE_USED_TV);
break;
case METATILE_SecretBase_RoundTV:
gSpecialVar_Result = 2;
if (!VarGet(VAR_CURRENT_SECRET_BASE))
return;
VarSet(VAR_SECRET_BASE_LOW_TV_FLAGS, VarGet(VAR_SECRET_BASE_LOW_TV_FLAGS) | SECRET_BASE_USED_TV);
break;
case METATILE_SecretBase_CuteTV:
gSpecialVar_Result = 3;
if (!VarGet(VAR_CURRENT_SECRET_BASE))
return;
VarSet(VAR_SECRET_BASE_LOW_TV_FLAGS, VarGet(VAR_SECRET_BASE_LOW_TV_FLAGS) | SECRET_BASE_USED_TV);
break;
}
}
// As opposed to a small one (single metatile) like the balloons
bool8 IsLargeBreakableDecoration(u16 metatileId, bool8 checkBase)
{
if (!CurMapIsSecretBase())
return FALSE;
if (!checkBase)
{
if (metatileId == METATILE_SecretBase_SandOrnament_Top || metatileId == METATILE_SecretBase_SandOrnament_TopWall)
return TRUE;
if (metatileId == METATILE_SecretBase_BreakableDoor_TopClosed)
return TRUE;
}
else
{
if (metatileId == METATILE_SecretBase_SandOrnament_Base1)
return TRUE;
if (metatileId == METATILE_SecretBase_BreakableDoor_BottomClosed)
return TRUE;
}
return FALSE;
}
static void Task_FieldPoisonEffect(u8 taskId)
{
s16 *data = gTasks[taskId].data;
switch (data[0])
{
case 0:
data[1] += 2;
if (data[1] > 8)
data[0]++;
break;
case 1:
data[1] -= 2;
if (data[1] == 0)
data[0]++;
break;
case 2:
DestroyTask(taskId);
return;
}
SetGpuReg(REG_OFFSET_MOSAIC, (data[1] << 4) | data[1]);
}
void FldEffPoison_Start(void)
{
PlaySE(SE_FIELD_POISON);
CreateTask(Task_FieldPoisonEffect, 80);
}
bool32 FldEffPoison_IsActive(void)
{
return FuncIsActiveTask(Task_FieldPoisonEffect);
}
static void Task_WateringBerryTreeAnim(u8 taskId)
{
gTasks[taskId].func = Task_WateringBerryTreeAnim_Start;
}
static void Task_WateringBerryTreeAnim_Start(u8 taskId)
{
struct ObjectEvent *playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (!ObjectEventIsMovementOverridden(playerObjEvent)
|| ObjectEventClearHeldMovementIfFinished(playerObjEvent))
{
// Start watering
SetPlayerAvatarWatering(GetPlayerFacingDirection());
ObjectEventSetHeldMovement(playerObjEvent, GetWalkInPlaceNormalMovementAction(GetPlayerFacingDirection()));
gTasks[taskId].func = Task_WateringBerryTreeAnim_Continue;
}
}
static void Task_WateringBerryTreeAnim_Continue(u8 taskId)
{
struct ObjectEvent *playerObjEvent = &gObjectEvents[gPlayerAvatar.objectEventId];
if (ObjectEventClearHeldMovementIfFinished(playerObjEvent))
{
s16 value = gTasks[taskId].data[1]++;
// Continue holding watering action 10 times, then end
if (value < 10)
ObjectEventSetHeldMovement(playerObjEvent, GetWalkInPlaceNormalMovementAction(GetPlayerFacingDirection()));
else
gTasks[taskId].func = Task_WateringBerryTreeAnim_End;
}
}
static void Task_WateringBerryTreeAnim_End(u8 taskId)
{
SetPlayerAvatarTransitionFlags(GetPlayerAvatarFlags());
DestroyTask(taskId);
EnableBothScriptContexts();
}
void DoWateringBerryTreeAnim(void)
{
CreateTask(Task_WateringBerryTreeAnim, 80);
}
// The lights that blink on the counter when mixing records in the cable club
u8 CreateRecordMixingLights(void)
{
u8 spriteId;
LoadSpritePalette(&sSpritePalette_RecordMixLights);
spriteId = CreateSprite(&sSpriteTemplate_RecordMixLights, 0, 0, 82);
if (spriteId == MAX_SPRITES)
{
return MAX_SPRITES;
}
else
{
struct Sprite *sprite = &gSprites[spriteId];
GetMapCoordsFromSpritePos(16, 13, &sprite->x, &sprite->y);
sprite->coordOffsetEnabled = TRUE;
sprite->x += 16;
sprite->y += 2;
}
return spriteId;
}
void DestroyRecordMixingLights(void)
{
int i;
for (i = 0; i < MAX_SPRITES; i++)
{
if (gSprites[i].template == &sSpriteTemplate_RecordMixLights)
{
FreeSpritePalette(&gSprites[i]);
DestroySprite(&gSprites[i]);
}
}
}