Decompile no0 Ouija Table components sub-entity (#1867)
Some checks are pending
Format code / format (push) Waiting to run
Build C code / extract-assets (push) Waiting to run
Build C code / build-linux (i686, Debug, clang, custom) (push) Blocked by required conditions
Build C code / build-linux (i686, Debug, clang, lle) (push) Blocked by required conditions
Build C code / build-linux (i686, Debug, gcc, custom) (push) Blocked by required conditions
Build C code / build-linux (i686, Debug, gcc, lle) (push) Blocked by required conditions
Build C code / build-linux (i686, RelWithDebInfo, clang, custom) (push) Blocked by required conditions
Build C code / build-linux (i686, RelWithDebInfo, clang, lle) (push) Blocked by required conditions
Build C code / build-linux (i686, RelWithDebInfo, gcc, custom) (push) Blocked by required conditions
Build C code / build-linux (i686, RelWithDebInfo, gcc, lle) (push) Blocked by required conditions
Build C code / build-linux (x86_64, Debug, clang, custom) (push) Blocked by required conditions
Build C code / build-linux (x86_64, Debug, clang, lle) (push) Blocked by required conditions
Build C code / build-linux (x86_64, Debug, gcc, custom) (push) Blocked by required conditions
Build C code / build-linux (x86_64, Debug, gcc, lle) (push) Blocked by required conditions
Build C code / build-linux (x86_64, RelWithDebInfo, clang, custom) (push) Blocked by required conditions
Build C code / build-linux (x86_64, RelWithDebInfo, clang, lle) (push) Blocked by required conditions
Build C code / build-linux (x86_64, RelWithDebInfo, gcc, custom) (push) Blocked by required conditions
Build C code / build-linux (x86_64, RelWithDebInfo, gcc, lle) (push) Blocked by required conditions
Build C code / build-macos (Debug, custom) (push) Blocked by required conditions
Build C code / build-macos (Debug, lle) (push) Blocked by required conditions
Build C code / build-macos (RelWithDebInfo, custom) (push) Blocked by required conditions
Build C code / build-macos (RelWithDebInfo, lle) (push) Blocked by required conditions
Build C code / build-windows (Debug, custom) (push) Blocked by required conditions
Build C code / build-windows (Debug, lle) (push) Blocked by required conditions
Build C code / build-windows (RelWithDebInfo, custom) (push) Blocked by required conditions
Build C code / build-windows (RelWithDebInfo, lle) (push) Blocked by required conditions
Build Saturn version / build-and-test-saturn (push) Waiting to run
Build Saturn version / function-finder-saturn (push) Waiting to run
Build Debug Module tool / build (push) Waiting to run
Build PSX and PSP version / build-and-test (pspeu, hd) (push) Waiting to run
Build PSX and PSP version / build-and-test (pspeu, pspeu) (push) Waiting to run
Build PSX and PSP version / build-and-test (us, us) (push) Waiting to run
Build PSX and PSP version / generate-progress-report (pspeu, hd) (push) Blocked by required conditions
Build PSX and PSP version / generate-progress-report (pspeu, pspeu) (push) Blocked by required conditions
Build PSX and PSP version / generate-progress-report (us, us) (push) Blocked by required conditions
Build PSX and PSP version / generate-duplicates-report (us, us) (push) Blocked by required conditions
Build PSX and PSP version / generate-duplicates-report-psp (pspeu, pspeu) (push) Blocked by required conditions

Also split into a separate entity file for Ouija Table

Matching both
PSX: https://decomp.me/scratch/oph7c
PSP: https://decomp.me/scratch/MTVot

I'll probably extract the data on this one shortly just to round it out
This commit is contained in:
Josh Schreuder 2024-11-03 11:40:36 +11:00 committed by GitHub
parent 078056fead
commit 95d91584b4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 339 additions and 146 deletions

View File

@ -74,7 +74,8 @@ segments:
- [0x4171C, .rodata, e_slinger]
- [0x41738, .rodata, 57C20]
- [0x4178C, .rodata, e_axe_knight]
- [0x417B0, .rodata, 5BDCC]
- [0x417B0, .rodata, e_ouija_table]
- [0x41804, .rodata, 5C64C]
- [0x41818, .rodata, e_skeleton]
- [0x41834, .rodata, e_magically_sealed_door]
- [0x4184C, .rodata, prim_helpers]
@ -102,7 +103,8 @@ segments:
- [0x575E4, c, e_slinger]
- [0x57C20, c, 57C20]
- [0x5AEBC, c, e_axe_knight]
- [0x5BDCC, c, 5BDCC]
- [0x5BDCC, c, e_ouija_table]
- [0x5C64C, c]
- [0x5CAB0, c, e_skeleton]
- [0x5D2B0, c, e_magically_sealed_door]
- [0x5E09C, c, prim_helpers]

View File

@ -190,6 +190,7 @@ EntityAxeKnight = 0x801DB4DC;
EntityAxeKnightRotateAxe = 0x801DBBE0;
EntityAxeKnightThrowingAxe = 0x801DBC38;
EntityOuijaTable = 0x801DBDCC;
EntityOuijaTableComponent = 0x801DC194;
SkeletonAttackCheck = 0x801DCAB0;
EntitySkeleton = 0x801DCB48;
EntitySkeletonPieces = 0x801DD018;

View File

@ -1998,10 +1998,12 @@ typedef struct {
typedef struct {
/* 0x7C */ s32 : 32;
/* 0x80 */ s32 : 32;
/* 0x80 */ s16 timer;
/* 0x82 */ s16 : 16;
/* 0x84 */ s32 spawned;
/* 0x88 */ struct Entity* parent;
/* 0x8C */ s32 : 32;
/* 0x8C */ s16 : 16;
/* 0x8E */ s16 unk8E;
/* 0x90 */ s32 isThrown;
} ET_OuijaTableContents;

View File

@ -1,141 +0,0 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#include "common.h"
#include "no0.h"
#define VASE self + 1
#define BEAKER self + 2
#define WALKING_TABLE self + 3
extern u16 D_us_80180C14[];
extern s16 D_us_801825CC[];
extern u16 D_us_801825D4[];
extern u8 D_us_801825F4[];
typedef enum {
/* 0 */ OUIJA_TABLE_INIT,
/* 1 */ OUIJA_TABLE_INIT_SUBENTITIES,
/* 2 */ OUIJA_TABLE_COME_ALIVE,
/* 3 */ OUIJA_TABLE_ATTACK,
/* 4 */ OUIJA_TABLE_UNUSED,
/* 255 */ OUIJA_TABLE_DEBUG = 255,
} OuijaTableSteps;
// Main Ouija Table entity
void EntityOuijaTable(Entity* self) {
Entity* otherEntity;
if (self->flags & FLAG_DEAD) {
otherEntity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (otherEntity != NULL) {
CreateEntityFromEntity(E_EXPLOSION, self, otherEntity);
otherEntity->params = 1;
}
PlaySfxPositional(SFX_STUTTER_FIREBALL);
// Destroy the spinning objects on the table
otherEntity = VASE;
otherEntity->flags |= FLAG_UNK_2000;
otherEntity = BEAKER;
otherEntity->flags |= FLAG_UNK_2000;
DestroyEntity(self);
return;
}
switch (self->step) {
case OUIJA_TABLE_INIT:
InitializeEntity(D_us_80180C14);
self->animCurFrame = 5;
self->hitboxOffY = 2;
return;
case OUIJA_TABLE_INIT_SUBENTITIES:
if (UnkCollisionFunc3(D_us_801825D4) & 1) {
// Spawn objects on the table
otherEntity = VASE;
CreateEntityFromEntity(E_OUIJA_TABLE_COMPONENT, self, otherEntity);
otherEntity->params = 0;
otherEntity->ext.ouijaTableContents.parent = self;
otherEntity->posX.i.hi -= 4;
otherEntity->posY.i.hi -= 16;
otherEntity = BEAKER;
CreateEntityFromEntity(E_OUIJA_TABLE_COMPONENT, self, otherEntity);
otherEntity->params = 1;
otherEntity->ext.ouijaTableContents.parent = self;
otherEntity->posX.i.hi += 2;
otherEntity->posY.i.hi -= 20;
otherEntity = WALKING_TABLE;
CreateEntityFromEntity(E_OUIJA_TABLE_COMPONENT, self, otherEntity);
otherEntity->params = 2;
otherEntity->ext.ouijaTableContents.parent = self;
if (self->params) {
otherEntity->posX.i.hi += 24;
otherEntity->facingLeft = 1;
} else {
otherEntity->posX.i.hi -= 24;
}
#ifndef VERSION_PSP
// Looks like a coding error
otherEntity->posY.i.hi = otherEntity->posY.i.hi;
#endif
self->step++;
}
break;
case OUIJA_TABLE_COME_ALIVE:
// When player approaches, spring to life
if ((GetDistanceToPlayerX() < 80) && (GetDistanceToPlayerY() < 64)) {
self->facingLeft = (GetSideToPlayer() & 1) ^ 1;
otherEntity = BEAKER;
otherEntity->ext.ouijaTableContents.spawned = true;
otherEntity = VASE;
otherEntity->ext.ouijaTableContents.spawned = true;
SetStep(3);
}
break;
case OUIJA_TABLE_ATTACK:
if (!self->step_s) {
self->ext.ouijaTable.attackTimer = 128;
self->step_s += 1;
}
if (AnimateEntity(D_us_801825F4, self) == 0) {
self->facingLeft = (GetSideToPlayer() & 1) ^ 1;
}
UnkCollisionFunc2(D_us_801825CC);
if (self->facingLeft) {
self->velocityX = FIX(0.5);
} else {
self->velocityX = FIX(-0.5);
}
if (!(--self->ext.ouijaTable.attackTimer)) {
self->ext.ouijaTable.attackTimer = 192;
// Throw out one of the objects on the table
if (self->ext.ouijaTable.unk90 != 0) {
otherEntity = VASE;
} else {
otherEntity = BEAKER;
}
otherEntity->ext.ouijaTableContents.isThrown = true;
// Alternate between throwing vase and beaker
self->ext.ouijaTable.unk90 ^= 1;
}
break;
case OUIJA_TABLE_UNUSED:
break;
case OUIJA_TABLE_DEBUG:
#include "../pad2_anim_debug.h"
}
}
INCLUDE_ASM("st/no0/nonmatchings/5BDCC", func_us_801DC194);
INCLUDE_ASM("st/no0/nonmatchings/5BDCC", func_us_801DC64C);
void func_us_801DC754(void) {
g_CurrentEntity->facingLeft = (GetSideToPlayer() & 1) ^ 1;
}
INCLUDE_ASM("st/no0/nonmatchings/5BDCC", func_us_801DC788);

11
src/st/no0/5C64C.c Normal file
View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#include "common.h"
#include "no0.h"
INCLUDE_ASM("st/no0/nonmatchings/5C64C", func_us_801DC64C);
void func_us_801DC754(void) {
g_CurrentEntity->facingLeft = (GetSideToPlayer() & 1) ^ 1;
}
INCLUDE_ASM("st/no0/nonmatchings/5C64C", func_us_801DC788);

317
src/st/no0/e_ouija_table.c Normal file
View File

@ -0,0 +1,317 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
#include "no0.h"
#define VASE self + 1
#define BEAKER self + 2
#define WALKING_TABLE self + 3
// Main
extern u16 D_us_80180C14[];
extern s16 D_us_801825CC[];
extern u16 D_us_801825D4[];
extern u8 D_us_801825F4[];
// Components
extern u16 D_us_80180C20[];
extern u16 D_us_801825E4[];
typedef enum {
/* 0 */ OUIJA_TABLE_INIT,
/* 1 */ OUIJA_TABLE_INIT_SUBENTITIES,
/* 2 */ OUIJA_TABLE_COME_ALIVE,
/* 3 */ OUIJA_TABLE_ATTACK,
/* 4 */ OUIJA_TABLE_UNUSED,
/* 255 */ OUIJA_TABLE_DEBUG = 255,
} OuijaTableSteps;
typedef enum {
/* 0 */ OUIJA_COMPONENT_INIT,
/* 1 */ OUIJA_COMPONENT_CHECK_SPAWN,
/* 2 */ OUIJA_COMPONENT_INIT_POS,
/* 3 */ OUIJA_COMPONENT_CIRCLE_TABLE,
/* 4 */ OUIJA_COMPONENT_SEEK_ATTACK,
/* 8 */ OUIJA_COMPONENT_DEATH = 8,
/* 16 */ OUIJA_COMPONENT_NOOP = 16
} OuijaTableComponentSteps;
typedef enum {
/* 0 */ OUIJA_COMPONENT_BEGIN_DEATH,
/* 1 */ OUIJA_COMPONENT_FALL_TO_FLOOR,
/* 2 */ OUIJA_COMPONENT_DIE_IN_FLAMES
} OuijaTableComponentDeathSubSteps;
// Main Ouija Table entity
void EntityOuijaTable(Entity* self) {
Entity* otherEntity;
if (self->flags & FLAG_DEAD) {
otherEntity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (otherEntity != NULL) {
CreateEntityFromEntity(E_EXPLOSION, self, otherEntity);
otherEntity->params = 1;
}
PlaySfxPositional(SFX_STUTTER_FIREBALL);
// Destroy the spinning objects on the table
otherEntity = VASE;
otherEntity->flags |= FLAG_UNK_2000;
otherEntity = BEAKER;
otherEntity->flags |= FLAG_UNK_2000;
DestroyEntity(self);
return;
}
switch (self->step) {
case OUIJA_TABLE_INIT:
InitializeEntity(D_us_80180C14);
self->animCurFrame = 5;
self->hitboxOffY = 2;
return;
case OUIJA_TABLE_INIT_SUBENTITIES:
if (UnkCollisionFunc3(D_us_801825D4) & 1) {
// Spawn objects on the table
otherEntity = VASE;
CreateEntityFromEntity(E_OUIJA_TABLE_COMPONENT, self, otherEntity);
otherEntity->params = 0;
otherEntity->ext.ouijaTableContents.parent = self;
otherEntity->posX.i.hi -= 4;
otherEntity->posY.i.hi -= 16;
otherEntity = BEAKER;
CreateEntityFromEntity(E_OUIJA_TABLE_COMPONENT, self, otherEntity);
otherEntity->params = 1;
otherEntity->ext.ouijaTableContents.parent = self;
otherEntity->posX.i.hi += 2;
otherEntity->posY.i.hi -= 20;
otherEntity = WALKING_TABLE;
CreateEntityFromEntity(E_OUIJA_TABLE_COMPONENT, self, otherEntity);
otherEntity->params = 2;
otherEntity->ext.ouijaTableContents.parent = self;
if (self->params) {
otherEntity->posX.i.hi += 24;
otherEntity->facingLeft = 1;
} else {
otherEntity->posX.i.hi -= 24;
}
#ifndef VERSION_PSP
// Looks like a coding error
otherEntity->posY.i.hi = otherEntity->posY.i.hi;
#endif
self->step++;
}
break;
case OUIJA_TABLE_COME_ALIVE:
// When player approaches, spring to life
if ((GetDistanceToPlayerX() < 80) && (GetDistanceToPlayerY() < 64)) {
self->facingLeft = (GetSideToPlayer() & 1) ^ 1;
otherEntity = BEAKER;
otherEntity->ext.ouijaTableContents.spawned = true;
otherEntity = VASE;
otherEntity->ext.ouijaTableContents.spawned = true;
SetStep(3);
}
break;
case OUIJA_TABLE_ATTACK:
if (!self->step_s) {
self->ext.ouijaTable.attackTimer = 128;
self->step_s += 1;
}
if (AnimateEntity(D_us_801825F4, self) == 0) {
self->facingLeft = (GetSideToPlayer() & 1) ^ 1;
}
UnkCollisionFunc2(D_us_801825CC);
if (self->facingLeft) {
self->velocityX = FIX(0.5);
} else {
self->velocityX = FIX(-0.5);
}
if (!(--self->ext.ouijaTable.attackTimer)) {
self->ext.ouijaTable.attackTimer = 192;
// Throw out one of the objects on the table
if (self->ext.ouijaTable.unk90 != 0) {
otherEntity = VASE;
} else {
otherEntity = BEAKER;
}
otherEntity->ext.ouijaTableContents.isThrown = true;
// Alternate between throwing vase and beaker
self->ext.ouijaTable.unk90 ^= 1;
}
break;
case OUIJA_TABLE_UNUSED:
break;
case OUIJA_TABLE_DEBUG:
#include "../pad2_anim_debug.h"
}
}
// Ouija table components (beaker, vase, walking table)
void EntityOuijaTableComponent(Entity* self) {
Entity* otherEntity;
s32 posX, posY;
s32 hitFlags;
s16 angle;
if (self->step < OUIJA_COMPONENT_DEATH) {
otherEntity = self->ext.ouijaTableContents.parent;
if (otherEntity->entityId != E_OUIJA_TABLE) {
SetStep(OUIJA_COMPONENT_DEATH);
}
}
switch (self->step) {
case OUIJA_COMPONENT_INIT:
InitializeEntity(D_us_80180C20);
switch (self->params) {
// Vase
case 0:
self->animCurFrame = 10;
break;
// Beaker
case 1:
self->animCurFrame = 12;
break;
// Walking table
case 2:
self->animCurFrame = 14;
self->hitboxState = 0;
self->step = OUIJA_COMPONENT_NOOP;
break;
// Unknown? Doesn't seem like this param is used
case 3:
self->animCurFrame = 15;
self->hitboxState = 0;
self->step = OUIJA_COMPONENT_NOOP;
break;
}
break;
case OUIJA_COMPONENT_CHECK_SPAWN:
if (self->ext.ouijaTableContents.spawned) {
self->step = OUIJA_COMPONENT_INIT_POS;
}
break;
case OUIJA_COMPONENT_INIT_POS:
MoveEntity();
otherEntity = self->ext.ouijaTableContents.parent;
posX = otherEntity->posX.i.hi;
posY = otherEntity->posY.i.hi - 32;
posY += ((rcos(self->ext.ouijaTableContents.unk8E) * 8) >> 12);
posX += ((rsin(self->ext.ouijaTableContents.unk8E) * 18) >> 12);
posX -= self->posX.i.hi;
posY -= self->posY.i.hi;
angle = ratan2(posY, posX);
self->velocityX = (rcos(angle) * FIX(1.5)) >> 12;
self->velocityY = (rsin(angle) * FIX(1.5)) >> 12;
// Beaker and vase move in opposite directions
if (self->params) {
self->ext.ouijaTableContents.unk8E += 48;
} else {
self->ext.ouijaTableContents.unk8E -= 48;
}
if (abs(posX) < 2 && abs(posY) < 2) {
self->step = OUIJA_COMPONENT_CIRCLE_TABLE;
}
break;
case OUIJA_COMPONENT_CIRCLE_TABLE:
// Components fly in a circular pattern around the table
otherEntity = self->ext.ouijaTableContents.parent;
posX = otherEntity->posX.i.hi;
posY = otherEntity->posY.i.hi - 32;
posY += ((rcos(self->ext.ouijaTableContents.unk8E) * 8) >> 12);
posX += ((rsin(self->ext.ouijaTableContents.unk8E) * 18) >> 12);
self->posX.i.hi = posX;
self->posY.i.hi = posY;
// Beaker and vase move in opposite directions
if (self->params) {
self->ext.ouijaTableContents.unk8E += 48;
} else {
self->ext.ouijaTableContents.unk8E -= 48;
}
// Fly towards player in attack motion
if (self->ext.ouijaTableContents.isThrown) {
otherEntity = &PLAYER;
angle = GetAngleBetweenEntities(self, otherEntity);
self->velocityX = (rcos(angle) << 18) >> 12;
self->velocityY = (rsin(angle) << 18) >> 12;
self->step = OUIJA_COMPONENT_SEEK_ATTACK;
}
break;
case OUIJA_COMPONENT_SEEK_ATTACK:
hitFlags = self->hitFlags;
if (self->ext.ouijaTableContents.isThrown) {
if (hitFlags & 0x80) {
self->ext.ouijaTableContents.isThrown = false;
} else if (hitFlags) {
self->velocityX = 0;
self->velocityY = 0;
}
}
MoveEntity();
self->velocityX -= self->velocityX / 32;
self->velocityY -= self->velocityY / 32;
// Once velocity is dampened to min value, return to origin
if (abs(self->velocityX) < FIX(0.5) &&
abs(self->velocityY) < FIX(0.5)) {
self->ext.ouijaTableContents.isThrown = false;
self->step = OUIJA_COMPONENT_INIT_POS;
}
break;
case OUIJA_COMPONENT_DEATH:
switch (self->step_s) {
case OUIJA_COMPONENT_BEGIN_DEATH:
self->hitboxState = 0;
self->velocityX = 0;
self->velocityY = 0;
self->drawFlags = FLAG_DRAW_ROTZ;
self->step_s++;
// fallthrough
case OUIJA_COMPONENT_FALL_TO_FLOOR:
// When hit, components rotate and fall to floor
// After hitting the floor, begin death timer
self->rotZ += 64;
if (UnkCollisionFunc3(D_us_801825E4) & 1) {
self->drawFlags = FLAG_DRAW_DEFAULT;
self->animCurFrame++;
self->ext.ouijaTableContents.timer = 32;
PlaySfxPositional(SFX_SKULL_BONK);
self->step_s++;
}
break;
case OUIJA_COMPONENT_DIE_IN_FLAMES:
// Once death timer expires, burst into flames and get destroyed
if (!(--self->ext.ouijaTableContents.timer)) {
otherEntity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (otherEntity != NULL) {
CreateEntityFromEntity(E_EXPLOSION, self, otherEntity);
otherEntity->params = 0;
}
PlaySfxPositional(SFX_SMALL_FLAME_IGNITE);
DestroyEntity(self);
}
break;
}
break;
case 16:
break;
}
}

View File

@ -34,7 +34,8 @@ typedef enum EntityIDs {
/* 0x3C */ E_SLINGER_PIECES,
/* 0x48 */ E_AXE_KNIGHT_AXE = 0x48,
/* 0x49 */ E_WARG_EXP_OPAQUE,
/* 0x4B */ E_OUIJA_TABLE_COMPONENT = 0x4B,
/* 0x4A */ E_OUIJA_TABLE = 0x4A,
/* 0x4B */ E_OUIJA_TABLE_COMPONENT,
/* 0x4D */ E_SKELETON = 0x4D,
/* 0x4E */ E_SKELETON_THROWN_BONE,
/* 0x4F */ E_SKELETON_PIECES,