Refactor/clean-up of some of Alucard's jumping logic (#1376)

There was a question in the decomp discord about the bounce when Alucard
dive-kicks and hits an enemy. I tracked this down, and in the process,
found some moderately old functions that could use some improvements to
their code style. I also cross-referenced these with PSP to make the
code make a bit more sense.

The PSP version makes it clear that D_800ACF7C is not a struct and is
just an array of s16, so I made that change. The struct version matched
on PS1, but now that PSP reveals that it was fake, we turn it back to
just an array.

Let me know if I missed anything! Nice to have real names for some of
these functions.
This commit is contained in:
bismurphy 2024-07-05 13:58:54 -04:00 committed by GitHub
parent 27d9dfeeb2
commit 8ac5ebc378
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 154 additions and 135 deletions

View File

@ -194,7 +194,7 @@ segments:
- [0x413C0, .rodata, 71830] # func_80111830
# - [0x41400, .rodata, 71830] # func_801119C4
- [0x41410, .rodata, 71830] # func_801120B4
- [0x41588, .rodata, 72BB0] # func_80112BB0
- [0x41588, .rodata, 72BB0] # PlayerStepJump
# - [0x41750, .rodata, 72BB0] # func_801131C4
# - [0x418E0, .rodata, 72BB0] # func_80113AAC
# - [0x418F4, .rodata, 72BB0] # AlucardHandleDamage

View File

@ -929,7 +929,7 @@ block_160:
func_80113148();
break;
case Player_Jump:
func_80112BB0();
PlayerStepJump();
break;
case Player_MorphBat:
ControlBatForm();

View File

