shithub: zelda3

ref: c056291dd15f9d32e54ac475657bca626db6b12a
dir: /ending.c/

View raw version
#include "zelda_rtl.h"
#include "snes/snes_regs.h"
#include "variables.h"
#include "load_gfx.h"
#include "dungeon.h"
#include "sprite.h"
#include "ending.h"
#include "overworld.h"
#include "player.h"
#include "misc.h"
#include "messaging.h"
#include "player_oam.h"
#include "sprite_main.h"
#include "ancilla.h"
#include "hud.h"
#include "assets.h"

static const uint16 kPolyhedralPalette[8] = { 0, 0x14d, 0x1b0, 0x1f3, 0x256, 0x279, 0x2fd, 0x35f };

#define ending_which_dung (*(uint16*)(g_ram+0xcc))
#define kPolyThreadRam (g_ram + 0x1f00)
static const int8 kIntroSprite0_Xvel[3] = { 1, 0, -1 };
static const int8 kIntroSprite0_Yvel[3] = { -1, 1, -1 };
static const uint8 kIntroSprite3_X[4] = { 0xc2, 0x98, 0x6f, 0x34 };
static const uint8 kIntroSprite3_Y[4] = { 0x7c, 0x54, 0x7c, 0x57 };
static const uint8 kIntroSprite3_State[8] = { 0, 1, 2, 3, 2, 1, 0xff, 0xff };
static const uint8 kTriforce_Xfinal[3] = { 0x59, 0x5f, 0x67 };
static const uint8 kTriforce_Yfinal[3] = { 0x74, 0x68, 0x74 };
static const uint16 kEndingSprites_X[] = {
  0x1e0, 0x200, 0x1ed, 0x203, 0x1da, 0x216, 0x1c8, 0x228, 0x1c0, 0x1e0, 0x208, 0x228,
  0xf8, 0xf0,
  0x278, 0x298, 0x1e0, 0x200, 0x220, 0x288, 0x1e2,
  0xe0, 0x150, 0xe8, 0x168, 0x128, 0x170, 0x170,
  0x335, 0x335, 0x300,
  0xb8, 0xce, 0xac, 0xc4,
  0x3b0, 0x390, 0x3d0,
  0xf8, 0xc8,
  0x80,
  0xf8, 0xf8, 0xf8, 0xf8, 0xf8, 0xe8, 0xf8, 0xd8, 0xf8, 0xc8, 0x108,
  0x70, 0x70, 0x70, 0x68, 0x88, 0x70,
  0x40, 0x70, 0x4f, 0x61, 0x37, 0x79,
  0xc8, 0x278, 0x258, 0x1d8, 0x1c8, 0x188, 0x270,
  0x180,
  0x2e8, 0x270, 0x270, 0x2a0, 0x2a0, 0x2a4, 0x2fc,
  0x76, 0x73, 0x76, 0x0, 0xd0, 0x80,
};
static const uint16 kEndingSprites_Y[] = {
  0x158, 0x158, 0x138, 0x138, 0x140, 0x140, 0x150, 0x150, 0x120, 0x120, 0x120, 0x120,
  0x60, 0x37,
  0xc2, 0xc2, 0x16b, 0x16c, 0x16b, 0xb8, 0x16b,
  0x80, 0x60, 0x146, 0x146, 0x1c6, 0x70, 0x70,
  0x128, 0x128, 0x16f,
  0xf5, 0xfc, 0x10d, 0x10d,
  0x40, 0x40, 0x40,
  0x150, 0x158,
  0xf4,
  0x120, 0x120, 0x120, 0x120, 0x120, 0x108, 0x100, 0xd8, 0xd8, 0xf0, 0xf0,
  0x3c, 0x3c, 0x3c, 0x90, 0x80, 0x3c,
  0x16c, 0x16c, 0x174, 0x174, 0x175, 0x175,
  0x250, 0x2b0, 0x2b0, 0x2a0, 0x2b0, 0x2b0, 0x2b8,
  0xd8,
  0x24b, 0x1b0, 0x1c8, 0x1c8, 0x1b0, 0x230, 0x230,
  0x8b, 0x83, 0x85, 0x2c, 0xf8, 0x100,
};
static const uint8 kEndingSprites_Idx[17] = {
  0, 12, 14, 21, 28, 31, 35, 38, 40, 41, 52, 58, 64, 71, 72, 79, 85
};
static PlayerHandlerFunc *const kEndSequence0_Funcs[3] = {
&Credits_LoadScene_Overworld_PrepGFX,
&Credits_LoadScene_Overworld_Overlay,
&Credits_LoadScene_Overworld_LoadMap,
};
static PrepOamCoordsRet g_ending_coords;
static const uint16 kEnding1_TargetScrollY[16] = { 0x6f2, 0x210, 0x72c, 0xc00, 0x10c, 0xa9b, 0x10, 0x510, 0x89, 0xa8e, 0x222c, 0x2510, 0x826, 0x5c, 0x20a, 0x30 };
static const uint16 kEnding1_TargetScrollX[16] = { 0x77f, 0x480, 0x193, 0xaa, 0x878, 0x847, 0x4fd, 0xc57, 0x40f, 0x478, 0xa00, 0x200, 0x201, 0xaa1, 0x26f, 0 };
static const int8 kEnding1_Yvel[16] = { -1, -1, 1, -1, 1, 1, 0, 1, 0, -1, -1, 0, 0, 0, 1, -1 };
static const int8 kEnding1_Xvel[16] = { 0, 0, -1, 0, 0, -1, 1, 0, -1, 0, 0, 0, 1, -1, 1, 0 };
static PlayerHandlerFunc *const kEndSequence_Funcs[39] = {
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Dungeon,
&Credits_ScrollScene_Dungeon,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Dungeon,
&Credits_ScrollScene_Dungeon,
&Credits_LoadNextScene_Dungeon,
&Credits_ScrollScene_Dungeon,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&Credits_LoadNextScene_Overworld,
&Credits_ScrollScene_Overworld,
&EndSequence_32,
&Credits_BrightenTriangles,
&Credits_FadeColorAndBeginAnimating,
&Credits_StopCreditsScroll,
&Credits_FadeAndDisperseTriangles,
&Credits_FadeInTheEnd,
&Credits_HangForever,
};
#define intro_sword_ypos WORD(g_ram[0xc8])
#define intro_sword_18 g_ram[0xca]
#define intro_sword_19 g_ram[0xcb]
#define intro_sword_20 g_ram[0xcc]
#define intro_sword_21 g_ram[0xcd]
#define intro_sword_24 g_ram[0xd0]
static const uint16 kEnding_Tab1[16] = {
  0x1000, 2, 0x1002, 0x1012, 0x1004, 0x1006, 0x1010, 0x1014, 0x100a,
  0x1016, 0x5d, 0x64, 0x100e, 0x1008, 0x1018, 0x180 };
static const uint8 kEnding_SpritePack[17] = {
  0x28, 0x46, 0x27, 0x2e, 0x2b, 0x2b, 0xe, 0x2c, 0x1a, 0x29, 0x47, 0x28, 0x27, 0x28, 0x2a, 0x28, 0x2d,
};
static const uint8 kEnding_SpritePal[17] = {
  1, 0x40, 1, 4, 1, 1, 1, 0x11, 1, 1, 0x47, 0x40, 1, 1, 1, 1, 1,
};
void Intro_SetupScreen() {  // 828000
  nmi_disable_core_updates = 0x80;
  EnableForceBlank();
  TM_copy = 16;
  TS_copy = 0;
  Intro_InitializeBackgroundSettings();
  CGWSEL_copy = 0x20;
  load_chr_halfslot_even_odd = 20;
  Graphics_LoadChrHalfSlot();
  load_chr_halfslot_even_odd = 0;
  LoadOWMusicIfNeeded();

  // why 17?
  for(int i = 0; i < 17; i++)
    main_palette_buffer[144 + i] = 0x7fff;

  for (int i = 0; i < 17; i++)
    g_zenv.vram[0x27f0 + i] = 0;

  R16 = 0x1ffe;
  R18 = 0x1bfe;
}

void Intro_LoadTextPointersAndPalettes() {  // 828116
  Text_GenerateMessagePointers();
  Overworld_LoadAllPalettes();
}

void Credits_LoadScene_Overworld_PrepGFX() {  // 828604
  EnableForceBlank();
  EraseTileMaps_normal();
  CGWSEL_copy = 0x82;
  int k = submodule_index >> 1;
  dungeon_room_index = kEnding_Tab1[k];

  if (k != 6 && k != 15)
    LoadOverworldFromDungeon();
  else
    Overworld_EnterSpecialArea();
  music_control = 0;
  sound_effect_ambient = 0;

  int t = BYTE(overworld_screen_index) & ~0x40;
  DecompressAnimatedOverworldTiles((t == 3 || t == 5 || t == 7) ? 0x58 : 0x5a);

  k = submodule_index >> 1;
  sprite_graphics_index = kEnding_SpritePack[k];
  uint8 sprpal = kEnding_SpritePal[k];
  InitializeTilesets();
  OverworldLoadScreensPaletteSet();
  Overworld_LoadPalettes(GetOverworldBgPalette(BYTE(overworld_screen_index)), sprpal);

  hud_palette = 1;
  Palette_Load_HUD();
  if (!submodule_index)
    TransferFontToVRAM();
  Overworld_LoadPalettesInner();
  Overworld_SetFixedColAndScroll();
  if (BYTE(overworld_screen_index) >= 128)
    Palette_SetOwBgColor();
  BGMODE_copy = 9;
  subsubmodule_index++;
}

void Credits_LoadScene_Overworld_Overlay() {  // 828697
  Overworld_LoadOverlays2();
  music_control = 0;
  sound_effect_ambient = 0;
  submodule_index--;
  subsubmodule_index++;
}

void Credits_LoadScene_Overworld_LoadMap() {  // 8286a5
  Overworld_LoadAndBuildScreen();
  Credits_PrepAndLoadSprites();
  R16 = 0;
  subsubmodule_index = 0;
}

void Credits_OperateScrollingAndTileMap() {  // 8286b3
  Credits_HandleCameraScrollControl();
  if (BYTE(overworld_screen_trans_dir_bits2))
    OverworldHandleMapScroll();
}

void Credits_LoadCoolBackground() {  // 8286c0
  main_tile_theme_index = 33;
  aux_tile_theme_index = 59;
  sprite_graphics_index = 45;
  InitializeTilesets();
  BYTE(overworld_screen_index) = 0x5b;
  Overworld_LoadPalettes(GetOverworldBgPalette(BYTE(overworld_screen_index)), 0x13);
  overworld_palette_aux2_bp5to7_hi = 3;
  Palette_Load_OWBG2();
  Overworld_CopyPalettesToCache();
  Overworld_LoadOverlays2();
  BG1VOFS_copy2 = 0;
  BG1HOFS_copy2 = 0;
  submodule_index--;
}

void Credits_LoadScene_Dungeon() {  // 8286fd
  EnableForceBlank();
  EraseTileMaps_normal();
  WORD(which_entrance) = kEnding_Tab1[submodule_index >> 1];

  Dungeon_LoadEntrance();
  dung_num_lit_torches = 0;
  hdr_dungeon_dark_with_lantern = 0;
  Dungeon_LoadAndDrawRoom();
  DecompressAnimatedDungeonTiles(kDungAnimatedTiles[main_tile_theme_index]);

  int i = submodule_index >> 1;
  sprite_graphics_index = kEnding_SpritePack[i];
  const DungPalInfo *dpi = GetDungPalInfo(kEnding_SpritePal[i] & 0x3f);
  palette_sp5l = dpi->pal2;
  palette_sp6l = dpi->pal3;
  misc_sprites_graphics_index = 10;
  InitializeTilesets();
  palette_sp6r_indoors = 10;
  Dungeon_LoadPalettes();
  BGMODE_copy = 9;
  R16 = 0;
  INIDISP_copy = 0;
  submodule_index++;
  Credits_PrepAndLoadSprites();
}

void Module18_GanonEmerges() {  // 829edc
  uint16 hofs2 = BG2HOFS_copy2;
  uint16 vofs2 = BG2VOFS_copy2;
  uint16 hofs1 = BG1HOFS_copy2;
  uint16 vofs1 = BG1VOFS_copy2;

  BG2HOFS_copy2 = BG2HOFS_copy = hofs2 + bg1_x_offset;
  BG2VOFS_copy2 = BG2VOFS_copy = vofs2 + bg1_y_offset;
  BG1HOFS_copy2 = BG1HOFS_copy = hofs1 + bg1_x_offset;
  BG1VOFS_copy2 = BG1VOFS_copy = vofs1 + bg1_y_offset;
  Sprite_Main();
  BG1VOFS_copy2 = vofs1;
  BG1HOFS_copy2 = hofs1;
  BG2VOFS_copy2 = vofs2;
  BG2HOFS_copy2 = hofs2;

  switch (overworld_map_state) {
  case 0:  // GetBirdForPursuit
    Dungeon_HandleLayerEffect();
    CallForDuckIndoors();
    SaveDungeonKeys();
    overworld_map_state++;
    flag_is_link_immobilized++;
    break;
  case 1:  // PrepForPyramidLocation
    Dungeon_HandleLayerEffect();
    if (submodule_index == 10) {
      overworld_screen_index = 91;
      player_is_indoors = 0;
      main_module_index = 24;
      submodule_index = 0;
      overworld_map_state = 2;
    }
    break;
  case 2:  // FadeOutDungeonScreen
    Dungeon_HandleLayerEffect();
    if (--INIDISP_copy)
      break;
    EnableForceBlank();
    overworld_map_state++;
    Hud_RebuildIndoor();
    link_x_vel = link_y_vel = 0;
    break;
  case 3:  // LOadPyramidArea
    birdtravel_var1[0] = 8;
    birdtravel_var1[1] = 0;
    FluteMenu_LoadSelectedScreen();
    LoadOWMusicIfNeeded();
    music_control = 9;
    break;
  case 4:  // LoadAmbientOverlay
    Overworld_LoadOverlayAndMap();
    subsubmodule_index = 0;
    break;
  case 5:  // BrightenScreenThenSpawnBat
    if (++INIDISP_copy == 15) {
      dung_savegame_state_bits = 0;
      flag_unk1 = 0;
      Sprite_SpawnBatCrashCutscene();
      link_direction_facing = 2;
      saved_module_for_menu = 9;
      player_is_indoors = 0;
      overworld_map_state++;
      subsubmodule_index = 128;
      BYTE(cur_palace_index_x2) = 255;
    }
    break;
  case 6:  // DelayForBatSmashIntoPyramid
    break;
  case 7:  // DelayPlayerDropOff
    if (!--subsubmodule_index)
      overworld_map_state++;
    break;
  case 8:  // DropOffPlayerAtPyramid
    BirdTravel_Finish_Doit();
    break;
  }

  LinkOam_Main();
}

