From cc4a9b9f4b5ea742cccab1dc5d889d6a9956a7ed Mon Sep 17 00:00:00 2001 From: Ct11217 Date: Thu, 15 Sep 2022 22:57:29 -0600 Subject: [PATCH 1/6] Recommit to Dev branch --- include/battle_ai_switch_items.h | 2 +- src/battle_ai_main.c | 4 +- src/battle_ai_switch_items.c | 168 +++++++++++++++---------- src/battle_controller_opponent.c | 5 +- src/battle_controller_player_partner.c | 2 +- 5 files changed, 109 insertions(+), 72 deletions(-) diff --git a/include/battle_ai_switch_items.h b/include/battle_ai_switch_items.h index 70dc41b34b..77d1e63151 100644 --- a/include/battle_ai_switch_items.h +++ b/include/battle_ai_switch_items.h @@ -33,7 +33,7 @@ enum { void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId); void AI_TrySwitchOrUseItem(void); -u8 GetMostSuitableMonToSwitchInto(void); +u8 GetMostSuitableMonToSwitchInto(bool8); bool32 ShouldSwitch(void); #endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 312ff853ca..7bc4edae6a 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -356,7 +356,7 @@ static u8 ChooseMoveOrAction_Singles(void) break; } - if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE) { AI_THINKING_STRUCT->switchMon = TRUE; return AI_CHOICE_SWITCH; @@ -370,7 +370,7 @@ static u8 ChooseMoveOrAction_Singles(void) && gDisableStructs[sBattler_AI].truantCounter && gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2) { - if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + if (GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE) { AI_THINKING_STRUCT->switchMon = TRUE; return AI_CHOICE_SWITCH; diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 4948113a21..f8e60ee7ee 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -26,6 +26,7 @@ static bool8 ShouldUseItem(void); static bool32 AiExpectsToFaintPlayer(void); static bool32 AI_ShouldHeal(u32 healAmount); static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount); +static bool32 AI_CheckSurvivabilty(bool8 checkSurvivability, int playerPokemon, int aiPokemon); void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId) { @@ -414,12 +415,12 @@ static bool8 ShouldSwitchIfAbilityBenefit(void) moduloChance = 4; //25% //Attempt to cure bad ailment if (gBattleMons[gActiveBattler].status1 & (STATUS1_SLEEP | STATUS1_FREEZE | STATUS1_TOXIC_POISON) - && GetMostSuitableMonToSwitchInto() != PARTY_SIZE) + && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE) break; //Attempt to cure lesser ailment if ((gBattleMons[gActiveBattler].status1 & STATUS1_ANY) && (gBattleMons[gActiveBattler].hp >= gBattleMons[gActiveBattler].maxHP / 2) - && GetMostSuitableMonToSwitchInto() != PARTY_SIZE + && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE && Random() % (moduloChance*chanceReducer) == 0) break; @@ -431,7 +432,7 @@ static bool8 ShouldSwitchIfAbilityBenefit(void) if (gBattleMons[gActiveBattler].status1 & STATUS1_ANY) return FALSE; if ((gBattleMons[gActiveBattler].hp <= ((gBattleMons[gActiveBattler].maxHP * 2) / 3)) - && GetMostSuitableMonToSwitchInto() != PARTY_SIZE + && GetMostSuitableMonToSwitchInto(TRUE) != PARTY_SIZE && Random() % (moduloChance*chanceReducer) == 0) break; @@ -726,7 +727,7 @@ void AI_TrySwitchOrUseItem(void) { if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) { - s32 monToSwitchId = GetMostSuitableMonToSwitchInto(); + s32 monToSwitchId = GetMostSuitableMonToSwitchInto(TRUE); if (monToSwitchId == PARTY_SIZE) { if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) @@ -746,6 +747,8 @@ void AI_TrySwitchOrUseItem(void) { if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0) continue; + if (GetMonData(&party[monToSwitchId], MON_DATA_SPECIES2) == SPECIES_NONE) + continue; if (monToSwitchId == gBattlerPartyIndexes[battlerIn1]) continue; if (monToSwitchId == gBattlerPartyIndexes[battlerIn2]) @@ -779,7 +782,7 @@ void AI_TrySwitchOrUseItem(void) // If there are two(or more) mons to choose from, always choose one that has baton pass // as most often it can't do much on its own. -static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount) +static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount, bool8 checkSurvivability) { int i, j, bits = 0; @@ -788,6 +791,9 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u if (invalidMons & gBitTable[i]) continue; + if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i)) + continue; + for (j = 0; j < MAX_MON_MOVES; j++) { if (GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL) == MOVE_BATON_PASS) @@ -810,69 +816,71 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u return PARTY_SIZE; } -static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler) +static u32 GetBestMonTypeMatchup(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler, bool8 checkSurvivability) { - int i, bits = 0; - - while (bits != 0x3F) // All mons were checked. + int i, j = 0; + u32 bestResist = UQ_4_12(1.0); + int bestMonId = PARTY_SIZE; + // Find the mon whose type is the most suitable defensively. + for (i = firstId; i < lastId; i++) { - u32 bestResist = UQ_4_12(1.0); - int bestMonId = PARTY_SIZE; - // Find the mon whose type is the most suitable defensively. - for (i = firstId; i < lastId; i++) + u16 species = GetMonData(&party[i], MON_DATA_SPECIES); + u32 typeEffectiveness = UQ_4_12(1.0); + + u8 atkType1 = gBattleMons[opposingBattler].type1; + u8 atkType2 = gBattleMons[opposingBattler].type2; + u8 defType1 = gBaseStats[species].type1; + u8 defType2 = gBaseStats[species].type2; + + if (gBitTable[i] & invalidMons) + continue; + if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i)) + continue; + + typeEffectiveness *= GetTypeModifier(atkType1, defType1); + if (atkType2 != atkType1) + typeEffectiveness *= GetTypeModifier(atkType2, defType1); + if (defType2 != defType1) { - if (!(gBitTable[i] & invalidMons) && !(gBitTable[i] & bits)) - { - u16 species = GetMonData(&party[i], MON_DATA_SPECIES); - u32 typeEffectiveness = UQ_4_12(1.0); - - u8 atkType1 = gBattleMons[opposingBattler].type1; - u8 atkType2 = gBattleMons[opposingBattler].type2; - u8 defType1 = gBaseStats[species].type1; - u8 defType2 = gBaseStats[species].type2; - - typeEffectiveness *= GetTypeModifier(atkType1, defType1); - if (atkType2 != atkType1) - typeEffectiveness *= GetTypeModifier(atkType2, defType1); - if (defType2 != defType1) - { - typeEffectiveness *= GetTypeModifier(atkType1, defType2); - if (atkType2 != atkType1) - typeEffectiveness *= GetTypeModifier(atkType2, defType2); - } - if (typeEffectiveness < bestResist) - { - bestResist = typeEffectiveness; - bestMonId = i; - } - } + typeEffectiveness *= GetTypeModifier(atkType1, defType2); + if (atkType2 != atkType1) + typeEffectiveness *= GetTypeModifier(atkType2, defType2); } - - // Ok, we know the mon has the right typing but does it have at least one super effective move? - if (bestMonId != PARTY_SIZE) + if ((typeEffectiveness < bestResist) + || ((typeEffectiveness <= bestResist) && !checkSurvivability)) //Fine with a nuetral matchup on second time through { - for (i = 0; i < MAX_MON_MOVES; i++) - { - u32 move = GetMonData(&party[bestMonId], MON_DATA_MOVE1 + i); - if (move != MOVE_NONE && AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0)) - break; - } - - if (i != MAX_MON_MOVES) - return bestMonId; // Has both the typing and at least one super effective move. - - bits |= gBitTable[bestMonId]; // Sorry buddy, we want something better. - } - else - { - bits = 0x3F; // No viable mon to switch. + bestResist = typeEffectiveness; + bestMonId = i; } } - return PARTY_SIZE; + // Ok, we don't have anything that type resists. But do we at least have something with a super effective move? + if (bestMonId == PARTY_SIZE) + { + // Find the mon that has an attack most suited offensively + for (i = firstId; i < lastId; i++) + { + if (gBitTable[i] & invalidMons) + continue; + if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i)) + continue; + + for (j = 0; j < MAX_MON_MOVES; j++) + { + u32 move = GetMonData(&party[i], MON_DATA_MOVE1 + j); + if (move != MOVE_NONE && AI_GetTypeEffectiveness(move, gActiveBattler, opposingBattler) >= UQ_4_12(2.0)) + { + bestMonId = i; // Has at least one super effective move. + break; + } + } + } + } + + return bestMonId; } -static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler) +static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler, bool8 checkSurvivability) { int i, j; int bestDmg = 0; @@ -884,6 +892,8 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva { if (gBitTable[i] & invalidMons) continue; + if (AI_CheckSurvivabilty(checkSurvivability, BATTLE_OPPOSITE(gActiveBattler), i)) + continue; for (j = 0; j < MAX_MON_MOVES; j++) { @@ -903,10 +913,10 @@ static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 inva return bestMonId; } -u8 GetMostSuitableMonToSwitchInto(void) +u8 GetMostSuitableMonToSwitchInto(bool8 checkSurvivability) { u32 opposingBattler = 0; - u32 bestMonId = 0; + u32 bestMonId = PARTY_SIZE; u8 battlerIn1 = 0, battlerIn2 = 0; s32 firstId = 0; s32 lastId = 0; // + 1 @@ -948,7 +958,7 @@ u8 GetMostSuitableMonToSwitchInto(void) // Get invalid slots ids. for (i = firstId; i < lastId; i++) { - if (GetMonData(&party[i], MON_DATA_SPECIES) == SPECIES_NONE + if (GetMonData(&party[i], MON_DATA_SPECIES2) == SPECIES_NONE || GetMonData(&party[i], MON_DATA_HP) == 0 || gBattlerPartyIndexes[battlerIn1] == i || gBattlerPartyIndexes[battlerIn2] == i @@ -962,19 +972,26 @@ u8 GetMostSuitableMonToSwitchInto(void) aliveCount++; } - bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount); + bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount, checkSurvivability); if (bestMonId != PARTY_SIZE) return bestMonId; - bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, opposingBattler); + bestMonId = GetBestMonTypeMatchup(party, firstId, lastId, invalidMons, opposingBattler, checkSurvivability); if (bestMonId != PARTY_SIZE) return bestMonId; - bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler); + bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler, checkSurvivability); if (bestMonId != PARTY_SIZE) return bestMonId; - return PARTY_SIZE; + //Didn't find any good options first time around. Try again without checking survivabilty, better than a random mon + if (checkSurvivability && bestMonId == PARTY_SIZE) + { + bestMonId = GetMostSuitableMonToSwitchInto(FALSE); + return bestMonId; + } + + return bestMonId; } static u8 GetAI_ItemType(u16 itemId, const u8 *itemEffect) @@ -1178,3 +1195,24 @@ static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount) } return FALSE; } + +static bool32 AI_CheckSurvivabilty(bool8 checkSurvivability, int playerPokemon, int aiPokemon) +{ + if (!checkSurvivability) + return FALSE; + + if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING) + { + //Opponent can OHKO AI, don't send this Pokemon out + if (CanTargetFaintAiWithMod(playerPokemon, aiPokemon, 0, 0)) + return TRUE; + + //Opponent can 2HKO AI and AI cannot strike first + //ToDo: Modify for switches when AI has already attacked (Volt Switch etc.) + if (CanTargetFaintAiWithMod(playerPokemon, aiPokemon, 0, 2) + && GetWhoStrikesFirst(playerPokemon, aiPokemon, TRUE) == 0) //Player strikes first + return TRUE; + } + + return FALSE; +} \ No newline at end of file diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index c32d0cd587..8e95e3bb99 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -1676,8 +1676,7 @@ static void OpponentHandleChoosePokemon(void) if (*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) == PARTY_SIZE) { - chosenMonId = GetMostSuitableMonToSwitchInto(); - + chosenMonId = GetMostSuitableMonToSwitchInto(TRUE); if (chosenMonId == PARTY_SIZE) { s32 battler1, battler2, firstId, lastId; @@ -1691,7 +1690,6 @@ static void OpponentHandleChoosePokemon(void) battler1 = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT); battler2 = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT); pokemonInBattle = 2; - } GetAIPartyIndexes(gActiveBattler, &firstId, &lastId); @@ -1699,6 +1697,7 @@ static void OpponentHandleChoosePokemon(void) for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--) { if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0 + && GetMonData(&gEnemyParty[chosenMonId], MON_DATA_SPECIES2) != SPECIES_NONE && chosenMonId != gBattlerPartyIndexes[battler1] && chosenMonId != gBattlerPartyIndexes[battler2] && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON diff --git a/src/battle_controller_player_partner.c b/src/battle_controller_player_partner.c index d39d745a6f..8af44377d1 100644 --- a/src/battle_controller_player_partner.c +++ b/src/battle_controller_player_partner.c @@ -1548,7 +1548,7 @@ static void PlayerPartnerHandleChooseItem(void) static void PlayerPartnerHandleChoosePokemon(void) { - s32 chosenMonId = GetMostSuitableMonToSwitchInto(); + s32 chosenMonId = GetMostSuitableMonToSwitchInto(TRUE); if (chosenMonId == 6) // just switch to the next mon { From 36c1c4dc11cb767a117ab34751cbb6d336bb379f Mon Sep 17 00:00:00 2001 From: Porygon23 <59948117+Porygon23@users.noreply.github.com> Date: Fri, 16 Sep 2022 08:21:13 -0600 Subject: [PATCH 2/6] Update include/battle_ai_switch_items.h Co-authored-by: Eduardo Quezada D'Ottone --- include/battle_ai_switch_items.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/battle_ai_switch_items.h b/include/battle_ai_switch_items.h index 77d1e63151..58b65396a6 100644 --- a/include/battle_ai_switch_items.h +++ b/include/battle_ai_switch_items.h @@ -33,7 +33,7 @@ enum { void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId); void AI_TrySwitchOrUseItem(void); -u8 GetMostSuitableMonToSwitchInto(bool8); +u8 GetMostSuitableMonToSwitchInto(bool8 checkSurvivability); bool32 ShouldSwitch(void); #endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H From f4dc5e931f7acf667178f6da84cbd55a0c256ee0 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Tue, 18 Jul 2023 12:01:25 +0200 Subject: [PATCH 3/6] some more stuff --- include/battle_ai_util.h | 2 ++ src/battle_ai_switch_items.c | 25 +++++++++++++++++-------- src/battle_ai_util.c | 28 ++++++++++++++++++---------- src/data/trainer_parties.h | 34 ++++++++++------------------------ src/data/trainers.h | 2 +- 5 files changed, 48 insertions(+), 43 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 0800e1cfb8..fd55a99f73 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -168,6 +168,8 @@ bool32 PartnerMoveIsSameNoTarget(u8 battlerAtkPartner, u16 move, u16 partnerMove bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move); // party logic +struct BattlePokemon *AllocSaveBattleMons(void); +void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons); s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon); s32 CountUsablePartyMons(u8 battlerId); bool32 IsPartyFullyHealedExceptBattler(u8 battler); diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 2418dbab85..625634af4e 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -788,7 +788,7 @@ void AI_TrySwitchOrUseItem(void) // If there are two(or more) mons to choose from, always choose one that has baton pass // as most often it can't do much on its own. -static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount) +static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, int aliveCount, u32 opposingBattler) { int i, j, bits = 0; @@ -796,7 +796,7 @@ static u32 GetBestMonBatonPass(struct Pokemon *party, int firstId, int lastId, u { if (invalidMons & gBitTable[i]) continue; - if (IsAiPartyMonOHKOBy(BATTLE_OPPOSITE(gActiveBattler), &party[i])) + if (IsAiPartyMonOHKOBy(opposingBattler, &party[i])) continue; for (j = 0; j < MAX_MON_MOVES; j++) @@ -980,7 +980,7 @@ u8 GetMostSuitableMonToSwitchInto(void) } } - bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount); + bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount, opposingBattler); if (bestMonId != PARTY_SIZE) return bestMonId; @@ -1161,17 +1161,26 @@ static bool32 AI_OpponentCanFaintAiWithMod(u32 healAmount) static bool32 IsAiPartyMonOHKOBy(u32 battlerAtk, struct Pokemon *aiMon) { - struct BattlePokemon *battleMon; + bool32 ret = FALSE; + struct BattlePokemon *savedBattleMons; s32 hp = GetMonData(aiMon, MON_DATA_HP); s32 bestDmg = AI_CalcPartyMonBestMoveDamage(battlerAtk, gActiveBattler, NULL, aiMon); switch (GetNoOfHitsToKO(bestDmg, hp)) { case 1: - return TRUE; - case 2: // TODO: Compare speeds, if AI mon is faster allow 2 turns - return TRUE; + ret = TRUE; + break; + case 2: // if AI mon is faster allow 2 turns + savedBattleMons = AllocSaveBattleMons(); + PokemonToBattleMon(aiMon, &gBattleMons[gActiveBattler]); + if (AI_WhoStrikesFirst(gActiveBattler, battlerAtk, 0) == AI_IS_SLOWER) + ret = TRUE; + else + ret = FALSE; + FreeRestoreBattleMons(savedBattleMons); + break; } - return FALSE; + return ret; } diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 036e9e4f61..66acaacfb7 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -3368,16 +3368,27 @@ bool32 ShouldUseWishAromatherapy(u8 battlerAtk, u8 battlerDef, u16 move) return FALSE; } +#define SIZE_G_BATTLE_MONS (sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT) + +struct BattlePokemon *AllocSaveBattleMons(void) +{ + struct BattlePokemon *savedBattleMons = Alloc(SIZE_G_BATTLE_MONS); + memcpy(savedBattleMons, gBattleMons, SIZE_G_BATTLE_MONS); + return savedBattleMons; +} + +void FreeRestoreBattleMons(struct BattlePokemon *savedBattleMons) +{ + memcpy(gBattleMons, savedBattleMons, SIZE_G_BATTLE_MONS); + Free(savedBattleMons); +} + // party logic s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon *attackerMon, struct Pokemon *targetMon) { - s32 bestDmg, dmg; - u32 i, move; + s32 i, move, bestDmg, dmg; u8 effectiveness; - struct BattlePokemon *battleMons = Alloc(sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT); - - for (i = 0; i < MAX_BATTLERS_COUNT; i++) - battleMons[i] = gBattleMons[i]; + struct BattlePokemon *savedBattleMons = AllocSaveBattleMons(); if (attackerMon != NULL) PokemonToBattleMon(attackerMon, &gBattleMons[battlerAtk]); @@ -3399,10 +3410,7 @@ s32 AI_CalcPartyMonBestMoveDamage(u32 battlerAtk, u32 battlerDef, struct Pokemon } } - for (i = 0; i < MAX_BATTLERS_COUNT; i++) - gBattleMons[i] = battleMons[i]; - - Free(battleMons); + FreeRestoreBattleMons(savedBattleMons); return dmg; } diff --git a/src/data/trainer_parties.h b/src/data/trainer_parties.h index a0e45ca0c6..1759120b7f 100644 --- a/src/data/trainer_parties.h +++ b/src/data/trainer_parties.h @@ -3366,39 +3366,25 @@ static const struct TrainerMonItemCustomMoves sParty_Drake[] = { static const struct TrainerMonItemCustomMoves sParty_Roxanne1[] = { { - .iv = 46, - .lvl = 46, - .species = SPECIES_SKARMORY, + .iv = 100, + .lvl = 12, + .species = SPECIES_GEODUDE, .heldItem = ITEM_NONE, - .moves = {MOVE_SPIKES, MOVE_TOXIC, MOVE_WHIRLWIND, MOVE_ROOST} + .moves = {MOVE_TACKLE, MOVE_DEFENSE_CURL, MOVE_ROCK_THROW, MOVE_ROCK_TOMB} }, { .iv = 100, - .lvl = 46, - .species = SPECIES_TROPIUS, + .lvl = 12, + .species = SPECIES_GEODUDE, .heldItem = ITEM_NONE, - .moves = {MOVE_LEAF_TORNADO, MOVE_BODY_SLAM, MOVE_PROTECT, MOVE_ROOST} - }, - { - .iv = 47, - .lvl = 47, - .species = SPECIES_HAWLUCHA, - .heldItem = ITEM_ORAN_BERRY, - .moves = {MOVE_SUPERPOWER, MOVE_ACROBATICS, MOVE_U_TURN, MOVE_ROOST} - }, - { - .iv = 47, - .lvl = 47, - .species = SPECIES_MANTINE, - .heldItem = ITEM_ORAN_BERRY, - .moves = {MOVE_CONFUSE_RAY, MOVE_WATER_PULSE, MOVE_AIR_SLASH, MOVE_ROOST} + .moves = {MOVE_TACKLE, MOVE_DEFENSE_CURL, MOVE_ROCK_THROW, MOVE_ROCK_TOMB} }, { .iv = 200, - .lvl = 48, - .species = SPECIES_ALTARIA, + .lvl = 15, + .species = SPECIES_NOSEPASS, .heldItem = ITEM_ORAN_BERRY, - .moves = {MOVE_DRAGON_PULSE, MOVE_TOXIC, MOVE_COTTON_GUARD, MOVE_ROOST} + .moves = {MOVE_BLOCK, MOVE_HARDEN, MOVE_TACKLE, MOVE_ROCK_TOMB} } }; diff --git a/src/data/trainers.h b/src/data/trainers.h index 22d4a48804..75f7edc8b7 100644 --- a/src/data/trainers.h +++ b/src/data/trainers.h @@ -3189,7 +3189,7 @@ const struct Trainer gTrainers[] = { .trainerName = _("ROXANNE"), .items = {ITEM_POTION, ITEM_POTION, ITEM_NONE, ITEM_NONE}, .doubleBattle = FALSE, - .aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_RISKY, + .aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY, .party = ITEM_CUSTOM_MOVES(sParty_Roxanne1), }, From 2860ac9c809be439acdb5b7bb5168bc85277b2f5 Mon Sep 17 00:00:00 2001 From: LOuroboros Date: Wed, 19 Jul 2023 13:29:36 -0300 Subject: [PATCH 4/6] Added a debug feature to hatch eggs --- data/scripts/debug.inc | 31 +++++++++++++++++++++++++++++++ src/debug.c | 10 ++++++++++ 2 files changed, 41 insertions(+) diff --git a/data/scripts/debug.inc b/data/scripts/debug.inc index d7f486907d..64b7e1dd01 100644 --- a/data/scripts/debug.inc +++ b/data/scripts/debug.inc @@ -112,4 +112,35 @@ Debug_SaveBlock2Size:: Debug_PokemonStorageSize:: .string "{PKMN}Storage size: {STR_VAR_1}/{STR_VAR_2}.$" +Debug_HatchAnEgg:: + lockall + getpartysize + goto_if_eq VAR_RESULT, 0, Debug_HatchAnEgg_NoPokemon + special ChoosePartyMon + waitstate + goto_if_ge VAR_0x8004, PARTY_SIZE, Debug_HatchAnEgg_End + specialvar VAR_RESULT, ScriptGetPartyMonSpecies + goto_if_ne VAR_RESULT, SPECIES_EGG, DebugScript_HatchAnEgg_CantForceHatch + special EggHatch + waitstate +Debug_HatchAnEgg_End:: + releaseall + end + +Debug_HatchAnEgg_NoPokemon:: + msgbox DebugScript_HatchAnEgg_Text_EmptyParty, MSGBOX_DEFAULT + releaseall + end + +DebugScript_HatchAnEgg_CantForceHatch:: + msgbox DebugScript_HatchAnEgg_Text_NotAnEgg, MSGBOX_DEFAULT + releaseall + end + +DebugScript_HatchAnEgg_Text_EmptyParty:: + .string "You have no Pokémon nor Eggs.$" + +DebugScript_HatchAnEgg_Text_NotAnEgg:: + .string "That's not a Pokémon Egg.$" + .endif diff --git a/src/debug.c b/src/debug.c index ffacba9daf..141fbf49a8 100644 --- a/src/debug.c +++ b/src/debug.c @@ -91,6 +91,7 @@ enum { // Util DEBUG_UTIL_MENU_ITEM_TRAINER_ID, DEBUG_UTIL_MENU_ITEM_CLEAR_BOXES, DEBUG_UTIL_MENU_ITEM_CHEAT, + DEBUG_UTIL_MENU_ITEM_HATCH_AN_EGG, }; enum { // Scripts DEBUG_UTIL_MENU_ITEM_SCRIPT_1, @@ -312,6 +313,7 @@ static void DebugAction_Util_Trainer_Gender(u8 taskId); static void DebugAction_Util_Trainer_Id(u8 taskId); static void DebugAction_Util_Clear_Boxes(u8 taskId); static void DebugAction_Util_CheatStart(u8 taskId); +static void DebugAction_Util_HatchAnEgg(u8 taskId); static void DebugAction_FlagsVars_Flags(u8 taskId); static void DebugAction_FlagsVars_FlagsSelect(u8 taskId); @@ -381,6 +383,7 @@ extern u8 Debug_Script_8[]; extern u8 Debug_ShowFieldMessageStringVar4[]; extern u8 Debug_CheatStart[]; +extern u8 Debug_HatchAnEgg[]; extern u8 PlayersHouse_2F_EventScript_SetWallClock[]; extern u8 PlayersHouse_2F_EventScript_CheckWallClock[]; extern u8 Debug_CheckSaveBlock[]; @@ -436,6 +439,7 @@ static const u8 sDebugText_Util_Trainer_Gender[] = _("Toggle T. Gender" static const u8 sDebugText_Util_Trainer_Id[] = _("New Trainer Id"); static const u8 sDebugText_Util_Clear_Boxes[] = _("Clear Storage Boxes"); static const u8 sDebugText_Util_CheatStart[] = _("CHEAT Start"); +static const u8 sDebugText_Util_HatchAnEgg[] = _("Hatch an Egg"); // Flags/Vars Menu static const u8 sDebugText_FlagsVars_Flags[] = _("Set Flag XYZ…{CLEAR_TO 110}{RIGHT_ARROW}"); static const u8 sDebugText_FlagsVars_Flag[] = _("Flag: {STR_VAR_1}{CLEAR_TO 90}\n{STR_VAR_2}{CLEAR_TO 90}\n{STR_VAR_3}"); @@ -597,6 +601,7 @@ static const struct ListMenuItem sDebugMenu_Items_Utilities[] = [DEBUG_UTIL_MENU_ITEM_TRAINER_ID] = {sDebugText_Util_Trainer_Id, DEBUG_UTIL_MENU_ITEM_TRAINER_ID}, [DEBUG_UTIL_MENU_ITEM_CLEAR_BOXES] = {sDebugText_Util_Clear_Boxes, DEBUG_UTIL_MENU_ITEM_CLEAR_BOXES}, [DEBUG_UTIL_MENU_ITEM_CHEAT] = {sDebugText_Util_CheatStart, DEBUG_UTIL_MENU_ITEM_CHEAT}, + [DEBUG_UTIL_MENU_ITEM_HATCH_AN_EGG] = {sDebugText_Util_HatchAnEgg, DEBUG_UTIL_MENU_ITEM_HATCH_AN_EGG}, }; static const struct ListMenuItem sDebugMenu_Items_Scripts[] = { @@ -729,6 +734,7 @@ static void (*const sDebugMenu_Actions_Utilities[])(u8) = [DEBUG_UTIL_MENU_ITEM_TRAINER_ID] = DebugAction_Util_Trainer_Id, [DEBUG_UTIL_MENU_ITEM_CLEAR_BOXES] = DebugAction_Util_Clear_Boxes, [DEBUG_UTIL_MENU_ITEM_CHEAT] = DebugAction_Util_CheatStart, + [DEBUG_UTIL_MENU_ITEM_HATCH_AN_EGG] = DebugAction_Util_HatchAnEgg, }; static void (*const sDebugMenu_Actions_Scripts[])(u8) = { @@ -1976,6 +1982,10 @@ static void DebugAction_Util_CheatStart(u8 taskId) { Debug_DestroyMenu_Full_Script(taskId, Debug_CheatStart); } +static void DebugAction_Util_HatchAnEgg(u8 taskId) +{ + Debug_DestroyMenu_Full_Script(taskId, Debug_HatchAnEgg); +} // ******************************* // Actions Scripts From c11839c648c402cae731c16fcd347252e0e0197e Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 26 Jul 2023 09:34:23 +0200 Subject: [PATCH 5/6] Use isValidForBattle --- src/battle_ai_switch_items.c | 46 ++++++++---------------------------- 1 file changed, 10 insertions(+), 36 deletions(-) diff --git a/src/battle_ai_switch_items.c b/src/battle_ai_switch_items.c index 625634af4e..d815d3f8bc 100644 --- a/src/battle_ai_switch_items.c +++ b/src/battle_ai_switch_items.c @@ -113,11 +113,7 @@ static bool8 ShouldSwitchIfWonderGuard(void) // Find a Pokemon in the party that has a super effective move. for (i = firstId; i < lastId; i++) { - if (GetMonData(&party[i], MON_DATA_HP) == 0) - continue; - if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) - continue; - if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) + if (!IsValidForBattle(&party[i])) continue; if (i == gBattlerPartyIndexes[gActiveBattler]) continue; @@ -196,13 +192,9 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void) for (i = firstId; i < lastId; i++) { - u16 species; u16 monAbility; - if (GetMonData(&party[i], MON_DATA_HP) == 0) - continue; - species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); - if (species == SPECIES_NONE || species == SPECIES_EGG) + if (!IsValidForBattle(&party[i])) continue; if (i == gBattlerPartyIndexes[battlerIn1]) continue; @@ -216,7 +208,6 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void) continue; monAbility = GetMonAbility(&party[i]); - if (absorbingTypeAbility == monAbility && Random() & 1) { // we found a mon. @@ -291,9 +282,7 @@ static bool8 ShouldSwitchIfGameStatePrompt(void) continue; //Look for mon in party that is able to be switched into and has ability that sets terrain - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG + if (IsValidForBattle(&party[i]) && i != gBattlerPartyIndexes[gActiveBattler] && i != gBattlerPartyIndexes[BATTLE_PARTNER(gActiveBattler)] && IsBattlerGrounded(gActiveBattler) @@ -562,13 +551,9 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent) for (i = firstId; i < lastId; i++) { - u16 species; - u16 monAbility; + u16 species, monAbility; - if (GetMonData(&party[i], MON_DATA_HP) == 0) - continue; - species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); - if (species == SPECIES_NONE || species == SPECIES_EGG) + if (!IsValidForBattle(&party[i])) continue; if (i == gBattlerPartyIndexes[battlerIn1]) continue; @@ -581,8 +566,8 @@ static bool8 FindMonWithFlagsAndSuperEffective(u16 flags, u8 moduloPercent) if (IsAceMon(gActiveBattler, i)) continue; + species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); monAbility = GetMonAbility(&party[i]); - CalcPartyMonTypeEffectivenessMultiplier(gLastLandedMoves[gActiveBattler], species, monAbility); if (gMoveResultFlags & flags) { @@ -651,11 +636,7 @@ bool32 ShouldSwitch(void) for (i = firstId; i < lastId; i++) { - if (GetMonData(&party[i], MON_DATA_HP) == 0) - continue; - if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE) - continue; - if (GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG) + if (!IsValidForBattle(&party[i])) continue; if (i == gBattlerPartyIndexes[battlerIn1]) continue; @@ -752,9 +733,7 @@ void AI_TrySwitchOrUseItem(void) for (monToSwitchId = (lastId-1); monToSwitchId >= firstId; monToSwitchId--) { - if (GetMonData(&party[monToSwitchId], MON_DATA_HP) == 0) - continue; - if (GetMonData(&party[monToSwitchId], MON_DATA_SPECIES) == SPECIES_NONE) + if (!IsValidForBattle(&party[monToSwitchId])) continue; if (monToSwitchId == gBattlerPartyIndexes[battlerIn1]) continue; @@ -957,10 +936,7 @@ u8 GetMostSuitableMonToSwitchInto(void) // Get invalid slots ids. for (i = firstId; i < lastId; i++) { - u16 species = GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG); - if (species == SPECIES_NONE - || species == SPECIES_EGG - || GetMonData(&party[i], MON_DATA_HP) == 0 + if (!IsValidForBattle(&party[i]) || gBattlerPartyIndexes[battlerIn1] == i || gBattlerPartyIndexes[battlerIn2] == i || i == *(gBattleStruct->monToSwitchIntoId + battlerIn1) @@ -1043,9 +1019,7 @@ static bool8 ShouldUseItem(void) for (i = 0; i < PARTY_SIZE; i++) { - if (GetMonData(&party[i], MON_DATA_HP) != 0 - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE - && GetMonData(&party[i], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG) + if (IsValidForBattle(&party[i])) { validMons++; } From e7bc42fcafd269c3f46c70568fa0655ac965b1eb Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Thu, 3 Aug 2023 21:17:39 +0200 Subject: [PATCH 6/6] use IsValidForBattle --- src/battle_controller_opponent.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/battle_controller_opponent.c b/src/battle_controller_opponent.c index 4b8820f9b9..a786dafa69 100644 --- a/src/battle_controller_opponent.c +++ b/src/battle_controller_opponent.c @@ -1707,8 +1707,7 @@ static void OpponentHandleChoosePokemon(void) for (chosenMonId = (lastId-1); chosenMonId >= firstId; chosenMonId--) { - if (GetMonData(&gEnemyParty[chosenMonId], MON_DATA_HP) != 0 - && GetMonData(&gEnemyParty[chosenMonId], MON_DATA_SPECIES) != SPECIES_NONE + if (IsValidForBattle(&gEnemyParty[chosenMonId]) && chosenMonId != gBattlerPartyIndexes[battler1] && chosenMonId != gBattlerPartyIndexes[battler2] && (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON)