/* * File: z_en_partner.c * Overlay: ovl_En_Partner * Description: Navi Coop Partner */ #include "z_en_partner.h" #include "objects/gameplay_keep/gameplay_keep.h" #include #include "overlays/actors/ovl_En_Si/z_en_si.h" #include #include #include #define FLAGS (ACTOR_FLAG_UPDATE_WHILE_CULLED | ACTOR_FLAG_DRAW_WHILE_CULLED | ACTOR_FLAG_DRAGGED_BY_HOOKSHOT | ACTOR_FLAG_CAN_PRESS_SWITCH) void EnPartner_Init(Actor* thisx, PlayState* play); void EnPartner_Destroy(Actor* thisx, PlayState* play); void EnPartner_Update(Actor* thisx, PlayState* play); void EnPartner_Draw(Actor* thisx, PlayState* play); void EnPartner_SpawnSparkles(EnPartner* this, PlayState* play, s32 sparkleLife); void func_808328EC(Player* this, u16 sfxId); void Player_RequestQuake(PlayState* play, s32 speed, s32 y, s32 countdown); s32 spawn_boomerang_ivan(EnPartner* this, PlayState* play); static InitChainEntry sInitChain[] = { ICHAIN_VEC3F_DIV1000(scale, 8, ICHAIN_STOP), }; static Color_RGBAf sInnerColors[] = { { 255.0f, 255.0f, 255.0f, 255.0f }, }; static Color_RGBAf sOuterColors[] = { { 0.0f, 255.0f, 0.0f, 255.0f }, }; static ColliderCylinderInit sCylinderInit = { { COLTYPE_NONE, AT_ON | AT_TYPE_PLAYER, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_2, COLSHAPE_CYLINDER, }, { ELEMTYPE_UNK0, { 0x00020002, 0x00, 0x01 }, { 0x4FC00748, 0x00, 0x00 }, TOUCH_ON | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, }, { 12, 27, 0, { 0, 0, 0 } }, }; static CollisionCheckInfoInit sCCInfoInit = { 0, 12, 60, MASS_HEAVY }; void EnPartner_Init(Actor* thisx, PlayState* play) { EnPartner* this = (EnPartner*)thisx; s32 pad; Player* player = GET_PLAYER(play); s32 i; this->usedItem = 0xFF; this->canMove = 1; this->shouldDraw = 1; this->hookshotTarget = NULL; GET_PLAYER(play)->ivanFloating = 0; this->innerColor.r = 255.0f; this->innerColor.g = 255.0f; this->innerColor.b = 255.0f; this->innerColor.a = 255.0f; this->outerColor.r = 0.0f; this->outerColor.g = 255.0f; this->outerColor.b = 0.0f; this->outerColor.a = 255.0f; this->usedItemButton = 0xFF; Collider_InitCylinder(play, &this->collider); Collider_SetCylinder(play, &this->collider, &this->actor, &sCylinderInit); Collider_UpdateCylinder(&this->actor, &this->collider); CollisionCheck_SetInfo(&this->actor.colChkInfo, NULL, &sCCInfoInit); this->actor.colChkInfo.mass = MASS_HEAVY; this->collider.base.ocFlags1 |= OC1_TYPE_PLAYER; this->collider.info.toucher.damage = 1; GET_PLAYER(play)->ivanDamageMultiplier = 1; Actor_ProcessInitChain(thisx, sInitChain); SkelAnime_Init(play, &this->skelAnime, &gFairySkel, &gFairyAnim, this->jointTable, this->morphTable, 15); ActorShape_Init(&thisx->shape, 1000.0f, ActorShadow_DrawCircle, 15.0f); thisx->shape.shadowAlpha = 0xFF; Lights_PointGlowSetInfo(&this->lightInfoGlow, thisx->world.pos.x, thisx->world.pos.y, thisx->world.pos.z, 200, 255, 200, 0); this->lightNodeGlow = LightContext_InsertLight(play, &play->lightCtx, &this->lightInfoGlow); Lights_PointNoGlowSetInfo(&this->lightInfoNoGlow, thisx->world.pos.x, thisx->world.pos.y, thisx->world.pos.z, 200, 255, 200, 0); this->lightNodeNoGlow = LightContext_InsertLight(play, &play->lightCtx, &this->lightInfoNoGlow); thisx->room = -1; } void EnPartner_Destroy(Actor* thisx, PlayState* play) { s32 pad; EnPartner* this = (EnPartner*)thisx; LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeGlow); LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeNoGlow); Collider_DestroyCylinder(play, &this->collider); ResourceMgr_UnregisterSkeleton(&this->skelAnime); } void EnPartner_UpdateLights(EnPartner* this, PlayState* play) { s16 glowLightRadius = 100; s16 lightRadius = 200; if (this->shouldDraw == 0) { glowLightRadius = 0; lightRadius = 0; } Player* player; player = GET_PLAYER(play); Lights_PointNoGlowSetInfo(&this->lightInfoNoGlow, player->actor.world.pos.x, (s16)(player->actor.world.pos.y) + 69, player->actor.world.pos.z, 200, 255, 200, lightRadius); Lights_PointGlowSetInfo(&this->lightInfoGlow, this->actor.world.pos.x, this->actor.world.pos.y + 9, this->actor.world.pos.z, 200, 255, 200, glowLightRadius); Actor_SetScale(&this->actor, this->actor.scale.x); } void EnPartner_SpawnSparkles(EnPartner* this, PlayState* play, s32 sparkleLife) { static Vec3f sparkleVelocity = { 0.0f, -0.05f, 0.0f }; static Vec3f sparkleAccel = { 0.0f, -0.025f, 0.0f }; s32 pad; Vec3f sparklePos; Color_RGBA8 primColor; Color_RGBA8 envColor; sparklePos.x = Rand_CenteredFloat(6.0f) + this->actor.world.pos.x; sparklePos.y = (Rand_ZeroOne() * 6.0f) + this->actor.world.pos.y + 5; sparklePos.z = Rand_CenteredFloat(6.0f) + this->actor.world.pos.z; primColor.r = this->innerColor.r; primColor.g = this->innerColor.g; primColor.b = this->innerColor.b; envColor.r = this->outerColor.r; envColor.g = this->outerColor.g; envColor.b = this->outerColor.b; EffectSsKiraKira_SpawnDispersed(play, &sparklePos, &sparkleVelocity, &sparkleAccel, &primColor, &envColor, 1500, sparkleLife); } Vec3f Vec3fNormalize(Vec3f vec) { f32 norm = sqrt((vec.x * vec.x) + (vec.y * vec.y) + (vec.z * vec.z)); if (norm != 0.0f) { vec.x /= norm; vec.y /= norm; vec.z /= norm; } else { vec.x = vec.y = vec.z = 0.0f; } return vec; } void CenterIvanOnLink(Actor* thisx, PlayState* play) { EnPartner* this = (EnPartner*)thisx; this->actor.world.pos = GET_PLAYER(play)->actor.world.pos; this->actor.world.pos.y += Player_GetHeight(GET_PLAYER(play)) + 5.0f; } static u8 magicArrowCosts[] = { 0, 4, 4, 8 }; void UseBow(Actor* thisx, PlayState* play, u8 started, u8 arrowType) { EnPartner* this = (EnPartner*)thisx; if (started == 1) { func_808328EC(this, NA_SE_PL_CHANGE_ARMS); this->canMove = 0; } else if (started == 0) { if (this->itemTimer <= 0) { if (AMMO(ITEM_BOW) > 0) { if (arrowType >= 1 && !Magic_RequestChange(play, magicArrowCosts[arrowType], MAGIC_CONSUME_NOW)) { func_80078884(NA_SE_SY_ERROR); this->canMove = 1; return; } this->itemTimer = 10; Actor* newarrow = Actor_SpawnAsChild( &play->actorCtx, &this->actor, play, ACTOR_EN_ARROW, this->actor.world.pos.x, this->actor.world.pos.y + 7, this->actor.world.pos.z, 0, this->actor.world.rot.y, 0, ARROW_NORMAL); switch (arrowType) { case 1: newarrow->params = ARROW_FIRE; break; case 2: newarrow->params = ARROW_ICE; break; case 3: newarrow->params = ARROW_LIGHT; break; } GET_PLAYER(play)->unk_A73 = 4; newarrow->parent = NULL; Inventory_ChangeAmmo(ITEM_BOW, -1); } } } } void UseSlingshot(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (started == 1) { func_808328EC(this, NA_SE_PL_CHANGE_ARMS); this->canMove = 0; } else if (started == 0) { if (this->itemTimer <= 0) { if (AMMO(ITEM_SLINGSHOT) > 0) { this->itemTimer = 10; Actor* newarrow = Actor_SpawnAsChild( &play->actorCtx, &this->actor, play, ACTOR_EN_ARROW, this->actor.world.pos.x, this->actor.world.pos.y + 7, this->actor.world.pos.z, 0, this->actor.world.rot.y, 0, ARROW_SEED); GET_PLAYER(play)->unk_A73 = 4; newarrow->parent = NULL; Inventory_ChangeAmmo(ITEM_SLINGSHOT, -1); } else { func_80078884(NA_SE_SY_ERROR); } } } } void UseBombs(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { if (AMMO(ITEM_BOMB) > 0 && play->actorCtx.actorLists[ACTORCAT_EXPLOSIVE].length < 3) { this->itemTimer = 10; Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, this->actor.world.pos.x, this->actor.world.pos.y + 7, this->actor.world.pos.z, 0, 0, 0, 0, false); Inventory_ChangeAmmo(ITEM_BOMB, -1); } else { func_80078884(NA_SE_SY_ERROR); } } } } static Vec3f D_80854A40 = { 0.0f, 40.0f, 45.0f }; void UseHammer(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { this->itemTimer = 10; static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; Vec3f shockwavePos = this->actor.world.pos; Player_RequestQuake(play, 27767, 7, 20); Player_PlaySfx(&this->actor, NA_SE_IT_HAMMER_HIT); EffectSsBlast_SpawnWhiteShockwave(play, &shockwavePos, &zeroVec, &zeroVec); if (this->actor.xzDistToPlayer < 100.0f && this->actor.yDistToPlayer < 35.0f) { func_8002F71C(play, &this->actor, 8.0f, this->actor.yawTowardsPlayer, 8.0f); } } } } void UseBombchus(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { if (AMMO(ITEM_BOMBCHU) > 0) { this->itemTimer = 10; EnBom* bomb = Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, this->actor.world.pos.x, this->actor.world.pos.y + 7, this->actor.world.pos.z, 0, 0, 0, 0, false); bomb->timer = 0; Inventory_ChangeAmmo(ITEM_BOMBCHU, -1); } else { func_80078884(NA_SE_SY_ERROR); } } } } static Vec3f D_808547A4 = { 0.0f, 0.5f, 0.0f }; static Vec3f D_808547B0 = { 0.0f, 0.5f, 0.0f }; static Color_RGBA8 D_808547BC = { 255, 255, 100, 255 }; static Color_RGBA8 D_808547C0 = { 255, 50, 0, 0 }; void UseDekuStick(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { if (AMMO(ITEM_STICK) > 0) { func_808328EC(this, NA_SE_EV_FLAME_IGNITION); } else { func_80078884(NA_SE_SY_ERROR); } } if (started == 2) { if (AMMO(ITEM_STICK) > 0) { this->stickWeaponInfo.tip = this->actor.world.pos; this->stickWeaponInfo.tip.y += 7.0f; func_8002836C(play, &this->stickWeaponInfo.tip, &D_808547A4, &D_808547B0, &D_808547BC, &D_808547C0, 200.0f, 0, 8); CollisionCheck_SetAT(play, &play->colChkCtx, &this->collider.base); if (this->damageTimer <= 0) { Inventory_ChangeAmmo(ITEM_STICK, -1); this->damageTimer = 20; } else { this->damageTimer--; } } } } } void UseNuts(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { if (AMMO(ITEM_NUT) > 0) { this->itemTimer = 10; Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ARROW, this->actor.world.pos.x, this->actor.world.pos.y + 7, this->actor.world.pos.z, 0x1000, this->actor.world.rot.y, 0, ARROW_NUT, false); Inventory_ChangeAmmo(ITEM_NUT, -1); } else { func_80078884(NA_SE_SY_ERROR); } } } } void UseHookshot(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { func_808328EC(this, NA_SE_PL_CHANGE_ARMS); this->canMove = 0; this->hookshotTarget = Actor_SpawnAsChild(&play->actorCtx, &this->actor, play, ACTOR_OBJ_HSBLOCK, this->actor.world.pos.x, this->actor.world.pos.y + 7.5f, this->actor.world.pos.z, this->actor.world.rot.x, this->actor.world.rot.y, this->actor.world.rot.z, 2); this->hookshotTarget->scale.x = 0.05f; this->hookshotTarget->scale.y = 0.05f; this->hookshotTarget->scale.z = 0.05f; } else if (started == 0) { Actor_Kill(this->hookshotTarget); this->hookshotTarget = NULL; func_808328EC(this, NA_SE_PL_CHANGE_ARMS); this->canMove = 1; } else if (started == 2) { this->hookshotTarget->shape.rot.y = this->actor.world.rot.y; } } } void UseOcarina(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { Audio_PlaySoundTransposed(&this->actor.projectedPos, NA_SE_VO_NA_HELLO_2, -6); } } } void UseBoomerang(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { this->itemTimer = 20; spawn_boomerang_ivan(&this->actor, play); } } } void UseLens(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { func_80078884(NA_SE_SY_GLASSMODE_ON); this->shouldDraw = 0; } if (started == 0) { func_80078884(NA_SE_SY_GLASSMODE_OFF); this->shouldDraw = 1; } } } void UseBeans(Actor* thisx, PlayState* play, u8 started) { EnPartner* this = (EnPartner*)thisx; if (this->itemTimer <= 0) { if (started == 1) { this->entry = ItemTable_Retrieve(GI_BEAN); if (play->actorCtx.titleCtx.alpha <= 0) { if (gSaveContext.rupees >= 100 && GiveItemEntryWithoutActor(play, this->entry)) { Rupees_ChangeBy(-100); } else { func_80078884(NA_SE_SY_ERROR); } } } } } void UseSpell(Actor* thisx, PlayState* play, u8 started, u8 spellType) { EnPartner* this = (EnPartner*)thisx; if (gSaveContext.magic > 0) { if (this->itemTimer <= 0 && this->usedSpell == 0) { if (started == 1) { this->usedSpell = spellType; } } if (started == 0 && this->usedSpell != 0) { this->itemTimer = 10; gSaveContext.magicState = MAGIC_STATE_RESET; switch (this->usedSpell) { case 1: GET_PLAYER(play)->ivanDamageMultiplier = 1; break; case 3: GET_PLAYER(play)->ivanFloating = 0; break; } this->usedSpell = 0; } if (started == 2) { if (this->usedSpell != 0) { Vec3f spE4[3]; Vec3f newBasePos[3]; switch (this->usedSpell) { case 1: //Din's GET_PLAYER(play)->ivanDamageMultiplier = 2; break; case 2: //Nayru's GET_PLAYER(play)->invincibilityTimer = -10; break; case 3: //Farore's GET_PLAYER(play)->hoverBootsTimer = 10; GET_PLAYER(play)->ivanFloating = 1; break; } gSaveContext.magicState = MAGIC_STATE_METER_FLASH_1; this->magicTimer--; if (this->magicTimer <= 0) { gSaveContext.magic--; this->magicTimer = 20; if (gSaveContext.magic <= 0) { gSaveContext.magic = 0; this->itemTimer = 10; this->usedSpell = 0; gSaveContext.magicState = MAGIC_STATE_RESET; } } } } } } void UseItem(uint8_t usedItem, u8 started, Actor* thisx, PlayState* play) { EnPartner* this = (EnPartner*)thisx; if (this->usedItem != 0xFF && this->itemTimer <= 0) { switch (usedItem) { case ITEM_STICK: UseDekuStick(this, play, started); break; case ITEM_BOMB: UseBombs(this, play, started); break; case ITEM_BOMBCHU: UseBombchus(this, play, started); break; case ITEM_NUT: UseNuts(this, play, started); break; case ITEM_BOW: UseBow(this, play, started, 0); break; case ITEM_ARROW_FIRE: UseBow(this, play, started, 1); break; case ITEM_ARROW_ICE: UseBow(this, play, started, 2); break; case ITEM_ARROW_LIGHT: UseBow(this, play, started, 3); break; case ITEM_SLINGSHOT: UseSlingshot(this, play, started); break; case ITEM_OCARINA_FAIRY: case ITEM_OCARINA_TIME: UseOcarina(this, play, started); break; case ITEM_HOOKSHOT: case ITEM_LONGSHOT: UseHookshot(this, play, started); break; case ITEM_DINS_FIRE: UseSpell(this, play, started, 1); break; case ITEM_NAYRUS_LOVE: UseSpell(this, play, started, 2); break; case ITEM_FARORES_WIND: UseSpell(this, play, started, 3); break; case ITEM_HAMMER: UseHammer(this, play, started); break; case ITEM_BOOMERANG: UseBoomerang(this, play, started); break; case ITEM_LENS: UseLens(this, play, started); break; case ITEM_BEAN: UseBeans(this, play, started); break; } } if (started == 0) { this->usedItem = 0xFF; } } void EnPartner_Update(Actor* thisx, PlayState* play) { s32 pad; EnPartner* this = (EnPartner*)thisx; Input sControlInput = play->state.input[this->actor.params]; f32 relX = sControlInput.cur.stick_x / 10.0f * (CVarGetInteger("gMirroredWorld", 0) ? -1 : 1); f32 relY = sControlInput.cur.stick_y / 10.0f; Vec3f camForward = { GET_ACTIVE_CAM(play)->at.x - GET_ACTIVE_CAM(play)->eye.x, 0.0f, GET_ACTIVE_CAM(play)->at.z - GET_ACTIVE_CAM(play)->eye.z }; camForward = Vec3fNormalize(camForward); Vec3f camRight = { -camForward.z, 0.0f, camForward.x }; this->actor.velocity.x = 0; this->actor.velocity.y = 0; this->actor.velocity.z = 0; this->actor.velocity.x += camRight.x * relX; this->actor.velocity.z += camRight.z * relX; this->actor.velocity.x += camForward.x * relY; this->actor.velocity.z += camForward.z * relY; if (this->actor.velocity.x != 0 || this->actor.velocity.z != 0) { int16_t finalDir = Math_Atan2S(-this->actor.velocity.x, this->actor.velocity.z) - 0x4000; Math_SmoothStepToS(&this->actor.world.rot.y, finalDir, 2, 10000, 0); Math_SmoothStepToS(&this->actor.shape.rot.y, finalDir, 2, 10000, 0); } if (!this->canMove) { relX = 0; relY = 0; } Math_SmoothStepToF(&this->actor.speedXZ, sqrtf(SQ(relX) + SQ(relY)), 1.0f, 1.3f, 0.0f); if (this->shouldDraw == 1) { thisx->shape.shadowAlpha = 0xFF; EnPartner_SpawnSparkles(this, play, 12); } else { thisx->shape.shadowAlpha = 0; } if (CHECK_BTN_ALL(sControlInput.cur.button, BTN_A) && this->canMove) { Math_SmoothStepToF(&this->yVelocity, 6.0f, 1.0f, 1.5f, 0.0f); } else if (CHECK_BTN_ALL(sControlInput.cur.button, BTN_B) && this->canMove) { Math_SmoothStepToF(&this->yVelocity, -6.0f, 1.0f, 1.5f, 0.0f); } else { Math_SmoothStepToF(&this->yVelocity, 0.0f, 1.0f, 1.5f, 0.0f); } this->actor.gravity = this->yVelocity; if (this->canMove == 1) { Actor_MoveForward(&this->actor); Actor_UpdateBgCheckInfo(play, &this->actor, 19.0f, 20.0f, 0.0f, 5); } if (this->usedSpell != 0) { func_8002F974(thisx, NA_SE_PL_MAGIC_SOUL_NORMAL - SFX_FLAG); } if (!Player_InCsMode(play)) { // Collect drops & rupees Actor* itemActor = play->actorCtx.actorLists[ACTORCAT_MISC].head; while (itemActor != NULL) { if (itemActor->id == ACTOR_EN_ITEM00) { if (itemActor->params == ITEM00_RUPEE_GREEN || itemActor->params == ITEM00_RUPEE_BLUE || itemActor->params == ITEM00_RUPEE_RED || itemActor->params == ITEM00_RUPEE_PURPLE || itemActor->params == ITEM00_RUPEE_ORANGE || itemActor->params == ITEM00_HEART || itemActor->params == ITEM00_BOMBS_A || itemActor->params == ITEM00_BOMBS_B || itemActor->params == ITEM00_ARROWS_SINGLE || itemActor->params == ITEM00_ARROWS_SMALL || itemActor->params == ITEM00_ARROWS_MEDIUM || itemActor->params == ITEM00_ARROWS_LARGE || itemActor->params == ITEM00_BOMBCHU || itemActor->params == ITEM00_MAGIC_SMALL || itemActor->params == ITEM00_MAGIC_LARGE || itemActor->params == ITEM00_NUTS || itemActor->params == ITEM00_STICK) { f32 distanceToObject = Actor_WorldDistXYZToActor(&this->actor, itemActor); if (distanceToObject <= 20.0f) { itemActor->world.pos = GET_PLAYER(play)->actor.world.pos; break; } } } itemActor = itemActor->next; } itemActor = play->actorCtx.actorLists[ACTORCAT_ITEMACTION].head; while (itemActor != NULL) { if (itemActor->id == ACTOR_EN_SI) { f32 distanceToObject = Actor_WorldDistXYZToActor(&this->actor, itemActor); if (distanceToObject <= 20.0f) { EnSi* ensi = (EnSi*)itemActor; ensi->collider.base.ocFlags2 = OC2_HIT_PLAYER; break; } } itemActor = itemActor->next; } } if (this->itemTimer > 0) { this->itemTimer--; if (this->itemTimer <= 0) { this->canMove = 1; } } if (!Player_InCsMode(play)) { uint8_t pressed = 0; uint8_t released = 0; uint8_t current = 0; uint16_t partnerButtons[7] = { BTN_CLEFT, BTN_CDOWN, BTN_CRIGHT, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT}; uint8_t buttonMax = 3; if (CVarGetInteger("gDpadEquips", 0) != 0) { buttonMax = ARRAY_COUNT(gSaveContext.equips.cButtonSlots); } if (this->usedItem == 0xFF && this->itemTimer <= 0) { for (uint8_t i = 0; i < buttonMax; i++) { if (CHECK_BTN_ALL(sControlInput.press.button, partnerButtons[i])) { this->usedItem = gSaveContext.equips.buttonItems[i+1]; this->usedItemButton = i; pressed = 1; } } } if (this->usedItem != 0xFF) { for (uint8_t i = 0; i < buttonMax; i++) { if (CHECK_BTN_ALL(sControlInput.cur.button, partnerButtons[i]) && this->usedItemButton == i) { current = 1; } } } if (this->usedItem != 0xFF) { for (uint8_t i = 0; i < buttonMax; i++) { if (CHECK_BTN_ALL(sControlInput.rel.button, partnerButtons[i]) && this->usedItemButton == i) { released = 1; } } } if (pressed == 1) { UseItem(this->usedItem, 1, this, play); } else if (released == 1) { UseItem(this->usedItem, 0, this, play); this->usedItemButton = 0xFF; } else if (current == 1) { UseItem(this->usedItem, 2, this, play); } } else { UseItem(this->usedItem, 0, this, play); this->usedItem = 0xFF; this->itemTimer = 10; } if (CHECK_BTN_ALL(sControlInput.press.button, BTN_Z) && this->canMove) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_FAIRY_DASH); } if (CHECK_BTN_ALL(sControlInput.cur.button, BTN_Z) && this->canMove) { CenterIvanOnLink(this, play); } else if (this->canMove == 1 && this->hookshotTarget == NULL) { Collider_UpdateCylinder(&this->actor, &this->collider); CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); } if (CVarGetInteger(CVAR_COSMETIC("Ivan.IdlePrimary.Changed"), 0)) { Color_RGB8 ivanColor1 = CVarGetColor24(CVAR_COSMETIC("Ivan.IdlePrimary.Value"), (Color_RGB8){ 255, 255, 255 }); this->innerColor.r = ivanColor1.r; this->innerColor.g = ivanColor1.g; this->innerColor.b = ivanColor1.b; } else { this->innerColor.r = 255; this->innerColor.g = 255; this->innerColor.b = 255; } if (CVarGetInteger(CVAR_COSMETIC("Ivan.IdleSecondary.Changed"), 0)) { Color_RGB8 ivanColor2 = CVarGetColor24(CVAR_COSMETIC("Ivan.IdleSecondary.Value"), (Color_RGB8){ 0, 255, 0 }); this->outerColor.r = ivanColor2.r; this->outerColor.g = ivanColor2.g; this->outerColor.b = ivanColor2.b; } else { this->outerColor.r = 0; this->outerColor.g = 255; this->outerColor.b = 0; } SkelAnime_Update(&this->skelAnime); EnPartner_UpdateLights(this, play); } s32 EnPartner_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot, void* thisx, Gfx** gfx) { static Vec3f zeroVec = { 0.0f, 0.0f, 0.0f }; s32 pad; f32 scale; Vec3f mtxMult; EnPartner* this = (EnPartner*)thisx; if (limbIndex == 8) { scale = ((Math_SinS(4096) * 0.1f) + 1.0f) * 0.012f; scale *= (this->actor.scale.x * 124.99999f); Matrix_MultVec3f(&zeroVec, &mtxMult); Matrix_Translate(mtxMult.x, mtxMult.y, mtxMult.z, MTXMODE_NEW); Matrix_Scale(scale, scale, scale, MTXMODE_APPLY); } return false; } void DrawOrb(Actor* thisx, PlayState* play, u8 color) { EnPartner* this = (EnPartner*)thisx; Vec3f pos; Player* player = GET_PLAYER(play); s32 pad; f32 sp6C = play->state.frames & 0x1F; pos = this->actor.world.pos; pos.y += 5.0f; pos.x -= (this->actor.scale.x * 300.0f * Math_SinS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play))) * Math_CosS(Camera_GetCamDirPitch(GET_ACTIVE_CAM(play)))); pos.y -= (this->actor.scale.x * 300.0f * Math_SinS(Camera_GetCamDirPitch(GET_ACTIVE_CAM(play)))); pos.z -= (this->actor.scale.x * 300.0f * Math_CosS(Camera_GetCamDirYaw(GET_ACTIVE_CAM(play))) * Math_CosS(Camera_GetCamDirPitch(GET_ACTIVE_CAM(play)))); OPEN_DISPS(play->state.gfxCtx); Gfx_SetupDL_25Xlu(play->state.gfxCtx); gDPSetPrimColor(POLY_XLU_DISP++, 0, 0x80, 255, 255, 255, 255); switch (color) { case 1: gDPSetEnvColor(POLY_XLU_DISP++, 255, 0, 0, 255); break; case 2: gDPSetEnvColor(POLY_XLU_DISP++, 0, 0, 255, 255); break; case 3: gDPSetEnvColor(POLY_XLU_DISP++, 0, 255, 0, 255); break; } Matrix_Translate(pos.x, pos.y, pos.z, MTXMODE_NEW); Matrix_Scale(this->actor.scale.x * 3.0f, this->actor.scale.y * 3.0f, this->actor.scale.z * 3.0f, MTXMODE_APPLY); Matrix_Mult(&play->billboardMtxF, MTXMODE_APPLY); Matrix_Push(); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); Matrix_RotateZ(sp6C * (M_PI / 32), MTXMODE_APPLY); gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); Matrix_Pop(); Matrix_RotateZ(-sp6C * (M_PI / 32), MTXMODE_APPLY); gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gSPDisplayList(POLY_XLU_DISP++, gEffFlash1DL); CLOSE_DISPS(play->state.gfxCtx); } void EnPartner_Draw(Actor* thisx, PlayState* play) { s32 pad; f32 alphaScale; s32 envAlpha; EnPartner* this = (EnPartner*)thisx; s32 pad1; Gfx* dListHead; Player* player = GET_PLAYER(play); if (play->pauseCtx.state != 0 && this->usedItem != 0xFF) { UseItem(this->usedItem, 0, this, play); this->usedItem = 0xFF; } if (this->shouldDraw == 0) { return; } dListHead = Graph_Alloc(play->state.gfxCtx, sizeof(Gfx) * 4); OPEN_DISPS(play->state.gfxCtx); Gfx_SetupDL_27Xlu(play->state.gfxCtx); envAlpha = (50) & 0x1FF; envAlpha = (envAlpha > 255) ? 511 - envAlpha : envAlpha; alphaScale = 1.0f; gSPSegment(POLY_XLU_DISP++, 0x08, dListHead); gDPPipeSync(dListHead++); gDPSetPrimColor(dListHead++, 0, 0x01, (u8)this->innerColor.r, (u8)this->innerColor.g, (u8)this->innerColor.b, (u8)(this->innerColor.a * alphaScale)); gDPSetRenderMode(dListHead++, G_RM_PASS, G_RM_ZB_CLD_SURF2); gSPEndDisplayList(dListHead++); gDPSetEnvColor(POLY_XLU_DISP++, (u8)this->outerColor.r, (u8)this->outerColor.g, (u8)this->outerColor.b, (u8)(envAlpha * alphaScale)); POLY_XLU_DISP = SkelAnime_Draw(play, this->skelAnime.skeleton, this->skelAnime.jointTable, EnPartner_OverrideLimbDraw, NULL, this, POLY_XLU_DISP); CLOSE_DISPS(play->state.gfxCtx); if (this->usedSpell > 0) { DrawOrb(this, play, this->usedSpell); } }