Refactor some stuff with subweapons (#1702)

Primary goal here was to eliminate `ext.generic.unkB0`. It turns out
that the only entity that reaches up into this unkB0 region is the
Subweapon entity.

I went through and changed these all to `ext.subweapon.subweaponId`, but
also did some analysis on all the uses to make sure this was an accurate
change to make.

Other highlights:

- Naming CheckSubwpnChainLimit and its RIC counterpart
- Moving the RIC version of that function to the next file, split makes
more sense this way.
- Made both versions of that function `static`
- renamed subweapondef's unk6 to be chainLimit
- Changed g_unkGraphicsStruct.unk0 to be called `pauseEnemies`. It's
true when the stopwatch subweapon is used, and in cutscenes. Found this
due to looking at the stopwatch subweapon setting this value. Also made
it a bool since it's only ever used as a bool.
- Added some playersteps enum uses where they were still using hex
values
- Studied RicEntityStopwatchCrashLightning in order to give it that name

I think that's about it. Overall just some modest cleanup, but continues
to make the game code more readable.
This commit is contained in:
bismurphy 2024-09-30 16:19:51 -04:00 committed by GitHub
parent ac00e388e0
commit 80d9bd8cc5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 134 additions and 108 deletions

View File

@ -227,7 +227,7 @@ set(SOURCE_FILES_RIC
src/ric/spriteparts.c
src/ric/1CB04.c
src/ric/202A8.c
src/ric/21250.c
src/ric/211D0.c
src/ric/26C84.c
src/ric/2C4C4.c
src/ric/e_giant_spinning_cross.c

View File

@ -44,9 +44,9 @@ segments:
- [0x170AC, .data, spriteparts]
- [0x18568, .data, d_18568]
- [0x18688, assets, subweapondefs, D_80154688]
- [0x188F4, .data, 21250]
- [0x1895C, .data, 21250] # entity table
- [0x18A6C, .data, 21250] # blueprints
- [0x188F4, .data, 211D0]
- [0x1895C, .data, 211D0] # entity table
- [0x18A6C, .data, 211D0] # blueprints
- [0x18C40, .data, 24788]
- [0x18F7C, .data, 26C84]
- [0x1938C, .data, pl_anims]
@ -57,7 +57,7 @@ segments:
- [0x1A444, .rodata, 1AC60]
- [0x1A608, .rodata, 1CB04]
- [0x1A7C0, .rodata, 1FCD0]
- [0x1A7D8, .rodata, 21250]
- [0x1A7D8, .rodata, 211D0]
- [0x1A8E4, .rodata, 24788]
- [0x1A9B4, .rodata, 26C84]
- [0x1A9F4, .rodata, 2A060]
@ -69,7 +69,7 @@ segments:
- [0x1FCD8, c, 1FCD0]
- [0x202B0, c, 202A8]
- [0x208E8, c, 20920]
- [0x211C8, c, 21250]
- [0x211C8, c, 211D0]
- [0x246C4, c, 24788]
- [0x26DD8, c, 26C84]
- [0x29F9C, c, 2A060]
@ -77,7 +77,7 @@ segments:
- [0x31918, c, 319C4]
- [0x371E0, c, e_giant_spinning_cross]
- [0x37AB8, .bss, 1CB04]
- [0x38ED0, .bss, 21250]
- [0x38ED0, .bss, 211D0]
- [0x38F54, .bss, 2A060]
- [0x38FDC, .bss, 2C4C4]
- [0x397E8, .bss, 319C4]

View File

@ -40,9 +40,9 @@ segments:
- [0x170AC, .data, spriteparts]
- [0x18568, .data, d_18568]
- [0x18688, assets, subweapondefs, D_80154688]
- [0x188F4, .data, 21250]
- [0x1895C, .data, 21250] # entity table
- [0x18A6C, .data, 21250] # blueprints
- [0x188F4, .data, 211D0]
- [0x1895C, .data, 211D0] # entity table
- [0x18A6C, .data, 211D0] # blueprints
- [0x18C40, .data, 24788]
- [0x18ED4, .data, 24788] # RicEntityMaria
- [0x18F7C, .data, 26C84]
@ -57,7 +57,7 @@ segments:
- [0x1A7A8, .rodata, 1FCD0]
- [0x1A7C0, .rodata, 202A8]
- [0x1A800, .rodata, 20920]
- [0x1A80C, .rodata, 21250]
- [0x1A80C, .rodata, 211D0]
- [0x1A918, .rodata, 24788]
- [0x1A9D0, .rodata, 24788] # RicEntityMaria
- [0x1A9E8, .rodata, 26C84] # EntityPlayerBlinkWhite
@ -76,7 +76,7 @@ segments:
- [0x1FCD0, c, 1FCD0]
- [0x202A8, c, 202A8]
- [0x20920, c, 20920]
- [0x21250, c, 21250]
- [0x211D0, c, 211D0]
- [0x24788, c, 24788]
- [0x26C84, c, 26C84]
- [0x2A060, c, 2A060]
@ -85,7 +85,7 @@ segments:
- [0x359A4, c, 319C4]
- [0x3728C, c, e_giant_spinning_cross]
- [0x37B64, .bss, 1CB04]
- [0x37F80, .bss, 21250]
- [0x37F80, .bss, 211D0]
- [0x38000, .bss, 2A060]
- [0x38088, .bss, 2C4C4]
- [0x39894, .bss, 319C4]

View File

@ -76,8 +76,7 @@ typedef struct ET_Generic {
/* 0xAC */ s16 : 16;
/* 0xAE */ s8 unkAE;
/* 0xAF */ s8 : 8;
/* 0xB0 */ s16 unkB0;
/* 0xB2 */ s16 : 16;
/* 0xB0 */ s32 : 32;
/* 0xB4 */ s32 : 32;
union {
/* 0xB8 */ void (*unkFuncB8)(struct Entity*);

View File

@ -1349,7 +1349,7 @@ typedef struct {
/* 0x00 */ s16 attack;
/* 0x02 */ s16 heartCost;
/* 0x04 */ u16 attackElement;
/* 0x06 */ u8 unk6;
/* 0x06 */ u8 chainLimit; // how many instances of subwpn can coexist
/* 0x07 */ u8 nFramesInvincibility;
/* 0x08 */ u16 stunFrames;
/* 0x0A */ u8 anim;

View File

@ -146,7 +146,7 @@ typedef struct {
typedef struct {
/* 0x800973F8 */ s32 D_800973F8;
/* 0x800973FC */ s32 D_800973FC;
/* 0x80097400 */ s32 unk0;
/* 0x80097400 */ bool pauseEnemies; // True for Stopwatch and cutscenes
/* 0x80097404 */ s32 unk4;
/* 0x80097408 */ s32 g_zEntityCenter;
/* 0x8009740C */ s32 unkC;

View File

@ -576,7 +576,7 @@ void func_us_8018C90C(Entity* self) {
case 0:
InitializeEntity(D_80180A60);
D_8003C8B8 = 0;
g_unkGraphicsStruct.unk0 = 1;
g_unkGraphicsStruct.pauseEnemies = true;
g_Player.padSim = PAD_RIGHT;
g_Player.D_80072EFC = 1;
break;
@ -596,8 +596,8 @@ void func_us_8018C90C(Entity* self) {
if (g_CutsceneFlags & 2) {
D_8003C8B8 = 1;
if (g_unkGraphicsStruct.unk0) {
g_unkGraphicsStruct.unk0 = 0;
if (g_unkGraphicsStruct.pauseEnemies) {
g_unkGraphicsStruct.pauseEnemies = false;
}
DestroyEntity(self);

View File

@ -963,7 +963,7 @@ void func_800F2404(s32 arg0) {
}
D_8003C704 = 0;
g_unkGraphicsStruct.unk0 = 0;
g_unkGraphicsStruct.pauseEnemies = 0;
g_unkGraphicsStruct.unk18 = 0;
g_unkGraphicsStruct.unk1C = 0;
g_unkGraphicsStruct.unkC = 0x80;
@ -1495,7 +1495,7 @@ void RunMainEngine(void) {
MuteCd();
}
} else if (D_8006BB00 != 0) {
if (g_unkGraphicsStruct.unk0 != 0) {
if (g_unkGraphicsStruct.pauseEnemies != 0) {
D_80097928 = 0;
D_8006BB00 = 0;
UnMuteCd();

View File

@ -700,20 +700,20 @@ void EntityAlucard(void) {
g_Player.padPressed = g_Player.padSim & 0xFFFF;
switch (g_Player.padSim >> 0x10) { /* switch 6; irregular */
case 1: /* switch 6 */
if (PLAYER.step != 0x30) {
if (PLAYER.step != Player_Unk48) {
func_8010E168(1, 4);
SetPlayerStep(0x30);
g_unkGraphicsStruct.unk0 = 1;
SetPlayerStep(Player_Unk48);
g_unkGraphicsStruct.pauseEnemies = 1;
}
break;
case 2: /* switch 6 */
func_8010E168(1, 4);
if (g_Player.unk0C & 0x01000000) {
SetPlayerStep(0x32);
SetPlayerStep(Player_Unk50);
} else {
SetPlayerStep(0x31);
SetPlayerStep(Player_Unk49);
}
g_unkGraphicsStruct.unk0 = 1;
g_unkGraphicsStruct.pauseEnemies = 1;
break;
}
} else {

View File

@ -703,39 +703,48 @@ void func_8010EA54(s32 arg0) {
}
}
s32 func_8010EADC(s16 arg0, s16 arg1) {
Entity* entity = &g_Entities[0x20];
static s32 CheckSubwpnChainLimit(s16 subwpnId, s16 limit) {
Entity* entity;
s32 nFound;
s32 nEmpty;
s32 i;
s32 var_a2;
s32 ret;
for (i = 0, var_a2 = 0, ret = 0; i < 16; i++) {
// Iterate through entities 32-48 (which hold subweapons)
// Any that match the proposed ID increments the count.
// If at any point the count reaches the limit, return -1.
entity = &g_Entities[32];
for (i = 0, nFound = 0, nEmpty = 0; i < 16; i++, entity++) {
if (entity->entityId == E_NONE) {
ret++;
nEmpty++;
}
if (entity->ext.generic.unkB0 != 0) {
if (entity->ext.generic.unkB0 == arg0) {
var_a2++;
if (entity->ext.subweapon.subweaponId != 0 &&
entity->ext.subweapon.subweaponId == subwpnId) {
nFound++;
}
}
if (var_a2 >= arg1) {
if (nFound >= limit) {
return -1;
}
entity++;
}
// This will indicate that there is an available entity slot
// to hold the subweapon we're trying to spawn.
// At the end, if this is zero, there are none available so return
// -1 to indicate there is no room for the proposed subweapon.
if (nEmpty == 0) {
return -1;
}
return 0;
}
return (ret == 0) ? -1 : 0;
}
s32 func_8010EB5C(void) {
// Attempts to use subweapon. Performs checks before activating.
// If it succeeds, factory is called to spawn the subweapon, and return 0.
// If it fails, return a number 1 through 4 indicating why.
static s32 func_8010EB5C(void) {
SubweaponDef subWpn;
s16 subWpnId;
s32 atLedge;
u8 anim;
atLedge = 0;
// If control is not pressed
if (!(g_Player.padPressed & PAD_UP)) {
return 1;
}
@ -743,16 +752,19 @@ s32 func_8010EB5C(void) {
atLedge = 1;
}
subWpnId = func_800FE3C4(&subWpn, 0, false);
// If we don't have a subweapon obtained
if (subWpnId == 0) {
return 1;
}
if (subWpnId == 6 && g_unkGraphicsStruct.unk0 != 0) {
// If it's the stopwatch, but we're already paused
if (subWpnId == SUBWPN_STOPWATCH && g_unkGraphicsStruct.pauseEnemies) {
return 4;
}
if (func_8010EADC(subWpnId, subWpn.unk6) < 0) {
// If we already have too many of the subweapon active
if (CheckSubwpnChainLimit(subWpnId, subWpn.chainLimit) < 0) {
return 2;
}
// Should be if we don't have enough hearts?
subWpnId = func_800FE3C4(&subWpn, 0, true);
if (subWpnId == 0) {
return 3;

View File

@ -87,7 +87,7 @@ void EntityStopWatch(Entity* self) {
s32 d;
s16 offsetX, offsetY;
if (g_unkGraphicsStruct.unk0) {
if (g_unkGraphicsStruct.pauseEnemies) {
g_unkGraphicsStruct.D_800973FC = 0;
if (self->step && (self->step < 4)) {
self->step = 4;
@ -96,7 +96,7 @@ void EntityStopWatch(Entity* self) {
switch (self->step) {
case 0:
if (g_unkGraphicsStruct.unk0) {
if (g_unkGraphicsStruct.pauseEnemies) {
DestroyEntity(self);
return;
}

View File

@ -281,28 +281,3 @@ void RicSetHighJump(void) {
PLAYER.velocityY = 0;
}
}
s32 func_8015D1D0(s16 subWpnId, s16 maxParticles) {
Entity* entity;
s32 nFound;
s32 nEmpty;
s32 i;
entity = &g_Entities[32];
for (i = 0, nFound = 0, nEmpty = 0; i < 16; i++, entity++) {
if (entity->entityId == E_NONE) {
nEmpty++;
}
if (entity->ext.generic.unkB0 != 0 &&
entity->ext.generic.unkB0 == subWpnId) {
nFound++;
}
if (nFound >= maxParticles) {
return -1;
}
}
if (nEmpty == 0) {
return -1;
}
return 0;
}

View File

@ -2,6 +2,37 @@
#include "ric.h"
#include "sfx.h"
static s32 RicCheckSubwpnChainLimit(s16 subwpnId, s16 limit) {
Entity* entity;
s32 nFound;
s32 nEmpty;
s32 i;
// Iterate through entities 32-48 (which hold subweapons)
// Any that match the proposed ID increments the count.
// If at any point the count reaches the limit, return -1.
entity = &g_Entities[32];
for (i = 0, nFound = 0, nEmpty = 0; i < 16; i++, entity++) {
if (entity->entityId == E_NONE) {
nEmpty++;
}
if (entity->ext.subweapon.subweaponId != 0 &&
entity->ext.subweapon.subweaponId == subwpnId) {
nFound++;
}
if (nFound >= limit) {
return -1;
}
}
// This will indicate that there is an available entity slot
// to hold the subweapon we're trying to spawn.
// At the end, if this is zero, there are none available so return
// -1 to indicate there is no room for the proposed subweapon.
if (nEmpty == 0) {
return -1;
}
return 0;
}
s32 func_8015D250(s32 unused_arg) {
SubweaponDef subweapon;
s16 subweaponId;
@ -17,7 +48,7 @@ s32 func_8015D250(s32 unused_arg) {
if (subweapon.blueprintNum == 0) {
return 4;
}
if (func_8015D1D0(subweaponId, subweapon.unk6) < 0) {
if (RicCheckSubwpnChainLimit(subweaponId, subweapon.chainLimit) < 0) {
return 2;
}
subweaponId = func_8015FB84(&subweapon, false, true);
@ -1428,7 +1459,7 @@ void RicEntityCrashReboundStoneParticles(Entity* self);
void RicEntityHitByDark(Entity* self);
void RicEntityHitByHoly(Entity* self);
void RicEntityCrashStopwatchDoneSparkle(Entity* self);
void func_80170548(Entity* self);
void RicEntityStopwatchCrashLightning(Entity* self);
void RicEntityTeleport(Entity* self);
void RicEntityDummy(Entity* self);
static PfnEntityUpdate entity_functions[] = {
@ -1497,7 +1528,7 @@ static PfnEntityUpdate entity_functions[] = {
RicEntityHitByDark,
RicEntityHitByHoly,
RicEntityCrashStopwatchDoneSparkle,
func_80170548,
RicEntityStopwatchCrashLightning,
RicEntityTeleport,
RicEntityDummy};
STATIC_ASSERT(LEN(entity_functions) == NUM_ENTITIES, "entity array wrong size");
@ -1656,7 +1687,7 @@ FactoryBlueprint g_RicFactoryBlueprints[] = {
B_MAKE(E_HIT_BY_DARK, 96, 1, true, true, 4, B_KIND_8, 1, 0),
B_MAKE(E_HIT_BY_HOLY, 1, 1, true, true, 0, B_DECOR, 0, 0),
B_MAKE(E_CRASH_STOPWATCH_DONE_PARTICLE, 1, 1, true, true, 0, B_DECOR, 0, 0),
B_MAKE(E_80170548, 1, 1, true, true, 0, B_WPN, 0, 0),
B_MAKE(E_CRASH_STOPWATCH_LIGHTNING, 1, 1, true, true, 0, B_WPN, 0, 0),
B_MAKE(E_SMOKE_PUFF, 1, 1, true, true, 0, B_WPN, 0, 0),
B_MAKE(E_SMOKE_PUFF, 4, 1, true, true, 2, B_DECOR, 3, 0),
B_MAKE(E_SMOKE_PUFF, 6, 6, true, true, 0, B_DECOR, 0, 0),

View File

@ -237,7 +237,8 @@ void RicEntityBladeDash(Entity* self) {
self->hitboxWidth = 20;
self->hitboxOffY = 0;
self->hitboxOffX = 0;
self->ext.generic.unkB0 = 0x11;
// Wow! So blade dash is treated as a subweapon!
self->ext.subweapon.subweaponId = 17;
RicSetSubweaponParams(self);
self->step++;
}
@ -262,7 +263,8 @@ void func_80160F0C(Entity* self) {
self->hitboxOffY = -0x1A;
self->hitboxWidth = 12;
self->hitboxHeight = 12;
self->ext.generic.unkB0 = 0x16;
// High jump attack is a subweapon!
self->ext.subweapon.subweaponId = 22;
RicSetSubweaponParams(self);
self->step++;
}

View File

@ -1054,7 +1054,8 @@ void RicEntityCrashStopwatchDoneSparkle(Entity* self) {
selfY = self->posY.i.hi;
self->posX.i.hi = self->ext.et_stopWatchSparkle.unk90;
self->posY.i.hi = self->ext.et_stopWatchSparkle.unk92;
RicCreateEntFactoryFromEntity(self, BP_73, 0);
RicCreateEntFactoryFromEntity(
self, BP_CRASH_STOPWATCH_LIGHTNING, 0);
self->posX.i.hi = selfX;
self->posY.i.hi = selfY;
} else {
@ -1078,19 +1079,25 @@ void RicEntityCrashStopwatchDoneSparkle(Entity* self) {
}
}
void func_80170548(Entity* entity) {
switch (entity->step) {
// Created by blueprint #73.
// When Stopwatch crash ends, each of the 4 stopwatches shoots out a lightning
// Each lightning can harm enemies. This entity represents the attacking part
// of that lightning. It does not do any graphics and just has the hitbox.
// Not clear why this is a dedicated entity rather than having one entity
// that is graphics + hitbox for the lightning.
void RicEntityStopwatchCrashLightning(Entity* self) {
switch (self->step) {
case 0:
entity->flags = FLAG_KEEP_ALIVE_OFFCAMERA;
entity->ext.generic.unkB0 = 0x1E;
RicSetSubweaponParams(entity);
entity->hitboxWidth = 8;
entity->hitboxHeight = 8;
entity->step++;
self->flags = FLAG_KEEP_ALIVE_OFFCAMERA;
self->ext.subweapon.subweaponId = 0x1E;
RicSetSubweaponParams(self);
self->hitboxWidth = 8;
self->hitboxHeight = 8;
self->step++;
break;
case 1:
if (++entity->ext.timer.t >= 5) {
DestroyEntity(entity);
if (++self->ext.timer.t >= 5) {
DestroyEntity(self);
}
break;
}
@ -1554,7 +1561,7 @@ void RicEntitySubwpnStopwatch(Entity* self) {
s32 temp_a1_3;
s32 temp_t0;
s32 temp_v1_11;
if (g_unkGraphicsStruct.unk0) {
if (g_unkGraphicsStruct.pauseEnemies) {
g_unkGraphicsStruct.D_800973FC = 0;
if ((self->step > 0) && (self->step < 4)) {
self->step = 4;

View File

@ -152,7 +152,7 @@ enum RicEntities {
E_HIT_BY_DARK, // RicEntityHitByDark
E_HIT_BY_HOLY, // RicEntityHitByHoly
E_CRASH_STOPWATCH_DONE_PARTICLE, // RicEntityCrashStopwatchDoneSparkle
E_80170548, // func_80170548
E_CRASH_STOPWATCH_LIGHTNING, // RicEntityStopwatchCrashLightning
E_TELEPORT, // RicEntityTeleport
E_DUMMY_66, // RicEntityDummy
NUM_ENTITIES,
@ -232,7 +232,7 @@ enum RicBlueprints {
BP_HIT_BY_DARK,
BP_HIT_BY_HOLY,
BP_AGUNEA_THUNDER,
BP_73,
BP_CRASH_STOPWATCH_LIGHTNING,
BP_SMOKE_PUFF_2,
BP_SKID_SMOKE_2,
BP_SKID_SMOKE_3,

View File

@ -113,7 +113,7 @@ void EntityPlatform(Entity* self) {
if ((GetDistanceToPlayerX() < 32) &&
((self->posY.i.hi - player->posY.i.hi) < 80)) {
D_8003C8B8 = 0;
g_unkGraphicsStruct.unk0 = 1;
g_unkGraphicsStruct.pauseEnemies = 1;
if (g_Player.unk0C & PLAYER_STATUS_BAT_FORM) {
g_Player.padSim = PAD_R1;
} else if (g_Player.unk0C & PLAYER_STATUS_MIST_FORM) {
@ -227,8 +227,8 @@ void EntityPlatform(Entity* self) {
D_80097488.x.i.hi++;
} else {
D_8003C8B8 = 1;
if (g_unkGraphicsStruct.unk0 != 0) {
g_unkGraphicsStruct.unk0 = 0;
if (g_unkGraphicsStruct.pauseEnemies != 0) {
g_unkGraphicsStruct.pauseEnemies = 0;
}
g_Entities[1].ext.generic.unk7C.S8.unk0 = 1;
self->step++;

View File

@ -27,7 +27,7 @@ void EntityCSMoveAlucard(Entity* self) {
case 0:
InitializeEntity(D_8018047C);
D_8003C8B8 = 0;
g_unkGraphicsStruct.unk0 = 1;
g_unkGraphicsStruct.pauseEnemies = 1;
g_Player.padSim = 0;
g_Player.D_80072EFC = 1;
if (g_DemoMode != Demo_None) {
@ -129,8 +129,8 @@ void EntityCSMoveAlucard(Entity* self) {
func_801961DC(0x80);
if (g_unkGraphicsStruct.unkC == 0x80) {
D_8003C8B8 = 1;
if (g_unkGraphicsStruct.unk0 != 0) {
g_unkGraphicsStruct.unk0 = 0;
if (g_unkGraphicsStruct.pauseEnemies != 0) {
g_unkGraphicsStruct.pauseEnemies = 0;
}
DestroyEntity(self);
}
@ -150,7 +150,7 @@ void EntityUnkId23(Entity* self) {
case 0:
InitializeEntity(D_8018047C);
D_8003C8B8 = 0;
g_unkGraphicsStruct.unk0 = 1;
g_unkGraphicsStruct.pauseEnemies = 1;
g_Player.padSim = 0;
if (g_Player.unk0C & PLAYER_STATUS_BAT_FORM) {
g_Player.padSim = PAD_R1;
@ -228,8 +228,8 @@ void EntityUnkId23(Entity* self) {
case 5:
D_8003C8B8 = 1;
if (g_unkGraphicsStruct.unk0 != 0) {
g_unkGraphicsStruct.unk0 = 0;
if (g_unkGraphicsStruct.pauseEnemies != 0) {
g_unkGraphicsStruct.pauseEnemies = 0;
}
player->posY.i.hi = player->posY.i.hi + 0x100;
g_Player.padSim = 0;

View File

@ -7,7 +7,7 @@ void func_801B8E0C(Entity* self) {
case 0:
InitializeEntity(g_MariaInit);
D_8003C8B8 = 0;
g_unkGraphicsStruct.unk0 = 1;
g_unkGraphicsStruct.pauseEnemies = 1;
g_Player.padSim = PAD_LEFT;
if (g_Player.unk0C & PLAYER_STATUS_WOLF_FORM) {
g_Player.padSim = PAD_R2;
@ -35,8 +35,8 @@ void func_801B8E0C(Entity* self) {
case 2:
if (g_CutsceneFlags & 0x2000) {
D_8003C8B8 = 1;
if (g_unkGraphicsStruct.unk0 != 0) {
g_unkGraphicsStruct.unk0 = 0;
if (g_unkGraphicsStruct.pauseEnemies != 0) {
g_unkGraphicsStruct.pauseEnemies = 0;
}
DestroyEntity(self);
}