void Module19_TriforceRoom() {  // 829fec
  switch (subsubmodule_index) {
  case 0:  //
    Link_ResetProperties_A();
    link_last_direction_moved_towards = 0;
    music_control = 0xf1;
    ResetTransitionPropsAndAdvance_ResetInterface();
    break;
  case 1:  //
    ConditionalMosaicControl();
    ApplyPaletteFilter_bounce();
    break;
  case 2:  //
    EnableForceBlank();
    LoadCreditsSongs();
    dungeon_room_index = 0x189;
    EraseTileMaps_normal();
    Palette_RevertTranslucencySwap();
    Overworld_EnterSpecialArea();
    Overworld_LoadOverlays2();
    subsubmodule_index++;
    main_module_index = 25;
    submodule_index = 0;
    break;
  case 3:  //
    main_tile_theme_index = 36;
    sprite_graphics_index = 125;
    aux_tile_theme_index = 81;
    InitializeTilesets();
    Overworld_LoadAreaPalettesEx(4);
    Overworld_LoadPalettes(14, 0);
    SpecialOverworld_CopyPalettesToCache();
    subsubmodule_index++;
    break;
  case 4: { //
    uint8 bak0 = subsubmodule_index;
    Module08_02_LoadAndAdvance();
    subsubmodule_index = bak0 + 1;
    INIDISP_copy = 15;
    palette_filter_countdown = 31;
    mosaic_target_level = 0;
    HIBYTE(BG1HOFS_copy2) = 1;
    CGWSEL_copy = 2;
    CGADSUB_copy = 50;
    mosaic_level = 240;
    BYTE(link_y_coord) = 236;
    BYTE(link_x_coord) = 120;
    link_is_on_lower_level = 2;
    music_control = 32;
    main_module_index = 25;
    submodule_index = 0;
    break;
  }
  case 5:  //
    link_direction = 8;
    link_direction_last = 8;
    link_direction_facing = 0;
    if (BYTE(link_y_coord) < 192) {
      link_direction = 0;
      link_direction_last = 0;
      link_animation_steps = 0;
      subsubmodule_index++;
    }
    break;
  case 6:  //
    if (!(palette_filter_countdown & 1) && mosaic_level != 0)
      mosaic_level -= 0x10;
    BGMODE_copy = 9;
    MOSAIC_copy = mosaic_level | 7;
    ApplyPaletteFilter_bounce();
    break;
  case 7:  //
    TriforceRoom_PrepGFXSlotForPoly();
    dialogue_message_index = 0x173;
    Main_ShowTextMessage();
    RenderText();
    BYTE(R16) = 0x80;
    main_module_index = 25;
    subsubmodule_index++;
    break;
  case 8:  //
  case 10:  //
    AdvancePolyhedral();
    if (subsubmodule_index == 11) {
      music_control = 33;
      main_module_index = 25;
      link_direction = 0;
      link_direction_last = 0;
      submodule_index++;
    }
    break;
  case 9:  //
    AdvancePolyhedral();
    RenderText();
    if (!submodule_index) {
      overworld_map_state = 0;
      main_module_index = 25;
      subsubmodule_index++;
    }
    break;
  case 11:  //
    AdvancePolyhedral();
    TriforceRoom_LinkApproachTriforce();
    if (subsubmodule_index == 12) {
      link_direction = 0;
      link_direction_last = 0;
    }
    break;
  case 12:  //
    AdvancePolyhedral();
    if (!--BYTE(R16)) {
      Palette_AnimGetMasterSword2();
      submodule_index++;
    }
    break;
  case 13:  //
    AdvancePolyhedral();
    PaletteFilter_BlindingWhiteTriforce();
    if (BYTE(darkening_or_lightening_screen) == 255)
      subsubmodule_index++;
    break;
  case 14:  //
    if (!--INIDISP_copy) {
      main_module_index = 26;
      submodule_index = 0;
      subsubmodule_index = 0;
      irq_flag = 255;
      is_nmi_thread_active = 0;
      nmi_flag_update_polyhedral = 0;
      savegame_is_darkworld = 0;
    }
    break;
  }
  BG1HOFS_copy = BG1HOFS_copy2;
  BG1VOFS_copy = BG1VOFS_copy2;
  BG2HOFS_copy = BG2HOFS_copy2;
  BG2VOFS_copy = BG2VOFS_copy2;
  if (subsubmodule_index < 7 || subsubmodule_index >= 11) {
    Link_HandleVelocity();
    Link_HandleMovingAnimation_FullLongEntry();
  }
  LinkOam_Main();
}

void Intro_InitializeBackgroundSettings() {  // 82c500
  BGMODE_copy = 9;
  MOSAIC_copy = 0;
  zelda_ppu_write(BG1SC, 0x13);
  zelda_ppu_write(BG2SC, 3);
  zelda_ppu_write(BG3SC, 0x63);
  CGADSUB_copy = 32;
  COLDATA_copy0 = 32;
  COLDATA_copy1 = 64;
  COLDATA_copy2 = 128;
}

void Polyhedral_InitializeThread() {  // 89f7de
  static const uint8 kPolyThreadInit[13] = { 9, 0, 0x1f, 0, 0, 0, 0, 0, 0, 0x30, 0x1d, 0xf8, 9 };
  memset(kPolyThreadRam, 0, 256);
  thread_other_stack = 0x1f31;
  memcpy(&g_ram[0x1f32], kPolyThreadInit, 13);
}

void Module00_Intro() {  // 8cc120
  uint8 skip_at = enhanced_features0 & kFeatures0_SkipIntroOnKeypress ? 4 : 8;

  if (submodule_index >= skip_at && ((filtered_joypad_L & 0xc0 | filtered_joypad_H) & 0xd0)) {
    FadeMusicAndResetSRAMMirror();
    return;
  }
  switch (submodule_index) {
  case 0: Intro_Init(); break;
  case 1: Intro_Init_Continue(); break;
  case 10:
  case 2: Intro_InitializeTriforcePolyThread(); break;
  case 3:
  case 4:
  case 9:
  case 11: Intro_HandleAllTriforceAnimations(); break;
  case 5: IntroZeldaFadein(); break;
  case 6: Intro_SwordComingDown(); break;
  case 7: Intro_FadeInBg(); break;
  case 8: Intro_WaitPlayer(); break;
  }
}

void Intro_Init() {  // 8cc15d
  Intro_SetupScreen();
  INIDISP_copy = 15;
  subsubmodule_index = 0;
  flag_update_cgram_in_nmi++;
  submodule_index++;
  sound_effect_2 = 10;
  Intro_Init_Continue();
}

void Intro_Init_Continue() {  // 8cc170
  Intro_DisplayLogo();
  int t = subsubmodule_index++;
  if (t >= 11) {
    if (--INIDISP_copy)
      return;
    Intro_InitializeMemory_darken();
    return;
  }
  switch (t) {
  case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
    Intro_Clear1kbBlocksOfWRAM();
    break;
  case 8: Intro_LoadTextPointersAndPalettes(); break;
  case 9: LoadItemGFXIntoWRAM4BPPBuffer(); break;
  case 10:LoadFollowerGraphics(); break;
  }
}

void Intro_Clear1kbBlocksOfWRAM() {  // 8cc1a0
  uint16 i = R16;
  uint8 *dst = (uint8 *)&g_ram[0x2000];
  do {
    for (int j = 0; j < 15; j++)
      WORD(dst[i + j * 0x2000]) = 0;
  } while ((i -= 2) != R18);
  R16 = i;
  R18 = i - 0x400;
}

void Intro_InitializeMemory_darken() {  // 8cc1f5
  EnableForceBlank();
  EraseTileMaps_normal();
  main_tile_theme_index = 35;
  sprite_graphics_index = 125;
  aux_tile_theme_index = 81;
  misc_sprites_graphics_index = 8;
  LoadDefaultGraphics();
  InitializeTilesets();
  DecompressAnimatedDungeonTiles(0x5d);
  bg_tile_animation_countdown = 2;
  BYTE(overworld_screen_index) = 0;
  palette_main_indoors = 0;
  overworld_palette_aux3_bp7_lo = 0;
  R16 = 0;
  R18 = 0;
  darkening_or_lightening_screen = 2;
  palette_filter_countdown = 31;
  mosaic_target_level = 0;
  submodule_index++;
}

void IntroZeldaFadein() {  // 8cc25c
  Intro_HandleAllTriforceAnimations();
  if (!(frame_counter & 1))
    return;
  Palette_FadeIntroOneStep();
  if (BYTE(palette_filter_countdown) == 0) {
    subsubmodule_index = 42;
    submodule_index++;
    Intro_SetupSwordAndIntroFlash();
  } else if (BYTE(palette_filter_countdown) == 13) {
    TM_copy = 0x15;
    TS_copy = 0;
  }
}

void Intro_FadeInBg() {  // 8cc284
  Intro_PeriodicSwordAndIntroFlash();
  Intro_HandleAllTriforceAnimations();
  if (BYTE(palette_filter_countdown)) {
    if (frame_counter & 1)
      Palette_FadeIntro2();
  } else {
    if ((filtered_joypad_L & 0xc0 | filtered_joypad_H) & 0xd0)
      FadeMusicAndResetSRAMMirror();
    else {
      if (!--subsubmodule_index)
        submodule_index++;
    }
  }
}

void Intro_SwordComingDown() {  // 8cc2ae
  Intro_HandleAllTriforceAnimations();
  intro_did_run_step = 0;
  is_nmi_thread_active = 0;
  Intro_PeriodicSwordAndIntroFlash();
  if (!--subsubmodule_index) {
    submodule_index++;
    CGWSEL_copy = 2;
    CGADSUB_copy = 0x22;
    palette_filter_countdown = 31;
    TS_copy = 2;
  }
}

void Intro_WaitPlayer() {  // 8cc2d4
  Intro_HandleAllTriforceAnimations();
  intro_did_run_step = 0;
  is_nmi_thread_active = 0;
  Intro_PeriodicSwordAndIntroFlash();
  if (!--subsubmodule_index) {
    submodule_index++;
    main_module_index = 20;
    submodule_index = 0;
    BYTE(link_x_coord) = 0;
  }
}

void FadeMusicAndResetSRAMMirror() {  // 8cc2f0
  irq_flag = 255;
  TM_copy = 0x15;
  TS_copy = 0;
  player_is_indoors = 0;
  music_control = 0xf1;
  SetBackdropcolorBlack();

  memset(&link_y_coord, 0, 0x70);
  memset(save_dung_info, 0, 256 * 5);

  main_module_index = 1;
  death_var4 = 1;
  submodule_index = 0;
}

void Intro_InitializeTriforcePolyThread() {  // 8cc33c
  misc_sprites_graphics_index = 8;
  LoadCommonSprites();
  Intro_InitGfx_Helper();
  intro_sprite_isinited[0] = 1;
  intro_sprite_isinited[1] = 1;
  intro_sprite_isinited[2] = 1;
  intro_sprite_subtype[0] = 0;
  intro_sprite_subtype[1] = 0;
  intro_sprite_subtype[2] = 0;
  intro_sprite_isinited[4] = 1;
  intro_sprite_subtype[4] = 2;
  INIDISP_copy = 15;
  submodule_index++;
}

void Intro_InitGfx_Helper() {  // 8cc36f
  Polyhedral_InitializeThread();
  LoadTriforceSpritePalette();
  virq_trigger = 0x90;
  poly_config1 = 255;
  poly_base_x = 32;
  poly_base_y = 32;
  BYTE(poly_var1) = 32;
  poly_a = 0xA0;
  poly_b = 0x60;
  poly_config_color_mode = 1;
  poly_which_model = 1;
  is_nmi_thread_active = 1;
  intro_did_run_step = 1;
  memset(&intro_step_index, 0, 7 * 16);
}

void LoadTriforceSpritePalette() {  // 8cc3bd
  memcpy(main_palette_buffer + 0xd0, kPolyhedralPalette, 16);
  flag_update_cgram_in_nmi++;
}

void Intro_HandleAllTriforceAnimations() {  // 8cc404
  intro_frame_ctr++;
  Intro_AnimateTriforce();
  Scene_AnimateEverySprite();
}

void Scene_AnimateEverySprite() {  // 8cc412
  intro_sprite_alloc = 0x800;
  for (int k = 7; k >= 0; k--)
    Intro_AnimOneObj(k);
}

void Intro_AnimateTriforce() {  // 8cc435
  is_nmi_thread_active = 1;
  if (!intro_did_run_step) {
    Intro_RunStep();
    intro_did_run_step = 1;
  }
}

