MOVE_REVIVAL_BLESSING Effect (#2883)

* implemented Revival Blessing
This commit is contained in:
AgustinGDLV 2023-04-11 17:14:25 -07:00 committed by GitHub
parent e4656f3ab8
commit 86242c1c70
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 309 additions and 21 deletions

View File

@ -2263,3 +2263,8 @@
.macro hitswitchtargetfailed
various 0, VARIOUS_HIT_SWITCH_TARGET_FAILED
.endm
.macro tryrevivalblessing, jumpInstr:req
various 0, VARIOUS_TRY_REVIVAL_BLESSING
.4byte \jumpInstr
.endm

View File

@ -423,6 +423,25 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectHitSetEntryHazard @ EFFECT_HIT_SET_ENTRY_HAZARD
.4byte BattleScript_EffectDireClaw @ EFFECT_DIRE_CLAW
.4byte BattleScript_EffectBarbBarrage @ EFFECT_BARB_BARRAGE
.4byte BattleScript_EffectRevivalBlessing @ EFFECT_REVIVAL_BLESSING
BattleScript_EffectRevivalBlessing::
attackcanceler
attackstring
ppreduce
attackanimation
waitanimation
tryrevivalblessing BattleScript_ButItFailed
printstring STRINGID_PKMNREVIVEDREADYTOFIGHT
waitmessage B_WAIT_TIME_LONG
jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_EffectRevivalBlessingSendOut
goto BattleScript_MoveEnd
BattleScript_EffectRevivalBlessingSendOut:
switchinanim BS_SCRIPTING, FALSE
waitstate
switchineffects BS_SCRIPTING
goto BattleScript_MoveEnd
BattleScript_StealthRockActivates::
setstealthrock BattleScript_MoveEnd

View File

@ -46,6 +46,7 @@ u8 GetCatchingBattler(void);
u32 GetHighestStatId(u32 battlerId);
bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType);
bool32 DoSwitchInAbilitiesItems(u32 battlerId);
u8 GetFirstFaintedPartyIndex(u8 battlerId);
extern void (* const gBattleScriptingCommandsTable[])(void);
extern const u8 gBattlePalaceNatureToMoveGroupLikelihood[NUM_NATURES][4];

View File

@ -404,7 +404,8 @@
#define EFFECT_HIT_SET_ENTRY_HAZARD 398
#define EFFECT_DIRE_CLAW 399
#define EFFECT_BARB_BARRAGE 400
#define EFFECT_REVIVAL_BLESSING 401
#define NUM_BATTLE_MOVE_EFFECTS 401
#define NUM_BATTLE_MOVE_EFFECTS 402
#endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H

View File

@ -259,6 +259,7 @@
#define VARIOUS_STORE_HEALING_WISH 167
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 168
#define VARIOUS_JUMP_IF_SHELL_TRAP 169
#define VARIOUS_TRY_REVIVAL_BLESSING 170
// Cmd_manipulatedamage
#define DMG_CHANGE_SIGN 0

View File

@ -647,8 +647,9 @@
#define STRINGID_STEALTHROCKDISAPPEAREDFROMTEAM 645
#define STRINGID_COULDNTFULLYPROTECT 646
#define STRINGID_STOCKPILEDEFFECTWOREOFF 647
#define STRINGID_PKMNREVIVEDREADYTOFIGHT 648
#define BATTLESTRINGS_COUNT 648
#define BATTLESTRINGS_COUNT 649
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View File

@ -48,6 +48,7 @@
#define PARTY_ACTION_MOVE_TUTOR 12
#define PARTY_ACTION_MINIGAME 13
#define PARTY_ACTION_REUSABLE_ITEM 14 // Unused. The only reusable items are handled separately
#define PARTY_ACTION_CHOOSE_FAINTED_MON 15
// IDs for DisplayPartyMenuStdMessage, to display the message at the bottom of the party menu
#define PARTY_MSG_CHOOSE_MON 0

View File

@ -2623,6 +2623,15 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
if (gBattleMons[battlerAtk].hp <= gBattleMons[battlerAtk].maxHP / 3)
score -= 10;
break;*/
case EFFECT_REVIVAL_BLESSING:
if (GetFirstFaintedPartyIndex(battlerAtk) == PARTY_SIZE)
score -= 10;
else if (CanAIFaintTarget(battlerAtk, battlerDef, 0))
score -= 10;
else if (CanTargetFaintAi(battlerDef, battlerAtk)
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)
score -= 10;
break;
case EFFECT_PLACEHOLDER:
return 0; // cannot even select
} // move effect checks
@ -4808,6 +4817,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
score++;
}
break;
case EFFECT_REVIVAL_BLESSING:
if (GetFirstFaintedPartyIndex(battlerAtk) != PARTY_SIZE)
score += 2;
break;
//case EFFECT_EXTREME_EVOBOOST: // TODO
//break;
//case EFFECT_CLANGOROUS_SOUL: // TODO

