Skip to content

Commit

Permalink
Merge pull request #1803 from abaresk/thunder
Browse files Browse the repository at this point in the history
Add more documentation to thunder weather effect
  • Loading branch information
GriffinRichards authored Oct 20, 2022
2 parents 0d0a9bd + 7f09894 commit fad92ec
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 76 deletions.
10 changes: 5 additions & 5 deletions include/field_weather.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,12 @@ struct Weather
u8 snowflakeSpriteCount;
u8 targetSnowflakeSpriteCount;
// Thunderstorm
u16 thunderDelay;
u16 thunderCounter;
u16 thunderTimer; // general-purpose timer for state transitions
u16 thunderSETimer; // timer for thunder sound effect
bool8 thunderAllowEnd;
bool8 thunderSkipShort;
u8 thunderShortRetries;
bool8 thunderTriggered;
bool8 thunderLongBolt; // true if this cycle will end in a long lightning bolt
u8 thunderShortBolts; // the number of short bolts this cycle
bool8 thunderEnqueued;
// Horizontal fog
u16 fogHScrollPosX;
u16 fogHScrollCounter;
Expand Down
152 changes: 81 additions & 71 deletions src/field_weather_effect.c
Original file line number Diff line number Diff line change
Expand Up @@ -1014,29 +1014,29 @@ static void UpdateSnowflakeSprite(struct Sprite *sprite)
enum {
// This block of states is run only once
// when first setting up the thunderstorm
TSTORM_STATE_LOAD_RAIN,
TSTORM_STATE_CREATE_RAIN,
TSTORM_STATE_INIT_RAIN,
TSTORM_STATE_WAIT_CHANGE,
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.
TSTORM_STATE_LOOP_START,
TSTORM_STATE_LOOP_WAIT,
TSTORM_STATE_INIT_THUNDER_SHORT_1,
TSTORM_STATE_INIT_THUNDER_SHORT_2,
TSTORM_STATE_TRY_THUNDER_SHORT,
TSTORM_STATE_TRY_NEW_THUNDER,
TSTORM_STATE_WAIT_THUNDER_SHORT,
TSTORM_STATE_INIT_THUNDER_LONG,
TSTORM_STATE_WAIT_THUNDER_LONG,
TSTORM_STATE_FADE_THUNDER_LONG,
TSTORM_STATE_END_THUNDER_LONG,
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 = TSTORM_STATE_LOAD_RAIN;
gWeatherPtr->initStep = THUNDER_STATE_LOAD_RAIN;
gWeatherPtr->weatherGfxLoaded = FALSE;
gWeatherPtr->rainSpriteVisibleCounter = 0;
gWeatherPtr->rainSpriteVisibleDelay = 4;
Expand All @@ -1045,7 +1045,7 @@ void Thunderstorm_InitVars(void)
gWeatherPtr->targetColorMapIndex = 3;
gWeatherPtr->colorMapStepDelay = 20;
gWeatherPtr->weatherGfxLoaded = FALSE; // duplicate assignment
gWeatherPtr->thunderTriggered = FALSE;
gWeatherPtr->thunderEnqueued = FALSE;
SetRainStrengthFromSoundEffect(SE_THUNDERSTORM);
}

Expand All @@ -1061,11 +1061,11 @@ void Thunderstorm_InitAll(void)
//------------------------------------------------------------------------------

static void UpdateThunderSound(void);
static void SetThunderCounter(u16);
static void EnqueueThunder(u16);

