add last used ball

This commit is contained in:
ghoulslash 2021-06-25 13:37:59 -06:00
parent 800e2395c0
commit c06029bc72
15 changed files with 283 additions and 22 deletions

View File

@ -0,0 +1,19 @@
JASC-PAL
0100
16
1 177 91
143 129 149
103 91 111
79 65 85
59 31 81
103 89 109
231 239 245
249 253 255
0 0 0
0 0 0
107 115 115
74 66 82
0 0 0
214 214 206
132 140 140
0 0 0

Binary file not shown.

After

Width:  |  Height:  |  Size: 223 B

View File

@ -37,6 +37,7 @@
#define B_ACTION_CANCEL_PARTNER 12 // when choosing an action
#define B_ACTION_NOTHING_FAINTED 13 // when choosing an action
#define B_ACTION_DEBUG 20
#define B_ACTION_THROW_BALL 21 // R to throw last used ball
#define B_ACTION_NONE 0xFF
#define MAX_TRAINER_ITEMS 4
@ -586,6 +587,7 @@ struct BattleStruct
u16 moveEffect2; // For Knock Off
u16 changedSpecies[PARTY_SIZE]; // For Zygarde or future forms when multiple mons can change into the same pokemon.
u8 quickClawBattlerId;
u8 ballSpriteIds[2]; // item gfx, window gfx
};
#define GET_MOVE_TYPE(move, typeArg) \

View File

@ -93,5 +93,9 @@ u8 GetScaledHPFraction(s16 hp, s16 maxhp, u8 scale);
u8 GetHPBarLevel(s16 hp, s16 maxhp);
void CreateAbilityPopUp(u8 battlerId, u32 ability, bool32 isDoubleBattle);
void DestroyAbilityPopUp(u8 battlerId);
bool32 CanThrowLastUsedBall(void);
void TryHideLastUsedBall(void);
void TryRestoreLastUsedBall(void);
void TryAddLastUsedBallItemSprites(void);
#endif // GUARD_BATTLE_INTERFACE_H

View File

@ -46,6 +46,7 @@ struct TypePower
extern const struct TypePower gNaturalGiftTable[];
void HandleAction_ThrowBall(void);
void HandleAction_UseMove(void);
void HandleAction_Switch(void);
void HandleAction_UseItem(void);

View File

@ -151,6 +151,10 @@
#define B_CRITICAL_CAPTURE TRUE // If set to TRUE, Critical Capture will be enabled.
#define B_CATCHING_CHARM_BOOST 20 // % boost in Critical Capture odds if player has the Catching Charm.
// Last Used Ball
#define B_LAST_USED_BALL TRUE // If TRUE, the "last used ball" feature from Gen 7 will be implemented
#define B_LAST_USED_BALL_BUTTON R_BUTTON // If last used ball is implemented, this button (or button combo) will trigger throwing the last used ball.
// Other
#define B_DOUBLE_WILD_CHANCE 0 // % chance of encountering two Pokémon in a Wild Encounter.
#define B_SLEEP_TURNS GEN_7 // In Gen5+, sleep lasts for 1-3 turns instead of 2-5 turns.

View File

@ -490,7 +490,8 @@ struct SaveBlock2
u16 optionsBattleSceneOff:1; // whether battle animations are disabled
u16 regionMapZoom:1; // whether the map is zoomed in
/*0x18*/ struct Pokedex pokedex;
/*0x90*/ u8 filler_90[0x8];
/*0x90*/ u16 lastUsedBall;
/*0x92*/ u8 filler_90[0x6];
/*0x98*/ struct Time localTimeOffset;
/*0xA0*/ struct Time lastBerryTreeUpdate;
/*0xA8*/ u32 gcnLinkFlags; // Read by Pokemon Colosseum/XD

View File

@ -33,5 +33,6 @@ void ItemUseInBattle_EnigmaBerry(u8);
void Task_UseDigEscapeRopeOnField(u8 taskId);
u8 CanUseDigOrEscapeRopeOnCurMap(void);
u8 CheckIfItemIsTMHMOrEvolutionStone(u16 itemId);
u32 CanThrowBall(void);
#endif // GUARD_ITEM_USE_H

View File