void Intro_RunStep() {  // 8cc448
  switch (intro_step_index) {
  case 0:
    if (++intro_step_timer == 64)
      intro_step_index++;
    poly_b += 5, poly_a += 3;
    break;
  case 1:
    if (poly_config1 < 2) {
      poly_config1 = 0;
      intro_step_index++;
      intro_step_timer = 64;
      return;
    }
    poly_config1 -= 2;
    poly_b += 5;
    poly_a += 3;
    if (poly_config1 < 225)
      submodule_index = 4;
    if (poly_config1 == 113)
      music_control = 1;
    break;
  case 2:
    if (!--intro_step_timer) {
      intro_step_index++;
    } else {
      poly_b += 5, poly_a += 3;
    }
    break;
  case 3:
    if (poly_b >= 250 && poly_a >= 252) {
      intro_step_index++;
      intro_step_timer = 32;
    } else {
      poly_b += 5, poly_a += 3;
    }
    break;
  case 4:
    poly_b = 0;
    poly_a = 0;
    if (!--intro_step_timer) {
      intro_step_index++;
      intro_sprite_isinited[5] = 1;
      intro_sprite_subtype[5] = 3;
      TM_copy = 0x10;
      TS_copy = 5;
      CGWSEL_copy = 2;
      CGADSUB_copy = 0x31;
      subsubmodule_index = 0;
      flag_update_cgram_in_nmi++;
      nmi_load_bg_from_vram = 3;
      submodule_index++;
    }
    break;
  }

}

void Intro_AnimOneObj(int k) {  // 8cc534
  switch (intro_sprite_isinited[k]) {
  case 0:
    break;
  case 1:
    switch (intro_sprite_subtype[k]) {
    case 0: Intro_SpriteType_A_0(k); break;
    case 1: EXIT_0CCA90(k); break;
    case 2: InitializeSceneSprite_Copyright(k); break;
    case 3: InitializeSceneSprite_Sparkle(k); break;
    case 4:
    case 5:
    case 6: InitializeSceneSprite_TriforceRoomTriangle(k); break;
    case 7: InitializeSceneSprite_CreditsTriangle(k); break;
    }
    break;
  case 2:
    switch (intro_sprite_subtype[k]) {
    case 0: Intro_SpriteType_B_0(k); break;
    case 1: EXIT_0CCA90(k); break;
    case 2: AnimateSceneSprite_Copyright(k); break;
    case 3: AnimateSceneSprite_Sparkle(k); break;
    case 4:
    case 5:
    case 6: Intro_SpriteType_B_456(k); break;
    case 7: AnimateSceneSprite_CreditsTriangle(k); break;
    }
    break;
  }
}

void Intro_SpriteType_A_0(int k) {  // 8cc57e
  static const int16 kIntroSprite0_X[3] = { -38, 95, 230 };
  static const int16 kIntroSprite0_Y[3] = { 200, -67, 200 };
  intro_x_lo[k] = kIntroSprite0_X[k];
  intro_x_hi[k] = kIntroSprite0_X[k] >> 8;
  intro_y_lo[k] = kIntroSprite0_Y[k];
  intro_y_hi[k] = kIntroSprite0_Y[k] >> 8;
  intro_x_vel[k] = kIntroSprite0_Xvel[k];
  intro_y_vel[k] = kIntroSprite0_Yvel[k];
  intro_sprite_isinited[k]++;
}

void Intro_SpriteType_B_0(int k) {  // 8cc5b1
  static const uint8 kIntroSprite0_XLimit[3] = { 75, 95, 117 };
  static const uint8 kIntroSprite0_YLimit[3] = { 88, 48, 88 };

  AnimateSceneSprite_DrawTriangle(k);
  AnimateSceneSprite_MoveTriangle(k);
  if (intro_step_index != 5) {
    if (!(intro_frame_ctr & 31)) {
      intro_x_vel[k] += kIntroSprite0_Xvel[k];
      intro_y_vel[k] += kIntroSprite0_Yvel[k];
    }
    if (intro_x_lo[k] == kIntroSprite0_XLimit[k])
      intro_x_vel[k] = 0;
    if (intro_y_lo[k] == kIntroSprite0_YLimit[k])
      intro_y_vel[k] = 0;
  } else {
    intro_x_vel[k] = 0;
    intro_y_vel[k] = 0;
  }
}

void AnimateSceneSprite_DrawTriangle(int k) {  // 8cc70f
  static const IntroSpriteEnt kIntroSprite0_Left_Ents[16] = {
    { 0,  0, 0x80, 0x1b, 2},
    {16,  0, 0x82, 0x1b, 2},
    {32,  0, 0x84, 0x1b, 2},
    {48,  0, 0x86, 0x1b, 2},
    { 0, 16, 0xa0, 0x1b, 2},
    {16, 16, 0xa2, 0x1b, 2},
    {32, 16, 0xa4, 0x1b, 2},
    {48, 16, 0xa6, 0x1b, 2},
    { 0, 32, 0x88, 0x1b, 2},
    {16, 32, 0x8a, 0x1b, 2},
    {32, 32, 0x8c, 0x1b, 2},
    {48, 32, 0x8e, 0x1b, 2},
    { 0, 48, 0xa8, 0x1b, 2},
    {16, 48, 0xaa, 0x1b, 2},
    {32, 48, 0xac, 0x1b, 2},
    {48, 48, 0xae, 0x1b, 2},
  };
  static const IntroSpriteEnt kIntroSprite0_Right_Ents[16] = {
    {48,  0, 0x80, 0x5b, 2},
    {32,  0, 0x82, 0x5b, 2},
    {16,  0, 0x84, 0x5b, 2},
    { 0,  0, 0x86, 0x5b, 2},
    {48, 16, 0xa0, 0x5b, 2},
    {32, 16, 0xa2, 0x5b, 2},
    {16, 16, 0xa4, 0x5b, 2},
    { 0, 16, 0xa6, 0x5b, 2},
    {48, 32, 0x88, 0x5b, 2},
    {32, 32, 0x8a, 0x5b, 2},
    {16, 32, 0x8c, 0x5b, 2},
    { 0, 32, 0x8e, 0x5b, 2},
    {48, 48, 0xa8, 0x5b, 2},
    {32, 48, 0xaa, 0x5b, 2},
    {16, 48, 0xac, 0x5b, 2},
    { 0, 48, 0xae, 0x5b, 2},
  };
  AnimateSceneSprite_AddObjectsToOamBuffer(k, k == 2 ? kIntroSprite0_Right_Ents : kIntroSprite0_Left_Ents, 16);
}

void Intro_CopySpriteType4ToOam(int k) {  // 8cc82f
  static const IntroSpriteEnt kIntroTriforceOam_Left[16] = {
    { 0,  0, 0x80, 0x2b, 2},
    {16,  0, 0x82, 0x2b, 2},
    {32,  0, 0x84, 0x2b, 2},
    {48,  0, 0x86, 0x2b, 2},
    { 0, 16, 0xa0, 0x2b, 2},
    {16, 16, 0xa2, 0x2b, 2},
    {32, 16, 0xa4, 0x2b, 2},
    {48, 16, 0xa6, 0x2b, 2},
    { 0, 32, 0x88, 0x2b, 2},
    {16, 32, 0x8a, 0x2b, 2},
    {32, 32, 0x8c, 0x2b, 2},
    {48, 32, 0x8e, 0x2b, 2},
    { 0, 48, 0xa8, 0x2b, 2},
    {16, 48, 0xaa, 0x2b, 2},
    {32, 48, 0xac, 0x2b, 2},
    {48, 48, 0xae, 0x2b, 2},
  };
  static const IntroSpriteEnt kIntroTriforceOam_Right[16] = {
    {48,  0, 0x80, 0x6b, 2},
    {32,  0, 0x82, 0x6b, 2},
    {16,  0, 0x84, 0x6b, 2},
    { 0,  0, 0x86, 0x6b, 2},
    {48, 16, 0xa0, 0x6b, 2},
    {32, 16, 0xa2, 0x6b, 2},
    {16, 16, 0xa4, 0x6b, 2},
    { 0, 16, 0xa6, 0x6b, 2},
    {48, 32, 0x88, 0x6b, 2},
    {32, 32, 0x8a, 0x6b, 2},
    {16, 32, 0x8c, 0x6b, 2},
    { 0, 32, 0x8e, 0x6b, 2},
    {48, 48, 0xa8, 0x6b, 2},
    {32, 48, 0xaa, 0x6b, 2},
    {16, 48, 0xac, 0x6b, 2},
    { 0, 48, 0xae, 0x6b, 2},
  };
  AnimateSceneSprite_AddObjectsToOamBuffer(k, k == 2 ? kIntroTriforceOam_Right : kIntroTriforceOam_Left, 16);
}

void EXIT_0CCA90(int k) {  // 8cc84f
  // empty
}

void InitializeSceneSprite_Copyright(int k) {  // 8cc850
  intro_x_lo[k] = 76;
  intro_x_hi[k] = 0;
  intro_y_lo[k] = 184;
  intro_y_hi[k] = 0;
  intro_sprite_isinited[k]++;
}

void AnimateSceneSprite_Copyright(int k) {  // 8cc864
  static const IntroSpriteEnt kIntroSprite2_Ents[13] = {
    { 0, 0, 0x40, 0x0a, 0},
    { 8, 0, 0x41, 0x0a, 0},
    {16, 0, 0x42, 0x0a, 0},
    {24, 0, 0x68, 0x0a, 0},
    {32, 0, 0x41, 0x0a, 0},
    {40, 0, 0x42, 0x0a, 0},
    {48, 0, 0x43, 0x0a, 0},
    {56, 0, 0x44, 0x0a, 0},
    {64, 0, 0x50, 0x0a, 0},
    {72, 0, 0x51, 0x0a, 0},
    {80, 0, 0x52, 0x0a, 0},
    {88, 0, 0x53, 0x0a, 0},
    {96, 0, 0x54, 0x0a, 0},
  };
  AnimateSceneSprite_AddObjectsToOamBuffer(k, kIntroSprite2_Ents, 13);
}

void InitializeSceneSprite_Sparkle(int k) {  // 8cc8e2
  int j = intro_frame_ctr >> 5 & 3;
  intro_x_lo[k] = kIntroSprite3_X[j];
  intro_x_hi[k] = 0;
  intro_y_lo[k] = kIntroSprite3_Y[j];
  intro_y_hi[k] = 0;
  intro_sprite_isinited[k]++;
}

void AnimateSceneSprite_Sparkle(int k) {  // 8cc90d
  static const IntroSpriteEnt kIntroSprite3_Ents[4] = {
    { 0,  0, 0x80, 0x34, 0},
    { 0,  0, 0xb7, 0x34, 0},
    {-4, -3, 0x64, 0x38, 2},
    {-4, -3, 0x62, 0x34, 2},
  };
  if (intro_sprite_state[k] < 4)
    AnimateSceneSprite_AddObjectsToOamBuffer(k, kIntroSprite3_Ents + intro_sprite_state[k], 1);

  intro_sprite_state[k] = kIntroSprite3_State[intro_frame_ctr >> 2 & 7];
  int j = intro_frame_ctr >> 5 & 3;
  intro_x_lo[k] = kIntroSprite3_X[j];
  intro_y_lo[k] = kIntroSprite3_Y[j];
}

void AnimateSceneSprite_AddObjectsToOamBuffer(int k, const IntroSpriteEnt *src, int num) {  // 8cc972
  uint16 x = intro_x_hi[k] << 8 | intro_x_lo[k];
  uint16 y = intro_y_hi[k] << 8 | intro_y_lo[k];
  OamEnt *oam = (OamEnt *)&g_ram[intro_sprite_alloc];
  intro_sprite_alloc += num * 4;
  do {
    SetOamHelper0(oam, x + src->x, y + src->y, src->charnum, src->flags, src->ext);
  } while (oam++, src++, --num);
}

void AnimateSceneSprite_MoveTriangle(int k) {  // 8cc9f1
  if (intro_x_vel[k] != 0) {
    uint32 t = intro_x_subpixel[k] + (intro_x_lo[k] << 8) + (intro_x_hi[k] << 16) + ((int8)intro_x_vel[k] << 4);
    intro_x_subpixel[k] = t, intro_x_lo[k] = t >> 8, intro_x_hi[k] = t >> 16;
  }
  if (intro_y_vel[k] != 0) {
    uint32 t = intro_y_subpixel[k] + (intro_y_lo[k] << 8) + (intro_y_hi[k] << 16) + ((int8)intro_y_vel[k] << 4);
    intro_y_subpixel[k] = t, intro_y_lo[k] = t >> 8, intro_y_hi[k] = t >> 16;
  }
}

void TriforceRoom_PrepGFXSlotForPoly() {  // 8cca54
  misc_sprites_graphics_index = 8;
  LoadCommonSprites();
  Intro_InitGfx_Helper();
  intro_sprite_isinited[0] = 1;
  intro_sprite_isinited[1] = 1;
  intro_sprite_isinited[2] = 1;
  intro_sprite_subtype[0] = 4;
  intro_sprite_subtype[1] = 5;
  intro_sprite_subtype[2] = 6;
  INIDISP_copy = 15;
  submodule_index++;
}

void Credits_InitializePolyhedral() {  // 8cca81
  misc_sprites_graphics_index = 8;
  LoadCommonSprites();
  Intro_InitGfx_Helper();
  poly_config1 = 0;
  intro_sprite_isinited[0] = 1;
  intro_sprite_isinited[1] = 1;
  intro_sprite_isinited[2] = 1;
  intro_sprite_subtype[0] = 7;
  intro_sprite_subtype[1] = 7;
  intro_sprite_subtype[2] = 7;
  INIDISP_copy = 15;
  submodule_index++;
}

void AdvancePolyhedral() {  // 8ccab1
  TriforceRoom_HandlePoly();
  Scene_AnimateEverySprite();
}

