mirror of
https://github.com/pret/pokefirered.git
synced 2026-05-25 23:33:07 -05:00
380 lines
10 KiB
C
380 lines
10 KiB
C
#include "global.h"
|
|
#include "gflib.h"
|
|
#include "bike.h"
|
|
#include "clock.h"
|
|
#include "event_data.h"
|
|
#include "field_camera.h"
|
|
#include "field_effect_helpers.h"
|
|
#include "field_player_avatar.h"
|
|
#include "fieldmap.h"
|
|
#include "metatile_behavior.h"
|
|
#include "overworld.h"
|
|
#include "quest_log.h"
|
|
#include "script.h"
|
|
#include "task.h"
|
|
#include "constants/field_tasks.h"
|
|
#include "constants/metatile_labels.h"
|
|
#include "constants/songs.h"
|
|
|
|
/* This file handles some persistent tasks that run in the overworld.
|
|
* - Task_RunTimeBasedEvents: Triggers ambient cries. In RSE, this also periodically updates local time and RTC events.
|
|
* - Task_RunPerStepCallback: Calls one of the functions in sPerStepCallbacks, listed below...
|
|
* . DummyPerStepCallback: Default, does nothing. Includes functionality from RS that was removed.
|
|
* . AshGrassPerStepCallback: Leftover from RS. Removes the ash from ash-covered grass that the player steps on.
|
|
* . IcefallCaveIcePerStepCallback: Cracks/breaks ice in Icefall Cave that the player steps on.
|
|
* . CrackedFloorPerStepCallback: Leftover from RS. Breaks cracked floors that the player steps on.
|
|
*
|
|
* NOTE: "PerStep" is perhaps misleading. One function in sPerStepCallbacks is called
|
|
* every frame while in the overworld by Task_RunPerStepCallback regardless of
|
|
* whether or not steps are being taken. However, nearly all of the functions in
|
|
* the table check if the player has moved from their previous position before
|
|
* doing anything else.
|
|
*/
|
|
|
|
static void DummyPerStepCallback(u8 taskId);
|
|
static void AshGrassPerStepCallback(u8 taskId);
|
|
static void IcefallCaveIcePerStepCallback(u8 taskId);
|
|
static void CrackedFloorPerStepCallback(u8 taskId);
|
|
|
|
static const TaskFunc sPerStepCallbacks[] =
|
|
{
|
|
[STEP_CB_DUMMY] = DummyPerStepCallback,
|
|
[STEP_CB_ASH] = AshGrassPerStepCallback,
|
|
[STEP_CB_FORTREE_BRIDGE] = DummyPerStepCallback,
|
|
[STEP_CB_PACIFIDLOG_BRIDGE] = DummyPerStepCallback,
|
|
[STEP_CB_ICE] = IcefallCaveIcePerStepCallback,
|
|
[STEP_CB_TRUCK] = DummyPerStepCallback,
|
|
[STEP_CB_SECRET_BASE] = DummyPerStepCallback,
|
|
[STEP_CB_CRACKED_FLOOR] = CrackedFloorPerStepCallback
|
|
};
|
|
|
|
// The positions of each map space with crackable ice in Icefall Cave.
|
|
static const u8 sIcefallCaveIceCoords[][2] =
|
|
{
|
|
{ 8, 3 },
|
|
{ 10, 5 },
|
|
{ 15, 5 },
|
|
{ 8, 9 },
|
|
{ 9, 9 },
|
|
{ 16, 9 },
|
|
{ 8, 10 },
|
|
{ 9, 10 },
|
|
{ 8, 14 }
|
|
};
|
|
|
|
#define tCallbackId data[0]
|
|
|
|
static void Task_RunPerStepCallback(u8 taskId)
|
|
{
|
|
int idx = gTasks[taskId].tCallbackId;
|
|
sPerStepCallbacks[idx](taskId);
|
|
}
|
|
|
|
|
|
// RTC functionality from RS was removed here.
|
|
#define tState data[0]
|
|
#define tAmbientCryState data[1]
|
|
#define tAmbientCryDelay data[2]
|
|
|
|
static void RunTimeBasedEvents(s16 *data)
|
|
{
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
if (*gMain.vblankCounter1 & 0x1000)
|
|
{
|
|
DoTimeBasedEvents();
|
|
tState++;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (!(*gMain.vblankCounter1 & 0x1000))
|
|
{
|
|
tState--;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Task_RunTimeBasedEvents(u8 taskId)
|
|
{
|
|
s16 *data = gTasks[taskId].data;
|
|
|
|
if (!ArePlayerFieldControlsLocked() && !QL_IS_PLAYBACK_STATE)
|
|
{
|
|
RunTimeBasedEvents(data);
|
|
UpdateAmbientCry(&tAmbientCryState, &tAmbientCryDelay);
|
|
}
|
|
}
|
|
|
|
#undef tState
|
|
|
|
void SetUpFieldTasks(void)
|
|
{
|
|
if (!FuncIsActiveTask(Task_RunPerStepCallback))
|
|
{
|
|
u8 taskId = CreateTask(Task_RunPerStepCallback, 80);
|
|
gTasks[taskId].tCallbackId = STEP_CB_DUMMY;
|
|
}
|
|
|
|
if (!FuncIsActiveTask(Task_RunTimeBasedEvents))
|
|
CreateTask(Task_RunTimeBasedEvents, 80);
|
|
}
|
|
|
|
void ActivatePerStepCallback(u8 callbackId)
|
|
{
|
|
u8 taskId = FindTaskIdByFunc(Task_RunPerStepCallback);
|
|
if (taskId != TASK_NONE)
|
|
{
|
|
s32 i;
|
|
s16 *data = gTasks[taskId].data;
|
|
|
|
for (i = 0; i < NUM_TASK_DATA; i++)
|
|
data[i] = 0;
|
|
|
|
if (callbackId >= ARRAY_COUNT(sPerStepCallbacks))
|
|
tCallbackId = STEP_CB_DUMMY;
|
|
else
|
|
tCallbackId = callbackId;
|
|
}
|
|
}
|
|
|
|
void ResetFieldTasksArgs(void)
|
|
{
|
|
u8 taskId;
|
|
s16 *data;
|
|
|
|
taskId = FindTaskIdByFunc(Task_RunPerStepCallback);
|
|
if (taskId != TASK_NONE)
|
|
data = gTasks[taskId].data;
|
|
|
|
taskId = FindTaskIdByFunc(Task_RunTimeBasedEvents);
|
|
if (taskId != TASK_NONE)
|
|
{
|
|
data = gTasks[taskId].data;
|
|
tAmbientCryState = 0;
|
|
tAmbientCryDelay = 0;
|
|
}
|
|
}
|
|
|
|
#undef tAmbientCryState
|
|
#undef tAmbientCryDelay
|
|
|
|
static void DummyPerStepCallback(u8 taskId)
|
|
{
|
|
}
|
|
|
|
static void MarkIcePuzzleCoordVisited(s16 x, s16 y)
|
|
{
|
|
u8 i;
|
|
for (i = 0; i < ARRAY_COUNT(sIcefallCaveIceCoords); i++)
|
|
{
|
|
if (sIcefallCaveIceCoords[i][0] + MAP_OFFSET == x && sIcefallCaveIceCoords[i][1] + MAP_OFFSET == y)
|
|
{
|
|
FlagSet(i + 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SetIcefallCaveCrackedIceMetatiles(void)
|
|
{
|
|
u8 i;
|
|
for (i = 0; i < ARRAY_COUNT(sIcefallCaveIceCoords); i++)
|
|
{
|
|
if (FlagGet(i + 1) == TRUE)
|
|
{
|
|
int x = sIcefallCaveIceCoords[i][0] + MAP_OFFSET;
|
|
int y = sIcefallCaveIceCoords[i][1] + MAP_OFFSET;
|
|
MapGridSetMetatileIdAt(x, y, METATILE_SeafoamIslands_CrackedIce);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define tState data[1]
|
|
#define tPrevX data[2]
|
|
#define tPrevY data[3]
|
|
#define tIceX data[4]
|
|
#define tIceY data[5]
|
|
#define tDelay data[6]
|
|
|
|
static void IcefallCaveIcePerStepCallback(u8 taskId)
|
|
{
|
|
s16 x, y;
|
|
u8 tileBehavior;
|
|
u16 *iceStepCount;
|
|
s16 *data = gTasks[taskId].data;
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
PlayerGetDestCoords(&x, &y);
|
|
tPrevX = x;
|
|
tPrevY = y;
|
|
tState = 1;
|
|
break;
|
|
case 1:
|
|
PlayerGetDestCoords(&x, &y);
|
|
// End if player hasn't moved
|
|
if (x == tPrevX && y == tPrevY)
|
|
return;
|
|
|
|
tPrevX = x;
|
|
tPrevY = y;
|
|
tileBehavior = MapGridGetMetatileBehaviorAt(x, y);
|
|
if (MetatileBehavior_IsThinIce(tileBehavior) == TRUE)
|
|
{
|
|
// Thin ice, set it to cracked ice
|
|
MarkIcePuzzleCoordVisited(x, y);
|
|
tDelay = 4;
|
|
tState = 2;
|
|
tIceX = x;
|
|
tIceY = y;
|
|
}
|
|
else if (MetatileBehavior_IsCrackedIce(tileBehavior) == TRUE)
|
|
{
|
|
// Cracked ice, set it to broken ice
|
|
tDelay = 4;
|
|
tState = 3;
|
|
tIceX = x;
|
|
tIceY = y;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (tDelay != 0)
|
|
{
|
|
tDelay--;
|
|
}
|
|
else
|
|
{
|
|
// Crack ice
|
|
x = tIceX;
|
|
y = tIceY;
|
|
PlaySE(SE_ICE_CRACK);
|
|
MapGridSetMetatileIdAt(x, y, METATILE_SeafoamIslands_CrackedIce);
|
|
CurrentMapDrawMetatileAt(x, y);
|
|
tState = 1;
|
|
}
|
|
break;
|
|
case 3:
|
|
if (tDelay != 0)
|
|
{
|
|
tDelay--;
|
|
}
|
|
else
|
|
{
|
|
// Break ice
|
|
x = tIceX;
|
|
y = tIceY;
|
|
PlaySE(SE_ICE_BREAK);
|
|
MapGridSetMetatileIdAt(x, y, METATILE_SeafoamIslands_IceHole);
|
|
CurrentMapDrawMetatileAt(x, y);
|
|
VarSet(VAR_TEMP_1, 1);
|
|
tState = 1;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef tState
|
|
#undef tPrevX
|
|
#undef tPrevY
|
|
#undef tIceX
|
|
#undef tIceY
|
|
#undef tDelay
|
|
|
|
#define tPrevX data[1]
|
|
#define tPrevY data[2]
|
|
|
|
// Unused. For some reason this was not dummied out like the other RSE-exclusive step callbacks.
|
|
static void AshGrassPerStepCallback(u8 taskId)
|
|
{
|
|
s16 x, y;
|
|
u16 *ashGatherCount;
|
|
s16 *data = gTasks[taskId].data;
|
|
PlayerGetDestCoords(&x, &y);
|
|
|
|
// End if player hasn't moved
|
|
if (x == tPrevX && y == tPrevY)
|
|
return;
|
|
|
|
tPrevX = x;
|
|
tPrevY = y;
|
|
if (MetatileBehavior_IsAshGrass((u8)MapGridGetMetatileBehaviorAt(x, y)))
|
|
{
|
|
// Remove ash from grass
|
|
if (MapGridGetMetatileIdAt(x, y) == METATILE_Fallarbor_AshGrass)
|
|
StartAshFieldEffect(x, y, METATILE_Fallarbor_NormalGrass, 4);
|
|
else
|
|
StartAshFieldEffect(x, y, METATILE_Lavaridge_NormalGrass, 4);
|
|
}
|
|
}
|
|
|
|
#undef tPrevX
|
|
#undef tPrevY
|
|
|
|
// Unused. For some reason these were not dummied out like the other RSE-exclusive step callbacks.
|
|
static void SetCrackedFloorHoleMetatile(s16 x, s16 y)
|
|
{
|
|
MapGridSetMetatileIdAt(x, y, MapGridGetMetatileIdAt(x, y) == METATILE_RSCave_CrackedFloor ? METATILE_RSCave_CrackedFloor_Hole : METATILE_Pacifidlog_SkyPillar_CrackedFloor_Hole);
|
|
CurrentMapDrawMetatileAt(x, y);
|
|
}
|
|
|
|
#define tPrevX data[2]
|
|
#define tPrevY data[3]
|
|
#define tFloor1Delay data[4]
|
|
#define tFloor1X data[5]
|
|
#define tFloor1Y data[6]
|
|
#define tFloor2Delay data[7]
|
|
#define tFloor2X data[8]
|
|
#define tFloor2Y data[9]
|
|
|
|
// Unused. See above.
|
|
static void CrackedFloorPerStepCallback(u8 taskId)
|
|
{
|
|
s16 x, y;
|
|
u16 behavior;
|
|
s16 *data = gTasks[taskId].data;
|
|
PlayerGetDestCoords(&x, &y);
|
|
behavior = MapGridGetMetatileBehaviorAt(x, y);
|
|
|
|
// Update up to 2 previous cracked floor spaces
|
|
if (tFloor1Delay != 0 && (--tFloor1Delay) == 0)
|
|
SetCrackedFloorHoleMetatile(tFloor1X, tFloor1Y);
|
|
if (tFloor2Delay != 0 && (--tFloor2Delay) == 0)
|
|
SetCrackedFloorHoleMetatile(tFloor2X, tFloor2Y);
|
|
|
|
// End if player hasn't moved
|
|
if (x == tPrevX && y == tPrevY)
|
|
return;
|
|
|
|
tPrevX = x;
|
|
tPrevY = y;
|
|
if (MetatileBehavior_IsCrackedFloor(behavior))
|
|
{
|
|
if (GetPlayerSpeed() != PLAYER_SPEED_FASTEST)
|
|
VarSet(VAR_ICE_STEP_COUNT, 0); // this var does double duty
|
|
|
|
if (tFloor1Delay == 0)
|
|
{
|
|
tFloor1Delay = 3;
|
|
tFloor1X = x;
|
|
tFloor1Y = y;
|
|
}
|
|
else if (tFloor2Delay == 0)
|
|
{
|
|
tFloor2Delay = 3;
|
|
tFloor2X = x;
|
|
tFloor2Y = y;
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef tPrevX
|
|
#undef tPrevY
|
|
#undef tFloor1Delay
|
|
#undef tFloor1X
|
|
#undef tFloor1Y
|
|
#undef tFloor2Delay
|
|
#undef tFloor2X
|
|
#undef tFloor2Y
|