void Downpour_InitVars(void)
{
gWeatherPtr->initStep = TSTORM_STATE_LOAD_RAIN;
gWeatherPtr->initStep = THUNDER_STATE_LOAD_RAIN;
gWeatherPtr->weatherGfxLoaded = FALSE;
gWeatherPtr->rainSpriteVisibleCounter = 0;
gWeatherPtr->rainSpriteVisibleDelay = 4;
Expand All @@ -1084,110 +1084,119 @@ void Downpour_InitAll(void)
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 TSTORM_STATE_LOAD_RAIN:
case THUNDER_STATE_LOAD_RAIN:
LoadRainSpriteSheet();
gWeatherPtr->initStep++;
break;
case TSTORM_STATE_CREATE_RAIN:
case THUNDER_STATE_CREATE_RAIN:
if (!CreateRainSprite())
gWeatherPtr->initStep++;
break;
case TSTORM_STATE_INIT_RAIN:
case THUNDER_STATE_INIT_RAIN:
if (!UpdateVisibleRainSprites())
{
gWeatherPtr->weatherGfxLoaded = TRUE;
gWeatherPtr->initStep++;
}
break;
case TSTORM_STATE_WAIT_CHANGE:
case THUNDER_STATE_WAIT_CHANGE:
if (gWeatherPtr->palProcessingState != WEATHER_PAL_STATE_CHANGING_WEATHER)
gWeatherPtr->initStep = TSTORM_STATE_INIT_THUNDER_SHORT_1;
gWeatherPtr->initStep = THUNDER_STATE_INIT_CYCLE_1;
break;
case TSTORM_STATE_LOOP_START:
case THUNDER_STATE_NEW_CYCLE:
gWeatherPtr->thunderAllowEnd = TRUE;
gWeatherPtr->thunderDelay = (Random() % 360) + 360;
gWeatherPtr->thunderTimer = (Random() % 360) + 360;
gWeatherPtr->initStep++;
// fall through
case TSTORM_STATE_LOOP_WAIT:
// Wait between 360-720 frames before trying thunder again
if (--gWeatherPtr->thunderDelay == 0)
case THUNDER_STATE_NEW_CYCLE_WAIT:
// Wait between 360-720 frames before starting a new cycle.
if (--gWeatherPtr->thunderTimer == 0)
gWeatherPtr->initStep++;
break;
case TSTORM_STATE_INIT_THUNDER_SHORT_1:
case THUNDER_STATE_INIT_CYCLE_1:
gWeatherPtr->thunderAllowEnd = TRUE;
gWeatherPtr->thunderSkipShort = Random() % 2;
gWeatherPtr->thunderLongBolt = Random() % 2;
gWeatherPtr->initStep++;
break;
case TSTORM_STATE_INIT_THUNDER_SHORT_2:
gWeatherPtr->thunderShortRetries = (Random() & 1) + 1;
case THUNDER_STATE_INIT_CYCLE_2:
gWeatherPtr->thunderShortBolts = (Random() & 1) + 1;
gWeatherPtr->initStep++;
// fall through
case TSTORM_STATE_TRY_THUNDER_SHORT:
case THUNDER_STATE_SHORT_BOLT:
// Short bolt of lightning strikes.
ApplyWeatherColorMapIfIdle(19);
if (!gWeatherPtr->thunderSkipShort && gWeatherPtr->thunderShortRetries == 1)
SetThunderCounter(20); // Do short thunder
// If final lightning bolt, enqueue thunder.
if (!gWeatherPtr->thunderLongBolt && gWeatherPtr->thunderShortBolts == 1)
EnqueueThunder(20);

gWeatherPtr->thunderDelay = (Random() % 3) + 6;
gWeatherPtr->thunderTimer = (Random() % 3) + 6;
gWeatherPtr->initStep++;
break;
case TSTORM_STATE_TRY_NEW_THUNDER:
if (--gWeatherPtr->thunderDelay == 0)
case THUNDER_STATE_TRY_NEW_BOLT:
if (--gWeatherPtr->thunderTimer == 0)
{
// Short bolt of lightning ends.
ApplyWeatherColorMapIfIdle(3);
gWeatherPtr->thunderAllowEnd = TRUE;
if (--gWeatherPtr->thunderShortRetries != 0)
if (--gWeatherPtr->thunderShortBolts != 0)
{
// Try a short thunder again
gWeatherPtr->thunderDelay = (Random() % 16) + 60;
gWeatherPtr->initStep = TSTORM_STATE_WAIT_THUNDER_SHORT;
// Wait a little, then do another short bolt.
gWeatherPtr->thunderTimer = (Random() % 16) + 60;
gWeatherPtr->initStep = THUNDER_STATE_WAIT_BOLT_SHORT;
}
else if (!gWeatherPtr->thunderSkipShort)
else if (!gWeatherPtr->thunderLongBolt)
{
// No more thunder, restart loop
gWeatherPtr->initStep = TSTORM_STATE_LOOP_START;
// No more bolts, restart loop.
gWeatherPtr->initStep = THUNDER_STATE_NEW_CYCLE;
}
else
{
// Set up long thunder
gWeatherPtr->initStep = TSTORM_STATE_INIT_THUNDER_LONG;
// Set up long bolt.
gWeatherPtr->initStep = THUNDER_STATE_INIT_BOLT_LONG;
}
}
break;
case TSTORM_STATE_WAIT_THUNDER_SHORT:
if (--gWeatherPtr->thunderDelay == 0)
gWeatherPtr->initStep = TSTORM_STATE_TRY_THUNDER_SHORT;
case THUNDER_STATE_WAIT_BOLT_SHORT:
if (--gWeatherPtr->thunderTimer == 0)
gWeatherPtr->initStep = THUNDER_STATE_SHORT_BOLT;
break;
case TSTORM_STATE_INIT_THUNDER_LONG:
gWeatherPtr->thunderDelay = (Random() % 16) + 60;
case THUNDER_STATE_INIT_BOLT_LONG:
gWeatherPtr->thunderTimer = (Random() % 16) + 60;
gWeatherPtr->initStep++;
break;
case TSTORM_STATE_WAIT_THUNDER_LONG:
if (--gWeatherPtr->thunderDelay == 0)
case THUNDER_STATE_WAIT_BOLT_LONG:
if (--gWeatherPtr->thunderTimer == 0)
{
// Do long thunder
SetThunderCounter(100);
// Do long bolt. Enqueue thunder with a potentially longer delay.
EnqueueThunder(100);
ApplyWeatherColorMapIfIdle(19);
gWeatherPtr->thunderDelay = (Random() & 0xF) + 30;
gWeatherPtr->thunderTimer = (Random() & 0xF) + 30;
gWeatherPtr->initStep++;
}
break;
case TSTORM_STATE_FADE_THUNDER_LONG:
if (--gWeatherPtr->thunderDelay == 0)
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 TSTORM_STATE_END_THUNDER_LONG:
case THUNDER_STATE_END_BOLT_LONG:
if (gWeatherPtr->palProcessingState == WEATHER_PAL_STATE_IDLE)
{
gWeatherPtr->thunderAllowEnd = TRUE;
gWeatherPtr->initStep = TSTORM_STATE_LOOP_START;
gWeatherPtr->initStep = THUNDER_STATE_NEW_CYCLE;
}
break;
}
Expand Down Expand Up @@ -1218,7 +1227,7 @@ bool8 Thunderstorm_Finish(void)
if (!UpdateVisibleRainSprites())
{
DestroyRainSprites();
gWeatherPtr->thunderTriggered = 0;
gWeatherPtr->thunderEnqueued = FALSE;
gWeatherPtr->finishStep++;
return FALSE;
}
Expand All @@ -1229,20 +1238,21 @@ bool8 Thunderstorm_Finish(void)
return TRUE;
}

