diff options
| author | petrie911 <69443847+petrie911@users.noreply.github.com> | 2020-12-28 19:56:24 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2020-12-28 20:56:24 -0500 |
| commit | 42f2d38b8fb8e97330bc12e06d71bd35ea2c33e2 (patch) | |
| tree | 2c904f252f415b9ef2336190620b60d773c63a0e /src | |
| parent | 1e6bd7f623dead2c5bcbdb5c89a0c9e65c7a86e4 (diff) | |
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>
Diffstat (limited to 'src')
| -rw-r--r-- | src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.c | 94 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.h | 4 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Torch2/z_en_torch2.c | 757 |
3 files changed, 796 insertions, 59 deletions
diff --git a/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.c b/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.c index 840af5d73..29bbc360f 100644 --- a/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.c +++ b/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.c @@ -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); diff --git a/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.h b/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.h index 4104d578b..a2b089399 100644 --- a/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.h +++ b/src/overlays/actors/ovl_En_Blkobj/z_en_blkobj.h @@ -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 diff --git a/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c index 752a678e6..e6bc82de7 100644 --- a/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c +++ b/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c @@ -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; + +/* 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; + +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, +}; + +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 -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1DB98.s") + } 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; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1DBD8.s") + // Handles Dark Link's reaction to sword attack other than jumpslashes -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1DD70.s") + 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 + +s32 EnTorch2_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, + Gfx** gfx) { + Player* this = THIS; + + return func_8008FCC8(globalCtx, limbIndex, dList, pos, rot, &this->actor); +} + +void EnTorch2_PostLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot, void* thisx, Gfx** gfx) { + Player* this = THIS; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1F7A8.s") + func_80090D20(globalCtx, limbIndex, dList, rot, &this->actor); +} -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/func_80B1F7D4.s") +void EnTorch2_Draw(Actor* thisx, GlobalContext* globalCtx2) { + GlobalContext* globalCtx = globalCtx2; + Player* this = THIS; + s32 pad; -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Torch2/EnTorch2_Draw.s") + 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); +} |
