DRE EntitySuccubus (#388)

Matching this one was tough, so many reused variables and strange array
uses.

![image](https://github.com/Xeeynamo/sotn-decomp/assets/96613413/a1297919-50e3-4302-b43b-749bf0ecd5ad)

Special thanks:
Co-authored-by: @bismurphy (for tackling those messy loops)
Co-authored-by: @MottZilla (for docs research)
This commit is contained in:
Alejandro Asenjo Nitti 2023-07-23 10:22:16 -03:00 committed by GitHub
parent 9300e42e5e
commit 9bd3d18609
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1574 additions and 315 deletions

View File

@ -39,18 +39,17 @@ segments:
- [0x14AC, rodata]
- [0x16C0, rodata]
- [0x1171C, .rodata, 11A64]
- [0x11728, rodata]
- [0x11770, rodata]
- [0x117A0, rodata]
- [0x117F0, .rodata, 13E18]
- [0x11808, .rodata, 14214]
- [0x11728, .rodata, succubus] # EntitySuccubus
- [0x117F0, .rodata, 13B3C]
- [0x11808, .rodata, 14774]
- [0x1181C, rodata]
- [0x118D0, rodata]
- [0x119C4, .rodata, 1C7DC]
- [0x119DC, rodata]
- [0x11A64, c, 11A64]
- [0x13E18, c, 13E18]
- [0x14214, c, 14214]
- [0x11A64, c, succubus]
- [0x13B3C, c, 13B3C]
- [0x14774, c, 14774]
- [0x1C7DC, c, 1C7DC]
- [0x23260, data]
- [0x23FCC]

View File

@ -6,6 +6,7 @@ g_eBreakableHitboxes = 0x80180630;
g_eBreakableExplosionTypes = 0x80180638;
g_eBreakableanimSets = 0x80180640;
g_eBreakableBlendModes = 0x80180650;
g_CloneShootOrder = 0x801807D4;
c_HeartPrizes = 0x801811AC;
g_Rooms = 0x80181498;
EntityUnkId11 = 0x80191A64;
@ -13,12 +14,12 @@ EntityBreakable = 0x80191D00;
EntityBackgroundClouds = 0x80191E34;
EntitySuccubus = 0x80192104;
EntitySuccubusPetal = 0x80193B3C;
EntityUnkId1B = 0x80193D7C;
EntityUnkId1C = 0x80193E18;
EntitySuccubusWingOverlay = 0x80193D7C;
EntitySuccubusClone = 0x80193E18;
EntityPinkBallProjectile = 0x80194214;
EntitySuccubusWingSpike = 0x80194488;
EntityUnkId1F = 0x801946C4;
EntitySuccubusIntroCutscene = 0x801950F8;
EntitySuccubusWingSpikeTip = 0x801946C4;
EntitySuccubusCutscene = 0x801950F8;
EntityUnkId21 = 0x80196238;
EntityUnkId23 = 0x80196678;
EntityFadeToWhite1 = 0x8019697C;
@ -44,6 +45,7 @@ MoveEntity = 0x8019A75C;
FallEntity = 0x8019A78C;
AllocEntity = 0x8019AC18;
SetStep = 0x8019AFE8;
SetSubStep = 0x8019B008;
InitializeEntity = 0x8019B0B8;
CollectSubweapon = 0x8019BB94;
CollectHeartVessel = 0x8019BCAC;

View File

@ -245,6 +245,23 @@ typedef struct {
/* 0x8E */ s16 unk8E;
} ET_StageTitleCard;
typedef struct ET_Succubus {
/* 0x7C */ char pad_7C[0x4];
/* 0x80 */ s16 timer;
/* 0x82 */ char pad_82[0x2];
/* 0x84 */ u8 facing;
/* 0x85 */ u8 unk85;
/* 0x86 */ u8 nextAttack;
/* 0x87 */ u8 unk87;
/* 0x88 */ u16 nextStep;
/* 0x8A */ char pad_8A[0x4];
/* 0x8E */ s16 yOffset;
/* 0x90 */ char pad_90[0xC];
/* 0x9C */ struct Entity* real;
/* 0xA0 */ s16 clonePosX;
/* 0xA2 */ s16 unkA2;
} ET_Succubus;
typedef union {
/* 0x7C */ struct Primitive* prim;
/* 0x7C */ ET_Generic generic;
@ -258,6 +275,7 @@ typedef union {
/* 0x7C */ ET_801CF254 et_801CF254;
/* 0x7C */ ET_GurkhaSword gurkhaSword;
/* 0x7C */ ET_Dracula dracula;
/* 0x7C */ ET_Succubus succubus;
/* 0x7C */ ET_StageTitleCard stageTitleCard;
/* 0x7C */ char stub[0x40];
} Ext;

View File

@ -20,6 +20,7 @@
* SH = Shaft
* ML = Master Librarian
* FE = Ferryman
* SU = Succubus
* AL = Alucard
* MA = Maria
* RI = Richter
@ -235,4 +236,25 @@ typedef enum { MONO_SOUND, STEREO_SOUND } soundMode;
#define NA_SE_VO_DR_HURT_3 0x85C
#define NA_SE_VO_DR_HURT_4 0x85D
#define NA_SE_PL_TELEPORT 0x8BA
#define NA_SE_CS_BURNING_PHOTOGRAPH 0x8BE
#define NA_SE_CS_BURNING_PHOTOGRAPH 0x8BE
// STAGE DRE
#define NA_SE_SU_SHOOT_PINKBALLS 0x62C
#define NA_SE_SU_LANDING 0x646
#define NA_VO_SU_CRYSTAL_2 0x6AF
#define NA_SE_SU_FLAPPING_WINGS 0x6C6
#define NA_SE_SU_PETAL_ATTACK 0x6B0
#define NA_SE_SU_CREATE_CLONES 0x6D5
#define NA_SE_SU_CHARGE_PINKBALLS 0x6E2
#define NA_VO_SU_LAUGH 0x86E
// Blank, may be a leftover from the Jap version.
#define NA_VO_SU_BLANK 0x86F
#define NA_VO_SU_GRUNT_1 0x870
#define NA_VO_SU_GRUNT_2 0x872
#define NA_VO_SU_GRUNT_3 0x874
#define NA_VO_SU_HURT_1 0x879
#define NA_VO_SU_HURT_2 0x87A
#define NA_VO_SU_CRYSTAL_1 0x87C
#define NA_VO_SU_SUCK_YOU_DRY 0x876
#define NA_VO_SU_NO_SCREAM 0x87B
#define NA_VO_SU_DELICIOUS 0x8D1

View File

@ -201,89 +201,3 @@ void EntityBackgroundClouds(Entity* self) {
g_GpuBuffers[1].draw.g0 = 24;
g_GpuBuffers[1].draw.b0 = 24;
}
// ID 0x19
INCLUDE_ASM("asm/us/st/dre/nonmatchings/11A64", EntitySuccubus);
// Petal projectile shot by succubus ID 0x1A
void EntitySuccubusPetal(Entity* self) {
Entity* newEntity;
s32 temp_s2;
s16 angle;
if (D_80180664 & 2) {
self->flags |= 0x100;
}
if (self->hitFlags & 0x80) {
D_80180668 = 1;
}
if (self->flags & 0x100) {
newEntity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (newEntity != NULL) {
CreateEntityFromEntity(E_EXPLOSION, self, newEntity);
newEntity->params = 0;
}
DestroyEntity(self);
return;
}
switch (self->step) {
case 0:
InitializeEntity(D_801804DC);
self->unk19 = 4;
self->rotAngle = rand() & 0xFFF;
temp_s2 = Random() & 3;
if (temp_s2 >= 3) {
temp_s2 = 0;
}
self->animCurFrame = temp_s2 + 64;
angle = ((Random() & 0x1F) * 16) + 0xC0;
if (self->facing == 0) {
angle = 0x800 - angle;
} else {
angle = angle;
}
temp_s2 = ((rand() * 4) + 0x38000) >> 0xC;
self->velocityX = temp_s2 * rcos(angle);
self->velocityY = temp_s2 * rsin(angle);
self->ext.generic.unk80.modeS16.unk0 = (Random() & 0x1F) + 0x10;
case 1:
self->velocityX = self->velocityX - (self->velocityX >> 6);
self->velocityY = self->velocityY - (self->velocityY >> 6);
MoveEntity();
if (--self->ext.generic.unk80.modeS16.unk0 == 0) {
self->ext.generic.unk80.modeS16.unk0 = (Random() & 0x1F) + 0x20;
self->step++;
}
break;
case 2:
MoveEntity();
self->rotAngle += self->ext.generic.unk80.modeS16.unk0;
break;
}
}
void EntityUnkId1B(Entity* entity) {
if (entity->step == 0) {
InitializeEntity(D_801804E8);
}
entity->posX.i.hi = entity[-1].posX.i.hi;
entity->animCurFrame = 0;
entity->posY.i.hi = entity[-1].posY.i.hi;
entity->facing = entity[-1].facing;
if (entity[-1].animCurFrame == 0x32) {
entity->animCurFrame = 0x3E;
}
if (entity[-1].animCurFrame == 0x33) {
entity->animCurFrame = 0x3F;
}
entity->zPriority = PLAYER.zPriority + 4;
}

367
src/st/dre/13B3C.c Normal file
View File

@ -0,0 +1,367 @@
#include "dre.h"
// Petal projectile shot by succubus ID 0x1A
void EntitySuccubusPetal(Entity* self) {
Entity* newEntity;
s32 temp_s2;
s16 angle;
if (D_80180664 & 2) {
self->flags |= 0x100;
}
if (self->hitFlags & 0x80) {
D_80180668 = 1;
}
if (self->flags & 0x100) {
newEntity = AllocEntity(&g_Entities[224], &g_Entities[256]);
if (newEntity != NULL) {
CreateEntityFromEntity(E_EXPLOSION, self, newEntity);
newEntity->params = 0;
}
DestroyEntity(self);
return;
}
switch (self->step) {
case 0:
InitializeEntity(D_801804DC);
self->unk19 = 4;
self->rotAngle = rand() & 0xFFF;
temp_s2 = Random() & 3;
if (temp_s2 >= 3) {
temp_s2 = 0;
}
self->animCurFrame = temp_s2 + 64;
angle = ((Random() & 0x1F) * 16) + 0xC0;
if (self->facing == 0) {
angle = 0x800 - angle;
} else {
angle = angle;
}
temp_s2 = ((rand() * 4) + 0x38000) >> 0xC;
self->velocityX = temp_s2 * rcos(angle);
self->velocityY = temp_s2 * rsin(angle);
self->ext.succubus.timer = (Random() & 31) + 16;
case 1:
self->velocityX = self->velocityX - (self->velocityX >> 6);
self->velocityY = self->velocityY - (self->velocityY >> 6);
MoveEntity();
if (--self->ext.succubus.timer == 0) {
self->ext.succubus.timer = (Random() & 31) + 32;
self->step++;
}
break;
case 2:
MoveEntity();
self->rotAngle += self->ext.succubus.timer;
break;
}
}
// Wings that appear over the player when the succubus does her charge attack
void EntitySuccubusWingOverlay(Entity* entity) {
if (entity->step == 0) {
InitializeEntity(D_801804E8);
}
entity->posX.i.hi = entity[-1].posX.i.hi;
entity->animCurFrame = 0;
entity->posY.i.hi = entity[-1].posY.i.hi;
entity->facing = entity[-1].facing;
if (entity[-1].animCurFrame == 50) {
entity->animCurFrame = 62;
}
if (entity[-1].animCurFrame == 51) {
entity->animCurFrame = 63;
}
entity->zPriority = PLAYER.zPriority + 4;
}
extern s32 D_80180660; // clones counter
void EntitySuccubusClone(Entity* self) {
Entity* newEntity;
s8* hitbox;
s32 velX;
s32 i;
if (D_80180660 == 0) {
self->flags |= 0x100;
}
if (self->flags & 0x100) {
if (self->step != 5) {
if (D_80180660 != 0) {
D_80180660--;
}
self->hitboxState = 0;
self->flags |= 0x100;
g_api.func_80134714(0x6D9, 0x54, 0);
SetStep(5);
}
}
switch (self->step) {
case 0:
InitializeEntity(D_801804F4);
self->hitboxState = 0;
velX = self->ext.succubus.clonePosX -
(self->posX.i.hi + g_Camera.posX.i.hi)
<< 0x10;
if (velX < 0) {
velX += 0x3F;
}
self->velocityX = velX >> 6;
self->ext.succubus.timer = 64;
case 1:
MoveEntity();
newEntity = self->ext.succubus.real;
self->animCurFrame = newEntity->animCurFrame;
self->facing = newEntity->facing;
if (--self->ext.succubus.timer == 0) {
self->hitboxState = 3;
SetStep(2);
}
break;
case 2:
newEntity = self->ext.succubus.real;
self->animCurFrame = newEntity->animCurFrame;
self->facing = newEntity->facing;
if (newEntity->ext.succubus.unk85 != 0) {
self->ext.succubus.timer = (self->params * 48) + 1;
SetStep(3);
}
break;
case 3:
self->animCurFrame = 26;
if (--self->ext.succubus.timer == 0) {
SetStep(4);
}
break;
case 4:
if (self->step_s == 0) {
self->ext.succubus.unk85 = 0;
self->step_s++;
}
if (AnimateEntity(D_80180780, self) == 0) {
self->ext.succubus.timer = 288;
SetStep(3);
}
if (self->animFrameIdx == 4 && self->animFrameDuration == 0) {
func_801A046C(0x6E2);
for (i = 0; i < 2; i++) {
newEntity = AllocEntity(&g_Entities[160], &g_Entities[192]);
if (newEntity != NULL) {
CreateEntityFromEntity(
E_SUCCUBUS_PINK_BALL_PROJECTILE, self, newEntity);
newEntity->params = i;
if (i != 0) {
newEntity->posX.i.hi -= 2;
} else {
newEntity->posX.i.hi += 2;
}
newEntity->ext.succubus.real = self;
newEntity->posY.i.hi -= 10;
newEntity->zPriority = self->zPriority + 1;
}
}
}
if (self->animFrameIdx == 5 && self->animFrameDuration == 0) {
func_801A046C(NA_VO_SU_GRUNT_2);
func_801A046C(NA_VO_SU_CRYSTAL_1);
func_801A046C(NA_SE_SU_SHOOT_PINKBALLS);
self->ext.succubus.unk85 = 1;
}
break;
case 5:
if (self->step_s == 0) {
self->ext.succubus.timer = 32;
self->step_s++;
}
if (self->ext.succubus.timer & 1) {
self->animSet = ANIMSET_DRA(0);
} else {
self->animSet = ANIMSET_OVL(1);
}
if (--self->ext.succubus.timer == 0) {
DestroyEntity(self);
return;
}
break;
}
hitbox = &D_80180830[self->animCurFrame][D_801807F8];
hitbox--;
hitbox++;
self->hitboxOffX = *hitbox++;
self->hitboxOffY = *hitbox++;
self->hitboxWidth = hitbox[0];
self->hitboxHeight = hitbox[1];
}
// Pink ball projectile shot by succubus duplicates ID 0x1D
void EntityPinkBallProjectile(Entity* self) {
Entity* entity;
s16 temp_s0;
s16 temp_v0;
if (D_80180664 & 2) {
DestroyEntity(self);
return;
}
switch (self->step) {
case 0:
InitializeEntity(D_80180500);
self->blendMode = 0x30;
self->unk19 = 3;
self->unk1C = 0;
self->unk1A = 0;
case 1:
self->unk1A = self->unk1C += 4;
if (self->unk1A > 256) {
self->unk19 = 0;
}
AnimateEntity(D_80180794, self);
entity = self->ext.succubus.real;
if (entity->ext.succubus.unk85 != 0) {
self->unk19 = 0;
self->step++;
}
if (entity->flags & 0x100) {
DestroyEntity(self);
}
break;
case 2:
temp_s0 = (self->params << 10) + 0x200;
self->velocityX = rcos(temp_s0) * 0x38;
self->velocityY = rsin(temp_s0) * 0x38;
self->ext.succubus.unkA2 = temp_s0;
self->ext.succubus.timer = 128;
self->step++;
case 3:
AnimateEntity(D_80180794, self);
MoveEntity();
temp_v0 = func_8019AF08(self, g_Entities);
temp_s0 = func_8019AF88(0x10, self->ext.succubus.unkA2, temp_v0);
self->velocityX = rcos(temp_s0) * 0x38;
self->velocityY = rsin(temp_s0) * 0x38;
self->ext.succubus.unkA2 = temp_s0;
if (self->hitFlags & 0x80) {
self->step = 4;
}
if (--self->ext.succubus.timer == 0) {
self->step = 4;
}
break;
case 4:
self->flags |= FLAG_DESTROY_IF_OUT_OF_CAMERA;
AnimateEntity(D_80180794, self);
MoveEntity();
break;
}
}
// Extending wing spike from succubus ID 0x1E
void EntitySuccubusWingSpike(Entity* self) {
s32 temp_s2;
s16 var_s0;
if (D_80180664 & 2) {
DestroyEntity(self);
return;
}
switch (self->step) {
case 0:
InitializeEntity(D_801804E8);
self->unk19 = 4;
self->animCurFrame = 0;
var_s0 = D_801807F0[self->params];
self->rotAngle = var_s0;
self->unk19 |= 1;
self->unk1A = 0x100;
CreateEntityFromEntity(E_SUCCUBUS_WING_SPIKE_TIP, self, &self[1]);
self[1].facing = self->facing;
self[1].params = self->params;
self[1].rotAngle = self->rotAngle;
case 1:
if (self->ext.succubus.real->ext.succubus.unk85 != 0) {
self->step++;
}
break;
case 2:
self->animCurFrame = 85;
self->unk1A += 0x40;
if (self->unk1A > 0x600) {
self->unk1A = 0x600;
}
if (self->ext.succubus.real->ext.succubus.unk85 == 0) {
self->step++;
}
break;
case 3:
self->unk1A -= 0x40;
if (self->unk1A < 0x100) {
DestroyEntity(self);
return;
}
}
var_s0 = self->rotAngle;
temp_s2 = (self->unk1A * 0xB) >> 6;
if (self->facing == 0) {
var_s0 = 0x800 - var_s0;
}
self[1].posX.i.hi = self->posX.i.hi;
self[1].posY.i.hi = self->posY.i.hi;
self[1].posX.i.hi += temp_s2 * rcos(var_s0) >> 0xC;
self[1].posY.i.hi -= temp_s2 * rsin(var_s0) >> 0xC;
}
void EntitySuccubusWingSpikeTip(Entity* self) {
switch (self->step) {
case 0:
InitializeEntity(D_8018050C);
self->animCurFrame = 0;
self->unk19 = 4;
self->hitboxState = 0;
case 1:
if (self[-1].animCurFrame != 0) {
self->hitboxState = 1;
self->animCurFrame = 86;
}
if (self->hitFlags != 0) {
D_80180668 = 1;
}
if (self[-1].entityId != 0x1E) {
DestroyEntity(self);
}
}
}

View File

@ -1,158 +1,5 @@
#include "dre.h"
// Pink ball projectile shot by succubus duplicates ID 0x1D
void EntityPinkBallProjectile(Entity* self) {
Entity* entity;
s16 temp_s0;
s16 temp_v0;
if (D_80180664 & 2) {
DestroyEntity(self);
return;
}
switch (self->step) {
case 0:
InitializeEntity(D_80180500);
self->blendMode = 0x30;
self->unk19 = 3;
self->unk1C = 0;
self->unk1A = 0;
case 1:
self->unk1A = self->unk1C += 4;
if (self->unk1A > 256) {
self->unk19 = 0;
}
AnimateEntity(D_80180794, self);
entity = self->ext.generic.unk9C;
if (entity->ext.generic.unk84.U8.unk1 != 0) {
self->unk19 = 0;
self->step++;
}
if (entity->flags & 0x100) {
DestroyEntity(self);
}
break;
case 2:
temp_s0 = (self->params << 0xA) + 0x200;
self->velocityX = rcos(temp_s0) * 0x38;
self->velocityY = rsin(temp_s0) * 0x38;
self->ext.generic.unkA2 = temp_s0;
self->ext.generic.unk80.modeS16.unk0 = 128;
self->step++;
case 3:
AnimateEntity(D_80180794, self);
MoveEntity();
temp_v0 = func_8019AF08(self, g_Entities);
temp_s0 = func_8019AF88(0x10, self->ext.generic.unkA2, temp_v0);
self->velocityX = rcos(temp_s0) * 0x38;
self->velocityY = rsin(temp_s0) * 0x38;
self->ext.generic.unkA2 = temp_s0;
if (self->hitFlags & 0x80) {
self->step = 4;
}
if (--self->ext.generic.unk80.modeS16.unk0 == 0) {
self->step = 4;
}
break;
case 4:
self->flags |= FLAG_DESTROY_IF_OUT_OF_CAMERA;
AnimateEntity(D_80180794, self);
MoveEntity();
break;
}
}
// Extending wing spike from succubus ID 0x1E
void EntitySuccubusWingSpike(Entity* self) {
s32 temp_s2;
s16 var_s0;
if (D_80180664 & 2) {
DestroyEntity(self);
return;
}
switch (self->step) {
case 0:
InitializeEntity(D_801804E8);
self->unk19 = 4;
self->animCurFrame = 0;
var_s0 = D_801807F0[self->params];
self->rotAngle = var_s0;
self->unk19 |= 1;
self->unk1A = 0x100;
CreateEntityFromEntity(0x1F, self, &self[1]);
self[1].facing = self->facing;
self[1].params = self->params;
self[1].rotAngle = self->rotAngle;
case 1:
if (self->ext.generic.unk9C->ext.generic.unk84.U8.unk1 != 0) {
self->step++;
}
break;
case 2:
self->animCurFrame = 85;
self->unk1A += 0x40;
if (self->unk1A > 0x600) {
self->unk1A = 0x600;
}
if (self->ext.generic.unk9C->ext.generic.unk84.U8.unk1 == 0) {
self->step++;
}
break;
case 3:
self->unk1A -= 0x40;
if (self->unk1A < 0x100) {
DestroyEntity(self);
return;
}
}
var_s0 = self->rotAngle;
temp_s2 = (self->unk1A * 0xB) >> 6;
if (self->facing == 0) {
var_s0 = 0x800 - var_s0;
}
self[1].posX.i.hi = self->posX.i.hi;
self[1].posY.i.hi = self->posY.i.hi;
self[1].posX.i.hi += temp_s2 * rcos(var_s0) >> 0xC;
self[1].posY.i.hi -= temp_s2 * rsin(var_s0) >> 0xC;
}
void EntityUnkId1F(Entity* entity) {
switch (entity->step) {
case 0:
InitializeEntity(D_8018050C);
entity->animCurFrame = 0;
entity->unk19 = 4;
entity->hitboxState = 0;
case 1:
if (entity[-1].animCurFrame != 0) {
entity->hitboxState = 1;
entity->animCurFrame = 0x56;
}
if (entity->hitFlags != 0) {
D_80180668 = 1;
}
if (entity[-1].entityId != 0x1E) {
DestroyEntity(entity);
}
}
}
void func_80194774(void) {
D_801A3EE4 = 2;
D_801A3EE2 = 2;
@ -163,7 +10,65 @@ void func_80194774(void) {
D_801A3EDE = D_801A3EE0 + 20;
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_801947C8);
s32 func_801947C8(s32 arg0) {
Primitive* prim;
s16 firstPrimIndex;
firstPrimIndex = g_api.AllocPrimitives(PRIM_SPRT, 7);
D_801A3F10[0] = firstPrimIndex;
if (firstPrimIndex == -1) {
D_801A3F10[0] = 0;
return 0;
}
D_801A3ED8 = arg0;
D_801A3F14 = 0;
D_801A3F0C = -1;
D_801A3F08 = -1;
func_80194774();
if (prim && prim) { // !FAKE
}
prim = D_801A3EF0[0] = &g_PrimBuf[D_801A3F10[0]];
prim->blendMode = BLEND_VISIBLE;
prim = D_801A3EF0[1] = prim->next;
prim->blendMode = BLEND_VISIBLE;
prim = D_801A3EF0[2] = prim->next;
prim->blendMode = BLEND_VISIBLE;
prim = D_801A3EF0[3] = prim->next;
prim->blendMode = BLEND_VISIBLE;
prim = D_801A3EF0[4] = prim->next;
prim->blendMode = BLEND_VISIBLE;
prim = D_801A3EF0[5] = prim->next;
prim->type = 4;
prim->blendMode = BLEND_VISIBLE;
prim = prim->next;
prim->type = PRIM_G4;
prim->r0 = prim->r1 = prim->r2 = prim->r3 = 0xFF;
prim->b0 = prim->b1 = prim->b2 = prim->b3 = prim->g0 = prim->g1 = prim->g2 =
prim->g3 = 0;
prim->x0 = prim->x2 = 4;
prim->x1 = prim->x3 = 0xF8;
prim->priority = 0x1FD;
prim->blendMode = BLEND_VISIBLE;
prim = prim->next;
prim->type = PRIM_TILE;
prim->x0 = 3;
prim->y0 = 0x2F;
prim->v0 = 0x4A;
prim->r0 = prim->g0 = prim->b0 = 0xFF;
prim->priority = 0x1FC;
prim->blendMode = BLEND_VISIBLE;
return 1;
}
void func_8019498C(s16 yOffset) {
RECT rect;
@ -175,9 +80,9 @@ void func_8019498C(s16 yOffset) {
ClearImage(&rect, 0, 0, 0);
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_801949E8);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_801949E8);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_80194AA0);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_80194AA0);
void func_80194C24(s32 arg0) {
D_801A3F18 = arg0 + 0x100000;
@ -185,14 +90,40 @@ void func_80194C24(s32 arg0) {
D_801A3F14 = 1;
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_80194C50);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_80194C50);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_80194F14);
void func_80194F14(Entity* self) {
/** TODO: !FAKE
* do while (0) fixed instruction reordering at
* entity->flags ^= FLAG_HAS_PRIMS;
* but intruduces a problem in PlaySfx, which is fixed
* by using gameApi pointer.
*/
GameApi* gameApi;
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_80194FF4);
if (g_pads[0].tapped == PAD_START) {
D_801A3ED4 = 1;
g_api.FreePrimitives(self->primIndex);
do {
self->flags ^= FLAG_HAS_PRIMS;
} while (0);
if (D_801A3F0C != -1) {
g_api.FreePrimitives(D_801A3F0C);
}
if (D_801A3F08 != -1) {
g_api.FreePrimitives(D_801A3F08);
}
gameApi = &g_api;
(*gameApi).PlaySfx(SET_STOP_MUSIC);
self->step = 1;
self->step_s = 0;
}
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_80194FF4);
// dialogue with mother opens as alucard walks right ID 20
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", EntitySuccubusIntroCutscene);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", EntitySuccubusCutscene);
void func_801961DC(s16 arg0) {
s16 temp_v0 = arg0 - *(s16*)D_8009740C;
@ -206,16 +137,16 @@ void func_801961DC(s16 arg0) {
}
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", EntityUnkId21);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", EntityUnkId21);
// appears to load from the CD and freeze the game
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", EntityUnkId23);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", EntityUnkId23);
// Fades to white
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", EntityFadeToWhite1);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", EntityFadeToWhite1);
// Fades to white ID 24
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", EntityFadeToWhite2);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", EntityFadeToWhite2);
s32 Random(void) {
g_randomNext = (g_randomNext * 0x01010101) + 1;
@ -305,11 +236,11 @@ void Update(void) {
}
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_801972BC);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_801972BC);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_801973C4);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_801973C4);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", EntityNumericDamage);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", EntityNumericDamage);
void CreateEntityFromLayout(Entity* entity, LayoutEntity* initDesc) {
DestroyEntity(entity);
@ -422,9 +353,9 @@ void func_80198EC0(s16 arg0) {
}
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_80198F18);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_80198F18);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_80199014);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_80199014);
void func_80199128(s16 arg0) {
while (1) {
@ -446,9 +377,9 @@ void func_80199174(s16 arg0) {
}
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_801991CC);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_801991CC);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_801992C8);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_801992C8);
void InitRoomEntities(s32 objLayoutId) {
u16* pObjLayoutStart = D_80180220[objLayoutId];
@ -546,7 +477,7 @@ s32 func_801996F8(Entity* e) {
return diff;
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", EntityRedDoor);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", EntityRedDoor);
void DestroyEntity(Entity* item) {
s32 i;
@ -932,11 +863,11 @@ void SetStep(u8 step) {
entity->animFrameDuration = 0;
}
void func_8019B008(u8 arg0) {
void SetSubStep(u8 step_s) {
Entity* entity;
entity = g_CurrentEntity;
entity->step_s = arg0;
entity->step_s = step_s;
entity->animFrameIdx = 0;
entity->animFrameDuration = 0;
}
@ -1062,7 +993,7 @@ void func_8019B304(u16* hitSensors, s16 sensorCount) {
}
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_8019B45C);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_8019B45C);
void ReplaceBreakableWithItemDrop(Entity* self) {
u16 params;
@ -1169,7 +1100,7 @@ void func_8019BA38(u16 arg0) {
DestroyEntity(g_CurrentEntity);
}
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", func_8019BAB8);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", func_8019BAB8);
void CollectSubweapon(u16 subWeaponIdx) {
Entity* player = &PLAYER;
@ -1227,7 +1158,7 @@ void CollectLifeVessel(void) {
void DestroyCurrentEntity(void) { DestroyEntity(g_CurrentEntity); }
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14214", EntityPrizeDrop);
INCLUDE_ASM("asm/us/st/dre/nonmatchings/14774", EntityPrizeDrop);
void EntityExplosion(Entity* entity) {
u32 temp_v0;

View File

@ -1,24 +1,33 @@
#include "stage.h"
#define STAGE_DRE_H
typedef enum {
E_NONE,
E_BREAKABLE,
E_EXPLOSION,
E_PRIZE_DROP,
E_NUMERIC_DAMAGE,
E_RED_DOOR,
E_INTENSE_EXPLOSION,
E_SOUL_STEAL_ORB,
E_ROOM_FOREGROUND,
E_STAGE_NAME_POPUP,
E_EQUIP_ITEM_DROP,
E_RELIC_ORB,
E_HEART_DROP,
E_ENEMY_BLOOD,
E_SAVE_GAME_POPUP,
E_DUMMY_0F,
E_DUMMY_10,
} EntityIDs;
/* 0x00 */ E_NONE,
/* 0x01 */ E_BREAKABLE,
/* 0x02 */ E_EXPLOSION,
/* 0x03 */ E_PRIZE_DROP,
/* 0x04 */ E_NUMERIC_DAMAGE,
/* 0x05 */ E_RED_DOOR,
/* 0x06 */ E_INTENSE_EXPLOSION,
/* 0x07 */ E_SOUL_STEAL_ORB,
/* 0x08 */ E_ROOM_FOREGROUND,
/* 0x09 */ E_STAGE_NAME_POPUP,
/* 0x0A */ E_EQUIP_ITEM_DROP,
/* 0x0B */ E_RELIC_ORB,
/* 0x0C */ E_HEART_DROP,
/* 0x0D */ E_ENEMY_BLOOD,
/* 0x0E */ E_SAVE_GAME_POPUP,
/* 0x0F */ E_DUMMY_0F,
/* 0x10 */ E_DUMMY_10,
/* 0x1A */ E_SUCCUBUS_PETAL = 0x1A,
/* 0x1B */ E_SUCCUBUS_WING_OVERLAY,
/* 0x1C */ E_SUCCUBUS_CLONE,
/* 0x1D */ E_SUCCUBUS_PINK_BALL_PROJECTILE,
/* 0x1E */ E_SUCCUBUS_WING_SPIKE,
/* 0x1F */ E_SUCCUBUS_WING_SPIKE_TIP,
/* 0x20 */ E_SUCCUBUS_CUTSCENE,
} DRE_EntityIDs;
void ReplaceBreakableWithItemDrop(Entity* arg0);
void DestroyEntity(Entity* entity);
@ -38,10 +47,13 @@ void func_8019E5E0(Entity* entity);
LayoutEntity* D_80180220[];
LayoutEntity* D_801802F4[];
/* *** Initializers *** */
extern u16 D_80180464[];
extern u16 D_80180488[]; // Init EntityBackgroundClouds
extern u16 D_80180488[]; // EntityBackgroundClouds
extern u16 D_80180494[];
extern u16 D_801804A0[];
extern u16 D_801804D0[]; // EntitySuccubus
extern u16 D_801804DC[];
extern u16 D_80180500[];
extern u8 D_80180580[];
@ -49,25 +61,43 @@ extern u8 D_80180588[];
extern u16 D_80180590[];
extern s32 D_80180664;
/* *** EntitySuccubus animations START *** */
extern u8 D_8018066C[];
extern u8 D_80180674[];
extern u8 D_80180680[];
extern u8 D_80180694[];
extern u8 D_801806A0[];
extern u8 D_801806C4[];
extern u8 D_801806D4[];
extern u8 D_801806E8[];
extern u8 D_801806F8[];
extern u8 D_8018070C[];
extern u8 D_8018071C[];
extern u8 D_8018072C[];
extern u8 D_80180734[];
extern u8 D_80180748[];
extern u8 D_80180760[];
extern u8 D_80180768[];
extern u8 D_80180770[];
extern u8 D_80180778[];
extern u8 D_80180780[];
extern u8 D_8018079C[];
extern u8 D_801807AC[];
/* *** EntitySuccubus animations END *** */
extern s8 g_CloneShootOrder[4][7]; // 0x801807D4
extern u8 D_80180780[]; // Animation
extern const u8 D_80180794[];
extern s32 D_801807F8[];
extern u8 D_80180830[];
extern u16 D_8018097C[];
extern s16 D_80180D80[];
extern LayoutEntity* D_801A32C4;
extern u16* D_801A32C8;
extern s8 D_801A32CC;
extern u8 D_801A32D0;
extern u16 D_80180470[];
extern u16 D_801804AC[];
extern u16 D_801804F4[];
extern s8 c_HeartPrizes[];
extern s32 D_801811B0[];
extern u32 D_8018125C[];
extern s16 D_801812E4[];
extern u32 D_801812F4[];
extern u8 D_80181338[];
extern PfnEntityUpdate PfnEntityUpdates[];
extern u16 D_801804E8[];
extern u16 D_8018050C[];
@ -75,24 +105,18 @@ extern u16 D_80180528[];
extern s32 D_80180664;
extern s32 D_80180668;
extern s16 D_801807F0[];
extern s32 D_801811B0[];
extern u32 D_8018125C[];
extern s16 D_801812E4[];
extern u32 D_801812F4[];
extern s8 D_801816C4; // Succubus facing assigned to it
extern u8 D_80181338[];
extern u16 D_801811A4[];
extern u32 D_8018130C[];
extern u8 D_80181324[];
extern u16 D_80181328[];
extern u16 D_801810B0[];
extern u16 D_801810E0[];
extern s16 D_801A3EDE;
extern u16 D_801A3EE0;
extern s16 D_801A3EE2;
extern s16 D_801A3EE4;
extern s16 D_801A3EE6;
extern s16 D_801A3EEA;
extern s8 D_801A3EEE;
extern s8 D_801A3EEF;
extern s16 D_801A3F14;
extern s16 D_801A3F16;
extern s32 D_801A3F18;
extern u16 D_801A3F8C[];
// *** EntitySoulStealOrb properties START ***
@ -101,3 +125,27 @@ extern u16 D_8018139C[]; // NOTE(sestren): Animation frame properties?
extern u8 D_801813FC;
// *** EntitySoulStealOrb properties END ***
extern LayoutEntity* D_801A32C4;
extern u16* D_801A32C8;
extern s8 D_801A32CC;
extern u8 D_801A32D0;
extern s32 D_801A3ED4;
extern s32 D_801A3ED8;
extern s16 D_801A3EDE;
extern u16 D_801A3EE0;
extern s16 D_801A3EE2;
extern s16 D_801A3EE4;
extern s16 D_801A3EE6;
extern s16 D_801A3EEA;
extern s8 D_801A3EEE;
extern s8 D_801A3EEF;
extern Primitive* D_801A3EF0[];
extern s32 D_801A3F08;
extern s32 D_801A3F0C;
extern s32 D_801A3F10[];
extern s32 D_801A3F84;
extern s16 D_801A3F14;
extern s16 D_801A3F16;
extern s32 D_801A3F18;
extern u16 D_801A3F8C[];

958
src/st/dre/succubus.c Normal file
View File

@ -0,0 +1,958 @@
/*
* Overlay: DRE
* Enemy: Succubus Boss
* ID: 0x19
* BOSS ID: 9
*/
#include "dre.h"
typedef enum {
/* 0 */ SUCCUBUS_INIT,
/* 1 */ SUCCUBUS_CS_1,
/* 2 */ SUCCUBUS_CS_2,
/* 3 */ SUCCUBUS_CS_3,
/* 4 */ SUCCUBUS_CS_4,
/* 5 */ SUCCUBUS_IDLE,
/* 7 */ SUCCUBUS_FLY = 7,
/* 8 */ SUCCUBUS_PETAL_ATTACK,
/* 9 */ SUCCUBUS_CHARGE,
/* 11 */ SUCCUBUS_CLONE_ATTACK = 11,
/* 12 */ SUCCUBUS_SPIKE_ATTACK,
/* 13 */ SUCCUBUS_TAUNT,
/* 14 */ SUCCUBUS_GET_HIT,
/* 15 */ SUCCUBUS_FACE_PLAYER,
/* 16 */ SUCCUBUS_NEXT_ACTION_CHECK,
/* 18 */ SUCCUBUS_DYING = 18,
/* 255 */ SUCCUBUS_DEBUG = 255,
} SuccubusSteps;
typedef enum {
/* 0 */ SUCCUBUS_FLY_0,
/* 1 */ SUCCUBUS_FLY_1,
/* 2 */ SUCCUBUS_FLY_2,
} SuccubusFlySubSteps;
typedef enum {
/* 0 */ SUCCUBUS_PETAL_ATTACK_SETUP,
/* 1 */ SUCCUBUS_PETAL_ATTACK_ANIM,
/* 2 */ SUCCUBUS_PETAL_ATTACK_CREATE_PETALS,
} SuccubusPetalAttackSubSteps;
typedef enum {
/* 0 */ SUCCUBUS_CHARGE_SETUP,
/* 1 */ SUCCUBUS_CHARGE_FLY_TOWARDS_PLAYER,
/* 2 */ SUCCUBUS_CHARGE_AT_PLAYER_POSITION,
/* 3 */ SUCCUBUS_CHARGE_DEAL_DAMAGE,
/* 4 */ SUCCUBUS_CHARGE_FLY_AWAY,
/* 5 */ SUCCUBUS_CHARGE_DECELERATE,
} SuccubusChargeSubSteps;
typedef enum {
/* 0 */ SUCCUBUS_CLONE_ATTACK_ANIM_1,
/* 1 */ SUCCUBUS_CLONE_ATTACK_CREATE_CLONES,
/* 2 */ SUCCUBUS_CLONE_ATTACK_WAIT,
/* 3 */ SUCCUBUS_CLONE_ATTACK_PLACE_REAL,
/* 4 */ SUCCUBUS_CLONE_ATTACK_ANIM_2,
/* 5 */ SUCCUBUS_CLONE_ATTACK_SET_SHOOTING,
/* 6 */ SUCCUBUS_CLONE_ATTACK_STOP_SHOOTING,
/* 7 */ SUCCUBUS_CLONE_ATTACK_SHOOT_PINKBALLS, // unused
} SuccubusCloneAttackSubSteps;
typedef enum {
/* 0 */ SUCCUBUS_SPIKE_ATTACK_CREATE_SPIKES,
/* 1 */ SUCCUBUS_SPIKE_ATTACK_1,
/* 2 */ SUCCUBUS_SPIKE_ATTACK_2,
/* 3 */ SUCCUBUS_SPIKE_ATTACK_3,
/* 4 */ SUCCUBUS_SPIKE_ATTACK_4,
} SuccubusSpikeAttackSubSteps;
typedef enum {
/* 0 */ SUCCUBUS_DYING_SETUP,
/* 1 */ SUCCUBUS_DYING_FALL,
/* 2 */ SUCCUBUS_DYING_LAND,
/* 3 */ SUCCUBUS_DYING_ANIM_1,
/* 4 */ SUCCUBUS_DYING_ANIM_2,
} SuccubusDyingSubSteps;
// Original name: multiple_count
extern s32 D_80180660; // clones counter
void EntitySuccubus(Entity* self) {
const int SeenCutscene = 212;
u8* clonesShootOrder;
s32 sideToPlayer;
Entity* entity;
s32 posX, posY;
s8* hitbox;
s32 facing;
s16 angle;
s32 temp;
s32 i;
FntPrint("multiple_count %x\n", D_80180660);
if ((self->hitFlags & 3) && (self->step & SUCCUBUS_CS_1)) {
SetStep(SUCCUBUS_GET_HIT);
}
if (self->flags & 0x100) {
if (self->step != SUCCUBUS_DYING) {
self->hitboxState = 0;
SetStep(SUCCUBUS_DYING);
}
}
switch (self->step) {
case SUCCUBUS_INIT:
InitializeEntity(D_801804D0);
self->animCurFrame = 82;
SetStep(SUCCUBUS_CS_1);
CreateEntityFromCurrentEntity(E_SUCCUBUS_WING_OVERLAY, &self[1]);
case SUCCUBUS_CS_1: // Disguised as Lisa
if (D_8003BDEC[SeenCutscene] || (g_DemoMode != Demo_None)) {
self->facing = 0;
self->posX.i.hi = 416 - g_Camera.posX.i.hi;
self->posY.i.hi = 175 - g_Camera.posY.i.hi;
SetStep(SUCCUBUS_CS_4);
self->step_s = 3;
}
self->animCurFrame = 82;
if (D_801A3F84 & 4) {
SetStep(SUCCUBUS_CS_2);
}
break;
case SUCCUBUS_CS_2: // Disguised as Lisa
if (D_801A3ED4 != 0) {
SetSubStep(4);
}
switch (self->step_s) {
case 0:
AnimateEntity(D_8018079C, self);
if (D_801A3F84 & 0x400) {
SetSubStep(1);
}
break;
case 1:
AnimateEntity(D_801807AC, self);
if (D_801A3F84 & 0x800) {
self->animCurFrame = 84;
SetSubStep(2);
}
break;
case 2:
if (D_801A3F84 & 0x1000) {
SetSubStep(3);
}
break;
case 3:
self->animCurFrame = 83;
if (D_801A3F84 & 0x2000) {
SetSubStep(4);
}
break;
case 4:
self->animCurFrame = 84;
if (D_801A3F84 & 0x20) {
SetStep(SUCCUBUS_CS_3);
}
break;
}
break;
// Sets Succubus in position
case SUCCUBUS_CS_3:
if ((D_801A3ED4 == 0) || (self->step_s == 0)) {
switch (self->step_s) {
case 0:
self->facing = 0;
self->posX.i.hi = 416 - g_Camera.posX.i.hi;
self->posY.i.hi = 175 - g_Camera.posY.i.hi;
self->step_s++;
case 1:
AnimateEntity(D_8018066C, self);
if (D_801A3F84 & 0x40) {
SetSubStep(2);
}
break;
case 2:
self->animCurFrame = 4;
if (D_801A3F84 & 0x80) {
SetSubStep(3);
}
break;
case 3:
AnimateEntity(D_80180674, self);
if (D_801A3F84 & 0x100) {
SetSubStep(4);
}
break;
case 4:
if (AnimateEntity(D_80180680, self) == 0) {
SetStep(SUCCUBUS_CS_4);
}
break;
}
} else {
SetStep(SUCCUBUS_CS_4);
}
break;
// Ascends Succubus into the air
case SUCCUBUS_CS_4:
switch (self->step_s) {
case 0:
if (AnimateEntity(D_80180694, self) == 0) {
SetSubStep(1);
}
break;
case 1:
g_api.PlaySfx(MU_ENCHANTED_BANQUET);
g_api.func_800FD4C0(9, 2);
self->velocityX = 0;
self->velocityY = -0x40000;
self->step_s++;
case 2:
MoveEntity();
self->velocityY += 0x2000;
if (self->velocityY > 0) {
self->velocityY = 0;
}
if (AnimateEntity(D_801806A0, self) == 0) {
SetStep(SUCCUBUS_IDLE);
}
break;
case 3:
AnimateEntity(D_80180694, self);
if (GetDistanceToPlayerX() < 96) {
SetSubStep(1);
}
break;
}
break;
case SUCCUBUS_DYING:
switch (self->step_s) {
case SUCCUBUS_DYING_SETUP:
func_801A046C(NA_VO_SU_NO_SCREAM);
CreateEntityFromCurrentEntity(
E_SUCCUBUS_CUTSCENE, &g_Entities[200]);
g_Entities[200].params = 1;
D_80180660 = 0;
D_80180664 |= 2;
g_api.func_800FD4C0(9, 1);
self->velocityX = 0;
self->velocityY = 0;
posY = self->posY.i.hi + g_Camera.posY.i.hi;
if (posY > 160) {
self->velocityY = -0x20000;
self->step_s = 1;
} else {
self->step_s = 2;
}
break;
case SUCCUBUS_DYING_FALL:
AnimateEntity(D_80180768, self);
MoveEntity();
self->velocityY += 0x2000;
if (self->velocityY > 0) {
self->step_s = 2;
}
break;
case SUCCUBUS_DYING_LAND:
AnimateEntity(D_80180768, self);
MoveEntity();
self->velocityY += 0x2000;
posY = self->posY.i.hi + g_Camera.posY.i.hi;
if (posY >= 176) {
func_801A046C(NA_SE_SU_LANDING);
self->posY.i.hi = 175 - g_Camera.posY.i.hi;
SetSubStep(SUCCUBUS_DYING_ANIM_1);
posX = self->posX.i.hi + g_Camera.posX.i.hi;
if (posX < 80) {
D_801816C4 = self->facing = 1;
} else if (posX > 432) {
D_801816C4 = self->facing = 0;
} else {
D_801816C4 = self->facing = (GetSideToPlayer() & 1) ^ 1;
}
D_801A3F84 |= 2;
}
break;
case SUCCUBUS_DYING_ANIM_1:
AnimateEntity(D_80180770, self);
if (D_801A3F84 & 0x10) {
SetSubStep(SUCCUBUS_DYING_ANIM_2);
}
break;
case SUCCUBUS_DYING_ANIM_2:
AnimateEntity(D_80180778, self);
}
break;
case SUCCUBUS_IDLE:
if (self->step_s == 0) {
self->ext.succubus.timer = 64;
self->step_s++;
}
AnimateEntity(D_801806C4, self);
if ((self->animFrameIdx == 3) && (self->animFrameDuration == 0)) {
func_801A046C(NA_SE_SU_FLAPPING_WINGS);
}
posY = self->posY.i.hi - self->ext.succubus.yOffset;
if (posY > 8) {
self->velocityY = -0xC000;
}
if (--self->ext.succubus.timer == 0) {
self->ext.succubus.nextStep = SUCCUBUS_FLY;
SetStep(SUCCUBUS_FACE_PLAYER);
}
if ((self->ext.succubus.timer < 80) && (GetDistanceToPlayerX() < 80)) {
self->ext.succubus.nextStep = SUCCUBUS_FLY;
SetStep(SUCCUBUS_FACE_PLAYER);
}
break;
case SUCCUBUS_FLY:
switch (self->step_s) {
case SUCCUBUS_FLY_0:
self->velocityY = 0;
self->ext.succubus.timer = (Random() & 31) + 32;
self->ext.succubus.nextAttack = SUCCUBUS_PETAL_ATTACK;
self->ext.succubus.yOffset = 88;
if (!(Random() % 4)) {
if (Random() % 2) {
self->ext.succubus.nextAttack = SUCCUBUS_SPIKE_ATTACK;
self->ext.succubus.yOffset = 120;
} else {
self->ext.succubus.nextAttack = SUCCUBUS_CLONE_ATTACK;
}
}
self->ext.succubus.facing = 0;
self->step_s++;
case SUCCUBUS_FLY_1:
AnimateEntity(D_801806E8, self);
if ((self->animFrameIdx == 3) && (self->animFrameDuration == 0)) {
func_801A046C(NA_SE_SU_FLAPPING_WINGS);
}
MoveEntity();
posY = self->posY.i.hi - self->ext.succubus.yOffset;
if (posY > 8) {
self->velocityY = -0xC000;
}
if (posY < -8) {
self->velocityY = 0xC000;
}
if (self->facing != self->ext.succubus.facing) {
self->velocityX += 0x1800;
if (self->velocityX >= 0x16000) {
self->velocityX = 0x16000;
}
} else {
self->velocityX -= 0x1800;
if (self->velocityX <= -0x16000) {
self->velocityX = -0x16000;
}
}
if (self->ext.succubus.nextAttack == SUCCUBUS_CLONE_ATTACK) {
posX = 64;
} else {
posX = 96;
}
temp = GetDistanceToPlayerX();
if (self->ext.succubus.facing == 0) {
if (temp < posX) {
self->ext.succubus.facing ^= 1;
}
if (self->ext.succubus.facing != 0) {
if (posX < temp) {
self->ext.succubus.facing ^= 1;
}
}
} else if (posX < temp) {
self->ext.succubus.facing ^= 1;
}
sideToPlayer = ((GetSideToPlayer() & 1) ^ 1);
if (self->facing != sideToPlayer) {
if (temp > 16) {
self->ext.succubus.nextStep = SUCCUBUS_CLONE_ATTACK;
SetStep(SUCCUBUS_NEXT_ACTION_CHECK);
}
}
posX = self->posX.i.hi + g_Camera.posX.i.hi;
if (self->facing != 0) {
posX = 512 - posX;
}
if (posX > 416) {
self->ext.succubus.facing = 0;
self->ext.succubus.timer = 96;
self->step_s++;
}
if (self->ext.succubus.timer == 0) {
if (GetDistanceToPlayerX() < 96) {
if (self->ext.succubus.nextAttack ==
SUCCUBUS_PETAL_ATTACK) {
SetStep(SUCCUBUS_PETAL_ATTACK);
} else {
self->ext.succubus.nextStep =
self->ext.succubus.nextAttack;
SetStep(SUCCUBUS_NEXT_ACTION_CHECK);
}
}
} else {
self->ext.succubus.timer--;
}
break;
case SUCCUBUS_FLY_2:
if (self->facing != self->ext.succubus.facing) {
self->velocityX += 0x1800;
if (self->velocityX >= 0x16000) {
self->velocityX = 0x16000;
}
} else {
self->velocityX -= 0x1800;
if (self->velocityX <= -0x16000) {
self->velocityX = -0x16000;
}
}
AnimateEntity(D_801806E8, self);
if ((self->animFrameIdx == 3) && (self->animFrameDuration == 0)) {
func_801A046C(NA_SE_SU_FLAPPING_WINGS);
}
MoveEntity();
if (--self->ext.succubus.timer == 0) {
self->step_s = 0;
}
break;
}
break;
case SUCCUBUS_FACE_PLAYER:
if (self->step_s == 0) {
self->facing = (GetSideToPlayer() & 1) ^ 1;
self->step_s++;
}
if (AnimateEntity(D_801806D4, self) == 0) {
if (self->ext.succubus.nextStep != 0) {
SetStep(self->ext.succubus.nextStep);
self->ext.succubus.nextStep = 0;
} else {
SetStep(SUCCUBUS_FLY);
}
}
break;
case SUCCUBUS_NEXT_ACTION_CHECK:
if (AnimateEntity(D_801806F8, self) == 0) {
if (self->ext.succubus.nextStep != 0) {
SetStep(self->ext.succubus.nextStep);
self->ext.succubus.nextStep = 0;
} else {
SetStep(SUCCUBUS_IDLE);
}
}
break;
case SUCCUBUS_PETAL_ATTACK:
switch (self->step_s) {
case SUCCUBUS_PETAL_ATTACK_SETUP:
self->facing = (GetSideToPlayer() & 1) ^ 1;
if (g_Player.unk0C & PLAYER_STATUS_CURSE) {
self->ext.succubus.unk87 = false;
} else {
self->ext.succubus.unk87 = true;
}
D_80180668 = 0;
self->step_s++;
case SUCCUBUS_PETAL_ATTACK_ANIM:
if (AnimateEntity(D_8018070C, self) == 0) {
self->ext.succubus.timer = 128;
func_801A046C(NA_VO_SU_LAUGH);
self->step_s++;
}
break;
case SUCCUBUS_PETAL_ATTACK_CREATE_PETALS:
if (!(self->ext.succubus.timer & 1)) {
entity = AllocEntity(&g_Entities[160], &g_Entities[192]);
if (entity != NULL) {
CreateEntityFromEntity(E_SUCCUBUS_PETAL, self, entity);
entity->facing = self->facing;
entity->zPriority = self->zPriority - 1;
}
}
if ((self->ext.succubus.timer % 64) == 0) {
func_801A046C(NA_SE_SU_PETAL_ATTACK);
}
if (--self->ext.succubus.timer == 0) {
SetStep(SUCCUBUS_FLY);
}
if (self->ext.succubus.unk87) {
if (g_Player.unk0C & PLAYER_STATUS_CURSE) {
SetStep(SUCCUBUS_CHARGE);
}
} else if (D_80180668 != 0) {
if (GetDistanceToPlayerX() > 72) {
self->ext.succubus.nextStep = SUCCUBUS_TAUNT;
SetStep(SUCCUBUS_FACE_PLAYER);
}
}
break;
}
break;
case SUCCUBUS_CHARGE:
switch (self->step_s) {
case SUCCUBUS_CHARGE_SETUP:
self->ext.succubus.timer = 112;
self->animCurFrame = 36;
func_801A046C(NA_VO_SU_BLANK);
self->step_s++;
case SUCCUBUS_CHARGE_FLY_TOWARDS_PLAYER:
entity = &PLAYER;
posX = entity->posX.i.hi - self->posX.i.hi;
if (self->facing != 0) {
posX -= 16;
} else {
posX += 16;
}
posY = entity->posY.i.hi - self->posY.i.hi;
angle = ratan2(posY, posX);
self->velocityX = (rcos(angle) << 0x11) >> 0xC;
self->velocityY = (rsin(angle) << 0x11) >> 0xC;
posX = self->velocityX;
if (self->facing != 0) {
posX = -posX;
}
if (posX > 0) {
self->velocityX = 0;
}
MoveEntity();
posX = PLAYER.posX.i.hi - self->posX.i.hi;
if (self->facing == 0) {
posX = -posX;
}
posX -= 4;
if (posX < 0) {
posX = 512;
}
posY = PLAYER.posY.i.hi - self->posY.i.hi;
if (posY < 0) {
posY = -posY;
}
if (!(g_Player.unk0C & 0x010401A2)) {
if ((posY < 12) && (posX < 24)) {
g_Player.unk60 = 1;
g_Player.unk64 = 0;
g_Player.unk62 = 0;
self->hitboxState = 0;
self->step_s++;
}
} else {
FntPrint("ng status\n");
}
posY = self->posY.i.hi + g_Camera.posY.i.hi;
if (posY > 176) {
self->posY.i.hi = 176 - g_Camera.posY.i.hi;
}
if (g_Player.unk60 == 0) {
if (--self->ext.succubus.timer == 0) {
self->ext.succubus.unk87 = false;
SetStep(SUCCUBUS_FLY);
}
}
break;
case SUCCUBUS_CHARGE_AT_PLAYER_POSITION:
entity = &PLAYER;
posX = entity->posX.i.hi;
if (self->facing != 0) {
posX -= 16;
} else {
posX += 16;
}
self->posY.i.hi = posY = entity->posY.i.hi;
self->posX.i.hi = posX;
func_801A046C(NA_VO_SU_SUCK_YOU_DRY);
self->step_s++;
case SUCCUBUS_CHARGE_DEAL_DAMAGE:
if (AnimateEntity(D_8018071C, self) == 0) {
g_Player.unk64 = g_api.enemyDefs[347].attack;
g_Player.unk60 = 3;
self->ext.succubus.timer = 48;
self->step_s++;
}
break;
case SUCCUBUS_CHARGE_FLY_AWAY:
if (--self->ext.succubus.timer == 0) {
g_Player.unk60 = 0;
self->hitboxState = 3;
if (self->facing != 0) {
self->velocityX = -0x40000;
} else {
self->velocityX = 0x40000;
}
self->velocityY = -0x40000;
self->animCurFrame = 47;
self->ext.succubus.timer = 24;
self->step_s++;
}
break;
case SUCCUBUS_CHARGE_DECELERATE:
MoveEntity();
self->velocityX -= self->velocityX >> 5;
self->velocityY -= self->velocityY >> 5;
if (--self->ext.succubus.timer == 0) {
SetStep(SUCCUBUS_TAUNT);
}
break;
}
break;
case SUCCUBUS_TAUNT:
if (self->step_s == 0) {
self->ext.succubus.timer = 80;
self->facing = (GetSideToPlayer() & 1) ^ 1;
self->step_s++;
if (self->ext.succubus.unk87) {
func_801A046C(NA_VO_SU_DELICIOUS);
} else {
func_801A046C(NA_VO_SU_LAUGH);
}
self->ext.succubus.unk87 = false;
}
AnimateEntity(D_8018072C, self);
if ((--self->ext.succubus.timer == 0) ||
(GetDistanceToPlayerX() < 32)) {
SetStep(SUCCUBUS_FLY);
}
break;
case SUCCUBUS_CLONE_ATTACK:
switch (self->step_s) {
case SUCCUBUS_CLONE_ATTACK_CREATE_CLONES:
posX = self->posX.i.hi + g_Camera.posX.i.hi;
posX -= 192;
// left bound limit
if (posX < 32) {
posX = 96 - ((32 - posX) % 64);
}
// right bound limit
temp = posX + 384;
if (temp > 480) {
posX = ((posX - 96) % 64) + 32;
}
clonesShootOrder = *g_CloneShootOrder;
temp = Random() & 3;
clonesShootOrder += temp * 7;
entity = &g_Entities[112];
temp = self->posX.i.hi + g_Camera.posX.i.hi;
for (i = 0; i < 6; i++, entity++, clonesShootOrder++, posX += 64) {
CreateEntityFromEntity(E_SUCCUBUS_CLONE, self, entity);
// Giving each clone a pointer to the real Succubus
entity->ext.succubus.real = self;
if (posX == temp) {
posX += 64;
}
entity->ext.succubus.clonePosX = posX;
entity->params = *clonesShootOrder;
}
self->params = *clonesShootOrder;
self->ext.succubus.timer = 64;
self->hitboxState = 0;
D_80180660 = 6;
func_801A046C(NA_VO_SU_GRUNT_1);
func_801A046C(NA_SE_SU_CREATE_CLONES);
self->step_s++;
case SUCCUBUS_CLONE_ATTACK_WAIT:
if (--self->ext.succubus.timer == 0) {
self->step_s = 3;
}
case SUCCUBUS_CLONE_ATTACK_ANIM_1:
self->ext.succubus.unk85 = false;
AnimateEntity(D_80180734, self);
if ((self->animFrameIdx == 4) && (self->animFrameDuration == 0)) {
self->step_s++;
}
break;
case SUCCUBUS_CLONE_ATTACK_PLACE_REAL:
entity = &g_Entities[112];
temp = Random() % 6;
entity += temp;
posX = entity->posX.i.hi;
posY = entity->posY.i.hi;
entity->posX.i.hi = self->posX.i.hi;
entity->posY.i.hi = self->posY.i.hi;
self->posX.i.hi = posX;
self->posY.i.hi = posY;
self->hitboxState = 3;
self->step_s++;
case SUCCUBUS_CLONE_ATTACK_ANIM_2:
if (AnimateEntity(D_80180734, self) == 0) {
SetSubStep(SUCCUBUS_CLONE_ATTACK_SET_SHOOTING);
}
break;
case SUCCUBUS_CLONE_ATTACK_SET_SHOOTING:
if (AnimateEntity(D_80180760, self) == 0) {
self->ext.succubus.unk85 = true;
// PinkBall attack delay set
self->ext.succubus.timer = (self->params * 48) + 1;
SetSubStep(SUCCUBUS_CLONE_ATTACK_STOP_SHOOTING);
}
break;
case SUCCUBUS_CLONE_ATTACK_STOP_SHOOTING:
self->animCurFrame = 26;
if (--self->ext.succubus.timer == 0) {
self->ext.succubus.unk85 = false;
/*! @bug: Likely a typo meant to be
* SetSubStep(SUCCUBUS_CLONE_ATTACK_SHOOT_PINKBALLS);
* case SUCCUBUS_CLONE_ATTACK_SHOOT_PINKBALLS is
* inaccessible causing the real Succubus to be
* unable to do the PinkBall attack.
*/
SetSubStep(SUCCUBUS_CLONE_ATTACK_STOP_SHOOTING);
}
if (D_80180660 == 0) {
SetStep(SUCCUBUS_IDLE);
}
break;
/* Unused: see @bug in previous case.
* The real Succubus was supposed to shoot last, hinting
* the player her real position, but the end result is
* she doesn't shoot at all.
*/
case SUCCUBUS_CLONE_ATTACK_SHOOT_PINKBALLS:
if (AnimateEntity(D_80180780, self) == 0) {
self->ext.succubus.timer = 288;
SetSubStep(SUCCUBUS_CLONE_ATTACK_ANIM_2);
}
if ((self->animFrameIdx == 4) && (self->animFrameDuration == 0)) {
func_801A046C(NA_SE_SU_CHARGE_PINKBALLS);
for (i = 0; i < 2; i++) {
entity = AllocEntity(&g_Entities[160], &g_Entities[192]);
if (entity != NULL) {
CreateEntityFromEntity(
E_SUCCUBUS_PINK_BALL_PROJECTILE, self, entity);
entity->params = i;
if (i != 0) {
entity->posX.i.hi -= 2;
} else {
entity->posX.i.hi += 2;
}
entity->ext.succubus.real = self;
entity->posY.i.hi -= 13;
entity->zPriority = self->zPriority + 1;
}
}
}
if ((self->animFrameIdx == 5) && (self->animFrameDuration == 0)) {
func_801A046C(NA_VO_SU_GRUNT_2);
func_801A046C(NA_VO_SU_CRYSTAL_1);
func_801A046C(NA_SE_SU_SHOOT_PINKBALLS);
self->ext.succubus.unk85 = true;
}
break;
}
break;
case SUCCUBUS_SPIKE_ATTACK:
switch (self->step_s) {
case SUCCUBUS_SPIKE_ATTACK_CREATE_SPIKES:
entity = &g_Entities[96];
self->facing = 0;
self->ext.succubus.unk85 = false;
D_80180668 = 0;
for (facing = 0; facing < 2; facing++) {
for (i = 0; i < 4; i++, entity += 2) {
CreateEntityFromEntity(E_SUCCUBUS_WING_SPIKE, self, entity);
entity->params = i;
entity->zPriority = self->zPriority;
entity->ext.succubus.real = self;
if (facing == 0) {
entity->posX.i.hi -= 5;
} else {
entity->posX.i.hi += 4;
}
entity->posY.i.hi -= 27;
entity->facing = facing;
}
}
self->step_s++;
case SUCCUBUS_SPIKE_ATTACK_1:
if (AnimateEntity(D_80180748, self) == 0) {
self->ext.succubus.unk85 = true;
func_801A046C(NA_VO_SU_CRYSTAL_2);
func_801A046C(NA_VO_SU_GRUNT_3);
self->ext.succubus.timer = 64;
SetSubStep(2);
}
break;
case SUCCUBUS_SPIKE_ATTACK_2:
if (--self->ext.succubus.timer == 0) {
self->ext.succubus.unk85 = false;
self->ext.succubus.timer = 64;
self->step_s++;
}
break;
case SUCCUBUS_SPIKE_ATTACK_3:
if (--self->ext.succubus.timer == 0) {
self->step_s++;
}
break;
case SUCCUBUS_SPIKE_ATTACK_4:
if (AnimateEntity(D_80180760, self) == 0) {
SetStep(SUCCUBUS_IDLE);
}
if (D_80180668 != 0) {
if (GetDistanceToPlayerX() > 64) {
self->ext.succubus.nextStep = SUCCUBUS_TAUNT;
SetStep(SUCCUBUS_FACE_PLAYER);
}
}
break;
}
break;
case SUCCUBUS_GET_HIT:
if (self->step_s == 0) {
if (Random() % 2) {
func_801A046C(NA_VO_SU_HURT_1);
} else {
func_801A046C(NA_VO_SU_HURT_2);
}
self->ext.succubus.timer = 32;
D_80180660 = 0;
if (GetSideToPlayer() & 1) {
self->velocityX = 0x20000;
} else {
self->velocityX = -0x20000;
}
self->velocityY = -0x20000;
self->step_s++;
}
AnimateEntity(D_80180768, self);
MoveEntity();
self->velocityX -= self->velocityX >> 5;
self->velocityY -= self->velocityY >> 5;
self->velocityY += 0x1000;
if (--self->ext.succubus.timer == 0) {
if (Random() % 2) {
self->ext.succubus.nextStep = SUCCUBUS_SPIKE_ATTACK;
} else {
self->ext.succubus.nextStep = SUCCUBUS_CLONE_ATTACK;
}
SetStep(SUCCUBUS_NEXT_ACTION_CHECK);
}
break;
case SUCCUBUS_DEBUG:
/**
* Debug: Press SQUARE / CIRCLE on the second controller
* to advance/rewind current animation frame
*/
FntPrint("charal %x\n", self->animCurFrame);
if (g_pads[1].pressed & PAD_SQUARE) {
if (self->params == 0) {
self->animCurFrame++;
self->params |= 1;
} else {
break;
}
} else {
self->params = 0;
}
if (g_pads[1].pressed & PAD_CIRCLE) {
if (self->step_s == 0) {
self->animCurFrame--;
self->step_s |= 1;
}
} else {
self->step_s = 0;
}
break;
}
posX = self->posX.i.hi + g_Camera.posX.i.hi;
posY = self->posY.i.hi + g_Camera.posY.i.hi;
if (self->velocityX < 0) {
if (posX < 40) {
self->posX.i.hi = 40 - g_Camera.posX.i.hi;
}
} else if (posX > 480) {
self->posX.i.hi = 480 - g_Camera.posX.i.hi;
}
if ((self->velocityY < 0) && (posY < 48)) {
self->posY.i.hi = 48 - g_Camera.posY.i.hi;
}
// TODO: !FAKE
hitbox = (s8*)&D_80180830[self->animCurFrame][D_801807F8];
hitbox--;
hitbox++;
self->hitboxOffX = *hitbox++;
self->hitboxOffY = *hitbox++;
self->hitboxWidth = hitbox[0];
self->hitboxHeight = hitbox[1];
}
const u32 rodataPadding = 0;