mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-11-16 19:47:35 +01:00
Merge pull request #2238 from DizzyEggg/multi
Wild multi battles and doubles 2 vs 1 interface
This commit is contained in:
commit
f736766c94
@ -72,6 +72,7 @@ enum
|
||||
HEALTHBOX_SAFARI_BALLS_TEXT
|
||||
};
|
||||
|
||||
u32 WhichBattleCoords(u32 battlerId);
|
||||
u8 GetMegaIndicatorSpriteId(u32 healthboxSpriteId);
|
||||
u8 CreateBattlerHealthboxSprites(u8 battler);
|
||||
u8 CreateSafariPlayerHealthboxSprites(void);
|
||||
|
@ -81,7 +81,7 @@
|
||||
|
||||
#define WILD_DOUBLE_BATTLE ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER))))
|
||||
#define BATTLE_TWO_VS_ONE_OPPONENT ((gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gTrainerBattleOpponent_B == 0xFFFF))
|
||||
#define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER)
|
||||
#define BATTLE_TYPE_HAS_AI (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER | BATTLE_TYPE_INGAME_PARTNER)
|
||||
|
||||
|
||||
// Battle Outcome defines
|
||||
|
@ -130,10 +130,10 @@ static u32 GetWildAiFlags(void)
|
||||
{
|
||||
u8 avgLevel = GetMonData(&gEnemyParty[0], MON_DATA_LEVEL);
|
||||
u32 flags;
|
||||
|
||||
|
||||
if (IsDoubleBattle())
|
||||
avgLevel = (GetMonData(&gEnemyParty[0], MON_DATA_LEVEL) + GetMonData(&gEnemyParty[1], MON_DATA_LEVEL)) / 2;
|
||||
|
||||
|
||||
flags |= AI_FLAG_CHECK_BAD_MOVE;
|
||||
if (avgLevel >= 20)
|
||||
flags |= AI_FLAG_CHECK_VIABILITY;
|
||||
@ -141,10 +141,10 @@ static u32 GetWildAiFlags(void)
|
||||
flags |= AI_FLAG_PREFER_STRONGEST_MOVE;
|
||||
if (avgLevel >= 80)
|
||||
flags |= AI_FLAG_HP_AWARE;
|
||||
|
||||
|
||||
if (B_VAR_WILD_AI_FLAGS != 0 && VarGet(B_VAR_WILD_AI_FLAGS) != 0)
|
||||
flags |= VarGet(B_VAR_WILD_AI_FLAGS);
|
||||
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
@ -166,7 +166,7 @@ void BattleAI_SetupFlags(void)
|
||||
AI_THINKING_STRUCT->aiFlags = gTrainers[gTrainerBattleOpponent_A].aiFlags | gTrainers[gTrainerBattleOpponent_B].aiFlags;
|
||||
else
|
||||
AI_THINKING_STRUCT->aiFlags = gTrainers[gTrainerBattleOpponent_A].aiFlags;
|
||||
|
||||
|
||||
// check smart wild AI
|
||||
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER)) && IsWildMonSmart())
|
||||
AI_THINKING_STRUCT->aiFlags |= GetWildAiFlags();
|
||||
@ -220,11 +220,11 @@ u8 BattleAI_ChooseMoveOrAction(void)
|
||||
ret = ChooseMoveOrAction_Singles();
|
||||
else
|
||||
ret = ChooseMoveOrAction_Doubles();
|
||||
|
||||
|
||||
// Clear protect structures, some flags may be set during AI calcs
|
||||
// e.g. pranksterElevated from GetMovePriority
|
||||
memset(&gProtectStructs, 0, MAX_BATTLERS_COUNT * sizeof(struct ProtectStruct));
|
||||
|
||||
|
||||
gCurrentMove = savedCurrentMove;
|
||||
return ret;
|
||||
}
|
||||
@ -253,13 +253,13 @@ void GetAiLogicData(void)
|
||||
u32 battlerAtk, battlerDef, i, move;
|
||||
u8 effectiveness;
|
||||
s32 dmg;
|
||||
|
||||
|
||||
memset(AI_DATA, 0, sizeof(struct AiLogicData));
|
||||
|
||||
|
||||
if (!(gBattleTypeFlags & (BATTLE_TYPE_TRAINER | BATTLE_TYPE_FIRST_BATTLE | BATTLE_TYPE_SAFARI | BATTLE_TYPE_ROAMER))
|
||||
&& !IsWildMonSmart())
|
||||
return;
|
||||
|
||||
|
||||
// get/assume all battler data
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
@ -267,7 +267,7 @@ void GetAiLogicData(void)
|
||||
SetBattlerAiData(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// simulate AI damage
|
||||
for (battlerAtk = 0; battlerAtk < gBattlersCount; battlerAtk++)
|
||||
{
|
||||
@ -275,26 +275,26 @@ void GetAiLogicData(void)
|
||||
|| !IsBattlerAIControlled(battlerAtk)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
|
||||
{
|
||||
if (battlerAtk == battlerDef)
|
||||
continue;
|
||||
|
||||
|
||||
RecordKnownMove(battlerDef, gLastMoves[battlerDef]);
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
dmg = 0;
|
||||
effectiveness = AI_EFFECTIVENESS_x0;
|
||||
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;
|
||||
}
|
||||
@ -334,7 +334,7 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
return AI_CHOICE_WATCH;
|
||||
|
||||
gActiveBattler = sBattler_AI;
|
||||
|
||||
|
||||
// If can switch.
|
||||
if (CountUsablePartyMons(sBattler_AI) > 0
|
||||
&& !IsAbilityPreventingEscape(sBattler_AI)
|
||||
@ -375,7 +375,7 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
numOfBestMoves = 1;
|
||||
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
|
||||
consideredMoveArray[0] = 0;
|
||||
@ -427,7 +427,7 @@ static u8 ChooseMoveOrAction_Doubles(void)
|
||||
BattleAI_SetupAIData(gBattleStruct->palaceFlags >> 4);
|
||||
else
|
||||
BattleAI_SetupAIData(0xF);
|
||||
|
||||
|
||||
gBattlerTarget = i;
|
||||
if ((i & BIT_SIDE) != (sBattler_AI & BIT_SIDE))
|
||||
RecordLastUsedMoveByTarget();
|
||||
@ -467,7 +467,7 @@ static u8 ChooseMoveOrAction_Doubles(void)
|
||||
{
|
||||
if (!CanTargetBattler(sBattler_AI, i, gBattleMons[sBattler_AI].moves[j]))
|
||||
continue;
|
||||
|
||||
|
||||
if (mostViableMovesScores[0] == AI_THINKING_STRUCT->score[j])
|
||||
{
|
||||
mostViableMovesScores[mostViableMovesNo] = AI_THINKING_STRUCT->score[j];
|
||||
@ -586,7 +586,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
|
||||
u32 i;
|
||||
u16 predictedMove = AI_DATA->predictedMoves[battlerDef];
|
||||
|
||||
|
||||
SetTypeBeforeUsingMove(move, battlerAtk);
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
|
||||
@ -594,7 +594,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
return score;
|
||||
|
||||
GET_MOVE_TYPE(move, moveType);
|
||||
|
||||
|
||||
// check non-user target
|
||||
if (!(moveTarget & MOVE_TARGET_USER))
|
||||
{
|
||||
@ -604,7 +604,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
RETURN_SCORE_MINUS(20);
|
||||
}
|
||||
|
||||
|
||||
// check ground immunities
|
||||
if (moveType == TYPE_GROUND
|
||||
&& !IsBattlerGrounded(battlerDef)
|
||||
@ -616,11 +616,11 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
RETURN_SCORE_MINUS(20);
|
||||
}
|
||||
|
||||
|
||||
// check off screen
|
||||
if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
|
||||
RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move
|
||||
|
||||
|
||||
// check if negates type
|
||||
switch (effectiveness)
|
||||
{
|
||||
@ -632,7 +632,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// target ability checks
|
||||
if (!DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battlerAtk], move))
|
||||
{
|
||||
@ -758,7 +758,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
} // def ability checks
|
||||
|
||||
|
||||
// target partner ability checks & not attacking partner
|
||||
if (isDoubleBattle)
|
||||
{
|
||||
@ -796,35 +796,35 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
} // def partner ability checks
|
||||
} // ignore def ability check
|
||||
|
||||
|
||||
// gen7+ dark type mons immune to priority->elevated moves from prankster
|
||||
#if B_PRANKSTER_DARK_TYPES >= GEN_7
|
||||
if (AI_DATA->abilities[battlerAtk] == ABILITY_PRANKSTER && IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK) && IS_MOVE_STATUS(move)
|
||||
&& !(moveTarget & (MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_USER)))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
#endif
|
||||
|
||||
|
||||
// terrain & effect checks
|
||||
if (AI_IsTerrainAffected(battlerDef, STATUS_FIELD_ELECTRIC_TERRAIN))
|
||||
{
|
||||
if (moveEffect == EFFECT_SLEEP || moveEffect == EFFECT_YAWN)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
}
|
||||
|
||||
|
||||
if (AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN))
|
||||
{
|
||||
if (IsNonVolatileStatusMoveEffect(moveEffect) || IsConfusionMoveEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(20);
|
||||
}
|
||||
|
||||
|
||||
if (AI_IsTerrainAffected(battlerAtk, STATUS_FIELD_PSYCHIC_TERRAIN) && atkPriority > 0)
|
||||
{
|
||||
RETURN_SCORE_MINUS(20);
|
||||
}
|
||||
} // end check MOVE_TARGET_USER
|
||||
|
||||
|
||||
// the following checks apply to any target (including user)
|
||||
|
||||
|
||||
// throat chop check
|
||||
if (gDisableStructs[battlerAtk].throatChopTimer && TestMoveFlags(move, FLAG_SOUND))
|
||||
return 0; // Can't even select move at all
|
||||
@ -860,7 +860,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check move effects
|
||||
switch (moveEffect)
|
||||
{
|
||||
@ -874,7 +874,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_EXPLOSION:
|
||||
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_WILL_SUICIDE))
|
||||
score -= 2;
|
||||
|
||||
|
||||
if (effectiveness == AI_EFFECTIVENESS_x0)
|
||||
{
|
||||
score -= 10;
|
||||
@ -920,7 +920,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPATK) || !HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL))
|
||||
score -= 10;
|
||||
break;
|
||||
case EFFECT_SPECIAL_DEFENSE_UP:
|
||||
case EFFECT_SPECIAL_DEFENSE_UP:
|
||||
case EFFECT_SPECIAL_DEFENSE_UP_2:
|
||||
if (!BattlerStatCanRise(battlerAtk, AI_DATA->abilities[battlerAtk], STAT_SPDEF))
|
||||
score -= 10;
|
||||
@ -1230,7 +1230,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_LOW_KICK:
|
||||
// AI_CBM_HighRiskForDamage
|
||||
if (AI_DATA->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2)
|
||||
score -= 10;
|
||||
score -= 10;
|
||||
break;
|
||||
case EFFECT_COUNTER:
|
||||
case EFFECT_MIRROR_COAT:
|
||||
@ -1240,7 +1240,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
|| DoesSubstituteBlockMove(battlerAtk, BATTLE_PARTNER(battlerDef), predictedMove))
|
||||
score -= 10;
|
||||
break;
|
||||
|
||||
|
||||
case EFFECT_ROAR:
|
||||
if (CountUsablePartyMons(battlerDef) == 0)
|
||||
score -= 10;
|
||||
@ -1392,7 +1392,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_SPIKES:
|
||||
if (gSideTimers[GetBattlerSide(battlerDef)].spikesAmount >= 3)
|
||||
score -= 10;
|
||||
else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)
|
||||
else if (PartnerMoveIsSameNoTarget(BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)
|
||||
&& gSideTimers[GetBattlerSide(battlerDef)].spikesAmount == 2)
|
||||
score -= 10; // only one mon needs to set up the last layer of Spikes
|
||||
break;
|
||||
@ -1570,7 +1570,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score -= 10;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (B_MENTAL_HERB >= GEN_5 && AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_MENTAL_HERB)
|
||||
score -= 6;
|
||||
break;
|
||||
@ -1802,7 +1802,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (gBattleMons[battlerAtk].hp > (gBattleMons[battlerAtk].hp + gBattleMons[battlerDef].hp) / 2)
|
||||
score -= 10;
|
||||
break;
|
||||
|
||||
|
||||
case EFFECT_CONVERSION_2:
|
||||
//TODO
|
||||
break;
|
||||
@ -1862,7 +1862,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
} // move check
|
||||
|
||||
|
||||
if (decreased)
|
||||
break;
|
||||
if (IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef]))
|
||||
@ -1904,7 +1904,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
IncreaseAllyProtectionViability(&viability, 0xFF);
|
||||
}*/
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case EFFECT_MIRACLE_EYE:
|
||||
if (gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED)
|
||||
score -= 10;
|
||||
@ -1952,7 +1952,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
|| ((AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY) && !IsTargetingPartner(battlerAtk, battlerDef))) // don't want to raise target stats unless its your partner
|
||||
score -= 10;
|
||||
break;
|
||||
|
||||
|
||||
case EFFECT_PSYCH_UP: // haze stats check
|
||||
{
|
||||
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
|
||||
@ -2116,7 +2116,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
u32 atkNegativeStages = CountNegativeStatStages(battlerAtk);
|
||||
u32 defPositiveStages = CountPositiveStatStages(battlerDef);
|
||||
u32 defNegativeStages = CountNegativeStatStages(battlerDef);
|
||||
|
||||
|
||||
if (atkPositiveStages >= defPositiveStages && atkNegativeStages <= defNegativeStages)
|
||||
score -= 10;
|
||||
break;
|
||||
@ -2513,21 +2513,21 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score -= 10;
|
||||
break;*/
|
||||
} // move effect checks
|
||||
|
||||
|
||||
if (score < 0)
|
||||
score = 0;
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
{
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||
return score;
|
||||
|
||||
|
||||
if (gBattleMoves[move].power == 0)
|
||||
return score; // can't make anything faint with no power
|
||||
|
||||
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0) && gBattleMoves[move].effect != EFFECT_EXPLOSION)
|
||||
{
|
||||
// this move can faint the target
|
||||
@ -2541,10 +2541,10 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// this move isn't expected to faint the target
|
||||
if (TestMoveFlags(move, FLAG_HIGH_CRIT))
|
||||
score += 2; // crit makes it more likely to make them faint
|
||||
|
||||
|
||||
if (GetMoveDamageResult(move) == MOVE_POWER_OTHER)
|
||||
score--;
|
||||
|
||||
|
||||
switch (AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex])
|
||||
{
|
||||
case AI_EFFECTIVENESS_x8:
|
||||
@ -2561,7 +2561,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//AI_TryToFaint_CheckIfDanger
|
||||
if (!WillAIStrikeFirst() && CanTargetFaintAi(battlerDef, battlerAtk))
|
||||
{ // AI_TryToFaint_Danger
|
||||
@ -2570,7 +2570,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
else
|
||||
score++;
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -2626,8 +2626,8 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
}
|
||||
} // check partner move effect
|
||||
|
||||
|
||||
|
||||
|
||||
// consider our move effect relative to partner state
|
||||
switch (effect)
|
||||
{
|
||||
@ -2648,8 +2648,8 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
} // our effect relative to partner
|
||||
|
||||
|
||||
|
||||
|
||||
// consider global move effects
|
||||
switch (effect)
|
||||
{
|
||||
@ -2679,8 +2679,8 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
break;
|
||||
} // global move effect check
|
||||
|
||||
|
||||
|
||||
|
||||
// check specific target
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||
{
|
||||
@ -2787,11 +2787,11 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
RETURN_SCORE_PLUS(1);
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
} // ability checks
|
||||
} // move power check
|
||||
|
||||
|
||||
// attacker move effects specifically targeting partner
|
||||
if (!partnerProtecting)
|
||||
{
|
||||
@ -2904,12 +2904,12 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
} // attacker move effects
|
||||
} // check partner protecting
|
||||
|
||||
|
||||
score -= 30; // otherwise, don't target partner
|
||||
}
|
||||
else // checking opponent
|
||||
{
|
||||
// these checks mostly handled in AI_CheckBadMove and AI_CheckViability
|
||||
// these checks mostly handled in AI_CheckBadMove and AI_CheckViability
|
||||
switch (effect)
|
||||
{
|
||||
case EFFECT_SKILL_SWAP:
|
||||
@ -2934,10 +2934,10 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score -= 3;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
// lightning rod, flash fire against enemy handled in AI_CheckBadMove
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -2974,11 +2974,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
u16 predictedMove = AI_DATA->predictedMoves[battlerDef];
|
||||
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
|
||||
u32 i;
|
||||
|
||||
|
||||
// Targeting partner, check benefits of doing that instead
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||
return score;
|
||||
|
||||
|
||||
// check always hits
|
||||
if (!IS_MOVE_STATUS(move) && gBattleMoves[move].accuracy == 0)
|
||||
{
|
||||
@ -2987,11 +2987,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (AI_RandLessThan(100) && (gBattleMons[battlerDef].statStages[STAT_EVASION] >= 8 || gBattleMons[battlerAtk].statStages[STAT_ACC] <= 4))
|
||||
score++;
|
||||
}
|
||||
|
||||
|
||||
// check high crit
|
||||
if (TestMoveFlags(move, FLAG_HIGH_CRIT) && effectiveness >= AI_EFFECTIVENESS_x2 && AI_RandLessThan(128))
|
||||
score++;
|
||||
|
||||
|
||||
// check already dead
|
||||
if (!IsBattlerIncapacitated(battlerDef, AI_DATA->abilities[battlerDef])
|
||||
&& CanTargetFaintAi(battlerAtk, battlerDef)
|
||||
@ -3002,7 +3002,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
else
|
||||
score--;
|
||||
}
|
||||
|
||||
|
||||
// check damage
|
||||
if (gBattleMoves[move].power != 0 && GetMoveDamageResult(move) == MOVE_POWER_WEAK)
|
||||
score--;
|
||||
@ -3010,11 +3010,11 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// check status move preference
|
||||
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_PREFER_STATUS_MOVES && IS_MOVE_STATUS(move) && effectiveness != AI_EFFECTIVENESS_x0)
|
||||
score++;
|
||||
|
||||
|
||||
// check thawing moves
|
||||
if ((gBattleMons[battlerAtk].status1 & STATUS1_FREEZE) && TestMoveFlags(move, FLAG_THAW_USER))
|
||||
score += (gBattleTypeFlags & BATTLE_TYPE_DOUBLE) ? 20 : 10;
|
||||
|
||||
|
||||
// check burn
|
||||
if (gBattleMons[battlerAtk].status1 & STATUS1_BURN)
|
||||
{
|
||||
@ -3033,7 +3033,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// attacker ability checks
|
||||
switch (AI_DATA->abilities[battlerAtk])
|
||||
{
|
||||
@ -3049,8 +3049,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 8; // prioritize killing target for stat boost
|
||||
}
|
||||
break;
|
||||
} // ability checks
|
||||
|
||||
} // ability checks
|
||||
|
||||
// move effect checks
|
||||
switch (moveEffect)
|
||||
{
|
||||
@ -3095,7 +3095,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!AI_RandLessThan(100))
|
||||
{
|
||||
score--;
|
||||
@ -3141,7 +3141,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!AI_RandLessThan(100))
|
||||
{
|
||||
score--;
|
||||
@ -3164,7 +3164,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score -= 2;
|
||||
else if (AI_DATA->hpPercents[battlerAtk] <= 70)
|
||||
score -= 2;
|
||||
else
|
||||
else
|
||||
score++;
|
||||
break;
|
||||
case EFFECT_EVASION_UP:
|
||||
@ -3294,7 +3294,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_ATTACK_SPATK_UP: // work up
|
||||
if (AI_DATA->hpPercents[battlerAtk] <= 40 || AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY)
|
||||
break;
|
||||
|
||||
|
||||
if (HasMoveWithSplit(battlerAtk, SPLIT_PHYSICAL))
|
||||
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
|
||||
else if (HasMoveWithSplit(battlerAtk, SPLIT_SPECIAL))
|
||||
@ -3350,7 +3350,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (ShouldRecover(battlerAtk, battlerDef, move, healPercent))
|
||||
score += 2;
|
||||
}
|
||||
@ -3578,7 +3578,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (newHp > healthBenchmark && ShouldAbsorb(battlerAtk, battlerDef, move, AI_DATA->simulatedDmg[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex]))
|
||||
score += 2;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
case EFFECT_SLEEP_TALK:
|
||||
case EFFECT_SNORE:
|
||||
if (!IsWakeupTurn(battlerAtk) && gBattleMons[battlerAtk].status1 & STATUS1_SLEEP)
|
||||
@ -3613,13 +3613,13 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_THIEF:
|
||||
{
|
||||
bool32 canSteal = FALSE;
|
||||
|
||||
|
||||
#if defined B_TRAINERS_KNOCK_OFF_ITEMS && B_TRAINERS_KNOCK_OFF_ITEMS == TRUE
|
||||
canSteal = TRUE;
|
||||
#endif
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER || GetBattlerSide(battlerAtk) == B_SIDE_PLAYER)
|
||||
canSteal = TRUE;
|
||||
|
||||
|
||||
if (canSteal && AI_DATA->items[battlerAtk] == ITEM_NONE
|
||||
&& AI_DATA->items[battlerDef] != ITEM_NONE
|
||||
&& CanBattlerGetOrLoseItem(battlerDef, AI_DATA->items[battlerDef])
|
||||
@ -3763,8 +3763,8 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (AI_DATA->abilities[battlerDef] == ABILITY_MAGIC_BOUNCE || CountUsablePartyMons(battlerDef) == 0)
|
||||
break;
|
||||
if (gDisableStructs[battlerAtk].isFirstTurn)
|
||||
score += 2;
|
||||
//TODO - track entire opponent party data to determine hazard effectiveness
|
||||
score += 2;
|
||||
//TODO - track entire opponent party data to determine hazard effectiveness
|
||||
break;
|
||||
case EFFECT_FORESIGHT:
|
||||
if (AI_DATA->abilities[battlerAtk] == ABILITY_SCRAPPY)
|
||||
@ -3793,7 +3793,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (HasMoveEffect(battlerDef, EFFECT_MORNING_SUN)
|
||||
|| HasMoveEffect(battlerDef, EFFECT_SYNTHESIS)
|
||||
|| HasMoveEffect(battlerDef, EFFECT_MOONLIGHT))
|
||||
score += 2;
|
||||
score += 2;
|
||||
}
|
||||
break;
|
||||
case EFFECT_HAIL:
|
||||
@ -3802,7 +3802,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if ((HasMoveEffect(battlerAtk, EFFECT_AURORA_VEIL) || HasMoveEffect(BATTLE_PARTNER(battlerAtk), EFFECT_AURORA_VEIL))
|
||||
&& ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL))
|
||||
score += 3;
|
||||
|
||||
|
||||
score++;
|
||||
if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_ICY_ROCK)
|
||||
score++;
|
||||
@ -3861,7 +3861,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_SPECTRAL_THIEF:
|
||||
// Want to copy positive stat changes
|
||||
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
|
||||
{
|
||||
{
|
||||
if (gBattleMons[battlerDef].statStages[i] > gBattleMons[battlerAtk].statStages[i])
|
||||
{
|
||||
switch (i)
|
||||
@ -3920,7 +3920,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (HasMoveEffect(battlerAtk, EFFECT_SWALLOW)
|
||||
|| HasMoveEffect(battlerAtk, EFFECT_SPIT_UP))
|
||||
score += 2;
|
||||
|
||||
|
||||
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_DEF, &score);
|
||||
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPDEF, &score);
|
||||
break;
|
||||
@ -3937,20 +3937,20 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
|| HasMoveEffect(battlerAtk, EFFECT_PSYCH_UP)
|
||||
|| HasMoveEffect(battlerAtk, EFFECT_SPECTRAL_THIEF))
|
||||
score++;
|
||||
|
||||
|
||||
if (AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY)
|
||||
score += 2;
|
||||
|
||||
|
||||
IncreaseConfusionScore(battlerAtk, battlerDef, move, &score);
|
||||
break;
|
||||
case EFFECT_FLATTER:
|
||||
if (HasMoveEffect(battlerAtk, EFFECT_PSYCH_UP)
|
||||
|| HasMoveEffect(battlerAtk, EFFECT_SPECTRAL_THIEF))
|
||||
score += 2;
|
||||
|
||||
|
||||
if (AI_DATA->abilities[battlerDef] == ABILITY_CONTRARY)
|
||||
score += 2;
|
||||
|
||||
|
||||
IncreaseConfusionScore(battlerAtk, battlerDef, move, &score);
|
||||
break;
|
||||
case EFFECT_FURY_CUTTER:
|
||||
@ -3991,7 +3991,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch (move)
|
||||
{
|
||||
case MOVE_DEFOG:
|
||||
@ -4007,7 +4007,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
&& AI_WhoStrikesFirst(battlerAtk, BATTLE_PARTNER(battlerAtk), move) == AI_IS_SLOWER) // Partner going first
|
||||
break; // Don't use Defog if partner is going to set up hazards
|
||||
}
|
||||
|
||||
|
||||
// check defog lowering evasion
|
||||
if (ShouldLowerEvasion(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef]))
|
||||
{
|
||||
@ -4179,10 +4179,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
u16 item = GetUsedHeldItem(battlerAtk);
|
||||
u16 toHeal = (ItemId_GetHoldEffectParam(item) == 10) ? 10 : gBattleMons[battlerAtk].maxHP / ItemId_GetHoldEffectParam(item);
|
||||
|
||||
|
||||
if (IsStatBoostingBerry(item) && AI_DATA->hpPercents[battlerAtk] > 60)
|
||||
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))
|
||||
|| !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
|
||||
@ -4229,7 +4229,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
if (AI_DATA->abilities[battlerDef] != AI_DATA->abilities[battlerAtk] && !(gStatuses3[battlerDef] & STATUS3_GASTRO_ACID))
|
||||
score += 2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EFFECT_IMPRISON:
|
||||
if (predictedMove != MOVE_NONE && HasMove(battlerAtk, predictedMove))
|
||||
@ -4300,7 +4300,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
case EFFECT_SHELL_SMASH:
|
||||
if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_RESTORE_STATS)
|
||||
score += 1;
|
||||
|
||||
|
||||
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score);
|
||||
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPATK, &score);
|
||||
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
|
||||
@ -4407,7 +4407,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (gStatuses3[battlerAtk] & STATUS3_YAWN && IsBattlerGrounded(battlerAtk))
|
||||
score += 10;
|
||||
//fallthrough
|
||||
case EFFECT_GRASSY_TERRAIN:
|
||||
case EFFECT_GRASSY_TERRAIN:
|
||||
case EFFECT_PSYCHIC_TERRAIN:
|
||||
score += 2;
|
||||
if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_TERRAIN_EXTENDER)
|
||||
@ -4680,7 +4680,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
//case EFFECT_SKY_DROP
|
||||
//break;
|
||||
} // move effect checks
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -4690,15 +4690,15 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef)
|
||||
|| gBattleResults.battleTurnCounter != 0)
|
||||
return score;
|
||||
|
||||
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING
|
||||
|
||||
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING
|
||||
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
|
||||
&& CanTargetFaintAi(battlerDef, battlerAtk)
|
||||
&& GetMovePriority(battlerAtk, move) == 0)
|
||||
{
|
||||
RETURN_SCORE_MINUS(20); // No point in setting up if you will faint. Should just switch if possible..
|
||||
}
|
||||
|
||||
|
||||
// check effects to prioritize first turn
|
||||
switch (gBattleMoves[move].effect)
|
||||
{
|
||||
@ -4787,7 +4787,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -4796,10 +4796,10 @@ static s16 AI_Risky(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||
return score;
|
||||
|
||||
|
||||
if (TestMoveFlags(move, FLAG_HIGH_CRIT))
|
||||
score += 2;
|
||||
|
||||
|
||||
switch (gBattleMoves[move].effect)
|
||||
{
|
||||
case EFFECT_SLEEP:
|
||||
@ -4826,7 +4826,7 @@ static s16 AI_Risky(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -4835,10 +4835,10 @@ static s16 AI_PreferStrongestMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 sc
|
||||
{
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef))
|
||||
return score;
|
||||
|
||||
|
||||
if (GetMoveDamageResult(move) == MOVE_POWER_BEST)
|
||||
score += 2;
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -4846,14 +4846,14 @@ static s16 AI_PreferStrongestMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 sc
|
||||
static s16 AI_PreferBatonPass(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
u32 i;
|
||||
|
||||
|
||||
if (IsTargetingPartner(battlerAtk, battlerDef)
|
||||
|| CountUsablePartyMons(battlerAtk) == 0
|
||||
|| GetMoveDamageResult(move) != MOVE_POWER_OTHER
|
||||
|| !HasMoveEffect(battlerAtk, EFFECT_BATON_PASS)
|
||||
|| IsBattlerTrapped(battlerAtk, TRUE))
|
||||
return score;
|
||||
|
||||
|
||||
if (IsStatRaisingEffect(gBattleMoves[move].effect))
|
||||
{
|
||||
if (gBattleResults.battleTurnCounter == 0)
|
||||
@ -4861,9 +4861,9 @@ static s16 AI_PreferBatonPass(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
else if (AI_DATA->hpPercents[battlerAtk] < 60)
|
||||
score -= 10;
|
||||
else
|
||||
score++;
|
||||
score++;
|
||||
}
|
||||
|
||||
|
||||
// other specific checks
|
||||
switch (gBattleMoves[move].effect)
|
||||
{
|
||||
@ -4889,12 +4889,12 @@ static s16 AI_PreferBatonPass(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
if (gStatuses3[battlerAtk] & (STATUS3_ROOTED | STATUS3_AQUA_RING))
|
||||
score += 2;
|
||||
if (gStatuses3[battlerAtk] & STATUS3_LEECHSEED)
|
||||
score -= 3;
|
||||
score -= 3;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -4914,11 +4914,11 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
if (gStatuses3[battlerDef] & STATUS3_HEAL_BLOCK)
|
||||
return 0;
|
||||
|
||||
|
||||
if (CanTargetFaintAi(FOE(battlerAtk), BATTLE_PARTNER(battlerAtk))
|
||||
|| (CanTargetFaintAi(BATTLE_PARTNER(FOE(battlerAtk)), BATTLE_PARTNER(battlerAtk))))
|
||||
score--;
|
||||
|
||||
|
||||
if (AI_DATA->hpPercents[battlerDef] <= 50)
|
||||
score++;
|
||||
}
|
||||
@ -4957,7 +4957,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// med hp
|
||||
if (IsStatRaisingEffect(effect) || IsStatLoweringEffect(effect))
|
||||
score -= 2;
|
||||
|
||||
|
||||
switch (effect)
|
||||
{
|
||||
case EFFECT_EXPLOSION:
|
||||
@ -4980,7 +4980,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
// low hp
|
||||
if (IsStatRaisingEffect(effect) || IsStatLoweringEffect(effect))
|
||||
score -= 2;
|
||||
|
||||
|
||||
// check other discouraged low hp effects
|
||||
switch (effect)
|
||||
{
|
||||
@ -5013,7 +5013,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// consider target HP
|
||||
if (CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0))
|
||||
{
|
||||
@ -5085,7 +5085,7 @@ static s16 AI_HPAware(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score -= 2; // don't use status moves if target is at low health
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -5104,7 +5104,7 @@ static s16 AI_Roaming(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
if (IsBattlerTrapped(battlerAtk, FALSE))
|
||||
return score;
|
||||
|
||||
|
||||
AI_Flee();
|
||||
return score;
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "global.h"
|
||||
#include "battle.h"
|
||||
#include "battle_anim.h"
|
||||
#include "battle_interface.h"
|
||||
#include "bg.h"
|
||||
#include "contest.h"
|
||||
#include "data.h"
|
||||
@ -134,10 +135,10 @@ u8 GetBattlerSpriteCoord(u8 battlerId, u8 coordType)
|
||||
{
|
||||
case BATTLER_COORD_X:
|
||||
case BATTLER_COORD_X_2:
|
||||
retVal = sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].x;
|
||||
retVal = sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].x;
|
||||
break;
|
||||
case BATTLER_COORD_Y:
|
||||
retVal = sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].y;
|
||||
retVal = sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].y;
|
||||
break;
|
||||
case BATTLER_COORD_Y_PIC_OFFSET:
|
||||
case BATTLER_COORD_Y_PIC_OFFSET_DEFAULT:
|
||||
@ -278,7 +279,7 @@ u8 GetBattlerSpriteFinal_Y(u8 battlerId, u16 species, bool8 a3)
|
||||
offset = GetBattlerYDelta(battlerId, species);
|
||||
offset -= GetBattlerElevation(battlerId, species);
|
||||
}
|
||||
y = offset + sBattlerCoords[IS_DOUBLE_BATTLE()][GetBattlerPosition(battlerId)].y;
|
||||
y = offset + sBattlerCoords[WhichBattleCoords(battlerId)][GetBattlerPosition(battlerId)].y;
|
||||
if (a3)
|
||||
{
|
||||
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
||||
@ -493,7 +494,7 @@ static void TranslateSpriteInWavePattern(struct Sprite *sprite)
|
||||
sprite->y2 = Cos(sprite->sCirclePosY, sprite->sAmplitude);
|
||||
sprite->sCirclePosX += sprite->sCircleSpeedX;
|
||||
sprite->sCirclePosY += sprite->sCircleSpeedY;
|
||||
|
||||
|
||||
if (sprite->sCirclePosX >= 0x100)
|
||||
sprite->sCirclePosX -= 0x100;
|
||||
else if (sprite->sCirclePosX < 0)
|
||||
@ -2445,7 +2446,7 @@ void AnimTask_AttackerPunchWithTrace(u8 taskId)
|
||||
|
||||
dest = (task->tPaletteNum + 16) * 16;
|
||||
src = (gSprites[task->tBattlerSpriteId].oam.paletteNum + 0x10) * 0x10;
|
||||
|
||||
|
||||
// Set trace's priority based on battler's subpriority
|
||||
task->tPriority = GetBattlerSpriteSubpriority(gBattleAnimAttacker);
|
||||
if (task->tPriority == 20 || task->tPriority == 40)
|
||||
|
@ -715,7 +715,12 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state)
|
||||
else
|
||||
{
|
||||
if (state == 2)
|
||||
LoadCompressedSpriteSheet(&sSpriteSheets_DoublesPlayerHealthbox[0]);
|
||||
{
|
||||
if (WhichBattleCoords(0))
|
||||
LoadCompressedSpriteSheet(&sSpriteSheets_DoublesPlayerHealthbox[0]);
|
||||
else
|
||||
LoadCompressedSpriteSheet(&sSpriteSheet_SinglesPlayerHealthbox);
|
||||
}
|
||||
else if (state == 3)
|
||||
LoadCompressedSpriteSheet(&sSpriteSheets_DoublesPlayerHealthbox[1]);
|
||||
else if (state == 4)
|
||||
|
@ -764,6 +764,15 @@ static void InitLastUsedBallAssets(void)
|
||||
gBattleStruct->ballSpriteIds[1] = MAX_SPRITES;
|
||||
}
|
||||
|
||||
// This function is here to cover a specific case - one player's mon in a 2 vs 1 double battle. In this scenario - display singles layout.
|
||||
u32 WhichBattleCoords(u32 battlerId) // 0 - singles, 1 - doubles
|
||||
{
|
||||
if (GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT && gPlayerPartyCount == 1)
|
||||
return 0;
|
||||
else
|
||||
return IsDoubleBattle();
|
||||
}
|
||||
|
||||
u8 CreateBattlerHealthboxSprites(u8 battlerId)
|
||||
{
|
||||
s16 data6 = 0;
|
||||
@ -771,7 +780,7 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId)
|
||||
u8 healthbarSpriteId, megaIndicatorSpriteId;
|
||||
struct Sprite *healthBarSpritePtr;
|
||||
|
||||
if (!IsDoubleBattle())
|
||||
if (WhichBattleCoords(battlerId) == 0) // Singles
|
||||
{
|
||||
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
||||
{
|
||||
@ -1035,7 +1044,7 @@ void GetBattlerHealthboxCoords(u8 battler, s16 *x, s16 *y)
|
||||
{
|
||||
*x = 0, *y = 0;
|
||||
|
||||
if (!IsDoubleBattle())
|
||||
if (!WhichBattleCoords(battler))
|
||||
{
|
||||
if (GetBattlerSide(battler) != B_SIDE_PLAYER)
|
||||
*x = 44, *y = 30;
|
||||
@ -1102,7 +1111,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl)
|
||||
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
|
||||
{
|
||||
objVram = (void *)(OBJ_VRAM0);
|
||||
if (!IsDoubleBattle())
|
||||
if (!WhichBattleCoords(battler))
|
||||
objVram += spriteTileNum + 0x820;
|
||||
else
|
||||
objVram += spriteTileNum + 0x420;
|
||||
@ -1118,12 +1127,13 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl)
|
||||
|
||||
void UpdateHpTextInHealthbox(u8 healthboxSpriteId, s16 value, u8 maxOrCurrent)
|
||||
{
|
||||
u32 windowId, spriteTileNum;
|
||||
u32 windowId, spriteTileNum, battler;
|
||||
u8 *windowTileData;
|
||||
u8 text[32];
|
||||
void *objVram;
|
||||
|
||||
if (GetBattlerSide(gSprites[healthboxSpriteId].hMain_Battler) == B_SIDE_PLAYER && !IsDoubleBattle())
|
||||
battler = gSprites[healthboxSpriteId].hMain_Battler;
|
||||
if (GetBattlerSide(battler) == B_SIDE_PLAYER && !WhichBattleCoords(battler))
|
||||
{
|
||||
spriteTileNum = gSprites[healthboxSpriteId].oam.tileNum * TILE_SIZE_4BPP;
|
||||
if (maxOrCurrent != HP_CURRENT) // singles, max
|
||||
@ -1152,10 +1162,7 @@ void UpdateHpTextInHealthbox(u8 healthboxSpriteId, s16 value, u8 maxOrCurrent)
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 battler;
|
||||
|
||||
memcpy(text, sEmptyWhiteText_GrayHighlight, sizeof(sEmptyWhiteText_GrayHighlight));
|
||||
battler = gSprites[healthboxSpriteId].hMain_Battler;
|
||||
if (IsDoubleBattle() == TRUE)
|
||||
{
|
||||
UpdateHpTextInHealthboxInDoubles(healthboxSpriteId, value, maxOrCurrent);
|
||||
@ -1362,7 +1369,7 @@ void SwapHpBarsWithHpText(void)
|
||||
{
|
||||
if (gSprites[gHealthboxSpriteIds[i]].callback == SpriteCallbackDummy
|
||||
&& GetBattlerSide(i) != B_SIDE_OPPONENT
|
||||
&& (IsDoubleBattle() || GetBattlerSide(i) != B_SIDE_PLAYER))
|
||||
&& (WhichBattleCoords(i) || GetBattlerSide(i) != B_SIDE_PLAYER))
|
||||
{
|
||||
bool8 noBars;
|
||||
|
||||
@ -1370,7 +1377,7 @@ void SwapHpBarsWithHpText(void)
|
||||
noBars = gBattleSpritesDataPtr->battlerData[i].hpNumbersNoBars;
|
||||
if (GetBattlerSide(i) == B_SIDE_PLAYER)
|
||||
{
|
||||
if (!IsDoubleBattle())
|
||||
if (!WhichBattleCoords(i))
|
||||
continue;
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
|
||||
continue;
|
||||
@ -1666,7 +1673,7 @@ u8 CreatePartyStatusSummarySprites(u8 battlerId, struct HpAndStatus *partyInfo,
|
||||
{
|
||||
isOpponent = TRUE;
|
||||
|
||||
if (!skipPlayer || !IsDoubleBattle())
|
||||
if (!skipPlayer || !WhichBattleCoords(battlerId))
|
||||
bar_X = 104, bar_Y = 40;
|
||||
else
|
||||
bar_X = 104, bar_Y = 16;
|
||||
@ -2152,7 +2159,7 @@ static void UpdateNickInHealthbox(u8 healthboxSpriteId, struct Pokemon *mon)
|
||||
{
|
||||
TextIntoHealthboxObject((void *)(OBJ_VRAM0 + 0x40 + spriteTileNum), windowTileData, 6);
|
||||
ptr = (void *)(OBJ_VRAM0);
|
||||
if (!IsDoubleBattle())
|
||||
if (!WhichBattleCoords(gSprites[healthboxSpriteId].data[6]))
|
||||
ptr += spriteTileNum + 0x800;
|
||||
else
|
||||
ptr += spriteTileNum + 0x400;
|
||||
@ -2203,7 +2210,7 @@ static void UpdateStatusIconInHealthbox(u8 healthboxSpriteId)
|
||||
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
|
||||
{
|
||||
status = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battlerId]], MON_DATA_STATUS);
|
||||
if (!IsDoubleBattle())
|
||||
if (!WhichBattleCoords(battlerId))
|
||||
tileNumAdder = 0x1A;
|
||||
else
|
||||
tileNumAdder = 0x12;
|
||||
@ -2259,7 +2266,7 @@ static void UpdateStatusIconInHealthbox(u8 healthboxSpriteId)
|
||||
FillPalette(sStatusIconColors[statusPalId], pltAdder + 0x100, 2);
|
||||
CpuCopy16(gPlttBufferUnfaded + 0x100 + pltAdder, (void *)(OBJ_PLTT + pltAdder * 2), 2);
|
||||
CpuCopy32(statusGfxPtr, (void *)(OBJ_VRAM0 + (gSprites[healthboxSpriteId].oam.tileNum + tileNumAdder) * TILE_SIZE_4BPP), 96);
|
||||
if (IsDoubleBattle() == TRUE || GetBattlerSide(battlerId) == B_SIDE_OPPONENT)
|
||||
if (WhichBattleCoords(battlerId) == 1 || GetBattlerSide(battlerId) == B_SIDE_OPPONENT)
|
||||
{
|
||||
if (!gBattleSpritesDataPtr->battlerData[battlerId].hpNumbersNoBars)
|
||||
{
|
||||
@ -2382,7 +2389,7 @@ void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elem
|
||||
SetBattleBarStruct(battlerId, healthboxSpriteId, maxHp, currHp, 0);
|
||||
MoveBattleBar(battlerId, healthboxSpriteId, HEALTH_BAR, 0);
|
||||
}
|
||||
isDoubles = IsDoubleBattle();
|
||||
isDoubles = WhichBattleCoords(battlerId);
|
||||
if (!isDoubles && (elementId == HEALTHBOX_EXP_BAR || elementId == HEALTHBOX_ALL))
|
||||
{
|
||||
u16 species;
|
||||
|
@ -2981,10 +2981,10 @@ static void BattleStartClearSetData(void)
|
||||
gBattleStruct->arenaLostOpponentMons = 0;
|
||||
|
||||
gBattleStruct->mega.triggerSpriteId = 0xFF;
|
||||
|
||||
|
||||
gBattleStruct->stickyWebUser = 0xFF;
|
||||
gBattleStruct->appearedInBattle = 0;
|
||||
|
||||
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
gBattleStruct->usedHeldItems[i][0] = 0;
|
||||
@ -3084,7 +3084,7 @@ void SwitchInClearSetData(void)
|
||||
gBattleStruct->lastTakenMoveFrom[gActiveBattler][3] = 0;
|
||||
gBattleStruct->lastMoveFailed &= ~(gBitTable[gActiveBattler]);
|
||||
gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]);
|
||||
|
||||
|
||||
if (gActiveBattler == gBattleStruct->stickyWebUser)
|
||||
gBattleStruct->stickyWebUser = 0xFF; // Switched into sticky web user slot so reset it
|
||||
|
||||
@ -3100,7 +3100,7 @@ void SwitchInClearSetData(void)
|
||||
gBattleResources->flags->flags[gActiveBattler] = 0;
|
||||
gCurrentMove = MOVE_NONE;
|
||||
gBattleStruct->arenaTurnCounter = 0xFF;
|
||||
|
||||
|
||||
// Reset damage to prevent things like red card activating if the switched-in mon is holding it
|
||||
gSpecialStatuses[gActiveBattler].physicalDmg = 0;
|
||||
gSpecialStatuses[gActiveBattler].specialDmg = 0;
|
||||
@ -3184,7 +3184,7 @@ void FaintClearSetData(void)
|
||||
gBattleStruct->lastTakenMoveFrom[gActiveBattler][3] = 0;
|
||||
|
||||
gBattleStruct->palaceFlags &= ~(gBitTable[gActiveBattler]);
|
||||
|
||||
|
||||
if (gActiveBattler == gBattleStruct->stickyWebUser)
|
||||
gBattleStruct->stickyWebUser = 0xFF; // User of sticky web fainted, so reset the stored battler ID
|
||||
|
||||
@ -3342,7 +3342,7 @@ static void DoBattleIntro(void)
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
}
|
||||
}
|
||||
else // wild mon 2
|
||||
else if (IsBattlerAlive(gActiveBattler)) // wild mon 2 if alive
|
||||
{
|
||||
BtlController_EmitLoadMonSprite(BUFFER_A);
|
||||
MarkBattlerForControllerExec(gActiveBattler);
|
||||
@ -3696,7 +3696,7 @@ static void TryDoEventsBeforeFirstTurn(void)
|
||||
gMoveResultFlags = 0;
|
||||
|
||||
gRandomTurnNumber = Random();
|
||||
|
||||
|
||||
GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
@ -3904,7 +3904,7 @@ static void HandleTurnActionSelectionState(void)
|
||||
case STATE_TURN_START_RECORD: // Recorded battle related action on start of every turn.
|
||||
RecordedBattle_CopyBattlerMoves();
|
||||
gBattleCommunication[gActiveBattler] = STATE_BEFORE_ACTION_CHOSEN;
|
||||
|
||||
|
||||
// Do AI score computations here so we can use them in AI_TrySwitchOrUseItem
|
||||
if ((gBattleTypeFlags & BATTLE_TYPE_HAS_AI || IsWildMonSmart()) && IsBattlerAIControlled(gActiveBattler)) {
|
||||
gBattleStruct->aiMoveOrAction[gActiveBattler] = ComputeBattleAiScores(gActiveBattler);
|
||||
@ -4564,7 +4564,7 @@ u8 GetWhoStrikesFirst(u8 battler1, u8 battler2, bool8 ignoreChosenMoves)
|
||||
// QUICK CLAW / CUSTAP - always first
|
||||
// LAGGING TAIL - always last
|
||||
// STALL - always last
|
||||
|
||||
|
||||
if (gProtectStructs[battler1].quickDraw && !gProtectStructs[battler2].quickDraw)
|
||||
strikesFirst = 0;
|
||||
else if (!gProtectStructs[battler1].quickDraw && gProtectStructs[battler2].quickDraw)
|
||||
|
@ -6115,7 +6115,7 @@ void ChooseMonForWirelessMinigame(void)
|
||||
|
||||
static u8 GetPartyLayoutFromBattleType(void)
|
||||
{
|
||||
if (IsDoubleBattle() == FALSE)
|
||||
if (!IsDoubleBattle() || gPlayerPartyCount == 1) // Draw the single layout in a double battle where the player has only one pokemon.
|
||||
return PARTY_LAYOUT_SINGLE;
|
||||
if (IsMultiBattle() == TRUE)
|
||||
return PARTY_LAYOUT_MULTI;
|
||||
|
Loading…
Reference in New Issue
Block a user