View File

@ -20,6 +20,7 @@
#include "main.h"
#include "m4a.h"
#include "palette.h"
#include "party_menu.h"
#include "pokeball.h"
#include "pokemon.h"
#include "random.h"
@ -33,6 +34,7 @@
#include "constants/battle_anim.h"
#include "constants/items.h"
#include "constants/moves.h"
#include "constants/party_menu.h"
#include "constants/songs.h"
#include "constants/trainers.h"
#include "trainer_hill.h"
@ -1674,7 +1676,13 @@ static void OpponentHandleChoosePokemon(void)
s32 chosenMonId;
s32 pokemonInBattle = 1;
if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
// Choosing Revival Blessing target
if ((gBattleResources->bufferA[gActiveBattler][1] & 0xF) == PARTY_ACTION_CHOOSE_FAINTED_MON)
{
chosenMonId = gSelectedMonPartyId = GetFirstFaintedPartyIndex(gActiveBattler);
}
// Switching out
else if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE)
{
chosenMonId = GetMostSuitableMonToSwitchInto();
@ -1709,17 +1717,17 @@ static void OpponentHandleChoosePokemon(void)
}
}
}
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = chosenMonId;
}
else
{
chosenMonId = *(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler);
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = chosenMonId;
}
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = chosenMonId;
BtlController_EmitChosenMonReturnValue(BUFFER_B, chosenMonId, NULL);
OpponentBufferExecCompleted();
}
static u8 CountAIAliveNonEggMonsExcept(u8 slotToIgnore)

View File

