Skip to content

Commit

Permalink
Smarter SwitchAI Mon Choices | HasBadOdds Switch Check (rh-hideout#3253)
Browse files Browse the repository at this point in the history
* SwitchAI makes much smarter mon choices

* Add HasHadOdds check to ShouldSwitch decision

* Remove early return

* Rework Baton Pass check as per discussion with Alex

* Forgot to adjust a comment

* Don't program before breakfast lol (if / else if fix)

* Switch AI_CalcDamage for AI_DATA->simulatedDmg in HasBadOdds

Thanks Alex! :D

* Typo in a hitToKO comparison

* Remove and replace AI_CalcPartyMonBestMoveDamage and IsAiPartyMonOHKOBy from rh-hideout#3146

See https://discord.com/channels/419213663107416084/1144447521960251472 for details

* Major refactor, new struct, switchin considers damage / healing from hazards / status / held item / weather

* Forgot Snow exists and heals Ice Body, haven't played Switch games lol

* (rh-hideout@766a1a2) Compatibility, use new struct field instead of function call

* Fixing oversight from previous upstream merge

* Improve TSpikes handling to make GetSwitchinHazardDamage more applicable

Small fixes:
- EFFECT_EXPLOSION typo (!= to ==)
- Order of if statements near bestResistEffective
- Spacing of terms in big HasBadOdds if statements

* Forgot to uncomment blocks disabled for debugging what turned out to be vanilla behaviour lol

* Remove another holdover from debugging, sorry :/

* Lastly, undoing my debug trainer

* Type matchup based on species type rather than current type

Suggested by BLourenco on Discord, the idea is that a mon that's had its type affected by a move like Soak will still have moves as though it was its regular typing, and so prioritizing the temporary typing wouldn't be ideal.
https://discord.com/channels/419213663107416084/1144447521960251472/1146644578141736970

* gActiveBattler upcoming merge fixes

* Egg changes part 1

* Egg changes part 2, just need to address EWRAM still

* Move SwitchinCandidate struct to AiLogicData

* Consider Steel type when checking TSpikes

* Comment about CanBePoisoned compatibility

* Changes for Egg's 2nd review

* Put period back in comment, whoops lol

* Latest upcoming merge fixes

* Missed a few u32 updates

* Combine GetBestMonIntegrate functions / flags, some modularization

* Fix merge error

* Make modern fixes

* Two tests done, two to go

* Accidentally pushed reference test, removing it

* Type matchup switching tests

* Tests for defensive vs offense switches

---------

Co-authored-by: DizzyEggg <jajkodizzy@wp.pl>
  • Loading branch information
2 people authored and Kasenn committed Dec 29, 2023
1 parent c9db739 commit f99fe0d
Show file tree
Hide file tree
Showing 13 changed files with 1,527 additions and 117 deletions.
8 changes: 8 additions & 0 deletions include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,12 @@ struct AIPartyData // Opposing battlers - party mons.
u8 count[NUM_BATTLE_SIDES];
};

struct SwitchinCandidate
{
struct BattlePokemon battleMon;
bool8 hypotheticalStatus;
};

// Ai Data used when deciding which move to use, computed only once before each turn's start.
struct AiLogicData
{
Expand All @@ -300,6 +306,8 @@ struct AiLogicData
bool8 shouldSwitchMon; // Because all available moves have no/little effect. Each bit per battler.
u8 monToSwitchId[MAX_BATTLERS_COUNT]; // ID of the mon to switch.
bool8 weatherHasEffect; // The same as WEATHER_HAS_EFFECT. Stored here, so it's called only once.
u8 mostSuitableMonId; // Stores result of GetMostSuitableMonToSwitchInto, which decides which generic mon the AI would switch into if they decide to switch. This can be overruled by specific mons found in ShouldSwitch; the final resulting mon is stored in AI_monToSwitchIntoId.
struct SwitchinCandidate switchinCandidate; // Struct used for deciding which mon to switch to in battle_ai_switch_items.c
};

struct AI_ThinkingStruct
Expand Down
2 changes: 1 addition & 1 deletion include/battle_ai_switch_items.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
void AI_TrySwitchOrUseItem(u32 battler);
u8 GetMostSuitableMonToSwitchInto(u32 battler);
u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd);
bool32 ShouldSwitch(u32 battler);

#endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H
3 changes: 2 additions & 1 deletion include/battle_ai_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,6 @@ bool32 ShouldUseWishAromatherapy(u32 battlerAtk, u32 battlerDef, u32 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(u32 battlerId);
bool32 IsPartyFullyHealedExceptBattler(u32 battler);
bool32 PartyHasMoveSplit(u32 battlerId, u32 split);
Expand All @@ -194,4 +193,6 @@ void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);

s32 AI_CalcPartyMonDamage(u32 move, u32 battlerAtk, u32 battlerDef, struct BattlePokemon switchinCandidate, bool8 isPartyMonAttacker);

#endif //GUARD_BATTLE_AI_UTIL_H
1 change: 1 addition & 0 deletions include/constants/battle_ai.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks
#define AI_FLAG_ACE_POKEMON (1 << 16) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining.
#define AI_FLAG_OMNISCIENT (1 << 17) // AI has full knowledge of player moves, abilities, hold items
#define AI_FLAG_SMART_MON_CHOICES (1 << 18) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Pairs very well with AI_FLAG_SMART_SWITCHING.

#define AI_FLAG_COUNT 18

Expand Down
14 changes: 14 additions & 0 deletions include/constants/items.h
Original file line number Diff line number Diff line change
Expand Up @@ -607,6 +607,20 @@
#define ITEM_UTILITY_UMBRELLA 513

// Berries
#if B_CONFUSE_BERRIES_HEAL >= GEN_8
#define CONFUSE_BERRY_HEAL_FRACTION 3
#elif B_CONFUSE_BERRIES_HEAL == GEN_7
#define CONFUSE_BERRY_HEAL_FRACTION 2
#else
#define CONFUSE_BERRY_HEAL_FRACTION 8
#endif

#if B_CONFUSE_BERRIES_HEAL >= GEN_7
#define CONFUSE_BERRY_HP_FRACTION 4
#else
#define CONFUSE_BERRY_HP_FRACTION 2
#endif

#define ITEM_CHERI_BERRY 514
#define ITEM_CHESTO_BERRY 515
#define ITEM_PECHA_BERRY 516
Expand Down
2 changes: 1 addition & 1 deletion src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -414,7 +414,7 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)

static bool32 AI_SwitchMonIfSuitable(u32 battler)
{
u32 monToSwitchId = GetMostSuitableMonToSwitchInto(battler);
u32 monToSwitchId = AI_DATA->mostSuitableMonId;
if (monToSwitchId != PARTY_SIZE)
{
AI_DATA->shouldSwitchMon |= gBitTable[battler];
Expand Down
Loading

0 comments on commit f99fe0d

Please sign in to comment.