void TriforceRoom_HandlePoly() {  // 8ccabc
  is_nmi_thread_active = 1;
  intro_want_double_ret = 1;
  if (intro_did_run_step)
    return;
  switch (intro_step_index) {
  case 0:
    poly_config1 -= 2;
    if (poly_config1 < 2) {
      poly_config1 = 0;
      intro_step_index++;
      subsubmodule_index++;
    }
    // fall through
  case 1:
    if (subsubmodule_index >= 10) {
      intro_step_index++;
      intro_y_vel[1] = 5;
    }
    poly_b += 2, poly_a += 1;
    break;
  case 2:
    triforce_ctr = 0x1c0;
    if (poly_config1 < 128) {
      poly_config1 += 1;
    } else {
      if ((poly_b - 10 & 0x7f) >= 92 &&
          (uint8)(poly_a - 11) >= 220) {
        poly_a = 0;
        poly_b = 0;
        subsubmodule_index++;
        intro_step_index++;
        sound_effect_1 = 44;
        main_palette_buffer[0xd7] = 0x7fff;
        flag_update_cgram_in_nmi++;
        intro_step_timer = 6;
        break;
      }
    }
    poly_b += 5, poly_a += 3;
    break;
  case 3:
    if (!--intro_step_timer) {
      main_palette_buffer[0xd7] = kPolyhedralPalette[7];
      flag_update_cgram_in_nmi++;
      intro_step_index++;
    }
    break;
  case 4:
    break;
  }
  intro_did_run_step = 1;
  intro_want_double_ret = 0;
  intro_frame_ctr++;
}

void Credits_AnimateTheTriangles() {  // 8ccba2
  intro_frame_ctr++;
  is_nmi_thread_active = 1;
  if (!intro_did_run_step) {
    poly_b += 3;
    poly_a += 1;
    intro_did_run_step = 1;
  }
  Scene_AnimateEverySprite();
}

void InitializeSceneSprite_TriforceRoomTriangle(int k) {  // 8ccbe8
  static const int16 kIntroTriforce_X[3] = { 0x4e, 0x5f, 0x72 };
  static const int16 kIntroTriforce_Y[3] = { 0x9c, 0x9c, 0x9c };
  static const int8 kIntroTriforce_Xvel[3] = { -2, 0, 2 };
  static const int8 kIntroTriforce_Yvel[3] = { 4, -4, 4 };

  intro_x_lo[k] = kIntroTriforce_X[k];
  intro_x_hi[k] = 0;
  intro_y_lo[k] = kIntroTriforce_Y[k];
  intro_y_hi[k] = 0;
  intro_x_vel[k] = kIntroTriforce_Xvel[k];
  intro_y_vel[k] = kIntroTriforce_Yvel[k];
  intro_sprite_isinited[k]++;
}

void Intro_SpriteType_B_456(int k) {  // 8ccc13
  static const int8 kTriforce_Xacc[3] = { -1, 0, 1 };
  static const int8 kTriforce_Yacc[3] = { -1, -1, -1 };
  static const uint8 kTriforce_Yfinal2[3] = { 0x72, 0x66, 0x72 };

  Intro_CopySpriteType4ToOam(k);
  if (intro_want_double_ret)
    return;
  AnimateSceneSprite_MoveTriangle(k);
  switch (intro_step_index) {
  case 0:
    if (!(intro_frame_ctr & 7))
      intro_x_vel[k] += kTriforce_Xacc[k];
    if (!(intro_frame_ctr & 3))
      intro_y_vel[k] += kTriforce_Yacc[k];
    break;
  case 1:
    intro_x_vel[k] = 0;
    intro_y_vel[k] = 0;
    break;
  case 2:
    if (!(intro_frame_ctr & 3))
      AnimateTriforceRoomTriangle_HandleContracting(k);
    if (kTriforce_Xfinal[k] == intro_x_lo[k])
      intro_x_vel[k] = 0;
    if (kTriforce_Yfinal[k] == intro_y_lo[k])
      intro_y_vel[k] = 0;
    break;
  case 3:
  case 4:
    if (triforce_ctr == 0) {
      intro_y_lo[k] = kTriforce_Yfinal2[k];
    } else {
      triforce_ctr -= 1;
    }
    break;
  }
}

void AnimateTriforceRoomTriangle_HandleContracting(int k) {  // 8cccb0
  uint8 new_vel = intro_x_vel[k] + (intro_x_lo[k] <= kTriforce_Xfinal[k] ? 1 : -1);
  intro_x_vel[k] = (new_vel == 0x11) ? 0x10 : (new_vel == 0xef) ? 0xf0 : new_vel;
  new_vel = intro_y_vel[k] + (intro_y_lo[k] <= kTriforce_Yfinal[k] ? 1 : -1);
  intro_y_vel[k] = (new_vel == 0x11) ? 0x10 : (new_vel == 0xef) ? 0xf0 : new_vel;
}

void InitializeSceneSprite_CreditsTriangle(int k) {  // 8ccd19
  static const uint8 kIntroSprite7_X[3] = { 0x29, 0x5f, 0x97 };
  static const uint8 kIntroSprite7_Y[3] = { 0x70, 0x20, 0x70 };
  intro_x_lo[k] = kIntroSprite7_X[k];
  intro_x_hi[k] = 0;
  intro_y_lo[k] = kIntroSprite7_Y[k];
  intro_y_hi[k] = 0;
  intro_sprite_isinited[k]++;
}

void AnimateSceneSprite_CreditsTriangle(int k) {  // 8ccd3e
  static const int8 kIntroSprite7_XAcc[3] = { -1, 0, 1 };
  static const int8 kIntroSprite7_YAcc[3] = { 1, -1, 1 };

  LoadTriforceSpritePalette();
  Intro_CopySpriteType4ToOam(k);
  AnimateSceneSprite_MoveTriangle(k);
  if (submodule_index != 36) {
    intro_sprite_state[k] = 0;
    return;
  }
  if (intro_sprite_state[k] != 80) {
    intro_sprite_state[k]++;
    intro_x_vel[k] += kIntroSprite7_XAcc[k];
    intro_y_vel[k] += kIntroSprite7_YAcc[k];
  }
}

void Intro_DisplayLogo() {  // 8ced82
  static const uint8 kIntroLogo_X[4] = { 0x60, 0x70, 0x80, 0x88 };
  static const uint8 kIntroLogo_Tile[4] = { 0x69, 0x6b, 0x6d, 0x6e };
  for (int i = 0; i < 4; i++)
    SetOamPlain(&oam_buf[i], kIntroLogo_X[i], 0x68, kIntroLogo_Tile[i], 0x32, 2);
}

void Intro_SetupSwordAndIntroFlash() {  // 8cfe45
  intro_sword_19 = 7;
  intro_sword_20 = 0;
  intro_sword_21 = 0;
  intro_sword_ypos = -130;

  Intro_PeriodicSwordAndIntroFlash();
}

void Intro_PeriodicSwordAndIntroFlash() {  // 8cfe56
  if (intro_sword_18)
    intro_sword_18--;
  SetBackdropcolorBlack();
  if (intro_times_pal_flash) {
    if ((intro_times_pal_flash & 3) != 0) {
      (&COLDATA_copy0)[intro_sword_24] |= 0x1f;
      intro_sword_24 = (intro_sword_24 == 2) ? 0 : intro_sword_24 + 1;
    }
    intro_times_pal_flash--;
  }
  OamEnt *oam = oam_buf + 0x52;
  for (int j = 9; j >= 0; j--) {
    static const uint8 kIntroSword_Char[10] = { 0, 2, 0x20, 0x22, 4, 6, 8, 0xa, 0xc, 0xe };
    static const uint8 kIntroSword_X[10] = { 0x40, 0x40, 0x30, 0x50, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 };
    static const uint16 kIntroSword_Y[10] = { 0x10, 0x20, 0x28, 0x28, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80 };
    uint16 y = intro_sword_ypos + kIntroSword_Y[j];
    SetOamPlain(&oam[j], kIntroSword_X[j], ((y & 0xff00) ? 0xf8 : y) - 8, kIntroSword_Char[j], 0x21, 2);
  }

  if (intro_sword_ypos != 30) {
    if (intro_sword_ypos == 0xffbe) {
      sound_effect_1 = 1;
    } else if (intro_sword_ypos == 14) {
      WORD(intro_sword_24) = 0;
      intro_times_pal_flash = 0x20;
      sound_effect_1 = 0x2c;
    }
    intro_sword_ypos += 16;
  }

  switch (intro_sword_20 >> 1) {
  case 0:
    if (!intro_times_pal_flash && intro_sword_ypos == 30)
      intro_sword_20 += 2;
    break;
  case 1: {
    static const uint8 kSwordSparkle_Tab[8] = { 4, 4, 6, 6, 6, 4, 4 };

    if (!intro_sword_18) {
      intro_sword_19 -= 1;
      if (sign8(intro_sword_19)) {
        intro_sword_19 = 0;
        intro_sword_18 = 2;
        intro_sword_20 += 2;
        return;
      }
      intro_sword_18 = kSwordSparkle_Tab[intro_sword_19];
    }
    static const uint8 kSwordSparkle_Char[7] = { 0x28, 0x37, 0x27, 0x36, 0x27, 0x37, 0x28 };
    SetOamPlain(&oam_buf[0x50], 0x44, 0x43, kSwordSparkle_Char[intro_sword_19], 0x25, 0);
    break;
  }
  case 2: {
    static const uint8 kIntroSwordSparkle_Char[8] = { 0x26, 0x20, 0x24, 0x34, 0x25, 0x20, 0x35, 0x20 };
    int k = intro_sword_19;
    if (k >= 7)
      return;
    uint8 y = (intro_sword_21 < 0x50 ? intro_sword_21 : 0x4f) + intro_sword_ypos + 0x31;
    SetOamPlain(&oam_buf[0x50], 0x42, y + 0, kIntroSwordSparkle_Char[k + 0], 0x23, 0);
    SetOamPlain(&oam_buf[0x51], 0x42, y + 8, kIntroSwordSparkle_Char[k + 1], 0x23, 0);
    if (intro_sword_18 == 0) {
      intro_sword_21 += 4;
      if (intro_sword_21 == 0x4 || intro_sword_21 == 0x48 || intro_sword_21 == 0x4c || intro_sword_21 == 0x58)
        intro_sword_19 += 2;
    }
    break;
  }
  }
}

void Module1A_Credits() {  // 8e986e
  oam_region_base[0] = 0x30;
  oam_region_base[1] = 0x1d0;
  oam_region_base[2] = 0x0;

  kEndSequence_Funcs[submodule_index]();
}

void Credits_LoadNextScene_Overworld() {  // 8e9889
  kEndSequence0_Funcs[subsubmodule_index]();
  Credits_AddEndingSequenceText();
}

void Credits_LoadNextScene_Dungeon() {  // 8e9891
  Credits_LoadScene_Dungeon();
  Credits_AddEndingSequenceText();
}

void Credits_PrepAndLoadSprites() {  // 8e98b9
  for (int k = 15; k >= 0; k--) {
    SpritePrep_ResetProperties(k);
    sprite_state[k] = 0;
    sprite_flags5[k] = 0;
    sprite_defl_bits[k] = 0;
  }
  int k = submodule_index >> 1;
  switch (k) {
init_sprites_0:
  case 0: case 4: case 5: case 8: case 13: {
    int idx = kEndingSprites_Idx[k];
    int num = kEndingSprites_Idx[k + 1] - idx;
    const uint16 *px = kEndingSprites_X + idx;
    const uint16 *py = kEndingSprites_Y + idx;
    for (k = num - 1; k >= 0; k--) {
      sprcoll_x_size = sprcoll_y_size = 0xffff;
      uint16 x = (swap16(overworld_area_index << 1) & 0xf00) + px[k];
      uint16 y = (swap16(overworld_area_index >> 2) & 0xe00) + py[k];
      Sprite_SetX(k, x);
      Sprite_SetY(k, y);
    }
    break;
  }
init_sprites_1:
  case 1: {
    int idx = kEndingSprites_Idx[k];
    int num = kEndingSprites_Idx[k + 1] - idx;
    const uint16 *px = kEndingSprites_X + idx;
    const uint16 *py = kEndingSprites_Y + idx;
    byte_7E0FB1 = dungeon_room_index2 >> 3 & 254;
    byte_7E0FB0 = (dungeon_room_index2 & 15) << 1;
    for (k = num - 1; k >= 0; k--) {
      sprcoll_x_size = sprcoll_y_size = 0xffff;
      uint16 x = byte_7E0FB0 * 256 + px[k];
      uint16 y = byte_7E0FB1 * 256 + py[k];
      Sprite_SetX(k, x);
      Sprite_SetY(k, y);
    }
    break;
  }
  case 2:
    sprite_y_vel[6] = -16;
    goto init_sprites_0;
  case 3:
    sprite_A[5] = 22;
    sprite_y_vel[0] = -16;
    sprite_y_vel[1] = 16;
    sprite_head_dir[1] = 1;
    for (int j = 2; j >= 0; j--) {
      sprite_type[2 + j] = 0x57;
      sprite_oam_flags[2 + j] = 0x31;
    }
    goto init_sprites_0;
  case 6:
    sprite_delay_main[0] = 255;
    sprite_delay_main[1] = 255;
    sprite_delay_main[2] = 255;
    goto init_sprites_0;
  case 7:
    sprite_delay_main[1] = 255;
    goto init_sprites_0;
  case 9:
    for (int j = 4; j >= 0; j--) {
      sprite_delay_main[j] = j * 19;
      sprite_state[j] = 0;
    }
    sprite_type[5] = 0x2e;
    for (int j = 1; j >= 0; j--) {
      sprite_type[7 + j] = 0x9f;
      sprite_type[9 + j] = 0xa0;
      sprite_flags2[7 + j] = 1;
      sprite_flags2[9 + j] = 2;
      sprite_flags3[7 + j] = 0x10;
      sprite_flags3[9 + j] = 0x10;
    }
    goto init_sprites_0;
  case 10:
    sprite_delay_main[1] = 0x10;
    sprite_delay_main[2] = 0x20;
    sprite_oam_flags[3] = 8;
    sprite_oam_flags[4] = 8;
    goto init_sprites_1;
  case 11:
    sprite_oam_flags[4] = 0x79;
    sprite_oam_flags[5] = 0x39;
    sprite_D[1] = 1;
    sprite_A[1] = 4;
    goto init_sprites_1;
  case 12:
    for (int j = 1; j >= 0; j--) {
      sprite_oam_flags[j + 3] = 0x39;
      sprite_type[j + 3] = 0xb;
      sprite_flags3[j + 3] = 0x10;
      sprite_flags2[j + 3] = 1;
    }
    sprite_type[5] = 0x2a;
    sprite_type[6] = 0x79;
    sprite_ai_state[6] = 1;
    sprite_z[6] = 5;
    goto init_sprites_0;
  case 14:
    sprite_y_vel[5] = -16;
    sprite_y_vel[6] = 16;
    sprite_head_dir[6] = 1;
    sprite_A[0] = 8;
    for (int j = 3; j >= 0; j--)
      sprite_y_vel[1 + j] = 4;
    goto init_sprites_0;
  case 15:
    sprite_C[4] = 2;
    sprite_y_vel[5] = 8;
    sprite_delay_main[1] = 0x13;
    sprite_delay_main[4] = 0x40;
    goto init_sprites_0;
  }
}

