diff --git a/.all-contributorsrc b/.all-contributorsrc
index e5ecce9602..0f6667d232 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -118,7 +118,8 @@
"profile": "https://linktr.ee/pkmnsnfrn",
"contributions": [
"maintenance",
- "code"
+ "code",
+ "projectManagement"
]
},
{
@@ -223,7 +224,7 @@
"name": "Ruby",
"avatar_url": "https://avatars.githubusercontent.com/u/178652077?v=4",
"profile": "https://github.com/RubyRaven6",
- "contributions": [
+ "contributions": [
"code",
"doc"
]
@@ -255,10 +256,45 @@
"contributions": [
"code"
]
+ },
+ {
+ "login": "bassforte123",
+ "name": "bassforte123",
+ "avatar_url": "https://avatars.githubusercontent.com/u/130828119?v=4",
+ "profile": "https://github.com/bassforte123",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "iriv24",
+ "name": "iriv24",
+ "avatar_url": "https://avatars.githubusercontent.com/u/40581123?v=4",
+ "profile": "https://github.com/iriv24",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "Bivurnum",
+ "name": "Bivurnum",
+ "avatar_url": "https://avatars.githubusercontent.com/u/147376167?v=4",
+ "profile": "https://github.com/Bivurnum",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "ravepossum",
+ "name": "RavePossum",
+ "avatar_url": "https://avatars.githubusercontent.com/u/145081120?v=4",
+ "profile": "https://github.com/ravepossum",
+ "contributions": [
+ "code"
+ ]
}
],
"contributorsPerLine": 7,
"linkToUsage": true,
- "commitType": "docs",
- "skipCi": true
+ "commitType": "docs"
}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index c3040f55c7..30231dbd0f 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,6 +9,7 @@ on:
jobs:
build:
+ if: github.actor != 'allcontributors[bot]'
runs-on: ubuntu-latest
env:
GAME_VERSION: EMERALD
@@ -36,3 +37,11 @@ jobs:
TEST: 1
run: |
make -j${nproc} check
+ allcontributors:
+ if: github.actor == 'allcontributors[bot]'
+ runs-on: ubuntu-latest
+ needs: []
+ steps:
+ - name: Automatically pass for allcontributors
+ run: echo "CI automatically passes for allcontributors" && exit 0
+
diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml
index 85800211ef..325e72950d 100644
--- a/.github/workflows/labels.yml
+++ b/.github/workflows/labels.yml
@@ -6,6 +6,7 @@ on:
jobs:
label:
+ if: github.actor != 'allcontributors[bot]'
runs-on: ubuntu-latest
steps:
- name: check labels
@@ -27,3 +28,11 @@ jobs:
category: pokemon
category: sprite-issue
type: documentation
+ allcontributors:
+ if: github.actor == 'allcontributors[bot]'
+ runs-on: ubuntu-latest
+ needs: []
+ steps:
+ - name: Automatically pass for allcontributors
+ run: echo "CI automatically passes for allcontributors" && exit 0
+
diff --git a/CREDITS.md b/CREDITS.md
index 753cf2b09e..54db10fd49 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -30,24 +30,28 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
 Pawkkie π§ π» π |
 Philipp AUER π§ π» |
 tertu π§ π» |
-  psf π§ π» |
+  psf π§ π» π |
+  wiz1989 π» |
 PCG π» |
 kittenchilly π» π¬ π£ |
-  ExpoSeed π» π§ π |
+  ExpoSeed π» π§ π |
 Linathan π» |
 Eduardo Quezada π» π£ π π π§ π π£ π¬ π β οΈ β
π |
 khbsd π π» |
 Cafe π¨ |
 agsmgmaster64 π» |
-  mudskipper13 π» π |
-  Ruby π» π |
+  Ruby π» π |
-  surskitty π» |
-  wiz1989 π» |
+  mudskipper13 π» π |
+  surskitty π» |
 grintoul π» |
