diff options
| author | Kenix3 <kenixwhisperwind@gmail.com> | 2021-03-29 13:14:08 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-03-29 19:14:08 +0200 |
| commit | f4499a8de21c9c94e869d812e059dfd7b4cfaddd (patch) | |
| tree | 35204d1db8dcda844cc79b9afe43654268778a70 /src | |
| parent | ed4021a6fe8f3418ba97d607d81f4cd16c75781d (diff) | |
En_Clear_Tag OK and documented (#689)
* Decompile and document En_Clear_Tag
* Adds a define for setting the total count of ClearTag particles.
* Fixes swap in particle effect struct member names.
Additionally, implements the DECR macro where available.
Additionally, implements the CLAMP_MIN macro.
Additionally, adds more comments inside of functions explaining how the Arwing works.
* Reformats z_en_clear_tag.c after recent fixes.
* Removes not OK DECR and CLAMP_MIN macros.
I don't know why they built locally OK for me. May have had a stale build.
* Added static to data declarations.
Additionally added white spacing between data definitions.
* Various syling fixes in EnClearTag
Fixes inconsistent usage of particle/effect. They're Effect is the better name, so I went with that.
Fixed a naming issue in CalculateFloorTangent.
Adds s prefix to all the static variables.
Renames clearTagParticlesBuffer to sClearTagEffects
Consistent capitalization in comments.
Adds more descriptive comments to function level comments that didn't say a lot already.
Renamed gameplay.particleEffects to gameplay.specialEffects.
Renames cameraNumber to cameraId.
Fixed capitalization in display list variable names.
* Always capitalize the A in arwing.
* Fixes issue with overzealous replacement of arwing -> Arwing
* Renames arwing display lists to include the g prefix.
* Renames effect seed -> random
* Removes unnecessary decrement timer comments.
* Updates Arwing comments to detail why it's important that the roll is updated to zero.
* Updates stack variable names in EnClearTag_Update to be more consistent with each other.
* Updates Arwing crashing documentation.
* Further update documentation on arwing crashing.
* Even further updates to Arwing crashing docs.
* Fixes issue of missing newline in En_Clear_Tag.xml
* targetDirection reset now has a chained assignment
* Rotation angle to hex
* Fixes rotation hex and identifies float.
* Updates arwing fire check.
* Arwing cutscene timer is multiplied by 128 rather than shifting 7
* Updates scaledCrashingTimer assignment to be more consistent.
* Updates max scale to multiply by 2
* Gives name to the work buffer indices.
Additionally, makes enum names more consistent.
* Removes unnecessary globalCtx2 in EnClearTag_CreateFlashEffect
* Runs format.sh after recent changes.
* Updated path of ClearTag assets.
* Clear tag now uses Arwing in the display list name for all effects.
* Updates work buffer to expand and contract as new enums are added.
* Uses c99 defines in my c89
* Updates the unknown 8 bytes in en_clear_tag
* review
Co-authored-by: fig02 <fig02srl@gmail.com>
Diffstat (limited to 'src')
| -rw-r--r-- | src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c | 1016 | ||||
| -rw-r--r-- | src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h | 83 |
2 files changed, 1081 insertions, 18 deletions
diff --git a/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c b/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c index abb22e80b..bb5e55a7e 100644 --- a/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c +++ b/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.c @@ -9,7 +9,18 @@ void EnClearTag_Destroy(Actor* thisx, GlobalContext* globalCtx); void EnClearTag_Update(Actor* thisx, GlobalContext* globalCtx); void EnClearTag_Draw(Actor* thisx, GlobalContext* globalCtx); -/* +void EnClearTag_UpdateEffects(GlobalContext* globalCtx); +void EnClearTag_DrawEffects(GlobalContext* globalCtx); + +void EnClearTag_CreateDebrisEffect(GlobalContext* globalCtx, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, + f32 scale, f32 floorHeight); +void EnClearTag_CreateFireEffect(GlobalContext* globalCtx, Vec3f* pos, f32 scale); +void EnClearTag_CreateSmokeEffect(GlobalContext* globalCtx, Vec3f* position, f32 scale); +void EnClearTag_CreateFlashEffect(GlobalContext* globalCtx, Vec3f* position, f32 scale, f32 floorHeight, + Vec3f* floorTangent); + +void EnClearTag_CalculateFloorTangent(EnClearTag* this); + const ActorInit En_Clear_Tag_InitVars = { ACTOR_EN_CLEAR_TAG, ACTORCAT_BOSS, @@ -22,9 +33,13 @@ const ActorInit En_Clear_Tag_InitVars = { (ActorFunc)EnClearTag_Draw, }; -static ColliderCylinderInit D_809D5C6C = { +static u8 sIsEffectsInitialized = false; + +static Vec3f sZeroVector = { 0.0f, 0.0f, 0.0f }; + +static ColliderCylinderInit sArwingCylinderInit = { { - COLTYPE_METAL, + COLTYPE_HIT3, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, @@ -42,9 +57,9 @@ static ColliderCylinderInit D_809D5C6C = { { 15, 30, 10, { 0, 0, 0 } }, }; -static ColliderCylinderInit D_809D5C40 = { +static ColliderCylinderInit sLaserCylinderInit = { { - COLTYPE_HIT3, + COLTYPE_METAL, AT_ON | AT_TYPE_ENEMY, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, @@ -61,25 +76,992 @@ static ColliderCylinderInit D_809D5C40 = { }, { 15, 30, 10, { 0, 0, 0 } }, }; -*/ -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/func_809D35B0.s") -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/func_809D36A8.s") +static UNK_TYPE4 D_809D5C98 = 0; // unused +static UNK_TYPE4 D_809D5C9C = 0; // unused + +static EnClearTagEffect sClearTagEffects[CLEAR_TAG_EFFECT_MAX_COUNT]; + +#include "overlays/ovl_En_Clear_Tag/ovl_En_Clear_Tag.c" + +/** + * Creates a debris effect. + * Debris effects are spawned when the Arwing dies. It spawns fire effects. + */ +void EnClearTag_CreateDebrisEffect(GlobalContext* globalCtx2, Vec3f* position, Vec3f* velocity, Vec3f* acceleration, + f32 scale, f32 floorHeight) { + s16 i; + s16 seed; + EnClearTagEffect* effect; + GlobalContext* globalCtx; + + globalCtx = globalCtx2; + effect = (EnClearTagEffect*)globalCtx->specialEffects; + + // Look for an available effect to allocate a Debris effect to. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_AVAILABLE) { + effect->type = CLEAR_TAG_EFFECT_DEBRIS; + + effect->position = *position; + effect->velocity = *velocity; + effect->acceleration = *acceleration; + + effect->scale = scale; + + // Set the debris effects to spawn in a circle. + effect->rotationY = Rand_ZeroFloat(M_PI * 2); + effect->rotationX = Rand_ZeroFloat(M_PI * 2); + + effect->timer = effect->bounces = 0; + + effect->floorHeight = floorHeight; + + seed = (s32)Rand_ZeroFloat(10.0f); + effect->random = seed; + + return; + } + } +} + +/** + * Creates a fire effect. + * Fire effects are spawned by debris effects. Fire effects spawn smoke effects + */ +void EnClearTag_CreateFireEffect(GlobalContext* globalCtx2, Vec3f* pos, f32 scale) { + s16 i; + s16 seed; + EnClearTagEffect* effect; + GlobalContext* globalCtx; + + globalCtx = globalCtx2; + effect = (EnClearTagEffect*)globalCtx->specialEffects; + + // Look for an available effect to allocate a fire effect to. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_AVAILABLE) { + seed = (s32)Rand_ZeroFloat(100.0f); + effect->random = seed; + effect->type = CLEAR_TAG_EFFECT_FIRE; + + effect->position = *pos; + effect->velocity = sZeroVector; + effect->acceleration = sZeroVector; + effect->acceleration.y = 0.15f; + + effect->scale = scale; + + effect->primColor.a = 200.0f; + + return; + } + } +} + +/** + * Creates a smoke effect. + * Smoke effects are spawned by fire effects. + */ +void EnClearTag_CreateSmokeEffect(GlobalContext* globalCtx2, Vec3f* position, f32 scale) { + s16 i; + s16 seed; + EnClearTagEffect* effect; + GlobalContext* globalCtx; + + globalCtx = globalCtx2; + effect = (EnClearTagEffect*)globalCtx->specialEffects; + + // Look for an available effect to allocate a smoke effect to. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_AVAILABLE) { + seed = (s32)Rand_ZeroFloat(100.0f); + effect->random = seed; + effect->type = CLEAR_TAG_EFFECT_SMOKE; + + effect->position = *position; + effect->velocity = sZeroVector; + effect->acceleration = sZeroVector; + + effect->scale = scale; + effect->maxScale = scale * 2.0f; + + effect->primColor.r = 200.0f; + effect->primColor.g = 20.0f; + effect->primColor.b = 0.0f; + effect->primColor.a = 255.0f; + effect->envColor.r = 255.0f; + effect->envColor.g = 215.0f; + effect->envColor.b = 255.0f; + + return; + } + } +} + +/** + * Creates a flash effect. + * Flash effects are spawned when the Arwing dies. + * Flash effects two components: 1) a billboard flash, and 2) a light effect on the ground. + */ +void EnClearTag_CreateFlashEffect(GlobalContext* globalCtx, Vec3f* position, f32 scale, f32 floorHeight, + Vec3f* floorTangent) { + s16 i; + EnClearTagEffect* effect; + effect = (EnClearTagEffect*)globalCtx->specialEffects; + + // Look for an available effect to allocate a flash effect to. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_AVAILABLE) { + effect->type = CLEAR_TAG_EFFECT_FLASH; + + effect->position = *position; + effect->velocity = sZeroVector; + effect->acceleration = sZeroVector; + + effect->scale = 0.0f; + effect->maxScale = scale * 2.0f; + + effect->floorHeight = floorHeight; + effect->floorTangent = *floorTangent; + + effect->primColor.a = 180.0f; + + return; + } + } +} + +/** + * EnClear_Tag destructor. + * This just destroys the collider. + */ +void EnClearTag_Destroy(Actor* thisx, GlobalContext* globalCtx) { + EnClearTag* this = THIS; + Collider_DestroyCylinder(globalCtx, &this->collider); +} + +/** + * EnClear_Tag constructor. + * This allocates a collider, initializes effects, and sets up ClearTag instance data. + */ +void EnClearTag_Init(Actor* thisx, GlobalContext* globalCtx) { + EnClearTag* this = THIS; + s32 defaultCutsceneTimer = 100; + s16 j, i; + + Collider_InitCylinder(globalCtx, &this->collider); + + // Initialize the Arwing laser. + if (this->actor.params == CLEAR_TAG_LASER) { + this->state = CLEAR_TAG_STATE_LASER; + this->work[CLEAR_TAG_TIMER_LASER_DEATH] = 70; + this->actor.speedXZ = 35.0f; + func_8002D908(&this->actor); + for (j = 0; j <= 0; j++) { + func_8002D7EC(&this->actor); + } + this->actor.scale.x = 0.4f; + this->actor.scale.y = 0.4f; + this->actor.scale.z = 2.0f; + this->actor.speedXZ = 70.0f; + this->actor.shape.rot.x = -this->actor.shape.rot.x; + + func_8002D908(&this->actor); + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sLaserCylinderInit); + Audio_PlayActorSound2(&this->actor, NA_SE_IT_SWORD_REFLECT_MG); + return; + } + + // Initialize the Arwing. + this->actor.flags |= 1; + this->actor.targetMode = 5; + Collider_SetCylinder(globalCtx, &this->collider, &this->actor, &sArwingCylinderInit); + this->actor.colChkInfo.health = 3; + + // Update the Arwing to play the intro cutscene. + if (this->actor.params == CLEAR_TAG_CUTSCENE_ARWING) { + this->work[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] = 70; + this->work[CLEAR_TAG_TIMER_ARWING_ENTER_LOCKED_ON] = 250; + this->state = CLEAR_TAG_STATE_DEMO; + this->actor.world.rot.x = 0x4000; + this->cutsceneMode = CLEAR_TAG_CUTSCENE_MODE_SETUP; + this->cutsceneTimer = defaultCutsceneTimer; + this->work[CLEAR_TAG_TIMER_ARWING_UPDATE_BG_INFO] = 20; + } + + // Initialize all effects to available if effects have not been initialized. + if (!sIsEffectsInitialized) { + sIsEffectsInitialized = true; + globalCtx->specialEffects = &sClearTagEffects[0]; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++) { + sClearTagEffects[i].type = CLEAR_TAG_EFFECT_AVAILABLE; + } + this->drawMode = CLEAR_TAG_DRAW_MODE_ALL; + } +} + +/** + * Calculate a floor tangent. + * This is used for the ground flash display lists and Arwing shadow display lists to snap onto the floor. + */ +void EnClearTag_CalculateFloorTangent(EnClearTag* this) { + f32 x; + f32 y; + f32 z; + + // If there is a floor poly below the Arwing, calculate the floor tangent. + if (this->actor.floorPoly != NULL) { + x = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.x); + y = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.y); + z = COLPOLY_GET_NORMAL(this->actor.floorPoly->normal.z); + + this->floorTangent.x = -Math_FAtan2F(-z * y, 1.0f); + this->floorTangent.z = Math_FAtan2F(-x * y, 1.0f); + } +} + +/** + * EnClear_Tag update function. + * + * ClearTag has three variants: + * CLEAR_TAG_CUTSCENE_ARWING which is the regular Arwing and plays a cutscene on spawn. + * CLEAR_TAG_ARWING which is a regular Arwing. + * CLEAR_TAG_LASER which are the lasers fired by the Arwing. + * + * This function controls the Arwing flying. A target position is set and the Arwing flies toward it based on + * calculated directions from the current position. + * This function fires Arwing lasers when the state is CLEAR_TAG_STATE_TARGET_LOCKED. + * This function controls the cutscene that plays when the Arwing has params for + * cutscene. The cutscene stops playing when the Arwing is a specified distance from the starting point. + */ +void EnClearTag_Update(Actor* thisx, GlobalContext* globalCtx2) { + u8 hasAtHit = 0; + s16 i; + s16 xRotationTarget; + s16 rotationScale; + EnClearTag* this = THIS; + GlobalContext* globalCtx = globalCtx2; + Player* player = PLAYER; + f32 vectorToTargetX; + f32 vectorToTargetY; + f32 vectorToTargetZ; + s16 worldRotationTargetZ; + s16 cutsceneTimer; + f32 loseTargetLockDistance; + s32 pad; + s16 worldRotationTargetY; + s16 worldRotationTargetX; + f32 targetCircleX; + f32 targetCircleZ; + f32 cutsceneCameraCircleX; + f32 cutsceneCameraCircleZ; + s16 isArwingAlive; + Vec3f cutsceneCameraAtTarget; + Vec3f cutsceneCameraEyeTarget; + Vec3f crashEffectLocation; + Vec3f crashEffectVelocity; + Vec3f debrisEffectAcceleration; + + this->timer++; + + if (this->drawMode != CLEAR_TAG_DRAW_MODE_EFFECT) { + for (i = 0; i < 3; i++) { + if (this->work[i] != 0) { + this->work[i]--; + } + } + + if (this->cutsceneTimer != 0) { + this->cutsceneTimer--; + } + + switch (this->state) { + case CLEAR_TAG_STATE_DEMO: + case CLEAR_TAG_STATE_TARGET_LOCKED: + case CLEAR_TAG_STATE_FLYING: + // Check if the Arwing should crash. + if (this->collider.base.acFlags & 2 && + (this->collider.base.acFlags &= ~2, this->crashingTimer = 20, + Actor_SetColorFilter(&this->actor, 0x4000, 255, 0, 5), + this->acceleration.x = Rand_CenteredFloat(15.0f), this->acceleration.y = Rand_CenteredFloat(15.0f), + this->acceleration.z = Rand_CenteredFloat(15.0f), + Audio_PlayActorSound2(&this->actor, NA_SE_EN_FANTOM_THUNDER_GND), this->actor.colChkInfo.health--, + (s8)this->actor.colChkInfo.health <= 0)) { + this->state = CLEAR_TAG_STATE_CRASHING; + this->actor.velocity.y = 0.0f; + } else { + Actor_SetScale(&this->actor, 0.2f); + this->actor.speedXZ = 7.0f; + + if (this->work[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] == 0) { + if (this->work[CLEAR_TAG_TIMER_ARWING_ENTER_LOCKED_ON] == 0) { + this->state = CLEAR_TAG_STATE_TARGET_LOCKED; + this->work[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] = 300; + } else { + this->state = CLEAR_TAG_STATE_FLYING; + this->work[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] = ((s16)Rand_ZeroFloat(50.0f)) + 20; + + if (this->actor.params == CLEAR_TAG_ARWING) { + // Set the Arwing to fly in a circle around the player. + targetCircleX = Math_SinS(player->actor.shape.rot.y) * 400.0f; + targetCircleZ = Math_CosS(player->actor.shape.rot.y) * 400.0f; + this->targetPosition.x = + Rand_CenteredFloat(700.0f) + (player->actor.world.pos.x + targetCircleX); + this->targetPosition.y = Rand_ZeroFloat(200.0f) + player->actor.world.pos.y + 150.0f; + this->targetPosition.z = + Rand_CenteredFloat(700.0f) + (player->actor.world.pos.z + targetCircleZ); + } else { + // Set the Arwing to fly to a random position. + this->targetPosition.x = Rand_CenteredFloat(700.0f); + this->targetPosition.y = Rand_ZeroFloat(200.0f) + 150.0f; + this->targetPosition.z = Rand_CenteredFloat(700.0f); + } + } + + this->targetDirection.x = this->targetDirection.y = this->targetDirection.z = 0.0f; + } + + rotationScale = 10; + xRotationTarget = 2048; + loseTargetLockDistance = 100.0f; + if (this->state == CLEAR_TAG_STATE_TARGET_LOCKED) { + // Set the Arwing to fly towards the player. + this->targetPosition.x = player->actor.world.pos.x; + this->targetPosition.y = player->actor.world.pos.y + 40.0f; + this->targetPosition.z = player->actor.world.pos.z; + rotationScale = 7; + xRotationTarget = 0x1000; + loseTargetLockDistance = 150.0f; + } else if (this->state == CLEAR_TAG_STATE_DEMO) { + // Move the Arwing for the intro cutscene. + + // Do a Barrel Roll! + this->roll += 0.5f; + if (this->roll > M_PI * 2) { + this->roll -= M_PI * 2; + } + + // Set the Arwing to fly to a hardcoded position. + this->targetPosition.x = 0.0f; + this->targetPosition.y = 300.0f; + this->targetPosition.z = 0.0f; + loseTargetLockDistance = 100.0f; + } + + // If the Arwing is not in cutscene state, smoothly update the roll to zero. + // This will reset the Arwing to be right side up after the cutscene is done. + // The cutscene will set the Arwing to do a barrel roll and doesn't end on right side up. + if (this->state != CLEAR_TAG_STATE_DEMO) { + Math_ApproachZeroF(&this->roll, 0.1f, 0.2f); + } + + // Calculate a vector towards the targetted position. + vectorToTargetX = this->targetPosition.x - this->actor.world.pos.x; + vectorToTargetY = this->targetPosition.y - this->actor.world.pos.y; + vectorToTargetZ = this->targetPosition.z - this->actor.world.pos.z; + + // If the Arwing is within a certain distance to the target position, it will be updated to flymode + if (sqrtf(SQ(vectorToTargetX) + SQ(vectorToTargetY) + SQ(vectorToTargetZ)) < + loseTargetLockDistance) { + this->work[CLEAR_TAG_TIMER_ARWING_UPDATE_STATE] = 0; + if (this->state == CLEAR_TAG_STATE_TARGET_LOCKED) { + this->work[CLEAR_TAG_TIMER_ARWING_ENTER_LOCKED_ON] = ((s16)Rand_ZeroFloat(100.0f)) + 100; + } + this->state = CLEAR_TAG_STATE_FLYING; + } + + // Calculate the direction for the Arwing to fly and the rotation for the Arwing + // based on the Arwing's direction, and current rotation. + worldRotationTargetY = (s32)(Math_FAtan2F(vectorToTargetX, vectorToTargetZ) * (0x8000 / M_PI)); + worldRotationTargetX = + (s32)(Math_FAtan2F(vectorToTargetY, sqrtf(SQ(vectorToTargetX) + SQ(vectorToTargetZ))) * + (0x8000 / M_PI)); + if (worldRotationTargetX < 0) { + if (this->actor.world.pos.y < this->actor.floorHeight + 20.0f) { + worldRotationTargetX = 0; + } + } + Math_ApproachS(&this->actor.world.rot.x, worldRotationTargetX, rotationScale, + this->targetDirection.x); + worldRotationTargetZ = Math_SmoothStepToS(&thisx->world.rot.y, worldRotationTargetY, rotationScale, + this->targetDirection.y, 0); + Math_ApproachF(&this->targetDirection.x, xRotationTarget, 1.0f, 256.0f); + this->targetDirection.y = this->targetDirection.x; + if (ABS(worldRotationTargetZ) < 4096) { + Math_ApproachS(&thisx->world.rot.z, 0, 15, this->targetDirection.z); + Math_ApproachF(&this->targetDirection.z, 1280.0f, 1.0f, 256.0f); + + // Check if the Arwing should fire it's laser. + if ((this->timer % 4) == 0 && (Rand_ZeroOne() < 0.75f) && + (this->state == CLEAR_TAG_STATE_TARGET_LOCKED)) { + this->shouldShootLaser = 1; + } + } else { + worldRotationTargetZ = worldRotationTargetZ > 0 ? -9472 : 9472; + Math_ApproachS(&this->actor.world.rot.z, worldRotationTargetZ, rotationScale, + this->targetDirection.z); + Math_ApproachF(&this->targetDirection.z, 4096.0f, 1.0f, 512.0f); + } + this->actor.shape.rot = this->actor.world.rot; + this->actor.shape.rot.x = -this->actor.shape.rot.x; + + // Update the Arwing's velocity. + func_8002D908(&this->actor); + this->actor.velocity.x += this->acceleration.x; + this->actor.velocity.y += this->acceleration.y; + this->actor.velocity.z += this->acceleration.z; + Math_ApproachZeroF(&this->acceleration.x, 1.0f, 1.0f); + Math_ApproachZeroF(&this->acceleration.y, 1.0f, 1.0f); + Math_ApproachZeroF(&this->acceleration.z, 1.0f, 1.0f); + + // Fire the Arwing laser. + if (this->shouldShootLaser) { + this->shouldShootLaser = 0; + Actor_Spawn(&globalCtx->actorCtx, globalCtx, ACTOR_EN_CLEAR_TAG, this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, this->actor.world.rot.x, + this->actor.world.rot.y, this->actor.world.rot.z, CLEAR_TAG_STATE_LASER); + } + } + + case CLEAR_TAG_STATE_CRASHING: + if (this->crashingTimer != 0) { + this->crashingTimer--; + } + + func_8002D7EC(&this->actor); + + Actor_SetFocus(&this->actor, 0.0f); + + // Update Arwing collider to better match a ground collision. + this->collider.dim.radius = 20; + this->collider.dim.height = 15; + this->collider.dim.yShift = -5; + Collider_UpdateCylinder(&this->actor, &this->collider); + + CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + + if (this->work[CLEAR_TAG_TIMER_ARWING_UPDATE_BG_INFO] == 0) { + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 30.0f, 100.0f, 5); + EnClearTag_CalculateFloorTangent(this); + } + + if (this->state == CLEAR_TAG_STATE_CRASHING) { + // Create fire effects while the Arwing crashes. + EnClearTag_CreateFireEffect(globalCtx, &this->actor.world.pos, 1.0f); + + // Causes the Arwing to roll around seemingly randomly while crashing. + this->roll -= 0.5f; + this->actor.velocity.y -= 0.2f; + this->actor.shape.rot.x += 16; + + Audio_PlayActorSound2(&this->actor, NA_SE_EN_DODO_K_BREATH - SFX_FLAG); + + // Check if the Arwing has hit the ground. + if (this->actor.bgCheckFlags & 9) { + this->shouldExplode = 1; + + if (this->drawMode != CLEAR_TAG_DRAW_MODE_ARWING) { + this->drawMode = CLEAR_TAG_DRAW_MODE_EFFECT; + this->deathTimer = 70; + this->actor.flags &= ~1; + } else { + Actor_Kill(&this->actor); + } + } + } + break; + + case CLEAR_TAG_STATE_LASER: + func_8002D7EC(&this->actor); + + // Check if the laser has hit a target. + if (this->collider.base.atFlags & AT_HIT) { + hasAtHit = 1; + } + + // Set laser collider properties. + this->collider.dim.radius = 23; + this->collider.dim.height = 25; + this->collider.dim.yShift = -10; + Collider_UpdateCylinder(&this->actor, &this->collider); + CollisionCheck_SetAT(globalCtx, &globalCtx->colChkCtx, &this->collider.base); + Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 50.0f, 80.0f, 100.0f, 5); + + // Check if the laser has hit a target, timed out, or hit the ground. + if (this->actor.bgCheckFlags & 9 || hasAtHit || this->work[CLEAR_TAG_TIMER_LASER_DEATH] == 0) { + // Kill the laser. + Actor_Kill(&this->actor); + // Player laser sound effect if the laser did not time out. + if (this->work[CLEAR_TAG_TIMER_LASER_DEATH] != 0) { + Audio_PlaySoundAtPosition(globalCtx, &this->actor.world.pos, 20, NA_SE_EN_FANTOM_THUNDER_GND); + } + } + break; + } + + if (this->state < CLEAR_TAG_STATE_LASER) { + // Play the Arwing cutscene. + osSyncPrintf("DEMO_MODE %d\n", this->cutsceneMode); + osSyncPrintf("CAMERA_NO %d\n", this->cameraId); + + if (this->cutsceneMode != CLEAR_TAG_CUTSCENE_MODE_NONE) { + switch (this->cutsceneMode) { + case CLEAR_TAG_CUTSCENE_MODE_SETUP: + // Initializes Arwing cutscene camera data. + this->cutsceneMode = CLEAR_TAG_CUTSCENE_MODE_PLAY; + func_80064520(globalCtx, &globalCtx->csCtx); + this->cameraId = Gameplay_CreateSubCamera(globalCtx); + Gameplay_ChangeCameraStatus(globalCtx, 0, 1); + Gameplay_ChangeCameraStatus(globalCtx, this->cameraId, 7); + case CLEAR_TAG_CUTSCENE_MODE_PLAY: + // Update the Arwing cutscene camera to spin around in a circle. + cutsceneTimer = this->timer * 128; + cutsceneCameraCircleX = Math_SinS(cutsceneTimer) * 200.0f; + cutsceneCameraCircleZ = Math_CosS(cutsceneTimer) * 200.0f; + cutsceneCameraAtTarget.x = this->actor.world.pos.x + cutsceneCameraCircleX; + cutsceneCameraAtTarget.y = 200.0f; + cutsceneCameraAtTarget.z = this->actor.world.pos.z + cutsceneCameraCircleZ; + cutsceneCameraEyeTarget = this->actor.world.pos; + break; + } + + // Make the Arwing cutscene camera approach the target. + if (this->cameraId != 0) { + Math_ApproachF(&this->cutsceneCameraAt.x, cutsceneCameraAtTarget.x, 0.1f, 500.0f); + Math_ApproachF(&this->cutsceneCameraAt.y, cutsceneCameraAtTarget.y, 0.1f, 500.0f); + Math_ApproachF(&this->cutsceneCameraAt.z, cutsceneCameraAtTarget.z, 0.1f, 500.0f); + Math_ApproachF(&this->cutsceneCameraEye.x, cutsceneCameraEyeTarget.x, 0.2f, 500.0f); + Math_ApproachF(&this->cutsceneCameraEye.y, cutsceneCameraEyeTarget.y, 0.2f, 500.0f); + Math_ApproachF(&this->cutsceneCameraEye.z, cutsceneCameraEyeTarget.z, 0.2f, 500.0f); + Gameplay_CameraSetAtEye(globalCtx, this->cameraId, &this->cutsceneCameraEye, + &this->cutsceneCameraAt); + } + + // Cutscene has finished. + if (this->cutsceneTimer == 1) { + func_800C08AC(globalCtx, this->cameraId, 0); + this->cutsceneMode = this->cameraId = 0; + func_80064534(globalCtx, &globalCtx->csCtx); + } + } + } + } + + // Explode the Arwing + if (this->shouldExplode) { + this->shouldExplode = 0; + Audio_PlaySoundAtPosition(globalCtx, &this->actor.world.pos, 40, NA_SE_IT_BOMB_EXPLOSION); + + // Spawn flash effect. + crashEffectLocation.x = this->actor.world.pos.x; + crashEffectLocation.y = (this->actor.world.pos.y + 40.0f) - 30.0f; + crashEffectLocation.z = this->actor.world.pos.z; + EnClearTag_CreateFlashEffect(globalCtx, &crashEffectLocation, 6.0f, this->actor.floorHeight, + &this->floorTangent); + + // Spawn smoke effect. + crashEffectLocation.y = (this->actor.world.pos.y + 30.0f) - 50.0f; + EnClearTag_CreateSmokeEffect(globalCtx, &crashEffectLocation, 3.0f); + crashEffectLocation.y = this->actor.world.pos.y; + + // Spawn debris effects. + for (i = 0; i < 15; i++) { + crashEffectVelocity.x = sinf(i * 1.65f) * i * 0.3f; + crashEffectVelocity.z = cosf(i * 1.65f) * i * 0.3f; + crashEffectVelocity.y = Rand_ZeroFloat(6.0f) + 5.0f; + crashEffectVelocity.x += Rand_CenteredFloat(0.5f); + crashEffectVelocity.z += Rand_CenteredFloat(0.5f); + + debrisEffectAcceleration.x = 0.0f; + debrisEffectAcceleration.z = 0.0f; + debrisEffectAcceleration.y = -1.0f; + + EnClearTag_CreateDebrisEffect(globalCtx, &crashEffectLocation, &crashEffectVelocity, + &debrisEffectAcceleration, Rand_ZeroFloat(0.15f) + 0.075f, + this->actor.floorHeight); + } + } + + if (this->drawMode != CLEAR_TAG_DRAW_MODE_ARWING) { + // Check if the Arwing should be removed. + if (this->drawMode == CLEAR_TAG_DRAW_MODE_EFFECT) { + if (this->deathTimer == 0) { + isArwingAlive = 0; + } else { + this->deathTimer--; + isArwingAlive = this->deathTimer; + } + + if (!isArwingAlive) { + Actor_Kill(thisx); + } + } + + EnClearTag_UpdateEffects(globalCtx); + } +} + +/** + * EnClear_Tag draw function. + * Laser clear tag type will draw two lasers. + * Arwing clear tage types will draw the Arwing, the backfire, and a shadow. + */ +void EnClearTag_Draw(Actor* thisx, GlobalContext* globalCtx2) { + EnClearTag* this = THIS; + GlobalContext* globalCtx = globalCtx2; + + OPEN_DISPS(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 983); + if (this->drawMode != CLEAR_TAG_DRAW_MODE_EFFECT) { + func_80093D84(globalCtx->state.gfxCtx); + + if (this->state >= CLEAR_TAG_STATE_LASER) { + // Draw Arwing lasers. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 255, 0, 255); + + Matrix_Translate(25.0f, 0.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1004), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingLaserDL); + + Matrix_Translate(-50.0f, 0.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1011), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingLaserDL); + } else { + // Draw the Arwing itself. + func_80093D18(globalCtx->state.gfxCtx); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, 255); + if (this->crashingTimer != 0) { + f32 xRotation; + f32 yRotation; + f32 scaledCrashingTimer = this->crashingTimer * 0.05f; + + xRotation = Math_SinS(this->timer * 12288) * scaledCrashingTimer; + yRotation = Math_SinS(this->timer * 14080) * scaledCrashingTimer; + Matrix_RotateX(xRotation, MTXMODE_APPLY); + Matrix_RotateY(yRotation, MTXMODE_APPLY); + } + Matrix_RotateZ(this->roll, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1030), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gArwingDL); + + // Draw the Arwing Backfire + Matrix_Translate(0.0f, 0.0f, -60.0f, MTXMODE_APPLY); + func_800D1FD4(&globalCtx->mf_11DA0); + Matrix_Scale(2.5f, 1.3f, 0.0f, MTXMODE_APPLY); + if ((this->timer & 1) != 0) { + Matrix_Scale(1.15f, 1.15f, 1.15f, MTXMODE_APPLY); + } + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 200, 155); + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 50, 0, 0); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1067), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingBackfireDL); + + // Draw the Arwing shadow. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 0, 0, 0, 130); + Matrix_Translate(this->actor.world.pos.x, this->actor.floorHeight, this->actor.world.pos.z, MTXMODE_NEW); + Matrix_RotateX(this->floorTangent.x, MTXMODE_APPLY); + Matrix_RotateZ(this->floorTangent.z, MTXMODE_APPLY); + Matrix_Scale(this->actor.scale.x + 0.35f, 0.0f, this->actor.scale.z + 0.35f, MTXMODE_APPLY); + Matrix_RotateY((this->actor.shape.rot.y / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateX((this->actor.shape.rot.x / 32768.0f) * M_PI, MTXMODE_APPLY); + Matrix_RotateZ((this->actor.shape.rot.z / 32768.0f) * M_PI, MTXMODE_APPLY); + if (this->crashingTimer != 0) { + f32 xRotation; + f32 yRotation; + f32 scaledCrashingTimer; + scaledCrashingTimer = this->crashingTimer * 0.05f; + xRotation = Math_SinS(this->timer * 12288) * scaledCrashingTimer; + yRotation = Math_SinS(this->timer * 14080) * scaledCrashingTimer; + Matrix_RotateX(xRotation, MTXMODE_APPLY); + Matrix_RotateY(yRotation, MTXMODE_APPLY); + } + Matrix_RotateZ(this->roll, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1104), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingShadowDL); + } + } + + if (this->drawMode != CLEAR_TAG_DRAW_MODE_ARWING) { + EnClearTag_DrawEffects(globalCtx); + } + + CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_en_clear_tag.c", 1119); +} + +/** + * Updates the Arwing effects. + * Performs effect physics. + * Moves and bounces debris effects. + * Fades most effects out of view. When effects are completely faded away they are removed. + */ +void EnClearTag_UpdateEffects(GlobalContext* globalCtx2) { + GlobalContext* globalCtx; + s16 i; + f32 originalYPosition; + Vec3f sphereCenter; + EnClearTagEffect* effect; + s32 pad; + + globalCtx = globalCtx2; + effect = (EnClearTagEffect*)globalCtx->specialEffects; + + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type != CLEAR_TAG_EFFECT_AVAILABLE) { + effect->random++; + + // Perform effect physics. + effect->position.x += effect->velocity.x; + originalYPosition = effect->position.y; + effect->position.y += effect->velocity.y; + effect->position.z += effect->velocity.z; + effect->velocity.x += effect->acceleration.x; + effect->velocity.y += effect->acceleration.y; + effect->velocity.z += effect->acceleration.z; + + if (effect->type == CLEAR_TAG_EFFECT_DEBRIS) { + // Clamp the velocity to -5.0 + if (effect->velocity.y < -5.0f) { + effect->velocity.y = -5.0f; + } + + // While the effect is falling check if it has hit the ground. + if (effect->velocity.y < 0.0f) { + sphereCenter = effect->position; + sphereCenter.y += 5.0f; + + // Check if the debris has hit the ground. + if (BgCheck_SphVsFirstPoly(&globalCtx->colCtx, &sphereCenter, 11.0f)) { + effect->position.y = originalYPosition; + + // Bounce the debris effect. + if (effect->bounces <= 0) { + effect->bounces++; + effect->velocity.y *= -0.5f; + effect->timer = ((s16)Rand_ZeroFloat(20)) + 25; + } else { + // The Debris effect is done bounding. Set it's velocity and acceleration to 0. + effect->velocity.y = 0.0f; + effect->acceleration.y = 0.0f; + effect->velocity.z = 0.0f; + effect->velocity.x = 0.0f; + } + } + } + + // Rotate the debris effect. + if (effect->acceleration.y != 0.0f) { + effect->rotationY += 0.5f; + effect->rotationX += 0.35f; + } + + // If the timer is completed, unload the debris effect. + if (effect->timer == 1) { + effect->type = CLEAR_TAG_EFFECT_AVAILABLE; + } + + // Spawn a fire effect every 3 frames. + if (effect->random >= 3) { + effect->random = 0; + EnClearTag_CreateFireEffect(globalCtx, &effect->position, effect->scale * 8.0f); + } + } else if (effect->type == CLEAR_TAG_EFFECT_FIRE) { + // Fade the fire effect. + Math_ApproachZeroF(&effect->primColor.a, 1.0f, 15.0f); + // If the fire effect is fully faded, unload it. + if (effect->primColor.a <= 0.0f) { + effect->type = CLEAR_TAG_EFFECT_AVAILABLE; + } + } else if (effect->type == CLEAR_TAG_EFFECT_SMOKE) { + // Fade the smoke effects. + Math_ApproachZeroF(&effect->primColor.r, 1.0f, 20.0f); + Math_ApproachZeroF(&effect->primColor.g, 1.0f, 2.0f); + Math_ApproachZeroF(&effect->envColor.r, 1.0f, 25.5f); + Math_ApproachZeroF(&effect->envColor.g, 1.0f, 21.5f); + Math_ApproachZeroF(&effect->envColor.b, 1.0f, 25.5f); + + // Smooth scale the smoke effects. + Math_ApproachF(&effect->scale, effect->maxScale, 0.05f, 0.1f); + + if (effect->primColor.r == 0.0f) { + // Fade the smoke effects. + Math_ApproachZeroF(&effect->primColor.a, 1.0f, 3.0f); + + // If the smoke effect has fully faded, unload it. + if (effect->primColor.a <= 0.0f) { + effect->type = CLEAR_TAG_EFFECT_AVAILABLE; + } + } + } else if (effect->type == CLEAR_TAG_EFFECT_FLASH) { + // Smooth scale the flash effects. + Math_ApproachF(&effect->scale, effect->maxScale, 1.0f, 3.0f); + // Fade the flash effects. + Math_ApproachZeroF(&effect->primColor.a, 1.0f, 10.0f); + + // If the flash effect has fully faded, unload it. + if (effect->primColor.a <= 0.0f) { + effect->type = CLEAR_TAG_EFFECT_AVAILABLE; + } + } + + if (effect->timer != 0) { + effect->timer--; + } + } + } +} + +/** + * Draws the Arwing effects. + * Each effect type is drawn before the next. The function will apply a material that applies to all effects of that + * type while drawing the first effect of that type. + */ +void EnClearTag_DrawEffects(GlobalContext* globalCtx2) { + GlobalContext* globalCtx; + GraphicsContext* gfxCtx; + u8 isMaterialApplied; + s16 i; + EnClearTagEffect* effect; + EnClearTagEffect* firstEffect; + + globalCtx = globalCtx2; + gfxCtx = globalCtx->state.gfxCtx; + + firstEffect = effect = (EnClearTagEffect*)globalCtx->specialEffects; + isMaterialApplied = false; + + OPEN_DISPS(gfxCtx, "../z_en_clear_tag.c", 1288); + func_80093D18(globalCtx->state.gfxCtx); + func_80093D84(globalCtx->state.gfxCtx); + + // Draw all Debris effects. + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_DEBRIS) { + // Apply the debris effect material if it has not already been applied. + if (!isMaterialApplied) { + isMaterialApplied++; + gSPDisplayList(POLY_OPA_DISP++, gArwingDebrisEffectMaterialDL); + } + + // Draw the debris effect. + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + Matrix_Scale(effect->scale, effect->scale, effect->scale, MTXMODE_APPLY); + Matrix_RotateY(effect->rotationY, MTXMODE_APPLY); + Matrix_RotateX(effect->rotationX, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1307), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gArwingDebrisEffectDL); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/func_809D3798.s") + // Draw all ground flash effects. + effect = firstEffect; + isMaterialApplied = false; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_FLASH) { + // Apply the flash ground effect material if it has not already been applied. + if (!isMaterialApplied) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 200, 0); + isMaterialApplied++; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/func_809D38BC.s") + // Draw the ground flash effect. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 200, (s8)effect->primColor.a); + Matrix_Translate(effect->position.x, effect->floorHeight, effect->position.z, MTXMODE_NEW); + Matrix_RotateX(effect->floorTangent.x, MTXMODE_APPLY); + Matrix_RotateZ(effect->floorTangent.z, MTXMODE_APPLY); + Matrix_Scale(effect->scale + effect->scale, 1.0f, effect->scale * 2.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1342), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingFlashEffectGroundDL); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/EnClearTag_Destroy.s") + // Draw all smoke effects. + effect = firstEffect; + isMaterialApplied = false; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_SMOKE) { + // Apply the smoke effect material if it has not already been applied. + if (!isMaterialApplied) { + gSPDisplayList(POLY_XLU_DISP++, gArwingFireEffectMaterialDL); + isMaterialApplied++; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/EnClearTag_Init.s") + // Draw the smoke effect. + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, (s8)effect->envColor.r, (s8)effect->envColor.g, (s8)effect->envColor.b, + 128); + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, (s8)effect->primColor.r, (s8)effect->primColor.g, + (s8)effect->primColor.b, (s8)effect->primColor.a); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, effect->random * -5, 32, 64, 1, 0, 0, 32, 32)); + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + func_800D1FD4(&globalCtx->mf_11DA0); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + Matrix_Translate(0.0f, 20.0f, 0.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1392), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingFireEffectDL); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/func_809D3B80.s") + // Draw all fire effects. + effect = firstEffect; + isMaterialApplied = false; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_FIRE) { + // Apply the fire effect material if it has not already been applied. + if (!isMaterialApplied) { + gSPDisplayList(POLY_XLU_DISP++, gArwingFireEffectMaterialDL); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 215, 255, 128); + isMaterialApplied++; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/EnClearTag_Update.s") + // Draw the fire effect. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 200, 20, 0, (s8)effect->primColor.a); + gSPSegment(POLY_XLU_DISP++, 8, + Gfx_TwoTexScroll(globalCtx->state.gfxCtx, 0, 0, (effect->random * -15) & 0xFF, 32, 64, 1, 0, 0, + 32, 32)); + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + // using globalCtx2 here fixes saved reg usage, but will need to be changed when switching to GameState + func_800D1FD4(&globalCtx2->mf_11DA0); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1439), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingFireEffectDL); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/EnClearTag_Draw.s") + // Draw all flash billboard effects. + effect = firstEffect; + isMaterialApplied = false; + for (i = 0; i < CLEAR_TAG_EFFECT_MAX_COUNT; i++, effect++) { + if (effect->type == CLEAR_TAG_EFFECT_FLASH) { + // Apply the flash billboard effect material if it has not already been applied. + if (!isMaterialApplied) { + gDPPipeSync(POLY_XLU_DISP++); + gDPSetEnvColor(POLY_XLU_DISP++, 255, 255, 200, 0); + isMaterialApplied++; + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/func_809D5004.s") + // Draw the flash billboard effect. + gDPSetPrimColor(POLY_XLU_DISP++, 0, 0, 255, 255, 200, (s8)effect->primColor.a); + Matrix_Translate(effect->position.x, effect->position.y, effect->position.z, MTXMODE_NEW); + func_800D1FD4(&globalCtx->mf_11DA0); + Matrix_Scale(effect->scale, effect->scale, 1.0f, MTXMODE_APPLY); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(gfxCtx, "../z_en_clear_tag.c", 1470), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_XLU_DISP++, gArwingFlashEffectDL); + } + } -#pragma GLOBAL_ASM("asm/non_matchings/overlays/actors/ovl_En_Clear_Tag/func_809D53CC.s") + CLOSE_DISPS(gfxCtx, "../z_en_clear_tag.c", 1477); +} diff --git a/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h b/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h index 28221ef39..1b24e0fe5 100644 --- a/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h +++ b/src/overlays/actors/ovl_En_Clear_Tag/z_en_clear_tag.h @@ -6,11 +6,92 @@ struct EnClearTag; +typedef enum { + /* 0x00 */ CLEAR_TAG_CUTSCENE_ARWING = 0, + /* 0x01 */ CLEAR_TAG_ARWING = 1, + /* 0x64 */ CLEAR_TAG_LASER = 100 +} ClearTagType; + +typedef enum { + /* 0x00 */ CLEAR_TAG_STATE_FLYING = 0, + /* 0x01 */ CLEAR_TAG_STATE_TARGET_LOCKED = 1, + /* 0x02 */ CLEAR_TAG_STATE_DEMO = 2, + /* 0x0A */ CLEAR_TAG_STATE_CRASHING = 10, + /* 0x64 */ CLEAR_TAG_STATE_LASER = 100 +} ClearTagState; + +typedef enum { + /* 0x00 */ CLEAR_TAG_CUTSCENE_MODE_NONE, + /* 0x01 */ CLEAR_TAG_CUTSCENE_MODE_SETUP, + /* 0x02 */ CLEAR_TAG_CUTSCENE_MODE_PLAY +} ClearTagDemoMode; + +typedef enum { + /* 0x00 */ CLEAR_TAG_DRAW_MODE_ARWING, + /* 0x01 */ CLEAR_TAG_DRAW_MODE_ALL, + /* 0x02 */ CLEAR_TAG_DRAW_MODE_EFFECT +} ClearTagDrawMode; + +typedef enum { + /* 0x00 */ CLEAR_TAG_EFFECT_AVAILABLE, + /* 0x01 */ CLEAR_TAG_EFFECT_DEBRIS, + /* 0x02 */ CLEAR_TAG_EFFECT_FIRE, + /* 0x03 */ CLEAR_TAG_EFFECT_SMOKE, + /* 0x04 */ CLEAR_TAG_EFFECT_FLASH +} ClearTagEffectType; + +typedef enum { + /* 0x00 */ CLEAR_TAG_TIMER_ARWING_UPDATE_STATE = 0, + /* 0x00 */ CLEAR_TAG_TIMER_LASER_DEATH = 0, + /* 0x01 */ CLEAR_TAG_TIMER_ARWING_ENTER_LOCKED_ON, + /* 0x02 */ CLEAR_TAG_TIMER_ARWING_UPDATE_BG_INFO, + /* 0x03 */ CLEAR_TAG_TIMER_COUNT +} ClearTagTimers; + typedef struct EnClearTag { /* 0x0000 */ Actor actor; - /* 0x014C */ char unk_14C[0xB8]; + /* 0x014C */ u8 shouldExplode; + /* 0x014D */ u8 drawMode; + /* 0x014E */ u8 state; + /* 0x0150 */ s16 work[CLEAR_TAG_TIMER_COUNT]; + /* 0x0158 */ Vec3f targetPosition; + /* 0x0164 */ Vec3f targetDirection; + /* 0x0170 */ Vec3f acceleration; + /* 0x017C */ u8 timer; + /* 0x017D */ u8 shouldShootLaser; + /* 0x0180 */ f32 roll; + /* 0x0184 */ s16 crashingTimer; + /* 0x0186 */ s16 deathTimer; + /* 0x0188 */ Vec3f floorTangent; + /* 0x0194 */ ColliderCylinder collider; + /* 0x01E0 */ u8 cutsceneMode; + /* 0x01E2 */ s16 cameraId; + /* 0x01E4 */ Vec3f cutsceneCameraAt; + /* 0x01F0 */ Vec3f cutsceneCameraEye; + /* 0x01FC */ s16 cutsceneTimer; + /* 0x01FE */ char unk_1FE[0x06]; } EnClearTag; // size = 0x0204 +typedef struct EnClearTagEffect { + /* 0x0000 */ u8 type; + /* 0x0001 */ u8 random; + /* 0x0004 */ Vec3f position; + /* 0x0010 */ Vec3f velocity; + /* 0x001C */ Vec3f acceleration; + /* 0x0028 */ Color_RGBAf primColor; + /* 0x0038 */ Color_RGBAf envColor; + /* 0x0048 */ s16 bounces; + /* 0x004A */ s16 timer; + /* 0x004C */ f32 scale; + /* 0x0050 */ f32 maxScale; + /* 0x0054 */ f32 rotationY; + /* 0x0058 */ f32 rotationX; + /* 0x005C */ f32 floorHeight; + /* 0x0060 */ Vec3f floorTangent; +} EnClearTagEffect; // size = 0x6C + +#define CLEAR_TAG_EFFECT_MAX_COUNT 100 + extern const ActorInit En_Clear_Tag_InitVars; #endif |