void Credits_ScrollScene_Overworld() {  // 8e9958

  for (int k = 15; k >= 0; k--)
    if (sprite_delay_main[k])
      sprite_delay_main[k]--;

  int i = submodule_index >> 1;

  link_x_vel = link_y_vel = 0;
  if (R16 >= 0x40 && !(R16 & 1)) {
    if (BG2VOFS_copy2 != kEnding1_TargetScrollY[i])
      link_y_vel = kEnding1_Yvel[i];
    if (BG2HOFS_copy2 != kEnding1_TargetScrollX[i])
      link_x_vel = kEnding1_Xvel[i];
  }

  Credits_OperateScrollingAndTileMap();
  Credits_HandleSceneFade();
}

void Credits_ScrollScene_Dungeon() {  // 8e99c5
  for (int k = 15; k >= 0; k--)
    if (sprite_delay_main[k])
      sprite_delay_main[k]--;

  int i = submodule_index >> 1;
  if (R16 >= 0x40 && !(R16 & 1)) {
    if (BG2VOFS_copy2 != kEnding1_TargetScrollY[i])
      BG2VOFS_copy2 += kEnding1_Yvel[i];
    if (BG2HOFS_copy2 != kEnding1_TargetScrollX[i])
      BG2HOFS_copy2 += kEnding1_Xvel[i];
  }
  Credits_HandleSceneFade();
}

void Credits_HandleSceneFade() {  // 8e9a2a
  static const uint16 kEnding1_3_Tab0[16] = { 0x300, 0x280, 0x250, 0x2e0, 0x280, 0x250, 0x2c0, 0x2c0, 0x250, 0x250, 0x280, 0x250, 0x480, 0x400, 0x250, 0x500 };
  static const uint8 kEndSequence_Case0_Tab1[12] = { 0x1e, 0x20, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x16, 0x16, 0x16, 0x16 };
  static const uint8 kEndSequence_Case0_Tab0[12] = { 6, 3, 2, 2, 2, 2, 2, 2, 6, 6, 6, 6 };
  static const uint8 kEndSequence_Case0_OamFlags[12] = { 0x3b, 0x31, 0x3d, 0x3f, 0x39, 0x3b, 0x37, 0x3d, 0x39, 0x37, 0x37, 0x39 };
  int i = submodule_index >> 1, j, k;

  switch (i) {
  case 0:
    for (int k = 11; k != 7; k--) {
      sprite_oam_flags[k] = kEndSequence_Case0_OamFlags[k];
      Credits_SpriteDraw_Single(k, kEndSequence_Case0_Tab0[k], kEndSequence_Case0_Tab1[k]);
    }
    for (int k = 7; k != 1; k--) {
      sprite_oam_flags[k] = kEndSequence_Case0_OamFlags[k] | (frame_counter << 2 & 0x40);
      Credits_SpriteDraw_Single(k, kEndSequence_Case0_Tab0[k], kEndSequence_Case0_Tab1[k]);
    }
    for (int k = 1; k >= 0; k--) {
      sprite_oam_flags[k] = kEndSequence_Case0_OamFlags[k];
      Credits_SpriteDraw_Single(k, kEndSequence_Case0_Tab0[k], kEndSequence_Case0_Tab1[k]);
    }
    break;
  case 1:
    Credits_SpriteDraw_Single(0, 3, 12);
    Credits_SpriteDraw_DrawShadow(0);
    k = 1;
    sprite_type[k] = 0x73;
    sprite_oam_flags[k] = 0x27;
    sprite_E[k] = 2;
    Credits_SpriteDraw_PreexistingSpriteDraw(k, 16);
    break;
  case 2: {
    static const uint8 kEnding_Case2_Tab0[2] = { 0x20, 0x40 };
    static const int8 kEnding_Case2_Tab1[2] = { 16, -16 };
    static const int8 kEnding_Case2_Tab2[5] = { 0x28, 0x2a, 0x2c, 0x2e, 0x2c };
    static const int8 kEnding_Case2_Tab3[5] = { 3, 3, 3, 3, 3 };
    static const uint8 kEnding_Case2_Delay[2] = { 0x30, 0x10 };

    BYTE(flag_travel_bird) = kEnding_Case2_Tab0[frame_counter >> 2 & 1];
    k = 6;
    j = sprite_x_vel[k] >> 7 & 1;
    sprite_oam_flags[k] = (sprite_x_vel[k] + kEnding_Case2_Tab1[j]) >> 1 & 0x40 | 0x32;
    Credits_SpriteDraw_Single(k, 2, 0x24);
    Credits_SpriteDraw_CirclingBirds(k);
    k -= 1;
    sprite_oam_flags[k] = 0x31;
    if (!sprite_delay_main[k]) {
      j = sprite_A[k];
      sprite_A[k] ^= 1;
      sprite_delay_main[k] = kEnding_Case2_Delay[j];
      sprite_graphics[k] = sprite_graphics[k] + 1 & 3;
    }
    Credits_SpriteDraw_Single(k, 2, 0x26);
    k -= 1;
    do {
      if (!(frame_counter & 15))
        sprite_graphics[k] ^= 1;
      sprite_oam_flags[k] = 0x31;
      Credits_SpriteDraw_Single(k, kEnding_Case2_Tab3[k], kEnding_Case2_Tab2[k]);
      EndSequence_DrawShadow2(k);
    } while (--k >= 0);
    break;
  }
  case 3: {
    static const uint8 kEnding_Case3_Gfx[4] = { 1, 2, 3, 2 };
    for (k = 0; k < 5; k++) {
      if (k < 2) {
        sprite_type[k] = 1;
        sprite_oam_flags[k] = 0xb;
        Credits_SpriteDraw_SetShadowProp(k, 2);
        sprite_z[k] = 48;
        j = (frame_counter + (k ? 0x5f : 0x7d)) >> 2 & 3;
        sprite_graphics[k] = kEnding_Case3_Gfx[j];
        Credits_SpriteDraw_CirclingBirds(k);
        Credits_SpriteDraw_PreexistingSpriteDraw(k, 12);
      } else {
        Credits_SpriteDraw_PreexistingSpriteDraw(k, 16);
      }
    }
    Credits_SpriteDraw_Single(k, 2, 0x38);
    Ending_Func2(k, 0x30);
    k++;
    Credits_SpriteDraw_Single(k, 3, 0x3a);
    break;
  }
  case 4: {
    static const uint8 kEnding_Case4_Tab1[2] = { 0x30, 0x32 };
    static const uint8 kEnding_Case4_Tab0[2] = { 2, 2 };
    static const uint8 kEnding_Case4_Ctr[2] = { 0x20, 0 };
    static const int8 kEnding_Case4_XYvel[10] = { 0, -12, -16, -12, 0, 12, 16, 12, 0, -12 };
    static const uint8 kEnding_Case4_DelayVel[24] = {
      0x3b, 0x14, 0x1e, 0x1d, 0x2c, 0x2b, 0x42, 0x20, 0x27, 0x28, 0x2e, 0x38, 0x3a, 0x4c, 0x32, 0x44,
      0x2e, 0x2f, 0x1e, 0x28, 0x47, 0x35, 0x32, 0x30,
    };
    k = 2;
    sprite_oam_flags[k] = 0x35;
    Credits_SpriteDraw_Single(k, 1, 0x3c);
    k--;
    do {
      sprite_oam_flags[k] = (sprite_x_vel[k] - 1) >> 1 & 0x40 ^ 0x71;
      sprite_graphics[k] = frame_counter >> 3 & 1;
      if (R16 >= kEnding_Case4_Ctr[k] && !sprite_delay_main[k]) {
        uint8 a = kEnding_Case4_DelayVel[sprite_A[k]];
        sprite_delay_main[k] = a & 0xf8;
        sprite_y_vel[k] = kEnding_Case4_XYvel[(a & 7) + 2];
        sprite_x_vel[k] = kEnding_Case4_XYvel[a & 7];
        sprite_A[k]++;
      }
      Credits_SpriteDraw_Single(k, kEnding_Case4_Tab0[k], kEnding_Case4_Tab1[k]);
      EndSequence_DrawShadow2(k);
      Sprite_MoveXY(k);
    } while (--k >= 0);
    break;
  }
  case 5: {
    static const uint8 kEnding_Case5_Tab0[2] = { 0, 4 };
    static const uint16 kEnding_Case5_Tab1[2] = { 0xa, 0x224 };
    static const uint8 kEnding_Case5_Tab2[2] = { 10, 14 };
    if (R16 == 0x200)
      sound_effect_1 = 1;
    else if (R16 == 0x208)
      sound_effect_1 = 0x2c;
    if ((uint16)(R16 - 0x208) < 0x30)
      Credits_SpriteDraw_AddSparkle(2, 10, R16 - 0x208); // wtf x,y
    k = 3;
    if (R16 >= 0x200)
      sprite_graphics[k] = 1;
    sprite_oam_flags[k] = 0x31;
    Credits_SpriteDraw_Single(k, 4, 8);
    EndSequence_DrawShadow2(k);
    int j = sprite_graphics[k];
    sprite_graphics[--k] = j;
    link_dma_var3 = 0;
    link_dma_var4 = kEnding_Case5_Tab0[j];
    sprite_oam_flags[k] = 0x30;

    link_dma_graphics_index = kEnding_Case5_Tab1[j];
    Credits_SpriteDraw_Single(k, 5, kEnding_Case5_Tab2[j]);
    EndSequence_DrawShadow2(k);
    break;
  }
  case 6: {
    static const uint8 kEnding_Case6_SprType[3] = { 0x52, 0x55, 0x55 };
    static const uint8 kEnding_Case6_OamSize[3] = { 0x20, 8, 8 };
    static const uint8 kEnding_Case6_State[3] = { 3, 1, 1 };
    static const uint8 kEnding_Case6_Gfx[6] = { 0, 5, 5, 1, 6, 6 };

    int idx = kEndingSprites_Idx[i];
    int num = kEndingSprites_Idx[i + 1] - idx;

    for (int k = num - 1; k >= 0; k--) {
      cur_object_index = k;
      sprite_type[k] = kEnding_Case6_SprType[k];
      Oam_AllocateFromRegionA(kEnding_Case6_OamSize[k]);
      sprite_ai_state[k] = kEnding_Case6_State[k];
      j = (R16 >= 0x26f) ? k + 3 : k;
      if (R16 == 0x26f)
        sound_effect_2 = 0x21;
      sprite_graphics[k] = kEnding_Case6_Gfx[j];
      sprite_oam_flags[k] = 0x33;
      Sprite_Get16BitCoords(k);
      SpriteActive_Main(k);
    }
    break;
  }
  case 7:
    k = 1;
    Credits_SpriteDraw_SetShadowProp(k, 2);
    sprite_type[k] = 0xe9;
    Oam_AllocateFromRegionA(0xc);
    sprite_oam_flags[k] = 0x37;
    Sprite_Get16BitCoords(k);
    if (!(frame_counter & 15))
      sprite_graphics[k] ^= 1;
    SpriteActive_Main(k);
    if (R16 >= 0x180) {
      sprite_y_vel[k] = 4;
      if (sprite_y_lo[k] != 0x7c)
        Sprite_MoveXY(k);
    }
    k--;
    sprite_type[k] = 0x36;
    Oam_AllocateFromRegionA(0x18);
    sprite_oam_flags[k] = 0x39;
    Sprite_Get16BitCoords(k);
    if (!sprite_delay_main[k]) {
      static const int8 kEnding_Case7_Gfx[2] = { 1, -1 };
      sprite_delay_main[k] = 4;
      sprite_graphics[k] = sprite_graphics[k] + kEnding_Case7_Gfx[R16 >> 9 & 1] & 7;
    }
    SpriteActive_Main(k);
    break;
  case 8:
    k = 0;
    sprite_type[k] = 0x2c;
    Oam_AllocateFromRegionA(0x2c);
    sprite_oam_flags[k] = 0x3b;
    Sprite_Get16BitCoords(k);
    sprite_graphics[k] = R16 < 0x1c0 ? R16 >> 5 & 1 : 2;
    SpriteActive_Main(k);
    break;
  case 9:
    for (k = 0; k < 5; k++) {
      if (!sprite_delay_main[k]) {
        sprite_delay_main[k] = 96;
        sprite_state[k] = 96;
        sprite_x_vel[k] = 0;
        sprite_x_lo[k] = 238;
        sprite_x_hi[k] = 4;
        sprite_y_lo[k] = 24;
        sprite_y_hi[k] = 11;
      }
      if (sprite_state[k]) {
        sprite_y_vel[k] = -8;
        Sprite_MoveXY(k);
        if (!(frame_counter & 1))
          sprite_x_vel[k] += ((frame_counter >> 5) ^ k) & 1 ? -1 : 1;
        Credits_SpriteDraw_Single(k, 1, 0x10);
      }
    }
    for (;;) {
      if (!sprite_delay_main[k]) {
        static const uint8 kEnding_Case8_Delay1[4] = { 16, 14, 16, 18 };
        static const uint8 kEnding_Case8_Delay2[4] = { 20, 48, 20, 20 };
        sprite_delay_main[k] = (k == 5) ? kEnding_Case8_Delay1[sprite_A[k]] : kEnding_Case8_Delay2[sprite_A[k]];
        sprite_A[k] = sprite_A[k] + 1 & 3;
        sprite_graphics[k] ^= 1;
      }
      if (k == 5) {
        sprite_oam_flags[k] = 0x31;
        Credits_SpriteDraw_PreexistingSpriteDraw(k, 0x10);
        k++;
      } else {
        Credits_SpriteDraw_Single(k, 2, 0x12);
        k++;
        break;
      }
    }
    do {
      static const uint8 kEnding_Case8_D[4] = { 0, 1, 0, 1 };
      static const uint8 kEnding_Case8_OamFlags[4] = { 55, 55, 59, 61 };
      static const uint8 kEnding_Case8_Tab0[4] = { 8, 8, 12, 12 };
      sprite_oam_flags[k] = kEnding_Case8_OamFlags[k - 7];
      sprite_D[k] = kEnding_Case8_D[k - 7];
      Credits_SpriteDraw_ActivateAndRunSprite(k, kEnding_Case8_Tab0[k - 7]);
    } while (++k != 11);
    break;
  case 10: {
    static const uint8 kWishPond_X[8] = { 0, 4, 8, 12, 16, 20, 24, 0 };
    static const uint8 kWishPond_Y[8] = { 0, 8, 16, 24, 32, 40, 4, 36 };
    k = 5;
    Sprite_Get16BitCoords(k);
    if (!sprite_pause[k]) {
      uint8 xb = kWishPond_X[GetRandomNumber() & 7] + cur_sprite_x;
      uint8 yb = kWishPond_Y[GetRandomNumber() & 7] + cur_sprite_y;
      Credits_SpriteDraw_AddSparkle(3, xb, yb);
    }
    for (int k = 3; k < 5; k++) {
      if (sprite_delay_aux1[k])
        sprite_delay_aux1[k]--;
      sprite_type[k] = 0xe3;
      Credits_SpriteDraw_SetShadowProp(k, 1);
      Credits_SpriteDraw_ActivateAndRunSprite(k, 8);
    }
    sprite_type[k] = 0x72;
    sprite_oam_flags[k] = 0x3b;
    sprite_state[k] = 9;
    sprite_B[k] = 9;
    Credits_SpriteDraw_PreexistingSpriteDraw(k, 0x30);
    break;
  }
  case 11:
    if (R16 >= 0x170) {
      for (int k = 4; k != 6; k++) {
        Credits_SpriteDraw_Single(k, 1, 0x3e);
      }
      k = 0;
      sprite_oam_flags[k] = 0x39;
      if (R16 < 0x1c0) {
        sprite_graphics[k] = 2;
      } else if (sprite_delay_main[k] == 0) {
        sprite_delay_main[k] = 0x20;
        sprite_graphics[k] = (sprite_graphics[k] ^ 1) & 1;
      }
      Credits_SpriteDraw_Single(k, 4, 6);
    } else {
      static const uint8 kEnding_Case11_Gfx[16] = { 1, 1, 2, 2, 1, 1, 1, 1, 2, 2, 2, 2, 0, 0, 0, 0 };
      for (int k = 0; k < 2; k++) {
        sprite_type[k] = 0x1a;
        sprite_oam_flags[k] = 0x39;
        Credits_SpriteDraw_SetShadowProp(k, 2);
        uint8 bak0 = main_module_index;
        Credits_SpriteDraw_ActivateAndRunSprite(k, 0xc);
        main_module_index = bak0;
        if (sprite_B[k] == 15 && sprite_A[k] == 4)
          sprite_delay_main[k + 2] = 15;
        int j = sprite_delay_main[k + 2];
        if (j != 0) {
          sprite_oam_flags[k + 2] = 2;
          sprite_graphics[k + 2] = kEnding_Case11_Gfx[j];
          Credits_SpriteDraw_Single(k + 2, 2, 0x36);
        }
      }
    }
    break;
  case 12:
    k = 6;
    sprite_graphics[k] = frame_counter & 1;
    if (!sprite_graphics[k]) {
      sprite_x_vel[k] += sign8(sprite_x_lo[k] - 0x80) ? 1 : -1;
      sprite_y_vel[k] += sign8(sprite_y_lo[k] - 0xb0) ? 1 : -1;
      Sprite_MoveXY(k);
    }

    sprite_oam_flags[k] = sprite_x_vel[k] >> 1 & 0x40 ^ 0x7e;
    sprite_flags2[k] = 1;
    sprite_flags3[k] = 0x30;
    sprite_z[k] = 16;
    Credits_SpriteDraw_PreexistingSpriteDraw(k, 8);
    k--;
    sprite_oam_flags[k] = 0x37;
    Credits_SpriteDraw_SetShadowProp(k, 2);
    Credits_SpriteDraw_ActivateAndRunSprite(k, 12);
    k--;
    Credits_SpriteDraw_ActivateAndRunSprite(k, 8);
    k--;
    Credits_SpriteDraw_ActivateAndRunSprite(k, 8);
    k--;
    do {
      static const uint8 kEnding_Case12_Tab[3] = { 3, 3, 8 };
      static const uint8 kEnding_Case12_Z[15] = { 2, 4, 5, 6, 6, 7, 7, 7, 7, 6, 6, 5, 4, 2, 0 };

      Credits_SpriteDraw_Single(k, kEnding_Case12_Tab[k], k * 2);
      if (k == 0) {
        Ending_Func2(k, 0x30);
      } else if (k & ~1) {
        sprite_graphics[k] = frame_counter >> 3 & 1;
      } else {
        int j = frame_counter & 0x1f;
        if (j < 0xf) {
          sprite_z[k] = kEnding_Case12_Z[j];
        }
        sprite_graphics[k] = (j < 0xf) ? 1 : 0;
        Credits_SpriteDraw_DrawShadow(k);
      }
    } while (--k >= 0);
    break;
  case 13:
    k = 0;
    if (R16 == 0x200)
      sprite_x_vel[k] = -4;
    sprite_graphics[k] = frame_counter >> 4 & 1;
    if (sprite_x_lo[k] == 56) {
      sprite_x_vel[k] = 0;
      sprite_graphics[k] += 2;
    }
    Credits_SpriteDraw_Single(k, 3, 0x34);
    Sprite_MoveXY(k);
    break;
  case 14: {
    static const int8 kEnding_Case14_Tab1[4] = { 0, 1, 0, 2 };
    static const int8 kEnding_Case14_Tab0[5] = { 2, 8, 32, 32, 8 };
    for (k = 6; k; k--) {
      if (k >= 5) {
        sprite_type[k] = 0;
        Credits_SpriteDraw_SetShadowProp(k, 1);
        sprite_graphics[k] = (frame_counter + 0x4a & 8) >> 3;
        sprite_z[k] = 32;
        Credits_SpriteDraw_CirclingBirds(k);
        sprite_oam_flags[k] = (sprite_x_vel[k] >> 1 & 0x40) ^ 0xf;
        Credits_SpriteDraw_PreexistingSpriteDraw(k, 8);
      } else {
        sprite_type[k] = 0xd;
        if (k == 1)
          sprite_head_dir[k] = 0xd;
        Credits_SpriteDraw_SetShadowProp(k, 3);
        sprite_oam_flags[k] = 0x2b;
        uint8 a = sprite_delay_main[k];
        if (!a)
          sprite_delay_main[k] = a = 0xc0;
        a >>= 1;
        if (a == 0) {
          sprite_y_vel[k] = sprite_x_vel[k] = 0;
        } else {
          if (a < kEnding_Case14_Tab0[k] && !(frame_counter & 3) && (a = sprite_y_vel[k]) != 0) {
            sprite_y_vel[k] = --a;
            a -= 4;
            if (k < 3)
              a = -a;
            sprite_x_vel[k] = a;
          }
        }
        Sprite_MoveXY(k);
        sprite_graphics[k] = kEnding_Case14_Tab1[frame_counter >> 3 & 3];
        Credits_SpriteDraw_PreexistingSpriteDraw(k, 16);
      }
    }
    Credits_SpriteDraw_Single(k, 3, 0x18);
    Ending_Func2(k, 0x20);
    break;
  }
  case 15: {
    static const uint8 kEnding_Case15_X[4] = { 0x76, 0x73, 0x71, 0x78 };
    static const uint8 kEnding_Case15_Y[4] = { 0x8b, 0x83, 0x8d, 0x85 };
    static const uint8 kEnding_Case15_Delay[8] = { 6, 6, 6, 6, 6, 6, 10, 8 };
    static const uint8 kEnding_Case15_OamFlags[4] = { 0x61, 0x61, 0x3b, 0x39 };
    j = kGeneratedEndSequence15[frame_counter] & 3;
    Credits_SpriteDraw_AddSparkle(2, kEnding_Case15_X[j], kEnding_Case15_Y[j]);
    k = 2;
    sprite_type[k] = 0x62;
    sprite_oam_flags[k] = 0x39;
    Credits_SpriteDraw_PreexistingSpriteDraw(k, 0x18);
    for (j = 1; j >= 0; j--) {
      k++;
      if (sprite_delay_aux1[k])
        sprite_delay_aux1[k]--;
      sprite_oam_flags[k] = (sprite_x_vel[k] >> 1 & 0x40) ^ kEnding_Case15_OamFlags[j];
      if (!sprite_delay_main[k]) {
        sprite_delay_main[k] = 128;
        sprite_A[k] = 0;
      }
      if (!sprite_A[k]) {
        sprite_graphics[k] = (frame_counter >> 2 & 1) + 2;
        Credits_SpriteDraw_MoveSquirrel(k);
      } else if (!sprite_delay_aux1[k]) {
        if (sprite_B[k] == 8)
          sprite_B[k] = 0;
        sprite_delay_aux1[k] = kEnding_Case15_Delay[sprite_B[k] & 7];
        sprite_graphics[k] = sprite_graphics[k] & 1 ^ 1;
        sprite_B[k]++;
      }
      Credits_SpriteDraw_Single(k, 1, 20);
      EndSequence_DrawShadow2(k);
    }
    Credits_SpriteDraw_WalkLinkAwayFromPedestal(k + 1);
    break;
  }
  }

  k = submodule_index >> 1;
  if (R16 >= kEnding1_3_Tab0[k]) {
    if (!(R16 & 1) && !--INIDISP_copy)
      submodule_index++;
    else
      R16++;
  } else {
    if (!(R16 & 1) && INIDISP_copy != 15)
      INIDISP_copy++;
    R16++;
  }
  BG2HOFS_copy = BG2HOFS_copy2;
  BG2VOFS_copy = BG2VOFS_copy2;
  BG1HOFS_copy = BG1HOFS_copy2;
  BG1VOFS_copy = BG1VOFS_copy2;
}

