Decompile no0 Skelerang entities (#1900)

~~Planning on giving this a tidy up when I import data and do some
analysis in debugger so I'll fix up some of the naming then unless
there's something glaring that sticks out here.~~

`func_us_801D191C` / `EntitySkelerangUnknown` was previous decompiled
but slightly updated here for PSP match:
https://decomp.me/scratch/8lEH0

`func_us_801D20A4` / `EntitySkelerangBoomerang` matching both
PSX: https://decomp.me/scratch/SMgwb
PSP: https://decomp.me/scratch/WFD0w

`func_us_801D191C` / `EntitySkelerang` matching both
PSX: https://decomp.me/scratch/BUkq2
PSP: https://decomp.me/scratch/OYg4D

Thanks to Discord gang for helping with some PSP stuff here ^
This commit is contained in:
Josh Schreuder 2024-11-15 12:57:00 +11:00 committed by GitHub
parent fe8c58f3a1
commit dfea3429ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 369 additions and 18 deletions

View File

@ -185,6 +185,9 @@ EntityStoneDoor = 0x801CE0F8;
EntityClockRoomUnused = 0x801CE2D8;
StageNamePopupHelper = 0x801CE654;
EntityStageNamePopup = 0x801CE824;
EntitySkelerang = 0x801D191C;
EntitySkelerangBoomerang = 0x801D20A4;
EntitySkelerangUnknown = 0x801D2318;
func_801CD78C = 0x801D2374;
EntityGhostEnemy = 0x801D5E4C;
EntityGhostEnemySpawner = 0x801D606C;

View File

@ -84,6 +84,7 @@
#define LOHU(x) (*(u16*)&(x))
#define LOW(x) (*(s32*)&(x))
#define LOWU(x) (*(u32*)&(x))
#define F(x) (*(f32*)&(x))
#if defined(HACKS) && !defined(PERMUTER)
#define ALIGNED4 __attribute__((aligned(4)))

View File

@ -2055,10 +2055,6 @@ typedef struct {
/* 0x84 */ u32 speed;
} ET_GhostEnemy;
// ====== RIC ENTITIES ======
// ==========================
typedef struct {
/* 0x7C */ struct Entity* next;
/* 0x80 */ s16 timer;
@ -2081,6 +2077,14 @@ typedef struct {
/* 0x94 */ s16 unk94;
} ET_StoneRose;
typedef struct {
/* 0x7C */ s32 : 32;
/* 0x80 */ u8 angle;
/* 0x81 */ u8 : 8;
/* 0x82 */ u16 : 16;
/* 0x84 */ s16 unk84;
} ET_Skelerang;
typedef union { // offset=0x7C
struct Primitive* prim;
ET_Placeholder ILLEGAL;
@ -2257,6 +2261,7 @@ typedef union { // offset=0x7C
ET_StoneRose stoneRose;
ET_GhostEnemy ghostEnemy;
ET_GhostEnemySpawner ghostEnemySpawner;
ET_Skelerang skelerang;
} Ext;
#define SYNC_FIELD(struct1, struct2, field) \

View File

@ -46,9 +46,9 @@ void func_us_801C27A4(Entity*);
void func_us_801C2A34(Entity*);
void func_us_801C2CD8(Entity*);
void func_us_801C2E7C(Entity*);
void func_us_801D191C(Entity*);
void func_us_801D20A4(Entity*);
void func_us_801D2318(Entity*);
void EntitySkelerang(Entity*);
void EntitySkelerangBoomerang(Entity*);
void EntitySkelerangUnknown(Entity*);
void func_us_801D2A64(Entity*);
void func_us_801D4324(Entity*);
void func_us_801D44A0(Entity*);
@ -129,9 +129,9 @@ PfnEntityUpdate OVL_EXPORT(EntityUpdates)[] = {
func_us_801C2A34,
func_us_801C2CD8,
func_us_801C2E7C,
func_us_801D191C,
func_us_801D20A4,
func_us_801D2318,
EntitySkelerang,
EntitySkelerangBoomerang,
EntitySkelerangUnknown,
func_us_801D2A64,
func_us_801D4324,
func_us_801D44A0,
@ -189,8 +189,8 @@ EInit D_us_80180B3C = {ANIMSET_OVL(0x09), 0x08, 0x4B, 0x20E, 0x005};
EInit D_us_80180B48 = {ANIMSET_OVL(0x09), 0x16, 0x4B, 0x20E, 0x012};
EInit D_us_80180B54 = {ANIMSET_OVL(0x0D), 0x00, 0x4E, 0x2C0, 0x013};
EInit g_EInitElevator = {ANIMSET_OVL(0x0B), 0x01, 0x48, 0x223, 0x005};
EInit D_us_80180B6C = {ANIMSET_OVL(0x04), 0x01, 0x48, 0x228, 0x00B};
EInit D_us_80180B78 = {ANIMSET_OVL(0x04), 0x2B, 0x48, 0x228, 0x00C};
EInit g_EInitSkelerang = {ANIMSET_OVL(0x04), 0x01, 0x48, 0x228, 0x00B};
EInit g_EInitSkelerangBoomerang = {ANIMSET_OVL(0x04), 0x2B, 0x48, 0x228, 0x00C};
EInit D_us_80180B84 = {ANIMSET_OVL(0x05), 0x01, 0x4C, 0x22B, 0x061};
EInit D_us_80180B90 = {ANIMSET_OVL(0x05), 0x16, 0x4C, 0x22B, 0x062};
EInit g_EInitGhostEnemy = {ANIMSET_OVL(0x06), 0x01, 0x4A, 0x200, 0x09C};

View File

@ -2,17 +2,351 @@
#include "common.h"
#include "no0.h"
INCLUDE_ASM("st/no0/nonmatchings/e_skelerang", func_us_801D191C);
typedef enum {
SKELERANG_INIT,
SKELERANG_GROUND_INIT,
SKELERANG_IDLE = 3,
SKELERANG_ATTACK_INIT,
SKELERANG_WAKE_ANIM,
SKELERANG_BOOMERANG_CHECK,
SKELERANG_ATTACK_PREAMBLE,
SKELERANG_COWER,
SKELERANG_POST_ATTACK,
SKELERANG_DEATH_COWER,
SKELERANG_DEATH = 12,
SKELERANG_DEATH_FLY = 14,
SKELERANG_DEBUG = 16
} SkelerangSteps;
INCLUDE_ASM("st/no0/nonmatchings/e_skelerang", func_us_801D20A4);
typedef enum {
BOOMERANG_INIT,
BOOMERANG_PRETHROW,
BOOMERANG_FLY,
BOOMERANG_IN_HAND,
BOOMERANG_DESTROY
} SkelerangBoomerangSteps;
extern u16 g_EInitInteractable[];
extern u16 D_us_80181E94[]; // sensors_ground
extern Point16 D_us_80181EA4[]; // positions
extern u8 D_us_80181EC8[]; // anim
extern u8 D_us_80181EDC[]; // anim
extern u8 D_us_80181F00[]; // anim
extern u8 D_us_80181F34[]; // anim
extern u8 D_us_80181F38[]; // anim
extern u8 D_us_80181F4C[]; // anim
extern u8 D_us_80181F60[]; // anim
void func_us_801D2318(Entity* entity) {
if (entity->step == 0) {
void EntitySkelerang(Entity* self) {
Entity* entity;
Entity* entityTwo;
s32 i;
u8 index;
if (self->step % 2 && GetDistanceToPlayerY() < 32 &&
GetDistanceToPlayerX() < 80) {
SetStep(SKELERANG_COWER);
}
if ((self->flags & FLAG_DEAD) && self->step < 10) {
self->hitboxState = 0;
PlaySfxPositional(SFX_SKELETON_DEATH_C);
DestroyEntity(self + 1);
(self + 2)->step = BOOMERANG_DESTROY;
(self + 3)->step = BOOMERANG_DESTROY;
if (self->animCurFrame > 39) {
SetStep(SKELERANG_DEATH_COWER);
} else {
SetStep(SKELERANG_DEATH);
}
}
switch (self->step) {
case SKELERANG_INIT:
InitializeEntity(g_EInitSkelerang);
CreateEntityFromEntity(E_SKELERANG_UNK, self, self + 1);
entity = self + 2;
CreateEntityFromEntity(E_SKELERANG_BOOMERANG, self, entity);
entity->params = 0;
entity->facingLeft = self->params;
entity = self + 3;
CreateEntityFromEntity(E_SKELERANG_BOOMERANG, self, entity);
entity->params = 1;
entity->facingLeft = self->params;
self->facingLeft = self->params;
break;
case SKELERANG_GROUND_INIT:
if (UnkCollisionFunc3(D_us_80181E94) & 1) {
SetStep(SKELERANG_IDLE);
}
break;
case SKELERANG_IDLE:
self->animCurFrame = 1;
// When player gets close enough shift into wake up animation
if (!(self->posY.i.hi & 256) && GetDistanceToPlayerX() < 128) {
if (((GetSideToPlayer() & 1) ^ 1) == self->facingLeft) {
SetStep(SKELERANG_WAKE_ANIM);
}
}
break;
case SKELERANG_WAKE_ANIM:
if (!AnimateEntity(D_us_80181EC8, self)) {
SetStep(SKELERANG_ATTACK_INIT);
}
break;
case SKELERANG_POST_ATTACK:
if (!self->step_s) {
self->ext.skelerang.unk84 = 0;
self->step_s++;
}
if (!AnimateEntity(D_us_80181EDC, self)) {
self->ext.skelerang.unk84++;
}
if (self->ext.skelerang.unk84 == 3) {
self->ext.skelerang.unk84 = 0;
// If player is still in range after throwing 3 boomerangs, keep
// attacking, else return to idle
if (GetDistanceToPlayerX() < 144 && GetDistanceToPlayerY() < 144 &&
((GetSideToPlayer() & 1) ^ 1) == self->facingLeft) {
SetStep(SKELERANG_ATTACK_INIT);
} else {
SetStep(SKELERANG_IDLE);
}
}
break;
case SKELERANG_ATTACK_INIT:
// Play catch animation
if (!AnimateEntity(D_us_80181F00, self)) {
SetStep(SKELERANG_BOOMERANG_CHECK);
}
if (self->animCurFrame == 30) {
// Spawn thrown boomerangs
PlaySfxPositional(SFX_THROW_WEAPON_SWISHES);
entityTwo = &PLAYER;
entity = self + 1;
entity->posX.i.hi = entityTwo->posX.i.hi;
entity->posY.i.hi = entityTwo->posY.i.hi;
entity = self + 2;
entity->posX.i.hi = self->posX.i.hi;
entity->posY.i.hi = self->posY.i.hi;
entity->animCurFrame = 43;
entity->step++;
entity = self + 3;
entity->posX.i.hi = self->posX.i.hi;
entity->posY.i.hi = self->posY.i.hi;
entity->animCurFrame = 43;
entity->step++;
}
break;
case SKELERANG_BOOMERANG_CHECK:
entity = self + 2;
entityTwo = self + 3;
// If boomerangs are back in hand, prep for another attack
if (entity->step == BOOMERANG_IN_HAND &&
entityTwo->step == BOOMERANG_IN_HAND) {
entity->step = BOOMERANG_PRETHROW;
entityTwo->step = BOOMERANG_PRETHROW;
SetStep(SKELERANG_ATTACK_PREAMBLE);
}
break;
case SKELERANG_ATTACK_PREAMBLE:
if (!AnimateEntity(D_us_80181F34, self)) {
self->ext.skelerang.unk84++;
// Throw boomerangs 3 times, then return to attack init
if (self->ext.skelerang.unk84 > 2) {
SetStep(SKELERANG_POST_ATTACK);
} else {
SetStep(SKELERANG_ATTACK_INIT);
}
}
break;
case SKELERANG_COWER:
switch (self->step_s) {
case 0:
if (!AnimateEntity(D_us_80181F38, self)) {
self->animFrameIdx = 0;
self->animFrameDuration = 0;
self->step_s++;
}
break;
case 1:
// Play cower animation until player gets far enough away
AnimateEntity(D_us_80181F60, self);
if ((GetDistanceToPlayerX() > 80) ||
(GetDistanceToPlayerY() > 48)) {
self->animFrameIdx = 0;
self->animFrameDuration = 0;
self->step_s++;
}
break;
case 2:
// Once player is far enough away move out of cower and back into
// potential attack state
if (!AnimateEntity(D_us_80181F4C, self)) {
SetStep(SKELERANG_POST_ATTACK);
}
break;
}
break;
case SKELERANG_DEATH_COWER:
entity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (entity != NULL) {
CreateEntityFromEntity(2, self, entity);
entity->params = 2;
entity->posY.i.hi += 24;
}
DestroyEntity(self);
break;
case SKELERANG_DEATH:
for (i = 0; i < 8; i++) {
entity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (entity != NULL) {
MakeEntityFromId(E_SKELERANG, self, entity);
entity->flags = FLAG_DESTROY_IF_OUT_OF_CAMERA |
FLAG_POS_CAMERA_LOCKED | FLAG_UNK_2000;
entity->hitboxState = 0;
entity->animCurFrame = i + 44;
entity->params = i;
entity->facingLeft = self->facingLeft;
if ((GetSideToPlayer() & 1) ^ 1) {
// Sus store to Entity 0xA
F(entity->velocityX).i.hi = -(Random() & 3);
} else {
// Sus store to Entity 0xA
// This + 1 - 1 is an oddity that is required to align PSP
F(entity->velocityX).i.hi = (Random() & 3) + 1 - 1;
}
// Sus store to Entity 0xE
F(entity->velocityY).i.hi = -2 - (Random() & 3);
entity->ext.skelerang.unk84 = 16;
entity->step = SKELERANG_DEATH_FLY;
}
}
entity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (entity != NULL) {
CreateEntityFromEntity(2, self, entity);
entity->params = 2;
}
DestroyEntity(self);
break;
case SKELERANG_DEATH_FLY:
// When dying body goes flying back in flames
MoveEntity();
self->velocityY += FIX(0.1875);
if (!--self->ext.skelerang.unk84) {
entity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (entity != NULL) {
CreateEntityFromEntity(2, self, entity);
index = self->params;
if (self->facingLeft) {
entity->posX.i.hi -= D_us_80181EA4[index].x;
} else {
entity->posX.i.hi += D_us_80181EA4[index].x;
}
entity->posY.i.hi += D_us_80181EA4[index].y;
}
DestroyEntity(self);
}
break;
case SKELERANG_DEBUG:
#include "../pad2_anim_debug.h"
}
if (self->animCurFrame > 39) {
self->hitboxOffX = -1;
self->hitboxOffY = 16;
self->hitboxWidth = 8;
self->hitboxHeight = 8;
} else {
self->hitboxOffX = 0;
self->hitboxOffY = 2;
self->hitboxWidth = 5;
self->hitboxHeight = 21;
}
}
void EntitySkelerangBoomerang(Entity* self) {
Entity* entity;
u8 step;
s16 angle;
s16 posX;
s16 posY;
switch (self->step) {
case BOOMERANG_INIT:
InitializeEntity(g_EInitSkelerangBoomerang);
self->drawFlags |= FLAG_DRAW_ROTZ;
self->animCurFrame = 0;
self->hitboxState = 0;
break;
case BOOMERANG_PRETHROW:
entity = (self - 2) - self->params;
self->posX.i.hi = entity->posX.i.hi;
self->posY.i.hi = entity->posY.i.hi;
self->hitboxState = 0;
self->animCurFrame = 0;
self->step_s = 0;
self->ext.skelerang.unk84 = 48;
if (self->params) {
self->ext.skelerang.angle = 0;
return;
}
self->ext.skelerang.angle = 128;
break;
case BOOMERANG_FLY:
self->hitboxState = 1;
MoveEntity();
self->rotZ += 256;
if (!self->ext.skelerang.unk84) {
self->step_s = 1;
} else {
self->ext.skelerang.unk84--;
}
entity = (self - 1) - self->params - self->step_s;
angle = GetAngleBetweenEntitiesShifted(self, entity);
step = self->step_s + 3;
self->ext.skelerang.angle =
AdjustValueWithinThreshold(step, self->ext.skelerang.angle, angle);
SetEntityVelocityFromAngle(self->ext.skelerang.angle, 48);
posX = entity->posX.i.hi - self->posX.i.hi;
posY = entity->posY.i.hi - self->posY.i.hi;
posX = abs(posX);
posY = abs(posY);
if (posX < 6 && posY < 6) {
PlaySfxPositional(SFX_MULTI_CLOCK_TICK);
self->step_s++;
}
if (self->step_s == 2) {
self->step++;
}
break;
case BOOMERANG_IN_HAND:
self->hitboxState = 0;
self->rotZ = 512;
break;
case BOOMERANG_DESTROY:
entity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (entity != NULL) {
CreateEntityFromEntity(2, self, entity);
entity->params = 1;
}
DestroyEntity(self);
break;
}
}
void EntitySkelerangUnknown(Entity* entity) {
Entity* parent;
if (!entity->step) {
InitializeEntity(g_EInitInteractable);
}
if ((entity - 1)->entityId != 0x2E) {
parent = entity - 1;
if (parent->entityId != E_SKELERANG) {
DestroyEntity(entity);
}
}

View File

@ -30,6 +30,9 @@ typedef enum EntityIDs {
/* 0x14 */ E_ID_14 = 0x14,
/* 0x15 */ E_GREY_PUFF,
/* 0x1D */ E_CLOCK_ROOM_SHADOW = 0x20,
/* 0x2E */ E_SKELERANG = 0x2E,
/* 0x2F */ E_SKELERANG_BOOMERANG,
/* 0x30 */ E_SKELERANG_UNK,
/* 0x37 */ E_GHOST_ENEMY = 0x37,
/* 0x3B */ E_SLINGER_THROWN_BONE = 0x3B,
/* 0x3C */ E_SLINGER_PIECES,
@ -52,6 +55,7 @@ typedef enum EntityIDs {
extern s16 g_SineTable[];
extern u16 g_EInitCommon[];
extern u16 g_EInitParticle[];
extern u16 g_EInitInteractable[];
// Axe knight
extern EInit g_EInitAxeKnightAxe;
@ -76,6 +80,10 @@ extern EInit g_EInitSlingerRib;
// Ghost (enemy)
extern EInit g_EInitGhostEnemy;
// Skelerang
extern EInit g_EInitSkelerang;
extern EInit g_EInitSkelerangBoomerang;
// Clock room
extern EInit g_Statues;