@ -249,7 +249,8 @@ static void HandleInputChooseAction(void)
if (JOY_NEW(A_BUTTON))
{
PlaySE(SE_SELECT);
TryHideLastUsedBall();
switch (gActionSelectionCursor[gActiveBattler])
{
case 0:
@ -336,6 +337,15 @@ static void HandleInputChooseAction(void)
BtlController_EmitTwoReturnValues(1, B_ACTION_DEBUG, 0);
PlayerBufferExecCompleted();
}
#if B_LAST_USED_BALL == TRUE
else if (JOY_NEW(B_LAST_USED_BALL_BUTTON) && CanThrowLastUsedBall())
{
PlaySE(SE_SELECT);
TryHideLastUsedBall();
BtlController_EmitTwoReturnValues(1, B_ACTION_THROW_BALL, 0);
PlayerBufferExecCompleted();
}
#endif
}
static void UnusedEndBounceEffect(void)
@ -372,6 +382,7 @@ static void HandleInputChooseTarget(void)
else
BtlController_EmitTwoReturnValues(1, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8));
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);
TryHideLastUsedBall();
HideMegaTriggerSprite();
PlayerBufferExecCompleted();
}
@ -514,6 +525,7 @@ static void HandleInputShowTargets(void)
else
BtlController_EmitTwoReturnValues(1, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8));
HideMegaTriggerSprite();
TryHideLastUsedBall();
PlayerBufferExecCompleted();
}
else if (gMain.newKeys & B_BUTTON || gPlayerDpadHoldFrames > 59)
@ -606,6 +618,7 @@ static void HandleInputChooseMove(void)
else
BtlController_EmitTwoReturnValues(1, 10, gMoveSelectionCursor[gActiveBattler] | (gMultiUsePlayerCursor << 8));
HideMegaTriggerSprite();
TryHideLastUsedBall();
PlayerBufferExecCompleted();
}
else if (canSelectTarget == 1)
@ -2691,6 +2704,7 @@ static void PlayerHandleChooseAction(void)
for (i = 0; i < 4; i++)
ActionSelectionDestroyCursorAt(i);
TryRestoreLastUsedBall();
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo);
BattlePutTextOnWindow(gDisplayedStringBattle, 1);

View File

