diff --git a/.all-contributorsrc b/.all-contributorsrc index 627b52827a..3d356bbc97 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -651,6 +651,15 @@ "contributions": [ "bug" ] + }, + { + "login": "KnightGallade", + "name": "KnightGallade", + "avatar_url": "https://avatars.githubusercontent.com/u/189022270?v=4", + "profile": "https://github.com/KnightGallade", + "contributions": [ + "bug" + ] } ], "contributorsPerLine": 7, diff --git a/CREDITS.md b/CREDITS.md index c3d7df099a..133208fa95 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -93,6 +93,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d PacFire
PacFire

🎨 ChrispyChris27
ChrispyChris27

💻 LogicalLlama
LogicalLlama

🐛 + KnightGallade
KnightGallade

🐛 diff --git a/asm/macros/battle_anim_script.inc b/asm/macros/battle_anim_script.inc index c24a6b8253..17d0ca3e7f 100644 --- a/asm/macros/battle_anim_script.inc +++ b/asm/macros/battle_anim_script.inc @@ -648,3 +648,7 @@ .macro shake_battle_platforms priority=2, x_offset:req, y_offset:req, shakes:req, delay:req createvisualtask AnimTask_ShakeBattlePlatforms, \priority, \x_offset, \y_offset, \shakes, \delay .endm + + .macro create_surf_wave priority=2, palette:req + createvisualtask AnimTask_CreateSurfWave, \priority, \palette + .endm diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 582dd5c331..e2cdc9b158 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -4409,7 +4409,7 @@ FlameBurstSpread: gBattleAnimMove_SludgeWave:: panse SE_M_WHIRLPOOL, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 0x2, 0x0 - createvisualtask AnimTask_CreateSurfWave, 2, ANIM_SURF_PAL_SLUDGE_WAVE + create_surf_wave palette=ANIM_SURF_PAL_SLUDGE_WAVE waitforvisualfinish end @@ -12875,7 +12875,7 @@ gBattleAnimMove_ZippyZap:: gBattleAnimMove_SplishySplash:: loadspritegfx ANIM_TAG_SPARK_2 - createvisualtask AnimTask_CreateSurfWave, 2, ANIM_SURF_PAL_SURF + create_surf_wave palette=ANIM_SURF_PAL_SURF delay 24 panse SE_M_SURF, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +2, 0 waitforvisualfinish @@ -27017,7 +27017,7 @@ gBattleAnimMove_Crabhammer:: end gBattleAnimMove_Surf:: - createvisualtask AnimTask_CreateSurfWave, 2, ANIM_SURF_PAL_SURF + create_surf_wave palette=ANIM_SURF_PAL_SURF delay 24 panse SE_M_SURF, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +2, 0 waitforvisualfinish @@ -29320,7 +29320,7 @@ ArmThrustLeft: gBattleAnimMove_MuddyWater:: panse SE_M_WHIRLPOOL, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +2, 0 - createvisualtask AnimTask_CreateSurfWave, 2, ANIM_SURF_PAL_MUDDY_WATER + create_surf_wave palette=ANIM_SURF_PAL_MUDDY_WATER waitforvisualfinish end @@ -32493,7 +32493,7 @@ FinishAcidDownpour: createsprite gAcidDownpourReversalSpriteTemplate, ANIM_ATTACKER, 2, 0x1a, 0xd2 delay 32 panse SE_M_WHIRLPOOL, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 0x2, 0x0 - createvisualtask AnimTask_CreateSurfWave, 0x2, ANIM_SURF_PAL_SLUDGE_WAVE + create_surf_wave priority=0x2, palette=ANIM_SURF_PAL_SLUDGE_WAVE createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 2, 0, 7, (RGB(28, 3, 22) | RGB_ALPHA) createvisualtask AnimTask_ShakeMon, 5, ANIM_TARGET, 0, 2, 50, 1 call AcidDownpourFlare @@ -33388,7 +33388,7 @@ gBattleAnimMove_HydroVortex:: loadspritegfx ANIM_TAG_WATER_ORB @whirlpool createvisualtask AnimTask_AllBattlersInvisibleExceptAttackerAndTarget, 0xA waitforvisualfinish - createvisualtask AnimTask_CreateSurfWave, 0x2, 0x0 + create_surf_wave priority=0x2, palette=0x0 delay 24 panse SE_M_SURF, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 0x2, 0x0 waitforvisualfinish diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index df8affa835..2000ef4e84 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -1422,6 +1422,7 @@ BattleScript_ToxicThreadTryPsn:: BattleScript_EffectVenomDrench:: attackcanceler + jumpifsubstituteblocks BattleScript_ButItFailed jumpifstatus BS_TARGET, STATUS1_PSN_ANY, BattleScript_EffectVenomDrenchCanBeUsed goto BattleScript_ButItFailed BattleScript_EffectVenomDrenchCanBeUsed: @@ -3856,6 +3857,7 @@ BattleScript_EffectWaterSport:: BattleScript_EffectTickle:: attackcanceler + jumpifsubstituteblocks BattleScript_ButItFailed jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_ATK, MIN_STAT_STAGE, BattleScript_TickleDoMoveAnim jumpifstat BS_TARGET, CMP_EQUAL, STAT_DEF, MIN_STAT_STAGE, BattleScript_CantLowerMultipleStats BattleScript_TickleDoMoveAnim:: diff --git a/include/constants/battle.h b/include/constants/battle.h index 67198a38bb..ba632e6f90 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -236,6 +236,7 @@ enum VolatileFlags F(VOLATILE_BEADS_OF_RUIN, beadsOfRuin, (u32, 1)) \ F(VOLATILE_IS_TRANSFORMED_MON_SHINY, isTransformedMonShiny, (u32, 1)) \ F(VOLATILE_TRANSFORMED_MON_PID, transformedMonPID, (u32, UINT32_MAX)) \ + F(VOLATILE_TRANSFORMED_MON_SPECIES, transformedMonSpecies, (u32, NUM_SPECIES)) \ F(VOLATILE_DISABLED_MOVE, disabledMove, (u32, MOVES_COUNT_ALL)) \ F(VOLATILE_ENCORED_MOVE, encoredMove, (u32, MOVES_COUNT_ALL)) \ F(VOLATILE_PROTECT_USES, consecutiveMoveUses, (u32, UINT8_MAX)) \ diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index 9b38f52915..ec8ec676d4 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -691,6 +691,10 @@ enum SpeciesGfxChange SPECIES_GFX_CHANGE_GHOST_UNVEIL, }; +// Surf wave palettes +#define ANIM_SURF_PAL_SURF 0 +#define ANIM_SURF_PAL_MUDDY_WATER 1 + // Flags given to various functions to indicate which palettes to consider. // Handled by UnpackSelectedBattlePalettes #define F_PAL_BG (1 << 0) diff --git a/include/constants/form_change_types.h b/include/constants/form_change_types.h index 1cdd150499..7f4b77a036 100644 --- a/include/constants/form_change_types.h +++ b/include/constants/form_change_types.h @@ -161,6 +161,8 @@ enum FormChanges FORM_CHANGE_OVERWORLD_WEATHER, // Form change that activates when the Pokémon is deposited into the PC or Daycare. FORM_CHANGE_DEPOSIT, + // Form change for Minior, which appears unchanged when encountered in the wild + FORM_CHANGE_BEGIN_WILD_ENCOUNTER, }; #endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index f71fadd26a..6ed5f37fc6 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -11,6 +11,15 @@ #define MAPGRID_COLLISION_SHIFT 10 #define MAPGRID_ELEVATION_SHIFT 12 +enum +{ + ELEVATION_TRANSITION = 0, + ELEVATION_SURF = 1, + ELEVATION_DEFAULT = 3, + ELEVATION_MULTI_LEVEL = 15, + ELEVATION_INVALID = 0xFFFF +}; + #define PACK_METATILE(metatileId) PACK(metatileId, MAPGRID_METATILE_ID_SHIFT, MAPGRID_METATILE_ID_MASK) #define PACK_COLLISION(collision) PACK(collision, MAPGRID_COLLISION_SHIFT, MAPGRID_COLLISION_MASK) #define PACK_ELEVATION(elevation) PACK(elevation, MAPGRID_ELEVATION_SHIFT, MAPGRID_ELEVATION_MASK) diff --git a/ld_script_modern.ld b/ld_script_modern.ld index 47989bd91f..bf1894b246 100644 --- a/ld_script_modern.ld +++ b/ld_script_modern.ld @@ -124,6 +124,9 @@ SECTIONS { .debug_loc 0 : { *(.debug_loc) } .debug_macinfo 0 : { *(.debug_macinfo) } + /* DWARF 5*/ + .debug_line_str 0 : { *(.debug_line_str) } + /* Discard everything not specifically mentioned above. */ /DISCARD/ : { diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index aa16daea3a..c920689ba4 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -6717,6 +6717,10 @@ static void AnimTask_AllySwitchDataSwap(u8 taskId) SwapStructData(&gBattleStruct->illusion[battlerAtk], &gBattleStruct->illusion[battlerPartner], data, sizeof(struct Illusion)); SwapStructData(&gBattleStruct->battlerState[battlerAtk], &gBattleStruct->battlerState[battlerPartner], data, sizeof(struct BattlerState)); + // Swap those back since they aren't affected by ally switch + SWAP(gBattleStruct->battlerState[battlerAtk].storedHealingWish, gBattleStruct->battlerState[battlerPartner].storedHealingWish, temp); + SWAP(gBattleStruct->battlerState[battlerAtk].storedLunarDance, gBattleStruct->battlerState[battlerPartner].storedLunarDance, temp); + SWAP(gBattleSpritesDataPtr->battlerData[battlerAtk].invisible, gBattleSpritesDataPtr->battlerData[battlerPartner].invisible, temp); SWAP(gTransformedPersonalities[battlerAtk], gTransformedPersonalities[battlerPartner], temp); SWAP(gTransformedShininess[battlerAtk], gTransformedShininess[battlerPartner], temp); diff --git a/src/battle_anim_water.c b/src/battle_anim_water.c index c866c68598..64ebf224b0 100644 --- a/src/battle_anim_water.c +++ b/src/battle_anim_water.c @@ -1,6 +1,7 @@ #include "global.h" #include "battle.h" #include "battle_anim.h" +#include "battle_anim_internal.h" #include "gpu_regs.h" #include "graphics.h" #include "palette.h" @@ -979,6 +980,8 @@ static void AnimSmallBubblePair_Step(struct Sprite *sprite) void AnimTask_CreateSurfWave(u8 taskId) { + CMD_ARGS(palette); + struct BattleAnimBgData animBg; u8 taskId2; u16 *x; @@ -1005,7 +1008,7 @@ void AnimTask_CreateSurfWave(u8 taskId) AnimLoadCompressedBgTilemapHandleContest(&animBg, gBattleAnimBgTilemap_SurfContest, TRUE); } AnimLoadCompressedBgGfx(animBg.bgId, gBattleAnimBgImage_Surf, animBg.tilesOffset); - switch (gBattleAnimArgs[0]) + switch (cmd->palette) { case ANIM_SURF_PAL_SURF: default: diff --git a/src/battle_main.c b/src/battle_main.c index 11abf82a38..133f118e79 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -617,6 +617,13 @@ static void CB2_InitBattleInternal(void) TryFormChange(&gEnemyParty[i], FORM_CHANGE_BEGIN_BATTLE); } + if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER)) + { + TryFormChange(&gEnemyParty[0], FORM_CHANGE_BEGIN_WILD_ENCOUNTER); + if (IsDoubleBattle()) + TryFormChange(&gEnemyParty[1], FORM_CHANGE_BEGIN_WILD_ENCOUNTER); + } + #if TESTING gPlayerPartyCount = CalculatePartyCount(gPlayerParty); gEnemyPartyCount = CalculatePartyCount(gEnemyParty); diff --git a/src/battle_move_resolution.c b/src/battle_move_resolution.c index 64a6d99520..48e17fce11 100644 --- a/src/battle_move_resolution.c +++ b/src/battle_move_resolution.c @@ -1304,8 +1304,10 @@ static enum CancelerResult CancelerMoveEffectFailureTarget(struct BattleContext } break; case EFFECT_SUCKER_PUNCH: + { + u32 defMove = GetBattlerChosenMove(battlerDef); if (HasBattlerActedThisTurn(battlerDef) - || (IsBattleMoveStatus(GetBattlerChosenMove(battlerDef)) && !gProtectStructs[battlerDef].noValidMoves)) + || (IsBattleMoveStatus(defMove) && !gProtectStructs[battlerDef].noValidMoves && GetMoveEffect(defMove) != EFFECT_ME_FIRST)) { battleScript = BattleScript_ButItFailed; } @@ -1314,6 +1316,8 @@ static enum CancelerResult CancelerMoveEffectFailureTarget(struct BattleContext numAffectedTargets++; continue; } + + } break; case EFFECT_UPPER_HAND: { @@ -4056,10 +4060,22 @@ static enum Move GetAssistMove(void) enum Move move = MOVE_NONE; u32 chooseableMovesNo = 0; struct Pokemon *party; + u8 battlerByPartyId[PARTY_SIZE]; enum Move validMoves[PARTY_SIZE * MAX_MON_MOVES] = {MOVE_NONE}; party = GetBattlerParty(gBattlerAttacker); + for (u32 i = 0; i < PARTY_SIZE; i++) + battlerByPartyId[i] = MAX_BATTLERS_COUNT; + for (u32 battler = 0; battler < gBattlersCount; battler++) + { + if (!IsBattlerAlly(battler, gBattlerAttacker)) + continue; + + if (gBattlerPartyIndexes[battler] < PARTY_SIZE) + battlerByPartyId[gBattlerPartyIndexes[battler]] = battler; + } + for (u32 monId = 0; monId < PARTY_SIZE; monId++) { if (monId == gBattlerPartyIndexes[gBattlerAttacker]) @@ -4071,7 +4087,12 @@ static enum Move GetAssistMove(void) for (u32 moveId = 0; moveId < MAX_MON_MOVES; moveId++) { - enum Move move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); + enum Move move; + + if (battlerByPartyId[monId] != MAX_BATTLERS_COUNT) + move = gBattleMons[battlerByPartyId[monId]].moves[moveId]; + else + move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId); if (IsMoveAssistBanned(move)) continue; diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index f96a08cc83..d8d6df5662 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2362,7 +2362,67 @@ static void SetNonVolatileStatus(enum BattlerId effectBattler, enum MoveEffect e gBattleStruct->poisonPuppeteerConfusion = TRUE; } -// To avoid confusion the arguments are naned battler/effectBattler since they can be different from gBattlerAttacker/gBattlerTarget +static inline bool32 IgnoreTargetingForMoveEffect(enum MoveEffect moveEffect) // Currently only used to determine move effects which happen even if the move's defined effectbattler is fainted +{ + switch (moveEffect) + { + case MOVE_EFFECT_PAYDAY: + case MOVE_EFFECT_BUG_BITE: + case MOVE_EFFECT_FLAME_BURST: + case MOVE_EFFECT_STEALTH_ROCK: + case MOVE_EFFECT_STEELSURGE: + case MOVE_EFFECT_SUN: + case MOVE_EFFECT_RAIN: + case MOVE_EFFECT_SANDSTORM: + case MOVE_EFFECT_HAIL: + case MOVE_EFFECT_MISTY_TERRAIN: + case MOVE_EFFECT_GRASSY_TERRAIN: + case MOVE_EFFECT_ELECTRIC_TERRAIN: + case MOVE_EFFECT_PSYCHIC_TERRAIN: + case MOVE_EFFECT_DEFOG: + case MOVE_EFFECT_REFLECT: + case MOVE_EFFECT_LIGHT_SCREEN: + case MOVE_EFFECT_AURORA_VEIL: + case MOVE_EFFECT_GRAVITY: + case MOVE_EFFECT_HEAL_TEAM: + case MOVE_EFFECT_AROMATHERAPY: + case MOVE_EFFECT_RECYCLE_BERRIES: + case MOVE_EFFECT_ION_DELUGE: + case MOVE_EFFECT_HAZE: + case MOVE_EFFECT_RAISE_TEAM_ATTACK: + case MOVE_EFFECT_RAISE_TEAM_DEFENSE: + case MOVE_EFFECT_RAISE_TEAM_SPEED: + case MOVE_EFFECT_RAISE_TEAM_SP_ATK: + case MOVE_EFFECT_RAISE_TEAM_SP_DEF: + case MOVE_EFFECT_CRIT_PLUS_SIDE: + case MOVE_EFFECT_LOWER_ATTACK_SIDE: + case MOVE_EFFECT_LOWER_DEFENSE_SIDE: + case MOVE_EFFECT_LOWER_SPEED_SIDE: + case MOVE_EFFECT_LOWER_SP_ATK_SIDE: + case MOVE_EFFECT_LOWER_SP_DEF_SIDE: + case MOVE_EFFECT_LOWER_SPEED_2_SIDE: + case MOVE_EFFECT_LOWER_EVASIVENESS_SIDE: + case MOVE_EFFECT_VINE_LASH: + case MOVE_EFFECT_WILDFIRE: + case MOVE_EFFECT_CANNONADE: + case MOVE_EFFECT_VOLCALITH: + case MOVE_EFFECT_PREVENT_ESCAPE_SIDE: + case MOVE_EFFECT_SANDBLAST_SIDE: + case MOVE_EFFECT_FIRE_SPIN_SIDE: + case MOVE_EFFECT_PARALYZE_SIDE: + case MOVE_EFFECT_POISON_SIDE: + case MOVE_EFFECT_CONFUSE_PAY_DAY_SIDE: + case MOVE_EFFECT_POISON_PARALYZE_SIDE: + case MOVE_EFFECT_EFFECT_SPORE_SIDE: + case MOVE_EFFECT_INFATUATE_SIDE: + case MOVE_EFFECT_CONFUSE_SIDE: + case MOVE_EFFECT_TORMENT_SIDE: + return TRUE; + default: + return FALSE; + } +} + void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum MoveEffect moveEffect, const u8 *battleScript, enum SetMoveEffectFlags effectFlags) { enum Ability abilities[MAX_BATTLERS_COUNT] = {ABILITY_NONE}; @@ -2375,7 +2435,6 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum bool32 affectsUser = (battlerAtk == effectBattler); bool32 mirrorArmorReflected = (abilities[gBattlerTarget] == ABILITY_MIRROR_ARMOR); union StatChangeFlags flags = {0}; - bool32 activateAfterFaint = FALSE; if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT && IsBattlerAlive(gBattlerTarget) @@ -2384,19 +2443,7 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum gBattlescriptCurrInstr = battleScript; return; } - - switch (moveEffect) // Set move effects which happen later on - { - case MOVE_EFFECT_STEALTH_ROCK: - case MOVE_EFFECT_PAYDAY: - case MOVE_EFFECT_BUG_BITE: - case MOVE_EFFECT_FLAME_BURST: - activateAfterFaint = TRUE; - break; - default: - break; - } - + gBattleScripting.battler = battlerAtk; gEffectBattler = effectBattler; @@ -2406,7 +2453,7 @@ void SetMoveEffect(enum BattlerId battlerAtk, enum BattlerId effectBattler, enum && IsSheerForceAffected(gCurrentMove, abilities[battlerAtk]) && !(moveEffect == MOVE_EFFECT_ORDER_UP && gBattleStruct->battlerState[gBattlerAttacker].commanderSpecies != SPECIES_NONE)) moveEffect = MOVE_EFFECT_NONE; - else if (!IsBattlerAlive(gEffectBattler) && !activateAfterFaint) + else if (!IsBattlerAlive(gEffectBattler) && !IgnoreTargetingForMoveEffect(moveEffect)) moveEffect = MOVE_EFFECT_NONE; else if (DoesSubstituteBlockMove(gBattlerAttacker, gEffectBattler, gCurrentMove) && !affectsUser) moveEffect = MOVE_EFFECT_NONE; @@ -4990,6 +5037,7 @@ static void PlayAnimation(enum BattlerId battler, u8 animId, const u16 *argPtr, || animId == B_ANIM_FORM_CHANGE || animId == B_ANIM_SUBSTITUTE_FADE || animId == B_ANIM_PRIMAL_REVERSION + || animId == B_ANIM_POWER_CONSTRUCT || animId == B_ANIM_ULTRA_BURST || animId == B_ANIM_TERA_CHARGE || animId == B_ANIM_TERA_ACTIVATE @@ -8525,6 +8573,7 @@ static void Cmd_transformdataexecution(void) gBattleMons[gBattlerAttacker].volatiles.transformed = TRUE; gBattleMons[gBattlerAttacker].volatiles.disabledMove = MOVE_NONE; gBattleMons[gBattlerAttacker].volatiles.disableTimer = 0; + gBattleMons[gBattlerAttacker].volatiles.transformedMonSpecies = gBattleMons[gBattlerAttacker].species; gBattleMons[gBattlerAttacker].volatiles.transformedMonPID = gBattleMons[gBattlerTarget].personality; if (B_TRANSFORM_SHINY >= GEN_4) @@ -13275,6 +13324,12 @@ void BS_JumpIfAbilityCantBeReactivated(void) enum BattlerId battler = GetBattlerForBattleScript(cmd->battler); u32 ability = gBattleMons[battler].ability; + if (GetBattlerHoldEffectIgnoreAbility(battler) == HOLD_EFFECT_ABILITY_SHIELD) + { + gBattlescriptCurrInstr = cmd->jumpInstr; + return; + } + switch (ability) { case ABILITY_IMPOSTER: diff --git a/src/battle_terastal.c b/src/battle_terastal.c index 0a63cd6878..02504ff12e 100644 --- a/src/battle_terastal.c +++ b/src/battle_terastal.c @@ -163,15 +163,19 @@ uq4_12_t GetTeraMultiplier(struct BattleContext *ctx) else return UQ_4_12(2.0); } - // Base or Tera type only. - else if ((ctx->moveType == teraType && !IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) - || (ctx->moveType != teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))) + // Tera type only (Adaptability applies). + else if (ctx->moveType == teraType && !IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) { if (ctx->abilityAtk == ABILITY_ADAPTABILITY) return UQ_4_12(2.0); else return UQ_4_12(1.5); } + // Base type only (Adaptability does not apply while Terastallized). + else if (ctx->moveType != teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)) + { + return UQ_4_12(1.5); + } // Neither base or Tera type. else { diff --git a/src/battle_util.c b/src/battle_util.c index 6009f9869f..c291139947 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5176,6 +5176,7 @@ static u32 GetStatValueWithStages(enum BattlerId battler, enum Stat stat) enum Stat GetParadoxHighestStatId(enum BattlerId battler) { enum Stat highestId = STAT_ATK; + bool32 wonderRoom = gFieldStatuses & STATUS_FIELD_WONDER_ROOM; u32 highestStat = GetStatValueWithStages(battler, STAT_ATK); for (u32 stat = STAT_DEF; stat < NUM_STATS; stat++) @@ -5183,7 +5184,23 @@ enum Stat GetParadoxHighestStatId(enum BattlerId battler) if (stat == STAT_SPEED) continue; - u32 statValue = GetStatValueWithStages(battler, stat); + u32 statValue; + switch (stat) + { + case STAT_DEF: + statValue = wonderRoom ? gBattleMons[battler].spDefense : gBattleMons[battler].defense; + statValue *= gStatStageRatios[gBattleMons[battler].statStages[STAT_DEF]][0]; + statValue /= gStatStageRatios[gBattleMons[battler].statStages[STAT_DEF]][1]; + break; + case STAT_SPDEF: + statValue = wonderRoom ? gBattleMons[battler].defense : gBattleMons[battler].spDefense; + statValue *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPDEF]][0]; + statValue /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPDEF]][1]; + break; + default: + statValue = GetStatValueWithStages(battler, stat); + break; + } if (statValue > highestStat) { highestStat = statValue; @@ -7282,8 +7299,13 @@ static inline u32 CalcDefenseStat(struct BattleContext *ctx) modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(2.0)); break; case HOLD_EFFECT_EVIOLITE: - if (CanEvolve(gBattleMons[battlerDef].species)) - modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); + { + u16 species = gBattleMons[battlerDef].species; + if (gBattleMons[battlerDef].volatiles.transformed && gBattleMons[battlerDef].volatiles.transformedMonSpecies != SPECIES_NONE) + species = gBattleMons[battlerDef].volatiles.transformedMonSpecies; + if (CanEvolve(species)) + modifier = uq4_12_multiply_half_down(modifier, UQ_4_12(1.5)); + } break; case HOLD_EFFECT_ASSAULT_VEST: if (!usesDefStat) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index acc0a3ab38..e0a3a475db 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -440,7 +440,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = [MOVE_RAZOR_WIND] = { .name = COMPOUND_STRING("Razor Wind"), - #if B_UPDATED_MOVE_DATA == GEN_3 + #if B_UPDATED_MOVE_DATA == GEN_3 || B_UPDATED_MOVE_DATA == GEN_1 .description = COMPOUND_STRING( "A 2-turn move that strikes\n" "the foe on the 2nd turn."), @@ -455,10 +455,10 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .accuracy = B_UPDATED_MOVE_DATA >= GEN_3 ? 100 : 75, #if B_UPDATED_MOVE_DATA >= GEN_4 .criticalHitStage = 1, - #elif B_UPDATED_MOVE_DATA == GEN_3 - .criticalHitStage = 0, - #else + #elif B_UPDATED_MOVE_DATA == GEN_2 .criticalHitStage = 2, + #else + .criticalHitStage = 0, #endif .pp = 10, .target = TARGET_BOTH, @@ -490,6 +490,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_RESET_STATS }, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .danceMove = TRUE, .snatchAffected = TRUE, .contestEffect = CONTEST_EFFECT_IMPROVE_CONDITION_PREVENT_NERVOUSNESS, @@ -3262,6 +3264,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_ATK_UP_2 }, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .mimicBanned = TRUE, .metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_4, .copycatBanned = TRUE, @@ -4633,7 +4637,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_DEF_UP_1 }, - .ignoresProtect = (B_UPDATED_MOVE_FLAGS >= GEN_6) || (B_UPDATED_MOVE_FLAGS < GEN_3), + .ignoresProtect = B_UPDATED_MOVE_FLAGS < GEN_3, .magicCoatAffected = TRUE, .contestEffect = CONTEST_EFFECT_MAKE_FOLLOWING_MONS_NERVOUS, .contestCategory = CONTEST_CATEGORY_SMART, @@ -4810,7 +4814,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 0, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_RECOVER_HP }, - .ignoresProtect = B_UPDATED_MOVE_FLAGS >= GEN_3, + .ignoresProtect = TRUE, .ignoresSubstitute = B_UPDATED_MOVE_FLAGS >= GEN_5, .mirrorMoveBanned = TRUE, .contestEffect = CONTEST_EFFECT_BETTER_IF_SAME_TYPE, @@ -4975,6 +4979,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .category = DAMAGE_CATEGORY_STATUS, .argument = { .protectMethod = PROTECT_NORMAL }, .zMove = { .effect = Z_EFFECT_RESET_STATS }, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .metronomeBanned = TRUE, .copycatBanned = TRUE, .assistBanned = TRUE, @@ -7234,6 +7240,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .target = B_UPDATED_MOVE_FLAGS >= GEN_6 ? TARGET_SELECTED : TARGET_DEPENDS, .priority = 0, .category = DAMAGE_CATEGORY_STATUS, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .metronomeBanned = B_UPDATED_MOVE_FLAGS >= GEN_5, .copycatBanned = TRUE, .sleepTalkBanned = TRUE, @@ -7806,6 +7814,8 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] = .priority = 4, .category = DAMAGE_CATEGORY_STATUS, .zMove = { .effect = Z_EFFECT_SPD_UP_2 }, + .ignoresProtect = TRUE, + .mirrorMoveBanned = TRUE, .ignoresSubstitute = TRUE, .forcePressure = TRUE, .metronomeBanned = TRUE, diff --git a/src/data/pokemon/form_change_tables.h b/src/data/pokemon/form_change_tables.h index 4bb6fdf4e2..12b9c06e79 100644 --- a/src/data/pokemon/form_change_tables.h +++ b/src/data/pokemon/form_change_tables.h @@ -1514,10 +1514,10 @@ static const struct FormChange sSilvallyFormChangeTable[] = {FORM_CHANGE_TERMINATOR}, }; #endif //P_FAMILY_TYPE_NULL - #if P_FAMILY_MINIOR static const struct FormChange sMiniorRedFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_RED}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_RED}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_RED, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_RED, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1530,6 +1530,7 @@ static const struct FormChange sMiniorRedFormChangeTable[] = }; static const struct FormChange sMiniorBlueFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_BLUE}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_BLUE}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_BLUE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_BLUE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1542,6 +1543,7 @@ static const struct FormChange sMiniorBlueFormChangeTable[] = }; static const struct FormChange sMiniorGreenFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_GREEN}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_GREEN}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_GREEN, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_GREEN, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1554,6 +1556,7 @@ static const struct FormChange sMiniorGreenFormChangeTable[] = }; static const struct FormChange sMiniorIndigoFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_INDIGO}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_INDIGO}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_INDIGO, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_INDIGO, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1566,6 +1569,7 @@ static const struct FormChange sMiniorIndigoFormChangeTable[] = }; static const struct FormChange sMiniorOrangeFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_ORANGE}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_ORANGE}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_ORANGE, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_ORANGE, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1578,6 +1582,7 @@ static const struct FormChange sMiniorOrangeFormChangeTable[] = }; static const struct FormChange sMiniorVioletFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_VIOLET}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_VIOLET}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_VIOLET, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_VIOLET, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, @@ -1590,6 +1595,7 @@ static const struct FormChange sMiniorVioletFormChangeTable[] = }; static const struct FormChange sMiniorYellowFormChangeTable[] = { + {FORM_CHANGE_BEGIN_WILD_ENCOUNTER, SPECIES_MINIOR_METEOR_YELLOW}, {FORM_CHANGE_BEGIN_BATTLE, SPECIES_MINIOR_CORE_YELLOW}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_METEOR_YELLOW, ABILITY_SHIELDS_DOWN, HP_HIGHER_THAN, 50}, {FORM_CHANGE_BATTLE_HP_PERCENT_SEND_OUT, SPECIES_MINIOR_CORE_YELLOW, ABILITY_SHIELDS_DOWN, HP_LOWER_EQ_THAN, 50}, diff --git a/src/decoration.c b/src/decoration.c index 9cbcfd3026..fa57f3d240 100644 --- a/src/decoration.c +++ b/src/decoration.c @@ -1197,7 +1197,7 @@ static void WarpToInitialPosition(u8 taskId) static u16 GetDecorationElevation(u8 decoration, u8 tileIndex) { - u16 elevation = -1; + u16 elevation = ELEVATION_INVALID; switch (decoration) { case DECOR_STAND: @@ -1242,7 +1242,7 @@ static void ShowDecorationOnMap_(u16 mapX, u16 mapY, u8 decWidth, u8 decHeight, overlapsWall = 0; elevation = GetDecorationElevation(gDecorations[decoration].id, j * decWidth + i); - if (elevation != 0xFFFF) + if (elevation != ELEVATION_INVALID) MapGridSetMetatileEntryAt(x, y, (gDecorations[decoration].tiles[j * decWidth + i] + (NUM_TILES_IN_PRIMARY | overlapsWall)) | impassableFlag | elevation); else MapGridSetMetatileIdAt(x, y, (gDecorations[decoration].tiles[j * decWidth + i] + (NUM_TILES_IN_PRIMARY | overlapsWall)) | impassableFlag); diff --git a/src/event_object_movement.c b/src/event_object_movement.c index 061c594723..7f78782ca5 100644 --- a/src/event_object_movement.c +++ b/src/event_object_movement.c @@ -3519,7 +3519,7 @@ u8 GetObjectEventIdByPosition(u16 x, u16 y, u8 elevation) static bool8 ObjectEventDoesElevationMatch(struct ObjectEvent *objectEvent, u8 elevation) { - if (objectEvent->currentElevation != 0 && elevation != 0 && objectEvent->currentElevation != elevation) + if (objectEvent->currentElevation != ELEVATION_TRANSITION && elevation != ELEVATION_TRANSITION && objectEvent->currentElevation != elevation) return FALSE; return TRUE; @@ -9983,12 +9983,12 @@ bool8 IsElevationMismatchAt(u8 elevation, s16 x, s16 y) { u8 mapElevation; - if (elevation == 0) + if (elevation == ELEVATION_TRANSITION) return FALSE; mapElevation = MapGridGetElevationAt(x, y); - if (mapElevation == 0 || mapElevation == 15) + if (mapElevation == ELEVATION_TRANSITION || mapElevation == ELEVATION_MULTI_LEVEL) return FALSE; if (mapElevation != elevation) @@ -10047,7 +10047,7 @@ void ObjectEventUpdateElevation(struct ObjectEvent *objEvent, struct Sprite *spr u8 curElevation = MapGridGetElevationAt(objEvent->currentCoords.x, objEvent->currentCoords.y); u8 prevElevation = MapGridGetElevationAt(objEvent->previousCoords.x, objEvent->previousCoords.y); - if (curElevation == 15 || prevElevation == 15) + if (curElevation == ELEVATION_MULTI_LEVEL || prevElevation == ELEVATION_MULTI_LEVEL) { // Ignore subsprite priorities under bridges // so all subsprites will display below it @@ -10058,7 +10058,7 @@ void ObjectEventUpdateElevation(struct ObjectEvent *objEvent, struct Sprite *spr objEvent->currentElevation = curElevation; - if (curElevation != 0 && curElevation != 15) + if (curElevation != ELEVATION_TRANSITION && curElevation != ELEVATION_MULTI_LEVEL) objEvent->previousElevation = curElevation; } @@ -10086,7 +10086,7 @@ static void ObjectEventUpdateSubpriority(struct ObjectEvent *objEvent, struct Sp static bool8 AreElevationsCompatible(u8 a, u8 b) { - if (a == 0 || b == 0) + if (a == ELEVATION_TRANSITION || b == ELEVATION_TRANSITION) return TRUE; if (a != b) diff --git a/src/fake_rtc.c b/src/fake_rtc.c index 2024065b7d..c6b68d9105 100644 --- a/src/fake_rtc.c +++ b/src/fake_rtc.c @@ -50,6 +50,9 @@ void FakeRtc_TickTimeForward(void) void FakeRtc_AdvanceTimeBy(u32 days, u32 hours, u32 minutes, u32 seconds) { + if (!OW_USE_FAKE_RTC) + return; + struct DateTime dateTime; struct SiiRtcInfo *rtc = FakeRtc_GetCurrentTime(); @@ -63,6 +66,9 @@ void FakeRtc_AdvanceTimeBy(u32 days, u32 hours, u32 minutes, u32 seconds) void FakeRtc_ForwardTimeTo(u32 hour, u32 minute, u32 second) { + if (!OW_USE_FAKE_RTC) + return; + Script_PauseFakeRtc(); struct Time diff, target; struct SiiRtcInfo *fakeRtc = FakeRtc_GetCurrentTime(); diff --git a/src/field_control_avatar.c b/src/field_control_avatar.c index 201ede59c9..96afec4c1f 100644 --- a/src/field_control_avatar.c +++ b/src/field_control_avatar.c @@ -268,10 +268,10 @@ static void GetInFrontOfPlayerPosition(struct MapPosition *position) GetXYCoordsOneStepInFrontOfPlayer(&position->x, &position->y); PlayerGetDestCoords(&x, &y); - if (MapGridGetElevationAt(x, y) != 0) + if (MapGridGetElevationAt(x, y) != ELEVATION_TRANSITION) position->elevation = PlayerGetElevation(); else - position->elevation = 0; + position->elevation = ELEVATION_TRANSITION; } static u16 GetPlayerCurMetatileBehavior(int runningState) @@ -1114,7 +1114,7 @@ static s8 GetWarpEventAtPosition(struct MapHeader *mapHeader, u16 x, u16 y, u8 e { if ((u16)warpEvent->x == x && (u16)warpEvent->y == y) { - if (warpEvent->elevation == elevation || warpEvent->elevation == 0) + if (warpEvent->elevation == elevation || warpEvent->elevation == ELEVATION_TRANSITION) return i; } } @@ -1161,7 +1161,7 @@ static const u8 *GetCoordEventScriptAtPosition(struct MapHeader *mapHeader, u16 { if ((u16)coordEvents[i].x == x && (u16)coordEvents[i].y == y) { - if (coordEvents[i].elevation == elevation || coordEvents[i].elevation == 0) + if (coordEvents[i].elevation == elevation || coordEvents[i].elevation == ELEVATION_TRANSITION) { const u8 *script = TryRunCoordEventScript(&coordEvents[i]); if (script != NULL) @@ -1187,7 +1187,7 @@ static const struct BgEvent *GetBackgroundEventAtPosition(struct MapHeader *mapH { if ((u16)bgEvents[i].x == x && (u16)bgEvents[i].y == y) { - if (bgEvents[i].elevation == elevation || bgEvents[i].elevation == 0) + if (bgEvents[i].elevation == elevation || bgEvents[i].elevation == ELEVATION_TRANSITION) return &bgEvents[i]; } } diff --git a/src/field_effect_helpers.c b/src/field_effect_helpers.c index 336e452d96..d7c56ea289 100755 --- a/src/field_effect_helpers.c +++ b/src/field_effect_helpers.c @@ -1289,7 +1289,7 @@ void SynchronizeSurfPosition(struct ObjectEvent *playerObj, struct Sprite *sprit for (i = DIR_SOUTH; i <= DIR_EAST; i++, x = sprite->sPrevX, y = sprite->sPrevY) { MoveCoords(i, &x, &y); - if (MapGridGetElevationAt(x, y) == 3) + if (MapGridGetElevationAt(x, y) == ELEVATION_DEFAULT) { // While dismounting the surf blob bobs at a slower rate sprite->sIntervalIdx++; diff --git a/src/field_player_avatar.c b/src/field_player_avatar.c index 82e766703b..492a5fc7b7 100644 --- a/src/field_player_avatar.c +++ b/src/field_player_avatar.c @@ -1013,9 +1013,9 @@ static enum Collision CheckForObjectEventStaticCollision(struct ObjectEvent *obj static bool8 CanStopSurfing(s16 x, s16 y, enum Direction direction) { if ((gPlayerAvatar.flags & PLAYER_AVATAR_FLAG_SURFING) - && MapGridGetElevationAt(x, y) == 3 - && (GetObjectEventIdByPosition(x, y, 3) == OBJECT_EVENTS_COUNT - || GetObjectEventIdByPosition(x, y, 3) == GetFollowerNPCObjectId() + && MapGridGetElevationAt(x, y) == ELEVATION_DEFAULT + && (GetObjectEventIdByPosition(x, y, ELEVATION_DEFAULT) == OBJECT_EVENTS_COUNT + || GetObjectEventIdByPosition(x, y, ELEVATION_DEFAULT) == GetFollowerNPCObjectId() )) { CreateStopSurfingTask(direction); @@ -1663,7 +1663,7 @@ bool8 IsPlayerFacingSurfableFishableWater(void) MoveCoords(playerObjEvent->facingDirection, &x, &y); if (GetCollisionAtCoords(playerObjEvent, x, y, playerObjEvent->facingDirection) == COLLISION_ELEVATION_MISMATCH - && PlayerGetElevation() == 3 + && PlayerGetElevation() == ELEVATION_DEFAULT && MetatileBehavior_IsSurfableFishableWater(MapGridGetMetatileBehaviorAt(x, y))) return TRUE; else @@ -1724,7 +1724,7 @@ void InitPlayerAvatar(s16 x, s16 y, enum Direction direction, enum Gender gender playerObjEventTemplate.graphicsId = GetPlayerAvatarGraphicsIdByStateIdAndGender(PLAYER_AVATAR_STATE_NORMAL, gender); playerObjEventTemplate.x = x - MAP_OFFSET; playerObjEventTemplate.y = y - MAP_OFFSET; - playerObjEventTemplate.elevation = 0; + playerObjEventTemplate.elevation = ELEVATION_TRANSITION; playerObjEventTemplate.movementType = MOVEMENT_TYPE_PLAYER; playerObjEventTemplate.movementRangeX = 0; playerObjEventTemplate.movementRangeY = 0; diff --git a/src/field_specials.c b/src/field_specials.c index 3d0485bcaa..10e202271d 100644 --- a/src/field_specials.c +++ b/src/field_specials.c @@ -1360,7 +1360,7 @@ void SpawnCameraObject(void) LOCALID_CAMERA, gSaveBlock1Ptr->pos.x + MAP_OFFSET, gSaveBlock1Ptr->pos.y + MAP_OFFSET, - 3); // elevation + ELEVATION_DEFAULT); gObjectEvents[obj].invisible = TRUE; CameraObjectSetFollowedSpriteId(gObjectEvents[obj].spriteId); } diff --git a/src/overworld.c b/src/overworld.c index 3600956d85..795dfed4a7 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -3149,7 +3149,7 @@ static const u8 *TryInteractWithPlayer(struct CableClubPlayer *player) otherPlayerPos = player->pos; otherPlayerPos.x += gDirectionToVectors[player->facing].x; otherPlayerPos.y += gDirectionToVectors[player->facing].y; - otherPlayerPos.elevation = 0; + otherPlayerPos.elevation = ELEVATION_TRANSITION; linkPlayerId = GetLinkPlayerIdAt(otherPlayerPos.x, otherPlayerPos.y); if (linkPlayerId != MAX_LINK_PLAYERS) diff --git a/src/pokemon.c b/src/pokemon.c index 499997fd41..2c923a5b54 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -6692,6 +6692,7 @@ u32 GetFormChangeTargetSpecies_Internal(struct FormChangeContext ctx) case FORM_CHANGE_DEPOSIT: case FORM_CHANGE_FAINT: case FORM_CHANGE_DAYS_PASSED: + case FORM_CHANGE_BEGIN_WILD_ENCOUNTER: targetSpecies = formChanges[i].targetSpecies; break; case FORM_CHANGE_STATUS: diff --git a/src/pokenav_match_call_data.c b/src/pokenav_match_call_data.c index 2c6316146a..24546bf2c7 100644 --- a/src/pokenav_match_call_data.c +++ b/src/pokenav_match_call_data.c @@ -28,8 +28,8 @@ enum typedef struct MatchCallTextDataStruct { const u8 *text; - u16 flag; - u16 flag2; + u16 availabilityFlag; + u16 flagToSetOnCompletion; } match_call_text_data_t; struct MatchCallStructCommon { @@ -157,21 +157,25 @@ static void MatchCall_BufferCallMessageText(const match_call_text_data_t *, u8 * static void MatchCall_BufferCallMessageTextByRematchTeam(const match_call_text_data_t *, u16, u8 *); static void MatchCall_GetNameAndDescByRematchIdx(u32, const u8 **, const u8 **); +#define ALWAYS_AVAILABLE 0xFFFF +#define NO_FLAG_TO_SET 0xFFFF +#define MATCH_CALL_TEXT_END {NULL, ALWAYS_AVAILABLE, NO_FLAG_TO_SET} + // .rodata static const match_call_text_data_t sMrStoneTextScripts[] = { - { MatchCall_Text_MrStone1, 0xFFFF, FLAG_ENABLE_MR_STONE_POKENAV }, - { MatchCall_Text_MrStone2, FLAG_ENABLE_MR_STONE_POKENAV, 0xFFFF }, - { MatchCall_Text_MrStone3, FLAG_DELIVERED_STEVEN_LETTER, 0xFFFF }, - { MatchCall_Text_MrStone4, FLAG_RECEIVED_EXP_SHARE, 0xFFFF }, - { MatchCall_Text_MrStone5, FLAG_RECEIVED_HM_STRENGTH, 0xFFFF }, - { MatchCall_Text_MrStone6, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_MrStone7, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_MrStone8, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, 0xFFFF }, - { MatchCall_Text_MrStone9, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_MrStone10, FLAG_DEFEATED_SOOTOPOLIS_GYM, 0xFFFF }, - { MatchCall_Text_MrStone11, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_MrStone1, ALWAYS_AVAILABLE, FLAG_ENABLE_MR_STONE_POKENAV }, + { MatchCall_Text_MrStone2, FLAG_ENABLE_MR_STONE_POKENAV, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone3, FLAG_DELIVERED_STEVEN_LETTER, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone4, FLAG_RECEIVED_EXP_SHARE, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone5, FLAG_RECEIVED_HM_STRENGTH, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone6, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone7, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone8, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone9, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone10, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_MrStone11, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructNPC sMrStoneMatchCallHeader = @@ -185,16 +189,16 @@ static const struct MatchCallStructNPC sMrStoneMatchCallHeader = }; static const match_call_text_data_t sNormanTextScripts[] = { - { MatchCall_Text_Norman1, FLAG_ENABLE_NORMAN_MATCH_CALL, 0xFFFF }, - { MatchCall_Text_Norman2, FLAG_DEFEATED_DEWFORD_GYM, 0xFFFF }, - { MatchCall_Text_Norman3, FLAG_DEFEATED_LAVARIDGE_GYM, 0xFFFF }, - { MatchCall_Text_Norman4, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_Norman5, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_Norman6, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Norman7, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { MatchCall_Text_Norman8, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { MatchCall_Text_Norman9, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Norman1, FLAG_ENABLE_NORMAN_MATCH_CALL, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman2, FLAG_DEFEATED_DEWFORD_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman3, FLAG_DEFEATED_LAVARIDGE_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman4, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman5, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman6, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman8, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + { MatchCall_Text_Norman9, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sNormanMatchCallHeader = @@ -218,10 +222,10 @@ static const struct MatchCallBirch sProfBirchMatchCallHeader = }; static const match_call_text_data_t sMomTextScripts[] = { - { MatchCall_Text_Mom1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Mom2, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_Mom3, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Mom1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Mom2, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Mom3, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructNPC sMomMatchCallHeader = @@ -235,14 +239,14 @@ static const struct MatchCallStructNPC sMomMatchCallHeader = }; static const match_call_text_data_t sStevenTextScripts[] = { - { MatchCall_Text_Steven1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Steven2, FLAG_RUSTURF_TUNNEL_OPENED, 0xFFFF }, - { MatchCall_Text_Steven3, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_Steven4, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_Steven5, FLAG_DEFEATED_MOSSDEEP_GYM, 0xFFFF }, - { MatchCall_Text_Steven6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, 0xFFFF }, - { MatchCall_Text_Steven7, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF }, + { MatchCall_Text_Steven1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven2, FLAG_RUSTURF_TUNNEL_OPENED, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven3, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven4, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven5, FLAG_DEFEATED_MOSSDEEP_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, + { MatchCall_Text_Steven7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END, }; static const struct MatchCallStructNPC sStevenMatchCallHeader = @@ -258,22 +262,22 @@ static const struct MatchCallStructNPC sStevenMatchCallHeader = static const u8 gText_MayBrendanMatchCallDesc[] = _("RAD NEIGHBOR"); static const match_call_text_data_t sMayTextScripts[] = { - { MatchCall_Text_May1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_May2, FLAG_DEFEATED_DEWFORD_GYM, 0xFFFF }, - { MatchCall_Text_May3, FLAG_DELIVERED_DEVON_GOODS, 0xFFFF }, - { MatchCall_Text_May4, FLAG_HIDE_MAUVILLE_CITY_WALLY, 0xFFFF }, - { MatchCall_Text_May5, FLAG_RECEIVED_HM_STRENGTH, 0xFFFF }, - { MatchCall_Text_May6, FLAG_DEFEATED_LAVARIDGE_GYM, 0xFFFF }, - { MatchCall_Text_May7, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_May8, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_May9, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_May10, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, 0xFFFF }, - { MatchCall_Text_May11, FLAG_MET_TEAM_AQUA_HARBOR, 0xFFFF }, - { MatchCall_Text_May12, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_May13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, 0xFFFF }, - { MatchCall_Text_May14, FLAG_DEFEATED_SOOTOPOLIS_GYM, 0xFFFF }, - { MatchCall_Text_May15, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_May1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_May2, FLAG_DEFEATED_DEWFORD_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_May3, FLAG_DELIVERED_DEVON_GOODS, NO_FLAG_TO_SET }, + { MatchCall_Text_May4, FLAG_HIDE_MAUVILLE_CITY_WALLY, NO_FLAG_TO_SET }, + { MatchCall_Text_May5, FLAG_RECEIVED_HM_STRENGTH, NO_FLAG_TO_SET }, + { MatchCall_Text_May6, FLAG_DEFEATED_LAVARIDGE_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_May7, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_May8, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_May9, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_May10, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, + { MatchCall_Text_May11, FLAG_MET_TEAM_AQUA_HARBOR, NO_FLAG_TO_SET }, + { MatchCall_Text_May12, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_May13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, + { MatchCall_Text_May14, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_May15, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallRival sMayMatchCallHeader = @@ -287,22 +291,22 @@ static const struct MatchCallRival sMayMatchCallHeader = }; static const match_call_text_data_t sBrendanTextScripts[] = { - { MatchCall_Text_Brendan1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Brendan2, FLAG_DEFEATED_DEWFORD_GYM, 0xFFFF }, - { MatchCall_Text_Brendan3, FLAG_DELIVERED_DEVON_GOODS, 0xFFFF }, - { MatchCall_Text_Brendan4, FLAG_HIDE_MAUVILLE_CITY_WALLY, 0xFFFF }, - { MatchCall_Text_Brendan5, FLAG_RECEIVED_HM_STRENGTH, 0xFFFF }, - { MatchCall_Text_Brendan6, FLAG_DEFEATED_LAVARIDGE_GYM, 0xFFFF }, - { MatchCall_Text_Brendan7, FLAG_DEFEATED_PETALBURG_GYM, 0xFFFF }, - { MatchCall_Text_Brendan8, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_Brendan9, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_Brendan10, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, 0xFFFF }, - { MatchCall_Text_Brendan11, FLAG_MET_TEAM_AQUA_HARBOR, 0xFFFF }, - { MatchCall_Text_Brendan12, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_Brendan13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, 0xFFFF }, - { MatchCall_Text_Brendan14, FLAG_DEFEATED_SOOTOPOLIS_GYM, 0xFFFF }, - { MatchCall_Text_Brendan15, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Brendan1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan2, FLAG_DEFEATED_DEWFORD_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan3, FLAG_DELIVERED_DEVON_GOODS, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan4, FLAG_HIDE_MAUVILLE_CITY_WALLY, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan5, FLAG_RECEIVED_HM_STRENGTH, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan6, FLAG_DEFEATED_LAVARIDGE_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan7, FLAG_DEFEATED_PETALBURG_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan8, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan9, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan10, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan11, FLAG_MET_TEAM_AQUA_HARBOR, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan12, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan13, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan14, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Brendan15, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallRival sBrendanMatchCallHeader = @@ -316,14 +320,14 @@ static const struct MatchCallRival sBrendanMatchCallHeader = }; static const match_call_text_data_t sWallyTextScripts[] = { - { MatchCall_Text_Wally1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Wally2, FLAG_RUSTURF_TUNNEL_OPENED, 0xFFFF }, - { MatchCall_Text_Wally3, FLAG_DEFEATED_LAVARIDGE_GYM, 0xFFFF }, - { MatchCall_Text_Wally4, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_Wally5, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, 0xFFFF }, - { MatchCall_Text_Wally6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, 0xFFFF }, - { MatchCall_Text_Wally7, FLAG_DEFEATED_WALLY_VICTORY_ROAD, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Wally1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally2, FLAG_RUSTURF_TUNNEL_OPENED, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally3, FLAG_DEFEATED_LAVARIDGE_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally4, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally5, FLAG_GROUDON_AWAKENED_MAGMA_HIDEOUT, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally6, FLAG_KYOGRE_ESCAPED_SEAFLOOR_CAVERN, NO_FLAG_TO_SET }, + { MatchCall_Text_Wally7, FLAG_DEFEATED_WALLY_VICTORY_ROAD, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallLocationOverride sWallyLocationData[] = { @@ -345,14 +349,14 @@ static const struct MatchCallWally sWallyMatchCallHeader = }; static const match_call_text_data_t sScottTextScripts[] = { - { MatchCall_Text_Scott1, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Scott2, FLAG_DEFEATED_EVIL_TEAM_MT_CHIMNEY, 0xFFFF }, - { MatchCall_Text_Scott3, FLAG_RECEIVED_CASTFORM, 0xFFFF }, - { MatchCall_Text_Scott4, FLAG_RECEIVED_RED_OR_BLUE_ORB, 0xFFFF }, - { MatchCall_Text_Scott5, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, 0xFFFF }, - { MatchCall_Text_Scott6, FLAG_DEFEATED_SOOTOPOLIS_GYM, 0xFFFF }, - { MatchCall_Text_Scott7, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Scott1, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott2, FLAG_DEFEATED_EVIL_TEAM_MT_CHIMNEY, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott3, FLAG_RECEIVED_CASTFORM, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott4, FLAG_RECEIVED_RED_OR_BLUE_ORB, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott5, FLAG_TEAM_AQUA_ESCAPED_IN_SUBMARINE, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott6, FLAG_DEFEATED_SOOTOPOLIS_GYM, NO_FLAG_TO_SET }, + { MatchCall_Text_Scott7, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; @@ -367,11 +371,11 @@ static const struct MatchCallStructNPC sScottMatchCallHeader = }; static const match_call_text_data_t sRoxanneTextScripts[] = { - { MatchCall_Text_Roxanne1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Roxanne2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Roxanne3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Roxanne4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Roxanne1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Roxanne2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Roxanne3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Roxanne4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sRoxanneMatchCallHeader = @@ -386,11 +390,11 @@ static const struct MatchCallStructTrainer sRoxanneMatchCallHeader = }; static const match_call_text_data_t sBrawlyTextScripts[] = { - { MatchCall_Text_Brawly1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Brawly2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Brawly3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Brawly4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Brawly1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brawly2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brawly3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Brawly4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sBrawlyMatchCallHeader = @@ -405,11 +409,11 @@ static const struct MatchCallStructTrainer sBrawlyMatchCallHeader = }; static const match_call_text_data_t sWattsonTextScripts[] = { - { MatchCall_Text_Wattson1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Wattson2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Wattson3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Wattson4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Wattson1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Wattson2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Wattson3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Wattson4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sWattsonMatchCallHeader = @@ -424,11 +428,11 @@ static const struct MatchCallStructTrainer sWattsonMatchCallHeader = }; static const match_call_text_data_t sFlanneryTextScripts[] = { - { MatchCall_Text_Flannery1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Flannery2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Flannery3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Flannery4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Flannery1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Flannery2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Flannery3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Flannery4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sFlanneryMatchCallHeader = @@ -443,11 +447,11 @@ static const struct MatchCallStructTrainer sFlanneryMatchCallHeader = }; static const match_call_text_data_t sWinonaTextScripts[] = { - { MatchCall_Text_Winona1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Winona2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Winona3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Winona4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Winona1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Winona2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Winona3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Winona4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sWinonaMatchCallHeader = @@ -462,11 +466,11 @@ static const struct MatchCallStructTrainer sWinonaMatchCallHeader = }; static const match_call_text_data_t sTateLizaTextScripts[] = { - { MatchCall_Text_TateLiza1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_TateLiza2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_TateLiza3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_TateLiza4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_TateLiza1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_TateLiza2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_TateLiza3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_TateLiza4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sTateLizaMatchCallHeader = @@ -481,11 +485,11 @@ static const struct MatchCallStructTrainer sTateLizaMatchCallHeader = }; static const match_call_text_data_t sJuanTextScripts[] = { - { MatchCall_Text_Juan1, 0xFFFE, 0xFFFF }, - { MatchCall_Text_Juan2, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Juan3, 0xFFFF, 0xFFFF }, - { MatchCall_Text_Juan4, FLAG_SYS_GAME_CLEAR, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Juan1, 0xFFFE, NO_FLAG_TO_SET }, + { MatchCall_Text_Juan2, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Juan3, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + { MatchCall_Text_Juan4, FLAG_SYS_GAME_CLEAR, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sJuanMatchCallHeader = @@ -502,8 +506,8 @@ static const struct MatchCallStructTrainer sJuanMatchCallHeader = static const u8 gText_EliteFourMatchCallDesc[] = _("ELITE FOUR"); static const match_call_text_data_t sSidneyTextScripts[] = { - { MatchCall_Text_Sidney, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Sidney, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sSidneyMatchCallHeader = @@ -518,8 +522,8 @@ static const struct MatchCallStructTrainer sSidneyMatchCallHeader = }; static const match_call_text_data_t sPhoebeTextScripts[] = { - { MatchCall_Text_Phoebe, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Phoebe, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sPhoebeMatchCallHeader = @@ -534,8 +538,8 @@ static const struct MatchCallStructTrainer sPhoebeMatchCallHeader = }; static const match_call_text_data_t sGlaciaTextScripts[] = { - { MatchCall_Text_Glacia, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Glacia, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sGlaciaMatchCallHeader = @@ -550,8 +554,8 @@ static const struct MatchCallStructTrainer sGlaciaMatchCallHeader = }; static const match_call_text_data_t sDrakeTextScripts[] = { - { MatchCall_Text_Drake, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Drake, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sDrakeMatchCallHeader = @@ -566,8 +570,8 @@ static const struct MatchCallStructTrainer sDrakeMatchCallHeader = }; static const match_call_text_data_t sWallaceTextScripts[] = { - { MatchCall_Text_Wallace, 0xFFFF, 0xFFFF }, - { NULL, 0xFFFF, 0xFFFF } + { MatchCall_Text_Wallace, ALWAYS_AVAILABLE, NO_FLAG_TO_SET }, + MATCH_CALL_TEXT_END }; static const struct MatchCallStructTrainer sWallaceMatchCallHeader = @@ -1018,12 +1022,12 @@ static void MatchCall_BufferCallMessageText(const match_call_text_data_t *textDa i--; while (i) { - if (textData[i].flag != 0xFFFF && FlagGet(textData[i].flag) == TRUE) + if (textData[i].availabilityFlag != ALWAYS_AVAILABLE && FlagGet(textData[i].availabilityFlag) == TRUE) break; i--; } - if (textData[i].flag2 != 0xFFFF) - FlagSet(textData[i].flag2); + if (textData[i].flagToSetOnCompletion != NO_FLAG_TO_SET) + FlagSet(textData[i].flagToSetOnCompletion); StringExpandPlaceholders(dest, textData[i].text); } @@ -1033,17 +1037,17 @@ static void MatchCall_BufferCallMessageTextByRematchTeam(const match_call_text_d u32 i; for (i = 0; textData[i].text != NULL; i++) { - if (textData[i].flag == 0xFFFE) + if (textData[i].availabilityFlag == 0xFFFE) break; - if (textData[i].flag != 0xFFFF && !FlagGet(textData[i].flag)) + if (textData[i].availabilityFlag != ALWAYS_AVAILABLE && !FlagGet(textData[i].availabilityFlag)) break; } - if (textData[i].flag != 0xFFFE) + if (textData[i].availabilityFlag != 0xFFFE) { if (i) i--; - if (textData[i].flag2 != 0xFFFF) - FlagSet(textData[i].flag2); + if (textData[i].flagToSetOnCompletion != NO_FLAG_TO_SET) + FlagSet(textData[i].flagToSetOnCompletion); StringExpandPlaceholders(dest, textData[i].text); } else diff --git a/src/rtc.c b/src/rtc.c index f553092909..2a9bc4adf6 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -351,7 +351,8 @@ void RtcCalcLocalTimeOffset(s32 days, s32 hours, s32 minutes, s32 seconds) gLocalTime.hours = hours; gLocalTime.minutes = minutes; gLocalTime.seconds = seconds; - FakeRtc_ManuallySetTime(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, seconds); + if (!OW_USE_FAKE_RTC) + FakeRtc_ManuallySetTime(gLocalTime.days, gLocalTime.hours, gLocalTime.minutes, seconds); RtcGetInfo(&sRtc); RtcCalcTimeDifference(&sRtc, &gSaveBlock2Ptr->localTimeOffset, &gLocalTime); } diff --git a/src/scrcmd.c b/src/scrcmd.c index 865b385941..ce22da1985 100644 --- a/src/scrcmd.c +++ b/src/scrcmd.c @@ -3257,6 +3257,9 @@ bool8 ScrCmd_fwdtime(struct ScriptContext *ctx) bool8 ScrCmd_fwdweekday(struct ScriptContext *ctx) { + if (!OW_USE_FAKE_RTC) + return FALSE; + struct SiiRtcInfo *rtc = FakeRtc_GetCurrentTime(); u32 weekdayTarget = ScriptReadWord(ctx); diff --git a/src/trainer_hill.c b/src/trainer_hill.c index f87100df25..12fa797d3d 100644 --- a/src/trainer_hill.c +++ b/src/trainer_hill.c @@ -263,7 +263,7 @@ static const u8 *const sModeStrings[NUM_TRAINER_HILL_MODES] = static const struct ObjectEventTemplate sTrainerObjectEventTemplate = { .graphicsId = OBJ_EVENT_GFX_RIVAL_BRENDAN_NORMAL, - .elevation = 3, + .elevation = ELEVATION_DEFAULT, .movementType = MOVEMENT_TYPE_LOOK_AROUND, .movementRangeX = 1, .movementRangeY = 1, @@ -716,7 +716,7 @@ static u16 GetMapDataForFloor(u8 floorId, u32 x, u32 y, u32 floorWidth) // floor impassable = (sHillData->floors[floorId].map.collisionData[y] >> (15 - x) & 1); metatileId = sHillData->floors[floorId].map.metatileData[floorWidth * y + x] + NUM_METATILES_IN_PRIMARY; - elevation = PACK_ELEVATION(3); + elevation = PACK_ELEVATION(ELEVATION_DEFAULT); return PACK_COLLISION(impassable) | elevation | PACK_METATILE(metatileId); } diff --git a/test/battle/ability/adaptability.c b/test/battle/ability/adaptability.c index 876dac212c..ba54590cd2 100644 --- a/test/battle/ability/adaptability.c +++ b/test/battle/ability/adaptability.c @@ -61,4 +61,39 @@ SINGLE_BATTLE_TEST("(TERA) Terastallizing into the same type with Adaptability g } } -TO_DO_BATTLE_TEST("Adaptability does not affect Stellar-type moves"); +SINGLE_BATTLE_TEST("(TERA) Adaptability does not increase non-Tera base STAB beyond 1.5x", s16 damage) +{ + u32 move; + PARAMETRIZE { move = MOVE_GUST; } + PARAMETRIZE { move = MOVE_WATER_GUN; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ABILITY_ADAPTABILITY); TeraType(TYPE_NORMAL); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, move, gimmick: GIMMICK_TERA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + // With Adaptability, non-Tera base type should still be 1.5x STAB (not 2.0x). + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +SINGLE_BATTLE_TEST("(TERA) Adaptability does not affect Stellar-type moves", s16 damage) +{ + u32 ability; + PARAMETRIZE { ability = ABILITY_HYPER_CUTTER; } + PARAMETRIZE { ability = ABILITY_ADAPTABILITY; } + GIVEN { + PLAYER(SPECIES_CRAWDAUNT) { Ability(ability); TeraType(TYPE_STELLAR); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TERA_BLAST, gimmick: GIMMICK_TERA); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TERA_BLAST, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} diff --git a/test/battle/ability/hadron_engine.c b/test/battle/ability/hadron_engine.c index e6324c0c51..0de50c94ec 100644 --- a/test/battle/ability/hadron_engine.c +++ b/test/battle/ability/hadron_engine.c @@ -1,4 +1,45 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Hadron Engine (Ability) test titles") +SINGLE_BATTLE_TEST("Hadron Engine creates Electric Terrain when entering the battle") +{ + GIVEN { + PLAYER(SPECIES_MIRAIDON) { Ability(ABILITY_HADRON_ENGINE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(player, ABILITY_HADRON_ENGINE); + MESSAGE("An electric current ran across the battlefield!"); + } +} + +SINGLE_BATTLE_TEST("Hadron Engine boosts the Pokemon's Special Attack on Electric Terrain even if not grounded", s16 damage) +{ + bool32 overrideTerrain, airBalloon; + + PARAMETRIZE { airBalloon = FALSE; overrideTerrain = TRUE; } + PARAMETRIZE { airBalloon = FALSE; overrideTerrain = FALSE; } + PARAMETRIZE { airBalloon = TRUE; overrideTerrain = TRUE; } + PARAMETRIZE { airBalloon = TRUE; overrideTerrain = FALSE; } + + GIVEN { + ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON); + ASSUME(GetMoveEffect(MOVE_GRASSY_TERRAIN) == EFFECT_GRASSY_TERRAIN); + ASSUME(GetMoveCategory(MOVE_POWER_GEM) == DAMAGE_CATEGORY_SPECIAL); + PLAYER(SPECIES_MIRAIDON) { Ability(ABILITY_HADRON_ENGINE); Moves(MOVE_POWER_GEM, MOVE_CELEBRATE); Item(airBalloon ? ITEM_AIR_BALLOON : ITEM_NONE); Speed(1); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_GRASSY_TERRAIN, MOVE_CELEBRATE); Speed(2); } + } WHEN { + if (overrideTerrain) + TURN { MOVE(opponent, MOVE_GRASSY_TERRAIN); } + TURN { MOVE(player, MOVE_POWER_GEM); } + } SCENE { + if (overrideTerrain) + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_POWER_GEM, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.3333), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.3333), results[3].damage); + } +} diff --git a/test/battle/ability/protosynthesis.c b/test/battle/ability/protosynthesis.c index e2761724b7..9127d3fe2f 100644 --- a/test/battle/ability/protosynthesis.c +++ b/test/battle/ability/protosynthesis.c @@ -134,6 +134,23 @@ SINGLE_BATTLE_TEST("Protosynthesis prioritizes stats in the case of a tie in the } } +SINGLE_BATTLE_TEST("Protosynthesis uses Wonder Room swapped defenses when choosing boosted stat") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_WONDER_ROOM) == EFFECT_WONDER_ROOM); + PLAYER(SPECIES_ROARING_MOON) { Ability(ABILITY_PROTOSYNTHESIS); Attack(50); Defense(200); SpAttack(40); SpDefense(60); Speed(70); Moves(MOVE_WONDER_ROOM); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUNNY_DAY); Speed(60); } + } WHEN { + TURN { MOVE(player, MOVE_WONDER_ROOM); MOVE(opponent, MOVE_SUNNY_DAY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WONDER_ROOM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent); + ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS); + MESSAGE("The harsh sunlight activated Roaring Moon's Protosynthesis!"); + MESSAGE("Roaring Moon's Sp. Def was heightened!"); + } +} + SINGLE_BATTLE_TEST("Protosynthesis activates in Sun before Booster Energy") { GIVEN { diff --git a/test/battle/ability/quark_drive.c b/test/battle/ability/quark_drive.c index b79362d639..ae2aee388a 100644 --- a/test/battle/ability/quark_drive.c +++ b/test/battle/ability/quark_drive.c @@ -201,6 +201,23 @@ SINGLE_BATTLE_TEST("Quark Drive prioritizes stats in the case of a tie in the fo } } +SINGLE_BATTLE_TEST("Quark Drive uses Wonder Room swapped defenses when choosing boosted stat") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_WONDER_ROOM) == EFFECT_WONDER_ROOM); + PLAYER(SPECIES_IRON_LEAVES) { Ability(ABILITY_QUARK_DRIVE); Attack(50); Defense(200); SpAttack(40); SpDefense(60); Speed(70); Moves(MOVE_WONDER_ROOM); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_ELECTRIC_TERRAIN); Speed(60); } + } WHEN { + TURN { MOVE(player, MOVE_WONDER_ROOM); MOVE(opponent, MOVE_ELECTRIC_TERRAIN); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WONDER_ROOM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, opponent); + ABILITY_POPUP(player, ABILITY_QUARK_DRIVE); + MESSAGE("The Electric Terrain activated Iron Leaves's Quark Drive!"); + MESSAGE("Iron Leaves's Sp. Def was heightened!"); + } +} + SINGLE_BATTLE_TEST("Quark Drive activates in Electric Terrain before Booster Energy") { GIVEN { diff --git a/test/battle/ability/shields_down.c b/test/battle/ability/shields_down.c index d16aa240da..ca1ade2c37 100644 --- a/test/battle/ability/shields_down.c +++ b/test/battle/ability/shields_down.c @@ -75,3 +75,39 @@ SINGLE_BATTLE_TEST("Shields Down protects Minior Meteor from status conditions") EXPECT(opponent->status1 & STATUS1_BURN); } } + +WILD_BATTLE_TEST("Wild Minior appear in Meteor form without transforming")// To be replaced with WILD_DOUBLE_BATTLE_TEST when that is made possible. +{ + GIVEN { + PLAYER(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); } + OPPONENT(SPECIES_MINIOR_CORE) { Ability(ABILITY_SHIELDS_DOWN); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(player, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player); + NONE_OF { + ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); + } + } THEN { + EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR); + EXPECT_EQ(player->species, SPECIES_MINIOR_METEOR); + } +} + +SINGLE_BATTLE_TEST("Trainers' Minior appear in Core form") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_MINIOR_METEOR) { Ability(ABILITY_SHIELDS_DOWN); } + } WHEN { + TURN {} + } SCENE { + ABILITY_POPUP(opponent, ABILITY_SHIELDS_DOWN); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent); + } THEN { + EXPECT_EQ(opponent->species, SPECIES_MINIOR_METEOR); + } +} + diff --git a/test/battle/gimmick/dynamax.c b/test/battle/gimmick/dynamax.c index aeb95099d1..4e91eb51c8 100644 --- a/test/battle/gimmick/dynamax.c +++ b/test/battle/gimmick/dynamax.c @@ -997,6 +997,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Volt Crash paralyzes both opponents") TURN { MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); } } SCENE { MESSAGE("Pikachu used G-Max Volt Crash!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_VOLT_CRASH, playerLeft); ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponentLeft); MESSAGE("The opposing Wobbuffet is paralyzed, so it may be unable to move!"); STATUS_ICON(opponentLeft, paralysis: TRUE); @@ -1309,6 +1310,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Replenish recycles allies' berries 50\% of th PASSES_RANDOMLY(1, 2, RNG_G_MAX_REPLENISH); GIVEN { ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_REPLENISH, MOVE_EFFECT_RECYCLE_BERRIES)); + ASSUME(GetItemHoldEffect(ITEM_APICOT_BERRY) == HOLD_EFFECT_SP_DEFENSE_UP); PLAYER(SPECIES_SNORLAX) { Item(ITEM_APICOT_BERRY); GigantamaxFactor(TRUE); } PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); Ability(ABILITY_THICK_FAT); } OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_APICOT_BERRY); } @@ -1321,12 +1323,14 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Replenish recycles allies' berries 50\% of th TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); } } SCENE { // turn 1 + MESSAGE("Using Apicot Berry, the Sp. Def of Snorlax rose!"); MESSAGE("Using Apicot Berry, the Sp. Def of Munchlax rose!"); MESSAGE("Using Apicot Berry, the Sp. Def of the opposing Wobbuffet rose!"); MESSAGE("Using Apicot Berry, the Sp. Def of the opposing Wobbuffet rose!"); // turn 2 MESSAGE("Snorlax used G-Max Replenish!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_REPLENISH, playerLeft); MESSAGE("Snorlax found one Apicot Berry!"); MESSAGE("Munchlax found one Apicot Berry!"); } @@ -1369,6 +1373,7 @@ DOUBLE_BATTLE_TEST("Dynamax: G-Max Finale heals allies by 1/6 of their health") TURN { MOVE(playerLeft, MOVE_MOONBLAST, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); } } SCENE { MESSAGE("Alcremie used G-Max Finale!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_FINALE, playerLeft); HP_BAR(playerLeft, captureDamage: &damage1); HP_BAR(playerRight, captureDamage: &damage2); } THEN { @@ -1713,5 +1718,81 @@ DOUBLE_BATTLE_TEST("Dynamax stat raising moves don't make stat-changing abilitie } } +DOUBLE_BATTLE_TEST("Dynamax: G-Max Finale heals allies by 1/6 of their health, even if it faints the foe") +{ + s16 damage1, damage2; + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_FINALE, MOVE_EFFECT_HEAL_TEAM)); + PLAYER(SPECIES_ALCREMIE) { HP(1); GigantamaxFactor(TRUE); } + PLAYER(SPECIES_MILCERY) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_MOONBLAST, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponentLeft, 2); } + } SCENE { + MESSAGE("Alcremie used G-Max Finale!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_FINALE, playerLeft); + HP_BAR(playerLeft, captureDamage: &damage1); + HP_BAR(playerRight, captureDamage: &damage2); + } THEN { + EXPECT_MUL_EQ(-damage1, Q_4_12(6), playerLeft->maxHP); // heals based on Dynamax HP. Appears to have a problem with milcery in this case!? + } +} + +DOUBLE_BATTLE_TEST("Dynamax: G-Max Replenish recycles allies' berries 50\% of the time, even if it faints the foe") +{ + PASSES_RANDOMLY(1, 2, RNG_G_MAX_REPLENISH); + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_REPLENISH, MOVE_EFFECT_RECYCLE_BERRIES)); + ASSUME(GetItemHoldEffect(ITEM_APICOT_BERRY) == HOLD_EFFECT_SP_DEFENSE_UP); + PLAYER(SPECIES_SNORLAX) { Item(ITEM_APICOT_BERRY); GigantamaxFactor(TRUE); } + PLAYER(SPECIES_MUNCHLAX) { Item(ITEM_APICOT_BERRY); Ability(ABILITY_THICK_FAT); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_STUFF_CHEEKS); \ + MOVE(playerRight, MOVE_STUFF_CHEEKS); \ + MOVE(opponentLeft, MOVE_CELEBRATE); \ + MOVE(opponentRight, MOVE_CELEBRATE); } + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponentLeft, 2);} + } SCENE { + // turn 1 + MESSAGE("Using Apicot Berry, the Sp. Def of Snorlax rose!"); + MESSAGE("Using Apicot Berry, the Sp. Def of Munchlax rose!"); + // turn 2 + MESSAGE("Snorlax used G-Max Replenish!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_REPLENISH, playerLeft); + MESSAGE("Snorlax found one Apicot Berry!"); + MESSAGE("Munchlax found one Apicot Berry!"); + } +} + +DOUBLE_BATTLE_TEST("Dynamax: G-Max Volt Crash paralyzes other opponent even if its target faints") +{ + GIVEN { + ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_VOLT_CRASH, MOVE_EFFECT_PARALYZE_SIDE)); + PLAYER(SPECIES_PIKACHU) { GigantamaxFactor(TRUE); } + PLAYER(SPECIES_PICHU); + OPPONENT(SPECIES_WOBBUFFET) { HP(1); } + OPPONENT(SPECIES_WYNAUT); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_THUNDERBOLT, target: opponentLeft, gimmick: GIMMICK_DYNAMAX); SEND_OUT(opponentLeft, 2); } + } SCENE { + MESSAGE("Pikachu used G-Max Volt Crash!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_G_MAX_VOLT_CRASH, playerLeft); + NONE_OF { + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponentLeft); + STATUS_ICON(opponentLeft, paralysis: TRUE); + } + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponentRight); + MESSAGE("The opposing Wynaut is paralyzed, so it may be unable to move!"); + STATUS_ICON(opponentRight, paralysis: TRUE); + } +} + TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-lowering Max Moves, without showing a message") TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-increasing Max Moves, without showing a message") diff --git a/test/battle/hold_effect/ability_shield.c b/test/battle/hold_effect/ability_shield.c index 40ea3328d5..5041734b79 100644 --- a/test/battle/hold_effect/ability_shield.c +++ b/test/battle/hold_effect/ability_shield.c @@ -35,6 +35,38 @@ SINGLE_BATTLE_TEST("Ability Shield protects against Neutralizing Gas") } } +DOUBLE_BATTLE_TEST("Ability Shield prevents Intimidate from reactivating after Neutralizing Gas ends") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + PLAYER(SPECIES_WYNAUT) { Speed(4); } + OPPONENT(SPECIES_KOFFING) { Ability(ABILITY_NEUTRALIZING_GAS); HP(1); Speed(1); } + OPPONENT(SPECIES_GYARADOS) { Ability(ABILITY_INTIMIDATE); Item(ITEM_ABILITY_SHIELD); Speed(3); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_SCRATCH, target: opponentLeft); } + } SCENE { + ABILITY_POPUP(opponentLeft, ABILITY_NEUTRALIZING_GAS); + MESSAGE("Neutralizing gas filled the area!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight); + MESSAGE("The opposing Gyarados's Ability is protected by the effects of its Ability Shield!"); + ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + HP_BAR(opponentLeft); + MESSAGE("The effects of the neutralizing gas wore off!"); + NONE_OF { + ABILITY_POPUP(opponentRight, ABILITY_INTIMIDATE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight); + } + MESSAGE("The opposing Koffing fainted!"); + } THEN { + EXPECT_EQ(playerLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + } +} + SINGLE_BATTLE_TEST("Ability Shield protects against Mold Breaker (no message)") { enum Item item; diff --git a/test/battle/hold_effect/eviolite.c b/test/battle/hold_effect/eviolite.c index 1aa0622e43..5387d50929 100644 --- a/test/battle/hold_effect/eviolite.c +++ b/test/battle/hold_effect/eviolite.c @@ -1,4 +1,82 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Eviolite (Hold Effect) test titles") +ASSUMPTIONS +{ + ASSUME(gItemsInfo[ITEM_EVIOLITE].holdEffect == HOLD_EFFECT_EVIOLITE); +} + +SINGLE_BATTLE_TEST("Eviolite boosts Defense and Sp. Def for unevolved Pokemon", s16 damage) +{ + u16 move; + u32 item; + + PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_EVIOLITE; } + PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_NONE; } + PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_EVIOLITE; } + PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_NONE; } + + GIVEN { + ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL); + PLAYER(SPECIES_PIKACHU) { Item(item); } + OPPONENT(SPECIES_MAGIKARP) { Moves(MOVE_SCRATCH, MOVE_WATER_GUN); } + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage); + } +} + +SINGLE_BATTLE_TEST("Eviolite does not boost Defense or Sp. Def for evolved Pokemon", s16 damage) +{ + u16 move; + u32 item; + + PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_EVIOLITE; } + PARAMETRIZE { move = MOVE_SCRATCH; item = ITEM_NONE; } + PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_EVIOLITE; } + PARAMETRIZE { move = MOVE_WATER_GUN; item = ITEM_NONE; } + + GIVEN { + ASSUME(GetMoveCategory(MOVE_SCRATCH) == DAMAGE_CATEGORY_PHYSICAL); + ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL); + PLAYER(SPECIES_RAICHU) { Item(item); } + OPPONENT(SPECIES_MAGIKARP) { Moves(MOVE_SCRATCH, MOVE_WATER_GUN); } + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.0), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.0), results[3].damage); + } +} + +SINGLE_BATTLE_TEST("Eviolite uses original species after Transform", s16 damage) +{ + u32 item; + + PARAMETRIZE { item = ITEM_EVIOLITE; } + PARAMETRIZE { item = ITEM_NONE; } + + GIVEN { + ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM); + PLAYER(SPECIES_PIKACHU) { Item(item); Moves(MOVE_TRANSFORM, MOVE_CELEBRATE); } + OPPONENT(SPECIES_GYARADOS) { Moves(MOVE_SCRATCH, MOVE_CELEBRATE); } + } WHEN { + TURN { MOVE(player, MOVE_TRANSFORM); MOVE(opponent, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_SCRATCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent); + HP_BAR(player, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} diff --git a/test/battle/move_effect/ally_switch.c b/test/battle/move_effect/ally_switch.c index c153ddc3a0..ce62705394 100644 --- a/test/battle/move_effect/ally_switch.c +++ b/test/battle/move_effect/ally_switch.c @@ -92,24 +92,24 @@ DOUBLE_BATTLE_TEST("Ally Switch does not redirect the target of Snipe Shot") DOUBLE_BATTLE_TEST("Ally Switch does not redirect moves done by Pokémon with Stalwart and Propeller Tail") { + u16 species; enum Ability ability; - PARAMETRIZE { ability = ABILITY_STALWART; } - PARAMETRIZE { ability = ABILITY_PROPELLER_TAIL; } - PARAMETRIZE { ability = ABILITY_TELEPATHY; } + PARAMETRIZE { species = SPECIES_DURALUDON; ability = ABILITY_STALWART; } + PARAMETRIZE { species = SPECIES_ARROKUDA; ability = ABILITY_PROPELLER_TAIL; } + PARAMETRIZE { species = SPECIES_RALTS; ability = ABILITY_TELEPATHY; } GIVEN { PLAYER(SPECIES_WOBBUFFET); // Wobb is playerLeft, but it'll be Wynaut after Ally Switch PLAYER(SPECIES_WYNAUT); - OPPONENT(SPECIES_KADABRA) { Ability(ability); } + OPPONENT(species) { Ability(ability); } OPPONENT(SPECIES_ABRA); } WHEN { - TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(opponentLeft, MOVE_SCRATCH, target:playerRight); } // Kadabra targets playerRight Wynaut. + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); MOVE(opponentLeft, MOVE_SCRATCH, target:playerRight); } // Opponent targets playerRight Wynaut. } SCENE { MESSAGE("Wobbuffet used Ally Switch!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); MESSAGE("Wobbuffet and Wynaut switched places!"); - MESSAGE("The opposing Kadabra used Scratch!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft); HP_BAR((ability == ABILITY_STALWART || ability == ABILITY_PROPELLER_TAIL) ? playerLeft : playerRight); } @@ -218,6 +218,7 @@ DOUBLE_BATTLE_TEST("Ally Switch increases the Protect-like moves counter (Gen9+) DOUBLE_BATTLE_TEST("Ally Switch works if ally used two-turn move like Dig") { GIVEN { + ASSUME(gBattleMoveEffects[GetMoveEffect(MOVE_DIG)].twoTurnEffect); PLAYER(SPECIES_WOBBUFFET); PLAYER(SPECIES_WYNAUT); OPPONENT(SPECIES_WOBBUFFET); @@ -313,9 +314,8 @@ DOUBLE_BATTLE_TEST("Ally switch swaps opposing sky drop targets if partner is be DOUBLE_BATTLE_TEST("Ally Switch swaps Illusion data") { GIVEN { - ASSUME(GetMoveEffect(MOVE_ALLY_SWITCH) == EFFECT_ALLY_SWITCH); PLAYER(SPECIES_HOOPA); - PLAYER(SPECIES_ZOROARK); + PLAYER(SPECIES_ZOROARK) {Ability(ABILITY_ILLUSION); } PLAYER(SPECIES_MAMOSWINE); // the third member here is required for zoroark OPPONENT(SPECIES_WOBBUFFET); OPPONENT(SPECIES_WOBBUFFET); @@ -329,6 +329,7 @@ DOUBLE_BATTLE_TEST("Ally Switch swaps Illusion data") DOUBLE_BATTLE_TEST("Ally switch updates last used moves for Mimic") { GIVEN { + ASSUME(GetMoveEffect(MOVE_MIMIC) == EFFECT_MIMIC); PLAYER(SPECIES_XATU) { Speed(100); } PLAYER(SPECIES_RIOLU) { Speed(150); } OPPONENT(SPECIES_FEAROW) { Speed(20); } @@ -348,9 +349,10 @@ DOUBLE_BATTLE_TEST("Ally switch updates last used moves for Mimic") } } -DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed battler") +DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed position") { GIVEN { + ASSUME(GetMoveEffect(MOVE_LEECH_SEED) == EFFECT_LEECH_SEED); PLAYER(SPECIES_WYNAUT); PLAYER(SPECIES_SOLOSIS); OPPONENT(SPECIES_BULBASAUR) { HP(50); MaxHP(100); } @@ -379,6 +381,114 @@ DOUBLE_BATTLE_TEST("Ally Switch does not update leech seed battler") } } +DOUBLE_BATTLE_TEST("Ally Switch does not update Future Sight target position") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ABRA); + OPPONENT(SPECIES_RALTS); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_FUTURE_SIGHT, target: playerLeft); } + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); + MESSAGE("Wynaut took the Future Sight attack!"); + HP_BAR(playerLeft); + NOT HP_BAR(playerRight); + } +} + +DOUBLE_BATTLE_TEST("Ally Switch does not update Future Sight target position when attacker side switches") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_FUTURE_SIGHT) == EFFECT_FUTURE_SIGHT); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_ABRA); + OPPONENT(SPECIES_RALTS); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponentLeft, MOVE_FUTURE_SIGHT, target: playerLeft); } + TURN { SWITCH(opponentLeft, 2); MOVE(opponentRight, MOVE_ALLY_SWITCH); } + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FUTURE_SIGHT, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, opponentRight); + MESSAGE("Wobbuffet took the Future Sight attack!"); + HP_BAR(playerLeft); + NOT HP_BAR(playerRight); + } +} + +DOUBLE_BATTLE_TEST("Ally Switch does not update Wish recovery position") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_WISH) == EFFECT_WISH); + PLAYER(SPECIES_WOBBUFFET) { HP(50); MaxHP(100); } + PLAYER(SPECIES_WYNAUT) { HP(20); MaxHP(100); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_WISH); } + TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WISH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ALLY_SWITCH, playerLeft); + HP_BAR(playerLeft); + NOT HP_BAR(playerRight); + } THEN { + EXPECT_EQ(playerLeft->hp, 70); + EXPECT_EQ(playerRight->hp, 50); + } +} + +DOUBLE_BATTLE_TEST("Ally Switch does not update Healing Wish/Lunar Dance recovery position") +{ + u16 move = MOVE_NONE; + struct BattlePokemon *switchTarget = NULL; + + PARAMETRIZE { move = MOVE_HEALING_WISH; switchTarget = playerLeft; } + PARAMETRIZE { move = MOVE_HEALING_WISH; switchTarget = playerRight; } + PARAMETRIZE { move = MOVE_LUNAR_DANCE; switchTarget = playerLeft; } + PARAMETRIZE { move = MOVE_LUNAR_DANCE; switchTarget = playerRight; } + + GIVEN { + WITH_CONFIG(B_HEALING_WISH_SWITCH, GEN_8); + ASSUME(GetMoveEffect(MOVE_HEALING_WISH) == EFFECT_HEALING_WISH); + ASSUME(GetMoveEffect(MOVE_LUNAR_DANCE) == EFFECT_LUNAR_DANCE); + PLAYER(SPECIES_GARDEVOIR); + PLAYER(SPECIES_ABRA); + PLAYER(SPECIES_WOBBUFFET) { HP(100); MaxHP(100); } + PLAYER(SPECIES_WYNAUT) { HP(50); MaxHP(80); Status1(STATUS1_PARALYSIS); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, move); SEND_OUT(playerLeft, 2); } + TURN { MOVE(playerRight, MOVE_ALLY_SWITCH); } + TURN { SWITCH(switchTarget, 3); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, move, playerLeft); + if (switchTarget == playerLeft) { + HP_BAR(playerLeft, hp: 80); + STATUS_ICON(playerLeft, none: TRUE); + } else { + NOT HP_BAR(playerRight); + } + } THEN { + if (switchTarget == playerLeft) { + EXPECT_EQ(playerLeft->hp, 80); + EXPECT_EQ(playerLeft->status1, STATUS1_NONE); + } else { + EXPECT_EQ(playerRight->hp, 50); + EXPECT_EQ(playerRight->status1, STATUS1_PARALYSIS); + } + } +} + DOUBLE_BATTLE_TEST("Ally Switch updates attract battler") { GIVEN { diff --git a/test/battle/move_effect/assist.c b/test/battle/move_effect/assist.c index bcf362e266..2414b8da21 100644 --- a/test/battle/move_effect/assist.c +++ b/test/battle/move_effect/assist.c @@ -12,7 +12,6 @@ TO_DO_BATTLE_TEST("Assist can call moves with no PP left"); TO_DO_BATTLE_TEST("Assist can call moves from a fainted party member"); TO_DO_BATTLE_TEST("Assist can call moves that are blocked to its partners"); // Eg. double battle parter blocked by Disable TO_DO_BATTLE_TEST("Assist can only call the original moves of a Transformed partner (Gen4 only)"); -TO_DO_BATTLE_TEST("Assist can only call the current moves of a Transformed partner (Gen5+)"); TO_DO_BATTLE_TEST("Assist cannot call a Mimicked move (Gen4 only)"); TO_DO_BATTLE_TEST("Assist can call a Mimicked move but not the original Mimic (Gen5+)"); TO_DO_BATTLE_TEST("Assist can call moves in unhatched Eggs (Gen5 only)"); @@ -57,3 +56,25 @@ SINGLE_BATTLE_TEST("Assisted move triggers correct weakness berry") ANIMATION(ANIM_TYPE_MOVE, MOVE_SURF, player); } } + +DOUBLE_BATTLE_TEST("Assist can only call the current moves of a Transformed partner (Gen5+)") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM); + PLAYER(SPECIES_WOBBUFFET) { Speed(3); Moves(MOVE_ASSIST); } + PLAYER(SPECIES_DITTO) { Speed(4); Moves(MOVE_TRANSFORM); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(2); Moves(MOVE_SCRATCH); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(1); } + } WHEN { + TURN { + MOVE(playerRight, MOVE_TRANSFORM, target: opponentLeft); + MOVE(playerLeft, MOVE_ASSIST); + MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRANSFORM, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSIST, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft); + } +} diff --git a/test/battle/move_effect/psyblade.c b/test/battle/move_effect/psyblade.c new file mode 100644 index 0000000000..044597ccc5 --- /dev/null +++ b/test/battle/move_effect/psyblade.c @@ -0,0 +1,38 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_PSYBLADE) == EFFECT_TERRAIN_BOOST); +} + +SINGLE_BATTLE_TEST("Psyblade's power increases by 50% on Electric Terrain even if the user is not grounded", s16 damage) +{ + bool32 terrain, airBalloon; + + PARAMETRIZE { terrain = FALSE; airBalloon = FALSE; } + PARAMETRIZE { terrain = TRUE; airBalloon = FALSE; } + PARAMETRIZE { terrain = FALSE; airBalloon = TRUE; } + PARAMETRIZE { terrain = TRUE; airBalloon = TRUE; } + + GIVEN { + ASSUME(gItemsInfo[ITEM_AIR_BALLOON].holdEffect == HOLD_EFFECT_AIR_BALLOON); + ASSUME(GetMoveEffect(MOVE_ELECTRIC_TERRAIN) == EFFECT_ELECTRIC_TERRAIN); + ASSUME(GetMoveTerrainBoost_Terrain(MOVE_PSYBLADE) == STATUS_FIELD_ELECTRIC_TERRAIN); + ASSUME(GetMoveTerrainBoost_GroundCheck(MOVE_PSYBLADE) == GROUND_CHECK_NONE); + PLAYER(SPECIES_SLOWKING) { Moves(MOVE_ELECTRIC_TERRAIN, MOVE_PSYBLADE); Item(airBalloon ? ITEM_AIR_BALLOON : ITEM_NONE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (terrain) + TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); } + TURN { MOVE(player, MOVE_PSYBLADE); } + } SCENE { + if (terrain) + ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYBLADE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + EXPECT_MUL_EQ(results[2].damage, Q_4_12(1.5), results[3].damage); + } +} diff --git a/test/battle/move_effect/sucker_punch.c b/test/battle/move_effect/sucker_punch.c index 0fd2273607..039e9541ab 100644 --- a/test/battle/move_effect/sucker_punch.c +++ b/test/battle/move_effect/sucker_punch.c @@ -1,6 +1,11 @@ #include "global.h" #include "test/battle.h" +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_SUCKER_PUNCH) == EFFECT_SUCKER_PUNCH); +} + SINGLE_BATTLE_TEST("Sucker Punch hits targets that are about to attack") { GIVEN { @@ -34,6 +39,25 @@ SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets using status moves") } } +SINGLE_BATTLE_TEST("Sucker Punch hits targets using Me First") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_ME_FIRST) == EFFECT_ME_FIRST); + ASSUME(GetMovePriority(MOVE_SUCKER_PUNCH) > GetMovePriority(MOVE_ME_FIRST)); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUCKER_PUNCH); MOVE(opponent, MOVE_ME_FIRST); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, player); + HP_BAR(opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ME_FIRST, opponent); + HP_BAR(player); + } + } +} + SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets that has already moved") { GIVEN { diff --git a/test/battle/move_effect/tickle.c b/test/battle/move_effect/tickle.c index 3a878868fd..8513340f4b 100644 --- a/test/battle/move_effect/tickle.c +++ b/test/battle/move_effect/tickle.c @@ -21,3 +21,20 @@ SINGLE_BATTLE_TEST("Tickle reduces the target's Attack and Defense by 1 stage ea EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); } } + +SINGLE_BATTLE_TEST("Tickle is blocked by Substitute (Gen4+)") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_TICKLE, MOVE_CELEBRATE); Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); Speed(10); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(player, MOVE_TICKLE); MOVE(opponent, MOVE_CELEBRATE); } + } SCENE { + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_TICKLE, player); + MESSAGE("But it failed!"); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE); + } +} diff --git a/test/battle/move_effect/venom_drench.c b/test/battle/move_effect/venom_drench.c index 00dac65858..aa09061f22 100644 --- a/test/battle/move_effect/venom_drench.c +++ b/test/battle/move_effect/venom_drench.c @@ -1,4 +1,42 @@ #include "global.h" #include "test/battle.h" -TO_DO_BATTLE_TEST("TODO: Write Venom Drench (Move Effect) test titles") +ASSUMPTIONS +{ + ASSUME(GetMoveEffect(MOVE_VENOM_DRENCH) == EFFECT_VENOM_DRENCH); +} + +SINGLE_BATTLE_TEST("Venom Drench lowers stats of a poisoned target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_VENOM_DRENCH); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); } + } WHEN { + TURN { MOVE(player, MOVE_VENOM_DRENCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOM_DRENCH, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE - 1); + } +} + +SINGLE_BATTLE_TEST("Venom Drench is blocked by Substitute") +{ + GIVEN { + ASSUME(GetMoveEffect(MOVE_SUBSTITUTE) == EFFECT_SUBSTITUTE); + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_VENOM_DRENCH, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_SUBSTITUTE, MOVE_CELEBRATE); Status1(STATUS1_POISON); } + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_CELEBRATE); } + TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_VENOM_DRENCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); + NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_VENOM_DRENCH, player); + } THEN { + EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE); + EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE); + } +}