@ -2907,7 +2907,8 @@ static void PlayerHandleChoosePokemon(void)
for (i = 0; i < ARRAY_COUNT(gBattlePartyCurrentOrder); i++)
gBattlePartyCurrentOrder[i] = gBattleResources->bufferA[gActiveBattler][4 + i];
if (gBattleTypeFlags & BATTLE_TYPE_ARENA && (gBattleResources->bufferA[gActiveBattler][1] & 0xF) != PARTY_ACTION_CANT_SWITCH)
if (gBattleTypeFlags & BATTLE_TYPE_ARENA && (gBattleResources->bufferA[gActiveBattler][1] & 0xF) != PARTY_ACTION_CANT_SWITCH
&& (gBattleResources->bufferA[gActiveBattler][1] & 0xF) != PARTY_ACTION_CHOOSE_FAINTED_MON)
{
BtlController_EmitChosenMonReturnValue(BUFFER_B, gBattlerPartyIndexes[gActiveBattler] + 1, gBattlePartyCurrentOrder);
PlayerBufferExecCompleted();

View File

@ -16,6 +16,7 @@
#include "main.h"
#include "m4a.h"
#include "palette.h"
#include "party_menu.h"
#include "pokeball.h"
#include "pokemon.h"
#include "reshow_battle_screen.h"
@ -27,6 +28,7 @@
#include "window.h"
#include "constants/battle_anim.h"
#include "constants/songs.h"
#include "constants/party_menu.h"
#include "constants/trainers.h"
static void PlayerPartnerHandleGetMonData(void);
@ -1549,25 +1551,33 @@ static void PlayerPartnerHandleChooseItem(void)
static void PlayerPartnerHandleChoosePokemon(void)
{
s32 chosenMonId = GetMostSuitableMonToSwitchInto();
if (chosenMonId == PARTY_SIZE) // just switch to the next mon
s32 chosenMonId;
// Choosing Revival Blessing target
if ((gBattleResources->bufferA[gActiveBattler][1] & 0xF) == PARTY_ACTION_CHOOSE_FAINTED_MON)
{
u8 playerMonIdentity = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
u8 selfIdentity = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
for (chosenMonId = PARTY_SIZE / 2; chosenMonId < PARTY_SIZE; chosenMonId++)
chosenMonId = gSelectedMonPartyId = GetFirstFaintedPartyIndex(gActiveBattler);
}
// Switching out
else
{
chosenMonId = GetMostSuitableMonToSwitchInto();
if (chosenMonId == PARTY_SIZE) // just switch to the next mon
{
if (GetMonData(&gPlayerParty[chosenMonId], MON_DATA_HP) != 0
&& chosenMonId != gBattlerPartyIndexes[playerMonIdentity]
&& chosenMonId != gBattlerPartyIndexes[selfIdentity])
u8 playerMonIdentity = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
u8 selfIdentity = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
for (chosenMonId = PARTY_SIZE / 2; chosenMonId < PARTY_SIZE; chosenMonId++)
{
break;
if (GetMonData(&gPlayerParty[chosenMonId], MON_DATA_HP) != 0
&& chosenMonId != gBattlerPartyIndexes[playerMonIdentity]
&& chosenMonId != gBattlerPartyIndexes[selfIdentity])
{
break;
}
}
}
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = chosenMonId;
}
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = chosenMonId;
BtlController_EmitChosenMonReturnValue(BUFFER_B, chosenMonId, NULL);
PlayerPartnerBufferExecCompleted();
}

View File

@ -15,6 +15,7 @@
#include "main.h"
#include "m4a.h"
#include "palette.h"
#include "party_menu.h"
#include "pokeball.h"
#include "pokemon.h"
#include "recorded_battle.h"
@ -1442,6 +1443,7 @@ static void RecordedOpponentHandleChooseItem(void)
static void RecordedOpponentHandleChoosePokemon(void)
{
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = RecordedBattle_GetBattlerAction(RECORDED_PARTY_INDEX, gActiveBattler);
gSelectedMonPartyId = gBattleStruct->monToSwitchIntoId[gActiveBattler]; // Revival Blessing
BtlController_EmitChosenMonReturnValue(BUFFER_B, *(gBattleStruct->monToSwitchIntoId + gActiveBattler), NULL);
RecordedOpponentBufferExecCompleted();
}

View File

@ -12,6 +12,7 @@
#include "main.h"
#include "m4a.h"
#include "palette.h"
#include "party_menu.h"
#include "pokeball.h"
#include "pokemon.h"
#include "recorded_battle.h"
@ -1466,6 +1467,7 @@ static void RecordedPlayerHandleChooseItem(void)
static void RecordedPlayerHandleChoosePokemon(void)
{
*(gBattleStruct->monToSwitchIntoId + gActiveBattler) = RecordedBattle_GetBattlerAction(RECORDED_PARTY_INDEX, gActiveBattler);
gSelectedMonPartyId = gBattleStruct->monToSwitchIntoId[gActiveBattler]; // Revival Blessing
BtlController_EmitChosenMonReturnValue(BUFFER_B, *(gBattleStruct->monToSwitchIntoId + gActiveBattler), NULL);
RecordedPlayerBufferExecCompleted();
}

View File

@ -3196,6 +3196,7 @@ static void BattleStartClearSetData(void)
}
gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
gSelectedMonPartyId = PARTY_SIZE; // Revival Blessing
}
void SwitchInClearSetData(void)
@ -3308,6 +3309,9 @@ void SwitchInClearSetData(void)
gBattleStruct->overwrittenAbilities[gActiveBattler] = ABILITY_NONE;
// Clear selected party ID so Revival Blessing doesn't get confused.
gSelectedMonPartyId = PARTY_SIZE;
Ai_UpdateSwitchInData(gActiveBattler);
}

View File

