892 lines
30 KiB
C
892 lines
30 KiB
C
/*
|
|
* 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 <overlays/actors/ovl_En_Arrow/z_en_arrow.h>
|
|
#include "overlays/actors/ovl_En_Si/z_en_si.h"
|
|
#include <objects/object_link_child/object_link_child.h>
|
|
#include <overlays/actors/ovl_En_Bom/z_en_bom.h>
|
|
#include <overlays/actors/ovl_Obj_Switch/z_obj_switch.h>
|
|
|
|
#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);
|
|
}
|
|
}
|