@ -406,7 +406,13 @@ void DecelerateY(s32 amount) {
}
}
s32 func_8010E27C(void) {
// Checks the player's left/right inputs and compares to the facing direction.
// If the player is pressing the opposite of facing, we change the facing value
// to turn the player around, and return -1.
// If the player is pressing the same direction they are facing, return 1
// If the player is not pressing left or right, return 0
// Note that if the player is pressing both left and right, left is ignored.
s32 CheckMoveDirection(void) {
if (g_Player.unk44 & 2) {
return 0;
}
@ -609,7 +615,7 @@ void func_8010E83C(s32 arg0) {
if (g_Player.unk72 != 0) {
func_8010E7AC();
return;
} else if (func_8010E27C() != 0) {
} else if (CheckMoveDirection() != 0) {
SetPlayerAnim(0x1A);
SetSpeedX(0x18000);
g_Player.unk44 = 0;
@ -619,7 +625,7 @@ void func_8010E83C(s32 arg0) {
g_Player.unk44 = 4;
}
PLAYER.velocityY = 0xFFFB0000 | 0x2000;
PLAYER.velocityY = FIX(-4.875);
SetPlayerStep(Player_Jump);
if (g_Player.unk50 == 1) {
@ -644,7 +650,7 @@ void func_8010E940(void) {
}
void DoGravityJump(void) {
if (func_8010E27C() != 0) {
if (CheckMoveDirection() != 0) {
SetSpeedX(FIX(3));
} else {
PLAYER.velocityX = 0;
@ -1056,14 +1062,14 @@ block_45:
goto block_98;
case 23: // Unknown, not a direct equippable item (but there are 4 of them)
PLAYER.step = 0;
func_8010E27C();
CheckMoveDirection();
SetSpeedX(FIX(5));
g_CurrentEntity->velocityY = 0;
PlaySfx(0x6EF);
goto block_98;
case 27: // Estoc
animVariant = atLedge;
func_8010E27C();
CheckMoveDirection();
SetSpeedX(FIX(4));
PLAYER.velocityX >>= 1;
PlaySfx(0x6EF);
@ -1318,7 +1324,7 @@ bool func_8010FDF8(s32 branchFlags) {
s32 YAccel;
if (branchFlags & 8 && g_Player.unk46 == 0) {
func_8010E27C();
CheckMoveDirection();
}
YAccel = -((branchFlags & 0x8000) != 0) & 0x2C00;
if (branchFlags & 0x10000) {

View File

@ -326,7 +326,7 @@ void func_801120B4(void) {
if ((u16)PLAYER.animFrameIdx < (u16)g_Player.unk54) {
local_flags = 0;
if (PLAYER.animFrameIdx < 2U) {
func_8010E27C();
CheckMoveDirection();
if (g_Player.padPressed & PAD_DOWN) {
PLAYER.step = 2;
PLAYER.ext.player.anim = 0x26;
@ -370,7 +370,7 @@ void func_801120B4(void) {
if (PLAYER.animFrameIdx >= 3U) {
break;
}
func_8010E27C();
CheckMoveDirection();
if (g_Player.padPressed & PAD_DOWN) {
// Note that to reach this point, our minimum case is 0x41
PLAYER.ext.player.anim = D_800B0608[PLAYER.step_s - 0x41] + 2;
@ -515,7 +515,7 @@ void func_801120B4(void) {
}
}
if ((local_flags & 1) && func_8010E27C()) {
if ((local_flags & 1) && CheckMoveDirection()) {
func_8010E6AC(0);
local_flags |= 0x8000;
}
@ -527,7 +527,7 @@ void func_801120B4(void) {
void func_80112B64(void) {
if (func_8010FDF8(0x4301C) == 0) {
SetSpeedX(0x18000);
if (func_8010E27C() == 0) {
if (CheckMoveDirection() == 0) {
func_8010E570(0);
}
}

View File

@ -2,55 +2,56 @@
#include "dra.h"
#include "sfx.h"
void func_80112BB0(void) {
char pad[12];
s32 temp_v0;
s32 temp_v1;
s32 var_s2;
void PlayerStepJump(void) {
s32 walkResult;
s16 stepSlot;
if (PLAYER.step_s != 0x58) {
DecelerateX(0x1000);
if (PLAYER.velocityY < -0x10000) {
DecelerateX(FIX(1.0 / 16));
if (PLAYER.velocityY < FIX(-1)) {
if (!(g_Player.unk44 & 0x40) &&
!(g_Player.padPressed & PAD_CROSS)) {
PLAYER.velocityY = -0x10000;
PLAYER.velocityY = FIX(-1);
}
if (g_Player.pl_vram_flag & 2) {
PLAYER.velocityY = -0x4000;
PLAYER.velocityY = FIX(-0.25);
g_Player.unk44 |= 0x20;
}
}
if (func_8010FDF8(0x11029) != 0) {
if (func_8010FDF8(0x11029)) {
return;
}
}
switch (PLAYER.step_s) {
case 0x0:
var_s2 = func_8010E27C();
if (var_s2 != 0) {
walkResult = CheckMoveDirection();
if (walkResult != 0) {
if ((PLAYER.ext.player.anim == 0x16) ||
(PLAYER.ext.player.anim == 0x19)) {
SetPlayerAnim(0x18U);
SetPlayerAnim(0x18);
}
SetSpeedX(0x18000);
SetSpeedX(FIX(1.5));
} else if ((PLAYER.ext.player.anim == 0x1A) ||
(PLAYER.ext.player.anim == 0x18)) {
SetPlayerAnim(0x19U);
SetPlayerAnim(0x19);
}
if (var_s2 <= 0) {
g_Player.unk44 &= 0xFFEF;
if (walkResult <= 0) {
g_Player.unk44 &= ~0x10;
}
if (PLAYER.velocityY > 0) {
if (PLAYER.ext.player.anim != 0x1B) {
SetPlayerAnim(0x1BU);
SetPlayerAnim(0x1B);
}
PLAYER.step_s = 1;
}
break;
case 0x1:
var_s2 = func_8010E27C();
if ((var_s2 == 0) || (SetSpeedX(0x18000), (var_s2 <= 0))) {
g_Player.unk44 &= 0xFFEF;
walkResult = CheckMoveDirection();
if (walkResult != 0) {
SetSpeedX(FIX(1.5));
}
if (walkResult <= 0) {
g_Player.unk44 &= ~0x10;
}
break;
case 0x58:
@ -70,12 +71,16 @@ void func_80112BB0(void) {
case 0x57:
case 0x5B:
func_8010DFF0(1, 1);
DecelerateX(0x1000);
DecelerateX(FIX(1.0 / 16));
if (PLAYER.ext.player.anim == 0x6C) {
if (PLAYER.animFrameDuration < 0) {
temp_v0 = (PLAYER.velocityY > 0x10000) ^ 1;
PLAYER.step_s = D_800ACF7C[temp_v0].step_s;
SetPlayerAnim(D_800ACF7C[temp_v0].anim);
if (PLAYER.velocityY > FIX(1)) {
stepSlot = 0;
} else {
stepSlot = 2;
}
PLAYER.step_s = D_800ACF7C[stepSlot];
SetPlayerAnim((u8)D_800ACF7C[stepSlot + 1]);
func_8010FAF4();
g_Player.unk44 = 1;
D_80138FC8 = 0xFE;
@ -95,11 +100,12 @@ void func_80112BB0(void) {
case 0x46:
case 0x47:
case 0x48:
case 0x49:
case 0x4A:
case 0x49:
case 0x4B:
case 0x4C:
case 0x4D:
case 0x5D:
case 0x4E:
case 0x4F:
case 0x50:
@ -110,58 +116,66 @@ void func_80112BB0(void) {
case 0x56:
case 0x5A:
case 0x5C:
case 0x5D:
func_8010DFF0(1, 1);
case 0x40:
case 0x59:
case 0x40:
func_8010DFF0(1, 1);
if (g_Player.padPressed & PAD_LEFT) {
PLAYER.velocityX = -0x18000;
PLAYER.velocityX = FIX(-1.5);
}
if (g_Player.padPressed & PAD_RIGHT) {
PLAYER.velocityX = 0x18000;
PLAYER.velocityX = FIX(1.5);
}
if (PLAYER.animFrameDuration < 0) {
temp_v1 = (PLAYER.velocityY > 0x10000) ^ 1;
PLAYER.step_s = D_800ACF7C[temp_v1].step_s;
SetPlayerAnim(D_800ACF7C[temp_v1].anim);
if (PLAYER.velocityY > FIX(1)) {
stepSlot = 0;
} else {
stepSlot = 2;
}
PLAYER.step_s = D_800ACF7C[stepSlot];
SetPlayerAnim((u8)D_800ACF7C[stepSlot + 1]);
func_8010FAF4();
}
break;
// This case is when we're dive-kicking
case 0x70:
// This flag is set in EntityDiveKickAttack if it detects a hit
if (g_Player.unk44 & 0x80) {
func_8010E83C(1);
// If cross is not pressed, we bounce up from the hit.
// But note that the above function call includes the line:
// PLAYER.velocityY = FIX(-4.875);
// So we will always bounce up, but this makes us bounce up less,
// if not pressing cross.
if (!(g_Player.padPressed & PAD_CROSS)) {
PLAYER.velocityY = -0x44000;
PLAYER.velocityY = FIX(-4.25);
}
g_Player.unk44 |= 0x40;
}
break;
}
if (PLAYER.step_s < 2) {
if (g_Player.unk44 & 1) {
if ((g_Player.padPressed & PAD_DOWN) &&
(g_Player.padTapped & PAD_CROSS)) {
SetPlayerAnim(0x22U);
PLAYER.step_s = 0x70;
CreateEntFactoryFromEntity(g_CurrentEntity, FACTORY(0, 5), 0);
PLAYER.velocityY = 0x60000;
g_Player.unk44 &= 0xFF7F;
if (var_s2 != 0) {
SetSpeedX(0x48000);
}
PlaySfx(0x6F0);
}
if (g_Player.unk44 & 0x100) {
PLAYER.velocityX = 0;
}
// This block initiates a dive-kick
if (PLAYER.step_s == 0 || PLAYER.step_s == 1) {
if (!(g_Player.unk44 & 1)) {
return;
}
} else {
if (g_Player.unk44 & 0x100) {
PLAYER.velocityX = 0;
if ((g_Player.padPressed & PAD_DOWN) &&
(g_Player.padTapped & PAD_CROSS)) {
SetPlayerAnim(0x22);
PLAYER.step_s = 0x70;
// blueprint 5 has child ID 6, which is EntityDiveKickAttack
CreateEntFactoryFromEntity(g_CurrentEntity, FACTORY(0, 5), 0);
g_Player.unk44 &= ~0x80;
PLAYER.velocityY = FIX(6);
if (walkResult != 0) {
SetSpeedX(FIX(4.5));
}
PlaySfx(0x6F0);
}
}
if (g_Player.unk44 & 0x100) {
PLAYER.velocityX = 0;
}
}
void func_80113148(void) {
@ -169,7 +183,7 @@ void func_80113148(void) {
func_8010E83C(1);
} else if (func_8010FDF8(0x9029) == 0) {
DecelerateX(0x1000);
if (func_8010E27C() != 0) {
if (CheckMoveDirection() != 0) {
SetSpeedX(0xC000);
}
}
@ -315,7 +329,7 @@ void func_801131C4(void) {
func_8010DFF0(1, 1);
if (PLAYER.animFrameIdx < g_Player.unk54) {
if (PLAYER.animFrameIdx < 2) {
func_8010E27C();
CheckMoveDirection();
if (!(g_Player.padPressed & PAD_DOWN)) {
if (g_Player.unk72 == 0) {
PLAYER.step = 0;
@ -369,7 +383,7 @@ void func_801131C4(void) {
func_8010DFF0(1, 1);
if (PLAYER.animFrameIdx < g_Player.unk54) {
if (PLAYER.animFrameIdx < 3) {
func_8010E27C();
CheckMoveDirection();
if (!(g_Player.padPressed & PAD_DOWN)) {
if (g_Player.unk72 == 0) {
PLAYER.ext.player.anim =
@ -413,7 +427,7 @@ void func_801131C4(void) {
}
}
if (local_flags & 1) {
if (func_8010E27C()) {
if (CheckMoveDirection()) {
switch ((u8)g_Player.unk72) {
case 0:
case 3:

View File

@ -143,7 +143,7 @@ void PlayerStepHellfire(void) {
break;
case 2:
PLAYER.velocityX = 0;
if (func_8010E27C() != 0) {
if (CheckMoveDirection() != 0) {
if (g_Player.padPressed & PAD_RIGHT) {
if ((g_Player.colliders[2].effects &
(EFFECT_UNK_8000 + EFFECT_SOLID)) ||
@ -165,7 +165,7 @@ void PlayerStepHellfire(void) {
}
break;
case 3:
func_8010E27C();
CheckMoveDirection();
if (g_Player.unk5C == 3) {
SetPlayerAnim(0x3C);
PLAYER.step_s += 1;
@ -490,7 +490,7 @@ void ControlBatForm(void) {
PLAYER.animFrameDuration = 1;
PLAYER.animFrameIdx = 2;
PLAYER.palette = 0x8100;
func_8010E27C();
CheckMoveDirection();
PLAYER.step_s++;
break;
case 1:
@ -861,7 +861,7 @@ void func_80117AC0(void) {
collisionCount += 1;
}
PLAYER.rotZ = 0;
func_8010E27C();
CheckMoveDirection();
if (collisionCount == 0) {
func_8010E7AC();
return;
@ -914,7 +914,7 @@ bool MistFormFinished(void) {
HandleTransformationMP(FORM_MIST, REDUCE) < 0 ||
(!IsRelicActive(RELIC_POWER_OF_MIST) &&
(g_MistTimer == 0 || --g_MistTimer == 0))) {
func_8010E27C();
CheckMoveDirection();
SetPlayerStep(0xE);
return 1;
}
@ -952,7 +952,7 @@ void ControlMistForm(void) {
switch (PLAYER.step_s) {
case 0:
func_800EA5E4(0x1BU);
func_8010E27C();
CheckMoveDirection();
g_Player.unk48 = 0;
g_Player.unk46 = 0;
g_Player.unk44 = 0;

View File

@ -9,7 +9,7 @@ PfnEntityUpdate g_DraEntityTbl[] = {
EntityGravityBootBeam,
EntitySubwpnThrownDagger,
func_8011E4BC,
func_8011B334,
EntityDiveKickAttack,
EntityGiantSpinningCross,
EntitySubwpnCrashCross,
EntitySubwpnCrashCrossParticles,
@ -473,36 +473,37 @@ void EntityUnarmedAttack(Entity* entity) {
}
}
void func_8011B334(Entity* entity) {
void EntityDiveKickAttack(Entity* self) {
Equipment equip;
s32 zero = 0; // needed for PSP
if (PLAYER.step_s != 0x70) {
DestroyEntity(entity);
DestroyEntity(self);
return;
}
entity->flags = FLAG_UNK_20000 | FLAG_UNK_40000;
entity->facingLeft = PLAYER.facingLeft;
entity->posY.i.hi = PLAYER.posY.i.hi;
entity->posX.i.hi = PLAYER.posX.i.hi;
self->flags = FLAG_UNK_20000 | FLAG_UNK_40000;
self->facingLeft = PLAYER.facingLeft;
self->posY.i.hi = PLAYER.posY.i.hi;
self->posX.i.hi = PLAYER.posX.i.hi;
g_Player.unk44 &= ~0x80;
if (entity->step == 0) {
GetEquipProperties(0, &equip, 0);
entity->attack = equip.attack;
entity->attackElement = equip.element;
entity->hitboxState = equip.hitType;
entity->nFramesInvincibility = equip.enemyInvincibilityFrames;
entity->stunFrames = equip.stunFrames;
entity->hitEffect = equip.hitEffect;
entity->entityRoomIndex = equip.criticalRate;
func_80118894(entity);
entity->hitboxOffX = 9;
entity->hitboxOffY = 21;
entity->hitboxWidth = 4;
entity->hitboxHeight = 5;
entity->step++;
} else if (entity->hitFlags == 1) {
if (self->step == 0) {
GetEquipProperties(zero, &equip, 0);
self->attack = equip.attack;
self->attackElement = equip.element;
self->hitboxState = equip.hitType;
self->nFramesInvincibility = equip.enemyInvincibilityFrames;
self->stunFrames = equip.stunFrames;
self->hitEffect = equip.hitEffect;
self->entityRoomIndex = equip.criticalRate;
func_80118894(self);
self->hitboxOffX = 9;
self->hitboxOffY = 21;
self->hitboxWidth = 4;
self->hitboxHeight = 5;
self->step++;
} else if (self->hitFlags == 1) {
g_Player.unk44 |= 0x80;
}
}

View File

@ -3295,7 +3295,7 @@ void func_8012CC30(s32 arg0) {
if (g_ButtonCombo[COMBO_QCF].buttonsCorrect == COMBO_COMPLETE &&
IsRelicActive(RELIC_SKILL_OF_WOLF) &&
CastSpell(SPELL_WOLF_CHARGE)) {
func_8010E27C();
CheckMoveDirection();
PLAYER.step_s = 2;
D_800B0914 = 4;
SetSpeedX(0x50000);

View File

@ -218,7 +218,7 @@ void func_8012DBBC(void) {
}
switch (D_800B0914) {
case 0:
func_8010E27C();
CheckMoveDirection();
break;
case 1:
if (((g_Player.pl_vram_flag & 4) && PLAYER.velocityX > FIX(5.5)) ||
@ -343,7 +343,7 @@ void func_8012E040(void) {
}
switch (D_800B0914) {
case 0:
func_8010E27C();
CheckMoveDirection();
if (var_s0 != 0) {
if (abs(PLAYER.velocityX) < FIX(1)) {
SetSpeedX(FIX(1));
@ -351,7 +351,7 @@ void func_8012E040(void) {
}
break;
case 1:
func_8010E27C();
CheckMoveDirection();
if (var_s0 != 0) {
if (abs(PLAYER.velocityX) >= FIX(1)) {
DecelerateX(FIX(16.0 / 128));
@ -379,7 +379,7 @@ void func_8012E040(void) {
}
if ((PLAYER.facingLeft != 0 && (g_Player.padPressed & PAD_RIGHT)) ||
(PLAYER.facingLeft == 0 && (g_Player.padPressed & PAD_LEFT))) {
func_8010E27C();
CheckMoveDirection();
D_800B0914 = 1;
SetSpeedX(FIX(1));
break;

View File

@ -319,12 +319,7 @@ extern s16 D_800ACF6C[];
extern s32 D_800ACF74; // These two might...
extern s32 D_800ACF78; // ...be an array
typedef struct {
s16 step_s;
u8 anim;
u8 unused;
} PlayerFallingAnim;
extern PlayerFallingAnim D_800ACF7C[2];
extern s16 D_800ACF7C[2];
extern s16 D_800ACF84[8]; // collection of sounds
extern s16 D_800ACF94[];
@ -876,7 +871,7 @@ void func_8011B5A4(Entity* self);
void EntityGravityBootBeam(Entity* self);
void EntitySubwpnThrownDagger(Entity* self);
void func_8011E4BC(Entity* self);
void func_8011B334(Entity* self);
void EntityDiveKickAttack(Entity* self);
void EntityGiantSpinningCross(Entity* self);
void EntitySubwpnCrashCross(Entity* self);
void EntitySubwpnCrashCrossParticles(Entity* self);

View File

@ -156,21 +156,21 @@ u8 D_800ACF4C[0x200] = { // crouching anim
0x00, 0x11, 0x04, 0x15, 0x01, 0x10, 0x03, 0x23};
u8 D_800ACF54[] = { // idle anim
4, 5, 10, 11, 14, 15, 29, 30, 4, 3};
s32 D_800ACF74; // These two might...
s32 D_800ACF78; // ...be an array
PlayerFallingAnim D_800ACF7C[2]; // random size just to play safe
s16 D_800ACF60[6]; // guessed size
s16 D_800ACF6C[4]; // guessed size
s16 D_800ACF8A[5]; // guessed size
s16 D_800ACF94[16]; // guessed size
u8 D_800ACFB4[20][4]; // TODO AnimationFrames*[], random size
s32 D_800B0830[99]; // random size to play safe
s32 D_800B083C[99]; // random size to play safe
u8 D_800B0846[18]; // guessed size
u16 D_800B0858[2]; // might be part of the next array
s16 D_800B0860[99]; // random size to play safe
AnimationFrame D_800B0798[40]; // random size to play safe
u8 D_800B0F94[100][5]; // random size to play safe
s32 D_800ACF74; // These two might...
s32 D_800ACF78; // ...be an array
s16 D_800ACF7C[2]; // random size just to play safe
s16 D_800ACF60[6]; // guessed size
s16 D_800ACF6C[4]; // guessed size
s16 D_800ACF8A[5]; // guessed size
s16 D_800ACF94[16]; // guessed size
u8 D_800ACFB4[20][4]; // TODO AnimationFrames*[], random size
s32 D_800B0830[99]; // random size to play safe
s32 D_800B083C[99]; // random size to play safe
u8 D_800B0846[18]; // guessed size
u16 D_800B0858[2]; // might be part of the next array
s16 D_800B0860[99]; // random size to play safe
AnimationFrame D_800B0798[40]; // random size to play safe
u8 D_800B0F94[100][5]; // random size to play safe
SVECTOR stubbbbbbbb = {0};
SVECTOR* D_800B0CB4[][4] = {
// a lot of random data to play safe

View File

@ -548,8 +548,8 @@ block_48:
case 25:
func_80158FA4();
break;
case 26:
func_8015BE84();
case Player_SlideKick:
PlayerStepSlideKick();
break;
case 24:
func_8015C178();

View File

@ -54,17 +54,20 @@ void func_8015BCD0(void) {
}
}
void func_8015BE84(void) {
void PlayerStepSlideKick(void) {
// If we are pressing square while in contact with an enemy
// (as detected in g_Player.unk44), we will bounce back.
if (g_Player.padPressed & PAD_SQUARE && g_Player.unk44 & 0x80) {
PLAYER.step = 4;
PLAYER.step = Player_Jump;
func_8015C920(&D_8015555C);
SetSpeedX(-0x18000);
SetSpeedX(FIX(-1.5));
PLAYER.velocityY = 0;
if (g_Player.unk72 == 0) {
PLAYER.velocityY = -0x48000;
PLAYER.velocityY = FIX(-4.5);
}
PLAYER.step_s = 2;
g_Player.unk44 = (g_Player.unk44 | 0xA) & 0xFFFB;
g_Player.unk44 |= (8 + 2);
g_Player.unk44 &= ~4;
return;
}
func_8015C93C(0x1000);