static void SetThunderCounter(u16 max)
// Enqueue a thunder sound effect for at most `waitFrames` frames from now.
static void EnqueueThunder(u16 waitFrames)
{
if (!gWeatherPtr->thunderTriggered)
if (!gWeatherPtr->thunderEnqueued)
{
gWeatherPtr->thunderCounter = Random() % max;
gWeatherPtr->thunderTriggered = TRUE;
gWeatherPtr->thunderSETimer = Random() % waitFrames;
gWeatherPtr->thunderEnqueued = TRUE;
}
}

static void UpdateThunderSound(void)
{
if (gWeatherPtr->thunderTriggered == TRUE)
if (gWeatherPtr->thunderEnqueued == TRUE)
{
if (gWeatherPtr->thunderCounter == 0)
if (gWeatherPtr->thunderSETimer == 0)
{
if (IsSEPlaying())
return;
Expand All @@ -1252,11 +1262,11 @@ static void UpdateThunderSound(void)
else
PlaySE(SE_THUNDER2);

gWeatherPtr->thunderTriggered = FALSE;
gWeatherPtr->thunderEnqueued = FALSE;
}
else
{
gWeatherPtr->thunderCounter--;
gWeatherPtr->thunderSETimer--;
}
}
}
Expand Down

0 comments on commit fad92ec

Please sign in to comment.