1
0
Fork 0
mirror of https://github.com/zeldaret/oot.git synced 2025-07-15 04:14:34 +00:00

En_Torch2 (#486)

* Darkmeiro decompilation

Bg_Gnd_Darkmeiro decompiled, matched, and documented.

* give this a shot

* fix conflict

* one more try

* Oh boy more giant functions

* now functional, but not equivalent

* Now only non-matching

* cleaned up illusion room

* much closer. static variables still suck though

* much closer. static variables still suck though

* some docs and cleanup. matching is horrible.

* static varaibles are a troublesome  lot

* ifdef

* merge ZAP

* merge again

* small cleanup

* small fixes

* swordstate

* resolve conflict

* comments and such

Co-authored-by: fig <fig02srl@gmail.com>
Co-authored-by: petrie911 <pmontag@DESKTOP-LG8A167.localdomain>
This commit is contained in:
petrie911 2020-12-28 19:56:24 -06:00 committed by GitHub
parent 1e6bd7f623
commit 42f2d38b8f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 1148 additions and 949 deletions

View file

@ -15,10 +15,10 @@ void EnBlkobj_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnBlkobj_Update(Actor* thisx, GlobalContext* globalCtx);
void EnBlkobj_Draw(Actor* thisx, GlobalContext* globalCtx);
void func_809C2148(EnBlkobj* this, GlobalContext* globalCtx);
void func_809C21A0(EnBlkobj* this, GlobalContext* globalCtx);
void func_809C2218(EnBlkobj* this, GlobalContext* globalCtx);
void func_809C22F4(EnBlkobj* this, GlobalContext* globalCtx);
void EnBlkobj_Wait(EnBlkobj* this, GlobalContext* globalCtx);
void EnBlkobj_SpawnDarkLink(EnBlkobj* this, GlobalContext* globalCtx);
void EnBlkobj_DarkLinkFight(EnBlkobj* this, GlobalContext* globalCtx);
void EnBlkobj_DoNothing(EnBlkobj* this, GlobalContext* globalCtx);
const ActorInit En_Blkobj_InitVars = {
ACTOR_EN_BLKOBJ,
@ -39,109 +39,109 @@ static InitChainEntry sInitChain[] = {
ICHAIN_F32(uncullZoneDownward, 300, ICHAIN_STOP),
};
static Gfx D_809C2590[] = {
static Gfx sSetupOpaDL[] = {
gsDPSetRenderMode(G_RM_FOG_SHADE_A, G_RM_AA_ZB_OPA_SURF2),
gsSPEndDisplayList(),
};
static Gfx D_809C25A0[] = {
static Gfx sSetupXluDL[] = {
gsDPSetRenderMode(G_RM_FOG_SHADE_A, G_RM_AA_ZB_XLU_SURF2),
gsSPEndDisplayList(),
};
extern Gfx D_060014E0[];
extern Gfx D_060053D0[];
extern UNK_TYPE D_06007564;
extern ColHeader D_06007564;
void func_809C2060(EnBlkobj* this, EnBlkobjActionFunc actionFunc) {
void EnBlkobj_SetupAction(EnBlkobj* this, EnBlkobjActionFunc actionFunc) {
this->actionFunc = actionFunc;
this->unk_166 = 0;
this->timer = 0;
}
void EnBlkobj_Init(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnBlkobj* this = THIS;
s32 localC = 0;
ColHeader* colHeader = NULL;
Actor_ProcessInitChain(&this->dyna.actor, sInitChain);
DynaPolyInfo_SetActorMove(&this->dyna, 0);
if (Flags_GetClear(globalCtx, this->dyna.actor.room)) {
this->unk_164 = 0xFF;
func_809C2060(this, func_809C22F4);
this->alpha = 255;
EnBlkobj_SetupAction(this, EnBlkobj_DoNothing);
} else {
DynaPolyInfo_Alloc(&D_06007564, &localC);
DynaPolyInfo_Alloc(&D_06007564, &colHeader);
this->dyna.dynaPolyId =
DynaPolyInfo_RegisterActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, localC);
func_809C2060(this, func_809C2148);
DynaPolyInfo_RegisterActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
EnBlkobj_SetupAction(this, EnBlkobj_Wait);
}
}
void EnBlkobj_Destroy(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnBlkobj* this = THIS;
DynaPolyInfo_Free(globalCtx, &globalCtx->colCtx.dyna, this->dyna.dynaPolyId);
}
void func_809C2148(EnBlkobj* this, GlobalContext* globalCtx) {
void EnBlkobj_Wait(EnBlkobj* this, GlobalContext* globalCtx) {
Player* player = PLAYER;
if (this->dyna.actor.xzDistFromLink < 120.0f) {
func_809C2060(this, func_809C21A0);
EnBlkobj_SetupAction(this, EnBlkobj_SpawnDarkLink);
}
player->stateFlags2 |= 0x4000000;
player->stateFlags2 |= 0x04000000;
}
void func_809C21A0(EnBlkobj* this, GlobalContext* globalCtx) {
void EnBlkobj_SpawnDarkLink(EnBlkobj* this, GlobalContext* globalCtx) {
if (!(this->dyna.actor.flags & 0x40)) {
Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_TORCH2, this->dyna.actor.posRot.pos.x,
this->dyna.actor.posRot.pos.y, this->dyna.actor.posRot.pos.z, 0, this->dyna.actor.yawTowardsLink, 0,
0);
func_809C2060(this, func_809C2218);
EnBlkobj_SetupAction(this, EnBlkobj_DarkLinkFight);
}
}
void func_809C2218(EnBlkobj* this, GlobalContext* globalCtx) {
s32 temp;
void EnBlkobj_DarkLinkFight(EnBlkobj* this, GlobalContext* globalCtx) {
s32 alphaMod;
if (this->unk_166 == 0) {
if (this->timer == 0) {
if (Actor_Find(&globalCtx->actorCtx, ACTOR_EN_TORCH2, ACTORTYPE_BOSS) == NULL) {
Flags_SetClear(globalCtx, this->dyna.actor.room);
this->unk_166 += 1;
this->timer++;
}
} else {
if (this->unk_166++ > 100) {
temp = (this->unk_166 - 100) >> 2;
if (temp > 5) {
temp = 5;
}
this->unk_164 += temp;
if (this->unk_164 > 255) {
this->unk_164 = 255;
func_809C2060(this, func_809C22F4);
DynaPolyInfo_Free(globalCtx, &globalCtx->colCtx.dyna, this->dyna.dynaPolyId);
}
} else if (this->timer++ > 100) {
alphaMod = (this->timer - 100) >> 2;
if (alphaMod > 5) {
alphaMod = 5;
}
this->alpha += alphaMod;
if (this->alpha > 255) {
this->alpha = 255;
EnBlkobj_SetupAction(this, EnBlkobj_DoNothing);
DynaPolyInfo_Free(globalCtx, &globalCtx->colCtx.dyna, this->dyna.dynaPolyId);
}
}
}
void func_809C22F4(EnBlkobj* this, GlobalContext* globalCtx) {
void EnBlkobj_DoNothing(EnBlkobj* this, GlobalContext* globalCtx) {
}
void EnBlkobj_Update(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnBlkobj* this = THIS;
this->actionFunc(this, globalCtx);
}
void func_809C2324(GlobalContext* globalCtx, Gfx* dList, s32 alpha) {
void EnBlkobj_DrawAlpha(GlobalContext* globalCtx, Gfx* dList, s32 alpha) {
Gfx* segment;
OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 322);
if (alpha == 255) {
segment = D_809C2590;
segment = sSetupOpaDL;
} else {
segment = D_809C25A0;
segment = sSetupXluDL;
}
gSPSegment(POLY_XLU_DISP++, 0x08, segment);
@ -152,9 +152,9 @@ void func_809C2324(GlobalContext* globalCtx, Gfx* dList, s32 alpha) {
}
void EnBlkobj_Draw(Actor* thisx, GlobalContext* globalCtx) {
EnBlkobj* this = THIS;
s32 pad;
s32 temp_a3;
EnBlkobj* this = THIS;
s32 illusionAlpha;
u32 gameplayFrames;
OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 349);
@ -168,12 +168,12 @@ void EnBlkobj_Draw(Actor* thisx, GlobalContext* globalCtx) {
gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 363),
G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
if (this->unk_164 != 0) {
func_809C2324(globalCtx, D_060014E0, this->unk_164);
if (this->alpha != 0) {
EnBlkobj_DrawAlpha(globalCtx, D_060014E0, this->alpha);
}
temp_a3 = 255 - this->unk_164;
if (temp_a3 != 0) {
func_809C2324(globalCtx, D_060053D0, temp_a3);
illusionAlpha = 255 - this->alpha;
if (illusionAlpha != 0) {
EnBlkobj_DrawAlpha(globalCtx, D_060053D0, illusionAlpha);
}
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_blkobj.c", 375);

View file

@ -10,8 +10,8 @@ typedef void (*EnBlkobjActionFunc)(struct EnBlkobj*, GlobalContext*);
typedef struct EnBlkobj {
/* 0x0000 */ DynaPolyActor dyna;
/* 0x0164 */ s16 unk_164;
/* 0x0166 */ s16 unk_166;
/* 0x0164 */ s16 alpha;
/* 0x0166 */ s16 timer;
/* 0x0168 */ EnBlkobjActionFunc actionFunc;
} EnBlkobj; // size = 0x016C

View file

@ -10,12 +10,51 @@
#define THIS ((Player*)thisx)
typedef enum {
/* 0 */ ENTORCH2_WAIT,
/* 1 */ ENTORCH2_ATTACK,
/* 2 */ ENTORCH2_DEATH,
/* 3 */ ENTORCH2_DAMAGE
} EnTorch2ActionStates;
typedef enum {
/* 0 */ FORWARD_SLASH_1H,
/* 1 */ FORWARD_SLASH_2H,
/* 2 */ FORWARD_COMBO_1H,
/* 3 */ FORWARD_COMBO_2H,
/* 4 */ RIGHT_SLASH_1H,
/* 5 */ RIGHT_SLASH_2H,
/* 6 */ RIGHT_COMBO_1H,
/* 7 */ RIGHT_COMBO_2H,
/* 8 */ LEFT_SLASH_1H,
/* 9 */ LEFT_SLASH_2H,
/* 10 */ LEFT_COMBO_1H,
/* 11 */ LEFT_COMBO_2H,
/* 12 */ STAB_1H,
/* 13 */ STAB_2H,
/* 14 */ STAB_COMBO_1H,
/* 15 */ STAB_COMBO_2H,
/* 16 */ FLIPSLASH_START,
/* 17 */ JUMPSLASH_START,
/* 18 */ FLIPSLASH_FINISH,
/* 19 */ JUMPSLASH_FINISH,
/* 20 */ BACKSLASH_RIGHT,
/* 21 */ BACKSLASH_LEFT,
/* 22 */ HAMMER_FORWARD,
/* 23 */ HAMMER_SIDE,
/* 24 */ SPIN_ATTACK_1H,
/* 25 */ SPIN_ATTACK_2H,
/* 26 */ BIG_SPIN_1H,
/* 27 */ BIG_SPIN_2H
} PlayerSwordAnimation;
void EnTorch2_Init(Actor* thisx, GlobalContext* globalCtx);
void EnTorch2_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnTorch2_Update(Actor* thisx, GlobalContext* globalCtx);
void EnTorch2_Draw(Actor* thisx, GlobalContext* globalCtx);
/*
extern FlexSkeletonHeader D_06004764;
const ActorInit En_Torch2_InitVars = {
ACTOR_EN_TORCH2,
ACTORTYPE_BOSS,
@ -27,21 +66,719 @@ const ActorInit En_Torch2_InitVars = {
(ActorFunc)EnTorch2_Update,
(ActorFunc)EnTorch2_Draw,
};
*/
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/EnTorch2_Init.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/EnTorch2_Destroy.s")
/* static */ f32 sStickTilt = 0.0f;
/* static */ s16 sStickAngle = 0;
/* static */ f32 sSwordJumpHeight = 0.0f;
/* static */ s32 sHoldShieldTimer = 0;
/* static */ u8 sZTargetFlag = false;
/* static */ u8 sDeathFlag = false;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1DB98.s")
/* static */ Input sInput;
/* static */ u8 sSwordJumpState;
/* static */ Vec3f sSpawnPoint;
/* static */ u8 sJumpslashTimer;
/* static */ u8 sJumpslashFlag;
/* static */ u8 sActionState;
/* static */ u8 sSwordJumpTimer;
/* static */ u8 sCounterState;
/* static */ u8 sDodgeRollState;
/* static */ u8 sStaggerCount;
/* static */ u8 sStaggerTimer;
/* static */ s8 sLastSwordAnim;
/* static */ u8 sAlpha;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1DBD8.s")
static DamageTable sDamageTable = {
0x10, 0x02, 0x01, 0x02, 0x10, 0x02, 0x02, 0x10, 0x01, 0x02, 0x04, 0x02, 0x02, 0x02, 0x02, 0x02,
0x02, 0xE2, 0x60, 0xD3, 0x00, 0x00, 0x01, 0x04, 0x02, 0x02, 0x08, 0x04, 0x00, 0x00, 0x04, 0x00,
};
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1DD70.s")
void EnTorch2_Init(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
Player* this = THIS;
sInput.cur.button = sInput.press.button = sInput.rel.button = 0;
sInput.cur.stick_x = sInput.cur.stick_y = 0;
this->currentShield = PLAYER_SHIELD_HYLIAN;
this->heldItemActionParam = this->heldItemId = PLAYER_AP_SWORD_MASTER;
Player_SetModelGroup(this, 2);
globalCtx->playerInit(this, globalCtx, &D_06004764);
this->actor.naviEnemyId = 0x26;
this->cylinder.base.acFlags = 9;
this->swordQuads[0].base.atFlags = this->swordQuads[1].base.atFlags = 0x11;
this->swordQuads[0].base.acFlags = this->swordQuads[1].base.acFlags = 0xD;
this->swordQuads[0].base.type = this->swordQuads[1].base.type = 9;
this->swordQuads[0].body.toucher.damage = this->swordQuads[1].body.toucher.damage = 8;
this->swordQuads[0].body.bumperFlags = this->swordQuads[1].body.bumperFlags = 1;
this->shieldQuad.base.atFlags = 0x11;
this->shieldQuad.base.acFlags = 0xD;
this->actor.colChkInfo.damageTable = &sDamageTable;
this->actor.colChkInfo.health = gSaveContext.healthCapacity >> 3;
this->actor.colChkInfo.unk_10 = 60;
this->actor.colChkInfo.unk_12 = 100;
globalCtx->func_11D54(this, globalCtx);
sActionState = ENTORCH2_WAIT;
sDodgeRollState = 0;
sSwordJumpHeight = 0.0f;
sSwordJumpState = 0;
sJumpslashTimer = 0;
sJumpslashFlag = false;
sCounterState = sStaggerTimer = sStaggerCount = 0;
sLastSwordAnim = 0;
sAlpha = 95;
sSpawnPoint = this->actor.initPosRot.pos;
}
void EnTorch2_Destroy(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
Player* this = THIS;
Effect_Delete(globalCtx, this->swordEffectIndex);
func_800F5B58();
Collider_DestroyCylinder(globalCtx, &this->cylinder);
Collider_DestroyQuad(globalCtx, &this->swordQuads[0]);
Collider_DestroyQuad(globalCtx, &this->swordQuads[1]);
Collider_DestroyQuad(globalCtx, &this->shieldQuad);
}
Actor* EnTorch2_GetAttackItem(GlobalContext* globalCtx, Player* this) {
Actor* rangedItem = func_80033780(globalCtx, &this->actor, 4000.0f);
if (rangedItem != NULL) {
return rangedItem;
} else {
return func_80033684(globalCtx, &this->actor);
}
}
s32 EnTorch2_SwingSword(GlobalContext* globalCtx, Input* input, Player* this) {
f32 noAttackChance = 0.0f;
s32 attackDelay = 7;
Player* player = PLAYER;
if ((this->linearVelocity < 0.0f) || (player->linearVelocity < 0.0f)) {
return 0;
}
if (gSaveContext.health < 0x50) {
attackDelay = 15;
noAttackChance += 0.3f;
}
if (sAlpha != 255) {
noAttackChance += 2.0f;
}
if ((((globalCtx->gameplayFrames & attackDelay) == 0) || (sSwordJumpState != 0)) &&
(noAttackChance <= Rand_ZeroOne())) {
if (sSwordJumpState == 0) {
switch ((s32)(Rand_ZeroOne() * 7.0f)) {
case 1:
case 5:
sStickAngle += 0x4000;
sStickTilt = 127.0f;
break;
case 2:
case 6:
sStickAngle -= 0x4000;
sStickTilt = 127.0f;
break;
}
}
input->cur.button = BTN_B;
return 1;
}
return 0;
}
void EnTorch2_Backflip(Player* this, Input* input, Actor* thisx) {
thisx->posRot.rot.y = thisx->shape.rot.y = thisx->yawTowardsLink;
sStickAngle = thisx->yawTowardsLink + 0x8000;
sStickTilt = 127.0f;
sZTargetFlag = true;
input->cur.button = BTN_A;
this->invincibilityTimer = 10;
sCounterState = 0;
}
#ifdef NON_MATCHING
/**
* Static variables are sometimes loaded from pointers and sometimes directly. While
* neither this nor the original are consistent about it, unfortunately they're not
* inconsistent in the same way. Also a small instruction mismatch in the input section
*/
void EnTorch2_Update(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
Player* player = PLAYER;
Player* this = THIS;
Input* input = &sInput;
u16 phi_a2;
s8 tempX;
s8 tempY;
Camera* camera;
u8 staggerThreshold;
s8 temp;
s16 sp66;
u32 phi_v0;
Actor* attackItem;
s16 sp5A;
s16 pad58;
s32 pad54;
f32 sp50;
s16 sp4E;
s16 pad4C;
sp5A = player->actor.shape.rot.y - this->actor.shape.rot.y;
input->cur.button = 0;
camera = Gameplay_GetCamera(globalCtx, 0);
attackItem = EnTorch2_GetAttackItem(globalCtx, this);
switch (sActionState) {
case ENTORCH2_WAIT:
this->actor.shape.rot.y = this->actor.posRot.rot.y = this->actor.yawTowardsLink;
this->skelAnime.animCurrentFrame = 0.0f;
this->skelAnime.animPlaybackSpeed = 0.0f;
this->actor.posRot.pos.x = (Math_SinS(this->actor.posRot.rot.y) * 25.0f) + sSpawnPoint.x;
this->actor.posRot.pos.z = (Math_CosS(this->actor.posRot.rot.y) * 25.0f) + sSpawnPoint.z;
if ((this->actor.xzDistFromLink <= 120.0f) || func_80033A84(globalCtx, &this->actor) ||
(attackItem != NULL)) {
if (attackItem != NULL) {
sDodgeRollState = 1;
sStickAngle = this->actor.yawTowardsLink;
sStickTilt = 127.0f;
input->cur.button = BTN_A;
sZTargetFlag = false;
sp66 = camera->camDir.y - sStickAngle;
input->cur.stick_x = sStickTilt * Math_SinS(sp66);
temp = sStickTilt * Math_CosS(sp66);
input->cur.stick_y = temp;
}
func_800F5ACC(0x38);
sActionState = ENTORCH2_ATTACK;
}
break;
case ENTORCH2_ATTACK:
sStickTilt = 0.0f;
// Handles Dark Link's sword clanking on Link's sword
if ((this->swordQuads[0].base.acFlags & 0x80) || (this->swordQuads[1].base.acFlags & 0x80)) {
this->swordQuads[0].base.acFlags &= ~0x80;
this->swordQuads[1].base.acFlags &= ~0x80;
this->swordQuads[0].base.atFlags |= 4; // Loads these out of order
this->swordQuads[1].base.atFlags |= 4;
this->cylinder.base.acFlags &= ~2;
if (sLastSwordAnim != this->swordAnimation) {
sStaggerCount++;
sLastSwordAnim = this->swordAnimation;
}
/*! @bug
* This code is needed to reset sCounterState, and should run regardless
* of how much health Link has. Without it, sCounterState stays at 2 until
* something else resets it, preventing Dark Link from using his shield and
* creating a hole in his defenses. This also makes Dark Link harder at low
* health, while the other health checks are intended to make him easier.
*/
if ((gSaveContext.health < 0x50) && (sCounterState != 0)) { // Loads in wrong order
sCounterState = 0;
sStaggerTimer = 50;
}
}
if ((sCounterState != 0) && (this->swordState != 0)) {
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->swordQuads[0].base);
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->swordQuads[1].base);
}
// Ignores hits when jumping on Link's sword
if ((this->invincibilityTimer < 0) && (sActionState != ENTORCH2_DAMAGE) &&
(this->cylinder.base.acFlags & 2)) {
this->cylinder.base.acFlags &= ~2;
}
// Handles Dark Link rolling to dodge item attacks
if (sDodgeRollState != 0) {
sStickTilt = 127.0f;
} else if (attackItem != NULL) {
sDodgeRollState = 1;
sStickAngle = this->actor.yawTowardsLink;
sStickTilt = 127.0f;
input->cur.button = BTN_A;
} else if (sJumpslashTimer == 0) {
// Handles Dark Link's initial reaction to jumpslashes
if (((player->swordState != 0) || (player->actor.velocity.y > -3.0f)) &&
(player->swordAnimation == JUMPSLASH_START)) {
this->actor.posRot.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsLink;
if (globalCtx->gameplayFrames % 2) {
sStickAngle = this->actor.yawTowardsLink + 0x4000;
} else {
sStickAngle = this->actor.yawTowardsLink - 0x4000;
}
sStickTilt = 127.0f; // Does not store with pointer
sJumpslashFlag = false; // Does not store with POinter
input->cur.button |= BTN_A;
sJumpslashTimer = 15;
// Handles jumping on Link's sword
} else if (sSwordJumpState != 0) {
sStickTilt = 0.0f;
player->stateFlags3 |= 4;
Math_SmoothStepToF(&this->actor.posRot.pos.x,
(Math_SinS(player->actor.shape.rot.y - 0x3E8) * 45.0f) +
player->actor.posRot.pos.x,
1.0f, 5.0f, 0.0f);
Math_SmoothStepToF(&this->actor.posRot.pos.z,
(Math_CosS(player->actor.shape.rot.y - 0x3E8) * 45.0f) +
player->actor.posRot.pos.z,
1.0f, 5.0f, 0.0f);
sSwordJumpTimer--;
if ((sSwordJumpTimer == 0) || ((player->invincibilityTimer > 0) && (this->swordState == 0))) {
this->actor.posRot.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsLink;
input->cur.button = BTN_A;
player->stateFlags3 &= ~4;
sStickTilt = 127.0f;
player->skelAnime.animCurrentFrame = 3.0f;
sStickAngle = this->actor.yawTowardsLink + 0x8000;
sSwordJumpTimer = sSwordJumpState = 0;
this->actor.flags |= 1;
} else if (sSwordJumpState == 1) {
if (sSwordJumpTimer < 16) {
EnTorch2_SwingSword(globalCtx, input, this);
sSwordJumpState++;
} else if (sSwordJumpTimer == 19) {
func_800F4190(&this->actor.projectedPos, NA_SE_VO_LI_AUTO_JUMP);
}
}
} else {
// This does nothing, as sHoldShieldTimer is never set.
if (sHoldShieldTimer != 0) {
sHoldShieldTimer--;
input->cur.button = BTN_R;
}
// Handles Dark Link's reaction to sword attack other than jumpslashes
if (func_800354B4(globalCtx, &this->actor, 120.0f, 0x7FFF, 0x7FFF, this->actor.posRot.rot.y)) {
// Loads arguments in wrong order. Probably related to static variables problem.
if ((player->swordAnimation == STAB_1H) && (this->actor.xzDistFromLink < 90.0f)) {
// Handles the reaction to a one-handed stab. If the conditions are satisfied,
// Dark Link jumps on Link's sword. Otherwise he backflips away.
if ((this->swordState == 0) && (sCounterState == 0) && (player->invincibilityTimer == 0) &&
(player->swordAnimation == STAB_1H) && (this->actor.xzDistFromLink <= 85.0f) &&
func_80033A84(globalCtx, &this->actor)) {
sStickTilt = 0.0f;
sSwordJumpState = 1;
player->stateFlags3 |= 4;
this->actor.flags &= ~1;
sSwordJumpTimer = 27;
player->swordState = 0;
player->linearVelocity = 0.0f;
this->invincibilityTimer = -7;
this->linearVelocity = 0.0f;
player->skelAnime.animCurrentFrame = 2.0f;
func_800A3BC0(globalCtx, &player->skelAnime);
sHoldShieldTimer = 0;
input->cur.button = BTN_A;
} else {
EnTorch2_Backflip(this, input, &this->actor);
}
} else {
// Handles reactions to all other sword attacks
sStickAngle = this->actor.yawTowardsLink; // Not loaded into pointer
input->cur.button = BTN_B;
if (player->swordAnimation <= FORWARD_COMBO_2H) {
sStickTilt = 0.0f;
} else if (player->swordAnimation <= RIGHT_COMBO_2H) {
sStickTilt = 127.0f;
sStickAngle += sStickAngle; // Not loaded from pointer
} else if (player->swordAnimation <= LEFT_COMBO_2H) {
sStickTilt = 127.0f;
sStickAngle += -0x4000; // Not loaded from pointer
} else if (player->swordAnimation <= HAMMER_SIDE) {
input->cur.button = BTN_R;
} else if (player->swordAnimation <= BIG_SPIN_2H) {
EnTorch2_Backflip(this, input, &this->actor);
} else {
EnTorch2_Backflip(this, input, &this->actor);
}
if (CHECK_BTN_ANY(input->cur.button, BTN_A | BTN_R) && (this->swordState == 0) &&
(player->swordState != 0)) {
sCounterState = 1;
}
}
} else {
// Handles movement and attacks when not reacting to Link's actions
sStickAngle = this->actor.yawTowardsLink;
sp50 = 0.0f;
if ((90.0f >= this->actor.xzDistFromLink) && (this->actor.xzDistFromLink > 70.0f) &&
(ABS(sp5A) >= 0x7800) &&
((this->actor.unk_10C != 0) || !(player->stateFlags1 & 0x00400000))) {
EnTorch2_SwingSword(globalCtx, input, this);
} else if (((this->actor.xzDistFromLink <= 70.0f) ||
((this->actor.xzDistFromLink <= 80.0f + sp50) && (player->swordState != 0))) &&
(this->swordState == 0)) {
if (!EnTorch2_SwingSword(globalCtx, input, this) && (this->swordState == 0) &&
(sCounterState == 0)) {
EnTorch2_Backflip(this, input, &this->actor);
}
} else if (this->actor.xzDistFromLink <= 50 + sp50) {
sStickTilt = 127.0f;
sStickAngle = this->actor.yawTowardsLink;
if (this->actor.unk_10C == 0) {
Math_SmoothStepToS(&sStickAngle, player->actor.shape.rot.y + 0x7FFF, 1, 0x2328, 0);
}
} else if (this->actor.xzDistFromLink > 100.0f + sp50) {
if ((player->swordState == 0) || (player->swordAnimation < SPIN_ATTACK_1H) ||
(player->swordAnimation > BIG_SPIN_2H) || (this->actor.xzDistFromLink >= 280.0f)) {
sStickTilt = 127.0f;
sStickAngle = this->actor.yawTowardsLink;
if (this->actor.unk_10C == 0) {
Math_SmoothStepToS(&sStickAngle, player->actor.shape.rot.y + 0x7FFF, 1, 0x2328, 0);
}
} else {
EnTorch2_Backflip(this, input, &this->actor);
}
} else if (((ABS(sp5A) < 0x7800) && (ABS(sp5A) >= 0x3000)) ||
!EnTorch2_SwingSword(globalCtx, input, this)) {
sStickAngle = this->actor.yawTowardsLink;
sStickTilt = 127.0f;
if (this->actor.unk_10C == 0) {
Math_SmoothStepToS(&sStickAngle, player->actor.shape.rot.y + 0x7FFF, 1, 0x2328, 0);
}
}
}
}
// Handles Dark Link's counterattack to jumpslashes
} else if (sJumpslashFlag && (sAlpha == 255) && (this->actor.velocity.y > 0)) {
input->cur.button |= BTN_B;
} else if (!sJumpslashFlag && (this->actor.bgCheckFlags & 1)) {
sStickAngle = this->actor.posRot.rot.y = this->actor.shape.rot.y = this->actor.yawTowardsLink;
if (sAlpha != 255) {
sStickAngle += 0x8000;
sStickTilt = 127.0f; // Not loaded from pointer
sZTargetFlag = true;
}
input->cur.button |= BTN_A;
sJumpslashFlag = true;
this->invincibilityTimer = 10;
}
// Rotates Dark Link's stick angle from Link-relative to camera-relative.
sp66 = camera->camDir.y - sStickAngle;
input->cur.stick_x = Math_SinS(sp66) * sStickTilt;
temp = Math_CosS(sp66) * sStickTilt;
input->cur.stick_y = temp;
if ((sAlpha != 255) && ((globalCtx->gameplayFrames % 8) == 0)) {
sAlpha++;
}
break;
case ENTORCH2_DAMAGE:
this->swordState = 0;
input->cur.stick_x = input->cur.stick_y = 0;
if ((this->invincibilityTimer > 0) && (this->actor.posRot.pos.y < (this->actor.groundY - 160.0f))) {
this->stateFlags3 &= ~1;
this->actor.flags |= 1;
this->invincibilityTimer = 0;
this->actor.velocity.y = 0.0f;
this->actor.posRot.pos.y = sSpawnPoint.y + 40.0f;
this->actor.posRot.pos.x =
(Math_SinS(player->actor.shape.rot.y) * -120.0f) + player->actor.posRot.pos.x;
this->actor.posRot.pos.z =
(Math_CosS(player->actor.shape.rot.y) * -120.0f) + player->actor.posRot.pos.z;
if (func_8002DB6C(&this->actor, &sSpawnPoint) > 800.0f) {
sp50 = Rand_ZeroOne() * 20.0f;
sp4E = Rand_CenteredFloat(4000.0f);
this->actor.shape.rot.y = this->actor.posRot.rot.y =
Math_Vec3f_Yaw(&sSpawnPoint, &player->actor.posRot.pos);
this->actor.posRot.pos.x =
(Math_SinS(this->actor.posRot.rot.y + sp4E) * (25.0f + sp50)) + sSpawnPoint.x;
this->actor.posRot.pos.z =
(Math_CosS(this->actor.posRot.rot.y + sp4E) * (25.0f + sp50)) + sSpawnPoint.z;
this->actor.posRot.pos.y = sSpawnPoint.y;
} else {
this->actor.posRot.pos.y = this->actor.groundY;
}
Math_Vec3f_Copy(&this->actor.initPosRot.pos, &this->actor.posRot.pos);
globalCtx->func_11D54(this, globalCtx);
sActionState = ENTORCH2_ATTACK;
sStickTilt = 0.0f;
if (sAlpha != 255) {
sStaggerCount = 0;
sStaggerTimer = 0;
}
}
break;
case ENTORCH2_DEATH:
if (sAlpha - 13 <= 0) {
sAlpha = 0;
Actor_Kill(&this->actor);
return;
}
sAlpha -= 13;
this->actor.shape.unk_14 -= 13;
break;
}
// Causes Dark Link to shield in place when Link is using magic attacks other than the spin attack
if ((gSaveContext.unk_13F0 == 3) && (player->swordState == 0 || (player->swordAnimation < SPIN_ATTACK_1H) ||
(player->swordAnimation > BIG_SPIN_2H))) {
sStickTilt = 0.0f;
input->cur.stick_x = 0;
input->cur.stick_y = 0;
input->cur.button = BTN_R;
}
if ((sActionState == ENTORCH2_ATTACK) && (this->actor.xzDistFromLink <= 610.0f) && sZTargetFlag) {
input->cur.button |= BTN_Z;
}
// Updates Dark Link's "controller". The conditional seems to cause him to
// stop targeting and hold shield if he's been holding it long enough.
phi_a2 = input->cur.button;
pad54 = input->cur.button ^ input->prev.button;
input->press.button = input->cur.button & pad54;
phi_v0 = input->cur.button;
if (input->cur.button & BTN_R) {
input->cur.button = phi_a2;
phi_a2 = ((sCounterState == 0) && (this->swordState == 0)) ? BTN_R : phi_v0 ^ BTN_R;
phi_v0 = phi_a2; // instruction mismatch
}
input->rel.button = input->prev.button & pad54;
input->prev.button = phi_v0 & 0x3FFF; // & ~(BTN_A | BTN_B)
input->cur.button = phi_a2;
PadUtils_UpdateRelXY(input);
input->press.stick_x += (s8)(input->cur.stick_x - input->prev.stick_x);
input->press.stick_y += (s8)(input->cur.stick_y - input->prev.stick_y);
// Handles Dark Link being damaged
if ((this->actor.colChkInfo.health == 0) && sDeathFlag) {
this->csMode = 0x18;
this->unk_448 = &player->actor;
this->unk_46A = 1;
sDeathFlag = false;
}
if ((this->invincibilityTimer == 0) && (this->actor.colChkInfo.health != 0) && (this->cylinder.base.acFlags & 2) &&
!(this->stateFlags1 & 0x04000000) && !(this->swordQuads[0].base.atFlags & 2) &&
!(this->swordQuads[1].base.atFlags & 2)) {
if (!Actor_ApplyDamage(&this->actor)) {
func_800F5B58();
this->actor.flags &= ~5;
this->unk_8A1 = 2;
this->unk_8A4 = 6.0f;
this->unk_8A8 = 6.0f;
this->unk_8A0 = this->actor.colChkInfo.damage;
this->unk_8A2 = this->actor.yawTowardsLink + 0x8000;
sDeathFlag++;
sActionState = ENTORCH2_DEATH;
func_80032C7C(globalCtx, &this->actor);
Item_DropCollectibleRandom(globalCtx, &this->actor, &this->actor.posRot.pos, 0xC0);
this->stateFlags3 &= ~4;
} else {
func_800F5ACC(0x38);
if (this->actor.colChkInfo.damageEffect == 1) {
if (sAlpha == 255) {
func_8003426C(&this->actor, 0, 0xFF, 0, 0x50);
} else {
func_8003426C(&this->actor, 0, 0xFF, 0x2000, 0x50);
}
} else {
this->actor.flags &= ~1;
this->unk_8A1 = 1;
this->unk_8A8 = 6.0f;
this->unk_8A0 = this->actor.colChkInfo.damage;
this->unk_8A4 = 8.0f;
this->unk_8A2 = this->actor.yawTowardsLink + 0x8000;
func_80035650(&this->actor, &this->cylinder.body, 1);
this->stateFlags3 &= ~4;
this->stateFlags3 |= 1;
sActionState = ENTORCH2_DAMAGE;
if (sAlpha == 255) {
func_8003426C(&this->actor, 0x4000, 0xFF, 0, 0xC);
} else {
func_8003426C(&this->actor, 0x4000, 0xFF, 0x2000, 0xC);
}
}
}
this->actor.colChkInfo.damage = 0;
this->unk_8A0 = 0;
}
// Handles being frozen by a deku nut
if ((this->actor.dmgEffectTimer == 0) || (this->actor.dmgEffectParams & 0x4000)) {
this->stateFlags3 &= ~4;
} else {
this->stateFlags3 |= 4;
this->stateFlags1 &= ~0x04000000;
this->invincibilityTimer = 0;
input->press.stick_x = input->press.stick_y = 0;
/*! @bug
* Setting cur.button to 0 clears the Z-trigger, causing Dark Link to break his
* lock on Link. If he presses A while not locked on, he'll put his sword away.
* This clears his held item param permanently and makes him unable to attack.
*/
input->cur.button = 0;
input->press.button = 0;
this->linearVelocity = 0.0f;
}
globalCtx->playerUpdate(this, globalCtx, input);
/*
* Handles sword clanks and removes their recoil for both Links. Dark Link staggers
* if he's had to counter with enough different sword animations in a row.
*/
if (this->linearVelocity == -18.0f) {
staggerThreshold = (u32)Rand_CenteredFloat(2.0f) + 6;
if (gSaveContext.health < 0x50) {
staggerThreshold = (u32)Rand_CenteredFloat(2.0f) + 3;
}
if (this->actor.xzDistFromLink > 80.0f) {
this->linearVelocity = 1.2f;
} else if (this->actor.xzDistFromLink < 70.0f) {
this->linearVelocity = -1.5f;
} else {
this->linearVelocity = 1.0f;
}
if (staggerThreshold < sStaggerCount) {
this->skelAnime.animPlaybackSpeed *= 0.6f;
func_800F4190(&this->actor.projectedPos, NA_SE_PL_DAMAGE);
sStaggerTimer = 0;
sStaggerCount = 0;
}
}
if (player->linearVelocity == -18.0f) {
if (this->actor.xzDistFromLink > 80.0f) {
player->linearVelocity = 1.2f;
} else if (this->actor.xzDistFromLink < 70.0f) {
player->linearVelocity = -1.5f;
} else {
player->linearVelocity = 1.0f;
}
}
/*
* This ensures Dark Link's counter animation mirrors Link's exactly.
*/
if ((sCounterState != 0) && (sCounterState == 1)) {
if (this->swordState == 0) {
sCounterState = 0;
} else {
sCounterState = 2;
this->swordState = 1;
this->skelAnime.animCurrentFrame = player->skelAnime.animCurrentFrame - player->skelAnime.animPlaybackSpeed;
this->skelAnime.animPlaybackSpeed = player->skelAnime.animPlaybackSpeed;
func_800A3BC0(globalCtx, &this->skelAnime);
Collider_QuadSetAT(globalCtx, &this->swordQuads[0].base);
Collider_QuadSetAT(globalCtx, &this->swordQuads[1].base);
}
}
if (sStaggerTimer != 0) {
sStaggerTimer--;
if (sStaggerTimer == 0) {
sCounterState = 0;
sStaggerCount = 0;
}
}
if (sDodgeRollState != 0) {
if (sDodgeRollState == 1) {
this->invincibilityTimer = 20;
}
sDodgeRollState = (this->invincibilityTimer > 0) ? 2 : 0;
}
if (this->invincibilityTimer != 0) {
this->cylinder.base.type = 0xA;
this->cylinder.body.flags = 5;
} else {
this->cylinder.base.type = 5;
this->cylinder.body.flags = 1;
}
/*
* Handles the jump movement onto Link's sword. Dark Link doesn't move during the
* sword jump. Instead, his shape y-offset is increased (see below). Once the sword
* jump is finished, the offset is added to his position to fix the discrepancy.
*/
if (sSwordJumpState != 0) {
Math_SmoothStepToF(&sSwordJumpHeight, 2630.0f, 1.0f, 2000.0f, 0.0f);
this->actor.velocity.y -= 0.6f;
} else if (sSwordJumpHeight != 0) {
this->actor.posRot.pos.y += sSwordJumpHeight * 0.01f;
sSwordJumpHeight = 0;
}
if ((sActionState == ENTORCH2_WAIT) || (this->invincibilityTimer < 0)) {
sZTargetFlag = false;
} else {
sZTargetFlag = true;
}
if (sJumpslashTimer != 0) {
sJumpslashTimer--;
}
this->actor.posRot2.pos = this->actor.posRot.pos;
this->actor.posRot2.pos.y += 20.0f;
this->actor.shape.unk_08 = sSwordJumpHeight;
}
#else
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/EnTorch2_Update.s")
#endif
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1F7A8.s")
s32 EnTorch2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx,
Gfx** gfx) {
Player* this = THIS;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1F7D4.s")
return func_8008FCC8(globalCtx, limbIndex, dList, pos, rot, &this->actor);
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/EnTorch2_Draw.s")
void EnTorch2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) {
Player* this = THIS;
func_80090D20(globalCtx, limbIndex, dList, rot, &this->actor);
}
void EnTorch2_Draw(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
Player* this = THIS;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_torch2.c", 1050);
func_80093C80(globalCtx);
func_80093D84(globalCtx->state.gfxCtx);
if (sAlpha == 255) {
gDPSetEnvColor(POLY_OPA_DISP++, 255, 0, 0, sAlpha);
gSPSegment(POLY_OPA_DISP++, 0x0C, D_80116280 + 2);
func_8002EBCC(&this->actor, globalCtx, 0);
func_8002ED80(&this->actor, globalCtx, 0);
POLY_OPA_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.limbDrawTbl,
this->skelAnime.dListCount, EnTorch2_OverrideLimbDraw, EnTorch2_PostLimbDraw,
this, POLY_OPA_DISP);
} else {
gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, sAlpha);
gSPSegment(POLY_XLU_DISP++, 0x0C, D_80116280);
func_8002EBCC(&this->actor, globalCtx, 0);
func_8002ED80(&this->actor, globalCtx, 0);
POLY_XLU_DISP = SkelAnime_DrawFlex(globalCtx, this->skelAnime.skeleton, this->skelAnime.limbDrawTbl,
this->skelAnime.dListCount, EnTorch2_OverrideLimbDraw, EnTorch2_PostLimbDraw,
this, POLY_XLU_DISP);
}
CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_torch2.c", 1114);
}