mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2025-02-04 18:30:07 +01:00
Truant not dumb
This commit is contained in:
parent
a41ecf7101
commit
cfb49cdd0b
@ -14,6 +14,7 @@ void BattleAI_SetupItems(void);
|
||||
void BattleAI_SetupFlags(void);
|
||||
void BattleAI_SetupAIData(u8 defaultScoreMoves);
|
||||
u8 BattleAI_ChooseMoveOrAction(void);
|
||||
bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler);
|
||||
void ClearBattlerMoveHistory(u8 battlerId);
|
||||
void RecordAbilityBattle(u8 battlerId, u8 abilityId);
|
||||
void ClearBattlerAbilityHistory(u8 battlerId);
|
||||
|
@ -450,12 +450,27 @@ static u32 GetTotalBaseStat(u32 species)
|
||||
+ gBaseStats[species].baseSpDefense;
|
||||
}
|
||||
|
||||
bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
u32 move = gBattleResources->battleHistory->usedMoves[opposingBattler].moves[i];
|
||||
if (gBattleMoves[move].effect == EFFECT_PROTECT && move != MOVE_ENDURE)
|
||||
return TRUE;
|
||||
if (gBattleMoves[move].effect == EFFECT_SEMI_INVULNERABLE && GetWhoStrikesFirst(battlerAI, opposingBattler, TRUE) == 1)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static u8 ChooseMoveOrAction_Singles(void)
|
||||
{
|
||||
u8 currentMoveArray[4];
|
||||
u8 consideredMoveArray[4];
|
||||
u8 numOfBestMoves;
|
||||
s32 i;
|
||||
s32 i, id;
|
||||
u32 flags = AI_THINKING_STRUCT->aiFlags;
|
||||
|
||||
RecordLastUsedMoveByTarget();
|
||||
@ -478,6 +493,7 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
if (AI_THINKING_STRUCT->aiAction & AI_ACTION_WATCH)
|
||||
return AI_CHOICE_WATCH;
|
||||
|
||||
gActiveBattler = sBattler_AI;
|
||||
// Consider switching if all moves are worthless to use.
|
||||
if (AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY | AI_SCRIPT_CHECK_BAD_MOVE | AI_SCRIPT_TRY_TO_FAINT | AI_SCRIPT_PREFER_BATON_PASS)
|
||||
&& CountUsablePartyMons(sBattler_AI) >= 1
|
||||
@ -493,7 +509,6 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
break;
|
||||
}
|
||||
|
||||
gActiveBattler = sBattler_AI;
|
||||
if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
||||
@ -501,6 +516,22 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
}
|
||||
}
|
||||
|
||||
// Consider switching if your mon with truant is bodied by Protect spam.
|
||||
// Or is using a double turn semi invulnerable move(such as Fly) and is faster.
|
||||
if (GetBattlerAbility(sBattler_AI) == ABILITY_TRUANT
|
||||
&& IsTruantMonVulnerable(sBattler_AI, gBattlerTarget)
|
||||
&& gDisableStructs[sBattler_AI].truantCounter
|
||||
&& AI_THINKING_STRUCT->aiFlags & (AI_SCRIPT_CHECK_VIABILITY)
|
||||
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2
|
||||
&& CountUsablePartyMons(sBattler_AI) >= 1)
|
||||
{
|
||||
if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
|
||||
{
|
||||
AI_THINKING_STRUCT->switchMon = TRUE;
|
||||
return AI_CHOICE_SWITCH;
|
||||
}
|
||||
}
|
||||
|
||||
numOfBestMoves = 1;
|
||||
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
|
||||
consideredMoveArray[0] = 0;
|
||||
|
@ -577,18 +577,143 @@ void AI_TrySwitchOrUseItem(void)
|
||||
BtlController_EmitTwoReturnValues(1, B_ACTION_USE_MOVE, (gActiveBattler ^ BIT_SIDE) << 8);
|
||||
}
|
||||
|
||||
// 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)
|
||||
{
|
||||
int i, j, bits = 0;
|
||||
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (invalidMons & gBitTable[i])
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL) == MOVE_BATON_PASS)
|
||||
{
|
||||
bits |= gBitTable[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((aliveCount == 2 || (aliveCount > 2 && Random() % 3 == 0)) && bits)
|
||||
{
|
||||
do
|
||||
{
|
||||
i = (Random() % (lastId - firstId)) + firstId;
|
||||
} while (!(bits & gBitTable[i]));
|
||||
return i;
|
||||
}
|
||||
|
||||
return PARTY_SIZE;
|
||||
}
|
||||
|
||||
static u32 GestBestMonOffensive(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler)
|
||||
{
|
||||
int i, bits = 0;
|
||||
|
||||
while (bits != 0x3F) // All mons were checked.
|
||||
{
|
||||
int bestDmg = 0;
|
||||
int bestMonId = PARTY_SIZE;
|
||||
// Find the mon whose type is the most suitable offensively.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (!(gBitTable[i] & invalidMons) && !(gBitTable[i] & bits))
|
||||
{
|
||||
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
|
||||
u32 typeDmg = UQ_4_12(1.0);
|
||||
|
||||
u8 atkType1 = gBaseStats[species].type1;
|
||||
u8 atkType2 = gBaseStats[species].type2;
|
||||
u8 defType1 = gBattleMons[opposingBattler].type1;
|
||||
u8 defType2 = gBattleMons[opposingBattler].type2;
|
||||
|
||||
typeDmg *= GetTypeModifier(atkType1, defType1);
|
||||
if (atkType2 != atkType1)
|
||||
typeDmg *= GetTypeModifier(atkType2, defType1);
|
||||
if (defType2 != defType1)
|
||||
{
|
||||
typeDmg *= GetTypeModifier(atkType1, defType2);
|
||||
if (atkType2 != atkType1)
|
||||
typeDmg *= GetTypeModifier(atkType2, defType2);
|
||||
}
|
||||
if (bestDmg < typeDmg)
|
||||
{
|
||||
bestDmg = typeDmg;
|
||||
bestMonId = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, we know the mon has the right typing but does it have at least one super effective move?
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
{
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
return PARTY_SIZE;
|
||||
}
|
||||
|
||||
static u32 GetBestMonDmg(struct Pokemon *party, int firstId, int lastId, u8 invalidMons, u32 opposingBattler)
|
||||
{
|
||||
int i, j;
|
||||
int bestDmg = 0;
|
||||
int bestMonId = PARTY_SIZE;
|
||||
|
||||
gMoveResultFlags = 0;
|
||||
// If we couldn't find the best mon in terms of typing, find the one that deals most damage.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (gBitTable[i] & invalidMons)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
u32 move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
|
||||
if (move != MOVE_NONE && gBattleMoves[move].power != 0)
|
||||
{
|
||||
s32 dmg = AI_CalcPartyMonDamage(move, gActiveBattler, opposingBattler, &party[i]);
|
||||
if (bestDmg < dmg)
|
||||
{
|
||||
bestDmg = dmg;
|
||||
bestMonId = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMonId;
|
||||
}
|
||||
|
||||
u8 GetMostSuitableMonToSwitchInto(void)
|
||||
{
|
||||
u8 opposingBattler = 0;
|
||||
u32 opposingBattler = 0;
|
||||
u32 bestDmg = 0;
|
||||
u8 bestMonId = 0;
|
||||
u32 bestMonId = 0;
|
||||
u8 battlerIn1 = 0, battlerIn2 = 0;
|
||||
s32 firstId = 0;
|
||||
s32 lastId = 0; // + 1
|
||||
struct Pokemon *party;
|
||||
s32 i, j, aliveCount = 0;
|
||||
u8 invalidMons = 0, bits = 0;
|
||||
u16 move;
|
||||
u8 invalidMons = 0;
|
||||
|
||||
if (*(gBattleStruct->monToSwitchIntoId + gActiveBattler) != PARTY_SIZE)
|
||||
return *(gBattleStruct->monToSwitchIntoId + gActiveBattler);
|
||||
@ -629,120 +754,26 @@ u8 GetMostSuitableMonToSwitchInto(void)
|
||||
|| gBattlerPartyIndexes[battlerIn1] == i
|
||||
|| gBattlerPartyIndexes[battlerIn2] == i
|
||||
|| i == *(gBattleStruct->monToSwitchIntoId + battlerIn1)
|
||||
|| i == *(gBattleStruct->monToSwitchIntoId + battlerIn2))
|
||||
|| i == *(gBattleStruct->monToSwitchIntoId + battlerIn2)
|
||||
|| (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(gActiveBattler, opposingBattler))) // While not really invalid per say, not really wise to switch into this mon.
|
||||
invalidMons |= gBitTable[i];
|
||||
else
|
||||
aliveCount++;
|
||||
}
|
||||
|
||||
// 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.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (invalidMons & gBitTable[i])
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
if (GetMonData(&party[i], MON_DATA_MOVE1 + j, NULL) == MOVE_BATON_PASS)
|
||||
{
|
||||
bits |= gBitTable[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((aliveCount == 2 || (aliveCount > 2 && Random() % 3 == 0)) && bits)
|
||||
{
|
||||
do
|
||||
{
|
||||
bestMonId = (Random() % (lastId - firstId)) + firstId;
|
||||
} while (!(bits & gBitTable[bestMonId]));
|
||||
bestMonId = GetBestMonBatonPass(party, firstId, lastId, invalidMons, aliveCount);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
return bestMonId;
|
||||
}
|
||||
|
||||
bits = 0;
|
||||
while (bits != 0x3F) // All mons are invalid.
|
||||
{
|
||||
bestDmg = 0;
|
||||
bestMonId = PARTY_SIZE;
|
||||
// Find the mon whose type is the most suitable offensively.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (!(gBitTable[i] & invalidMons) && !(gBitTable[i] & bits))
|
||||
{
|
||||
u16 species = GetMonData(&party[i], MON_DATA_SPECIES);
|
||||
u32 typeDmg = UQ_4_12(1.0);
|
||||
bestMonId = GestBestMonOffensive(party, firstId, lastId, invalidMons, opposingBattler);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
return bestMonId;
|
||||
|
||||
u8 atkType1 = gBaseStats[species].type1;
|
||||
u8 atkType2 = gBaseStats[species].type2;
|
||||
u8 defType1 = gBattleMons[opposingBattler].type1;
|
||||
u8 defType2 = gBattleMons[opposingBattler].type2;
|
||||
bestMonId = GetBestMonDmg(party, firstId, lastId, invalidMons, opposingBattler);
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
return bestMonId;
|
||||
|
||||
typeDmg *= GetTypeModifier(atkType1, defType1);
|
||||
if (atkType2 != atkType1)
|
||||
typeDmg *= GetTypeModifier(atkType2, defType1);
|
||||
if (defType2 != defType1)
|
||||
{
|
||||
typeDmg *= GetTypeModifier(atkType1, defType2);
|
||||
if (atkType2 != atkType1)
|
||||
typeDmg *= GetTypeModifier(atkType2, defType2);
|
||||
}
|
||||
if (bestDmg < typeDmg)
|
||||
{
|
||||
bestDmg = typeDmg;
|
||||
bestMonId = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ok, we know the mon has the right typing but does it have at least one super effective move?
|
||||
if (bestMonId != PARTY_SIZE)
|
||||
{
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
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.
|
||||
}
|
||||
}
|
||||
|
||||
gBattleStruct->dynamicMoveType = 0;
|
||||
gMoveResultFlags = 0;
|
||||
bestDmg = 0;
|
||||
bestMonId = 6;
|
||||
|
||||
// If we couldn't find the best mon in terms of typing, find the one that deals most damage.
|
||||
for (i = firstId; i < lastId; i++)
|
||||
{
|
||||
if (gBitTable[i] & invalidMons)
|
||||
continue;
|
||||
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
|
||||
if (move != MOVE_NONE && gBattleMoves[move].power != 0)
|
||||
{
|
||||
s32 dmg = AI_CalcPartyMonDamage(move, gActiveBattler, opposingBattler, &party[i]);
|
||||
if (bestDmg < dmg)
|
||||
{
|
||||
bestDmg = dmg;
|
||||
bestMonId = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bestMonId;
|
||||
return PARTY_SIZE;
|
||||
}
|
||||
|
||||
static u8 GetAI_ItemType(u16 itemId, const u8 *itemEffect)
|
||||
|
Loading…
x
Reference in New Issue
Block a user