diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 973a1cec1..65525e82a 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1660,6 +1660,19 @@ various \battler, VARIOUS_TRACE_ABILITY .endm + .macro updatenick battler:req + various \battler, VARIOUS_UPDATE_NICK + .endm + + .macro tryillusionoff battler:req + various \battler, VARIOUS_TRY_ILLUSION_OFF + .endm + + .macro spriteignore0hp val:req + various BS_ATTACKER, VARIOUS_SET_SPRITEIGNORE0HP + .byte \val + .endm + @ helpful macros .macro setstatchanger stat:req, stages:req, down:req setbyte sSTATCHANGER \stat | \stages << 3 | \down << 7 diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 619629f09..2ed9fb918 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -682,6 +682,7 @@ gBattleAnims_General:: .4byte General_TerrainGrassy .4byte General_TerrainElectric .4byte General_TerrainPsychic + .4byte General_IllusionOff .align 2 gBattleAnims_Special:: @@ -14755,6 +14756,13 @@ General_WishHeal: waitforvisualfinish createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, 1, 3, 10, 0, RGB_BLACK end + +General_IllusionOff: + monbg ANIM_TARGET + createvisualtask sub_815B7D0, 2, 0, 1 + waitforvisualfinish + clearmonbg ANIM_TARGET + end General_MegaEvolution: loadspritegfx ANIM_TAG_MEGA_STONE diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 0f7bab883..6c5952b2b 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4675,20 +4675,22 @@ BattleScript_EffectCamouflage:: goto BattleScript_MoveEnd BattleScript_FaintAttacker:: + tryillusionoff BS_ATTACKER playfaintcry BS_ATTACKER pause 0x40 dofaintanimation BS_ATTACKER - cleareffectsonfaint BS_ATTACKER printstring STRINGID_ATTACKERFAINTED + cleareffectsonfaint BS_ATTACKER trytrainerslidefirstdownmsg BS_ATTACKER return BattleScript_FaintTarget:: + tryillusionoff BS_TARGET playfaintcry BS_TARGET pause 0x40 dofaintanimation BS_TARGET - cleareffectsonfaint BS_TARGET printstring STRINGID_TARGETFAINTED + cleareffectsonfaint BS_TARGET tryactivatemoxie BS_ATTACKER tryactivatefellstinger BS_ATTACKER trytrainerslidefirstdownmsg BS_TARGET @@ -5850,6 +5852,17 @@ BattleScript_MegaEvolution:: waitmessage 0x40 end2 +BattleScript_IllusionOff:: + spriteignore0hp TRUE + playanimation BS_TARGET, B_ANIM_ILLUSION_OFF, NULL + waitanimation + updatenick BS_TARGET + waitstate + spriteignore0hp FALSE + printstring STRINGID_ILLUSIONWOREOFF + waitmessage 0x40 + return + BattleScript_MoveUsedIsAsleep:: printstring STRINGID_PKMNFASTASLEEP waitmessage 0x40 diff --git a/include/battle.h b/include/battle.h index ce6495e1f..f5cb1317a 100644 --- a/include/battle.h +++ b/include/battle.h @@ -426,6 +426,14 @@ struct MegaEvolutionData u8 indicatorSpriteIds[MAX_BATTLERS_COUNT]; }; +struct Illusion +{ + u8 on:1; + u8 broken:1; + u8 partyId:3; + struct Pokemon *mon; +}; + struct BattleStruct { u8 turnEffectsTracker; @@ -530,6 +538,8 @@ struct BattleStruct u8 lastMoveTarget[MAX_BATTLERS_COUNT]; // The last target on which each mon used a move, for the sake of Instruct u8 debugHoldEffects[MAX_BATTLERS_COUNT]; // These override actual items' hold effects. u8 tracedAbility[MAX_BATTLERS_COUNT]; + bool8 spriteIgnore0Hp; + struct Illusion illusion[MAX_BATTLERS_COUNT]; }; #define GET_MOVE_TYPE(move, typeArg) \ diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 3e33c4ead..4288bb141 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -323,5 +323,6 @@ extern const u8 BattleScript_ToxicOrb[]; extern const u8 BattleScript_FlameOrb[]; extern const u8 BattleScript_MoveEffectIncinerate[]; extern const u8 BattleScript_MoveEffectBugBite[]; +extern const u8 BattleScript_IllusionOff[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/battle_util.h b/include/battle_util.h index 383229261..8b3fa9de3 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -103,5 +103,8 @@ bool32 CanMegaEvolve(u8 battlerId); void UndoMegaEvolution(u8 monId); bool32 DoBattlersShareType(u32 battler1, u32 battler2); bool32 CanBattlerGetOrLoseItem(u8 battlerId, u16 itemId); +struct Pokemon *GetIllusionMonPtr(u32 battlerId); +void ClearIllusionMon(u32 battlerId); +bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId); #endif // GUARD_BATTLE_UTIL_H diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index b7496f5a6..c47168c5f 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -406,6 +406,7 @@ #define B_ANIM_TERRAIN_GRASSY 0x19 #define B_ANIM_TERRAIN_ELECTRIC 0x1A #define B_ANIM_TERRAIN_PSYCHIC 0x1B +#define B_ANIM_ILLUSION_OFF 0x1C // special animations table #define B_ANIM_LVL_UP 0x0 diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index a0c581174..89faba55a 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -142,6 +142,9 @@ #define VARIOUS_TRY_INSTRUCT 79 #define VARIOUS_JUMP_IF_NOT_BERRY 80 #define VARIOUS_TRACE_ABILITY 81 +#define VARIOUS_UPDATE_NICK 82 +#define VARIOUS_TRY_ILLUSION_OFF 83 +#define VARIOUS_SET_SPRITEIGNORE0HP 84 // atk80, dmg manipulation #define ATK80_DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index d42237973..af497e73b 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -539,7 +539,8 @@ #define STRINGID_AIRBALLOONPOP 535 #define STRINGID_INCINERATEBURN 536 #define STRINGID_BUGBITE 537 +#define STRINGID_ILLUSIONWOREOFF 538 -#define BATTLESTRINGS_COUNT 530 +#define BATTLESTRINGS_COUNT 539 #endif // GUARD_CONSTANTS_BATTLE_STRING_IDS_H diff --git a/src/battle_ai_script_commands.c b/src/battle_ai_script_commands.c index bc624a554..c33cfaaf2 100644 --- a/src/battle_ai_script_commands.c +++ b/src/battle_ai_script_commands.c @@ -750,13 +750,15 @@ static void SetBattlerData(u8 battlerId) { if (!IsBattlerAIControlled(battlerId)) { + struct Pokemon *illusionMon; u32 i; // Use the known battler's ability. if (BATTLE_HISTORY->abilities[battlerId] != ABILITY_NONE) gBattleMons[battlerId].ability = BATTLE_HISTORY->abilities[battlerId]; // Check if mon can only have one ability. - else if (gBaseStats[gBattleMons[battlerId].species].abilities[1] == ABILITY_NONE) + else if (gBaseStats[gBattleMons[battlerId].species].abilities[1] == ABILITY_NONE + || gBaseStats[gBattleMons[battlerId].species].abilities[1] == gBaseStats[gBattleMons[battlerId].species].abilities[0]) gBattleMons[battlerId].ability = gBaseStats[gBattleMons[battlerId].species].abilities[0]; // The ability is unknown. else @@ -770,6 +772,10 @@ static void SetBattlerData(u8 battlerId) if (BATTLE_HISTORY->usedMoves[battlerId].moves[i] == 0) gBattleMons[battlerId].moves[i] = 0; } + + // Simulate Illusion + if ((illusionMon = GetIllusionMonPtr(battlerId)) != NULL) + gBattleMons[battlerId].species = GetMonData(illusionMon, MON_DATA_SPECIES2); } } diff --git a/src/battle_anim_mons.c b/src/battle_anim_mons.c index a667daecf..ea5316946 100644 --- a/src/battle_anim_mons.c +++ b/src/battle_anim_mons.c @@ -119,6 +119,7 @@ u8 GetBattlerSpriteCoord(u8 battlerId, u8 coordType) { u8 retVal; u16 species; + struct Pokemon *mon, *illusionMon; struct BattleSpriteInfo *spriteInfo; if (IsContest()) @@ -149,21 +150,18 @@ u8 GetBattlerSpriteCoord(u8 battlerId, u8 coordType) else { if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) - { - spriteInfo = gBattleSpritesDataPtr->battlerData; - if (!spriteInfo[battlerId].transformSpecies) - species = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES); - else - species = spriteInfo[battlerId].transformSpecies; - } + mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; else - { - spriteInfo = gBattleSpritesDataPtr->battlerData; - if (!spriteInfo[battlerId].transformSpecies) - species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_SPECIES); - else - species = spriteInfo[battlerId].transformSpecies; - } + mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; + + illusionMon = GetIllusionMonPtr(battlerId); + if (illusionMon != NULL) + mon = illusionMon; + spriteInfo = gBattleSpritesDataPtr->battlerData; + if (!spriteInfo[battlerId].transformSpecies) + species = GetMonData(mon, MON_DATA_SPECIES); + else + species = spriteInfo[battlerId].transformSpecies; } if (coordType == BATTLER_COORD_Y_PIC_OFFSET) retVal = GetBattlerSpriteFinal_Y(battlerId, species, TRUE); @@ -831,21 +829,23 @@ bool8 IsBattlerSpritePresent(u8 battlerId) else { if (gBattlerPositions[battlerId] == 0xff) - { return FALSE; - } - else if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) + + if (!gBattleStruct->spriteIgnore0Hp) { - if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_HP) != 0) - return TRUE; - } - else - { - if (GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_HP) != 0) - return TRUE; + if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT) + { + if (GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_HP) == 0) + return FALSE; + } + else + { + if (GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_HP) == 0) + return FALSE; + } } + return TRUE; } - return FALSE; } bool8 IsDoubleBattle(void) diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index e690c3ced..887fb47a4 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -410,6 +410,9 @@ bool8 TryHandleLaunchBattleTableAnimation(u8 activeBattler, u8 atkBattler, u8 de return TRUE; } + if (tableId == B_ANIM_ILLUSION_OFF) + ClearIllusionMon(activeBattler); + gBattleAnimAttacker = atkBattler; gBattleAnimTarget = defBattler; gBattleSpritesDataPtr->animationData->animArg = argument; @@ -510,6 +513,9 @@ static void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battlerId, bool32 op { u32 monsPersonality, currentPersonality, otId, species, paletteOffset, position; const void *lzPaletteData; + struct Pokemon *illusionMon = GetIllusionMonPtr(battlerId); + if (illusionMon != NULL) + mon = illusionMon; monsPersonality = GetMonData(mon, MON_DATA_PERSONALITY); if (gBattleSpritesDataPtr->battlerData[battlerId].transformSpecies == SPECIES_NONE) @@ -1152,6 +1158,11 @@ void ClearTemporarySpeciesSpriteData(u8 battlerId, bool8 dontClearSubstitute) gBattleMonForms[battlerId] = 0; if (!dontClearSubstitute) ClearBehindSubstituteBit(battlerId); + + if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) + SetIllusionMon(&gPlayerParty[gBattlerPartyIndexes[battlerId]], battlerId); + else + SetIllusionMon(&gEnemyParty[gBattlerPartyIndexes[battlerId]], battlerId); } void AllocateMonSpritesGfx(void) diff --git a/src/battle_interface.c b/src/battle_interface.c index 5f48a908b..31bd23471 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -1868,10 +1868,12 @@ static void UpdateNickInHealthbox(u8 healthboxSpriteId, struct Pokemon *mon) u8 nickname[POKEMON_NAME_LENGTH + 1]; void *ptr; const u8 *genderTxt; - u32 windowId, spriteTileNum; + u32 windowId, spriteTileNum, species; u8 *windowTileData; - u16 species; u8 gender; + struct Pokemon *illusionMon = GetIllusionMonPtr(gSprites[healthboxSpriteId].hMain_Battler); + if (illusionMon != NULL) + mon = illusionMon; StringCopy(gDisplayedStringBattle, gText_HighlightDarkGrey); GetMonData(mon, MON_DATA_NICKNAME, nickname); diff --git a/src/battle_message.c b/src/battle_message.c index 63abfd100..f85e90afe 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -664,9 +664,11 @@ static const u8 sText_AirBalloonFloat[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} flo static const u8 sText_AirBalloonPop[] = _("{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ITEM} popped!"); static const u8 sText_IncinerateBurn[] = _("{B_EFF_NAME_WITH_PREFIX}'s {B_LAST_ITEM}\nwas burnt up!"); static const u8 sText_BugBite[] = _("{B_ATK_NAME_WITH_PREFIX} stole and ate\n{B_EFF_NAME_WITH_PREFIX}'s {B_LAST_ITEM}!"); +static const u8 sText_IllusionWoreOff[] = _("{B_DEF_NAME_WITH_PREFIX}'s Illusion wore off!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_ILLUSIONWOREOFF - 12] = sText_IllusionWoreOff, [STRINGID_BUGBITE - 12] = sText_BugBite, [STRINGID_INCINERATEBURN - 12] = sText_IncinerateBurn, [STRINGID_AIRBALLOONPOP - 12] = sText_AirBalloonPop, @@ -2307,10 +2309,10 @@ void BufferStringBattle(u16 stringID) gLastUsedItem = gBattleMsgDataPtr->lastItem; gLastUsedAbility = gBattleMsgDataPtr->lastAbility; gBattleScripting.battler = gBattleMsgDataPtr->scrActive; - *(&gBattleStruct->field_52) = gBattleMsgDataPtr->unk1605E; - *(&gBattleStruct->hpScale) = gBattleMsgDataPtr->hpScale; + gBattleStruct->field_52 = gBattleMsgDataPtr->unk1605E; + gBattleStruct->hpScale = gBattleMsgDataPtr->hpScale; gPotentialItemEffectBattler = gBattleMsgDataPtr->itemEffectBattler; - *(&gBattleStruct->stringMoveType) = gBattleMsgDataPtr->moveType; + gBattleStruct->stringMoveType = gBattleMsgDataPtr->moveType; for (i = 0; i < MAX_BATTLERS_COUNT; i++) { @@ -2630,7 +2632,23 @@ static const u8* TryGetStatusString(u8 *src) return NULL; } -#define HANDLE_NICKNAME_STRING_CASE(battlerId, monIndex) \ +static void GetBattlerNick(u32 battlerId, u8 *dst) +{ + struct Pokemon *mon, *illusionMon; + + if (GET_BATTLER_SIDE(battlerId) == B_SIDE_PLAYER) + mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; + else + mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; + + illusionMon = GetIllusionMonPtr(battlerId); + if (illusionMon != NULL) + mon = illusionMon; + GetMonData(mon, MON_DATA_NICKNAME, dst); + StringGetEnd10(dst); +} + +#define HANDLE_NICKNAME_STRING_CASE(battlerId) \ if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) \ { \ if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) \ @@ -2643,13 +2661,8 @@ static const u8* TryGetStatusString(u8 *src) dstID++; \ toCpy++; \ } \ - GetMonData(&gEnemyParty[monIndex], MON_DATA_NICKNAME, text); \ } \ - else \ - { \ - GetMonData(&gPlayerParty[monIndex], MON_DATA_NICKNAME, text); \ - } \ - StringGetEnd10(text); \ + GetBattlerNick(battlerId, text); \ toCpy = text; static const u8 *BattleStringGetOpponentNameByTrainerId(u16 trainerId, u8 *text, u8 multiplayerId, u8 battlerId) @@ -2848,89 +2861,61 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) toCpy = gStringVar3; break; case B_TXT_PLAYER_MON1_NAME: // first player poke name - GetMonData(&gPlayerParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_PLAYER_LEFT)]], - MON_DATA_NICKNAME, text); - StringGetEnd10(text); + GetBattlerNick(GetBattlerAtPosition(B_POSITION_PLAYER_LEFT), text); toCpy = text; break; case B_TXT_OPPONENT_MON1_NAME: // first enemy poke name - GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)]], - MON_DATA_NICKNAME, text); - StringGetEnd10(text); + GetBattlerNick(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), text); toCpy = text; break; case B_TXT_PLAYER_MON2_NAME: // second player poke name - GetMonData(&gPlayerParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT)]], - MON_DATA_NICKNAME, text); - StringGetEnd10(text); + GetBattlerNick(GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT), text); toCpy = text; break; case B_TXT_OPPONENT_MON2_NAME: // second enemy poke name - GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT)]], - MON_DATA_NICKNAME, text); - StringGetEnd10(text); + GetBattlerNick(GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT), text); toCpy = text; break; case B_TXT_LINK_PLAYER_MON1_NAME: // link first player poke name - GetMonData(&gPlayerParty[gBattlerPartyIndexes[gLinkPlayers[multiplayerId].id]], - MON_DATA_NICKNAME, text); - StringGetEnd10(text); + GetBattlerNick(gLinkPlayers[multiplayerId].id, text); toCpy = text; break; case B_TXT_LINK_OPPONENT_MON1_NAME: // link first opponent poke name - GetMonData(&gEnemyParty[gBattlerPartyIndexes[gLinkPlayers[multiplayerId].id ^ 1]], - MON_DATA_NICKNAME, text); - StringGetEnd10(text); + GetBattlerNick(gLinkPlayers[multiplayerId].id ^ 1, text); toCpy = text; break; case B_TXT_LINK_PLAYER_MON2_NAME: // link second player poke name - GetMonData(&gPlayerParty[gBattlerPartyIndexes[gLinkPlayers[multiplayerId].id ^ 2]], - MON_DATA_NICKNAME, text); - StringGetEnd10(text); + GetBattlerNick(gLinkPlayers[multiplayerId].id ^ 2, text); toCpy = text; break; case B_TXT_LINK_OPPONENT_MON2_NAME: // link second opponent poke name - GetMonData(&gEnemyParty[gBattlerPartyIndexes[gLinkPlayers[multiplayerId].id ^ 3]], - MON_DATA_NICKNAME, text); - StringGetEnd10(text); + GetBattlerNick(gLinkPlayers[multiplayerId].id ^ 3, text); toCpy = text; break; - case B_TXT_ATK_NAME_WITH_PREFIX_MON1: // attacker name with prefix, only battlerId 0/1 - HANDLE_NICKNAME_STRING_CASE(gBattlerAttacker, - gBattlerPartyIndexes[GetBattlerAtPosition(GET_BATTLER_SIDE(gBattlerAttacker))]) + case B_TXT_ATK_NAME_WITH_PREFIX_MON1: // Unused, to change into sth else. break; case B_TXT_ATK_PARTNER_NAME: // attacker partner name - if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER) - GetMonData(&gPlayerParty[gBattlerPartyIndexes[GetBattlerAtPosition(GET_BATTLER_SIDE(gBattlerAttacker)) + 2]], MON_DATA_NICKNAME, text); - else - GetMonData(&gEnemyParty[gBattlerPartyIndexes[GetBattlerAtPosition(GET_BATTLER_SIDE(gBattlerAttacker)) + 2]], MON_DATA_NICKNAME, text); - - StringGetEnd10(text); + GetBattlerNick(BATTLE_PARTNER(gBattlerAttacker), text); toCpy = text; break; case B_TXT_ATK_NAME_WITH_PREFIX: // attacker name with prefix - HANDLE_NICKNAME_STRING_CASE(gBattlerAttacker, gBattlerPartyIndexes[gBattlerAttacker]) + HANDLE_NICKNAME_STRING_CASE(gBattlerAttacker) break; case B_TXT_DEF_NAME_WITH_PREFIX: // target name with prefix - HANDLE_NICKNAME_STRING_CASE(gBattlerTarget, gBattlerPartyIndexes[gBattlerTarget]) + HANDLE_NICKNAME_STRING_CASE(gBattlerTarget) break; case B_TXT_DEF_NAME: // target name - if (GetBattlerSide(gBattlerTarget) == B_SIDE_PLAYER) - GetMonData(&gPlayerParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_NICKNAME, text); - else - GetMonData(&gEnemyParty[gBattlerPartyIndexes[gBattlerTarget]], MON_DATA_NICKNAME, text); - - StringGetEnd10(text); + GetBattlerNick(gBattlerTarget, text); toCpy = text; break; case B_TXT_EFF_NAME_WITH_PREFIX: // effect battlerId name with prefix - HANDLE_NICKNAME_STRING_CASE(gEffectBattler, gBattlerPartyIndexes[gEffectBattler]) + HANDLE_NICKNAME_STRING_CASE(gEffectBattler) break; case B_TXT_ACTIVE_NAME_WITH_PREFIX: // active battlerId name with prefix - HANDLE_NICKNAME_STRING_CASE(gActiveBattler, gBattlerPartyIndexes[gActiveBattler]) + HANDLE_NICKNAME_STRING_CASE(gActiveBattler) break; case B_TXT_SCR_ACTIVE_NAME_WITH_PREFIX: // scripting active battlerId name with prefix - HANDLE_NICKNAME_STRING_CASE(gBattleScripting.battler, gBattlerPartyIndexes[gBattleScripting.battler]) + HANDLE_NICKNAME_STRING_CASE(gBattleScripting.battler) break; case B_TXT_CURRENT_MOVE: // current move name if (gBattleMsgDataPtr->currentMove >= MOVES_COUNT) @@ -3055,7 +3040,26 @@ u32 BattleStringExpandPlaceholders(const u8 *src, u8 *dst) } break; case B_TXT_26: // ? - HANDLE_NICKNAME_STRING_CASE(gBattleScripting.battler, *(&gBattleStruct->field_52)); + if (GetBattlerSide(gBattleScripting.battler) != B_SIDE_PLAYER) + { + if (gBattleTypeFlags & BATTLE_TYPE_TRAINER) + toCpy = sText_FoePkmnPrefix; + else + toCpy = sText_WildPkmnPrefix; + while (*toCpy != EOS) + { + dst[dstID] = *toCpy; + dstID++; + toCpy++; + } + GetMonData(&gEnemyParty[gBattleStruct->field_52], MON_DATA_NICKNAME, text); + } + else + { + GetMonData(&gPlayerParty[gBattleStruct->field_52], MON_DATA_NICKNAME, text); + } + StringGetEnd10(text); + toCpy = text; break; case B_TXT_PC_CREATOR_NAME: // lanette pc if (FlagGet(FLAG_SYS_PC_LANETTE)) @@ -3287,11 +3291,18 @@ static void ExpandBattleTextBuffPlaceholders(const u8 *src, u8 *dst) srcID += 3; break; case B_BUFF_MON_NICK: // poke nick without prefix - if (GetBattlerSide(src[srcID + 1]) == B_SIDE_PLAYER) - GetMonData(&gPlayerParty[src[srcID + 2]], MON_DATA_NICKNAME, dst); + if (src[srcID + 2] == gBattlerPartyIndexes[src[srcID + 1]]) + { + GetBattlerNick(src[srcID + 1], dst); + } else - GetMonData(&gEnemyParty[src[srcID + 2]], MON_DATA_NICKNAME, dst); - StringGetEnd10(dst); + { + if (GetBattlerSide(src[srcID + 1]) == B_SIDE_PLAYER) + GetMonData(&gPlayerParty[src[srcID + 2]], MON_DATA_NICKNAME, dst); + else + GetMonData(&gEnemyParty[src[srcID + 2]], MON_DATA_NICKNAME, dst); + StringGetEnd10(dst); + } srcID += 3; break; case B_BUFF_NEGATIVE_FLAVOR: // flavor table diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index af5982f44..e6a2303b1 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4068,6 +4068,7 @@ static void atk45_playanimation(void) if (gBattlescriptCurrInstr[2] == B_ANIM_STATS_CHANGE || gBattlescriptCurrInstr[2] == B_ANIM_SNATCH_MOVE || gBattlescriptCurrInstr[2] == B_ANIM_MEGA_EVOLUTION + || gBattlescriptCurrInstr[2] == B_ANIM_ILLUSION_OFF || gBattlescriptCurrInstr[2] == B_ANIM_SUBSTITUTE_FADE) { BtlController_EmitBattleAnimation(0, gBattlescriptCurrInstr[2], *argumentPtr); @@ -4112,6 +4113,7 @@ static void atk46_playanimation2(void) // animation Id is stored in the first po if (*animationIdPtr == B_ANIM_STATS_CHANGE || *animationIdPtr == B_ANIM_SNATCH_MOVE || *animationIdPtr == B_ANIM_MEGA_EVOLUTION + || *animationIdPtr == B_ANIM_ILLUSION_OFF || *animationIdPtr == B_ANIM_SUBSTITUTE_FADE) { BtlController_EmitBattleAnimation(0, *animationIdPtr, *argumentPtr); @@ -6570,10 +6572,9 @@ u32 IsFlowerVeilProtected(u32 battler) static void atk76_various(void) { struct Pokemon *mon; - u8 side; s32 i, j; u8 data[10]; - u32 bits; + u32 side, bits; if (gBattleControllerExecFlags) return; @@ -6600,6 +6601,26 @@ static void atk76_various(void) case VARIOUS_TRACE_ABILITY: gBattleMons[gActiveBattler].ability = gBattleStruct->tracedAbility[gActiveBattler]; break; + case VARIOUS_TRY_ILLUSION_OFF: + if (GetIllusionMonPtr(gActiveBattler) != NULL) + { + gBattlescriptCurrInstr += 3; + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_IllusionOff; + return; + } + break; + case VARIOUS_SET_SPRITEIGNORE0HP: + gBattleStruct->spriteIgnore0Hp = gBattlescriptCurrInstr[3]; + gBattlescriptCurrInstr += 4; + return; + case VARIOUS_UPDATE_NICK: + if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER) + mon = &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]]; + else + mon = &gEnemyParty[gBattlerPartyIndexes[gActiveBattler]]; + UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], mon, HEALTHBOX_NICK); + break; case VARIOUS_JUMP_IF_NOT_BERRY: if (ItemId_GetPocket(gBattleMons[gActiveBattler].item) == POCKET_BERRIES) gBattlescriptCurrInstr += 7; diff --git a/src/battle_util.c b/src/battle_util.c index 4e0164bd2..4e2f95fc0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -9,6 +9,7 @@ #include "constants/moves.h" #include "constants/hold_effects.h" #include "constants/battle_anim.h" +#include "party_menu.h" #include "pokemon.h" #include "constants/species.h" #include "item.h" @@ -3423,6 +3424,14 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u8 ability, u8 special, u16 moveA effect++; } break; + case ABILITY_ILLUSION: + if (gBattleStruct->illusion[battler].on && !gBattleStruct->illusion[battler].broken && IsBattlerAlive(battler) && TARGET_TURN_DAMAGED) + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_IllusionOff; + effect++; + } + break; } break; case ABILITYEFFECT_IMMUNITY: // 5 @@ -6241,3 +6250,56 @@ bool32 CanBattlerGetOrLoseItem(u8 battlerId, u16 itemId) else return TRUE; } + +struct Pokemon *GetIllusionMonPtr(u32 battlerId) +{ + if (!gBattleStruct->illusion[battlerId].on || gBattleStruct->illusion[battlerId].broken) + return NULL; + + return gBattleStruct->illusion[battlerId].mon; +} + +void ClearIllusionMon(u32 battlerId) +{ + gBattleStruct->illusion[battlerId].on = 0; + gBattleStruct->illusion[battlerId].mon = NULL; + gBattleStruct->illusion[battlerId].broken = 1; +} + +bool32 SetIllusionMon(struct Pokemon *mon, u32 battlerId) +{ + struct Pokemon *party, *partnerMon; + s32 i, id; + + if (GetMonAbility(mon) != ABILITY_ILLUSION) + return FALSE; + + if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) + party = gPlayerParty; + else + party = gEnemyParty; + + if (IsBattlerAlive(BATTLE_PARTNER(battlerId))) + partnerMon = &party[gBattlerPartyIndexes[BATTLE_PARTNER(battlerId)]]; + else + partnerMon = mon; + + // Find last alive non-egg pokemon. + for (i = PARTY_SIZE - 1; i >= 0; i--) + { + id = pokemon_order_func(i); + if (GetMonData(&party[id], MON_DATA_SANITY_HAS_SPECIES) + && GetMonData(&party[id], MON_DATA_HP) + && &party[id] != mon + && &party[id] != partnerMon) + { + gBattleStruct->illusion[battlerId].on = 1; + gBattleStruct->illusion[battlerId].broken = 0; + gBattleStruct->illusion[battlerId].partyId = id; + gBattleStruct->illusion[battlerId].mon = &party[id]; + return TRUE; + } + } + + return FALSE; +} diff --git a/src/pokeball.c b/src/pokeball.c index 793609ec2..5fcfd15e1 100644 --- a/src/pokeball.c +++ b/src/pokeball.c @@ -342,11 +342,8 @@ u8 DoPokeballSendOutAnimation(s16 pan, u8 kindOfThrow) static void Task_DoPokeballSendOutAnim(u8 taskId) { - u16 throwCaseId; - u8 battlerId; - u16 itemId, ballId; - u8 ballSpriteId; - bool8 notSendOut = FALSE; + u32 throwCaseId, ballId, battlerId, ballSpriteId; + bool32 notSendOut = FALSE; if (gTasks[taskId].tFrames == 0) { @@ -356,13 +353,7 @@ static void Task_DoPokeballSendOutAnim(u8 taskId) throwCaseId = gTasks[taskId].tThrowId; battlerId = gTasks[taskId].tBattler; - - if (GetBattlerSide(battlerId) != B_SIDE_PLAYER) - itemId = GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); - else - itemId = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); - - ballId = ItemIdToBallId(itemId); + ballId = ItemIdToBallId(GetBattlerPokeballItemId(battlerId)); LoadBallGfx(ballId); ballSpriteId = CreateSprite(&gBallSpriteTemplates[ballId], 32, 80, 29); gSprites[ballSpriteId].data[0] = 0x80; @@ -743,8 +734,7 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) if (gMain.inBattle) { - struct Pokemon *mon; - u16 species; + struct Pokemon *mon, *illusionMon; s8 pan; u16 wantedCryCase; u8 taskId; @@ -760,7 +750,6 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) pan = -25; } - species = GetMonData(mon, MON_DATA_SPECIES); if ((battlerId == GetBattlerAtPosition(B_POSITION_PLAYER_LEFT) || battlerId == GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)) && IsDoubleBattle() && gBattleSpritesDataPtr->animationData->field_9_x1) { @@ -783,9 +772,14 @@ static void SpriteCB_ReleaseMonFromBall(struct Sprite *sprite) wantedCryCase = 2; gBattleSpritesDataPtr->healthBoxesData[battlerId].field_1_x40 = 1; - taskId = CreateTask(Task_PlayCryWhenReleasedFromBall, 3); - gTasks[taskId].tCryTaskSpecies = species; + + illusionMon = GetIllusionMonPtr(battlerId); + if (illusionMon != NULL) + gTasks[taskId].tCryTaskSpecies = GetMonData(illusionMon, MON_DATA_SPECIES); + else + gTasks[taskId].tCryTaskSpecies = GetMonData(mon, MON_DATA_SPECIES); + gTasks[taskId].tCryTaskPan = pan; gTasks[taskId].tCryTaskWantedCry = wantedCryCase; gTasks[taskId].tCryTaskBattler = battlerId; @@ -1269,8 +1263,16 @@ void FreeBallGfx(u8 ballId) static u16 GetBattlerPokeballItemId(u8 battlerId) { + struct Pokemon *mon, *illusionMon; + if (GetBattlerSide(battlerId) == B_SIDE_PLAYER) - return GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); + mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]]; else - return GetMonData(&gEnemyParty[gBattlerPartyIndexes[battlerId]], MON_DATA_POKEBALL); + mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]]; + + illusionMon = GetIllusionMonPtr(battlerId); + if (illusionMon != NULL) + mon = illusionMon; + + return GetMonData(mon, MON_DATA_POKEBALL); }