void Credits_SpriteDraw_DrawShadow(int k) {  // 8ea5f8
  sprite_oam_flags[k] = 0x30;
  Credits_SpriteDraw_SetShadowProp(k, 0);
  Oam_AllocateFromRegionA(4);
  SpriteDraw_Shadow(k, &g_ending_coords);
}

void EndSequence_DrawShadow2(int k) {  // 8ea5fd
  Credits_SpriteDraw_SetShadowProp(k, 0);
  Oam_AllocateFromRegionA(4);
  SpriteDraw_Shadow(k, &g_ending_coords);
}

void Ending_Func2(int k, uint8 ain) {  // 8ea645
  static const uint8 kEnding_Func2_Delay[27] = {
  10, 10, 10, 10, 20, 8,   8,   0, 255, 12, 12, 12, 12, 12, 12, 30,
   8,  4,  4,  4,  0, 0, 255, 255, 144,  4, 0,
  };
  static const int8 kEnding_Func2_Tab0[28] = {
    0, 0, 1, 0, 1, 0, 2,  3,  0,  2, 0, 1, 0, 1, 0, 1,
    2, 3, 4, 5, 6, 3, 0, -1, -1, -1, 2, 3,
  };
  sprite_oam_flags[k] = ain;
  EndSequence_DrawShadow2(k);
  int j = sprite_A[k];
  if (!sprite_delay_main[k]) {
    j++;
    if (j == 8)
      j = 6;
    else if (j == 22)
      j = 21;
    else if (j == 28)
      j = 27;
    sprite_A[k] = j;
    sprite_delay_main[k] = kEnding_Func2_Delay[j - 1];
  }
  uint8 a = kEnding_Func2_Tab0[j];
  sprite_graphics[k] = (a == 255) ? frame_counter >> 3 & 1 : a;
  if ((j < 5 || j >= 10 && j < 15) && !(frame_counter & 1))
    sprite_y_lo[k]++;
}

void Credits_SpriteDraw_ActivateAndRunSprite(int k, uint8 a) {  // 8ea694
  cur_object_index = k;
  Oam_AllocateFromRegionA(a);
  Sprite_Get16BitCoords(k);
  uint8 bak0 = submodule_index;
  submodule_index = 0;
  sprite_state[k] = 9;
  SpriteActive_Main(k);
  submodule_index = bak0;
}

void Credits_SpriteDraw_PreexistingSpriteDraw(int k, uint8 a) {  // 8ea6b3
  Oam_AllocateFromRegionA(a);
  cur_object_index = k;
  Sprite_Get16BitCoords(k);
  SpriteActive_Main(k);
}

