mirror of https://github.com/zeldaret/tmc.git
483 lines
15 KiB
C
483 lines
15 KiB
C
/**
|
|
* @file title.c
|
|
* @ingroup Tasks
|
|
*
|
|
* @brief Title task
|
|
*/
|
|
|
|
#include "sound.h"
|
|
#include "main.h"
|
|
#include "entity.h"
|
|
#include "common.h"
|
|
#include "screen.h"
|
|
#include "object.h"
|
|
#include "message.h"
|
|
#include "functions.h"
|
|
#include "save.h"
|
|
#include "area.h"
|
|
#include "item.h"
|
|
|
|
typedef struct {
|
|
u8 filler0[0x4];
|
|
u8 language;
|
|
u8 state;
|
|
u8 subState;
|
|
u8 filler7[0x1];
|
|
u16 timer;
|
|
u8 fillerA[0x6];
|
|
u8 lightRaysPaletteGroup;
|
|
u8 lightRaysAlphaBlendIndex;
|
|
u8 counter;
|
|
u8 filler13[0x19];
|
|
int swordBgScaleRatio;
|
|
} IntroState;
|
|
|
|
// TODO: This occupies the same memory region as gMenu
|
|
extern IntroState gIntroState;
|
|
|
|
enum {
|
|
ADVANCE_NONE,
|
|
ADVANCE_TIMER_EXPIRED,
|
|
ADVANCE_KEY_PRESSED,
|
|
};
|
|
|
|
static void HandleNintendoCapcomLogos(void);
|
|
static void HandleTitlescreen(void);
|
|
static void UpdateSwordBgAffineData(void);
|
|
static void ExitTitlescreen(void);
|
|
static void HandleJapaneseTitlescreenAnimationIntro(void);
|
|
static void HandleTitlescreenAnimationIntro(void);
|
|
static u32 GetAdvanceState(void);
|
|
static void UpdateLightRays(void);
|
|
static void UpdatePressStartIcon(void);
|
|
|
|
static void (*const sIntroSequenceHandlers[])(void) = {
|
|
HandleNintendoCapcomLogos,
|
|
HandleTitlescreen,
|
|
ExitTitlescreen,
|
|
};
|
|
|
|
static const u16 sLightRaysAlphaBlends[] = {
|
|
BLDALPHA_BLEND(9, 9), BLDALPHA_BLEND(8, 10), BLDALPHA_BLEND(7, 11), BLDALPHA_BLEND(6, 12),
|
|
BLDALPHA_BLEND(5, 13), BLDALPHA_BLEND(6, 12), BLDALPHA_BLEND(7, 11), BLDALPHA_BLEND(8, 10),
|
|
};
|
|
|
|
#define FLAG_BYTE(bank, flag) (((bank) + (flag)) >> 3)
|
|
#define FLAG_X(flag) (1 << ((flag)&7))
|
|
|
|
#ifdef DEMO_JP
|
|
static const SaveFile gDemoSave = {
|
|
.initialized = 1,
|
|
.msg_speed = 1,
|
|
.brightness = 1,
|
|
.global_progress = 1,
|
|
.field_0x9 = { [23] = 0x1F },
|
|
.windcrests = 0x00013780,
|
|
.unk50 = 7,
|
|
.areaVisitFlags = { 0x0114C300 },
|
|
.name = "\x97\x7f\xdd",
|
|
.saved_status = {
|
|
.area_next = AREA_DEEPWOOD_SHRINE,
|
|
.room_next = 0xb,
|
|
.start_pos_x = 0xa8,
|
|
.start_pos_y = 0xc8,
|
|
.layer = 1,
|
|
.overworld_map_x = 0xd66,
|
|
.overworld_map_y = 0xae0,
|
|
},
|
|
.stats = {
|
|
.health = 40,
|
|
.maxHealth = 40,
|
|
.itemButtons = { ITEM_SHIELD, ITEM_SMITH_SWORD },
|
|
.rupees = 5,
|
|
},
|
|
.fillerD0 = {
|
|
[34] = 5,
|
|
[37] = 4,
|
|
[47] = 6,
|
|
[51] = 0x40,
|
|
[56] = 0x40,
|
|
},
|
|
.flags = {
|
|
[FLAG_BYTE(FLAG_BANK_G, START)] = FLAG_X(START) | FLAG_X(EZERO_1ST) | FLAG_X(TABIDACHI),
|
|
[FLAG_BYTE(FLAG_BANK_G, OUTDOOR)] = FLAG_X(OUTDOOR),
|
|
[FLAG_BYTE(FLAG_BANK_G, ENTRANCE_0)] = FLAG_X(ENTRANCE_0),
|
|
[FLAG_BYTE(FLAG_BANK_1, MORI_00_KOBITO)] = FLAG_X(MORI_00_KOBITO) | FLAG_X(MORI_ENTRANCE_1ST),
|
|
[FLAG_BYTE(FLAG_BANK_1, SOUGEN_01_ZELDA)] = FLAG_X(SOUGEN_01_ZELDA),
|
|
[FLAG_BYTE(FLAG_BANK_1, SOUGEN_06_WAKAGI_1)] = FLAG_X(SOUGEN_06_WAKAGI_1) | FLAG_X(SOUGEN_06_WAKAGI_2) | FLAG_X(SOUGEN_06_WAKAGI_3),
|
|
[FLAG_BYTE(FLAG_BANK_1, SOUGEN_06_AKINDO)] = FLAG_X(SOUGEN_06_AKINDO),
|
|
[FLAG_BYTE(FLAG_BANK_1, CASTLE_04_MEZAME)] = FLAG_X(CASTLE_04_MEZAME),
|
|
[FLAG_BYTE(FLAG_BANK_1, MACHI_01_DEMO)] = FLAG_X(MACHI_01_DEMO),
|
|
[FLAG_BYTE(FLAG_BANK_2, MHOUSE15_OP1ST)] = FLAG_X(MHOUSE15_OP1ST),
|
|
[FLAG_BYTE(FLAG_BANK_2, M_PRIEST_TALK)] = FLAG_X(M_PRIEST_TALK) | FLAG_X(M_ELDER_TALK1ST) | FLAG_X(M_PRIEST_MOVE),
|
|
[FLAG_BYTE(FLAG_BANK_2, KOBITO_MORI_1ST)] = FLAG_X(KOBITO_MORI_1ST),
|
|
[FLAG_BYTE(FLAG_BANK_5, LV1_0B_WALK)] = FLAG_X(LV1_0B_WALK),
|
|
},
|
|
};
|
|
static const u8 unk[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd0,
|
|
0xc2, 0xd1, 0xc2, 0xd2, 0xc2, 0xd3, 0xc2, 0xd4, 0xc2, 0xd5, 0xc2, 0xd6, 0xc2,
|
|
0xd7, 0xc2, 0xd8, 0xc2, 0x00, 0xf0, 0x11, 0x90, 0x11, 0x90, 0x05, 0xd3, 0x49,
|
|
0xd3, 0x26, 0xd3, 0x65, 0xdf, 0x11, 0x90, 0x11, 0x90, 0x11, 0x90, 0x11, 0x90,
|
|
0x11, 0x90, 0x11, 0x90, 0x11, 0x90, 0x11, 0x90, 0x11, 0x90, 0x11, 0x90, 0x11,
|
|
0x90, 0x11, 0x90, 0x8a, 0xc2, 0x8b, 0xc2, 0x8c, 0xc2, 0x8d, 0xc2 };
|
|
#endif
|
|
|
|
static u32 AdvanceIntroSequence(u32 transition) {
|
|
gUnk_02032EC0.lastState = transition;
|
|
gMain.state = 2;
|
|
MemClear(&gIntroState, sizeof(gIntroState));
|
|
SetFade(7, 8);
|
|
}
|
|
|
|
void TitleTask(void) {
|
|
FlushSprites();
|
|
switch (gMain.state) {
|
|
case 0:
|
|
MessageInitialize();
|
|
MemClear(&gUnk_02032EC0, sizeof(gUnk_02032EC0));
|
|
AdvanceIntroSequence(0);
|
|
break;
|
|
case 1:
|
|
sIntroSequenceHandlers[gUnk_02032EC0.lastState]();
|
|
break;
|
|
case 2:
|
|
if (gFadeControl.active) {
|
|
return;
|
|
}
|
|
DispReset(1);
|
|
gMain.state = 1;
|
|
break;
|
|
}
|
|
CopyOAM();
|
|
}
|
|
|
|
static void HandleNintendoCapcomLogos(void) {
|
|
u32 advance;
|
|
u32 paletteGroup;
|
|
|
|
advance = GetAdvanceState();
|
|
if (gIntroState.state == 0) {
|
|
DispReset(1);
|
|
gIntroState.state = 1;
|
|
gIntroState.timer = 120;
|
|
LoadGfxGroup(16);
|
|
LoadGfxGroup(1);
|
|
if (gSaveHeader->language == 0) {
|
|
paletteGroup = 1;
|
|
} else {
|
|
paletteGroup = 2;
|
|
}
|
|
LoadPaletteGroup(paletteGroup);
|
|
gScreen.lcd.displayControl |= DISPCNT_BG2_ON;
|
|
gScreen.bg1.updated = 1;
|
|
SetFade(6, 8);
|
|
advance = ADVANCE_NONE;
|
|
#if defined(DEMO_USA)
|
|
if (gUnk_02000010.listenForKeyPresses == 0) {
|
|
if ((gInput.heldKeys & 0x204) == 0x204) { // TODO
|
|
gUnk_02000010.field_0x7 = 1;
|
|
SoundReq(SFX_SECRET_BIG);
|
|
} else {
|
|
if ((gInput.heldKeys & 0x104) == 0x104) { // TODO
|
|
gUnk_02000010.field_0x7 = 2;
|
|
SoundReq(SFX_TASK_COMPLETE);
|
|
} else {
|
|
gUnk_02000010.field_0x7 = 0;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
} else {
|
|
if (advance == ADVANCE_TIMER_EXPIRED) {
|
|
advance = ADVANCE_KEY_PRESSED;
|
|
}
|
|
}
|
|
|
|
if (advance == ADVANCE_KEY_PRESSED) {
|
|
gUnk_02000010.listenForKeyPresses = 1;
|
|
AdvanceIntroSequence(1);
|
|
}
|
|
}
|
|
|
|
static void HandleTitlescreen(void) {
|
|
int advance;
|
|
u32 paletteGroup;
|
|
|
|
gIntroState.counter++;
|
|
switch (gIntroState.state) {
|
|
case 0:
|
|
gIntroState.state = 1;
|
|
gIntroState.subState = 0;
|
|
gIntroState.timer = 30;
|
|
gIntroState.language = 7;
|
|
EraseAllEntities();
|
|
ResetPaletteTable(0);
|
|
ResetPalettes();
|
|
gGFXSlots.unk0 = 1;
|
|
LoadGfxGroup(2);
|
|
if (gSaveHeader->language == 0) {
|
|
paletteGroup = 3;
|
|
} else {
|
|
paletteGroup = 4;
|
|
}
|
|
LoadPaletteGroup(paletteGroup);
|
|
if (gSaveHeader->language == 0) {
|
|
// Blend first and second layer
|
|
gScreen.controls.layerFXControl = BLDCNT_TGT1_BG2 | BLDCNT_TGT2_BG3 | BLDCNT_EFFECT_BLEND;
|
|
gScreen.controls.alphaBlend = BLDALPHA_BLEND(9, 9);
|
|
gScreen.bg1.control = BGCNT_SCREENBASE(28) | BGCNT_PRIORITY(1) | BGCNT_CHARBASE(2);
|
|
gScreen.bg2.control = BGCNT_SCREENBASE(29) | BGCNT_PRIORITY(2);
|
|
gScreen.bg3.control = BGCNT_SCREENBASE(30) | BGCNT_PRIORITY(3);
|
|
gScreen.lcd.displayControl |= DISPCNT_BG1_ON | DISPCNT_BG2_ON | DISPCNT_BG3_ON | DISPCNT_OBJ_ON;
|
|
gScreen.bg1.yOffset = -160;
|
|
} else {
|
|
gScreen.controls.layerFXControl = BLDCNT_TGT1_BG0 | BLDCNT_TGT2_BG1 | BLDCNT_EFFECT_BLEND;
|
|
gScreen.controls.alphaBlend = BLDALPHA_BLEND(9, 9);
|
|
gScreen.bg0.control = BGCNT_SCREENBASE(29) | BGCNT_PRIORITY(2);
|
|
gScreen.bg1.control = BGCNT_SCREENBASE(30) | BGCNT_PRIORITY(3);
|
|
gScreen.bg2.control = BGCNT_SCREENBASE(28) | BGCNT_PRIORITY(1) | BGCNT_CHARBASE(2) | BGCNT_256COLOR |
|
|
BGCNT_WRAP | BGCNT_TXT512x256;
|
|
gScreen.lcd.displayControl |= DISPCNT_MODE_1;
|
|
gScreen.lcd.displayControl |= DISPCNT_BG0_ON | DISPCNT_BG1_ON | DISPCNT_OBJ_ON;
|
|
gIntroState.swordBgScaleRatio = 0x10;
|
|
UpdateSwordBgAffineData();
|
|
}
|
|
InitSoundPlayingInfo();
|
|
SoundReq(BGM_TITLE_SCREEN);
|
|
SetFade(6, 8);
|
|
break;
|
|
case 1:
|
|
if (gFadeControl.active) {
|
|
return;
|
|
}
|
|
if (gSaveHeader->language == 0) {
|
|
HandleJapaneseTitlescreenAnimationIntro();
|
|
} else {
|
|
HandleTitlescreenAnimationIntro();
|
|
}
|
|
break;
|
|
case 2:
|
|
#if defined(JP) || defined(DEMO_JP) || defined(EU)
|
|
if (GetAdvanceState()) {
|
|
#else
|
|
if (--gIntroState.timer == 0) {
|
|
#endif
|
|
gIntroState.timer = 3600;
|
|
gIntroState.state++;
|
|
}
|
|
#if defined(USA) || defined(DEMO_USA)
|
|
UpdatePressStartIcon();
|
|
#endif
|
|
break;
|
|
default:
|
|
advance = GetAdvanceState();
|
|
if (advance != ADVANCE_NONE) {
|
|
if (advance == ADVANCE_KEY_PRESSED) {
|
|
SoundReq(SFX_TEXTBOX_SELECT);
|
|
} else {
|
|
advance = ADVANCE_NONE;
|
|
}
|
|
AdvanceIntroSequence(advance);
|
|
SoundReq(SONG_VOL_FADE_OUT);
|
|
}
|
|
#if defined(JP) || defined(DEMO_JP) || defined(DEMO_JP)
|
|
gOamCmd._4 = 0;
|
|
gOamCmd._6 = 0;
|
|
gOamCmd._8 = 0xE020;
|
|
gOamCmd.x = 120;
|
|
gOamCmd.y = 152;
|
|
DrawDirect(511, 1);
|
|
#elif defined(EU)
|
|
gOamCmd._4 = 0;
|
|
gOamCmd._6 = 0;
|
|
gOamCmd._8 = 0xE020;
|
|
gOamCmd.x = 120;
|
|
gOamCmd.y = 152;
|
|
DrawDirect(510, 1);
|
|
#else
|
|
UpdatePressStartIcon();
|
|
#endif
|
|
if ((gIntroState.timer & 0x20) == 0) {
|
|
gOamCmd._8 = 0xe000;
|
|
gOamCmd.y = 0x84;
|
|
#ifdef EU
|
|
DrawDirect(0x1fe, 0);
|
|
#else
|
|
DrawDirect(0x1ff, 0);
|
|
#endif
|
|
}
|
|
}
|
|
if (gIntroState.language != gSaveHeader->language) {
|
|
gIntroState.language = gSaveHeader->language;
|
|
LoadGfxGroup(3);
|
|
}
|
|
UpdateLightRays();
|
|
UpdateEntities();
|
|
DrawEntities();
|
|
}
|
|
|
|
#if defined(USA) || defined(DEMO_USA)
|
|
static void UpdatePressStartIcon(void) {
|
|
gOamCmd._4 = 0;
|
|
gOamCmd._6 = 0;
|
|
gOamCmd._8 = 0xE020;
|
|
gOamCmd.x = 120;
|
|
gOamCmd.y = 152;
|
|
DrawDirect(511, 1);
|
|
}
|
|
#endif
|
|
|
|
static void UpdateSwordBgAffineData(void) {
|
|
struct BgAffineSrcData aff;
|
|
aff.texY = 0x8000;
|
|
aff.texX = 0x8000;
|
|
aff.scrX = DISPLAY_WIDTH / 2;
|
|
aff.scrY = DISPLAY_HEIGHT / 2 - 8;
|
|
aff.alpha = 0;
|
|
aff.sy = aff.sx = gIntroState.swordBgScaleRatio;
|
|
BgAffineSet(&aff, (struct BgAffineDstData*)&gScreen.controls, 1);
|
|
}
|
|
|
|
static void HandleJapaneseTitlescreenAnimationIntro(void) {
|
|
Entity* pEVar2;
|
|
|
|
switch (gIntroState.subState) {
|
|
case 0:
|
|
if (!gFadeControl.active) {
|
|
if ((gIntroState.counter & 1) == 0) {
|
|
gScreen.bg1.yOffset++;
|
|
}
|
|
|
|
if (GetAdvanceState() == ADVANCE_KEY_PRESSED || gScreen.bg1.yOffset == 0) {
|
|
gIntroState.subState++;
|
|
gScreen.bg1.yOffset = 0;
|
|
gScreen.bg1.control = BGCNT_SCREENBASE(12) | BGCNT_PRIORITY(1) | BGCNT_CHARBASE(2);
|
|
gFadeControl.mask = 0x00000040;
|
|
SetFade(6, 0x10);
|
|
SoundReq(SFX_F8);
|
|
}
|
|
}
|
|
break;
|
|
case 1:
|
|
if (!gFadeControl.active) {
|
|
gFadeControl.mask = 0xFFFFFFFF;
|
|
gIntroState.subState++;
|
|
#if defined(JP) || defined(EU) || defined(DEMO_JP)
|
|
gIntroState.timer = 120;
|
|
#else
|
|
gIntroState.timer = 90;
|
|
#endif
|
|
pEVar2 = CreateObject(OBJECT_B4, 0, 0);
|
|
if (pEVar2 != NULL) {
|
|
pEVar2->x.HALF.HI = 0;
|
|
pEVar2->y.HALF.HI = DISPLAY_HEIGHT / 2 - 8;
|
|
}
|
|
}
|
|
break;
|
|
case 2:
|
|
if (GetAdvanceState() != ADVANCE_NONE) {
|
|
gIntroState.state++;
|
|
#if defined(JP) || defined(EU) || defined(DEMO_JP)
|
|
gIntroState.timer = 30;
|
|
#else
|
|
gIntroState.timer = 60;
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
static void HandleTitlescreenAnimationIntro(void) {
|
|
switch (gIntroState.subState) {
|
|
case 0:
|
|
if (!gFadeControl.active) {
|
|
gIntroState.subState = 1;
|
|
gScreen.lcd.displayControl |= DISPCNT_BG2_ON;
|
|
SoundReq(SFX_EVAPORATE);
|
|
}
|
|
break;
|
|
case 1:
|
|
gIntroState.swordBgScaleRatio += 0x10;
|
|
if (gIntroState.swordBgScaleRatio > 0x100) {
|
|
gIntroState.swordBgScaleRatio = 0x100;
|
|
gIntroState.timer = 40;
|
|
gIntroState.subState++;
|
|
SetFade(6, 16);
|
|
}
|
|
UpdateSwordBgAffineData();
|
|
break;
|
|
case 2:
|
|
if (--gIntroState.timer == 0) {
|
|
#if defined(JP) || defined(EU) || defined(DEMO_JP)
|
|
gIntroState.timer = 360;
|
|
#else
|
|
gIntroState.timer = 300;
|
|
#endif
|
|
gIntroState.subState++;
|
|
CreateObject(OBJECT_BD, 0, 0);
|
|
SetFade(6, 16);
|
|
SoundReq(SFX_F8);
|
|
}
|
|
break;
|
|
default:
|
|
if (!gFadeControl.active && GetAdvanceState() != ADVANCE_NONE) {
|
|
gIntroState.state++;
|
|
#if defined(JP) || defined(EU) || defined(DEMO_JP)
|
|
gIntroState.timer = 30;
|
|
#else
|
|
gIntroState.timer = 60;
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
static void ExitTitlescreen(void) {
|
|
if (!gFadeControl.active) {
|
|
#ifdef DEMO_JP
|
|
MemCopy(&gDemoSave, &gSave, sizeof(gSave));
|
|
SetTask(TASK_GAME);
|
|
#else
|
|
SetTask(TASK_FILE_SELECT);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
static u32 GetAdvanceState(void) {
|
|
u32 newKeys;
|
|
|
|
if (gFadeControl.active) {
|
|
return ADVANCE_NONE;
|
|
}
|
|
|
|
if (!gUnk_02000010.listenForKeyPresses) {
|
|
newKeys = 0;
|
|
} else {
|
|
newKeys = gInput.newKeys & (A_BUTTON | START_BUTTON);
|
|
}
|
|
|
|
if (--gIntroState.timer == 0) {
|
|
return ADVANCE_TIMER_EXPIRED;
|
|
}
|
|
|
|
if (newKeys) {
|
|
return ADVANCE_KEY_PRESSED;
|
|
}
|
|
|
|
return ADVANCE_NONE;
|
|
}
|
|
|
|
static void UpdateLightRays(void) {
|
|
// Periodically rotate the palette to give a shimmering effect.
|
|
if ((gIntroState.counter & 0x7) == 0) {
|
|
gIntroState.lightRaysPaletteGroup++;
|
|
gIntroState.lightRaysPaletteGroup &= 0x3;
|
|
LoadPaletteGroup(5 + gIntroState.lightRaysPaletteGroup);
|
|
}
|
|
|
|
// Periodically update the transparency of the light rays.
|
|
if ((gIntroState.counter & 0x1F) == 0) {
|
|
gIntroState.lightRaysAlphaBlendIndex = (gIntroState.lightRaysAlphaBlendIndex + 1) & 0x7;
|
|
gScreen.controls.alphaBlend = sLightRaysAlphaBlends[gIntroState.lightRaysAlphaBlendIndex];
|
|
}
|
|
}
|