1785 lines
55 KiB
C
1785 lines
55 KiB
C
#include "global.h"
|
|
#include "gflib.h"
|
|
#include "task.h"
|
|
#include "menu.h"
|
|
#include "menu_helpers.h"
|
|
#include "text_window.h"
|
|
#include "event_data.h"
|
|
#include "script.h"
|
|
#include "overworld.h"
|
|
#include "field_fadetransition.h"
|
|
#include "field_weather.h"
|
|
#include "event_object_movement.h"
|
|
#include "event_object_lock.h"
|
|
#include "field_player_avatar.h"
|
|
#include "item.h"
|
|
#include "region_map.h"
|
|
#include "map_name_popup.h"
|
|
#include "wild_encounter.h"
|
|
#include "help_system.h"
|
|
#include "pokemon_storage_system.h"
|
|
#include "save.h"
|
|
#include "quest_log_objects.h"
|
|
#include "quest_log_player.h"
|
|
#include "quest_log.h"
|
|
#include "new_menu_helpers.h"
|
|
#include "strings.h"
|
|
#include "constants/event_objects.h"
|
|
#include "constants/maps.h"
|
|
#include "constants/quest_log.h"
|
|
#include "constants/field_weather.h"
|
|
#include "constants/event_object_movement.h"
|
|
|
|
/*
|
|
QUEST LOG
|
|
|
|
## What is the Quest Log?
|
|
|
|
When the player resumes playing a saved game a black-and-white sequence will play recounting some
|
|
significant events that happened leading up to the last time the player saved.
|
|
The name comes from the text displayed at the top of these scenes: "Previously on your quest..."
|
|
|
|
## Quest Log Terminology
|
|
|
|
- "Scene": This is the name for each sequence that plays during the quest log. There are QUEST_LOG_SCENE_COUNT (4)
|
|
of these scenes that play, after which a final screen shows where the game was saved before the game resumes.
|
|
All the data stored for each scene is in 'struct QuestLogScene', defined in include/global.h.
|
|
The entirety of the Quest Log's save data is represented by an array of these scenes in SaveBlock1.
|
|
|
|
- "Action": TODO: Copy from quest_log.h
|
|
|
|
- "Event":
|
|
|
|
- "Command":
|
|
|
|
- "Script":
|
|
|
|
*/
|
|
|
|
enum {
|
|
WIN_TOP_BAR, // Contains the "Previously on..." text
|
|
WIN_BOTTOM_BAR, // Empty, only briefly visible at the end or when the description window isn't covering it.
|
|
WIN_DESCRIPTION,
|
|
WIN_COUNT
|
|
};
|
|
|
|
#define DESC_WIN_WIDTH (DISPLAY_WIDTH / 8)
|
|
#define DESC_WIN_HEIGHT 6
|
|
|
|
// sQuestLogActionRecordBuffer should be large enough to fill a scene's script with the maximum number of actions
|
|
#define SCRIPT_BUFFER_SIZE (sizeof(gSaveBlock1Ptr->questLog[0].script) / sizeof(struct QuestLogAction))
|
|
|
|
enum {
|
|
END_MODE_NONE,
|
|
END_MODE_FINISH,
|
|
END_MODE_SCENE,
|
|
};
|
|
|
|
struct PlaybackControl
|
|
{
|
|
u8 state:4;
|
|
u8 playingEvent:2;
|
|
u8 endMode:2;
|
|
u8 cursor;
|
|
u8 timer;
|
|
u8 overlapTimer;
|
|
};
|
|
|
|
struct FlagOrVarRecord
|
|
{
|
|
u16 idx:15;
|
|
u16 isFlag:1;
|
|
u16 value;
|
|
};
|
|
|
|
|
|
u8 gQuestLogPlaybackState;
|
|
u16 sMaxActionsInScene;
|
|
struct FieldInput gQuestLogFieldInput;
|
|
struct QuestLogAction * sCurSceneActions;
|
|
|
|
static struct FlagOrVarRecord * sFlagOrVarRecords;
|
|
static u16 sNumFlagsOrVars;
|
|
|
|
static EWRAM_DATA u8 sCurrentSceneNum = 0;
|
|
static EWRAM_DATA u8 sNumScenes = 0;
|
|
EWRAM_DATA u8 gQuestLogState = 0;
|
|
static EWRAM_DATA u16 gUnknown_203ADFC = 0;
|
|
static EWRAM_DATA u8 sWindowIds[WIN_COUNT] = {0};
|
|
EWRAM_DATA u16 *gUnknown_203AE04 = NULL;
|
|
EWRAM_DATA u16 *gQuestLogRecordingPointer = NULL;
|
|
static EWRAM_DATA u16 *gUnknown_203AE0C[32] = {NULL};
|
|
static EWRAM_DATA void (* sQuestLogCB)(void) = NULL;
|
|
static EWRAM_DATA u16 *sPalettesBackup = NULL;
|
|
static EWRAM_DATA struct PlaybackControl sPlaybackControl = {0};
|
|
static EWRAM_DATA struct QuestLogAction sQuestLogActionRecordBuffer[SCRIPT_BUFFER_SIZE] = {0};
|
|
EWRAM_DATA u16 gQuestLogCurActionIdx = 0;
|
|
static EWRAM_DATA u8 sMovementScripts[OBJECT_EVENT_TEMPLATES_COUNT][2] = {{0}};
|
|
static EWRAM_DATA u16 sNextActionDelay = 0;
|
|
static EWRAM_DATA u16 sLastQuestLogCursor = 0;
|
|
static EWRAM_DATA u16 sFlagOrVarPlayhead = 0;
|
|
|
|
static void QLogCB_Recording(void);
|
|
static void QLogCB_Playback(void);
|
|
static void SetPlayerInitialCoordsAtScene(u8);
|
|
static void SetNPCInitialCoordsAtScene(u8);
|
|
static void RecordSceneEnd(void);
|
|
static void BackUpTrainerRematches(void);
|
|
static void BackUpMapLayout(void);
|
|
static void SetGameStateAtScene(u8);
|
|
static u8 TryRecordActionSequence(struct QuestLogAction *);
|
|
static void Task_BeginQuestLogPlayback(u8);
|
|
static void QuestLogPlaybackSetObjectEventTemplates(u8);
|
|
static void QLPlayback_InitOverworldState(void);
|
|
static void SetPokemonCounts(void);
|
|
static u16 QuestLog_GetPartyCount(void);
|
|
static u16 QuestLog_GetBoxMonCount(void);
|
|
static void RestoreTrainerRematches(void);
|
|
static void ReadQuestLogScriptFromSav1(u8, struct QuestLogAction *);
|
|
static void DoSceneEndTransition(s8 delay);
|
|
static void DoSkipToEndTransition(s8 delay);
|
|
static void QuestLog_AdvancePlayhead(void);
|
|
static void QuestLog_StartFinalScene(void);
|
|
static void Task_AvoidDisplay(u8);
|
|
static void QuestLog_PlayCurrentEvent(void);
|
|
static void HandleShowQuestLogMessage(void);
|
|
static u8 GetQuestLogTextDisplayDuration(void);
|
|
static void DrawSceneDescription(void);
|
|
static void CopyDescriptionWindowTiles(u8);
|
|
static void QuestLog_CloseTextWindow(void);
|
|
static void QuestLog_WaitFadeAndCancelPlayback(void);
|
|
static bool8 FieldCB2_FinalScene(void);
|
|
static void Task_FinalScene_WaitFade(u8);
|
|
static void Task_QuestLogScene_SavedGame(u8);
|
|
static void Task_WaitAtEndOfQuestLog(u8);
|
|
static void Task_EndQuestLog(u8);
|
|
static bool8 sub_81121D8(u8);
|
|
static void QL_SlightlyDarkenSomePals(void);
|
|
static void TogglePlaybackStateForOverworldLock(u8);
|
|
static void SetUpQuestLogAction(u8, struct QuestLogAction *, u16);
|
|
static bool8 RecordHeadAtEndOfEntryOrScriptContext2Enabled(void);
|
|
static bool8 RecordHeadAtEndOfEntry(void);
|
|
static bool8 InQuestLogDisabledLocation(void);
|
|
static bool8 TrySetLinkQuestLogEvent(u16, const u16 *);
|
|
static bool8 TrySetTrainerBattleQuestLogEvent(u16, const u16 *);
|
|
|
|
static const struct WindowTemplate sWindowTemplates[WIN_COUNT] = {
|
|
[WIN_TOP_BAR] = {
|
|
.bg = 0,
|
|
.tilemapLeft = 0,
|
|
.tilemapTop = 0,
|
|
.width = 30,
|
|
.height = 2,
|
|
.paletteNum = 15,
|
|
.baseBlock = 0x0e9
|
|
},
|
|
[WIN_BOTTOM_BAR] = {
|
|
.bg = 0,
|
|
.tilemapLeft = 0,
|
|
.tilemapTop = 18,
|
|
.width = 30,
|
|
.height = 2,
|
|
.paletteNum = 15,
|
|
.baseBlock = 0x0ad
|
|
},
|
|
[WIN_DESCRIPTION] = {
|
|
.bg = 0,
|
|
.tilemapLeft = 0,
|
|
.tilemapTop = 14,
|
|
.width = DESC_WIN_WIDTH,
|
|
.height = DESC_WIN_HEIGHT,
|
|
.paletteNum = 15,
|
|
.baseBlock = 0x14c
|
|
}
|
|
};
|
|
|
|
static const u8 sTextColors[3] = {TEXT_DYNAMIC_COLOR_6, TEXT_COLOR_WHITE, TEXT_DYNAMIC_COLOR_3};
|
|
|
|
static const u16 sDescriptionWindow_Gfx[] = INCBIN_U16("graphics/quest_log/description_window.4bpp");
|
|
|
|
static const u8 sQuestLogTextLineYCoords[] = {17, 10, 3};
|
|
|
|
void SetQuestLogRecordAndPlaybackPointers(void *oldPointer)
|
|
{
|
|
ptrdiff_t offset = (void *)gSaveBlock1Ptr - oldPointer;
|
|
if (gUnknown_203AE04)
|
|
gUnknown_203AE04 = (void *)gUnknown_203AE04 + offset;
|
|
if (gQuestLogState != 0)
|
|
{
|
|
if (gQuestLogRecordingPointer)
|
|
gQuestLogRecordingPointer = (void *)gQuestLogRecordingPointer + offset;
|
|
if (gQuestLogState == QL_STATE_PLAYBACK)
|
|
{
|
|
int r3;
|
|
for (r3 = 0; r3 < (int)ARRAY_COUNT(gUnknown_203AE0C); r3++)
|
|
if (gUnknown_203AE0C[r3])
|
|
gUnknown_203AE0C[r3] = (void *)gUnknown_203AE0C[r3] + offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ResetQuestLog(void)
|
|
{
|
|
memset(gSaveBlock1Ptr->questLog, 0, sizeof(gSaveBlock1Ptr->questLog));
|
|
sCurrentSceneNum = 0;
|
|
gQuestLogState = 0;
|
|
sQuestLogCB = NULL;
|
|
gQuestLogRecordingPointer = NULL;
|
|
gUnknown_203AE04 = NULL;
|
|
sub_8113BD8();
|
|
ResetDeferredLinkEvent();
|
|
}
|
|
|
|
static void ClearSavedScene(u8 sceneNum)
|
|
{
|
|
memset(&gSaveBlock1Ptr->questLog[sceneNum], 0, sizeof(gSaveBlock1Ptr->questLog[sceneNum]));
|
|
gUnknown_203AE04 = NULL;
|
|
}
|
|
|
|
void sub_8110920(void)
|
|
{
|
|
gUnknown_203AE04 = NULL;
|
|
}
|
|
|
|
void RunQuestLogCB(void)
|
|
{
|
|
if (sQuestLogCB != NULL)
|
|
sQuestLogCB();
|
|
}
|
|
|
|
bool8 QL_IsRoomToSaveEvent(const void * cursor, size_t size)
|
|
{
|
|
const void *start = gSaveBlock1Ptr->questLog[sCurrentSceneNum].script;
|
|
const void *end = gSaveBlock1Ptr->questLog[sCurrentSceneNum].end;
|
|
end -= size;
|
|
if (cursor < start || cursor > end)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
// Identical to QL_IsRoomToSaveEvent
|
|
bool8 QL_IsRoomToSaveAction(const void * cursor, size_t size)
|
|
{
|
|
const void *start = gSaveBlock1Ptr->questLog[sCurrentSceneNum].script;
|
|
const void *end = gSaveBlock1Ptr->questLog[sCurrentSceneNum].end;
|
|
end -= size;
|
|
if (cursor < start || cursor > end)
|
|
return FALSE;
|
|
return TRUE;
|
|
}
|
|
|
|
static void SetQuestLogState(u8 state)
|
|
{
|
|
gQuestLogState = state;
|
|
if (state == QL_STATE_RECORDING)
|
|
sQuestLogCB = QLogCB_Recording;
|
|
else
|
|
sQuestLogCB = QLogCB_Playback;
|
|
}
|
|
|
|
static void QLogCB_Recording(void)
|
|
{
|
|
if (TryRecordActionSequence(sQuestLogActionRecordBuffer) != 1)
|
|
{
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
RecordSceneEnd();
|
|
gQuestLogState = 0;
|
|
sQuestLogCB = NULL;
|
|
}
|
|
}
|
|
|
|
static void QLogCB_Playback(void)
|
|
{
|
|
if (sPlaybackControl.state == 2)
|
|
sPlaybackControl.state = 0;
|
|
|
|
if (sPlaybackControl.endMode == END_MODE_NONE)
|
|
{
|
|
if (gQuestLogPlaybackState != QL_PLAYBACK_STATE_0
|
|
|| sPlaybackControl.state == 1
|
|
|| (sPlaybackControl.cursor < ARRAY_COUNT(gUnknown_203AE0C)
|
|
&& gUnknown_203AE0C[sPlaybackControl.cursor] != NULL))
|
|
QuestLog_PlayCurrentEvent();
|
|
else
|
|
{
|
|
sPlaybackControl.endMode = END_MODE_SCENE;
|
|
LockPlayerFieldControls();
|
|
DoSceneEndTransition(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GetQuestLogState(void)
|
|
{
|
|
gSpecialVar_Result = gQuestLogState;
|
|
}
|
|
|
|
u8 GetQuestLogStartType(void)
|
|
{
|
|
return gSaveBlock1Ptr->questLog[sCurrentSceneNum].startType;
|
|
}
|
|
|
|
void StartRecordingQuestLogAction(u16 eventId)
|
|
{
|
|
if (sCurrentSceneNum >= QUEST_LOG_SCENE_COUNT)
|
|
sCurrentSceneNum = 0;
|
|
|
|
ClearSavedScene(sCurrentSceneNum);
|
|
ResetUnk203B044();
|
|
gQuestLogRecordingPointer = gSaveBlock1Ptr->questLog[sCurrentSceneNum].script;
|
|
if (IS_LINK_QL_EVENT(eventId) || eventId == QL_EVENT_DEPARTED)
|
|
gSaveBlock1Ptr->questLog[sCurrentSceneNum].startType = QL_START_WARP;
|
|
else
|
|
gSaveBlock1Ptr->questLog[sCurrentSceneNum].startType = QL_START_NORMAL;
|
|
SetPokemonCounts();
|
|
SetPlayerInitialCoordsAtScene(sCurrentSceneNum);
|
|
SetNPCInitialCoordsAtScene(sCurrentSceneNum);
|
|
BackUpTrainerRematches();
|
|
BackUpMapLayout();
|
|
SetGameStateAtScene(sCurrentSceneNum);
|
|
gUnknown_203ADFC = 0;
|
|
SetUpQuestLogAction(2, sQuestLogActionRecordBuffer, sizeof(sQuestLogActionRecordBuffer));
|
|
TryRecordActionSequence(sQuestLogActionRecordBuffer);
|
|
SetQuestLogState(QL_STATE_RECORDING);
|
|
}
|
|
|
|
static void SetPlayerInitialCoordsAtScene(u8 sceneNum)
|
|
{
|
|
struct QuestLogScene * questLog = &gSaveBlock1Ptr->questLog[sceneNum];
|
|
questLog->mapGroup = gSaveBlock1Ptr->location.mapGroup;
|
|
questLog->mapNum = gSaveBlock1Ptr->location.mapNum;
|
|
questLog->warpId = gSaveBlock1Ptr->location.warpId;
|
|
questLog->x = gSaveBlock1Ptr->pos.x;
|
|
questLog->y = gSaveBlock1Ptr->pos.y;
|
|
}
|
|
|
|
static void SetNPCInitialCoordsAtScene(u8 sceneNum)
|
|
{
|
|
struct QuestLogScene * questLog = &gSaveBlock1Ptr->questLog[sceneNum];
|
|
u16 i;
|
|
|
|
QL_RecordObjects(questLog);
|
|
|
|
for (i = 0; i < OBJECT_EVENT_TEMPLATES_COUNT; i++)
|
|
{
|
|
if (gSaveBlock1Ptr->objectEventTemplates[i].x < 0)
|
|
{
|
|
questLog->objectEventTemplates[i].x = -1 * gSaveBlock1Ptr->objectEventTemplates[i].x;
|
|
questLog->objectEventTemplates[i].negx = TRUE;
|
|
}
|
|
else
|
|
{
|
|
questLog->objectEventTemplates[i].x = (u8)gSaveBlock1Ptr->objectEventTemplates[i].x;
|
|
questLog->objectEventTemplates[i].negx = FALSE;
|
|
}
|
|
if (gSaveBlock1Ptr->objectEventTemplates[i].y < 0)
|
|
{
|
|
questLog->objectEventTemplates[i].y = (-gSaveBlock1Ptr->objectEventTemplates[i].y << 24) >> 24;
|
|
questLog->objectEventTemplates[i].negy = TRUE;
|
|
}
|
|
else
|
|
{
|
|
questLog->objectEventTemplates[i].y = (u8)gSaveBlock1Ptr->objectEventTemplates[i].y;
|
|
questLog->objectEventTemplates[i].negy = FALSE;
|
|
}
|
|
questLog->objectEventTemplates[i].elevation = gSaveBlock1Ptr->objectEventTemplates[i].objUnion.normal.elevation;
|
|
questLog->objectEventTemplates[i].movementType = gSaveBlock1Ptr->objectEventTemplates[i].objUnion.normal.movementType;
|
|
}
|
|
}
|
|
|
|
static void SetGameStateAtScene(u8 sceneNum)
|
|
{
|
|
struct QuestLogScene * questLog = &gSaveBlock1Ptr->questLog[sceneNum];
|
|
|
|
CpuCopy16(gSaveBlock1Ptr->flags, questLog->flags, sizeof(gSaveBlock1Ptr->flags));
|
|
CpuCopy16(gSaveBlock1Ptr->vars, questLog->vars, sizeof(gSaveBlock1Ptr->vars));
|
|
}
|
|
|
|
static void BackUpTrainerRematches(void)
|
|
{
|
|
u16 i, j;
|
|
u16 vars[4];
|
|
|
|
// Save the contents of gSaveBlock1Ptr->trainerRematches to the 4 saveblock
|
|
// vars starting at VAR_QLBAK_TRAINER_REMATCHES. The rematch array is 100 bytes
|
|
// long, but each byte is only ever 0 or 1 to indicate that a rematch is available.
|
|
// They're compressed into single bits across 4 u16 save vars, which is only enough
|
|
// to save 64 elements of gSaveBlock1Ptr->trainerRematches. 64 however is the maximum
|
|
// that could ever be used, as its the maximum number of NPCs per map (OBJECT_EVENT_TEMPLATES_COUNT).
|
|
for (i = 0; i < ARRAY_COUNT(vars); i++)
|
|
{
|
|
vars[i] = 0;
|
|
|
|
// 16 bits per var
|
|
for (j = 0; j < 16; j++)
|
|
{
|
|
if (gSaveBlock1Ptr->trainerRematches[16 * i + j])
|
|
vars[i] += (1 << j);
|
|
}
|
|
VarSet(VAR_QLBAK_TRAINER_REMATCHES + i, vars[i]);
|
|
}
|
|
}
|
|
|
|
static void BackUpMapLayout(void)
|
|
{
|
|
VarSet(VAR_QLBAK_MAP_LAYOUT, gSaveBlock1Ptr->mapLayoutId);
|
|
}
|
|
|
|
static void RecordSceneEnd(void)
|
|
{
|
|
QL_RecordAction_SceneEnd(gQuestLogRecordingPointer);
|
|
if (++sCurrentSceneNum >= QUEST_LOG_SCENE_COUNT)
|
|
sCurrentSceneNum = 0;
|
|
}
|
|
|
|
static bool8 TryRecordActionSequence(struct QuestLogAction * actions)
|
|
{
|
|
u16 i;
|
|
|
|
for (i = gUnknown_203ADFC; i < gQuestLogCurActionIdx; i++)
|
|
{
|
|
if (gQuestLogRecordingPointer == NULL)
|
|
return FALSE;
|
|
switch (actions[i].type)
|
|
{
|
|
case QL_ACTION_MOVEMENT:
|
|
case QL_ACTION_GFX_CHANGE:
|
|
gQuestLogRecordingPointer = QL_RecordAction_MovementOrGfxChange(gQuestLogRecordingPointer, &actions[i]);
|
|
break;
|
|
default:
|
|
gQuestLogRecordingPointer = QL_RecordAction_Input(gQuestLogRecordingPointer, &actions[i]);
|
|
break;
|
|
}
|
|
if (gQuestLogRecordingPointer == NULL)
|
|
{
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (gQuestLogPlaybackState == QL_PLAYBACK_STATE_0)
|
|
{
|
|
gQuestLogRecordingPointer = QL_RecordAction_SceneEnd(gQuestLogRecordingPointer);
|
|
return FALSE;
|
|
}
|
|
gUnknown_203ADFC = gQuestLogCurActionIdx;
|
|
return TRUE;
|
|
}
|
|
|
|
void TryStartQuestLogPlayback(u8 taskId)
|
|
{
|
|
u8 i;
|
|
|
|
QL_EnableRecordingSteps();
|
|
sNumScenes = 0;
|
|
for (i = 0; i < QUEST_LOG_SCENE_COUNT; i++)
|
|
{
|
|
if (gSaveBlock1Ptr->questLog[i].startType != 0)
|
|
sNumScenes++;
|
|
}
|
|
|
|
if (sNumScenes != 0)
|
|
{
|
|
gHelpSystemEnabled = FALSE;
|
|
Task_BeginQuestLogPlayback(taskId);
|
|
DestroyTask(taskId);
|
|
}
|
|
else
|
|
{
|
|
SetMainCallback2(CB2_ContinueSavedGame);
|
|
DestroyTask(taskId);
|
|
}
|
|
}
|
|
|
|
static void Task_BeginQuestLogPlayback(u8 taskId)
|
|
{
|
|
gSaveBlock1Ptr->location.mapGroup = MAP_GROUP(ROUTE1);
|
|
gSaveBlock1Ptr->location.mapNum = MAP_NUM(ROUTE1);
|
|
gSaveBlock1Ptr->location.warpId = WARP_ID_NONE;
|
|
sCurrentSceneNum = 0;
|
|
gDisableMapMusicChangeOnMapLoad = 1;
|
|
DisableWildEncounters(TRUE);
|
|
QLPlayback_InitOverworldState();
|
|
}
|
|
|
|
void sub_8110FCC(void)
|
|
{
|
|
ReadQuestLogScriptFromSav1(sCurrentSceneNum, sQuestLogActionRecordBuffer);
|
|
ResetUnk203B044();
|
|
SetUpQuestLogAction(1, sQuestLogActionRecordBuffer, sizeof(sQuestLogActionRecordBuffer));
|
|
QuestLogPlaybackSetObjectEventTemplates(sCurrentSceneNum);
|
|
}
|
|
|
|
static bool8 FieldCB2_QuestLogStartPlaybackWithWarpExit(void)
|
|
{
|
|
LoadPalette(GetTextWindowPalette(4), 0xF0, 0x20);
|
|
SetQuestLogState(QL_STATE_PLAYBACK);
|
|
FieldCB_DefaultWarpExit();
|
|
sPlaybackControl = (struct PlaybackControl){};
|
|
sPlaybackControl.state = 2;
|
|
return 1;
|
|
}
|
|
|
|
static bool8 FieldCB2_QuestLogStartPlaybackStandingInPlace(void)
|
|
{
|
|
LoadPalette(GetTextWindowPalette(4), 0xF0, 0x20);
|
|
SetQuestLogState(QL_STATE_PLAYBACK);
|
|
FieldCB_WarpExitFadeFromBlack();
|
|
sPlaybackControl = (struct PlaybackControl){};
|
|
sPlaybackControl.state = 2;
|
|
return 1;
|
|
}
|
|
|
|
void DrawPreviouslyOnQuestHeader(u8 sceneNum)
|
|
{
|
|
u8 i;
|
|
|
|
for (i = 0; i < WIN_COUNT; i++)
|
|
{
|
|
sWindowIds[i] = AddWindow(&sWindowTemplates[i]);
|
|
FillWindowPixelRect(sWindowIds[i], 15, 0, 0, sWindowTemplates[i].width * 8, sWindowTemplates[i].height * 8);
|
|
}
|
|
|
|
StringExpandPlaceholders(gStringVar4, gText_QuestLog_PreviouslyOnYourQuest);
|
|
|
|
// Scene numbers count from 4 to 0, 0 being where the player saved
|
|
if (sceneNum != 0)
|
|
{
|
|
ConvertIntToDecimalStringN(gStringVar1, sceneNum, STR_CONV_MODE_LEFT_ALIGN, 1);
|
|
StringAppend(gStringVar4, gStringVar1);
|
|
}
|
|
|
|
AddTextPrinterParameterized4(sWindowIds[WIN_TOP_BAR], FONT_NORMAL, 2, 2, 1, 2, sTextColors, 0, gStringVar4);
|
|
PutWindowTilemap(sWindowIds[WIN_TOP_BAR]);
|
|
PutWindowTilemap(sWindowIds[WIN_BOTTOM_BAR]);
|
|
CopyWindowToVram(sWindowIds[WIN_TOP_BAR], COPYWIN_GFX);
|
|
CopyWindowToVram(sWindowIds[WIN_DESCRIPTION], COPYWIN_GFX);
|
|
CopyWindowToVram(sWindowIds[WIN_BOTTOM_BAR], COPYWIN_FULL);
|
|
}
|
|
|
|
void CommitQuestLogWindow1(void)
|
|
{
|
|
PutWindowTilemap(sWindowIds[WIN_BOTTOM_BAR]);
|
|
CopyWindowToVram(sWindowIds[WIN_BOTTOM_BAR], COPYWIN_MAP);
|
|
}
|
|
|
|
static void QuestLogPlaybackSetObjectEventTemplates(u8 sceneNum)
|
|
{
|
|
struct QuestLogScene *questLog = &gSaveBlock1Ptr->questLog[sceneNum];
|
|
u16 i;
|
|
|
|
for (i = 0; i < OBJECT_EVENT_TEMPLATES_COUNT; i++)
|
|
{
|
|
if (questLog->objectEventTemplates[i].negx)
|
|
gSaveBlock1Ptr->objectEventTemplates[i].x = -questLog->objectEventTemplates[i].x;
|
|
else
|
|
gSaveBlock1Ptr->objectEventTemplates[i].x = questLog->objectEventTemplates[i].x;
|
|
if (questLog->objectEventTemplates[i].negy)
|
|
gSaveBlock1Ptr->objectEventTemplates[i].y = -(u8)questLog->objectEventTemplates[i].y;
|
|
else
|
|
gSaveBlock1Ptr->objectEventTemplates[i].y = questLog->objectEventTemplates[i].y;
|
|
gSaveBlock1Ptr->objectEventTemplates[i].objUnion.normal.elevation = questLog->objectEventTemplates[i].elevation;
|
|
gSaveBlock1Ptr->objectEventTemplates[i].objUnion.normal.movementType = questLog->objectEventTemplates[i].movementType;
|
|
}
|
|
|
|
QL_LoadObjects(questLog, gSaveBlock1Ptr->objectEventTemplates);
|
|
}
|
|
|
|
static void QLPlayback_SetInitialPlayerPosition(u8 sceneNum, bool8 isWarp)
|
|
{
|
|
struct WarpData sp0;
|
|
|
|
if (!isWarp)
|
|
{
|
|
gSaveBlock1Ptr->location.mapGroup = gSaveBlock1Ptr->questLog[sceneNum].mapGroup;
|
|
gSaveBlock1Ptr->location.mapNum = gSaveBlock1Ptr->questLog[sceneNum].mapNum;
|
|
gSaveBlock1Ptr->location.warpId = gSaveBlock1Ptr->questLog[sceneNum].warpId;
|
|
gSaveBlock1Ptr->pos.x = gSaveBlock1Ptr->questLog[sceneNum].x;
|
|
gSaveBlock1Ptr->pos.y = gSaveBlock1Ptr->questLog[sceneNum].y;
|
|
}
|
|
else
|
|
{
|
|
sp0.mapGroup = gSaveBlock1Ptr->questLog[sceneNum].mapGroup;
|
|
sp0.mapNum = gSaveBlock1Ptr->questLog[sceneNum].mapNum;
|
|
sp0.warpId = gSaveBlock1Ptr->questLog[sceneNum].warpId;
|
|
sp0.x = gSaveBlock1Ptr->questLog[sceneNum].x;
|
|
sp0.y = gSaveBlock1Ptr->questLog[sceneNum].y;
|
|
Overworld_SetWarpDestinationFromWarp(&sp0);
|
|
}
|
|
}
|
|
|
|
static void QLPlayback_InitOverworldState(void)
|
|
{
|
|
gQuestLogState = QL_STATE_PLAYBACK;
|
|
ResetSpecialVars();
|
|
ClearBag();
|
|
ClearPCItemSlots();
|
|
if (GetQuestLogStartType() == QL_START_NORMAL)
|
|
{
|
|
QLPlayback_SetInitialPlayerPosition(sCurrentSceneNum, FALSE);
|
|
gFieldCallback2 = FieldCB2_QuestLogStartPlaybackStandingInPlace;
|
|
SetMainCallback2(CB2_SetUpOverworldForQLPlayback);
|
|
}
|
|
else
|
|
{
|
|
QLPlayback_SetInitialPlayerPosition(sCurrentSceneNum, TRUE);
|
|
WarpIntoMap();
|
|
gFieldCallback2 = FieldCB2_QuestLogStartPlaybackWithWarpExit;
|
|
SetMainCallback2(CB2_SetUpOverworldForQLPlaybackWithWarpExit);
|
|
}
|
|
}
|
|
|
|
void sub_81113E4(void)
|
|
{
|
|
struct QuestLogScene * questLog = &gSaveBlock1Ptr->questLog[sCurrentSceneNum];
|
|
|
|
CpuCopy16(questLog->flags, gSaveBlock1Ptr->flags, sizeof(gSaveBlock1Ptr->flags));
|
|
CpuCopy16(questLog->vars, gSaveBlock1Ptr->vars, sizeof(gSaveBlock1Ptr->vars));
|
|
RestoreTrainerRematches();
|
|
}
|
|
|
|
struct PokemonAndSomethingElse
|
|
{
|
|
struct Pokemon mon;
|
|
u16 partyCount;
|
|
u16 boxMonCount;
|
|
};
|
|
|
|
void sub_8111438(void)
|
|
{
|
|
struct PokemonAndSomethingElse *prev = AllocZeroed(sizeof(struct PokemonAndSomethingElse));
|
|
u16 packedCounts, i, count, j;
|
|
|
|
CreateMon(&prev->mon, SPECIES_RATTATA, 1, USE_RANDOM_IVS, FALSE, 0, OT_ID_PLAYER_ID, 0);
|
|
packedCounts = VarGet(VAR_QUEST_LOG_MON_COUNTS);
|
|
prev->partyCount = packedCounts >> 12;
|
|
prev->boxMonCount = packedCounts % 0x1000;
|
|
|
|
count = QuestLog_GetPartyCount();
|
|
if (count > prev->partyCount)
|
|
{
|
|
for (i = 0; i < count - prev->partyCount; i++)
|
|
ZeroMonData(&gPlayerParty[PARTY_SIZE - 1 - i]);
|
|
}
|
|
else if (count < prev->partyCount)
|
|
{
|
|
for (i = 0; i < PARTY_SIZE - 1; i++)
|
|
ZeroBoxMonAt(0, i);
|
|
|
|
for (i = count; i < prev->partyCount; i++)
|
|
CopyMon(&gPlayerParty[i], &prev->mon, sizeof(struct Pokemon));
|
|
}
|
|
|
|
count = QuestLog_GetBoxMonCount();
|
|
if (count > prev->boxMonCount)
|
|
{
|
|
for (i = 0; i < TOTAL_BOXES_COUNT; i++)
|
|
{
|
|
for (j = 0; j < IN_BOX_COUNT; j++)
|
|
{
|
|
if (GetBoxMonDataAt(i, j, MON_DATA_SANITY_HAS_SPECIES))
|
|
{
|
|
ZeroBoxMonAt(i, j);
|
|
if (--count == prev->boxMonCount)
|
|
break;
|
|
}
|
|
}
|
|
if (count == prev->boxMonCount)
|
|
break;
|
|
}
|
|
}
|
|
else if (count < prev->boxMonCount)
|
|
{
|
|
for (i = 0; i < TOTAL_BOXES_COUNT; i++)
|
|
{
|
|
for (j = 0; j < IN_BOX_COUNT; j++)
|
|
{
|
|
struct BoxPokemon * boxMon = GetBoxedMonPtr(i, j);
|
|
if (!GetBoxMonData(boxMon, MON_DATA_SANITY_HAS_SPECIES))
|
|
{
|
|
CopyMon(boxMon, &prev->mon.box, sizeof(struct BoxPokemon));
|
|
if (++count == prev->boxMonCount)
|
|
break;
|
|
}
|
|
}
|
|
if (count == prev->boxMonCount)
|
|
break;
|
|
}
|
|
}
|
|
Free(prev);
|
|
}
|
|
|
|
static void SetPokemonCounts(void)
|
|
{
|
|
u16 partyCount = QuestLog_GetPartyCount();
|
|
u16 boxesCount = QuestLog_GetBoxMonCount();
|
|
VarSet(VAR_QUEST_LOG_MON_COUNTS, (partyCount << 12) + boxesCount);
|
|
}
|
|
|
|
static u16 QuestLog_GetPartyCount(void)
|
|
{
|
|
u16 count = 0;
|
|
u16 i;
|
|
|
|
for (i = 0; i < PARTY_SIZE; i++)
|
|
{
|
|
if (GetMonData(&gPlayerParty[i], MON_DATA_SANITY_HAS_SPECIES))
|
|
count++;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
static u16 QuestLog_GetBoxMonCount(void)
|
|
{
|
|
u16 count = 0;
|
|
u16 i, j;
|
|
|
|
for (i = 0; i < TOTAL_BOXES_COUNT; i++)
|
|
{
|
|
for (j = 0; j < IN_BOX_COUNT; j++)
|
|
{
|
|
if (GetBoxMonDataAt(i, j, MON_DATA_SANITY_HAS_SPECIES))
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
// Inverse of BackUpTrainerRematches
|
|
static void RestoreTrainerRematches(void)
|
|
{
|
|
u16 i, j;
|
|
u16 vars[4];
|
|
|
|
for (i = 0; i < ARRAY_COUNT(vars); i++)
|
|
{
|
|
vars[i] = VarGet(VAR_QLBAK_TRAINER_REMATCHES + i);
|
|
|
|
// 16 bits per var
|
|
for (j = 0; j < 16; j++)
|
|
{
|
|
if (vars[i] & 1)
|
|
gSaveBlock1Ptr->trainerRematches[16 * i + j] = 30;
|
|
else
|
|
gSaveBlock1Ptr->trainerRematches[16 * i + j] = 0;
|
|
vars[i] >>= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Inverse of BackUpMapLayout
|
|
void QL_RestoreMapLayoutId(void)
|
|
{
|
|
gSaveBlock1Ptr->mapLayoutId = VarGet(VAR_QLBAK_MAP_LAYOUT);
|
|
if (gSaveBlock1Ptr->mapLayoutId == 0)
|
|
{
|
|
struct MapHeader header = *Overworld_GetMapHeaderByGroupAndId(gSaveBlock1Ptr->location.mapGroup, gSaveBlock1Ptr->location.mapNum);
|
|
gSaveBlock1Ptr->mapLayoutId = header.mapLayoutId;
|
|
}
|
|
}
|
|
|
|
static void ReadQuestLogScriptFromSav1(u8 sceneNum, struct QuestLogAction * a1)
|
|
{
|
|
u16 i;
|
|
u16 *script;
|
|
u16 r6 = 0;
|
|
u16 r9 = 0;
|
|
|
|
memset(a1, 0, 32 * sizeof(struct QuestLogAction));
|
|
for (i = 0; i < ARRAY_COUNT(gUnknown_203AE0C); i++)
|
|
{
|
|
gUnknown_203AE0C[i] = NULL;
|
|
}
|
|
|
|
script = gSaveBlock1Ptr->questLog[sceneNum].script;
|
|
for (i = 0; i < 32; i++)
|
|
{
|
|
switch (script[0] & QL_CMD_EVENT_MASK)
|
|
{
|
|
case QL_EVENT_INPUT:
|
|
script = QL_LoadAction_Input(script, &a1[r6]);
|
|
r6++;
|
|
break;
|
|
case QL_EVENT_GFX_CHANGE:
|
|
case QL_EVENT_MOVEMENT:
|
|
script = QL_LoadAction_MovementOrGfxChange(script, &a1[r6]);
|
|
r6++;
|
|
break;
|
|
case QL_EVENT_SCENE_END:
|
|
script = QL_LoadAction_SceneEnd(script, &a1[r6]);
|
|
r6++;
|
|
break;
|
|
case QL_EVENT_WAIT:
|
|
script = QL_LoadAction_Wait(script, &a1[r6]);
|
|
r6++;
|
|
break;
|
|
default: // Normal event
|
|
script = QuestLog_SkipCommand(script, &gUnknown_203AE0C[r9]);
|
|
if (r9 == 0)
|
|
sub_8113ABC(gUnknown_203AE0C[0]);
|
|
r9++;
|
|
break;
|
|
}
|
|
if (script == NULL)
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void DoSceneEndTransition(s8 delay)
|
|
{
|
|
FadeScreen(FADE_TO_BLACK, delay);
|
|
sQuestLogCB = QuestLog_AdvancePlayhead;
|
|
}
|
|
|
|
static void QuestLog_AdvancePlayhead(void)
|
|
{
|
|
if (gPaletteFade.active)
|
|
return;
|
|
|
|
LockPlayerFieldControls();
|
|
if (++sCurrentSceneNum < QUEST_LOG_SCENE_COUNT && gSaveBlock1Ptr->questLog[sCurrentSceneNum].startType != 0)
|
|
{
|
|
sNumScenes--;
|
|
QLPlayback_InitOverworldState();
|
|
}
|
|
else
|
|
{
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
QuestLog_StartFinalScene();
|
|
}
|
|
}
|
|
|
|
static void QuestLog_StartFinalScene(void)
|
|
{
|
|
ResetSpecialVars();
|
|
Save_ResetSaveCounters();
|
|
LoadGameSave(SAVE_NORMAL);
|
|
SetMainCallback2(CB2_EnterFieldFromQuestLog);
|
|
gFieldCallback2 = FieldCB2_FinalScene;
|
|
FreeAllWindowBuffers();
|
|
gQuestLogState = QL_STATE_PLAYBACK_LAST;
|
|
sQuestLogCB = NULL;
|
|
}
|
|
|
|
void QuestLog_AdvancePlayhead_(void)
|
|
{
|
|
QuestLog_AdvancePlayhead();
|
|
}
|
|
|
|
#define tTimer data[0]
|
|
#define tState data[1]
|
|
#define DATA_IDX_CALLBACK 14 // data[14] and data[15]
|
|
|
|
// This is used to avoid recording or displaying certain windows or images, like a shop menu.
|
|
// During playback it returns TRUE (meaning the action should be avoided) and calls the
|
|
// provided callback, which would be used to e.g. destroy any resources that were set up to do
|
|
// whatever is being avoided. In all cases the provided callback will be QL_DestroyAbortedDisplay.
|
|
// If we are not currently in playback return FALSE (meaning allow the action to occur) and
|
|
// stop recording (if we are currently).
|
|
bool8 QL_AvoidDisplay(void (*callback)(void))
|
|
{
|
|
u8 taskId;
|
|
|
|
switch (gQuestLogState)
|
|
{
|
|
case QL_STATE_RECORDING:
|
|
QuestLog_CutRecording();
|
|
break;
|
|
case QL_STATE_PLAYBACK:
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_3;
|
|
taskId = CreateTask(Task_AvoidDisplay, 80);
|
|
gTasks[taskId].tTimer = 0;
|
|
gTasks[taskId].tState = 0;
|
|
SetWordTaskArg(taskId, DATA_IDX_CALLBACK, (uintptr_t)callback);
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
static void Task_AvoidDisplay(u8 taskId)
|
|
{
|
|
void (*routine)(void);
|
|
s16 *data = gTasks[taskId].data;
|
|
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
// Instead of displaying anything, wait and then end the scene.
|
|
if (++tTimer == 127)
|
|
{
|
|
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 16, 0);
|
|
sPlaybackControl.endMode = END_MODE_SCENE;
|
|
tState++;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (!gPaletteFade.active)
|
|
{
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
|
|
// Call the provided function (if any). In practice this is always QL_DestroyAbortedDisplay
|
|
routine = (void (*)(void)) GetWordTaskArg(taskId, DATA_IDX_CALLBACK);
|
|
if (routine != NULL)
|
|
routine();
|
|
|
|
DestroyTask(taskId);
|
|
sQuestLogCB = QuestLog_AdvancePlayhead;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef tTimer
|
|
#undef tState
|
|
|
|
static void QuestLog_PlayCurrentEvent(void)
|
|
{
|
|
if (sPlaybackControl.state == 1)
|
|
{
|
|
if (--sPlaybackControl.timer != 0)
|
|
return;
|
|
sPlaybackControl.state = 0;
|
|
sPlaybackControl.playingEvent = 1;
|
|
TogglePlaybackStateForOverworldLock(2);
|
|
}
|
|
|
|
if (sPlaybackControl.playingEvent == 1)
|
|
{
|
|
if (++sPlaybackControl.overlapTimer > 15)
|
|
{
|
|
QuestLog_CloseTextWindow();
|
|
sPlaybackControl.playingEvent = 0;
|
|
sPlaybackControl.overlapTimer = 0;
|
|
}
|
|
}
|
|
if (sPlaybackControl.cursor < ARRAY_COUNT(gUnknown_203AE0C))
|
|
{
|
|
if (sub_8113B44(gUnknown_203AE0C[sPlaybackControl.cursor]) == 1)
|
|
HandleShowQuestLogMessage();
|
|
else if (sub_8113AE8(gUnknown_203AE0C[sPlaybackControl.cursor]) == 1)
|
|
HandleShowQuestLogMessage();
|
|
}
|
|
}
|
|
|
|
static void HandleShowQuestLogMessage(void)
|
|
{
|
|
if (sPlaybackControl.state == 0)
|
|
{
|
|
sPlaybackControl.state = 1;
|
|
sPlaybackControl.playingEvent = 0;
|
|
sPlaybackControl.overlapTimer = 0;
|
|
sPlaybackControl.timer = GetQuestLogTextDisplayDuration();
|
|
if (gUnknown_203B044.unk_2 == 0)
|
|
sPlaybackControl.cursor++;
|
|
if (sPlaybackControl.cursor > 32)
|
|
return;
|
|
DrawSceneDescription();
|
|
}
|
|
TogglePlaybackStateForOverworldLock(1); // lock
|
|
}
|
|
|
|
static u8 GetQuestLogTextDisplayDuration(void)
|
|
{
|
|
u16 i;
|
|
u16 count = 0;
|
|
|
|
for (i = 0; i < 0x400 && gStringVar4[i] != EOS; i++)
|
|
{
|
|
if (gStringVar4[i] != CHAR_NEWLINE)
|
|
count++;
|
|
}
|
|
|
|
if (count < 20)
|
|
return 0x5F;
|
|
if (count < 36)
|
|
return 0x7F;
|
|
if (count < 46)
|
|
return 0xBF;
|
|
return 0xFF;
|
|
}
|
|
|
|
bool8 sub_8111C2C(void)
|
|
{
|
|
if (gQuestLogState != QL_STATE_PLAYBACK)
|
|
return FALSE;
|
|
if (gQuestLogPlaybackState == QL_PLAYBACK_STATE_0 || sPlaybackControl.state == 1 || sPlaybackControl.state == 2)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
void HandleQuestLogInput(void)
|
|
{
|
|
// Ignore input if we're currently ending a scene/playback
|
|
if (sPlaybackControl.endMode != END_MODE_NONE)
|
|
return;
|
|
|
|
if (JOY_NEW(A_BUTTON))
|
|
{
|
|
// Pressed A, skip to next scene
|
|
sPlaybackControl.endMode = END_MODE_SCENE;
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
DoSceneEndTransition(-3);
|
|
}
|
|
else if (JOY_NEW(B_BUTTON))
|
|
{
|
|
// Pressed B, end playback
|
|
sPlaybackControl.endMode = END_MODE_FINISH;
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
DoSkipToEndTransition(-3);
|
|
}
|
|
}
|
|
|
|
bool8 QuestLogScenePlaybackIsEnding(void)
|
|
{
|
|
if (sPlaybackControl.endMode != END_MODE_NONE)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
void QuestLog_DrawPreviouslyOnQuestHeaderIfInPlaybackMode(void)
|
|
{
|
|
if (gQuestLogState == QL_STATE_PLAYBACK)
|
|
DrawPreviouslyOnQuestHeader(sNumScenes);
|
|
}
|
|
|
|
static void DrawSceneDescription(void)
|
|
{
|
|
u16 i;
|
|
u8 numLines = 0;
|
|
|
|
for (i = 0; i < 0x100 && gStringVar4[i] != EOS; i++)
|
|
{
|
|
if (gStringVar4[i] == CHAR_NEWLINE)
|
|
numLines++;
|
|
}
|
|
|
|
PutWindowTilemap(sWindowIds[WIN_DESCRIPTION]);
|
|
CopyDescriptionWindowTiles(sWindowIds[WIN_DESCRIPTION]);
|
|
AddTextPrinterParameterized4(sWindowIds[WIN_DESCRIPTION], FONT_NORMAL, 2, sQuestLogTextLineYCoords[numLines], 1, 0, sTextColors, 0, gStringVar4);
|
|
ScheduleBgCopyTilemapToVram(0);
|
|
}
|
|
|
|
static void CopyDescriptionWindowTiles(u8 windowId)
|
|
{
|
|
const u16 *src = sDescriptionWindow_Gfx;
|
|
u16 *buffer = Alloc(DESC_WIN_WIDTH * DESC_WIN_HEIGHT * 32);
|
|
u8 i, j, y;
|
|
|
|
if (buffer)
|
|
{
|
|
for (i = 0; i < DESC_WIN_HEIGHT; i++)
|
|
{
|
|
switch (i)
|
|
{
|
|
default:
|
|
// Middle tile
|
|
y = 1;
|
|
break;
|
|
case 0:
|
|
// Top edge tile
|
|
y = 0;
|
|
break;
|
|
case DESC_WIN_HEIGHT - 1:
|
|
// Bottom edge tile
|
|
y = 2;
|
|
break;
|
|
}
|
|
|
|
for (j = 0; j < DESC_WIN_WIDTH; j++)
|
|
CpuCopy32(src + 16 * y, buffer + 16 * (2 * (15 * i) + j), 32);
|
|
}
|
|
|
|
CopyToWindowPixelBuffer(windowId, (const u8 *)buffer, DESC_WIN_WIDTH * DESC_WIN_HEIGHT * 32, 0);
|
|
Free(buffer);
|
|
}
|
|
}
|
|
|
|
static void QuestLog_CloseTextWindow(void)
|
|
{
|
|
ClearWindowTilemap(sWindowIds[WIN_DESCRIPTION]);
|
|
FillWindowPixelRect(sWindowIds[WIN_DESCRIPTION], 15, 0, 0, 0xf0, 0x30);
|
|
CopyWindowToVram(sWindowIds[WIN_DESCRIPTION], COPYWIN_GFX);
|
|
PutWindowTilemap(sWindowIds[WIN_BOTTOM_BAR]);
|
|
CopyWindowToVram(sWindowIds[WIN_BOTTOM_BAR], COPYWIN_MAP);
|
|
}
|
|
|
|
static void DoSkipToEndTransition(s8 delay)
|
|
{
|
|
FadeScreen(FADE_TO_BLACK, delay);
|
|
sQuestLogCB = QuestLog_WaitFadeAndCancelPlayback;
|
|
}
|
|
|
|
static void QuestLog_WaitFadeAndCancelPlayback(void)
|
|
{
|
|
if (!gPaletteFade.active)
|
|
{
|
|
LockPlayerFieldControls();
|
|
for (sCurrentSceneNum = sCurrentSceneNum; sCurrentSceneNum < QUEST_LOG_SCENE_COUNT; sCurrentSceneNum++)
|
|
{
|
|
if (gSaveBlock1Ptr->questLog[sCurrentSceneNum].startType == 0)
|
|
break;
|
|
ReadQuestLogScriptFromSav1(sCurrentSceneNum, sQuestLogActionRecordBuffer);
|
|
}
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
QuestLog_StartFinalScene();
|
|
}
|
|
}
|
|
|
|
void QuestLog_InitPalettesBackup(void)
|
|
{
|
|
if (gQuestLogState == QL_STATE_PLAYBACK_LAST)
|
|
sPalettesBackup = AllocZeroed(PLTT_SIZE);
|
|
}
|
|
|
|
void QuestLog_BackUpPalette(u16 offset, u16 size)
|
|
{
|
|
CpuCopy16(gPlttBufferUnfaded + offset, sPalettesBackup + offset, size * 2);
|
|
}
|
|
|
|
static bool8 FieldCB2_FinalScene(void)
|
|
{
|
|
LoadPalette(GetTextWindowPalette(4), 0xF0, 0x20);
|
|
DrawPreviouslyOnQuestHeader(0);
|
|
FieldCB_WarpExitFadeFromBlack();
|
|
CreateTask(Task_FinalScene_WaitFade, 0xFF);
|
|
return TRUE;
|
|
}
|
|
|
|
static void Task_FinalScene_WaitFade(u8 taskId)
|
|
{
|
|
struct Task *task = &gTasks[taskId];
|
|
|
|
if (ArePlayerFieldControlsLocked() != TRUE)
|
|
{
|
|
FreezeObjectEvents();
|
|
HandleEnforcedLookDirectionOnPlayerStopMoving();
|
|
StopPlayerAvatar();
|
|
LockPlayerFieldControls();
|
|
task->func = Task_QuestLogScene_SavedGame;
|
|
}
|
|
}
|
|
|
|
static void Task_QuestLogScene_SavedGame(u8 taskId)
|
|
{
|
|
struct Task *task = &gTasks[taskId];
|
|
|
|
if (!gPaletteFade.active)
|
|
{
|
|
if (sPlaybackControl.endMode != END_MODE_FINISH)
|
|
{
|
|
GetMapNameGeneric(gStringVar1, gMapHeader.regionMapSectionId);
|
|
StringExpandPlaceholders(gStringVar4, gText_QuestLog_SavedGameAtLocation);
|
|
DrawSceneDescription();
|
|
}
|
|
task->data[0] = 0;
|
|
task->data[1] = 0;
|
|
task->func = Task_WaitAtEndOfQuestLog;
|
|
FreezeObjectEvents();
|
|
LockPlayerFieldControls();
|
|
}
|
|
}
|
|
|
|
#define tTimer data[0]
|
|
|
|
static void Task_WaitAtEndOfQuestLog(u8 taskId)
|
|
{
|
|
struct Task *task = &gTasks[taskId];
|
|
|
|
if (JOY_NEW(A_BUTTON | B_BUTTON) || task->tTimer >= 127 || sPlaybackControl.endMode == END_MODE_FINISH)
|
|
{
|
|
QuestLog_CloseTextWindow();
|
|
task->tTimer = 0;
|
|
task->func = Task_EndQuestLog;
|
|
gQuestLogState = 0;
|
|
}
|
|
else
|
|
task->tTimer++;
|
|
}
|
|
|
|
#undef tTimer
|
|
|
|
#define tState data[0]
|
|
#define tTimer data[1]
|
|
|
|
static void Task_EndQuestLog(u8 taskId)
|
|
{
|
|
s16 *data = gTasks[taskId].data;
|
|
u8 i;
|
|
|
|
switch (tState)
|
|
{
|
|
case 0:
|
|
gDisableMapMusicChangeOnMapLoad = 0;
|
|
Overworld_PlaySpecialMapMusic();
|
|
QL_SlightlyDarkenSomePals();
|
|
FillWindowPixelRect(sWindowIds[WIN_TOP_BAR],
|
|
0xF, 0, 0,
|
|
sWindowTemplates[WIN_TOP_BAR].width * 8,
|
|
sWindowTemplates[WIN_TOP_BAR].height * 8);
|
|
tState++;
|
|
break;
|
|
case 1:
|
|
if (sub_81121D8(taskId))
|
|
{
|
|
for (i = 0; i < WIN_COUNT; i++)
|
|
{
|
|
ClearWindowTilemap(sWindowIds[i]);
|
|
CopyWindowToVram(sWindowIds[i], COPYWIN_MAP);
|
|
RemoveWindow(sWindowIds[i]);
|
|
}
|
|
tTimer = 0;
|
|
tState++;
|
|
}
|
|
break;
|
|
case 2:
|
|
if (tTimer < 32)
|
|
tTimer++;
|
|
else
|
|
tState++;
|
|
break;
|
|
default:
|
|
if (sPlaybackControl.endMode == END_MODE_FINISH)
|
|
ShowMapNamePopup(TRUE);
|
|
CpuCopy16(sPalettesBackup, gPlttBufferUnfaded, PLTT_SIZE);
|
|
Free(sPalettesBackup);
|
|
sPlaybackControl = (struct PlaybackControl){};
|
|
ClearPlayerHeldMovementAndUnfreezeObjectEvents();
|
|
UnlockPlayerFieldControls();
|
|
gTextFlags.autoScroll = FALSE;
|
|
gGlobalFieldTintMode = QL_TINT_NONE;
|
|
DisableWildEncounters(FALSE);
|
|
gHelpSystemEnabled = TRUE;
|
|
DestroyTask(taskId);
|
|
break;
|
|
}
|
|
}
|
|
|
|
#undef tState
|
|
#undef tTimer
|
|
|
|
static bool8 sub_81121D8(u8 taskId)
|
|
{
|
|
s16 *data = gTasks[taskId].data;
|
|
|
|
if (data[1] > 15)
|
|
return TRUE;
|
|
|
|
CopyPaletteInvertedTint(gPlttBufferUnfaded + 0x01, gPlttBufferFaded + 0x01, 0xDF, 0x0F - data[1]);
|
|
CopyPaletteInvertedTint(gPlttBufferUnfaded + 0x100, gPlttBufferFaded + 0x100, 0x100, 0x0F - data[1]);
|
|
FillWindowPixelRect(sWindowIds[WIN_TOP_BAR],
|
|
0x00, 0,
|
|
sWindowTemplates[WIN_TOP_BAR].height * 8 - 1 - data[1],
|
|
sWindowTemplates[WIN_TOP_BAR].width * 8,
|
|
1);
|
|
FillWindowPixelRect(sWindowIds[WIN_BOTTOM_BAR],
|
|
0x00, 0,
|
|
data[1],
|
|
sWindowTemplates[WIN_BOTTOM_BAR].width * 8, 1);
|
|
CopyWindowToVram(sWindowIds[WIN_TOP_BAR], COPYWIN_GFX);
|
|
CopyWindowToVram(sWindowIds[WIN_BOTTOM_BAR], COPYWIN_GFX);
|
|
data[1]++;
|
|
return FALSE;
|
|
}
|
|
|
|
static void QL_SlightlyDarkenSomePals(void)
|
|
{
|
|
u16 *buffer = Alloc(PLTT_SIZE);
|
|
CpuCopy16(sPalettesBackup, buffer, PLTT_SIZE);
|
|
SlightlyDarkenPalsInWeather(sPalettesBackup, sPalettesBackup, 13 * 16);
|
|
SlightlyDarkenPalsInWeather(sPalettesBackup + 17 * 16, sPalettesBackup + 17 * 16, 1 * 16);
|
|
SlightlyDarkenPalsInWeather(sPalettesBackup + 22 * 16, sPalettesBackup + 22 * 16, 4 * 16);
|
|
SlightlyDarkenPalsInWeather(sPalettesBackup + 27 * 16, sPalettesBackup + 27 * 16, 5 * 16);
|
|
CpuCopy16(sPalettesBackup, gPlttBufferUnfaded, PLTT_SIZE);
|
|
CpuCopy16(buffer, sPalettesBackup, PLTT_SIZE);
|
|
Free(buffer);
|
|
}
|
|
|
|
void QL_FinishRecordingScene(void)
|
|
{
|
|
if (gQuestLogState == QL_STATE_RECORDING)
|
|
{
|
|
TryRecordActionSequence(sQuestLogActionRecordBuffer);
|
|
RecordSceneEnd();
|
|
gQuestLogState = 0;
|
|
sQuestLogCB = NULL;
|
|
gUnknown_203AE04 = NULL;
|
|
gQuestLogRecordingPointer = NULL;
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
}
|
|
}
|
|
|
|
void QuestLog_CutRecording(void)
|
|
{
|
|
if (gQuestLogPlaybackState != QL_PLAYBACK_STATE_0 && gQuestLogState == QL_STATE_RECORDING)
|
|
{
|
|
TryRecordActionSequence(sQuestLogActionRecordBuffer);
|
|
QL_RecordWait(1);
|
|
RecordSceneEnd();
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
gQuestLogState = 0;
|
|
sQuestLogCB = NULL;
|
|
}
|
|
gUnknown_203AE04 = NULL;
|
|
gQuestLogRecordingPointer = NULL;
|
|
}
|
|
|
|
static void SortQuestLogInSav1(void)
|
|
{
|
|
struct QuestLogScene * buffer = AllocZeroed(sizeof(gSaveBlock1Ptr->questLog));
|
|
u8 i;
|
|
u8 sceneNum = sCurrentSceneNum;
|
|
u8 count = 0;
|
|
for (i = 0; i < QUEST_LOG_SCENE_COUNT; i++)
|
|
{
|
|
if (sceneNum >= QUEST_LOG_SCENE_COUNT)
|
|
sceneNum = 0;
|
|
if (gSaveBlock1Ptr->questLog[sceneNum].startType != 0)
|
|
{
|
|
buffer[count] = gSaveBlock1Ptr->questLog[sceneNum];
|
|
count++;
|
|
}
|
|
sceneNum++;
|
|
}
|
|
sCurrentSceneNum = count % QUEST_LOG_SCENE_COUNT;
|
|
CpuCopy16(buffer, gSaveBlock1Ptr->questLog, sizeof(gSaveBlock1Ptr->questLog));
|
|
Free(buffer);
|
|
}
|
|
|
|
void SaveQuestLogData(void)
|
|
{
|
|
if (MenuHelpers_IsLinkActive() != TRUE)
|
|
{
|
|
QuestLog_CutRecording();
|
|
SortQuestLogInSav1();
|
|
}
|
|
}
|
|
|
|
void sub_811246C(struct Sprite *sprite)
|
|
{
|
|
struct ObjectEvent *objectEvent = &gObjectEvents[sprite->data[0]];
|
|
if (objectEvent->localId == OBJ_EVENT_ID_PLAYER)
|
|
{
|
|
if (sMovementScripts[0][0] != MOVEMENT_ACTION_NONE)
|
|
{
|
|
ObjectEventSetHeldMovement(objectEvent, sMovementScripts[0][0]);
|
|
sMovementScripts[0][0] = MOVEMENT_ACTION_NONE;
|
|
}
|
|
if (sMovementScripts[0][1] != QL_PLAYER_GFX_NONE)
|
|
{
|
|
QuestLogUpdatePlayerSprite(sMovementScripts[0][1]);
|
|
sMovementScripts[0][1] = QL_PLAYER_GFX_NONE;
|
|
}
|
|
UpdateQuestLogObjectEventCurrentMovement(objectEvent, sprite);
|
|
}
|
|
else
|
|
{
|
|
if (sMovementScripts[objectEvent->localId][0] != MOVEMENT_ACTION_NONE)
|
|
{
|
|
ObjectEventSetHeldMovement(objectEvent, sMovementScripts[objectEvent->localId][0]);
|
|
sMovementScripts[objectEvent->localId][0] = MOVEMENT_ACTION_NONE;
|
|
}
|
|
UpdateQuestLogObjectEventCurrentMovement(objectEvent, sprite);
|
|
}
|
|
}
|
|
|
|
void QuestLogRecordNPCStep(u8 localId, u8 mapNum, u8 mapGroup, u8 movementActionId)
|
|
{
|
|
if (!RecordHeadAtEndOfEntryOrScriptContext2Enabled())
|
|
{
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = sNextActionDelay;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_MOVEMENT;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.localId = localId;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.mapNum = mapNum;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.mapGroup = mapGroup;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId = movementActionId;
|
|
gQuestLogCurActionIdx++;
|
|
sNextActionDelay = 0;
|
|
}
|
|
}
|
|
|
|
void QuestLogRecordNPCStepWithDuration(u8 localId, u8 mapNum, u8 mapGroup, u8 movementActionId, u8 duration)
|
|
{
|
|
if (!RecordHeadAtEndOfEntry())
|
|
{
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = sNextActionDelay;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_MOVEMENT;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.localId = localId;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.mapNum = mapNum;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.mapGroup = mapGroup;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId = movementActionId;
|
|
gQuestLogCurActionIdx++;
|
|
sNextActionDelay = duration;
|
|
}
|
|
}
|
|
|
|
void QuestLogRecordPlayerStep(u8 movementActionId)
|
|
{
|
|
if (!RecordHeadAtEndOfEntryOrScriptContext2Enabled())
|
|
{
|
|
if (movementActionId != sCurSceneActions[sLastQuestLogCursor].data.a.movementActionId || movementActionId > MOVEMENT_ACTION_FACE_RIGHT)
|
|
{
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = sNextActionDelay;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_MOVEMENT;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.localId = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId = movementActionId;
|
|
sLastQuestLogCursor = gQuestLogCurActionIdx;
|
|
gQuestLogCurActionIdx++;
|
|
sNextActionDelay = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QuestLogRecordPlayerStepWithDuration(u8 movementActionId, u8 duration)
|
|
{
|
|
if (!RecordHeadAtEndOfEntry())
|
|
{
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = sNextActionDelay;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_MOVEMENT;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.localId = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId = movementActionId;
|
|
sLastQuestLogCursor = gQuestLogCurActionIdx;
|
|
gQuestLogCurActionIdx++;
|
|
sNextActionDelay = duration;
|
|
}
|
|
}
|
|
|
|
void QuestLogRecordPlayerAvatarGfxTransition(u8 gfxState)
|
|
{
|
|
if (!RecordHeadAtEndOfEntry())
|
|
{
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = sNextActionDelay;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_GFX_CHANGE;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.b.localId = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.b.gfxState = gfxState;
|
|
gQuestLogCurActionIdx++;
|
|
sNextActionDelay = 0;
|
|
}
|
|
}
|
|
|
|
void QuestLogRecordPlayerAvatarGfxTransitionWithDuration(u8 gfxState, u8 duration)
|
|
{
|
|
if (!RecordHeadAtEndOfEntry())
|
|
{
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = sNextActionDelay;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_GFX_CHANGE;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.b.localId = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.b.gfxState = gfxState;
|
|
gQuestLogCurActionIdx++;
|
|
sNextActionDelay = duration;
|
|
}
|
|
}
|
|
|
|
void sub_81127F8(struct FieldInput * a0)
|
|
{
|
|
if (gQuestLogCurActionIdx < sMaxActionsInScene)
|
|
{
|
|
// Retain only the following fields:
|
|
// - pressedAButton
|
|
// - checkStandardWildEncounter
|
|
// - heldDirection
|
|
// - heldDirection2
|
|
// - tookStep
|
|
// - pressedBButton
|
|
// - dpadDirection
|
|
u32 r2 = *(u32 *)a0 & 0x00FF00F3;
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = sNextActionDelay;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_INPUT;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[0] = r2;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[1] = r2 >> 8; // always 0
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[2] = r2 >> 16;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[3] = r2 >> 24; // always 0
|
|
gQuestLogCurActionIdx++;
|
|
if (ArePlayerFieldControlsLocked())
|
|
sNextActionDelay = 1;
|
|
else
|
|
sNextActionDelay = 0;
|
|
}
|
|
}
|
|
|
|
static void TogglePlaybackStateForOverworldLock(u8 a0)
|
|
{
|
|
switch (a0)
|
|
{
|
|
case 1:
|
|
if (gQuestLogPlaybackState == QL_PLAYBACK_STATE_1)
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_3; // Message visible, overworld locked
|
|
break;
|
|
case 2:
|
|
if (gQuestLogPlaybackState == QL_PLAYBACK_STATE_3)
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_1; // Overworld unlocked
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QuestLog_OnEscalatorWarp(u8 direction)
|
|
{
|
|
u8 r1 = sub_8112CAC();
|
|
|
|
switch (direction)
|
|
{
|
|
case QL_ESCALATOR_OUT: // warp out
|
|
if (r1 == 1)
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_3;
|
|
else if (r1 == 2)
|
|
{
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = sNextActionDelay;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_3;
|
|
gQuestLogCurActionIdx++;
|
|
sNextActionDelay = 0;
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_4;
|
|
}
|
|
break;
|
|
case QL_ESCALATOR_IN: // warp in
|
|
if (r1 == 1)
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_1;
|
|
else if (r1 == 2)
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void SetUpQuestLogAction(u8 kind, struct QuestLogAction *actions, u16 size)
|
|
{
|
|
int i;
|
|
|
|
switch (kind)
|
|
{
|
|
default:
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
break;
|
|
case 1:
|
|
sCurSceneActions = actions;
|
|
sMaxActionsInScene = size / sizeof(*sCurSceneActions);
|
|
for (i = 0; i < (s32)ARRAY_COUNT(sMovementScripts); i++)
|
|
{
|
|
sMovementScripts[i][0] |= MOVEMENT_ACTION_NONE;
|
|
sMovementScripts[i][1] |= QL_PLAYER_GFX_NONE;
|
|
}
|
|
gQuestLogCurActionIdx = 0;
|
|
sLastQuestLogCursor = 0;
|
|
gQuestLogFieldInput = (struct FieldInput){};
|
|
sNextActionDelay = sCurSceneActions[gQuestLogCurActionIdx].duration;
|
|
sMovementScripts[0][0] = sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId;
|
|
sMovementScripts[0][1] = QL_PLAYER_GFX_NONE;
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_1;
|
|
break;
|
|
case 2:
|
|
sCurSceneActions = actions;
|
|
sMaxActionsInScene = size / sizeof(*sCurSceneActions);
|
|
for (i = 0; i < sMaxActionsInScene; i++)
|
|
{
|
|
sCurSceneActions[i] = (struct QuestLogAction){
|
|
.duration = 0xFFFF,
|
|
.type = QL_ACTION_SCENE_END
|
|
};
|
|
}
|
|
gQuestLogCurActionIdx = 0;
|
|
sNextActionDelay = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_MOVEMENT;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.localId = 0;
|
|
switch (GetPlayerFacingDirection())
|
|
{
|
|
case DIR_NONE:
|
|
case DIR_SOUTH:
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId = MOVEMENT_ACTION_FACE_DOWN;
|
|
break;
|
|
case DIR_EAST:
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId = MOVEMENT_ACTION_FACE_RIGHT;
|
|
break;
|
|
case DIR_NORTH:
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId = MOVEMENT_ACTION_FACE_UP;
|
|
break;
|
|
case DIR_WEST:
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId = MOVEMENT_ACTION_FACE_LEFT;
|
|
break;
|
|
}
|
|
sLastQuestLogCursor = 0;
|
|
gQuestLogCurActionIdx++;
|
|
sCurSceneActions[gQuestLogCurActionIdx].duration = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].type = QL_ACTION_INPUT;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[0] = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[1] = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[2] = 0;
|
|
sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[3] = 0;
|
|
gQuestLogCurActionIdx++;
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void sub_8112B3C(void)
|
|
{
|
|
switch (gQuestLogPlaybackState)
|
|
{
|
|
case QL_PLAYBACK_STATE_0:
|
|
case QL_PLAYBACK_STATE_3:
|
|
case QL_PLAYBACK_STATE_4:
|
|
break;
|
|
case QL_PLAYBACK_STATE_1:
|
|
if (!RecordHeadAtEndOfEntryOrScriptContext2Enabled())
|
|
{
|
|
if (sNextActionDelay != 0)
|
|
{
|
|
sNextActionDelay--;
|
|
}
|
|
else
|
|
{
|
|
do
|
|
{
|
|
switch (sCurSceneActions[gQuestLogCurActionIdx].type)
|
|
{
|
|
case QL_ACTION_MOVEMENT:
|
|
// NPC movement action
|
|
sMovementScripts[sCurSceneActions[gQuestLogCurActionIdx].data.a.localId][0] = sCurSceneActions[gQuestLogCurActionIdx].data.a.movementActionId;
|
|
break;
|
|
case QL_ACTION_GFX_CHANGE:
|
|
// State transition
|
|
sMovementScripts[sCurSceneActions[gQuestLogCurActionIdx].data.b.localId][1] = sCurSceneActions[gQuestLogCurActionIdx].data.b.gfxState;
|
|
break;
|
|
case QL_ACTION_INPUT:
|
|
// Player input
|
|
*(u32 *)&gQuestLogFieldInput = ((sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[3] << 24)
|
|
| (sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[2] << 16)
|
|
| (sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[1] << 8)
|
|
| (sCurSceneActions[gQuestLogCurActionIdx].data.fieldInput[0] << 0));
|
|
break;
|
|
case QL_ACTION_3:
|
|
// End
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_3;
|
|
break;
|
|
case QL_ACTION_WAIT:
|
|
// Nothing. The wait action uses sNextActionDelay to add a pause to playback.
|
|
// When the counter is finished and this is reached there's nothing else that needs to be done.
|
|
break;
|
|
case QL_ACTION_SCENE_END:
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
break;
|
|
}
|
|
|
|
if (gQuestLogPlaybackState == QL_PLAYBACK_STATE_0)
|
|
break;
|
|
if (++gQuestLogCurActionIdx >= sMaxActionsInScene)
|
|
{
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
break;
|
|
}
|
|
sNextActionDelay = sCurSceneActions[gQuestLogCurActionIdx].duration;
|
|
|
|
} while (gQuestLogPlaybackState != QL_PLAYBACK_STATE_3 && (sNextActionDelay == 0 || sNextActionDelay == 0xFFFF));
|
|
}
|
|
}
|
|
else if (gQuestLogCurActionIdx >= sMaxActionsInScene)
|
|
{
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
}
|
|
break;
|
|
case QL_PLAYBACK_STATE_2:
|
|
if (ArePlayerFieldControlsLocked() != TRUE)
|
|
{
|
|
sNextActionDelay++;
|
|
if (gQuestLogCurActionIdx >= sMaxActionsInScene)
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QL_AfterRecordFishActionSuccessful(void)
|
|
{
|
|
sNextActionDelay++;
|
|
}
|
|
|
|
u8 sub_8112CAC(void)
|
|
{
|
|
switch (gQuestLogPlaybackState)
|
|
{
|
|
case QL_PLAYBACK_STATE_0:
|
|
default:
|
|
return 0;
|
|
case QL_PLAYBACK_STATE_1:
|
|
case QL_PLAYBACK_STATE_3:
|
|
return 1;
|
|
case QL_PLAYBACK_STATE_2:
|
|
case QL_PLAYBACK_STATE_4:
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
static bool8 RecordHeadAtEndOfEntryOrScriptContext2Enabled(void)
|
|
{
|
|
if (gQuestLogCurActionIdx >= sMaxActionsInScene || ArePlayerFieldControlsLocked() == TRUE)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static bool8 RecordHeadAtEndOfEntry(void)
|
|
{
|
|
if (gQuestLogCurActionIdx >= sMaxActionsInScene)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
|
|
static const struct FlagOrVarRecord sDummyFlagOrVarRecord = {
|
|
.idx = 0,
|
|
.isFlag = FALSE,
|
|
.value = 0x7FFF
|
|
};
|
|
|
|
void *QuestLogGetFlagOrVarPtr(bool8 isFlag, u16 idx)
|
|
{
|
|
void *response;
|
|
if (gQuestLogCurActionIdx == 0)
|
|
return NULL;
|
|
if (gQuestLogCurActionIdx >= sMaxActionsInScene)
|
|
return NULL;
|
|
if (sFlagOrVarPlayhead >= sNumFlagsOrVars)
|
|
return NULL;
|
|
if (sFlagOrVarRecords[sFlagOrVarPlayhead].idx == idx && sFlagOrVarRecords[sFlagOrVarPlayhead].isFlag == isFlag)
|
|
{
|
|
response = &sFlagOrVarRecords[sFlagOrVarPlayhead].value;
|
|
sFlagOrVarPlayhead++;
|
|
}
|
|
else
|
|
response = NULL;
|
|
return response;
|
|
}
|
|
|
|
void QuestLogSetFlagOrVar(bool8 isFlag, u16 idx, u16 value)
|
|
{
|
|
if (gQuestLogCurActionIdx == 0)
|
|
return;
|
|
if (gQuestLogCurActionIdx >= sMaxActionsInScene)
|
|
return;
|
|
if (sFlagOrVarPlayhead >= sNumFlagsOrVars)
|
|
return;
|
|
sFlagOrVarRecords[sFlagOrVarPlayhead].idx = idx;
|
|
sFlagOrVarRecords[sFlagOrVarPlayhead].isFlag = isFlag;
|
|
sFlagOrVarRecords[sFlagOrVarPlayhead].value = value;
|
|
sFlagOrVarPlayhead++;
|
|
}
|
|
|
|
// Unused
|
|
static void QuestLogResetFlagsOrVars(u8 state, struct FlagOrVarRecord * records, u16 size)
|
|
{
|
|
s32 i;
|
|
|
|
if (state == 0 || state > QL_STATE_PLAYBACK)
|
|
{
|
|
gQuestLogPlaybackState = QL_PLAYBACK_STATE_0;
|
|
}
|
|
else
|
|
{
|
|
sFlagOrVarRecords = records;
|
|
sNumFlagsOrVars = size / 4;
|
|
sFlagOrVarPlayhead = 0;
|
|
if (state == QL_STATE_PLAYBACK)
|
|
{
|
|
for (i = 0; i < sMaxActionsInScene; i++)
|
|
sFlagOrVarRecords[i] = sDummyFlagOrVarRecord;
|
|
}
|
|
}
|
|
}
|