mirror of
https://github.com/rh-hideout/pokeemerald-expansion
synced 2025-10-05 23:42:46 +02:00
11/08/25 Master to upcoming merge
This commit is contained in:
@@ -179,6 +179,6 @@ If you targeted a specific version that is not the latest version listed on the
|
||||
# Useful additional tools
|
||||
|
||||
* [porymap](https://github.com/huderlem/porymap) for viewing and editing maps
|
||||
* [porytiles](https://github.com/gruntlucas/porytiles) for add new metatiles for maps
|
||||
* [porytiles](https://github.com/grunt-lucas/porytiles) for add new metatiles for maps
|
||||
* [poryscript](https://github.com/huderlem/poryscript) for scripting ([VS Code extension](https://marketplace.visualstudio.com/items?itemName=karathan.poryscript))
|
||||
* [Tilemap Studio](https://github.com/Rangi42/tilemap-studio) for viewing and editing tilemaps
|
||||
|
@@ -5622,6 +5622,7 @@ BattleScript_LeechSeedTurnDrainHealBlock::
|
||||
BattleScript_LeechSeedTurnDrainRecovery::
|
||||
call BattleScript_LeechSeedTurnDrain
|
||||
BattleScript_LeechSeedTurnDrainGainHp:
|
||||
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE
|
||||
healthbarupdate BS_TARGET
|
||||
datahpupdate BS_TARGET
|
||||
printfromtable gLeechSeedStringIds
|
||||
|
@@ -527,9 +527,9 @@ Edit [src/data/graphics/pokemon.h](https://github.com/rh-hideout/pokeemerald-exp
|
||||
```diff
|
||||
#if P_FAMILY_PECHARUNT
|
||||
const u32 gMonFrontPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/front.4bpp.lz");
|
||||
const u32 gMonPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/normal.gbapal.lz");
|
||||
const u16 gMonPalette_Pecharunt[] = INCBIN_U16("graphics/pokemon/pecharunt/normal.gbapal");
|
||||
const u32 gMonBackPic_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/back.4bpp.lz");
|
||||
const u32 gMonShinyPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/shiny.gbapal.lz");
|
||||
const u16 gMonShinyPalette_Pecharunt[] = INCBIN_U16("graphics/pokemon/pecharunt/shiny.gbapal");
|
||||
const u8 gMonIcon_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/icon.4bpp");
|
||||
#if P_FOOTPRINTS
|
||||
const u8 gMonFootprint_Pecharunt[] = INCBIN_U8("graphics/pokemon/pecharunt/footprint.1bpp");
|
||||
@@ -537,20 +537,20 @@ Edit [src/data/graphics/pokemon.h](https://github.com/rh-hideout/pokeemerald-exp
|
||||
#if OW_POKEMON_OBJECT_EVENTS
|
||||
const u32 gObjectEventPic_Pecharunt[] = INCBIN_COMP("graphics/pokemon/pecharunt/overworld.4bpp");
|
||||
#if OW_PKMN_OBJECTS_SHARE_PALETTES == FALSE
|
||||
const u32 gOverworldPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/overworld_normal.gbapal.lz");
|
||||
const u32 gShinyOverworldPalette_Pecharunt[] = INCBIN_U32("graphics/pokemon/pecharunt/overworld_shiny.gbapal.lz");
|
||||
const u16 gOverworldPalette_Pecharunt[] = INCBIN_U16("graphics/pokemon/pecharunt/overworld_normal.gbapal");
|
||||
const u16 gShinyOverworldPalette_Pecharunt[] = INCBIN_U16("graphics/pokemon/pecharunt/overworld_shiny.gbapal");
|
||||
#endif //OW_PKMN_OBJECTS_SHARE_PALETTES
|
||||
#endif //OW_POKEMON_OBJECT_EVENTS
|
||||
#endif //P_FAMILY_PECHARUNT
|
||||
|
||||
const u32 gMonFrontPic_Egg[] = INCBIN_U32("graphics/pokemon/egg/anim_front.4bpp.lz");
|
||||
const u32 gMonPalette_Egg[] = INCBIN_U32("graphics/pokemon/egg/normal.gbapal.lz");
|
||||
const u16 gMonPalette_Egg[] = INCBIN_U16("graphics/pokemon/egg/normal.gbapal");
|
||||
const u8 gMonIcon_Egg[] = INCBIN_U8("graphics/pokemon/egg/icon.4bpp");
|
||||
|
||||
+ const u32 gMonFrontPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/anim_front.4bpp.lz");
|
||||
+ const u32 gMonBackPic_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/back.4bpp.lz");
|
||||
+ const u32 gMonPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/normal.gbapal.lz");
|
||||
+ const u32 gMonShinyPalette_Mewthree[] = INCBIN_U32("graphics/pokemon/mewthree/shiny.gbapal.lz");
|
||||
+ const u16 gMonPalette_Mewthree[] = INCBIN_U16("graphics/pokemon/mewthree/normal.gbapal");
|
||||
+ const u16 gMonShinyPalette_Mewthree[] = INCBIN_U16("graphics/pokemon/mewthree/shiny.gbapal");
|
||||
+ const u8 gMonIcon_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/icon.4bpp");
|
||||
+ const u8 gMonFootprint_Mewthree[] = INCBIN_U8("graphics/pokemon/mewthree/footprint.1bpp");
|
||||
```
|
||||
|
@@ -166,7 +166,8 @@ struct ProtectStruct
|
||||
u16 activateOpportunist:2; // 2 - to copy stats. 1 - stats copied (do not repeat). 0 - no stats to copy
|
||||
u16 usedAllySwitch:1;
|
||||
u16 lashOutAffected:1;
|
||||
u16 padding:4;
|
||||
u16 assuranceDoubled:1;
|
||||
u16 padding:3;
|
||||
// End of 16-bit bitfield
|
||||
u16 physicalDmg;
|
||||
u16 specialDmg;
|
||||
|
@@ -3,44 +3,41 @@
|
||||
|
||||
// AI Flags. Most run specific functions to update score, new flags are used for internal logic in other scripts
|
||||
// See docs/ai_flags.md for more details.
|
||||
#define AI_FLAG_CHECK_BAD_MOVE (1 << 0) // AI will avoid using moves that are likely to fail or be ineffective in the current situation.
|
||||
#define AI_FLAG_TRY_TO_FAINT (1 << 1) // AI will prioritize KOing the player's mon if able.
|
||||
#define AI_FLAG_CHECK_VIABILITY (1 << 2) // AI damaging moves and move effects to determine the best available move in the current situation.
|
||||
#define AI_FLAG_FORCE_SETUP_FIRST_TURN (1 << 3) // AI will prioritize using setup moves on the first turn at the expensve of all else. AI_FLAG_CHECK_VIABILITY will instead do this when the AI determines it makes sense.
|
||||
#define AI_FLAG_RISKY (1 << 4) // AI will generally behave more recklessly, prioritizing damage over accuracy, explosions, etc.
|
||||
#define AI_FLAG_TRY_TO_2HKO (1 << 5) // AI adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.
|
||||
#define AI_FLAG_PREFER_BATON_PASS (1 << 6) // AI prefers raising its own stats and setting for / using Baton Pass.
|
||||
#define AI_FLAG_DOUBLE_BATTLE (1 << 7) // Automatically set for double battles, handles AI behaviour with partner.
|
||||
#define AI_FLAG_HP_AWARE (1 << 8) // AI will favour certain move effects based on how much remaining HP it and the player's mon have.
|
||||
#define AI_FLAG_POWERFUL_STATUS (1 << 9) // AI prefers moves that set up field effects or side statuses, even if the user can faint the target.
|
||||
#define AI_FLAG(x) ((u64)1 << x)
|
||||
|
||||
#define AI_FLAG_CHECK_BAD_MOVE AI_FLAG(0) // AI will avoid using moves that are likely to fail or be ineffective in the current situation.
|
||||
#define AI_FLAG_TRY_TO_FAINT AI_FLAG(1) // AI will prioritize KOing the player's mon if able.
|
||||
#define AI_FLAG_CHECK_VIABILITY AI_FLAG(2) // AI damaging moves and move effects to determine the best available move in the current situation.
|
||||
#define AI_FLAG_FORCE_SETUP_FIRST_TURN AI_FLAG(3) // AI will prioritize using setup moves on the first turn at the expensve of all else. AI_FLAG_CHECK_VIABILITY will instead do this when the AI determines it makes sense.
|
||||
#define AI_FLAG_RISKY AI_FLAG(4) // AI will generally behave more recklessly, prioritizing damage over accuracy, explosions, etc.
|
||||
#define AI_FLAG_TRY_TO_2HKO AI_FLAG(5) // AI adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.
|
||||
#define AI_FLAG_PREFER_BATON_PASS AI_FLAG(6) // AI prefers raising its own stats and setting for / using Baton Pass.
|
||||
#define AI_FLAG_DOUBLE_BATTLE AI_FLAG(7) // Automatically set for double battles, handles AI behaviour with partner.
|
||||
#define AI_FLAG_HP_AWARE AI_FLAG(8) // AI will favour certain move effects based on how much remaining HP it and the player's mon have.
|
||||
#define AI_FLAG_POWERFUL_STATUS AI_FLAG(9) // AI prefers moves that set up field effects or side statuses, even if the user can faint the target.
|
||||
// New, Trainer Handicap Flags
|
||||
#define AI_FLAG_NEGATE_UNAWARE (1 << 10) // AI is NOT aware of negating effects like wonder room, mold breaker, etc.
|
||||
#define AI_FLAG_WILL_SUICIDE (1 << 11) // AI will use explosion / self destruct / final gambit / etc.
|
||||
#define AI_FLAG_NEGATE_UNAWARE AI_FLAG(10) // AI is NOT aware of negating effects like wonder room, mold breaker, etc.
|
||||
#define AI_FLAG_WILL_SUICIDE AI_FLAG(11) // AI will use explosion / self destruct / final gambit / etc.
|
||||
// New, Trainer Strategy Flags
|
||||
#define AI_FLAG_PREFER_STATUS_MOVES (1 << 12) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves.
|
||||
#define AI_FLAG_STALL (1 << 13) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished.
|
||||
#define AI_FLAG_SMART_SWITCHING (1 << 14) // AI includes a lot more switching checks. Automatically includes AI_FLAG_SMART_MON_CHOICES.
|
||||
#define AI_FLAG_ACE_POKEMON (1 << 15) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining.
|
||||
#define AI_FLAG_OMNISCIENT (1 << 16) // AI has full knowledge of player moves, abilities, hold items.
|
||||
#define AI_FLAG_SMART_MON_CHOICES (1 << 17) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Automatically included by AI_FLAG_SMART_SWITCHING.
|
||||
#define AI_FLAG_CONSERVATIVE (1 << 18) // AI assumes all moves will low roll damage.
|
||||
#define AI_FLAG_SEQUENCE_SWITCHING (1 << 19) // AI switches in mons in exactly party order, and never switches mid-battle.
|
||||
#define AI_FLAG_DOUBLE_ACE_POKEMON (1 << 20) // AI has *two* Ace Pokémon. The last two Pokémons in the party won't be used unless they're the last ones remaining. Goes well in battles where the trainer ID equals to twins, couples, etc.
|
||||
#define AI_FLAG_WEIGH_ABILITY_PREDICTION (1 << 21) // AI will predict player's ability based on aiRating
|
||||
#define AI_FLAG_PREFER_HIGHEST_DAMAGE_MOVE (1 << 22) // AI adds score to highest damage move regardless of accuracy or secondary effect
|
||||
#define AI_FLAG_PREDICT_SWITCH (1 << 23) // AI will predict the player's switches and switchins based on how it would handle the situation. Recommend using AI_FLAG_OMNISCIENT
|
||||
#define AI_FLAG_PREDICT_INCOMING_MON (1 << 24) // AI will score against the predicting incoming mon if it predicts the player to switch. Requires AI_FLAG_PREDICT_SWITCH
|
||||
#define AI_FLAG_PP_STALL_PREVENTION (1 << 25) // AI keeps track of the player's switches where the incoming mon is immune to the chosen move
|
||||
#define AI_FLAG_PREDICT_MOVE (1 << 26) // AI will predict the player's move based on what move it would use in the same situation. Recommend using AI_FLAG_OMNISCIENT
|
||||
#define AI_FLAG_SMART_TERA (1 << 27) // AI will make smarter decisions when choosing whether to terrastalize (default is to always tera whenever available).
|
||||
#define AI_FLAG_ASSUME_STAB (1 << 28) // AI knows player's STAB moves, but nothing else. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
#define AI_FLAG_ASSUME_STATUS_MOVES (1 << 29) // AI has a chance to know certain non-damaging moves, and also Fake Out and Super Fang. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
#define AI_FLAG_ATTACKS_PARTNER (1 << 30) // AI specific to double battles; AI can deliberately attack its 'partner.'
|
||||
|
||||
#define AI_FLAG_COUNT 31
|
||||
|
||||
// Flags at and after 32 need different formatting, as in
|
||||
// #define AI_FLAG_PLACEHOLDER ((u64)1 << 32)
|
||||
#define AI_FLAG_PREFER_STATUS_MOVES AI_FLAG(12) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves.
|
||||
#define AI_FLAG_STALL AI_FLAG(13) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished.
|
||||
#define AI_FLAG_SMART_SWITCHING AI_FLAG(14) // AI includes a lot more switching checks. Automatically includes AI_FLAG_SMART_MON_CHOICES.
|
||||
#define AI_FLAG_ACE_POKEMON AI_FLAG(15) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining.
|
||||
#define AI_FLAG_OMNISCIENT AI_FLAG(16) // AI has full knowledge of player moves, abilities, hold items.
|
||||
#define AI_FLAG_SMART_MON_CHOICES AI_FLAG(17) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Automatically included by AI_FLAG_SMART_SWITCHING.
|
||||
#define AI_FLAG_CONSERVATIVE AI_FLAG(18) // AI assumes all moves will low roll damage.
|
||||
#define AI_FLAG_SEQUENCE_SWITCHING AI_FLAG(19) // AI switches in mons in exactly party order, and never switches mid-battle.
|
||||
#define AI_FLAG_DOUBLE_ACE_POKEMON AI_FLAG(20) // AI has *two* Ace Pokémon. The last two Pokémons in the party won't be used unless they're the last ones remaining. Goes well in battles where the trainer ID equals to twins, couples, etc.
|
||||
#define AI_FLAG_WEIGH_ABILITY_PREDICTION AI_FLAG(21) // AI will predict player's ability based on aiRating
|
||||
#define AI_FLAG_PREFER_HIGHEST_DAMAGE_MOVE AI_FLAG(22) // AI adds score to highest damage move regardless of accuracy or secondary effect
|
||||
#define AI_FLAG_PREDICT_SWITCH AI_FLAG(23) // AI will predict the player's switches and switchins based on how it would handle the situation. Recommend using AI_FLAG_OMNISCIENT
|
||||
#define AI_FLAG_PREDICT_INCOMING_MON AI_FLAG(24) // AI will score against the predicting incoming mon if it predicts the player to switch. Requires AI_FLAG_PREDICT_SWITCH
|
||||
#define AI_FLAG_PP_STALL_PREVENTION AI_FLAG(25) // AI keeps track of the player's switches where the incoming mon is immune to the chosen move
|
||||
#define AI_FLAG_PREDICT_MOVE AI_FLAG(26) // AI will predict the player's move based on what move it would use in the same situation. Recommend using AI_FLAG_OMNISCIENT
|
||||
#define AI_FLAG_SMART_TERA AI_FLAG(27) // AI will make smarter decisions when choosing whether to terrastalize (default is to always tera whenever available).
|
||||
#define AI_FLAG_ASSUME_STAB AI_FLAG(28) // AI knows player's STAB moves, but nothing else. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
#define AI_FLAG_ASSUME_STATUS_MOVES AI_FLAG(29) // AI has a chance to know certain non-damaging moves, and also Fake Out and Super Fang. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
#define AI_FLAG_ATTACKS_PARTNER AI_FLAG(30) // AI specific to double battles; AI can deliberately attack its 'partner.'
|
||||
|
||||
// The following options are enough to have a basic/smart trainer. Any other addtion could make the trainer worse/better depending on the flag
|
||||
#define AI_FLAG_BASIC_TRAINER (AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY)
|
||||
@@ -49,10 +46,10 @@
|
||||
#define AI_FLAG_ASSUMPTIONS (AI_FLAG_ASSUME_STAB | AI_FLAG_ASSUME_STATUS_MOVES | AI_FLAG_WEIGH_ABILITY_PREDICTION)
|
||||
|
||||
// 'other' ai logic flags
|
||||
#define AI_FLAG_DYNAMIC_FUNC ((u64)1 << 60) // Create custom AI functions for specific battles via "setdynamicaifunc" cmd
|
||||
#define AI_FLAG_ROAMING ((u64)1 << 61)
|
||||
#define AI_FLAG_SAFARI ((u64)1 << 62)
|
||||
#define AI_FLAG_FIRST_BATTLE ((u64)1 << 63)
|
||||
#define AI_FLAG_DYNAMIC_FUNC AI_FLAG(60) // Create custom AI functions for specific battles via "setdynamicaifunc" cmd
|
||||
#define AI_FLAG_ROAMING AI_FLAG(61)
|
||||
#define AI_FLAG_SAFARI AI_FLAG(62)
|
||||
#define AI_FLAG_FIRST_BATTLE AI_FLAG(63)
|
||||
|
||||
#define AI_SCORE_DEFAULT 100 // Default score for all AI moves.
|
||||
|
||||
|
@@ -20,6 +20,14 @@ enum GenConfigTag
|
||||
GEN_CONFIG_DEFIANT_STICKY_WEB,
|
||||
GEN_CONFIG_ENCORE_TARGET,
|
||||
GEN_CONFIG_TIME_OF_DAY_HEALING_MOVES,
|
||||
GEN_PICKUP_WILD,
|
||||
GEN_PROTEAN_LIBERO,
|
||||
GEN_INTREPID_SWORD,
|
||||
GEN_DAUNTLESS_SHIELD,
|
||||
GEN_ILLUMINATE_EFFECT,
|
||||
GEN_STEAL_WILD_ITEMS,
|
||||
GEN_SNOW_WARNING,
|
||||
GEN_ALLY_SWITCH_FAIL_CHANCE,
|
||||
GEN_CONFIG_COUNT
|
||||
};
|
||||
|
||||
|
@@ -37,6 +37,7 @@ bool8 FieldEffectCmd_loadgfx_callnative(u8 **script, u32 *val);
|
||||
bool8 FieldEffectCmd_loadtiles_callnative(u8 **script, u32 *val);
|
||||
bool8 FieldEffectCmd_loadfadedpal_callnative(u8 **script, u32 *val);
|
||||
void FieldCB_FallWarpExit(void);
|
||||
void HideFollowerForFieldEffect(void);
|
||||
void StartEscalatorWarp(u8 metatileBehavior, u8 priority);
|
||||
void StartLavaridgeGymB1FWarp(u8 priority);
|
||||
void StartLavaridgeGym1FWarp(u8 priority);
|
||||
|
@@ -21,6 +21,7 @@ enum FollowerNPCDataTypes
|
||||
FNPC_DATA_WARP_END,
|
||||
FNPC_DATA_SURF_BLOB,
|
||||
FNPC_DATA_COME_OUT_DOOR,
|
||||
FNPC_DATA_FORCED_MOVEMENT,
|
||||
FNPC_DATA_OBJ_ID,
|
||||
FNPC_DATA_CURRENT_SPRITE,
|
||||
FNPC_DATA_DELAYED_STATE,
|
||||
@@ -60,7 +61,8 @@ enum FollowerNPCSurfBlobStates
|
||||
FNPC_SURF_BLOB_DESTROY
|
||||
};
|
||||
|
||||
enum FollowerNPCOutOfDoorTaskStates{
|
||||
enum FollowerNPCOutOfDoorTaskStates
|
||||
{
|
||||
OPEN_DOOR,
|
||||
NPC_WALK_OUT,
|
||||
CLOSE_DOOR,
|
||||
@@ -68,7 +70,8 @@ enum FollowerNPCOutOfDoorTaskStates{
|
||||
REALLOW_MOVEMENT
|
||||
};
|
||||
|
||||
enum FollowerNPCHandleEscalatorFinishTaskStates{
|
||||
enum FollowerNPCHandleEscalatorFinishTaskStates
|
||||
{
|
||||
MOVE_TO_PLAYER_POS,
|
||||
WAIT_FOR_PLAYER_MOVE,
|
||||
SHOW_FOLLOWER_DOWN,
|
||||
|
@@ -23,6 +23,14 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
|
||||
[GEN_CONFIG_DEFIANT_STICKY_WEB] = B_DEFIANT_STICKY_WEB,
|
||||
[GEN_CONFIG_ENCORE_TARGET] = B_ENCORE_TARGET,
|
||||
[GEN_CONFIG_TIME_OF_DAY_HEALING_MOVES] = B_TIME_OF_DAY_HEALING_MOVES,
|
||||
[GEN_PICKUP_WILD] = B_PICKUP_WILD,
|
||||
[GEN_PROTEAN_LIBERO] = B_PROTEAN_LIBERO,
|
||||
[GEN_INTREPID_SWORD] = B_INTREPID_SWORD,
|
||||
[GEN_DAUNTLESS_SHIELD] = B_DAUNTLESS_SHIELD,
|
||||
[GEN_ILLUMINATE_EFFECT] = B_ILLUMINATE_EFFECT,
|
||||
[GEN_STEAL_WILD_ITEMS] = B_STEAL_WILD_ITEMS,
|
||||
[GEN_SNOW_WARNING] = B_SNOW_WARNING,
|
||||
[GEN_ALLY_SWITCH_FAIL_CHANCE] = B_ALLY_SWITCH_FAIL_CHANCE,
|
||||
};
|
||||
|
||||
#if TESTING
|
||||
|
@@ -219,7 +219,8 @@ struct NPCFollower
|
||||
u8 inProgress:1;
|
||||
u8 warpEnd:1;
|
||||
u8 createSurfBlob:3;
|
||||
u8 comeOutDoorStairs:3;
|
||||
u8 comeOutDoorStairs:2;
|
||||
u8 forcedMovement:1;
|
||||
u8 objId;
|
||||
u8 currentSprite;
|
||||
u8 delayedState;
|
||||
|
@@ -16,10 +16,12 @@ struct PartyMenu
|
||||
s8 slotId2;
|
||||
u8 action;
|
||||
u16 bagItem;
|
||||
s16 data1; // used variously as a move, counter, moveSlotId, or cursorPos
|
||||
s16 data1; // used variously as a move, counter, moveSlotId, cursorPos, or indicator that the menu is opened from the field
|
||||
s16 learnMoveState; // data2, used only as a learn move state
|
||||
};
|
||||
|
||||
#define DATA1_PARTY_MENU_FROM_FIELD -1
|
||||
|
||||
extern struct PartyMenu gPartyMenu;
|
||||
extern bool8 gPartyMenuUseExitCallback;
|
||||
extern u8 gSelectedMonPartyId;
|
||||
|
@@ -12,10 +12,10 @@ TOOLDIRS := $(TOOL_NAMES:%=$(TOOLS_DIR)/%)
|
||||
CHECKTOOLDIRS := $(CHECK_TOOL_NAMES:%=$(TOOLS_DIR)/%)
|
||||
|
||||
# Tool making doesnt require a pokeemerald dependency scan.
|
||||
RULES_NO_SCAN += tools check-tools clean-tools clean-check-tools $(TOOLDIRS) $(CHECKTOOLDIRS)
|
||||
RULES_NO_SCAN += tools check-tools clean-tools clean-check-tools history $(TOOLDIRS) $(CHECKTOOLDIRS)
|
||||
.PHONY: $(RULES_NO_SCAN)
|
||||
|
||||
tools: $(TOOLDIRS)
|
||||
tools: history $(TOOLDIRS)
|
||||
|
||||
check-tools: $(CHECKTOOLDIRS)
|
||||
|
||||
@@ -30,3 +30,6 @@ clean-tools:
|
||||
|
||||
clean-check-tools:
|
||||
@$(foreach tooldir,$(CHECKTOOLDIRS),$(MAKE) clean -C $(tooldir);)
|
||||
|
||||
history:
|
||||
@$(SHELL) ./check_history.sh
|
||||
|
@@ -717,7 +717,7 @@ static bool32 ShouldSwitchIfBadlyStatused(u32 battler)
|
||||
&& gAiLogicData->abilities[opposingBattler] != ABILITY_UNAWARE
|
||||
&& gAiLogicData->abilities[opposingBattler] != ABILITY_KEEN_EYE
|
||||
&& gAiLogicData->abilities[opposingBattler] != ABILITY_MINDS_EYE
|
||||
&& (B_ILLUMINATE_EFFECT >= GEN_9 && gAiLogicData->abilities[opposingBattler] != ABILITY_ILLUMINATE)
|
||||
&& (GetGenConfig(GEN_ILLUMINATE_EFFECT) >= GEN_9 && gAiLogicData->abilities[opposingBattler] != ABILITY_ILLUMINATE)
|
||||
&& !gBattleMons[battler].volatiles.foresight
|
||||
&& !(gStatuses3[battler] & STATUS3_MIRACLE_EYED))
|
||||
switchMon = FALSE;
|
||||
|
@@ -2007,7 +2007,7 @@ bool32 CanLowerStat(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData,
|
||||
if (stat == STAT_DEF)
|
||||
return FALSE;
|
||||
case ABILITY_ILLUMINATE:
|
||||
if (B_ILLUMINATE_EFFECT < GEN_9)
|
||||
if (GetGenConfig(GEN_ILLUMINATE_EFFECT) < GEN_9)
|
||||
break;
|
||||
case ABILITY_KEEN_EYE:
|
||||
case ABILITY_MINDS_EYE:
|
||||
|
@@ -1265,8 +1265,8 @@ void SpriteCB_EnemyShadow(struct Sprite *shadowSprite)
|
||||
}
|
||||
else if (transformSpecies != SPECIES_NONE)
|
||||
{
|
||||
xOffset = gSpeciesInfo[transformSpecies].enemyShadowXOffset;
|
||||
yOffset = gSpeciesInfo[transformSpecies].enemyShadowYOffset;
|
||||
xOffset = gSpeciesInfo[transformSpecies].enemyShadowXOffset + (shadowSprite->tSpriteSide == SPRITE_SIDE_LEFT ? -16 : 16);
|
||||
yOffset = gSpeciesInfo[transformSpecies].enemyShadowYOffset + 16;
|
||||
size = gSpeciesInfo[transformSpecies].enemyShadowSize;
|
||||
|
||||
invisible = (B_ENEMY_MON_SHADOW_STYLE >= GEN_4 && P_GBA_STYLE_SPECIES_GFX == FALSE)
|
||||
|
@@ -486,10 +486,9 @@ static void CB2_InitBattleInternal(void)
|
||||
else
|
||||
{
|
||||
gBattle_WIN0V = WIN_RANGE(DISPLAY_HEIGHT / 2, DISPLAY_HEIGHT / 2 + 1);
|
||||
ScanlineEffect_Clear();
|
||||
if (B_FAST_INTRO_NO_SLIDE == FALSE && !gTestRunnerHeadless)
|
||||
{
|
||||
ScanlineEffect_Clear();
|
||||
|
||||
for (i = 0; i < DISPLAY_HEIGHT / 2; i++)
|
||||
{
|
||||
gScanlineEffectRegBuffers[0][i] = 0xF0;
|
||||
|
@@ -1962,6 +1962,9 @@ static void Cmd_adjustdamage(void)
|
||||
gBattleStruct->moveDamage[battlerDef] = gBattleMons[battlerDef].hp - 1;
|
||||
gSpecialStatuses[battlerDef].enduredDamage = TRUE;
|
||||
}
|
||||
|
||||
if (gSpecialStatuses[battlerDef].enduredDamage)
|
||||
gProtectStructs[battlerDef].assuranceDoubled = TRUE;
|
||||
}
|
||||
|
||||
if (calcSpreadMoveDamage)
|
||||
@@ -2410,6 +2413,7 @@ static void Cmd_datahpupdate(void)
|
||||
gProtectStructs[battler].physicalBattlerId = gBattlerAttacker;
|
||||
else
|
||||
gProtectStructs[battler].physicalBattlerId = gBattlerTarget;
|
||||
gProtectStructs[battler].assuranceDoubled = TRUE;
|
||||
}
|
||||
else if (!IsBattleMovePhysical(gCurrentMove) && !(gHitMarker & HITMARKER_PASSIVE_DAMAGE) && effect != EFFECT_PAIN_SPLIT)
|
||||
{
|
||||
@@ -2420,6 +2424,7 @@ static void Cmd_datahpupdate(void)
|
||||
gProtectStructs[battler].specialBattlerId = gBattlerAttacker;
|
||||
else
|
||||
gProtectStructs[battler].specialBattlerId = gBattlerTarget;
|
||||
gProtectStructs[battler].assuranceDoubled = TRUE;
|
||||
}
|
||||
}
|
||||
gHitMarker &= ~HITMARKER_PASSIVE_DAMAGE;
|
||||
@@ -2816,7 +2821,7 @@ void StealTargetItem(u8 battlerStealer, u8 battlerItem)
|
||||
gLastUsedItem = gBattleMons[battlerItem].item;
|
||||
gBattleMons[battlerItem].item = ITEM_NONE;
|
||||
|
||||
if (B_STEAL_WILD_ITEMS >= GEN_9
|
||||
if (GetGenConfig(GEN_STEAL_WILD_ITEMS) >= GEN_9
|
||||
&& !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE))
|
||||
&& GetMoveEffect(gCurrentMove) == EFFECT_STEAL_ITEM
|
||||
&& battlerStealer == gBattlerAttacker) // ensure that Pickpocket isn't activating this
|
||||
@@ -3362,7 +3367,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
|
||||
if (gBattleMons[gEffectBattler].statStages[i] != DEFAULT_STAT_STAGE)
|
||||
break;
|
||||
}
|
||||
if ((gSpecialStatuses[gEffectBattler].physicalDmg || gSpecialStatuses[gEffectBattler].specialDmg) && i != NUM_BATTLE_STATS)
|
||||
if (IsBattlerTurnDamaged(gEffectBattler) && i != NUM_BATTLE_STATS)
|
||||
{
|
||||
for (i = 0; i < NUM_BATTLE_STATS; i++)
|
||||
gBattleMons[gEffectBattler].statStages[i] = DEFAULT_STAT_STAGE;
|
||||
@@ -5757,7 +5762,7 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
|
||||
{
|
||||
StealTargetItem(gBattlerAttacker, gBattlerTarget); // Attacker steals target item
|
||||
|
||||
if (!(B_STEAL_WILD_ITEMS >= GEN_9
|
||||
if (!(GetGenConfig(GEN_STEAL_WILD_ITEMS) >= GEN_9
|
||||
&& !(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_PALACE))))
|
||||
{
|
||||
gBattleMons[gBattlerAttacker].item = ITEM_NONE; // Item assigned later on with thief (see MOVEEND_CHANGED_ITEMS)
|
||||
@@ -6249,6 +6254,7 @@ static void Cmd_moveend(void)
|
||||
UpdateStallMons();
|
||||
if ((gBattleStruct->moveResultFlags[gBattlerTarget] & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE))
|
||||
|| (gBattleMons[gBattlerAttacker].volatiles.flinched)
|
||||
|| gBattleStruct->pledgeMove == TRUE // Is the battler that uses the first Pledge move in the combo
|
||||
|| gProtectStructs[gBattlerAttacker].nonVolatileStatusImmobility)
|
||||
gBattleStruct->battlerState[gBattlerAttacker].stompingTantrumTimer = 2;
|
||||
|
||||
@@ -9530,8 +9536,8 @@ static void TryResetProtectUseCounter(u32 battler)
|
||||
enum BattleMoveEffects lastEffect = GetMoveEffect(lastMove);
|
||||
if (lastMove == MOVE_UNAVAILABLE
|
||||
|| (!gBattleMoveEffects[lastEffect].usesProtectCounter
|
||||
&& ((B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9 && lastEffect != EFFECT_ALLY_SWITCH)
|
||||
|| B_ALLY_SWITCH_FAIL_CHANCE < GEN_9)))
|
||||
&& ((GetGenConfig(GEN_ALLY_SWITCH_FAIL_CHANCE) >= GEN_9 && lastEffect != EFFECT_ALLY_SWITCH)
|
||||
|| GetGenConfig(GEN_ALLY_SWITCH_FAIL_CHANCE) < GEN_9)))
|
||||
gDisableStructs[battler].protectUses = 0;
|
||||
}
|
||||
|
||||
@@ -10007,7 +10013,7 @@ void BS_RemoveStockpileCounters(void)
|
||||
{
|
||||
NATIVE_ARGS();
|
||||
|
||||
if (GetMoveEffect(gCurrentMove) == EFFECT_SWALLOW
|
||||
if (GetMoveEffect(gCurrentMove) == EFFECT_SPIT_UP
|
||||
&& gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_1ST_HIT
|
||||
&& IsBattlerAlive(gBattlerTarget))
|
||||
{
|
||||
@@ -10129,7 +10135,7 @@ static void TryPlayStatChangeAnimation(u32 battler, u32 ability, u32 stats, s32
|
||||
}
|
||||
}
|
||||
else if (!((ability == ABILITY_KEEN_EYE || ability == ABILITY_MINDS_EYE) && currStat == STAT_ACC)
|
||||
&& !(B_ILLUMINATE_EFFECT >= GEN_9 && ability == ABILITY_ILLUMINATE && currStat == STAT_ACC)
|
||||
&& !(GetGenConfig(GEN_ILLUMINATE_EFFECT) >= GEN_9 && ability == ABILITY_ILLUMINATE && currStat == STAT_ACC)
|
||||
&& !(ability == ABILITY_HYPER_CUTTER && currStat == STAT_ATK)
|
||||
&& !(ability == ABILITY_BIG_PECKS && currStat == STAT_DEF))
|
||||
{
|
||||
@@ -10298,7 +10304,7 @@ static u32 ChangeStatBuffs(u32 battler, s8 statValue, u32 statId, union StatChan
|
||||
}
|
||||
else if (!flags.certain
|
||||
&& (((battlerAbility == ABILITY_KEEN_EYE || battlerAbility == ABILITY_MINDS_EYE) && statId == STAT_ACC)
|
||||
|| (B_ILLUMINATE_EFFECT >= GEN_9 && battlerAbility == ABILITY_ILLUMINATE && statId == STAT_ACC)
|
||||
|| (GetGenConfig(GEN_ILLUMINATE_EFFECT) >= GEN_9 && battlerAbility == ABILITY_ILLUMINATE && statId == STAT_ACC)
|
||||
|| (battlerAbility == ABILITY_HYPER_CUTTER && statId == STAT_ATK)
|
||||
|| (battlerAbility == ABILITY_BIG_PECKS && statId == STAT_DEF)))
|
||||
{
|
||||
@@ -14928,7 +14934,7 @@ static void TryUpdateRoundTurnOrder(void)
|
||||
}
|
||||
|
||||
// update turn order for round users
|
||||
for (i = 0; roundUsers[i] != 0xFF && i < 3; i++)
|
||||
for (i = 0; i < 3 && roundUsers[i] != 0xFF; i++)
|
||||
{
|
||||
gBattlerByTurnOrder[currRounder] = roundUsers[i];
|
||||
gProtectStructs[roundUsers[i]].quash = TRUE; // Make it so their turn order can't be changed again
|
||||
@@ -14936,7 +14942,7 @@ static void TryUpdateRoundTurnOrder(void)
|
||||
}
|
||||
|
||||
// Update turn order for non-round users
|
||||
for (i = 0; nonRoundUsers[i] != 0xFF && i < 3; i++)
|
||||
for (i = 0; i < 3 && nonRoundUsers[i] != 0xFF; i++)
|
||||
{
|
||||
gBattlerByTurnOrder[currRounder] = nonRoundUsers[i];
|
||||
currRounder++;
|
||||
@@ -15648,7 +15654,7 @@ void BS_TryAllySwitch(void)
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
else if (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9)
|
||||
else if (GetGenConfig(GEN_ALLY_SWITCH_FAIL_CHANCE) >= GEN_9)
|
||||
{
|
||||
TryResetProtectUseCounter(gBattlerAttacker);
|
||||
if (sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] < Random())
|
||||
|
@@ -2379,7 +2379,7 @@ static enum MoveCanceller CancellerProtean(void)
|
||||
u32 moveType = GetBattleMoveType(gCurrentMove);
|
||||
if (ProteanTryChangeType(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), gCurrentMove, moveType))
|
||||
{
|
||||
if (B_PROTEAN_LIBERO == GEN_9)
|
||||
if (GetGenConfig(GEN_PROTEAN_LIBERO) >= GEN_9)
|
||||
gDisableStructs[gBattlerAttacker].usedProteanLibero = TRUE;
|
||||
PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
|
||||
gBattlerAbility = gBattlerAttacker;
|
||||
@@ -3856,12 +3856,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
}
|
||||
break;
|
||||
case ABILITY_SNOW_WARNING:
|
||||
if (B_SNOW_WARNING >= GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_SNOW, TRUE))
|
||||
if (GetGenConfig(GEN_SNOW_WARNING) >= GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_SNOW, TRUE))
|
||||
{
|
||||
BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesSnow);
|
||||
effect++;
|
||||
}
|
||||
else if (B_SNOW_WARNING < GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_HAIL, TRUE))
|
||||
else if (GetGenConfig(GEN_SNOW_WARNING) < GEN_9 && TryChangeBattleWeather(battler, BATTLE_WEATHER_HAIL, TRUE))
|
||||
{
|
||||
BattleScriptPushCursorAndCallback(BattleScript_SnowWarningActivatesHail);
|
||||
effect++;
|
||||
@@ -3959,7 +3959,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
|
||||
&& !GetBattlerPartyState(battler)->intrepidSwordBoost)
|
||||
{
|
||||
if (B_INTREPID_SWORD == GEN_9)
|
||||
if (GetGenConfig(GEN_INTREPID_SWORD) == GEN_9)
|
||||
GetBattlerPartyState(battler)->intrepidSwordBoost = TRUE;
|
||||
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
|
||||
SET_STATCHANGER(STAT_ATK, 1, FALSE);
|
||||
@@ -3971,7 +3971,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)
|
||||
&& !GetBattlerPartyState(battler)->dauntlessShieldBoost)
|
||||
{
|
||||
if (B_DAUNTLESS_SHIELD == GEN_9)
|
||||
if (GetGenConfig(GEN_DAUNTLESS_SHIELD) == GEN_9)
|
||||
GetBattlerPartyState(battler)->dauntlessShieldBoost = TRUE;
|
||||
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
|
||||
SET_STATCHANGER(STAT_DEF, 1, FALSE);
|
||||
@@ -7040,11 +7040,9 @@ u32 ItemBattleEffects(enum ItemCaseId caseID, u32 battler)
|
||||
case HOLD_EFFECT_LIFE_ORB:
|
||||
if (IsBattlerAlive(gBattlerAttacker)
|
||||
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
|
||||
&& !IsBattleMoveStatus(gCurrentMove)
|
||||
&& (IsBattlerTurnDamaged(gBattlerTarget) || !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)) // Needs the second check in case of Substitute
|
||||
&& !gProtectStructs[gBattlerAttacker].confusionSelfDmg
|
||||
&& !IsFutureSightAttackerInParty(gBattlerAttacker, gBattlerTarget, gCurrentMove)
|
||||
&& !IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD))
|
||||
&& (IsBattlerTurnDamaged(gBattlerTarget) || gBattleScripting.savedDmg > 0)
|
||||
&& !IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD)
|
||||
&& !IsFutureSightAttackerInParty(gBattlerAttacker, gBattlerTarget, gCurrentMove))
|
||||
{
|
||||
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 10;
|
||||
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
|
||||
@@ -7979,6 +7977,7 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
|
||||
|
||||
u32 i;
|
||||
u32 basePower = GetMovePower(move);
|
||||
u32 moveEffect = GetMoveEffect(move);
|
||||
u32 weight, hpFraction, speed;
|
||||
|
||||
if (GetActiveGimmick(battlerAtk) == GIMMICK_Z_MOVE)
|
||||
@@ -7987,7 +7986,7 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
|
||||
if (GetActiveGimmick(battlerAtk) == GIMMICK_DYNAMAX)
|
||||
return GetMaxMovePower(move);
|
||||
|
||||
switch (GetMoveEffect(move))
|
||||
switch (moveEffect)
|
||||
{
|
||||
case EFFECT_PLEDGE:
|
||||
if (gBattleStruct->pledgeMove)
|
||||
@@ -8060,7 +8059,7 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
|
||||
basePower = gBattleMons[battlerDef].hp * basePower / gBattleMons[battlerDef].maxHP;
|
||||
break;
|
||||
case EFFECT_ASSURANCE:
|
||||
if (gProtectStructs[battlerDef].physicalDmg != 0 || gProtectStructs[battlerDef].specialDmg != 0 || gProtectStructs[battlerDef].confusionSelfDmg)
|
||||
if (gProtectStructs[battlerDef].assuranceDoubled)
|
||||
basePower *= 2;
|
||||
break;
|
||||
case EFFECT_TRUMP_CARD:
|
||||
@@ -8146,18 +8145,15 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
|
||||
|| gDisableStructs[battlerDef].isFirstTurn == 2)
|
||||
basePower *= 2;
|
||||
break;
|
||||
case EFFECT_ROUND:
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (i != battlerAtk && IsBattlerAlive(i) && GetMoveEffect(gLastUsedMove) == EFFECT_ROUND)
|
||||
{
|
||||
basePower *= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_FUSION_COMBO:
|
||||
if (GetMoveEffect(gLastUsedMove) == EFFECT_FUSION_COMBO && move != gLastUsedMove)
|
||||
if (move == gLastUsedMove)
|
||||
break;
|
||||
// fallthrough
|
||||
case EFFECT_ROUND:
|
||||
// don't double power due to previous turn's Round/Fusion move
|
||||
if (gCurrentTurnActionNumber != 0
|
||||
&& gActionsByTurnOrder[gCurrentTurnActionNumber - 1] == B_ACTION_USE_MOVE
|
||||
&& GetMoveEffect(gLastUsedMove) == moveEffect)
|
||||
basePower *= 2;
|
||||
break;
|
||||
case EFFECT_LASH_OUT:
|
||||
@@ -10798,7 +10794,7 @@ u16 GetUsedHeldItem(u32 battler)
|
||||
bool32 CantPickupItem(u32 battler)
|
||||
{
|
||||
// Used by RandomUniformExcept() for RNG_PICKUP
|
||||
if (battler == gBattlerAttacker && (B_PICKUP_WILD < GEN_9 || gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK)))
|
||||
if (battler == gBattlerAttacker && (GetGenConfig(GEN_PICKUP_WILD) < GEN_9 || gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_LINK)))
|
||||
return TRUE;
|
||||
return !(IsBattlerAlive(battler) && GetUsedHeldItem(battler) && gBattleStruct->battlerState[battler].canPickupItem);
|
||||
}
|
||||
@@ -11700,7 +11696,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
|
||||
accStage = gBattleMons[battlerAtk].statStages[STAT_ACC];
|
||||
evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION];
|
||||
if (atkAbility == ABILITY_UNAWARE || atkAbility == ABILITY_KEEN_EYE || atkAbility == ABILITY_MINDS_EYE
|
||||
|| (B_ILLUMINATE_EFFECT >= GEN_9 && atkAbility == ABILITY_ILLUMINATE))
|
||||
|| (GetGenConfig(GEN_ILLUMINATE_EFFECT) >= GEN_9 && atkAbility == ABILITY_ILLUMINATE))
|
||||
evasionStage = DEFAULT_STAT_STAGE;
|
||||
if (MoveIgnoresDefenseEvasionStages(move))
|
||||
evasionStage = DEFAULT_STAT_STAGE;
|
||||
|
@@ -1661,7 +1661,7 @@ static bool8 FallWarpEffect_End(struct Task *task)
|
||||
#define tState data[0]
|
||||
#define tGoingUp data[1]
|
||||
|
||||
static void HideFollowerForFieldEffect(void)
|
||||
void HideFollowerForFieldEffect(void)
|
||||
{
|
||||
struct ObjectEvent *followerObj = GetFollowerObject();
|
||||
if (!followerObj || followerObj->invisible)
|
||||
|
@@ -382,8 +382,16 @@ void PlayerStep(u8 direction, u16 newKeys, u16 heldKeys)
|
||||
DoPlayerAvatarTransition();
|
||||
if (TryDoMetatileBehaviorForcedMovement() == 0)
|
||||
{
|
||||
MovePlayerAvatarUsingKeypadInput(direction, newKeys, heldKeys);
|
||||
PlayerAllowForcedMovementIfMovingSameDirection();
|
||||
if (GetFollowerNPCData(FNPC_DATA_FORCED_MOVEMENT) != FALSE)
|
||||
{
|
||||
gPlayerAvatar.preventStep = TRUE;
|
||||
CreateTask(Task_MoveNPCFollowerAfterForcedMovement, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
MovePlayerAvatarUsingKeypadInput(direction, newKeys, heldKeys);
|
||||
PlayerAllowForcedMovementIfMovingSameDirection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -510,7 +518,11 @@ static bool8 DoForcedMovement(u8 direction, void (*moveFunc)(u8))
|
||||
else
|
||||
{
|
||||
if (collision == COLLISION_LEDGE_JUMP)
|
||||
{
|
||||
SetFollowerNPCData(FNPC_DATA_FORCED_MOVEMENT, FALSE);
|
||||
PlayerJumpLedge(direction);
|
||||
}
|
||||
|
||||
playerAvatar->flags |= PLAYER_AVATAR_FLAG_FORCED_MOVE;
|
||||
playerAvatar->runningState = MOVING;
|
||||
return TRUE;
|
||||
@@ -518,12 +530,11 @@ static bool8 DoForcedMovement(u8 direction, void (*moveFunc)(u8))
|
||||
}
|
||||
else
|
||||
{
|
||||
if (PlayerHasFollowerNPC())
|
||||
SetFollowerNPCData(FNPC_DATA_FORCED_MOVEMENT, TRUE);
|
||||
|
||||
playerAvatar->runningState = MOVING;
|
||||
moveFunc(direction);
|
||||
if (PlayerHasFollowerNPC()
|
||||
&& gObjectEvents[GetFollowerNPCObjectId()].invisible == FALSE
|
||||
&& FindTaskIdByFunc(Task_MoveNPCFollowerAfterForcedMovement) == TASK_NONE)
|
||||
CreateTask(Task_MoveNPCFollowerAfterForcedMovement, 3);
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
@@ -1571,12 +1571,14 @@ static void Task_ExitStairs(u8 taskId)
|
||||
tState++;
|
||||
break;
|
||||
}
|
||||
gObjectEvents[gPlayerAvatar.objectEventId].noShadow = FALSE;
|
||||
}
|
||||
|
||||
static void ForceStairsMovement(u32 metatileBehavior, s16 *speedX, s16 *speedY)
|
||||
{
|
||||
ObjectEventForceSetHeldMovement(&gObjectEvents[gPlayerAvatar.objectEventId], GetWalkInPlaceNormalMovementAction(GetPlayerFacingDirection()));
|
||||
GetStairsMovementDirection(metatileBehavior, speedX, speedY);
|
||||
gObjectEvents[gPlayerAvatar.objectEventId].noShadow = TRUE;
|
||||
}
|
||||
#undef tSpeedX
|
||||
#undef tSpeedY
|
||||
@@ -1620,6 +1622,7 @@ static void Task_StairWarp(u8 taskId)
|
||||
LockPlayerFieldControls();
|
||||
FreezeObjectEvents();
|
||||
CameraObjectFreeze();
|
||||
HideFollowerForFieldEffect();
|
||||
tState++;
|
||||
break;
|
||||
case 1:
|
||||
|
@@ -84,6 +84,9 @@ void SetFollowerNPCData(enum FollowerNPCDataTypes type, u32 value)
|
||||
case FNPC_DATA_COME_OUT_DOOR:
|
||||
gSaveBlock3Ptr->NPCfollower.comeOutDoorStairs = value;
|
||||
break;
|
||||
case FNPC_DATA_FORCED_MOVEMENT:
|
||||
gSaveBlock3Ptr->NPCfollower.forcedMovement = value;
|
||||
break;
|
||||
case FNPC_DATA_OBJ_ID:
|
||||
gSaveBlock3Ptr->NPCfollower.objId = value;
|
||||
break;
|
||||
@@ -147,6 +150,8 @@ u32 GetFollowerNPCData(enum FollowerNPCDataTypes type)
|
||||
return gSaveBlock3Ptr->NPCfollower.createSurfBlob;
|
||||
case FNPC_DATA_COME_OUT_DOOR:
|
||||
return gSaveBlock3Ptr->NPCfollower.comeOutDoorStairs;
|
||||
case FNPC_DATA_FORCED_MOVEMENT:
|
||||
return gSaveBlock3Ptr->NPCfollower.forcedMovement;
|
||||
case FNPC_DATA_OBJ_ID:
|
||||
return gSaveBlock3Ptr->NPCfollower.objId;
|
||||
case FNPC_DATA_CURRENT_SPRITE:
|
||||
@@ -757,9 +762,7 @@ u32 DetermineFollowerNPCState(struct ObjectEvent *follower, u32 state, u32 direc
|
||||
|
||||
MoveCoords(direction, &followerX, &followerY);
|
||||
nextBehavior = MapGridGetMetatileBehaviorAt(followerX, followerY);
|
||||
|
||||
if (FindTaskIdByFunc(Task_MoveNPCFollowerAfterForcedMovement) == TASK_NONE)
|
||||
follower->facingDirectionLocked = FALSE;
|
||||
follower->facingDirectionLocked = FALSE;
|
||||
|
||||
// Follower won't do delayed movement until player does a movement.
|
||||
if (!IsStateMovement(state) && delayedState)
|
||||
@@ -814,10 +817,6 @@ u32 DetermineFollowerNPCState(struct ObjectEvent *follower, u32 state, u32 direc
|
||||
RETURN_STATE(MOVEMENT_ACTION_WALK_NORMAL_DOWN, direction);
|
||||
|
||||
case MOVEMENT_ACTION_WALK_FAST_DOWN ... MOVEMENT_ACTION_WALK_FAST_RIGHT:
|
||||
// Handle player on waterfall.
|
||||
if (PlayerIsUnderWaterfall(&gObjectEvents[gPlayerAvatar.objectEventId]) && (state == MOVEMENT_ACTION_WALK_FAST_UP))
|
||||
return MOVEMENT_INVALID;
|
||||
|
||||
// Handle ice tile (some walking animation).
|
||||
if (MetatileBehavior_IsIce(follower->currentMetatileBehavior) || MetatileBehavior_IsTrickHouseSlipperyFloor(follower->currentMetatileBehavior))
|
||||
follower->disableAnim = TRUE;
|
||||
@@ -826,6 +825,9 @@ u32 DetermineFollowerNPCState(struct ObjectEvent *follower, u32 state, u32 direc
|
||||
if (GetFollowerNPCData(FNPC_DATA_CURRENT_SPRITE) == FOLLOWER_NPC_SPRITE_INDEX_SURF && GetFollowerNPCSprite() == GetFollowerNPCData(FNPC_DATA_GFX_ID))
|
||||
RETURN_STATE(MOVEMENT_ACTION_SURF_STILL_DOWN, direction);
|
||||
|
||||
if (MetatileBehavior_IsMuddySlope(follower->currentMetatileBehavior))
|
||||
follower->facingDirectionLocked = TRUE;
|
||||
|
||||
RETURN_STATE(MOVEMENT_ACTION_WALK_FAST_DOWN, direction);
|
||||
|
||||
case MOVEMENT_ACTION_WALK_FASTER_DOWN ... MOVEMENT_ACTION_WALK_FASTER_RIGHT:
|
||||
@@ -835,10 +837,6 @@ u32 DetermineFollowerNPCState(struct ObjectEvent *follower, u32 state, u32 direc
|
||||
RETURN_STATE(MOVEMENT_ACTION_WALK_FASTER_DOWN, direction);
|
||||
|
||||
case MOVEMENT_ACTION_RIDE_WATER_CURRENT_DOWN ... MOVEMENT_ACTION_RIDE_WATER_CURRENT_RIGHT:
|
||||
// Handle player on waterfall.
|
||||
if (PlayerIsUnderWaterfall(&gObjectEvents[gPlayerAvatar.objectEventId]) && IsPlayerSurfingNorth())
|
||||
return MOVEMENT_INVALID;
|
||||
|
||||
RETURN_STATE(MOVEMENT_ACTION_RIDE_WATER_CURRENT_DOWN, direction);
|
||||
|
||||
// Acro bike.
|
||||
@@ -1581,37 +1579,20 @@ void FollowerNPC_TryRemoveFollowerOnWhiteOut(void)
|
||||
#undef tDoorY
|
||||
|
||||
// Task data
|
||||
#define PREVENT_PLAYER_STEP 0
|
||||
#define DO_ALL_FORCED_MOVEMENTS 1
|
||||
#define NPC_INTO_PLAYER 2
|
||||
#define ENABLE_PLAYER_STEP 3
|
||||
#define NPC_INTO_PLAYER 0
|
||||
#define ENABLE_PLAYER_STEP 1
|
||||
|
||||
void Task_MoveNPCFollowerAfterForcedMovement(u8 taskId)
|
||||
{
|
||||
struct ObjectEvent *follower = &gObjectEvents[GetFollowerNPCObjectId()];
|
||||
struct ObjectEvent *player = &gObjectEvents[gPlayerAvatar.objectEventId];
|
||||
|
||||
// Prevent player input until all forced mmovements are done and the follower is hidden.
|
||||
if (gTasks[taskId].tState == PREVENT_PLAYER_STEP)
|
||||
// The NPC will take an extra step and be on the same tile as the player.
|
||||
if (gTasks[taskId].tState == NPC_INTO_PLAYER && ObjectEventClearHeldMovementIfFinished(player) != 0 && ObjectEventClearHeldMovementIfFinished(follower) != 0)
|
||||
{
|
||||
gPlayerAvatar.preventStep = TRUE;
|
||||
gTasks[taskId].tState = DO_ALL_FORCED_MOVEMENTS;
|
||||
}
|
||||
// The player will keep doing forced movments until they land on a non-forced-move metatile or hit collision.
|
||||
else if (gTasks[taskId].tState == DO_ALL_FORCED_MOVEMENTS && ObjectEventClearHeldMovementIfFinished(player) != 0)
|
||||
{
|
||||
// Lock follower facing direction for muddy slope.
|
||||
if (follower->currentMetatileBehavior == MB_MUDDY_SLOPE)
|
||||
follower->facingDirectionLocked = TRUE;
|
||||
|
||||
if (TryDoMetatileBehaviorForcedMovement() == 0)
|
||||
gTasks[taskId].tState = NPC_INTO_PLAYER;
|
||||
|
||||
return;
|
||||
}
|
||||
// The NPC will take an extra step and be on the same tile as the player.
|
||||
else if (gTasks[taskId].tState == NPC_INTO_PLAYER && ObjectEventClearHeldMovementIfFinished(player) != 0 && ObjectEventClearHeldMovementIfFinished(follower) != 0)
|
||||
{
|
||||
ObjectEventSetHeldMovement(follower, GetWalkFastMovementAction(DetermineFollowerNPCDirection(player, follower)));
|
||||
gTasks[taskId].tState = ENABLE_PLAYER_STEP;
|
||||
return;
|
||||
@@ -1622,14 +1603,13 @@ void Task_MoveNPCFollowerAfterForcedMovement(u8 taskId)
|
||||
follower->facingDirectionLocked = FALSE;
|
||||
HideNPCFollower();
|
||||
SetFollowerNPCData(FNPC_DATA_WARP_END, FNPC_WARP_REAPPEAR);
|
||||
SetFollowerNPCData(FNPC_DATA_FORCED_MOVEMENT, FALSE);
|
||||
gPlayerAvatar.preventStep = FALSE;
|
||||
DestroyTask(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
#undef tState
|
||||
#undef PREVENT_PLAYER_STEP
|
||||
#undef DO_ALL_FORCED_MOVEMENTS
|
||||
#undef NPC_INTO_PLAYER
|
||||
#undef ENABLE_PLAYER_STEP
|
||||
|
||||
|
104
src/item_use.c
104
src/item_use.c
@@ -49,6 +49,7 @@
|
||||
static void SetUpItemUseCallback(u8);
|
||||
static void FieldCB_UseItemOnField(void);
|
||||
static void Task_CallItemUseOnFieldCallback(u8);
|
||||
static void Task_PartyMenuItemUseFromField(u8);
|
||||
static void Task_UseItemfinder(u8);
|
||||
static void Task_CloseItemfinderMessage(u8);
|
||||
static void Task_HiddenItemNearby(u8);
|
||||
@@ -127,15 +128,25 @@ static void SetUpItemUseCallback(u8 taskId)
|
||||
type = gTasks[taskId].tEnigmaBerryType - 1;
|
||||
else
|
||||
type = GetItemType(gSpecialVar_ItemId) - 1;
|
||||
if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE)
|
||||
|
||||
if (gTasks[taskId].tUsingRegisteredKeyItem && type == (ITEM_USE_PARTY_MENU - 1))
|
||||
{
|
||||
gBagMenu->newScreenCallback = sItemUseCallbacks[type];
|
||||
Task_FadeAndCloseBagMenu(taskId);
|
||||
FadeScreen(FADE_TO_BLACK, 0);
|
||||
gPartyMenu.data1 = DATA1_PARTY_MENU_FROM_FIELD;
|
||||
gTasks[taskId].func = Task_PartyMenuItemUseFromField;
|
||||
}
|
||||
else
|
||||
{
|
||||
gPyramidBagMenu->newScreenCallback = sItemUseCallbacks[type];
|
||||
CloseBattlePyramidBag(taskId);
|
||||
if (CurrentBattlePyramidLocation() == PYRAMID_LOCATION_NONE)
|
||||
{
|
||||
gBagMenu->newScreenCallback = sItemUseCallbacks[type];
|
||||
Task_FadeAndCloseBagMenu(taskId);
|
||||
}
|
||||
else
|
||||
{
|
||||
gPyramidBagMenu->newScreenCallback = sItemUseCallbacks[type];
|
||||
CloseBattlePyramidBag(taskId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +175,16 @@ static void Task_CallItemUseOnFieldCallback(u8 taskId)
|
||||
sItemUseOnFieldCB(taskId);
|
||||
}
|
||||
|
||||
static void Task_PartyMenuItemUseFromField(u8 taskId)
|
||||
{
|
||||
if (!gPaletteFade.active)
|
||||
{
|
||||
CleanupOverworldWindowsAndTilemaps();
|
||||
SetMainCallback2(CB2_ShowPartyMenuForItemUse);
|
||||
DestroyTask(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
static void DisplayCannotUseItemMessage(u8 taskId, bool8 isUsingRegisteredKeyItemOnField, const u8 *str)
|
||||
{
|
||||
StringExpandPlaceholders(gStringVar4, str);
|
||||
@@ -1392,77 +1413,37 @@ void ItemUseOutOfBattle_EnigmaBerry(u8 taskId)
|
||||
|
||||
void ItemUseOutOfBattle_FormChange(u8 taskId)
|
||||
{
|
||||
if (!gTasks[taskId].tUsingRegisteredKeyItem)
|
||||
{
|
||||
gItemUseCB = ItemUseCB_FormChange;
|
||||
gTasks[taskId].data[0] = FALSE;
|
||||
SetUpItemUseOnFieldCallback(taskId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle key items with callbacks to menus allow to be used by registering them.
|
||||
DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].tUsingRegisteredKeyItem);
|
||||
}
|
||||
gItemUseCB = ItemUseCB_FormChange;
|
||||
gTasks[taskId].data[0] = FALSE;
|
||||
SetUpItemUseCallback(taskId);
|
||||
}
|
||||
|
||||
void ItemUseOutOfBattle_FormChange_ConsumedOnUse(u8 taskId)
|
||||
{
|
||||
if (!gTasks[taskId].tUsingRegisteredKeyItem)
|
||||
{
|
||||
gItemUseCB = ItemUseCB_FormChange_ConsumedOnUse;
|
||||
gTasks[taskId].data[0] = TRUE;
|
||||
SetUpItemUseOnFieldCallback(taskId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle key items with callbacks to menus allow to be used by registering them.
|
||||
DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].tUsingRegisteredKeyItem);
|
||||
}
|
||||
gItemUseCB = ItemUseCB_FormChange_ConsumedOnUse;
|
||||
gTasks[taskId].data[0] = TRUE;
|
||||
SetUpItemUseCallback(taskId);
|
||||
}
|
||||
|
||||
void ItemUseOutOfBattle_RotomCatalog(u8 taskId)
|
||||
{
|
||||
if (!gTasks[taskId].tUsingRegisteredKeyItem)
|
||||
{
|
||||
gItemUseCB = ItemUseCB_RotomCatalog;
|
||||
gTasks[taskId].data[0] = TRUE;
|
||||
SetUpItemUseOnFieldCallback(taskId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle key items with callbacks to menus allow to be used by registering them.
|
||||
DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].tUsingRegisteredKeyItem);
|
||||
}
|
||||
gItemUseCB = ItemUseCB_RotomCatalog;
|
||||
gTasks[taskId].data[0] = TRUE;
|
||||
SetUpItemUseCallback(taskId);
|
||||
}
|
||||
|
||||
void ItemUseOutOfBattle_ZygardeCube(u8 taskId)
|
||||
{
|
||||
if (!gTasks[taskId].tUsingRegisteredKeyItem)
|
||||
{
|
||||
gItemUseCB = ItemUseCB_ZygardeCube;
|
||||
gTasks[taskId].data[0] = TRUE;
|
||||
SetUpItemUseOnFieldCallback(taskId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle key items with callbacks to menus allow to be used by registering them.
|
||||
DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].tUsingRegisteredKeyItem);
|
||||
}
|
||||
gItemUseCB = ItemUseCB_ZygardeCube;
|
||||
gTasks[taskId].data[0] = TRUE;
|
||||
SetUpItemUseCallback(taskId);
|
||||
}
|
||||
|
||||
void ItemUseOutOfBattle_Fusion(u8 taskId)
|
||||
{
|
||||
if (!gTasks[taskId].tUsingRegisteredKeyItem)
|
||||
{
|
||||
gItemUseCB = ItemUseCB_Fusion;
|
||||
gTasks[taskId].data[0] = FALSE;
|
||||
SetUpItemUseCallback(taskId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle key items with callbacks to menus allow to be used by registering them.
|
||||
DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].tUsingRegisteredKeyItem);
|
||||
}
|
||||
gItemUseCB = ItemUseCB_Fusion;
|
||||
gTasks[taskId].data[0] = FALSE;
|
||||
SetUpItemUseCallback(taskId);
|
||||
}
|
||||
|
||||
void Task_UseHoneyOnField(u8 taskId)
|
||||
@@ -1621,8 +1602,7 @@ void ItemUseOutOfBattle_TownMap(u8 taskId)
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: handle key items with callbacks to menus allow to be used by registering them.
|
||||
DisplayDadsAdviceCannotUseItemMessage(taskId, gTasks[taskId].tUsingRegisteredKeyItem);
|
||||
gTasks[taskId].func = ItemUseOnFieldCB_TownMap;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1558,7 +1558,7 @@ const struct BlendSettings gTimeOfDayBlend[] =
|
||||
};
|
||||
|
||||
#define DEFAULT_WEIGHT 256
|
||||
#define TIME_BLEND_WEIGHT(begin, end) (DEFAULT_WEIGHT - (DEFAULT_WEIGHT * ((hours - begin) * MINUTES_PER_HOUR + minutes) / ((end - begin) * MINUTES_PER_HOUR)))
|
||||
#define TIME_BLEND_WEIGHT(begin, end) (DEFAULT_WEIGHT - (DEFAULT_WEIGHT * SAFE_DIV(((hours - begin) * MINUTES_PER_HOUR + minutes), ((end - begin) * MINUTES_PER_HOUR))))
|
||||
|
||||
#define MORNING_HOUR_MIDDLE (MORNING_HOUR_BEGIN + ((MORNING_HOUR_END - MORNING_HOUR_BEGIN) / 2))
|
||||
|
||||
|
@@ -4536,6 +4536,12 @@ void CB2_ShowPartyMenuForItemUse(void)
|
||||
u8 msgId;
|
||||
TaskFunc task;
|
||||
|
||||
if (gPartyMenu.data1 == DATA1_PARTY_MENU_FROM_FIELD)
|
||||
{
|
||||
callback = CB2_ReturnToField;
|
||||
gPartyMenu.data1 = 0;
|
||||
}
|
||||
|
||||
if (gMain.inBattle)
|
||||
{
|
||||
menuType = PARTY_MENU_TYPE_IN_BATTLE;
|
||||
|
@@ -1,11 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(B_DAUNTLESS_SHIELD == GEN_9);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Dauntless Shield raises Defense by one stage")
|
||||
{
|
||||
GIVEN {
|
||||
@@ -22,9 +17,32 @@ SINGLE_BATTLE_TEST("Dauntless Shield raises Defense by one stage")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Dauntless Shield raises Defense by one stage only once per battle")
|
||||
SINGLE_BATTLE_TEST("Dauntless Shield raises Defense by one stage every time it switches in (Gen8)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_DAUNTLESS_SHIELD, GEN_8);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_ZAMAZENTA) { Ability(ABILITY_DAUNTLESS_SHIELD); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN { SWITCH(opponent, 0); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponent, ABILITY_DAUNTLESS_SHIELD);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("The opposing Zamazenta's Dauntless Shield raised its Defense!");
|
||||
ABILITY_POPUP(opponent, ABILITY_DAUNTLESS_SHIELD);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("The opposing Zamazenta's Dauntless Shield raised its Defense!");
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Dauntless Shield raises Defense by one stage only once per battle (Gen 9+)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_DAUNTLESS_SHIELD, GEN_9);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_ZAMAZENTA) { Ability(ABILITY_DAUNTLESS_SHIELD); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
@@ -63,4 +81,3 @@ SINGLE_BATTLE_TEST("Dauntless Shield activates when it's no longer effected by N
|
||||
MESSAGE("The opposing Zamazenta's Dauntless Shield raised its Defense!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,11 +1,6 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(B_INTREPID_SWORD == GEN_9);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Intrepid Sword raises Attack by one stage")
|
||||
{
|
||||
GIVEN {
|
||||
@@ -22,9 +17,32 @@ SINGLE_BATTLE_TEST("Intrepid Sword raises Attack by one stage")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Intrepid Sword raises Attack by one stage only once per battle")
|
||||
SINGLE_BATTLE_TEST("Intrepid Sword raises Attack by one stage every time it switches in (Gen8)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_INTREPID_SWORD, GEN_8);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN { SWITCH(opponent, 0); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("The opposing Zacian's Intrepid Sword raised its Attack!");
|
||||
ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
|
||||
MESSAGE("The opposing Zacian's Intrepid Sword raised its Attack!");
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Intrepid Sword raises Attack by one stage only once per battle (Gen9+)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_INTREPID_SWORD, GEN_9);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
|
@@ -5,7 +5,6 @@ ASSUMPTIONS
|
||||
{
|
||||
ASSUME(GetMoveAccuracy(MOVE_SCRATCH) == 100);
|
||||
ASSUME(GetMoveEffect(MOVE_SAND_ATTACK) == EFFECT_ACCURACY_DOWN);
|
||||
ASSUME(B_ILLUMINATE_EFFECT >= GEN_9);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Keen Eye, Gen9+ Illuminate & Minds Eye prevent accuracy stage reduction from moves")
|
||||
@@ -19,6 +18,7 @@ SINGLE_BATTLE_TEST("Keen Eye, Gen9+ Illuminate & Minds Eye prevent accuracy stag
|
||||
|
||||
PASSES_RANDOMLY(100, 100, RNG_ACCURACY);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_ILLUMINATE_EFFECT, GEN_9);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(species) { Ability(ability); }
|
||||
} WHEN {
|
||||
@@ -47,6 +47,7 @@ SINGLE_BATTLE_TEST("Keen Eye, Gen9+ Illuminate & Minds Eye ignore target's evasi
|
||||
|
||||
PASSES_RANDOMLY(100, 100, RNG_ACCURACY);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_ILLUMINATE_EFFECT, GEN_9);
|
||||
ASSUME(GetMoveEffect(MOVE_DOUBLE_TEAM) == EFFECT_EVASION_UP);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(species) { Ability(ability); }
|
||||
@@ -80,6 +81,7 @@ SINGLE_BATTLE_TEST("Keen Eye, Gen9+ Illuminate & Minds Eye are ignored by Mold B
|
||||
|
||||
PASSES_RANDOMLY(GetMoveAccuracy(MOVE_SCRATCH) * 3 / 4, 100, RNG_ACCURACY);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_ILLUMINATE_EFFECT, GEN_9);
|
||||
PLAYER(speciesPlayer) { Ability(abilityPlayer); }
|
||||
OPPONENT(speciesOpponent) { Ability(abilityOpponent); }
|
||||
} WHEN {
|
||||
@@ -102,6 +104,7 @@ SINGLE_BATTLE_TEST("Keen Eye, Gen9+ Illuminate & Minds Eye don't prevent Topsy-T
|
||||
PARAMETRIZE { species = SPECIES_URSALUNA_BLOODMOON; ability = ABILITY_MINDS_EYE; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_ILLUMINATE_EFFECT, GEN_9);
|
||||
ASSUME(GetMoveEffect(MOVE_HONE_CLAWS) == EFFECT_ATTACK_ACCURACY_UP);
|
||||
ASSUME(GetMoveEffect(MOVE_TOPSY_TURVY) == EFFECT_TOPSY_TURVY);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@@ -141,6 +144,7 @@ SINGLE_BATTLE_TEST("Keen Eye, Gen9+ Illuminate & Minds Eye don't prevent receivi
|
||||
PARAMETRIZE { species = SPECIES_URSALUNA_BLOODMOON; ability = ABILITY_MINDS_EYE; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_ILLUMINATE_EFFECT, GEN_9);
|
||||
ASSUME(GetMoveEffect(MOVE_BATON_PASS) == EFFECT_BATON_PASS);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
@@ -173,6 +177,7 @@ SINGLE_BATTLE_TEST("Keen Eye & Gen9+ Illuminate don't prevent Spectral Thief fro
|
||||
PARAMETRIZE { species = SPECIES_STARYU; ability = ABILITY_ILLUMINATE; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_ILLUMINATE_EFFECT, GEN_9);
|
||||
ASSUME(GetMoveEffect(MOVE_HONE_CLAWS) == EFFECT_ATTACK_ACCURACY_UP);
|
||||
ASSUME(GetMoveEffect(MOVE_SPECTRAL_THIEF) == EFFECT_SPECTRAL_THIEF);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
|
@@ -109,7 +109,7 @@ SINGLE_BATTLE_TEST("Liquid Ooze causes Strength Sap users to lose HP instead of
|
||||
}
|
||||
|
||||
/* * https://bulbapedia.bulbagarden.net/wiki/Liquid_Ooze_(Ability)#In_battle:
|
||||
* If the recipient of Leech Seed's effect were to faint due to Liquid Ooze on the same turn as the victim of Leech Seed, then the victim faints before the recipient. This means that the victim's team loses the battle if both teams had their final Pokémon sent out.
|
||||
* If the recipient of Leech Seed's effect were to faint due to Liquid Ooze on the same turn as the victim of Leech Seed, then the victim faints before the recipient. This means that the victim's team loses the battle if both teams had their final Pokémon sent out.
|
||||
*/
|
||||
SINGLE_BATTLE_TEST("Liquid Ooze causes leech seed victim to faint before seeder")
|
||||
{
|
||||
@@ -140,6 +140,7 @@ SINGLE_BATTLE_TEST("Liquid Ooze causes leech seed victim to faint before seeder"
|
||||
|
||||
SINGLE_BATTLE_TEST("Liquid Ooze causes Dream Eater users to lose HP instead of heal (Gen 5+)")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
s16 damage;
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_SPORE) == EFFECT_NON_VOLATILE_STATUS);
|
||||
@@ -158,8 +159,30 @@ SINGLE_BATTLE_TEST("Liquid Ooze causes Dream Eater users to lose HP instead of h
|
||||
HP_BAR(opponent);
|
||||
HP_BAR(player, captureDamage: &damage);
|
||||
} THEN {
|
||||
EXPECT_LT(damage, 0);
|
||||
EXPECT_GT(damage, 0); // Positive damage
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Liquid Ooze does not cause Dream Eater users to lose HP instead of heal (Gen 3-4")
|
||||
SINGLE_BATTLE_TEST("Liquid Ooze does not cause Dream Eater users to lose HP instead of heal (Gen 3-4")
|
||||
{
|
||||
s16 damage;
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_SPORE) == EFFECT_NON_VOLATILE_STATUS);
|
||||
ASSUME(GetMoveNonVolatileStatus(MOVE_SPORE) == MOVE_EFFECT_SLEEP);
|
||||
ASSUME(GetMoveEffect(MOVE_DREAM_EATER) == EFFECT_DREAM_EATER);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_TENTACRUEL) { Ability(ABILITY_LIQUID_OOZE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SCRATCH); MOVE(player, MOVE_SPORE); }
|
||||
TURN { MOVE(player, MOVE_DREAM_EATER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
|
||||
HP_BAR(player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPORE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_DREAM_EATER, player);
|
||||
HP_BAR(opponent);
|
||||
HP_BAR(player, captureDamage: &damage);
|
||||
} THEN {
|
||||
EXPECT_LT(damage, 0); // Negative damage = Heal
|
||||
}
|
||||
}
|
||||
|
@@ -156,53 +156,29 @@ SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s1
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Normalize-affected moves become Electric-type under Electrify's effect", s16 damage)
|
||||
SINGLE_BATTLE_TEST("Normalize-affected moves become Electric-type under Electrify's effect")
|
||||
{
|
||||
u32 ability, genConfig;
|
||||
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_7; }
|
||||
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_6; }
|
||||
PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_7; }
|
||||
PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_6; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_ELECTRIFY) == EFFECT_ELECTRIFY);
|
||||
WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, genConfig);
|
||||
PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_WATER_GUN); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_SKITTY) { Ability(ABILITY_NORMALIZE); }
|
||||
OPPONENT(SPECIES_ROOKIDEE) { Item(ITEM_WACAN_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_ELECTRIFY); MOVE(player, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
if (genConfig >= GEN_7)
|
||||
EXPECT_EQ(results[0].damage, results[2].damage);
|
||||
else
|
||||
EXPECT_EQ(results[1].damage, results[3].damage);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Normalize-affected moves become Electric-type under Ion Deluge's effect", s16 damage)
|
||||
SINGLE_BATTLE_TEST("Normalize-affected moves become Electric-type under Ion Deluge's effect")
|
||||
{
|
||||
u32 ability, genConfig;
|
||||
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_7; }
|
||||
PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_6; }
|
||||
PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_7; }
|
||||
PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_6; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_ION_DELUGE) == EFFECT_ION_DELUGE);
|
||||
WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, genConfig);
|
||||
PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_WATER_GUN); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_SKITTY) { Ability(ABILITY_NORMALIZE); Moves(MOVE_WATER_GUN); }
|
||||
OPPONENT(SPECIES_ROOKIDEE) { Item(ITEM_WACAN_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_ION_DELUGE); MOVE(player, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
if (genConfig >= GEN_7)
|
||||
EXPECT_EQ(results[0].damage, results[2].damage);
|
||||
else
|
||||
EXPECT_EQ(results[1].damage, results[3].damage);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -23,10 +23,10 @@ SINGLE_BATTLE_TEST("Pickup grants an item used by another Pokémon")
|
||||
}
|
||||
}
|
||||
|
||||
WILD_BATTLE_TEST("Pickup grants an item used by itself in wild battles (Gen 9)")
|
||||
WILD_BATTLE_TEST("Pickup grants an item used by itself in wild battles (Gen9+)")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_PICKUP_WILD >= GEN_9);
|
||||
WITH_CONFIG(GEN_PICKUP_WILD, GEN_9);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Ability(ABILITY_PICKUP); MaxHP(100); HP(51); Item(ITEM_SITRUS_BERRY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
|
@@ -1,14 +1,36 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(B_PROTEAN_LIBERO == GEN_9);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in")
|
||||
SINGLE_BATTLE_TEST("Protean changes the type of the user to the move used every time (Gen6-8)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_PROTEAN_LIBERO, GEN_6);
|
||||
PLAYER(SPECIES_REGIROCK);
|
||||
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_WATER_GUN); }
|
||||
TURN { MOVE(opponent, MOVE_SCRATCH); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
TURN { SWITCH(opponent, 0); }
|
||||
TURN { MOVE(opponent, MOVE_WATER_GUN); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
|
||||
MESSAGE("The opposing Kecleon transformed into the Water type!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
|
||||
MESSAGE("The opposing Kecleon transformed into the Normal type!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
|
||||
MESSAGE("The opposing Kecleon transformed into the Water type!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in (Gen9+)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_PROTEAN_LIBERO, GEN_9);
|
||||
PLAYER(SPECIES_REGIROCK);
|
||||
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
|
@@ -52,6 +52,7 @@ SINGLE_BATTLE_TEST("Refrigerate doesn't affect Weather Ball's type", s16 damage)
|
||||
PARAMETRIZE { move = MOVE_CELEBRATE; ability = ABILITY_REFRIGERATE; }
|
||||
PARAMETRIZE { move = MOVE_SUNNY_DAY; ability = ABILITY_REFRIGERATE; }
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_SNOW_WARNING, GEN_9); //To prevent capturing hail damage
|
||||
ASSUME(GetMoveEffect(MOVE_WEATHER_BALL) == EFFECT_WEATHER_BALL);
|
||||
ASSUME(GetSpeciesType(SPECIES_PINSIR, 0) == TYPE_BUG);
|
||||
PLAYER(SPECIES_AMAURA) { Ability(ability); }
|
||||
|
@@ -1,24 +1,30 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
#if B_SNOW_WARNING < GEN_9
|
||||
SINGLE_BATTLE_TEST("Snow Warning summons hail")
|
||||
#elif B_SNOW_WARNING >= GEN_9
|
||||
SINGLE_BATTLE_TEST("Snow Warning summons snow")
|
||||
#endif
|
||||
SINGLE_BATTLE_TEST("Snow Warning summons hail (Gen4-8)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_SNOW_WARNING, GEN_8);
|
||||
PLAYER(SPECIES_ABOMASNOW) { Ability(ABILITY_SNOW_WARNING); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {}
|
||||
} SCENE {
|
||||
#if B_SNOW_WARNING < GEN_9
|
||||
MESSAGE("It started to hail!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HAIL_CONTINUES);
|
||||
#elif B_SNOW_WARNING >= GEN_9
|
||||
MESSAGE("It started to snow!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SNOW_CONTINUES);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Snow Warning summons snow (Gen9+)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_SNOW_WARNING, GEN_9);
|
||||
PLAYER(SPECIES_ABOMASNOW) { Ability(ABILITY_SNOW_WARNING); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN {}
|
||||
} SCENE {
|
||||
MESSAGE("It started to snow!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SNOW_CONTINUES);
|
||||
}
|
||||
}
|
||||
|
@@ -842,6 +842,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has an
|
||||
PARAMETRIZE { aiMon = SPECIES_SHIFTRY; absorbingAbility = ABILITY_WIND_RIDER; move = MOVE_HURRICANE;}
|
||||
GIVEN {
|
||||
ASSUME(B_REDIRECT_ABILITY_IMMUNITY >= GEN_5);
|
||||
ASSUME(P_UPDATED_ABILITIES >= GEN_9); //For the predicted ability for Shiftry
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Moves(move); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH); }
|
||||
|
@@ -81,3 +81,53 @@ SINGLE_BATTLE_TEST("Life Orb doesn't cause any HP loss if user is unable to atta
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Life Orb does not activate if on a confusion hit")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_CONFUSE_RAY); MOVE(player, MOVE_POUND, WITH_RNG(RNG_CONFUSION, TRUE)); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, opponent);
|
||||
HP_BAR(player);
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, player);
|
||||
HP_BAR(opponent);
|
||||
HP_BAR(player);
|
||||
MESSAGE("Wobbuffet was hurt by the Life Orb!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Life Orb does not activate if move was absorbed by target")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); }
|
||||
OPPONENT(SPECIES_RAICHU) { Ability(ABILITY_LIGHTNING_ROD); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHOCK_WAVE); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHOCK_WAVE, player);
|
||||
HP_BAR(opponent);
|
||||
HP_BAR(player);
|
||||
MESSAGE("Wobbuffet was hurt by the Life Orb!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Life Orb activates if move connected but no damage was dealt")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LIFE_ORB); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_FALSE_SWIPE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FALSE_SWIPE, player);
|
||||
HP_BAR(player);
|
||||
MESSAGE("Wobbuffet was hurt by the Life Orb!");
|
||||
}
|
||||
}
|
||||
|
@@ -185,10 +185,25 @@ DOUBLE_BATTLE_TEST("Ally Switch doesn't make self-targeting status moves fail")
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Ally Switch increases the Protect-like moves counter")
|
||||
DOUBLE_BATTLE_TEST("Ally Switch doesn't increase the Protect-like moves counter (Gen5-8)")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9);
|
||||
WITH_CONFIG(GEN_ALLY_SWITCH_FAIL_CHANCE, GEN_8);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_ALLY_SWITCH); }
|
||||
} THEN {
|
||||
EXPECT(gDisableStructs[B_POSITION_PLAYER_RIGHT].protectUses == 0);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Ally Switch increases the Protect-like moves counter (Gen9+)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_ALLY_SWITCH_FAIL_CHANCE, GEN_9);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
@@ -319,7 +334,7 @@ DOUBLE_BATTLE_TEST("Ally switch updates last used moves for Mimic")
|
||||
OPPONENT(SPECIES_FEAROW) { Speed(20); }
|
||||
OPPONENT(SPECIES_ARON) { Speed(30); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_FAKE_OUT, target: opponentRight); MOVE(playerLeft, MOVE_ALLY_SWITCH);
|
||||
TURN { MOVE(playerRight, MOVE_FAKE_OUT, target: opponentRight); MOVE(playerLeft, MOVE_ALLY_SWITCH);
|
||||
MOVE(opponentLeft, MOVE_MIMIC, target: playerLeft);
|
||||
}
|
||||
} SCENE {
|
||||
|
@@ -6,3 +6,30 @@ TO_DO_BATTLE_TEST("Assurance doubles in power if the target has been damaged in
|
||||
TO_DO_BATTLE_TEST("Assurance doubles in power if the target has been damaged in the same turn - Crash");
|
||||
TO_DO_BATTLE_TEST("Assurance doubles in power if the target has been damaged in the same turn - Confusion");
|
||||
TO_DO_BATTLE_TEST("Assurance doubles in power if the target has been damaged in the same turn - Rocky Helmet");
|
||||
|
||||
DOUBLE_BATTLE_TEST("Assurance doubles in power if False Swipe connected but didn't do any damage")
|
||||
{
|
||||
s16 hits[2];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_ASSURANCE, target: playerRight); }
|
||||
TURN {
|
||||
MOVE(opponentLeft, MOVE_FALSE_SWIPE, target: playerLeft);
|
||||
MOVE(playerLeft, MOVE_RECOVER);
|
||||
MOVE(opponentRight, MOVE_ASSURANCE, target: playerLeft);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSURANCE, opponentLeft);
|
||||
HP_BAR(playerRight, captureDamage: &hits[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FALSE_SWIPE, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ASSURANCE, opponentRight);
|
||||
HP_BAR(playerLeft, captureDamage: &hits[1]);
|
||||
} THEN {
|
||||
EXPECT_MUL_EQ(hits[0], Q_4_12(2.0), hits[1]);
|
||||
}
|
||||
}
|
||||
|
@@ -137,9 +137,14 @@ SINGLE_BATTLE_TEST("Chloroblast is not affected by Reckless", s16 damage)
|
||||
u32 move;
|
||||
|
||||
PARAMETRIZE { move = MOVE_CHLOROBLAST; }
|
||||
PARAMETRIZE { move = MOVE_FRENZY_PLANT; }
|
||||
if (B_UPDATED_MOVE_DATA >= GEN_9) {
|
||||
PARAMETRIZE { move = MOVE_FRENZY_PLANT; } // 150 power
|
||||
} else {
|
||||
PARAMETRIZE { move = MOVE_SEED_FLARE; } // 120 power
|
||||
}
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMovePower(MOVE_CHLOROBLAST) == GetMovePower(move));
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
|
@@ -66,11 +66,11 @@ DOUBLE_BATTLE_TEST("Leech Seed will drain HP based on speed of the drained mon")
|
||||
OPPONENT(SPECIES_WYNAUT) { Speed(3); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
MOVE(playerLeft, MOVE_LEECH_SEED, target: opponentLeft);
|
||||
MOVE(playerRight, MOVE_LEECH_SEED, target: opponentRight);
|
||||
MOVE(opponentLeft, MOVE_LEECH_SEED, target: playerLeft);
|
||||
MOVE(opponentRight, MOVE_LEECH_SEED, target: playerRight);
|
||||
TURN {
|
||||
MOVE(playerLeft, MOVE_LEECH_SEED, target: opponentLeft);
|
||||
MOVE(playerRight, MOVE_LEECH_SEED, target: opponentRight);
|
||||
MOVE(opponentLeft, MOVE_LEECH_SEED, target: playerLeft);
|
||||
MOVE(opponentRight, MOVE_LEECH_SEED, target: playerRight);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, opponentRight);
|
||||
@@ -88,6 +88,24 @@ DOUBLE_BATTLE_TEST("Leech Seed will drain HP based on speed of the drained mon")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Leech Seeded recovers health through Substitute")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SUBSTITUTE); }
|
||||
TURN { MOVE(player, MOVE_LEECH_SEED); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEECH_SEED, player);
|
||||
HP_BAR(player);
|
||||
HP_BAR(opponent);
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Leech Seed doesn't affect already seeded targets")
|
||||
TO_DO_BATTLE_TEST("Leech Seed's effect is paused until a new battler replaces the original user's position") // Faint, can't be replaced, then revived.
|
||||
TO_DO_BATTLE_TEST("Leech Seed's effect pause still prevents it from being seeded again")
|
||||
|
@@ -50,14 +50,27 @@ SINGLE_BATTLE_TEST("Synthesis recovers 1/4 of the user's max HP in Rain, Sandsto
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Synthesis recovers regular amount in sandstorm if holding utility umbrella")
|
||||
{
|
||||
u32 item;
|
||||
PARAMETRIZE { item = ITEM_LIFE_ORB; }
|
||||
PARAMETRIZE { item = ITEM_UTILITY_UMBRELLA; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(400); Item(item); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SANDSTORM); MOVE(player, MOVE_SYNTHESIS); }
|
||||
} SCENE {
|
||||
if (item != ITEM_UTILITY_UMBRELLA)
|
||||
HP_BAR(player, damage: -(400 / 4));
|
||||
else
|
||||
HP_BAR(player, damage: -(400 / 2));
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Synthesis recovers 1/4 of the user's max HP while it is not day (Gen2)")
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Synthesis recovers 1/2 of the user's max HP in Sunlight while it is not day (Gen2)")
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Synthesis recovers 1/8 of the user's max HP in Rain, Sandstorm, Hail, and Snow while it is not day (Gen2)")
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Synthesis recovers 2/4 of the user's max HP while it is day (Gen2)")
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Synthesis recovers 2/2 of the user's max HP in Sunlight while it is day (Gen2)")
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Synthesis recovers 2/8 of the user's max HP in Rain, Sandstorm, Hail, and Snow while it is day (Gen2)")
|
||||
|
19
test/battle/move_effect_secondary/clear_smog.c
Normal file
19
test/battle/move_effect_secondary/clear_smog.c
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Clear Smog removes stat changes even if it did no damage")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SWORDS_DANCE); }
|
||||
TURN { MOVE(player, MOVE_ENDURE); MOVE(opponent, MOVE_CLEAR_SMOG); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CLEAR_SMOG, opponent);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
|
||||
}
|
||||
}
|
@@ -107,13 +107,13 @@ SINGLE_BATTLE_TEST("Thief and Covet don't steal target's held item if target has
|
||||
}
|
||||
|
||||
// Test can't currently verify if the item is sent to Bag
|
||||
WILD_BATTLE_TEST("Thief and Covet steal target's held item and it's added to Bag in wild battles (Gen 9)")
|
||||
WILD_BATTLE_TEST("Thief and Covet steal target's held item and it's added to Bag in wild battles (Gen 9+)")
|
||||
{
|
||||
u32 move;
|
||||
PARAMETRIZE { move = MOVE_THIEF; }
|
||||
PARAMETRIZE { move = MOVE_COVET; }
|
||||
GIVEN {
|
||||
ASSUME(B_STEAL_WILD_ITEMS >= GEN_9);
|
||||
WITH_CONFIG(GEN_STEAL_WILD_ITEMS, GEN_9);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_HYPER_POTION); }
|
||||
} WHEN {
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#include "pokemon.h"
|
||||
#include "test/overworld_script.h"
|
||||
#include "test/test.h"
|
||||
#include "constants/characters.h"
|
||||
|
||||
TEST("Nature independent from Hidden Nature")
|
||||
{
|
||||
@@ -475,3 +476,100 @@ TEST("Optimised SetMonData")
|
||||
EXPECT_EQ(GetMonData(&gPlayerParty[0], MON_DATA_EXP), exp);
|
||||
EXPECT_FASTER(optimised, vanilla);
|
||||
}
|
||||
|
||||
TEST("BoxPokemon encryption works")
|
||||
{
|
||||
u32 raw[20] =
|
||||
{
|
||||
990384375,
|
||||
2948624514,
|
||||
3907508686,
|
||||
14410461,
|
||||
35316705,
|
||||
3907508686,
|
||||
64742109,
|
||||
718729,
|
||||
3102307966,
|
||||
2160206402,
|
||||
49956971,
|
||||
2495766612,
|
||||
1424318580,
|
||||
273408756,
|
||||
2371630199,
|
||||
2708871082,
|
||||
3059937332,
|
||||
2529190026,
|
||||
2290634828,
|
||||
2870614922
|
||||
};
|
||||
|
||||
struct Pokemon mon;
|
||||
BoxMonToMon((struct BoxPokemon *)&raw, &mon);
|
||||
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SANITY_IS_BAD_EGG), 0);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SPECIES), SPECIES_TORCHIC);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_MARKINGS), 3);
|
||||
const u8 *actualNickname = COMPOUND_STRING("Testing mon");
|
||||
u8 nickname[12];
|
||||
GetMonData(&mon, MON_DATA_NICKNAME, nickname);
|
||||
u32 charIndex = 0;
|
||||
while (actualNickname[charIndex] != EOS)
|
||||
{
|
||||
EXPECT_EQ(actualNickname[charIndex], nickname[charIndex]);
|
||||
charIndex++;
|
||||
}
|
||||
EXPECT_EQ(GetNature(&mon), NATURE_HARDY);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HIDDEN_NATURE), NATURE_ADAMANT);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HP_LOST), 10);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HELD_ITEM), ITEM_ORAN_BERRY);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_MOVE1), MOVE_TACKLE);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_MOVE2), MOVE_SCRATCH);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_MOVE3), MOVE_POUND);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_MOVE4), MOVE_GROWL);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_PP1), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_PP2), 2);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_PP3), 3);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_PP4), 4);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_PP_BONUSES), 255);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_COOL), 10);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_BEAUTY), 20);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_CUTE), 30);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SMART), 40);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_TOUGH), 50);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SHEEN), 150);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_EXP), 12345);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_MET_LEVEL), 20);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HP_EV), 11);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_ATK_EV), 22);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_DEF_EV), 33);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SPEED_EV), 44);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SPATK_EV), 55);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SPDEF_EV), 66);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_FRIENDSHIP), 123);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_POKERUS), 2);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_POKEBALL), BALL_FRIEND);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HP_IV), 31);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_ATK_IV), 30);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_DEF_IV), 29);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SPEED_IV), 28);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SPATK_IV), 27);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SPDEF_IV), 26);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_CUTE_RIBBON), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_BEAUTY_RIBBON), 0);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_TOUGH_RIBBON), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_SMART_RIBBON), 0);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_CHAMPION_RIBBON), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_VICTORY_RIBBON), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_EFFORT_RIBBON), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_LAND_RIBBON), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_COUNTRY_RIBBON), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_EARTH_RIBBON), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HYPER_TRAINED_HP), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HYPER_TRAINED_ATK), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HYPER_TRAINED_DEF), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HYPER_TRAINED_SPEED), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HYPER_TRAINED_SPATK), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_HYPER_TRAINED_SPDEF), 1);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_DYNAMAX_LEVEL), 3);
|
||||
EXPECT_EQ(GetMonData(&mon, MON_DATA_OT_GENDER), 0);
|
||||
}
|
||||
|
@@ -1341,6 +1341,10 @@ void TestRunner_Battle_AfterLastTurn(void)
|
||||
|
||||
static void TearDownBattle(void)
|
||||
{
|
||||
// Zero out the parties, data in them could potentially carry over
|
||||
ZeroPlayerPartyMons();
|
||||
ZeroEnemyPartyMons();
|
||||
|
||||
FreeMonSpritesGfx();
|
||||
FreeBattleSpritesData();
|
||||
FreeBattleResources();
|
||||
|
Reference in New Issue
Block a user