@ -782,9 +782,11 @@ static const u8 sText_PrepareShellTrap[] = _("{B_ATK_NAME_WITH_PREFIX} set a she
static const u8 sText_ShellTrapDidntWork[] = _("{B_ATK_NAME_WITH_PREFIX}'s shell trap didn't work!");
static const u8 sText_CouldntFullyProtect[] = _("{B_DEF_NAME_WITH_PREFIX} couldn't fully protect\nitself and got hurt!");
static const u8 sText_StockpiledEffectWoreOff[] = _("{B_ATK_NAME_WITH_PREFIX}'s stockpiled\neffect wore off!");
static const u8 sText_PkmnRevivedReadyToFight[] = _("{B_BUFF1} was revived and\nis ready to fight again!");
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
[STRINGID_PKMNREVIVEDREADYTOFIGHT - BATTLESTRINGS_TABLE_START] = sText_PkmnRevivedReadyToFight,
[STRINGID_STOCKPILEDEFFECTWOREOFF - BATTLESTRINGS_TABLE_START] = sText_StockpiledEffectWoreOff,
[STRINGID_COULDNTFULLYPROTECT - BATTLESTRINGS_TABLE_START] = sText_CouldntFullyProtect,
[STRINGID_ATTACKERGAINEDSTRENGTHFROMTHEFALLEN - BATTLESTRINGS_TABLE_START] = sText_AttackerGainedStrengthFromTheFallen,

View File

@ -11225,6 +11225,50 @@ static void Cmd_various(void)
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_TRY_REVIVAL_BLESSING:
{
VARIOUS_ARGS(const u8 *failInstr);
u32 side = GetBattlerSide(gBattlerAttacker);
u8 index = GetFirstFaintedPartyIndex(gBattlerAttacker);
// Move fails if there are no battlers to revive.
if (index == PARTY_SIZE)
{
gBattlescriptCurrInstr = cmd->failInstr;
return;
}
// Battler selected! Revive and go to next instruction.
if (gSelectedMonPartyId != PARTY_SIZE)
{
struct Pokemon *party = (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty;
u16 hp = GetMonData(&party[gSelectedMonPartyId], MON_DATA_MAX_HP) / 2;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HP_BATTLE, gBitTable[gSelectedMonPartyId], sizeof(hp), &hp);
MarkBattlerForControllerExec(gBattlerAttacker);
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gSelectedMonPartyId], MON_DATA_SPECIES));
// If an on-field battler is revived, it needs to be sent out again.
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)] == gSelectedMonPartyId)
{
gBattleScripting.battler = BATTLE_PARTNER(gBattlerAttacker);
gBattleCommunication[MULTIUSE_STATE] = TRUE;
}
gSelectedMonPartyId = PARTY_SIZE;
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
// Open party menu, wait to go to next instruction.
else
{
BtlController_EmitChoosePokemon(BUFFER_A, PARTY_ACTION_CHOOSE_FAINTED_MON, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[gActiveBattler]);
MarkBattlerForControllerExec(gBattlerAttacker);
}
return;
}
} // End of switch (cmd->id)
gBattlescriptCurrInstr = cmd->nextInstr;
@ -16346,3 +16390,40 @@ static void TryUpdateRoundTurnOrder(void)
}
}
u8 GetFirstFaintedPartyIndex(u8 battlerId)
{
u32 i;
u32 start = 0;
u32 end = PARTY_SIZE;
struct Pokemon *party = (GetBattlerSide(battlerId) == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty;
// Check whether partner is separate trainer.
if ((GetBattlerSide(battlerId) == B_SIDE_PLAYER && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|| (GetBattlerSide(battlerId) == B_SIDE_OPPONENT && gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS))
{
if (GetBattlerPosition(battlerId) == B_POSITION_OPPONENT_LEFT
|| GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT)
{
end = PARTY_SIZE / 2;
}
else
{
start = PARTY_SIZE / 2;
}
}
// Loop through to find fainted battler.
for (i = start; i < end; ++i)
{
u32 species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG);
if (species != SPECIES_NONE
&& species != SPECIES_EGG
&& GetMonData(&party[i], MON_DATA_HP) == 0)
{
return i;
}
}
// Returns PARTY_SIZE if none found.
return PARTY_SIZE;
}

View File

@ -13048,7 +13048,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_REVIVAL_BLESSING] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_REVIVAL_BLESSING
.effect = EFFECT_REVIVAL_BLESSING,
.power = 0,
.type = TYPE_NORMAL,
.accuracy = 0,

View File

