From 017f90e3cfed73e1964793af1cb0e234c54e0bc2 Mon Sep 17 00:00:00 2001 From: Josh Schreuder <681706+JoshSchreuder@users.noreply.github.com> Date: Sat, 26 Oct 2024 21:45:01 +1100 Subject: [PATCH] Decompile `no0` Slinger functions (#1830) That's these guys in Marble Gallery ![image](https://github.com/user-attachments/assets/502158ca-2629-443a-a3a9-a2d01ca1ec4f) They share quite a bit in common with e_skeleton and e_bone_scimitar, but I decided not to deduplicate these ones at this stage --- config/splat.us.stno0.yaml | 6 + config/symbols.us.stno0.txt | 6 +- include/sfx.h | 2 +- src/st/no0/57C20.c | 24 ++++ src/st/no0/e_slinger.c | 231 ++++++++++++++++++++++++++++++++++++ src/st/no0/no0.h | 2 + src/st/no0/unk_4F4A8.c | 28 ----- 7 files changed, 268 insertions(+), 31 deletions(-) create mode 100644 src/st/no0/57C20.c create mode 100644 src/st/no0/e_slinger.c diff --git a/config/splat.us.stno0.yaml b/config/splat.us.stno0.yaml index b16908e3c..0ef010e78 100644 --- a/config/splat.us.stno0.yaml +++ b/config/splat.us.stno0.yaml @@ -56,6 +56,8 @@ segments: - [0x1C14, data] - [0x1C34, data, clock_room] - [0x1CA0, data] + - [0x2194, .data, e_slinger] + - [0x2268, data] - [0x2478, .data, e_axe_knight] - [0x25CC, data] - [0x2680, .data, e_skeleton] @@ -69,6 +71,8 @@ segments: - [0x414A8, .rodata, 4C750] - [0x414B8, .rodata, e_stage_name] - [0x414EC, .rodata, unk_4F4A8] + - [0x4171C, .rodata, e_slinger] + - [0x41738, .rodata, 57C20] - [0x4178C, .rodata, e_axe_knight] - [0x417B0, .rodata, 5BDCC] - [0x41818, .rodata, e_skeleton] @@ -95,6 +99,8 @@ segments: - [0x4E2E0, c] - [0x4E654, c, e_stage_name] - [0x4F4A8, c, unk_4F4A8] + - [0x575E4, c, e_slinger] + - [0x57C20, c, 57C20] - [0x5AEBC, c, e_axe_knight] - [0x5BDCC, c, 5BDCC] - [0x5CAB0, c, e_skeleton] diff --git a/config/symbols.us.stno0.txt b/config/symbols.us.stno0.txt index f019ba301..2b8e6403b 100644 --- a/config/symbols.us.stno0.txt +++ b/config/symbols.us.stno0.txt @@ -9,7 +9,8 @@ g_EInitUnkId13 = 0x80180AD0; g_EInitCommon = 0x80180AE8; g_EInitDamageNum = 0x80180B18; g_EInitElevator = 0x80180B60; -D_80180C94 = 0x80180BC0; +g_EInitSlinger = 0x80180BB4; +g_EInitSlingerPieces = 0x80180BC0; g_EInitAxeKnight = 0x80180BFC; D_80180C6A = 0x80180C02; g_EInitAxeKnightAxe = 0x80180C08; @@ -66,7 +67,6 @@ anim_bird_cage = 0x80181C5C; anim_gear_1 = 0x80181C78; anim_gear_2 = 0x80181C84; g_StoneDoorTiles = 0x80181C90; -anim_bone_rot = 0x801821DC; steps = 0x801825AC; init_velocity_x = 0x801825B4; init_velocity_y = 0x801825C0; @@ -181,6 +181,8 @@ EntityClockRoomUnused = 0x801CE2D8; StageNamePopupHelper = 0x801CE654; EntityStageNamePopup = 0x801CE824; func_801CD78C = 0x801D2374; +EntitySlinger = 0x801D7670; +EntitySlingerPieces = 0x801D7B40; func_801C3F9C = 0x801DAEBC; func_801C4198 = 0x801DB0B8; func_801C4550 = 0x801DB470; diff --git a/include/sfx.h b/include/sfx.h index d19ef4cfa..74367b723 100644 --- a/include/sfx.h +++ b/include/sfx.h @@ -323,7 +323,7 @@ enum Sfx { SFX_ARROW_SHOT_C, SFX_ARROW_SHOT_D, SFX_SKELETON_DEATH_A, - SFX_SKELETON_DEATH_B, + SFX_SKELETON_DEATH_B, // Slinger SFX_SKELETON_DEATH_C, SFX_FIRE_SHOT, SFX_WEAPON_STAB_A, diff --git a/src/st/no0/57C20.c b/src/st/no0/57C20.c new file mode 100644 index 000000000..0c60adf1c --- /dev/null +++ b/src/st/no0/57C20.c @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +#include "common.h" + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801D7C20); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801D7D00); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801D7DAC); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801D8150); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801D8DF0); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801D8FFC); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801D91C4); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801D9264); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801DA488); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801DA6B4); + +INCLUDE_ASM("st/no0/nonmatchings/57C20", func_us_801DADD0); diff --git a/src/st/no0/e_slinger.c b/src/st/no0/e_slinger.c new file mode 100644 index 000000000..e8cf8b4bc --- /dev/null +++ b/src/st/no0/e_slinger.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +#include "no0.h" + +typedef enum { + SLINGER_INIT, + SLINGER_IDLE, + SLINGER_WALK_TOWARDS_PLAYER, + SLINGER_WALK_AWAY_FROM_PLAYER, + SLINGER_ATTACK, + SLINGER_JUMP, + SLINGER_DESTROY +} SLINGER_STEPS; + +typedef enum { + SLINGER_JUMPING, + SLINGER_IN_AIR, + SLINGER_LAND +} SlingerJumpSubSteps; + +static u8 anim_walk[] = {0x06, 0x01, 0x04, 0x02, 0x04, 0x03, 0x06, + 0x04, 0x05, 0x05, 0x05, 0x06, 0x00}; +static u8 anim_walk_backwards[] = {0x06, 0x01, 0x05, 0x06, 0x05, 0x05, 0x06, + 0x04, 0x04, 0x03, 0x04, 0x02, 0x00}; +static u8 anim_throw_bone[] = {0x05, 0x07, 0x06, 0x08, 0x05, 0x09, 0x05, + 0x0A, 0x05, 0x0B, 0x05, 0x0C, 0xFF, 0x00}; +static u8 anim_jump1[] = {0x01, 0x01, 0x04, 0x0D, 0x04, 0x0E, 0x01, 0x01, 0x00}; +static u8 anim_jump2[] = { + 0x01, 0x01, 0x04, 0x0D, 0x06, 0x0E, 0x04, 0x0D, 0x01, 0x01, 0x00}; +static u16 anim_bone_rot[] = { + 0x0100, 0x0080, 0x0048, 0x0020, 0x0040, 0x0010, 0x0030, 0x0000}; +static s8 dead_parts_selector[] = { + 0x30, 0x20, 0x14, 0x0C, 0x18, 0x10, 0x20, 0x00}; +static s32 dead_parts_velocity_x[] = { + FIX(.75), FIX(1.75), FIX(1.5), FIX(1), FIX(2), FIX(1.75), 0}; +static s32 dead_parts_velocity_y[] = { + FIX(-5), FIX(-3), FIX(-2), FIX(-3), FIX(-4), FIX(-.875), FIX(-4)}; +static u16 dead_parts_pos_x[] = {-4, 0, 4, -4, -4, 4, 0, 0}; +static u16 dead_parts_pos_y[] = {-16, -8, -4, -4, 9, 9, 0, 0}; +static u8 attack_timer_cycles[2][4] = { + {0x80, 0x08, 0x08, 0x40}, {0xF0, 0xC0, 0xA0, 0x80}}; +static s16 sensors_ground[][2] = {{0, 19}, {8, 0}}; +static s16 sensors_move[][2] = {{-12, 16}, {0, -16}, {0, -16}}; + +void SlingerAttackCheck(Entity* self) { + s32 groundCollision = UnkCollisionFunc2(sensors_ground); + s16 moveCollision = UnkCollisionFunc(sensors_move, 3); + + if ((groundCollision & 0xFFFF) == 128 && (moveCollision & 2) != 0) { + SetStep(SLINGER_JUMP); + } else if (--self->ext.skeleton.attackTimer == 0) { + SetStep(SLINGER_ATTACK); + } +} + +extern u16 g_EInitSlinger[]; +void EntitySlinger(Entity* self) { + Entity* newEntity; + u8 animStatus; + u8 i; + + if (self->flags & FLAG_DEAD) { + self->step = SLINGER_DESTROY; + } + + switch (self->step) { + case SLINGER_INIT: + InitializeEntity(g_EInitSlinger); + self->ext.skeleton.attackTimer = 80; // Slinger attack timer cycle + self->ext.skeleton.facingLeft = 0; // Facing init + self->ext.skeleton.attackTimerIndex = 0; + break; + case SLINGER_IDLE: // Wait for player to be close enough + if (UnkCollisionFunc3(sensors_ground) != 0) { + self->step++; + } + break; + case SLINGER_WALK_TOWARDS_PLAYER: + self->facingLeft = (GetSideToPlayer() & 1) ^ 1; + self->ext.skeleton.facingLeft = self->facingLeft; + AnimateEntity(anim_walk, self); + + if (self->ext.skeleton.facingLeft == 0) { + self->velocityX = FIX(-0.5); + } else { + self->velocityX = FIX(0.5); + } + + if (GetDistanceToPlayerX() < 76) { + self->step = SLINGER_WALK_AWAY_FROM_PLAYER; + } + SlingerAttackCheck(self); + break; + case SLINGER_WALK_AWAY_FROM_PLAYER: + self->facingLeft = (GetSideToPlayer() & 1) ^ 1; + self->ext.skeleton.facingLeft = self->facingLeft ^ 1; + AnimateEntity(anim_walk_backwards, self); + + if (self->ext.skeleton.facingLeft == 0) { + self->velocityX = FIX(-0.5); + } else { + self->velocityX = FIX(0.5); + } + + if (GetDistanceToPlayerX() > 92) { + self->step = SLINGER_WALK_TOWARDS_PLAYER; + } + SlingerAttackCheck(self); + break; + case SLINGER_ATTACK: + animStatus = AnimateEntity(anim_throw_bone, self); + if (!animStatus) { + SetStep(SLINGER_WALK_AWAY_FROM_PLAYER); + self->ext.skeleton.attackTimerIndex++; + self->ext.skeleton.attackTimer = + attack_timer_cycles[self->params & 1] + [self->ext.skeleton.attackTimerIndex & 3]; + break; + } + + if ((animStatus & 0x80) && (self->animCurFrame == 10)) { + if (((u32)(((u16)self->posX.i.hi + 16) & 0xFFFF) <= 288) && + ((u16)self->posY.i.hi <= 240)) { + newEntity = AllocEntity(g_Entities + 160, g_Entities + 192); + if (newEntity != NULL) { // Spawn bone + PlaySfxPositional(SFX_BONE_THROW); + CreateEntityFromCurrentEntity( + E_SLINGER_THROWN_BONE, newEntity); + if (self->facingLeft) { + newEntity->posX.i.hi -= 8; + } else { + newEntity->posX.i.hi += 8; + } + newEntity->posY.i.hi -= 16; + newEntity->facingLeft = self->facingLeft; + } + } + } + break; + case SLINGER_JUMP: + switch (self->step_s) { + case SLINGER_JUMPING: + if (!(AnimateEntity(anim_jump1, self) & 1)) { + u8 facing_ = self->ext.skeleton.facingLeft; + s32 facing; + + if (Random() % 4) { + facing = facing_; + } else { + facing_ ^= 1; + facing = facing_; + } + + if (facing == 0) { + self->velocityX = FIX(-2); + } else { + self->velocityX = FIX(2); + } + + self->velocityY = FIX(-3); + self->animFrameIdx = 0; + self->animFrameDuration = 0; + self->step_s++; + } + break; + case SLINGER_IN_AIR: + if (UnkCollisionFunc3(sensors_ground) != 0) { + self->step_s++; + } + CheckFieldCollision(sensors_move, 2); + break; + case SLINGER_LAND: + if (AnimateEntity(anim_jump2, self) & 1) { + self->step_s = 0; + SetStep(SLINGER_WALK_AWAY_FROM_PLAYER); + } + } + break; + case SLINGER_DESTROY: + PlaySfxPositional(SFX_SKELETON_DEATH_B); + for (i = 0; i < 7; i++) { // Spawn Slinger pieces + newEntity = AllocEntity(&g_Entities[224], &g_Entities[256]); + if (newEntity != NULL) { + CreateEntityFromCurrentEntity(E_SLINGER_PIECES, newEntity); + newEntity->facingLeft = self->facingLeft; + newEntity->params = i; + newEntity->ext.skeleton.explosionTimer = dead_parts_selector[i]; + if (self->facingLeft) { + newEntity->posX.i.hi -= dead_parts_pos_x[i]; + } else { + newEntity->posX.i.hi += dead_parts_pos_x[i]; + } + newEntity->posY.i.hi += dead_parts_pos_y[i]; + newEntity->velocityX = dead_parts_velocity_x[i]; + newEntity->velocityY = dead_parts_velocity_y[i]; + } else { + break; + } + } + DestroyEntity(self); + break; + } +} + +// Bone parts that rotate and fall down when killed +// This is a duplicate of EntitySkeletonPieces and EntityBoneScimitarParts +extern u16 g_EInitSlingerPieces[]; +void EntitySlingerPieces(Entity* self) { + if (self->step) { + self->ext.skeleton.explosionTimer--; + if (self->ext.skeleton.explosionTimer & 0xFF) { + self->rotZ += anim_bone_rot[self->params]; + FallEntity(); + MoveEntity(); + return; + } + + self->entityId = E_EXPLOSION; + self->pfnUpdate = (PfnEntityUpdate)EntityExplosion; + self->params = 0; + self->step = 0; + return; + } + + InitializeEntity(g_EInitSlingerPieces); + self->drawFlags = FLAG_DRAW_ROTZ; + self->animCurFrame = self->params + 15; + + if (self->facingLeft) { + self->velocityX = -self->velocityX; + } +} diff --git a/src/st/no0/no0.h b/src/st/no0/no0.h index 977f35eee..d86b43e70 100644 --- a/src/st/no0/no0.h +++ b/src/st/no0/no0.h @@ -34,6 +34,8 @@ typedef enum EntityIDs { /* 0x14 */ E_ID_14 = 0x14, /* 0x15 */ E_GREY_PUFF, /* 0x1D */ E_CLOCK_ROOM_SHADOW = 0x20, + /* 0x3B */ E_SLINGER_THROWN_BONE = 0x3B, + /* 0x3C */ E_SLINGER_PIECES, /* 0x48 */ E_AXE_KNIGHT_AXE = 0x48, /* 0x49 */ E_WARG_EXP_OPAQUE, /* 0x4D */ E_SKELETON = 0x4D, diff --git a/src/st/no0/unk_4F4A8.c b/src/st/no0/unk_4F4A8.c index a55052889..9865c5aaa 100644 --- a/src/st/no0/unk_4F4A8.c +++ b/src/st/no0/unk_4F4A8.c @@ -107,31 +107,3 @@ INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D6254); INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D6474); INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D66F8); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D75E4); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D7670); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D7B40); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D7C20); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D7D00); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D7DAC); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D8150); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D8DF0); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D8FFC); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D91C4); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801D9264); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801DA488); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801DA6B4); - -INCLUDE_ASM("st/no0/nonmatchings/unk_4F4A8", func_us_801DADD0);