Merge branch '_RHH/master' into _RHH/upcoming
This commit is contained in:
commit
8821779815
3
Makefile
3
Makefile
@ -29,6 +29,9 @@ endif
|
||||
ifeq (debug,$(MAKECMDGOALS))
|
||||
DEBUG := 1
|
||||
endif
|
||||
ifeq ($(TESTELF),$(MAKECMDGOALS))
|
||||
TEST := 1
|
||||
endif
|
||||
|
||||
# Default make rule
|
||||
all: rom
|
||||
|
||||
@ -385,6 +385,18 @@ EventScript_UseDig::
|
||||
lockall
|
||||
goto EventScript_DigCommon
|
||||
|
||||
|
||||
EventScript_CutGrassCommon:
|
||||
isfollowerfieldmoveuser VAR_0x8004
|
||||
setfieldeffectargument 3, VAR_0x8004 @ skip pose if true
|
||||
dofieldeffect FLDEFF_USE_CUT_ON_GRASS
|
||||
waitstate
|
||||
|
||||
@ Use Cut grass from party menu
|
||||
EventScript_UseCutGrass::
|
||||
lockall
|
||||
goto EventScript_CutGrassCommon
|
||||
|
||||
Text_CantDive:
|
||||
.string "The sea is deep here. A POKéMON\n"
|
||||
.string "may be able to go underwater.$"
|
||||
|
||||
@ -42,9 +42,9 @@ A pull request meets the scope criteria if:
|
||||
4. **Non-SS Items**: Adds Items that have NOT appeared in a Showdown-supported title
|
||||
5. **Non-SS Gimmicks**: Adds Gimmicks that have NOT appeared in a Showdown-supported title (Showdown's Other Metagames, etc.)
|
||||
6. **Non-SS Battle Types**: Adds Special Battle Types that have NOT appeared in a Showdown-supported title
|
||||
7. **Non-SS or SS Overworld Maps**: Adds overworld maps from either Showdown-supported titles or non-Showdown-supported titles
|
||||
7. **Duplicate Feature UI**: Adds functionality that duplicates the core functionality of an existing vanilla feature
|
||||
8. **Vanilla Link Compatibility**: The ability for Base Expansion Version and Vanilla Emerald Version to connect, trade, and battle one another
|
||||
7. **Overworld Maps**: Adds overworld maps from either Showdown-supported titles or non-Showdown-supported titles
|
||||
8. **Duplicate UIs**: Adds additional user interface that covers the same functionality of an existing feature (HGSS Pokédex, BW Summary Screen, etc.)
|
||||
9. **Vanilla Link Compatibility**: The ability for Base Expansion Version and Vanilla Emerald Version to connect, trade, and battle one another
|
||||
|
||||
## Discussion Required Categories
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ AI will generally behave more recklessly. This AI enables the following behaviou
|
||||
* Switch offensively mid battle rather than defensively (if using `AI_FLAG_SMART_MON_CHOICES`)
|
||||
* Prioritize Explosion moves
|
||||
|
||||
## `AI_FLAG_PREFER_STRONGEST_MOVE`
|
||||
## `AI_FLAG_TRY_TO_2HKO`
|
||||
Adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.
|
||||
|
||||
Keep in mind that this is a weaker form of `AI_FLAG_TRY_TO_FAINT` at scoring OHKOs as it does not take into account who is attacking first, it does however handle 2HKOs.
|
||||
|
||||
@ -28,7 +28,7 @@ Let's look at an example:
|
||||
```c
|
||||
[MOVE_THUNDER_SHOCK] =
|
||||
{
|
||||
.name = HANDLE_EXPANDED_MOVE_NAME("ThunderShock", "Thunder Shock"),
|
||||
.name = COMPOUND_STRING("Thunder Shock"),
|
||||
.description = COMPOUND_STRING(
|
||||
"An electrical attack that\n"
|
||||
"may paralyze the foe."),
|
||||
@ -51,7 +51,7 @@ Let's look at an example:
|
||||
.contestComboMoves = {COMBO_STARTER_CHARGE},
|
||||
},
|
||||
```
|
||||
The `HANDLE_EXPANDED_MOVE_NAME` allows the usage of a name of extended character length, so long as the `B_EXPANDED_MOVE_NAMES` is set to `TRUE`, whereas by default it's limited in Gen 3 to 12 characters. Most of the fields here are obvious, but the two important ones for determining what a move actually *does* are `effect` and `additionalEffects`.
|
||||
Most of the fields here are obvious, but the two important ones for determining what a move actually *does* are `effect` and `additionalEffects`.
|
||||
|
||||
The `effect` represents how the move actually works when called in battle - it can be a two turn move, or a move that only works if the target is holding an item, for example. How each effect works is pretty much unique, but the way a move of a particular effect is executed is defined by a script [`data/battle_scripts_1.s`](#databattle_scripts_1s), and any *variable* characteristics such as typing or power are defined in either [`src/battle_script_commands.c`](#srcbattle_script_commandsc) or [`src/battle_util.c`](#srcbattle_utilc), depending on the effect. The vast majority of non-status moves are simply `EFFECT_HIT`, in that they deal damage and apply `additionalEffects` (if defined).
|
||||
|
||||
|
||||
@ -297,6 +297,7 @@ struct WishFutureKnock
|
||||
u8 wishPartyId[MAX_BATTLERS_COUNT];
|
||||
u8 weatherDuration;
|
||||
u8 knockedOffMons[NUM_BATTLE_SIDES]; // Each battler is represented by a bit.
|
||||
s16 futureSightDmg;
|
||||
};
|
||||
|
||||
struct AI_SavedBattleMon
|
||||
@ -575,7 +576,6 @@ struct DynamaxData
|
||||
u8 dynamaxTurns[MAX_BATTLERS_COUNT];
|
||||
u16 baseMoves[MAX_BATTLERS_COUNT]; // base move of Max Move
|
||||
u16 lastUsedBaseMove;
|
||||
u16 levelUpHP;
|
||||
};
|
||||
|
||||
struct BattleGimmickData
|
||||
|
||||
@ -58,7 +58,7 @@ enum MaxMoveEffect
|
||||
|
||||
bool32 CanDynamax(u32 battler);
|
||||
bool32 IsGigantamaxed(u32 battler);
|
||||
void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon);
|
||||
void ApplyDynamaxHPMultiplier(struct Pokemon* mon);
|
||||
void ActivateDynamax(u32 battler);
|
||||
u16 GetNonDynamaxHP(u32 battler);
|
||||
u16 GetNonDynamaxMaxHP(u32 battler);
|
||||
|
||||
@ -296,7 +296,7 @@ u32 GetBattlerMoveTargetType(u32 battler, u32 move);
|
||||
bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move);
|
||||
void CopyMonLevelAndBaseStatsToBattleMon(u32 battler, struct Pokemon *mon);
|
||||
void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon);
|
||||
void RecalcBattlerStats(u32 battler, struct Pokemon *mon);
|
||||
void RecalcBattlerStats(u32 battler, struct Pokemon *mon, bool32 isDynamaxing);
|
||||
bool32 IsAlly(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 IsGen6ExpShareEnabled(void);
|
||||
bool32 MoveHasAdditionalEffect(u32 move, u32 moveEffect);
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
#define AI_FLAG_CHECK_VIABILITY (1 << 2) // AI damaging moves and move effects to determine the best available move in the current situation.
|
||||
#define AI_FLAG_FORCE_SETUP_FIRST_TURN (1 << 3) // AI will prioritize using setup moves on the first turn at the expensve of all else. AI_FLAG_CHECK_VIABILITY will instead do this when the AI determines it makes sense.
|
||||
#define AI_FLAG_RISKY (1 << 4) // AI will generally behave more recklessly, prioritizing damage over accuracy, explosions, etc.
|
||||
#define AI_FLAG_PREFER_STRONGEST_MOVE (1 << 5) // AI adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.
|
||||
#define AI_FLAG_TRY_TO_2HKO (1 << 5) // AI adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.
|
||||
#define AI_FLAG_PREFER_BATON_PASS (1 << 6) // AI prefers raising its own stats and setting for / using Baton Pass.
|
||||
#define AI_FLAG_DOUBLE_BATTLE (1 << 7) // Automatically set for double battles, handles AI behaviour with partner.
|
||||
#define AI_FLAG_HP_AWARE (1 << 8) // AI will favour certain move effects based on how much remaining HP it and the player's mon have.
|
||||
|
||||
@ -406,6 +406,7 @@ extern const u8 EventScript_UseFlash[];
|
||||
extern const u8 EventScript_UseCut[];
|
||||
extern const u8 EventScript_UseRockSmash[];
|
||||
extern const u8 EventScript_UseDig[];
|
||||
extern const u8 EventScript_UseCutGrass[];
|
||||
|
||||
//player pc
|
||||
extern const u8 LittlerootTown_BrendansHouse_2F_EventScript_TurnOffPlayerPC[];
|
||||
|
||||
@ -804,5 +804,6 @@ void HealBoxPokemon(struct BoxPokemon *boxMon);
|
||||
void UpdateDaysPassedSinceFormChange(u16 days);
|
||||
void TrySetDayLimitToFormChange(struct Pokemon *mon);
|
||||
u32 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler);
|
||||
uq4_12_t GetDynamaxLevelHPMultiplier(u32 dynamaxLevel, bool32 inverseMultiplier);
|
||||
|
||||
#endif // GUARD_POKEMON_H
|
||||
|
||||
@ -48,7 +48,7 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_PreferStrongestMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
|
||||
@ -66,7 +66,7 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) =
|
||||
[2] = AI_CheckViability, // AI_FLAG_CHECK_VIABILITY
|
||||
[3] = AI_ForceSetupFirstTurn, // AI_FLAG_FORCE_SETUP_FIRST_TURN
|
||||
[4] = AI_Risky, // AI_FLAG_RISKY
|
||||
[5] = AI_PreferStrongestMove, // AI_FLAG_PREFER_STRONGEST_MOVE
|
||||
[5] = AI_TryTo2HKO, // AI_FLAG_TRY_TO_2HKO
|
||||
[6] = AI_PreferBatonPass, // AI_FLAG_PREFER_BATON_PASS
|
||||
[7] = AI_DoubleBattle, // AI_FLAG_DOUBLE_BATTLE
|
||||
[8] = AI_HPAware, // AI_FLAG_HP_AWARE
|
||||
@ -136,7 +136,7 @@ static u32 GetWildAiFlags(void)
|
||||
if (avgLevel >= 20)
|
||||
flags |= AI_FLAG_CHECK_VIABILITY;
|
||||
if (avgLevel >= 60)
|
||||
flags |= AI_FLAG_PREFER_STRONGEST_MOVE;
|
||||
flags |= AI_FLAG_TRY_TO_2HKO;
|
||||
if (avgLevel >= 80)
|
||||
flags |= AI_FLAG_HP_AWARE;
|
||||
|
||||
@ -4919,7 +4919,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
|
||||
// Adds score bonus to best powered move
|
||||
static s32 AI_PreferStrongestMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
static s32 AI_TryTo2HKO(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
{
|
||||
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
|
||||
return score;
|
||||
|
||||
@ -1410,6 +1410,14 @@ void Task_PlayerController_RestoreBgmAfterCry(u8 taskId)
|
||||
#define tExpTask_gainedExp_2 data[4] // Stored as two half-words containing a word.
|
||||
#define tExpTask_frames data[10]
|
||||
|
||||
static void DynamaxModifyHPLevelUp(struct Pokemon *mon, u32 battler, u32 oldMaxHP)
|
||||
{
|
||||
ApplyDynamaxHPMultiplier(mon);
|
||||
gBattleScripting.levelUpHP = GetMonData(mon, MON_DATA_MAX_HP) - oldMaxHP; // overwrite levelUpHP since it overflows
|
||||
gBattleMons[battler].hp += gBattleScripting.levelUpHP;
|
||||
SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp);
|
||||
}
|
||||
|
||||
static s32 GetTaskExpValue(u8 taskId)
|
||||
{
|
||||
return (u16)(gTasks[taskId].tExpTask_gainedExp_1) | (gTasks[taskId].tExpTask_gainedExp_2 << 16);
|
||||
@ -1428,21 +1436,16 @@ static void Task_GiveExpToMon(u8 taskId)
|
||||
u8 level = GetMonData(mon, MON_DATA_LEVEL);
|
||||
u32 currExp = GetMonData(mon, MON_DATA_EXP);
|
||||
u32 nextLvlExp = gExperienceTables[gSpeciesInfo[species].growthRate][level + 1];
|
||||
u32 oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP);
|
||||
|
||||
if (currExp + gainedExp >= nextLvlExp)
|
||||
{
|
||||
SetMonData(mon, MON_DATA_EXP, &nextLvlExp);
|
||||
gBattleStruct->dynamax.levelUpHP = GetMonData(mon, MON_DATA_HP) \
|
||||
+ UQ_4_12_TO_INT((gBattleScripting.levelUpHP * UQ_4_12(1.5)) + UQ_4_12_ROUND);
|
||||
CalculateMonStats(mon);
|
||||
|
||||
// Reapply Dynamax HP multiplier after stats are recalculated.
|
||||
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && monId == gBattlerPartyIndexes[battler])
|
||||
{
|
||||
ApplyDynamaxHPMultiplier(battler, mon);
|
||||
gBattleMons[battler].hp = gBattleStruct->dynamax.levelUpHP;
|
||||
SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp);
|
||||
}
|
||||
DynamaxModifyHPLevelUp(mon, battler, oldMaxHP);
|
||||
|
||||
gainedExp -= nextLvlExp - currExp;
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, RET_VALUE_LEVELED_UP, gainedExp);
|
||||
@ -1491,6 +1494,7 @@ static void Task_GiveExpWithExpBar(u8 taskId)
|
||||
{
|
||||
u8 level;
|
||||
u16 species;
|
||||
u32 oldMaxHP;
|
||||
s32 currExp, expOnNextLvl, newExpPoints;
|
||||
|
||||
if (gTasks[taskId].tExpTask_frames < 13)
|
||||
@ -1502,31 +1506,27 @@ static void Task_GiveExpWithExpBar(u8 taskId)
|
||||
u8 monId = gTasks[taskId].tExpTask_monId;
|
||||
s32 gainedExp = GetTaskExpValue(taskId);
|
||||
u8 battler = gTasks[taskId].tExpTask_battler;
|
||||
struct Pokemon *mon = &gPlayerParty[monId];
|
||||
|
||||
newExpPoints = MoveBattleBar(battler, gHealthboxSpriteIds[battler], EXP_BAR, 0);
|
||||
SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]);
|
||||
if (newExpPoints == -1) // The bar has been filled with given exp points.
|
||||
{
|
||||
m4aSongNumStop(SE_EXP);
|
||||
level = GetMonData(&gPlayerParty[monId], MON_DATA_LEVEL);
|
||||
currExp = GetMonData(&gPlayerParty[monId], MON_DATA_EXP);
|
||||
species = GetMonData(&gPlayerParty[monId], MON_DATA_SPECIES);
|
||||
level = GetMonData(mon, MON_DATA_LEVEL);
|
||||
currExp = GetMonData(mon, MON_DATA_EXP);
|
||||
species = GetMonData(mon, MON_DATA_SPECIES);
|
||||
oldMaxHP = GetMonData(mon, MON_DATA_MAX_HP);
|
||||
expOnNextLvl = gExperienceTables[gSpeciesInfo[species].growthRate][level + 1];
|
||||
|
||||
if (currExp + gainedExp >= expOnNextLvl)
|
||||
{
|
||||
SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &expOnNextLvl);
|
||||
gBattleStruct->dynamax.levelUpHP = GetMonData(&gPlayerParty[monId], MON_DATA_HP) \
|
||||
+ UQ_4_12_TO_INT((gBattleScripting.levelUpHP * UQ_4_12(1.5)) + UQ_4_12_ROUND);
|
||||
CalculateMonStats(&gPlayerParty[monId]);
|
||||
SetMonData(mon, MON_DATA_EXP, &expOnNextLvl);
|
||||
CalculateMonStats(mon);
|
||||
|
||||
// Reapply Dynamax HP multiplier after stats are recalculated.
|
||||
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && monId == gBattlerPartyIndexes[battler])
|
||||
{
|
||||
ApplyDynamaxHPMultiplier(battler, &gPlayerParty[monId]);
|
||||
gBattleMons[battler].hp = gBattleStruct->dynamax.levelUpHP;
|
||||
SetMonData(&gPlayerParty[monId], MON_DATA_HP, &gBattleMons[battler].hp);
|
||||
}
|
||||
DynamaxModifyHPLevelUp(mon, battler, oldMaxHP);
|
||||
|
||||
gainedExp -= expOnNextLvl - currExp;
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, RET_VALUE_LEVELED_UP, gainedExp);
|
||||
@ -1535,7 +1535,7 @@ static void Task_GiveExpWithExpBar(u8 taskId)
|
||||
else
|
||||
{
|
||||
currExp += gainedExp;
|
||||
SetMonData(&gPlayerParty[monId], MON_DATA_EXP, &currExp);
|
||||
SetMonData(mon, MON_DATA_EXP, &currExp);
|
||||
gBattlerControllerFuncs[battler] = Controller_WaitForString;
|
||||
DestroyTask(taskId);
|
||||
}
|
||||
@ -2042,7 +2042,6 @@ static void PlayerHandleChooseAction(u32 battler)
|
||||
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
|
||||
PREPARE_MON_NICK_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]);
|
||||
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo);
|
||||
BreakStringAutomatic(gDisplayedStringBattle, WindowWidthPx(B_WIN_ACTION_PROMPT), 2, FONT_NORMAL);
|
||||
|
||||
if (B_SHOW_PARTNER_TARGET && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && IsBattlerAlive(B_POSITION_PLAYER_RIGHT))
|
||||
{
|
||||
@ -2236,7 +2235,7 @@ void PlayerHandleExpUpdate(u32 battler)
|
||||
|
||||
static void PlayerHandleStatusXor(u32 battler)
|
||||
{
|
||||
u8 val = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_STATUS) ^ gBattleResources->bufferA[battler][1];
|
||||
u32 val = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_STATUS) ^ gBattleResources->bufferA[battler][1];
|
||||
|
||||
SetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_STATUS, &val);
|
||||
PlayerBufferExecCompleted(battler);
|
||||
|
||||
@ -299,7 +299,6 @@ static void SafariHandleChooseAction(u32 battler)
|
||||
|
||||
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
|
||||
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo2);
|
||||
BreakStringAutomatic(gDisplayedStringBattle, WindowWidthPx(B_WIN_ACTION_PROMPT), 2, FONT_NORMAL);
|
||||
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT);
|
||||
}
|
||||
|
||||
|
||||
@ -220,7 +220,7 @@ enum
|
||||
LIST_AI_CHECK_VIABILITY,
|
||||
LIST_AI_SETUP_FIRST_TURN,
|
||||
LIST_AI_RISKY,
|
||||
LIST_AI_PREFER_STRONGEST_MOVE,
|
||||
LIST_AI_TRY_TO_2HKO,
|
||||
LIST_AI_PREFER_BATON_PASS,
|
||||
LIST_AI_DOUBLE_BATTLE,
|
||||
LIST_AI_HP_AWARE,
|
||||
@ -385,7 +385,7 @@ static const u8 sText_TryToFaint[] = _("Try to Faint");
|
||||
static const u8 sText_CheckViability[] = _("Check Viability");
|
||||
static const u8 sText_SetUpFirstTurn[] = _("Setup First Turn");
|
||||
static const u8 sText_Risky[] = _("Risky");
|
||||
static const u8 sText_PreferStrongestMove[] = _("Prefer Strongest Move");
|
||||
static const u8 sText_TryTo2HKO[] = _("Try to 2HKO");
|
||||
static const u8 sText_PreferBatonPass[] = _("Prefer Baton Pass");
|
||||
static const u8 sText_DoubleBattle[] = _("Double Battle");
|
||||
static const u8 sText_HpAware[] = _("HP Aware");
|
||||
@ -628,7 +628,7 @@ static const struct ListMenuItem sAIListItems[] =
|
||||
{sText_CheckViability, LIST_AI_CHECK_VIABILITY},
|
||||
{sText_SetUpFirstTurn, LIST_AI_SETUP_FIRST_TURN},
|
||||
{sText_Risky, LIST_AI_RISKY},
|
||||
{sText_PreferStrongestMove, LIST_AI_PREFER_STRONGEST_MOVE},
|
||||
{sText_TryTo2HKO, LIST_AI_TRY_TO_2HKO},
|
||||
{sText_PreferBatonPass, LIST_AI_PREFER_BATON_PASS},
|
||||
{sText_DoubleBattle, LIST_AI_DOUBLE_BATTLE},
|
||||
{sText_HpAware, LIST_AI_HP_AWARE},
|
||||
|
||||
@ -123,22 +123,22 @@ bool32 CanDynamax(u32 battler)
|
||||
// Returns whether a battler is transformed into a Gigantamax form.
|
||||
bool32 IsGigantamaxed(u32 battler)
|
||||
{
|
||||
struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]];
|
||||
struct Pokemon *mon = GetPartyBattlerData(battler);
|
||||
if ((gSpeciesInfo[gBattleMons[battler].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
|
||||
return TRUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Applies the HP Multiplier for Dynamaxed Pokemon and Raid Bosses.
|
||||
void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon)
|
||||
void ApplyDynamaxHPMultiplier(struct Pokemon* mon)
|
||||
{
|
||||
if (GetMonData(mon, MON_DATA_SPECIES) == SPECIES_SHEDINJA)
|
||||
return;
|
||||
else
|
||||
{
|
||||
u32 scale = 150 + 5 * GetMonData(mon, MON_DATA_DYNAMAX_LEVEL);
|
||||
u32 hp = (GetMonData(mon, MON_DATA_HP) * scale + 99) / 100;
|
||||
u32 maxHP = (GetMonData(mon, MON_DATA_MAX_HP) * scale + 99) / 100;
|
||||
uq4_12_t multiplier = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), FALSE);
|
||||
u32 hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * multiplier) + UQ_4_12_ROUND);
|
||||
u32 maxHP = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_MAX_HP) * multiplier) + UQ_4_12_ROUND);
|
||||
SetMonData(mon, MON_DATA_HP, &hp);
|
||||
SetMonData(mon, MON_DATA_MAX_HP, &maxHP);
|
||||
}
|
||||
@ -151,8 +151,9 @@ u16 GetNonDynamaxHP(u32 battler)
|
||||
return gBattleMons[battler].hp;
|
||||
else
|
||||
{
|
||||
u16 mult = UQ_4_12_FLOORED(1.0/1.5); // placeholder
|
||||
u16 hp = UQ_4_12_TO_INT((gBattleMons[battler].hp * mult) + UQ_4_12_ROUND);
|
||||
struct Pokemon *mon = GetPartyBattlerData(battler);
|
||||
uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE);
|
||||
u32 hp = UQ_4_12_TO_INT((gBattleMons[battler].hp * mult) + UQ_4_12_ROUND);
|
||||
return hp;
|
||||
}
|
||||
}
|
||||
@ -164,8 +165,9 @@ u16 GetNonDynamaxMaxHP(u32 battler)
|
||||
return gBattleMons[battler].maxHP;
|
||||
else
|
||||
{
|
||||
u16 mult = UQ_4_12_FLOORED(1.0/1.5); // placeholder
|
||||
u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battler].maxHP * mult) + UQ_4_12_ROUND);
|
||||
struct Pokemon *mon = GetPartyBattlerData(battler);
|
||||
uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE);
|
||||
u32 maxHP = UQ_4_12_TO_INT((gBattleMons[battler].maxHP * mult) + UQ_4_12_ROUND);
|
||||
return maxHP;
|
||||
}
|
||||
}
|
||||
@ -202,7 +204,7 @@ void UndoDynamax(u32 battler)
|
||||
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
|
||||
{
|
||||
struct Pokemon *mon = (side == B_SIDE_PLAYER) ? &gPlayerParty[monId] : &gEnemyParty[monId];
|
||||
u16 mult = UQ_4_12_FLOORED(1.0/1.5); // placeholder
|
||||
uq4_12_t mult = GetDynamaxLevelHPMultiplier(GetMonData(mon, MON_DATA_DYNAMAX_LEVEL), TRUE);
|
||||
gBattleMons[battler].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up
|
||||
SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp);
|
||||
CalculateMonStats(mon);
|
||||
@ -510,10 +512,10 @@ void BS_UpdateDynamax(void)
|
||||
{
|
||||
NATIVE_ARGS();
|
||||
u32 battler = gBattleScripting.battler;
|
||||
struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]];
|
||||
struct Pokemon *mon = GetPartyBattlerData(battler);
|
||||
|
||||
if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change.
|
||||
RecalcBattlerStats(battler, mon);
|
||||
RecalcBattlerStats(battler, mon, GetActiveGimmick(battler) == GIMMICK_DYNAMAX);
|
||||
|
||||
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
|
||||
@ -1399,8 +1399,8 @@ const u8 gText_PkmnIsEvolving[] = _("What?\n{STR_VAR_1} is evolving!");
|
||||
const u8 gText_CongratsPkmnEvolved[] = _("Congratulations! Your {STR_VAR_1}\nevolved into {STR_VAR_2}!{WAIT_SE}\p");
|
||||
const u8 gText_PkmnStoppedEvolving[] = _("Huh? {STR_VAR_1}\nstopped evolving!\p");
|
||||
const u8 gText_EllipsisQuestionMark[] = _("……?\p");
|
||||
const u8 gText_WhatWillPkmnDo[] = _("What will {B_BUFF1} do?");
|
||||
const u8 gText_WhatWillPkmnDo2[] = _("What will {B_PLAYER_NAME} do?");
|
||||
const u8 gText_WhatWillPkmnDo[] = _("What will\n{B_BUFF1} do?");
|
||||
const u8 gText_WhatWillPkmnDo2[] = _("What will\n{B_PLAYER_NAME} do?");
|
||||
const u8 gText_WhatWillWallyDo[] = _("What will\nWALLY do?");
|
||||
const u8 gText_LinkStandby[] = _("{PAUSE 16}Link standby…");
|
||||
const u8 gText_BattleMenu[] = _("Battle{CLEAR_TO 56}Bag\nPokémon{CLEAR_TO 56}Run");
|
||||
|
||||
@ -2647,6 +2647,9 @@ static void Cmd_datahpupdate(void)
|
||||
|
||||
u32 effect = GetMoveEffect(gCurrentMove);
|
||||
|
||||
// Record damage for foreseen moves
|
||||
gWishFutureKnock.futureSightDmg = gBattleStruct->moveDamage[battler];
|
||||
|
||||
// Note: While physicalDmg/specialDmg below are only distinguished between for Counter/Mirror Coat, they are
|
||||
// used in combination as general damage trackers for other purposes. specialDmg is additionally used
|
||||
// to help determine if a fire move should defrost the target.
|
||||
@ -6701,18 +6704,22 @@ static void Cmd_moveend(void)
|
||||
gLastUsedItem = gBattleMons[battler].item;
|
||||
if (moveEffect == EFFECT_HIT_ESCAPE)
|
||||
gBattlescriptCurrInstr = BattleScript_MoveEnd; // Prevent user switch-in selection
|
||||
effect = TRUE;
|
||||
BattleScriptPushCursor();
|
||||
gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
|
||||
if (ejectButtonBattlers & (1u << battler))
|
||||
{
|
||||
effect = TRUE;
|
||||
gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_EjectButtonActivates;
|
||||
AI_DATA->ejectButtonSwitch = TRUE;
|
||||
}
|
||||
else // Eject Pack
|
||||
{
|
||||
if (!(gBattleResources->flags->flags[gBattlerTarget] & RESOURCE_FLAG_EMERGENCY_EXIT))
|
||||
if (!(gBattleResources->flags->flags[gBattlerTarget] & RESOURCE_FLAG_EMERGENCY_EXIT)
|
||||
&& !(gMovesInfo[gCurrentMove].effect == EFFECT_PARTING_SHOT && CanBattlerSwitch(gBattlerAttacker)))
|
||||
{
|
||||
effect = TRUE;
|
||||
gBattleStruct->battlerState[battler].usedEjectItem = TRUE;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_EjectPackActivates;
|
||||
AI_DATA->ejectPackSwitch = TRUE;
|
||||
gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = TRUE;
|
||||
@ -10446,7 +10453,7 @@ static void Cmd_various(void)
|
||||
// Change stats.
|
||||
else if (cmd->case_ == 1)
|
||||
{
|
||||
RecalcBattlerStats(battler, mon);
|
||||
RecalcBattlerStats(battler, mon, FALSE);
|
||||
}
|
||||
// Update healthbox.
|
||||
else
|
||||
@ -14233,6 +14240,7 @@ static void Cmd_trysetfutureattack(void)
|
||||
gWishFutureKnock.futureSightBattlerIndex[gBattlerTarget] = gBattlerAttacker;
|
||||
gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] = gBattlerPartyIndexes[gBattlerAttacker];
|
||||
gWishFutureKnock.futureSightCounter[gBattlerTarget] = 3;
|
||||
gWishFutureKnock.futureSightDmg = 0;
|
||||
|
||||
if (gCurrentMove == MOVE_DOOM_DESIRE)
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE;
|
||||
|
||||
@ -1569,7 +1569,7 @@ void CreateFacilityMon(const struct TrainerMon *fmon, u16 level, u8 fixedIV, u32
|
||||
{
|
||||
u8 ball = (fmon->ball == 0xFF) ? Random() % POKEBALL_COUNT : fmon->ball;
|
||||
u16 move;
|
||||
u32 personality, ability, friendship, j;
|
||||
u32 personality = 0, ability, friendship, j;
|
||||
|
||||
if (fmon->gender == TRAINER_MON_MALE)
|
||||
{
|
||||
|
||||
@ -5887,10 +5887,27 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
&& !IsBattlerAlive(gBattlerTarget)
|
||||
&& IsBattlerAlive(gBattlerAttacker))
|
||||
{
|
||||
gBattleStruct->moveDamage[gBattlerAttacker] = gBattleStruct->moveDamage[gBattlerTarget];
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_AftermathDmg;
|
||||
effect++;
|
||||
//special Future Sight handling
|
||||
if (gMovesInfo[gWishFutureKnock.futureSightMove[battler]].effect == EFFECT_FUTURE_SIGHT)
|
||||
{
|
||||
//no Innards Out effect if Future Sight user is currently not on field
|
||||
if (gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] == gBattlerPartyIndexes[gBattlerAttacker]
|
||||
|| gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] == BATTLE_PARTNER(gBattlerPartyIndexes[gBattlerAttacker]))
|
||||
{
|
||||
gBattleStruct->moveDamage[gBattlerAttacker] = gWishFutureKnock.futureSightDmg;
|
||||
gWishFutureKnock.futureSightDmg = 0;
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_AftermathDmg;
|
||||
effect++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattleStruct->moveDamage[gBattlerAttacker] = gBattleStruct->moveDamage[gBattlerTarget];
|
||||
BattleScriptPushCursor();
|
||||
gBattlescriptCurrInstr = BattleScript_AftermathDmg;
|
||||
effect++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ABILITY_EFFECT_SPORE:
|
||||
@ -11188,7 +11205,7 @@ bool32 TryBattleFormChange(u32 battler, u32 method)
|
||||
TryToSetBattleFormChangeMoves(&party[monId], method);
|
||||
SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies);
|
||||
gBattleMons[battler].species = targetSpecies;
|
||||
RecalcBattlerStats(battler, &party[monId]);
|
||||
RecalcBattlerStats(battler, &party[monId], method == FORM_CHANGE_BATTLE_GIGANTAMAX);
|
||||
return TRUE;
|
||||
}
|
||||
else if (gBattleStruct->changedSpecies[side][monId] != SPECIES_NONE)
|
||||
@ -11213,7 +11230,7 @@ bool32 TryBattleFormChange(u32 battler, u32 method)
|
||||
// Reverts the original species
|
||||
TryToSetBattleFormChangeMoves(&party[monId], method);
|
||||
SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[side][monId]);
|
||||
RecalcBattlerStats(battler, &party[monId]);
|
||||
RecalcBattlerStats(battler, &party[monId], method == FORM_CHANGE_BATTLE_GIGANTAMAX);
|
||||
// Battler data is not updated with regular form's ability, not doing so could cause wrong ability activation.
|
||||
if (method == FORM_CHANGE_FAINT)
|
||||
gBattleMons[battler].ability = abilityForm;
|
||||
@ -11803,11 +11820,28 @@ void CopyMonAbilityAndTypesToBattleMon(u32 battler, struct Pokemon *mon)
|
||||
gBattleMons[battler].types[2] = TYPE_MYSTERY;
|
||||
}
|
||||
|
||||
void RecalcBattlerStats(u32 battler, struct Pokemon *mon)
|
||||
void RecalcBattlerStats(u32 battler, struct Pokemon *mon, bool32 isDynamaxing)
|
||||
{
|
||||
u32 hp = GetMonData(mon, MON_DATA_HP);
|
||||
u32 oldMaxHp = GetMonData(mon, MON_DATA_MAX_HP);
|
||||
CalculateMonStats(mon);
|
||||
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && gChosenActionByBattler[battler] != B_ACTION_SWITCH)
|
||||
ApplyDynamaxHPMultiplier(battler, mon);
|
||||
{
|
||||
ApplyDynamaxHPMultiplier(mon);
|
||||
u32 newMaxHp = GetMonData(mon, MON_DATA_MAX_HP);
|
||||
if (!isDynamaxing)
|
||||
{
|
||||
if (newMaxHp > oldMaxHp) // restore hp gained from changing form, without this, dynamaxed form changes are calculated incorrectly
|
||||
{
|
||||
hp += (newMaxHp - oldMaxHp);
|
||||
SetMonData(mon, MON_DATA_HP, &hp);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetMonData(mon, MON_DATA_HP, &hp);
|
||||
}
|
||||
}
|
||||
}
|
||||
CopyMonLevelAndBaseStatsToBattleMon(battler, mon);
|
||||
CopyMonAbilityAndTypesToBattleMon(battler, mon);
|
||||
}
|
||||
|
||||
25
src/berry.c
25
src/berry.c
@ -1654,6 +1654,24 @@ const struct BerryCrushBerryData gBerryCrush_BerryData[] = {
|
||||
[ITEM_WATMEL_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 160, .powder = 250},
|
||||
[ITEM_DURIN_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 160, .powder = 250},
|
||||
[ITEM_BELUE_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 160, .powder = 250},
|
||||
[ITEM_CHILAN_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 80, .powder = 70},
|
||||
[ITEM_OCCA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 100, .powder = 100},
|
||||
[ITEM_PASSHO_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 60, .powder = 30},
|
||||
[ITEM_WACAN_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_RINDO_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_YACHE_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_CHOPLE_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_KEBIA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_SHUCA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 20},
|
||||
[ITEM_COBA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_PAYAPA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_TANGA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_CHARTI_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_KASIB_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_HABAN_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 50, .powder = 30},
|
||||
[ITEM_COLBUR_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 60, .powder = 50},
|
||||
[ITEM_BABIRI_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 80, .powder = 50},
|
||||
[ITEM_ROSELI_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 60, .powder = 50},
|
||||
[ITEM_LIECHI_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 180, .powder = 500},
|
||||
[ITEM_GANLON_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 180, .powder = 500},
|
||||
[ITEM_SALAC_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 180, .powder = 500},
|
||||
@ -1661,6 +1679,13 @@ const struct BerryCrushBerryData gBerryCrush_BerryData[] = {
|
||||
[ITEM_APICOT_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 180, .powder = 500},
|
||||
[ITEM_LANSAT_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 200, .powder = 750},
|
||||
[ITEM_STARF_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 200, .powder = 750},
|
||||
[ITEM_ENIGMA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 150, .powder = 200},
|
||||
[ITEM_MICLE_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 130, .powder = 250},
|
||||
[ITEM_CUSTAP_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 200, .powder = 750},
|
||||
[ITEM_JABOCA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 130, .powder = 250},
|
||||
[ITEM_ROWAP_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 130, .powder = 250},
|
||||
[ITEM_KEE_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 160, .powder = 500},
|
||||
[ITEM_MARANGA_BERRY - FIRST_BERRY_INDEX] = {.difficulty = 160, .powder = 500},
|
||||
[ITEM_ENIGMA_BERRY_E_READER - FIRST_BERRY_INDEX] = {.difficulty = 150, .powder = 200}
|
||||
};
|
||||
|
||||
|
||||
@ -277,8 +277,8 @@ bool8 SetUpFieldMove_Cut(void)
|
||||
|
||||
static void FieldCallback_CutGrass(void)
|
||||
{
|
||||
FieldEffectStart(FLDEFF_USE_CUT_ON_GRASS);
|
||||
gFieldEffectArguments[0] = GetCursorSelectionMonId();
|
||||
ScriptContext_SetupScript(EventScript_UseCutGrass);
|
||||
}
|
||||
|
||||
bool8 FldEff_UseCutOnGrass(void)
|
||||
|
||||
@ -1007,7 +1007,6 @@ void MoveRelearnerShowHideCategoryIcon(s32 moveId)
|
||||
DestroySprite(&gSprites[sMoveRelearnerStruct->categoryIconSpriteId]);
|
||||
|
||||
sMoveRelearnerStruct->categoryIconSpriteId = 0xFF;
|
||||
gSprites[sMoveRelearnerStruct->categoryIconSpriteId].invisible = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@ -7000,3 +7000,10 @@ u32 CheckDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler)
|
||||
return moveType;
|
||||
return GetMoveType(move);
|
||||
}
|
||||
|
||||
uq4_12_t GetDynamaxLevelHPMultiplier(u32 dynamaxLevel, bool32 inverseMultiplier)
|
||||
{
|
||||
if (inverseMultiplier)
|
||||
return UQ_4_12(1.0/(1.5 + 0.05 * dynamaxLevel));
|
||||
return UQ_4_12(1.5 + 0.05 * dynamaxLevel);
|
||||
}
|
||||
|
||||
@ -65,3 +65,66 @@ SINGLE_BATTLE_TEST("Innards Out does not damage Magic Guard Pokemon")
|
||||
NOT HP_BAR(opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Innards Out uses correct damage amount for Future Sight")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_FUTURE_SIGHT].effect == EFFECT_FUTURE_SIGHT);
|
||||
PLAYER(SPECIES_PYUKUMUKU) { HP(1); Ability(ABILITY_INNARDS_OUT); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_FUTURE_SIGHT); }
|
||||
TURN { }
|
||||
TURN { SEND_OUT(player, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponent);
|
||||
MESSAGE("Pyukumuku took the Future Sight attack!");
|
||||
HP_BAR(player);
|
||||
ABILITY_POPUP(player, ABILITY_INNARDS_OUT);
|
||||
HP_BAR(opponent, damage: 1);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Innards Out doesn't trigger if Future Sight user is not on field")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_FUTURE_SIGHT].effect == EFFECT_FUTURE_SIGHT);
|
||||
PLAYER(SPECIES_PYUKUMUKU) { HP(1); Ability(ABILITY_INNARDS_OUT); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_FUTURE_SIGHT); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN { SEND_OUT(player, 1); } //SEND_OUT(opponent, 0); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponent);
|
||||
MESSAGE("Pyukumuku took the Future Sight attack!");
|
||||
HP_BAR(player);
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(player, ABILITY_INNARDS_OUT);
|
||||
HP_BAR(opponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Innards Out triggers if Future Sight user is back on the field")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_FUTURE_SIGHT].effect == EFFECT_FUTURE_SIGHT);
|
||||
PLAYER(SPECIES_PYUKUMUKU) { HP(1); Ability(ABILITY_INNARDS_OUT); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_FUTURE_SIGHT); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN { SWITCH(opponent, 0); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponent);
|
||||
MESSAGE("Pyukumuku took the Future Sight attack!");
|
||||
HP_BAR(player);
|
||||
ABILITY_POPUP(player, ABILITY_INNARDS_OUT);
|
||||
HP_BAR(opponent);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax increases HP and max HP by 1.5x", u16 hp)
|
||||
u32 dynamax;
|
||||
PARAMETRIZE { dynamax = GIMMICK_NONE; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; }
|
||||
GIVEN { // TODO: Dynamax level
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
@ -25,6 +25,49 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax increases HP and max HP by 1.5x", u16 hp)
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax Level increases HP and max HP multipliers by 0.05 for each level", u16 hp)
|
||||
{
|
||||
u32 dynamax, level;
|
||||
PARAMETRIZE { dynamax = GIMMICK_NONE; level = 0; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 0; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 1; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 2; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 3; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 4; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 5; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 6; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 7; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 8; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 9; }
|
||||
PARAMETRIZE { dynamax = GIMMICK_DYNAMAX; level = 10; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { DynamaxLevel(level); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE, gimmick: dynamax); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
if (dynamax) {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player);
|
||||
MESSAGE("Wobbuffet used Max Strike!");
|
||||
}
|
||||
MESSAGE("The opposing Wobbuffet used Celebrate!");
|
||||
} THEN {
|
||||
results[i].hp = player->hp;
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.5), results[1].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.55), results[2].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.6), results[3].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.65), results[4].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.7), results[5].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.75), results[6].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.8), results[7].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.85), results[8].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.9), results[9].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(1.95), results[10].hp);
|
||||
EXPECT_MUL_EQ(results[0].hp, Q_4_12(2.0), results[11].hp);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp)
|
||||
{
|
||||
u32 dynamax;
|
||||
@ -38,8 +81,8 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp)
|
||||
TURN { MOVE(player, MOVE_TACKLE); } // 2nd max move
|
||||
TURN { MOVE(player, MOVE_TACKLE); } // 3rd max move
|
||||
} SCENE {
|
||||
int i;
|
||||
for (i = 0; i < DYNAMAX_TURNS_COUNT; ++i) {
|
||||
int j;
|
||||
for (j = 0; j < DYNAMAX_TURNS_COUNT; ++j) {
|
||||
if (dynamax)
|
||||
MESSAGE("Wobbuffet used Max Strike!");
|
||||
else
|
||||
@ -55,6 +98,49 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp)
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns and correctly converts HP according to Dynamax Level")
|
||||
{
|
||||
u32 dynamaxLevel, dynamax;
|
||||
u16 capturedHP, finalHP;
|
||||
s16 capturedDamage;
|
||||
PARAMETRIZE { dynamaxLevel = 0; dynamax = GIMMICK_NONE; }
|
||||
PARAMETRIZE { dynamaxLevel = 0; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 1; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 2; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 3; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 4; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 5; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 6; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 7; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 8; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 9; dynamax = GIMMICK_DYNAMAX; }
|
||||
PARAMETRIZE { dynamaxLevel = 10; dynamax = GIMMICK_DYNAMAX; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { DynamaxLevel(dynamaxLevel); HP(200); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CELEBRATE, gimmick: dynamax); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { }
|
||||
} SCENE {
|
||||
if (dynamax)
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
if (dynamax)
|
||||
HP_BAR(player, captureHP: &capturedHP);
|
||||
else
|
||||
HP_BAR(player, captureDamage: &capturedDamage);
|
||||
if (dynamax)
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
|
||||
} THEN {
|
||||
finalHP = player->hp;
|
||||
if (dynamax)
|
||||
EXPECT_MUL_EQ(finalHP, GetDynamaxLevelHPMultiplier(dynamaxLevel, FALSE), capturedHP);
|
||||
EXPECT_LE(finalHP, 200);
|
||||
EXPECT_GE(finalHP, 200 - capturedDamage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot be flinched")
|
||||
{
|
||||
GIVEN {
|
||||
@ -311,6 +397,47 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon lose their substitutes")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon that changes forms does not gain HP")
|
||||
{
|
||||
u16 capturedHP, finalHP;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GRENINJA_BATTLE_BOND) { Ability(ABILITY_BATTLE_BOND); HP(100); Speed(100); }
|
||||
OPPONENT(SPECIES_CATERPIE) { HP(1); Speed(1000); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(10); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureHP: &capturedHP);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAX_STRIKE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
|
||||
} THEN {
|
||||
finalHP = player->hp;
|
||||
EXPECT_EQ(capturedHP, finalHP);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon that changes forms does not gain HP unless the new form gains Max HP")
|
||||
{
|
||||
u32 hp = 1, maxHP = 200;
|
||||
u32 species;
|
||||
PARAMETRIZE { species = SPECIES_ZYGARDE_10_POWER_CONSTRUCT; }
|
||||
PARAMETRIZE { species = SPECIES_ZYGARDE_50_POWER_CONSTRUCT; }
|
||||
GIVEN {
|
||||
PLAYER(species) { Ability(ABILITY_POWER_CONSTRUCT); HP(hp); MaxHP(maxHP); DynamaxLevel(0); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE, gimmick: GIMMICK_DYNAMAX); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_DYNAMAX_GROWTH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MAX_STRIKE, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(maxHP - hp, GetDynamaxLevelHPMultiplier(0, FALSE), player->maxHP - player->hp);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(DYNAMAX) Max Moves deal 1/4 damage through protect", s16 damage)
|
||||
{
|
||||
bool32 protected;
|
||||
|
||||
@ -102,3 +102,22 @@ SINGLE_BATTLE_TEST("Eject Pack activates once intimidate mon switches in")
|
||||
MESSAGE("Wobbuffet is switched out with the Eject Pack!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Eject Pack will not activate if Parting Shot user can switch out")
|
||||
{
|
||||
ASSUME(gItemsInfo[ITEM_EJECT_PACK].holdEffect == HOLD_EFFECT_EJECT_PACK);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_PARTING_SHOT); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PARTING_SHOT, opponent);
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
MESSAGE("Wobbuffet is switched out with the Eject Pack!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,6 +116,41 @@ SINGLE_BATTLE_TEST("Shell Bell does not activate on Future Sight if the original
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Bell restores 1/8 HP of damage dealt")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Level(16); Item(ITEM_SHELL_BELL); HP(10); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Level(16); };
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SEISMIC_TOSS); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SEISMIC_TOSS, player);
|
||||
HP_BAR(opponent);
|
||||
HP_BAR(player, damage: -2);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Bell doesn't restore HP for damage dealt by a foreseen move")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_FUTURE_SIGHT].effect == EFFECT_FUTURE_SIGHT);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Level(16); Item(ITEM_SHELL_BELL); HP(10); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Level(16); };
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FUTURE_SIGHT); }
|
||||
TURN { }
|
||||
TURN { }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, player);
|
||||
MESSAGE("The opposing Wobbuffet took the Future Sight attack!");
|
||||
HP_BAR(opponent);
|
||||
NONE_OF {
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Bell does not activate on Future Sight if the original user is not on the field")
|
||||
{
|
||||
GIVEN {
|
||||
|
||||
@ -6,7 +6,7 @@ ASSUMPTIONS
|
||||
ASSUME(GetMoveEffect(MOVE_U_TURN) == EFFECT_HIT_ESCAPE);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn switches the user out")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: U-turn switches the user out")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -21,7 +21,7 @@ SINGLE_BATTLE_TEST("U-turn switches the user out")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn does not switch the user out if the battle ends")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: U-turn does not switch the user out if the battle ends")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -35,7 +35,7 @@ SINGLE_BATTLE_TEST("U-turn does not switch the user out if the battle ends")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn does not switch the user out if no replacements")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: U-turn does not switch the user out if no replacements")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -48,7 +48,7 @@ SINGLE_BATTLE_TEST("U-turn does not switch the user out if no replacements")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn does not switch the user out if replacements fainted")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: U-turn does not switch the user out if replacements fainted")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -62,7 +62,7 @@ SINGLE_BATTLE_TEST("U-turn does not switch the user out if replacements fainted"
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn does not switch the user out if Wimp Out activates")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: U-turn does not switch the user out if Wimp Out activates")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -79,7 +79,7 @@ SINGLE_BATTLE_TEST("U-turn does not switch the user out if Wimp Out activates")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn switches the user out if Wimp Out fails to activate")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: U-turn switches the user out if Wimp Out fails to activate")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -95,7 +95,7 @@ SINGLE_BATTLE_TEST("U-turn switches the user out if Wimp Out fails to activate")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("U-turn switches the user out after Ice Face activates")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: U-turn switches the user out after Ice Face activates")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveCategory(MOVE_U_TURN) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
@ -113,7 +113,7 @@ SINGLE_BATTLE_TEST("U-turn switches the user out after Ice Face activates")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: player side")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: player side")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); };
|
||||
@ -136,7 +136,7 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: Held items are consumed immediately after a mon switched in by U-turn and Intimidate activates after it: opposing side")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); };
|
||||
@ -159,7 +159,7 @@ SINGLE_BATTLE_TEST("Held items are consumed immediately after a mon switched in
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Electric Seed boost is received by the right pokemon after U-turn and Intimidate")
|
||||
SINGLE_BATTLE_TEST("Hit Escape: Electric Seed boost is received by the right pokemon after U-turn and Intimidate")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user