@ -1354,6 +1354,24 @@ static void HandleChooseMonSelection(u8 taskId, s8 *slotPtr)
TryEnterMonForMinigame(taskId, (u8)*slotPtr);
}
break;
case PARTY_ACTION_CHOOSE_FAINTED_MON:
{
u8 partyId = GetPartyIdFromBattleSlot((u8)*slotPtr);
if (GetMonData(&gPlayerParty[*slotPtr], MON_DATA_HP) > 0
|| GetMonData(&gPlayerParty[*slotPtr], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG
|| ((gBattleTypeFlags & BATTLE_TYPE_MULTI) && partyId >= (PARTY_SIZE / 2)))
{
// Can't select if egg, alive, or doesn't belong to you
PlaySE(SE_FAILURE);
}
else
{
PlaySE(SE_SELECT);
gSelectedMonPartyId = partyId;
Task_ClosePartyMenu(taskId);
}
break;
}
default:
case PARTY_ACTION_ABILITY_PREVENTS:
case PARTY_ACTION_SWITCHING:
@ -1379,6 +1397,7 @@ static void HandleChooseMonCancel(u8 taskId, s8 *slotPtr)
switch (gPartyMenu.action)
{
case PARTY_ACTION_SEND_OUT:
case PARTY_ACTION_CHOOSE_FAINTED_MON:
PlaySE(SE_FAILURE);
break;
case PARTY_ACTION_SWITCH:

View File

@ -0,0 +1,117 @@
#include "global.h"
#include "test_battle.h"
// Note: Since these tests are recorded battle, they don't test the right battle controller
// behaviors. These have been tested in-game, in double, in multi, and in link battles. AI will always
// revive their first fainted party member in order.
#define MOVE_MESSAGE(name) \
do { \
if (B_EXPANDED_MOVE_NAMES == FALSE) \
MESSAGE(name" used RevivlBlesng!"); \
else \
MESSAGE(name" used Revival Blessing!"); \
} while (0); \
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_REVIVAL_BLESSING].effect == EFFECT_REVIVAL_BLESSING);
}
SINGLE_BATTLE_TEST("Revival Blessing revives a chosen fainted party member for the player")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET) { HP(0); }
PLAYER(SPECIES_WYNAUT) { HP(0); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_REVIVAL_BLESSING); SEND_OUT(player, 2); }
} SCENE {
MOVE_MESSAGE("Wobbuffet")
MESSAGE("Wynaut was revived and is ready to fight again!");
}
}
SINGLE_BATTLE_TEST("Revival Blessing revives a fainted party member for an opponent")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_RAICHU);
OPPONENT(SPECIES_PICHU) { HP(0); }
OPPONENT(SPECIES_PIKACHU) { HP(0); }
} WHEN {
TURN { MOVE(opponent, MOVE_REVIVAL_BLESSING); SEND_OUT(opponent, 1); }
} SCENE {
MOVE_MESSAGE("Foe Raichu")
MESSAGE("Pichu was revived and is ready to fight again!");
}
}
SINGLE_BATTLE_TEST("Revival Blessing fails if no party members are fainted")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_REVIVAL_BLESSING); }
} SCENE {
MOVE_MESSAGE("Wobbuffet")
MESSAGE("But it failed!");
}
}
// Note: There isn't a good way to test multi battles at the moment, but
// this PASSES in game!
TO_DO_BATTLE_TEST("Revival Blessing cannot revive a partner's party member");
// DOUBLE_BATTLE_TEST("Revival Blessing cannot revive a partner's party member")
// {
// struct BattlePokemon *user;
// gBattleTypeFlags |= BATTLE_TYPE_TWO_OPPONENTS;
// PARAMETRIZE { user = opponentLeft; }
// PARAMETRIZE { user = opponentRight; }
// GIVEN {
// ASSUME((gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) != FALSE);
// PLAYER(SPECIES_WOBBUFFET);
// PLAYER(SPECIES_WOBBUFFET);
// OPPONENT(SPECIES_WOBBUFFET);
// OPPONENT(SPECIES_WOBBUFFET);
// OPPONENT(SPECIES_WOBBUFFET);
// OPPONENT(SPECIES_WYNAUT);
// OPPONENT(SPECIES_WYNAUT) { HP(0); }
// OPPONENT(SPECIES_WYNAUT);
// } WHEN {
// TURN { MOVE(user, MOVE_REVIVAL_BLESSING); }
// } SCENE {
// if (user == opponentLeft) {
// MOVE_MESSAGE(Foe Wobbuffet)
// MESSAGE("But it failed!");
// } else {
// MOVE_MESSAGE(Foe Wynaut)
// MESSAGE("Wynaut was revived and is ready to fight again!");
// }
// }
// }
// Note: The test runner gets upset about "sending out" a battler on the field,
// but this PASSES in game!
TO_DO_BATTLE_TEST("Revived battlers still lose their turn");
// DOUBLE_BATTLE_TEST("Revived battlers still lose their turn")
// {
// GIVEN {
// PLAYER(SPECIES_WOBBUFFET);
// PLAYER(SPECIES_WYNAUT);
// OPPONENT(SPECIES_WOBBUFFET);
// OPPONENT(SPECIES_WYNAUT) { HP(1); }
// } WHEN {
// TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentRight);
// MOVE(opponentLeft, MOVE_REVIVAL_BLESSING);
// SEND_OUT(opponentLeft, 1); }
// } SCENE {
// MESSAGE("Wobbuffet used Tackle!");
// MESSAGE("Foe Wynaut fainted!");
// MOVE_MESSAGE("Foe Wobbuffet")
// MESSAGE("Wynaut was revived and is ready to fight again!");
// NOT { MESSAGE("Wynaut used Celebrate!"); }
// }
// }