diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/berry_pouch.c | 2 | ||||
-rw-r--r-- | src/buy_menu_helpers.c | 4 | ||||
-rw-r--r-- | src/fieldmap.c | 2 | ||||
-rw-r--r-- | src/pokeball.c | 1275 | ||||
-rw-r--r-- | src/sea_cottage_special_anim.c | 264 | ||||
-rw-r--r-- | src/shop.c | 1161 | ||||
-rw-r--r-- | src/tm_case.c | 2 |
7 files changed, 2705 insertions, 5 deletions
diff --git a/src/berry_pouch.c b/src/berry_pouch.c index bb1b106af..711a17768 100644 --- a/src/berry_pouch.c +++ b/src/berry_pouch.c @@ -1384,7 +1384,7 @@ static void Task_SellBerries_PlaySfxAndRemoveBerries(u8 taskId) PlaySE(SE_SHOP); RemoveBagItem(gSpecialVar_ItemId, data[8]); AddMoney(&gSaveBlock1Ptr->money, itemid_get_market_price(gSpecialVar_ItemId) / 2 * data[8]); - sub_809C09C(gSpecialVar_ItemId, data[8], 2); + RecordItemPurchase(gSpecialVar_ItemId, data[8], 2); DestroyListMenuTask(data[0], &sStaticCnt.listMenuScrollOffset, &sStaticCnt.listMenuSelectedRow); SortAndCountBerries(); SanitizeListMenuSelectionParams(); diff --git a/src/buy_menu_helpers.c b/src/buy_menu_helpers.c index cf8072977..1fd160576 100644 --- a/src/buy_menu_helpers.c +++ b/src/buy_menu_helpers.c @@ -177,14 +177,14 @@ void BuyMenuDrawMoneyBox(void) PrintMoneyAmountInMoneyBoxWithBorder(0, 0xA, 0xF, GetMoney(&gSaveBlock1Ptr->money)); } -void BuyMenuPrint(u8 windowId, u8 font, const u8 *text, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, s8 speed, u8 color) +void BuyMenuPrint(u8 windowId, u8 font, const u8 *text, u8 x, u8 y, u8 letterSpacing, u8 lineSpacing, u8 speed, u8 color) { AddTextPrinterParameterized4(windowId, font, x, y, letterSpacing, lineSpacing, sShopBuyMenuTextColors[color], speed, text); } void BuyMenuDisplayMessage(u8 taskId, const u8 *text, TaskFunc callback) { - DisplayMessageAndContinueTask(taskId, 2, 0x13, 0xE, sub_809B56C(), GetTextSpeedSetting(), text, callback); + DisplayMessageAndContinueTask(taskId, 2, 0x13, 0xE, GetMartUnk16_4(), GetTextSpeedSetting(), text, callback); ScheduleBgCopyTilemapToVram(0); } diff --git a/src/fieldmap.c b/src/fieldmap.c index 4244762c0..e5901c149 100644 --- a/src/fieldmap.c +++ b/src/fieldmap.c @@ -463,7 +463,7 @@ u32 MapGridGetMetatileBehaviorAt(s32 x, s32 y) return sub_8058F48(x, y, 0); } -u8 MapGridGetMetatileLayerTypeAt(s32 x, s32 y) +u8 MapGridGetMetatileLayerTypeAt(s16 x, s16 y) { return sub_8058F48(x, y, 6); } diff --git a/src/pokeball.c b/src/pokeball.c new file mode 100644 index 000000000..aeaba08ab --- /dev/null +++ b/src/pokeball.c @@ -0,0 +1,1275 @@ +#include "global.h" +#include "battle.h" +#include "battle_anim.h" +#include "decompress.h" +#include "graphics.h" +#include "main.h" +#include "m4a.h" +#include "pokeball.h" +#include "pokemon.h" +#include "sound.h" +#include "sprite.h" +#include "task.h" +#include "trig.h" +#include "util.h" +#include "link.h" +#include "battle_gfx_sfx_util.h" +#include "constants/songs.h" +#include "constants/species.h" + +#define tFrames data[0] +#define tPan data[1] +#define tThrowId data[2] +#define tBattler data[3] +#define tOpponentBattler data[4] + +#define sBattler data[6] + +#define GFX_TAG_POKE_BALL 55000 +#define GFX_TAG_GREAT_BALL 55001 +#define GFX_TAG_SAFARI_BALL 55002 +#define GFX_TAG_ULTRA_BALL 55003 +#define GFX_TAG_MASTER_BALL 55004 +#define GFX_TAG_NET_BALL 55005 +#define GFX_TAG_DIVE_BALL 55006 +#define GFX_TAG_NEST_BALL 55007 +#define GFX_TAG_REPEAT_BALL 55008 +#define GFX_TAG_TIMER_BALL 55009 +#define GFX_TAG_LUXURY_BALL 55010 +#define GFX_TAG_PREMIER_BALL 55011 + +// Function Declarations +static void Task_DoPokeballSendOutAnim(u8 taskId); +static void SpriteCB_TestBallThrow(struct Sprite *sprite); +static void sub_804AC88(struct Sprite *sprite); +static void sub_804AC94(struct Sprite *sprite); +static void sub_804AD00(struct Sprite *sprite); +static void sub_804AD98(struct Sprite *sprite); +static void sub_804ADEC(struct Sprite *sprite); +static void sub_804AEE4(struct Sprite *sprite); +static void sub_804AF24(struct Sprite *sprite); +static void Task_PlayCryWhenReleasedFromBall(u8 taskId); +static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite); +static void sub_804B484(struct Sprite *sprite); +static void HandleBallAnimEnd(struct Sprite *sprite); +static void sub_804B5C8(struct Sprite *sprite); +static void SpriteCB_PlayerMonSendOut_1(struct Sprite *sprite); +static void SpriteCB_PlayerMonSendOut_2(struct Sprite *sprite); +static void SpriteCB_ReleaseMon2FromBall(struct Sprite *sprite); +static void SpriteCB_OpponentMonSendOut(struct Sprite *sprite); +static u8 LaunchBallStarsTaskForPokeball(u8 x, u8 y, u8 kindOfStars, u8 d); +static u8 LaunchBallFadeMonTaskForPokeball(bool8 unFadeLater, u8 battlerId, u32 arg2); +static void sub_804B9E8(struct Sprite *sprite); +static void sub_804BAA4(struct Sprite *sprite); +static void sub_804BC50(struct Sprite *sprite); +static void sub_804BCF8(struct Sprite *sprite); +static void sub_804BD6C(struct Sprite *sprite); +static void sub_804BE24(struct Sprite *sprite); +static void sub_804BE48(struct Sprite *sprite); +static void SpriteCB_HitAnimHealthoxEffect(struct Sprite *sprite); +static u16 GetBattlerPokeballItemId(u8 battlerId); + +// Data +const struct CompressedSpriteSheet gBallSpriteSheets[POKEBALL_COUNT] = +{ + {gInterfaceGfx_PokeBall, 384, GFX_TAG_POKE_BALL}, + {gInterfaceGfx_GreatBall, 384, GFX_TAG_GREAT_BALL}, + {gInterfaceGfx_SafariBall, 384, GFX_TAG_SAFARI_BALL}, + {gInterfaceGfx_UltraBall, 384, GFX_TAG_ULTRA_BALL}, + {gInterfaceGfx_MasterBall, 384, GFX_TAG_MASTER_BALL}, + {gInterfaceGfx_NetBall, 384, GFX_TAG_NET_BALL}, + {gInterfaceGfx_DiveBall, 384, GFX_TAG_DIVE_BALL}, + {gInterfaceGfx_NestBall, 384, GFX_TAG_NEST_BALL}, + {gInterfaceGfx_RepeatBall, 384, GFX_TAG_REPEAT_BALL}, + {gInterfaceGfx_TimerBall, 384, GFX_TAG_TIMER_BALL}, + {gInterfaceGfx_LuxuryBall, 384, GFX_TAG_LUXURY_BALL}, + {gInterfaceGfx_PremierBall, 384, GFX_TAG_PREMIER_BALL}, +}; + +const struct CompressedSpritePalette gBallSpritePalettes[POKEBALL_COUNT] = +{ + {gInterfacePal_PokeBall, GFX_TAG_POKE_BALL}, + {gInterfacePal_GreatBall, GFX_TAG_GREAT_BALL}, + {gInterfacePal_SafariBall, GFX_TAG_SAFARI_BALL}, + {gInterfacePal_UltraBall, GFX_TAG_ULTRA_BALL}, + {gInterfacePal_MasterBall, GFX_TAG_MASTER_BALL}, + {gInterfacePal_NetBall, GFX_TAG_NET_BALL}, + {gInterfacePal_DiveBall, GFX_TAG_DIVE_BALL}, + {gInterfacePal_NestBall, GFX_TAG_NEST_BALL}, + {gInterfacePal_RepeatBall, GFX_TAG_REPEAT_BALL}, + {gInterfacePal_TimerBall, GFX_TAG_TIMER_BALL}, + {gInterfacePal_LuxuryBall, GFX_TAG_LUXURY_BALL}, + {gInterfacePal_PremierBall, GFX_TAG_PREMIER_BALL}, +}; + +static const struct OamData sBallOamData = +{ + .y = 0, + .affineMode = ST_OAM_AFFINE_DOUBLE, + .objMode = ST_OAM_OBJ_NORMAL, + .mosaic = FALSE, + .bpp = ST_OAM_4BPP, + .shape = SPRITE_SHAPE(16x16), + .x = 0, + .matrixNum = 0, + .size = SPRITE_SIZE(16x16), + .tileNum = 0, + .priority = 2, + .paletteNum = 0, + .affineParam = 0, +}; + +static const union AnimCmd sBallAnimSeq3[] = +{ + ANIMCMD_FRAME(0, 5), + ANIMCMD_JUMP(0), +}; + +static const union AnimCmd sBallAnimSeq5[] = +{ + ANIMCMD_FRAME(4, 1), + ANIMCMD_JUMP(0), +}; + +static const union AnimCmd sBallAnimSeq4[] = +{ + ANIMCMD_FRAME(8, 5), + ANIMCMD_JUMP(0), +}; + +static const union AnimCmd sBallAnimSeq6[] = +{ + ANIMCMD_FRAME(12, 1), + ANIMCMD_JUMP(0), +}; + +static const union AnimCmd sBallAnimSeq0[] = +{ + ANIMCMD_FRAME(0, 1), + ANIMCMD_END, +}; + +static const union AnimCmd sBallAnimSeq1[] = +{ + ANIMCMD_FRAME(4, 5), + ANIMCMD_FRAME(8, 5), + ANIMCMD_END, +}; + +static const union AnimCmd sBallAnimSeq2[] = +{ + ANIMCMD_FRAME(4, 5), + ANIMCMD_FRAME(0, 5), + ANIMCMD_END, +}; + +static const union AnimCmd *const sBallAnimSequences[] = +{ + sBallAnimSeq0, + sBallAnimSeq1, + sBallAnimSeq2, + sBallAnimSeq3, + sBallAnimSeq4, + sBallAnimSeq5, + sBallAnimSeq6, +}; + +static const union AffineAnimCmd sBallAffineAnimSeq0[] = +{ + AFFINEANIMCMD_FRAME(0, 0, 0, 1), + AFFINEANIMCMD_JUMP(0), +}; + +static const union AffineAnimCmd sBallAffineAnimSeq1[] = +{ + AFFINEANIMCMD_FRAME(0, 0, -3, 1), + AFFINEANIMCMD_JUMP(0), +}; + +static const union AffineAnimCmd sBallAffineAnimSeq2[] = +{ + AFFINEANIMCMD_FRAME(0, 0, 3, 1), + AFFINEANIMCMD_JUMP(0), +}; + +static const union AffineAnimCmd sBallAffineAnimSeq3[] = +{ + AFFINEANIMCMD_FRAME(256, 256, 0, 0), + AFFINEANIMCMD_END, +}; + +static const union AffineAnimCmd sBallAffineAnimSeq4[] = +{ + AFFINEANIMCMD_FRAME(0, 0, 25, 1), + AFFINEANIMCMD_JUMP(0), +}; + +static const union AffineAnimCmd *const sBallAffineAnimSequences[] = +{ + sBallAffineAnimSeq0, + sBallAffineAnimSeq1, + sBallAffineAnimSeq2, + sBallAffineAnimSeq3, + sBallAffineAnimSeq4, +}; + +const struct SpriteTemplate gBallSpriteTemplates[POKEBALL_COUNT] = +{ + { + .tileTag = GFX_TAG_POKE_BALL, + .paletteTag = GFX_TAG_POKE_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_GREAT_BALL, + .paletteTag = GFX_TAG_GREAT_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_SAFARI_BALL, + .paletteTag = GFX_TAG_SAFARI_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_ULTRA_BALL, + .paletteTag = GFX_TAG_ULTRA_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_MASTER_BALL, + .paletteTag = GFX_TAG_MASTER_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_NET_BALL, + .paletteTag = GFX_TAG_NET_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_DIVE_BALL, + .paletteTag = GFX_TAG_DIVE_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_NEST_BALL, + .paletteTag = GFX_TAG_NEST_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_REPEAT_BALL, + .paletteTag = GFX_TAG_REPEAT_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_TIMER_BALL, + .paletteTag = GFX_TAG_TIMER_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_LUXURY_BALL, + .paletteTag = GFX_TAG_LUXURY_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, + { + .tileTag = GFX_TAG_PREMIER_BALL, + .paletteTag = GFX_TAG_PREMIER_BALL, + .oam = &sBallOamData, + .anims = sBallAnimSequences, + .images = NULL, + .affineAnims = sBallAffineAnimSequences, + .callback = SpriteCB_TestBallThrow, + }, +}; + +// Functions +u8 DoPokeballSendOutAnimation(s16 pan, u8 kindOfThrow) +{ + u8 taskId; + + gDoingBattleAnim = TRUE; + gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].ballAnimActive = TRUE; + taskId = CreateTask(Task_DoPokeballSendOutAnim, 5); + gTasks[taskId].tPan = pan; + gTasks[taskId].tThrowId = kindOfThrow; + gTasks[taskId].tBattler = gActiveBattler; + return 0; +} + +static void Task_DoPokeballSendOutAnim(u8 taskId) +{ + u16 throwCaseId; + u8 battlerId; + u16 itemId, ballId; + u8 ballSpriteId; + bool8 notSendOut = FALSE; + s16 x, y; + u32 gender; + + if (gTasks[taskId].tFrames == 0) + { + gTasks[taskId].tFrames++; + return; + } + + throwCaseId = gTasks[taskId].tThrowId; + battlerId = gTasks[taskId].tBattler; + + if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) + itemId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); + else + itemId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); + + ballId = ItemIdToBallId(itemId); + LoadBallGfx(ballId); + + if (gBattleTypeFlags & BATTLE_TYPE_LINK) + gender = gLinkPlayers[GetBattlerMultiplayerId(battlerId)].gender; + else + gender = gSaveBlock2Ptr->playerGender; + + ballSpriteId = CreateSprite(&gBallSpriteTemplates[ballId], 32, 80, 29); + gSprites[ballSpriteId].data[0] = 0x80; + gSprites[ballSpriteId].data[1] = 0; + gSprites[ballSpriteId].data[7] = throwCaseId; + + switch (throwCaseId) + { + case POKEBALL_PLAYER_SENDOUT: + if (gBattleTypeFlags & BATTLE_TYPE_POKEDUDE) + { + x = 32; + y = 64; + } + else + { + gender = !!gender; // something unknown got optimized out + x = 48; + y = 70; + } + + gBattlerTarget = battlerId; + gSprites[ballSpriteId].pos1.x = x; + gSprites[ballSpriteId].pos1.y = y; + gSprites[ballSpriteId].callback = SpriteCB_PlayerMonSendOut_1; + break; + case POKEBALL_OPPONENT_SENDOUT: + gSprites[ballSpriteId].pos1.x = GetBattlerSpriteCoord(battlerId, BATTLER_COORD_X); + gSprites[ballSpriteId].pos1.y = GetBattlerSpriteCoord(battlerId, BATTLER_COORD_Y) + 24; + gBattlerTarget = battlerId; + gSprites[ballSpriteId].data[0] = 0; + gSprites[ballSpriteId].callback = SpriteCB_OpponentMonSendOut; + break; + default: + gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); + notSendOut = TRUE; + break; + } + + gSprites[ballSpriteId].sBattler = gBattlerTarget; + if (!notSendOut) + { + DestroyTask(taskId); + return; + } + + // this will perform an unused ball throw animation + gSprites[ballSpriteId].data[0] = 34; + gSprites[ballSpriteId].data[2] = GetBattlerSpriteCoord(gBattlerTarget, BATTLER_COORD_X); + gSprites[ballSpriteId].data[4] = GetBattlerSpriteCoord(gBattlerTarget, BATTLER_COORD_Y) - 16; + gSprites[ballSpriteId].data[5] = -40; + InitAnimArcTranslation(&gSprites[ballSpriteId]); + gSprites[ballSpriteId].oam.affineParam = taskId; + gTasks[taskId].tOpponentBattler = gBattlerTarget; + gTasks[taskId].func = TaskDummy; + PlaySE(SE_NAGERU); +} + +static void SpriteCB_TestBallThrow(struct Sprite *sprite) +{ + if (TranslateAnimHorizontalArc(sprite)) + { + u16 ballId; + u8 taskId = sprite->oam.affineParam; + u8 opponentBattler = gTasks[taskId].tOpponentBattler; + u8 noOfShakes = gTasks[taskId].tThrowId; + + StartSpriteAnim(sprite, 1); + sprite->affineAnimPaused = TRUE; + sprite->pos1.x += sprite->pos2.x; + sprite->pos1.y += sprite->pos2.y; + sprite->pos2.x = 0; + sprite->pos2.y = 0; + sprite->data[5] = 0; + ballId = ItemIdToBallId(GetBattlerPokeballItemId(opponentBattler)); + LaunchBallStarsTask(sprite->pos1.x, sprite->pos1.y - 5, 1, 0x1C, ballId); + sprite->data[0] = LaunchBallFadeMonTask(FALSE, opponentBattler, 14, ballId); + sprite->sBattler = opponentBattler; + sprite->data[7] = noOfShakes; + DestroyTask(taskId); + sprite->callback = sub_804AC88; + } +} + +#undef tFrames +#undef tPan +#undef tThrowId +#undef tBattler +#undef tOpponentBattler + +static void sub_804AC88(struct Sprite *sprite) +{ + sprite->callback = sub_804AC94; +} + +static void sub_804AC94(struct Sprite *sprite) +{ + if (++sprite->data[5] == 10) + { + sprite->data[5] = 0; + sprite->callback = sub_804AD00; + StartSpriteAffineAnim(&gSprites[gBattlerSpriteIds[sprite->sBattler]], 2); + AnimateSprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]); + gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] = 0; + } +} + +static void sub_804AD00(struct Sprite *sprite) +{ + sprite->data[5]++; + if (sprite->data[5] == 11) + PlaySE(SE_SUIKOMU); + + if (gSprites[gBattlerSpriteIds[sprite->sBattler]].affineAnimEnded) + { + StartSpriteAnim(sprite, 2); + gSprites[gBattlerSpriteIds[sprite->sBattler]].invisible = TRUE; + sprite->data[5] = 0; + sprite->callback = sub_804AD98; + } + else + { + gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] += 0x60; + gSprites[gBattlerSpriteIds[sprite->sBattler]].pos2.y = -gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] >> 8; + } +} + +static void sub_804AD98(struct Sprite *sprite) +{ + if (sprite->animEnded) + { + sprite->data[5]++; + if (sprite->data[5] == 1) + { + sprite->data[3] = 0; + sprite->data[4] = 32; + sprite->data[5] = 0; + sprite->pos1.y += Cos(0, 32); + sprite->pos2.y = -Cos(0, sprite->data[4]); + sprite->callback = sub_804ADEC; + } + } +} + +static void sub_804ADEC(struct Sprite *sprite) +{ + bool8 r5 = FALSE; + + switch (sprite->data[3] & 0xFF) + { + case 0: + sprite->pos2.y = -Cos(sprite->data[5], sprite->data[4]); + sprite->data[5] += 4 + (sprite->data[3] >> 8); + if (sprite->data[5] >= 64) + { + sprite->data[4] -= 10; + sprite->data[3] += 0x101; + if (sprite->data[3] >> 8 == 4) + r5 = TRUE; + switch (sprite->data[3] >> 8) + { + case 1: + PlaySE(SE_KON); + break; + case 2: + PlaySE(SE_KON2); + break; + case 3: + PlaySE(SE_KON3); + break; + default: + PlaySE(SE_KON4); + break; + } + } + break; + case 1: + sprite->pos2.y = -Cos(sprite->data[5], sprite->data[4]); + sprite->data[5] -= 4 + (sprite->data[3] >> 8); + if (sprite->data[5] <= 0) + { + sprite->data[5] = 0; + sprite->data[3] &= 0xFF00; + } + break; + } + if (r5) + { + sprite->data[3] = 0; + sprite->pos1.y += Cos(64, 32); + sprite->pos2.y = 0; + if (sprite->data[7] == 0) + { + sprite->callback = SpriteCB_ReleaseMonFromBall; + } + else + { + sprite->callback = sub_804AEE4; + sprite->data[4] = 1; + sprite->data[5] = 0; + } + } +} + +static void sub_804AEE4(struct Sprite *sprite) +{ + sprite->data[3]++; + if (sprite->data[3] == 31) + { + sprite->data[3] = 0; + sprite->affineAnimPaused = TRUE; + StartSpriteAffineAnim(sprite, 1); + sprite->callback = sub_804AF24; + PlaySE(SE_BOWA); + } +} + +static void sub_804AF24(struct Sprite *sprite) +{ + switch (sprite->data[3] & 0xFF) + { + case 0: + case 2: + sprite->pos2.x += sprite->data[4]; + sprite->data[5] += sprite->data[4]; + sprite->affineAnimPaused = FALSE; + if (sprite->data[5] > 3 || sprite->data[5] < -3) + { + sprite->data[3]++; + sprite->data[5] = 0; + } + break; + case 1: + sprite->data[5]++; + if (sprite->data[5] == 1) + { + sprite->data[5] = 0; + sprite->data[4] = -sprite->data[4]; + sprite->data[3]++; + sprite->affineAnimPaused = FALSE; + if (sprite->data[4] < 0) + ChangeSpriteAffineAnim(sprite, 2); + else + ChangeSpriteAffineAnim(sprite, 1); + } + else + { + sprite->affineAnimPaused = TRUE; + } + break; + case 3: + sprite->data[3] += 0x100; + if (sprite->data[3] >> 8 == sprite->data[7]) + { + sprite->callback = SpriteCB_ReleaseMonFromBall; + } + else + { + if (sprite->data[7] == 4 && sprite->data[3] >> 8 == 3) + { + sprite->callback = sub_804B484; + sprite->affineAnimPaused = TRUE; + } + else + { + sprite->data[3]++; + sprite->affineAnimPaused = TRUE; + } + } + break; + case 4: + default: + sprite->data[5]++; + if (sprite->data[5] == 31) + { + sprite->data[5] = 0; + sprite->data[3] &= 0xFF00; + StartSpriteAffineAnim(sprite, 3); + if (sprite->data[4] < 0) + StartSpriteAffineAnim(sprite, 2); + else + StartSpriteAffineAnim(sprite, 1); + + PlaySE(SE_BOWA); + } + break; + } +} + +#define tCryTaskSpecies data[0] +#define tCryTaskPan data[1] +#define tCryTaskWantedCry data[2] +#define tCryTaskMonPtr1 data[3] +#define tCryTaskMonPtr2 data[4] +#define tCryTaskFrames data[10] +#define tCryTaskState data[15] + +static void Task_PlayCryWhenReleasedFromBall(u8 taskId) +{ + u8 state2 = gTasks[taskId].data[2]; + s8 pan = gTasks[taskId].tCryTaskPan; + u16 species = gTasks[taskId].tCryTaskSpecies; + struct Pokemon *mon = (void*)(u32)((u32)(gTasks[taskId].tCryTaskMonPtr1 << 0x10) | ((u16)gTasks[taskId].tCryTaskMonPtr2)); + + switch (gTasks[taskId].tCryTaskState) + { + case 0: + default: + if (gTasks[taskId].data[8] < 3) + gTasks[taskId].data[8]++; + else + gTasks[taskId].tCryTaskState = state2 + 1; + break; + case 1: + if (ShouldPlayNormalPokeCry(mon) == TRUE) + PlayCry3(species, pan, 0); + else + PlayCry3(species, pan, 11); + + DestroyTask(taskId); + break; + case 2: + StopCryAndClearCrySongs(); + gTasks[taskId].tCryTaskFrames = 3; + gTasks[taskId].tCryTaskState = 20; + break; + case 20: + if (gTasks[taskId].tCryTaskFrames == 0) + { + if (ShouldPlayNormalPokeCry(mon) == TRUE) + PlayCry4(species, pan, 1); + else + PlayCry4(species, pan, 12); + + DestroyTask(taskId); + } + else + { + gTasks[taskId].tCryTaskFrames--; + } + break; + case 3: + gTasks[taskId].tCryTaskFrames = 6; + gTasks[taskId].tCryTaskState = 30; + break; + case 30: + if (gTasks[taskId].tCryTaskFrames != 0) + { + gTasks[taskId].tCryTaskFrames--; + break; + } + gTasks[taskId].tCryTaskState++; + case 31: + if (!IsCryPlayingOrClearCrySongs()) + { + StopCryAndClearCrySongs(); + gTasks[taskId].tCryTaskFrames = 3; + gTasks[taskId].tCryTaskState++; + } + break; + case 32: + if (gTasks[taskId].tCryTaskFrames != 0) + { + gTasks[taskId].tCryTaskFrames--; + break; + } + + if (ShouldPlayNormalPokeCry(mon) == TRUE) + PlayCry4(species, pan, 0); + else + PlayCry4(species, pan, 11); + + DestroyTask(taskId); + break; + } +} + +static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) +{ + u8 battlerId = sprite->sBattler; + u32 ballId; + + StartSpriteAnim(sprite, 1); + ballId = ItemIdToBallId(GetBattlerPokeballItemId(battlerId)); + LaunchBallStarsTask(sprite->pos1.x, sprite->pos1.y - 5, 1, 0x1C, ballId); + sprite->data[0] = LaunchBallFadeMonTask(1, sprite->sBattler, 14, ballId); + sprite->callback = HandleBallAnimEnd; + + if (gMain.inBattle) + { + struct Pokemon *mon; + u16 species; + s8 pan; + u16 wantedCryCase; + u8 taskId; + + if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) + { + mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; + pan = 25; + } + else + { + mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; + pan = -25; + } + + species = GetMonData(mon, MON_DATA_SPECIES); + if ((battlerId == GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) || battlerId == GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)) + && IsDoubleBattle() && gBattleSpritesDataPtr->animationData->field_9_x1) + { + if (gBattleTypeFlags & BATTLE_TYPE_MULTI) + { + if (IsBGMPlaying()) + m4aMPlayStop(&gMPlayInfo_BGM); + } + else + { + m4aMPlayVolumeControl(&gMPlayInfo_BGM, 0xFFFF, 128); + } + } + + if (!IsDoubleBattle() || !gBattleSpritesDataPtr->animationData->field_9_x1) + wantedCryCase = 0; + else if (battlerId == GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) || battlerId == GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)) + wantedCryCase = 1; + else + wantedCryCase = 2; + + taskId = CreateTask(Task_PlayCryWhenReleasedFromBall, 3); + gTasks[taskId].tCryTaskSpecies = species; + gTasks[taskId].tCryTaskPan = pan; + gTasks[taskId].tCryTaskWantedCry = wantedCryCase; + gTasks[taskId].tCryTaskMonPtr1 = (u32)(mon) >> 0x10; + gTasks[taskId].tCryTaskMonPtr2 = (u32)(mon); + gTasks[taskId].tCryTaskState = 0; + } + + StartSpriteAffineAnim(&gSprites[gBattlerSpriteIds[sprite->sBattler]], 1); + AnimateSprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]); + gSprites[gBattlerSpriteIds[sprite->sBattler]].data[1] = 0x1000; +} + +#undef tCryTaskSpecies +#undef tCryTaskPan +#undef tCryTaskWantedCry +#undef tCryTaskMonPtr1 +#undef tCryTaskMonPtr2 +#undef tCryTaskFrames +#undef tCryTaskState + +static void sub_804B484(struct Sprite *sprite) +{ + sprite->animPaused = TRUE; + sprite->callback = sub_804B5C8; + sprite->data[3] = 0; + sprite->data[4] = 0; + sprite->data[5] = 0; +} + +static void HandleBallAnimEnd(struct Sprite *sprite) +{ + bool8 affineAnimEnded = FALSE; + u8 battlerId = sprite->sBattler; + + gSprites[gBattlerSpriteIds[battlerId]].invisible = FALSE; + if (sprite->animEnded) + sprite->invisible = TRUE; + if (gSprites[gBattlerSpriteIds[battlerId]].affineAnimEnded) + { + StartSpriteAffineAnim(&gSprites[gBattlerSpriteIds[battlerId]], 0); + affineAnimEnded = TRUE; + } + else + { + gSprites[gBattlerSpriteIds[battlerId]].data[1] -= 288; + gSprites[gBattlerSpriteIds[battlerId]].pos2.y = gSprites[gBattlerSpriteIds[battlerId]].data[1] >> 8; + } + if (sprite->animEnded && affineAnimEnded) + { + s32 i, doneBattlers; + + gSprites[gBattlerSpriteIds[battlerId]].pos2.y = 0; + gDoingBattleAnim = FALSE; + gBattleSpritesDataPtr->healthBoxesData[battlerId].ballAnimActive = FALSE; + FreeSpriteOamMatrix(sprite); + DestroySprite(sprite); + + for (doneBattlers = 0, i = 0; i < MAX_BATTLERS_COUNT; i++) + { + if (!gBattleSpritesDataPtr->healthBoxesData[i].ballAnimActive) + doneBattlers++; + } + if (doneBattlers == MAX_BATTLERS_COUNT) + { + for (i = 0; i < POKEBALL_COUNT; i++) + FreeBallGfx(i); + } + } +} + +static void sub_804B5C8(struct Sprite *sprite) +{ + u8 battlerId = sprite->sBattler; + + sprite->data[4]++; + if (sprite->data[4] == 40) + { + return; + } + else if (sprite->data[4] == 95) + { + gDoingBattleAnim = FALSE; + m4aMPlayAllStop(); + PlaySE(MUS_FAN6); + } + else if (sprite->data[4] == 315) + { + FreeOamMatrix(gSprites[gBattlerSpriteIds[sprite->sBattler]].oam.matrixNum); + DestroySprite(&gSprites[gBattlerSpriteIds[sprite->sBattler]]); + DestroySpriteAndFreeResources(sprite); + if (gMain.inBattle) + gBattleSpritesDataPtr->healthBoxesData[battlerId].ballAnimActive = FALSE; + } +} + +static void SpriteCB_PlayerMonSendOut_1(struct Sprite *sprite) +{ + sprite->data[0] = 25; + sprite->data[2] = GetBattlerSpriteCoord(sprite->sBattler, 2); + sprite->data[4] = GetBattlerSpriteCoord(sprite->sBattler, 3) + 24; + sprite->data[5] = -30; + sprite->oam.affineParam = sprite->sBattler; + InitAnimArcTranslation(sprite); + sprite->callback = SpriteCB_PlayerMonSendOut_2; +} + +#define HIBYTE(x) (((x) >> 8) & 0xFF) + +static void SpriteCB_PlayerMonSendOut_2(struct Sprite *sprite) +{ + u32 r6; + u32 r7; + + if (HIBYTE(sprite->data[7]) >= 35 && HIBYTE(sprite->data[7]) < 80) + { + s16 r4; + + if ((sprite->oam.affineParam & 0xFF00) == 0) + { + r6 = sprite->data[1] & 1; + r7 = sprite->data[2] & 1; + sprite->data[1] = ((sprite->data[1] / 3) & ~1) | r6; + sprite->data[2] = ((sprite->data[2] / 3) & ~1) | r7; + StartSpriteAffineAnim(sprite, 4); + } + r4 = sprite->data[0]; + AnimTranslateLinear(sprite); + sprite->data[7] += sprite->sBattler / 3; + sprite->pos2.y += Sin(HIBYTE(sprite->data[7]), sprite->data[5]); + sprite->oam.affineParam += 0x100; + if ((sprite->oam.affineParam >> 8) % 3 != 0) + sprite->data[0] = r4; + else + sprite->data[0] = r4 - 1; + if (HIBYTE(sprite->data[7]) >= 80) + { + r6 = sprite->data[1] & 1; + r7 = sprite->data[2] & 1; + sprite->data[1] = ((sprite->data[1] * 3) & ~1) | r6; + sprite->data[2] = ((sprite->data[2] * 3) & ~1) | r7; + } + } + else + { + if (TranslateAnimHorizontalArc(sprite)) + { + sprite->pos1.x += sprite->pos2.x; + sprite->pos1.y += sprite->pos2.y; + sprite->pos2.x = sprite->pos2.y = 0; + sprite->sBattler = sprite->oam.affineParam & 0xFF; + sprite->data[0] = 0; + + if (IsDoubleBattle() && gBattleSpritesDataPtr->animationData->field_9_x1 + && sprite->sBattler == GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)) + sprite->callback = SpriteCB_ReleaseMon2FromBall; + else + sprite->callback = SpriteCB_ReleaseMonFromBall; + + StartSpriteAffineAnim(sprite, 0); + } + } +} + +static void SpriteCB_ReleaseMon2FromBall(struct Sprite *sprite) +{ + if (sprite->data[0]++ > 24) + { + sprite->data[0] = 0; + sprite->callback = SpriteCB_ReleaseMonFromBall; + } +} + +static void SpriteCB_OpponentMonSendOut(struct Sprite *sprite) +{ + sprite->data[0]++; + if (sprite->data[0] > 15) + { + sprite->data[0] = 0; + if (IsDoubleBattle() && gBattleSpritesDataPtr->animationData->field_9_x1 + && sprite->sBattler == GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)) + sprite->callback = SpriteCB_ReleaseMon2FromBall; + else + sprite->callback = SpriteCB_ReleaseMonFromBall; + } +} + +#undef sBattler + +static u8 LaunchBallStarsTaskForPokeball(u8 x, u8 y, u8 kindOfStars, u8 d) +{ + return LaunchBallStarsTask(x, y, kindOfStars, d, BALL_POKE); +} + +static u8 LaunchBallFadeMonTaskForPokeball(bool8 unFadeLater, u8 battlerId, u32 arg2) +{ + return LaunchBallFadeMonTask(unFadeLater, battlerId, arg2, BALL_POKE); +} + +void CreatePokeballSpriteToReleaseMon(u8 monSpriteId, u8 battlerId, u8 x, u8 y, u8 oamPriority, u8 subpriortiy, u8 g, u32 h) +{ + u8 spriteId; + + LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[0]); + LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[0]); + spriteId = CreateSprite(&gBallSpriteTemplates[0], x, y, subpriortiy); + gSprites[spriteId].data[0] = monSpriteId; + gSprites[spriteId].data[5] = gSprites[monSpriteId].pos1.x; + gSprites[spriteId].data[6] = gSprites[monSpriteId].pos1.y; + gSprites[monSpriteId].pos1.x = x; + gSprites[monSpriteId].pos1.y = y; + gSprites[spriteId].data[1] = g; + gSprites[spriteId].data[2] = battlerId; + gSprites[spriteId].data[3] = h; + gSprites[spriteId].data[4] = h >> 0x10; + gSprites[spriteId].oam.priority = oamPriority; + gSprites[spriteId].callback = sub_804B9E8; + gSprites[monSpriteId].invisible = TRUE; +} + +static void sub_804B9E8(struct Sprite *sprite) +{ + if (sprite->data[1] == 0) + { + u8 r5; + u8 r7 = sprite->data[0]; + u8 battlerId = sprite->data[2]; + u32 r4 = (u16)sprite->data[3] | ((u16)sprite->data[4] << 16); + + if (sprite->subpriority != 0) + r5 = sprite->subpriority - 1; + else + r5 = 0; + + StartSpriteAnim(sprite, 1); + LaunchBallStarsTaskForPokeball(sprite->pos1.x, sprite->pos1.y - 5, sprite->oam.priority, r5); + sprite->data[1] = LaunchBallFadeMonTaskForPokeball(1, battlerId, r4); + sprite->callback = sub_804BAA4; + gSprites[r7].invisible = FALSE; + StartSpriteAffineAnim(&gSprites[r7], 1); + AnimateSprite(&gSprites[r7]); + gSprites[r7].data[1] = 0x1000; + sprite->data[7] = 0; + } + else + { + sprite->data[1]--; + } +} + +static void sub_804BAA4(struct Sprite *sprite) +{ + bool8 r12 = FALSE; + bool8 r6 = FALSE; + u8 monSpriteId = sprite->data[0]; + u16 var1; + u16 var2; + + if (sprite->animEnded) + sprite->invisible = TRUE; + + if (gSprites[monSpriteId].affineAnimEnded) + { + StartSpriteAffineAnim(&gSprites[monSpriteId], 0); + r12 = TRUE; + } + + var1 = (sprite->data[5] - sprite->pos1.x) * sprite->data[7] / 128 + sprite->pos1.x; + var2 = (sprite->data[6] - sprite->pos1.y) * sprite->data[7] / 128 + sprite->pos1.y; + gSprites[monSpriteId].pos1.x = var1; + gSprites[monSpriteId].pos1.y = var2; + if (sprite->data[7] < 128) + { + s16 sine = -(gSineTable[(u8)sprite->data[7]] / 8); + + sprite->data[7] += 4; + gSprites[monSpriteId].pos2.x = sine; + gSprites[monSpriteId].pos2.y = sine; + } + else + { + gSprites[monSpriteId].pos1.x = sprite->data[5]; + gSprites[monSpriteId].pos1.y = sprite->data[6]; + gSprites[monSpriteId].pos2.x = 0; + gSprites[monSpriteId].pos2.y = 0; + r6 = TRUE; + } + + if (sprite->animEnded && r12 && r6) + DestroySpriteAndFreeResources(sprite); +} + +u8 CreateTradePokeballSprite(u8 a, u8 b, u8 x, u8 y, u8 oamPriority, u8 subPriority, u8 g, u32 h) +{ + u8 spriteId; + + LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[0]); + LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[0]); + spriteId = CreateSprite(&gBallSpriteTemplates[0], x, y, subPriority); + gSprites[spriteId].data[0] = a; + gSprites[spriteId].data[1] = g; + gSprites[spriteId].data[2] = b; + gSprites[spriteId].data[3] = h; + gSprites[spriteId].data[4] = h >> 16; + gSprites[spriteId].oam.priority = oamPriority; + gSprites[spriteId].callback = sub_804BC50; + return spriteId; +} + +static void sub_804BC50(struct Sprite *sprite) +{ + if (sprite->data[1] == 0) + { + u8 r6; + u8 r7 = sprite->data[0]; + u8 r8 = sprite->data[2]; + u32 r5 = (u16)sprite->data[3] | ((u16)sprite->data[4] << 16); + + if (sprite->subpriority != 0) + r6 = sprite->subpriority - 1; + else + r6 = 0; + + StartSpriteAnim(sprite, 1); + LaunchBallStarsTaskForPokeball(sprite->pos1.x, sprite->pos1.y - 5, sprite->oam.priority, r6); + sprite->data[1] = LaunchBallFadeMonTaskForPokeball(1, r8, r5); + sprite->callback = sub_804BCF8; + StartSpriteAffineAnim(&gSprites[r7], 2); + AnimateSprite(&gSprites[r7]); + gSprites[r7].data[1] = 0; + } + else + { + sprite->data[1]--; + } +} + +static void sub_804BCF8(struct Sprite *sprite) +{ + u8 r1; + + sprite->data[5]++; + if (sprite->data[5] == 11) + PlaySE(SE_SUIKOMU); + + r1 = sprite->data[0]; + if (gSprites[r1].affineAnimEnded) + { + StartSpriteAnim(sprite, 2); + gSprites[r1].invisible = TRUE; + sprite->data[5] = 0; + sprite->callback = sub_804BD6C; + } + else + { + gSprites[r1].data[1] += 96; + gSprites[r1].pos2.y = -gSprites[r1].data[1] >> 8; + } +} + +static void sub_804BD6C(struct Sprite *sprite) +{ + if (sprite->animEnded) + sprite->callback = SpriteCallbackDummy; +} + +void DestroySpriteAndFreeResources2(struct Sprite *sprite) +{ + DestroySpriteAndFreeResources(sprite); +} + +void sub_804BD94(u8 battlerId) +{ + struct Sprite *healthboxSprite = &gSprites[gHealthboxSpriteIds[battlerId]]; + + healthboxSprite->data[0] = 5; + healthboxSprite->data[1] = 0; + healthboxSprite->pos2.x = 0x73; + healthboxSprite->pos2.y = 0; + healthboxSprite->callback = sub_804BE48; + if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) + { + healthboxSprite->data[0] = -healthboxSprite->data[0]; + healthboxSprite->data[1] = -healthboxSprite->data[1]; + healthboxSprite->pos2.x = -healthboxSprite->pos2.x; + healthboxSprite->pos2.y = -healthboxSprite->pos2.y; + } + + gSprites[healthboxSprite->data[5]].callback(&gSprites[healthboxSprite->data[5]]); + if (GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT) + healthboxSprite->callback = sub_804BE24; +} + +static void sub_804BE24(struct Sprite *sprite) +{ + sprite->data[1]++; + if (sprite->data[1] == 20) + { + sprite->data[1] = 0; + sprite->callback = sub_804BE48; + } +} + +static void sub_804BE48(struct Sprite *sprite) +{ + sprite->pos2.x -= sprite->data[0]; + sprite->pos2.y -= sprite->data[1]; + if (sprite->pos2.x == 0 && sprite->pos2.y == 0) + sprite->callback = SpriteCallbackDummy; +} + +void DoHitAnimHealthboxEffect(u8 battlerId) +{ + u8 spriteId; + + spriteId = CreateInvisibleSpriteWithCallback(SpriteCB_HitAnimHealthoxEffect); + gSprites[spriteId].data[0] = 1; + gSprites[spriteId].data[1] = gHealthboxSpriteIds[battlerId]; + gSprites[spriteId].callback = SpriteCB_HitAnimHealthoxEffect; +} + +static void SpriteCB_HitAnimHealthoxEffect(struct Sprite *sprite) +{ + u8 r1 = sprite->data[1]; + + gSprites[r1].pos2.y = sprite->data[0]; + sprite->data[0] = -sprite->data[0]; + sprite->data[2]++; + if (sprite->data[2] == 21) + { + gSprites[r1].pos2.x = 0; + gSprites[r1].pos2.y = 0; + DestroySprite(sprite); + } +} + +void LoadBallGfx(u8 ballId) +{ + u16 var; + + if (GetSpriteTileStartByTag(gBallSpriteSheets[ballId].tag) == SPRITE_INVALID_TAG) + { + LoadCompressedSpriteSheetUsingHeap(&gBallSpriteSheets[ballId]); + LoadCompressedSpritePaletteUsingHeap(&gBallSpritePalettes[ballId]); + } + + switch (ballId) + { + case BALL_DIVE: + case BALL_LUXURY: + case BALL_PREMIER: + break; + default: + var = GetSpriteTileStartByTag(gBallSpriteSheets[ballId].tag); + LZDecompressVram(gOpenPokeballGfx, (void *)(VRAM + 0x10100 + var * 32)); + break; + } +} + +void FreeBallGfx(u8 ballId) +{ + FreeSpriteTilesByTag(gBallSpriteSheets[ballId].tag); + FreeSpritePaletteByTag(gBallSpritePalettes[ballId].tag); +} + +static u16 GetBattlerPokeballItemId(u8 battlerId) +{ + if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) + return GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); + else + return GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); +} + diff --git a/src/sea_cottage_special_anim.c b/src/sea_cottage_special_anim.c new file mode 100644 index 000000000..18a8da234 --- /dev/null +++ b/src/sea_cottage_special_anim.c @@ -0,0 +1,264 @@ +#include "global.h" +#include "event_data.h" +#include "task.h" +#include "menu.h" +#include "field_player_avatar.h" +#include "fieldmap.h" +#include "field_map_obj.h" +#include "field_camera.h" + +static EWRAM_DATA u8 gUnknown_2039984 = 0; + +static void sub_809C1D8(u8 taskId, const s16 *a1, u16 a2); +static void sub_809C334(u8 taskId); +static void sub_809C500(u8 taskId); +static void sub_809C640(u8 taskId); + +static const u16 gUnknown_83DF0D4[] = {0x0308, 0x030a, 0x02d0}; +static const u16 gUnknown_83DF0DA[] = {0x0309, 0x030b, 0x02d1}; +static const u16 gUnknown_83DF0E0[] = {0x0310, 0x0312, 0x02d8}; +static const u16 gUnknown_83DF0E6[] = {0x0311, 0x0313, 0x02d9}; +static const u16 gUnknown_83DF0EC[] = {0x02e3, 0x0316, 0x0314}; +static const u16 gUnknown_83DF0F2[] = {0x02e4, 0x0317, 0x0315}; +static const u16 gUnknown_83DF0F8[] = {0x02eb, 0x031e, 0x031c}; + +// Functions +static void sub_809C1D8(u8 taskId, const s16 *a1, u16 a2) +{ + s16 r5, r3, r4; + s16 i, j; + + r5 = gTasks[taskId].data[4] - 1; + r3 = gTasks[taskId].data[5] - 1; + r4 = gTasks[taskId].data[1]; + + if (gTasks[taskId].data[2] == 0) + { + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + { + s32 id = MapGridGetMetatileIdAt(r5 + j, r3 + i); + + if (a1[r4] == (s16)id) + { + if (r4 != 2) + MapGridSetMetatileIdAt(r5 + j, r3 + i, a2 | a1[r4 + 1]); + else + MapGridSetMetatileIdAt(r5 + j, r3 + i, a2 | a1[0]); + } + } + } + } + else + { + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + { + s32 id = MapGridGetMetatileIdAt(r5 + j, r3 + i); + + if (a1[2 - r4] == (s16)id) + { + if (r4 != 2) + MapGridSetMetatileIdAt(r5 + j, r3 + i, a2 | a1[1 - r4]); + else + MapGridSetMetatileIdAt(r5 + j, r3 + i, a2 | a1[2]); + } + } + } + } +} + +static void sub_809C334(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + u16 v1; + + data[3] = 1; + switch (data[0]) + { + case 0: + sub_809C1D8(taskId, gUnknown_83DF0D4, 0); + break; + case 1: + sub_809C1D8(taskId, gUnknown_83DF0DA, 0); + break; + case 2: + sub_809C1D8(taskId, gUnknown_83DF0E0, 0xC00); + break; + case 3: + sub_809C1D8(taskId, gUnknown_83DF0E6, 0); + break; + case 4: + sub_809C1D8(taskId, gUnknown_83DF0EC, 0xC00); + break; + case 5: + sub_809C1D8(taskId, gUnknown_83DF0F2, 0); + break; + case 6: + sub_809C1D8(taskId, gUnknown_83DF0F8, 0); + default: + break; + } + + data[0] = (data[0] + 1) & 7; + v1 = data[0] & 7; + if (v1 == 0) + { + DrawWholeMapView(); + data[1] = (data[1] + 1) % 3; + data[3] = v1; + } +} + +static u8 sub_809C3FC(u16 a0) +{ + u8 taskId; + s16 *data; + + taskId = CreateTask(sub_809C334, 0); + data = gTasks[taskId].data; + PlayerGetDestCoords(&data[4], &data[5]); + data[0] = 0; + data[1] = 0; + data[2] = a0; + sub_809C334(taskId); + return taskId; +} + +void sub_809C448(u8 a0) +{ + u8 taskId; + + taskId = sub_809C3FC(a0); + gUnknown_2039984 = taskId; +} + +void sub_809C460(void) +{ + DestroyTask(gUnknown_2039984); +} + +bool8 sub_809C474(void) +{ + if (gTasks[gUnknown_2039984].data[3] == 0) + { + if (gTasks[gUnknown_2039984].data[1] != 2) + return TRUE; + return FALSE; + } + else + { + return TRUE; + } +} + +// special 0x1b5 - creates a tile animaiton one block left two-four up the player +void sub_809C4A8(void) +{ + u8 taskId; + s16 *data; + + taskId = CreateTask(sub_809C500, 0); + gTasks[taskId].data[0] = 0; + gTasks[taskId].data[1] = 0; + data = gTasks[taskId].data; + PlayerGetDestCoords(&data[2], &data[3]); + if (gSpecialVar_0x8004 == 0) + { + gTasks[taskId].data[2] += 6; + gTasks[taskId].data[3] -= 5; + } + else + { + gTasks[taskId].data[2]--; + gTasks[taskId].data[3] -= 5; + } +} + +static void sub_809C500(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + if (data[0] == 0) + { + if ((data[1] & 1) == 0) + { + MapGridSetMetatileIdAt(data[2], data[3], 0xEB5); + MapGridSetMetatileIdAt(data[2], data[3] + 2, 0xEB7); + } + else + { + MapGridSetMetatileIdAt(data[2], data[3], 0xEB6); + MapGridSetMetatileIdAt(data[2], data[3] + 2, 0xEB8); + } + CurrentMapDrawMetatileAt(data[2], data[3]); + CurrentMapDrawMetatileAt(data[2], data[3] + 2); + } + + data[0]++; + if (data[0] != 0x10) + return; + + data[0] = 0; + data[1]++; + if (data[1] != 0xD) + return; + + MapGridSetMetatileIdAt(data[2], data[3], 0xE8A); + MapGridSetMetatileIdAt(data[2], data[3] + 2, 0xE96); + CurrentMapDrawMetatileAt(data[2], data[3]); + CurrentMapDrawMetatileAt(data[2], data[3] + 2); + DestroyTask(taskId); +} + +// special 0x1B7 - creates a tile animation two-six blocks right from the top-left corner of the screen +void sub_809C5FC(void) +{ + u8 taskId; + s16 *data; + + taskId = CreateTask(sub_809C640, 0); + gTasks[taskId].data[0] = 0; + gTasks[taskId].data[1] = 0; + data = gTasks[taskId].data; + PlayerGetDestCoords(&data[2], &data[3]); + gTasks[taskId].data[2] += 4; + gTasks[taskId].data[3] -= 5; +} + +static void sub_809C640(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + if (data[0] == 0) + { + if (data[1] != 0) + { + MapGridSetMetatileIdAt(data[2], data[3], 0xE85); + MapGridSetMetatileIdAt(data[2], data[3] + 1, 0xEB4); + CurrentMapDrawMetatileAt(data[2], data[3]); + CurrentMapDrawMetatileAt(data[2], data[3] + 1); + if (data[1] == 4) + { + DestroyTask(taskId); + return; + } + + data[2]--; + } + MapGridSetMetatileIdAt(data[2], data[3], 0xEB9); + MapGridSetMetatileIdAt(data[2], data[3] + 1, 0xEBA); + CurrentMapDrawMetatileAt(data[2], data[3]); + CurrentMapDrawMetatileAt(data[2], data[3] + 1); + } + + data[0]++; + if (data[0] == 4) + { + data[0] = 0; + data[1]++; + } +} + diff --git a/src/shop.c b/src/shop.c new file mode 100644 index 000000000..12f3f3263 --- /dev/null +++ b/src/shop.c @@ -0,0 +1,1161 @@ +#include "global.h" +#include "shop.h" +#include "menu.h" +#include "data.h" +#include "graphics.h" +#include "strings.h" +#include "list_menu.h" +#include "new_menu_helpers.h" +#include "party_menu.h" +#include "window.h" +#include "field_specials.h" +#include "field_weather.h" +#include "field_camera.h" +#include "task.h" +#include "text.h" +#include "item.h" +#include "item_menu.h" +#include "main.h" +#include "sound.h" +#include "string_util.h" +#include "overworld.h" +#include "window.h" +#include "palette.h" +#include "field_fadetransition.h" +#include "scanline_effect.h" +#include "item_menu_icons.h" +#include "bg.h" +#include "gpu_regs.h" +#include "malloc.h" +#include "decompress.h" +#include "menu_indicators.h" +#include "field_player_avatar.h" +#include "fieldmap.h" +#include "field_map_obj.h" +#include "money.h" +#include "quest_log.h" +#include "script.h" +#include "constants/songs.h" +#include "constants/items.h" +#include "constants/game_stat.h" + +#define tItemCount data[1] +#define tItemId data[5] +#define tListTaskId data[7] + +// mart types +enum +{ + MART_TYPE_REGULAR = 0, + MART_TYPE_TMHM, + MART_TYPE_DECOR, + MART_TYPE_DECOR2, +}; + +// shop view window NPC info enum +enum +{ + EVENT_OBJ_ID, + X_COORD, + Y_COORD, + ANIM_NUM +}; + +struct ShopData +{ + /*0x00*/ void (*callback)(void); + /*0x04*/ const u16 *itemList; + /*0x08*/ u32 itemPrice; + /*0x0C*/ u16 selectedRow; + /*0x0E*/ u16 scrollOffset; + /*0x10*/ u16 itemCount; + /*0x12*/ u16 field12; + /*0x14*/ u16 maxQuantity; + /*0x16*/ u16 martType:4; // 0x1 if tm list + u16 unk16_4:5; + u16 itemSlot:2; + u16 unk16_11:5; + /*0x18*/ u16 unk18; +}; + +struct MartHistory +{ + /*0x00*/ u32 unk0; + /*0x04*/ u16 unk4; + /*0x06*/ u16 unk6; + /*0x08*/ u8 unk8; + /*0x09*/ u8 unk9; + /*0x0A*/ u8 unkA; + /*0x0B*/ u8 unkB; +}; /* size = 12 */ + +static EWRAM_DATA s16 sViewportMapObjects[MAP_OBJECTS_COUNT][4] = {0}; +EWRAM_DATA struct ShopData gShopData = {0}; +static EWRAM_DATA u8 sShopMenuWindowId = 0; +EWRAM_DATA u16 (*gShopTilemapBuffer1)[0x400] = {0}; +EWRAM_DATA u16 (*gShopTilemapBuffer2)[0x400] = {0}; +EWRAM_DATA u16 (*gShopTilemapBuffer3)[0x400] = {0}; +EWRAM_DATA u16 (*gShopTilemapBuffer4)[0x400] = {0}; +EWRAM_DATA struct ListMenuItem *sShopMenuListMenu = {0}; +static EWRAM_DATA u8 (*sShopMenuItemStrings)[13] = {0}; +EWRAM_DATA struct MartHistory gShopMenuHistory[2] = {0}; + +//Function Declarations +static u8 CreateShopMenu(u8 a0); +static u8 GetMartTypeFromItemList(u32 a0); +static void SetShopItemsForSale(const u16 *items); +static void SetShopMenuCallback(MainCallback callback); +static void Task_ShopMenu(u8 taskId); +static void Task_HandleShopMenuBuy(u8 taskId); +static void Task_HandleShopMenuSell(u8 taskId); +static void CB2_GoToSellMenu(void); +static void Task_HandleShopMenuQuit(u8 taskId); +static void ClearShopMenuWindow(void); +static void Task_GoToBuyOrSellMenu(u8 taskId); +static void MapPostLoadHook_ReturnToShopMenu(void); +static void Task_ReturnToShopMenu(u8 taskId); +static void ShowShopMenuAfterExitingBuyOrSellMenu(u8 taskId); +static void CB2_BuyMenu(void); +static void VBlankCB_BuyMenu(void); +static void CB2_InitBuyMenu(void); +static bool8 InitShopData(void); +static void BuyMenuInitBgs(void); +static void BuyMenuDecompressBgGraphics(void); +static void sub_809B10C(bool32 a0); +static void BuyMenuDrawGraphics(void); +static bool8 BuyMenuBuildListMenuTemplate(void); +static void PokeMartWriteNameAndIdAt(struct ListMenuItem *list, u16 index, u8* dst); +static void BuyMenuPrintItemDescriptionAndShowItemIcon(s32 item, bool8 onInit, struct ListMenu *list); +static void BuyMenuPrintPriceInList(u8 windowId, s32 itemId, u8 y); +static void LoadTmHmNameInMart(s32 item); +static void BuyMenuPrintCursor(u8 listTaskId, u8 a1); +static void BuyMenuPrintCursorAtYPosition(u8 y, u8 a1); +static void BuyMenuFreeMemory(void); +static void SetShopExitCallback(void); +static void BuyMenuAddScrollIndicatorArrows(void); +static void BuyQuantityAddScrollIndicatorArrows(void); +static void BuyMenuRemoveScrollIndicatorArrows(void); +static void sub_809B764(void); +static void BuyMenuDrawMapBg(void); +static void BuyMenuDrawMapMetatile(s16 x, s16 y, const u16 *src, u8 metatileLayerType); +static void BuyMenuDrawMapMetatileLayer(u16 *dest, s16 offset1, s16 offset2, const u16 *src); +static void BuyMenuCollectEventObjectData(void); +static void BuyMenuDrawEventObjects(void); +static void BuyMenuCopyTilemapData(void); +static void BuyMenuPrintItemQuantityAndPrice(u8 taskId); +static void Task_BuyMenu(u8 taskId); +static void Task_BuyHowManyDialogueInit(u8 taskId); +static void Task_BuyHowManyDialogueHandleInput(u8 taskId); +static void CreateBuyMenuConfirmPurchaseWindow(u8 taskId); +static void BuyMenuTryMakePurchase(u8 taskId); +static void BuyMenuSubtractMoney(u8 taskId); +static void Task_ReturnToItemListAfterItemPurchase(u8 taskId); +static void BuyMenuReturnToItemList(u8 taskId); +static void ExitBuyMenu(u8 taskId); +static void Task_ExitBuyMenu(u8 taskId); +static void nullsub_52(u8 taskId); +static void nullsub_53(void); +static void RecordQuestLogItemPurchase(void); + +static const struct MenuAction sShopMenuActions_BuySellQuit[] = +{ + {gText_ShopBuy, {.void_u8 = Task_HandleShopMenuBuy}}, + {gText_ShopSell, {.void_u8 = Task_HandleShopMenuSell}}, + {gText_ShopQuit, {.void_u8 = Task_HandleShopMenuQuit}} +}; + +static const struct YesNoFuncTable sShopMenuActions_BuyQuit[] = +{ + BuyMenuTryMakePurchase, + BuyMenuReturnToItemList +}; + +static const struct WindowTemplate sShopMenuWindowTemplate = +{ + .bg = 0, + .tilemapLeft = 2, + .tilemapTop = 1, + .width = 12, + .height = 6, + .paletteNum = 15, + .baseBlock = 8 +}; + +static const struct BgTemplate sShopBuyMenuBgTemplates[4] = +{ + { + .bg = 0, + .charBaseIndex = 2, + .mapBaseIndex = 31, + .screenSize = 0, + .paletteMode = 0, + .priority = 0, + .baseTile = 0 + }, + { + .bg = 1, + .charBaseIndex = 0, + .mapBaseIndex = 30, + .screenSize = 0, + .paletteMode = 0, + .priority = 1, + .baseTile = 0 + }, + { + .bg = 2, + .charBaseIndex = 0, + .mapBaseIndex = 29, + .screenSize = 0, + .paletteMode = 0, + .priority = 2, + .baseTile = 0 + }, + { + .bg = 3, + .charBaseIndex = 0, + .mapBaseIndex = 28, + .screenSize = 0, + .paletteMode = 0, + .priority = 3, + .baseTile = 0 + } +}; + +// Functions +static u8 CreateShopMenu(u8 a0) +{ + gShopData.martType = GetMartTypeFromItemList(a0); + gShopData.selectedRow = 0; + if (ContextNpcGetTextColor() == 0) + gShopData.unk16_4 = 4; + else + gShopData.unk16_4 = 5; + + sShopMenuWindowId = AddWindow(&sShopMenuWindowTemplate); + SetStdWindowBorderStyle(sShopMenuWindowId, 0); + PrintTextArray(sShopMenuWindowId, 2, GetMenuCursorDimensionByFont(2, 0), 2, 16, 3, sShopMenuActions_BuySellQuit); + Menu_InitCursor(sShopMenuWindowId, 2, 0, 2, 16, 3, 0); + PutWindowTilemap(sShopMenuWindowId); + CopyWindowToVram(sShopMenuWindowId, 1); + return CreateTask(Task_ShopMenu, 8); +} + +static u8 GetMartTypeFromItemList(u32 a0) +{ + u16 i; + + if (a0) + return a0; + + for (i = 0; i < gShopData.itemCount && gShopData.itemList[i] != 0; i++) + { + if (ItemId_GetPocket(gShopData.itemList[i]) == POCKET_TM_CASE) + return 1; + } + return 0; +} + +static void SetShopItemsForSale(const u16 *items) +{ + gShopData.itemList = items; + gShopData.itemCount = 0; + if (gShopData.itemList[0] == 0) + return; + + while (gShopData.itemList[gShopData.itemCount]) + { + ++gShopData.itemCount; + } +} + +static void SetShopMenuCallback(void (*callback)(void)) +{ + gShopData.callback = callback; +} + +static void Task_ShopMenu(u8 taskId) +{ + s8 input = Menu_ProcessInputNoWrapAround(); + + switch (input) + { + case MENU_NOTHING_CHOSEN: + break; + case MENU_B_PRESSED: + PlaySE(SE_SELECT); + Task_HandleShopMenuQuit(taskId); + break; + default: + sShopMenuActions_BuySellQuit[Menu_GetCursorPos()].func.void_u8(taskId); + break; + } +} + +static void Task_HandleShopMenuBuy(u8 taskId) +{ + SetWordTaskArg(taskId, 0xE, (u32)CB2_InitBuyMenu); + fade_screen(1, 0); + gTasks[taskId].func = Task_GoToBuyOrSellMenu; +} + +static void Task_HandleShopMenuSell(u8 taskId) +{ + SetWordTaskArg(taskId, 0xE, (u32)CB2_GoToSellMenu); + fade_screen(1, 0); + gTasks[taskId].func = Task_GoToBuyOrSellMenu; +} + +static void CB2_GoToSellMenu(void) +{ + GoToBagMenu(2, POCKET_POKE_BALLS, CB2_ReturnToField); + gFieldCallback = MapPostLoadHook_ReturnToShopMenu; +} + +static void Task_HandleShopMenuQuit(u8 taskId) +{ + ClearShopMenuWindow(); + RecordQuestLogItemPurchase(); + DestroyTask(taskId); + if (gShopData.callback != NULL) + gShopData.callback(); +} + +static void ClearShopMenuWindow(void) +{ + ClearStdWindowAndFrameToTransparent(sShopMenuWindowId, 2); + RemoveWindow(sShopMenuWindowId); +} + +static void Task_GoToBuyOrSellMenu(u8 taskId) +{ + if (gPaletteFade.active) + return; + + SetMainCallback2((void *)GetWordTaskArg(taskId, 0xE)); + FreeAllWindowBuffers(); + DestroyTask(taskId); +} + +static void MapPostLoadHook_ReturnToShopMenu(void) +{ + sub_807DC00(); + CreateTask(Task_ReturnToShopMenu, 8); +} + +static void Task_ReturnToShopMenu(u8 taskId) +{ + if (IsWeatherNotFadingIn() != TRUE) + return; + + DisplayItemMessageOnField(taskId, GetMartUnk16_4(), gText_CanIHelpWithAnythingElse, ShowShopMenuAfterExitingBuyOrSellMenu); +} + +static void ShowShopMenuAfterExitingBuyOrSellMenu(u8 taskId) +{ + CreateShopMenu(gShopData.martType); + DestroyTask(taskId); +} + +static void CB2_BuyMenu(void) +{ + RunTasks(); + AnimateSprites(); + BuildOamBuffer(); + UpdatePaletteFade(); + DoScheduledBgTilemapCopiesToVram(); +} + +static void VBlankCB_BuyMenu(void) +{ + LoadOam(); + ProcessSpriteCopyRequests(); + TransferPlttBuffer(); +} + +static void CB2_InitBuyMenu(void) +{ + u8 taskId; + + switch (gMain.state) + { + case 0: + SetVBlankHBlankCallbacksToNull(); + CpuFastFill(0, (void *)OAM, 0x400); + ScanlineEffect_Stop(); + ResetTempTileDataBuffers(); + FreeAllSpritePalettes(); + ResetPaletteFade(); + ResetSpriteData(); + ResetTasks(); + ClearScheduledBgCopiesToVram(); + ResetItemMenuIconState(); + if (!(InitShopData()) || !(BuyMenuBuildListMenuTemplate())) + return; + BuyMenuInitBgs(); + FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 0x20, 0x20); + FillBgTilemapBufferRect_Palette0(1, 0, 0, 0, 0x20, 0x20); + FillBgTilemapBufferRect_Palette0(2, 0, 0, 0, 0x20, 0x20); + FillBgTilemapBufferRect_Palette0(3, 0, 0, 0, 0x20, 0x20); + BuyMenuInitWindows(gShopData.martType); + BuyMenuDecompressBgGraphics(); + gMain.state++; + break; + case 1: + if (FreeTempTileDataBuffersIfPossible()) + return; + gMain.state++; + break; + default: + gShopData.selectedRow = 0; + gShopData.scrollOffset = 0; + BuyMenuDrawGraphics(); + BuyMenuAddScrollIndicatorArrows(); + taskId = CreateTask(Task_BuyMenu, 8); + gTasks[taskId].tListTaskId = ListMenuInit(&gMultiuseListMenuTemplate, 0, 0); + BlendPalettes(0xFFFFFFFF, 0x10, RGB_BLACK); + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0x10, 0, RGB_BLACK); + SetVBlankCallback(VBlankCB_BuyMenu); + SetMainCallback2(CB2_BuyMenu); + break; + } +} + +static bool8 InitShopData(void) +{ + gShopTilemapBuffer1 = Alloc(sizeof(*gShopTilemapBuffer1)); + if (gShopTilemapBuffer1 == NULL) + { + BuyMenuFreeMemory(); + SetShopExitCallback(); + return FALSE; + } + + gShopTilemapBuffer2 = Alloc(sizeof(*gShopTilemapBuffer2)); + if (gShopTilemapBuffer2 == NULL) + { + BuyMenuFreeMemory(); + SetShopExitCallback(); + return FALSE; + } + + gShopTilemapBuffer3 = Alloc(sizeof(*gShopTilemapBuffer3)); + if (gShopTilemapBuffer3 == NULL) + { + BuyMenuFreeMemory(); + SetShopExitCallback(); + return FALSE; + } + + gShopTilemapBuffer4 = Alloc(sizeof(*gShopTilemapBuffer4)); + if (gShopTilemapBuffer4 == NULL) + { + BuyMenuFreeMemory(); + SetShopExitCallback(); + return FALSE; + } + + return TRUE; +} + +static void BuyMenuInitBgs(void) +{ + ResetBgsAndClearDma3BusyFlags(0); + InitBgsFromTemplates(0, sShopBuyMenuBgTemplates, NELEMS(sShopBuyMenuBgTemplates)); + SetBgTilemapBuffer(1, gShopTilemapBuffer2); + SetBgTilemapBuffer(2, gShopTilemapBuffer4); + SetBgTilemapBuffer(3, gShopTilemapBuffer3); + SetGpuReg(REG_OFFSET_BG0HOFS, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_BG0VOFS, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_BG1HOFS, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_BG1VOFS, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_BG2HOFS, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_BG2VOFS, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_BG3HOFS, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_BG3VOFS, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_BLDCNT, DISPCNT_MODE_0); + SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP); + ShowBg(0); + ShowBg(1); + ShowBg(2); + ShowBg(3); +} + +static void BuyMenuDecompressBgGraphics(void) +{ + void* pal; + + DecompressAndCopyTileDataToVram(1, gBuyMenuFrame_Gfx, 0x480, 0x3DC, 0); + if ((gShopData.martType) != MART_TYPE_TMHM) + LZDecompressWram(gBuyMenuFrame_Tilemap, gShopTilemapBuffer1); + else + LZDecompressWram(gBuyMenuFrame_TmHmTilemap, gShopTilemapBuffer1); + + pal = Alloc(0x40); + LZDecompressWram(gBuyMenuFrame_Pal, pal); + LoadPalette(pal, 0xB0, 0x20); + LoadPalette(pal + 0x20, 0x60, 0x20); + Free(pal); +} + +static void sub_809B10C(bool32 a0) +{ + u8 v; + + if (a0 == FALSE) + v = 0xB; + else + v = 6; + + if ((gShopData.martType) != MART_TYPE_TMHM) + SetBgTilemapPalette(1, 0, 0xE, 0x1E, 6, v); + else + SetBgTilemapPalette(1, 0, 0xC, 0x1E, 8, v); + + ScheduleBgCopyTilemapToVram(1); +} + +static void BuyMenuDrawGraphics(void) +{ + sub_809B764(); + BuyMenuCopyTilemapData(); + BuyMenuDrawMoneyBox(); + ScheduleBgCopyTilemapToVram(0); + ScheduleBgCopyTilemapToVram(1); + ScheduleBgCopyTilemapToVram(2); + ScheduleBgCopyTilemapToVram(3); +} + +bool8 BuyMenuBuildListMenuTemplate(void) +{ + u16 i, v; + + sShopMenuListMenu = Alloc((gShopData.itemCount + 1) * sizeof(*sShopMenuListMenu)); + if (sShopMenuListMenu == NULL + || (sShopMenuItemStrings = Alloc((gShopData.itemCount + 1) * sizeof(*sShopMenuItemStrings))) == NULL) + { + BuyMenuFreeMemory(); + SetShopExitCallback(); + return FALSE; + } + + for (i = 0; i < gShopData.itemCount; i++) + { + PokeMartWriteNameAndIdAt(&sShopMenuListMenu[i], gShopData.itemList[i], sShopMenuItemStrings[i]); + } + StringCopy(sShopMenuItemStrings[i], gFameCheckerText_Cancel); + sShopMenuListMenu[i].label = sShopMenuItemStrings[i]; + sShopMenuListMenu[i].index = -2; + gMultiuseListMenuTemplate.items = sShopMenuListMenu; + gMultiuseListMenuTemplate.totalItems = gShopData.itemCount + 1; + gMultiuseListMenuTemplate.windowId = 4; + gMultiuseListMenuTemplate.header_X = 0; + gMultiuseListMenuTemplate.item_X = 9; + gMultiuseListMenuTemplate.cursor_X = 1; + gMultiuseListMenuTemplate.lettersSpacing = 0; + gMultiuseListMenuTemplate.itemVerticalPadding = 2; + gMultiuseListMenuTemplate.upText_Y = 2; + gMultiuseListMenuTemplate.fontId = 2; + gMultiuseListMenuTemplate.fillValue = 0; + gMultiuseListMenuTemplate.cursorPal = GetFontAttribute(2, FONTATTR_COLOR_FOREGROUND); + gMultiuseListMenuTemplate.cursorShadowPal = GetFontAttribute(2, FONTATTR_COLOR_SHADOW); + gMultiuseListMenuTemplate.moveCursorFunc = BuyMenuPrintItemDescriptionAndShowItemIcon; + gMultiuseListMenuTemplate.itemPrintFunc = BuyMenuPrintPriceInList; + gMultiuseListMenuTemplate.scrollMultiple = 0; + gMultiuseListMenuTemplate.cursorKind = 0; + + if (gShopData.martType == MART_TYPE_TMHM) + v = 5; + else + v = 6; + + if ((gShopData.itemCount + 1) > v) + gMultiuseListMenuTemplate.maxShowed = v; + else + gMultiuseListMenuTemplate.maxShowed = gShopData.itemCount + 1; + + gShopData.field12 = gMultiuseListMenuTemplate.maxShowed; + return TRUE; +} + +static void PokeMartWriteNameAndIdAt(struct ListMenuItem *list, u16 index, u8* dst) +{ + CopyItemName(index, dst); + list->label = dst; + list->index = index; +} + +static void BuyMenuPrintItemDescriptionAndShowItemIcon(s32 item, bool8 onInit, struct ListMenu *list) +{ + const u8 *description; + + if (onInit != TRUE) + PlaySE(SE_SELECT); + + if (item != INDEX_CANCEL) + description = ItemId_GetDescription(item); + else + description = gText_QuitShopping; + + FillWindowPixelBuffer(5, PIXEL_FILL(0)); + if (gShopData.martType != 1) + { + DestroyItemMenuIcon(gShopData.itemSlot ^ 1); + if (item != INDEX_CANCEL) + CreateItemMenuIcon(item, gShopData.itemSlot); + else + CreateItemMenuIcon(ITEM_N_A, gShopData.itemSlot); + + gShopData.itemSlot ^= 1; + BuyMenuPrint(5, 2, description, 0, 3, 2, 1, 0, 0); + } + else //TM Mart + { + FillWindowPixelBuffer(6, PIXEL_FILL(0)); + LoadTmHmNameInMart(item); + BuyMenuPrint(5, 2, description, 2, 3, 1, 0, 0, 0); + } +} + +static void BuyMenuPrintPriceInList(u8 windowId, s32 item, u8 y) +{ + s32 x; + u8 *loc; + + if (item != INDEX_CANCEL) + { + ConvertIntToDecimalStringN(gStringVar1, itemid_get_market_price(item), 0, 4); + x = 4 - StringLength(gStringVar1); + loc = gStringVar4; + while (x-- != 0) + *loc++ = 0; + StringExpandPlaceholders(loc, gText_PokedollarVar1); + BuyMenuPrint(windowId, 0, gStringVar4, 0x69, y, 0, 0, TEXT_SPEED_FF, 1); + } +} + +static void LoadTmHmNameInMart(s32 item) +{ + if (item != INDEX_CANCEL) + { + ConvertIntToDecimalStringN(gStringVar1, item - ITEM_DEVON_SCOPE, 2, 2); + StringCopy(gStringVar4, gOtherText_UnkF9_08_Clear_01); + StringAppend(gStringVar4, gStringVar1); + BuyMenuPrint(6, 0, gStringVar4, 0, 0, 0, 0, TEXT_SPEED_FF, 1); + StringCopy(gStringVar4, gMoveNames[ItemIdToBattleMoveId(item)]); + BuyMenuPrint(6, 2, gStringVar4, 0, 0x10, 0, 0, 0, 1); + } + else + { + BuyMenuPrint(6, 0, gText_ThreeHyphens, 0, 0, 0, 0, TEXT_SPEED_FF, 1); + BuyMenuPrint(6, 2, gText_SevenHyphens, 0, 0x10, 0, 0, 0, 1); + } +} + +u8 GetMartUnk16_4(void) +{ + return gShopData.unk16_4; +} + +static void BuyMenuPrintCursor(u8 listTaskId, u8 a1) +{ + BuyMenuPrintCursorAtYPosition(ListMenuGetYCoordForPrintingArrowCursor(listTaskId), a1); +} + +static void BuyMenuPrintCursorAtYPosition(u8 y, u8 a1) +{ + if (a1 == 0xFF) + { + FillWindowPixelRect(4, 0, 1, y, GetFontAttribute(2, FONTATTR_MAX_LETTER_WIDTH), GetFontAttribute(2, FONTATTR_MAX_LETTER_HEIGHT)); + CopyWindowToVram(4, 2); + } + else + { + BuyMenuPrint(4, 2, gFameCheckerText_ListMenuCursor, 1, y, 0, 0, 0, a1); + } +} + +static void BuyMenuFreeMemory(void) +{ + if (gShopTilemapBuffer1 != NULL) + Free(gShopTilemapBuffer1); + + if (gShopTilemapBuffer2 != NULL) + Free(gShopTilemapBuffer2); + + if (gShopTilemapBuffer3 != NULL) + Free(gShopTilemapBuffer3); + + if (gShopTilemapBuffer4 != NULL) + Free(gShopTilemapBuffer4); + + if (sShopMenuListMenu != NULL) + Free(sShopMenuListMenu); + + if (sShopMenuItemStrings != NULL) + Free(sShopMenuItemStrings); + + FreeAllWindowBuffers(); +} + +static void SetShopExitCallback(void) +{ + gFieldCallback = MapPostLoadHook_ReturnToShopMenu; + SetMainCallback2(CB2_ReturnToField); +} + + +static void BuyMenuAddScrollIndicatorArrows(void) +{ + if (gShopData.martType != MART_TYPE_TMHM) + { + gShopData.unk16_11 = AddScrollIndicatorArrowPairParameterized(SCROLL_ARROW_UP, 160, 8, 104, + (gShopData.itemCount - gShopData.field12) + 1, 110, 110, &gShopData.scrollOffset); + } + else + { + gShopData.unk16_11 = AddScrollIndicatorArrowPairParameterized(SCROLL_ARROW_UP, 160, 8, 88, + (gShopData.itemCount - gShopData.field12) + 1, 110, 110, &gShopData.scrollOffset); + } +} + +static void BuyQuantityAddScrollIndicatorArrows(void) +{ + gShopData.unk18 = 1; + gShopData.unk16_11 = AddScrollIndicatorArrowPairParameterized(SCROLL_ARROW_UP, 0x98, 0x48, 0x68, 2, 0x6E, 0x6E, &gShopData.unk18); +} + +static void BuyMenuRemoveScrollIndicatorArrows(void) +{ + if ((gShopData.unk16_11) == 0x1F) + return; + + RemoveScrollIndicatorArrowPair(gShopData.unk16_11); + gShopData.unk16_11 = 0x1F; +} + +static void sub_809B764(void) +{ + BuyMenuCollectEventObjectData(); + BuyMenuDrawEventObjects(); + BuyMenuDrawMapBg(); +} + +static void BuyMenuDrawMapBg(void) +{ + s16 i, j, x, y; + const struct MapData *mapData; + u16 metatile; + u8 metatileLayerType; + + mapData = gMapHeader.mapData; + GetXYCoordsOneStepInFrontOfPlayer(&x, &y); + x -= 2; + y -= 3; + + for (j = 0; j < 10; j++) + { + for (i = 0; i < 5; i++) + { + metatile = MapGridGetMetatileIdAt(x + i, y + j); + metatileLayerType = MapGridGetMetatileLayerTypeAt(x + i, y + j); + + if (metatile < NUM_METATILES_IN_PRIMARY) + { + BuyMenuDrawMapMetatile(i, j, (u16*)mapData->primaryTileset->metatiles + metatile * 8, metatileLayerType); + } + else + { + BuyMenuDrawMapMetatile(i, j, (u16*)mapData->secondaryTileset->metatiles + ((metatile - NUM_METATILES_IN_PRIMARY) * 8), metatileLayerType); + } + } + } +} + +static void BuyMenuDrawMapMetatile(s16 x, s16 y, const u16 *src, u8 metatileLayerType) +{ + u16 offset1 = x * 2; + u16 offset2 = y * 64 + 64; + + switch (metatileLayerType) + { + case 0: + BuyMenuDrawMapMetatileLayer(*gShopTilemapBuffer4, offset1, offset2, src); + BuyMenuDrawMapMetatileLayer(*gShopTilemapBuffer2, offset1, offset2, src + 4); + break; + case 1: + BuyMenuDrawMapMetatileLayer(*gShopTilemapBuffer3, offset1, offset2, src); + BuyMenuDrawMapMetatileLayer(*gShopTilemapBuffer4, offset1, offset2, src + 4); + break; + case 2: + BuyMenuDrawMapMetatileLayer(*gShopTilemapBuffer3, offset1, offset2, src); + BuyMenuDrawMapMetatileLayer(*gShopTilemapBuffer2, offset1, offset2, src + 4); + break; + } +} + +static void BuyMenuDrawMapMetatileLayer(u16 *dest, s16 offset1, s16 offset2, const u16 *src) +{ + dest[offset1 + offset2] = src[0]; // top left + dest[offset1 + offset2 + 1] = src[1]; // top right + dest[offset1 + offset2 + 32] = src[2]; // bottom left + dest[offset1 + offset2 + 33] = src[3]; // bottom right +} + +static void BuyMenuCollectEventObjectData(void) +{ + s16 facingX, facingY; + u8 x, y, z; + u8 num = 0; + + GetXYCoordsOneStepInFrontOfPlayer(&facingX, &facingY); + z = PlayerGetZCoord(); + + for (y = 0; y < MAP_OBJECTS_COUNT; y++) + sViewportMapObjects[y][EVENT_OBJ_ID] = MAP_OBJECTS_COUNT; + + for (y = 0; y < 5; y++) + { + for (x = 0; x < 7; x++) + { + u8 eventObjId = GetFieldObjectIdByXYZ(facingX - 3 + x, facingY - 2 + y, z); + if (eventObjId != MAP_OBJECTS_COUNT) + { + sViewportMapObjects[num][EVENT_OBJ_ID] = eventObjId; + sViewportMapObjects[num][X_COORD] = x; + sViewportMapObjects[num][Y_COORD] = y; + + switch (gMapObjects[eventObjId].facingDirection) + { + case DIR_SOUTH: + sViewportMapObjects[num][ANIM_NUM] = 0; + break; + case DIR_NORTH: + sViewportMapObjects[num][ANIM_NUM] = 1; + break; + case DIR_WEST: + sViewportMapObjects[num][ANIM_NUM] = 2; + break; + case DIR_EAST: + default: + sViewportMapObjects[num][ANIM_NUM] = 3; + break; + } + num++; + } + } + } +} + +static void BuyMenuDrawEventObjects(void) +{ + u8 i, spriteId; + const struct MapObjectGraphicsInfo *graphicsInfo; + + for (i = 0; i < MAP_OBJECTS_COUNT; i++) + { + if (sViewportMapObjects[i][EVENT_OBJ_ID] == MAP_OBJECTS_COUNT) + continue; + + graphicsInfo = GetFieldObjectGraphicsInfo(gMapObjects[sViewportMapObjects[i][EVENT_OBJ_ID]].graphicsId); + spriteId = AddPseudoEventObject( + gMapObjects[sViewportMapObjects[i][EVENT_OBJ_ID]].graphicsId, + SpriteCallbackDummy, + (u16)sViewportMapObjects[i][X_COORD] * 16 - 8, + (u16)sViewportMapObjects[i][Y_COORD] * 16 + 48 - graphicsInfo->height / 2, + 2); + StartSpriteAnim(&gSprites[spriteId], sViewportMapObjects[i][ANIM_NUM]); + } +} + +static void BuyMenuCopyTilemapData(void) +{ + s16 i; + u16 *dst = *gShopTilemapBuffer2; + u16 *src = *gShopTilemapBuffer1; + + for (i = 0; i < 0x400; i++) + { + if (src[i] == 0) + continue; + dst[i] = src[i] + 0xb3dc; + } +} + +static void BuyMenuPrintItemQuantityAndPrice(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + FillWindowPixelBuffer(3, PIXEL_FILL(1)); + PrintMoneyAmount(3, 0x36, 0xA, gShopData.itemPrice, TEXT_SPEED_FF); + ConvertIntToDecimalStringN(gStringVar1, tItemCount, STR_CONV_MODE_LEADING_ZEROS, 2); + StringExpandPlaceholders(gStringVar4, gText_TimesStrVar1); + BuyMenuPrint(3, 0, gStringVar4, 2, 0xA, 0, 0, 0, 1); +} + +static void Task_BuyMenu(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + if (!gPaletteFade.active) + { + s32 itemId = ListMenu_ProcessInput(tListTaskId); + ListMenuGetScrollAndRow(tListTaskId, &gShopData.scrollOffset, &gShopData.selectedRow); + switch (itemId) + { + case LIST_NOTHING_CHOSEN: + break; + case LIST_CANCEL: + PlaySE(SE_SELECT); + ExitBuyMenu(taskId); + break; + default: + PlaySE(SE_SELECT); + tItemId = itemId; + ClearWindowTilemap(5); + BuyMenuRemoveScrollIndicatorArrows(); + BuyMenuPrintCursor(tListTaskId, 2); + sub_809B10C(1); + gShopData.itemPrice = itemid_get_market_price(itemId); + if (!IsEnoughMoney(&gSaveBlock1Ptr->money, gShopData.itemPrice)) + { + BuyMenuDisplayMessage(taskId, gText_YouDontHaveMoney, BuyMenuReturnToItemList); + } + else + { + CopyItemName(itemId, gStringVar1); + BuyMenuDisplayMessage(taskId, gText_Var1CertainlyHowMany, Task_BuyHowManyDialogueInit); + } + break; + } + } +} + +static void Task_BuyHowManyDialogueInit(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + u16 quantityInBag = BagGetQuantityByItemId(tItemId); + u16 maxQuantity; + + BuyMenuQuantityBoxThinBorder(1, 0); + ConvertIntToDecimalStringN(gStringVar1, quantityInBag, STR_CONV_MODE_RIGHT_ALIGN, 3); + StringExpandPlaceholders(gStringVar4, gText_InBagVar1); + BuyMenuPrint(1, 2, gStringVar4, 0, 2, 0, 0, 0, 1); + tItemCount = 1; + BuyMenuQuantityBoxNormalBorder(3, 0); + BuyMenuPrintItemQuantityAndPrice(taskId); + ScheduleBgCopyTilemapToVram(0); + maxQuantity = GetMoney(&gSaveBlock1Ptr->money) / itemid_get_market_price(tItemId); + if (maxQuantity > 99) + gShopData.maxQuantity = 99; + else + gShopData.maxQuantity = (u8)maxQuantity; + + if (maxQuantity != 1) + BuyQuantityAddScrollIndicatorArrows(); + + gTasks[taskId].func = Task_BuyHowManyDialogueHandleInput; +} + +static void Task_BuyHowManyDialogueHandleInput(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + if (AdjustQuantityAccordingToDPadInput(&tItemCount, gShopData.maxQuantity) == TRUE) + { + gShopData.itemPrice = itemid_get_market_price(tItemId) * tItemCount; + BuyMenuPrintItemQuantityAndPrice(taskId); + } + else + { + if (JOY_NEW(A_BUTTON)) + { + PlaySE(SE_SELECT); + BuyMenuRemoveScrollIndicatorArrows(); + ClearStdWindowAndFrameToTransparent(3, 0); + ClearStdWindowAndFrameToTransparent(1, 0); + ClearWindowTilemap(3); + ClearWindowTilemap(1); + PutWindowTilemap(4); + CopyItemName(tItemId, gStringVar1); + ConvertIntToDecimalStringN(gStringVar2, tItemCount, STR_CONV_MODE_LEFT_ALIGN, 2); + ConvertIntToDecimalStringN(gStringVar3, gShopData.itemPrice, STR_CONV_MODE_LEFT_ALIGN, 8); + BuyMenuDisplayMessage(taskId, gText_Var1AndYouWantedVar2, CreateBuyMenuConfirmPurchaseWindow); + } + else if (JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + BuyMenuRemoveScrollIndicatorArrows(); + ClearStdWindowAndFrameToTransparent(3, 0); + ClearStdWindowAndFrameToTransparent(1, 0); + ClearWindowTilemap(3); + ClearWindowTilemap(1); + BuyMenuReturnToItemList(taskId); + } + } +} + +static void CreateBuyMenuConfirmPurchaseWindow(u8 taskId) +{ + BuyMenuConfirmPurchase(taskId, sShopMenuActions_BuyQuit); +} + +static void BuyMenuTryMakePurchase(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + PutWindowTilemap(4); + if (AddBagItem(tItemId, tItemCount) == TRUE) + { + BuyMenuDisplayMessage(taskId, gText_HereYouGoThankYou, BuyMenuSubtractMoney); + nullsub_52(taskId); + RecordItemPurchase(tItemId, tItemCount, 1); + } + else + { + BuyMenuDisplayMessage(taskId, gText_NoMoreRoomForThis, BuyMenuReturnToItemList); + } +} + +static void BuyMenuSubtractMoney(u8 taskId) +{ + IncrementGameStat(GAME_STAT_SHOPPED); + RemoveMoney(&gSaveBlock1Ptr->money, gShopData.itemPrice); + PlaySE(SE_SHOP); + PrintMoneyAmountInMoneyBox(0, GetMoney(&gSaveBlock1Ptr->money), 0); + gTasks[taskId].func = Task_ReturnToItemListAfterItemPurchase; +} + +static void Task_ReturnToItemListAfterItemPurchase(u8 taskId) +{ + if (JOY_NEW(A_BUTTON) || JOY_NEW(B_BUTTON)) + { + PlaySE(SE_SELECT); + BuyMenuReturnToItemList(taskId); + } +} + +static void BuyMenuReturnToItemList(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + ClearDialogWindowAndFrameToTransparent(2, 0); + BuyMenuPrintCursor(tListTaskId, 1); + sub_809B10C(0); + PutWindowTilemap(4); + PutWindowTilemap(5); + if (gShopData.martType == MART_TYPE_TMHM) + PutWindowTilemap(6); + + ScheduleBgCopyTilemapToVram(0); + BuyMenuAddScrollIndicatorArrows(); + gTasks[taskId].func = Task_BuyMenu; +} + +static void ExitBuyMenu(u8 taskId) +{ + gFieldCallback = MapPostLoadHook_ReturnToShopMenu; + BeginNormalPaletteFade(0xFFFFFFFF, 0, 0, 16, RGB_BLACK); + gTasks[taskId].func = Task_ExitBuyMenu; +} + +static void Task_ExitBuyMenu(u8 taskId) +{ + s16 *data = gTasks[taskId].data; + + if (!gPaletteFade.active) + { + DestroyListMenuTask(tListTaskId, NULL, NULL); + BuyMenuFreeMemory(); + SetMainCallback2(CB2_ReturnToField); + DestroyTask(taskId); + } +} + +static void nullsub_52(u8 taskId) +{ +} + +static void nullsub_53(void) +{ +} + +void RecordItemPurchase(u16 item, u16 quantity, u8 a2) +{ + struct MartHistory *history; + + if (gShopMenuHistory[0].unkA == a2) + { + history = &gShopMenuHistory[0]; + } + else if (gShopMenuHistory[1].unkA == a2) + { + history = &gShopMenuHistory[1]; + } + else + { + if (gShopMenuHistory[0].unkA == 0) + history = &gShopMenuHistory[0]; + else + history = &gShopMenuHistory[1]; + history->unkA = a2; + } + + if (history->unk4 != 0) + { + history->unk9 = 1; + } + + history->unk4 = item; + if (history->unk6 < 999) + { + history->unk6 += quantity; + if (history->unk6 > 999) + history->unk6 = 999; + } + + if (history->unk0 < 999999) + { + history->unk0 += (itemid_get_market_price(item) >> (a2 - 1)) * quantity; + if (history->unk0 > 999999) + history->unk0 = 999999; + } +} + +static void RecordQuestLogItemPurchase(void) +{ + u16 v; + + v = gShopMenuHistory[0].unkA; + if (v != 0) + sub_8113550(v + 0x24, (const u16 *)&gShopMenuHistory[0]); + + v = gShopMenuHistory[1].unkA; + if (v != 0) + sub_8113550(v + 0x24, (const u16 *)&gShopMenuHistory[1]); +} + +void CreatePokemartMenu(const u16 *itemsForSale) +{ + SetShopItemsForSale(itemsForSale); + CreateShopMenu(MART_TYPE_REGULAR); + SetShopMenuCallback(EnableBothScriptContexts); + nullsub_53(); + memset(&gShopMenuHistory, 0, sizeof(gShopMenuHistory)); + gShopMenuHistory[0].unk8 = gMapHeader.regionMapSectionId; + gShopMenuHistory[1].unk8 = gMapHeader.regionMapSectionId; +} + +void CreateDecorationShop1Menu(const u16 *itemsForSale) +{ + SetShopItemsForSale(itemsForSale); + CreateShopMenu(MART_TYPE_DECOR); + SetShopMenuCallback(EnableBothScriptContexts); +} + +void CreateDecorationShop2Menu(const u16 *itemsForSale) +{ + SetShopItemsForSale(itemsForSale); + CreateShopMenu(MART_TYPE_DECOR2); + SetShopMenuCallback(EnableBothScriptContexts); +} + diff --git a/src/tm_case.c b/src/tm_case.c index a9af2332e..40509ed1b 100644 --- a/src/tm_case.c +++ b/src/tm_case.c @@ -1107,7 +1107,7 @@ static void Task_DoSaleOfTMs(u8 taskId) PlaySE(SE_SHOP); RemoveBagItem(gSpecialVar_ItemId, data[8]); AddMoney(&gSaveBlock1Ptr->money, itemid_get_market_price(gSpecialVar_ItemId) / 2 * data[8]); - sub_809C09C(gSpecialVar_ItemId, data[8], 2); + RecordItemPurchase(gSpecialVar_ItemId, data[8], 2); DestroyListMenuTask(data[0], &sTMCaseStaticResources.scrollOffset, &sTMCaseStaticResources.selectedRow); TMCaseSetup_GetTMCount(); TMCaseSetup_InitListMenuPositions(); |