summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/berry_pouch.c2
-rw-r--r--src/buy_menu_helpers.c4
-rw-r--r--src/fieldmap.c2
-rw-r--r--src/pokeball.c1275
-rw-r--r--src/sea_cottage_special_anim.c264
-rw-r--r--src/shop.c1161
-rw-r--r--src/tm_case.c2
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();