mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-11-16 11:37:40 +01:00
optimize dmg move result and ai who is faster
This commit is contained in:
parent
406209f738
commit
1a64938c9b
@ -289,6 +289,8 @@ struct AiLogicData
|
|||||||
u16 predictedMoves[MAX_BATTLERS_COUNT];
|
u16 predictedMoves[MAX_BATTLERS_COUNT];
|
||||||
u8 hpPercents[MAX_BATTLERS_COUNT];
|
u8 hpPercents[MAX_BATTLERS_COUNT];
|
||||||
u16 partnerMove;
|
u16 partnerMove;
|
||||||
|
u16 speedStats[MAX_BATTLERS_COUNT]; // Speed stats for all battles, calculated only once, same way as damages
|
||||||
|
u8 moveDmgResult[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // MOVE_POWER defines for GetMoveDamageResult ; attacker, target, moveIndex
|
||||||
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
||||||
u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
|
||||||
u8 moveLimitations[MAX_BATTLERS_COUNT];
|
u8 moveLimitations[MAX_BATTLERS_COUNT];
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
|
|
||||||
#define FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE)
|
#define FOE(battler) ((BATTLE_OPPOSITE(battler)) & BIT_SIDE)
|
||||||
|
|
||||||
|
#define AI_STRIKES_FIRST(battlerAi, battlerDef, move)((AI_WhoStrikesFirst(battlerAi, battlerDef, move) == AI_IS_FASTER))
|
||||||
|
|
||||||
bool32 AI_RandLessThan(u8 val);
|
bool32 AI_RandLessThan(u8 val);
|
||||||
bool32 IsAiVsAiBattle(void);
|
bool32 IsAiVsAiBattle(void);
|
||||||
bool32 BattlerHasAi(u32 battlerId);
|
bool32 BattlerHasAi(u32 battlerId);
|
||||||
@ -23,13 +25,12 @@ void SetBattlerData(u32 battlerId);
|
|||||||
void RestoreBattlerData(u32 battlerId);
|
void RestoreBattlerData(u32 battlerId);
|
||||||
u16 GetAIChosenMove(u8 battlerId);
|
u16 GetAIChosenMove(u8 battlerId);
|
||||||
|
|
||||||
bool32 WillAIStrikeFirst(void);
|
|
||||||
u32 GetTotalBaseStat(u32 species);
|
u32 GetTotalBaseStat(u32 species);
|
||||||
bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler);
|
bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler);
|
||||||
bool32 AtMaxHp(u8 battler);
|
bool32 AtMaxHp(u8 battler);
|
||||||
u32 GetHealthPercentage(u8 battler);
|
u32 GetHealthPercentage(u8 battler);
|
||||||
bool32 IsBattlerTrapped(u8 battler, bool8 switching);
|
bool32 IsBattlerTrapped(u8 battler, bool8 switching);
|
||||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 consideredMove);
|
u32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered);
|
||||||
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk);
|
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk);
|
||||||
bool32 CanMoveFaintBattler(u16 move, u32 battlerDef, u32 battlerAtk, u8 nHits);
|
bool32 CanMoveFaintBattler(u16 move, u32 battlerDef, u32 battlerAtk, u8 nHits);
|
||||||
bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod);
|
bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod);
|
||||||
@ -87,7 +88,8 @@ bool32 MovesWithSplitUnusable(u32 attacker, u32 target, u32 split);
|
|||||||
s32 AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower);
|
s32 AI_CalcDamageSaveBattlers(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower);
|
||||||
s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower);
|
s32 AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u8 *typeEffectiveness, bool32 considerZPower);
|
||||||
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
|
u32 GetNoOfHitsToKO(u32 dmg, s32 hp);
|
||||||
u8 GetMoveDamageResult(u16 move);
|
void SetMoveDamageResult(u32 battlerAtk, u16 *moves);
|
||||||
|
u32 GetMoveDamageResult(u32 battlerAtk, u32 battlerDef, u32 moveIndex);
|
||||||
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef);
|
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef);
|
||||||
uq4_12_t AI_GetTypeEffectiveness(u16 move, u32 battlerAtk, u32 battlerDef);
|
uq4_12_t AI_GetTypeEffectiveness(u16 move, u32 battlerAtk, u32 battlerDef);
|
||||||
u32 AI_GetMoveEffectiveness(u16 move, u32 battlerAtk, u32 battlerDef);
|
u32 AI_GetMoveEffectiveness(u16 move, u32 battlerAtk, u32 battlerDef);
|
||||||
|
@ -59,10 +59,13 @@ void BattleTurnPassed(void);
|
|||||||
u8 IsRunningFromBattleImpossible(u32 battler);
|
u8 IsRunningFromBattleImpossible(u32 battler);
|
||||||
void SwitchPartyOrder(u8 battlerId);
|
void SwitchPartyOrder(u8 battlerId);
|
||||||
void SwapTurnOrder(u8 id1, u8 id2);
|
void SwapTurnOrder(u8 id1, u8 id2);
|
||||||
u32 GetBattlerTotalSpeedStat(u8 battlerId);
|
u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect);
|
||||||
|
u32 GetBattlerTotalSpeedStat(u32 battler);
|
||||||
s8 GetChosenMovePriority(u32 battlerId);
|
s8 GetChosenMovePriority(u32 battlerId);
|
||||||
s8 GetMovePriority(u32 battlerId, u16 move);
|
s8 GetMovePriority(u32 battlerId, u16 move);
|
||||||
u8 GetWhoStrikesFirst(u8 battlerId1, u8 battlerId2, bool8 ignoreChosenMoves);
|
u32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
|
||||||
|
u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2);
|
||||||
|
u32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves);
|
||||||
void RunBattleScriptCommands_PopCallbacksStack(void);
|
void RunBattleScriptCommands_PopCallbacksStack(void);
|
||||||
void RunBattleScriptCommands(void);
|
void RunBattleScriptCommands(void);
|
||||||
void SpecialStatusesClear(void);
|
void SpecialStatusesClear(void);
|
||||||
|
@ -167,7 +167,7 @@ u32 GetBattlerHoldEffectParam(u32 battler);
|
|||||||
bool32 IsMoveMakingContact(u32 move, u32 battlerAtk);
|
bool32 IsMoveMakingContact(u32 move, u32 battlerAtk);
|
||||||
bool32 IsBattlerGrounded(u32 battler);
|
bool32 IsBattlerGrounded(u32 battler);
|
||||||
bool32 IsBattlerAlive(u32 battler);
|
bool32 IsBattlerAlive(u32 battler);
|
||||||
u32 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u32 move);
|
u32 GetMoveSlot(u16 *moves, u32 move);
|
||||||
u32 GetBattlerWeight(u32 battler);
|
u32 GetBattlerWeight(u32 battler);
|
||||||
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer);
|
u32 CalcRolloutBasePower(u32 battlerAtk, u32 basePower, u32 rolloutTimer);
|
||||||
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
|
u32 CalcFuryCutterBasePower(u32 basePower, u32 furyCutterCounter);
|
||||||
|
@ -338,18 +338,55 @@ void Ai_UpdateFaintData(u32 battler)
|
|||||||
|
|
||||||
static void SetBattlerAiData(u32 battler)
|
static void SetBattlerAiData(u32 battler)
|
||||||
{
|
{
|
||||||
AI_DATA->abilities[battler] = AI_GetAbility(battler);
|
u32 ability, holdEffect;
|
||||||
|
|
||||||
|
ability = AI_DATA->abilities[battler] = AI_GetAbility(battler);
|
||||||
AI_DATA->items[battler] = gBattleMons[battler].item;
|
AI_DATA->items[battler] = gBattleMons[battler].item;
|
||||||
AI_DATA->holdEffects[battler] = AI_GetHoldEffect(battler);
|
holdEffect = AI_DATA->holdEffects[battler] = AI_GetHoldEffect(battler);
|
||||||
AI_DATA->holdEffectParams[battler] = GetBattlerHoldEffectParam(battler);
|
AI_DATA->holdEffectParams[battler] = GetBattlerHoldEffectParam(battler);
|
||||||
AI_DATA->predictedMoves[battler] = gLastMoves[battler];
|
AI_DATA->predictedMoves[battler] = gLastMoves[battler];
|
||||||
AI_DATA->hpPercents[battler] = GetHealthPercentage(battler);
|
AI_DATA->hpPercents[battler] = GetHealthPercentage(battler);
|
||||||
AI_DATA->moveLimitations[battler] = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL);
|
AI_DATA->moveLimitations[battler] = CheckMoveLimitations(battler, 0, MOVE_LIMITATIONS_ALL);
|
||||||
|
AI_DATA->speedStats[battler] = GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetBattlerAiMovesData(u32 battlerAtk, u32 battlersCount)
|
||||||
|
{
|
||||||
|
u32 battlerDef, i;
|
||||||
|
u16 *moves;
|
||||||
|
|
||||||
|
// Simulate dmg for both ai controlled mons and for player controlled mons.
|
||||||
|
SaveBattlerData(battlerAtk);
|
||||||
|
moves = GetMovesArray(battlerAtk);
|
||||||
|
for (battlerDef = 0; battlerDef < battlersCount; battlerDef++)
|
||||||
|
{
|
||||||
|
if (battlerAtk == battlerDef)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
SaveBattlerData(battlerDef);
|
||||||
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||||
|
{
|
||||||
|
s32 dmg = 0;
|
||||||
|
u8 effectiveness = AI_EFFECTIVENESS_x0;
|
||||||
|
u32 move = moves[i];
|
||||||
|
|
||||||
|
if (move != 0
|
||||||
|
&& move != 0xFFFF
|
||||||
|
//&& gBattleMoves[move].power != 0 /* we want to get effectiveness of status moves */
|
||||||
|
&& !(AI_DATA->moveLimitations[battlerAtk] & gBitTable[i])) {
|
||||||
|
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
AI_DATA->simulatedDmg[battlerAtk][battlerDef][i] = dmg;
|
||||||
|
AI_DATA->effectiveness[battlerAtk][battlerDef][i] = effectiveness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SetMoveDamageResult(battlerAtk, moves);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetAiLogicDataForTurn(void)
|
void SetAiLogicDataForTurn(void)
|
||||||
{
|
{
|
||||||
u32 battlerAtk, battlerDef, i, battlersCount;
|
u32 battlerAtk, battlersCount;
|
||||||
|
|
||||||
memset(AI_DATA, 0, sizeof(struct AiLogicData));
|
memset(AI_DATA, 0, sizeof(struct AiLogicData));
|
||||||
|
|
||||||
@ -366,33 +403,7 @@ void SetAiLogicDataForTurn(void)
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
SetBattlerAiData(battlerAtk);
|
SetBattlerAiData(battlerAtk);
|
||||||
if (!IsAiBattlerAware(battlerAtk))
|
SetBattlerAiMovesData(battlerAtk, battlersCount);
|
||||||
continue;
|
|
||||||
|
|
||||||
SaveBattlerData(battlerAtk);
|
|
||||||
for (battlerDef = 0; battlerDef < battlersCount; battlerDef++)
|
|
||||||
{
|
|
||||||
if (battlerAtk == battlerDef)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
SaveBattlerData(battlerDef);
|
|
||||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
||||||
{
|
|
||||||
s32 dmg = 0;
|
|
||||||
u8 effectiveness = AI_EFFECTIVENESS_x0;
|
|
||||||
u32 move = gBattleMons[battlerAtk].moves[i];
|
|
||||||
|
|
||||||
if (move != 0
|
|
||||||
&& move != 0xFFFF
|
|
||||||
//&& gBattleMoves[move].power != 0 /* we want to get effectiveness of status moves */
|
|
||||||
&& !(AI_DATA->moveLimitations[battlerAtk] & gBitTable[i])) {
|
|
||||||
dmg = AI_CalcDamage(move, battlerAtk, battlerDef, &effectiveness, TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
AI_DATA->simulatedDmg[battlerAtk][battlerDef][i] = dmg;
|
|
||||||
AI_DATA->effectiveness[battlerAtk][battlerDef][i] = effectiveness;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -715,6 +726,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
u32 i;
|
u32 i;
|
||||||
u16 predictedMove = AI_DATA->predictedMoves[battlerDef];
|
u16 predictedMove = AI_DATA->predictedMoves[battlerDef];
|
||||||
|
|
||||||
|
|
||||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||||
GET_MOVE_TYPE(move, moveType);
|
GET_MOVE_TYPE(move, moveType);
|
||||||
|
|
||||||
@ -2616,7 +2628,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
score--;
|
score--;
|
||||||
break;
|
break;
|
||||||
case EFFECT_VITAL_THROW:
|
case EFFECT_VITAL_THROW:
|
||||||
if (WillAIStrikeFirst() && AI_DATA->hpPercents[battlerAtk] < 40)
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move) && AI_DATA->hpPercents[battlerAtk] < 40)
|
||||||
score--; // don't want to move last
|
score--; // don't want to move last
|
||||||
break;
|
break;
|
||||||
case EFFECT_FLAIL:
|
case EFFECT_FLAIL:
|
||||||
@ -2688,16 +2700,20 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
|
|
||||||
static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
||||||
{
|
{
|
||||||
|
u32 movesetIndex = AI_THINKING_STRUCT->movesetIndex;
|
||||||
|
bool32 aiFaster;
|
||||||
|
|
||||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||||
return score;
|
return score;
|
||||||
|
|
||||||
if (gBattleMoves[move].power == 0)
|
if (gBattleMoves[move].power == 0)
|
||||||
return score; // can't make anything faint with no power
|
return score; // can't make anything faint with no power
|
||||||
|
|
||||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0) && gBattleMoves[move].effect != EFFECT_EXPLOSION)
|
aiFaster = AI_STRIKES_FIRST(battlerAtk, battlerDef, move);
|
||||||
|
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, movesetIndex, 0) && gBattleMoves[move].effect != EFFECT_EXPLOSION)
|
||||||
{
|
{
|
||||||
// this move can faint the target
|
// this move can faint the target
|
||||||
if (WillAIStrikeFirst() || GetMovePriority(battlerAtk, move) > 0)
|
if (aiFaster || GetMovePriority(battlerAtk, move) > 0)
|
||||||
score += 4; // we go first or we're using priority move
|
score += 4; // we go first or we're using priority move
|
||||||
else
|
else
|
||||||
score += 2;
|
score += 2;
|
||||||
@ -2708,10 +2724,10 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
if (gBattleMoves[move].highCritRatio)
|
if (gBattleMoves[move].highCritRatio)
|
||||||
score += 2; // crit makes it more likely to make them faint
|
score += 2; // crit makes it more likely to make them faint
|
||||||
|
|
||||||
if (GetMoveDamageResult(move) == MOVE_POWER_OTHER)
|
if (GetMoveDamageResult(battlerAtk, battlerDef, movesetIndex) == MOVE_POWER_OTHER)
|
||||||
score--;
|
score--;
|
||||||
|
|
||||||
switch (AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex])
|
switch (AI_DATA->effectiveness[battlerAtk][battlerDef][movesetIndex])
|
||||||
{
|
{
|
||||||
case AI_EFFECTIVENESS_x8:
|
case AI_EFFECTIVENESS_x8:
|
||||||
score += 8;
|
score += 8;
|
||||||
@ -2729,9 +2745,9 @@ static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
}
|
}
|
||||||
|
|
||||||
//AI_TryToFaint_CheckIfDanger
|
//AI_TryToFaint_CheckIfDanger
|
||||||
if (!WillAIStrikeFirst() && CanTargetFaintAi(battlerDef, battlerAtk))
|
if (!aiFaster && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||||
{ // AI_TryToFaint_Danger
|
{ // AI_TryToFaint_Danger
|
||||||
if (GetMoveDamageResult(move) != MOVE_POWER_BEST)
|
if (GetMoveDamageResult(battlerAtk, battlerDef, movesetIndex) != MOVE_POWER_BEST)
|
||||||
score--;
|
score--;
|
||||||
else
|
else
|
||||||
score++;
|
score++;
|
||||||
@ -2793,7 +2809,6 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
}
|
}
|
||||||
} // check partner move effect
|
} // check partner move effect
|
||||||
|
|
||||||
|
|
||||||
// consider our move effect relative to partner state
|
// consider our move effect relative to partner state
|
||||||
switch (effect)
|
switch (effect)
|
||||||
{
|
{
|
||||||
@ -2815,7 +2830,6 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
break;
|
break;
|
||||||
} // our effect relative to partner
|
} // our effect relative to partner
|
||||||
|
|
||||||
|
|
||||||
// consider global move effects
|
// consider global move effects
|
||||||
switch (effect)
|
switch (effect)
|
||||||
{
|
{
|
||||||
@ -2853,11 +2867,10 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
break;
|
break;
|
||||||
} // global move effect check
|
} // global move effect check
|
||||||
|
|
||||||
|
|
||||||
// check specific target
|
// check specific target
|
||||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||||
{
|
{
|
||||||
if (GetMoveDamageResult(move) == MOVE_POWER_OTHER)
|
if (GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_OTHER)
|
||||||
{
|
{
|
||||||
// partner ability checks
|
// partner ability checks
|
||||||
if (!partnerProtecting && moveTarget != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move))
|
if (!partnerProtecting && moveTarget != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move))
|
||||||
@ -2900,7 +2913,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u16 move, s32 score)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ABILITY_WATER_COMPACTION:
|
case ABILITY_WATER_COMPACTION:
|
||||||
if (moveType == TYPE_WATER && GetMoveDamageResult(move) == MOVE_POWER_WEAK)
|
if (moveType == TYPE_WATER && GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_WEAK)
|
||||||
{
|
{
|
||||||
RETURN_SCORE_PLUS(1); // only mon with this ability is weak to water so only make it okay if we do very little damage
|
RETURN_SCORE_PLUS(1); // only mon with this ability is weak to water so only make it okay if we do very little damage
|
||||||
}
|
}
|
||||||
@ -3161,6 +3174,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
u32 effectiveness = AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
u32 effectiveness = AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
|
||||||
u8 atkPriority = GetMovePriority(battlerAtk, move);
|
u8 atkPriority = GetMovePriority(battlerAtk, move);
|
||||||
u16 predictedMove = AI_DATA->predictedMoves[battlerDef];
|
u16 predictedMove = AI_DATA->predictedMoves[battlerDef];
|
||||||
|
u32 predictedMoveSlot = GetMoveSlot(GetMovesArray(battlerDef), predictedMove);
|
||||||
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
|
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
|
||||||
u32 i;
|
u32 i;
|
||||||
// We only check for moves that have a 20% chance or more for their secondary effect to happen because moves with a smaller chance are rather worthless. We don't want the AI to use those.
|
// We only check for moves that have a 20% chance or more for their secondary effect to happen because moves with a smaller chance are rather worthless. We don't want the AI to use those.
|
||||||
@ -3203,7 +3217,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check damage
|
// check damage
|
||||||
if (gBattleMoves[move].power != 0 && GetMoveDamageResult(move) == MOVE_POWER_WEAK)
|
if (gBattleMoves[move].power != 0 && GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_WEAK)
|
||||||
score--;
|
score--;
|
||||||
|
|
||||||
// check status move preference
|
// check status move preference
|
||||||
@ -3334,7 +3348,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
break;
|
break;
|
||||||
case EFFECT_SPEED_UP:
|
case EFFECT_SPEED_UP:
|
||||||
case EFFECT_SPEED_UP_2:
|
case EFFECT_SPEED_UP_2:
|
||||||
if (!WillAIStrikeFirst())
|
if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
|
||||||
{
|
{
|
||||||
if (!AI_RandLessThan(70))
|
if (!AI_RandLessThan(70))
|
||||||
score += 3;
|
score += 3;
|
||||||
@ -3432,7 +3446,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
break;
|
break;
|
||||||
case EFFECT_SPEED_DOWN:
|
case EFFECT_SPEED_DOWN:
|
||||||
case EFFECT_SPEED_DOWN_2:
|
case EFFECT_SPEED_DOWN_2:
|
||||||
if (WillAIStrikeFirst())
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
|
||||||
score -= 3;
|
score -= 3;
|
||||||
else if (!AI_RandLessThan(70))
|
else if (!AI_RandLessThan(70))
|
||||||
score += 2;
|
score += 2;
|
||||||
@ -3675,7 +3689,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
score += 2;
|
score += 2;
|
||||||
break;
|
break;
|
||||||
case EFFECT_SPEED_DOWN_HIT:
|
case EFFECT_SPEED_DOWN_HIT:
|
||||||
if (WillAIStrikeFirst())
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
|
||||||
score -= 2;
|
score -= 2;
|
||||||
else if (!AI_RandLessThan(70))
|
else if (!AI_RandLessThan(70))
|
||||||
score++;
|
score++;
|
||||||
@ -3819,7 +3833,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
score++;
|
score++;
|
||||||
break;
|
break;
|
||||||
case EFFECT_SPEED_UP_HIT:
|
case EFFECT_SPEED_UP_HIT:
|
||||||
if (sereneGraceBoost && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY && !WillAIStrikeFirst())
|
if (sereneGraceBoost && AI_DATA->abilities[battlerDef] != ABILITY_CONTRARY && !AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
|
||||||
score += 3;
|
score += 3;
|
||||||
break;
|
break;
|
||||||
case EFFECT_DESTINY_BOND:
|
case EFFECT_DESTINY_BOND:
|
||||||
@ -4432,7 +4446,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
if (IsStatBoostingBerry(item) && AI_DATA->hpPercents[battlerAtk] > 60)
|
if (IsStatBoostingBerry(item) && AI_DATA->hpPercents[battlerAtk] > 60)
|
||||||
score++;
|
score++;
|
||||||
else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0)
|
else if (ShouldRestoreHpBerry(battlerAtk, item) && !CanAIFaintTarget(battlerAtk, battlerDef, 0)
|
||||||
&& ((GetWhoStrikesFirst(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0))
|
&& ((GetWhichBattlerFaster(battlerAtk, battlerDef, TRUE) == 0 && CanTargetFaintAiWithMod(battlerDef, battlerAtk, 0, 0))
|
||||||
|| !CanTargetFaintAiWithMod(battlerDef, battlerAtk, toHeal, 0)))
|
|| !CanTargetFaintAiWithMod(battlerDef, battlerAtk, toHeal, 0)))
|
||||||
score++; // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry
|
score++; // Recycle healing berry if we can't otherwise faint the target and the target wont kill us after we activate the berry
|
||||||
}
|
}
|
||||||
@ -4865,7 +4879,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
{
|
{
|
||||||
if (gDisableStructs[battlerDef].tauntTimer != 0)
|
if (gDisableStructs[battlerDef].tauntTimer != 0)
|
||||||
score++; // target must use damaging move
|
score++; // target must use damaging move
|
||||||
if (GetMoveDamageResult(predictedMove) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_PHYSICAL)
|
if (GetMoveDamageResult(battlerDef, battlerAtk, predictedMoveSlot) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_PHYSICAL)
|
||||||
score += 3;
|
score += 3;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -4874,7 +4888,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u16 move, s32 score
|
|||||||
{
|
{
|
||||||
if (gDisableStructs[battlerDef].tauntTimer != 0)
|
if (gDisableStructs[battlerDef].tauntTimer != 0)
|
||||||
score++; // target must use damaging move
|
score++; // target must use damaging move
|
||||||
if (GetMoveDamageResult(predictedMove) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_SPECIAL)
|
if (GetMoveDamageResult(battlerDef, battlerAtk, predictedMoveSlot) >= MOVE_POWER_GOOD && GetBattleMoveSplit(predictedMove) == SPLIT_SPECIAL)
|
||||||
score += 3;
|
score += 3;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -5111,7 +5125,7 @@ static s32 AI_PreferStrongestMove(u32 battlerAtk, u32 battlerDef, u16 move, s32
|
|||||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||||
return score;
|
return score;
|
||||||
|
|
||||||
if (GetMoveDamageResult(move) == MOVE_POWER_BEST)
|
if (GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == MOVE_POWER_BEST)
|
||||||
score += 2;
|
score += 2;
|
||||||
|
|
||||||
return score;
|
return score;
|
||||||
@ -5124,7 +5138,7 @@ static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u16 move, s32 scor
|
|||||||
|
|
||||||
if (IsTargetingPartner(battlerAtk, battlerDef)
|
if (IsTargetingPartner(battlerAtk, battlerDef)
|
||||||
|| CountUsablePartyMons(battlerAtk) == 0
|
|| CountUsablePartyMons(battlerAtk) == 0
|
||||||
|| GetMoveDamageResult(move) != MOVE_POWER_OTHER
|
|| GetMoveDamageResult(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) != MOVE_POWER_OTHER
|
||||||
|| !HasMoveEffect(battlerAtk, EFFECT_BATON_PASS)
|
|| !HasMoveEffect(battlerAtk, EFFECT_BATON_PASS)
|
||||||
|| IsBattlerTrapped(battlerAtk, TRUE))
|
|| IsBattlerTrapped(battlerAtk, TRUE))
|
||||||
return score;
|
return score;
|
||||||
|
@ -400,7 +400,7 @@ static bool8 ShouldSwitchIfGameStatePrompt(u32 battler)
|
|||||||
&& AnyStatIsRaised(battler))
|
&& AnyStatIsRaised(battler))
|
||||||
switchMon = FALSE;
|
switchMon = FALSE;
|
||||||
if (AiExpectsToFaintPlayer(battler)
|
if (AiExpectsToFaintPlayer(battler)
|
||||||
&& !WillAIStrikeFirst()
|
&& !AI_STRIKES_FIRST(battler, opposingBattler, 0)
|
||||||
&& !AI_OpponentCanFaintAiWithMod(battler, 0))
|
&& !AI_OpponentCanFaintAiWithMod(battler, 0))
|
||||||
switchMon = FALSE;
|
switchMon = FALSE;
|
||||||
}
|
}
|
||||||
|
@ -424,11 +424,6 @@ u16 GetAIChosenMove(u8 battlerId)
|
|||||||
return (gBattleMons[battlerId].moves[gBattleStruct->aiMoveOrAction[battlerId]]);
|
return (gBattleMons[battlerId].moves[gBattleStruct->aiMoveOrAction[battlerId]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool32 WillAIStrikeFirst(void)
|
|
||||||
{
|
|
||||||
return (AI_WhoStrikesFirst(sBattler_AI, gBattlerTarget, AI_THINKING_STRUCT->moveConsidered) == AI_IS_FASTER);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool32 AI_RandLessThan(u8 val)
|
bool32 AI_RandLessThan(u8 val)
|
||||||
{
|
{
|
||||||
if ((Random() % 0xFF) < val)
|
if ((Random() % 0xFF) < val)
|
||||||
@ -945,85 +940,91 @@ u32 GetNoOfHitsToKO(u32 dmg, s32 hp)
|
|||||||
return hp / (dmg + 1) + 1;
|
return hp / (dmg + 1) + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 GetMoveDamageResult(u16 move)
|
void SetMoveDamageResult(u32 battlerAtk, u16 *moves)
|
||||||
{
|
{
|
||||||
s32 i, checkedMove, bestId, currId, hp;
|
s32 i, j, battlerDef, bestId, currId, hp, result;
|
||||||
s32 moveDmgs[MAX_MON_MOVES];
|
s32 moveDmgs[MAX_MON_MOVES];
|
||||||
u8 result;
|
bool8 isNotConsidered[MAX_MON_MOVES];
|
||||||
|
|
||||||
for (i = 0; sIgnoredPowerfulMoveEffects[i] != IGNORED_MOVES_END; i++)
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||||
{
|
{
|
||||||
if (gBattleMoves[move].effect == sIgnoredPowerfulMoveEffects[i])
|
u16 move = moves[i];
|
||||||
break;
|
for (j = 0; sIgnoredPowerfulMoveEffects[j] != IGNORED_MOVES_END; j++)
|
||||||
}
|
|
||||||
|
|
||||||
if (gBattleMoves[move].power != 0 && sIgnoredPowerfulMoveEffects[i] == IGNORED_MOVES_END)
|
|
||||||
{
|
|
||||||
// Considered move has power and is not in sIgnoredPowerfulMoveEffects
|
|
||||||
// Check all other moves and calculate their power
|
|
||||||
for (checkedMove = 0; checkedMove < MAX_MON_MOVES; checkedMove++)
|
|
||||||
{
|
{
|
||||||
for (i = 0; sIgnoredPowerfulMoveEffects[i] != IGNORED_MOVES_END; i++)
|
if (gBattleMoves[move].effect == sIgnoredPowerfulMoveEffects[j])
|
||||||
{
|
break;
|
||||||
if (gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].effect == sIgnoredPowerfulMoveEffects[i])
|
}
|
||||||
break;
|
if (move == 0 || move == 0xFFFF || gBattleMoves[move].power == 0 || sIgnoredPowerfulMoveEffects[j] != IGNORED_MOVES_END)
|
||||||
}
|
isNotConsidered[i] = TRUE;
|
||||||
|
else
|
||||||
|
isNotConsidered[i] = FALSE;
|
||||||
|
|
||||||
if (gBattleMons[sBattler_AI].moves[checkedMove] != MOVE_NONE
|
for (battlerDef = 0; battlerDef < MAX_BATTLERS_COUNT; battlerDef++)
|
||||||
&& sIgnoredPowerfulMoveEffects[i] == IGNORED_MOVES_END
|
{
|
||||||
&& gBattleMoves[gBattleMons[sBattler_AI].moves[checkedMove]].power != 0)
|
if (battlerDef == battlerAtk)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (isNotConsidered[i])
|
||||||
{
|
{
|
||||||
moveDmgs[checkedMove] = AI_DATA->simulatedDmg[sBattler_AI][gBattlerTarget][checkedMove];
|
AI_DATA->moveDmgResult[battlerAtk][battlerDef][i] = MOVE_POWER_OTHER; // Move has a power of 0/1, or is in the group sIgnoredPowerfulMoveEffects
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
moveDmgs[checkedMove] = 0;
|
// Considered move has power and is not in sIgnoredPowerfulMoveEffects
|
||||||
}
|
// Check all other moves and calculate their power
|
||||||
}
|
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||||
|
|
||||||
hp = gBattleMons[gBattlerTarget].hp + (20 * gBattleMons[gBattlerTarget].hp / 100); // 20 % add to make sure the battler is always fainted
|
|
||||||
// If a move can faint battler, it doesn't matter how much damage it does
|
|
||||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
|
||||||
{
|
|
||||||
if (moveDmgs[i] > hp)
|
|
||||||
moveDmgs[i] = hp;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (bestId = 0, i = 1; i < MAX_MON_MOVES; i++)
|
|
||||||
{
|
|
||||||
if (moveDmgs[i] > moveDmgs[bestId])
|
|
||||||
bestId = i;
|
|
||||||
if (moveDmgs[i] == moveDmgs[bestId])
|
|
||||||
{
|
|
||||||
switch (WhichMoveBetter(gBattleMons[sBattler_AI].moves[bestId], gBattleMons[sBattler_AI].moves[i]))
|
|
||||||
{
|
{
|
||||||
case 2:
|
if (!isNotConsidered[j])
|
||||||
if (Random() & 1)
|
moveDmgs[j] = AI_DATA->simulatedDmg[battlerAtk][battlerDef][j];
|
||||||
break;
|
else
|
||||||
case 1:
|
moveDmgs[j] = 0;
|
||||||
bestId = i;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hp = gBattleMons[battlerDef].hp + (20 * gBattleMons[battlerDef].hp / 100); // 20 % add to make sure the battler is always fainted
|
||||||
|
// If a move can faint battler, it doesn't matter how much damage it does
|
||||||
|
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||||
|
{
|
||||||
|
if (moveDmgs[j] > hp)
|
||||||
|
moveDmgs[j] = hp;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (bestId = 0, j = 1; j < MAX_MON_MOVES; j++)
|
||||||
|
{
|
||||||
|
if (moveDmgs[j] > moveDmgs[bestId])
|
||||||
|
bestId = j;
|
||||||
|
if (moveDmgs[j] == moveDmgs[bestId])
|
||||||
|
{
|
||||||
|
switch (WhichMoveBetter(gBattleMons[battlerAtk].moves[bestId], gBattleMons[battlerAtk].moves[j]))
|
||||||
|
{
|
||||||
|
case 2:
|
||||||
|
if (Random() & 1)
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
bestId = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currId = i;
|
||||||
|
if (currId == bestId)
|
||||||
|
result = MOVE_POWER_BEST;
|
||||||
|
else if ((moveDmgs[currId] >= hp || moveDmgs[bestId] < hp) // If current move can faint as well, or if neither can
|
||||||
|
&& GetNoOfHitsToKO(moveDmgs[currId], hp) - GetNoOfHitsToKO(moveDmgs[bestId], hp) <= 2 // Consider a move weak if it needs to be used at least 2 times more to faint the target, compared to the best move.
|
||||||
|
&& WhichMoveBetter(gBattleMons[battlerAtk].moves[bestId], gBattleMons[battlerAtk].moves[currId]) != 0)
|
||||||
|
result = MOVE_POWER_GOOD;
|
||||||
|
else
|
||||||
|
result = MOVE_POWER_WEAK;
|
||||||
|
|
||||||
|
AI_DATA->moveDmgResult[battlerAtk][battlerDef][i] = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currId = AI_THINKING_STRUCT->movesetIndex;
|
|
||||||
if (currId == bestId)
|
|
||||||
AI_THINKING_STRUCT->funcResult = MOVE_POWER_BEST;
|
|
||||||
else if ((moveDmgs[currId] >= hp || moveDmgs[bestId] < hp) // If current move can faint as well, or if neither can
|
|
||||||
&& GetNoOfHitsToKO(moveDmgs[currId], hp) - GetNoOfHitsToKO(moveDmgs[bestId], hp) <= 2 // Consider a move weak if it needs to be used at least 2 times more to faint the target, compared to the best move.
|
|
||||||
&& WhichMoveBetter(gBattleMons[sBattler_AI].moves[bestId], gBattleMons[sBattler_AI].moves[currId]) != 0)
|
|
||||||
AI_THINKING_STRUCT->funcResult = MOVE_POWER_GOOD;
|
|
||||||
else
|
|
||||||
AI_THINKING_STRUCT->funcResult = MOVE_POWER_WEAK;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Move has a power of 0/1, or is in the group sIgnoredPowerfulMoveEffects
|
|
||||||
AI_THINKING_STRUCT->funcResult = MOVE_POWER_OTHER;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return AI_THINKING_STRUCT->funcResult;
|
u32 GetMoveDamageResult(u32 battlerAtk, u32 battlerDef, u32 moveIndex)
|
||||||
|
{
|
||||||
|
return AI_DATA->moveDmgResult[battlerAtk][battlerDef][moveIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef)
|
u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef)
|
||||||
@ -1090,7 +1091,7 @@ static u32 AI_GetEffectiveness(uq4_12_t multiplier)
|
|||||||
* AI_IS_FASTER: is user(ai) faster
|
* AI_IS_FASTER: is user(ai) faster
|
||||||
* AI_IS_SLOWER: is target faster
|
* AI_IS_SLOWER: is target faster
|
||||||
*/
|
*/
|
||||||
u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 moveConsidered)
|
u32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered)
|
||||||
{
|
{
|
||||||
u32 fasterAI = 0, fasterPlayer = 0, i;
|
u32 fasterAI = 0, fasterPlayer = 0, i;
|
||||||
s8 prioAI = 0;
|
s8 prioAI = 0;
|
||||||
@ -1126,7 +1127,11 @@ u8 AI_WhoStrikesFirst(u8 battlerAI, u8 battler2, u16 moveConsidered)
|
|||||||
if (prioAI > prioBattler2)
|
if (prioAI > prioBattler2)
|
||||||
return AI_IS_FASTER; // if we didn't know any of battler 2's moves to compare priorities, assume they don't have a prio+ move
|
return AI_IS_FASTER; // if we didn't know any of battler 2's moves to compare priorities, assume they don't have a prio+ move
|
||||||
// Priorities are the same(at least comparing to moves the AI is aware of), decide by speed.
|
// Priorities are the same(at least comparing to moves the AI is aware of), decide by speed.
|
||||||
if (GetWhoStrikesFirst(battlerAI, battler2, TRUE) == 0)
|
if (GetWhichBattlerFasterArgs(battlerAI, battler2, TRUE,
|
||||||
|
AI_DATA->abilities[battlerAI], AI_DATA->abilities[battler2],
|
||||||
|
AI_DATA->holdEffects[battlerAI], AI_DATA->holdEffects[battler2],
|
||||||
|
AI_DATA->speedStats[battlerAI], AI_DATA->speedStats[battler2],
|
||||||
|
prioAI, prioBattler2) == 0)
|
||||||
return AI_IS_FASTER;
|
return AI_IS_FASTER;
|
||||||
else
|
else
|
||||||
return AI_IS_SLOWER;
|
return AI_IS_SLOWER;
|
||||||
@ -1795,7 +1800,7 @@ u32 CountNegativeStatStages(u8 battlerId)
|
|||||||
|
|
||||||
bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
||||||
{
|
{
|
||||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||||
|
|
||||||
if (gBattleMons[battlerDef].statStages[STAT_ATK] > 4
|
if (gBattleMons[battlerDef].statStages[STAT_ATK] > 4
|
||||||
@ -1812,7 +1817,7 @@ bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
|||||||
|
|
||||||
bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
||||||
{
|
{
|
||||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||||
|
|
||||||
if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4
|
if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4
|
||||||
@ -1829,10 +1834,10 @@ bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
|||||||
|
|
||||||
bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
||||||
{
|
{
|
||||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||||
|
|
||||||
if (!WillAIStrikeFirst()
|
if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
|
||||||
&& defAbility != ABILITY_CONTRARY
|
&& defAbility != ABILITY_CONTRARY
|
||||||
&& defAbility != ABILITY_CLEAR_BODY
|
&& defAbility != ABILITY_CLEAR_BODY
|
||||||
&& defAbility != ABILITY_FULL_METAL_BODY
|
&& defAbility != ABILITY_FULL_METAL_BODY
|
||||||
@ -1844,7 +1849,7 @@ bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
|||||||
|
|
||||||
bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
||||||
{
|
{
|
||||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||||
|
|
||||||
if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 4
|
if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 4
|
||||||
@ -1860,7 +1865,7 @@ bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
|||||||
|
|
||||||
bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
||||||
{
|
{
|
||||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||||
|
|
||||||
if (gBattleMons[battlerDef].statStages[STAT_SPDEF] > 4
|
if (gBattleMons[battlerDef].statStages[STAT_SPDEF] > 4
|
||||||
@ -1876,7 +1881,7 @@ bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
|||||||
|
|
||||||
bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
||||||
{
|
{
|
||||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||||
|
|
||||||
if (defAbility != ABILITY_CONTRARY
|
if (defAbility != ABILITY_CONTRARY
|
||||||
@ -1891,7 +1896,7 @@ bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
|||||||
|
|
||||||
bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u16 defAbility)
|
||||||
{
|
{
|
||||||
if (WillAIStrikeFirst() && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||||
return FALSE; // Don't bother lowering stats if can kill enemy.
|
return FALSE; // Don't bother lowering stats if can kill enemy.
|
||||||
|
|
||||||
if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE
|
if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE
|
||||||
@ -3637,7 +3642,7 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u8 statId, s32 *score)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case STAT_SPEED:
|
case STAT_SPEED:
|
||||||
if (!WillAIStrikeFirst())
|
if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered))
|
||||||
{
|
{
|
||||||
if (gBattleMons[battlerAtk].statStages[STAT_SPEED] < STAT_UP_2_STAGE)
|
if (gBattleMons[battlerAtk].statStages[STAT_SPEED] < STAT_UP_2_STAGE)
|
||||||
*score += 2;
|
*score += 2;
|
||||||
|
@ -3771,7 +3771,7 @@ static void TryDoEventsBeforeFirstTurn(void)
|
|||||||
{
|
{
|
||||||
for (j = i + 1; j < gBattlersCount; j++)
|
for (j = i + 1; j < gBattlersCount; j++)
|
||||||
{
|
{
|
||||||
if (GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], TRUE) != 0)
|
if (GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], TRUE) != 0)
|
||||||
SwapTurnOrder(i, j);
|
SwapTurnOrder(i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4598,11 +4598,10 @@ void SwapTurnOrder(u8 id1, u8 id2)
|
|||||||
SWAP(gBattlerByTurnOrder[id1], gBattlerByTurnOrder[id2], temp);
|
SWAP(gBattlerByTurnOrder[id1], gBattlerByTurnOrder[id2], temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetBattlerTotalSpeedStat(u8 battler)
|
// For AI, so it doesn't 'cheat' by knowing player's ability
|
||||||
|
u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect)
|
||||||
{
|
{
|
||||||
u32 speed = gBattleMons[battler].speed;
|
u32 speed = gBattleMons[battler].speed;
|
||||||
u32 ability = GetBattlerAbility(battler);
|
|
||||||
u32 holdEffect = GetBattlerHoldEffect(battler, TRUE);
|
|
||||||
u32 highestStat = GetHighestStatId(battler);
|
u32 highestStat = GetHighestStatId(battler);
|
||||||
|
|
||||||
// weather abilities
|
// weather abilities
|
||||||
@ -4669,6 +4668,13 @@ u32 GetBattlerTotalSpeedStat(u8 battler)
|
|||||||
return speed;
|
return speed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetBattlerTotalSpeedStat(u32 battler)
|
||||||
|
{
|
||||||
|
u32 ability = GetBattlerAbility(battler);
|
||||||
|
u32 holdEffect = GetBattlerHoldEffect(battler, TRUE);
|
||||||
|
return GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect);
|
||||||
|
}
|
||||||
|
|
||||||
s8 GetChosenMovePriority(u32 battler)
|
s8 GetChosenMovePriority(u32 battler)
|
||||||
{
|
{
|
||||||
u16 move;
|
u16 move;
|
||||||
@ -4733,17 +4739,13 @@ s8 GetMovePriority(u32 battler, u16 move)
|
|||||||
return priority;
|
return priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
|
// Function for AI with variables provided as arguments to speed the computation time
|
||||||
|
u32 GetWhichBattlerFasterArgs(u32 battler1, u32 battler2, bool32 ignoreChosenMoves, u32 ability1, u32 ability2,
|
||||||
|
u32 holdEffectBattler1, u32 holdEffectBattler2, u32 speedBattler1, u32 speedBattler2, s32 priority1, s32 priority2)
|
||||||
{
|
{
|
||||||
u8 strikesFirst = 0;
|
u32 strikesFirst = 0;
|
||||||
u32 speedBattler1 = 0, speedBattler2 = 0;
|
|
||||||
u32 holdEffectBattler1 = 0, holdEffectBattler2 = 0;
|
|
||||||
s8 priority1 = 0, priority2 = 0;
|
|
||||||
u16 ability1 = GetBattlerAbility(battler1), ability2 = GetBattlerAbility(battler2);
|
|
||||||
|
|
||||||
// Battler 1
|
// Battler 1
|
||||||
speedBattler1 = GetBattlerTotalSpeedStat(battler1);
|
|
||||||
holdEffectBattler1 = GetBattlerHoldEffect(battler1, TRUE);
|
|
||||||
// Quick Draw
|
// Quick Draw
|
||||||
if (!ignoreChosenMoves && ability1 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler1]) && Random() % 100 < 30)
|
if (!ignoreChosenMoves && ability1 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler1]) && Random() % 100 < 30)
|
||||||
gProtectStructs[battler1].quickDraw = TRUE;
|
gProtectStructs[battler1].quickDraw = TRUE;
|
||||||
@ -4754,8 +4756,6 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
|
|||||||
gProtectStructs[battler1].usedCustapBerry = TRUE;
|
gProtectStructs[battler1].usedCustapBerry = TRUE;
|
||||||
|
|
||||||
// Battler 2
|
// Battler 2
|
||||||
speedBattler2 = GetBattlerTotalSpeedStat(battler2);
|
|
||||||
holdEffectBattler2 = GetBattlerHoldEffect(battler2, TRUE);
|
|
||||||
// Quick Draw
|
// Quick Draw
|
||||||
if (!ignoreChosenMoves && ability2 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler2]) && Random() % 100 < 30)
|
if (!ignoreChosenMoves && ability2 == ABILITY_QUICK_DRAW && !IS_MOVE_STATUS(gChosenMoveByBattler[battler2]) && Random() % 100 < 30)
|
||||||
gProtectStructs[battler2].quickDraw = TRUE;
|
gProtectStructs[battler2].quickDraw = TRUE;
|
||||||
@ -4765,14 +4765,6 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
|
|||||||
|| (holdEffectBattler2 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler2, 4, gBattleMons[battler2].item))))
|
|| (holdEffectBattler2 == HOLD_EFFECT_CUSTAP_BERRY && HasEnoughHpToEatBerry(battler2, 4, gBattleMons[battler2].item))))
|
||||||
gProtectStructs[battler2].usedCustapBerry = TRUE;
|
gProtectStructs[battler2].usedCustapBerry = TRUE;
|
||||||
|
|
||||||
if (!ignoreChosenMoves)
|
|
||||||
{
|
|
||||||
if (gChosenActionByBattler[battler1] == B_ACTION_USE_MOVE)
|
|
||||||
priority1 = GetChosenMovePriority(battler1);
|
|
||||||
if (gChosenActionByBattler[battler2] == B_ACTION_USE_MOVE)
|
|
||||||
priority2 = GetChosenMovePriority(battler2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (priority1 == priority2)
|
if (priority1 == priority2)
|
||||||
{
|
{
|
||||||
// QUICK CLAW / CUSTAP - always first
|
// QUICK CLAW / CUSTAP - always first
|
||||||
@ -4835,6 +4827,28 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
|
|||||||
return strikesFirst;
|
return strikesFirst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 GetWhichBattlerFaster(u32 battler1, u32 battler2, bool32 ignoreChosenMoves)
|
||||||
|
{
|
||||||
|
s32 priority1 = 0, priority2 = 0;
|
||||||
|
u32 ability1 = GetBattlerAbility(battler1);
|
||||||
|
u32 speedBattler1 = GetBattlerTotalSpeedStat(battler1);
|
||||||
|
u32 holdEffectBattler1 = GetBattlerHoldEffect(battler1, TRUE);
|
||||||
|
u32 speedBattler2 = GetBattlerTotalSpeedStat(battler2);
|
||||||
|
u32 holdEffectBattler2 = GetBattlerHoldEffect(battler2, TRUE);
|
||||||
|
u32 ability2 = GetBattlerAbility(battler2);
|
||||||
|
|
||||||
|
if (!ignoreChosenMoves)
|
||||||
|
{
|
||||||
|
if (gChosenActionByBattler[battler1] == B_ACTION_USE_MOVE)
|
||||||
|
priority1 = GetChosenMovePriority(battler1);
|
||||||
|
if (gChosenActionByBattler[battler2] == B_ACTION_USE_MOVE)
|
||||||
|
priority2 = GetChosenMovePriority(battler2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetWhichBattlerFasterArgs(battler1, battler2, ignoreChosenMoves, ability1, ability2,
|
||||||
|
holdEffectBattler1, holdEffectBattler2, speedBattler1, speedBattler2, priority1, priority2);
|
||||||
|
}
|
||||||
|
|
||||||
static void SetActionsAndBattlersTurnOrder(void)
|
static void SetActionsAndBattlersTurnOrder(void)
|
||||||
{
|
{
|
||||||
s32 turnOrderId = 0;
|
s32 turnOrderId = 0;
|
||||||
@ -4928,7 +4942,7 @@ static void SetActionsAndBattlersTurnOrder(void)
|
|||||||
&& gActionsByTurnOrder[i] != B_ACTION_THROW_BALL
|
&& gActionsByTurnOrder[i] != B_ACTION_THROW_BALL
|
||||||
&& gActionsByTurnOrder[j] != B_ACTION_THROW_BALL)
|
&& gActionsByTurnOrder[j] != B_ACTION_THROW_BALL)
|
||||||
{
|
{
|
||||||
if (GetWhoStrikesFirst(battler1, battler2, FALSE))
|
if (GetWhichBattlerFaster(battler1, battler2, FALSE))
|
||||||
SwapTurnOrder(i, j);
|
SwapTurnOrder(i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -5092,7 +5106,7 @@ static void TryChangeTurnOrder(void)
|
|||||||
if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE
|
if (gActionsByTurnOrder[i] == B_ACTION_USE_MOVE
|
||||||
&& gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)
|
&& gActionsByTurnOrder[j] == B_ACTION_USE_MOVE)
|
||||||
{
|
{
|
||||||
if (GetWhoStrikesFirst(battler1, battler2, FALSE))
|
if (GetWhichBattlerFaster(battler1, battler2, FALSE))
|
||||||
SwapTurnOrder(i, j);
|
SwapTurnOrder(i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -875,12 +875,12 @@ void HandleAction_ActionFinished(void)
|
|||||||
// have been executed before. The only recalculation needed is for moves/switch. Mega evolution is handled in src/battle_main.c/TryChangeOrder
|
// have been executed before. The only recalculation needed is for moves/switch. Mega evolution is handled in src/battle_main.c/TryChangeOrder
|
||||||
if((gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE))
|
if((gActionsByTurnOrder[i] == B_ACTION_USE_MOVE && gActionsByTurnOrder[j] == B_ACTION_USE_MOVE))
|
||||||
{
|
{
|
||||||
if (GetWhoStrikesFirst(battler1, battler2, FALSE))
|
if (GetWhichBattlerFaster(battler1, battler2, FALSE))
|
||||||
SwapTurnOrder(i, j);
|
SwapTurnOrder(i, j);
|
||||||
}
|
}
|
||||||
else if ((gActionsByTurnOrder[i] == B_ACTION_SWITCH && gActionsByTurnOrder[j] == B_ACTION_SWITCH))
|
else if ((gActionsByTurnOrder[i] == B_ACTION_SWITCH && gActionsByTurnOrder[j] == B_ACTION_SWITCH))
|
||||||
{
|
{
|
||||||
if (GetWhoStrikesFirst(battler1, battler2, TRUE)) // If the actions chosen are switching, we recalc order but ignoring the moves
|
if (GetWhichBattlerFaster(battler1, battler2, TRUE)) // If the actions chosen are switching, we recalc order but ignoring the moves
|
||||||
SwapTurnOrder(i, j);
|
SwapTurnOrder(i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2043,7 +2043,7 @@ u8 DoFieldEndTurnEffects(void)
|
|||||||
{
|
{
|
||||||
if (!gProtectStructs[i].quash
|
if (!gProtectStructs[i].quash
|
||||||
&& !gProtectStructs[j].quash
|
&& !gProtectStructs[j].quash
|
||||||
&& GetWhoStrikesFirst(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE))
|
&& GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE))
|
||||||
SwapTurnOrder(i, j);
|
SwapTurnOrder(i, j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8259,13 +8259,13 @@ bool32 IsBattlerAlive(u32 battler)
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 GetBattleMonMoveSlot(struct BattlePokemon *battleMon, u32 move)
|
u32 GetMoveSlot(u16 *moves, u32 move)
|
||||||
{
|
{
|
||||||
u32 i;
|
u32 i;
|
||||||
|
|
||||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||||
{
|
{
|
||||||
if (battleMon->moves[i] == move)
|
if (moves[i] == move)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return i;
|
return i;
|
||||||
@ -8555,8 +8555,8 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3
|
|||||||
basePower *= 2;
|
basePower *= 2;
|
||||||
break;
|
break;
|
||||||
case EFFECT_TRUMP_CARD:
|
case EFFECT_TRUMP_CARD:
|
||||||
i = GetBattleMonMoveSlot(&gBattleMons[battlerAtk], move);
|
i = GetMoveSlot(gBattleMons[battlerAtk].moves, move);
|
||||||
if (i != 4)
|
if (i != MAX_MON_MOVES)
|
||||||
{
|
{
|
||||||
if (gBattleMons[battlerAtk].pp[i] >= ARRAY_COUNT(sTrumpCardPowerTable))
|
if (gBattleMons[battlerAtk].pp[i] >= ARRAY_COUNT(sTrumpCardPowerTable))
|
||||||
basePower = sTrumpCardPowerTable[ARRAY_COUNT(sTrumpCardPowerTable) - 1];
|
basePower = sTrumpCardPowerTable[ARRAY_COUNT(sTrumpCardPowerTable) - 1];
|
||||||
|
Loading…
Reference in New Issue
Block a user