mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-12-26 03:34:15 +01:00
2656 lines
70 KiB
C
2656 lines
70 KiB
C
#include "global.h"
|
|
#include "battle_anim.h"
|
|
#include "event_object_movement.h"
|
|
#include "fieldmap.h"
|
|
#include "field_weather.h"
|
|
#include "overworld.h"
|
|
#include "random.h"
|
|
#include "script.h"
|
|
#include "constants/weather.h"
|
|
#include "constants/songs.h"
|
|
#include "sound.h"
|
|
#include "sprite.h"
|
|
#include "task.h"
|
|
#include "trig.h"
|
|
#include "gpu_regs.h"
|
|
#include "palette.h"
|
|
#include "constants/rgb.h"
|
|
|
|
EWRAM_DATA static u8 sCurrentAbnormalWeather = 0;
|
|
EWRAM_DATA static u16 sUnusedWeatherRelated = 0;
|
|
|
|
const u16 gCloudsWeatherPalette[] = INCBIN_U16("graphics/weather/cloud.gbapal");
|
|
const u16 gSandstormWeatherPalette[] = INCBIN_U16("graphics/weather/sandstorm.gbapal");
|
|
const u8 gWeatherFogDiagonalTiles[] = INCBIN_U8("graphics/weather/fog_diagonal.4bpp");
|
|
const u8 gWeatherFogHorizontalTiles[] = INCBIN_U8("graphics/weather/fog_horizontal.4bpp");
|
|
const u8 gWeatherCloudTiles[] = INCBIN_U8("graphics/weather/cloud.4bpp");
|
|
const u8 gWeatherSnow1Tiles[] = INCBIN_U8("graphics/weather/snow0.4bpp");
|
|
const u8 gWeatherSnow2Tiles[] = INCBIN_U8("graphics/weather/snow1.4bpp");
|
|
const u8 gWeatherBubbleTiles[] = INCBIN_U8("graphics/weather/bubble.4bpp");
|
|
const u8 gWeatherAshTiles[] = INCBIN_U8("graphics/weather/ash.4bpp");
|
|
const u8 gWeatherRainTiles[] = INCBIN_U8("graphics/weather/rain.4bpp");
|
|
const u8 gWeatherSandstormTiles[] = INCBIN_U8("graphics/weather/sandstorm.4bpp");
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_SUNNY_CLOUDS
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void CreateCloudSprites(void);
|
|
static void DestroyCloudSprites(void);
|
|
static void UpdateCloudSprite(struct Sprite *);
|
|
|
|
// The clouds are positioned on the map's grid.
|
|
// These coordinates are for the lower half of Route 120.
|
|
static const struct Coords16 sCloudSpriteMapCoords[] =
|
|
{
|
|
{ 0, 66},
|
|
{ 5, 73},
|
|
{10, 78},
|
|
};
|
|
|
|
static const struct SpriteSheet sCloudSpriteSheet =
|
|
{
|
|
.data = gWeatherCloudTiles,
|
|
.size = sizeof(gWeatherCloudTiles),
|
|
.tag = GFXTAG_CLOUD
|
|
};
|
|
|
|
static const struct OamData sCloudSpriteOamData =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_BLEND,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0,
|
|
.priority = 3,
|
|
.paletteNum = 0,
|
|
.affineParam = 0,
|
|
};
|
|
|
|
static const union AnimCmd sCloudSpriteAnimCmd[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sCloudSpriteAnimCmds[] =
|
|
{
|
|
sCloudSpriteAnimCmd,
|
|
};
|
|
|
|
static const struct SpriteTemplate sCloudSpriteTemplate =
|
|
{
|
|
.tileTag = GFXTAG_CLOUD,
|
|
.paletteTag = PALTAG_WEATHER_2,
|
|
.oam = &sCloudSpriteOamData,
|
|
.anims = sCloudSpriteAnimCmds,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = UpdateCloudSprite,
|
|
};
|
|
|
|
void Clouds_InitVars(void)
|
|
{
|
|
gWeatherPtr->noShadows = FALSE;
|
|
gWeatherPtr->targetColorMapIndex = 0;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE;
|
|
gWeatherPtr->initStep = 0;
|
|
if (gWeatherPtr->cloudSpritesCreated == FALSE)
|
|
Weather_SetBlendCoeffs(0, 16);
|
|
}
|
|
|
|
void Clouds_InitAll(void)
|
|
{
|
|
Clouds_InitVars();
|
|
while (gWeatherPtr->weatherGfxLoaded == FALSE)
|
|
Clouds_Main();
|
|
}
|
|
|
|
void Clouds_Main(void)
|
|
{
|
|
switch (gWeatherPtr->initStep)
|
|
{
|
|
case 0:
|
|
CreateCloudSprites();
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 1:
|
|
Weather_SetTargetBlendCoeffs(12, 8, 1);
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 2:
|
|
if (Weather_UpdateBlend())
|
|
{
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool8 Clouds_Finish(void)
|
|
{
|
|
switch (gWeatherPtr->finishStep)
|
|
{
|
|
case 0:
|
|
Weather_SetTargetBlendCoeffs(0, 16, 1);
|
|
gWeatherPtr->finishStep++;
|
|
return TRUE;
|
|
case 1:
|
|
if (Weather_UpdateBlend())
|
|
{
|
|
DestroyCloudSprites();
|
|
gWeatherPtr->finishStep++;
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
void Sunny_InitVars(void)
|
|
{
|
|
gWeatherPtr->targetColorMapIndex = 0;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
Weather_SetBlendCoeffs(8, 12);
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Sunny_InitAll(void)
|
|
{
|
|
Sunny_InitVars();
|
|
}
|
|
|
|
void Sunny_Main(void)
|
|
{
|
|
}
|
|
|
|
bool8 Sunny_Finish(void)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
static void CreateCloudSprites(void)
|
|
{
|
|
u16 i;
|
|
u8 spriteId;
|
|
struct Sprite *sprite;
|
|
|
|
if (gWeatherPtr->cloudSpritesCreated == TRUE)
|
|
return;
|
|
|
|
LoadSpriteSheet(&sCloudSpriteSheet);
|
|
LoadCustomWeatherSpritePalette(gCloudsWeatherPalette);
|
|
for (i = 0; i < NUM_CLOUD_SPRITES; i++)
|
|
{
|
|
spriteId = CreateSprite(&sCloudSpriteTemplate, 0, 0, 0xFF);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
gWeatherPtr->sprites.s1.cloudSprites[i] = &gSprites[spriteId];
|
|
sprite = gWeatherPtr->sprites.s1.cloudSprites[i];
|
|
SetSpritePosToMapCoords(sCloudSpriteMapCoords[i].x + MAP_OFFSET, sCloudSpriteMapCoords[i].y + MAP_OFFSET, &sprite->x, &sprite->y);
|
|
sprite->coordOffsetEnabled = TRUE;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->sprites.s1.cloudSprites[i] = NULL;
|
|
}
|
|
}
|
|
|
|
gWeatherPtr->cloudSpritesCreated = TRUE;
|
|
}
|
|
|
|
static void DestroyCloudSprites(void)
|
|
{
|
|
u16 i;
|
|
|
|
if (!gWeatherPtr->cloudSpritesCreated)
|
|
return;
|
|
|
|
for (i = 0; i < NUM_CLOUD_SPRITES; i++)
|
|
{
|
|
if (gWeatherPtr->sprites.s1.cloudSprites[i] != NULL)
|
|
DestroySprite(gWeatherPtr->sprites.s1.cloudSprites[i]);
|
|
}
|
|
|
|
FreeSpriteTilesByTag(GFXTAG_CLOUD);
|
|
gWeatherPtr->cloudSpritesCreated = FALSE;
|
|
}
|
|
|
|
static void UpdateCloudSprite(struct Sprite *sprite)
|
|
{
|
|
// Move 1 pixel left every 2 frames.
|
|
sprite->data[0] = (sprite->data[0] + 1) & 1;
|
|
if (sprite->data[0])
|
|
sprite->x--;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_DROUGHT
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void UpdateDroughtBlend(u8);
|
|
|
|
void Drought_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = 0;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE;
|
|
gWeatherPtr->targetColorMapIndex = 0;
|
|
gWeatherPtr->colorMapStepDelay = 0;
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Drought_InitAll(void)
|
|
{
|
|
Drought_InitVars();
|
|
while (gWeatherPtr->weatherGfxLoaded == FALSE)
|
|
Drought_Main();
|
|
}
|
|
|
|
void Drought_Main(void)
|
|
{
|
|
switch (gWeatherPtr->initStep)
|
|
{
|
|
case 0:
|
|
if (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_CHANGING_WEATHER)
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 1:
|
|
ResetDroughtWeatherPaletteLoading();
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 2:
|
|
if (LoadDroughtWeatherPalettes() == FALSE)
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 3:
|
|
DroughtStateInit();
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 4:
|
|
DroughtStateRun();
|
|
if (gWeatherPtr->droughtBrightnessStage == 6)
|
|
{
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
default:
|
|
DroughtStateRun();
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool8 Drought_Finish(void)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
void StartDroughtWeatherBlend(void)
|
|
{
|
|
CreateTask(UpdateDroughtBlend, 80);
|
|
}
|
|
|
|
#define tState data[0]
|
|
#define tBlendY data[1]
|
|
#define tBlendDelay data[2]
|
|
#define tWinRange data[3]
|
|
|
|
static void UpdateDroughtBlend(u8 taskId)
|
|
{
|
|
struct Task *task = &gTasks[taskId];
|
|
|
|
switch (task->tState)
|
|
{
|
|
case 0:
|
|
task->tBlendY = 0;
|
|
task->tBlendDelay = 0;
|
|
task->tWinRange = REG_WININ;
|
|
SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_ALL | WININ_WIN1_ALL);
|
|
SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG1 | BLDCNT_TGT1_BG2 | BLDCNT_TGT1_BG3 | BLDCNT_TGT1_OBJ | BLDCNT_EFFECT_LIGHTEN);
|
|
SetGpuReg(REG_OFFSET_BLDY, 0);
|
|
task->tState++;
|
|
// fall through
|
|
case 1:
|
|
task->tBlendY += 3;
|
|
if (task->tBlendY > 16)
|
|
task->tBlendY = 16;
|
|
SetGpuReg(REG_OFFSET_BLDY, task->tBlendY);
|
|
if (task->tBlendY >= 16)
|
|
task->tState++;
|
|
break;
|
|
case 2:
|
|
task->tBlendDelay++;
|
|
if (task->tBlendDelay > 9)
|
|
{
|
|
task->tBlendDelay = 0;
|
|
task->tBlendY--;
|
|
if (task->tBlendY <= 0)
|
|
{
|
|
task->tBlendY = 0;
|
|
task->tState++;
|
|
}
|
|
SetGpuReg(REG_OFFSET_BLDY, task->tBlendY);
|
|
}
|
|
break;
|
|
case 3:
|
|
SetGpuReg(REG_OFFSET_BLDCNT, 0);
|
|
SetGpuReg(REG_OFFSET_BLDY, 0);
|
|
SetGpuReg(REG_OFFSET_WININ, task->tWinRange);
|
|
task->tState++;
|
|
break;
|
|
case 4:
|
|
ScriptContext_Enable();
|
|
DestroyTask(taskId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef tState
|
|
#undef tBlendY
|
|
#undef tBlendDelay
|
|
#undef tWinRange
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_RAIN
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void LoadRainSpriteSheet(void);
|
|
static bool8 CreateRainSprite(void);
|
|
static void UpdateRainSprite(struct Sprite *sprite);
|
|
static bool8 UpdateVisibleRainSprites(void);
|
|
static void DestroyRainSprites(void);
|
|
|
|
static const struct Coords16 sRainSpriteCoords[] =
|
|
{
|
|
{ 0, 0},
|
|
{ 0, 160},
|
|
{ 0, 64},
|
|
{144, 224},
|
|
{144, 128},
|
|
{ 32, 32},
|
|
{ 32, 192},
|
|
{ 32, 96},
|
|
{ 72, 128},
|
|
{ 72, 32},
|
|
{ 72, 192},
|
|
{216, 96},
|
|
{216, 0},
|
|
{104, 160},
|
|
{104, 64},
|
|
{104, 224},
|
|
{144, 0},
|
|
{144, 160},
|
|
{144, 64},
|
|
{ 32, 224},
|
|
{ 32, 128},
|
|
{ 72, 32},
|
|
{ 72, 192},
|
|
{ 48, 96},
|
|
};
|
|
|
|
static const struct OamData sRainSpriteOamData =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(16x32),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(16x32),
|
|
.tileNum = 0,
|
|
.priority = 1,
|
|
.paletteNum = 2,
|
|
.affineParam = 0,
|
|
};
|
|
|
|
static const union AnimCmd sRainSpriteFallAnimCmd[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 16),
|
|
ANIMCMD_JUMP(0),
|
|
};
|
|
|
|
static const union AnimCmd sRainSpriteSplashAnimCmd[] =
|
|
{
|
|
ANIMCMD_FRAME(8, 3),
|
|
ANIMCMD_FRAME(32, 2),
|
|
ANIMCMD_FRAME(40, 2),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sRainSpriteHeavySplashAnimCmd[] =
|
|
{
|
|
ANIMCMD_FRAME(8, 3),
|
|
ANIMCMD_FRAME(16, 3),
|
|
ANIMCMD_FRAME(24, 4),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sRainSpriteAnimCmds[] =
|
|
{
|
|
sRainSpriteFallAnimCmd,
|
|
sRainSpriteSplashAnimCmd,
|
|
sRainSpriteHeavySplashAnimCmd,
|
|
};
|
|
|
|
static const struct SpriteTemplate sRainSpriteTemplate =
|
|
{
|
|
.tileTag = GFXTAG_RAIN,
|
|
.paletteTag = PALTAG_WEATHER,
|
|
.oam = &sRainSpriteOamData,
|
|
.anims = sRainSpriteAnimCmds,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = UpdateRainSprite,
|
|
};
|
|
|
|
// Q28.4 fixed-point format values
|
|
static const s16 sRainSpriteMovement[][2] =
|
|
{
|
|
{-0x68, 0xD0},
|
|
{-0xA0, 0x140},
|
|
};
|
|
|
|
// First byte is the number of frames a raindrop falls before it splashes.
|
|
// Second byte is the maximum number of frames a raindrop can "wait" before
|
|
// it appears and starts falling. (This is only for the initial raindrop spawn.)
|
|
static const u16 sRainSpriteFallingDurations[][2] =
|
|
{
|
|
{18, 7},
|
|
{12, 10},
|
|
};
|
|
|
|
static const struct SpriteSheet sRainSpriteSheet =
|
|
{
|
|
.data = gWeatherRainTiles,
|
|
.size = sizeof(gWeatherRainTiles),
|
|
.tag = GFXTAG_RAIN,
|
|
};
|
|
|
|
void Rain_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = 0;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE;
|
|
gWeatherPtr->rainSpriteVisibleCounter = 0;
|
|
gWeatherPtr->rainSpriteVisibleDelay = 8;
|
|
gWeatherPtr->isDownpour = FALSE;
|
|
gWeatherPtr->targetRainSpriteCount = 10;
|
|
gWeatherPtr->targetColorMapIndex = 3;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
SetRainStrengthFromSoundEffect(SE_RAIN);
|
|
Weather_SetBlendCoeffs(8, 12);
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Rain_InitAll(void)
|
|
{
|
|
Rain_InitVars();
|
|
while (!gWeatherPtr->weatherGfxLoaded)
|
|
Rain_Main();
|
|
}
|
|
|
|
void Rain_Main(void)
|
|
{
|
|
switch (gWeatherPtr->initStep)
|
|
{
|
|
case 0:
|
|
LoadRainSpriteSheet();
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 1:
|
|
if (!CreateRainSprite())
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 2:
|
|
if (!UpdateVisibleRainSprites())
|
|
{
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool8 Rain_Finish(void)
|
|
{
|
|
switch (gWeatherPtr->finishStep)
|
|
{
|
|
case 0:
|
|
if (gWeatherPtr->nextWeather == WEATHER_RAIN
|
|
|| gWeatherPtr->nextWeather == WEATHER_RAIN_THUNDERSTORM
|
|
|| gWeatherPtr->nextWeather == WEATHER_DOWNPOUR)
|
|
{
|
|
gWeatherPtr->finishStep = 0xFF;
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->targetRainSpriteCount = 0;
|
|
gWeatherPtr->finishStep++;
|
|
}
|
|
// fall through
|
|
case 1:
|
|
if (!UpdateVisibleRainSprites())
|
|
{
|
|
DestroyRainSprites();
|
|
gWeatherPtr->finishStep++;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
#define tCounter data[0]
|
|
#define tRandom data[1]
|
|
#define tPosX data[2]
|
|
#define tPosY data[3]
|
|
#define tState data[4]
|
|
#define tActive data[5]
|
|
#define tWaiting data[6]
|
|
|
|
static void StartRainSpriteFall(struct Sprite *sprite)
|
|
{
|
|
u32 rand;
|
|
u16 numFallingFrames;
|
|
int tileX;
|
|
int tileY;
|
|
|
|
if (sprite->tRandom == 0)
|
|
sprite->tRandom = 361;
|
|
|
|
rand = ISO_RANDOMIZE2(sprite->tRandom);
|
|
sprite->tRandom = ((rand & 0x7FFF0000) >> 16) % 600;
|
|
|
|
numFallingFrames = sRainSpriteFallingDurations[gWeatherPtr->isDownpour][0];
|
|
|
|
tileX = sprite->tRandom % 30;
|
|
sprite->tPosX = tileX * 8; // Useless assignment, leftover from before fixed-point values were used
|
|
|
|
tileY = sprite->tRandom / 30;
|
|
sprite->tPosY = tileY * 8; // Useless assignment, leftover from before fixed-point values were used
|
|
|
|
sprite->tPosX = tileX;
|
|
sprite->tPosX <<= 7; // This is tileX * 8, using a fixed-point value with 4 decimal places
|
|
|
|
sprite->tPosY = tileY;
|
|
sprite->tPosY <<= 7; // This is tileX * 8, using a fixed-point value with 4 decimal places
|
|
|
|
// "Rewind" the rain sprites, from their ending position.
|
|
sprite->tPosX -= sRainSpriteMovement[gWeatherPtr->isDownpour][0] * numFallingFrames;
|
|
sprite->tPosY -= sRainSpriteMovement[gWeatherPtr->isDownpour][1] * numFallingFrames;
|
|
|
|
StartSpriteAnim(sprite, 0);
|
|
sprite->tState = 0;
|
|
sprite->coordOffsetEnabled = FALSE;
|
|
sprite->tCounter = numFallingFrames;
|
|
}
|
|
|
|
static void UpdateRainSprite(struct Sprite *sprite)
|
|
{
|
|
if (sprite->tState == 0)
|
|
{
|
|
// Raindrop is in its "falling" motion.
|
|
sprite->tPosX += sRainSpriteMovement[gWeatherPtr->isDownpour][0];
|
|
sprite->tPosY += sRainSpriteMovement[gWeatherPtr->isDownpour][1];
|
|
sprite->x = sprite->tPosX >> 4;
|
|
sprite->y = sprite->tPosY >> 4;
|
|
|
|
if (sprite->tActive
|
|
&& (sprite->x >= -8 && sprite->x <= DISPLAY_WIDTH + 8)
|
|
&& sprite->y >= -16 && sprite->y <= DISPLAY_HEIGHT + 16)
|
|
sprite->invisible = FALSE;
|
|
else
|
|
sprite->invisible = TRUE;
|
|
|
|
if (--sprite->tCounter == 0)
|
|
{
|
|
// Make raindrop splash on the ground
|
|
StartSpriteAnim(sprite, gWeatherPtr->isDownpour + 1);
|
|
sprite->tState = 1;
|
|
sprite->x -= gSpriteCoordOffsetX;
|
|
sprite->y -= gSpriteCoordOffsetY;
|
|
sprite->coordOffsetEnabled = TRUE;
|
|
}
|
|
}
|
|
else if (sprite->animEnded)
|
|
{
|
|
// The splashing animation ended.
|
|
sprite->invisible = TRUE;
|
|
StartRainSpriteFall(sprite);
|
|
}
|
|
}
|
|
|
|
static void WaitRainSprite(struct Sprite *sprite)
|
|
{
|
|
if (sprite->tCounter == 0)
|
|
{
|
|
StartRainSpriteFall(sprite);
|
|
sprite->callback = UpdateRainSprite;
|
|
}
|
|
else
|
|
{
|
|
sprite->tCounter--;
|
|
}
|
|
}
|
|
|
|
static void InitRainSpriteMovement(struct Sprite *sprite, u16 val)
|
|
{
|
|
u16 numFallingFrames = sRainSpriteFallingDurations[gWeatherPtr->isDownpour][0];
|
|
u16 numAdvanceRng = val / (sRainSpriteFallingDurations[gWeatherPtr->isDownpour][1] + numFallingFrames);
|
|
u16 frameVal = val % (sRainSpriteFallingDurations[gWeatherPtr->isDownpour][1] + numFallingFrames);
|
|
|
|
while (--numAdvanceRng != 0xFFFF)
|
|
StartRainSpriteFall(sprite);
|
|
|
|
if (frameVal < numFallingFrames)
|
|
{
|
|
while (--frameVal != 0xFFFF)
|
|
UpdateRainSprite(sprite);
|
|
|
|
sprite->tWaiting = 0;
|
|
}
|
|
else
|
|
{
|
|
sprite->tCounter = frameVal - numFallingFrames;
|
|
sprite->invisible = TRUE;
|
|
sprite->tWaiting = 1;
|
|
}
|
|
}
|
|
|
|
static void LoadRainSpriteSheet(void)
|
|
{
|
|
LoadSpriteSheet(&sRainSpriteSheet);
|
|
}
|
|
|
|
static bool8 CreateRainSprite(void)
|
|
{
|
|
u8 spriteIndex;
|
|
u8 spriteId;
|
|
|
|
if (gWeatherPtr->rainSpriteCount == MAX_RAIN_SPRITES)
|
|
return FALSE;
|
|
|
|
spriteIndex = gWeatherPtr->rainSpriteCount;
|
|
spriteId = CreateSpriteAtEnd(&sRainSpriteTemplate,
|
|
sRainSpriteCoords[spriteIndex].x, sRainSpriteCoords[spriteIndex].y, 78);
|
|
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
gSprites[spriteId].tActive = FALSE;
|
|
gSprites[spriteId].tRandom = spriteIndex * 145;
|
|
while (gSprites[spriteId].tRandom >= 600)
|
|
gSprites[spriteId].tRandom -= 600;
|
|
|
|
StartRainSpriteFall(&gSprites[spriteId]);
|
|
InitRainSpriteMovement(&gSprites[spriteId], spriteIndex * 9);
|
|
gSprites[spriteId].invisible = TRUE;
|
|
gWeatherPtr->sprites.s1.rainSprites[spriteIndex] = &gSprites[spriteId];
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->sprites.s1.rainSprites[spriteIndex] = NULL;
|
|
}
|
|
|
|
if (++gWeatherPtr->rainSpriteCount == MAX_RAIN_SPRITES)
|
|
{
|
|
u16 i;
|
|
for (i = 0; i < MAX_RAIN_SPRITES; i++)
|
|
{
|
|
if (gWeatherPtr->sprites.s1.rainSprites[i])
|
|
{
|
|
if (!gWeatherPtr->sprites.s1.rainSprites[i]->tWaiting)
|
|
gWeatherPtr->sprites.s1.rainSprites[i]->callback = UpdateRainSprite;
|
|
else
|
|
gWeatherPtr->sprites.s1.rainSprites[i]->callback = WaitRainSprite;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static bool8 UpdateVisibleRainSprites(void)
|
|
{
|
|
if (gWeatherPtr->curRainSpriteIndex == gWeatherPtr->targetRainSpriteCount)
|
|
return FALSE;
|
|
|
|
if (++gWeatherPtr->rainSpriteVisibleCounter > gWeatherPtr->rainSpriteVisibleDelay)
|
|
{
|
|
gWeatherPtr->rainSpriteVisibleCounter = 0;
|
|
if (gWeatherPtr->curRainSpriteIndex < gWeatherPtr->targetRainSpriteCount)
|
|
{
|
|
gWeatherPtr->sprites.s1.rainSprites[gWeatherPtr->curRainSpriteIndex++]->tActive = TRUE;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->curRainSpriteIndex--;
|
|
gWeatherPtr->sprites.s1.rainSprites[gWeatherPtr->curRainSpriteIndex]->tActive = FALSE;
|
|
gWeatherPtr->sprites.s1.rainSprites[gWeatherPtr->curRainSpriteIndex]->invisible = TRUE;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void DestroyRainSprites(void)
|
|
{
|
|
u16 i;
|
|
|
|
for (i = 0; i < gWeatherPtr->rainSpriteCount; i++)
|
|
{
|
|
if (gWeatherPtr->sprites.s1.rainSprites[i] != NULL)
|
|
DestroySprite(gWeatherPtr->sprites.s1.rainSprites[i]);
|
|
}
|
|
gWeatherPtr->rainSpriteCount = 0;
|
|
FreeSpriteTilesByTag(GFXTAG_RAIN);
|
|
}
|
|
|
|
#undef tCounter
|
|
#undef tRandom
|
|
#undef tPosX
|
|
#undef tPosY
|
|
#undef tState
|
|
#undef tActive
|
|
#undef tWaiting
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Snow
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void UpdateSnowflakeSprite(struct Sprite *);
|
|
static bool8 UpdateVisibleSnowflakeSprites(void);
|
|
static bool8 CreateSnowflakeSprite(void);
|
|
static bool8 DestroySnowflakeSprite(void);
|
|
static void InitSnowflakeSpriteMovement(struct Sprite *);
|
|
|
|
void Snow_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = 0;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE;
|
|
gWeatherPtr->targetColorMapIndex = 0;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
gWeatherPtr->targetSnowflakeSpriteCount = 16;
|
|
gWeatherPtr->snowflakeVisibleCounter = 0;
|
|
Weather_SetBlendCoeffs(8, 12);
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Snow_InitAll(void)
|
|
{
|
|
u16 i;
|
|
|
|
Snow_InitVars();
|
|
while (gWeatherPtr->weatherGfxLoaded == FALSE)
|
|
{
|
|
Snow_Main();
|
|
for (i = 0; i < gWeatherPtr->snowflakeSpriteCount; i++)
|
|
UpdateSnowflakeSprite(gWeatherPtr->sprites.s1.snowflakeSprites[i]);
|
|
}
|
|
}
|
|
|
|
void Snow_Main(void)
|
|
{
|
|
if (gWeatherPtr->initStep == 0 && !UpdateVisibleSnowflakeSprites())
|
|
{
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
}
|
|
|
|
bool8 Snow_Finish(void)
|
|
{
|
|
switch (gWeatherPtr->finishStep)
|
|
{
|
|
case 0:
|
|
gWeatherPtr->targetSnowflakeSpriteCount = 0;
|
|
gWeatherPtr->snowflakeVisibleCounter = 0;
|
|
gWeatherPtr->finishStep++;
|
|
// fall through
|
|
case 1:
|
|
if (!UpdateVisibleSnowflakeSprites())
|
|
{
|
|
gWeatherPtr->finishStep++;
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static bool8 UpdateVisibleSnowflakeSprites(void)
|
|
{
|
|
if (gWeatherPtr->snowflakeSpriteCount == gWeatherPtr->targetSnowflakeSpriteCount)
|
|
return FALSE;
|
|
|
|
if (++gWeatherPtr->snowflakeVisibleCounter > 36)
|
|
{
|
|
gWeatherPtr->snowflakeVisibleCounter = 0;
|
|
if (gWeatherPtr->snowflakeSpriteCount < gWeatherPtr->targetSnowflakeSpriteCount)
|
|
CreateSnowflakeSprite();
|
|
else
|
|
DestroySnowflakeSprite();
|
|
}
|
|
|
|
return gWeatherPtr->snowflakeSpriteCount != gWeatherPtr->targetSnowflakeSpriteCount;
|
|
}
|
|
|
|
static const struct OamData sSnowflakeSpriteOamData =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_NORMAL,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(8x8),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(8x8),
|
|
.tileNum = 0,
|
|
.priority = 1,
|
|
.paletteNum = 0,
|
|
.affineParam = 0,
|
|
};
|
|
|
|
static const struct SpriteFrameImage sSnowflakeSpriteImages[] =
|
|
{
|
|
{gWeatherSnow1Tiles, sizeof(gWeatherSnow1Tiles)},
|
|
{gWeatherSnow2Tiles, sizeof(gWeatherSnow2Tiles)},
|
|
};
|
|
|
|
static const union AnimCmd sSnowflakeAnimCmd0[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sSnowflakeAnimCmd1[] =
|
|
{
|
|
ANIMCMD_FRAME(1, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sSnowflakeAnimCmds[] =
|
|
{
|
|
sSnowflakeAnimCmd0,
|
|
sSnowflakeAnimCmd1,
|
|
};
|
|
|
|
static const struct SpriteTemplate sSnowflakeSpriteTemplate =
|
|
{
|
|
.tileTag = TAG_NONE,
|
|
.paletteTag = PALTAG_WEATHER,
|
|
.oam = &sSnowflakeSpriteOamData,
|
|
.anims = sSnowflakeAnimCmds,
|
|
.images = sSnowflakeSpriteImages,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = UpdateSnowflakeSprite,
|
|
};
|
|
|
|
#define tPosY data[0]
|
|
#define tDeltaY data[1]
|
|
#define tWaveDelta data[2]
|
|
#define tWaveIndex data[3]
|
|
#define tSnowflakeId data[4]
|
|
#define tFallCounter data[5]
|
|
#define tFallDuration data[6]
|
|
#define tDeltaY2 data[7]
|
|
|
|
static bool8 CreateSnowflakeSprite(void)
|
|
{
|
|
u8 spriteId = CreateSpriteAtEnd(&sSnowflakeSpriteTemplate, 0, 0, 78);
|
|
if (spriteId == MAX_SPRITES)
|
|
return FALSE;
|
|
|
|
gSprites[spriteId].tSnowflakeId = gWeatherPtr->snowflakeSpriteCount;
|
|
InitSnowflakeSpriteMovement(&gSprites[spriteId]);
|
|
gSprites[spriteId].coordOffsetEnabled = TRUE;
|
|
gWeatherPtr->sprites.s1.snowflakeSprites[gWeatherPtr->snowflakeSpriteCount++] = &gSprites[spriteId];
|
|
return TRUE;
|
|
}
|
|
|
|
static bool8 DestroySnowflakeSprite(void)
|
|
{
|
|
if (gWeatherPtr->snowflakeSpriteCount)
|
|
{
|
|
DestroySprite(gWeatherPtr->sprites.s1.snowflakeSprites[--gWeatherPtr->snowflakeSpriteCount]);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static void InitSnowflakeSpriteMovement(struct Sprite *sprite)
|
|
{
|
|
u16 rand;
|
|
u16 x = ((sprite->tSnowflakeId * 5) & 7) * 30 + (Random() % 30);
|
|
|
|
sprite->y = -3 - (gSpriteCoordOffsetY + sprite->centerToCornerVecY);
|
|
sprite->x = x - (gSpriteCoordOffsetX + sprite->centerToCornerVecX);
|
|
sprite->tPosY = sprite->y * 128;
|
|
sprite->x2 = 0;
|
|
rand = Random();
|
|
sprite->tDeltaY = (rand & 3) * 5 + 64;
|
|
sprite->tDeltaY2 = sprite->tDeltaY;
|
|
StartSpriteAnim(sprite, (rand & 1) ? 0 : 1);
|
|
sprite->tWaveIndex = 0;
|
|
sprite->tWaveDelta = ((rand & 3) == 0) ? 2 : 1;
|
|
sprite->tFallDuration = (rand & 0x1F) + 210;
|
|
sprite->tFallCounter = 0;
|
|
}
|
|
|
|
static void WaitSnowflakeSprite(struct Sprite *sprite)
|
|
{
|
|
if (++gWeatherPtr->snowflakeTimer > 18)
|
|
{
|
|
sprite->invisible = FALSE;
|
|
sprite->callback = UpdateSnowflakeSprite;
|
|
sprite->y = 250 - (gSpriteCoordOffsetY + sprite->centerToCornerVecY);
|
|
sprite->tPosY = sprite->y * 128;
|
|
gWeatherPtr->snowflakeTimer = 0;
|
|
}
|
|
}
|
|
|
|
static void UpdateSnowflakeSprite(struct Sprite *sprite)
|
|
{
|
|
s16 x;
|
|
s16 y;
|
|
|
|
sprite->tPosY += sprite->tDeltaY;
|
|
sprite->y = sprite->tPosY >> 7;
|
|
sprite->tWaveIndex += sprite->tWaveDelta;
|
|
sprite->tWaveIndex &= 0xFF;
|
|
sprite->x2 = gSineTable[sprite->tWaveIndex] / 64;
|
|
|
|
x = (sprite->x + sprite->centerToCornerVecX + gSpriteCoordOffsetX) & 0x1FF;
|
|
if (x & 0x100)
|
|
x |= -0x100;
|
|
|
|
if (x < -3)
|
|
sprite->x = 242 - (gSpriteCoordOffsetX + sprite->centerToCornerVecX);
|
|
else if (x > 242)
|
|
sprite->x = -3 - (gSpriteCoordOffsetX + sprite->centerToCornerVecX);
|
|
}
|
|
|
|
#undef tPosY
|
|
#undef tDeltaY
|
|
#undef tWaveDelta
|
|
#undef tWaveIndex
|
|
#undef tSnowflakeId
|
|
#undef tFallCounter
|
|
#undef tFallDuration
|
|
#undef tDeltaY2
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_RAIN_THUNDERSTORM
|
|
//------------------------------------------------------------------------------
|
|
|
|
enum {
|
|
// This block of states is run only once
|
|
// when first setting up the thunderstorm
|
|
THUNDER_STATE_LOAD_RAIN,
|
|
THUNDER_STATE_CREATE_RAIN,
|
|
THUNDER_STATE_INIT_RAIN,
|
|
THUNDER_STATE_WAIT_CHANGE,
|
|
|
|
// The thunderstorm loops through these states,
|
|
// not necessarily in order.
|
|
THUNDER_STATE_NEW_CYCLE,
|
|
THUNDER_STATE_NEW_CYCLE_WAIT,
|
|
THUNDER_STATE_INIT_CYCLE_1,
|
|
THUNDER_STATE_INIT_CYCLE_2,
|
|
THUNDER_STATE_SHORT_BOLT,
|
|
THUNDER_STATE_TRY_NEW_BOLT,
|
|
THUNDER_STATE_WAIT_BOLT_SHORT,
|
|
THUNDER_STATE_INIT_BOLT_LONG,
|
|
THUNDER_STATE_WAIT_BOLT_LONG,
|
|
THUNDER_STATE_FADE_BOLT_LONG,
|
|
THUNDER_STATE_END_BOLT_LONG,
|
|
};
|
|
|
|
void Thunderstorm_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = THUNDER_STATE_LOAD_RAIN;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE;
|
|
gWeatherPtr->rainSpriteVisibleCounter = 0;
|
|
gWeatherPtr->rainSpriteVisibleDelay = 4;
|
|
gWeatherPtr->isDownpour = FALSE;
|
|
gWeatherPtr->targetRainSpriteCount = 16;
|
|
gWeatherPtr->targetColorMapIndex = 3;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE; // duplicate assignment
|
|
gWeatherPtr->thunderEnqueued = FALSE;
|
|
SetRainStrengthFromSoundEffect(SE_THUNDERSTORM);
|
|
Weather_SetBlendCoeffs(8, 12);
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Thunderstorm_InitAll(void)
|
|
{
|
|
Thunderstorm_InitVars();
|
|
while (gWeatherPtr->weatherGfxLoaded == FALSE)
|
|
Thunderstorm_Main();
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_DOWNPOUR
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void UpdateThunderSound(void);
|
|
static void EnqueueThunder(u16);
|
|
|
|
void Downpour_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = THUNDER_STATE_LOAD_RAIN;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE;
|
|
gWeatherPtr->rainSpriteVisibleCounter = 0;
|
|
gWeatherPtr->rainSpriteVisibleDelay = 4;
|
|
gWeatherPtr->isDownpour = TRUE;
|
|
gWeatherPtr->targetRainSpriteCount = 24;
|
|
gWeatherPtr->targetColorMapIndex = 3;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE; // duplicate assignment
|
|
SetRainStrengthFromSoundEffect(SE_DOWNPOUR);
|
|
Weather_SetBlendCoeffs(8, 12);
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Downpour_InitAll(void)
|
|
{
|
|
Downpour_InitVars();
|
|
while (gWeatherPtr->weatherGfxLoaded == FALSE)
|
|
Thunderstorm_Main();
|
|
}
|
|
|
|
// In a given cycle, there will be some shorter bolts of lightning, potentially
|
|
// followed by a longer bolt. As a "regex", the pattern is:
|
|
// (SHORT_BOLT){1,2}(LONG_BOLT)?
|
|
//
|
|
// Thunder only plays on the final bolt of the cycle.
|
|
void Thunderstorm_Main(void)
|
|
{
|
|
UpdateThunderSound();
|
|
switch (gWeatherPtr->initStep)
|
|
{
|
|
case THUNDER_STATE_LOAD_RAIN:
|
|
LoadRainSpriteSheet();
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case THUNDER_STATE_CREATE_RAIN:
|
|
if (!CreateRainSprite())
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case THUNDER_STATE_INIT_RAIN:
|
|
if (!UpdateVisibleRainSprites())
|
|
{
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
case THUNDER_STATE_WAIT_CHANGE:
|
|
if (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_CHANGING_WEATHER)
|
|
gWeatherPtr->initStep = THUNDER_STATE_INIT_CYCLE_1;
|
|
break;
|
|
case THUNDER_STATE_NEW_CYCLE:
|
|
gWeatherPtr->thunderAllowEnd = TRUE;
|
|
gWeatherPtr->thunderTimer = (Random() % 360) + 360;
|
|
gWeatherPtr->initStep++;
|
|
// fall through
|
|
case THUNDER_STATE_NEW_CYCLE_WAIT:
|
|
// Wait between 360-720 frames before starting a new cycle.
|
|
if (--gWeatherPtr->thunderTimer == 0)
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case THUNDER_STATE_INIT_CYCLE_1:
|
|
gWeatherPtr->thunderAllowEnd = TRUE;
|
|
gWeatherPtr->thunderLongBolt = Random() % 2;
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case THUNDER_STATE_INIT_CYCLE_2:
|
|
gWeatherPtr->thunderShortBolts = (Random() & 1) + 1;
|
|
gWeatherPtr->initStep++;
|
|
// fall through
|
|
case THUNDER_STATE_SHORT_BOLT:
|
|
// Short bolt of lightning strikes.
|
|
ApplyWeatherColorMapIfIdle(19);
|
|
// If final lightning bolt, enqueue thunder.
|
|
if (!gWeatherPtr->thunderLongBolt && gWeatherPtr->thunderShortBolts == 1)
|
|
EnqueueThunder(20);
|
|
|
|
gWeatherPtr->thunderTimer = (Random() % 3) + 6;
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case THUNDER_STATE_TRY_NEW_BOLT:
|
|
if (--gWeatherPtr->thunderTimer == 0)
|
|
{
|
|
// Short bolt of lightning ends.
|
|
ApplyWeatherColorMapIfIdle(3);
|
|
gWeatherPtr->thunderAllowEnd = TRUE;
|
|
if (--gWeatherPtr->thunderShortBolts != 0)
|
|
{
|
|
// Wait a little, then do another short bolt.
|
|
gWeatherPtr->thunderTimer = (Random() % 16) + 60;
|
|
gWeatherPtr->initStep = THUNDER_STATE_WAIT_BOLT_SHORT;
|
|
}
|
|
else if (!gWeatherPtr->thunderLongBolt)
|
|
{
|
|
// No more bolts, restart loop.
|
|
gWeatherPtr->initStep = THUNDER_STATE_NEW_CYCLE;
|
|
}
|
|
else
|
|
{
|
|
// Set up long bolt.
|
|
gWeatherPtr->initStep = THUNDER_STATE_INIT_BOLT_LONG;
|
|
}
|
|
}
|
|
break;
|
|
case THUNDER_STATE_WAIT_BOLT_SHORT:
|
|
if (--gWeatherPtr->thunderTimer == 0)
|
|
gWeatherPtr->initStep = THUNDER_STATE_SHORT_BOLT;
|
|
break;
|
|
case THUNDER_STATE_INIT_BOLT_LONG:
|
|
gWeatherPtr->thunderTimer = (Random() % 16) + 60;
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case THUNDER_STATE_WAIT_BOLT_LONG:
|
|
if (--gWeatherPtr->thunderTimer == 0)
|
|
{
|
|
// Do long bolt. Enqueue thunder with a potentially longer delay.
|
|
EnqueueThunder(100);
|
|
ApplyWeatherColorMapIfIdle(19);
|
|
gWeatherPtr->thunderTimer = (Random() & 0xF) + 30;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
case THUNDER_STATE_FADE_BOLT_LONG:
|
|
if (--gWeatherPtr->thunderTimer == 0)
|
|
{
|
|
// Fade long bolt out over time.
|
|
ApplyWeatherColorMapIfIdle_Gradual(19, 3, 5);
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
case THUNDER_STATE_END_BOLT_LONG:
|
|
if (gWeatherPtr->palProcessingState == WEATHER_PAL_STATE_IDLE)
|
|
{
|
|
gWeatherPtr->thunderAllowEnd = TRUE;
|
|
gWeatherPtr->initStep = THUNDER_STATE_NEW_CYCLE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool8 Thunderstorm_Finish(void)
|
|
{
|
|
switch (gWeatherPtr->finishStep)
|
|
{
|
|
case 0:
|
|
gWeatherPtr->thunderAllowEnd = FALSE;
|
|
gWeatherPtr->finishStep++;
|
|
// fall through
|
|
case 1:
|
|
Thunderstorm_Main();
|
|
if (gWeatherPtr->thunderAllowEnd)
|
|
{
|
|
if (gWeatherPtr->nextWeather == WEATHER_RAIN
|
|
|| gWeatherPtr->nextWeather == WEATHER_RAIN_THUNDERSTORM
|
|
|| gWeatherPtr->nextWeather == WEATHER_DOWNPOUR)
|
|
return FALSE;
|
|
|
|
gWeatherPtr->targetRainSpriteCount = 0;
|
|
gWeatherPtr->finishStep++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (!UpdateVisibleRainSprites())
|
|
{
|
|
DestroyRainSprites();
|
|
gWeatherPtr->thunderEnqueued = FALSE;
|
|
gWeatherPtr->finishStep++;
|
|
return FALSE;
|
|
}
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
// Enqueue a thunder sound effect for at most `waitFrames` frames from now.
|
|
static void EnqueueThunder(u16 waitFrames)
|
|
{
|
|
if (!gWeatherPtr->thunderEnqueued)
|
|
{
|
|
gWeatherPtr->thunderSETimer = Random() % waitFrames;
|
|
gWeatherPtr->thunderEnqueued = TRUE;
|
|
}
|
|
}
|
|
|
|
static void UpdateThunderSound(void)
|
|
{
|
|
if (gWeatherPtr->thunderEnqueued == TRUE)
|
|
{
|
|
if (gWeatherPtr->thunderSETimer == 0)
|
|
{
|
|
if (IsSEPlaying())
|
|
return;
|
|
|
|
if (Random() & 1)
|
|
PlaySE(SE_THUNDER);
|
|
else
|
|
PlaySE(SE_THUNDER2);
|
|
|
|
gWeatherPtr->thunderEnqueued = FALSE;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->thunderSETimer--;
|
|
}
|
|
}
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_FOG_HORIZONTAL and WEATHER_UNDERWATER
|
|
//------------------------------------------------------------------------------
|
|
|
|
static const u16 sUnusedData[] = {0, 6, 6, 12, 18, 42, 300, 300};
|
|
|
|
static const struct OamData sOamData_FogH =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_BLEND,
|
|
.mosaic = FALSE,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.x = 0,
|
|
.matrixNum = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0,
|
|
.priority = 2,
|
|
.paletteNum = 0,
|
|
.affineParam = 0,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_FogH_0[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_FogH_1[] =
|
|
{
|
|
ANIMCMD_FRAME(32, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_FogH_2[] =
|
|
{
|
|
ANIMCMD_FRAME(64, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_FogH_3[] =
|
|
{
|
|
ANIMCMD_FRAME(96, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_FogH_4[] =
|
|
{
|
|
ANIMCMD_FRAME(128, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sAnim_FogH_5[] =
|
|
{
|
|
ANIMCMD_FRAME(160, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sAnims_FogH[] =
|
|
{
|
|
sAnim_FogH_0,
|
|
sAnim_FogH_1,
|
|
sAnim_FogH_2,
|
|
sAnim_FogH_3,
|
|
sAnim_FogH_4,
|
|
sAnim_FogH_5,
|
|
};
|
|
|
|
static const union AffineAnimCmd sAffineAnim_FogH[] =
|
|
{
|
|
AFFINEANIMCMD_FRAME(0x200, 0x200, 0, 0),
|
|
AFFINEANIMCMD_END,
|
|
};
|
|
|
|
static const union AffineAnimCmd *const sAffineAnims_FogH[] =
|
|
{
|
|
sAffineAnim_FogH,
|
|
};
|
|
|
|
static void FogHorizontalSpriteCallback(struct Sprite *);
|
|
static const struct SpriteTemplate sFogHorizontalSpriteTemplate =
|
|
{
|
|
.tileTag = GFXTAG_FOG_H,
|
|
.paletteTag = PALTAG_WEATHER,
|
|
.oam = &sOamData_FogH,
|
|
.anims = sAnims_FogH,
|
|
.images = NULL,
|
|
.affineAnims = sAffineAnims_FogH,
|
|
.callback = FogHorizontalSpriteCallback,
|
|
};
|
|
|
|
void FogHorizontal_Main(void);
|
|
static void CreateFogHorizontalSprites(void);
|
|
static void DestroyFogHorizontalSprites(void);
|
|
|
|
#define SHADOW_COLOR_INDEX 9
|
|
|
|
u8 UpdateShadowColor(u16 color)
|
|
{
|
|
u8 paletteNum = IndexOfSpritePaletteTag(TAG_WEATHER_START);
|
|
ALIGNED(4) u16 tempBuffer[16];
|
|
u16 blendedColor;
|
|
if (paletteNum != 0xFF)
|
|
{
|
|
u16 index = OBJ_PLTT_ID(paletteNum) + SHADOW_COLOR_INDEX;
|
|
gPlttBufferUnfaded[index] = gPlttBufferFaded[index] = color;
|
|
CpuFastCopy(&gPlttBufferFaded[index - SHADOW_COLOR_INDEX], tempBuffer, PLTT_SIZE_4BPP);
|
|
UpdateSpritePaletteWithTime(paletteNum);
|
|
blendedColor = gPlttBufferFaded[index];
|
|
CpuFastCopy(tempBuffer, &gPlttBufferFaded[index - SHADOW_COLOR_INDEX], PLTT_SIZE_4BPP);
|
|
gPlttBufferFaded[index] = blendedColor;
|
|
}
|
|
return paletteNum;
|
|
}
|
|
|
|
void FogHorizontal_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = 0;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE;
|
|
gWeatherPtr->targetColorMapIndex = 0;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
if (gWeatherPtr->fogHSpritesCreated == 0)
|
|
{
|
|
gWeatherPtr->fogHScrollCounter = 0;
|
|
gWeatherPtr->fogHScrollOffset = 0;
|
|
gWeatherPtr->fogHScrollPosX = 0;
|
|
Weather_SetBlendCoeffs(0, 16);
|
|
}
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void FogHorizontal_InitAll(void)
|
|
{
|
|
FogHorizontal_InitVars();
|
|
while (gWeatherPtr->weatherGfxLoaded == FALSE)
|
|
FogHorizontal_Main();
|
|
}
|
|
|
|
void FogHorizontal_Main(void)
|
|
{
|
|
gWeatherPtr->fogHScrollPosX = (gSpriteCoordOffsetX - gWeatherPtr->fogHScrollOffset) & 0xFF;
|
|
if (++gWeatherPtr->fogHScrollCounter > 3)
|
|
{
|
|
gWeatherPtr->fogHScrollCounter = 0;
|
|
gWeatherPtr->fogHScrollOffset++;
|
|
}
|
|
switch (gWeatherPtr->initStep)
|
|
{
|
|
case 0:
|
|
CreateFogHorizontalSprites();
|
|
if (gWeatherPtr->currWeather == WEATHER_FOG_HORIZONTAL)
|
|
{
|
|
u8 paletteNum = IndexOfSpritePaletteTag(TAG_WEATHER_START);
|
|
Weather_SetTargetBlendCoeffs(12, 8, 3);
|
|
UpdateShadowColor(0x3DEF);
|
|
}
|
|
else
|
|
Weather_SetTargetBlendCoeffs(4, 16, 0);
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 1:
|
|
if (Weather_UpdateBlend())
|
|
{
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool8 FogHorizontal_Finish(void)
|
|
{
|
|
gWeatherPtr->fogHScrollPosX = (gSpriteCoordOffsetX - gWeatherPtr->fogHScrollOffset) & 0xFF;
|
|
if (++gWeatherPtr->fogHScrollCounter > 3)
|
|
{
|
|
gWeatherPtr->fogHScrollCounter = 0;
|
|
gWeatherPtr->fogHScrollOffset++;
|
|
}
|
|
|
|
switch (gWeatherPtr->finishStep)
|
|
{
|
|
case 0:
|
|
Weather_SetTargetBlendCoeffs(0, 16, 3);
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
case 1:
|
|
if (Weather_UpdateBlend())
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
case 2:
|
|
DestroyFogHorizontalSprites();
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
default:
|
|
UpdateShadowColor(RGB_BLACK);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
#define tSpriteColumn data[0]
|
|
|
|
static void FogHorizontalSpriteCallback(struct Sprite *sprite)
|
|
{
|
|
sprite->y2 = (u8)gSpriteCoordOffsetY;
|
|
sprite->x = gWeatherPtr->fogHScrollPosX + 32 + sprite->tSpriteColumn * 64;
|
|
if (sprite->x >= DISPLAY_WIDTH + 32)
|
|
{
|
|
sprite->x = (DISPLAY_WIDTH * 2) + gWeatherPtr->fogHScrollPosX - (4 - sprite->tSpriteColumn) * 64;
|
|
sprite->x &= 0x1FF;
|
|
}
|
|
}
|
|
|
|
static void CreateFogHorizontalSprites(void)
|
|
{
|
|
u16 i;
|
|
u8 spriteId;
|
|
struct Sprite *sprite;
|
|
|
|
if (!gWeatherPtr->fogHSpritesCreated)
|
|
{
|
|
struct SpriteSheet fogHorizontalSpriteSheet = {
|
|
.data = gWeatherFogHorizontalTiles,
|
|
.size = sizeof(gWeatherFogHorizontalTiles),
|
|
.tag = GFXTAG_FOG_H,
|
|
};
|
|
LoadSpriteSheet(&fogHorizontalSpriteSheet);
|
|
for (i = 0; i < NUM_FOG_HORIZONTAL_SPRITES; i++)
|
|
{
|
|
spriteId = CreateSpriteAtEnd(&sFogHorizontalSpriteTemplate, 0, 0, 0xFF);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
sprite = &gSprites[spriteId];
|
|
sprite->tSpriteColumn = i % 5;
|
|
sprite->x = (i % 5) * 64 + 32;
|
|
sprite->y = (i / 5) * 64 + 32;
|
|
gWeatherPtr->sprites.s2.fogHSprites[i] = sprite;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->sprites.s2.fogHSprites[i] = NULL;
|
|
}
|
|
}
|
|
|
|
gWeatherPtr->fogHSpritesCreated = TRUE;
|
|
}
|
|
}
|
|
|
|
static void DestroyFogHorizontalSprites(void)
|
|
{
|
|
u16 i;
|
|
|
|
if (gWeatherPtr->fogHSpritesCreated)
|
|
{
|
|
for (i = 0; i < NUM_FOG_HORIZONTAL_SPRITES; i++)
|
|
{
|
|
if (gWeatherPtr->sprites.s2.fogHSprites[i] != NULL)
|
|
DestroySprite(gWeatherPtr->sprites.s2.fogHSprites[i]);
|
|
}
|
|
|
|
FreeSpriteTilesByTag(GFXTAG_FOG_H);
|
|
gWeatherPtr->fogHSpritesCreated = 0;
|
|
}
|
|
}
|
|
|
|
#undef tSpriteColumn
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_VOLCANIC_ASH
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void LoadAshSpriteSheet(void);
|
|
static void CreateAshSprites(void);
|
|
static void DestroyAshSprites(void);
|
|
static void UpdateAshSprite(struct Sprite *);
|
|
|
|
void Ash_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = 0;
|
|
gWeatherPtr->weatherGfxLoaded = FALSE;
|
|
gWeatherPtr->targetColorMapIndex = 0;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
gWeatherPtr->ashUnused = 20; // Never read
|
|
if (!gWeatherPtr->ashSpritesCreated)
|
|
Weather_SetBlendCoeffs(0, 12);
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Ash_InitAll(void)
|
|
{
|
|
Ash_InitVars();
|
|
while (gWeatherPtr->weatherGfxLoaded == FALSE)
|
|
Ash_Main();
|
|
}
|
|
|
|
void Ash_Main(void)
|
|
{
|
|
gWeatherPtr->ashBaseSpritesX = gSpriteCoordOffsetX & 0x1FF;
|
|
while (gWeatherPtr->ashBaseSpritesX >= DISPLAY_WIDTH)
|
|
gWeatherPtr->ashBaseSpritesX -= DISPLAY_WIDTH;
|
|
|
|
switch (gWeatherPtr->initStep)
|
|
{
|
|
case 0:
|
|
LoadAshSpriteSheet();
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 1:
|
|
if (!gWeatherPtr->ashSpritesCreated)
|
|
CreateAshSprites();
|
|
|
|
Weather_SetTargetBlendCoeffs(10, 12, 1);
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 2:
|
|
if (Weather_UpdateBlend())
|
|
{
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
default:
|
|
Weather_UpdateBlend();
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool8 Ash_Finish(void)
|
|
{
|
|
switch (gWeatherPtr->finishStep)
|
|
{
|
|
case 0:
|
|
Weather_SetTargetBlendCoeffs(0, 12, 1);
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
case 1:
|
|
if (Weather_UpdateBlend())
|
|
{
|
|
DestroyAshSprites();
|
|
gWeatherPtr->finishStep++;
|
|
}
|
|
break;
|
|
case 2:
|
|
SetGpuReg(REG_OFFSET_BLDALPHA, 0);
|
|
gWeatherPtr->finishStep++;
|
|
return FALSE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static const struct SpriteSheet sAshSpriteSheet =
|
|
{
|
|
.data = gWeatherAshTiles,
|
|
.size = sizeof(gWeatherAshTiles),
|
|
.tag = GFXTAG_ASH,
|
|
};
|
|
|
|
static void LoadAshSpriteSheet(void)
|
|
{
|
|
LoadSpriteSheet(&sAshSpriteSheet);
|
|
}
|
|
|
|
static const struct OamData sAshSpriteOamData =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_BLEND,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.x = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0,
|
|
.priority = 1,
|
|
.paletteNum = 15,
|
|
};
|
|
|
|
static const union AnimCmd sAshSpriteAnimCmd0[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 60),
|
|
ANIMCMD_FRAME(64, 60),
|
|
ANIMCMD_JUMP(0),
|
|
};
|
|
|
|
static const union AnimCmd *const sAshSpriteAnimCmds[] =
|
|
{
|
|
sAshSpriteAnimCmd0,
|
|
};
|
|
|
|
static const struct SpriteTemplate sAshSpriteTemplate =
|
|
{
|
|
.tileTag = GFXTAG_ASH,
|
|
.paletteTag = PALTAG_WEATHER,
|
|
.oam = &sAshSpriteOamData,
|
|
.anims = sAshSpriteAnimCmds,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = UpdateAshSprite,
|
|
};
|
|
|
|
#define tOffsetY data[0]
|
|
#define tCounterY data[1]
|
|
#define tSpriteColumn data[2]
|
|
#define tSpriteRow data[3]
|
|
|
|
static void CreateAshSprites(void)
|
|
{
|
|
u8 i;
|
|
u8 spriteId;
|
|
struct Sprite *sprite;
|
|
|
|
if (!gWeatherPtr->ashSpritesCreated)
|
|
{
|
|
for (i = 0; i < NUM_ASH_SPRITES; i++)
|
|
{
|
|
spriteId = CreateSpriteAtEnd(&sAshSpriteTemplate, 0, 0, 0x4E);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
sprite = &gSprites[spriteId];
|
|
sprite->tCounterY = 0;
|
|
sprite->tSpriteColumn = (u8)(i % 5);
|
|
sprite->tSpriteRow = (u8)(i / 5);
|
|
sprite->tOffsetY = sprite->tSpriteRow * 64 + 32;
|
|
gWeatherPtr->sprites.s2.ashSprites[i] = sprite;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->sprites.s2.ashSprites[i] = NULL;
|
|
}
|
|
}
|
|
|
|
gWeatherPtr->ashSpritesCreated = TRUE;
|
|
}
|
|
}
|
|
|
|
static void DestroyAshSprites(void)
|
|
{
|
|
u16 i;
|
|
|
|
if (gWeatherPtr->ashSpritesCreated)
|
|
{
|
|
for (i = 0; i < NUM_ASH_SPRITES; i++)
|
|
{
|
|
if (gWeatherPtr->sprites.s2.ashSprites[i] != NULL)
|
|
DestroySprite(gWeatherPtr->sprites.s2.ashSprites[i]);
|
|
}
|
|
|
|
FreeSpriteTilesByTag(GFXTAG_ASH);
|
|
gWeatherPtr->ashSpritesCreated = FALSE;
|
|
}
|
|
}
|
|
|
|
static void UpdateAshSprite(struct Sprite *sprite)
|
|
{
|
|
if (++sprite->tCounterY > 5)
|
|
{
|
|
sprite->tCounterY = 0;
|
|
sprite->tOffsetY++;
|
|
}
|
|
|
|
sprite->y = gSpriteCoordOffsetY + sprite->tOffsetY;
|
|
sprite->x = gWeatherPtr->ashBaseSpritesX + 32 + sprite->tSpriteColumn * 64;
|
|
if (sprite->x >= DISPLAY_WIDTH + 32)
|
|
{
|
|
sprite->x = gWeatherPtr->ashBaseSpritesX + (DISPLAY_WIDTH * 2) - (4 - sprite->tSpriteColumn) * 64;
|
|
sprite->x &= 0x1FF;
|
|
}
|
|
}
|
|
|
|
#undef tOffsetY
|
|
#undef tCounterY
|
|
#undef tSpriteColumn
|
|
#undef tSpriteRow
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_FOG_DIAGONAL
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void UpdateFogDiagonalMovement(void);
|
|
static void CreateFogDiagonalSprites(void);
|
|
static void DestroyFogDiagonalSprites(void);
|
|
static void UpdateFogDiagonalSprite(struct Sprite *);
|
|
|
|
void FogDiagonal_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = 0;
|
|
gWeatherPtr->weatherGfxLoaded = 0;
|
|
gWeatherPtr->targetColorMapIndex = 0;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
gWeatherPtr->fogHScrollCounter = 0;
|
|
gWeatherPtr->fogHScrollOffset = 1;
|
|
if (!gWeatherPtr->fogDSpritesCreated)
|
|
{
|
|
gWeatherPtr->fogDScrollXCounter = 0;
|
|
gWeatherPtr->fogDScrollYCounter = 0;
|
|
gWeatherPtr->fogDXOffset = 0;
|
|
gWeatherPtr->fogDYOffset = 0;
|
|
gWeatherPtr->fogDBaseSpritesX = 0;
|
|
gWeatherPtr->fogDPosY = 0;
|
|
Weather_SetBlendCoeffs(0, 16);
|
|
}
|
|
gWeatherPtr->noShadows = TRUE;
|
|
}
|
|
|
|
void FogDiagonal_InitAll(void)
|
|
{
|
|
FogDiagonal_InitVars();
|
|
while (gWeatherPtr->weatherGfxLoaded == FALSE)
|
|
FogDiagonal_Main();
|
|
}
|
|
|
|
void FogDiagonal_Main(void)
|
|
{
|
|
UpdateFogDiagonalMovement();
|
|
switch (gWeatherPtr->initStep)
|
|
{
|
|
case 0:
|
|
CreateFogDiagonalSprites();
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 1:
|
|
Weather_SetTargetBlendCoeffs(12, 8, 8);
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 2:
|
|
if (!Weather_UpdateBlend())
|
|
break;
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool8 FogDiagonal_Finish(void)
|
|
{
|
|
UpdateFogDiagonalMovement();
|
|
switch (gWeatherPtr->finishStep)
|
|
{
|
|
case 0:
|
|
Weather_SetTargetBlendCoeffs(0, 16, 1);
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
case 1:
|
|
if (!Weather_UpdateBlend())
|
|
break;
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
case 2:
|
|
DestroyFogDiagonalSprites();
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
static void UpdateFogDiagonalMovement(void)
|
|
{
|
|
if (++gWeatherPtr->fogDScrollXCounter > 2)
|
|
{
|
|
gWeatherPtr->fogDXOffset++;
|
|
gWeatherPtr->fogDScrollXCounter = 0;
|
|
}
|
|
|
|
if (++gWeatherPtr->fogDScrollYCounter > 4)
|
|
{
|
|
gWeatherPtr->fogDYOffset++;
|
|
gWeatherPtr->fogDScrollYCounter = 0;
|
|
}
|
|
|
|
gWeatherPtr->fogDBaseSpritesX = (gSpriteCoordOffsetX - gWeatherPtr->fogDXOffset) & 0xFF;
|
|
gWeatherPtr->fogDPosY = gSpriteCoordOffsetY + gWeatherPtr->fogDYOffset;
|
|
}
|
|
|
|
static const struct SpriteSheet sFogDiagonalSpriteSheet =
|
|
{
|
|
.data = gWeatherFogDiagonalTiles,
|
|
.size = sizeof(gWeatherFogDiagonalTiles),
|
|
.tag = GFXTAG_FOG_D,
|
|
};
|
|
|
|
static const struct OamData sFogDiagonalSpriteOamData =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_BLEND,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.x = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0,
|
|
.priority = 2,
|
|
.paletteNum = 0,
|
|
};
|
|
|
|
static const union AnimCmd sFogDiagonalSpriteAnimCmd0[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sFogDiagonalSpriteAnimCmds[] =
|
|
{
|
|
sFogDiagonalSpriteAnimCmd0,
|
|
};
|
|
|
|
static const struct SpriteTemplate sFogDiagonalSpriteTemplate =
|
|
{
|
|
.tileTag = GFXTAG_FOG_D,
|
|
.paletteTag = PALTAG_WEATHER,
|
|
.oam = &sFogDiagonalSpriteOamData,
|
|
.anims = sFogDiagonalSpriteAnimCmds,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = UpdateFogDiagonalSprite,
|
|
};
|
|
|
|
#define tSpriteColumn data[0]
|
|
#define tSpriteRow data[1]
|
|
|
|
static void CreateFogDiagonalSprites(void)
|
|
{
|
|
u16 i;
|
|
struct SpriteSheet fogDiagonalSpriteSheet;
|
|
u8 spriteId;
|
|
struct Sprite *sprite;
|
|
|
|
if (!gWeatherPtr->fogDSpritesCreated)
|
|
{
|
|
fogDiagonalSpriteSheet = sFogDiagonalSpriteSheet;
|
|
LoadSpriteSheet(&fogDiagonalSpriteSheet);
|
|
for (i = 0; i < NUM_FOG_DIAGONAL_SPRITES; i++)
|
|
{
|
|
spriteId = CreateSpriteAtEnd(&sFogDiagonalSpriteTemplate, 0, (i / 5) * 64, 0xFF);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
sprite = &gSprites[spriteId];
|
|
sprite->tSpriteColumn = i % 5;
|
|
sprite->tSpriteRow = i / 5;
|
|
gWeatherPtr->sprites.s2.fogDSprites[i] = sprite;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->sprites.s2.fogDSprites[i] = NULL;
|
|
}
|
|
}
|
|
|
|
gWeatherPtr->fogDSpritesCreated = TRUE;
|
|
}
|
|
}
|
|
|
|
static void DestroyFogDiagonalSprites(void)
|
|
{
|
|
u16 i;
|
|
|
|
if (gWeatherPtr->fogDSpritesCreated)
|
|
{
|
|
for (i = 0; i < NUM_FOG_DIAGONAL_SPRITES; i++)
|
|
{
|
|
if (gWeatherPtr->sprites.s2.fogDSprites[i])
|
|
DestroySprite(gWeatherPtr->sprites.s2.fogDSprites[i]);
|
|
}
|
|
|
|
FreeSpriteTilesByTag(GFXTAG_FOG_D);
|
|
gWeatherPtr->fogDSpritesCreated = FALSE;
|
|
}
|
|
}
|
|
|
|
static void UpdateFogDiagonalSprite(struct Sprite *sprite)
|
|
{
|
|
sprite->y2 = gWeatherPtr->fogDPosY;
|
|
sprite->x = gWeatherPtr->fogDBaseSpritesX + 32 + sprite->tSpriteColumn * 64;
|
|
if (sprite->x >= DISPLAY_WIDTH + 32)
|
|
{
|
|
sprite->x = gWeatherPtr->fogDBaseSpritesX + (DISPLAY_WIDTH * 2) - (4 - sprite->tSpriteColumn) * 64;
|
|
sprite->x &= 0x1FF;
|
|
}
|
|
}
|
|
|
|
#undef tSpriteColumn
|
|
#undef tSpriteRow
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_SANDSTORM
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void UpdateSandstormWaveIndex(void);
|
|
static void UpdateSandstormMovement(void);
|
|
static void CreateSandstormSprites(void);
|
|
static void CreateSwirlSandstormSprites(void);
|
|
static void DestroySandstormSprites(void);
|
|
static void UpdateSandstormSprite(struct Sprite *);
|
|
static void WaitSandSwirlSpriteEntrance(struct Sprite *);
|
|
static void UpdateSandstormSwirlSprite(struct Sprite *);
|
|
|
|
#define MIN_SANDSTORM_WAVE_INDEX 0x20
|
|
|
|
void Sandstorm_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = 0;
|
|
gWeatherPtr->weatherGfxLoaded = 0;
|
|
gWeatherPtr->targetColorMapIndex = 0;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
if (!gWeatherPtr->sandstormSpritesCreated)
|
|
{
|
|
gWeatherPtr->sandstormXOffset = gWeatherPtr->sandstormYOffset = 0;
|
|
gWeatherPtr->sandstormWaveIndex = 8;
|
|
gWeatherPtr->sandstormWaveCounter = 0;
|
|
// Dead code. How does the compiler not optimize this out?
|
|
if (gWeatherPtr->sandstormWaveIndex >= 0x80 - MIN_SANDSTORM_WAVE_INDEX)
|
|
gWeatherPtr->sandstormWaveIndex = 0x80 - gWeatherPtr->sandstormWaveIndex;
|
|
|
|
Weather_SetBlendCoeffs(0, 16);
|
|
}
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Sandstorm_InitAll(void)
|
|
{
|
|
Sandstorm_InitVars();
|
|
while (!gWeatherPtr->weatherGfxLoaded)
|
|
Sandstorm_Main();
|
|
}
|
|
|
|
void Sandstorm_Main(void)
|
|
{
|
|
UpdateSandstormMovement();
|
|
UpdateSandstormWaveIndex();
|
|
if (gWeatherPtr->sandstormWaveIndex >= 0x80 - MIN_SANDSTORM_WAVE_INDEX)
|
|
gWeatherPtr->sandstormWaveIndex = MIN_SANDSTORM_WAVE_INDEX;
|
|
|
|
switch (gWeatherPtr->initStep)
|
|
{
|
|
case 0:
|
|
CreateSandstormSprites();
|
|
CreateSwirlSandstormSprites();
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 1:
|
|
Weather_SetTargetBlendCoeffs(16, 2, 0);
|
|
UpdateShadowColor(0x3DEF);
|
|
gWeatherPtr->initStep++;
|
|
break;
|
|
case 2:
|
|
if (Weather_UpdateBlend())
|
|
{
|
|
gWeatherPtr->weatherGfxLoaded = TRUE;
|
|
gWeatherPtr->initStep++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
bool8 Sandstorm_Finish(void)
|
|
{
|
|
UpdateSandstormMovement();
|
|
UpdateSandstormWaveIndex();
|
|
switch (gWeatherPtr->finishStep)
|
|
{
|
|
case 0:
|
|
Weather_SetTargetBlendCoeffs(0, 16, 0);
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
case 1:
|
|
if (Weather_UpdateBlend())
|
|
gWeatherPtr->finishStep++;
|
|
if (gWeatherPtr->currBlendEVB == 12)
|
|
UpdateShadowColor(RGB_BLACK);
|
|
break;
|
|
case 2:
|
|
DestroySandstormSprites();
|
|
UpdateShadowColor(RGB_BLACK);
|
|
gWeatherPtr->finishStep++;
|
|
break;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void UpdateSandstormWaveIndex(void)
|
|
{
|
|
if (gWeatherPtr->sandstormWaveCounter++ > 4)
|
|
{
|
|
gWeatherPtr->sandstormWaveIndex++;
|
|
gWeatherPtr->sandstormWaveCounter = 0;
|
|
}
|
|
}
|
|
|
|
static void UpdateSandstormMovement(void)
|
|
{
|
|
gWeatherPtr->sandstormXOffset -= gSineTable[gWeatherPtr->sandstormWaveIndex] * 4;
|
|
gWeatherPtr->sandstormYOffset -= gSineTable[gWeatherPtr->sandstormWaveIndex];
|
|
gWeatherPtr->sandstormBaseSpritesX = (gSpriteCoordOffsetX + (gWeatherPtr->sandstormXOffset >> 8)) & 0xFF;
|
|
gWeatherPtr->sandstormPosY = gSpriteCoordOffsetY + (gWeatherPtr->sandstormYOffset >> 8);
|
|
}
|
|
|
|
static void DestroySandstormSprites(void)
|
|
{
|
|
u16 i;
|
|
|
|
if (gWeatherPtr->sandstormSpritesCreated)
|
|
{
|
|
for (i = 0; i < NUM_SANDSTORM_SPRITES; i++)
|
|
{
|
|
if (gWeatherPtr->sprites.s2.sandstormSprites1[i])
|
|
DestroySprite(gWeatherPtr->sprites.s2.sandstormSprites1[i]);
|
|
}
|
|
|
|
gWeatherPtr->sandstormSpritesCreated = FALSE;
|
|
FreeSpriteTilesByTag(GFXTAG_SANDSTORM);
|
|
}
|
|
|
|
if (gWeatherPtr->sandstormSwirlSpritesCreated)
|
|
{
|
|
for (i = 0; i < NUM_SWIRL_SANDSTORM_SPRITES; i++)
|
|
{
|
|
if (gWeatherPtr->sprites.s2.sandstormSprites2[i] != NULL)
|
|
DestroySprite(gWeatherPtr->sprites.s2.sandstormSprites2[i]);
|
|
}
|
|
|
|
gWeatherPtr->sandstormSwirlSpritesCreated = FALSE;
|
|
}
|
|
}
|
|
|
|
static const struct OamData sSandstormSpriteOamData =
|
|
{
|
|
.y = 0,
|
|
.affineMode = ST_OAM_AFFINE_OFF,
|
|
.objMode = ST_OAM_OBJ_BLEND,
|
|
.bpp = ST_OAM_4BPP,
|
|
.shape = SPRITE_SHAPE(64x64),
|
|
.x = 0,
|
|
.size = SPRITE_SIZE(64x64),
|
|
.tileNum = 0,
|
|
.priority = 1,
|
|
.paletteNum = 0,
|
|
};
|
|
|
|
static const union AnimCmd sSandstormSpriteAnimCmd0[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 3),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd sSandstormSpriteAnimCmd1[] =
|
|
{
|
|
ANIMCMD_FRAME(64, 3),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sSandstormSpriteAnimCmds[] =
|
|
{
|
|
sSandstormSpriteAnimCmd0,
|
|
sSandstormSpriteAnimCmd1,
|
|
};
|
|
|
|
static const struct SpriteTemplate sSandstormSpriteTemplate =
|
|
{
|
|
.tileTag = GFXTAG_SANDSTORM,
|
|
.paletteTag = PALTAG_WEATHER_2,
|
|
.oam = &sSandstormSpriteOamData,
|
|
.anims = sSandstormSpriteAnimCmds,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = UpdateSandstormSprite,
|
|
};
|
|
|
|
static const struct SpriteSheet sSandstormSpriteSheet =
|
|
{
|
|
.data = gWeatherSandstormTiles,
|
|
.size = sizeof(gWeatherSandstormTiles),
|
|
.tag = GFXTAG_SANDSTORM,
|
|
};
|
|
|
|
// Regular sandstorm sprites
|
|
#define tSpriteColumn data[0]
|
|
#define tSpriteRow data[1]
|
|
|
|
// Swirly sandstorm sprites
|
|
#define tRadius data[0]
|
|
#define tWaveIndex data[1]
|
|
#define tRadiusCounter data[2]
|
|
#define tEntranceDelay data[3]
|
|
|
|
static void CreateSandstormSprites(void)
|
|
{
|
|
u16 i;
|
|
u8 spriteId;
|
|
|
|
if (!gWeatherPtr->sandstormSpritesCreated)
|
|
{
|
|
LoadSpriteSheet(&sSandstormSpriteSheet);
|
|
LoadCustomWeatherSpritePalette(gSandstormWeatherPalette);
|
|
for (i = 0; i < NUM_SANDSTORM_SPRITES; i++)
|
|
{
|
|
spriteId = CreateSpriteAtEnd(&sSandstormSpriteTemplate, 0, (i / 5) * 64, 1);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
gWeatherPtr->sprites.s2.sandstormSprites1[i] = &gSprites[spriteId];
|
|
gWeatherPtr->sprites.s2.sandstormSprites1[i]->tSpriteColumn = i % 5;
|
|
gWeatherPtr->sprites.s2.sandstormSprites1[i]->tSpriteRow = i / 5;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->sprites.s2.sandstormSprites1[i] = NULL;
|
|
}
|
|
}
|
|
|
|
gWeatherPtr->sandstormSpritesCreated = TRUE;
|
|
}
|
|
}
|
|
|
|
static const u16 sSwirlEntranceDelays[] = {0, 120, 80, 160, 40, 0};
|
|
|
|
static void CreateSwirlSandstormSprites(void)
|
|
{
|
|
u16 i;
|
|
u8 spriteId;
|
|
|
|
if (!gWeatherPtr->sandstormSwirlSpritesCreated)
|
|
{
|
|
for (i = 0; i < NUM_SWIRL_SANDSTORM_SPRITES; i++)
|
|
{
|
|
spriteId = CreateSpriteAtEnd(&sSandstormSpriteTemplate, i * 48 + 24, 208, 1);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i] = &gSprites[spriteId];
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i]->oam.size = ST_OAM_SIZE_2;
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i]->tSpriteRow = i * 51;
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i]->tRadius = 8;
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i]->tRadiusCounter = 0;
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i]->data[4] = 0x6730; // unused value
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i]->tEntranceDelay = sSwirlEntranceDelays[i];
|
|
StartSpriteAnim(gWeatherPtr->sprites.s2.sandstormSprites2[i], 1);
|
|
CalcCenterToCornerVec(gWeatherPtr->sprites.s2.sandstormSprites2[i], SPRITE_SHAPE(32x32), SPRITE_SIZE(32x32), ST_OAM_AFFINE_OFF);
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i]->callback = WaitSandSwirlSpriteEntrance;
|
|
}
|
|
else
|
|
{
|
|
gWeatherPtr->sprites.s2.sandstormSprites2[i] = NULL;
|
|
}
|
|
|
|
gWeatherPtr->sandstormSwirlSpritesCreated = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void UpdateSandstormSprite(struct Sprite *sprite)
|
|
{
|
|
sprite->y2 = gWeatherPtr->sandstormPosY;
|
|
sprite->x = gWeatherPtr->sandstormBaseSpritesX + 32 + sprite->tSpriteColumn * 64;
|
|
if (sprite->x >= DISPLAY_WIDTH + 32)
|
|
{
|
|
sprite->x = gWeatherPtr->sandstormBaseSpritesX + (DISPLAY_WIDTH * 2) - (4 - sprite->tSpriteColumn) * 64;
|
|
sprite->x &= 0x1FF;
|
|
}
|
|
}
|
|
|
|
static void WaitSandSwirlSpriteEntrance(struct Sprite *sprite)
|
|
{
|
|
if (--sprite->tEntranceDelay == -1)
|
|
sprite->callback = UpdateSandstormSwirlSprite;
|
|
}
|
|
|
|
static void UpdateSandstormSwirlSprite(struct Sprite *sprite)
|
|
{
|
|
u32 x, y;
|
|
|
|
if (--sprite->y < -48)
|
|
{
|
|
sprite->y = DISPLAY_HEIGHT + 48;
|
|
sprite->tRadius = 4;
|
|
}
|
|
|
|
x = sprite->tRadius * gSineTable[sprite->tWaveIndex];
|
|
y = sprite->tRadius * gSineTable[sprite->tWaveIndex + 0x40];
|
|
sprite->x2 = x >> 8;
|
|
sprite->y2 = y >> 8;
|
|
sprite->tWaveIndex = (sprite->tWaveIndex + 10) & 0xFF;
|
|
if (++sprite->tRadiusCounter > 8)
|
|
{
|
|
sprite->tRadiusCounter = 0;
|
|
sprite->tRadius++;
|
|
}
|
|
}
|
|
|
|
#undef tSpriteColumn
|
|
#undef tSpriteRow
|
|
|
|
#undef tRadius
|
|
#undef tWaveIndex
|
|
#undef tRadiusCounter
|
|
#undef tEntranceDelay
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_SHADE
|
|
//------------------------------------------------------------------------------
|
|
|
|
void Shade_InitVars(void)
|
|
{
|
|
gWeatherPtr->initStep = 0;
|
|
gWeatherPtr->targetColorMapIndex = 3;
|
|
gWeatherPtr->colorMapStepDelay = 20;
|
|
Weather_SetBlendCoeffs(8, 12);
|
|
gWeatherPtr->noShadows = FALSE;
|
|
}
|
|
|
|
void Shade_InitAll(void)
|
|
{
|
|
Shade_InitVars();
|
|
}
|
|
|
|
void Shade_Main(void)
|
|
{
|
|
}
|
|
|
|
bool8 Shade_Finish(void)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// WEATHER_UNDERWATER_BUBBLES
|
|
//------------------------------------------------------------------------------
|
|
|
|
static void CreateBubbleSprite(u16);
|
|
static void DestroyBubbleSprites(void);
|
|
static void UpdateBubbleSprite(struct Sprite *);
|
|
|
|
static const u8 sBubbleStartDelays[] = {40, 90, 60, 90, 2, 60, 40, 30};
|
|
|
|
static const struct SpriteSheet sWeatherBubbleSpriteSheet =
|
|
{
|
|
.data = gWeatherBubbleTiles,
|
|
.size = sizeof(gWeatherBubbleTiles),
|
|
.tag = GFXTAG_BUBBLE,
|
|
};
|
|
|
|
static const s16 sBubbleStartCoords[][2] =
|
|
{
|
|
{120, 160},
|
|
{376, 160},
|
|
{ 40, 140},
|
|
{296, 140},
|
|
{180, 130},
|
|
{436, 130},
|
|
{ 60, 160},
|
|
{436, 160},
|
|
{220, 180},
|
|
{476, 180},
|
|
{ 10, 90},
|
|
{266, 90},
|
|
{256, 160},
|
|
};
|
|
|
|
void Bubbles_InitVars(void)
|
|
{
|
|
FogHorizontal_InitVars();
|
|
if (!gWeatherPtr->bubblesSpritesCreated)
|
|
{
|
|
LoadSpriteSheet(&sWeatherBubbleSpriteSheet);
|
|
gWeatherPtr->bubblesDelayIndex = 0;
|
|
gWeatherPtr->bubblesDelayCounter = sBubbleStartDelays[0];
|
|
gWeatherPtr->bubblesCoordsIndex = 0;
|
|
gWeatherPtr->bubblesSpriteCount = 0;
|
|
}
|
|
gWeatherPtr->noShadows = TRUE;
|
|
}
|
|
|
|
void Bubbles_InitAll(void)
|
|
{
|
|
Bubbles_InitVars();
|
|
while (!gWeatherPtr->weatherGfxLoaded)
|
|
Bubbles_Main();
|
|
}
|
|
|
|
void Bubbles_Main(void)
|
|
{
|
|
FogHorizontal_Main();
|
|
if (++gWeatherPtr->bubblesDelayCounter > sBubbleStartDelays[gWeatherPtr->bubblesDelayIndex])
|
|
{
|
|
gWeatherPtr->bubblesDelayCounter = 0;
|
|
if (++gWeatherPtr->bubblesDelayIndex > ARRAY_COUNT(sBubbleStartDelays) - 1)
|
|
gWeatherPtr->bubblesDelayIndex = 0;
|
|
|
|
CreateBubbleSprite(gWeatherPtr->bubblesCoordsIndex);
|
|
if (++gWeatherPtr->bubblesCoordsIndex > ARRAY_COUNT(sBubbleStartCoords) - 1)
|
|
gWeatherPtr->bubblesCoordsIndex = 0;
|
|
}
|
|
}
|
|
|
|
bool8 Bubbles_Finish(void)
|
|
{
|
|
if (!FogHorizontal_Finish())
|
|
{
|
|
DestroyBubbleSprites();
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static const union AnimCmd sBubbleSpriteAnimCmd0[] =
|
|
{
|
|
ANIMCMD_FRAME(0, 16),
|
|
ANIMCMD_FRAME(1, 16),
|
|
ANIMCMD_END,
|
|
};
|
|
|
|
static const union AnimCmd *const sBubbleSpriteAnimCmds[] =
|
|
{
|
|
sBubbleSpriteAnimCmd0,
|
|
};
|
|
|
|
static const struct SpriteTemplate sBubbleSpriteTemplate =
|
|
{
|
|
.tileTag = GFXTAG_BUBBLE,
|
|
.paletteTag = PALTAG_WEATHER,
|
|
.oam = &gOamData_AffineOff_ObjNormal_8x8,
|
|
.anims = sBubbleSpriteAnimCmds,
|
|
.images = NULL,
|
|
.affineAnims = gDummySpriteAffineAnimTable,
|
|
.callback = UpdateBubbleSprite,
|
|
};
|
|
|
|
#define tScrollXCounter data[0]
|
|
#define tScrollXDir data[1]
|
|
#define tCounter data[2]
|
|
|
|
static void CreateBubbleSprite(u16 coordsIndex)
|
|
{
|
|
s16 x = sBubbleStartCoords[coordsIndex][0];
|
|
s16 y = sBubbleStartCoords[coordsIndex][1] - gSpriteCoordOffsetY;
|
|
u8 spriteId = CreateSpriteAtEnd(&sBubbleSpriteTemplate, x, y, 0);
|
|
if (spriteId != MAX_SPRITES)
|
|
{
|
|
gSprites[spriteId].oam.priority = 1;
|
|
gSprites[spriteId].coordOffsetEnabled = TRUE;
|
|
gSprites[spriteId].tScrollXCounter = 0;
|
|
gSprites[spriteId].tScrollXDir = 0;
|
|
gSprites[spriteId].tCounter = 0;
|
|
gWeatherPtr->bubblesSpriteCount++;
|
|
}
|
|
}
|
|
|
|
static void DestroyBubbleSprites(void)
|
|
{
|
|
u16 i;
|
|
|
|
if (gWeatherPtr->bubblesSpriteCount)
|
|
{
|
|
for (i = 0; i < MAX_SPRITES; i++)
|
|
{
|
|
if (gSprites[i].template == &sBubbleSpriteTemplate)
|
|
DestroySprite(&gSprites[i]);
|
|
}
|
|
|
|
FreeSpriteTilesByTag(GFXTAG_BUBBLE);
|
|
gWeatherPtr->bubblesSpriteCount = 0;
|
|
}
|
|
}
|
|
|
|
static void UpdateBubbleSprite(struct Sprite *sprite)
|
|
{
|
|
++sprite->tScrollXCounter;
|
|
if (++sprite->tScrollXCounter > 8) // double increment
|
|
{
|
|
sprite->tScrollXCounter = 0;
|
|
if (sprite->tScrollXDir == 0)
|
|
{
|
|
if (++sprite->x2 > 4)
|
|
sprite->tScrollXDir = 1;
|
|
}
|
|
else
|
|
{
|
|
if (--sprite->x2 <= 0)
|
|
sprite->tScrollXDir = 0;
|
|
}
|
|
}
|
|
|
|
sprite->y -= 3;
|
|
if (++sprite->tCounter >= 120)
|
|
DestroySprite(sprite);
|
|
}
|
|
|
|
#undef tScrollXCounter
|
|
#undef tScrollXDir
|
|
#undef tCounter
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
// Unused function.
|
|
static void UnusedSetCurrentAbnormalWeather(u32 weather, u32 unknown)
|
|
{
|
|
sCurrentAbnormalWeather = weather;
|
|
sUnusedWeatherRelated = unknown;
|
|
}
|
|
|
|
#define tState data[0]
|
|
#define tWeatherA data[1]
|
|
#define tWeatherB data[2]
|
|
#define tDelay data[15]
|
|
|
|
static void Task_DoAbnormalWeather(u8 taskId)
|
|
{
|
|
s16 *data = gTasks[taskId].data;
|
|
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
if (tDelay-- <= 0)
|
|
{
|
|
SetNextWeather(tWeatherA);
|
|
sCurrentAbnormalWeather = tWeatherA;
|
|
tDelay = 600;
|
|
tState++;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (tDelay-- <= 0)
|
|
{
|
|
SetNextWeather(tWeatherB);
|
|
sCurrentAbnormalWeather = tWeatherB;
|
|
tDelay = 600;
|
|
tState = 0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void CreateAbnormalWeatherTask(void)
|
|
{
|
|
u8 taskId = CreateTask(Task_DoAbnormalWeather, 0);
|
|
s16 *data = gTasks[taskId].data;
|
|
|
|
tDelay = 600;
|
|
if (sCurrentAbnormalWeather == WEATHER_DOWNPOUR)
|
|
{
|
|
// Currently Downpour, next will be Drought
|
|
tWeatherA = WEATHER_DROUGHT;
|
|
tWeatherB = WEATHER_DOWNPOUR;
|
|
}
|
|
else if (sCurrentAbnormalWeather == WEATHER_DROUGHT)
|
|
{
|
|
// Currently Drought, next will be Downpour
|
|
tWeatherA = WEATHER_DOWNPOUR;
|
|
tWeatherB = WEATHER_DROUGHT;
|
|
}
|
|
else
|
|
{
|
|
// Default to starting with Downpour
|
|
sCurrentAbnormalWeather = WEATHER_DOWNPOUR;
|
|
tWeatherA = WEATHER_DROUGHT;
|
|
tWeatherB = WEATHER_DOWNPOUR;
|
|
}
|
|
}
|
|
|
|
#undef tState
|
|
#undef tWeatherA
|
|
#undef tWeatherB
|
|
#undef tDelay
|
|
|
|
static u8 TranslateWeatherNum(u8);
|
|
static void UpdateRainCounter(u8, u8);
|
|
|
|
void SetSavedWeather(u32 weather)
|
|
{
|
|
u8 oldWeather = gSaveBlock1Ptr->weather;
|
|
gSaveBlock1Ptr->weather = TranslateWeatherNum(weather);
|
|
UpdateRainCounter(gSaveBlock1Ptr->weather, oldWeather);
|
|
}
|
|
|
|
u8 GetSavedWeather(void)
|
|
{
|
|
return gSaveBlock1Ptr->weather;
|
|
}
|
|
|
|
void SetSavedWeatherFromCurrMapHeader(void)
|
|
{
|
|
u8 oldWeather = gSaveBlock1Ptr->weather;
|
|
gSaveBlock1Ptr->weather = TranslateWeatherNum(gMapHeader.weather);
|
|
UpdateRainCounter(gSaveBlock1Ptr->weather, oldWeather);
|
|
}
|
|
|
|
void SetWeather(u32 weather)
|
|
{
|
|
SetSavedWeather(weather);
|
|
SetNextWeather(GetSavedWeather());
|
|
}
|
|
|
|
void SetWeather_Unused(u32 weather)
|
|
{
|
|
SetSavedWeather(weather);
|
|
SetCurrentAndNextWeather(GetSavedWeather());
|
|
}
|
|
|
|
void DoCurrentWeather(void)
|
|
{
|
|
u8 weather = GetSavedWeather();
|
|
|
|
if (weather == WEATHER_ABNORMAL)
|
|
{
|
|
if (!FuncIsActiveTask(Task_DoAbnormalWeather))
|
|
CreateAbnormalWeatherTask();
|
|
weather = sCurrentAbnormalWeather;
|
|
}
|
|
else
|
|
{
|
|
if (FuncIsActiveTask(Task_DoAbnormalWeather))
|
|
DestroyTask(FindTaskIdByFunc(Task_DoAbnormalWeather));
|
|
sCurrentAbnormalWeather = WEATHER_DOWNPOUR;
|
|
}
|
|
SetNextWeather(weather);
|
|
}
|
|
|
|
void ResumePausedWeather(void)
|
|
{
|
|
u8 weather = GetSavedWeather();
|
|
|
|
if (weather == WEATHER_ABNORMAL)
|
|
{
|
|
if (!FuncIsActiveTask(Task_DoAbnormalWeather))
|
|
CreateAbnormalWeatherTask();
|
|
weather = sCurrentAbnormalWeather;
|
|
}
|
|
else
|
|
{
|
|
if (FuncIsActiveTask(Task_DoAbnormalWeather))
|
|
DestroyTask(FindTaskIdByFunc(Task_DoAbnormalWeather));
|
|
sCurrentAbnormalWeather = WEATHER_DOWNPOUR;
|
|
}
|
|
SetCurrentAndNextWeather(weather);
|
|
}
|
|
|
|
#define WEATHER_CYCLE_LENGTH 4
|
|
|
|
static const u8 sWeatherCycleRoute119[WEATHER_CYCLE_LENGTH] =
|
|
{
|
|
WEATHER_SUNNY,
|
|
WEATHER_RAIN,
|
|
WEATHER_RAIN_THUNDERSTORM,
|
|
WEATHER_RAIN,
|
|
};
|
|
static const u8 sWeatherCycleRoute123[WEATHER_CYCLE_LENGTH] =
|
|
{
|
|
WEATHER_SUNNY,
|
|
WEATHER_SUNNY,
|
|
WEATHER_RAIN,
|
|
WEATHER_SUNNY,
|
|
};
|
|
|
|
static u8 TranslateWeatherNum(u8 weather)
|
|
{
|
|
switch (weather)
|
|
{
|
|
case WEATHER_NONE: return WEATHER_NONE;
|
|
case WEATHER_SUNNY_CLOUDS: return WEATHER_SUNNY_CLOUDS;
|
|
case WEATHER_SUNNY: return WEATHER_SUNNY;
|
|
case WEATHER_RAIN: return WEATHER_RAIN;
|
|
case WEATHER_SNOW: return WEATHER_SNOW;
|
|
case WEATHER_RAIN_THUNDERSTORM: return WEATHER_RAIN_THUNDERSTORM;
|
|
case WEATHER_FOG_HORIZONTAL: return WEATHER_FOG_HORIZONTAL;
|
|
case WEATHER_VOLCANIC_ASH: return WEATHER_VOLCANIC_ASH;
|
|
case WEATHER_SANDSTORM: return WEATHER_SANDSTORM;
|
|
case WEATHER_FOG_DIAGONAL: return WEATHER_FOG_DIAGONAL;
|
|
case WEATHER_UNDERWATER: return WEATHER_UNDERWATER;
|
|
case WEATHER_SHADE: return WEATHER_SHADE;
|
|
case WEATHER_DROUGHT: return WEATHER_DROUGHT;
|
|
case WEATHER_DOWNPOUR: return WEATHER_DOWNPOUR;
|
|
case WEATHER_UNDERWATER_BUBBLES: return WEATHER_UNDERWATER_BUBBLES;
|
|
case WEATHER_ABNORMAL: return WEATHER_ABNORMAL;
|
|
case WEATHER_ROUTE119_CYCLE: return sWeatherCycleRoute119[gSaveBlock1Ptr->weatherCycleStage];
|
|
case WEATHER_ROUTE123_CYCLE: return sWeatherCycleRoute123[gSaveBlock1Ptr->weatherCycleStage];
|
|
default: return WEATHER_NONE;
|
|
}
|
|
}
|
|
|
|
void UpdateWeatherPerDay(u16 increment)
|
|
{
|
|
u16 weatherStage = gSaveBlock1Ptr->weatherCycleStage + increment;
|
|
weatherStage %= WEATHER_CYCLE_LENGTH;
|
|
gSaveBlock1Ptr->weatherCycleStage = weatherStage;
|
|
}
|
|
|
|
static void UpdateRainCounter(u8 newWeather, u8 oldWeather)
|
|
{
|
|
if (newWeather != oldWeather
|
|
&& (newWeather == WEATHER_RAIN || newWeather == WEATHER_RAIN_THUNDERSTORM))
|
|
IncrementGameStat(GAME_STAT_GOT_RAINED_ON);
|
|
}
|