From 93cadd80778d8459569f17efbc3c6b875a1d9fa2 Mon Sep 17 00:00:00 2001 From: bismurphy Date: Sat, 8 Jun 2024 11:22:29 -0400 Subject: [PATCH] Decompile NZ0 EntityBossFightManager (#1283) A few changes here. First, Slogra needs to be split out to his own file, as mentioned in my previous PR. I decompiled EntityCloseBossRoom, but I decided it needed a better name, since, while it does close the door to the boss room, it actually does a lot more and in general manages the fight (most importantly, it starts the fight, spawns Slogra and Gaibon, and when they die, spawns the life max up). Therefore I named it BossFightManager. Since this function, and the door blocks it spawns, are the only ones in this file, I decided to call it bossfight. I think we should try to be liberal with naming files, once we know all the functions contained in them. Otherwise I think that should do it! Very cool to have the first boss fight in the game all figured out. --- config/splat.us.stnz0.yaml | 9 +- config/symbols.us.stnz0.txt | 3 +- src/st/nz0/bossfight.c | 187 +++++++++++++++++++++++++++++++ src/st/nz0/gaibon.c | 2 - src/st/nz0/nz0.h | 12 +- src/st/nz0/{33FCC.c => slogra.c} | 60 ---------- 6 files changed, 201 insertions(+), 72 deletions(-) create mode 100644 src/st/nz0/bossfight.c rename src/st/nz0/{33FCC.c => slogra.c} (92%) diff --git a/config/splat.us.stnz0.yaml b/config/splat.us.stnz0.yaml index 871ad914c..d99e886a5 100644 --- a/config/splat.us.stnz0.yaml +++ b/config/splat.us.stnz0.yaml @@ -81,9 +81,9 @@ segments: - [0x2085C, tilelayout, D_801A085C] - [0x20A5C, data] - [0x305A4, .rodata, 30958] # func_801B3C38 - - [0x305B8, rodata] - - [0x305E8, .rodata, 33FCC] # EntityBossRoomBlock - - [0x305FC, .rodata, 33FCC] # EntitySlogra + - [0x305B8, .rodata, bossfight] # EntityBossFightManager + - [0x305E8, .rodata, bossfight] # EntityBossRoomBlock + - [0x305FC, .rodata, slogra] # EntitySlogra - [0x30618, .rodata, gaibon] # EntityGaibon .rodata, 33FCC - [0x3063C, .rodata, 36DE4] # func_801B7034 - [0x3064C, rodata] @@ -104,7 +104,8 @@ segments: - [0x30900, .rodata, 48ADC] # EntityStageNamePopup - [0x30934, rodata] - [0x30958, c] - - [0x33FCC, c] # Slogra & Gaibon Boss + - [0x33FCC, c, bossfight] # Slogra & Gaibon Boss + - [0x34690, c, slogra] - [0x35778, c, gaibon] - [0x36DE4, c] - [0x394D4, c, random] diff --git a/config/symbols.us.stnz0.txt b/config/symbols.us.stnz0.txt index 335d2ee7b..1bf5c421d 100644 --- a/config/symbols.us.stnz0.txt +++ b/config/symbols.us.stnz0.txt @@ -58,7 +58,7 @@ EntityCannonWall = 0x801B2978; EntityElevator2 = 0x801B2D08; EntityFloorSpikes = 0x801B3294; EntityTableWithGlobe = 0x801B3534; -EntityCloseBossRoom = 0x801B3FCC; +EntityBossFightManager = 0x801B3FCC; EntityBossRoomBlock = 0x801B4518; EntitySlogra = 0x801B4778; EntitySlograSpear = 0x801B54A8; @@ -75,7 +75,6 @@ EntityDamageDisplay = 0x801BAA20; InitRoomEntities = 0x801BB920; UpdateRoomPosition = 0x801BBA98; CreateEntityFromCurrentEntity = 0x801BBB4C; -CreateEntityFromEntity = 0x801BBBC0; EntityRedDoor = 0x801BBCB4; DestroyEntity = 0x801BC8EC; AnimateEntity = 0x801BCA1C; diff --git a/src/st/nz0/bossfight.c b/src/st/nz0/bossfight.c new file mode 100644 index 000000000..41cb3c3ee --- /dev/null +++ b/src/st/nz0/bossfight.c @@ -0,0 +1,187 @@ +#include "nz0.h" + +// This file holds functions to handle the Slogra and Gaibon fight. +// Slogra and Gaibon themselves are in individual files. + +void EntityBossFightManager(Entity* self) { + Entity* newEnt; + s32 i; + s16* temp_s1; + s32 xPos; + s32 newEntY; + bool bosses_defeated; + + FntPrint("boss_flag %x\n", g_BossFlag); + FntPrint("boss_step %x\n", self->step); + switch (self->step) { + case 0: + bosses_defeated = g_api.TimeAttackController( + TIMEATTACK_EVENT_SLOGRA_GAIBON_DEFEAT, TIMEATTACK_GET_RECORD); + if (bosses_defeated) { + DestroyEntity(self); + return; + } + InitializeEntity(g_EInitGeneric); + g_BossFlag = 0; + newEnt = self + 1; + temp_s1 = &D_80181014[0]; + for (i = 0; i < 12; i++, newEnt++, temp_s1 += 3) { + CreateEntityFromCurrentEntity(E_BOSS_ROOM_BLOCK, newEnt); + newEnt->params = temp_s1[2]; + newEnt->posX.i.hi = temp_s1[0] - g_Tilemap.scrollX.i.hi; + newEnt->posY.i.hi = temp_s1[1] - g_Tilemap.scrollY.i.hi; + } + // This spawns Slogra and Gaibon! Note that they always spawn at slot + // 80 and 88, which allows the SLOGRA and GAIBON macros (self[8]) to + // work. + newEnt = &g_Entities[80]; + CreateEntityFromCurrentEntity(E_SLOGRA, newEnt); + newEnt->posX.i.hi = 0x280 - g_Tilemap.scrollX.i.hi; + newEnt->posY.i.hi = 0x1A0 - g_Tilemap.scrollY.i.hi; + newEnt = &g_Entities[88]; + CreateEntityFromCurrentEntity(E_GAIBON, newEnt); + newEnt->posX.i.hi = 0x2A0 - g_Tilemap.scrollX.i.hi; + newEnt->posY.i.hi = 0x160 - g_Tilemap.scrollY.i.hi; + // fall through + case 1: // Detect whether player is in the room. If so, close the door. + xPos = PLAYER.posX.i.hi + g_Tilemap.scrollX.i.hi; + if (24 < xPos && xPos < 968) { + g_BossFlag |= BOSS_FLAG_DOORS_CLOSED; + // Unknown sound + g_api.PlaySfx(0x90); + D_80097928 = 0; + self->step++; + } + break; + case 2: // Door is now closed. Wait for player to get far enough to start + // the fight. + xPos = PLAYER.posX.i.hi + g_Tilemap.scrollX.i.hi; + if (0x220 < xPos && xPos < 0x340) { + g_BossFlag |= BOSS_FLAG_FIGHT_BEGIN; + } + if (g_BossFlag & BOSS_FLAG_FIGHT_BEGIN) { + g_api.TimeAttackController( + TIMEATTACK_EVENT_SLOGRA_GAIBON_DEFEAT, TIMEATTACK_SET_VISITED); + D_80097928 = 1; + D_80097910 = 0x31D; + self->step++; + } + break; + case 3: // Fight is now active. + if (g_api.func_80131F68() == false) { + D_80097928 = 0; + g_api.PlaySfx(D_80097910); + self->step++; + } + /* fallthrough */ + case 4: + // Wait for the fight to be over. + if ((g_BossFlag & BOSS_FLAG_GAIBON_DEAD) && + (g_BossFlag & BOSS_FLAG_SLOGRA_DEAD)) { + g_api.TimeAttackController( + TIMEATTACK_EVENT_SLOGRA_GAIBON_DEFEAT, TIMEATTACK_SET_RECORD); + if (g_api.func_80131F68() != false) { + g_api.PlaySfx(0x90); + } + D_80097910 = 0x32E; + self->step++; + } + return; + case 5: // Fight is now over. + xPos = 0x80; + newEntY = 0x180 - g_Tilemap.scrollY.i.hi; + newEnt = AllocEntity(&g_Entities[160], &g_Entities[192]); + if (newEnt == NULL) { + return; + } + CreateEntityFromEntity(E_LIFE_UP_SPAWN, self, newEnt); + newEnt->posX.i.hi = xPos; + newEnt->posY.i.hi = newEntY; + newEnt->params = 5; + g_BossFlag |= BOSS_FLAG_DOORS_OPEN; // Reopen the door + g_CastleFlags[132] = 1; + D_80097928 = 1; + D_80097910 = 0x32E; + self->step++; + return; + case 6: + if (g_api.func_80131F68() == false) { + D_80097928 = 0; + g_api.PlaySfx(D_80097910); + self->step++; + return; + } + break; + case 0xFF: // Unreachable debug state + FntPrint("charal %x\n", self->animCurFrame); + if (g_pads[1].pressed & PAD_SQUARE) { + if (self->params) { + return; + } + self->animCurFrame++; + self->params |= 1; + } else { + self->params = 0; + } + if (g_pads[1].pressed & PAD_CIRCLE) { + if (!self->step_s) { + self->animCurFrame--; + self->step_s |= 1; + return; + } + } else { + self->step_s = 0; + } + break; + } +} + +// blocks that move to close slogra/gaibon room +void EntityBossRoomBlock(Entity* self) { + switch (self->step) { + case 0: + InitializeEntity(D_80180D00); + self->animCurFrame = 8; + + case 1: + if (g_BossFlag & 1) { + self->ext.GS_Props.timer = 16; + self->step++; + } + break; + + case 2: + if (self->params == 0) { + self->velocityX = FIX(1); + } else { + self->velocityX = FIX(-1); + } + MoveEntity(); + GetPlayerCollisionWith(self, 8, 8, 5); + if (!(g_Timer & 3)) { + g_api.PlaySfx(0x608); + } + if (--self->ext.GS_Props.timer) { + break; + } + self->step++; + break; + + case 3: + GetPlayerCollisionWith(self, 8, 8, 5); + if (g_BossFlag & BOSS_FLAG_DOORS_OPEN) { + self->step++; + } + break; + + case 4: + self->flags |= FLAG_DESTROY_IF_OUT_OF_CAMERA; + if (self->params != 0) { + self->velocityX = FIX(1); + } else { + self->velocityX = FIX(-1); + } + MoveEntity(); + break; + } +} diff --git a/src/st/nz0/gaibon.c b/src/st/nz0/gaibon.c index c532a24af..b7a6e75e6 100644 --- a/src/st/nz0/gaibon.c +++ b/src/st/nz0/gaibon.c @@ -78,8 +78,6 @@ void EntityGaibon(Entity* self) { s32 yVar; s32 xVar; - s32 stupid; - if ((self->step) && (!self->ext.GS_Props.nearDeath) && self->hitPoints < g_api.enemyDefs[0xFE].hitPoints / 2) { self->ext.GS_Props.grabedAscending = 0; diff --git a/src/st/nz0/nz0.h b/src/st/nz0/nz0.h index 6443a478a..5b7f4eed8 100644 --- a/src/st/nz0/nz0.h +++ b/src/st/nz0/nz0.h @@ -29,11 +29,14 @@ typedef enum { /* 0x32 */ E_ROTATE_SPITTLEBONE, /* 0x33 */ E_SPITTLEBONE_SPIT, /* 0x38 */ E_FIRE = 0x38, - /* 0x41 */ E_SLOGRA_SPEAR = 0x41, - /* 0x42 */ E_SLOGRA_SPEAR_PROJECTILE = 0x42, - /* 0x43 */ E_GAIBON = 0x43, + /* 0x3F */ E_BOSS_ROOM_BLOCK = 0x3F, + /* 0x40 */ E_SLOGRA, + /* 0x41 */ E_SLOGRA_SPEAR, + /* 0x42 */ E_SLOGRA_SPEAR_PROJECTILE, + /* 0x43 */ E_GAIBON, /* 0x45 */ E_GAIBON_SMALL_FIREBALL = 0x45, - /* 0x46 */ E_GAIBON_BIG_FIREBALL = 0x46, + /* 0x46 */ E_GAIBON_BIG_FIREBALL, + /* 0x49 */ E_LIFE_UP_SPAWN = 0x49 } EntityIDs; #define BOSS_FLAG_DOORS_CLOSED (1 << 0) @@ -176,6 +179,7 @@ extern const u8 D_80180F74[]; extern u8 D_80180F88[]; extern u16 D_80180F9C[]; extern Unkstruct_80180FE0 D_80180FE0[]; +extern s16 D_80181014[]; extern u32 g_randomNext; extern s8 c_HeartPrizes[]; extern Entity* g_CurrentEntity; diff --git a/src/st/nz0/33FCC.c b/src/st/nz0/slogra.c similarity index 92% rename from src/st/nz0/33FCC.c rename to src/st/nz0/slogra.c index e25d344c3..53474befd 100644 --- a/src/st/nz0/33FCC.c +++ b/src/st/nz0/slogra.c @@ -1,8 +1,3 @@ -/* - * Overlay: NZ0 - * Enemy: Slogra & Gaibon Boss - */ - #include "nz0.h" #define GAIBON self[8] @@ -50,61 +45,6 @@ typedef enum { SLOGRA_DYING_END, } SlograDyingSubSteps; -// DECOMP_ME_WIP EntityCloseBossRoom https://decomp.me/scratch/bqgN9 95.04 % -// figuring out D_80181014 struct might help -// trigger to stop music and close slogra/gaibon room -INCLUDE_ASM("st/nz0/nonmatchings/33FCC", EntityCloseBossRoom); - -// blocks that move to close slogra/gaibon room -void EntityBossRoomBlock(Entity* self) { - switch (self->step) { - case 0: - InitializeEntity(D_80180D00); - self->animCurFrame = 8; - - case 1: - if (g_BossFlag & 1) { - self->ext.GS_Props.timer = 16; - self->step++; - } - break; - - case 2: - if (self->params == 0) { - self->velocityX = FIX(1); - } else { - self->velocityX = FIX(-1); - } - MoveEntity(); - GetPlayerCollisionWith(self, 8, 8, 5); - if (!(g_Timer & 3)) { - g_api.PlaySfx(0x608); - } - if (--self->ext.GS_Props.timer) { - break; - } - self->step++; - break; - - case 3: - GetPlayerCollisionWith(self, 8, 8, 5); - if (g_BossFlag & BOSS_FLAG_DOORS_OPEN) { - self->step++; - } - break; - - case 4: - self->flags |= FLAG_DESTROY_IF_OUT_OF_CAMERA; - if (self->params != 0) { - self->velocityX = FIX(1); - } else { - self->velocityX = FIX(-1); - } - MoveEntity(); - break; - } -} - s32 EntitySlograSpecialCollision(u16* unused) { /** * This function keeps Slogra between safe