@ -27,6 +27,9 @@
#include "constants/battle_config.h"
#include "data.h"
#include "pokemon_summary_screen.h"
#include "item_icon.h"
#include "item_use.h"
#include "item.h"
enum
{ // Corresponds to gHealthboxElementsGfxTable (and the tables after it) in graphics.c
@ -196,6 +199,9 @@ static u8 CalcBarFilledPixels(s32 maxValue, s32 oldValue, s32 receivedValue, s32
static void SpriteCb_AbilityPopUp(struct Sprite *sprite);
static void Task_FreeAbilityPopUpGfx(u8 taskId);
static void SpriteCB_LastUsedBall(struct Sprite *sprite);
static void SpriteCB_LastUsedBallWin(struct Sprite *sprite);
// const rom data
static const struct OamData sUnknown_0832C138 =
{
@ -812,6 +818,9 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId)
gSprites[megaIndicatorSpriteId].invisible = TRUE;
}
gBattleStruct->ballSpriteIds[0] = MAX_SPRITES;
gBattleStruct->ballSpriteIds[1] = MAX_SPRITES;
return healthboxLeftSpriteId;
}
@ -3128,3 +3137,175 @@ static void Task_FreeAbilityPopUpGfx(u8 taskId)
DestroyTask(taskId);
}
}
// last used ball
#define LAST_BALL_WINDOW_TAG 0xD721
static const struct OamData sOamData_LastUsedBall =
{
.y = 0,
.affineMode = 0,
.objMode = 0,
.mosaic = 0,
.bpp = 0,
.shape = SPRITE_SHAPE(32x32),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(32x32),
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
.affineParam = 0,
};
static const struct SpriteTemplate sSpriteTemplate_LastUsedBallWindow =
{
.tileTag = LAST_BALL_WINDOW_TAG,
.paletteTag = ABILITY_POP_UP_TAG,
.oam = &sOamData_LastUsedBall,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_LastUsedBallWin
};
static const u8 sLastUsedBallWindowGfx[] = INCBIN_U8("graphics/battle_interface/last_used_ball.4bpp");
static const struct SpriteSheet sSpriteSheet_LastUsedBallWindow =
{
sLastUsedBallWindowGfx, sizeof(sLastUsedBallWindowGfx), LAST_BALL_WINDOW_TAG
};
#define LAST_USED_BALL_X_F 15
#define LAST_USED_BALL_X_0 -15
#define LAST_USED_BALL_Y 68
#define LAST_BALL_WIN_X_F (LAST_USED_BALL_X_F - 1)
#define LAST_BALL_WIN_X_0 (LAST_USED_BALL_X_0 - 0)
#define LAST_USED_WIN_Y (LAST_USED_BALL_Y - 8)
#define sHide data[0]
bool32 CanThrowLastUsedBall(void)
{
return (!(CanThrowBall() != 0
|| (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|| !CheckBagHasItem(gSaveBlock2Ptr->lastUsedBall, 1)));
}
void TryAddLastUsedBallItemSprites(void)
{
if (CanThrowBall() != 0
|| (gBattleTypeFlags & BATTLE_TYPE_TRAINER)
|| !CheckBagHasItem(gSaveBlock2Ptr->lastUsedBall, 1))
return;
// ball
if (gBattleStruct->ballSpriteIds[0] == MAX_SPRITES)
{
gBattleStruct->ballSpriteIds[0] = AddItemIconSprite(102, 102, gSaveBlock2Ptr->lastUsedBall);
gSprites[gBattleStruct->ballSpriteIds[0]].pos1.x = LAST_USED_BALL_X_0;
gSprites[gBattleStruct->ballSpriteIds[0]].pos1.y = LAST_USED_BALL_Y;
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = FALSE; // restore
gSprites[gBattleStruct->ballSpriteIds[0]].callback = SpriteCB_LastUsedBall;
}
// window
LoadSpritePalette(&sSpritePalette_AbilityPopUp);
if (GetSpriteTileStartByTag(LAST_BALL_WINDOW_TAG) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_LastUsedBallWindow);
if (gBattleStruct->ballSpriteIds[1] == MAX_SPRITES)
{
gBattleStruct->ballSpriteIds[1] = CreateSprite(&sSpriteTemplate_LastUsedBallWindow,
LAST_BALL_WIN_X_0,
LAST_USED_WIN_Y, 5);
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = FALSE; // restore
}
}
static void DestroyLastUsedBallWinGfx(struct Sprite *sprite)
{
FreeSpriteTilesByTag(LAST_BALL_WINDOW_TAG);
FreeSpritePaletteByTag(ABILITY_POP_UP_TAG);
DestroySprite(sprite);
gBattleStruct->ballSpriteIds[1] = MAX_SPRITES;
}
static void DestroyLastUsedBallGfx(struct Sprite *sprite)
{
FreeSpriteTilesByTag(102);
FreeSpritePaletteByTag(102);
DestroySprite(sprite);
gBattleStruct->ballSpriteIds[0] = MAX_SPRITES;
}
static void SpriteCB_LastUsedBallWin(struct Sprite *sprite)
{
if (sprite->sHide)
{
if (sprite->pos1.x != LAST_BALL_WIN_X_0)
sprite->pos1.x--;
if (sprite->pos1.x == LAST_BALL_WIN_X_0)
DestroyLastUsedBallWinGfx(sprite);
}
else
{
if (sprite->pos1.x != LAST_BALL_WIN_X_F)
sprite->pos1.x++;
}
}
static void SpriteCB_LastUsedBall(struct Sprite *sprite)
{
if (sprite->sHide)
{
if (sprite->pos1.x != LAST_USED_BALL_X_0)
sprite->pos1.x--;
if (sprite->pos1.x == LAST_USED_BALL_X_0)
DestroyLastUsedBallGfx(sprite);
}
else
{
if (sprite->pos1.x != LAST_USED_BALL_X_F)
sprite->pos1.x++;
}
}
static void TryHideOrRestoreLastUsedBall(u8 caseId)
{
if (gBattleStruct->ballSpriteIds[0] == MAX_SPRITES)
return;
switch (caseId)
{
case 0: // hide
if (gBattleStruct->ballSpriteIds[0] != MAX_SPRITES)
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = TRUE; // hide
if (gBattleStruct->ballSpriteIds[1] != MAX_SPRITES)
gSprites[gBattleStruct->ballSpriteIds[1]].sHide = TRUE; // hide
break;
case 1: // restore
if (gBattleStruct->ballSpriteIds[0] != MAX_SPRITES)
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = FALSE; // restore
if (gBattleStruct->ballSpriteIds[1] != MAX_SPRITES)
gSprites[gBattleStruct->ballSpriteIds[1]].sHide = FALSE; // restore
break;
}
}
void TryHideLastUsedBall(void)
{
TryHideOrRestoreLastUsedBall(0);
}
void TryRestoreLastUsedBall(void)
{
if (gBattleStruct->ballSpriteIds[0] != MAX_SPRITES)
TryHideOrRestoreLastUsedBall(1);
else
TryAddLastUsedBallItemSprites();
}

View File

@ -401,6 +401,7 @@ static void (* const sTurnActionsFuncsTable[])(void) =
[B_ACTION_TRY_FINISH] = HandleAction_TryFinish,
[B_ACTION_FINISHED] = HandleAction_ActionFinished,
[B_ACTION_NOTHING_FAINTED] = HandleAction_NothingIsFainted,
[B_ACTION_THROW_BALL] = HandleAction_ThrowBall,
};
static void (* const sEndTurnFuncsTable[])(void) =
@ -4053,6 +4054,9 @@ static void HandleTurnActionSelectionState(void)
case B_ACTION_SAFARI_BALL:
gBattleCommunication[gActiveBattler]++;
break;
case B_ACTION_THROW_BALL:
gBattleCommunication[gActiveBattler]++;
break;
case B_ACTION_SAFARI_POKEBLOCK:
if ((gBattleResources->bufferB[gActiveBattler][1] | (gBattleResources->bufferB[gActiveBattler][2] << 8)) != 0)
{

View File

@ -12017,7 +12017,8 @@ static void Cmd_handleballthrow(void)
{
u32 odds;
u8 catchRate;
gSaveBlock2Ptr->lastUsedBall = gLastUsedItem;
if (gLastUsedItem == ITEM_SAFARI_BALL)
catchRate = gBattleStruct->safariCatchFactor * 1275 / 100;
else

View File

@ -742,6 +742,17 @@ void HandleAction_SafariZoneBallThrow(void)
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
void HandleAction_ThrowBall(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
gLastUsedItem = gSaveBlock2Ptr->lastUsedBall;
RemoveBagItem(gLastUsedItem, 1);
gBattlescriptCurrInstr = BattleScript_BallThrow;
gCurrentActionFuncId = B_ACTION_EXEC_SCRIPT;
}
void HandleAction_ThrowPokeblock(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];

View File

@ -936,42 +936,58 @@ void ItemUseOutOfBattle_EvolutionStone(u8 taskId)
SetUpItemUseCallback(taskId);
}
void ItemUseInBattle_PokeBall(u8 taskId)
u32 CanThrowBall(void)
{
if (IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT))
&& IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT))) // There are two present pokemon.
&& IsBattlerAlive(GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)))
{
static const u8 textCantThrowPokeBall[] = _("Cannot throw a ball!\nThere are two pokemon out there!\p");
if (!InBattlePyramid())
DisplayItemMessage(taskId, 1, textCantThrowPokeBall, BagMenu_InitListsMenu);
else
DisplayItemMessageInBattlePyramid(taskId, textCantThrowPokeBall, Task_CloseBattlePyramidBagMessage);
return 1; // There are two present pokemon.
}
else if (gBattlerInMenuId == GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)
&& IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT))) // Attempting to throw a ball with the second pokemon while both are alive.
&& IsBattlerAlive(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)))
{
static const u8 textCantThrowPokeBall[] = _("Cannot throw a ball!\p");
if (!InBattlePyramid())
DisplayItemMessage(taskId, 1, textCantThrowPokeBall, BagMenu_InitListsMenu);
else
DisplayItemMessageInBattlePyramid(taskId, textCantThrowPokeBall, Task_CloseBattlePyramidBagMessage);
return 2; // Attempting to throw a ball with the second pokemon while both are alive.
}
else if (IsPlayerPartyAndPokemonStorageFull() == FALSE) // have room for mon?
else if (IsPlayerPartyAndPokemonStorageFull() == TRUE)
{
return 3; // No room for mon
}
return 0; // usable
}
static const u8 sText_CantThrowPokeBall_TwoMons[] = _("Cannot throw a ball!\nThere are two pokemon out there!\p");
static const u8 sText_CantThrowPokeBall[] = _("Cannot throw a ball!\p");
void ItemUseInBattle_PokeBall(u8 taskId)
{
switch (CanThrowBall())
{
case 0: // usable
default:
RemoveBagItem(gSpecialVar_ItemId, 1);
if (!InBattlePyramid())
Task_FadeAndCloseBagMenu(taskId);
else
CloseBattlePyramidBag(taskId);
}
else
{
break;
case 1: // There are two present pokemon.
if (!InBattlePyramid())
DisplayItemMessage(taskId, 1, sText_CantThrowPokeBall_TwoMons, BagMenu_InitListsMenu);
else
DisplayItemMessageInBattlePyramid(taskId, sText_CantThrowPokeBall_TwoMons, Task_CloseBattlePyramidBagMessage);
break;
case 2: // Attempting to throw a ball with the second pokemon while both are alive.
if (!InBattlePyramid())
DisplayItemMessage(taskId, 1, sText_CantThrowPokeBall_TwoMons, BagMenu_InitListsMenu);
else
DisplayItemMessageInBattlePyramid(taskId, sText_CantThrowPokeBall_TwoMons, Task_CloseBattlePyramidBagMessage);
break;
case 3: // No room for mon
if (!InBattlePyramid())
DisplayItemMessage(taskId, 1, gText_BoxFull, BagMenu_InitListsMenu);
else
DisplayItemMessageInBattlePyramid(taskId, gText_BoxFull, Task_CloseBattlePyramidBagMessage);
break;
}
}

View File

@ -45,6 +45,7 @@
#include "berry_powder.h"
#include "mevent.h"
#include "union_room_chat.h"
#include "constants/items.h"
extern const u8 EventScript_ResetAllMapFlags[];
@ -204,6 +205,7 @@ void NewGameInitData(void)
WipeTrainerNameRecords();
ResetTrainerHillResults();
ResetContestLinkResults();
gSaveBlock2Ptr->lastUsedBall = ITEM_POKE_BALL;
}
static void ResetMiniGamesRecords(void)