From 3598a187031c7b93c760919bc2bc8826c638609f Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 11 Feb 2024 10:40:30 +0100 Subject: [PATCH 01/22] Adds config for Soundproof change during Uproar status (#4174) --- include/config/battle.h | 1 + src/battle_script_commands.c | 4 ++-- test/battle/move_effect/uproar.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 test/battle/move_effect/uproar.c diff --git a/include/config/battle.h b/include/config/battle.h index a003b8f5d8..2d40bdbdb2 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -50,6 +50,7 @@ // Turn settings #define B_BINDING_TURNS GEN_LATEST // In Gen5+, binding moves last for 4-5 turns instead of 2-5 turns. (With Grip Claw, 7 and 5 turns respectively.) #define B_UPROAR_TURNS GEN_LATEST // In Gen5+, Uproar lasts for 3 turns instead of 2-5 turns. +#define B_UPROAR_IGNORE_SOUNDPROOF GEN_LATEST // In Gen5+, Uproar status ignores Soundproof. #define B_DISABLE_TURNS GEN_LATEST // Disable's turns. See Cmd_disablelastusedattack. #define B_TAILWIND_TURNS GEN_LATEST // In Gen5+, Tailwind lasts 4 turns instead of 3. #define B_SLEEP_TURNS GEN_LATEST // In Gen5+, sleep lasts for 1-3 turns instead of 2-5 turns. diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 8d4df643c8..9965b3e80d 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2848,7 +2848,7 @@ void SetMoveEffect(bool32 primary, u32 certain) { case STATUS1_SLEEP: // check active uproar - if (battlerAbility != ABILITY_SOUNDPROOF) + if (battlerAbility != ABILITY_SOUNDPROOF || B_UPROAR_IGNORE_SOUNDPROOF >= GEN_5) { for (i = 0; i < gBattlersCount && !(gBattleMons[i].status2 & STATUS2_UPROAR); i++) ; @@ -11004,7 +11004,7 @@ bool8 UproarWakeUpCheck(u8 battler) for (i = 0; i < gBattlersCount; i++) { - if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || GetBattlerAbility(battler) == ABILITY_SOUNDPROOF) + if (!(gBattleMons[i].status2 & STATUS2_UPROAR) || (GetBattlerAbility(battler) == ABILITY_SOUNDPROOF && B_UPROAR_IGNORE_SOUNDPROOF < GEN_5)) continue; gBattleScripting.battler = i; diff --git a/test/battle/move_effect/uproar.c b/test/battle/move_effect/uproar.c new file mode 100644 index 0000000000..a4faab1efa --- /dev/null +++ b/test/battle/move_effect/uproar.c @@ -0,0 +1,28 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_UPROAR].effect == EFFECT_UPROAR); +} + +DOUBLE_BATTLE_TEST("Uproar status causes sleeping pokemon to wake up during an attack") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } + OPPONENT(SPECIES_VOLTORB) { Ability(ABILITY_SOUNDPROOF); Status1(STATUS1_SLEEP); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_UPROAR); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_UPROAR, playerLeft); + HP_BAR(opponentRight); + MESSAGE("Wobbuffet woke up in the UPROAR!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + MESSAGE("Foe Voltorb woke up in the UPROAR!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + MESSAGE("Foe Wobbuffet woke up in the UPROAR!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } +} From ce99db0086a60b85d789971c936d8b47d669f7a6 Mon Sep 17 00:00:00 2001 From: ghoulslash <41651341+ghoulslash@users.noreply.github.com> Date: Wed, 14 Feb 2024 04:05:37 -0500 Subject: [PATCH 02/22] Generic Starting Battle Status Variable (#4176) * setup generic starting battle status variable, ABILITYEFFECT_SWITCH_IN_STATUSES * fix B_ANIM_TAILWIND, assign to starting statuses, and change B_VAR_STARTING_STATUS check for only the variable and not trainers * Update src/battle_main.c Co-authored-by: Bassoonian * Update src/battle_util.c Co-authored-by: Bassoonian * Update src/battle_util.c Co-authored-by: Bassoonian * style fixes * General_Room naims play SE * fix sText_BizarreArenaCreated --------- Co-authored-by: ghoulslash Co-authored-by: Bassoonian --- data/battle_anim_scripts.s | 40 ++++- data/battle_scripts_1.s | 19 ++- include/battle.h | 9 +- include/battle_scripts.h | 1 + include/battle_util.h | 1 + include/config/battle.h | 4 +- include/constants/battle.h | 13 ++ include/constants/battle_anim.h | 4 + include/constants/battle_string_ids.h | 18 ++- include/data.h | 2 +- src/battle_main.c | 26 ++++ src/battle_message.c | 19 +++ src/battle_util.c | 205 ++++++++++++++++++-------- src/overworld.c | 8 +- 14 files changed, 292 insertions(+), 77 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 398005f824..becdadac1a 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -1028,6 +1028,10 @@ gBattleAnims_General:: .4byte General_Rainbow @ B_ANIM_RAINBOW .4byte General_SeaOfFire @ B_ANIM_SEA_OF_FIRE .4byte General_Swamp @ B_ANIM_SWAMP + .4byte General_TrickRoom @ B_ANIM_TRICK_ROOM + .4byte General_WonderRoom @ B_ANIM_WONDER_ROOM + .4byte General_MagicRoom @ B_ANIM_MAGIC_ROOM + .4byte General_Tailwind @ B_ANIM_TAILLWIND .align 2 gBattleAnims_Special:: @@ -1316,6 +1320,35 @@ Move_TAILWIND: delay 1 end +General_Tailwind: + loadspritegfx ANIM_TAG_FLYING_DIRT + playsewithpan SE_M_GUST, SOUND_PAN_ATTACKER + call SetHighSpeedBg + setalpha 12, 8 + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 10, 2304, 96, 0 + delay 12 + playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 90, 2048, 96, 0 + delay 12 + playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 50, 2560, 96, 0 + delay 12 + playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 20, 2304, 96, 0 + delay 12 + playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_ATTACKER + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 70, 1984, 96, 0 + delay 12 + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 0, 2816, 96, 0 + delay 10 + createsprite gFlyingSandCrescentSpriteTemplate, ANIM_ATTACKER, 40, 60, 2560, 96, 0 + waitforvisualfinish + stopsound + call UnsetHighSpeedBg + blendoff + delay 1 + end + Move_ACUPRESSURE: loadspritegfx ANIM_TAG_ACUPRESSURE loadspritegfx ANIM_TAG_SPARK_2 @@ -3303,6 +3336,8 @@ Move_DEFOG: Move_TRICK_ROOM:: call InitRoomAnimation +General_TrickRoom: + playsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET fadetobg BG_TRICK_ROOM waitbgfadein delay 0x40 @@ -3312,7 +3347,6 @@ Move_TRICK_ROOM:: end InitRoomAnimation: setalpha 8, 8 - playsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET createvisualtask AnimTask_ScaleMonAndRestore, 5, -6, -6, 15, ANIM_TARGET, 1 return @@ -4932,6 +4966,8 @@ PowerSplitLaunch: Move_WONDER_ROOM:: call InitRoomAnimation +General_WonderRoom: + playsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET fadetobg BG_WONDER_ROOM waitbgfadein delay 0x40 @@ -5112,6 +5148,8 @@ Move_TELEKINESIS:: Move_MAGIC_ROOM:: call InitRoomAnimation +General_MagicRoom: + playsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET fadetobg BG_MAGIC_ROOM waitbgfadein delay 0x40 diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index d451cf566f..572aedbac7 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -2534,6 +2534,10 @@ BattleScript_EffectTrickRoom:: waitanimation printfromtable gRoomsStringIds waitmessage B_WAIT_TIME_LONG + call BattleScript_TryRoomServiceLoop + goto BattleScript_MoveEnd + +BattleScript_TryRoomServiceLoop: savetarget setbyte gBattlerTarget, 0 BattleScript_RoomServiceLoop: @@ -2544,7 +2548,7 @@ BattleScript_RoomServiceLoop_NextBattler: addbyte gBattlerTarget, 0x1 jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_RoomServiceLoop restoretarget - goto BattleScript_MoveEnd + return BattleScript_EffectWonderRoom:: BattleScript_EffectMagicRoom:: @@ -5983,6 +5987,19 @@ BattleScript_SunlightFaded:: call BattleScript_ActivateWeatherAbilities end2 +BattleScript_OverworldStatusStarts:: + printfromtable gStartingStatusStringIds + waitmessage B_WAIT_TIME_LONG + playanimation_var BS_ATTACKER, sB_ANIM_ARG1 + call BattleScript_OverworldStatusStarts_TryActivations + end3 + +BattleScript_OverworldStatusStarts_TryActivations: + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SET_TRICK_ROOM, BattleScript_TryRoomServiceLoop + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SET_TAILWIND_PLAYER, BattleScript_TryTailwindAbilitiesLoop + jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SET_TAILWIND_OPPONENT, BattleScript_TryTailwindAbilitiesLoop + return + BattleScript_OverworldWeatherStarts:: printfromtable gWeatherStartsStringIds waitmessage B_WAIT_TIME_LONG diff --git a/include/battle.h b/include/battle.h index 5628dd3146..360faace34 100644 --- a/include/battle.h +++ b/include/battle.h @@ -682,9 +682,12 @@ struct BattleStruct } multiBuffer; u8 wishPerishSongState; u8 wishPerishSongBattlerId; - bool8 overworldWeatherDone; - bool8 terrainDone; - u8 isAtkCancelerForCalledMove; // Certain cases in atk canceler should only be checked once, when the original move is called, however others need to be checked the twice. + u8 overworldWeatherDone:1; + u8 startingStatusDone:1; + u8 isAtkCancelerForCalledMove:1; // Certain cases in atk canceler should only be checked once, when the original move is called, however others need to be checked the twice. + u8 terrainDone:1; + u8 startingStatus; // status to apply at battle start. defined in constants/battle.h + u8 startingStatusTimer; u8 atkCancellerTracker; struct BattleTvMovePoints tvMovePoints; struct BattleTv tv; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 3ff275d3bf..2eeba2cc33 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -54,6 +54,7 @@ extern const u8 BattleScript_DamagingWeatherContinues[]; extern const u8 BattleScript_SandStormHailSnowEnds[]; extern const u8 BattleScript_SunlightContinues[]; extern const u8 BattleScript_SunlightFaded[]; +extern const u8 BattleScript_OverworldStatusStarts[]; extern const u8 BattleScript_OverworldWeatherStarts[]; extern const u8 BattleScript_OverworldTerrain[]; extern const u8 BattleScript_SideStatusWoreOff[]; diff --git a/include/battle_util.h b/include/battle_util.h index 02eec4870f..ad3838b1fc 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -39,6 +39,7 @@ #define ABILITYEFFECT_SWITCH_IN_TERRAIN 16 #define ABILITYEFFECT_SWITCH_IN_WEATHER 17 #define ABILITYEFFECT_OPPORTUNIST 18 +#define ABILITYEFFECT_SWITCH_IN_STATUSES 19 // Special cases #define ABILITYEFFECT_MUD_SPORT 252 // Only used if B_SPORT_TURNS >= GEN_6 #define ABILITYEFFECT_WATER_SPORT 253 // Only used if B_SPORT_TURNS >= GEN_6 diff --git a/include/config/battle.h b/include/config/battle.h index 163e4c0f08..d67b589a47 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -180,8 +180,8 @@ // Var Settings // To use the following features in scripting, replace the 0s with the var ID you're assigning it to. // Eg: Replace with VAR_UNUSED_0x40F7 so you can use B_VAR_TERRAIN for that feature. -#define B_VAR_TERRAIN 0 // If this var has a value, assigning a STATUS_FIELD_xx_TERRAIN to it before battle causes the battle to start with that terrain active. -#define B_VAR_TERRAIN_TIMER 0 // If this var has a value greater or equal than 1 field terrains will last that number of turns, otherwise they will last until they're overwritten. +#define B_VAR_STARTING_STATUS 0 // If this var has a value, assigning a STATUS_FIELD_xx_TERRAIN to it before battle causes the battle to start with that terrain active. +#define B_VAR_STARTING_STATUS_TIMER 0 // If this var has a value greater or equal than 1 field terrains will last that number of turns, otherwise they will last until they're overwritten. #define B_VAR_WILD_AI_FLAGS 0 // If not 0, you can use this var to add to default wild AI flags. NOT usable with flags above (1 << 15) // Sky Battles diff --git a/include/constants/battle.h b/include/constants/battle.h index a353fbb357..727e746443 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -517,4 +517,17 @@ // Constants for Torment #define PERMANENT_TORMENT 0xF +// Constants for B_VAR_STARTING_STATUS +// Timer value controlled by B_VAR_STARTING_STATUS_TIMER +#define STARTING_STATUS_NONE 0 +#define STARTING_STATUS_ELECTRIC_TERRAIN 1 +#define STARTING_STATUS_MISTY_TERRAIN 2 +#define STARTING_STATUS_GRASSY_TERRAIN 3 +#define STARTING_STATUS_PSYCHIC_TERRAIN 4 +#define STARTING_STATUS_TRICK_ROOM 5 +#define STARTING_STATUS_MAGIC_ROOM 6 +#define STARTING_STATUS_WONDER_ROOM 7 +#define STARTING_STATUS_TAILWIND_PLAYER 8 +#define STARTING_STATUS_TAILWIND_OPPONENT 9 + #endif // GUARD_CONSTANTS_BATTLE_H diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index b085d305ab..8bc09ba530 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -563,6 +563,10 @@ #define B_ANIM_RAINBOW 42 #define B_ANIM_SEA_OF_FIRE 43 #define B_ANIM_SWAMP 44 +#define B_ANIM_TRICK_ROOM 45 +#define B_ANIM_WONDER_ROOM 46 +#define B_ANIM_MAGIC_ROOM 47 +#define B_ANIM_TAILWIND 48 // special animations table (gBattleAnims_Special) #define B_ANIM_LVL_UP 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index 852e9a26c6..e79e3f55a0 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -703,8 +703,11 @@ #define STRINGID_SHEDITSTAIL 701 #define STRINGID_CLOAKEDINAHARSHLIGHT 702 #define STRINGID_SUPERSWEETAROMAWAFTS 703 +#define STRINGID_DIMENSIONSWERETWISTED 704 +#define STRINGID_BIZARREARENACREATED 705 +#define STRINGID_BIZARREAREACREATED 706 -#define BATTLESTRINGS_COUNT 704 +#define BATTLESTRINGS_COUNT 707 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, @@ -967,6 +970,19 @@ #define B_MSG_TERRAIN_END_GRASSY 7 #define B_MSG_TERRAIN_COUNT 8 +// gStartingStatusStringIds +#define B_MSG_TERRAIN_SET_MISTY 0 +#define B_MSG_TERRAIN_SET_ELECTRIC 1 +#define B_MSG_TERRAIN_SET_PSYCHIC 2 +#define B_MSG_TERRAIN_SET_GRASSY 3 +#define B_MSG_SET_TRICK_ROOM 4 +#define B_MSG_SET_MAGIC_ROOM 5 +#define B_MSG_SET_WONDER_ROOM 6 +#define B_MSG_SET_TAILWIND_PLAYER 7 +#define B_MSG_SET_TAILWIND_OPPONENT 8 +#define B_MSG_STARTING_STATUS_COUNT 9 + + // gWrappedStringIds #define B_MSG_WRAPPED_BIND 0 #define B_MSG_WRAPPED_WRAP 1 diff --git a/include/data.h b/include/data.h index 4727dc605b..4ac1d327fa 100644 --- a/include/data.h +++ b/include/data.h @@ -88,7 +88,7 @@ struct Trainer /*0x13*/ u8 trainerName[TRAINER_NAME_LENGTH + 1]; /*0x1E*/ bool8 doubleBattle:1; bool8 mugshotEnabled:1; - u8 padding:6; + u8 startingStatus:6; // this trainer starts a battle with a given status. see include/constants/battle.h for values /*0x1F*/ u8 mugshotColor; /*0x20*/ u8 partySize; }; diff --git a/src/battle_main.c b/src/battle_main.c index 4aa7e43a91..7ed28ea63c 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3701,6 +3701,24 @@ static void DoBattleIntro(void) gBattleStruct->overworldWeatherDone = FALSE; SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield. + + // Try to set a status to start the battle with + gBattleStruct->startingStatus = 0; + if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && gTrainers[gTrainerBattleOpponent_B].startingStatus) + { + gBattleStruct->startingStatus = gTrainers[gTrainerBattleOpponent_B].startingStatus; + gBattleStruct->startingStatusTimer = 0; // infinite + } + else if (gTrainers[gTrainerBattleOpponent_A].startingStatus) + { + gBattleStruct->startingStatus = gTrainers[gTrainerBattleOpponent_A].startingStatus; + gBattleStruct->startingStatusTimer = 0; // infinite + } + else if (B_VAR_STARTING_STATUS != 0) + { + gBattleStruct->startingStatus = VarGet(B_VAR_STARTING_STATUS); + gBattleStruct->startingStatusTimer = VarGet(B_VAR_STARTING_STATUS_TIMER); + } gBattleMainFunc = TryDoEventsBeforeFirstTurn; } break; @@ -3766,6 +3784,14 @@ static void TryDoEventsBeforeFirstTurn(void) return; } + if (!gBattleStruct->startingStatusDone + && gBattleStruct->startingStatus + && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_STATUSES, 0, 0, ABILITYEFFECT_SWITCH_IN_STATUSES, 0) != 0) + { + gBattleStruct->startingStatusDone = TRUE; + return; + } + // Totem boosts for (i = 0; i < gBattlersCount; i++) { diff --git a/src/battle_message.c b/src/battle_message.c index a99f3cde9d..cad7916b94 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -559,6 +559,7 @@ static const u8 sText_PkmnSwitchedStatChanges[] = _("{B_ATK_NAME_WITH_PREFIX} sw static const u8 sText_PkmnSurroundedWithVeilOfWater[] = _("{B_ATK_NAME_WITH_PREFIX} surrounded itself\nwith a veil of water!"); static const u8 sText_PkmnLevitatedOnElectromagnetism[] = _("{B_ATK_NAME_WITH_PREFIX} levitated on\nelectromagnetism!"); static const u8 sText_PkmnTwistedDimensions[] = _("{B_ATK_NAME_WITH_PREFIX} twisted\nthe dimensions!"); +static const u8 sText_DimensionsWereTwisted[] = _("The dimensions were\ntwisted!"); static const u8 sText_PointedStonesFloat[] =_("Pointed stones float in the air\naround {B_DEF_TEAM2} team!"); static const u8 sText_CloakedInMysticalMoonlight[] =_("It became cloaked in mystical\nmoonlight!"); static const u8 sText_TrappedBySwirlingMagma[] =_("{B_DEF_NAME_WITH_PREFIX} became\ntrapped by swirling magma!"); @@ -567,9 +568,11 @@ static const u8 sText_ProtectedTeam[] =_("{B_CURRENT_MOVE} protected\n{B_ATK_TEA static const u8 sText_SharedItsGuard[] =_("{B_ATK_NAME_WITH_PREFIX} shared its\nguard with the target!"); static const u8 sText_SharedItsPower[] =_("{B_ATK_NAME_WITH_PREFIX} shared its\npower with the target!"); static const u8 sText_SwapsDefAndSpDefOfAllPkmn[] =_("It created a bizarre area in which the\nDefense and Sp. Def stats are swapped!"); +static const u8 sText_BizzareAreaCreated[] =_("A bizarre area was created in which the\nDefense and Sp. Def stats are swapped!"); static const u8 sText_BecameNimble[] =_("{B_ATK_NAME_WITH_PREFIX} became nimble!"); static const u8 sText_HurledIntoTheAir[] =_("{B_DEF_NAME_WITH_PREFIX} was hurled\ninto the air!"); static const u8 sText_HeldItemsLoseEffects[] =_("It created a bizarre area in which\nPokémon's held items lose their effects!"); +static const u8 sText_BizarreArenaCreated[] =_("A bizarre area was created!\nHold items lost their effects!"); static const u8 sText_FellStraightDown[] =_("{B_DEF_NAME_WITH_PREFIX} fell\nstraight down!"); static const u8 sText_TargetChangedType[] =_("{B_DEF_NAME_WITH_PREFIX} transformed\ninto the {B_BUFF1} type!"); static const u8 sText_PkmnAcquiredSimple[] =_("{B_DEF_NAME_WITH_PREFIX} acquired\nSimple!"); @@ -1384,6 +1387,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_PKMNSURROUNDEDWITHVEILOFWATER - BATTLESTRINGS_TABLE_START] = sText_PkmnSurroundedWithVeilOfWater, [STRINGID_PKMNLEVITATEDONELECTROMAGNETISM - BATTLESTRINGS_TABLE_START] = sText_PkmnLevitatedOnElectromagnetism, [STRINGID_PKMNTWISTEDDIMENSIONS - BATTLESTRINGS_TABLE_START] = sText_PkmnTwistedDimensions, + [STRINGID_DIMENSIONSWERETWISTED - BATTLESTRINGS_TABLE_START] = sText_DimensionsWereTwisted, [STRINGID_POINTEDSTONESFLOAT - BATTLESTRINGS_TABLE_START] = sText_PointedStonesFloat, [STRINGID_CLOAKEDINMYSTICALMOONLIGHT - BATTLESTRINGS_TABLE_START] = sText_CloakedInMysticalMoonlight, [STRINGID_TRAPPEDBYSWIRLINGMAGMA - BATTLESTRINGS_TABLE_START] = sText_TrappedBySwirlingMagma, @@ -1392,9 +1396,11 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = [STRINGID_SHAREDITSGUARD - BATTLESTRINGS_TABLE_START] = sText_SharedItsGuard, [STRINGID_SHAREDITSPOWER - BATTLESTRINGS_TABLE_START] = sText_SharedItsPower, [STRINGID_SWAPSDEFANDSPDEFOFALLPOKEMON - BATTLESTRINGS_TABLE_START] = sText_SwapsDefAndSpDefOfAllPkmn, + [STRINGID_BIZARREAREACREATED - BATTLESTRINGS_TABLE_START] = sText_BizzareAreaCreated, [STRINGID_BECAMENIMBLE - BATTLESTRINGS_TABLE_START] = sText_BecameNimble, [STRINGID_HURLEDINTOTHEAIR - BATTLESTRINGS_TABLE_START] = sText_HurledIntoTheAir, [STRINGID_HELDITEMSLOSEEFFECTS - BATTLESTRINGS_TABLE_START] = sText_HeldItemsLoseEffects, + [STRINGID_BIZARREARENACREATED - BATTLESTRINGS_TABLE_START] = sText_BizarreArenaCreated, [STRINGID_FELLSTRAIGHTDOWN - BATTLESTRINGS_TABLE_START] = sText_FellStraightDown, [STRINGID_TARGETCHANGEDTYPE - BATTLESTRINGS_TABLE_START] = sText_TargetChangedType, [STRINGID_PKMNACQUIREDSIMPLE - BATTLESTRINGS_TABLE_START] = sText_PkmnAcquiredSimple, @@ -1563,6 +1569,19 @@ const u16 gMentalHerbCureStringIds[] = [B_MSG_MENTALHERBCURE_DISABLE] = STRINGID_PKMNMOVEDISABLEDNOMORE, }; +const u16 gStartingStatusStringIds[B_MSG_STARTING_STATUS_COUNT] = +{ + [B_MSG_TERRAIN_SET_MISTY] = STRINGID_TERRAINBECOMESMISTY, + [B_MSG_TERRAIN_SET_ELECTRIC] = STRINGID_TERRAINBECOMESELECTRIC, + [B_MSG_TERRAIN_SET_PSYCHIC] = STRINGID_TERRAINBECOMESPSYCHIC, + [B_MSG_TERRAIN_SET_GRASSY] = STRINGID_TERRAINBECOMESGRASSY, + [B_MSG_SET_TRICK_ROOM] = STRINGID_DIMENSIONSWERETWISTED, + [B_MSG_SET_MAGIC_ROOM] = STRINGID_BIZARREARENACREATED, + [B_MSG_SET_WONDER_ROOM] = STRINGID_BIZARREAREACREATED, + [B_MSG_SET_TAILWIND_PLAYER] = STRINGID_TAILWINDBLEW, + [B_MSG_SET_TAILWIND_OPPONENT] = STRINGID_TAILWINDBLEW, +}; + const u16 gTerrainStringIds[B_MSG_TERRAIN_COUNT] = { [B_MSG_TERRAIN_SET_MISTY] = STRINGID_TERRAINBECOMESMISTY, diff --git a/src/battle_util.c b/src/battle_util.c index b6f959ac67..43d8d675b6 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1907,7 +1907,7 @@ u8 DoFieldEndTurnEffects(void) gBattlerAttacker = gSideTimers[side].tailwindBattlerId; if (gSideStatuses[side] & SIDE_STATUS_TAILWIND) { - if (--gSideTimers[side].tailwindTimer == 0) + if (gSideTimers[side].tailwindTimer > 0 && --gSideTimers[side].tailwindTimer == 0) { gSideStatuses[side] &= ~SIDE_STATUS_TAILWIND; BattleScriptExecute(BattleScript_TailwindEnds); @@ -2088,7 +2088,7 @@ u8 DoFieldEndTurnEffects(void) } break; case ENDTURN_TRICK_ROOM: - if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && --gFieldTimers.trickRoomTimer == 0) + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer > 0 && --gFieldTimers.trickRoomTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_TRICK_ROOM; BattleScriptExecute(BattleScript_TrickRoomEnds); @@ -2097,7 +2097,7 @@ u8 DoFieldEndTurnEffects(void) gBattleStruct->turnCountersTracker++; break; case ENDTURN_WONDER_ROOM: - if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM && --gFieldTimers.wonderRoomTimer == 0) + if (gFieldStatuses & STATUS_FIELD_WONDER_ROOM && gFieldTimers.wonderRoomTimer > 0 && --gFieldTimers.wonderRoomTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_WONDER_ROOM; BattleScriptExecute(BattleScript_WonderRoomEnds); @@ -2106,7 +2106,7 @@ u8 DoFieldEndTurnEffects(void) gBattleStruct->turnCountersTracker++; break; case ENDTURN_MAGIC_ROOM: - if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM && --gFieldTimers.magicRoomTimer == 0) + if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM && gFieldTimers.magicRoomTimer > 0 && --gFieldTimers.magicRoomTimer == 0) { gFieldStatuses &= ~STATUS_FIELD_MAGIC_ROOM; BattleScriptExecute(BattleScript_MagicRoomEnds); @@ -4000,78 +4000,155 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 switch (caseID) { - case ABILITYEFFECT_SWITCH_IN_TERRAIN: + case ABILITYEFFECT_SWITCH_IN_STATUSES: // starting field/side/etc statuses with a variable { - u8 varTerrainTimer = VarGet(B_VAR_TERRAIN_TIMER); + u8 timerVal = gBattleStruct->startingStatusTimer; gBattleScripting.battler = battler; - if (VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) + switch (gBattleStruct->startingStatus) { - u16 terrainFlags = VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY; // only works for status flag (1 << 15) - - if (varTerrainTimer == 0) + case STARTING_STATUS_ELECTRIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) { - gFieldStatuses = terrainFlags | STATUS_FIELD_TERRAIN_PERMANENT; // terrain is permanent - } - else - { - gFieldStatuses |= terrainFlags; - gFieldTimers.terrainTimer = varTerrainTimer; - } - - switch (VarGet(B_VAR_TERRAIN) & STATUS_FIELD_TERRAIN_ANY) - { - case STATUS_FIELD_ELECTRIC_TERRAIN: gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; - break; - case STATUS_FIELD_MISTY_TERRAIN: + gFieldStatuses |= STATUS_FIELD_ELECTRIC_TERRAIN; + if (timerVal == 0) + gFieldStatuses |= STATUS_FIELD_TERRAIN_PERMANENT; + else + gFieldTimers.terrainTimer = timerVal; + effect = 2; + } + break; + case STARTING_STATUS_MISTY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; - break; - case STATUS_FIELD_GRASSY_TERRAIN: + gFieldStatuses |= STATUS_FIELD_MISTY_TERRAIN; + if (timerVal == 0) + gFieldStatuses |= STATUS_FIELD_TERRAIN_PERMANENT; + else + gFieldTimers.terrainTimer = timerVal; + effect = 2; + } + break; + case STARTING_STATUS_GRASSY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_GRASSY; - break; - case STATUS_FIELD_PSYCHIC_TERRAIN: + gFieldStatuses |= STATUS_FIELD_GRASSY_TERRAIN; + if (timerVal == 0) + gFieldStatuses |= STATUS_FIELD_TERRAIN_PERMANENT; + else + gFieldTimers.terrainTimer = timerVal; + effect = 2; + } + break; + case STARTING_STATUS_PSYCHIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)) + { gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_PSYCHIC; - break; + gFieldStatuses |= STATUS_FIELD_PSYCHIC_TERRAIN; + if (timerVal == 0) + gFieldStatuses |= STATUS_FIELD_TERRAIN_PERMANENT; + else + gFieldTimers.terrainTimer = timerVal; + effect = 2; } - BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; + break; + case STARTING_STATUS_TRICK_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_TRICK_ROOM; + gFieldStatuses |= STATUS_FIELD_TRICK_ROOM; + gBattleScripting.animArg1 = B_ANIM_TRICK_ROOM; + if (timerVal == 0) + gFieldTimers.trickRoomTimer = 0; // infinite + else + gFieldTimers.trickRoomTimer = 5; + effect = 1; + } + break; + case STARTING_STATUS_MAGIC_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_MAGIC_ROOM; + gFieldStatuses |= STATUS_FIELD_MAGIC_ROOM; + gBattleScripting.animArg1 = B_ANIM_MAGIC_ROOM; + if (timerVal == 0) + gFieldTimers.magicRoomTimer = 0; // infinite + else + gFieldTimers.magicRoomTimer = 5; + effect = 1; + } + break; + case STARTING_STATUS_WONDER_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_WONDER_ROOM)) + { + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_WONDER_ROOM; + gFieldStatuses |= STATUS_FIELD_WONDER_ROOM; + gBattleScripting.animArg1 = B_ANIM_WONDER_ROOM; + if (timerVal == 0) + gFieldTimers.wonderRoomTimer = 0; // infinite + else + gFieldTimers.wonderRoomTimer = 5; + effect = 1; + } + break; + case STARTING_STATUS_TAILWIND_PLAYER: + if (!(gSideStatuses[B_SIDE_PLAYER] & SIDE_STATUS_TAILWIND)) + { + gBattlerAttacker = B_POSITION_PLAYER_LEFT; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_TAILWIND_PLAYER; + gSideStatuses[B_SIDE_PLAYER] |= SIDE_STATUS_TAILWIND; + gBattleScripting.animArg1 = B_ANIM_TAILWIND; + if (timerVal == 0) + gSideTimers[B_SIDE_PLAYER].tailwindTimer = 0; // infinite + else + gSideTimers[B_SIDE_PLAYER].tailwindTimer = 5; + effect = 1; + } + break; + case STARTING_STATUS_TAILWIND_OPPONENT: + if (!(gSideStatuses[B_SIDE_OPPONENT] & SIDE_STATUS_TAILWIND)) + { + gBattlerAttacker = B_POSITION_OPPONENT_LEFT; + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SET_TAILWIND_OPPONENT; + gSideStatuses[B_SIDE_OPPONENT] |= SIDE_STATUS_TAILWIND; + gBattleScripting.animArg1 = B_ANIM_TAILWIND; + if (timerVal == 0) + gSideTimers[B_SIDE_OPPONENT].tailwindTimer = 0; // infinite + else + gSideTimers[B_SIDE_OPPONENT].tailwindTimer = 5; + effect = 1; + } + break; } - else if (B_THUNDERSTORM_TERRAIN == TRUE - && GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM - && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) - { - // overworld weather started rain, so just do electric terrain anim - if (varTerrainTimer == 0) - { - gFieldStatuses = (STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); - } - else - { - gFieldStatuses |= STATUS_FIELD_ELECTRIC_TERRAIN; - gFieldTimers.terrainTimer = varTerrainTimer; - } - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + + if (effect == 1) + BattleScriptPushCursorAndCallback(BattleScript_OverworldStatusStarts); + else if (effect == 2) BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; - } - else if (B_FOG_TERRAIN == TRUE - && (GetCurrentWeather() == WEATHER_FOG_HORIZONTAL || GetCurrentWeather() == WEATHER_FOG_DIAGONAL) - && !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) - { - if (varTerrainTimer == 0) - { - gFieldStatuses = (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); - } - else - { - gFieldStatuses |= STATUS_FIELD_ELECTRIC_TERRAIN; - gFieldTimers.terrainTimer = varTerrainTimer; - } - gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; - BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); - effect++; - } + } + break; + case ABILITYEFFECT_SWITCH_IN_TERRAIN: // terrain starting from overworld weather + if (B_THUNDERSTORM_TERRAIN == TRUE + && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) + && GetCurrentWeather() == WEATHER_RAIN_THUNDERSTORM) + { + // overworld weather started rain, so just do electric terrain anim + gFieldStatuses = (STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_ELECTRIC; + BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); + effect++; + } + else if (B_FOG_TERRAIN == TRUE + && (GetCurrentWeather() == WEATHER_FOG_HORIZONTAL || GetCurrentWeather() == WEATHER_FOG_DIAGONAL) + && !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + { + gFieldStatuses = (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_TERRAIN_PERMANENT); + gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_TERRAIN_SET_MISTY; + BattleScriptPushCursorAndCallback(BattleScript_OverworldTerrain); + effect++; } break; case ABILITYEFFECT_SWITCH_IN_WEATHER: diff --git a/src/overworld.c b/src/overworld.c index 8bad752cc0..e53e556302 100644 --- a/src/overworld.c +++ b/src/overworld.c @@ -401,12 +401,12 @@ void Overworld_ResetStateAfterDigEscRope(void) #if B_RESET_FLAGS_VARS_AFTER_WHITEOUT == TRUE void Overworld_ResetBattleFlagsAndVars(void) { - #if B_VAR_TERRAIN != 0 - VarSet(B_VAR_TERRAIN, 0); + #if B_VAR_STARTING_STATUS != 0 + VarSet(B_VAR_STARTING_STATUS, 0); #endif - #if B_VAR_TERRAIN_TIMER != 0 - VarSet(B_VAR_TERRAIN_TIMER, 0); + #if B_VAR_STARTING_STATUS_TIMER != 0 + VarSet(B_VAR_STARTING_STATUS_TIMER, 0); #endif #if B_VAR_WILD_AI_FLAGS != 0 From 7f6e1e2aea15a8c0f864b5ba8d76422cfd029634 Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Wed, 14 Feb 2024 01:17:23 -0800 Subject: [PATCH 03/22] Add configs for measurement systems and decimal separators (#4183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Allow developers to choose metric or imperial, and their decimal seperator of choice for Pokédex entries - Creates which cleans up the existing implementing of printing height and weight to the pokedex - Developers can choose to use metric or imperial units of measurement in the Pokédex - - Developers can choose to use any character as a decimal seperator in the Pokédex - Allows users to define units and decimal seperators independently - Fixes a bug in Lotad / Seedot house * Fixed compilation issue with agbcc * Updated to include HGSS Dex and address PR Feedback --- include/config.h | 17 +- include/constants/pokedex.h | 20 +++ include/pokedex.h | 1 + include/strings.h | 2 + src/pokedex.c | 306 +++++++++++++++++++++++++++++------- src/pokedex_plus_hgss.c | 108 +------------ src/pokemon_size_record.c | 6 +- src/strings.c | 2 + 8 files changed, 280 insertions(+), 182 deletions(-) diff --git a/include/config.h b/include/config.h index f4be812643..41695a77b4 100644 --- a/include/config.h +++ b/include/config.h @@ -34,16 +34,6 @@ #define LOG_HANDLER (LOG_HANDLER_MGBA_PRINT) #endif -#define ENGLISH - -#ifdef ENGLISH -#define UNITS_IMPERIAL -#define CHAR_DEC_SEPARATOR CHAR_PERIOD // Period is used as a decimal separator only in the UK and the US. -#else -#define UNITS_METRIC -#define CHAR_DEC_SEPARATOR CHAR_COMMA -#endif - // Uncomment to fix some identified minor bugs #define BUGFIX @@ -81,4 +71,11 @@ #define SUMMARY_SCREEN_NATURE_COLORS TRUE // If TRUE, nature-based stat boosts and reductions will be red and blue in the summary screen. #define HQ_RANDOM TRUE // If TRUE, replaces the default RNG with an implementation of SFC32 RNG. May break code that relies on RNG. +// Measurement system constants to be used for UNITS +#define UNITS_IMPERIAL 0 // Inches, feet, pounds +#define UNITS_METRIC 1 // meters, kilograms + +#define UNITS UNITS_IMPERIAL +#define CHAR_DEC_SEPARATOR CHAR_PERIOD // CHAR_PERIOD is used as a decimal separator only in the UK and the US. The rest of the world uses CHAR_COMMA. + #endif // GUARD_CONFIG_H diff --git a/include/constants/pokedex.h b/include/constants/pokedex.h index c03b096f9a..8058f01e5c 100644 --- a/include/constants/pokedex.h +++ b/include/constants/pokedex.h @@ -1307,4 +1307,24 @@ enum { #define HOENN_DEX_COUNT (HOENN_DEX_DEOXYS + 1) +#define DECAGRAMS_IN_POUND 4536 +#define CM_PER_INCH 2.54 +#define CM_PER_INCH_FACTOR (CM_PER_INCH * 100) +#define INCHES_IN_FOOT 12 +#define INCHES_IN_ONE_AND_HALF_FOOT (INCHES_IN_FOOT * 1.5) +#define INCHES_IN_FOOT_FACTOR (INCHES_IN_FOOT * 10) + +#define WEIGHT_HEIGHT_STR_LEN 16 +#define WEIGHT_HEIGHT_STR_MEM (WEIGHT_HEIGHT_STR_LEN * sizeof(u8)) + +#define DEX_HEADER_X 96 +#define DEX_Y_TOP 57 +#define DEX_Y_BOTTOM 73 +#define DEX_MEASUREMENT_X 129 + +#define DEX_HGSS_HEADER_X_PADDING 59 +#define DEX_HGSS_Y_TOP_PADDING 7 +#define DEX_HGSS_Y_BOTTOM_PADDING 4 +#define DEX_HGSS_MEASUREMENT_X_PADDING 51 + #endif // GUARD_CONSTANTS_POKEDEX_H diff --git a/include/pokedex.h b/include/pokedex.h index c53d3e013b..a3f7f80a5b 100644 --- a/include/pokedex.h +++ b/include/pokedex.h @@ -29,5 +29,6 @@ bool16 HasAllHoennMons(void); void ResetPokedexScrollPositions(void); bool16 HasAllMons(void); void CB2_OpenPokedex(void); +void PrintMonMeasurements(u16 species, u32 owned); #endif // GUARD_POKEDEX_H diff --git a/include/strings.h b/include/strings.h index bb4c3f9de2..431f90df16 100644 --- a/include/strings.h +++ b/include/strings.h @@ -531,7 +531,9 @@ extern const u8 gText_PokedexRegistration[]; extern const u8 gText_NumberClear01[]; extern const u8 gText_5MarksPokemon[]; extern const u8 gText_UnkHeight[]; +extern const u8 gText_UnkHeightMetric[]; extern const u8 gText_UnkWeight[]; +extern const u8 gText_UnkWeightMetric[]; extern const u8 gText_HTHeight[]; extern const u8 gText_WTWeight[]; extern const u8 gText_SearchingPleaseWait[]; diff --git a/src/pokedex.c b/src/pokedex.c index a7aa98c424..682c000b34 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -274,8 +274,19 @@ static void Task_HandleCaughtMonPageInput(u8); static void Task_ExitCaughtMonPage(u8); static void SpriteCB_SlideCaughtMonToCenter(struct Sprite *sprite); static void PrintMonInfo(u32 num, u32, u32 owned, u32 newEntry); -static void PrintMonHeight(u16 height, u8 left, u8 top); -static void PrintMonWeight(u16 weight, u8 left, u8 top); +static u32 GetMeasurementTextPositions(u32 textElement); +static void PrintUnknownMonMeasurements(void); +static u8* GetUnknownMonHeightString(void); +static u8* GetUnknownMonWeightString(void); +static u8* ReplaceDecimalSeparator(const u8* originalString); +static void PrintOwnedMonMeasurements(u16 species); +static void PrintOwnedMonHeight(u16 species); +static void PrintOwnedMonWeight(u16 species); +static u8* ConvertMonHeightToImperialString(u32 height); +static u8* ConvertMonHeightToMetricString(u32 height); +static u8* ConvertMonWeightToImperialString(u32 weight); +static u8* ConvertMonWeightToMetricString(u32 weight); +static u8* ConvertMeasurementToMetricString(u16 num, u32* index); static void ResetOtherVideoRegisters(u16); static u8 PrintCryScreenSpeciesName(u8, u16, u8, u8); static void PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top); @@ -4141,7 +4152,7 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) const u8 *name; const u8 *category; const u8 *description; - u8 digitCount = (NATIONAL_DEX_COUNT > 999 && IsNationalPokedexEnabled()) ? 4 : 3; + u8 digitCount = (NATIONAL_DEX_COUNT > 999 && IsNationalPokedexEnabled()) ? 4 : 3; if (newEntry) PrintInfoScreenText(gText_PokedexRegistration, GetStringCenterAlignXOffset(FONT_NORMAL, gText_PokedexRegistration, DISPLAY_WIDTH), 0); @@ -4169,18 +4180,7 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) category = gText_5MarksPokemon; } PrintInfoScreenText(category, 0x64, 0x29); - PrintInfoScreenText(gText_HTHeight, 0x60, 0x39); - PrintInfoScreenText(gText_WTWeight, 0x60, 0x49); - if (owned) - { - PrintMonHeight(GetSpeciesHeight(species), 0x81, 0x39); - PrintMonWeight(GetSpeciesWeight(species), 0x81, 0x49); - } - else - { - PrintInfoScreenText(gText_UnkHeight, 0x81, 0x39); - PrintInfoScreenText(gText_UnkWeight, 0x81, 0x49); - } + PrintMonMeasurements(species,owned); if (owned) description = GetSpeciesPokedexDescription(species); else @@ -4188,95 +4188,279 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) PrintInfoScreenText(description, GetStringCenterAlignXOffset(FONT_NORMAL, description, DISPLAY_WIDTH), 95); } -static void PrintMonHeight(u16 height, u8 left, u8 top) +void PrintMonMeasurements(u16 species, u32 owned) { - u8 buffer[16]; - u32 inches, feet; - u8 i = 0; + u32 x = GetMeasurementTextPositions(DEX_HEADER_X); + u32 yTop = GetMeasurementTextPositions(DEX_Y_TOP); + u32 yBottom = GetMeasurementTextPositions(DEX_Y_BOTTOM); - inches = (height * 10000) / 254; + PrintInfoScreenText(gText_HTHeight, x, yTop); + PrintInfoScreenText(gText_WTWeight, x, yBottom); + + if (owned) + PrintOwnedMonMeasurements(species); + else + PrintUnknownMonMeasurements(); +} + +static u32 GetMeasurementTextPositions(u32 textElement) +{ + if (!POKEDEX_PLUS_HGSS) + return textElement; + + switch(textElement) + { + case DEX_HEADER_X: + return (DEX_HEADER_X + DEX_HGSS_HEADER_X_PADDING); + case DEX_Y_TOP: + return (DEX_Y_TOP + DEX_HGSS_Y_TOP_PADDING); + case DEX_Y_BOTTOM: + return (DEX_Y_BOTTOM + DEX_HGSS_Y_BOTTOM_PADDING); + default: + case DEX_MEASUREMENT_X: + return (DEX_MEASUREMENT_X + DEX_HGSS_MEASUREMENT_X_PADDING); + } +} + +static void PrintUnknownMonMeasurements(void) +{ + u8* heightString = GetUnknownMonHeightString(); + u8* weightString = GetUnknownMonWeightString(); + + u32 x = GetMeasurementTextPositions(DEX_MEASUREMENT_X); + u32 yTop = GetMeasurementTextPositions(DEX_Y_TOP); + u32 yBottom = GetMeasurementTextPositions(DEX_Y_BOTTOM); + + PrintInfoScreenText(heightString, x, yTop); + PrintInfoScreenText(weightString, x, yBottom); + + Free(heightString); + Free(weightString); +} + +static u8* GetUnknownMonHeightString(void) +{ + if (UNITS == UNITS_IMPERIAL) + return ReplaceDecimalSeparator(gText_UnkHeight); + else + return ReplaceDecimalSeparator(gText_UnkHeightMetric); +} + +static u8* GetUnknownMonWeightString(void) +{ + if (UNITS == UNITS_IMPERIAL) + return ReplaceDecimalSeparator(gText_UnkWeight); + else + return ReplaceDecimalSeparator(gText_UnkWeightMetric); +} + +static u8* ReplaceDecimalSeparator(const u8* originalString) +{ + bool32 replaced = FALSE; + u32 length = StringLength(originalString), i; + u8* modifiedString = Alloc(WEIGHT_HEIGHT_STR_MEM); + + for (i = 0; i < length; i++) + { + if ((originalString[i] != CHAR_PERIOD) || replaced) + { + modifiedString[i] = originalString[i]; + continue; + } + + modifiedString[i] = CHAR_DEC_SEPARATOR; + replaced = TRUE; + } + modifiedString[length] = EOS; + return modifiedString; +} + +static void PrintOwnedMonMeasurements(u16 species) +{ + PrintOwnedMonHeight(species); + PrintOwnedMonWeight(species); +} + +static void PrintOwnedMonHeight(u16 species) +{ + u32 height = GetSpeciesHeight(species); + u8* heightString; + + u32 x = GetMeasurementTextPositions(DEX_MEASUREMENT_X); + u32 yTop = GetMeasurementTextPositions(DEX_Y_TOP); + + if (UNITS == UNITS_IMPERIAL) + heightString = ConvertMonHeightToImperialString(height); + else + heightString = ConvertMonHeightToMetricString(height); + + PrintInfoScreenText(heightString, x, yTop); + Free(heightString); +} + +static void PrintOwnedMonWeight(u16 species) +{ + u32 weight = GetSpeciesWeight(species); + u8* weightString; + u32 x = GetMeasurementTextPositions(DEX_MEASUREMENT_X); + u32 yBottom = GetMeasurementTextPositions(DEX_Y_BOTTOM); + + if (UNITS == UNITS_IMPERIAL) + weightString = ConvertMonWeightToImperialString(weight); + else + weightString = ConvertMonWeightToMetricString(weight); + + PrintInfoScreenText(weightString, x, yBottom); + Free(weightString); +} + +static u8* ConvertMonHeightToImperialString(u32 height) +{ + u8* heightString = Alloc(WEIGHT_HEIGHT_STR_MEM); + u32 inches, feet, index = 0; + + inches = (height * 10000) / CM_PER_INCH_FACTOR; if (inches % 10 >= 5) inches += 10; - feet = inches / 120; - inches = (inches - (feet * 120)) / 10; + feet = inches / INCHES_IN_FOOT_FACTOR; + inches = (inches - (feet * INCHES_IN_FOOT_FACTOR)) / 10; - buffer[i++] = EXT_CTRL_CODE_BEGIN; - buffer[i++] = EXT_CTRL_CODE_CLEAR_TO; + heightString[index++] = EXT_CTRL_CODE_BEGIN; + heightString[index++] = EXT_CTRL_CODE_CLEAR_TO; if (feet / 10 == 0) { - buffer[i++] = 18; - buffer[i++] = feet + CHAR_0; + heightString[index++] = INCHES_IN_ONE_AND_HALF_FOOT; + heightString[index++] = feet + CHAR_0; } else { - buffer[i++] = 12; - buffer[i++] = feet / 10 + CHAR_0; - buffer[i++] = (feet % 10) + CHAR_0; + heightString[index++] = INCHES_IN_FOOT; + heightString[index++] = feet / 10 + CHAR_0; + heightString[index++] = (feet % 10) + CHAR_0; } - buffer[i++] = CHAR_SGL_QUOTE_RIGHT; - buffer[i++] = (inches / 10) + CHAR_0; - buffer[i++] = (inches % 10) + CHAR_0; - buffer[i++] = CHAR_DBL_QUOTE_RIGHT; - buffer[i++] = EOS; - PrintInfoScreenText(buffer, left, top); + heightString[index++] = CHAR_SGL_QUOTE_RIGHT; + heightString[index++] = (inches / 10) + CHAR_0; + heightString[index++] = (inches % 10) + CHAR_0; + heightString[index++] = CHAR_DBL_QUOTE_RIGHT; + heightString[index++] = EOS; + + return heightString; } -static void PrintMonWeight(u16 weight, u8 left, u8 top) +static u8* ConvertMonHeightToMetricString(u32 height) { - u8 buffer[16]; - bool8 output; - u8 i; - u32 lbs = (weight * 100000) / 4536; + u32 index = 0; + u8* heightString = ConvertMeasurementToMetricString(height, &index); + + heightString[index++] = CHAR_m; + heightString[index++] = EOS; + return heightString; +} + +static u8* ConvertMonWeightToImperialString(u32 weight) +{ + u8* weightString = Alloc(WEIGHT_HEIGHT_STR_MEM); + bool32 output = FALSE; + u32 index = 0, lbs = (weight * 100000) / DECAGRAMS_IN_POUND; if (lbs % 10u >= 5) lbs += 10; - i = 0; - output = FALSE; - if ((buffer[i] = (lbs / 100000) + CHAR_0) == CHAR_0 && !output) + if ((weightString[index] = (lbs / 100000) + CHAR_0) == CHAR_0 && !output) { - buffer[i++] = CHAR_SPACER; + weightString[index++] = CHAR_SPACER; } else { output = TRUE; - i++; + index++; } lbs %= 100000; - if ((buffer[i] = (lbs / 10000) + CHAR_0) == CHAR_0 && !output) + if ((weightString[index] = (lbs / 10000) + CHAR_0) == CHAR_0 && !output) { - buffer[i++] = CHAR_SPACER; + weightString[index++] = CHAR_SPACER; } else { output = TRUE; - i++; + index++; } lbs %= 10000; - if ((buffer[i] = (lbs / 1000) + CHAR_0) == CHAR_0 && !output) + if ((weightString[index] = (lbs / 1000) + CHAR_0) == CHAR_0 && !output) { - buffer[i++] = CHAR_SPACER; + weightString[index++] = CHAR_SPACER; } else { output = TRUE; - i++; + index++; } lbs %= 1000; - buffer[i++] = (lbs / 100) + CHAR_0; + weightString[index++] = (lbs / 100) + CHAR_0; lbs %= 100; - buffer[i++] = CHAR_PERIOD; - buffer[i++] = (lbs / 10) + CHAR_0; - buffer[i++] = CHAR_SPACE; - buffer[i++] = CHAR_l; - buffer[i++] = CHAR_b; - buffer[i++] = CHAR_s; - buffer[i++] = CHAR_PERIOD; - buffer[i++] = EOS; - PrintInfoScreenText(buffer, left, top); + weightString[index++] = CHAR_DEC_SEPARATOR; + weightString[index++] = (lbs / 10) + CHAR_0; + weightString[index++] = CHAR_SPACE; + weightString[index++] = CHAR_l; + weightString[index++] = CHAR_b; + weightString[index++] = CHAR_s; + weightString[index++] = CHAR_PERIOD; + weightString[index++] = EOS; + + return weightString; +} + +static u8* ConvertMonWeightToMetricString(u32 weight) +{ + u32 index = 0; + u8* weightString = ConvertMeasurementToMetricString(weight, &index); + + weightString[index++] = CHAR_k; + weightString[index++] = CHAR_g; + weightString[index++] = CHAR_PERIOD; + weightString[index++] = EOS; + return weightString; +} + +static u8* ConvertMeasurementToMetricString(u16 num, u32* index) +{ + u8* string = Alloc(WEIGHT_HEIGHT_STR_MEM); + bool32 outputted = FALSE; + u32 result; + + result = num / 1000; + if (result == 0) + { + string[(*index)++] = CHAR_SPACER; + outputted = FALSE; + } + else + { + string[(*index)++] = CHAR_0 + result; + outputted = TRUE; + } + + result = (num % 1000) / 100; + if (result == 0 && !outputted) + { + string[(*index)++] = CHAR_SPACER; + outputted = FALSE; + } + else + { + string[(*index)++] = CHAR_0 + result; + outputted = TRUE; + } + + string[(*index)++] = CHAR_0 + ((num % 1000) % 100) / 10; + string[(*index)++] = CHAR_DEC_SEPARATOR; + string[(*index)++] = CHAR_0 + ((num % 1000) % 100) % 10; + string[(*index)++] = CHAR_SPACE; + + return string; } s8 GetSetPokedexFlag(u16 nationalDexNo, u8 caseID) diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index 0e3817d4ac..4910fe6e66 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -536,8 +536,6 @@ static void Task_HandleCaughtMonPageInput(u8); static void Task_ExitCaughtMonPage(u8); static void SpriteCB_SlideCaughtMonToCenter(struct Sprite *sprite); static void PrintMonInfo(u32 num, u32, u32 owned, u32 newEntry); -static void PrintMonHeight(u16 height, u8 left, u8 top); -static void PrintMonWeight(u16 weight, u8 left, u8 top); static void ResetOtherVideoRegisters(u16); static u8 PrintCryScreenSpeciesName(u8, u16, u8, u8); static void PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top); @@ -4487,18 +4485,7 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) category = gText_5MarksPokemon; } PrintInfoScreenText(category, 123, 31); - PrintInfoScreenText(gText_HTHeight, 155, 64); - PrintInfoScreenText(gText_WTWeight, 155, 77); - if (owned) - { - PrintMonHeight(GetSpeciesHeight(species), 180, 64); - PrintMonWeight(GetSpeciesWeight(species), 180, 77); - } - else - { - PrintInfoScreenText(gText_UnkHeight, 180, 64); - PrintInfoScreenText(gText_UnkWeight, 180, 77); - } + PrintMonMeasurements(species,owned); if (owned) description = GetSpeciesPokedexDescription(species); else @@ -4510,97 +4497,6 @@ static void PrintMonInfo(u32 num, u32 value, u32 owned, u32 newEntry) PrintCurrentSpeciesTypeInfo(newEntry, species); } -static void PrintMonHeight(u16 height, u8 left, u8 top) -{ - u8 buffer[16]; - u32 inches, feet; - u8 i = 0; - - inches = (height * 10000) / 254; - if (inches % 10 >= 5) - inches += 10; - feet = inches / 120; - inches = (inches - (feet * 120)) / 10; - - buffer[i++] = EXT_CTRL_CODE_BEGIN; - buffer[i++] = EXT_CTRL_CODE_CLEAR_TO; - if (feet / 10 == 0) - { - buffer[i++] = 18; - buffer[i++] = feet + CHAR_0; - } - else - { - buffer[i++] = 12; - buffer[i++] = feet / 10 + CHAR_0; - buffer[i++] = (feet % 10) + CHAR_0; - } - buffer[i++] = CHAR_SGL_QUOTE_RIGHT; - buffer[i++] = (inches / 10) + CHAR_0; - buffer[i++] = (inches % 10) + CHAR_0; - buffer[i++] = CHAR_DBL_QUOTE_RIGHT; - buffer[i++] = EOS; - PrintInfoScreenText(buffer, left, top); -} - -static void PrintMonWeight(u16 weight, u8 left, u8 top) -{ - u8 buffer[16]; - bool8 output; - u8 i; - u32 lbs = (weight * 100000) / 4536; - - if (lbs % 10u >= 5) - lbs += 10; - i = 0; - output = FALSE; - - if ((buffer[i] = (lbs / 100000) + CHAR_0) == CHAR_0 && !output) - { - buffer[i++] = CHAR_SPACER; - } - else - { - output = TRUE; - i++; - } - - lbs %= 100000; - if ((buffer[i] = (lbs / 10000) + CHAR_0) == CHAR_0 && !output) - { - buffer[i++] = CHAR_SPACER; - } - else - { - output = TRUE; - i++; - } - - lbs %= 10000; - if ((buffer[i] = (lbs / 1000) + CHAR_0) == CHAR_0 && !output) - { - buffer[i++] = CHAR_SPACER; - } - else - { - output = TRUE; - i++; - } - - lbs %= 1000; - buffer[i++] = (lbs / 100) + CHAR_0; - lbs %= 100; - buffer[i++] = CHAR_PERIOD; - buffer[i++] = (lbs / 10) + CHAR_0; - buffer[i++] = CHAR_SPACE; - buffer[i++] = CHAR_l; - buffer[i++] = CHAR_b; - buffer[i++] = CHAR_s; - buffer[i++] = CHAR_PERIOD; - buffer[i++] = EOS; - PrintInfoScreenText(buffer, left, top); -} - // Unused in the English version, used to print height/weight in versions which use metric system. static void UNUSED PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top) { @@ -5294,7 +5190,7 @@ static bool8 CalculateMoves(void) if (j >= NUM_TECHNICAL_MACHINES + NUM_HIDDEN_MACHINES) { numTutorMoves++; - + sStatsMoves[movesTotal] = move; movesTotal++; } diff --git a/src/pokemon_size_record.c b/src/pokemon_size_record.c index 3f98e65ba6..54341b96e9 100644 --- a/src/pokemon_size_record.c +++ b/src/pokemon_size_record.c @@ -47,8 +47,6 @@ static const u8 sGiftRibbonsMonDataIds[GIFT_RIBBONS_COUNT - 4] = extern const u8 gText_DecimalPoint[]; extern const u8 gText_Marco[]; -#define CM_PER_INCH 2.54 - static u32 GetMonSizeHash(struct Pokemon *pkmn) { u16 personality = GetMonData(pkmn, MON_DATA_PERSONALITY); @@ -95,10 +93,8 @@ static u32 GetMonSize(u16 species, u16 b) static void FormatMonSizeRecord(u8 *string, u32 size) { -#ifdef UNITS_IMPERIAL //Convert size from centimeters to inches - size = (f64)(size * 10) / (CM_PER_INCH * 10); -#endif + size = (f64)(size * 10) / (CM_PER_INCH * 10); string = ConvertIntToDecimalStringN(string, size / 10, STR_CONV_MODE_LEFT_ALIGN, 8); string = StringAppend(string, gText_DecimalPoint); diff --git a/src/strings.c b/src/strings.c index 35f610017a..c98ff2519e 100644 --- a/src/strings.c +++ b/src/strings.c @@ -101,7 +101,9 @@ const u8 gText_DefaultNameHalie[] = _("HALIE"); const u8 gText_ThisIsAPokemon[] = _("This is what we call a “POKéMON.”{PAUSE 96}\p"); const u8 gText_5MarksPokemon[] = _("????? POKéMON"); const u8 gText_UnkHeight[] = _("{CLEAR_TO 0x0C}??'??”"); +const u8 gText_UnkHeightMetric[] = _("???.? m"); const u8 gText_UnkWeight[] = _("????.? lbs."); +const u8 gText_UnkWeightMetric[] = _("???.? kg."); const u8 gText_EmptyPkmnCategory[] = _(" POKéMON"); // Unused const u8 gText_EmptyHeight[] = _("{CLEAR_TO 0x0C} ' ”"); // Unused const u8 gText_EmptyWeight[] = _(" . lbs."); // Unused From eb7ddeb66cf433502d7b92e70f8d60aa71b7b0df Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Thu, 15 Feb 2024 04:01:34 -0300 Subject: [PATCH 04/22] Updated the way in which ScriptGiveMonParameterized and ScrCmd_givemon chooe a default ability (#4192) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated the way in which ScrCmd_givemon and ScriptGiveMon assign a default ability When an abilityNum is not assigned in a call to givemon performed inside of an overworld script, ScriptGiveMonParameterized will make sure to generate an abilityNum of 0 or 1 in the same way vanilla does it; by defaulting to 0, and then tweaking it based on the least relevant bit of the Pokémon's personality. ScriptGiveMon will set the default ability of a Pokémon in the same way now too, because even though it was rewritten in #3924, it should ideally produce a Pokémon in a similar way than vanilla does it. * Removed pointless abilityNum setup in ScriptGiveMonParameterized --- include/constants/pokemon.h | 3 +++ src/script_pokemon_util.c | 10 +++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 13b76ed328..49de31f1b3 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -323,6 +323,9 @@ #define NUM_NORMAL_ABILITY_SLOTS 2 #define NUM_HIDDEN_ABILITY_SLOTS 1 +// Used as a signal for givemon to generate a default ability in the vanilla Pokémon Emerald way. +#define NUM_ABILITY_VANILLA 0xFF + #define LEGENDARY_PERFECT_IV_COUNT 3 #endif // GUARD_CONSTANTS_POKEMON_H diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index f37a70a146..8ed44e8f59 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -345,7 +345,11 @@ u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 natu } // ability - if (abilityNum >= NUM_ABILITY_SLOTS || GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE) + if (abilityNum == NUM_ABILITY_VANILLA) + { + abilityNum = GetMonData(&mon, MON_DATA_PERSONALITY) & 1; + } + else if (abilityNum > NUM_NORMAL_ABILITY_SLOTS || GetAbilityBySpecies(species, abilityNum) == ABILITY_NONE) { do { abilityNum = Random() % NUM_ABILITY_SLOTS; // includes hidden abilities @@ -410,7 +414,7 @@ u32 ScriptGiveMon(u16 species, u8 level, u16 item) MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1}; // ScriptGiveMonParameterized won't touch the stats' IV. u16 moves[MAX_MON_MOVES] = {MOVE_NONE, MOVE_NONE, MOVE_NONE, MOVE_NONE}; - return ScriptGiveMonParameterized(species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_SLOTS, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES); + return ScriptGiveMonParameterized(species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_VANILLA, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES); } #define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_) @@ -424,7 +428,7 @@ void ScrCmd_givemon(struct ScriptContext *ctx) u16 item = PARSE_FLAG(0, ITEM_NONE); u8 ball = PARSE_FLAG(1, ITEM_POKE_BALL); u8 nature = PARSE_FLAG(2, NUM_NATURES); - u8 abilityNum = PARSE_FLAG(3, NUM_ABILITY_SLOTS); + u8 abilityNum = PARSE_FLAG(3, NUM_ABILITY_VANILLA); u8 gender = PARSE_FLAG(4, MON_GENDERLESS); // TODO: Find a better way to assign a random gender. u8 hpEv = PARSE_FLAG(5, 0); u8 atkEv = PARSE_FLAG(6, 0); From c21ab741f7dd43bf7546deccf258b3f5a053b634 Mon Sep 17 00:00:00 2001 From: Martin Griffin Date: Thu, 15 Feb 2024 07:07:28 +0000 Subject: [PATCH 05/22] randompercentage, randomelement (#4189) 'randompercentage X' sets VAR_RESULT to TRUE X% of the time, or FALSE 100-X% of the time. 'randomelement X, Y, ...' sets VAR_RESULT to one of X, Y, ... with equal probability. --- asm/macros/event.inc | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/asm/macros/event.inc b/asm/macros/event.inc index 5715d91ea3..b93798f457 100644 --- a/asm/macros/event.inc +++ b/asm/macros/event.inc @@ -2133,3 +2133,34 @@ callnative ToggleGigantamaxFactor .2byte \slot .endm + + @ Sets VAR_RESULT to one of the arguments (via setorcopyvar). + .macro randomelement element:req, elements:vararg + .set _randomelement_n, 0 + .irp el, \element, \elements + .set _randomelement_n, _randomelement_n + 1 + .endr + random _randomelement_n + + .set _randomelement_n, 0 + .irp el, \element, \elements + goto_if_ne VAR_RESULT, _randomelement_n, 1f + setorcopyvar VAR_RESULT, \el + goto 2f + 1: + .set _randomelement_n, _randomelement_n + 1 + .endr + 2: + .endm + + @ Sets VAR_RESULT to TRUE with probability 'percent', and FALSE + @ with probability '100% - percent'. + .macro randompercentage percent:req + random 100 + goto_if_lt VAR_RESULT, \percent, 1f + setvar VAR_RESULT, FALSE + goto 2f + 1: + setvar VAR_RESULT, TRUE + 2: + .endm From ab2260a81ee1da9c7c11f6fb58adc52d814ef0c6 Mon Sep 17 00:00:00 2001 From: cmy2008 <65344456+cmy2008@users.noreply.github.com> Date: Thu, 15 Feb 2024 17:21:35 +0800 Subject: [PATCH 06/22] Deleted a space (#4194) --- src/pokemon_animation.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pokemon_animation.c b/src/pokemon_animation.c index 95557e431f..69f6003a4e 100644 --- a/src/pokemon_animation.c +++ b/src/pokemon_animation.c @@ -425,7 +425,7 @@ static const u8 sBackAnimationIds[] = [(BACK_ANIM_SHAKE_FLASH_YELLOW - 1) * 3] = ANIM_SHAKE_FLASH_YELLOW_FAST, ANIM_SHAKE_FLASH_YELLOW, ANIM_SHAKE_FLASH_YELLOW_SLOW, [(BACK_ANIM_SHAKE_GLOW_RED - 1) * 3] = ANIM_SHAKE_GLOW_RED_FAST, ANIM_SHAKE_GLOW_RED, ANIM_SHAKE_GLOW_RED_SLOW, [(BACK_ANIM_SHAKE_GLOW_GREEN - 1) * 3] = ANIM_SHAKE_GLOW_GREEN_FAST, ANIM_SHAKE_GLOW_GREEN, ANIM_SHAKE_GLOW_GREEN_SLOW, - [(BACK_ANIM_SHAKE_GLOW_BLUE - 1) * 3] = ANIM_SHAKE_GLOW_BLUE_FAST, ANIM_SHAKE_GLOW_BLUE, ANIM_SHAKE_GLOW_BLUE_SLOW, + [(BACK_ANIM_SHAKE_GLOW_BLUE - 1) * 3] = ANIM_SHAKE_GLOW_BLUE_FAST, ANIM_SHAKE_GLOW_BLUE, ANIM_SHAKE_GLOW_BLUE_SLOW, }; static const u8 sBackAnimNatureModTable[NUM_NATURES] = From cc22fef6c83e8defc737b59dd0bdd27a6fde7919 Mon Sep 17 00:00:00 2001 From: psf <77138753+pkmnsnfrn@users.noreply.github.com> Date: Thu, 15 Feb 2024 01:23:11 -0800 Subject: [PATCH 07/22] - Fixes Seedot and Lotad House to give measurements based on the unit system and decimal seperator chosen by the developer. (#4193) - Created `ConvertMonHeightToString` and `ConvertMonWeightToString` for developers to use --- .../scripts.inc | 12 ++++---- include/pokedex.h | 2 ++ src/pokedex.c | 30 ++++++++++++------- src/pokemon_size_record.c | 23 ++++++++++---- 4 files changed, 46 insertions(+), 21 deletions(-) diff --git a/data/maps/SootopolisCity_LotadAndSeedotHouse/scripts.inc b/data/maps/SootopolisCity_LotadAndSeedotHouse/scripts.inc index 52bdf3acee..0463906506 100644 --- a/data/maps/SootopolisCity_LotadAndSeedotHouse/scripts.inc +++ b/data/maps/SootopolisCity_LotadAndSeedotHouse/scripts.inc @@ -116,7 +116,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_PleaseShowMeBigSeedot: .string "P-p-please, show me!$" SootopolisCity_LotadAndSeedotHouse_Text_GoshMightBeBiggerThanLotad: - .string "{STR_VAR_2} inches!\n" + .string "{STR_VAR_2}!\n" .string "Oh, my gosh, this is a big one!\p" .string "It might even beat the big LOTAD\n" .string "my younger brother saw!\p" @@ -132,7 +132,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_BagCrammedFull1: .string "Your BAG is crammed full.$" SootopolisCity_LotadAndSeedotHouse_Text_SeenBiggerSeedot: - .string "{STR_VAR_2} inches, is it?\p" + .string "{STR_VAR_2}, is it?\p" .string "Hmm… I've seen a bigger SEEDOT\n" .string "than this one.$" @@ -148,7 +148,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_DontHaveBigSeedot: SootopolisCity_LotadAndSeedotHouse_Text_BiggestSeedotInHistory: .string "The biggest SEEDOT in history!\n" - .string "{STR_VAR_2}'s {STR_VAR_3}-inch giant!\p" + .string "{STR_VAR_2}'s {STR_VAR_3} giant!\p" .string "A SEEDOT bigger than a LOTAD\n" .string "always wanted!$" @@ -164,7 +164,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_PleaseShowMeBigLotad: .string "P-p-please show me!$" SootopolisCity_LotadAndSeedotHouse_Text_WowMightBeBiggerThanSeedot: - .string "{STR_VAR_2} inches!\n" + .string "{STR_VAR_2}!\n" .string "Wow, that is big!\p" .string "It might be even bigger than the huge\n" .string "SEEDOT my big brother saw.\p" @@ -180,7 +180,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_BagCrammedFull2: .string "Your BAG is crammed full.$" SootopolisCity_LotadAndSeedotHouse_Text_SeenBiggerLotad: - .string "{STR_VAR_2} inches?\p" + .string "{STR_VAR_2}?\p" .string "Hmm… I've seen a bigger LOTAD\n" .string "than this one here.$" @@ -196,7 +196,7 @@ SootopolisCity_LotadAndSeedotHouse_Text_DontHaveBigLotad: SootopolisCity_LotadAndSeedotHouse_Text_BiggestLotadInHistory: .string "The biggest LOTAD in history!\n" - .string "{STR_VAR_2}'s {STR_VAR_3}-inch colossus!\p" + .string "{STR_VAR_2}'s {STR_VAR_3} colossus!\p" .string "A LOTAD bigger than a SEEDOT\n" .string "always wanted!$" diff --git a/include/pokedex.h b/include/pokedex.h index a3f7f80a5b..be861fe682 100644 --- a/include/pokedex.h +++ b/include/pokedex.h @@ -30,5 +30,7 @@ void ResetPokedexScrollPositions(void); bool16 HasAllMons(void); void CB2_OpenPokedex(void); void PrintMonMeasurements(u16 species, u32 owned); +u8* ConvertMonHeightToString(u32 height); +u8* ConvertMonWeightToString(u32 weight); #endif // GUARD_POKEDEX_H diff --git a/src/pokedex.c b/src/pokedex.c index 682c000b34..74a95aa1d7 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -286,7 +286,7 @@ static u8* ConvertMonHeightToImperialString(u32 height); static u8* ConvertMonHeightToMetricString(u32 height); static u8* ConvertMonWeightToImperialString(u32 weight); static u8* ConvertMonWeightToMetricString(u32 weight); -static u8* ConvertMeasurementToMetricString(u16 num, u32* index); +static u8* ConvertMeasurementToMetricString(u32 num, u32* index); static void ResetOtherVideoRegisters(u16); static u8 PrintCryScreenSpeciesName(u8, u16, u8, u8); static void PrintDecimalNum(u8 windowId, u16 num, u8 left, u8 top); @@ -4289,15 +4289,20 @@ static void PrintOwnedMonHeight(u16 species) u32 x = GetMeasurementTextPositions(DEX_MEASUREMENT_X); u32 yTop = GetMeasurementTextPositions(DEX_Y_TOP); - if (UNITS == UNITS_IMPERIAL) - heightString = ConvertMonHeightToImperialString(height); - else - heightString = ConvertMonHeightToMetricString(height); + heightString = ConvertMonHeightToString(height); PrintInfoScreenText(heightString, x, yTop); Free(heightString); } +u8* ConvertMonHeightToString(u32 height) +{ + if (UNITS == UNITS_IMPERIAL) + return ConvertMonHeightToImperialString(height); + else + return ConvertMonHeightToMetricString(height); +} + static void PrintOwnedMonWeight(u16 species) { u32 weight = GetSpeciesWeight(species); @@ -4305,15 +4310,20 @@ static void PrintOwnedMonWeight(u16 species) u32 x = GetMeasurementTextPositions(DEX_MEASUREMENT_X); u32 yBottom = GetMeasurementTextPositions(DEX_Y_BOTTOM); - if (UNITS == UNITS_IMPERIAL) - weightString = ConvertMonWeightToImperialString(weight); - else - weightString = ConvertMonWeightToMetricString(weight); + weightString = ConvertMonWeightToString(weight); PrintInfoScreenText(weightString, x, yBottom); Free(weightString); } +u8* ConvertMonWeightToString(u32 weight) +{ + if (UNITS == UNITS_IMPERIAL) + return ConvertMonWeightToImperialString(weight); + else + return ConvertMonWeightToMetricString(weight); +} + static u8* ConvertMonHeightToImperialString(u32 height) { u8* heightString = Alloc(WEIGHT_HEIGHT_STR_MEM); @@ -4425,7 +4435,7 @@ static u8* ConvertMonWeightToMetricString(u32 weight) return weightString; } -static u8* ConvertMeasurementToMetricString(u16 num, u32* index) +static u8* ConvertMeasurementToMetricString(u32 num, u32* index) { u8* string = Alloc(WEIGHT_HEIGHT_STR_MEM); bool32 outputted = FALSE; diff --git a/src/pokemon_size_record.c b/src/pokemon_size_record.c index 54341b96e9..d7eaa5383f 100644 --- a/src/pokemon_size_record.c +++ b/src/pokemon_size_record.c @@ -8,6 +8,7 @@ #include "text.h" #define DEFAULT_MAX_SIZE 0x8000 // was 0x8100 in Ruby/Sapphire +static u8* ReturnHeightStringNoWhitespace(u32 size); struct UnknownStruct { @@ -93,12 +94,24 @@ static u32 GetMonSize(u16 species, u16 b) static void FormatMonSizeRecord(u8 *string, u32 size) { - //Convert size from centimeters to inches - size = (f64)(size * 10) / (CM_PER_INCH * 10); + size = (f64)(size / 100); + StringCopy(string,ReturnHeightStringNoWhitespace(size)); +} - string = ConvertIntToDecimalStringN(string, size / 10, STR_CONV_MODE_LEFT_ALIGN, 8); - string = StringAppend(string, gText_DecimalPoint); - ConvertIntToDecimalStringN(string, size % 10, STR_CONV_MODE_LEFT_ALIGN, 1); +static u8* ReturnHeightStringNoWhitespace(u32 size) +{ + u8* heightStr = ConvertMonHeightToString(size); + u32 length = StringLength(heightStr); + u32 i = 0, j = 0; + + while (i < length && !(heightStr[i] >= CHAR_0 && heightStr[i] <= CHAR_9)) + i++; + + while (i < length) + heightStr[j++] = heightStr[i++]; + + heightStr[j] = EOS; + return heightStr; } static u8 CompareMonSize(u16 species, u16 *sizeRecord) From 6a61e6525e28b8e7b4e0985363cfcfc7a43ffcc0 Mon Sep 17 00:00:00 2001 From: Bassoonian Date: Thu, 15 Feb 2024 13:07:31 +0100 Subject: [PATCH 08/22] Fix height call (#4195) --- src/pokemon_size_record.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pokemon_size_record.c b/src/pokemon_size_record.c index 3f98e65ba6..71d3c9dd49 100644 --- a/src/pokemon_size_record.c +++ b/src/pokemon_size_record.c @@ -84,7 +84,7 @@ static u32 GetMonSize(u16 species, u16 b) u32 height; u32 var; - height = GetSpeciesWeight(species); + height = GetSpeciesHeight(species); var = TranslateBigMonSizeTableIndex(b); unk0 = sBigMonSizeTable[var].unk0; unk2 = sBigMonSizeTable[var].unk2; From 1f349e0fb9753f070bc6f0504b5ae1ea47fc1bc3 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Thu, 15 Feb 2024 11:22:25 -0300 Subject: [PATCH 09/22] Renamed NUM_ABILITY_VANILLA to NUM_ABILITY_PERSONALITY (#4196) --- include/constants/pokemon.h | 4 ++-- src/script_pokemon_util.c | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/constants/pokemon.h b/include/constants/pokemon.h index 49de31f1b3..186ef5de59 100644 --- a/include/constants/pokemon.h +++ b/include/constants/pokemon.h @@ -323,8 +323,8 @@ #define NUM_NORMAL_ABILITY_SLOTS 2 #define NUM_HIDDEN_ABILITY_SLOTS 1 -// Used as a signal for givemon to generate a default ability in the vanilla Pokémon Emerald way. -#define NUM_ABILITY_VANILLA 0xFF +// Used as a signal for givemon to generate a default ability by personality. +#define NUM_ABILITY_PERSONALITY 0xFF #define LEGENDARY_PERFECT_IV_COUNT 3 diff --git a/src/script_pokemon_util.c b/src/script_pokemon_util.c index 8ed44e8f59..358e02e5d9 100644 --- a/src/script_pokemon_util.c +++ b/src/script_pokemon_util.c @@ -345,7 +345,7 @@ u32 ScriptGiveMonParameterized(u16 species, u8 level, u16 item, u8 ball, u8 natu } // ability - if (abilityNum == NUM_ABILITY_VANILLA) + if (abilityNum == NUM_ABILITY_PERSONALITY) { abilityNum = GetMonData(&mon, MON_DATA_PERSONALITY) & 1; } @@ -414,7 +414,7 @@ u32 ScriptGiveMon(u16 species, u8 level, u16 item) MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1, MAX_PER_STAT_IVS + 1}; // ScriptGiveMonParameterized won't touch the stats' IV. u16 moves[MAX_MON_MOVES] = {MOVE_NONE, MOVE_NONE, MOVE_NONE, MOVE_NONE}; - return ScriptGiveMonParameterized(species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_VANILLA, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES); + return ScriptGiveMonParameterized(species, level, item, ITEM_POKE_BALL, NUM_NATURES, NUM_ABILITY_PERSONALITY, MON_GENDERLESS, evs, ivs, moves, FALSE, FALSE, NUMBER_OF_MON_TYPES); } #define PARSE_FLAG(n, default_) (flags & (1 << (n))) ? VarGet(ScriptReadHalfword(ctx)) : (default_) @@ -428,7 +428,7 @@ void ScrCmd_givemon(struct ScriptContext *ctx) u16 item = PARSE_FLAG(0, ITEM_NONE); u8 ball = PARSE_FLAG(1, ITEM_POKE_BALL); u8 nature = PARSE_FLAG(2, NUM_NATURES); - u8 abilityNum = PARSE_FLAG(3, NUM_ABILITY_VANILLA); + u8 abilityNum = PARSE_FLAG(3, NUM_ABILITY_PERSONALITY); u8 gender = PARSE_FLAG(4, MON_GENDERLESS); // TODO: Find a better way to assign a random gender. u8 hpEv = PARSE_FLAG(5, 0); u8 atkEv = PARSE_FLAG(6, 0); From 20a3d91de7292e0115fb2e028a83b39464f2a5f5 Mon Sep 17 00:00:00 2001 From: MelonSpeedruns Date: Thu, 15 Feb 2024 13:56:32 -0500 Subject: [PATCH 10/22] Fixed Basculegion Back Sprite Offset & Greninja forms animations (#4198) --- src/data/pokemon/species_info/gen_5.h | 2 +- src/data/pokemon/species_info/gen_6.h | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/data/pokemon/species_info/gen_5.h b/src/data/pokemon/species_info/gen_5.h index 15ff00fcf1..dda86c9a45 100644 --- a/src/data/pokemon/species_info/gen_5.h +++ b/src/data/pokemon/species_info/gen_5.h @@ -3028,7 +3028,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] = .frontAnimFrames = sAnims_Basculegion, \ .frontPicYOffset = 0, \ .enemyMonElevation = 5, \ - .backPicYOffset = 0, \ + .backPicYOffset = 8, \ LEARNSETS(Basculegion), \ .formSpeciesIdTable = sBasculegionFormSpeciesIdTable //.frontAnimId = ANIM_V_SQUISH_AND_BOUNCE, diff --git a/src/data/pokemon/species_info/gen_6.h b/src/data/pokemon/species_info/gen_6.h index 93261648d0..f5fcbb7ee9 100644 --- a/src/data/pokemon/species_info/gen_6.h +++ b/src/data/pokemon/species_info/gen_6.h @@ -448,8 +448,8 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .genderRatio = MON_MALE, .eggGroups = { EGG_GROUP_NO_EGGS_DISCOVERED, EGG_GROUP_NO_EGGS_DISCOVERED }, .abilities = { ABILITY_BATTLE_BOND, ABILITY_NONE, ABILITY_NONE }, - .frontAnimId = ANIM_FLICKER_INCREASING, - .backAnimId = BACK_ANIM_V_STRETCH, + .frontAnimId = ANIM_V_STRETCH, + .backAnimId = BACK_ANIM_JOLT_RIGHT, .formChangeTable = sGreninjaBattleBondFormChangeTable, }, @@ -478,7 +478,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] = .backPicYOffset = 11, PALETTES(GreninjaAsh), ICON(GreninjaAsh, 0), - .frontAnimId = ANIM_FLICKER_INCREASING, + .frontAnimId = ANIM_V_STRETCH, .backAnimId = BACK_ANIM_SHAKE_GLOW_BLUE, .formChangeTable = sGreninjaBattleBondFormChangeTable, }, From fcc28393463429d600b6bf91e93e26a9c270cdc1 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Fri, 16 Feb 2024 15:18:26 -0300 Subject: [PATCH 11/22] Fixed missing Z-Move power override (#4201) --- src/data/moves_info.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 280e38d2ad..25027a760f 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -1847,6 +1847,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .zMove = { .powerOverride = 120 }, .ignoresKingsRock = (B_UPDATED_MOVE_FLAGS == GEN_3 || B_UPDATED_MOVE_FLAGS == GEN_4), .healingMove = B_HEAL_BLOCKING >= GEN_6, .contestEffect = CONTEST_EFFECT_BADLY_STARTLE_FRONT_MON, @@ -7649,6 +7650,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .zMove = { .powerOverride = 160 }, .ballisticMove = TRUE, .contestEffect = CONTEST_EFFECT_HIGHLY_APPEALING, .contestCategory = CONTEST_CATEGORY_SMART, @@ -12247,6 +12249,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, + .zMove = { .powerOverride = 160 }, .argument = STATUS1_ANY, .contestEffect = CONTEST_EFFECT_BETTER_IF_SAME_TYPE, .contestCategory = CONTEST_CATEGORY_SMART, @@ -13114,6 +13117,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, + .zMove = { .powerOverride = 180 }, .makesContact = TRUE, .strikeCount = 2, .contestEffect = CONTEST_EFFECT_REPETITION_NOT_BORING, @@ -13449,6 +13453,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, + .zMove = { .powerOverride = 220 }, .makesContact = TRUE, .metronomeBanned = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ @@ -13516,6 +13521,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, + .zMove = { .powerOverride = 170 }, .argument = TYPE_FLYING, .makesContact = TRUE, .minimizeDoubleDamage = TRUE, From d102467d8d34c4c4b8f61d8a04f93e195eeb758e Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 16 Feb 2024 19:18:43 +0100 Subject: [PATCH 12/22] AI PR 4036 follow up (#4199) --- src/battle_ai_main.c | 2 +- src/battle_ai_util.c | 36 ++++++++++++++++++++++++++++-------- src/data/moves_info.h | 1 + 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index d3ef2dca0e..2b6f8de292 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -3846,7 +3846,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_FIRST_TURN_ONLY: if (ShouldFakeOut(battlerAtk, battlerDef, move)) ADJUST_SCORE(GOOD_EFFECT); - else if (gMovesInfo[move].priority >= 1 && gDisableStructs[battlerAtk].isFirstTurn && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move) + else if (gMovesInfo[move].argument == MOVE_FIRST_IMPRESSION && gDisableStructs[battlerAtk].isFirstTurn && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move) ADJUST_SCORE(BEST_EFFECT); break; case EFFECT_STOCKPILE: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index cb903dae54..61c1115f86 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3297,7 +3297,7 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) return; // Don't set up if AI is dead to residual damage from weather - if (BattlerWillFaintFromWeather(battlerAtk, AI_DATA->abilities[battlerAtk])) + if (GetBattlerSecondaryDamage(battlerAtk) >= gBattleMons[battlerAtk].hp) return; // Don't increase stats if opposing battler has Opportunist @@ -3312,10 +3312,15 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) break; case STAT_CHANGE_DEF: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)) - ADJUST_SCORE_PTR(DECENT_EFFECT); + { + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) + ADJUST_SCORE_PTR(DECENT_EFFECT); + else + ADJUST_SCORE_PTR(WEAK_EFFECT); + } break; case STAT_CHANGE_SPEED: - if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == 0) + if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) ADJUST_SCORE_PTR(DECENT_EFFECT); break; case STAT_CHANGE_SPATK: @@ -3324,7 +3329,12 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) break; case STAT_CHANGE_SPDEF: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)) - ADJUST_SCORE_PTR(DECENT_EFFECT); + { + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) + ADJUST_SCORE_PTR(DECENT_EFFECT); + else + ADJUST_SCORE_PTR(WEAK_EFFECT); + } break; case STAT_CHANGE_ATK_2: if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL) && shouldSetUp) @@ -3332,10 +3342,15 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) break; case STAT_CHANGE_DEF_2: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)) - ADJUST_SCORE_PTR(GOOD_EFFECT); + { + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) + ADJUST_SCORE_PTR(GOOD_EFFECT); + else + ADJUST_SCORE_PTR(DECENT_EFFECT); + } break; case STAT_CHANGE_SPEED_2: - if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == 0) + if ((noOfHitsToFaint >= 3 && !aiIsFaster) || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) ADJUST_SCORE_PTR(GOOD_EFFECT); break; case STAT_CHANGE_SPATK_2: @@ -3344,14 +3359,19 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score) break; case STAT_CHANGE_SPDEF_2: if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL) || !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)) - ADJUST_SCORE_PTR(GOOD_EFFECT); + { + if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL) + ADJUST_SCORE_PTR(GOOD_EFFECT); + else + ADJUST_SCORE_PTR(DECENT_EFFECT); + } break; case STAT_CHANGE_ACC: if (gBattleMons[battlerAtk].statStages[STAT_ACC] <= 3) // Increase only if necessary ADJUST_SCORE_PTR(DECENT_EFFECT); break; case STAT_CHANGE_EVASION: - if (GetBattlerSecondaryDamage(battlerAtk) && ((noOfHitsToFaint > 3) || noOfHitsToFaint == 0)) + if (noOfHitsToFaint > 3 || noOfHitsToFaint == UNKNOWN_NO_OF_HITS) ADJUST_SCORE_PTR(GOOD_EFFECT); else ADJUST_SCORE_PTR(DECENT_EFFECT); diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 25027a760f..7fb94a42b1 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -15027,6 +15027,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 2, .category = DAMAGE_CATEGORY_PHYSICAL, + .argument = MOVE_FIRST_IMPRESSION, .makesContact = TRUE, .contestEffect = CONTEST_EFFECT_BETTER_IF_FIRST, .contestCategory = CONTEST_CATEGORY_COOL, From 45cee8124d09eccc235679ec7643f12a403fe58b Mon Sep 17 00:00:00 2001 From: Wesmaster <34153881+Wesmaster@users.noreply.github.com> Date: Sat, 17 Feb 2024 20:26:45 +0100 Subject: [PATCH 13/22] Fixed LastUsedBall not being saved and DisplayBall not being shown (#4209) In #4168 b7d7709 a memset was added but this causes the issue #4200. The sizeof was done on the variable instead of the struct. This caused other variables in EWRAM to loose their value. I have no idea if this fix breaks what was intented to do in #4168. Also fixed the issue reported in my comment. When you run out of balls gLastThrownBall has a value, but you enter the if statement because you have no more balls. There the next in line ball is set to display but if there are non there is nothing to display. Afterwards when you get a new ball, you do not enter the if statement to update the gBallToDisplay because the ball is in the bag and gLastThrownBall is still set to not 0. Then it's checked if the bag has gBallToDisplay which does not point to a ball and therefor nothing is shown. Now we only update gBallToDisplay if there is actually a ball to display. --- src/battle_interface.c | 7 ++++++- src/battle_main.c | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/battle_interface.c b/src/battle_interface.c index d3751f702f..c0f68cf811 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -3468,9 +3468,14 @@ void TryAddLastUsedBallItemSprites(void) || (gLastThrownBall != 0 && !CheckBagHasItem(gLastThrownBall, 1))) { // we're out of the last used ball, so just set it to the first ball in the bag + u16 firstBall; + // we have to compact the bag first bc it is typically only compacted when you open it CompactItemsInBagPocket(&gBagPockets[BALLS_POCKET]); - gBallToDisplay = gBagPockets[BALLS_POCKET].itemSlots[0].itemId; + + firstBall = gBagPockets[BALLS_POCKET].itemSlots[0].itemId; + if (firstBall > ITEM_NONE) + gBallToDisplay = firstBall; } if (!CanThrowLastUsedBall()) diff --git a/src/battle_main.c b/src/battle_main.c index 99ab2a87a1..19ee70f162 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4914,7 +4914,7 @@ static void TurnValuesCleanUp(bool8 var0) else { memset(&gProtectStructs[i], 0, sizeof(struct ProtectStruct)); - memset(&gQueuedStatBoosts[i], 0, sizeof(gQueuedStatBoosts)); + memset(&gQueuedStatBoosts[i], 0, sizeof(struct QueuedStatBoost)); if (gDisableStructs[i].isFirstTurn) gDisableStructs[i].isFirstTurn--; From 3d2e0d2065d80015ee79131b0020b7385fb6760b Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Sun, 18 Feb 2024 06:21:56 -0300 Subject: [PATCH 14/22] Fixed Ursaluna's cry using P_GEN_9_CROSS_EVOS (#4210) --- include/constants/cries.h | 4 ++-- sound/cry_tables.inc | 8 ++++---- sound/direct_sound_data.inc | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/constants/cries.h b/include/constants/cries.h index ddeee56d42..4b9936fc3a 100644 --- a/include/constants/cries.h +++ b/include/constants/cries.h @@ -626,9 +626,9 @@ enum { #if P_FAMILY_TEDDIURSA CRY_TEDDIURSA, CRY_URSARING, -#if P_GEN_9_CROSS_EVOS +#if P_GEN_8_CROSS_EVOS CRY_URSALUNA, -#endif //P_GEN_9_CROSS_EVOS +#endif //P_GEN_8_CROSS_EVOS #endif //P_FAMILY_TEDDIURSA #if P_FAMILY_SLUGMA CRY_SLUGMA, diff --git a/sound/cry_tables.inc b/sound/cry_tables.inc index 6d0c9b97dc..7e4b890161 100644 --- a/sound/cry_tables.inc +++ b/sound/cry_tables.inc @@ -623,9 +623,9 @@ gCryTable:: .if P_FAMILY_TEDDIURSA == TRUE cry Cry_Teddiursa cry Cry_Ursaring -.if P_GEN_9_CROSS_EVOS == TRUE +.if P_GEN_8_CROSS_EVOS == TRUE cry Cry_Ursaluna -.endif @ P_GEN_9_CROSS_EVOS +.endif @ P_GEN_8_CROSS_EVOS .endif @ P_FAMILY_TEDDIURSA .if P_FAMILY_SLUGMA == TRUE cry Cry_Slugma @@ -3027,9 +3027,9 @@ gCryTable_Reverse:: .if P_FAMILY_TEDDIURSA == TRUE cry_reverse Cry_Teddiursa cry_reverse Cry_Ursaring -.if P_GEN_9_CROSS_EVOS == TRUE +.if P_GEN_8_CROSS_EVOS == TRUE cry_reverse Cry_Ursaluna -.endif @ P_GEN_9_CROSS_EVOS +.endif @ P_GEN_8_CROSS_EVOS .endif @ P_FAMILY_TEDDIURSA .if P_FAMILY_SLUGMA == TRUE cry_reverse Cry_Slugma diff --git a/sound/direct_sound_data.inc b/sound/direct_sound_data.inc index 8a915b6de1..1fddde82d6 100644 --- a/sound/direct_sound_data.inc +++ b/sound/direct_sound_data.inc @@ -1844,11 +1844,11 @@ Cry_Teddiursa:: Cry_Ursaring:: .incbin "sound/direct_sound_samples/cries/ursaring.bin" -.if P_GEN_9_CROSS_EVOS == TRUE +.if P_GEN_8_CROSS_EVOS == TRUE .align 2 Cry_Ursaluna:: .incbin "sound/direct_sound_samples/cries/ursaluna.bin" -.endif @ P_GEN_9_CROSS_EVOS +.endif @ P_GEN_8_CROSS_EVOS .endif @ P_FAMILY_TEDDIURSA .if P_FAMILY_SLUGMA == TRUE From d608af5662e4af51facbf06e98244eddba23f4bc Mon Sep 17 00:00:00 2001 From: Ultimate_Bob Date: Sun, 18 Feb 2024 20:24:21 +1100 Subject: [PATCH 15/22] Copy null terminator when decapping player name. (#4206) --- src/battle_message.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_message.c b/src/battle_message.c index cad7916b94..b186f059a6 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3208,7 +3208,7 @@ static const u8 *BattleStringGetPlayerName(u8 *text, u8 battler) if (DECAP_ENABLED && !DECAP_NICKNAMES && toCpy != text && *toCpy != CHAR_FIXED_CASE) { *text = CHAR_FIXED_CASE; - StringCopyN(text+1, toCpy, PLAYER_NAME_LENGTH); + StringCopyN(text+1, toCpy, PLAYER_NAME_LENGTH + 1); toCpy = text; } From 7ab23cf426afeb5b1d8250b7212e66edd332ff42 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 18 Feb 2024 15:02:58 +0100 Subject: [PATCH 16/22] Sets neutral nature and ability 0 as default in trainer control (#4172) * Sets neutral nature and ability 0 as default in trainer control * add config to generate a random ability * minor correction * move config to battle.h * fixed compiling --- include/config/battle.h | 1 + include/data.h | 1 - src/battle_main.c | 24 +++++++++++++++++------- src/data/partner_parties.h | 6 +++--- test/battle/trainer_control.c | 8 +++++++- 5 files changed, 28 insertions(+), 12 deletions(-) diff --git a/include/config/battle.h b/include/config/battle.h index 3b4b5ed96f..5ec007e686 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -230,6 +230,7 @@ #define B_WILD_NATURAL_ENEMIES TRUE // If set to TRUE, certain wild mon species will attack other species when partnered in double wild battles (eg. Zangoose vs Seviper) #define B_AFFECTION_MECHANICS TRUE // In Gen6+, there's a stat called affection that can trigger different effects in battle. From LGPE onwards, those effects use friendship instead. #define B_TRAINER_CLASS_POKE_BALLS GEN_LATEST // In Gen7+, trainers will use certain types of Poké Balls depending on their trainer class. +#define B_TRAINER_MON_RANDOM_ABILITY FALSE // If this is set to TRUE a random legal ability will be generated for a trainer mon #define B_OBEDIENCE_MECHANICS GEN_LATEST // In PLA+ (here Gen8+), obedience restrictions also apply to non-outsider Pokémon, albeit based on their level met rather than actual level #define B_USE_FROSTBITE FALSE // In PLA, Frostbite replaces Freeze. Enabling this flag does the same here. Moves can still be cherry-picked to either Freeze or Frostbite. Freeze-Dry, Secret Power & Tri Attack depend on this config. #define B_OVERWORLD_SNOW GEN_LATEST // In Gen9+, overworld Snow will summon snow instead of hail. diff --git a/include/data.h b/include/data.h index 4ac1d327fa..6baf5b3059 100644 --- a/include/data.h +++ b/include/data.h @@ -52,7 +52,6 @@ struct TrainerBacksprite #define GET_MON_COORDS_HEIGHT(size)((size & 0xF) * 8) #define TRAINER_PARTY_IVS(hp, atk, def, speed, spatk, spdef) (hp | (atk << 5) | (def << 10) | (speed << 15) | (spatk << 20) | (spdef << 25)) #define TRAINER_PARTY_EVS(hp, atk, def, speed, spatk, spdef) ((const u8[6]){hp,atk,def,spatk,spdef,speed}) -#define TRAINER_PARTY_NATURE(nature) (nature+1) struct TrainerMon { diff --git a/src/battle_main.c b/src/battle_main.c index 7ed28ea63c..3ae5289c58 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -1928,7 +1928,7 @@ void CustomTrainerPartyAssignMoves(struct Pokemon *mon, const struct TrainerMon u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer *trainer, bool32 firstTrainer, u32 battleTypeFlags) { u32 personalityValue; - s32 i, j; + s32 i; u8 monsCount; if (battleTypeFlags & BATTLE_TYPE_TRAINER && !(battleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER @@ -1956,6 +1956,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer const struct TrainerMon *partyData = trainer->party; u32 otIdType = OT_ID_RANDOM_NO_SHINY; u32 fixedOtId = 0; + u32 ability = 0; if (trainer->doubleBattle == TRUE) personalityValue = 0x80; @@ -1969,8 +1970,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_MALE, partyData[i].species); else if (partyData[i].gender == TRAINER_MON_FEMALE) personalityValue = (personalityValue & 0xFFFFFF00) | GeneratePersonalityForGender(MON_FEMALE, partyData[i].species); - if (partyData[i].nature != 0) - ModifyPersonalityForNature(&personalityValue, partyData[i].nature - 1); + ModifyPersonalityForNature(&personalityValue, partyData[i].nature); if (partyData[i].isShiny) { otIdType = OT_ID_PRESET; @@ -1994,14 +1994,24 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer { const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; u32 maxAbilities = ARRAY_COUNT(speciesInfo->abilities); - for (j = 0; j < maxAbilities; ++j) + for (ability = 0; ability < maxAbilities; ++ability) { - if (speciesInfo->abilities[j] == partyData[i].ability) + if (speciesInfo->abilities[ability] == partyData[i].ability) break; } - if (j < maxAbilities) - SetMonData(&party[i], MON_DATA_ABILITY_NUM, &j); + if (ability >= maxAbilities) + ability = 0; } + else if (B_TRAINER_MON_RANDOM_ABILITY) + { + const struct SpeciesInfo *speciesInfo = &gSpeciesInfo[partyData[i].species]; + ability = personalityHash % 3; + while (speciesInfo->abilities[ability] == ABILITY_NONE) + { + ability--; + } + } + SetMonData(&party[i], MON_DATA_ABILITY_NUM, &ability); SetMonData(&party[i], MON_DATA_FRIENDSHIP, &(partyData[i].friendship)); if (partyData[i].ball != ITEM_NONE) { diff --git a/src/data/partner_parties.h b/src/data/partner_parties.h index fec80c6ca1..1b071ec28e 100644 --- a/src/data/partner_parties.h +++ b/src/data/partner_parties.h @@ -2,7 +2,7 @@ static const struct TrainerMon sParty_StevenPartner[] = { { .species = SPECIES_METANG, .lvl = 42, - .nature = TRAINER_PARTY_NATURE(NATURE_BRAVE), + .nature = NATURE_BRAVE, .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), .ev = TRAINER_PARTY_EVS(0, 252, 252, 0, 6, 0), .moves = {MOVE_LIGHT_SCREEN, MOVE_PSYCHIC, MOVE_REFLECT, MOVE_METAL_CLAW}, @@ -10,7 +10,7 @@ static const struct TrainerMon sParty_StevenPartner[] = { { .species = SPECIES_SKARMORY, .lvl = 43, - .nature = TRAINER_PARTY_NATURE(NATURE_IMPISH), + .nature = NATURE_IMPISH, .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), .ev = TRAINER_PARTY_EVS(252, 0, 0, 0, 6, 252), .moves = {MOVE_TOXIC, MOVE_AERIAL_ACE, MOVE_PROTECT, MOVE_STEEL_WING}, @@ -18,7 +18,7 @@ static const struct TrainerMon sParty_StevenPartner[] = { { .species = SPECIES_AGGRON, .lvl = 44, - .nature = TRAINER_PARTY_NATURE(NATURE_ADAMANT), + .nature = NATURE_ADAMANT, .iv = TRAINER_PARTY_IVS(31, 31, 31, 31, 31, 31), .ev = TRAINER_PARTY_EVS(0, 252, 0, 0, 252, 6), .moves = {MOVE_THUNDER, MOVE_PROTECT, MOVE_SOLAR_BEAM, MOVE_DRAGON_CLAW}, diff --git a/test/battle/trainer_control.c b/test/battle/trainer_control.c index e3e071745d..9415360948 100644 --- a/test/battle/trainer_control.c +++ b/test/battle/trainer_control.c @@ -26,7 +26,7 @@ static const struct TrainerMon sTestParty1[] = .ev = TRAINER_PARTY_EVS(252, 0, 0, 252, 4, 0), .lvl = 67, .moves = {MOVE_AIR_SLASH, MOVE_BARRIER, MOVE_SOLAR_BEAM, MOVE_EXPLOSION}, - .nature = TRAINER_PARTY_NATURE(NATURE_HASTY), + .nature = NATURE_HASTY, .nickname = COMPOUND_STRING("Bubbles"), .dynamaxLevel = 5, }, @@ -35,6 +35,10 @@ static const struct TrainerMon sTestParty1[] = .ability = ABILITY_SHADOW_TAG, .lvl = 5, }, + { + .species = SPECIES_WYNAUT, + .lvl = 5, + }, }; static const struct Trainer sTestTrainer1 = @@ -59,6 +63,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon") EXPECT(GetMonAbility(&testParty[0]) == ABILITY_TELEPATHY); EXPECT(GetMonAbility(&testParty[1]) == ABILITY_SHADOW_TAG); + EXPECT(GetMonAbility(&testParty[2]) == ABILITY_SHADOW_TAG); EXPECT(GetMonData(&testParty[0], MON_DATA_FRIENDSHIP, 0) == 42); EXPECT(GetMonData(&testParty[1], MON_DATA_FRIENDSHIP, 0) == 0); @@ -110,6 +115,7 @@ TEST("CreateNPCTrainerPartyForTrainer generates customized Pokémon") EXPECT(GetMonGender(&testParty[0]) == MON_FEMALE); EXPECT(GetNature(&testParty[0]) == NATURE_HASTY); + EXPECT(GetNature(&testParty[1]) == NATURE_HARDY); EXPECT_EQ(GetMonData(&testParty[0], MON_DATA_DYNAMAX_LEVEL), 5); EXPECT_EQ(GetMonData(&testParty[1], MON_DATA_DYNAMAX_LEVEL), 0); From 76946282964b1ce70f4ac6ecb3b46b131509f766 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 18 Feb 2024 15:05:08 +0100 Subject: [PATCH 17/22] Adds Powerful status move flag (#4125) * Adds Powerful status move flag * fix flag * fixed final issues * review changes --- include/battle_ai_main.h | 3 +- include/battle_ai_util.h | 2 +- include/constants/battle.h | 2 +- include/constants/battle_ai.h | 14 +-- src/battle_ai_main.c | 157 ++++++++++++++++++++++----- src/battle_ai_util.c | 15 ++- src/battle_script_commands.c | 2 +- src/battle_tv.c | 2 +- src/data/moves_info.h | 4 +- test/battle/ability/shield_dust.c | 2 +- test/battle/ai_check_viability.c | 1 - test/battle/ai_powerful_status.c | 54 +++++++++ test/battle/move_effect/rapid_spin.c | 4 +- 13 files changed, 213 insertions(+), 49 deletions(-) create mode 100644 test/battle/ai_powerful_status.c diff --git a/include/battle_ai_main.h b/include/battle_ai_main.h index 8f482c5812..18148a89b0 100644 --- a/include/battle_ai_main.h +++ b/include/battle_ai_main.h @@ -29,7 +29,8 @@ #define STAT_CHANGE_ACC 10 #define STAT_CHANGE_EVASION 11 -#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect +#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect +#define POWERFUL_STATUS_MOVE 10 // Moves with this score will be chosen over a move that faints target // Temporary scores that are added together to determine a final score at the at of AI_CalcMoveScore #define WEAK_EFFECT 1 diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index cfdb791e0e..2d541ea73e 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -189,6 +189,6 @@ s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct Battl s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, struct AiLogicData *aiData, u32 predictedMove, bool32 isDoubleBattle); s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle); bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef); -s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); +bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); #endif //GUARD_BATTLE_AI_UTIL_H diff --git a/include/constants/battle.h b/include/constants/battle.h index 727e746443..9a3654195e 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -354,7 +354,7 @@ #define MOVE_EFFECT_PREVENT_ESCAPE 33 #define MOVE_EFFECT_NIGHTMARE 34 #define MOVE_EFFECT_ALL_STATS_UP 35 -#define MOVE_EFFECT_RAPIDSPIN 36 +#define MOVE_EFFECT_RAPID_SPIN 36 #define MOVE_EFFECT_REMOVE_STATUS 37 #define MOVE_EFFECT_ATK_DEF_DOWN 38 #define MOVE_EFFECT_ATK_PLUS_2 39 diff --git a/include/constants/battle_ai.h b/include/constants/battle_ai.h index ad489a4dd6..2093312ea6 100644 --- a/include/constants/battle_ai.h +++ b/include/constants/battle_ai.h @@ -34,20 +34,20 @@ #define AI_FLAG_PREFER_BATON_PASS (1 << 6) #define AI_FLAG_DOUBLE_BATTLE (1 << 7) // removed, split between AI_FLAG_CHECK_BAD_MOVE & AI_FLAG_CHECK_GOOD_MOVE #define AI_FLAG_HP_AWARE (1 << 8) +#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 // New, Trainer Handicap Flags -#define AI_FLAG_NEGATE_UNAWARE (1 << 9) // AI is NOT aware of negating effects like wonder room, mold breaker, etc -#define AI_FLAG_WILL_SUICIDE (1 << 10) // AI will use explosion / self destruct / final gambit / etc +#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 // New, Trainer Strategy Flags -#define AI_FLAG_HELP_PARTNER (1 << 11) // AI can try to help partner. If not set, will tend not to target partner -#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_SCREENER (1 << 14) // AI prefers screening effects like reflect, mist, etc. TODO unfinished +#define AI_FLAG_HELP_PARTNER (1 << 12) // AI can try to help partner. If not set, will tend not to target partner +#define AI_FLAG_PREFER_STATUS_MOVES (1 << 13) // 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 << 14) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished #define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks #define AI_FLAG_ACE_POKEMON (1 << 16) // 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 << 17) // AI has full knowledge of player moves, abilities, hold items #define AI_FLAG_SMART_MON_CHOICES (1 << 18) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Pairs very well with AI_FLAG_SMART_SWITCHING. -#define AI_FLAG_COUNT 18 +#define AI_FLAG_COUNT 19 // 'other' ai logic flags #define AI_FLAG_ROAMING (1 << 29) diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 2b6f8de292..19d43d7f51 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -52,6 +52,7 @@ static s32 AI_Roaming(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_Safari(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_FirstBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); +static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score); static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) = @@ -65,10 +66,10 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) = [6] = AI_PreferBatonPass, // AI_FLAG_PREFER_BATON_PASS [7] = AI_DoubleBattle, // AI_FLAG_DOUBLE_BATTLE [8] = AI_HPAware, // AI_FLAG_HP_AWARE - [9] = NULL, // AI_FLAG_NEGATE_UNAWARE - [10] = NULL, // AI_FLAG_WILL_SUICIDE - [11] = NULL, // AI_FLAG_HELP_PARTNER - [12] = NULL, // Unused + [9] = AI_PowerfulStatus, // AI_FLAG_POWERFUL_STATUS + [10] = NULL, // AI_FLAG_NEGATE_UNAWARE + [11] = NULL, // AI_FLAG_WILL_SUICIDE + [12] = NULL, // AI_FLAG_HELP_PARTNER [13] = NULL, // Unused [14] = NULL, // Unused [15] = NULL, // Unused @@ -2341,15 +2342,18 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) { ADJUST_SCORE(-10); } - else if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up + else if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS)) { - if (GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) // Attacker side slower than target side - ADJUST_SCORE(-10); // Keep the Trick Room up - } - else - { - if (GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) // Attacker side faster than target side - ADJUST_SCORE(-10); // Keep the Trick Room down + if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room Up + { + if (GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) // Attacker side slower than target side + ADJUST_SCORE(-10); // Keep the Trick Room up + } + else + { + if (GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) // Attacker side faster than target side + ADJUST_SCORE(-10); // Keep the Trick Room down + } } break; case EFFECT_MAGIC_ROOM: @@ -3489,8 +3493,6 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(BEST_EFFECT); if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_LIGHT_CLAY) ADJUST_SCORE(DECENT_EFFECT); - if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER) - ADJUST_SCORE(DECENT_EFFECT); } break; case EFFECT_REST: @@ -3518,10 +3520,6 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) if (ShouldTrap(battlerAtk, battlerDef, move)) ADJUST_SCORE(GOOD_EFFECT); break; - case EFFECT_MIST: - if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER) - ADJUST_SCORE(DECENT_EFFECT); - break; case EFFECT_FOCUS_ENERGY: case EFFECT_LASER_FOCUS: if (aiData->abilities[battlerAtk] == ABILITY_SUPER_LUCK @@ -3562,7 +3560,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_LEECH_SEED: if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || gStatuses3[battlerDef] & STATUS3_LEECHSEED - || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) + || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) || aiData->abilities[battlerDef] == ABILITY_LIQUID_OOZE || aiData->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; @@ -3721,7 +3719,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_STEALTH_ROCK: case EFFECT_STICKY_WEB: case EFFECT_TOXIC_SPIKES: - score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData); + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData)); + { + if (gDisableStructs[battlerAtk].isFirstTurn) + ADJUST_SCORE(BEST_EFFECT); + else + ADJUST_SCORE(DECENT_EFFECT); + } break; case EFFECT_FORESIGHT: if (aiData->abilities[battlerAtk] == ABILITY_SCRAPPY || aiData->abilities[battlerAtk] == ABILITY_MINDS_EYE) @@ -4254,10 +4258,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) ADJUST_SCORE(GOOD_EFFECT); // Partner might use pledge move break; case EFFECT_TRICK_ROOM: - if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) - ADJUST_SCORE(GOOD_EFFECT); - else if ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) - ADJUST_SCORE(GOOD_EFFECT); + if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_POWERFUL_STATUS)) + { + if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) < GetBattlerSideSpeedAverage(battlerDef)) + ADJUST_SCORE(GOOD_EFFECT); + else if ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && GetBattlerSideSpeedAverage(battlerAtk) >= GetBattlerSideSpeedAverage(battlerDef)) + ADJUST_SCORE(GOOD_EFFECT); + } break; case EFFECT_MAGIC_ROOM: ADJUST_SCORE(WEAK_EFFECT); @@ -4505,7 +4512,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPDEF, &score); } break; - case MOVE_EFFECT_RAPIDSPIN: + case MOVE_EFFECT_RAPID_SPIN: if ((gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0) || (gStatuses3[battlerAtk] & STATUS3_LEECHSEED || gBattleMons[battlerAtk].status2 & STATUS2_WRAPPED)) ADJUST_SCORE(GOOD_EFFECT); @@ -4638,7 +4645,13 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) break; case MOVE_EFFECT_STEALTH_ROCK: case MOVE_EFFECT_SPIKES: - score += AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData); + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, aiData)); + { + if (gDisableStructs[battlerAtk].isFirstTurn) + ADJUST_SCORE(BEST_EFFECT); + else + ADJUST_SCORE(DECENT_EFFECT); + } break; case MOVE_EFFECT_FEINT: if (gMovesInfo[predictedMove].effect == EFFECT_PROTECT) @@ -4654,7 +4667,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) } break; case MOVE_EFFECT_WRAP: - if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPIDSPIN) && ShouldTrap(battlerAtk, battlerDef, move)) + if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) && ShouldTrap(battlerAtk, battlerDef, move)) ADJUST_SCORE(BEST_EFFECT); break; } @@ -5124,6 +5137,96 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) return score; } +static s32 AI_PowerfulStatus(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) +{ + u32 moveEffect = gMovesInfo[move].effect; + + if (gMovesInfo[move].category != DAMAGE_CATEGORY_STATUS || gMovesInfo[AI_DATA->partnerMove].effect == moveEffect) + return score; + + switch (moveEffect) + { + case EFFECT_TAILWIND: + if (!gSideTimers[GetBattlerSide(battlerAtk)].tailwindTimer && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer > 1)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_TRICK_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && !HasMoveEffect(battlerDef, EFFECT_TRICK_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MAGIC_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_MAGIC_ROOM) && !HasMoveEffect(battlerDef, EFFECT_MAGIC_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_WONDER_ROOM: + if (!(gFieldStatuses & STATUS_FIELD_WONDER_ROOM) && !HasMoveEffect(battlerDef, EFFECT_WONDER_ROOM)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_GRAVITY: + if (!(gFieldStatuses & STATUS_FIELD_GRAVITY)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SAFEGUARD: + if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_SAFEGUARD)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MIST: + if (!(gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_MIST)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_LIGHT_SCREEN: + case EFFECT_REFLECT: + case EFFECT_AURORA_VEIL: + if (ShouldSetScreen(battlerAtk, battlerDef, moveEffect)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SPIKES: + case EFFECT_STEALTH_ROCK: + case EFFECT_STICKY_WEB: + case EFFECT_TOXIC_SPIKES: + if (AI_ShouldSetUpHazards(battlerAtk, battlerDef, AI_DATA)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_GRASSY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_ELECTRIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_PSYCHIC_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_MISTY_TERRAIN: + if (!(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SANDSTORM: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SANDSTORM | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SUNNY_DAY: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SUN | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_RAIN_DANCE: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_RAIN | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_HAIL: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_HAIL | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + break; + case EFFECT_SNOWSCAPE: + if (!(AI_GetWeather(AI_DATA) & (B_WEATHER_SNOW | B_WEATHER_PRIMAL_ANY))) + ADJUST_SCORE(POWERFUL_STATUS_MOVE); + } + + return score; +} + static void AI_Flee(void) { AI_THINKING_STRUCT->aiAction |= (AI_ACTION_DONE | AI_ACTION_FLEE | AI_ACTION_DO_NOT_ATTACK); diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 61c1115f86..2c878097db 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2860,6 +2860,10 @@ bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent) bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, u32 moveEffect) { u32 atkSide = GetBattlerSide(battlerAtk); + + if (HasMoveEffect(battlerDef, EFFECT_BRICK_BREAK)) // Don't waste a turn if screens will be broken + return FALSE; + switch (moveEffect) { case EFFECT_AURORA_VEIL: @@ -3587,10 +3591,13 @@ bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef) } //TODO - track entire opponent party data to determine hazard effectiveness -s32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData) +bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData) { - if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0) - return 0; + if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE + || CountUsablePartyMons(battlerDef) == 0 + || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) + || HasMoveEffect(battlerDef, EFFECT_DEFOG)) + return FALSE; - return 2 * gDisableStructs[battlerAtk].isFirstTurn; + return TRUE; } diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 07087bb6bf..6e3ae76b4e 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -3451,7 +3451,7 @@ void SetMoveEffect(bool32 primary, bool32 certain) gBattlescriptCurrInstr = BattleScript_AllStatsUp; } break; - case MOVE_EFFECT_RAPIDSPIN: + case MOVE_EFFECT_RAPID_SPIN: BattleScriptPush(gBattlescriptCurrInstr + 1); gBattlescriptCurrInstr = BattleScript_RapidSpinAway; break; diff --git a/src/battle_tv.c b/src/battle_tv.c index cb70d1deed..80cb0020f8 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -942,7 +942,7 @@ static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3) // Various cases to add/remove points if (gMovesInfo[arg2].recoil > 0) baseFromEffect++; // Recoil moves - if (MoveHasMoveEffect(arg2, MOVE_EFFECT_RAPIDSPIN)) + if (MoveHasMoveEffect(arg2, MOVE_EFFECT_RAPID_SPIN)) baseFromEffect++; if (MoveHasMoveEffect(arg2, MOVE_EFFECT_SP_ATK_TWO_DOWN) || MoveHasMoveEffect(arg2, MOVE_EFFECT_ATK_DEF_DOWN)) baseFromEffect += 2; // Overheat, Superpower, etc. diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 7fb94a42b1..9de5dbaf1d 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -5639,7 +5639,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .category = DAMAGE_CATEGORY_PHYSICAL, .makesContact = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ - .moveEffect = MOVE_EFFECT_RAPIDSPIN, + .moveEffect = MOVE_EFFECT_RAPID_SPIN, .self = TRUE, } #if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 @@ -18934,7 +18934,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .sheerForceBoost = TRUE, .makesContact = TRUE, .additionalEffects = ADDITIONAL_EFFECTS({ - .moveEffect = MOVE_EFFECT_RAPIDSPIN, + .moveEffect = MOVE_EFFECT_RAPID_SPIN, .self = TRUE, }, { diff --git a/test/battle/ability/shield_dust.c b/test/battle/ability/shield_dust.c index 4cd95de619..cac3dfac4c 100644 --- a/test/battle/ability/shield_dust.c +++ b/test/battle/ability/shield_dust.c @@ -94,7 +94,7 @@ SINGLE_BATTLE_TEST("Shield Dust does not block self-targeting effects, primary o GIVEN { ASSUME(MoveHasMoveEffectSelf(MOVE_POWER_UP_PUNCH, MOVE_EFFECT_ATK_PLUS_1) == TRUE); - ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); ASSUME(MoveHasMoveEffectSelf(MOVE_LEAF_STORM, MOVE_EFFECT_SP_ATK_TWO_DOWN) == TRUE); ASSUME(MoveHasMoveEffectSelf(MOVE_METEOR_ASSAULT, MOVE_EFFECT_RECHARGE) == TRUE); PLAYER(SPECIES_WOBBUFFET); diff --git a/test/battle/ai_check_viability.c b/test/battle/ai_check_viability.c index 69794804c7..a2cd33ed2c 100644 --- a/test/battle/ai_check_viability.c +++ b/test/battle/ai_check_viability.c @@ -179,7 +179,6 @@ AI_SINGLE_BATTLE_TEST("AI chooses moves with secondary effect that have a 100% c PARAMETRIZE { ability = ABILITY_SERENE_GRACE; } GIVEN { - AI_LOG; ASSUME(MoveHasMoveEffectWithChance(MOVE_SHADOW_BALL, MOVE_EFFECT_SP_DEF_MINUS_1, 20)); ASSUME(MoveHasMoveEffectWithChance(MOVE_OCTAZOOKA, MOVE_EFFECT_ACC_MINUS_1, 50)); AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); diff --git a/test/battle/ai_powerful_status.c b/test/battle/ai_powerful_status.c new file mode 100644 index 0000000000..b625b1126f --- /dev/null +++ b/test/battle/ai_powerful_status.c @@ -0,0 +1,54 @@ +#include "global.h" +#include "test/battle.h" +#include "battle_ai_util.h" + +AI_SINGLE_BATTLE_TEST("AI prefers to set up a powerful Status over fainting a target") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_TRICK_ROOM].effect == EFFECT_TRICK_ROOM); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_POWERFUL_STATUS); + PLAYER(SPECIES_WOBBUFFET) { HP(1); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_DUSCLOPS) { Moves(MOVE_TRICK_ROOM, MOVE_TACKLE); } + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_TRICK_ROOM); } + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI will try to do damage on target instead of setting up hazards if target has a way to remove them") +{ + GIVEN { + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); + ASSUME(gMovesInfo[MOVE_STEALTH_ROCK].effect == EFFECT_STEALTH_ROCK); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_POWERFUL_STATUS | AI_FLAG_OMNISCIENT); + PLAYER(SPECIES_WOBBUFFET) { HP(1); Moves(MOVE_RAPID_SPIN, MOVE_DEFOG, MOVE_CELEBRATE); } + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_GLIGAR) { Moves(MOVE_STEALTH_ROCK, MOVE_TACKLE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); SEND_OUT(player, 1); } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI will not set up Rain if it is already raining") +{ + GIVEN { + ASSUME(gMovesInfo[MOVE_RAIN_DANCE].effect == EFFECT_RAIN_DANCE); + ASSUME(gMovesInfo[MOVE_TACKLE].power > 0); + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_POWERFUL_STATUS); + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_RAIN_DANCE, MOVE_TACKLE); } + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { EXPECT_MOVE(opponent, MOVE_RAIN_DANCE); } + TURN { EXPECT_MOVE(opponent, MOVE_TACKLE); } + } +} diff --git a/test/battle/move_effect/rapid_spin.c b/test/battle/move_effect/rapid_spin.c index 2874aa4527..1d078ef71e 100644 --- a/test/battle/move_effect/rapid_spin.c +++ b/test/battle/move_effect/rapid_spin.c @@ -3,11 +3,11 @@ ASSUMPTIONS { - ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); #if B_SPEED_BUFFING_RAPID_SPIN >= GEN_8 ASSUME(MoveHasMoveEffectSelf(MOVE_RAPID_SPIN, MOVE_EFFECT_SPD_PLUS_1) == TRUE); #endif - ASSUME(MoveHasMoveEffectSelf(MOVE_MORTAL_SPIN, MOVE_EFFECT_RAPIDSPIN) == TRUE); + ASSUME(MoveHasMoveEffectSelf(MOVE_MORTAL_SPIN, MOVE_EFFECT_RAPID_SPIN) == TRUE); ASSUME(MoveHasMoveEffect(MOVE_MORTAL_SPIN, MOVE_EFFECT_POISON) == TRUE); } From cd596fdd802fc5c26cf5ead808ed274d2e73f538 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 18 Feb 2024 20:00:36 +0100 Subject: [PATCH 18/22] Adds Tidy Up + minor Dragon Cheer follow up (#4136) * Adds Tidy Up + minor Dragon Cheer follow up * improve tidy up script * Add IncreaseTidyUpScore function * remove useless calls * 2 small tests and a correction for IncreasyTidyUpScore --- asm/macros/battle_script.inc | 12 ++- data/battle_scripts_1.s | 18 +++++ include/battle_ai_util.h | 4 +- include/battle_scripts.h | 1 + include/constants/battle_move_effects.h | 1 + include/constants/battle_string_ids.h | 3 +- src/battle_ai_main.c | 14 ++-- src/battle_ai_util.c | 37 ++++++++- src/battle_message.c | 2 + src/battle_script_commands.c | 58 +++++++++++++++ src/data/battle_move_effects.h | 9 +++ src/data/battle_partners.h | 1 - src/data/moves_info.h | 2 +- test/battle/move_effect/tidy_up.c | 99 +++++++++++++++++++++++++ test/test_runner_battle.c | 2 +- 15 files changed, 248 insertions(+), 15 deletions(-) create mode 100644 test/battle/move_effect/tidy_up.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 6e26a267ac..d2a8115089 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1360,11 +1360,11 @@ .byte \battler .4byte \jumpInstr .endm - + .macro allyswitchswapbattlers callnative BS_AllySwitchSwapBattler .endm - + .macro allyswitchfailchance jumpInstr:req callnative BS_AllySwitchFailChance .4byte \jumpInstr @@ -1616,11 +1616,17 @@ callnative BS_TryUpperHand .4byte \failInstr .endm - + .macro tryupdaterecoiltracker callnative BS_TryUpdateRecoilTracker .endm + .macro trytidyup clear:req, jumpInstr:req + callnative BS_TryTidyUp + .byte \clear + .4byte \jumpInstr + .endm + @ various command changed to more readable macros .macro cancelmultiturnmoves battler:req various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 572aedbac7..e2d696a081 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -20,6 +20,23 @@ .section script_data, "aw", %progbits +BattleScript_EffectTidyUp:: + attackcanceler + attackstring + pause B_WAIT_TIME_MED + ppreduce + waitstate + trytidyup FALSE, BattleScript_EffectTidyUpDoMoveAnimation + goto BattleScript_EffectDragonDanceFromStatUp + +BattleScript_EffectTidyUpDoMoveAnimation:: + attackanimation + waitanimation + trytidyup TRUE, NULL + printstring STRINGID_TIDYINGUPCOMPLETE + waitmessage B_WAIT_TIME_LONG + goto BattleScript_EffectDragonDanceFromStatUp + BattleScript_EffectUpperHand:: attackcanceler tryupperhand BattleScript_FailedFromAtkString @@ -5460,6 +5477,7 @@ BattleScript_EffectDragonDance:: attackcanceler attackstring ppreduce +BattleScript_EffectDragonDanceFromStatUp:: jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_DragonDanceDoMoveAnim jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_SPEED, MAX_STAT_STAGE, BattleScript_CantRaiseMultipleStats BattleScript_DragonDanceDoMoveAnim:: diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 2d541ea73e..1a2157bc37 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -99,7 +99,8 @@ bool32 HasMoveWithCategory(u32 battler, u32 category); bool32 HasMoveWithType(u32 battler, u32 type); bool32 HasMoveEffect(u32 battlerId, u32 moveEffect); bool32 HasMoveEffectANDArg(u32 battlerId, u32 effect, u32 argument); -bool32 HasMoveWithMoveEffect(u32 battlerId, u32 moveEffect); +bool32 HasMoveWithAdditionalEffect(u32 battlerId, u32 moveEffect); +bool32 HasMoveWithCriticalHitChance(u32 battlerId); bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, u32 exception); bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect); bool32 IsAromaVeilProtectedMove(u32 move); @@ -190,5 +191,6 @@ s32 AI_CheckMoveEffects(u32 battlerAtk, u32 battlerDef, u32 move, s32 score, str s32 AI_TryToClearStats(u32 battlerAtk, u32 battlerDef, bool32 isDoubleBattle); bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef); bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData); +void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score); #endif //GUARD_BATTLE_AI_UTIL_H diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 2eeba2cc33..7ce97b6ec3 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -824,5 +824,6 @@ extern const u8 BattleScript_EffectDoodle[]; extern const u8 BattleScript_EffectFilletAway[]; extern const u8 BattleScript_EffectShedTail[]; extern const u8 BattleScript_EffectUpperHand[]; +extern const u8 BattleScript_EffectTidyUp[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 41c1e82f1f..81ab42f51d 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -350,6 +350,7 @@ enum { EFFECT_UPPER_HAND, EFFECT_DRAGON_CHEER, EFFECT_LAST_RESPECTS, + EFFECT_TIDY_UP, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index e79e3f55a0..7a64f6d6fd 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -706,8 +706,9 @@ #define STRINGID_DIMENSIONSWERETWISTED 704 #define STRINGID_BIZARREARENACREATED 705 #define STRINGID_BIZARREAREACREATED 706 +#define STRINGID_TIDYINGUPCOMPLETE 707 -#define BATTLESTRINGS_COUNT 707 +#define BATTLESTRINGS_COUNT 708 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 19d43d7f51..0e1e9aec11 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1763,7 +1763,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_FOLLOW_ME: case EFFECT_HELPING_HAND: - case EFFECT_DRAGON_CHEER: if (!isDoubleBattle || !IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) || PartnerHasSameMoveEffectWithoutTarget(BATTLE_PARTNER(battlerAtk), move, aiData->partnerMove) @@ -2791,7 +2790,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) ADJUST_SCORE(-5); else if (atkPartnerHoldEffect == HOLD_EFFECT_SCOPE_LENS || IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_DRAGON) - || gMovesInfo[aiData->partnerMove].criticalHitStage > 0) + || gMovesInfo[aiData->partnerMove].criticalHitStage > 0 + || HasMoveWithCriticalHitChance(battlerAtkPartner)) ADJUST_SCORE(GOOD_EFFECT); } // our effect relative to partner @@ -3535,6 +3535,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) IncreaseParalyzeScore(battlerAtk, battlerDef, move, &score); break; case EFFECT_SUBSTITUTE: + ADJUST_SCORE(GOOD_EFFECT); if (gStatuses3[battlerDef] & STATUS3_PERISH_SONG) ADJUST_SCORE(GOOD_EFFECT); if (gBattleMons[battlerDef].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_FROSTBITE)) @@ -3560,7 +3561,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_LEECH_SEED: if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || gStatuses3[battlerDef] & STATUS3_LEECHSEED - || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) + || HasMoveWithAdditionalEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) || aiData->abilities[battlerDef] == ABILITY_LIQUID_OOZE || aiData->abilities[battlerDef] == ABILITY_MAGIC_GUARD) break; @@ -3865,7 +3866,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) case EFFECT_FLATTER: if (HasMoveEffect(battlerAtk, EFFECT_FOUL_PLAY) || HasMoveEffect(battlerAtk, EFFECT_PSYCH_UP) - || HasMoveWithMoveEffect(battlerAtk, MOVE_EFFECT_SPECTRAL_THIEF)) + || HasMoveWithAdditionalEffect(battlerAtk, MOVE_EFFECT_SPECTRAL_THIEF)) ADJUST_SCORE(DECENT_EFFECT); if (aiData->abilities[battlerDef] == ABILITY_CONTRARY) ADJUST_SCORE(GOOD_EFFECT); @@ -4177,6 +4178,8 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK, &score); IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK, &score); break; + case EFFECT_TIDY_UP: + IncreaseTidyUpScore(battlerAtk, battlerDef, move, &score); case EFFECT_DRAGON_DANCE: case EFFECT_SHIFT_GEAR: IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPEED, &score); @@ -4667,7 +4670,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move) } break; case MOVE_EFFECT_WRAP: - if (!HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) && ShouldTrap(battlerAtk, battlerDef, move)) + if (!HasMoveWithAdditionalEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) && ShouldTrap(battlerAtk, battlerDef, move)) ADJUST_SCORE(BEST_EFFECT); break; } @@ -4794,6 +4797,7 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score case EFFECT_MAGIC_ROOM: case EFFECT_TAILWIND: case EFFECT_DRAGON_DANCE: + case EFFECT_TIDY_UP: case EFFECT_STICKY_WEB: case EFFECT_RAIN_DANCE: case EFFECT_SUNNY_DAY: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 2c878097db..b77750f652 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1709,7 +1709,7 @@ bool32 HasMoveEffectANDArg(u32 battlerId, u32 effect, u32 argument) return FALSE; } -bool32 HasMoveWithMoveEffect(u32 battlerId, u32 moveEffect) +bool32 HasMoveWithAdditionalEffect(u32 battlerId, u32 moveEffect) { s32 i; u16 *moves = GetMovesArray(battlerId); @@ -1724,6 +1724,21 @@ bool32 HasMoveWithMoveEffect(u32 battlerId, u32 moveEffect) return FALSE; } +bool32 HasMoveWithCriticalHitChance(u32 battlerId) +{ + s32 i; + u16 *moves = GetMovesArray(battlerId); + + for (i = 0; i < MAX_MON_MOVES; i++) + { + if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE + && gMovesInfo[moves[i]].criticalHitStage > 0) + return TRUE; + } + + return FALSE; +} + bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, u32 exception) { s32 i; @@ -3595,9 +3610,27 @@ bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData { if (aiData->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0 - || HasMoveWithMoveEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) + || HasMoveWithAdditionalEffect(battlerDef, MOVE_EFFECT_RAPID_SPIN) || HasMoveEffect(battlerDef, EFFECT_DEFOG)) return FALSE; return TRUE; } + +void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score) +{ + if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerAtk) != 0) + ADJUST_SCORE_PTR(GOOD_EFFECT); + if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_HAZARDS_ANY && CountUsablePartyMons(battlerDef) != 0) + ADJUST_SCORE_PTR(-2); + + if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE && AI_STRIKES_FIRST(battlerAtk, battlerDef, move)) + ADJUST_SCORE_PTR(-10); + if (gBattleMons[battlerDef].status2 & STATUS2_SUBSTITUTE) + ADJUST_SCORE_PTR(GOOD_EFFECT); + + if (gStatuses3[battlerAtk] & STATUS3_LEECHSEED) + ADJUST_SCORE_PTR(DECENT_EFFECT); + if (gStatuses3[battlerDef] & STATUS3_LEECHSEED) + ADJUST_SCORE_PTR(-2); +} diff --git a/src/battle_message.c b/src/battle_message.c index b186f059a6..c17997dab0 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -843,9 +843,11 @@ static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up..."); static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!"); static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!"); static const u8 sText_SupersweetAromaWafts[] = _("A supersweet aroma is wafting from\nthe syrup covering {B_ATK_NAME_WITH_PREFIX}!"); +static const u8 sText_TidyingUpComplete[] = _("Tidying up complete!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_TIDYINGUPCOMPLETE - BATTLESTRINGS_TABLE_START] = sText_TidyingUpComplete, [STRINGID_SUPERSWEETAROMAWAFTS - BATTLESTRINGS_TABLE_START] = sText_SupersweetAromaWafts, [STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail, [STRINGID_ELECTROSHOTCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShotCharging, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 6e3ae76b4e..7a3dc7ff76 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -8397,6 +8397,44 @@ static bool32 TryDefogClear(u32 battlerAtk, bool32 clear) return FALSE; } +static bool32 TryTidyUpClear(u32 battlerAtk, bool32 clear) +{ + s32 i; + u8 saveBattler = gBattlerAttacker; + + for (i = 0; i < NUM_BATTLE_SIDES; i++) + { + struct SideTimer *sideTimer = &gSideTimers[i]; + u32 *sideStatuses = &gSideStatuses[i]; + + gBattlerAttacker = i; // For correct battle string. Ally's / Foe's + DEFOG_CLEAR(SIDE_STATUS_SPIKES, spikesAmount, BattleScript_SpikesDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_STEALTH_ROCK, stealthRockAmount, BattleScript_StealthRockDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_TOXIC_SPIKES, toxicSpikesAmount, BattleScript_ToxicSpikesDefog, 0); + DEFOG_CLEAR(SIDE_STATUS_STICKY_WEB, stickyWebAmount, BattleScript_StickyWebDefog, 0); + } + + for (i = 0; i < MAX_BATTLERS_COUNT; i++) + { + if (gBattleMons[i].status2 & STATUS2_SUBSTITUTE) + { + if (clear) + { + gBattlerTarget = i; + gDisableStructs[i].substituteHP = 0; + gBattleMons[i].status2 &= ~STATUS2_SUBSTITUTE; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_SubstituteFade; + } + gBattlerAttacker = saveBattler; + return TRUE; + } + } + + gBattlerAttacker = saveBattler; + return FALSE; +} + u32 IsFlowerVeilProtected(u32 battler) { if (IS_BATTLER_OF_TYPE(battler, TYPE_GRASS)) @@ -16671,3 +16709,23 @@ void BS_TryUpdateRecoilTracker(void) TryUpdateEvolutionTracker(EVO_LEVEL_RECOIL_DAMAGE_FEMALE, gBattleMoveDamage); gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_TryTidyUp(void) +{ + NATIVE_ARGS(u8 clear, const u8 *jumpInstr); + + if (cmd->clear) + { + if (TryTidyUpClear(gEffectBattler, TRUE)) + return; + else + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + if (TryTidyUpClear(gBattlerAttacker, FALSE)) + gBattlescriptCurrInstr = cmd->jumpInstr; + else + gBattlescriptCurrInstr = cmd->nextInstr; + } +} diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 0aa18ceeea..32438fa812 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2200,12 +2200,14 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = { .battleScript = BattleScript_EffectShedTail, .battleTvScore = 0, // TODO: Assign points + .encourageEncore = TRUE, }, [EFFECT_UPPER_HAND] = { .battleScript = BattleScript_EffectUpperHand, .battleTvScore = 0, // TODO: Assign points + .encourageEncore = TRUE, }, [EFFECT_DRAGON_CHEER] = @@ -2220,4 +2222,11 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleScript = BattleScript_EffectHit, .battleTvScore = 0, // TODO: Assign points }, + + [EFFECT_TIDY_UP] = + { + .battleScript = BattleScript_EffectTidyUp, + .battleTvScore = 0, // TODO: Assign points + .encourageEncore = TRUE, + }, }; diff --git a/src/data/battle_partners.h b/src/data/battle_partners.h index 806a50bdf6..39bb91132f 100644 --- a/src/data/battle_partners.h +++ b/src/data/battle_partners.h @@ -17,5 +17,4 @@ const struct Trainer gBattlePartners[] = { .trainerPic = TRAINER_BACK_PIC_STEVEN, .trainerName = _("STEVEN"), }, - }; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 9de5dbaf1d..958142a49e 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -19207,7 +19207,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "User tidies up hazards and\n" "raises its Attack and Speed."), - .effect = EFFECT_PLACEHOLDER, // EFFECT_TIDY_UP + .effect = EFFECT_TIDY_UP, .power = 0, .type = TYPE_NORMAL, .accuracy = 0, diff --git a/test/battle/move_effect/tidy_up.c b/test/battle/move_effect/tidy_up.c new file mode 100644 index 0000000000..463fb65af8 --- /dev/null +++ b/test/battle/move_effect/tidy_up.c @@ -0,0 +1,99 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_TIDY_UP].effect == EFFECT_TIDY_UP); +} + +SINGLE_BATTLE_TEST("Tidy Up raises Attack and Speed by one") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TIDY_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TIDY_UP, player); + NOT MESSAGE("Tidying up complete!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's Speed rose!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Tidy Up removes hazards and raises Stats") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SPIKES); } + TURN { MOVE(opponent, MOVE_STEALTH_ROCK); } + TURN { MOVE(opponent, MOVE_TOXIC_SPIKES); } + TURN { MOVE(opponent, MOVE_STICKY_WEB); MOVE(player, MOVE_TIDY_UP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SPIKES, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STEALTH_ROCK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponent); + MESSAGE("Wobbuffet used Tidy Up!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TIDY_UP, player); + MESSAGE("The spikes disappeared from the ground around your team!"); + MESSAGE("The pointed stones disappeared from around your team!"); + MESSAGE("The poison spikes disappeared from the ground around your team!"); + MESSAGE("The sticky web has disappeared from the ground around your team!"); + MESSAGE("Tidying up complete!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's Speed rose!"); + } +} + +SINGLE_BATTLE_TEST("Tidy Up removes Substitute") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_SUBSTITUTE); MOVE(player, MOVE_TIDY_UP); } + } SCENE { + MESSAGE("Foe Wobbuffet used Substitute!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, opponent); + MESSAGE("Foe Wobbuffet made a SUBSTITUTE!"); + MESSAGE("Wobbuffet used Tidy Up!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TIDY_UP, player); + MESSAGE("Foe Wobbuffet's SUBSTITUTE faded!"); + MESSAGE("Tidying up complete!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's Speed rose!"); + } +} + +AI_SINGLE_BATTLE_TEST("AI prefers to keep it's substitute over removing hazards if target is slower") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Speed(50); Status1(STATUS1_PARALYSIS); Moves(MOVE_SLEEP_POWDER, MOVE_STEALTH_ROCK, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_BITE, MOVE_TACKLE, MOVE_SUBSTITUTE, MOVE_TIDY_UP); } + } WHEN { + TURN { MOVE(player, MOVE_STEALTH_ROCK); EXPECT_MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { EXPECT_MOVE(opponent, MOVE_BITE); } + } +} + +AI_SINGLE_BATTLE_TEST("AI will try to remove hazards if slower then target even with a Substitute because it expects the Sub to be broken") +{ + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { Speed(100); Status1(STATUS1_BURN); Moves(MOVE_SLEEP_POWDER, MOVE_STEALTH_ROCK, MOVE_CELEBRATE); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_BITE, MOVE_TACKLE, MOVE_SUBSTITUTE, MOVE_TIDY_UP); } + } WHEN { + TURN { MOVE(player, MOVE_STEALTH_ROCK); EXPECT_MOVE(opponent, MOVE_SUBSTITUTE); } + TURN { EXPECT_MOVE(opponent, MOVE_TIDY_UP); } + } +} diff --git a/test/test_runner_battle.c b/test/test_runner_battle.c index 3adebd78ab..68a83321fe 100644 --- a/test/test_runner_battle.c +++ b/test/test_runner_battle.c @@ -1533,7 +1533,7 @@ void SetFlagForTest(u32 sourceLine, u16 flagId) void ClearFlagAfterTest(void) { - if (DATA.flagId != 0) + if (DATA.flagId != 0) { FlagClear(DATA.flagId); DATA.flagId = 0; From 585e06e756b2cc73ae12cf577f81f248885f80cd Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sun, 18 Feb 2024 14:30:52 -0600 Subject: [PATCH 19/22] Move Tatsugiri and Squawkabilly base species icons to graphic root folders (#4212) --- .../squawkabilly/{green_plumage => }/icon.png | Bin .../squawkabilly/{green_plumage => }/normal.pal | 0 .../squawkabilly/{green_plumage => }/shiny.pal | 0 graphics/pokemon/tatsugiri/{curly => }/back.png | Bin .../pokemon/tatsugiri/{curly => }/front.png | Bin graphics/pokemon/tatsugiri/{curly => }/icon.png | Bin .../pokemon/tatsugiri/{curly => }/normal.pal | 0 .../pokemon/tatsugiri/{curly => }/shiny.pal | 0 src/data/graphics/pokemon.h | 16 ++++++++-------- 9 files changed, 8 insertions(+), 8 deletions(-) rename graphics/pokemon/squawkabilly/{green_plumage => }/icon.png (100%) mode change 100755 => 100644 rename graphics/pokemon/squawkabilly/{green_plumage => }/normal.pal (100%) rename graphics/pokemon/squawkabilly/{green_plumage => }/shiny.pal (100%) rename graphics/pokemon/tatsugiri/{curly => }/back.png (100%) rename graphics/pokemon/tatsugiri/{curly => }/front.png (100%) rename graphics/pokemon/tatsugiri/{curly => }/icon.png (100%) mode change 100755 => 100644 rename graphics/pokemon/tatsugiri/{curly => }/normal.pal (100%) rename graphics/pokemon/tatsugiri/{curly => }/shiny.pal (100%) diff --git a/graphics/pokemon/squawkabilly/green_plumage/icon.png b/graphics/pokemon/squawkabilly/icon.png old mode 100755 new mode 100644 similarity index 100% rename from graphics/pokemon/squawkabilly/green_plumage/icon.png rename to graphics/pokemon/squawkabilly/icon.png diff --git a/graphics/pokemon/squawkabilly/green_plumage/normal.pal b/graphics/pokemon/squawkabilly/normal.pal similarity index 100% rename from graphics/pokemon/squawkabilly/green_plumage/normal.pal rename to graphics/pokemon/squawkabilly/normal.pal diff --git a/graphics/pokemon/squawkabilly/green_plumage/shiny.pal b/graphics/pokemon/squawkabilly/shiny.pal similarity index 100% rename from graphics/pokemon/squawkabilly/green_plumage/shiny.pal rename to graphics/pokemon/squawkabilly/shiny.pal diff --git a/graphics/pokemon/tatsugiri/curly/back.png b/graphics/pokemon/tatsugiri/back.png similarity index 100% rename from graphics/pokemon/tatsugiri/curly/back.png rename to graphics/pokemon/tatsugiri/back.png diff --git a/graphics/pokemon/tatsugiri/curly/front.png b/graphics/pokemon/tatsugiri/front.png similarity index 100% rename from graphics/pokemon/tatsugiri/curly/front.png rename to graphics/pokemon/tatsugiri/front.png diff --git a/graphics/pokemon/tatsugiri/curly/icon.png b/graphics/pokemon/tatsugiri/icon.png old mode 100755 new mode 100644 similarity index 100% rename from graphics/pokemon/tatsugiri/curly/icon.png rename to graphics/pokemon/tatsugiri/icon.png diff --git a/graphics/pokemon/tatsugiri/curly/normal.pal b/graphics/pokemon/tatsugiri/normal.pal similarity index 100% rename from graphics/pokemon/tatsugiri/curly/normal.pal rename to graphics/pokemon/tatsugiri/normal.pal diff --git a/graphics/pokemon/tatsugiri/curly/shiny.pal b/graphics/pokemon/tatsugiri/shiny.pal similarity index 100% rename from graphics/pokemon/tatsugiri/curly/shiny.pal rename to graphics/pokemon/tatsugiri/shiny.pal diff --git a/src/data/graphics/pokemon.h b/src/data/graphics/pokemon.h index ef66ca981c..a2f707aea9 100644 --- a/src/data/graphics/pokemon.h +++ b/src/data/graphics/pokemon.h @@ -10157,9 +10157,9 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar const u32 gMonBackPic_Squawkabilly[] = INCBIN_U32("graphics/pokemon/squawkabilly/back.4bpp.lz"); // const u8 gMonFootprint_Squawkabilly[] = INCBIN_U8("graphics/pokemon/squawkabilly/footprint.1bpp"); - const u32 gMonPalette_SquawkabillyGreenPlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/green_plumage/normal.gbapal.lz"); - const u32 gMonShinyPalette_SquawkabillyGreenPlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/green_plumage/shiny.gbapal.lz"); - const u8 gMonIcon_SquawkabillyGreenPlumage[] = INCBIN_U8("graphics/pokemon/squawkabilly/green_plumage/icon.4bpp"); + const u32 gMonPalette_SquawkabillyGreenPlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/normal.gbapal.lz"); + const u32 gMonShinyPalette_SquawkabillyGreenPlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/shiny.gbapal.lz"); + const u8 gMonIcon_SquawkabillyGreenPlumage[] = INCBIN_U8("graphics/pokemon/squawkabilly/icon.4bpp"); const u32 gMonPalette_SquawkabillyBluePlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/blue_plumage/normal.gbapal.lz"); const u32 gMonShinyPalette_SquawkabillyBluePlumage[] = INCBIN_U32("graphics/pokemon/squawkabilly/blue_plumage/shiny.gbapal.lz"); @@ -10553,11 +10553,11 @@ const u8 gMonFootprint_QuestionMark[] = INCBIN_U8("graphics/pokemon/question_mar #endif //P_FAMILY_DONDOZO #if P_FAMILY_TATSUGIRI - const u32 gMonFrontPic_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/curly/front.4bpp.lz"); - const u32 gMonPalette_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/curly/normal.gbapal.lz"); - const u32 gMonBackPic_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/curly/back.4bpp.lz"); - const u32 gMonShinyPalette_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/curly/shiny.gbapal.lz"); - const u8 gMonIcon_TatsugiriCurly[] = INCBIN_U8("graphics/pokemon/tatsugiri/curly/icon.4bpp"); + const u32 gMonFrontPic_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/front.4bpp.lz"); + const u32 gMonPalette_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/normal.gbapal.lz"); + const u32 gMonBackPic_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/back.4bpp.lz"); + const u32 gMonShinyPalette_TatsugiriCurly[] = INCBIN_U32("graphics/pokemon/tatsugiri/shiny.gbapal.lz"); + const u8 gMonIcon_TatsugiriCurly[] = INCBIN_U8("graphics/pokemon/tatsugiri/icon.4bpp"); // const u8 gMonFootprint_Tatsugiri[] = INCBIN_U8("graphics/pokemon/tatsugiri/footprint.1bpp"); const u32 gMonFrontPic_TatsugiriDroopy[] = INCBIN_U32("graphics/pokemon/tatsugiri/droopy/front.4bpp.lz"); From bb01ab6ff13f55d09f355b3259a11fa804c18814 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Sun, 18 Feb 2024 21:53:12 -0300 Subject: [PATCH 20/22] Version 1.7.4 (#4203) * Version 1.7.4 --- .../ISSUE_TEMPLATE/01_battle_engine_bugs.yaml | 3 +- .../ISSUE_TEMPLATE/02_battle_ai_issues.yaml | 4 +- .github/ISSUE_TEMPLATE/04_other_errors.yaml | 3 +- CHANGELOG.md | 1 + README.md | 2 +- docs/changelogs/1.7.4.md | 68 +++++++++++++++++++ include/constants/expansion.h | 4 +- 7 files changed, 79 insertions(+), 6 deletions(-) create mode 100644 docs/changelogs/1.7.4.md diff --git a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml index 6493f4c11b..33af7d9649 100644 --- a/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml +++ b/.github/ISSUE_TEMPLATE/01_battle_engine_bugs.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.7.3 (Latest release) + - 1.7.4 (Latest release) - master (default when pulling, unreleased bugfixes) - upcoming (Edge) + - 1.7.3 - 1.7.2 - 1.7.1 - 1.7.0 diff --git a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml index 3fa2d2dfd5..cabfda4990 100644 --- a/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml +++ b/.github/ISSUE_TEMPLATE/02_battle_ai_issues.yaml @@ -23,9 +23,11 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.7.2 (Latest release) + - 1.7.4 (Latest release) - master (default when pulling, unreleased bugfixes) - upcoming (Edge) + - 1.7.3 + - 1.7.2 - 1.7.1 - 1.7.0 - 1.6.2 diff --git a/.github/ISSUE_TEMPLATE/04_other_errors.yaml b/.github/ISSUE_TEMPLATE/04_other_errors.yaml index e39eac4f56..33c85c15fb 100644 --- a/.github/ISSUE_TEMPLATE/04_other_errors.yaml +++ b/.github/ISSUE_TEMPLATE/04_other_errors.yaml @@ -23,9 +23,10 @@ body: label: Version description: What version of pokeemerald-expansion are you using as a base? options: - - 1.7.3 (Latest release) + - 1.7.4 (Latest release) - master (default when pulling, unreleased bugfixes) - upcoming (Edge) + - 1.7.3 - 1.7.2 - 1.7.1 - 1.7.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index d4805a5d40..f8e0c66e20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # Pokeemerald-Expansion Changelogs ## Version 1.7.x +### [Version 1.7.4](docs/changelogs/1.7.4.md) - Bugfix Release ### [Version 1.7.3](docs/changelogs/1.7.3.md) - Bugfix Release ### [Version 1.7.2](docs/changelogs/1.7.2.md) - Bugfix Release ### [Version 1.7.1](docs/changelogs/1.7.1.md) - Bugfix Release diff --git a/README.md b/README.md index d4fe25ea99..7d8d18f939 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ With this, you'll get the latest version of pokeemerald-expansion, plus a couple ## **How do I update my version of pokeemerald-expansion?** - If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. -- Once you have your remote set up, run the command `git pull RHH expansion/1.7.3`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.7.4`. ### Please consider crediting the entire [list of contributors](https://github.com/rh-hideout/pokeemerald-expansion/wiki/Credits) in your project, as they have all worked hard to develop this project :) diff --git a/docs/changelogs/1.7.4.md b/docs/changelogs/1.7.4.md new file mode 100644 index 0000000000..50556bec77 --- /dev/null +++ b/docs/changelogs/1.7.4.md @@ -0,0 +1,68 @@ +# Version 1.7.4 + +```md +## How to update +- If you haven't set up a remote, run the command `git remote add RHH https://github.com/rh-hideout/pokeemerald-expansion`. +- Once you have your remote set up, run the command `git pull RHH expansion/1.Y.Z`. +``` + +## 🧬 General 🧬 +### Fixed +* HGSS Dex fixes: + * Fixed inconsistent list tileset between capped and decapped modes by @ravepossum in https://github.com/rh-hideout/pokeemerald-expansion/pull/4126 + * Fixed screen select bar popping in too early for area screen by @ravepossum in https://github.com/rh-hideout/pokeemerald-expansion/pull/4094 +* Fixed Lotad/Seedot house using species weight instead of height by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4195 + +## 🐉 Pokémon 🐉 +### Fixed +* Fixed Ogerpon shiny palettes by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4165 +* Fixed Basculegion back sprite offset by @MelonSpeedruns in https://github.com/rh-hideout/pokeemerald-expansion/pull/4198 +* Fixed Greninja form animations by @MelonSpeedruns in https://github.com/rh-hideout/pokeemerald-expansion/pull/4198 +* Fixed compile error when `P_GEN_8_CROSS_EVOS` is enabled but not `P_GEN_9_CROSS_EVOS` due to Ursaluna's cry being labled as Gen 9 by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4210 +* Fixed Curly Tatsugiri and Green Plumage Squawkabilly icons not showing up properly in later versions of Porymap by @kittenchilly in https://github.com/rh-hideout/pokeemerald-expansion/pull/4212 + +## ⚔️ Battle General ⚔️ ## +### Fixed +* Fixed disobedience not resetting multihit moves by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4133 +* Fixed switch in interactions with hold effects abilities by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4091 + * Download activating on an empty field. + * Intimidate activating on an empty field and not activating upon new opponent switching in. + * Primal Reversion not happening immediately upon switching in from U-turn. + * Held items not being triggered when switching in fron U-turn and Intimidate being triggered beforehand. +* Fixed LastUsedBall issues not being saved and DisplayBall not being shown by @Wesmaster in https://github.com/rh-hideout/pokeemerald-expansion/pull/4209 + +## 🤹 Moves 🤹 +### Fixed +* Fixed move animations crashing on some emulators because of division by zero by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4121 + * Flip Turn, Anchor Shot, Snipe Shot and Incinerate were affected by this. +* Fixed Eerie Spell consuming double PP and showing its message twice by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4127 +* Fixed Fairy Lock animation by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4111 + +## 🎭 Abilities 🎭 +### Added +* Added Gen 5+ config for Soundproof no longer preventing Uproar status by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4174 +### Fixed +* Fixed Mycelium Might speed calculation by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4146 +* Fixed Stench triggering on non-damaging attacks by @HungryPickle in https://github.com/rh-hideout/pokeemerald-expansion/pull/4159 +* Fixed Disguise not ending the battle in the correct form by @AsparagusEduardo in https://github.com/rh-hideout/pokeemerald-expansion/pull/4167 +* Fixed Opportunist accumulating stat changes by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4168 + +## 🧶 Items 🧶 +### Fixed +* Kee Berry fixes by @AlexOn1ine in https://github.com/rh-hideout/pokeemerald-expansion/pull/4149 + * Fixed triggering if the holder does a physical attack instead of receiving it. + * Fixed it boosting the target's defense if the attacker holds a Kee Berry, while still displaying the boost animation for the holder. +* Fixed poison-healing items not reseting Toxic Counter by @DizzyEggg in https://github.com/rh-hideout/pokeemerald-expansion/pull/4135 + +## 🧹 Other Cleanup 🧹 +### Changed +* Clean up space/tabs difference by @Bassoonian in https://github.com/rh-hideout/pokeemerald-expansion/pull/4163 +* Deleted a space by @cmy2008 in https://github.com/rh-hideout/pokeemerald-expansion/pull/4194 + +## New Contributors +* @cmy2008 made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4194 +* @MelonSpeedruns made their first contribution in https://github.com/rh-hideout/pokeemerald-expansion/pull/4198 + +**Full Changelog**: https://github.com/rh-hideout/pokeemerald-expansion/compare/expansion/1.7.3...expansion/1.7.4 + + diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 7e6cf52b26..1f983eb65c 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -3,10 +3,10 @@ #define EXPANSION_VERSION_MAJOR 1 #define EXPANSION_VERSION_MINOR 7 -#define EXPANSION_VERSION_PATCH 3 +#define EXPANSION_VERSION_PATCH 4 // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. -#define EXPANSION_TAGGED_RELEASE FALSE +#define EXPANSION_TAGGED_RELEASE TRUE #endif From 5be97faf9da0cf512e347a9e55c57c5f52605ced Mon Sep 17 00:00:00 2001 From: Eduardo Quezada Date: Sun, 18 Feb 2024 22:09:08 -0300 Subject: [PATCH 21/22] Non-tagged --- include/constants/expansion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/constants/expansion.h b/include/constants/expansion.h index 1f983eb65c..63f262aeee 100644 --- a/include/constants/expansion.h +++ b/include/constants/expansion.h @@ -7,6 +7,6 @@ // FALSE if this this version of Expansion is not a tagged commit, i.e. // it contains unreleased changes. -#define EXPANSION_TAGGED_RELEASE TRUE +#define EXPANSION_TAGGED_RELEASE FALSE #endif From 57e0d7b20bee5f5a8ae2e1d610e4ec664260efb9 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:36:21 +0100 Subject: [PATCH 22/22] Partial fix for Teeter Dance and Ability Dancer interaction (#4129) * Parial fix for Teeter Dance and Ability Dancer interaction * Removes rest of teeter dance checks and make it work with effect_confuse * Update test/battle/ability/dancer.c Co-authored-by: Bassoonian * Update test/battle/ability/dancer.c Co-authored-by: ultima-soul <33333039+ultima-soul@users.noreply.github.com> --------- Co-authored-by: Bassoonian Co-authored-by: ultima-soul <33333039+ultima-soul@users.noreply.github.com> --- data/battle_scripts_1.s | 58 --- include/battle_ai_util.h | 2 +- include/constants/battle_move_effects.h | 447 ++++++++++++------------ src/battle_ai_main.c | 19 +- src/battle_ai_util.c | 26 +- src/battle_tv.c | 1 - src/data/battle_moves.h | 2 +- test/battle/ability/dancer.c | 35 ++ test/battle/move_effect/confuse.c | 58 +++ 9 files changed, 333 insertions(+), 315 deletions(-) create mode 100644 test/battle/ability/dancer.c create mode 100644 test/battle/move_effect/confuse.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 24dfec1ff8..ac9e88bea8 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -216,7 +216,6 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectHit @ EFFECT_LOW_KICK .4byte BattleScript_EffectSecretPower @ EFFECT_SECRET_POWER .4byte BattleScript_EffectHit @ EFFECT_RECOIL_33 - .4byte BattleScript_EffectTeeterDance @ EFFECT_TEETER_DANCE .4byte BattleScript_EffectHitEscape @ EFFECT_HIT_ESCAPE .4byte BattleScript_EffectMudSport @ EFFECT_MUD_SPORT .4byte BattleScript_EffectPoisonFang @ EFFECT_POISON_FANG @@ -6129,63 +6128,6 @@ BattleScript_EffectRecoilHP25: incrementgamestat GAME_STAT_USED_STRUGGLE goto BattleScript_EffectHit -BattleScript_EffectTeeterDance:: - attackcanceler - attackstring - ppreduce - setbyte gBattlerTarget, 0 -BattleScript_TeeterDanceLoop:: - movevaluescleanup - setmoveeffect MOVE_EFFECT_CONFUSION - jumpifbyteequal gBattlerAttacker, gBattlerTarget, BattleScript_TeeterDanceLoopIncrement - jumpifability BS_TARGET, ABILITY_OWN_TEMPO, BattleScript_TeeterDanceOwnTempoPrevents - jumpifsubstituteblocks BattleScript_TeeterDanceSubstitutePrevents - jumpifstatus2 BS_TARGET, STATUS2_CONFUSION, BattleScript_TeeterDanceAlreadyConfused - jumpifhasnohp BS_TARGET, BattleScript_TeeterDanceLoopIncrement - accuracycheck BattleScript_TeeterDanceMissed, ACC_CURR_MOVE - jumpifsafeguard BattleScript_TeeterDanceSafeguardProtected - attackanimation - waitanimation - seteffectprimary - resultmessage - waitmessage B_WAIT_TIME_LONG -BattleScript_TeeterDanceDoMoveEndIncrement:: - moveendto MOVEEND_NEXT_TARGET -BattleScript_TeeterDanceLoopIncrement:: - addbyte gBattlerTarget, 1 - jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_TeeterDanceLoop - end - -BattleScript_TeeterDanceOwnTempoPrevents:: - pause B_WAIT_TIME_SHORT - printstring STRINGID_PKMNPREVENTSCONFUSIONWITH - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - -BattleScript_TeeterDanceSafeguardProtected:: - pause B_WAIT_TIME_SHORT - printstring STRINGID_PKMNUSEDSAFEGUARD - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - -BattleScript_TeeterDanceSubstitutePrevents:: - pause B_WAIT_TIME_SHORT - printstring STRINGID_BUTITFAILED - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - -BattleScript_TeeterDanceAlreadyConfused:: - setalreadystatusedmoveattempt BS_ATTACKER - pause B_WAIT_TIME_SHORT - printstring STRINGID_PKMNALREADYCONFUSED - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - -BattleScript_TeeterDanceMissed:: - resultmessage - waitmessage B_WAIT_TIME_LONG - goto BattleScript_TeeterDanceDoMoveEndIncrement - BattleScript_EffectMudSport:: BattleScript_EffectWaterSport:: attackcanceler diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 9835265109..69b09eaaf9 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -139,7 +139,7 @@ bool32 HasSnatchAffectedMove(u32 battler); // status checks bool32 AI_CanBeBurned(u32 battler, u32 ability); bool32 AI_CanGetFrostbite(u32 battler, u32 ability); -bool32 AI_CanBeConfused(u32 battler, u32 ability); +bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability); bool32 AI_CanSleep(u32 battler, u32 ability); bool32 IsBattlerIncapacitated(u32 battler, u32 ability); bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove); diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 056f0ce4f7..4edbcc1ed0 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -195,232 +195,231 @@ #define EFFECT_LOW_KICK 191 #define EFFECT_SECRET_POWER 192 #define EFFECT_RECOIL_33 193 -#define EFFECT_TEETER_DANCE 194 -#define EFFECT_HIT_ESCAPE 195 -#define EFFECT_MUD_SPORT 196 -#define EFFECT_POISON_FANG 197 -#define EFFECT_WEATHER_BALL 198 -#define EFFECT_OVERHEAT 199 -#define EFFECT_TICKLE 200 -#define EFFECT_COSMIC_POWER 201 -#define EFFECT_SKY_UPPERCUT 202 -#define EFFECT_BULK_UP 203 -#define EFFECT_PLACEHOLDER 204 -#define EFFECT_WATER_SPORT 205 -#define EFFECT_CALM_MIND 206 -#define EFFECT_DRAGON_DANCE 207 -#define EFFECT_CAMOUFLAGE 208 +#define EFFECT_HIT_ESCAPE 194 +#define EFFECT_MUD_SPORT 195 +#define EFFECT_POISON_FANG 196 +#define EFFECT_WEATHER_BALL 197 +#define EFFECT_OVERHEAT 198 +#define EFFECT_TICKLE 199 +#define EFFECT_COSMIC_POWER 200 +#define EFFECT_SKY_UPPERCUT 201 +#define EFFECT_BULK_UP 202 +#define EFFECT_PLACEHOLDER 203 +#define EFFECT_WATER_SPORT 204 +#define EFFECT_CALM_MIND 205 +#define EFFECT_DRAGON_DANCE 206 +#define EFFECT_CAMOUFLAGE 207 // New move effects -#define EFFECT_PLEDGE 209 -#define EFFECT_FLING 210 -#define EFFECT_NATURAL_GIFT 211 -#define EFFECT_WAKE_UP_SLAP 212 -#define EFFECT_WRING_OUT 213 -#define EFFECT_HEX 214 -#define EFFECT_ASSURANCE 215 -#define EFFECT_TRUMP_CARD 216 -#define EFFECT_ACROBATICS 217 -#define EFFECT_HEAT_CRASH 218 -#define EFFECT_PUNISHMENT 219 -#define EFFECT_STORED_POWER 220 -#define EFFECT_ELECTRO_BALL 221 -#define EFFECT_GYRO_BALL 222 -#define EFFECT_ECHOED_VOICE 223 -#define EFFECT_PAYBACK 224 -#define EFFECT_ROUND 225 -#define EFFECT_BRINE 226 -#define EFFECT_VENOSHOCK 227 -#define EFFECT_RETALIATE 228 -#define EFFECT_BULLDOZE 229 -#define EFFECT_FOUL_PLAY 230 -#define EFFECT_PSYSHOCK 231 -#define EFFECT_ROOST 232 -#define EFFECT_GRAVITY 233 -#define EFFECT_MIRACLE_EYE 234 -#define EFFECT_TAILWIND 235 -#define EFFECT_EMBARGO 236 -#define EFFECT_AQUA_RING 237 -#define EFFECT_TRICK_ROOM 238 -#define EFFECT_WONDER_ROOM 239 -#define EFFECT_MAGIC_ROOM 240 -#define EFFECT_MAGNET_RISE 241 -#define EFFECT_TOXIC_SPIKES 242 -#define EFFECT_GASTRO_ACID 243 -#define EFFECT_STEALTH_ROCK 244 -#define EFFECT_TELEKINESIS 245 -#define EFFECT_POWER_SWAP 246 -#define EFFECT_GUARD_SWAP 247 -#define EFFECT_HEART_SWAP 248 -#define EFFECT_POWER_SPLIT 249 -#define EFFECT_GUARD_SPLIT 250 -#define EFFECT_STICKY_WEB 251 -#define EFFECT_METAL_BURST 252 -#define EFFECT_LUCKY_CHANT 253 -#define EFFECT_SUCKER_PUNCH 254 -#define EFFECT_SPECIAL_DEFENSE_DOWN_HIT_2 255 -#define EFFECT_SIMPLE_BEAM 256 -#define EFFECT_ENTRAINMENT 257 -#define EFFECT_HEAL_PULSE 258 -#define EFFECT_QUASH 259 -#define EFFECT_ION_DELUGE 260 -#define EFFECT_FREEZE_DRY 261 -#define EFFECT_TOPSY_TURVY 262 -#define EFFECT_MISTY_TERRAIN 263 -#define EFFECT_GRASSY_TERRAIN 264 -#define EFFECT_ELECTRIC_TERRAIN 265 -#define EFFECT_PSYCHIC_TERRAIN 266 -#define EFFECT_ATTACK_ACCURACY_UP 267 -#define EFFECT_ATTACK_SPATK_UP 268 -#define EFFECT_HURRICANE 269 -#define EFFECT_TWO_TYPED_MOVE 270 -#define EFFECT_ME_FIRST 271 -#define EFFECT_SPEED_UP_HIT 272 -#define EFFECT_QUIVER_DANCE 273 -#define EFFECT_COIL 274 -#define EFFECT_ELECTRIFY 275 -#define EFFECT_REFLECT_TYPE 276 -#define EFFECT_SOAK 277 -#define EFFECT_GROWTH 278 -#define EFFECT_CLOSE_COMBAT 279 -#define EFFECT_LAST_RESORT 280 -#define EFFECT_RECOIL_33_STATUS 281 -#define EFFECT_FLINCH_STATUS 282 -#define EFFECT_RECOIL_50 283 -#define EFFECT_SHELL_SMASH 284 -#define EFFECT_SHIFT_GEAR 285 -#define EFFECT_DEFENSE_UP_3 286 -#define EFFECT_NOBLE_ROAR 287 -#define EFFECT_VENOM_DRENCH 288 -#define EFFECT_TOXIC_THREAD 289 -#define EFFECT_CLEAR_SMOG 290 -#define EFFECT_HIT_SWITCH_TARGET 291 -#define EFFECT_FINAL_GAMBIT 292 -#define EFFECT_CHANGE_TYPE_ON_ITEM 293 -#define EFFECT_AUTOTOMIZE 294 -#define EFFECT_COPYCAT 295 -#define EFFECT_DEFOG 296 -#define EFFECT_HIT_ENEMY_HEAL_ALLY 297 -#define EFFECT_SMACK_DOWN 298 -#define EFFECT_SYNCHRONOISE 299 -#define EFFECT_PSYCHO_SHIFT 300 -#define EFFECT_POWER_TRICK 301 -#define EFFECT_FLAME_BURST 302 -#define EFFECT_AFTER_YOU 303 -#define EFFECT_BESTOW 304 -#define EFFECT_ROTOTILLER 305 -#define EFFECT_FLOWER_SHIELD 306 -#define EFFECT_HIT_PREVENT_ESCAPE 307 -#define EFFECT_SPEED_SWAP 308 -#define EFFECT_DEFENSE_UP2_HIT 309 -#define EFFECT_REVELATION_DANCE 310 -#define EFFECT_AURORA_VEIL 311 -#define EFFECT_THIRD_TYPE 312 -#define EFFECT_FEINT 313 -#define EFFECT_SPARKLING_ARIA 314 -#define EFFECT_ACUPRESSURE 315 -#define EFFECT_AROMATIC_MIST 316 -#define EFFECT_POWDER 317 -#define EFFECT_SP_ATTACK_UP_HIT 318 -#define EFFECT_BELCH 319 -#define EFFECT_PARTING_SHOT 320 -#define EFFECT_SPECTRAL_THIEF 321 -#define EFFECT_V_CREATE 322 -#define EFFECT_MAT_BLOCK 323 -#define EFFECT_STOMPING_TANTRUM 324 -#define EFFECT_CORE_ENFORCER 325 -#define EFFECT_INSTRUCT 326 -#define EFFECT_THROAT_CHOP 327 -#define EFFECT_LASER_FOCUS 328 -#define EFFECT_MAGNETIC_FLUX 329 -#define EFFECT_GEAR_UP 330 -#define EFFECT_INCINERATE 331 -#define EFFECT_BUG_BITE 332 -#define EFFECT_STRENGTH_SAP 333 -#define EFFECT_MIND_BLOWN 334 -#define EFFECT_PURIFY 335 -#define EFFECT_BURN_UP 336 -#define EFFECT_SHORE_UP 337 -#define EFFECT_GEOMANCY 338 -#define EFFECT_FAIRY_LOCK 339 -#define EFFECT_ALLY_SWITCH 340 -#define EFFECT_RELIC_SONG 341 -#define EFFECT_ATTACKER_DEFENSE_DOWN_HIT 342 -#define EFFECT_BODY_PRESS 343 -#define EFFECT_EERIE_SPELL 344 -#define EFFECT_JUNGLE_HEALING 345 -#define EFFECT_COACHING 346 -#define EFFECT_LASH_OUT 347 -#define EFFECT_GRASSY_GLIDE 348 -#define EFFECT_DYNAMAX_DOUBLE_DMG 349 -#define EFFECT_DECORATE 350 -#define EFFECT_SNIPE_SHOT 351 -#define EFFECT_RECOIL_HP_25 352 -#define EFFECT_STUFF_CHEEKS 353 -#define EFFECT_GRAV_APPLE 354 -#define EFFECT_EVASION_UP_HIT 355 -#define EFFECT_GLITZY_GLOW 356 -#define EFFECT_BADDY_BAD 357 -#define EFFECT_SAPPY_SEED 358 -#define EFFECT_FREEZY_FROST 359 -#define EFFECT_SPARKLY_SWIRL 360 -#define EFFECT_PLASMA_FISTS 361 -#define EFFECT_HYPERSPACE_FURY 362 -#define EFFECT_AURA_WHEEL 363 -#define EFFECT_PHOTON_GEYSER 364 -#define EFFECT_SHELL_SIDE_ARM 365 -#define EFFECT_TERRAIN_PULSE 366 -#define EFFECT_JAW_LOCK 367 -#define EFFECT_NO_RETREAT 368 -#define EFFECT_TAR_SHOT 369 -#define EFFECT_POLTERGEIST 370 -#define EFFECT_OCTOLOCK 371 -#define EFFECT_CLANGOROUS_SOUL 372 -#define EFFECT_BOLT_BEAK 373 -#define EFFECT_SKY_DROP 374 -#define EFFECT_EXPANDING_FORCE 375 -#define EFFECT_METEOR_BEAM 376 -#define EFFECT_RISING_VOLTAGE 377 -#define EFFECT_BEAK_BLAST 378 -#define EFFECT_COURT_CHANGE 379 -#define EFFECT_MAX_HP_50_RECOIL 380 -#define EFFECT_EXTREME_EVOBOOST 381 -#define EFFECT_HIT_SET_REMOVE_TERRAIN 382 // genesis supernova -#define EFFECT_DARK_VOID 383 -#define EFFECT_SLEEP_HIT 384 -#define EFFECT_DOUBLE_SHOCK 385 -#define EFFECT_SPECIAL_ATTACK_UP_HIT 386 -#define EFFECT_VICTORY_DANCE 387 -#define EFFECT_TEATIME 388 -#define EFFECT_ATTACK_UP_USER_ALLY 389 // Howl 8th Gen -#define EFFECT_SHELL_TRAP 390 -#define EFFECT_PSYBLADE 391 -#define EFFECT_HYDRO_STEAM 392 -#define EFFECT_HIT_SET_ENTRY_HAZARD 393 -#define EFFECT_DIRE_CLAW 394 -#define EFFECT_BARB_BARRAGE 395 -#define EFFECT_REVIVAL_BLESSING 396 -#define EFFECT_FROSTBITE_HIT 397 -#define EFFECT_SNOWSCAPE 398 -#define EFFECT_TRIPLE_ARROWS 399 -#define EFFECT_INFERNAL_PARADE 400 -#define EFFECT_TAKE_HEART 401 -#define EFFECT_AXE_KICK 402 -#define EFFECT_COLLISION_COURSE 403 -#define EFFECT_SPIN_OUT 404 -#define EFFECT_MAKE_IT_RAIN 405 -#define EFFECT_CORROSIVE_GAS 406 -#define EFFECT_POPULATION_BOMB 407 -#define EFFECT_MORTAL_SPIN 408 -#define EFFECT_SALT_CURE 409 -#define EFFECT_CHILLY_RECEPTION 410 -#define EFFECT_MATCHA_GOTCHA 411 -#define EFFECT_SYRUP_BOMB 412 -#define EFFECT_MAX_MOVE 413 -#define EFFECT_GLAIVE_RUSH 414 -#define EFFECT_RAGING_BULL 415 -#define EFFECT_RAGE_FIST 416 +#define EFFECT_PLEDGE 208 +#define EFFECT_FLING 209 +#define EFFECT_NATURAL_GIFT 210 +#define EFFECT_WAKE_UP_SLAP 211 +#define EFFECT_WRING_OUT 212 +#define EFFECT_HEX 213 +#define EFFECT_ASSURANCE 214 +#define EFFECT_TRUMP_CARD 215 +#define EFFECT_ACROBATICS 216 +#define EFFECT_HEAT_CRASH 217 +#define EFFECT_PUNISHMENT 218 +#define EFFECT_STORED_POWER 219 +#define EFFECT_ELECTRO_BALL 220 +#define EFFECT_GYRO_BALL 221 +#define EFFECT_ECHOED_VOICE 222 +#define EFFECT_PAYBACK 223 +#define EFFECT_ROUND 224 +#define EFFECT_BRINE 225 +#define EFFECT_VENOSHOCK 226 +#define EFFECT_RETALIATE 227 +#define EFFECT_BULLDOZE 228 +#define EFFECT_FOUL_PLAY 229 +#define EFFECT_PSYSHOCK 230 +#define EFFECT_ROOST 231 +#define EFFECT_GRAVITY 232 +#define EFFECT_MIRACLE_EYE 233 +#define EFFECT_TAILWIND 234 +#define EFFECT_EMBARGO 235 +#define EFFECT_AQUA_RING 236 +#define EFFECT_TRICK_ROOM 237 +#define EFFECT_WONDER_ROOM 238 +#define EFFECT_MAGIC_ROOM 239 +#define EFFECT_MAGNET_RISE 240 +#define EFFECT_TOXIC_SPIKES 241 +#define EFFECT_GASTRO_ACID 242 +#define EFFECT_STEALTH_ROCK 243 +#define EFFECT_TELEKINESIS 244 +#define EFFECT_POWER_SWAP 245 +#define EFFECT_GUARD_SWAP 246 +#define EFFECT_HEART_SWAP 247 +#define EFFECT_POWER_SPLIT 248 +#define EFFECT_GUARD_SPLIT 249 +#define EFFECT_STICKY_WEB 250 +#define EFFECT_METAL_BURST 251 +#define EFFECT_LUCKY_CHANT 252 +#define EFFECT_SUCKER_PUNCH 253 +#define EFFECT_SPECIAL_DEFENSE_DOWN_HIT_2 254 +#define EFFECT_SIMPLE_BEAM 255 +#define EFFECT_ENTRAINMENT 256 +#define EFFECT_HEAL_PULSE 257 +#define EFFECT_QUASH 258 +#define EFFECT_ION_DELUGE 259 +#define EFFECT_FREEZE_DRY 260 +#define EFFECT_TOPSY_TURVY 261 +#define EFFECT_MISTY_TERRAIN 262 +#define EFFECT_GRASSY_TERRAIN 263 +#define EFFECT_ELECTRIC_TERRAIN 264 +#define EFFECT_PSYCHIC_TERRAIN 265 +#define EFFECT_ATTACK_ACCURACY_UP 266 +#define EFFECT_ATTACK_SPATK_UP 267 +#define EFFECT_HURRICANE 268 +#define EFFECT_TWO_TYPED_MOVE 269 +#define EFFECT_ME_FIRST 270 +#define EFFECT_SPEED_UP_HIT 271 +#define EFFECT_QUIVER_DANCE 272 +#define EFFECT_COIL 273 +#define EFFECT_ELECTRIFY 274 +#define EFFECT_REFLECT_TYPE 275 +#define EFFECT_SOAK 276 +#define EFFECT_GROWTH 277 +#define EFFECT_CLOSE_COMBAT 278 +#define EFFECT_LAST_RESORT 279 +#define EFFECT_RECOIL_33_STATUS 280 +#define EFFECT_FLINCH_STATUS 281 +#define EFFECT_RECOIL_50 282 +#define EFFECT_SHELL_SMASH 283 +#define EFFECT_SHIFT_GEAR 284 +#define EFFECT_DEFENSE_UP_3 285 +#define EFFECT_NOBLE_ROAR 286 +#define EFFECT_VENOM_DRENCH 287 +#define EFFECT_TOXIC_THREAD 288 +#define EFFECT_CLEAR_SMOG 289 +#define EFFECT_HIT_SWITCH_TARGET 290 +#define EFFECT_FINAL_GAMBIT 291 +#define EFFECT_CHANGE_TYPE_ON_ITEM 292 +#define EFFECT_AUTOTOMIZE 293 +#define EFFECT_COPYCAT 294 +#define EFFECT_DEFOG 295 +#define EFFECT_HIT_ENEMY_HEAL_ALLY 296 +#define EFFECT_SMACK_DOWN 297 +#define EFFECT_SYNCHRONOISE 298 +#define EFFECT_PSYCHO_SHIFT 299 +#define EFFECT_POWER_TRICK 300 +#define EFFECT_FLAME_BURST 301 +#define EFFECT_AFTER_YOU 302 +#define EFFECT_BESTOW 303 +#define EFFECT_ROTOTILLER 304 +#define EFFECT_FLOWER_SHIELD 305 +#define EFFECT_HIT_PREVENT_ESCAPE 306 +#define EFFECT_SPEED_SWAP 307 +#define EFFECT_DEFENSE_UP2_HIT 308 +#define EFFECT_REVELATION_DANCE 309 +#define EFFECT_AURORA_VEIL 310 +#define EFFECT_THIRD_TYPE 311 +#define EFFECT_FEINT 312 +#define EFFECT_SPARKLING_ARIA 313 +#define EFFECT_ACUPRESSURE 314 +#define EFFECT_AROMATIC_MIST 315 +#define EFFECT_POWDER 316 +#define EFFECT_SP_ATTACK_UP_HIT 317 +#define EFFECT_BELCH 318 +#define EFFECT_PARTING_SHOT 319 +#define EFFECT_SPECTRAL_THIEF 320 +#define EFFECT_V_CREATE 321 +#define EFFECT_MAT_BLOCK 322 +#define EFFECT_STOMPING_TANTRUM 323 +#define EFFECT_CORE_ENFORCER 324 +#define EFFECT_INSTRUCT 325 +#define EFFECT_THROAT_CHOP 326 +#define EFFECT_LASER_FOCUS 327 +#define EFFECT_MAGNETIC_FLUX 328 +#define EFFECT_GEAR_UP 329 +#define EFFECT_INCINERATE 330 +#define EFFECT_BUG_BITE 331 +#define EFFECT_STRENGTH_SAP 332 +#define EFFECT_MIND_BLOWN 333 +#define EFFECT_PURIFY 334 +#define EFFECT_BURN_UP 335 +#define EFFECT_SHORE_UP 336 +#define EFFECT_GEOMANCY 337 +#define EFFECT_FAIRY_LOCK 338 +#define EFFECT_ALLY_SWITCH 339 +#define EFFECT_RELIC_SONG 340 +#define EFFECT_ATTACKER_DEFENSE_DOWN_HIT 341 +#define EFFECT_BODY_PRESS 342 +#define EFFECT_EERIE_SPELL 343 +#define EFFECT_JUNGLE_HEALING 344 +#define EFFECT_COACHING 345 +#define EFFECT_LASH_OUT 346 +#define EFFECT_GRASSY_GLIDE 347 +#define EFFECT_DYNAMAX_DOUBLE_DMG 348 +#define EFFECT_DECORATE 349 +#define EFFECT_SNIPE_SHOT 350 +#define EFFECT_RECOIL_HP_25 351 +#define EFFECT_STUFF_CHEEKS 352 +#define EFFECT_GRAV_APPLE 353 +#define EFFECT_EVASION_UP_HIT 354 +#define EFFECT_GLITZY_GLOW 355 +#define EFFECT_BADDY_BAD 356 +#define EFFECT_SAPPY_SEED 357 +#define EFFECT_FREEZY_FROST 358 +#define EFFECT_SPARKLY_SWIRL 359 +#define EFFECT_PLASMA_FISTS 360 +#define EFFECT_HYPERSPACE_FURY 361 +#define EFFECT_AURA_WHEEL 362 +#define EFFECT_PHOTON_GEYSER 363 +#define EFFECT_SHELL_SIDE_ARM 364 +#define EFFECT_TERRAIN_PULSE 365 +#define EFFECT_JAW_LOCK 366 +#define EFFECT_NO_RETREAT 367 +#define EFFECT_TAR_SHOT 368 +#define EFFECT_POLTERGEIST 369 +#define EFFECT_OCTOLOCK 370 +#define EFFECT_CLANGOROUS_SOUL 371 +#define EFFECT_BOLT_BEAK 372 +#define EFFECT_SKY_DROP 373 +#define EFFECT_EXPANDING_FORCE 374 +#define EFFECT_METEOR_BEAM 375 +#define EFFECT_RISING_VOLTAGE 376 +#define EFFECT_BEAK_BLAST 377 +#define EFFECT_COURT_CHANGE 378 +#define EFFECT_MAX_HP_50_RECOIL 379 +#define EFFECT_EXTREME_EVOBOOST 380 +#define EFFECT_HIT_SET_REMOVE_TERRAIN 381 // genesis supernova +#define EFFECT_DARK_VOID 382 +#define EFFECT_SLEEP_HIT 383 +#define EFFECT_DOUBLE_SHOCK 384 +#define EFFECT_SPECIAL_ATTACK_UP_HIT 385 +#define EFFECT_VICTORY_DANCE 386 +#define EFFECT_TEATIME 387 +#define EFFECT_ATTACK_UP_USER_ALLY 388 // Howl 8th Gen +#define EFFECT_SHELL_TRAP 389 +#define EFFECT_PSYBLADE 390 +#define EFFECT_HYDRO_STEAM 391 +#define EFFECT_HIT_SET_ENTRY_HAZARD 392 +#define EFFECT_DIRE_CLAW 393 +#define EFFECT_BARB_BARRAGE 394 +#define EFFECT_REVIVAL_BLESSING 395 +#define EFFECT_FROSTBITE_HIT 396 +#define EFFECT_SNOWSCAPE 397 +#define EFFECT_TRIPLE_ARROWS 398 +#define EFFECT_INFERNAL_PARADE 399 +#define EFFECT_TAKE_HEART 400 +#define EFFECT_AXE_KICK 401 +#define EFFECT_COLLISION_COURSE 402 +#define EFFECT_SPIN_OUT 403 +#define EFFECT_MAKE_IT_RAIN 404 +#define EFFECT_CORROSIVE_GAS 405 +#define EFFECT_POPULATION_BOMB 406 +#define EFFECT_MORTAL_SPIN 407 +#define EFFECT_SALT_CURE 408 +#define EFFECT_CHILLY_RECEPTION 409 +#define EFFECT_MATCHA_GOTCHA 410 +#define EFFECT_SYRUP_BOMB 411 +#define EFFECT_MAX_MOVE 412 +#define EFFECT_GLAIVE_RUSH 413 +#define EFFECT_RAGING_BULL 414 +#define EFFECT_RAGE_FIST 415 -#define NUM_BATTLE_MOVE_EFFECTS 417 +#define NUM_BATTLE_MOVE_EFFECTS 416 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 130770fec4..f8ffac4c06 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -1893,19 +1893,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; } break; - case EFFECT_TEETER_DANCE: - if (((gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) - || (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move) && aiData->abilities[battlerDef] == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(battlerDef) && AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN)) - || (DoesSubstituteBlockMove(battlerAtk, battlerDef, move))) - && ((gBattleMons[BATTLE_PARTNER(battlerDef)].status2 & STATUS2_CONFUSION) - || (!DoesBattlerIgnoreAbilityChecks(aiData->abilities[battlerAtk], move) && aiData->abilities[BATTLE_PARTNER(battlerDef)] == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(BATTLE_PARTNER(battlerDef)) && AI_IsTerrainAffected(BATTLE_PARTNER(battlerDef), STATUS_FIELD_MISTY_TERRAIN)) - || (DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), move)))) - { - ADJUST_SCORE(-10); - } - break; case EFFECT_TRANSFORM: if (gBattleMons[battlerAtk].status2 & STATUS2_TRANSFORMED || (gBattleMons[battlerDef].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE))) //Leave out Illusion b/c AI is supposed to be fooled @@ -2945,7 +2932,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_SWAGGER: if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] < MAX_STAT_STAGE && HasMoveWithSplit(battlerAtkPartner, SPLIT_PHYSICAL) - && (!AI_CanBeConfused(battlerAtkPartner, TRUE) + && (!AI_CanBeConfused(battlerAtk, battlerAtkPartner, move, TRUE) || atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION || atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS)) { @@ -2955,7 +2942,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_FLATTER: if (gBattleMons[battlerAtkPartner].statStages[STAT_SPATK] < MAX_STAT_STAGE && HasMoveWithSplit(battlerAtkPartner, SPLIT_SPECIAL) - && (!AI_CanBeConfused(battlerAtkPartner, TRUE) + && (!AI_CanBeConfused(battlerAtk, battlerAtkPartner, move, TRUE) || atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION || atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS)) { @@ -4959,7 +4946,6 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score case EFFECT_WILL_O_WISP: case EFFECT_INGRAIN: case EFFECT_IMPRISON: - case EFFECT_TEETER_DANCE: case EFFECT_TICKLE: case EFFECT_COSMIC_POWER: case EFFECT_BULK_UP: @@ -5029,7 +5015,6 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_MIRROR_COAT: case EFFECT_FOCUS_PUNCH: case EFFECT_REVENGE: - case EFFECT_TEETER_DANCE: if (Random() & 1) ADJUST_SCORE(2); break; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index f675602fb9..e89380aad8 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -356,7 +356,6 @@ static const u16 sEncouragedEncoreEffects[] = EFFECT_IMPRISON, EFFECT_REFRESH, EFFECT_GRUDGE, - EFFECT_TEETER_DANCE, EFFECT_MUD_SPORT, EFFECT_WATER_SPORT, EFFECT_DRAGON_DANCE, @@ -1422,7 +1421,6 @@ bool32 IsConfusionMoveEffect(u32 moveEffect) case EFFECT_CONFUSE: case EFFECT_SWAGGER: case EFFECT_FLATTER: - case EFFECT_TEETER_DANCE: return TRUE; default: return FALSE; @@ -2819,25 +2817,27 @@ bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, return TRUE; } -bool32 AI_CanBeConfused(u32 battler, u32 ability) +bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability) { - if ((gBattleMons[battler].status2 & STATUS2_CONFUSION) - || (ability == ABILITY_OWN_TEMPO) - || (IsBattlerGrounded(battler) && (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN))) + if ((gBattleMons[battlerDef].status2 & STATUS2_CONFUSION) + || (ability == ABILITY_OWN_TEMPO && !DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move)) + || (IsBattlerGrounded(battlerDef) && (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)) + || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD + || DoesSubstituteBlockMove(battlerAtk, battlerDef, move)) return FALSE; return TRUE; } bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove) { - if (!AI_CanBeConfused(battlerDef, defAbility) - || AI_GetMoveEffectiveness(move, battlerAtk, battlerDef) == AI_EFFECTIVENESS_x0 - || gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD - || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) - || DoesPartnerHaveSameMoveEffect(battlerAtkPartner, battlerDef, move, partnerMove)) - { + if (gBattleMoves[move].target == MOVE_TARGET_FOES_AND_ALLY + && AI_CanBeConfused(battlerAtk, battlerDef, move, defAbility) + && !AI_CanBeConfused(battlerAtk, BATTLE_PARTNER(battlerDef), move, AI_DATA->abilities[BATTLE_PARTNER(battlerDef)])) + return FALSE; + + if (!AI_CanBeConfused(battlerAtk, battlerDef, move, defAbility) + || DoesPartnerHaveSameMoveEffect(battlerAtkPartner, battlerDef, move, partnerMove)) return FALSE; - } return TRUE; } diff --git a/src/battle_tv.c b/src/battle_tv.c index f22b9f96e5..d7af63cd68 100644 --- a/src/battle_tv.c +++ b/src/battle_tv.c @@ -281,7 +281,6 @@ static const u16 sPoints_MoveEffect[NUM_BATTLE_MOVE_EFFECTS] = [EFFECT_LOW_KICK] = 1, [EFFECT_SECRET_POWER] = 1, [EFFECT_RECOIL_33] = 2, - [EFFECT_TEETER_DANCE] = 6, // [EFFECT_BLAZE_KICK] = 1, [EFFECT_MUD_SPORT] = 4, [EFFECT_POISON_FANG] = 1, diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 1a2347f557..5731b5fc69 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -5340,7 +5340,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_DYNAMAX] = [MOVE_TEETER_DANCE] = { - .effect = EFFECT_TEETER_DANCE, + .effect = EFFECT_CONFUSE, .power = 0, .type = TYPE_NORMAL, .accuracy = 100, diff --git a/test/battle/ability/dancer.c b/test/battle/ability/dancer.c new file mode 100644 index 0000000000..4a7a58f47d --- /dev/null +++ b/test/battle/ability/dancer.c @@ -0,0 +1,35 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Dancer can copy a dance move immediately after it was used and allow the user of Dancer to still use its move") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_QUIVER_DANCE].danceMove == TRUE); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); } + } WHEN { + TURN { MOVE(player, MOVE_QUIVER_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUIVER_DANCE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + ABILITY_POPUP(opponent, ABILITY_DANCER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_QUIVER_DANCE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); // Same turn + } +} + +SINGLE_BATTLE_TEST("Dancer can copy Teeter Dance") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_TEETER_DANCE].danceMove == TRUE); + PLAYER(SPECIES_WOBBUFFET) + OPPONENT(SPECIES_ORICORIO) { Ability(ABILITY_DANCER); Item(ITEM_LUM_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_TEETER_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, player); + ABILITY_POPUP(opponent, ABILITY_DANCER); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, opponent); + } +} diff --git a/test/battle/move_effect/confuse.c b/test/battle/move_effect/confuse.c new file mode 100644 index 0000000000..be4b8f596e --- /dev/null +++ b/test/battle/move_effect/confuse.c @@ -0,0 +1,58 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_TEETER_DANCE].effect == EFFECT_CONFUSE); +} + +SINGLE_BATTLE_TEST("Teeter Dance confuses target") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TEETER_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + MESSAGE("Foe Wobbuffet became confused!"); + } +} + +SINGLE_BATTLE_TEST("Teeter Dance confusion is blocked by Own Tempo") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_SLOWPOKE) { Ability(ABILITY_OWN_TEMPO); } + } WHEN { + TURN { MOVE(player, MOVE_TEETER_DANCE); } + } SCENE { + ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + MESSAGE("Foe Wobbuffet became confused!"); + } + } +} + +DOUBLE_BATTLE_TEST("Teeter Dance can confuse foes and allies") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WYNAUT); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(playerLeft, MOVE_TEETER_DANCE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TEETER_DANCE, playerLeft); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponentLeft); + MESSAGE("Foe Wobbuffet became confused!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, playerRight); + MESSAGE("Wynaut became confused!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponentRight); + MESSAGE("Foe Wynaut became confused!"); + } +}