Shipwright/soh/src/overlays/actors/ovl_En_Ossan/z_en_ossan.c

2539 lines
100 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "z_en_ossan.h"
#include "vt.h"
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/object_ossan/object_ossan.h"
#include "objects/object_oF1d_map/object_oF1d_map.h"
#include "objects/object_os/object_os.h"
#include "objects/object_zo/object_zo.h"
#include "objects/object_rs/object_rs.h"
#include "objects/object_ds2/object_ds2.h"
#include "overlays/actors/ovl_En_Elf/z_en_elf.h"
#include "objects/object_masterkokiri/object_masterkokiri.h"
#include "objects/object_km1/object_km1.h"
#include "objects/object_mastergolon/object_mastergolon.h"
#include "objects/object_masterzoora/object_masterzoora.h"
#include "objects/object_masterkokirihead/object_masterkokirihead.h"
#define FLAGS (ACTOR_FLAG_0 | ACTOR_FLAG_3 | ACTOR_FLAG_4)
void EnOssan_Init(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_Update(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_DrawKokiriShopkeeper(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_DrawPotionShopkeeper(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_DrawBombchuShopkeeper(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_DrawBazaarShopkeeper(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_DrawZoraShopkeeper(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_DrawGoronShopkeeper(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_DrawHappyMaskShopkeeper(Actor* thisx, GlobalContext* globalCtx);
void EnOssan_InitActionFunc(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_MainActionFunc(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_TalkDefaultShopkeeper(GlobalContext* globalCtx);
void EnOssan_TalkKokiriShopkeeper(GlobalContext* globalCtx);
void EnOssan_TalkKakarikoPotionShopkeeper(GlobalContext* globalCtx);
void EnOssan_TalkBombchuShopkeeper(GlobalContext* globalCtx);
void EnOssan_TalkMarketPotionShopkeeper(GlobalContext* globalCtx);
void EnOssan_TalkBazaarShopkeeper(GlobalContext* globalCtx);
void EnOssan_TalkZoraShopkeeper(GlobalContext* globalCtx);
void EnOssan_TalkGoronShopkeeper(GlobalContext* globalCtx);
void EnOssan_TalkHappyMaskShopkeeper(GlobalContext* globalCtx);
s16 ShopItemDisp_Default(s16 v);
s16 ShopItemDisp_SpookyMask(s16 v);
s16 ShopItemDisp_SkullMask(s16 v);
s16 ShopItemDisp_BunnyHood(s16 v);
s16 ShopItemDisp_ZoraMask(s16 v);
s16 ShopItemDisp_GoronMask(s16 v);
s16 ShopItemDisp_GerudoMask(s16 v);
void EnOssan_InitKokiriShopkeeper(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_InitPotionShopkeeper(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_InitBombchuShopkeeper(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_InitBazaarShopkeeper(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_InitZoraShopkeeper(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_InitGoronShopkeeper(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_InitHappyMaskShopkeeper(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_State_Idle(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_StartConversation(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_FacingShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_TalkingToShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_LookToLeftShelf(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_LookToRightShelf(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_BrowseLeftShelf(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_BrowseRightShelf(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_LookFromShelfToShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_ItemSelected(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_SelectMilkBottle(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_SelectWeirdEgg(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_SelectUnimplementedItem(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_SelectBombs(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_CantGetItem(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_GiveItemWithFanfare(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_ItemPurchased(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_ContinueShoppingPrompt(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_GiveLonLonMilk(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_DisplayOnlyBombDialog(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_WaitForDisplayOnlyBombDialog(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_21(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_22(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_QuickBuyDialog(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_SelectMaskItem(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_LendMaskOfTruth(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_State_GiveDiscountDialog(EnOssan* this, GlobalContext* globalCtx, Player* player);
void EnOssan_Obj3ToSeg6(EnOssan* this, GlobalContext* globalCtx);
void EnOssan_StartShopping(GlobalContext* globalCtx, EnOssan* this);
void EnOssan_WaitForBlink(EnOssan* this);
void EnOssan_Blink(EnOssan* this);
u16 EnOssan_SetupHelloDialog(EnOssan* this);
s32 EnOssan_TakeItemOffShelf(EnOssan* this);
s32 EnOssan_ReturnItemToShelf(EnOssan* this);
void EnOssan_ResetItemPosition(EnOssan* this);
void EnOssan_SetStateGiveDiscountDialog(GlobalContext* globalCtx, EnOssan* this);
#define CURSOR_INVALID 0xFF
const ActorInit En_Ossan_InitVars = {
ACTOR_EN_OSSAN,
ACTORCAT_NPC,
FLAGS,
OBJECT_GAMEPLAY_KEEP,
sizeof(EnOssan),
(ActorFunc)EnOssan_Init,
(ActorFunc)EnOssan_Destroy,
(ActorFunc)EnOssan_Update,
NULL,
NULL,
};
// Unused collider
static ColliderCylinderInitType1 sCylinderInit = {
{
COLTYPE_NONE,
AT_NONE,
AC_NONE,
OC1_ON | OC1_TYPE_ALL,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0x00000000, 0x00, 0x00 },
{ 0x00000000, 0x00, 0x00 },
TOUCH_NONE | TOUCH_SFX_NORMAL,
BUMP_NONE,
OCELEM_ON,
},
{ 30, 80, 0, { 0, 0, 0 } },
};
// Rupees to pay back to Happy Mask Shop
static s16 sMaskPaymentPrice[] = { 10, 30, 20, 50 };
// item yaw offsets
static s16 sItemShelfRot[] = { 0xEAAC, 0xEAAC, 0xEAAC, 0xEAAC, 0x1554, 0x1554, 0x1554, 0x1554 };
// unused values?
static s16 D_80AC8904[] = { 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025 };
static char* sShopkeeperPrintName[] = {
"コキリの店 ", // "Kokiri Shop"
"薬屋 ", // "Potion Shop"
"夜の店 ", // "Night Shop"
"路地裏の店 ", // "Back Alley Shop"
"盾の店 ", // "Shield Shop"
"大人の店 ", // "Adult Shop"
"タロンの店 ", // "Talon Shop"
"ゾーラの店 ", // "Zora Shop"
"ゴロン夜の店", // "Goron Night Shop"
"インゴーの店", // "Ingo Store"
"お面屋 ", // "Mask Shop"
};
typedef struct {
/* 0x00 */ s16 objId;
/* 0x02 */ s16 unk_02;
/* 0x04 */ s16 unk_04;
} ShopkeeperObjInfo;
static s16 sShopkeeperObjectIds[][3] = {
{ OBJECT_KM1, OBJECT_MASTERKOKIRIHEAD, OBJECT_MASTERKOKIRI },
{ OBJECT_DS2, OBJECT_ID_MAX, OBJECT_ID_MAX },
{ OBJECT_RS, OBJECT_ID_MAX, OBJECT_ID_MAX },
{ OBJECT_DS2, OBJECT_ID_MAX, OBJECT_ID_MAX },
{ OBJECT_OSSAN, OBJECT_ID_MAX, OBJECT_ID_MAX },
{ OBJECT_OSSAN, OBJECT_ID_MAX, OBJECT_ID_MAX },
{ OBJECT_OSSAN, OBJECT_ID_MAX, OBJECT_ID_MAX },
{ OBJECT_ZO, OBJECT_ID_MAX, OBJECT_MASTERZOORA },
{ OBJECT_OF1D_MAP, OBJECT_ID_MAX, OBJECT_MASTERGOLON },
{ OBJECT_OSSAN, OBJECT_ID_MAX, OBJECT_ID_MAX },
{ OBJECT_OS, OBJECT_ID_MAX, OBJECT_ID_MAX },
};
static EnOssanTalkOwnerFunc sShopkeeperTalkOwner[] = {
EnOssan_TalkKokiriShopkeeper, EnOssan_TalkKakarikoPotionShopkeeper, EnOssan_TalkBombchuShopkeeper,
EnOssan_TalkMarketPotionShopkeeper, EnOssan_TalkBazaarShopkeeper, EnOssan_TalkDefaultShopkeeper,
EnOssan_TalkDefaultShopkeeper, EnOssan_TalkZoraShopkeeper, EnOssan_TalkGoronShopkeeper,
EnOssan_TalkDefaultShopkeeper, EnOssan_TalkHappyMaskShopkeeper,
};
static f32 sShopkeeperScale[] = {
0.01f, 0.011f, 0.0105f, 0.011f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f, 0.01f,
};
typedef struct {
/* 0x00 */ s16 shopItemIndex;
/* 0x02 */ s16 xOffset;
/* 0x04 */ s16 yOffset;
/* 0x06 */ s16 zOffset;
} ShopItem; // size 0x08
ShopItem sShopkeeperStores[][8] = {
{ { SI_DEKU_SHIELD, 50, 52, -20 },
{ SI_DEKU_NUTS_5, 50, 76, -20 },
{ SI_DEKU_NUTS_10, 80, 52, -3 },
{ SI_DEKU_STICK, 80, 76, -3 },
{ SI_DEKU_SEEDS_30, -50, 52, -20 },
{ SI_ARROWS_10, -50, 76, -20 },
{ SI_ARROWS_30, -80, 52, -3 },
{ SI_HEART, -80, 76, -3 } },
{ { SI_GREEN_POTION, 50, 52, -20 },
{ SI_BLUE_FIRE, 50, 76, -20 },
{ SI_RED_POTION_R30, 80, 52, -3 },
{ SI_FAIRY, 80, 76, -3 },
{ SI_DEKU_NUTS_5, -50, 52, -20 },
{ SI_BUGS, -50, 76, -20 },
{ SI_POE, -80, 52, -3 },
{ SI_FISH, -80, 76, -3 } },
{ { SI_BOMBCHU_10_2, 50, 52, -20 },
{ SI_BOMBCHU_10_4, 50, 76, -20 },
{ SI_BOMBCHU_10_3, 80, 52, -3 },
{ SI_BOMBCHU_10_1, 80, 76, -3 },
{ SI_BOMBCHU_20_3, -50, 52, -20 },
{ SI_BOMBCHU_20_1, -50, 76, -20 },
{ SI_BOMBCHU_20_4, -80, 52, -3 },
{ SI_BOMBCHU_20_2, -80, 76, -3 } },
{ { SI_GREEN_POTION, 50, 52, -20 },
{ SI_BLUE_FIRE, 50, 76, -20 },
{ SI_RED_POTION_R30, 80, 52, -3 },
{ SI_FAIRY, 80, 76, -3 },
{ SI_DEKU_NUTS_5, -50, 52, -20 },
{ SI_BUGS, -50, 76, -20 },
{ SI_POE, -80, 52, -3 },
{ SI_FISH, -80, 76, -3 } },
{ { SI_HYLIAN_SHIELD, 50, 52, -20 },
{ SI_BOMBS_5_R35, 50, 76, -20 },
{ SI_DEKU_NUTS_5, 80, 52, -3 },
{ SI_HEART, 80, 76, -3 },
{ SI_ARROWS_10, -50, 52, -20 },
{ SI_ARROWS_50, -50, 76, -20 },
{ SI_DEKU_STICK, -80, 52, -3 },
{ SI_ARROWS_30, -80, 76, -3 } },
{ { SI_HYLIAN_SHIELD, 50, 52, -20 },
{ SI_BOMBS_5_R25, 50, 76, -20 },
{ SI_DEKU_NUTS_5, 80, 52, -3 },
{ SI_HEART, 80, 76, -3 },
{ SI_ARROWS_10, -50, 52, -20 },
{ SI_ARROWS_50, -50, 76, -20 },
{ SI_DEKU_STICK, -80, 52, -3 },
{ SI_ARROWS_30, -80, 76, -3 } },
{ { SI_MILK_BOTTLE, 50, 52, -20 },
{ SI_DEKU_NUTS_5, 50, 76, -20 },
{ SI_DEKU_NUTS_10, 80, 52, -3 },
{ SI_HEART, 80, 76, -3 },
{ SI_WEIRD_EGG, -50, 52, -20 },
{ SI_DEKU_STICK, -50, 76, -20 },
{ SI_HEART, -80, 52, -3 },
{ SI_HEART, -80, 76, -3 } },
{ { SI_ZORA_TUNIC, 50, 52, -20 },
{ SI_ARROWS_10, 50, 76, -20 },
{ SI_HEART, 80, 52, -3 },
{ SI_ARROWS_30, 80, 76, -3 },
{ SI_DEKU_NUTS_5, -50, 52, -20 },
{ SI_ARROWS_50, -50, 76, -20 },
{ SI_FISH, -80, 52, -3 },
{ SI_RED_POTION_R50, -80, 76, -3 } },
{ { SI_BOMBS_5_R25, 50, 52, -20 },
{ SI_BOMBS_10, 50, 76, -20 },
{ SI_BOMBS_20, 80, 52, -3 },
{ SI_BOMBS_30, 80, 76, -3 },
{ SI_GORON_TUNIC, -50, 52, -20 },
{ SI_HEART, -50, 76, -20 },
{ SI_RED_POTION_R40, -80, 52, -3 },
{ SI_HEART, -80, 76, -3 } },
{ { SI_19, 50, 52, -20 },
{ SI_19, 50, 76, -20 },
{ SI_19, 80, 52, -3 },
{ SI_19, 80, 76, -3 },
{ SI_20, -50, 52, -20 },
{ SI_20, -50, 76, -20 },
{ SI_20, -80, 52, -3 },
{ SI_20, -80, 76, -3 } },
{ { SI_GERUDO_MASK, 50, 52, -20 },
{ SI_ZORA_MASK, 50, 76, -20 },
{ SI_MASK_OF_TRUTH, 80, 52, -3 },
{ SI_GORON_MASK, 80, 76, -3 },
{ SI_SKULL_MASK, -50, 52, -20 },
{ SI_KEATON_MASK, -50, 76, -20 },
{ SI_BUNNY_HOOD, -80, 52, -3 },
{ SI_SPOOKY_MASK, -80, 76, -3 } },
};
static EnOssanGetGirlAParamsFunc sShopItemReplaceFunc[] = {
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_SpookyMask,
ShopItemDisp_SkullMask, ShopItemDisp_BunnyHood, ShopItemDisp_Default, ShopItemDisp_ZoraMask,
ShopItemDisp_GoronMask, ShopItemDisp_GerudoMask, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default, ShopItemDisp_Default,
ShopItemDisp_Default, ShopItemDisp_Default,
};
static InitChainEntry sInitChain[] = {
ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 500, ICHAIN_STOP),
};
// When selecting an item to buy, this is the position the item moves to
static Vec3f sSelectedItemPosition[] = { { 17.0f, 58.0f, 30.0f }, { -17.0f, 58.0f, 30.0f } };
static EnOssanInitFunc sInitFuncs[] = {
EnOssan_InitKokiriShopkeeper, EnOssan_InitPotionShopkeeper, EnOssan_InitBombchuShopkeeper,
EnOssan_InitPotionShopkeeper, EnOssan_InitBazaarShopkeeper, EnOssan_InitBazaarShopkeeper,
EnOssan_InitBazaarShopkeeper, EnOssan_InitZoraShopkeeper, EnOssan_InitGoronShopkeeper,
EnOssan_InitBazaarShopkeeper, EnOssan_InitHappyMaskShopkeeper,
};
static Vec3f sShopkeeperPositionOffsets[] = {
{ 0.0f, 0.0f, 33.0f }, { 0.0f, 0.0f, 31.0f }, { 0.0f, 0.0f, 31.0f }, { 0.0f, 0.0f, 31.0f },
{ 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 36.0f },
{ 0.0f, 0.0f, 15.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 26.0f },
};
static EnOssanStateFunc sStateFunc[] = {
EnOssan_State_Idle,
EnOssan_State_StartConversation,
EnOssan_State_FacingShopkeeper,
EnOssan_State_TalkingToShopkeeper,
EnOssan_State_LookToLeftShelf,
EnOssan_State_LookToRightShelf,
EnOssan_State_BrowseLeftShelf,
EnOssan_State_BrowseRightShelf,
EnOssan_State_LookFromShelfToShopkeeper,
EnOssan_State_ItemSelected,
EnOssan_State_SelectMilkBottle,
EnOssan_State_SelectWeirdEgg,
EnOssan_State_SelectUnimplementedItem,
EnOssan_State_SelectBombs,
EnOssan_State_CantGetItem,
EnOssan_State_GiveItemWithFanfare,
EnOssan_State_ItemPurchased,
EnOssan_State_ContinueShoppingPrompt,
EnOssan_State_GiveLonLonMilk,
EnOssan_State_DisplayOnlyBombDialog,
EnOssan_State_WaitForDisplayOnlyBombDialog,
EnOssan_State_21,
EnOssan_State_22,
EnOssan_State_QuickBuyDialog,
EnOssan_State_SelectMaskItem,
EnOssan_State_LendMaskOfTruth,
EnOssan_State_GiveDiscountDialog,
};
void EnOssan_SetupAction(EnOssan* this, EnOssanActionFunc actionFunc) {
this->actionFunc = actionFunc;
}
s16 ShopItemDisp_Default(s16 v) {
return v;
}
s16 ShopItemDisp_SpookyMask(s16 v) {
// Sold Skull Mask
if (gSaveContext.itemGetInf[3] & 0x200) {
return v;
}
return -1;
}
s16 ShopItemDisp_SkullMask(s16 v) {
// Sold Keaton Mask
if (gSaveContext.itemGetInf[3] & 0x100) {
return v;
}
return -1;
}
s16 ShopItemDisp_BunnyHood(s16 v) {
// Sold Spooky Mask
if (gSaveContext.itemGetInf[3] & 0x400) {
return v;
}
return -1;
}
s16 ShopItemDisp_ZoraMask(s16 v) {
// Obtained Mask of Truth
if (gSaveContext.itemGetInf[3] & 0x8000) {
return v;
}
return -1;
}
s16 ShopItemDisp_GoronMask(s16 v) {
// Obtained Mask of Truth
if (gSaveContext.itemGetInf[3] & 0x8000) {
return v;
}
return -1;
}
s16 ShopItemDisp_GerudoMask(s16 v) {
// Obtained Mask of Truth
if (gSaveContext.itemGetInf[3] & 0x8000) {
return v;
}
return -1;
}
void EnOssan_SpawnItemsOnShelves(EnOssan* this, GlobalContext* globalCtx, ShopItem* shopItems) {
EnTana* shelves;
s16 itemParams;
s32 i;
for (i = 0; i < 8; i++, shopItems++) {
if (shopItems->shopItemIndex < 0) {
this->shelfSlots[i] = NULL;
} else {
itemParams = sShopItemReplaceFunc[shopItems->shopItemIndex](shopItems->shopItemIndex);
if (itemParams < 0) {
this->shelfSlots[i] = NULL;
} else {
shelves = this->shelves;
this->shelfSlots[i] = (EnGirlA*)Actor_Spawn(
&globalCtx->actorCtx, globalCtx, ACTOR_EN_GIRLA, shelves->actor.world.pos.x + shopItems->xOffset,
shelves->actor.world.pos.y + shopItems->yOffset, shelves->actor.world.pos.z + shopItems->zOffset,
shelves->actor.shape.rot.x, shelves->actor.shape.rot.y + sItemShelfRot[i],
shelves->actor.shape.rot.z, itemParams);
}
}
}
}
void EnOssan_UpdateShopOfferings(EnOssan* this, GlobalContext* globalCtx) {
s32 i;
ShopItem* storeItems;
ShopItem* shopItem;
if (this->actor.params == OSSAN_TYPE_MASK) {
storeItems = sShopkeeperStores[this->actor.params];
for (i = 0; i < 8; i++) {
shopItem = &storeItems[i];
if (shopItem->shopItemIndex >= 0 && this->shelfSlots[i] == NULL) {
s16 params = sShopItemReplaceFunc[shopItem->shopItemIndex](shopItem->shopItemIndex);
if (params >= 0) {
this->shelfSlots[i] = (EnGirlA*)Actor_Spawn(
&globalCtx->actorCtx, globalCtx, ACTOR_EN_GIRLA,
this->shelves->actor.world.pos.x + shopItem->xOffset,
this->shelves->actor.world.pos.y + shopItem->yOffset,
this->shelves->actor.world.pos.z + shopItem->zOffset, this->shelves->actor.shape.rot.x,
this->shelves->actor.shape.rot.y + sItemShelfRot[i], this->shelves->actor.shape.rot.z, params);
}
}
}
}
}
void EnOssan_TalkDefaultShopkeeper(GlobalContext* globalCtx) {
Message_ContinueTextbox(globalCtx, 0x9E);
}
void EnOssan_TalkKakarikoPotionShopkeeper(GlobalContext* globalCtx) {
if (globalCtx->curSpawn == 0) {
Message_ContinueTextbox(globalCtx, 0x5046);
} else {
Message_ContinueTextbox(globalCtx, 0x504E);
}
}
void EnOssan_TalkMarketPotionShopkeeper(GlobalContext* globalCtx) {
Message_ContinueTextbox(globalCtx, 0x504E);
}
void EnOssan_TalkKokiriShopkeeper(GlobalContext* globalCtx) {
Message_ContinueTextbox(globalCtx, 0x10BA);
}
void EnOssan_TalkBazaarShopkeeper(GlobalContext* globalCtx) {
if (globalCtx->curSpawn == 0) {
Message_ContinueTextbox(globalCtx, 0x9D);
} else {
Message_ContinueTextbox(globalCtx, 0x9C);
}
}
void EnOssan_TalkBombchuShopkeeper(GlobalContext* globalCtx) {
Message_ContinueTextbox(globalCtx, 0x7076);
}
void EnOssan_TalkZoraShopkeeper(GlobalContext* globalCtx) {
if (LINK_AGE_IN_YEARS == YEARS_CHILD) {
Message_ContinueTextbox(globalCtx, 0x403A);
} else {
Message_ContinueTextbox(globalCtx, 0x403B);
}
}
// Goron City, Goron
void EnOssan_TalkGoronShopkeeper(GlobalContext* globalCtx) {
if (LINK_AGE_IN_YEARS == YEARS_CHILD) {
if (gSaveContext.eventChkInf[2] & 0x20) {
Message_ContinueTextbox(globalCtx, 0x3028);
} else if (CUR_UPG_VALUE(UPG_STRENGTH) != 0) {
Message_ContinueTextbox(globalCtx, 0x302D);
} else {
Message_ContinueTextbox(globalCtx, 0x300F);
}
} else if ((!gSaveContext.n64ddFlag && !CHECK_QUEST_ITEM(QUEST_MEDALLION_FIRE)) ||
(gSaveContext.n64ddFlag && !gSaveContext.dungeonsDone[4])) {
Message_ContinueTextbox(globalCtx, 0x3057);
} else {
Message_ContinueTextbox(globalCtx, 0x305B);
}
}
// Happy Mask Shop
void EnOssan_TalkHappyMaskShopkeeper(GlobalContext* globalCtx) {
if ((gSaveContext.itemGetInf[3] & 0x100) // Sold Keaton Mask
&& (gSaveContext.itemGetInf[3] & 0x200) // Sold Skull Mask
&& (gSaveContext.itemGetInf[3] & 0x400) // Sold Spooky Mask
&& (gSaveContext.itemGetInf[3] & 0x800)) { // Sold Bunny Hood
Message_ContinueTextbox(globalCtx, 0x70AE);
} else {
switch (globalCtx->msgCtx.choiceIndex) {
case 1:
Message_ContinueTextbox(globalCtx, 0x70A4);
break;
case 0:
Message_ContinueTextbox(globalCtx, 0x70A3);
break;
}
}
}
void EnOssan_UpdateCameraDirection(EnOssan* this, GlobalContext* globalCtx, f32 cameraFaceAngle) {
this->cameraFaceAngle = cameraFaceAngle;
Camera_SetCameraData(GET_ACTIVE_CAM(globalCtx), 0xC, NULL, NULL, cameraFaceAngle, 0, 0);
}
s32 EnOssan_TryGetObjBankIndexes(EnOssan* this, GlobalContext* globalCtx, s16* objectIds) {
if (objectIds[1] != OBJECT_ID_MAX) {
this->objBankIndex2 = Object_GetIndex(&globalCtx->objectCtx, objectIds[1]);
if (this->objBankIndex2 < 0) {
return false;
}
} else {
this->objBankIndex2 = -1;
}
if (objectIds[2] != OBJECT_ID_MAX) {
this->objBankIndex3 = Object_GetIndex(&globalCtx->objectCtx, objectIds[2]);
if (this->objBankIndex3 < 0) {
return false;
}
} else {
this->objBankIndex3 = -1;
}
return true;
}
void EnOssan_Init(Actor* thisx, GlobalContext* globalCtx) {
EnOssan* this = (EnOssan*)thisx;
s32 pad;
s16* objectIds;
if (this->actor.params == OSSAN_TYPE_TALON && (LINK_AGE_IN_YEARS != YEARS_CHILD)) {
this->actor.params = OSSAN_TYPE_INGO;
}
//! @bug This check will always evaluate to false, it should be || not &&
if (this->actor.params > OSSAN_TYPE_MASK && this->actor.params < OSSAN_TYPE_KOKIRI) {
Actor_Kill(&this->actor);
osSyncPrintf(VT_COL(RED, WHITE));
osSyncPrintf("引数がおかしいよ(arg_data=%d)\n", this->actor.params);
osSyncPrintf(VT_RST);
ASSERT(this->actor.params > OSSAN_TYPE_MASK && this->actor.params < OSSAN_TYPE_KOKIRI);
return;
}
// If you haven't given Zelda's Letter to the Kakariko Guard
// or are rando'd and haven't gotten gotten the letter from zelda yet
if (this->actor.params == OSSAN_TYPE_MASK && (!(gSaveContext.infTable[7] & 0x40) ||
(gSaveContext.n64ddFlag && !(gSaveContext.eventChkInf[4] & 1)))) {
Actor_Kill(&this->actor);
return;
}
if (this->actor.params == OSSAN_TYPE_KAKARIKO_POTION && (LINK_AGE_IN_YEARS == YEARS_CHILD)) {
Actor_Kill(&this->actor);
return;
}
// Don't kill bombchu shop actor in rando, making it so the shop is immediately open
// gSaveContext.eventChkInf[2] & 0x20 - Completed Dodongo's Cavern
if (this->actor.params == OSSAN_TYPE_BOMBCHUS && !(gSaveContext.eventChkInf[2] & 0x20) && !gSaveContext.n64ddFlag) {
Actor_Kill(&this->actor);
return;
}
objectIds = sShopkeeperObjectIds[this->actor.params];
this->objBankIndex1 = Object_GetIndex(&globalCtx->objectCtx, objectIds[0]);
if (this->objBankIndex1 < 0) {
Actor_Kill(&this->actor);
osSyncPrintf(VT_COL(RED, WHITE));
osSyncPrintf("バンクが無いよ!!(%s)\n", sShopkeeperPrintName[this->actor.params]);
osSyncPrintf(VT_RST);
ASSERT(this->objBankIndex1 < 0);
return;
}
if (EnOssan_TryGetObjBankIndexes(this, globalCtx, objectIds) == 0) {
Actor_Kill(&this->actor);
osSyncPrintf(VT_COL(RED, WHITE));
osSyncPrintf("予備バンクが無いよ!!(%s)\n", sShopkeeperPrintName[this->actor.params]);
osSyncPrintf(VT_RST);
ASSERT(EnOssan_TryGetObjBankIndexes(this, globalCtx, objectIds) == 0);
return;
}
Actor_ProcessInitChain(&this->actor, sInitChain);
EnOssan_SetupAction(this, EnOssan_InitActionFunc);
}
void EnOssan_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnOssan* this = (EnOssan*)thisx;
SkelAnime_Free(&this->skelAnime, globalCtx);
Collider_DestroyCylinder(globalCtx, &this->collider);
}
void EnOssan_UpdateCursorPos(GlobalContext* globalCtx, EnOssan* this) {
s16 x;
s16 y;
Actor_GetScreenPos(globalCtx, &this->shelfSlots[this->cursorIndex]->actor, &x, &y);
this->cursorX = x;
this->cursorY = y;
}
void EnOssan_EndInteraction(GlobalContext* globalCtx, EnOssan* this) {
Player* player = GET_PLAYER(globalCtx);
// "End of conversation!"
osSyncPrintf(VT_FGCOL(YELLOW) "%s[%d]:★★★ 会話終了!! ★★★" VT_RST "\n", __FILE__, __LINE__);
YREG(31) = 0;
Actor_ProcessTalkRequest(&this->actor, globalCtx);
globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING;
globalCtx->msgCtx.stateTimer = 4;
player->stateFlags2 &= ~0x20000000;
func_800BC490(globalCtx, 1);
Interface_ChangeAlpha(50);
this->drawCursor = 0;
this->stickLeftPrompt.isEnabled = false;
this->stickRightPrompt.isEnabled = false;
EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f);
this->actor.textId = EnOssan_SetupHelloDialog(this);
this->stateFlag = OSSAN_STATE_IDLE;
}
s32 EnOssan_TestEndInteraction(EnOssan* this, GlobalContext* globalCtx, Input* input) {
if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
EnOssan_EndInteraction(globalCtx, this);
return true;
} else {
return false;
}
}
s32 EnOssan_TestCancelOption(EnOssan* this, GlobalContext* globalCtx, Input* input) {
if (CHECK_BTN_ALL(input->press.button, BTN_B)) {
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
return true;
} else {
return false;
}
}
void EnOssan_SetStateStartShopping(GlobalContext* globalCtx, EnOssan* this, u8 skipHelloState) {
YREG(31) = 1;
this->headRot = this->headTargetRot = 0;
Interface_SetDoAction(globalCtx, DO_ACTION_NEXT);
EnOssan_UpdateCameraDirection(this, globalCtx, 0);
if (!skipHelloState) {
this->stateFlag = OSSAN_STATE_START_CONVERSATION;
} else {
EnOssan_StartShopping(globalCtx, this);
}
}
void EnOssan_StartShopping(GlobalContext* globalCtx, EnOssan* this) {
this->stateFlag = OSSAN_STATE_FACING_SHOPKEEPER;
if (this->actor.params == OSSAN_TYPE_MASK) {
// if all masks have been sold, give the option to ask about the mask of truth
if ((gSaveContext.itemGetInf[3] & 0x100) && (gSaveContext.itemGetInf[3] & 0x200) &&
(gSaveContext.itemGetInf[3] & 0x400) && (gSaveContext.itemGetInf[3] & 0x800)) {
Message_ContinueTextbox(globalCtx, 0x70AD);
} else {
Message_ContinueTextbox(globalCtx, 0x70A2);
}
} else {
Message_ContinueTextbox(globalCtx, 0x83);
}
Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE);
this->stickRightPrompt.isEnabled = true;
this->stickLeftPrompt.isEnabled = true;
EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f);
}
void EnOssan_ChooseTalkToOwner(GlobalContext* globalCtx, EnOssan* this) {
this->stateFlag = OSSAN_STATE_TALKING_TO_SHOPKEEPER;
sShopkeeperTalkOwner[this->actor.params](globalCtx);
Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE);
this->stickLeftPrompt.isEnabled = false;
this->stickRightPrompt.isEnabled = false;
}
void EnOssan_SetLookToShopkeeperFromShelf(GlobalContext* globalCtx, EnOssan* this) {
func_80078884(NA_SE_SY_CURSOR);
this->drawCursor = 0;
this->stateFlag = OSSAN_STATE_LOOK_SHOPKEEPER;
}
void EnOssan_State_Idle(EnOssan* this, GlobalContext* globalCtx, Player* player) {
this->headTargetRot = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
if (Actor_ProcessTalkRequest(&this->actor, globalCtx)) {
// "Start conversation!!"
osSyncPrintf(VT_FGCOL(YELLOW) "★★★ 会話開始!! ★★★" VT_RST "\n");
player->stateFlags2 |= 0x20000000;
func_800BC590(globalCtx);
EnOssan_SetStateStartShopping(globalCtx, this, false);
} else if (this->actor.xzDistToPlayer < 100.0f) {
func_8002F2CC(&this->actor, globalCtx, 100);
}
}
void EnOssan_UpdateJoystickInputState(GlobalContext* globalCtx, EnOssan* this) {
Input* input = &globalCtx->state.input[0];
s8 stickX = input->rel.stick_x;
s8 stickY = input->rel.stick_y;
this->moveHorizontal = this->moveVertical = false;
if (this->stickAccumX == 0) {
if (stickX > 30 || stickX < -30) {
this->stickAccumX = stickX;
this->moveHorizontal = true;
}
} else if (stickX <= 30 && stickX >= -30) {
this->stickAccumX = 0;
} else if (this->stickAccumX * stickX < 0) { // Stick has swapped directions
this->stickAccumX = stickX;
this->moveHorizontal = true;
} else {
this->stickAccumX += stickX;
if (this->stickAccumX > 2000) {
this->stickAccumX = 2000;
} else if (this->stickAccumX < -2000) {
this->stickAccumX = -2000;
}
}
if (this->stickAccumY == 0) {
if (stickY > 30 || stickY < -30) {
this->stickAccumY = stickY;
this->moveVertical = true;
}
} else if (stickY <= 30 && stickY >= -30) {
this->stickAccumY = 0;
} else if (this->stickAccumY * stickY < 0) { // Stick has swapped directions
this->stickAccumY = stickY;
this->moveVertical = true;
} else {
this->stickAccumY += stickY;
if (this->stickAccumY > 2000) {
this->stickAccumY = 2000;
} else if (this->stickAccumY < -2000) {
this->stickAccumY = -2000;
}
}
}
u8 EnOssan_SetCursorIndexFromNeutral(EnOssan* this, u8 shelfOffset) {
u8 i;
// if cursor is on the top shelf
if (this->cursorIndex & 1) {
// scan top shelf for non-null item
for (i = shelfOffset + 1; i < shelfOffset + 4; i += 2) {
if (this->shelfSlots[i] != NULL) {
return i;
}
}
// scan bottom shelf for non-null item
for (i = shelfOffset; i < shelfOffset + 4; i += 2) {
if (this->shelfSlots[i] != NULL) {
return i;
}
}
} else {
// scan bottom shelf for non-null item
for (i = shelfOffset; i < shelfOffset + 4; i += 2) {
if (this->shelfSlots[i] != NULL) {
return i;
}
}
// scan top shelf for non-null item
for (i = shelfOffset + 1; i < shelfOffset + 4; i += 2) {
if (this->shelfSlots[i] != NULL) {
return i;
}
}
}
return CURSOR_INVALID;
}
u8 EnOssan_CursorRight(EnOssan* this, u8 cursorIndex, u8 shelfSlotMin) {
u8 c = shelfSlotMin + 4;
while (cursorIndex >= shelfSlotMin && cursorIndex < c) {
cursorIndex -= 2;
if (cursorIndex >= shelfSlotMin && cursorIndex < c) {
if (this->shelfSlots[cursorIndex] != NULL) {
return cursorIndex;
}
}
}
return CURSOR_INVALID;
}
u8 EnOssan_CursorLeft(EnOssan* this, u8 cursorIndex, u8 shelfSlotMax) {
while (cursorIndex < shelfSlotMax) {
cursorIndex += 2;
if ((cursorIndex < shelfSlotMax) && this->shelfSlots[cursorIndex] != NULL) {
return cursorIndex;
}
}
return CURSOR_INVALID;
}
// pay salesman back
void EnOssan_TryPaybackMask(EnOssan* this, GlobalContext* globalCtx) {
s16 price = sMaskPaymentPrice[this->happyMaskShopState];
if (gSaveContext.rupees < price) {
Message_ContinueTextbox(globalCtx, 0x70A8);
this->happyMaskShopkeeperEyeIdx = 1;
this->happyMaskShopState = OSSAN_HAPPY_STATE_ANGRY;
} else {
Rupees_ChangeBy(-price);
if (this->happyMaskShopState == OSSAN_HAPPY_STATE_REQUEST_PAYMENT_BUNNY_HOOD) {
gSaveContext.eventChkInf[8] |= 0x8000;
Message_ContinueTextbox(globalCtx, 0x70A9);
this->happyMaskShopState = OSSAN_HAPPY_STATE_ALL_MASKS_SOLD;
return;
}
if (this->happyMaskShopState == OSSAN_HAPPY_STATE_REQUEST_PAYMENT_KEATON_MASK) {
gSaveContext.eventChkInf[8] |= 0x1000;
} else if (this->happyMaskShopState == OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SPOOKY_MASK) {
gSaveContext.eventChkInf[8] |= 0x4000;
} else if (this->happyMaskShopState == OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SKULL_MASK) {
gSaveContext.eventChkInf[8] |= 0x2000;
}
Message_ContinueTextbox(globalCtx, 0x70A7);
this->happyMaskShopState = OSSAN_HAPPY_STATE_NONE;
}
this->stateFlag = OSSAN_STATE_START_CONVERSATION;
}
void EnOssan_State_StartConversation(EnOssan* this, GlobalContext* globalCtx, Player* player) {
u8 dialogState = Message_GetState(&globalCtx->msgCtx);
if (this->actor.params == OSSAN_TYPE_MASK && dialogState == TEXT_STATE_CHOICE) {
if (!EnOssan_TestEndInteraction(this, globalCtx, &globalCtx->state.input[0]) &&
Message_ShouldAdvance(globalCtx)) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
EnOssan_StartShopping(globalCtx, this);
break;
case 1:
EnOssan_EndInteraction(globalCtx, this);
break;
}
}
} else if (dialogState == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
func_80078884(NA_SE_SY_MESSAGE_PASS);
switch (this->happyMaskShopState) {
case OSSAN_HAPPY_STATE_ALL_MASKS_SOLD:
Message_ContinueTextbox(globalCtx, 0x70AA);
this->stateFlag = OSSAN_STATE_LEND_MASK_OF_TRUTH;
return;
case OSSAN_HAPPY_STATE_BORROWED_FIRST_MASK:
EnOssan_EndInteraction(globalCtx, this);
return;
case OSSAN_HAPPY_STATE_REQUEST_PAYMENT_KEATON_MASK:
case OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SPOOKY_MASK:
case OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SKULL_MASK:
case OSSAN_HAPPY_STATE_REQUEST_PAYMENT_BUNNY_HOOD:
EnOssan_TryPaybackMask(this, globalCtx);
return;
case OSSAN_HAPPY_STATE_ANGRY:
globalCtx->nextEntranceIndex = 0x1D1;
globalCtx->sceneLoadFlag = 0x14;
globalCtx->fadeTransition = 0x2E;
return;
}
if (!EnOssan_TestEndInteraction(this, globalCtx, &globalCtx->state.input[0])) {
// "Shop around by moving the stick left and right"
osSyncPrintf("「スティック左右で品物みてくれ!」\n");
EnOssan_StartShopping(globalCtx, this);
}
}
}
s32 EnOssan_FacingShopkeeperDialogResult(EnOssan* this, GlobalContext* globalCtx) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
EnOssan_ChooseTalkToOwner(globalCtx, this);
return true;
case 1:
EnOssan_EndInteraction(globalCtx, this);
return true;
default:
return false;
}
}
void EnOssan_State_FacingShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player) {
Input* input = &globalCtx->state.input[0];
u8 nextIndex;
bool dpad = CVar_GetS32("gDpadText", 0);
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE) &&
!EnOssan_TestEndInteraction(this, globalCtx, &globalCtx->state.input[0])) {
if (Message_ShouldAdvance(globalCtx) && EnOssan_FacingShopkeeperDialogResult(this, globalCtx)) {
func_80078884(NA_SE_SY_DECIDE);
return;
}
// Stick Left
if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) {
nextIndex = EnOssan_SetCursorIndexFromNeutral(this, 4);
if (nextIndex != CURSOR_INVALID) {
this->cursorIndex = nextIndex;
this->stateFlag = OSSAN_STATE_LOOK_SHELF_LEFT;
Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE);
this->stickLeftPrompt.isEnabled = false;
func_80078884(NA_SE_SY_CURSOR);
}
} else if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) {
nextIndex = EnOssan_SetCursorIndexFromNeutral(this, 0);
if (nextIndex != CURSOR_INVALID) {
this->cursorIndex = nextIndex;
this->stateFlag = OSSAN_STATE_LOOK_SHELF_RIGHT;
Interface_SetDoAction(globalCtx, DO_ACTION_DECIDE);
this->stickRightPrompt.isEnabled = false;
func_80078884(NA_SE_SY_CURSOR);
}
}
}
}
void EnOssan_State_TalkingToShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(globalCtx)) {
EnOssan_StartShopping(globalCtx, this);
}
}
void EnOssan_State_LookToLeftShelf(EnOssan* this, GlobalContext* globalCtx, Player* player) {
Math_ApproachF(&this->cameraFaceAngle, 30.0f, 0.5f, 10.0f);
if (this->cameraFaceAngle > 29.5f) {
EnOssan_UpdateCameraDirection(this, globalCtx, 30.0f);
}
EnOssan_UpdateCameraDirection(this, globalCtx, this->cameraFaceAngle);
if (this->cameraFaceAngle >= 30.0f) {
EnOssan_UpdateCameraDirection(this, globalCtx, 30.0f);
EnOssan_UpdateCursorPos(globalCtx, this);
this->stateFlag = OSSAN_STATE_BROWSE_LEFT_SHELF;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
} else {
this->stickAccumX = 0;
}
}
void EnOssan_State_LookToRightShelf(EnOssan* this, GlobalContext* globalCtx, Player* player) {
Math_ApproachF(&this->cameraFaceAngle, -30.0f, 0.5f, 10.0f);
if (this->cameraFaceAngle < -29.5f) {
EnOssan_UpdateCameraDirection(this, globalCtx, -30.0f);
}
EnOssan_UpdateCameraDirection(this, globalCtx, this->cameraFaceAngle);
if (this->cameraFaceAngle <= -30.0f) {
EnOssan_UpdateCameraDirection(this, globalCtx, -30.0f);
EnOssan_UpdateCursorPos(globalCtx, this);
this->stateFlag = OSSAN_STATE_BROWSE_RIGHT_SHELF;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
} else {
this->stickAccumX = 0;
}
}
void EnOssan_CursorUpDown(EnOssan* this, GlobalContext* globalCtx) {
Input* input = &globalCtx->state.input[0];
u8 curTemp = this->cursorIndex;
u8 curScanTemp;
bool dpad = CVar_GetS32("gDpadText", 0);
if ((this->stickAccumY < 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DDOWN))) {
curTemp &= 0xFE;
if (this->shelfSlots[curTemp] != NULL) {
this->cursorIndex = curTemp;
return;
}
// cursorIndex on right shelf
if (curTemp < 4) {
curScanTemp = curTemp + 2;
if (curScanTemp >= 4) {
curScanTemp = 0;
}
while (curScanTemp != curTemp) {
if (this->shelfSlots[curScanTemp] != NULL) {
this->cursorIndex = curScanTemp;
return;
}
curScanTemp += 2;
if (curScanTemp >= 4) {
curScanTemp = 0;
}
}
} else {
// cursorIndex on left shelf
curScanTemp = curTemp + 2;
if (curScanTemp >= 8) {
curScanTemp = 4;
}
while (curScanTemp != curTemp) {
if (this->shelfSlots[curScanTemp] != NULL) {
this->cursorIndex = curScanTemp;
return;
}
curScanTemp += 2;
if (curScanTemp >= 8) {
curScanTemp = 4;
}
}
}
} else if ((this->stickAccumY > 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DUP))) {
curTemp |= 1;
if (this->shelfSlots[curTemp] != NULL) {
this->cursorIndex = curTemp;
return;
}
// cursorIndex on right shelf
if (curTemp < 4) {
curScanTemp = curTemp + 2;
if (curScanTemp >= 4) {
curScanTemp = 1;
}
while (curScanTemp != curTemp) {
if (this->shelfSlots[curScanTemp] != NULL) {
this->cursorIndex = curScanTemp;
return;
}
curScanTemp += 2;
if (curScanTemp >= 4) {
curScanTemp = 1;
}
}
} else {
// cursorIndex on left shelf
curScanTemp = curTemp + 2;
if (curScanTemp >= 8) {
curScanTemp = 5;
}
while (curScanTemp != curTemp) {
if (this->shelfSlots[curScanTemp] != NULL) {
this->cursorIndex = curScanTemp;
return;
}
curScanTemp += 2;
if (curScanTemp >= 8) {
curScanTemp = 5;
}
}
}
}
}
s32 EnOssan_HasPlayerSelectedItem(GlobalContext* globalCtx, EnOssan* this, Input* input) {
EnGirlA* selectedItem = this->shelfSlots[this->cursorIndex];
if (EnOssan_TestEndInteraction(this, globalCtx, input)) {
return true;
}
if (Message_ShouldAdvance(globalCtx)) {
if (selectedItem->actor.params != SI_SOLD_OUT && selectedItem->isInvisible == 0) {
this->tempStateFlag = this->stateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->itemBuyPromptTextId);
this->stickLeftPrompt.isEnabled = false;
this->stickRightPrompt.isEnabled = false;
switch (selectedItem->actor.params) {
case SI_KEATON_MASK:
case SI_SPOOKY_MASK:
case SI_SKULL_MASK:
case SI_BUNNY_HOOD:
case SI_MASK_OF_TRUTH:
case SI_ZORA_MASK:
case SI_GORON_MASK:
case SI_GERUDO_MASK:
func_80078884(NA_SE_SY_DECIDE);
this->drawCursor = 0;
this->stateFlag = OSSAN_STATE_SELECT_ITEM_MASK;
return true;
case SI_MILK_BOTTLE:
func_80078884(NA_SE_SY_DECIDE);
this->drawCursor = 0;
this->stateFlag = OSSAN_STATE_SELECT_ITEM_MILK_BOTTLE;
return true;
case SI_WEIRD_EGG:
func_80078884(NA_SE_SY_DECIDE);
this->drawCursor = 0;
this->stateFlag = OSSAN_STATE_SELECT_ITEM_WEIRD_EGG;
return true;
case SI_19:
case SI_20:
func_80078884(NA_SE_SY_ERROR);
this->drawCursor = 0;
this->stateFlag = OSSAN_STATE_SELECT_ITEM_UNIMPLEMENTED;
return true;
case SI_BOMBS_5_R25:
case SI_BOMBS_10:
case SI_BOMBS_20:
case SI_BOMBS_30:
case SI_BOMBS_5_R35:
func_80078884(NA_SE_SY_DECIDE);
this->drawCursor = 0;
this->stateFlag = OSSAN_STATE_SELECT_ITEM_BOMBS;
return true;
default:
func_80078884(NA_SE_SY_DECIDE);
this->drawCursor = 0;
this->stateFlag = OSSAN_STATE_SELECT_ITEM;
return true;
}
}
func_80078884(NA_SE_SY_ERROR);
return true;
}
return false;
}
void EnOssan_State_BrowseLeftShelf(EnOssan* this, GlobalContext* globalCtx, Player* player) {
Input* input = &globalCtx->state.input[0];
s32 a;
s32 b;
u8 prevIndex = this->cursorIndex;
s32 c;
s32 d;
bool dpad = CVar_GetS32("gDpadText", 0);
if (!EnOssan_ReturnItemToShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
this->delayTimer = 3;
return;
}
if (this->delayTimer != 0) {
this->delayTimer--;
return;
}
this->drawCursor = 0xFF;
this->stickRightPrompt.isEnabled = true;
EnOssan_UpdateCursorPos(globalCtx, this);
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) &&
!EnOssan_HasPlayerSelectedItem(globalCtx, this, &globalCtx->state.input[0])) {
if (this->moveHorizontal) {
if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) {
a = EnOssan_CursorRight(this, this->cursorIndex, 4);
if (a != CURSOR_INVALID) {
this->cursorIndex = a;
} else {
EnOssan_SetLookToShopkeeperFromShelf(globalCtx, this);
return;
}
} else if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) {
b = EnOssan_CursorLeft(this, this->cursorIndex, 8);
if (b != CURSOR_INVALID) {
this->cursorIndex = b;
}
}
} else {
if ((this->stickAccumX > 0 && this->stickAccumX > 500) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) {
c = EnOssan_CursorRight(this, this->cursorIndex, 4);
if (c != CURSOR_INVALID) {
this->cursorIndex = c;
} else {
EnOssan_SetLookToShopkeeperFromShelf(globalCtx, this);
return;
}
} else if ((this->stickAccumX < 0 && this->stickAccumX < -500) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) {
d = EnOssan_CursorLeft(this, this->cursorIndex, 8);
if (d != CURSOR_INVALID) {
this->cursorIndex = d;
}
}
}
EnOssan_CursorUpDown(this, globalCtx);
if (this->cursorIndex != prevIndex) {
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
func_80078884(NA_SE_SY_CURSOR);
}
}
}
void EnOssan_State_BrowseRightShelf(EnOssan* this, GlobalContext* globalCtx, Player* player) {
Input* input = &globalCtx->state.input[0];
s32 pad[2];
u8 prevIndex;
u8 nextIndex;
bool dpad = CVar_GetS32("gDpadText", 0);
prevIndex = this->cursorIndex;
if (!EnOssan_ReturnItemToShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
this->delayTimer = 3;
return;
}
if (this->delayTimer != 0) {
this->delayTimer--;
return;
}
this->drawCursor = 0xFF;
this->stickLeftPrompt.isEnabled = true;
EnOssan_UpdateCursorPos(globalCtx, this);
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT) &&
!EnOssan_HasPlayerSelectedItem(globalCtx, this, &globalCtx->state.input[0])) {
if (this->moveHorizontal) {
if ((this->stickAccumX < 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) {
nextIndex = EnOssan_CursorRight(this, this->cursorIndex, 0);
if (nextIndex != CURSOR_INVALID) {
this->cursorIndex = nextIndex;
} else {
EnOssan_SetLookToShopkeeperFromShelf(globalCtx, this);
return;
}
} else if ((this->stickAccumX > 0) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) {
nextIndex = EnOssan_CursorLeft(this, this->cursorIndex, 4);
if (nextIndex != CURSOR_INVALID) {
this->cursorIndex = nextIndex;
}
}
} else {
if ((this->stickAccumX < 0 && this->stickAccumX < -500) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DLEFT))) {
nextIndex = EnOssan_CursorRight(this, this->cursorIndex, 0);
if (nextIndex != CURSOR_INVALID) {
this->cursorIndex = nextIndex;
} else {
EnOssan_SetLookToShopkeeperFromShelf(globalCtx, this);
return;
}
} else if ((this->stickAccumX > 0 && this->stickAccumX > 500) || (dpad && CHECK_BTN_ALL(input->press.button, BTN_DRIGHT))) {
nextIndex = EnOssan_CursorLeft(this, this->cursorIndex, 4);
if (nextIndex != CURSOR_INVALID) {
this->cursorIndex = nextIndex;
}
}
}
EnOssan_CursorUpDown(this, globalCtx);
if (this->cursorIndex != prevIndex) {
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
func_80078884(NA_SE_SY_CURSOR);
}
}
}
void EnOssan_State_LookFromShelfToShopkeeper(EnOssan* this, GlobalContext* globalCtx, Player* player) {
Math_ApproachF(&this->cameraFaceAngle, 0.0f, 0.5f, 10.0f);
if ((this->cameraFaceAngle < 0.5f) && (this->cameraFaceAngle > -0.5f)) {
EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f);
}
EnOssan_UpdateCameraDirection(this, globalCtx, this->cameraFaceAngle);
if (this->cameraFaceAngle == 0.0f) {
EnOssan_StartShopping(globalCtx, this);
}
}
void EnOssan_State_DisplayOnlyBombDialog(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (!EnOssan_ReturnItemToShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
return;
}
Math_ApproachF(&this->cameraFaceAngle, 0.0f, 0.5f, 10.0f);
if (this->cameraFaceAngle < 0.5f && this->cameraFaceAngle > -0.5f) {
EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f);
}
EnOssan_UpdateCameraDirection(this, globalCtx, this->cameraFaceAngle);
if (this->cameraFaceAngle == 0.0f) {
Message_ContinueTextbox(globalCtx, 0x3010);
this->stateFlag = OSSAN_STATE_WAIT_FOR_DISPLAY_ONLY_BOMB_DIALOG;
}
}
void EnOssan_GiveItemWithFanfare(GlobalContext* globalCtx, EnOssan* this) {
Player* player = GET_PLAYER(globalCtx);
osSyncPrintf("\n" VT_FGCOL(YELLOW) "初めて手にいれた!!" VT_RST "\n\n");
func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f);
globalCtx->msgCtx.msgMode = MSGMODE_TEXT_CLOSING;
globalCtx->msgCtx.stateTimer = 4;
player->stateFlags2 &= ~0x20000000;
func_800BC490(globalCtx, 1);
Interface_ChangeAlpha(50);
this->drawCursor = 0;
EnOssan_UpdateCameraDirection(this, globalCtx, 0.0f);
this->stateFlag = OSSAN_STATE_GIVE_ITEM_FANFARE;
osSyncPrintf(VT_FGCOL(YELLOW) "持ち上げ開始!!" VT_RST "\n\n");
}
void EnOssan_SetStateCantGetItem(GlobalContext* globalCtx, EnOssan* this, u16 textId) {
Message_ContinueTextbox(globalCtx, textId);
this->stateFlag = OSSAN_STATE_CANT_GET_ITEM;
}
void EnOssan_SetStateQuickBuyDialog(GlobalContext* globalCtx, EnOssan* this, u16 textId) {
Message_ContinueTextbox(globalCtx, textId);
this->stateFlag = OSSAN_STATE_QUICK_BUY;
}
void EnOssan_HandleCanBuyItem(GlobalContext* globalCtx, EnOssan* this) {
EnGirlA* selectedItem = this->shelfSlots[this->cursorIndex];
switch (selectedItem->canBuyFunc(globalCtx, selectedItem)) {
case CANBUY_RESULT_SUCCESS_FANFARE:
if (selectedItem->actor.params == SI_HYLIAN_SHIELD && gSaveContext.infTable[7] & 0x40) {
EnOssan_SetStateGiveDiscountDialog(globalCtx, this);
} else {
EnOssan_GiveItemWithFanfare(globalCtx, this);
this->drawCursor = 0;
this->shopItemSelectedTween = 0.0f;
selectedItem->setOutOfStockFunc(globalCtx, selectedItem);
}
break;
case CANBUY_RESULT_SUCCESS:
selectedItem->itemGiveFunc(globalCtx, selectedItem);
EnOssan_SetStateQuickBuyDialog(globalCtx, this, 0x84);
this->drawCursor = 0;
this->shopItemSelectedTween = 0.0f;
selectedItem->setOutOfStockFunc(globalCtx, selectedItem);
break;
case CANBUY_RESULT_CANT_GET_NOW:
func_80078884(NA_SE_SY_ERROR);
EnOssan_SetStateCantGetItem(globalCtx, this, 0x86);
break;
case CANBUY_RESULT_NEED_BOTTLE:
func_80078884(NA_SE_SY_ERROR);
EnOssan_SetStateCantGetItem(globalCtx, this, 0x96);
break;
case CANBUY_RESULT_NEED_RUPEES:
func_80078884(NA_SE_SY_ERROR);
EnOssan_SetStateCantGetItem(globalCtx, this, 0x85);
break;
case CANBUY_RESULT_CANT_GET_NOW_5:
func_80078884(NA_SE_SY_ERROR);
EnOssan_SetStateCantGetItem(globalCtx, this, 0x86);
break;
}
}
void EnOssan_HandleCanBuyLonLonMilk(GlobalContext* globalCtx, EnOssan* this) {
EnGirlA* item = this->shelfSlots[this->cursorIndex];
switch (item->canBuyFunc(globalCtx, item)) {
case CANBUY_RESULT_SUCCESS_FANFARE:
Message_ContinueTextbox(globalCtx, 0x9C);
this->stateFlag = OSSAN_STATE_GIVE_LON_LON_MILK;
this->drawCursor = 0;
break;
case CANBUY_RESULT_SUCCESS:
item->itemGiveFunc(globalCtx, item);
EnOssan_SetStateQuickBuyDialog(globalCtx, this, 0x98);
this->drawCursor = 0;
this->shopItemSelectedTween = 0.0f;
item->setOutOfStockFunc(globalCtx, item);
break;
case CANBUY_RESULT_NEED_BOTTLE:
EnOssan_SetStateCantGetItem(globalCtx, this, 0x96);
break;
case CANBUY_RESULT_NEED_RUPEES:
EnOssan_SetStateCantGetItem(globalCtx, this, 0x85);
break;
}
}
void EnOssan_HandleCanBuyWeirdEgg(GlobalContext* globalCtx, EnOssan* this) {
EnGirlA* item = this->shelfSlots[this->cursorIndex];
switch (item->canBuyFunc(globalCtx, item)) {
case CANBUY_RESULT_SUCCESS_FANFARE:
EnOssan_GiveItemWithFanfare(globalCtx, this);
this->drawCursor = 0;
this->shopItemSelectedTween = 0.0f;
item->setOutOfStockFunc(globalCtx, item);
break;
case CANBUY_RESULT_SUCCESS:
item->itemGiveFunc(globalCtx, item);
EnOssan_SetStateQuickBuyDialog(globalCtx, this, 0x9A);
this->drawCursor = 0;
this->shopItemSelectedTween = 0.0f;
item->setOutOfStockFunc(globalCtx, item);
break;
case CANBUY_RESULT_CANT_GET_NOW:
func_80078884(NA_SE_SY_ERROR);
EnOssan_SetStateCantGetItem(globalCtx, this, 0x9D);
break;
case CANBUY_RESULT_NEED_RUPEES:
func_80078884(NA_SE_SY_ERROR);
EnOssan_SetStateCantGetItem(globalCtx, this, 0x85);
break;
}
}
void EnOssan_HandleCanBuyBombs(GlobalContext* globalCtx, EnOssan* this) {
EnGirlA* item = this->shelfSlots[this->cursorIndex];
switch (item->canBuyFunc(globalCtx, item)) {
case CANBUY_RESULT_SUCCESS_FANFARE:
case CANBUY_RESULT_SUCCESS:
item->itemGiveFunc(globalCtx, item);
EnOssan_SetStateQuickBuyDialog(globalCtx, this, 0x84);
this->drawCursor = 0;
this->shopItemSelectedTween = 0.0f;
item->setOutOfStockFunc(globalCtx, item);
break;
case CANBUY_RESULT_CANT_GET_NOW:
func_80078884(NA_SE_SY_ERROR);
EnOssan_SetStateCantGetItem(globalCtx, this, 0x86);
break;
case CANBUY_RESULT_NEED_RUPEES:
func_80078884(NA_SE_SY_ERROR);
EnOssan_SetStateCantGetItem(globalCtx, this, 0x85);
break;
}
}
void EnOssan_BuyGoronCityBombs(GlobalContext* globalCtx, EnOssan* this) {
if (LINK_AGE_IN_YEARS == YEARS_CHILD) {
// Let players buy the right side of the goron shop in rando regardless of DC completion
// Players will still need a bomb bag to buy bombs (handled by vanilla behaviour)
// gSaveContext.eventChkInf[2] & 0x20 - Completed Dodongo's Cavern
if (!gSaveContext.n64ddFlag && !(gSaveContext.eventChkInf[2] & 0x20)) {
if (gSaveContext.infTable[15] & 0x1000) {
EnOssan_SetStateCantGetItem(globalCtx, this, 0x302E);
} else {
this->stickLeftPrompt.isEnabled = false;
this->stickRightPrompt.isEnabled = false;
this->drawCursor = 0;
this->stateFlag = OSSAN_STATE_DISPLAY_ONLY_BOMB_DIALOG;
}
} else {
EnOssan_HandleCanBuyBombs(globalCtx, this);
}
} else {
EnOssan_HandleCanBuyBombs(globalCtx, this);
}
}
void EnOssan_State_ItemSelected(EnOssan* this, GlobalContext* globalCtx2, Player* player) {
GlobalContext* globalCtx = globalCtx2; // Necessary for OKs
if (!EnOssan_TakeItemOffShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
return;
}
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE &&
!EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && Message_ShouldAdvance(globalCtx)) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
EnOssan_HandleCanBuyItem(globalCtx, this);
break;
case 1:
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
break;
}
}
}
void EnOssan_State_SelectMilkBottle(EnOssan* this, GlobalContext* globalCtx2, Player* player) {
GlobalContext* globalCtx = globalCtx2; // Need for OK
if (!EnOssan_TakeItemOffShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
return;
}
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE &&
!EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && Message_ShouldAdvance(globalCtx)) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
EnOssan_HandleCanBuyLonLonMilk(globalCtx, this);
break;
case 1:
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
break;
}
}
}
void EnOssan_State_SelectWeirdEgg(EnOssan* this, GlobalContext* globalCtx2, Player* player) {
GlobalContext* globalCtx = globalCtx2; // Needed for OK
if (!EnOssan_TakeItemOffShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
return;
}
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE &&
!EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && Message_ShouldAdvance(globalCtx)) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
EnOssan_HandleCanBuyWeirdEgg(globalCtx, this);
break;
case 1:
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
break;
}
}
}
void EnOssan_State_SelectUnimplementedItem(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (!EnOssan_TakeItemOffShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
return;
}
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
}
}
void EnOssan_State_SelectBombs(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (!EnOssan_TakeItemOffShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
return;
}
osSyncPrintf("店主の依頼 ( %d )\n", gSaveContext.infTable[15] & 0x1000);
if (this->actor.params != OSSAN_TYPE_GORON) {
EnOssan_State_ItemSelected(this, globalCtx, player);
return;
}
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_CHOICE &&
!EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) && Message_ShouldAdvance(globalCtx)) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
EnOssan_BuyGoronCityBombs(globalCtx, this);
break;
case 1:
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
break;
}
}
}
void EnOssan_State_SelectMaskItem(EnOssan* this, GlobalContext* globalCtx, Player* player) {
u8 talkState = Message_GetState(&globalCtx->msgCtx);
EnGirlA* item = this->shelfSlots[this->cursorIndex];
if (!EnOssan_TakeItemOffShelf(this)) {
osSyncPrintf("%s[%d]:" VT_FGCOL(GREEN) "ズーム中!!" VT_RST "\n", __FILE__, __LINE__);
return;
}
if (talkState == TEXT_STATE_EVENT) {
if (Message_ShouldAdvance(globalCtx)) {
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
}
} else if (talkState == TEXT_STATE_CHOICE &&
!EnOssan_TestCancelOption(this, globalCtx, &globalCtx->state.input[0]) &&
Message_ShouldAdvance(globalCtx)) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
switch (item->actor.params) {
case SI_KEATON_MASK:
gSaveContext.itemGetInf[2] |= 0x08;
break;
case SI_SPOOKY_MASK:
gSaveContext.itemGetInf[2] |= 0x20;
break;
case SI_SKULL_MASK:
gSaveContext.itemGetInf[2] |= 0x10;
break;
case SI_BUNNY_HOOD:
gSaveContext.itemGetInf[2] |= 0x40;
break;
case SI_MASK_OF_TRUTH:
case SI_ZORA_MASK:
case SI_GORON_MASK:
case SI_GERUDO_MASK:
break;
}
EnOssan_GiveItemWithFanfare(globalCtx, this);
this->drawCursor = 0;
this->shopItemSelectedTween = 0.0f;
item->setOutOfStockFunc(globalCtx, item);
break;
case 1:
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
break;
}
}
}
void EnOssan_State_CantGetItem(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
}
}
void EnOssan_State_QuickBuyDialog(EnOssan* this, GlobalContext* globalCtx, Player* player) {
EnGirlA* item;
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
this->shopItemSelectedTween = 0.0f;
EnOssan_ResetItemPosition(this);
item = this->shelfSlots[this->cursorIndex];
item->updateStockedItemFunc(globalCtx, item);
this->stateFlag = this->tempStateFlag;
Message_ContinueTextbox(globalCtx, this->shelfSlots[this->cursorIndex]->actor.textId);
}
}
void EnOssan_State_GiveItemWithFanfare(EnOssan* this, GlobalContext* globalCtx, Player* player) {
// The player sets itself as the parent actor to signal that it has obtained the give item request
if (Actor_HasParent(&this->actor, globalCtx)) {
this->actor.parent = NULL;
this->stateFlag = OSSAN_STATE_ITEM_PURCHASED;
return;
}
func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f);
}
void EnOssan_State_ItemPurchased(EnOssan* this, GlobalContext* globalCtx, Player* player) {
EnGirlA* item;
EnGirlA* itemTemp;
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx)) {
if (this->actor.params == OSSAN_TYPE_MASK) {
itemTemp = this->shelfSlots[this->cursorIndex];
EnOssan_ResetItemPosition(this);
item = this->shelfSlots[this->cursorIndex];
item->updateStockedItemFunc(globalCtx, item);
if (itemTemp->actor.params == SI_MASK_OF_TRUTH && !(gSaveContext.itemGetInf[3] & 0x8000)) {
gSaveContext.itemGetInf[3] |= 0x8000;
Message_ContinueTextbox(globalCtx, 0x70AB);
this->happyMaskShopState = OSSAN_HAPPY_STATE_BORROWED_FIRST_MASK;
EnOssan_UpdateShopOfferings(this, globalCtx);
this->stateFlag = OSSAN_STATE_START_CONVERSATION;
return;
} else {
EnOssan_EndInteraction(globalCtx, this);
return;
}
}
item = this->shelfSlots[this->cursorIndex];
item->buyEventFunc(globalCtx, item);
this->stateFlag = OSSAN_STATE_CONTINUE_SHOPPING_PROMPT;
Message_ContinueTextbox(globalCtx, 0x6B);
}
}
void EnOssan_State_ContinueShoppingPrompt(EnOssan* this, GlobalContext* globalCtx, Player* player) {
EnGirlA* selectedItem;
u8 talkState = Message_GetState(&globalCtx->msgCtx);
if (talkState == TEXT_STATE_CHOICE) {
if (Message_ShouldAdvance(globalCtx)) {
EnOssan_ResetItemPosition(this);
selectedItem = this->shelfSlots[this->cursorIndex];
selectedItem->updateStockedItemFunc(globalCtx, selectedItem);
if (!EnOssan_TestEndInteraction(this, globalCtx, &globalCtx->state.input[0])) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
osSyncPrintf(VT_FGCOL(YELLOW) "★★★ 続けるよ!! ★★★" VT_RST "\n");
player->actor.shape.rot.y += 0x8000;
player->stateFlags2 |= 0x20000000;
func_800BC490(globalCtx, 2);
Message_StartTextbox(globalCtx, this->actor.textId, &this->actor);
EnOssan_SetStateStartShopping(globalCtx, this, true);
func_8002F298(&this->actor, globalCtx, 100.0f, -1);
break;
case 1:
default:
osSyncPrintf(VT_FGCOL(YELLOW) "★★★ やめるよ!! ★★★" VT_RST "\n");
EnOssan_EndInteraction(globalCtx, this);
break;
}
}
}
} else if (talkState == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
EnOssan_ResetItemPosition(this);
selectedItem = this->shelfSlots[this->cursorIndex];
selectedItem->updateStockedItemFunc(globalCtx, selectedItem);
player->actor.shape.rot.y += 0x8000;
player->stateFlags2 |= 0x20000000;
func_800BC490(globalCtx, 2);
Message_StartTextbox(globalCtx, this->actor.textId, &this->actor);
EnOssan_SetStateStartShopping(globalCtx, this, true);
func_8002F298(&this->actor, globalCtx, 100.0f, -1);
}
}
void EnOssan_State_WaitForDisplayOnlyBombDialog(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
gSaveContext.infTable[15] |= 0x1000;
EnOssan_StartShopping(globalCtx, this);
}
}
// Unreachable
void EnOssan_State_21(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE_HAS_NEXT && Message_ShouldAdvance(globalCtx)) {
this->stateFlag = OSSAN_STATE_22;
Message_ContinueTextbox(globalCtx, 0x3012);
gSaveContext.infTable[15] |= 0x1000;
}
}
// Unreachable
void EnOssan_State_22(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
EnOssan_StartShopping(globalCtx, this);
}
}
void EnOssan_State_GiveLonLonMilk(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
EnOssan_GiveItemWithFanfare(globalCtx, this);
}
}
// For giving Mask of Truth when you first sell all masks
void EnOssan_State_LendMaskOfTruth(EnOssan* this, GlobalContext* globalCtx, Player* player) {
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_EVENT && Message_ShouldAdvance(globalCtx)) {
gSaveContext.itemGetInf[2] |= 0x400;
this->cursorIndex = 2;
EnOssan_GiveItemWithFanfare(globalCtx, this);
}
}
// Hylian Shield discount dialog
void EnOssan_SetStateGiveDiscountDialog(GlobalContext* globalCtx, EnOssan* this) {
Message_ContinueTextbox(globalCtx, 0x71B2);
this->stateFlag = OSSAN_STATE_DISCOUNT_DIALOG;
}
void EnOssan_State_GiveDiscountDialog(EnOssan* this, GlobalContext* globalCtx, Player* player) {
EnGirlA* selectedItem;
if (Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE && Message_ShouldAdvance(globalCtx)) {
selectedItem = this->shelfSlots[this->cursorIndex];
EnOssan_GiveItemWithFanfare(globalCtx, this);
this->drawCursor = 0;
this->shopItemSelectedTween = 0.0f;
selectedItem->setOutOfStockFunc(globalCtx, selectedItem);
}
}
void EnOssan_PositionSelectedItem(EnOssan* this) {
EnGirlA* item;
u8 i;
u8 i2;
ShopItem* shopItem;
f32 tx;
f32 ty;
f32 tz;
i = this->cursorIndex;
shopItem = &sShopkeeperStores[this->actor.params][i];
item = this->shelfSlots[i];
i2 = i >> 2;
tx = (sSelectedItemPosition[i2].x - shopItem->xOffset) * this->shopItemSelectedTween + shopItem->xOffset;
ty = (sSelectedItemPosition[i2].y - shopItem->yOffset) * this->shopItemSelectedTween + shopItem->yOffset;
tz = (sSelectedItemPosition[i2].z - shopItem->zOffset) * this->shopItemSelectedTween + shopItem->zOffset;
item->actor.world.pos.x = this->shelves->actor.world.pos.x + tx;
item->actor.world.pos.y = this->shelves->actor.world.pos.y + ty;
item->actor.world.pos.z = this->shelves->actor.world.pos.z + tz;
}
void EnOssan_ResetItemPosition(EnOssan* this) {
this->shopItemSelectedTween = 0.0f;
EnOssan_PositionSelectedItem(this);
}
// returns true if animation has completed
s32 EnOssan_TakeItemOffShelf(EnOssan* this) {
Math_ApproachF(&this->shopItemSelectedTween, 1.0f, 1.0f, 0.15f);
if (this->shopItemSelectedTween >= 0.85f) {
this->shopItemSelectedTween = 1.0f;
}
EnOssan_PositionSelectedItem(this);
if (this->shopItemSelectedTween == 1.0f) {
return true;
} else {
return false;
}
}
// returns true if animation has completed
s32 EnOssan_ReturnItemToShelf(EnOssan* this) {
Math_ApproachF(&this->shopItemSelectedTween, 0.0f, 1.0f, 0.15f);
if (this->shopItemSelectedTween <= 0.15f) {
this->shopItemSelectedTween = 0.0f;
}
EnOssan_PositionSelectedItem(this);
if (this->shopItemSelectedTween == 0.0f) {
return true;
} else {
return false;
}
}
void EnOssan_UpdateItemSelectedProperty(EnOssan* this) {
EnGirlA** temp_a1 = this->shelfSlots;
s32 i;
for (i = 0; i < 8; i++) {
if (temp_a1[0] != NULL) {
if (this->stateFlag != OSSAN_STATE_SELECT_ITEM && this->stateFlag != OSSAN_STATE_SELECT_ITEM_MILK_BOTTLE &&
this->stateFlag != OSSAN_STATE_SELECT_ITEM_WEIRD_EGG &&
this->stateFlag != OSSAN_STATE_SELECT_ITEM_UNIMPLEMENTED &&
this->stateFlag != OSSAN_STATE_SELECT_ITEM_BOMBS && this->stateFlag != OSSAN_STATE_SELECT_ITEM_MASK &&
this->stateFlag != OSSAN_STATE_CANT_GET_ITEM && this->drawCursor == 0) {
temp_a1[0]->isSelected = false;
} else {
if (this->cursorIndex == i) {
temp_a1[0]->isSelected = true;
} else {
temp_a1[0]->isSelected = false;
}
}
}
temp_a1++;
}
}
void EnOssan_UpdateCursorAnim(EnOssan* this) {
Color_RGB8 A_button_ori = {0,255,80};
Color_RGB8 A_button = CVar_GetRGB("gCCABtnPrim", A_button_ori);
f32 t;
t = this->cursorAnimTween;
if (this->cursorAnimState == 0) {
t += 0.05f;
if (t >= 1.0f) {
t = 1.0f;
this->cursorAnimState = 1;
}
} else {
t -= 0.05f;
if (t <= 0.0f) {
t = 0.0f;
this->cursorAnimState = 0;
}
}
if (CVar_GetS32("gHudColors", 1) == 0) {
this->cursorColorR = ColChanMix(0, 0.0f, t);
this->cursorColorG = ColChanMix(80, 80.0f, t);
this->cursorColorB = ColChanMix(255, 0.0f, t);
} else if (CVar_GetS32("gHudColors", 1) == 1) {
this->cursorColorR = ColChanMix(A_button_ori.r, 0.0f, t);
this->cursorColorG = ColChanMix(A_button_ori.b, 80.0f, t);
this->cursorColorB = ColChanMix(A_button_ori.r, 0.0f, t);
} else if (CVar_GetS32("gHudColors", 1) == 2) {
this->cursorColorR = ColChanMix(A_button.r, ((A_button.r/255)*100), t);
this->cursorColorG = ColChanMix(A_button.g, ((A_button.g/255)*100), t);
this->cursorColorB = ColChanMix(A_button.b, ((A_button.b/255)*100), t);
}
this->cursorColorA = ColChanMix(255, 0.0f, t);
this->cursorAnimTween = t;
}
void EnOssan_UpdateStickDirectionPromptAnim(EnOssan* this) {
f32 arrowAnimTween;
f32 new_var3; // likely fake temp
s32 new_var2 = 255; // likely fake temp
f32 stickAnimTween;
arrowAnimTween = this->arrowAnimTween;
stickAnimTween = this->stickAnimTween;
if (this->arrowAnimState == 0) {
arrowAnimTween += 0.05f;
if (arrowAnimTween > 1.0f) {
arrowAnimTween = 1.0f;
this->arrowAnimState = 1;
}
} else {
arrowAnimTween -= 0.05f;
if (arrowAnimTween < 0.0f) {
arrowAnimTween = 0.0f;
this->arrowAnimState = 0;
}
}
this->arrowAnimTween = arrowAnimTween;
if (this->stickAnimState == 0) {
stickAnimTween += 0.1f;
if (stickAnimTween > 1.0f) {
stickAnimTween = 1.0f;
this->stickAnimState = 1;
}
} else {
stickAnimTween = 0.0f;
this->stickAnimState = 0;
}
this->stickAnimTween = stickAnimTween;
this->stickLeftPrompt.arrowColorR = (u8)(255 - ((s32)(155.0f * arrowAnimTween)));
this->stickLeftPrompt.arrowColorG = (u8)(new_var2 - (s32)(155.0f * arrowAnimTween));
new_var3 = (155.0f * arrowAnimTween);
this->stickLeftPrompt.arrowColorB = (u8)(0 - ((s32)((-100.0f) * arrowAnimTween)));
this->stickLeftPrompt.arrowColorA = (u8)(200 - ((s32)(50.0f * arrowAnimTween)));
this->stickRightPrompt.arrowColorR = (u8)(new_var2 - (s32)new_var3);
this->stickRightPrompt.arrowColorG = (u8)(255 - (s32)new_var3);
this->stickRightPrompt.arrowColorB = (u8)(0 - ((s32)((-100.0f) * arrowAnimTween)));
this->stickRightPrompt.arrowColorA = (u8)(200 - ((s32)(50.0f * arrowAnimTween)));
this->stickRightPrompt.arrowTexX = 290.0f;
this->stickLeftPrompt.arrowTexX = 33.0f;
this->stickRightPrompt.stickTexX = 274.0f;
this->stickLeftPrompt.stickTexX = 49.0f;
this->stickRightPrompt.stickTexX += (8.0f * stickAnimTween);
this->stickLeftPrompt.stickTexX -= (8.0f * stickAnimTween);
this->stickLeftPrompt.arrowTexY = this->stickRightPrompt.arrowTexY = 91.0f;
this->stickLeftPrompt.stickTexY = this->stickRightPrompt.stickTexY = 95.0f;
}
void EnOssan_WaitForBlink(EnOssan* this) {
s16 decr = this->blinkTimer - 1;
if (decr != 0) {
this->blinkTimer = decr;
} else {
this->blinkFunc = EnOssan_Blink;
}
}
void EnOssan_Blink(EnOssan* this) {
s16 decr;
s16 eyeTextureIdxTemp;
decr = this->blinkTimer - 1;
if (decr != 0) {
this->blinkTimer = decr;
return;
}
eyeTextureIdxTemp = this->eyeTextureIdx + 1;
if (eyeTextureIdxTemp > 2) {
this->eyeTextureIdx = 0;
this->blinkTimer = (s32)(Rand_ZeroOne() * 60.0f) + 20;
this->blinkFunc = EnOssan_WaitForBlink;
} else {
this->eyeTextureIdx = eyeTextureIdxTemp;
this->blinkTimer = 1;
}
}
s32 EnOssan_AreShopkeeperObjectsLoaded(EnOssan* this, GlobalContext* globalCtx) {
if (Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex1)) {
if (this->objBankIndex2 >= 0 && !Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex2)) {
return false;
}
if (this->objBankIndex3 >= 0 && !Object_IsLoaded(&globalCtx->objectCtx, this->objBankIndex3)) {
return false;
}
return true;
}
return false;
}
void EnOssan_InitBazaarShopkeeper(EnOssan* this, GlobalContext* globalCtx) {
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gObjectOssanSkel, &gObjectOssanAnim_000338, NULL, NULL, 0);
this->actor.draw = EnOssan_DrawBazaarShopkeeper;
this->obj3ToSeg6Func = NULL;
}
void EnOssan_InitKokiriShopkeeper(EnOssan* this, GlobalContext* globalCtx) {
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gKm1Skel, NULL, NULL, NULL, 0);
gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objBankIndex3].segment);
Animation_Change(&this->skelAnime, &object_masterkokiri_Anim_0004A8, 1.0f, 0.0f,
Animation_GetLastFrame(&object_masterkokiri_Anim_0004A8), 0, 0.0f);
this->actor.draw = EnOssan_DrawKokiriShopkeeper;
this->obj3ToSeg6Func = EnOssan_Obj3ToSeg6;
Actor_SpawnAsChild(&globalCtx->actorCtx, &this->actor, globalCtx, ACTOR_EN_ELF, this->actor.world.pos.x,
this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, FAIRY_KOKIRI);
}
void EnOssan_InitGoronShopkeeper(EnOssan* this, GlobalContext* globalCtx) {
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gGoronSkel, NULL, NULL, NULL, 0);
gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objBankIndex3].segment);
Animation_Change(&this->skelAnime, &gGoronShopkeeperAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gGoronShopkeeperAnim),
0, 0.0f);
this->actor.draw = EnOssan_DrawGoronShopkeeper;
this->obj3ToSeg6Func = EnOssan_Obj3ToSeg6;
}
void EnOssan_InitZoraShopkeeper(EnOssan* this, GlobalContext* globalCtx) {
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &gZoraSkel, NULL, NULL, NULL, 0);
gSegments[6] = PHYSICAL_TO_VIRTUAL(globalCtx->objectCtx.status[this->objBankIndex3].segment);
Animation_Change(&this->skelAnime, &gZoraShopkeeperAnim, 1.0f, 0.0f, Animation_GetLastFrame(&gZoraShopkeeperAnim),
0, 0.0f);
this->actor.draw = EnOssan_DrawZoraShopkeeper;
this->obj3ToSeg6Func = EnOssan_Obj3ToSeg6;
}
void EnOssan_InitPotionShopkeeper(EnOssan* this, GlobalContext* globalCtx) {
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_ds2_Skel_004258, &object_ds2_Anim_0002E4, 0, 0, 0);
this->actor.draw = EnOssan_DrawPotionShopkeeper;
this->obj3ToSeg6Func = NULL;
}
void EnOssan_InitHappyMaskShopkeeper(EnOssan* this, GlobalContext* globalCtx) {
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_os_Skel_004658, &object_os_Anim_0002E4, NULL, NULL, 0);
this->actor.draw = EnOssan_DrawHappyMaskShopkeeper;
this->obj3ToSeg6Func = NULL;
}
void EnOssan_InitBombchuShopkeeper(EnOssan* this, GlobalContext* globalCtx) {
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_rs_Skel_004868, &object_rs_Anim_00065C, 0, 0, 0);
this->actor.draw = EnOssan_DrawBombchuShopkeeper;
this->obj3ToSeg6Func = NULL;
}
u16 EnOssan_SetupHelloDialog(EnOssan* this) {
this->happyMaskShopState = OSSAN_HAPPY_STATE_NONE;
// mask shop messages
if (this->actor.params == OSSAN_TYPE_MASK) {
if (INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_SOLD_OUT) {
if (gSaveContext.itemGetInf[3] & 0x800) {
if (!(gSaveContext.eventChkInf[8] & 0x8000)) {
// Pay back Bunny Hood
this->happyMaskShopState = OSSAN_HAPPY_STATE_REQUEST_PAYMENT_BUNNY_HOOD;
return 0x70C6;
} else {
return 0x70AC;
}
}
if (gSaveContext.itemGetInf[3] & 0x400) {
if (!(gSaveContext.eventChkInf[8] & 0x4000)) {
// Pay back Spooky Mask
this->happyMaskShopState = OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SPOOKY_MASK;
return 0x70C5;
} else {
return 0x70AC;
}
}
if (gSaveContext.itemGetInf[3] & 0x200) {
if (!(gSaveContext.eventChkInf[8] & 0x2000)) {
// Pay back Skull Mask
this->happyMaskShopState = OSSAN_HAPPY_STATE_REQUEST_PAYMENT_SKULL_MASK;
return 0x70C4;
} else {
return 0x70AC;
}
}
if (gSaveContext.itemGetInf[3] & 0x100) {
if (!(gSaveContext.eventChkInf[8] & 0x1000)) {
// Pay back Keaton Mask
this->happyMaskShopState = OSSAN_HAPPY_STATE_REQUEST_PAYMENT_KEATON_MASK;
return 0x70A5;
} else {
return 0x70AC;
}
}
} else {
if (gSaveContext.itemGetInf[3] & 0x800) {
return 0x70AC;
} else if (!(gSaveContext.itemGetInf[3] & 0x400) && !(gSaveContext.itemGetInf[2] & 0x10) &&
!(gSaveContext.itemGetInf[3] & 0x100)) {
// Haven't borrowed the Keaton Mask
if (!(gSaveContext.itemGetInf[2] & 0x8)) {
return 0x70A1;
} else {
// Haven't sold the Keaton Mask
this->happyMaskShopState = OSSAN_HAPPY_STATE_BORROWED_FIRST_MASK;
return 0x70A6;
}
} else {
return 0x70C7;
}
}
}
return 0x9E;
}
void EnOssan_InitActionFunc(EnOssan* this, GlobalContext* globalCtx) {
ShopItem* items;
if (EnOssan_AreShopkeeperObjectsLoaded(this, globalCtx)) {
this->actor.flags &= ~ACTOR_FLAG_4;
this->actor.objBankIndex = this->objBankIndex1;
Actor_SetObjectDependency(globalCtx, &this->actor);
this->shelves = (EnTana*)Actor_Find(&globalCtx->actorCtx, ACTOR_EN_TANA, ACTORCAT_PROP);
if (this->shelves == NULL) {
osSyncPrintf(VT_COL(RED, WHITE));
// "Warning!! There are no shelves!!"
osSyncPrintf("★★★ 警告!! 棚がないよ!! ★★★\n");
osSyncPrintf(VT_RST);
return;
}
// "Shopkeeper (params) init"
osSyncPrintf(VT_FGCOL(YELLOW) "◇◇◇ 店のおやじ( %d ) 初期設定 ◇◇◇" VT_RST "\n", this->actor.params);
this->actor.world.pos.x += sShopkeeperPositionOffsets[this->actor.params].x;
this->actor.world.pos.y += sShopkeeperPositionOffsets[this->actor.params].y;
this->actor.world.pos.z += sShopkeeperPositionOffsets[this->actor.params].z;
items = sShopkeeperStores[this->actor.params];
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 20.0f);
sInitFuncs[this->actor.params](this, globalCtx);
this->actor.textId = EnOssan_SetupHelloDialog(this);
this->cursorY = this->cursorX = 100.0f;
this->actor.colChkInfo.mass = MASS_IMMOVABLE;
this->actor.colChkInfo.cylRadius = 50;
this->stateFlag = OSSAN_STATE_IDLE;
this->stickAccumX = this->stickAccumY = 0;
this->cursorIndex = 0;
this->cursorZ = 1.5f;
this->cursorColorR = 0;
this->cursorColorG = 255;
this->cursorColorB = 80;
this->cursorColorA = 255;
this->cursorAnimTween = 0;
this->cursorAnimState = 0;
this->drawCursor = 0;
this->happyMaskShopkeeperEyeIdx = 0;
this->stickLeftPrompt.stickColorR = 200;
this->stickLeftPrompt.stickColorG = 200;
this->stickLeftPrompt.stickColorB = 200;
this->stickLeftPrompt.stickColorA = 180;
this->stickLeftPrompt.stickTexX = 49;
this->stickLeftPrompt.stickTexY = 95;
this->stickLeftPrompt.arrowColorR = 255;
this->stickLeftPrompt.arrowColorG = 255;
this->stickLeftPrompt.arrowColorB = 0;
this->stickLeftPrompt.arrowColorA = 200;
this->stickLeftPrompt.arrowTexX = 33;
this->stickLeftPrompt.arrowTexY = 91;
this->stickLeftPrompt.z = 1;
this->stickLeftPrompt.isEnabled = false;
this->stickRightPrompt.stickColorR = 200;
this->stickRightPrompt.stickColorG = 200;
this->stickRightPrompt.stickColorB = 200;
this->stickRightPrompt.stickColorA = 180;
this->stickRightPrompt.stickTexX = 274;
this->stickRightPrompt.stickTexY = 95;
this->stickRightPrompt.arrowColorR = 255;
this->stickRightPrompt.arrowColorG = 255;
this->stickRightPrompt.arrowColorB = 0;
this->stickRightPrompt.arrowColorA = 200;
this->stickRightPrompt.arrowTexX = 290;
this->stickRightPrompt.arrowTexY = 91;
this->stickRightPrompt.z = 1;
this->stickRightPrompt.isEnabled = false;
this->arrowAnimState = 0;
this->stickAnimState = 0;
this->arrowAnimTween = 0;
this->stickAnimTween = 0;
this->shopItemSelectedTween = 0;
Actor_SetScale(&this->actor, sShopkeeperScale[this->actor.params]);
EnOssan_SpawnItemsOnShelves(this, globalCtx, items);
this->headRot = this->headTargetRot = 0;
this->blinkTimer = 20;
this->eyeTextureIdx = 0;
this->blinkFunc = EnOssan_WaitForBlink;
this->actor.flags &= ~ACTOR_FLAG_0;
EnOssan_SetupAction(this, EnOssan_MainActionFunc);
}
}
void EnOssan_Obj3ToSeg6(EnOssan* this, GlobalContext* globalCtx) {
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndex3].segment);
}
void EnOssan_MainActionFunc(EnOssan* this, GlobalContext* globalCtx) {
Player* player = GET_PLAYER(globalCtx);
this->blinkFunc(this);
EnOssan_UpdateJoystickInputState(globalCtx, this);
EnOssan_UpdateItemSelectedProperty(this);
EnOssan_UpdateStickDirectionPromptAnim(this);
EnOssan_UpdateCursorAnim(this);
Math_StepToS(&this->headRot, this->headTargetRot, 0x190);
if (player != NULL) {
sStateFunc[this->stateFlag](this, globalCtx, player);
}
Actor_MoveForward(&this->actor);
Actor_UpdateBgCheckInfo(globalCtx, &this->actor, 26.0f, 10.0f, 0.0f, 5);
Actor_SetFocus(&this->actor, 90.0f);
Actor_SetScale(&this->actor, sShopkeeperScale[this->actor.params]);
// use animation object if needed
if (this->obj3ToSeg6Func != NULL) {
this->obj3ToSeg6Func(this, globalCtx);
}
SkelAnime_Update(&this->skelAnime);
}
void EnOssan_Update(Actor* thisx, GlobalContext* globalCtx) {
EnOssan* this = (EnOssan*)thisx;
this->timer++;
this->actionFunc(this, globalCtx);
}
s32 EnOssan_OverrideLimbDrawDefaultShopkeeper(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos,
Vec3s* rot, void* thisx) {
EnOssan* this = (EnOssan*)thisx;
if (limbIndex == 8) {
rot->x += this->headRot;
}
return 0;
}
void EnOssan_DrawCursor(GlobalContext* globalCtx, EnOssan* this, f32 x, f32 y, f32 z, u8 drawCursor) {
s32 ulx, uly, lrx, lry;
f32 w;
s32 dsdx;
OPEN_DISPS(globalCtx->state.gfxCtx);
if (drawCursor != 0) {
func_80094520(globalCtx->state.gfxCtx);
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, this->cursorColorR, this->cursorColorG, this->cursorColorB,
this->cursorColorA);
gDPLoadTextureBlock_4b(OVERLAY_DISP++, gSelectionCursorTex, G_IM_FMT_IA, 16, 16, 0, G_TX_MIRROR | G_TX_WRAP,
G_TX_MIRROR | G_TX_WRAP, 4, 4, G_TX_NOLOD, G_TX_NOLOD);
w = 16.0f * z;
ulx = (x - w) * 4.0f;
uly = (y - w) * 4.0f;
lrx = (x + w) * 4.0f;
lry = (y + w) * 4.0f;
dsdx = (1.0f / z) * 1024.0f;
gSPTextureRectangle(OVERLAY_DISP++, ulx, uly, lrx, lry, G_TX_RENDERTILE, 0, 0, dsdx, dsdx);
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void EnOssan_DrawTextRec(GlobalContext* globalCtx, s32 r, s32 g, s32 b, s32 a, f32 x, f32 y, f32 z, s32 s, s32 t,
f32 dx, f32 dy) {
f32 unk;
s32 ulx, uly, lrx, lry;
f32 w, h;
s32 dsdx, dtdy;
OPEN_DISPS(globalCtx->state.gfxCtx);
gDPPipeSync(OVERLAY_DISP++);
gDPSetPrimColor(OVERLAY_DISP++, 0, 0, r, g, b, a);
w = 8.0f * z;
h = 12.0f * z;
unk = (1.0f / z) * 1024;
dsdx = unk * dx;
dtdy = dy * unk;
ulx = (x - w) * 4.0f;
uly = (y - h) * 4.0f;
lrx = (x + w) * 4.0f;
lry = (y + h) * 4.0f;
gSPTextureRectangle(OVERLAY_DISP++, ulx, uly, lrx, lry, G_TX_RENDERTILE, s, t, dsdx, dtdy);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void EnOssan_DrawStickDirectionPrompts(GlobalContext* globalCtx, EnOssan* this) {
s32 drawStickLeftPrompt = this->stickLeftPrompt.isEnabled;
s32 drawStickRightPrompt = this->stickRightPrompt.isEnabled;
OPEN_DISPS(globalCtx->state.gfxCtx);
if (drawStickLeftPrompt || drawStickRightPrompt) {
func_80094520(globalCtx->state.gfxCtx);
gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM);
gDPLoadTextureBlock(OVERLAY_DISP++, gArrowCursorTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 24, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOLOD);
if (drawStickLeftPrompt) {
EnOssan_DrawTextRec(globalCtx, this->stickLeftPrompt.arrowColorR, this->stickLeftPrompt.arrowColorG,
this->stickLeftPrompt.arrowColorB, this->stickLeftPrompt.arrowColorA,
this->stickLeftPrompt.arrowTexX, this->stickLeftPrompt.arrowTexY,
this->stickLeftPrompt.z, 0, 0, -1.0f, 1.0f);
}
if (drawStickRightPrompt) {
EnOssan_DrawTextRec(globalCtx, this->stickRightPrompt.arrowColorR, this->stickRightPrompt.arrowColorG,
this->stickRightPrompt.arrowColorB, this->stickRightPrompt.arrowColorA,
this->stickRightPrompt.arrowTexX, this->stickRightPrompt.arrowTexY,
this->stickRightPrompt.z, 0, 0, 1.0f, 1.0f);
}
gDPLoadTextureBlock(OVERLAY_DISP++, gControlStickTex, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, 0,
G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, G_TX_NOMASK, G_TX_NOLOD,
G_TX_NOLOD);
if (drawStickLeftPrompt) {
EnOssan_DrawTextRec(globalCtx, this->stickLeftPrompt.stickColorR, this->stickLeftPrompt.stickColorG,
this->stickLeftPrompt.stickColorB, this->stickLeftPrompt.stickColorA,
this->stickLeftPrompt.stickTexX, this->stickLeftPrompt.stickTexY,
this->stickLeftPrompt.z, 0, 0, -1.0f, 1.0f);
}
if (drawStickRightPrompt) {
EnOssan_DrawTextRec(globalCtx, this->stickRightPrompt.stickColorR, this->stickRightPrompt.stickColorG,
this->stickRightPrompt.stickColorB, this->stickRightPrompt.stickColorA,
this->stickRightPrompt.stickTexX, this->stickRightPrompt.stickTexY,
this->stickRightPrompt.z, 0, 0, 1.0f, 1.0f);
}
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void EnOssan_DrawBazaarShopkeeper(Actor* thisx, GlobalContext* globalCtx) {
static void* sBazaarShopkeeperEyeTextures[] = { gOssanEyeOpenTex, gOssanEyeHalfTex, gOssanEyeClosedTex };
EnOssan* this = (EnOssan*)thisx;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sBazaarShopkeeperEyeTextures[this->eyeTextureIdx]));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
EnOssan_OverrideLimbDrawDefaultShopkeeper, NULL, this);
EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor);
EnOssan_DrawStickDirectionPrompts(globalCtx, this);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
s32 EnOssan_OverrideLimbDrawKokiriShopkeeper(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos,
Vec3s* rot, void* thisx) {
static void* sKokiriShopkeeperEyeTextures[] = {
gKokiriShopkeeperEyeDefaultTex,
gKokiriShopkeeperEyeHalfTex,
gKokiriShopkeeperEyeOpenTex,
};
EnOssan* this = (EnOssan*)thisx;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
if (limbIndex == 15) {
gSPSegment(POLY_OPA_DISP++, 0x06, globalCtx->objectCtx.status[this->objBankIndex2].segment);
gSegments[6] = VIRTUAL_TO_PHYSICAL(globalCtx->objectCtx.status[this->objBankIndex2].segment);
*dList = gKokiriShopkeeperHeadDL;
gSPSegment(POLY_OPA_DISP++, 0x0A, SEGMENTED_TO_VIRTUAL(sKokiriShopkeeperEyeTextures[this->eyeTextureIdx]));
}
CLOSE_DISPS(globalCtx->state.gfxCtx);
return 0;
}
Gfx* EnOssan_EndDList(GraphicsContext* gfxCtx) {
Gfx* disp = Graph_Alloc(gfxCtx, sizeof(Gfx));
gSPEndDisplayList(disp);
return disp;
}
Gfx* EnOssan_SetEnvColor(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b, u8 a) {
Gfx* disp = Graph_Alloc(gfxCtx, sizeof(Gfx) * 2);
gDPSetEnvColor(disp, r, g, b, a);
gSPEndDisplayList(disp + 1);
return disp;
}
void EnOssan_DrawKokiriShopkeeper(Actor* thisx, GlobalContext* globalCtx) {
EnOssan* this = (EnOssan*)thisx;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255);
gSPSegment(POLY_OPA_DISP++, 0x08, EnOssan_SetEnvColor(globalCtx->state.gfxCtx, 0, 130, 70, 255));
gSPSegment(POLY_OPA_DISP++, 0x09, EnOssan_SetEnvColor(globalCtx->state.gfxCtx, 110, 170, 20, 255));
gSPSegment(POLY_OPA_DISP++, 0x0C, EnOssan_EndDList(globalCtx->state.gfxCtx));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
EnOssan_OverrideLimbDrawKokiriShopkeeper, NULL, this);
EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor);
EnOssan_DrawStickDirectionPrompts(globalCtx, this);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void EnOssan_DrawGoronShopkeeper(Actor* thisx, GlobalContext* globalCtx) {
static void* sGoronShopkeeperEyeTextures[] = { gGoronCsEyeOpenTex, gGoronCsEyeHalfTex, gGoronCsEyeClosedTex };
EnOssan* this = (EnOssan*)thisx;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sGoronShopkeeperEyeTextures[this->eyeTextureIdx]));
gSPSegment(POLY_OPA_DISP++, 0x09, SEGMENTED_TO_VIRTUAL(gGoronCsMouthNeutralTex));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
NULL, NULL, this);
EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor);
EnOssan_DrawStickDirectionPrompts(globalCtx, this);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
s32 EnOssan_OverrideLimbDrawZoraShopkeeper(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
void* thisx) {
EnOssan* this = (EnOssan*)thisx;
if (limbIndex == 15) {
rot->x += this->headRot;
}
return 0;
}
void EnOssan_DrawZoraShopkeeper(Actor* thisx, GlobalContext* globalCtx) {
static void* sZoraShopkeeperEyeTextures[] = { gZoraEyeOpenTex, gZoraEyeHalfTex, gZoraEyeClosedTex };
EnOssan* this = (EnOssan*)thisx;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 255);
gSPSegment(POLY_OPA_DISP++, 0x0C, EnOssan_EndDList(globalCtx->state.gfxCtx));
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sZoraShopkeeperEyeTextures[this->eyeTextureIdx]));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
EnOssan_OverrideLimbDrawZoraShopkeeper, NULL, this);
EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor);
EnOssan_DrawStickDirectionPrompts(globalCtx, this);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void EnOssan_DrawPotionShopkeeper(Actor* thisx, GlobalContext* globalCtx) {
static void* sPotionShopkeeperEyeTextures[] = { gPotionShopkeeperEyeOpenTex, gPotionShopkeeperEyeHalfTex,
gPotionShopkeeperEyeClosedTex };
EnOssan* this = (EnOssan*)thisx;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sPotionShopkeeperEyeTextures[this->eyeTextureIdx]));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
NULL, NULL, this);
EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor);
EnOssan_DrawStickDirectionPrompts(globalCtx, this);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void EnOssan_DrawHappyMaskShopkeeper(Actor* thisx, GlobalContext* globalCtx) {
static void* sHappyMaskShopkeeperEyeTextures[] = { gOsEyeClosedTex, gOsEyeOpenTex };
EnOssan* this = (EnOssan*)thisx;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08,
SEGMENTED_TO_VIRTUAL(sHappyMaskShopkeeperEyeTextures[this->happyMaskShopkeeperEyeIdx]));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
NULL, NULL, this);
EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor);
EnOssan_DrawStickDirectionPrompts(globalCtx, this);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
void EnOssan_DrawBombchuShopkeeper(Actor* thisx, GlobalContext* globalCtx) {
static void* sBombchuShopkeeperEyeTextures[] = { gBombchuShopkeeperEyeOpenTex, gBombchuShopkeeperEyeHalfTex,
gBombchuShopkeeperEyeClosedTex };
EnOssan* this = (EnOssan*)thisx;
s32 pad;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_80093D18(globalCtx->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, SEGMENTED_TO_VIRTUAL(sBombchuShopkeeperEyeTextures[this->eyeTextureIdx]));
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
NULL, NULL, this);
EnOssan_DrawCursor(globalCtx, this, this->cursorX, this->cursorY, this->cursorZ, this->drawCursor);
EnOssan_DrawStickDirectionPrompts(globalCtx, this);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}