+  bassforte123 π» |
+  iriv24 π» |
+  Bivurnum π» |
+  RavePossum π» |
diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s
index aab7bff6b3..2d6a0c7d7a 100644
--- a/data/battle_scripts_1.s
+++ b/data/battle_scripts_1.s
@@ -8285,7 +8285,9 @@ BattleScript_WanderingSpiritActivates::
printstring STRINGID_SWAPPEDABILITIES
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
+ jumpiffainted BS_TARGET, TRUE, BattleScript_WanderingSpiritActivatesRet
switchinabilities BS_TARGET
+BattleScript_WanderingSpiritActivatesRet:
return
BattleScript_TargetsStatWasMaxedOut::
diff --git a/docs/STYLEGUIDE.md b/docs/STYLEGUIDE.md
index f53e1a2dcc..dc99be6b1b 100644
--- a/docs/STYLEGUIDE.md
+++ b/docs/STYLEGUIDE.md
@@ -265,6 +265,15 @@ void SetCurrentDifficultyLevel(enum DifficultyLevel desiredDifficulty)
.power = B_UPDATED_MOVE_DATA >= GEN_6 ? 45 : 35,
},
```
+### Variable Declarations
+Loop iterators should be declared as part of the loop unless there's a very good reason not to.
+```C
+for (u32 i = 0; i < LOOP_ITERATIONS; i++)
+{
+ dst1[i] = i;
+ dst2[i] = i;
+}
+```
## Data Type Sizes
When a variable number is used, the data type should generally `u32` (unsigned) or `s32` (signed). There are a few exceptions to this rule, such as:
* Values stored in the saveblock should use the smallest data type possible.
@@ -350,7 +359,7 @@ enum DifficultyLevel GetCurrentDifficultyLevel(void)
}
```
-### Data file format
+## Data file format
External data files should use JSON.
diff --git a/docs/tutorials/how_to_trainer_pic.md b/docs/tutorials/how_to_trainer_pic.md
index 78f8a717e4..beddb641e4 100644
--- a/docs/tutorials/how_to_trainer_pic.md
+++ b/docs/tutorials/how_to_trainer_pic.md
@@ -31,13 +31,13 @@ Export the palette and place into the same folder.
Sadly, just putting the image files into the graphics folder is not enough. To use the sprites we have to register them by linking the graphic files.
[src/data/graphics/trainers](https://github.com/rh-hideout/pokeemerald-expansion/blob/master/src/data/graphics/trainers.h):
```diff
-const u32 gTrainerPalette_RubySapphireBrendan[] = INCBIN_U32("graphics/trainers/palettes/ruby_sapphire_brendan.gbapal.lz");
+const u16 gTrainerPalette_RubySapphireBrendan[] = INCBIN_U16("graphics/trainers/palettes/ruby_sapphire_brendan.gbapal");
const u32 gTrainerFrontPic_RubySapphireMay[] = INCBIN_U32("graphics/trainers/front_pics/ruby_sapphire.4bpp.lz");
-const u32 gTrainerPalette_RubySapphireMay[] = INCBIN_U32("graphics/trainers/palettes/ruby_sapphire_may.gbapal.lz");
+const u16 gTrainerPalette_RubySapphireMay[] = INCBIN_U16("graphics/trainers/palettes/ruby_sapphire_may.gbapal");
+ const u32 gTrainerFrontPic_myTrainerClass[] = INCBIN_U32("graphics/trainers/front_pics/myTrainerClass.4bpp.lz");
-+ const u32 gTrainerPalette_myTrainerClass[] = INCBIN_U32("graphics/trainers/palettes/myTrainerClass.gbapal.lz");
++ const u16 gTrainerPalette_myTrainerClass[] = INCBIN_U16("graphics/trainers/palettes/myTrainerClass.gbapal");
const u8 gTrainerBackPic_Brendan[] = INCBIN_U8("graphics/trainers/back_pics/brendan.4bpp");
```
diff --git a/include/battle_controllers.h b/include/battle_controllers.h
index 8f2d78ed41..2ce28ca609 100644
--- a/include/battle_controllers.h
+++ b/include/battle_controllers.h
@@ -72,41 +72,63 @@ enum {
// (e.g. MarkBattlerForControllerExec) instead of using these macros
// directly.
-#define MARK_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler) \
- gBattleControllerExecFlags |= (1u << battler)
+static inline void MarkBattleControllerActiveOnLocal(u32 battler)
+{
+ gBattleControllerExecFlags |= (1u << battler);
+}
-#define MARK_BATTLE_CONTROLLER_IDLE_ON_LOCAL(battler) \
- gBattleControllerExecFlags &= ~(1u << battler)
+static inline void MarkBattleControllerIdleOnLocal(u32 battler)
+{
+ gBattleControllerExecFlags &= ~(1u << battler);
+}
-#define IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler) \
- (gBattleControllerExecFlags & (1u << battler))
+static inline bool32 IsBattleControllerActiveOnLocal(u32 battler)
+{
+ return gBattleControllerExecFlags & (1u << battler);
+}
-#define MARK_BATTLE_CONTROLLER_MESSAGE_OUTBOUND_OVER_LINK(battler) \
- gBattleControllerExecFlags |= ((1u << battler) << (32 - MAX_BATTLERS_COUNT))
+static inline void MarkBattleControllerMessageOutboundOverLink(u32 battler)
+{
+ gBattleControllerExecFlags |= ((1u << battler) << (32 - MAX_BATTLERS_COUNT));
+}
-#define MARK_BATTLE_CONTROLLER_MESSAGE_SYNCHRONIZED_OVER_LINK(battler) \
- gBattleControllerExecFlags &= ~((1 << 28) << (battler))
+static inline void MarkBattleControllerMessageSynchronizedOverLink(u32 battler)
+{
+ gBattleControllerExecFlags &= ~((1 << 28) << (battler));
+}
-#define MARK_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battler, playerId) \
- gBattleControllerExecFlags |= ((1u << battler) << ((playerId) << 2))
+static inline bool32 IsBattleControllerMessageSynchronizedOverLink(u32 battler)
+{
+ return gBattleControllerExecFlags & (1u << (battler + 28));
+}
-#define MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battler, playerId) \
- gBattleControllerExecFlags &= ~((1u << battler) << ((playerId) * 4))
+static inline void MarkBattleControllerActiveForPlayer(u32 battler, u32 playerId)
+{
+ gBattleControllerExecFlags |= ((1u << battler) << ((playerId) << 2));
+}
-#define IS_BATTLE_CONTROLLER_ACTIVE_FOR_PLAYER(battler, playerId) \
- (gBattleControllerExecFlags & ((1u << battler) << ((playerId) * 4)))
+static inline void MarkBattleControllerIdleForPlayer(u32 battler, u32 playerId)
+{
+ gBattleControllerExecFlags &= ~((1u << battler) << ((playerId) * 4));
+}
+
+static inline bool32 IsBattleControllerActiveForPlayer(u32 battler, u32 playerId)
+{
+ return gBattleControllerExecFlags & ((1u << battler) << ((playerId) * 4));
+}
// This actually checks if a specific controller is active on any player or if
// *any* controller is pending sync over link communications, but the macro name
// can only be so specific before it just gets ridiculous.
-#define IS_BATTLE_CONTROLLER_ACTIVE_OR_PENDING_SYNC_ANYWHERE(battler) \
- (gBattleControllerExecFlags & ( \
- (1u << battler) \
- | (0xF << 28) \
- | (1u << battler << 4) \
- | (1u << battler << 8) \
- | (1u << battler << 12) \
- ))
+static inline bool32 IsBattleControllerActiveOrPendingSyncAnywhere(u32 battler)
+{
+ return gBattleControllerExecFlags & (
+ (1u << battler)
+ | (0xF << 28)
+ | (1u << battler << 4)
+ | (1u << battler << 8)
+ | (1u << battler << 12));
+}
// Special arguments for Battle Controller functions.
diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h
index 4637d583e1..fdd256740e 100644
--- a/include/constants/battle_move_effects.h
+++ b/include/constants/battle_move_effects.h
@@ -310,6 +310,7 @@ enum BattleMoveEffects
EFFECT_COURT_CHANGE,
EFFECT_MAX_HP_50_RECOIL,
EFFECT_MIND_BLOWN, // Same as EFFECT_MAX_HP_50_RECOIL but is cancelled by Damp
+ EFFECT_CHLOROBLAST, // Same effect as EFFECT_MAX_HP_50_RECOIL but follows the same rules as EFFECT_RECOIL
EFFECT_EXTREME_EVOBOOST,
EFFECT_HIT_SET_REMOVE_TERRAIN,
EFFECT_DARK_VOID,
diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c
index 9924dc74c4..28af35bc4a 100644
--- a/src/battle_ai_main.c
+++ b/src/battle_ai_main.c
@@ -5559,6 +5559,7 @@ static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_MAX_HP_50_RECOIL:
case EFFECT_MIND_BLOWN:
+ case EFFECT_CHLOROBLAST:
case EFFECT_SWAGGER:
case EFFECT_FLATTER:
case EFFECT_ATTRACT:
diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c
index dbbd30c68a..487a1facc4 100644
--- a/src/battle_ai_util.c
+++ b/src/battle_ai_util.c
@@ -1022,6 +1022,7 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s
{
case EFFECT_MAX_HP_50_RECOIL:
case EFFECT_MIND_BLOWN:
+ case EFFECT_CHLOROBLAST:
case EFFECT_EXPLOSION:
case EFFECT_MISTY_EXPLOSION:
case EFFECT_FINAL_GAMBIT:
diff --git a/src/battle_anim_effects_2.c b/src/battle_anim_effects_2.c
index 93dddfc6d9..96e78cb0d6 100755
--- a/src/battle_anim_effects_2.c
+++ b/src/battle_anim_effects_2.c
@@ -3395,7 +3395,7 @@ void AnimTask_ScaryFace(u8 taskId)
bool32 onPlayer;
if (gAnimMoveIndex == MOVE_BITTER_MALICE)
- onPlayer = IsOnPlayerSide(gBattleAnimAttacker);
+ onPlayer = !IsOnPlayerSide(gBattleAnimAttacker);
else
onPlayer = !IsOnPlayerSide(gBattleAnimTarget);
diff --git a/src/battle_controller_link_opponent.c b/src/battle_controller_link_opponent.c
index c79588695d..a039a5b522 100644
--- a/src/battle_controller_link_opponent.c
+++ b/src/battle_controller_link_opponent.c
@@ -114,7 +114,7 @@ void SetControllerToLinkOpponent(u32 battler)
static void LinkOpponentBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sLinkOpponentBufferCommands))
sLinkOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@@ -320,7 +320,7 @@ static void LinkOpponentBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
diff --git a/src/battle_controller_link_partner.c b/src/battle_controller_link_partner.c
index f54ee0d220..9b27309bf6 100644
--- a/src/battle_controller_link_partner.c
+++ b/src/battle_controller_link_partner.c
@@ -113,7 +113,7 @@ void SetControllerToLinkPartner(u32 battler)
static void LinkPartnerBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sLinkPartnerBufferCommands))
sLinkPartnerBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@@ -164,7 +164,7 @@ static void LinkPartnerBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c
index e8cdb43945..0f33ae73fd 100644
--- a/src/battle_controller_opponent.c
+++ b/src/battle_controller_opponent.c
@@ -127,7 +127,7 @@ void SetControllerToOpponent(u32 battler)
static void OpponentBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sOpponentBufferCommands))
sOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@@ -364,7 +364,7 @@ static void OpponentBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c
index ad74cd03c9..768be59c4a 100644
--- a/src/battle_controller_player.c
+++ b/src/battle_controller_player.c
@@ -182,13 +182,13 @@ static void PlayerBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
static void PlayerBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerBufferCommands))
sPlayerBufferCommands[gBattleResources->bufferA[battler][0]](battler);
diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c
index d7eec61c32..0127e4bd20 100644
--- a/src/battle_controller_player_partner.c
+++ b/src/battle_controller_player_partner.c
@@ -117,7 +117,7 @@ void SetControllerToPlayerPartner(u32 battler)
static void PlayerPartnerBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sPlayerPartnerBufferCommands))
sPlayerPartnerBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@@ -236,7 +236,7 @@ static void PlayerPartnerBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
diff --git a/src/battle_controller_recorded_opponent.c b/src/battle_controller_recorded_opponent.c
index b670a4d001..2f836b45ce 100644
--- a/src/battle_controller_recorded_opponent.c
+++ b/src/battle_controller_recorded_opponent.c
@@ -120,7 +120,7 @@ void SetControllerToRecordedOpponent(u32 battler)
static void RecordedOpponentBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sRecordedOpponentBufferCommands))
sRecordedOpponentBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@@ -141,7 +141,7 @@ static void RecordedOpponentBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
diff --git a/src/battle_controller_recorded_player.c b/src/battle_controller_recorded_player.c
index f3686d7805..d84276e30f 100644
--- a/src/battle_controller_recorded_player.c
+++ b/src/battle_controller_recorded_player.c
@@ -117,7 +117,7 @@ void SetControllerToRecordedPlayer(u32 battler)
static void RecordedPlayerBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sRecordedPlayerBufferCommands))
sRecordedPlayerBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@@ -313,7 +313,7 @@ static void RecordedPlayerBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
diff --git a/src/battle_controller_safari.c b/src/battle_controller_safari.c
index 560a95cc5f..4a1fc3888c 100644
--- a/src/battle_controller_safari.c
+++ b/src/battle_controller_safari.c
@@ -109,7 +109,7 @@ void SetControllerToSafari(u32 battler)
static void SafariBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sSafariBufferCommands))
sSafariBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@@ -237,7 +237,7 @@ static void SafariBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
diff --git a/src/battle_controller_wally.c b/src/battle_controller_wally.c
index cea2578ac4..21492821bd 100644
--- a/src/battle_controller_wally.c
+++ b/src/battle_controller_wally.c
@@ -125,7 +125,7 @@ void SetControllerToWally(u32 battler)
static void WallyBufferRunCommand(u32 battler)
{
- if (gBattleControllerExecFlags & (1u << battler))
+ if (IsBattleControllerActiveOnLocal(battler))
{
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sWallyBufferCommands))
sWallyBufferCommands[gBattleResources->bufferA[battler][0]](battler);
@@ -284,7 +284,7 @@ static void WallyBufferExecCompleted(u32 battler)
}
else
{
- gBattleControllerExecFlags &= ~(1u << battler);
+ MarkBattleControllerIdleOnLocal(battler);
}
}
diff --git a/src/battle_controllers.c b/src/battle_controllers.c
index 9b6a74fc6e..4faec49613 100644
--- a/src/battle_controllers.c
+++ b/src/battle_controllers.c
@@ -956,7 +956,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId)
switch (BYTE_TO_RECEIVE(0))
{
case B_COMM_TO_CONTROLLER:
- if (IS_BATTLE_CONTROLLER_ACTIVE_ON_LOCAL(battler))
+ if (IsBattleControllerActiveOnLocal(battler))
return;
memcpy(gBattleResources->bufferA[battler], &BYTE_TO_RECEIVE(LINK_BUFF_DATA), blockSize);
@@ -975,7 +975,7 @@ static void Task_HandleCopyReceivedLinkBuffersData(u8 taskId)
break;
case B_COMM_CONTROLLER_IS_DONE:
playerId = BYTE_TO_RECEIVE(LINK_BUFF_DATA);
- MARK_BATTLE_CONTROLLER_IDLE_FOR_PLAYER(battler, playerId);
+ MarkBattleControllerIdleForPlayer(battler, playerId);
break;
}
diff --git a/src/battle_end_turn.c b/src/battle_end_turn.c
index 7e9f8b15f5..6cf45ded4d 100644
--- a/src/battle_end_turn.c
+++ b/src/battle_end_turn.c
@@ -490,7 +490,7 @@ static bool32 HandleEndTurnFirstEventBlock(u32 battler)
gBattleStruct->eventBlockCounter++;
break;
case FIRST_EVENT_BLOCK_GRASSY_TERRAIN_HEAL:
- if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler))
+ if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerAlive(battler) && !IsBattlerAtMaxHp(battler) && IsBattlerGrounded(battler))
{
gBattlerAttacker = battler;
gBattleStruct->moveDamage[battler] = -(GetNonDynamaxMaxHP(battler) / 16);
diff --git a/src/battle_main.c b/src/battle_main.c
index 2bf62fa1ed..ba41faf697 100644
--- a/src/battle_main.c
+++ b/src/battle_main.c
@@ -4210,7 +4210,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_ACTION_CHOSEN: // Try to perform an action.
- if (!(gBattleControllerExecFlags & (((1u << battler)) | (0xF << 28) | ((1u << battler) << 4) | ((1u << battler) << 8) | ((1u << battler) << 12))))
+ if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
RecordedBattle_SetBattlerAction(battler, gBattleResources->bufferB[battler][1]);
gChosenActionByBattler[battler] = gBattleResources->bufferB[battler][1];
@@ -4414,7 +4414,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_ACTION_CASE_CHOSEN:
- if (!(gBattleControllerExecFlags & (((1u << battler)) | (0xF << 28) | ((1u << battler) << 4) | ((1u << battler) << 8) | ((1u << battler) << 12))))
+ if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
switch (gChosenActionByBattler[battler])
{
@@ -4544,11 +4544,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_ACTION_CONFIRMED_STANDBY:
- if (!(gBattleControllerExecFlags & ((1u << battler)
- | (0xF << 28)
- | (1u << (battler + 4))
- | (1u << (battler + 8))
- | (1u << (battler + 12)))))
+ if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
if (AllAtActionConfirmed())
i = TRUE;
@@ -4570,7 +4566,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_ACTION_CONFIRMED:
- if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
+ if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
gBattleCommunication[ACTIONS_CONFIRMED_COUNT]++;
}
@@ -4584,7 +4580,7 @@ static void HandleTurnActionSelectionState(void)
{
gBattlerAttacker = battler;
gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
- if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
+ if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
@@ -4592,7 +4588,7 @@ static void HandleTurnActionSelectionState(void)
}
break;
case STATE_WAIT_SET_BEFORE_ACTION:
- if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
+ if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
gBattleCommunication[battler] = STATE_BEFORE_ACTION_CHOSEN;
}
@@ -4616,7 +4612,7 @@ static void HandleTurnActionSelectionState(void)
{
gBattlerAttacker = battler;
gBattlescriptCurrInstr = gSelectionBattleScripts[battler];
- if (!(gBattleControllerExecFlags & ((1u << battler) | (0xF << 28) | (1u << (battler + 4)) | (1u << (battler + 8)) | (1u << (battler + 12)))))
+ if (!IsBattleControllerActiveOrPendingSyncAnywhere(battler))
{
gBattleScriptingCommandsTable[gBattlescriptCurrInstr[0]]();
}
@@ -4651,7 +4647,7 @@ static void HandleTurnActionSelectionState(void)
for (i = 0; i < gBattlersCount; i++)
{
if (gChosenActionByBattler[i] == B_ACTION_SWITCH)
- SwitchPartyOrderInGameMulti(i, gBattleStruct->monToSwitchIntoId[battler]);
+ SwitchPartyOrderInGameMulti(i, gBattleStruct->monToSwitchIntoId[i]);
}
}
}
@@ -4703,6 +4699,10 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
{
u32 speed = gBattleMons[battler].speed;
+ // stat stages
+ speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0];
+ speed /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][1];
+
// weather abilities
if (HasWeatherEffect())
{
@@ -4730,10 +4730,6 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, enum ItemHoldEffect h
else if (ability == ABILITY_UNBURDEN && gDisableStructs[battler].unburdenActive)
speed *= 2;
- // stat stages
- speed *= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][0];
- speed /= gStatStageRatios[gBattleMons[battler].statStages[STAT_SPEED]][1];
-
// player's badge boost
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_FRONTIER))
&& ShouldGetStatBadgeBoost(B_FLAG_BADGE_BOOST_SPEED, battler)
@@ -6002,7 +5998,9 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState
{
return TYPE_WATER;
}
- else if (moveEffect == EFFECT_AURA_WHEEL && species == SPECIES_MORPEKO_HANGRY)
+ else if (moveEffect == EFFECT_AURA_WHEEL
+ && species == SPECIES_MORPEKO_HANGRY
+ && ability != ABILITY_NORMALIZE)
{
return TYPE_DARK;
}
@@ -6016,7 +6014,9 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, enum MonState
gBattleStruct->ateBoost[battler] = TRUE;
return ateType;
}
- else if (moveType != TYPE_NORMAL
+ else if (moveEffect != EFFECT_CHANGE_TYPE_ON_ITEM
+ && moveEffect != EFFECT_TERRAIN_PULSE
+ && moveEffect != EFFECT_NATURAL_GIFT
&& moveEffect != EFFECT_HIDDEN_POWER
&& moveEffect != EFFECT_WEATHER_BALL
&& ability == ABILITY_NORMALIZE
diff --git a/src/battle_message.c b/src/battle_message.c
index 98a44a1ef7..fbb36fa6d1 100644
--- a/src/battle_message.c
+++ b/src/battle_message.c
@@ -3164,7 +3164,7 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst, u32 dstSize)
dst[dstID] = *src;
dstID++;
- BreakStringAutomatic(dst, BATTLE_MSG_MAX_WIDTH, BATTLE_MSG_MAX_LINES, fontId, TRUE);
+ BreakStringAutomatic(dst, BATTLE_MSG_MAX_WIDTH, BATTLE_MSG_MAX_LINES, fontId, SHOW_SCROLL_PROMPT);
return dstID;
}
diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c
index 5614a9e3e9..75d15d6449 100644
--- a/src/battle_script_commands.c
+++ b/src/battle_script_commands.c
@@ -6991,6 +6991,15 @@ static void Cmd_moveend(void)
effect = TRUE;
}
break;
+ case EFFECT_CHLOROBLAST:
+ if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
+ {
+ gBattleStruct->moveDamage[gBattlerAttacker] = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
+ BattleScriptPushCursor();
+ gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
+ effect = TRUE;
+ }
+ break;
case EFFECT_RAPID_SPIN:
if (IsBattlerTurnDamaged(gBattlerTarget))
{
@@ -7393,6 +7402,12 @@ static void Cmd_moveend(void)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
if (B_CHARGE >= GEN_9 && moveType == TYPE_ELECTRIC && (IsBattlerTurnDamaged(gBattlerTarget) || gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
gStatuses3[gBattlerAttacker] &= ~(STATUS3_CHARGED_UP);
+ // check if Stellar type boost should be used up
+ if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
+ && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
+ && GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS
+ && IsTypeStellarBoosted(gBattlerAttacker, moveType))
+ ExpendTypeStellarBoost(gBattlerAttacker, moveType);
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
for (i = 0; i < gBattlersCount; i++)
@@ -7882,7 +7897,16 @@ static void Cmd_openpartyscreen(void)
{
if (((1u << i) & hitmarkerFaintBits))
{
- if (i > 1 && ((1u << BATTLE_PARTNER(i)) & hitmarkerFaintBits))
+ u32 skipPartnerCheck = FALSE;
+ if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS
+ && GetBattlerSide(i) == B_SIDE_OPPONENT
+ && TRAINER_BATTLE_PARAM.opponentB != TRAINER_NONE)
+ skipPartnerCheck = TRUE;
+
+ // In a 1v2 Double Battle if trainer A didn't have any more mons left
+ // the battler for trainer B wasn't being registered to be send out.
+ // Likely reason is because hitmarkerFaintBits was not set for battler 1 due to it being missing for a turn or cleared somewhere
+ if (!skipPartnerCheck && i > 1 && ((1u << BATTLE_PARTNER(i)) & hitmarkerFaintBits))
continue;
battler = i;
diff --git a/src/battle_util.c b/src/battle_util.c
index 7efebde7c2..c197ed2b14 100644
--- a/src/battle_util.c
+++ b/src/battle_util.c
@@ -545,6 +545,16 @@ void HandleAction_UseMove(void)
void HandleAction_Switch(void)
{
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
+
+ // if switching to a mon that is already on field, cancel switch
+ if (!(gAbsentBattlerFlags & (1u << BATTLE_PARTNER(gBattlerAttacker)))
+ && IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker))
+ && gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)] == gBattleStruct->monToSwitchIntoId[gBattlerAttacker])
+ {
+ gCurrentActionFuncId = B_ACTION_FINISHED;
+ return;
+ }
+
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
gActionSelectionCursor[gBattlerAttacker] = 0;
@@ -851,7 +861,7 @@ void HandleAction_NothingIsFainted(void)
void HandleAction_ActionFinished(void)
{
- u32 i, j, moveType;
+ u32 i, j;
bool32 afterYouActive = gSpecialStatuses[gBattlerByTurnOrder[gCurrentTurnActionNumber + 1]].afterYou;
gBattleStruct->monToSwitchIntoId[gBattlerByTurnOrder[gCurrentTurnActionNumber]] = gSelectedMonPartyId = PARTY_SIZE;
gCurrentTurnActionNumber++;
@@ -862,16 +872,6 @@ void HandleAction_ActionFinished(void)
| HITMARKER_OBEYS | HITMARKER_SYNCHRONIZE_EFFECT
| HITMARKER_CHARGING | HITMARKER_IGNORE_DISGUISE);
- // check if Stellar type boost should be used up
- moveType = GetBattleMoveType(gCurrentMove);
-
- if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA
- && GetBattlerTeraType(gBattlerAttacker) == TYPE_STELLAR
- && GetMoveCategory(gCurrentMove) != DAMAGE_CATEGORY_STATUS
- && IsTypeStellarBoosted(gBattlerAttacker, moveType))
- {
- ExpendTypeStellarBoost(gBattlerAttacker, moveType);
- }
ClearDamageCalcResults();
gCurrentMove = 0;
gBattleScripting.animTurn = 0;
@@ -988,29 +988,29 @@ static void UNUSED MarkAllBattlersForControllerExec(void)
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
for (i = 0; i < gBattlersCount; i++)
- gBattleControllerExecFlags |= 1u << (i + 32 - MAX_BATTLERS_COUNT);
+ MarkBattleControllerMessageOutboundOverLink(i);
}
else
{
for (i = 0; i < gBattlersCount; i++)
- gBattleControllerExecFlags |= 1u << i;
+ MarkBattleControllerActiveOnLocal(i);
}
}
bool32 IsBattlerMarkedForControllerExec(u32 battler)
{
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
- return (gBattleControllerExecFlags & (1u << (battler + 28))) != 0;
+ return IsBattleControllerMessageSynchronizedOverLink(battler);
else
- return (gBattleControllerExecFlags & (1u << battler)) != 0;
+ return IsBattleControllerActiveOnLocal(battler);
}
void MarkBattlerForControllerExec(u32 battler)
{
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
- gBattleControllerExecFlags |= 1u << (battler + 32 - MAX_BATTLERS_COUNT);
+ MarkBattleControllerMessageOutboundOverLink(battler);
else
- gBattleControllerExecFlags |= 1u << battler;
+ MarkBattleControllerActiveOnLocal(battler);
}
void MarkBattlerReceivedLinkData(u32 battler)
@@ -1018,9 +1018,9 @@ void MarkBattlerReceivedLinkData(u32 battler)
s32 i;
for (i = 0; i < GetLinkPlayerCount(); i++)
- gBattleControllerExecFlags |= 1u << (battler + (i << 2));
+ MarkBattleControllerActiveForPlayer(battler, i);
- gBattleControllerExecFlags &= ~(1u << (28 + battler));
+ MarkBattleControllerMessageSynchronizedOverLink(battler);
}
const u8 *CheckSkyDropState(u32 battler, enum SkyDropState skyDropState)
@@ -4309,7 +4309,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_HUNGER_SWITCH:
- if (TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END))
+ if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
+ && GetActiveGimmick(battler) != GIMMICK_TERA
+ && TryBattleFormChange(battler, FORM_CHANGE_BATTLE_TURN_END))
{
gBattlerAttacker = battler;
BattleScriptPushCursorAndCallback(BattleScript_AttackerFormChangeEnd3NoPopup);
diff --git a/src/contest_painting.c b/src/contest_painting.c
index 6dadf2b275..cb78caac26 100644
--- a/src/contest_painting.c
+++ b/src/contest_painting.c
@@ -363,7 +363,7 @@ static void VBlankCB_ContestPainting(void)
static void InitContestMonPixels(u16 species, bool8 backPic)
{
const void *pal = GetMonSpritePalFromSpeciesAndPersonality(species, gContestPaintingWinner->isShiny, gContestPaintingWinner->personality);
- DecompressDataWithHeaderVram(pal, gContestPaintingMonPalette);
+ memcpy(gContestPaintingMonPalette, pal, PLTT_SIZE_4BPP);
if (!backPic)
{
HandleLoadSpecialPokePic(TRUE,
diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h
index 27fb0a55ea..bd757914e5 100644
--- a/src/data/battle_move_effects.h
+++ b/src/data/battle_move_effects.h
@@ -1978,6 +1978,12 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleTvScore = 0, // TODO: Assign points
},
+ [EFFECT_CHLOROBLAST] =
+ {
+ .battleScript = BattleScript_EffectHit,
+ .battleTvScore = 0, // TODO: Assign points
+ },
+
[EFFECT_EXTREME_EVOBOOST] =
{
.battleScript = BattleScript_EffectExtremeEvoboost,
diff --git a/src/data/moves_info.h b/src/data/moves_info.h
index 217ef20689..0a34c5672f 100644
--- a/src/data/moves_info.h
+++ b/src/data/moves_info.h
@@ -19320,7 +19320,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.description = COMPOUND_STRING(
"A user-hurting blast of\n"
"amassed chlorophyll."),
- .effect = EFFECT_MAX_HP_50_RECOIL,
+ .effect = EFFECT_CHLOROBLAST,
.power = B_UPDATED_MOVE_DATA >= GEN_9 ? 150 : 120,
.type = TYPE_GRASS,
.accuracy = 95,
diff --git a/src/field_screen_effect.c b/src/field_screen_effect.c
index af1297a0e2..44ef4dd286 100644
--- a/src/field_screen_effect.c
+++ b/src/field_screen_effect.c
@@ -528,6 +528,7 @@ void DoDiveWarp(void)
TryFadeOutOldMapMusic();
WarpFadeOutScreen();
PlayRainStoppingSoundEffect();
+ SetFollowerNPCData(FNPC_DATA_COME_OUT_DOOR, FNPC_DOOR_NONE);
gFieldCallback = FieldCB_DefaultWarpExit;
CreateTask(Task_WarpAndLoadMap, 10);
}
diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c
index 77661a7fbd..15cc80a438 100644
--- a/src/pokedex_plus_hgss.c
+++ b/src/pokedex_plus_hgss.c
@@ -6431,7 +6431,7 @@ u32 GetSpeciesNameWidthInChars(const u8 *speciesName)
bool32 IsSpeciesAlcremie(u32 targetSpecies)
{
- return targetSpecies >= SPECIES_ALCREMIE_STRAWBERRY_VANILLA_CREAM && targetSpecies <= SPECIES_ALCREMIE_RIBBON_RAINBOW_SWIRL;
+ return GET_BASE_SPECIES_ID(targetSpecies) == SPECIES_ALCREMIE;
}
bool32 IsItemSweet(u32 item)
@@ -6628,10 +6628,10 @@ static void PrintEvolutionTargetSpeciesAndMethod(u8 taskId, u16 species, u8 dept
case IF_PID_UPPER_MODULO_10_EQ:
case IF_PID_UPPER_MODULO_10_LT:
arg = evolutions[i].params[j].arg1;
- if ((enum EvolutionConditions)evolutions[i].params[j].condition == IF_PID_UPPER_MODULO_10_GT
+ if ((enum EvolutionConditions)evolutions[i].params[j].condition == IF_PID_UPPER_MODULO_10_GT
&& arg < 10 && arg >= 0)
arg = 9 - arg;
- else if ((enum EvolutionConditions)evolutions[i].params[j].condition == IF_PID_UPPER_MODULO_10_EQ
+ else if ((enum EvolutionConditions)evolutions[i].params[j].condition == IF_PID_UPPER_MODULO_10_EQ
&& arg < 10 && arg >= 0)
arg = 1;
ConvertIntToDecimalStringN(gStringVar2, arg * 10, STR_CONV_MODE_LEFT_ALIGN, 3);
@@ -6741,10 +6741,10 @@ static void PrintEvolutionTargetSpeciesAndMethod(u8 taskId, u16 species, u8 dept
case IF_PID_MODULO_100_EQ:
case IF_PID_MODULO_100_LT:
arg = evolutions[i].params[j].arg1;
- if ((enum EvolutionConditions)evolutions[i].params[j].condition == IF_PID_MODULO_100_GT
+ if ((enum EvolutionConditions)evolutions[i].params[j].condition == IF_PID_MODULO_100_GT
&& arg < 100 && arg >= 0)
arg = 99 - arg;
- else if ((enum EvolutionConditions)evolutions[i].params[j].condition == IF_PID_MODULO_100_EQ
+ else if ((enum EvolutionConditions)evolutions[i].params[j].condition == IF_PID_MODULO_100_EQ
&& arg < 100 && arg >= 0)
arg = 1;
ConvertIntToDecimalStringN(gStringVar2, arg, STR_CONV_MODE_LEFT_ALIGN, 3);
@@ -6776,7 +6776,7 @@ static void PrintEvolutionTargetSpeciesAndMethod(u8 taskId, u16 species, u8 dept
}
if (isAlcremie)
- fontId = FONT_NARROWER;
+ fontId = FONT_SMALL_NARROWER;
else
fontId = GetFontIdToFit(gStringVar4, FONT_SMALL, 0, maxScreenWidth);
diff --git a/test/battle/ability/hunger_switch.c b/test/battle/ability/hunger_switch.c
index 38c3d2527d..1892de4424 100644
--- a/test/battle/ability/hunger_switch.c
+++ b/test/battle/ability/hunger_switch.c
@@ -22,3 +22,52 @@ SINGLE_BATTLE_TEST("Hunger Switch switches Morpeko's forms at the end of the tur
EXPECT_EQ(player->species, SPECIES_MORPEKO_FULL_BELLY);
}
}
+
+SINGLE_BATTLE_TEST("Hunger Switch does not switch a mon transformed into Morpeko's form")
+{
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_TRANSFORM) == EFFECT_TRANSFORM);
+ PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_TRANSFORM); }
+ } SCENE {
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, opponent);
+ }
+}
+
+SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form when Terastallized")
+{
+ GIVEN {
+ PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { }
+ TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_TERA); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
+ }
+}
+
+SINGLE_BATTLE_TEST("Hunger Switch does not switch Morpeko's form after switching out while Terastallized")
+{
+ KNOWN_FAILING; // #7062
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_ROAR) == EFFECT_ROAR);
+ PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); TeraType(TYPE_NORMAL); }
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { }
+ TURN { MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_TERA); MOVE(opponent, MOVE_ROAR); }
+ TURN { SWITCH(player, 0); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
+ NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
+ } THEN {
+ EXPECT_EQ(player->species, SPECIES_MORPEKO_HANGRY);
+ }
+}
diff --git a/test/battle/ability/normalize.c b/test/battle/ability/normalize.c
index 175e5b5954..ad171663ab 100644
--- a/test/battle/ability/normalize.c
+++ b/test/battle/ability/normalize.c
@@ -80,40 +80,52 @@ SINGLE_BATTLE_TEST("Normalize still makes Freeze-Dry do super effective damage t
}
}
-SINGLE_BATTLE_TEST("Normalize boosts power of unaffected moves by 20% (Gen7+)", s16 damage)
+SINGLE_BATTLE_TEST("Normalize doesn't boost power of unaffected moves by 20% (< Gen7)", s16 damage)
{
- u32 ability, genConfig;
- PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_7; }
- PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_6; }
- PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_7; }
- PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_6; }
+ u32 ability;
+ PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
+ PARAMETRIZE { ability = ABILITY_NORMALIZE; }
GIVEN {
- WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, genConfig);
- PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_TACKLE); }
+ WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_6);
+ PLAYER(SPECIES_DELCATTY) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
- TURN { MOVE(player, MOVE_TACKLE); }
+ TURN { MOVE(player, MOVE_POUND); }
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
- if (genConfig >= GEN_7)
- EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.2), results[2].damage); // Ate
- else
- EXPECT_EQ(results[1].damage, results[3].damage); // No boost
+ EXPECT_EQ(results[0].damage, results[1].damage); // No boost
}
}
-SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s16 damage)
+SINGLE_BATTLE_TEST("Normalize boosts power of unaffected moves by 20% (Gen7+)", s16 damage)
{
- u32 ability, genConfig;
- PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_7; }
- PARAMETRIZE { ability = ABILITY_CUTE_CHARM; genConfig = GEN_6; }
- PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_7; }
- PARAMETRIZE { ability = ABILITY_NORMALIZE; genConfig = GEN_6; }
+ u32 ability;
+ PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
+ PARAMETRIZE { ability = ABILITY_NORMALIZE; }
GIVEN {
- WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, genConfig);
+ WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_7);
+ PLAYER(SPECIES_DELCATTY) { Ability(ability); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_POUND); }
+ } SCENE {
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, UQ_4_12(1.2), results[1].damage); // Ate
+ }
+}
+
+SINGLE_BATTLE_TEST("Normalize doesn't boost power of affected moves by 20% (< Gen7)", s16 damage)
+{
+ u32 ability;
+ PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
+ PARAMETRIZE { ability = ABILITY_NORMALIZE; }
+
+ GIVEN {
+ WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_6);
PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_WATER_GUN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
@@ -121,10 +133,26 @@ SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s1
} SCENE {
HP_BAR(opponent, captureDamage: &results[i].damage);
} FINALLY {
- if (genConfig >= GEN_7)
- EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.8), results[2].damage); // STAB + ate
- else
- EXPECT_MUL_EQ(results[1].damage, Q_4_12(1.5), results[3].damage); // STAB + no ate
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); // STAB + no ate
+ }
+}
+
+SINGLE_BATTLE_TEST("Normalize boosts power of affected moves by 20% (Gen7+)", s16 damage)
+{
+ u32 ability;
+ PARAMETRIZE { ability = ABILITY_CUTE_CHARM; }
+ PARAMETRIZE { ability = ABILITY_NORMALIZE; }
+
+ GIVEN {
+ WITH_CONFIG(GEN_CONFIG_ATE_MULTIPLIER, GEN_7);
+ PLAYER(SPECIES_SKITTY) { Ability(ability); Moves(MOVE_WATER_GUN); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_WATER_GUN); }
+ } SCENE {
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.8), results[1].damage); // STAB + ate
}
}
diff --git a/test/battle/ability/wandering_spirit.c b/test/battle/ability/wandering_spirit.c
index d32fb7c7b2..9d8d7fad36 100644
--- a/test/battle/ability/wandering_spirit.c
+++ b/test/battle/ability/wandering_spirit.c
@@ -2,3 +2,18 @@
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Wandering Spirit (Ability) test titles")
+
+SINGLE_BATTLE_TEST("Wandering Spirit copied ability should not trigger on fainted mon")
+{
+ GIVEN {
+ PLAYER(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
+ PLAYER(SPECIES_WOBBUFFET)
+ OPPONENT(SPECIES_YAMASK_GALAR) { HP(1); Ability(ABILITY_WANDERING_SPIRIT); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_POISON_FANG); SEND_OUT(opponent, 1); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_FANG, player);
+ NOT ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
+ }
+}
diff --git a/test/battle/move_effect/aura_wheel.c b/test/battle/move_effect/aura_wheel.c
index dfd31c878f..18602b8bc9 100644
--- a/test/battle/move_effect/aura_wheel.c
+++ b/test/battle/move_effect/aura_wheel.c
@@ -50,4 +50,41 @@ SINGLE_BATTLE_TEST("Aura Wheel changes type depending on Morpeko's form")
}
}
-TO_DO_BATTLE_TEST("Aura Wheel can be used by PokΓ©mon transformed into Morpeko");
+SINGLE_BATTLE_TEST("Aura Wheel can be used by PokΓ©mon transformed into Morpeko")
+{
+ KNOWN_FAILING; // Aura Wheel for some reason isn't used by the opponent?
+ GIVEN {
+ PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); }
+ OPPONENT(SPECIES_DITTO) { Ability(ABILITY_IMPOSTER); }
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_AURA_WHEEL); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, opponent);
+ }
+}
+
+SINGLE_BATTLE_TEST("Aura Wheel can be turned into a Normal-type move after Morpeko gains Normalize")
+{
+ bool32 hangryMode;
+ PARAMETRIZE { hangryMode = FALSE; }
+ PARAMETRIZE { hangryMode = TRUE; }
+ GIVEN {
+ ASSUME(GetMoveEffect(MOVE_ENTRAINMENT) == EFFECT_ENTRAINMENT);
+ ASSUME(gSpeciesInfo[SPECIES_DUSKULL].types[0] == TYPE_GHOST || gSpeciesInfo[SPECIES_DUSKULL].types[1] == TYPE_GHOST);
+ PLAYER(SPECIES_MORPEKO) { Ability(ABILITY_HUNGER_SWITCH); }
+ OPPONENT(SPECIES_DELCATTY) { Ability(ABILITY_NORMALIZE); }
+ OPPONENT(SPECIES_DUSKULL);
+ } WHEN {
+ if (hangryMode)
+ TURN { }
+ TURN { MOVE(opponent, MOVE_ENTRAINMENT); }
+ TURN { MOVE(player, MOVE_AURA_WHEEL); SWITCH(opponent, 1); }
+ } SCENE {
+ if (hangryMode)
+ ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FORM_CHANGE, player);
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_AURA_WHEEL, player);
+ HP_BAR(opponent);
+ }
+ }
+}
diff --git a/test/battle/move_effect/chloroblast.c b/test/battle/move_effect/chloroblast.c
new file mode 100644
index 0000000000..6452736478
--- /dev/null
+++ b/test/battle/move_effect/chloroblast.c
@@ -0,0 +1,153 @@
+#include "global.h"
+#include "test/battle.h"
+
+ASSUMPTIONS
+{
+ ASSUME(GetMoveEffect(MOVE_CHLOROBLAST) == EFFECT_CHLOROBLAST);
+}
+
+SINGLE_BATTLE_TEST("Chloroblast makes the user lose 1/2 of its Max HP")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_CHLOROBLAST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
+ HP_BAR(player, damage: 200);
+ NOT MESSAGE("Wobbuffet fainted!"); // Wobb had more than 1/2 of its HP, so it can't faint.
+ }
+}
+
+SINGLE_BATTLE_TEST("Chloroblast causes the user to faint when below 1/2 of its Max HP")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { HP(200); MaxHP(400); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_CHLOROBLAST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
+ HP_BAR(player, hp: 0);
+ MESSAGE("Wobbuffet fainted!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Chloroblast causes the user & the target to faint when below 1/2 of its Max HP")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET) { HP(200) ; MaxHP(400); }
+ OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_CHLOROBLAST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
+ HP_BAR(opponent, hp: 0);
+ MESSAGE("The opposing Wobbuffet fainted!");
+ HP_BAR(player, hp: 0);
+ MESSAGE("Wobbuffet fainted!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Chloroblast hp loss is prevented by Magic Guard")
+{
+ GIVEN {
+ PLAYER(SPECIES_CLEFAIRY) { Ability(ABILITY_MAGIC_GUARD); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, MOVE_CHLOROBLAST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
+ HP_BAR(opponent);
+ NOT HP_BAR(player);
+ }
+}
+
+SINGLE_BATTLE_TEST("Chloroblast does not cause recoil damage if the user has Rock Head")
+{
+ GIVEN {
+ PLAYER(SPECIES_AERODACTYL) { Ability(ABILITY_ROCK_HEAD); }
+ OPPONENT(SPECIES_WOBBUFFET) { HP(400); MaxHP(400); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_CHLOROBLAST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
+ HP_BAR(opponent);
+ NOT HP_BAR(player);
+ }
+}
+
+SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP even if the opposing mon protected")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_PROTECT); MOVE(player, MOVE_CHLOROBLAST); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_PROTECT, opponent);
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
+ HP_BAR(player);
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP even if it is absorbed by Sap Sipper")
+{
+ GIVEN {
+ ASSUME(GetMoveType(MOVE_CHLOROBLAST) == TYPE_GRASS);
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_GOGOAT) { Ability(ABILITY_SAP_SIPPER); }
+ } WHEN {
+ TURN { MOVE(player, MOVE_CHLOROBLAST); }
+ } SCENE {
+ ABILITY_POPUP(opponent, ABILITY_SAP_SIPPER);
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
+ HP_BAR(player);
+ }
+ }
+}
+
+SINGLE_BATTLE_TEST("Chloroblast does not cause the user to lose HP if there is no target")
+{
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(opponent, MOVE_MEMENTO); MOVE(player, MOVE_CHLOROBLAST); SEND_OUT(opponent, 1); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_MEMENTO, opponent);
+ NONE_OF {
+ ANIMATION(ANIM_TYPE_MOVE, MOVE_CHLOROBLAST, player);
+ HP_BAR(player);
+ }
+ MESSAGE("Wobbuffet used Chloroblast!");
+ MESSAGE("But it failed!");
+ MESSAGE("2 sent out Wobbuffet!");
+ }
+}
+
+SINGLE_BATTLE_TEST("Chloroblast is not affected by Reckless", s16 damage)
+{
+ u32 move;
+
+ PARAMETRIZE { move = MOVE_CHLOROBLAST; }
+ PARAMETRIZE { move = MOVE_FRENZY_PLANT; }
+
+ GIVEN {
+ PLAYER(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_WOBBUFFET);
+ } WHEN {
+ TURN { MOVE(player, move); }
+ } SCENE {
+ ANIMATION(ANIM_TYPE_MOVE, move, player);
+ HP_BAR(opponent, captureDamage: &results[i].damage);
+ } FINALLY {
+ EXPECT_EQ(results[0].damage, results[1].damage);
+ }
+}
diff --git a/test/battle/move_effect/pursuit.c b/test/battle/move_effect/pursuit.c
index 649b331eec..b18aa74b09 100644
--- a/test/battle/move_effect/pursuit.c
+++ b/test/battle/move_effect/pursuit.c
@@ -673,4 +673,24 @@ SINGLE_BATTLE_TEST("Pursuit user faints to Life Orb and target still switches ou
}
}
+DOUBLE_BATTLE_TEST("Pursuit user switches out due to Red Card and partner's switch is cancelled if switching to same PokΓ©mon")
+{
+ GIVEN {
+ ASSUME(GetItemHoldEffect(ITEM_RED_CARD) == HOLD_EFFECT_RED_CARD);
+ PLAYER(SPECIES_WOBBUFFET);
+ PLAYER(SPECIES_WYNAUT);
+ PLAYER(SPECIES_ARCEUS);
+ OPPONENT(SPECIES_WYNAUT) { Item(ITEM_RED_CARD); }
+ OPPONENT(SPECIES_WOBBUFFET);
+ OPPONENT(SPECIES_ARCEUS);
+ } WHEN {
+ TURN { SWITCH(opponentLeft, 2); SWITCH(playerRight, 2); MOVE(playerLeft, MOVE_PURSUIT, target: opponentLeft); }
+ } THEN {
+ // playerLeft switches to Arceus
+ EXPECT_EQ(playerLeft->species, SPECIES_ARCEUS);
+ // playerRight has their switch cancelled
+ EXPECT_EQ(playerRight->species, SPECIES_WYNAUT);
+ }
+}
+
TO_DO_BATTLE_TEST("Baton Pass doesn't cause Pursuit to increase its power or priority");