mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2024-08-13 17:03:47 -04:00
2574 lines
102 KiB
C
2574 lines
102 KiB
C
#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 (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHOPSANITY)) {
|
||
ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, i);
|
||
if (shopItemIdentity.randomizerCheck != RC_UNKNOWN_CHECK) {
|
||
itemParams = shopItemIdentity.enGirlAShopItem;
|
||
|
||
if (Flags_GetRandomizerInf(shopItemIdentity.randomizerInf)) {
|
||
itemParams = SI_SOLD_OUT;
|
||
}
|
||
}
|
||
}
|
||
|
||
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);
|
||
if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_SHOPSANITY)) {
|
||
this->shelfSlots[i]->randoSlotIndex = i;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
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 && !Flags_GetRandomizerInf(RAND_INF_DUNGEONS_DONE_FIRE_TEMPLE))) {
|
||
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("gDpadShop", 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("gDpadShop", 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("gDpadShop", 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("gDpadShop", 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");
|
||
if (!gSaveContext.n64ddFlag) {
|
||
func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f);
|
||
} else {
|
||
ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->cursorIndex);
|
||
if (shopItemIdentity.randomizerCheck != RC_UNKNOWN_CHECK) {
|
||
GetItemEntry getItemEntry = Randomizer_GetItemFromKnownCheck(shopItemIdentity.randomizerCheck, shopItemIdentity.ogItemId);
|
||
GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 120.0f, 120.0f);
|
||
} else {
|
||
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;
|
||
}
|
||
if (!gSaveContext.n64ddFlag) {
|
||
func_8002F434(&this->actor, globalCtx, this->shelfSlots[this->cursorIndex]->getItemId, 120.0f, 120.0f);
|
||
} else {
|
||
ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->cursorIndex);
|
||
if (shopItemIdentity.randomizerCheck != RC_UNKNOWN_CHECK) {
|
||
GetItemEntry getItemEntry =
|
||
Randomizer_GetItemFromKnownCheck(shopItemIdentity.randomizerCheck, shopItemIdentity.ogItemId);
|
||
GiveItemEntryFromActor(&this->actor, globalCtx, getItemEntry, 120.0f, 120.0f);
|
||
} else {
|
||
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;
|
||
ShopItemIdentity shopItemIdentity = Randomizer_IdentifyShopItem(globalCtx->sceneNum, this->cursorIndex);
|
||
|
||
if ((Message_GetState(&globalCtx->msgCtx) == TEXT_STATE_DONE) && Message_ShouldAdvance(globalCtx) || (gSaveContext.n64ddFlag && Randomizer_ItemIsIceTrap(shopItemIdentity.randomizerCheck, GI_NONE))) {
|
||
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);
|
||
}
|