void Credits_SpriteDraw_Single(int k, uint8 a, uint8 j) {  // 8ea703
  static const DrawMultipleData kEndSequence_Dmd0[12] = {
    { 0, -8, 0x072a, 2},
    { 0, -8, 0x072a, 2},
    { 0,  0, 0x4fca, 2},
    { 0, -8, 0x072a, 2},
    { 0, -8, 0x072a, 2},
    { 0,  0, 0x0fca, 2},
    {-2,  0, 0x0f77, 0},
    { 0, -8, 0x072a, 2},
    { 0,  0, 0x4fca, 2},
    {-3,  0, 0x0f66, 0},
    { 0, -8, 0x072a, 2},
    { 0,  0, 0x4fca, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd1[6] = {
    {14,  -7, 0x0d48, 2},
    { 0,  -6, 0x0944, 2},
    { 0,   0, 0x094e, 2},
    {13, -14, 0x0d48, 2},
    { 0,  -8, 0x0944, 2},
    { 0,   0, 0x0946, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd2[16] = {
    {-2, -16, 0x3d78, 0},
    { 0, -24, 0x3d24, 2},
    { 0, -16, 0x3dc2, 2},
    {61, -16, 0x3777, 0},
    {64, -24, 0x37c4, 2},
    {64, -16, 0x77ca, 2},
    { 0,  -6, 0x326c, 2},
    {64,  -6, 0x326c, 2},
    {-2, -16, 0x3d68, 0},
    { 0, -24, 0x3d24, 2},
    { 0, -16, 0x3dc2, 2},
    {61, -16, 0x3766, 0},
    {64, -24, 0x37c4, 2},
    {64, -16, 0x77ca, 2},
    { 0,  -6, 0x326c, 2},
    {64,  -6, 0x326c, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd3[12] = {
    { 0,  0, 0x0022, 2},
    {48,  0, 0x0064, 2},
    { 0, 10, 0x016c, 2},
    {48, 10, 0x016c, 2},
    { 0,  0, 0x0064, 2},
    {48,  0, 0x0022, 2},
    { 0, 10, 0x016c, 2},
    {48, 10, 0x016c, 2},
    { 0,  0, 0x0064, 2},
    {48,  0, 0x0064, 2},
    { 0, 10, 0x016c, 2},
    {48, 10, 0x016c, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd4[8] = {
    {10,   8, 0x8a32, 0},
    {10,  16, 0x8a22, 0},
    { 0, -10, 0x0800, 2},
    { 0,   0, 0x082c, 2},
    {10, -14, 0x0a22, 0},
    {10,  -6, 0x0a32, 0},
    {0, -10, 0x082a, 2},
    {0,   0, 0x0828, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd5[10] = {
    {10,  16, 0x8a05, 0},
    {10,   8, 0x8a15, 0},
    {-4,   2, 0x0a07, 2},
    { 0,  -7, 0x0e00, 2},
    { 0,   1, 0x0e02, 2},
    {10, -20, 0x0a05, 0},
    {10, -12, 0x0a15, 0},
    {-7,   1, 0x4a07, 2},
    { 0,  -7, 0x0e00, 2},
    { 0,   1, 0x0e02, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd6[3] = {
    {-6, -2, 0x0706, 2},
    { 0, -9, 0x090e, 2},
    { 0, -1, 0x0908, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd7[10] = {
    {0, -10, 0x082a, 2},
    {0,   0, 0x0828, 2},
    {10,  16, 0x8a05, 0},
    {10,   8, 0x8a15, 0},
    {-4,   2, 0x0a07, 2},
    { 0,  -7, 0x0e00, 2},
    { 0,   1, 0x0e02, 2},
    {10, -20, 0x0a05, 0},
    {10, -12, 0x0a15, 0},
    {-7,   1, 0x4a07, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd8[1] = {
    {0, -19, 0x39af, 0},
  };
  static const DrawMultipleData kEndSequence_Dmd9[4] = {
    {-16, -24, 0x3704, 2},
    {-16, -16, 0x3764, 2},
    {-16, -24, 0x3762, 2},
    {-16, -16, 0x3764, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd10[4] = {
    {0, 0, 0x0c0c, 2},
    {0, 0, 0x0c0a, 2},
    {0, 0, 0x0cc5, 2},
    {0, 0, 0x0ce1, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd11[6] = {
    {1,  4, 0x002a, 0},
    {1, 12, 0x003a, 0},
    {4,  0, 0x0026, 2},
    {0,  9, 0x0024, 2},
    {8,  9, 0x4024, 2},
    {4, 20, 0x016c, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd12[21] = {
    { 0, -7, 0x0d00, 2},
    { 0, -7, 0x0d00, 2},
    { 0,  0, 0x0d06, 2},
    { 0, -7, 0x0d00, 2},
    { 0, -7, 0x0d00, 2},
    { 0,  0, 0x4d06, 2},
    { 0, -8, 0x0d00, 2},
    { 0, -8, 0x0d00, 2},
    { 0,  0, 0x0d20, 2},
    { 0, -8, 0x0d02, 2},
    { 0, -8, 0x0d02, 2},
    { 0,  0, 0x0d2c, 2},
    {-3,  0, 0x0d2f, 0},
    { 0, -7, 0x0d02, 2},
    { 0,  0, 0x0d2c, 2},
    {-5,  2, 0x0d2f, 0},
    { 0, -8, 0x0d02, 2},
    { 0,  0, 0x0d2c, 2},
    {-5,  2, 0x0d3f, 0},
    { 0, -8, 0x0d02, 2},
    { 0,  0, 0x0d2c, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd13[16] = {
    {0, -7, 0x0e00, 2},
    {0,  1, 0x4e02, 2},
    {0, -8, 0x0e00, 2},
    {0,  1, 0x0e02, 2},
    {0, -9, 0x0e00, 2},
    {0,  1, 0x0e02, 2},
    {0, -7, 0x0e00, 2},
    {0,  1, 0x0e02, 2},
    {0, -7, 0x0e00, 2},
    {0,  1, 0x4e02, 2},
    {0, -8, 0x0e00, 2},
    {0,  1, 0x4e02, 2},
    {0, -9, 0x0e00, 2},
    {0,  1, 0x4e02, 2},
    {0, -7, 0x0e00, 2},
    {0,  1, 0x4e02, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd14[6] = {
    {0, 0, 0, 0},
    {0, 0, 0x34c7, 0},
    {0, 0, 0x3480, 0},
    {0, 0, 0x34b6, 0},
    {0, 0, 0x34b7, 0},
    {0, 0, 0x34a6, 0},
  };
  static const DrawMultipleData kEndSequence_Dmd15[6] = {
    {-3, 17, 0x002b, 0},
    {-3, 25, 0x003b, 0},
    { 0,  0, 0x000e, 2},
    {16,  0, 0x400e, 2},
    { 0, 16, 0x002e, 2},
    {16, 16, 0x402e, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd16[3] = {
    { 8,  5, 0x0a04, 2},
    { 0, 16, 0x0806, 2},
    {16, 16, 0x4806, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd17[2] = {
    {0,  0, 0x0000, 2},
    {0, 11, 0x0002, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd18[2] = {
    {0,  0, 0x000e, 2},
    {0, 64, 0x006c, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd19[8] = {
    {0, 0, 0x0882, 2},
    {0, 7, 0x0a4e, 2},
    {0, 0, 0x4880, 2},
    {0, 7, 0x0a4e, 2},
    {0, 0, 0x0882, 2},
    {0, 7, 0x0a4e, 2},
    {0, 0, 0x0880, 2},
    {0, 7, 0x0a4e, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd20[6] = {
    {-4,  1, 0x0c68, 0},
    { 0, -8, 0x0c40, 2},
    { 0,  1, 0x0c42, 2},
    {-4,  1, 0x0c78, 0},
    { 0, -8, 0x0c40, 2},
    { 0,  1, 0x0c42, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd21[6] = {
    {8,   5, 0x0679, 0},
    {0, -10, 0x088e, 2},
    {0,   0, 0x066e, 2},
    {0, -10, 0x088e, 2},
    {0, -10, 0x088e, 2},
    {0,   0, 0x066e, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd22[6] = {
    {11,  -3, 0x0869, 0},
    { 0, -12, 0x0804, 2},
    { 0,   0, 0x0860, 2},
    {10,  -3, 0x0867, 0},
    { 0, -12, 0x0804, 2},
    { 0,   0, 0x0860, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd23[6] = {
    {-2,  1, 0x0868, 0},
    { 0, -8, 0x08c0, 2},
    { 0,  0, 0x08c2, 2},
    {-3,  1, 0x0878, 0},
    { 0, -8, 0x08c0, 2},
    { 0,  0, 0x08c2, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd24[4] = {
    {0, -10, 0x084c, 2},
    {0,   0, 0x0a6c, 2},
    {0,  -9, 0x084c, 2},
    {0,   0, 0x0aa8, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd25[4] = {
    {0, -7, 0x084a, 2},
    {0,  0, 0x0c6a, 2},
    {0, -7, 0x084a, 2},
    {0,  0, 0x0ca6, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd26[12] = {
    {-18, -24, 0x39a4, 2},
    {-16, -16, 0x39a8, 2},
    {-18, -24, 0x39a4, 2},
    {-18, -24, 0x39a4, 2},
    {-16, -16, 0x39a6, 2},
    {-18, -24, 0x39a4, 2},
    { -6, -17, 0x392d, 0},
    {-16, -24, 0x39a0, 2},
    {-16, -16, 0x39aa, 2},
    { -5, -17, 0x392c, 0},
    {-16, -24, 0x39a0, 2},
    {-16, -16, 0x39aa, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd27[6] = {
    { 0,  -4, 0x30aa, 2},
    { 0,  -4, 0x30aa, 2},
    {-4,  -8, 0x3090, 0},
    {12,  -8, 0x7090, 0},
    {-6, -10, 0x3091, 0},
    {14, -10, 0x7091, 0},
  };
  static const DrawMultipleData kEndSequence_Dmd28[8] = {
    {0,  0, 0x0722, 2},
    {0, -8, 0x09c2, 2},
    {0,  0, 0x4722, 2},
    {0, -8, 0x09c2, 2},
    {0, -9, 0x09c4, 2},
    {0,  0, 0x0722, 2},
    {0, -9, 0x0924, 2},
    {0,  0, 0x0722, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd29[3] = {
    {-16, -12, 0x3f08, 2},
    {  0, -12, 0x3f20, 2},
    { 16, -12, 0x3f20, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd30[1] = {
    {0, 0, 0x0086, 2},
  };
  static const DrawMultipleData kEndSequence_Dmd31[1] = {
    {0, 0, 0x8060, 2},
  };
  static const DrawMultipleData *const kEndSequence_Dmds[] = {
    kEndSequence_Dmd0, kEndSequence_Dmd1, kEndSequence_Dmd2, kEndSequence_Dmd3,
    kEndSequence_Dmd4, kEndSequence_Dmd5, kEndSequence_Dmd6, kEndSequence_Dmd7,
    kEndSequence_Dmd8, kEndSequence_Dmd9, kEndSequence_Dmd10, kEndSequence_Dmd11,
    kEndSequence_Dmd12, kEndSequence_Dmd13, kEndSequence_Dmd14, kEndSequence_Dmd15,
    kEndSequence_Dmd16, kEndSequence_Dmd17, kEndSequence_Dmd18, kEndSequence_Dmd19,
    kEndSequence_Dmd20, kEndSequence_Dmd21, kEndSequence_Dmd22, kEndSequence_Dmd23,
    kEndSequence_Dmd24, kEndSequence_Dmd25, kEndSequence_Dmd26, kEndSequence_Dmd27,
    kEndSequence_Dmd28, kEndSequence_Dmd29, kEndSequence_Dmd30, kEndSequence_Dmd31
  };

  Oam_AllocateFromRegionA(a * 4);
  Sprite_Get16BitCoords(k);
  Sprite_DrawMultiple(k, kEndSequence_Dmds[j >> 1] + a * sprite_graphics[k], a, &g_ending_coords);
}

void Credits_SpriteDraw_SetShadowProp(int k, uint8 a) {  // 8eaca2
  sprite_flags2[k] = a;
  sprite_flags3[k] = 16;
}

void Credits_SpriteDraw_AddSparkle(int j_count, uint8 xb, uint8 yb) {  // 8eace5
  static const uint8 kEnding_Func3_Delay[6] = { 32, 4, 4, 4, 5, 6 };
  sprite_C[0] = j_count;
  for (int k = 0; k < j_count; k++) {
    int j = sprite_graphics[k];
    if (!sprite_delay_main[k]) {
      if (++j >= 6) {
        sprite_x_lo[k] = xb;
        sprite_y_lo[k] = yb;
        j = 0;
      }
      sprite_graphics[k] = j;
      sprite_delay_main[k] = kEnding_Func3_Delay[j];
    }
    if (j)
      Credits_SpriteDraw_Single(k, 1, 0x1c);
  }
}

void Credits_SpriteDraw_WalkLinkAwayFromPedestal(int k) {  // 8eadf7
  static const uint16 kEnding_Func6_Dma[8] = { 0x16c, 0x16e, 0x170, 0x172, 0x16c, 0x174, 0x176, 0x178 };
  if (!sprite_delay_main[k]) {
    sprite_graphics[k] = sprite_graphics[k] + 1 & 7;
    sprite_delay_main[k] = 4;
  }
  link_dma_graphics_index = kEnding_Func6_Dma[sprite_graphics[k]];
  sprite_oam_flags[k] = 32;
  Credits_SpriteDraw_Single(k, 2, 26);
  EndSequence_DrawShadow2(k);
  Sprite_MoveXY(k);
}

void Credits_SpriteDraw_MoveSquirrel(int k) {  // 8eae35
  static const int8 kEnding_Func5_Xvel[4] = { 32, 24, -32, -24 };
  static const int8 kEnding_Func5_Yvel[4] = { 8, -8, -8, 8 };
  if (sprite_delay_main[k] < 64) {
    sprite_C[k] = sprite_C[k] + 1 & 3;
    sprite_A[k]++;
  } else {
    int j = sprite_C[k];
    sprite_x_vel[k] = kEnding_Func5_Xvel[j];
    sprite_y_vel[k] = kEnding_Func5_Yvel[j];
    Sprite_MoveXY(k);
  }
}

void Credits_SpriteDraw_CirclingBirds(int k) {  // 8eae63
  static const int8 kEnding_MoveSprite_Func1_TargetX[2] = { 0x20, -0x20 };
  static const int8 kEnding_MoveSprite_Func1_TargetY[2] = { 0x10, -0x10 };

  int j = sprite_D[k] & 1;
  sprite_x_vel[k] += j ? -1 : 1;
  if (sprite_x_vel[k] == (uint8)kEnding_MoveSprite_Func1_TargetX[j])
    sprite_D[k]++;
  if (!(frame_counter & 1)) {
    j = sprite_head_dir[k] & 1;
    sprite_y_vel[k] += j ? -1 : 1;
    if (sprite_y_vel[k] == (uint8)kEnding_MoveSprite_Func1_TargetY[j])
      sprite_head_dir[k]++;
  }
  Sprite_MoveXY(k);
}

void Credits_HandleCameraScrollControl() {  // 8eaea6
  if (link_y_vel != 0) {
    uint8 yvel = link_y_vel;
    BG2VOFS_copy2 += (int8)yvel;
    uint16 *which = sign8(yvel) ? &overworld_unk1 : &overworld_unk1_neg;
    *which += abs8(yvel);
    if (!sign16(*which - 0x10)) {
      *which -= 0x10;
      overworld_screen_trans_dir_bits2 |= sign8(yvel) ? 8 : 4;
    }
    *(sign8(yvel) ? &overworld_unk1_neg : &overworld_unk1) = -*which;
    uint16 r4 = (int8)yvel, subp;
    WORD(byte_7E069E[0]) = r4;
    uint8 oi = BYTE(overlay_index);
    if (oi != 0x97 && oi != 0x9d) {
      if (oi == 0xb5 || oi == 0xbe) {
        subp = (r4 & 3) << 14;
        r4 >>= 2;
        if (r4 >= 0x3000)
          r4 |= 0xf000;
      } else {
        subp = (r4 & 1) << 15;
        r4 >>= 1;
        if (r4 >= 0x7000)
          r4 |= 0xf000;
      }
      uint32 tmp = BG1VOFS_subpixel | BG1VOFS_copy2 << 16;
      tmp += subp | r4 << 16;
      BG1VOFS_subpixel = (uint16)(tmp);
      BG1VOFS_copy2 = (uint16)(tmp >> 16);
    }
  }

  if (link_x_vel != 0) {
    uint8 xvel = link_x_vel;
    BG2HOFS_copy2 += (int8)xvel;
    uint16 *which = sign8(xvel) ? &overworld_unk3 : &overworld_unk3_neg;
    *which += abs8(xvel);
    if (!sign16(*which - 0x10)) {
      *which -= 0x10;
      overworld_screen_trans_dir_bits2 |= sign8(xvel) ? 2 : 1;
    }
    *(sign8(xvel) ? &overworld_unk3_neg : &overworld_unk3) = -*which;

    uint16 r4 = (int8)xvel, subp;
    WORD(byte_7E069E[1]) = r4;
    uint8 oi = BYTE(overlay_index);
    if (oi != 0x97 && oi != 0x9d && r4 != 0) {
      if (oi == 0x95 || oi == 0x9e) {
        subp = (r4 & 3) << 14;
        r4 >>= 2;
        if (r4 >= 0x3000)
          r4 |= 0xf000;
      } else {
        subp = (r4 & 1) << 15;
        r4 >>= 1;
        if (r4 >= 0x7000)
          r4 |= 0xf000;
      }
      uint32 tmp = BG1HOFS_subpixel | BG1HOFS_copy2 << 16;
      tmp += subp | r4 << 16;
      BG1HOFS_subpixel = (uint16)(tmp), BG1HOFS_copy2 = (uint16)(tmp >> 16);
    }
  }

  if (BYTE(overlay_index) == 0x9c) {
    uint32 tmp = BG1VOFS_subpixel | BG1VOFS_copy2 << 16;
    tmp -= 0x2000;
    BG1VOFS_subpixel = (uint16)(tmp), BG1VOFS_copy2 = (uint16)(tmp >> 16) + WORD(byte_7E069E[0]);
    BG1HOFS_copy2 = BG2HOFS_copy2;
  } else if (BYTE(overlay_index) == 0x97 || BYTE(overlay_index) == 0x9d) {
    uint32 tmp = BG1VOFS_subpixel | BG1VOFS_copy2 << 16;
    tmp += 0x2000;
    BG1VOFS_subpixel = (uint16)(tmp), BG1VOFS_copy2 = (uint16)(tmp >> 16);
    tmp = BG1HOFS_subpixel | BG1HOFS_copy2 << 16;
    tmp += 0x2000;
    BG1HOFS_subpixel = (uint16)(tmp), BG1HOFS_copy2 = (uint16)(tmp >> 16);
  }

  if (dungeon_room_index == 0x181) {
    BG1VOFS_copy2 = BG2VOFS_copy2 | 0x100;
    BG1HOFS_copy2 = BG2HOFS_copy2;
  }
}

void EndSequence_32() {  // 8ebc6d
  EnableForceBlank();
  EraseTileMaps_triforce();
  TransferFontToVRAM();
  Credits_LoadCoolBackground();
  Credits_InitializePolyhedral();
  INIDISP_copy = 128;
  overworld_palette_aux_or_main = 0x200;
  hud_palette = 1;
  Palette_Load_HUD();
  flag_update_cgram_in_nmi++;
  deaths_per_palace[4] = 0;
  deaths_per_palace[13] += death_save_counter;
  int sum = deaths_per_palace[13];
  for (int i = 12; i >= 0; i--)
    sum += deaths_per_palace[i];
  death_var2 = sum;
  death_save_counter = 0;
  link_health_current = kHealthAfterDeath[link_health_capacity >> 3];
  savegame_is_darkworld = 0x40;
  SaveGameFile();
  aux_palette_buffer[38] = 0;
  main_palette_buffer[38] = 0;
  aux_palette_buffer[0] = 0;
  main_palette_buffer[0] = 0;
  TM_copy = 0x16;
  TS_copy = 0;
  R16 = 0x6800;
  R18 = 0;
  ending_which_dung = 0;
  BG2VOFS_copy2 = -0x48;
  BG2HOFS_copy2 = 0x90;
  BG3VOFS_copy2 = 0;
  BG3HOFS_copy2 = 0;
  Credits_AddNextAttribution();
  music_control = 0x22;
  CGWSEL_copy = 0;
  CGADSUB_copy = 162;
  // real zelda does 0x12 here but this seems to work too
  zelda_ppu_write(BG2SC, 0x13);
  COLDATA_copy0 = 0x3f;
  COLDATA_copy1 = 0x5f;
  COLDATA_copy2 = 0x9f;
  subsubmodule_index = 64;
  INIDISP_copy = 0;

  HdmaSetup(0, 0xebd53, 0x42, 0, (uint8)BG2HOFS, 0);
  HDMAEN_copy = 0x80;

  BG2HOFS_copy = BG2HOFS_copy2;
  BG2VOFS_copy = BG2VOFS_copy2;
  BG1HOFS_copy = BG1HOFS_copy2;
  BG1VOFS_copy = BG1VOFS_copy2;
}

void Credits_FadeOutFixedCol() {  // 8ebd66
  if (--subsubmodule_index == 0) {
    subsubmodule_index = 16;
    if (COLDATA_copy0 != 32) {
      COLDATA_copy0--;
    } else if (COLDATA_copy1 != 64) {
      COLDATA_copy1--;
    } else if (COLDATA_copy2 != 128) {
      COLDATA_copy2--;
    }
  }
}

void Credits_FadeColorAndBeginAnimating() {  // 8ebd8b
  Credits_FadeOutFixedCol();
  nmi_disable_core_updates = 1;
  Credits_AnimateTheTriangles();
  if (!(frame_counter & 3)) {
    if (++BG2HOFS_copy2 == 0xc00) {
      // real zelda writes 0x00 to BG1SC here but that doesn't seem needed
      zelda_ppu_write(BG2SC, 0x13);
    }
    room_bounds_y.a1 = BG2HOFS_copy2 >> 1;
    room_bounds_y.a0 = room_bounds_y.a1 + BG2HOFS_copy2;
    room_bounds_y.b0 = room_bounds_y.a0 >> 1;
    room_bounds_y.b1 = room_bounds_y.a1 >> 1;
    if (BG3VOFS_copy2 == 3288) {
      R16 = 0x80;
      submodule_index++;
    } else {
      BG3VOFS_copy2++;
      if ((BG3VOFS_copy2 & 7) == 0) {
        R18 = BG3VOFS_copy2 >> 3;
        Credits_AddNextAttribution();
      }
    }
  }
  BG2HOFS_copy = BG2HOFS_copy2;
  BG2VOFS_copy = BG2VOFS_copy2;
  BG1HOFS_copy = BG1HOFS_copy2;
  BG1VOFS_copy = BG1VOFS_copy2;
}

void Credits_AddNextAttribution() {  // 8ebe24
  static const uint8 kEnding_Func9_Tab2[14] = { 1, 0, 2, 3, 10, 6, 5, 8, 11, 9, 7, 12, 13, 15 };
  static const uint16 kEnding_Digits_ScrollY[14] = { 0x290, 0x298, 0x2a0, 0x2a8, 0x2b0, 0x2ba, 0x2c2, 0x2ca, 0x2d2, 0x2da, 0x2e2, 0x2ea, 0x2f2, 0x310 };
  static const uint16 kEnding_Credits_DigitChar[2] = { 0x3ce6, 0x3cf6 };

  uint16 *dst = vram_upload_data + (vram_upload_offset >> 1);

  dst[0] = swap16(R16);
  dst[1] = 0x3e40;
  dst[2] = kEnding_MapData[159];
  dst += 3;

  if (R18 < 394) {
    const uint8 *src = &kEnding_Credits_Text[kEnding_Credits_Offs[R18]];
    if (*src != 0xff) {
      *dst++ = swap16(R16 + *src++);
      int n = *src++;
      *dst++ = swap16(n);
      n = (n + 1) >> 1;
      do {
        *dst++ = kEnding_MapData[*src++];
      } while (--n);
    }

    if ((ending_which_dung & 1) || R18 * 2 == kEnding_Digits_ScrollY[ending_which_dung >> 1]) {
      int t = kEnding_Credits_DigitChar[ending_which_dung & 1];
      WORD(g_ram[0xce]) = t;

      dst[0] = swap16(R16 + 0x19);
      dst[1] = 0x500;

      uint16 deaths = deaths_per_palace[kEnding_Func9_Tab2[ending_which_dung >> 1]];
      if (deaths >= 1000)
        deaths = 999;

      dst[4] = t + deaths % 10, deaths /= 10;
      dst[3] = t + deaths % 10, deaths /= 10;
      dst[2] = t + deaths;
      dst += 5;
      ending_which_dung++;
    }
  }

  R16 += 0x20;
  if (!(R16 & 0x3ff))
    R16 = (R16 & 0x6800) ^ 0x800;
  vram_upload_offset = (char *)dst - (char *)vram_upload_data;
  BYTE(*dst) = 0xff;
  nmi_load_bg_from_vram = 1;
}

void Credits_AddEndingSequenceText() {  // 8ec303

  uint16 *dst = vram_upload_data;
  dst[0] = 0x60;
  dst[1] = 0xfe47;
  dst[2] = kEnding_MapData[159];
  dst += 3;

  const uint8 *curo = &kEnding0_Data[kEnding0_Offs[submodule_index >> 1]];
  const uint8 *endo = &kEnding0_Data[kEnding0_Offs[(submodule_index >> 1) + 1]];
  do {
    dst[0] = WORD(curo[0]);
    dst[1] = WORD(curo[2]);
    int m = (dst[1] >> 9) & 0x7f;
    dst += 2, curo += 4;
    do {
      *dst++ = kEnding_MapData[*curo++];
    } while (--m >= 0);
  } while (curo != endo);

  vram_upload_offset = (char *)dst - (char *)vram_upload_data;
  BYTE(*dst) = 0xff;
  nmi_load_bg_from_vram = 1;
}

void Credits_BrightenTriangles() {  // 8ec37c
  if (!(frame_counter & 15) && ++INIDISP_copy == 15)
    submodule_index++;
  Credits_AnimateTheTriangles();
}

void Credits_StopCreditsScroll() {  // 8ec391
  if (!--BYTE(R16)) {
    darkening_or_lightening_screen = 0;
    palette_filter_countdown = 0;
    WORD(mosaic_target_level) = 0x1f;
    submodule_index++;
    R16 = 0xc0;
    R18 = 0;
  }
  Credits_AnimateTheTriangles();
}

void Credits_FadeAndDisperseTriangles() {  // 8ec3b8
  BYTE(R16)--;
  if (!BYTE(R18)) {
    ApplyPaletteFilter_bounce();
    if (BYTE(palette_filter_countdown)) {
      Credits_AnimateTheTriangles();
      return;
    }
    BYTE(R18)++;
  }
  if (BYTE(R16)) {
    Credits_AnimateTheTriangles();
    return;
  }
  submodule_index++;
  PaletteFilter_WishPonds_Inner();
}

void Credits_FadeInTheEnd() {  // 8ec3d5
  if (!(frame_counter & 7)) {
    PaletteFilter_SP5F();
    if (!BYTE(palette_filter_countdown))
      submodule_index++;;
  }
  Credits_HangForever();
}

void Credits_HangForever() {  // 8ec41a
  SetOamPlain(&oam_buf[0], -96, -72, 0x00, 0x3b, 2);
  SetOamPlain(&oam_buf[1], -80, -72, 0x02, 0x3b, 2);
  SetOamPlain(&oam_buf[2], -64, -72, 0x04, 0x3b, 2);
  SetOamPlain(&oam_buf[3], -48, -72, 0x06, 0x3b, 2);
}

void CrystalCutscene_InitializePolyhedral() {  // 9ecdd9
  poly_config1 = 156;
  poly_config_color_mode = 1;
  is_nmi_thread_active = 1;
  intro_did_run_step = 1;
  poly_base_x = 32;
  poly_base_y = 32;
  BYTE(poly_var1) = 32;
  poly_which_model = 0;
  poly_a = 16;
  TS_copy = 0;
  TM_copy = 0x16;
}