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.
This commit is contained in:
bismurphy 2024-06-08 11:22:29 -04:00 committed by GitHub
parent d7200b96a1
commit 93cadd8077
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 201 additions and 72 deletions

View File

@ -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]

View File

@ -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;

187
src/st/nz0/bossfight.c Normal file
View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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