mirror of
https://github.com/Ninjdai1/pokeemerald.git
synced 2024-11-16 11:37:40 +01:00
Save party mons ai
This commit is contained in:
parent
eb0cc91dce
commit
327782646b
2
Makefile
2
Makefile
@ -107,7 +107,7 @@ LIBPATH := -L ../../tools/agbcc/lib
|
||||
LIB := $(LIBPATH) -lgcc -lc -L../../libagbsyscall -lagbsyscall
|
||||
else
|
||||
CC1 = $(shell $(PATH_MODERNCC) --print-prog-name=cc1) -quiet
|
||||
override CFLAGS += -mthumb -mthumb-interwork -O2 -mabi=apcs-gnu -mtune=arm7tdmi -march=armv4t -fno-toplevel-reorder -Wno-pointer-to-int-cast
|
||||
override CFLAGS += -mthumb -mthumb-interwork -O0 -mabi=apcs-gnu -mtune=arm7tdmi -march=armv4t -fno-toplevel-reorder -Wno-pointer-to-int-cast
|
||||
ROM := $(MODERN_ROM_NAME)
|
||||
OBJ_DIR := $(MODERN_OBJ_DIR_NAME)
|
||||
LIBPATH := -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libgcc.a))" -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libnosys.a))" -L "$(dir $(shell $(PATH_MODERNCC) -mthumb -print-file-name=libc.a))"
|
||||
|
BIN
NO$GBA.EXE - Shortcut.lnk
Normal file
BIN
NO$GBA.EXE - Shortcut.lnk
Normal file
Binary file not shown.
@ -245,6 +245,26 @@ struct AI_SavedBattleMon
|
||||
u16 species;
|
||||
};
|
||||
|
||||
struct AiPartyMon
|
||||
{
|
||||
u16 species;
|
||||
u16 item;
|
||||
u16 heldEffect;
|
||||
u16 ability;
|
||||
u16 gender;
|
||||
u16 level;
|
||||
u16 moves[MAX_MON_MOVES];
|
||||
bool8 isFainted;
|
||||
u8 switchInCount; // Counts how many times this Pokemon has been sent out or switched into in a battle.
|
||||
bool8 wasSentInBattle;
|
||||
};
|
||||
|
||||
struct AIPartyData // Opposing battlers - party mons.
|
||||
{
|
||||
struct AiPartyMon mons[2][PARTY_SIZE]; // 2 parties(player, opponent). Used to save information on opposing party.
|
||||
u8 count[2];
|
||||
};
|
||||
|
||||
struct AiLogicData
|
||||
{
|
||||
u16 abilities[MAX_BATTLERS_COUNT];
|
||||
@ -313,6 +333,7 @@ struct BattleResources
|
||||
struct StatsArray* beforeLvlUp;
|
||||
struct AI_ThinkingStruct *ai;
|
||||
struct AiLogicData *aiData;
|
||||
struct AIPartyData *aiParty;
|
||||
struct BattleHistory *battleHistory;
|
||||
u8 bufferA[MAX_BATTLERS_COUNT][0x200];
|
||||
u8 bufferB[MAX_BATTLERS_COUNT][0x200];
|
||||
@ -320,6 +341,7 @@ struct BattleResources
|
||||
|
||||
#define AI_THINKING_STRUCT ((struct AI_ThinkingStruct *)(gBattleResources->ai))
|
||||
#define AI_DATA ((struct AiLogicData *)(gBattleResources->aiData))
|
||||
#define AI_PARTY ((struct AIPartyData *)(gBattleResources->aiParty))
|
||||
#define BATTLE_HISTORY ((struct BattleHistory *)(gBattleResources->battleHistory))
|
||||
|
||||
struct BattleResults
|
||||
@ -670,7 +692,7 @@ struct BattleStruct
|
||||
#define SET_STATCHANGER(statId, stage, goesDown)(gBattleScripting.statChanger = (statId) + ((stage) << 3) + (goesDown << 7))
|
||||
#define SET_STATCHANGER2(dst, statId, stage, goesDown)(dst = (statId) + ((stage) << 3) + (goesDown << 7))
|
||||
|
||||
// NOTE: The members of this struct have hard-coded offsets
|
||||
// NOTE: The members of this struct have hard-coded offsets
|
||||
// in include/constants/battle_script_commands.h
|
||||
struct BattleScripting
|
||||
{
|
||||
|
@ -24,6 +24,8 @@ void BattleAI_SetupItems(void);
|
||||
void BattleAI_SetupFlags(void);
|
||||
void BattleAI_SetupAIData(u8 defaultScoreMoves);
|
||||
u8 BattleAI_ChooseMoveOrAction(void);
|
||||
void Ai_InitPartyStruct(void);
|
||||
void Ai_UpdateSwitchInData(u32 battler);
|
||||
void GetAiLogicData(void);
|
||||
|
||||
extern u8 sBattler_AI;
|
||||
|
@ -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;
|
||||
}
|
||||
@ -237,6 +237,66 @@ u8 ComputeBattleAiScores(u8 battler)
|
||||
return BattleAI_ChooseMoveOrAction();
|
||||
}
|
||||
|
||||
static void CopyBattlerDataToAIParty(u32 bPosition, u32 side)
|
||||
{
|
||||
u32 battler = GetBattlerAtPosition(bPosition);
|
||||
struct AiPartyMon *aiMon = &AI_PARTY->mons[side][gBattlerPartyIndexes[battler]];
|
||||
struct BattlePokemon *bMon = &gBattleMons[battler];
|
||||
|
||||
aiMon->species = bMon->species;
|
||||
aiMon->level = bMon->level;
|
||||
aiMon->gender = GetGenderFromSpeciesAndPersonality(bMon->species, bMon->personality);
|
||||
aiMon->isFainted = FALSE;
|
||||
aiMon->wasSentInBattle = TRUE;
|
||||
aiMon->switchInCount++;
|
||||
}
|
||||
|
||||
void Ai_InitPartyStruct(void)
|
||||
{
|
||||
AI_PARTY->count[B_SIDE_PLAYER] = gPlayerPartyCount;
|
||||
AI_PARTY->count[B_SIDE_OPPONENT] = gEnemyPartyCount;
|
||||
|
||||
// Save first 2 or 4(in doubles) mons
|
||||
CopyBattlerDataToAIParty(B_POSITION_PLAYER_LEFT, B_SIDE_PLAYER);
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
|
||||
CopyBattlerDataToAIParty(B_POSITION_PLAYER_RIGHT, B_SIDE_PLAYER);
|
||||
|
||||
// If player's partner is AI, save opponent mons
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|
||||
{
|
||||
CopyBattlerDataToAIParty(B_POSITION_OPPONENT_LEFT, B_SIDE_OPPONENT);
|
||||
CopyBattlerDataToAIParty(B_POSITION_OPPONENT_RIGHT, B_SIDE_OPPONENT);
|
||||
}
|
||||
}
|
||||
|
||||
void Ai_UpdateSwitchInData(u32 battler)
|
||||
{
|
||||
u32 i;
|
||||
u32 side = GetBattlerSide(battler);
|
||||
struct AiPartyMon *aiMon = &AI_PARTY->mons[side][gBattlerPartyIndexes[battler]];
|
||||
|
||||
// See if the switched-in mon has been already in battle
|
||||
if (aiMon->wasSentInBattle)
|
||||
{
|
||||
if (aiMon->ability)
|
||||
BATTLE_HISTORY->abilities[battler] = aiMon->ability;
|
||||
if (aiMon->heldEffect)
|
||||
BATTLE_HISTORY->itemEffects[battler] = aiMon->heldEffect;
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
if (aiMon->moves[i])
|
||||
BATTLE_HISTORY->usedMoves[battler][i] = aiMon->moves[i];
|
||||
}
|
||||
}
|
||||
else // If not, copy the newly switched-in mon in battle and clear battle history.
|
||||
{
|
||||
ClearBattlerMoveHistory(battler);
|
||||
ClearBattlerAbilityHistory(battler);
|
||||
ClearBattlerItemEffectHistory(battler);
|
||||
CopyBattlerDataToAIParty(GetBattlerPosition(battler), side);
|
||||
}
|
||||
}
|
||||
|
||||
static void SetBattlerAiData(u8 battlerId)
|
||||
{
|
||||
AI_DATA->abilities[battlerId] = AI_GetAbility(battlerId);
|
||||
@ -253,13 +313,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 +327,7 @@ void GetAiLogicData(void)
|
||||
SetBattlerAiData(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// simulate AI damage
|
||||
for (battlerAtk = 0; battlerAtk < gBattlersCount; battlerAtk++)
|
||||
{
|
||||
@ -275,26 +335,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 +394,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 +435,7 @@ static u8 ChooseMoveOrAction_Singles(void)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
numOfBestMoves = 1;
|
||||
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
|
||||
consideredMoveArray[0] = 0;
|
||||
@ -427,7 +487,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 +527,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 +646,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 +654,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 +664,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 +676,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 +692,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 +818,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 +856,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 +920,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check move effects
|
||||
switch (moveEffect)
|
||||
{
|
||||
@ -874,7 +934,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 +980,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 +1290,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 +1300,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 +1452,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 +1630,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 +1862,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 +1922,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 +1964,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 +2012,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 +2176,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 +2573,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 +2601,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 +2621,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 +2630,7 @@ static s16 AI_TryToFaint(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
else
|
||||
score++;
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -2626,8 +2686,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 +2708,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 +2739,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 +2847,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 +2964,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 +2994,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 +3034,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 +3047,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 +3062,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 +3070,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 +3093,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// attacker ability checks
|
||||
switch (AI_DATA->abilities[battlerAtk])
|
||||
{
|
||||
@ -3049,8 +3109,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 +3155,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!AI_RandLessThan(100))
|
||||
{
|
||||
score--;
|
||||
@ -3141,7 +3201,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!AI_RandLessThan(100))
|
||||
{
|
||||
score--;
|
||||
@ -3164,7 +3224,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 +3354,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 +3410,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 +3638,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 +3673,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 +3823,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 +3853,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 +3862,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 +3921,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 +3980,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 +3997,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 +4051,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
score += 3;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
switch (move)
|
||||
{
|
||||
case MOVE_DEFOG:
|
||||
@ -4007,7 +4067,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 +4239,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 +4289,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 +4360,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 +4467,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 +4740,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 +4750,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 +4847,7 @@ static s16 AI_SetupFirstTurn(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -4796,10 +4856,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 +4886,7 @@ static s16 AI_Risky(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
@ -4835,10 +4895,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 +4906,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 +4921,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 +4949,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 +4974,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 +5017,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 +5040,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 +5073,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 +5145,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 +5164,7 @@ static s16 AI_Roaming(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|
||||
{
|
||||
if (IsBattlerTrapped(battlerAtk, FALSE))
|
||||
return score;
|
||||
|
||||
|
||||
AI_Flee();
|
||||
return score;
|
||||
}
|
||||
|
@ -499,6 +499,7 @@ void RecordKnownMove(u8 battlerId, u32 move)
|
||||
if (BATTLE_HISTORY->usedMoves[battlerId][i] == MOVE_NONE)
|
||||
{
|
||||
BATTLE_HISTORY->usedMoves[battlerId][i] = move;
|
||||
AI_PARTY->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].moves[i] = move;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -507,6 +508,7 @@ void RecordKnownMove(u8 battlerId, u32 move)
|
||||
void RecordAbilityBattle(u8 battlerId, u16 abilityId)
|
||||
{
|
||||
BATTLE_HISTORY->abilities[battlerId] = abilityId;
|
||||
AI_PARTY->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].ability = abilityId;
|
||||
}
|
||||
|
||||
void ClearBattlerAbilityHistory(u8 battlerId)
|
||||
@ -517,6 +519,7 @@ void ClearBattlerAbilityHistory(u8 battlerId)
|
||||
void RecordItemEffectBattle(u8 battlerId, u8 itemEffect)
|
||||
{
|
||||
BATTLE_HISTORY->itemEffects[battlerId] = itemEffect;
|
||||
AI_PARTY->mons[GetBattlerSide(battlerId)][gBattlerPartyIndexes[battlerId]].heldEffect = itemEffect;
|
||||
}
|
||||
|
||||
void ClearBattlerItemEffectHistory(u8 battlerId)
|
||||
@ -787,7 +790,7 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness,
|
||||
dmg *= 2;
|
||||
else if (move == MOVE_SURGING_STRIKES || (move == MOVE_WATER_SHURIKEN && gBattleMons[battlerAtk].species == SPECIES_GRENINJA_ASH))
|
||||
dmg *= 3;
|
||||
|
||||
|
||||
if (dmg == 0)
|
||||
dmg = 1;
|
||||
}
|
||||
@ -798,7 +801,7 @@ s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef, u8 *typeEffectiveness,
|
||||
|
||||
RestoreBattlerData(battlerAtk);
|
||||
RestoreBattlerData(battlerDef);
|
||||
|
||||
|
||||
// convert multiper to AI_EFFECTIVENESS_xX
|
||||
*typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier);
|
||||
|
||||
@ -1153,11 +1156,11 @@ bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability)
|
||||
s32 AI_GetAbility(u32 battlerId)
|
||||
{
|
||||
u32 knownAbility = GetBattlerAbility(battlerId);
|
||||
|
||||
|
||||
// The AI knows its own ability.
|
||||
if (IsBattlerAIControlled(battlerId))
|
||||
return knownAbility;
|
||||
|
||||
|
||||
// Check neutralizing gas, gastro acid
|
||||
if (knownAbility == ABILITY_NONE)
|
||||
return knownAbility;
|
||||
@ -1177,10 +1180,10 @@ s32 AI_GetAbility(u32 battlerId)
|
||||
{
|
||||
abilityGuess = gBaseStats[gBattleMons[battlerId].species].abilities[Random() % NUM_ABILITY_SLOTS];
|
||||
}
|
||||
|
||||
|
||||
return abilityGuess;
|
||||
}
|
||||
|
||||
|
||||
return ABILITY_NONE; // Unknown.
|
||||
}
|
||||
|
||||
@ -2668,7 +2671,7 @@ static bool32 AI_CanPoisonType(u8 battlerAttacker, u8 battlerTarget)
|
||||
static bool32 AI_CanBePoisoned(u8 battlerAtk, u8 battlerDef)
|
||||
{
|
||||
u16 ability = AI_DATA->abilities[battlerDef];
|
||||
|
||||
|
||||
if (!(AI_CanPoisonType(battlerAtk, battlerDef))
|
||||
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|
||||
|| gBattleMons[battlerDef].status1 & STATUS1_ANY
|
||||
@ -3019,7 +3022,7 @@ bool32 IsValidDoubleBattle(u8 battlerAtk)
|
||||
u16 GetAllyChosenMove(u8 battlerId)
|
||||
{
|
||||
u8 partnerBattler = BATTLE_PARTNER(battlerId);
|
||||
|
||||
|
||||
if (!IsBattlerAlive(partnerBattler) || !IsBattlerAIControlled(partnerBattler))
|
||||
return MOVE_NONE;
|
||||
else if (partnerBattler > battlerId) // Battler with the lower id chooses the move first.
|
||||
@ -3425,7 +3428,7 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
|
||||
|
||||
if (AI_DATA->hpPercents[battlerAtk] < 80 && AI_RandLessThan(128))
|
||||
return;
|
||||
|
||||
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return; // Damaging moves would get a score boost from AI_TryToFaint or PreferStrongestMove so we don't consider them here
|
||||
|
||||
@ -3544,7 +3547,7 @@ void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
{
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return;
|
||||
|
||||
|
||||
if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove))
|
||||
{
|
||||
u8 atkSpeed = GetBattlerTotalSpeedStat(battlerAtk);
|
||||
@ -3565,7 +3568,7 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
{
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return;
|
||||
|
||||
|
||||
if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove))
|
||||
*score += 2;
|
||||
else
|
||||
@ -3583,7 +3586,7 @@ void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
|
||||
{
|
||||
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|
||||
return;
|
||||
|
||||
|
||||
if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)
|
||||
&& AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_CURE_CONFUSION
|
||||
&& AI_DATA->holdEffects[battlerDef] != HOLD_EFFECT_CURE_STATUS)
|
||||
@ -3614,28 +3617,28 @@ bool32 ShouldUseZMove(u8 battlerAtk, u8 battlerDef, u16 chosenMove)
|
||||
return FALSE; //don't use z move on partner
|
||||
if (gBattleStruct->zmove.used[battlerAtk])
|
||||
return FALSE; //cant use z move twice
|
||||
|
||||
|
||||
if (IsViableZMove(battlerAtk, chosenMove))
|
||||
{
|
||||
u8 effectiveness;
|
||||
|
||||
|
||||
#ifdef POKEMON_EXPANSION
|
||||
if (gBattleMons[battlerDef].ability == ABILITY_DISGUISE && gBattleMons[battlerDef].species == SPECIES_MIMIKYU)
|
||||
return FALSE; // Don't waste a Z-Move busting disguise
|
||||
if (gBattleMons[battlerDef].ability == ABILITY_ICE_FACE && gBattleMons[battlerDef].species == SPECIES_EISCUE && IS_MOVE_PHYSICAL(chosenMove))
|
||||
return FALSE; // Don't waste a Z-Move busting Ice Face
|
||||
#endif
|
||||
|
||||
|
||||
if (IS_MOVE_STATUS(chosenMove) && !IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove))
|
||||
return FALSE;
|
||||
else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove))
|
||||
return FALSE;
|
||||
|
||||
|
||||
if (!IS_MOVE_STATUS(chosenMove) && AI_CalcDamage(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE) >= gBattleMons[battlerDef].hp)
|
||||
return FALSE; // don't waste damaging z move if can otherwise faint target
|
||||
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -43,8 +43,8 @@ struct BattleDebugModifyArrows
|
||||
u16 minValue;
|
||||
u16 maxValue;
|
||||
int currValue;
|
||||
u8 currentDigit;
|
||||
u8 maxDigits;
|
||||
u8 currentDigit:4;
|
||||
u8 maxDigits:4;
|
||||
u8 charDigits[MAX_MODIFY_DIGITS];
|
||||
void *modifiedValPtr;
|
||||
u8 typeOfVal;
|
||||
@ -52,7 +52,9 @@ struct BattleDebugModifyArrows
|
||||
|
||||
struct BattleDebugMenu
|
||||
{
|
||||
u8 battlerId;
|
||||
u8 battlerId:2;
|
||||
u8 aiBattlerId:2;
|
||||
|
||||
u8 battlerWindowId;
|
||||
|
||||
u8 mainListWindowId;
|
||||
@ -72,11 +74,16 @@ struct BattleDebugMenu
|
||||
const struct BitfieldInfo *bitfield;
|
||||
bool8 battlerWasChanged[MAX_BATTLERS_COUNT];
|
||||
|
||||
u8 aiBattlerId;
|
||||
u8 aiViewState;
|
||||
u8 aiIconSpriteIds[MAX_BATTLERS_COUNT];
|
||||
|
||||
u8 aiMonSpriteId;
|
||||
u8 aiMovesWindowId;
|
||||
|
||||
union
|
||||
{
|
||||
u8 aiIconSpriteIds[MAX_BATTLERS_COUNT];
|
||||
u8 aiPartyIcons[PARTY_SIZE];
|
||||
} spriteIds;
|
||||
};
|
||||
|
||||
struct __attribute__((__packed__)) BitfieldInfo
|
||||
@ -102,6 +109,7 @@ enum
|
||||
LIST_ITEM_AI,
|
||||
LIST_ITEM_AI_MOVES_PTS,
|
||||
LIST_ITEM_AI_INFO,
|
||||
LIST_ITEM_AI_PARTY,
|
||||
LIST_ITEM_VARIOUS,
|
||||
LIST_ITEM_COUNT
|
||||
};
|
||||
@ -234,6 +242,7 @@ static const u8 sText_Unknown[] = _("Unknown");
|
||||
static const u8 sText_InLove[] = _("In Love");
|
||||
static const u8 sText_AIMovePts[] = _("AI Pts/Dmg");
|
||||
static const u8 sText_AiKnowledge[] = _("AI Info");
|
||||
static const u8 sText_AiParty[] = _("AI Party");
|
||||
static const u8 sText_EffectOverride[] = _("Effect Override");
|
||||
|
||||
static const u8 sText_EmptyString[] = _("");
|
||||
@ -340,6 +349,7 @@ static const struct ListMenuItem sMainListItems[] =
|
||||
{sText_AI, LIST_ITEM_AI},
|
||||
{sText_AIMovePts, LIST_ITEM_AI_MOVES_PTS},
|
||||
{sText_AiKnowledge, LIST_ITEM_AI_INFO},
|
||||
{sText_AiParty, LIST_ITEM_AI_PARTY},
|
||||
{sText_Various, LIST_ITEM_VARIOUS},
|
||||
};
|
||||
|
||||
@ -610,6 +620,7 @@ static void UpdateMonData(struct BattleDebugMenu *data);
|
||||
static u8 *GetSideStatusValue(struct BattleDebugMenu *data, bool32 changeStatus, bool32 statusTrue);
|
||||
static bool32 TryMoveDigit(struct BattleDebugModifyArrows *modArrows, bool32 moveUp);
|
||||
static void SwitchToDebugView(u8 taskId);
|
||||
static void SwitchToDebugViewFromAiParty(u8 taskId);
|
||||
|
||||
// code
|
||||
static struct BattleDebugMenu *GetStructPtr(u8 taskId)
|
||||
@ -725,9 +736,9 @@ static void PutMovesPointsText(struct BattleDebugMenu *data)
|
||||
AddTextPrinterParameterized(data->aiMovesWindowId, 1, text, 0, i * 15, 0, NULL);
|
||||
for (count = 0, j = 0; j < MAX_BATTLERS_COUNT; j++)
|
||||
{
|
||||
if (data->aiIconSpriteIds[j] == 0xFF)
|
||||
if (data->spriteIds.aiIconSpriteIds[j] == 0xFF)
|
||||
continue;
|
||||
battlerDef = gSprites[data->aiIconSpriteIds[j]].data[0];
|
||||
battlerDef = gSprites[data->spriteIds.aiIconSpriteIds[j]].data[0];
|
||||
ConvertIntToDecimalStringN(text,
|
||||
gBattleStruct->aiFinalScore[data->aiBattlerId][battlerDef][i],
|
||||
STR_CONV_MODE_RIGHT_ALIGN, 3);
|
||||
@ -772,20 +783,20 @@ static void Task_ShowAiPoints(u8 taskId)
|
||||
if (i != data->aiBattlerId && IsBattlerAlive(i))
|
||||
{
|
||||
#ifndef POKEMON_EXPANSION
|
||||
data->aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
|
||||
data->spriteIds.aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
|
||||
SpriteCallbackDummy,
|
||||
95 + (count * 60), 17, 0, 0, FALSE);
|
||||
#else
|
||||
data->aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
|
||||
data->spriteIds.aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
|
||||
SpriteCallbackDummy,
|
||||
95 + (count * 60), 17, 0, 0);
|
||||
#endif
|
||||
gSprites[data->aiIconSpriteIds[i]].data[0] = i; // battler id
|
||||
gSprites[data->spriteIds.aiIconSpriteIds[i]].data[0] = i; // battler id
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->aiIconSpriteIds[i] = 0xFF;
|
||||
data->spriteIds.aiIconSpriteIds[i] = 0xFF;
|
||||
}
|
||||
}
|
||||
#ifndef POKEMON_EXPANSION
|
||||
@ -831,25 +842,26 @@ static void SwitchToAiPointsView(u8 taskId)
|
||||
GetStructPtr(taskId)->aiViewState = 0;
|
||||
}
|
||||
|
||||
static const u8 *const sAiInfoItemNames[] =
|
||||
static const u8 *const sAiInfoItemNames[] =
|
||||
{
|
||||
sText_Ability,
|
||||
sText_HeldItem,
|
||||
sText_HoldEffect,
|
||||
};
|
||||
|
||||
static void PutAiInfoText(struct BattleDebugMenu *data)
|
||||
{
|
||||
u32 i, j, count;
|
||||
u8 *text = malloc(0x50);
|
||||
|
||||
FillWindowPixelBuffer(data->aiMovesWindowId, 0x11);
|
||||
|
||||
|
||||
// item names
|
||||
for (i = 0; i < ARRAY_COUNT(sAiInfoItemNames); i++)
|
||||
{
|
||||
AddTextPrinterParameterized(data->aiMovesWindowId, 1, sAiInfoItemNames[i], 3, i * 15, 0, NULL);
|
||||
}
|
||||
|
||||
|
||||
// items info
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
@ -869,6 +881,31 @@ static void PutAiInfoText(struct BattleDebugMenu *data)
|
||||
free(text);
|
||||
}
|
||||
|
||||
static void PutAiPartyText(struct BattleDebugMenu *data)
|
||||
{
|
||||
u32 i, j, count, maxWidth;
|
||||
u8 *text = malloc(0x50), *txtPtr;
|
||||
struct AiPartyMon *aiMons = AI_PARTY->mons[GET_BATTLER_SIDE(data->aiBattlerId)];
|
||||
|
||||
FillWindowPixelBuffer(data->aiMovesWindowId, 0x11);
|
||||
count = AI_PARTY->count[GET_BATTLER_SIDE(data->aiBattlerId)];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
txtPtr = StringCopyN(text, gAbilityNames[aiMons[i].ability], 7); // The screen is too small to fit the whole string, so we need to drop the last letters.
|
||||
*txtPtr = EOS;
|
||||
AddTextPrinterParameterized5(data->aiMovesWindowId, FONT_SMALL_NARROW, text, i * 41, 0, 0, NULL, 0, 0);
|
||||
for (j = 0; j < MAX_MON_MOVES; j++)
|
||||
{
|
||||
txtPtr = StringCopyN(text, gMoveNames[aiMons[i].moves[j]], 8);
|
||||
*txtPtr = EOS;
|
||||
AddTextPrinterParameterized5(data->aiMovesWindowId, FONT_SMALL_NARROW, text, i * 41, 20 + j * 15, 0, NULL, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
CopyWindowToVram(data->aiMovesWindowId, 3);
|
||||
free(text);
|
||||
}
|
||||
|
||||
static void Task_ShowAiKnowledge(u8 taskId)
|
||||
{
|
||||
u32 i, count;
|
||||
@ -895,20 +932,20 @@ static void Task_ShowAiKnowledge(u8 taskId)
|
||||
if (GET_BATTLER_SIDE(i) == B_SIDE_PLAYER && IsBattlerAlive(i))
|
||||
{
|
||||
#ifndef POKEMON_EXPANSION
|
||||
data->aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
|
||||
data->spriteIds.aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
|
||||
SpriteCallbackDummy,
|
||||
95 + (count * 80), 17, 0, 0, FALSE);
|
||||
#else
|
||||
data->aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
|
||||
data->spriteIds.aiIconSpriteIds[i] = CreateMonIcon(gBattleMons[i].species,
|
||||
SpriteCallbackDummy,
|
||||
95 + (count * 80), 17, 0, 0);
|
||||
#endif
|
||||
gSprites[data->aiIconSpriteIds[i]].data[0] = i; // battler id
|
||||
gSprites[data->spriteIds.aiIconSpriteIds[i]].data[0] = i; // battler id
|
||||
count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
data->aiIconSpriteIds[i] = 0xFF;
|
||||
data->spriteIds.aiIconSpriteIds[i] = 0xFF;
|
||||
}
|
||||
}
|
||||
#ifndef POKEMON_EXPANSION
|
||||
@ -947,12 +984,82 @@ static void Task_ShowAiKnowledge(u8 taskId)
|
||||
}
|
||||
}
|
||||
|
||||
static void Task_ShowAiParty(u8 taskId)
|
||||
{
|
||||
u32 i;
|
||||
struct WindowTemplate winTemplate;
|
||||
struct AiPartyMon *aiMons;
|
||||
struct BattleDebugMenu *data = GetStructPtr(taskId);
|
||||
|
||||
switch (data->aiViewState)
|
||||
{
|
||||
case 0:
|
||||
HideBg(0);
|
||||
ShowBg(1);
|
||||
|
||||
LoadMonIconPalettes();
|
||||
data->aiBattlerId = data->battlerId;
|
||||
aiMons = AI_PARTY->mons[GET_BATTLER_SIDE(data->aiBattlerId)];
|
||||
for (i = 0; i < AI_PARTY->count[GET_BATTLER_SIDE(data->aiBattlerId)]; i++)
|
||||
{
|
||||
u16 species = SPECIES_OLD_UNOWN_B; // Question mark
|
||||
if (aiMons[i].wasSentInBattle && aiMons[i].species)
|
||||
species = aiMons[i].species;
|
||||
data->spriteIds.aiPartyIcons[i] = CreateMonIcon(species, SpriteCallbackDummy, (i * 41) - 5 + 20, 7, 0, 0, FALSE);
|
||||
}
|
||||
for (; i < PARTY_SIZE; i++)
|
||||
data->spriteIds.aiPartyIcons[i] = 0xFF;
|
||||
data->aiViewState++;
|
||||
break;
|
||||
// Put text
|
||||
case 1:
|
||||
winTemplate = CreateWindowTemplate(1, 0, 4, 30, 14, 15, 0x200);
|
||||
data->aiMovesWindowId = AddWindow(&winTemplate);
|
||||
PutWindowTilemap(data->aiMovesWindowId);
|
||||
PutAiPartyText(data);
|
||||
data->aiViewState++;
|
||||
break;
|
||||
// Input
|
||||
case 2:
|
||||
if (gMain.newKeys & (SELECT_BUTTON | B_BUTTON))
|
||||
{
|
||||
SwitchToDebugViewFromAiParty(taskId);
|
||||
HideBg(1);
|
||||
ShowBg(0);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void SwitchToAiInfoView(u8 taskId)
|
||||
{
|
||||
gTasks[taskId].func = Task_ShowAiKnowledge;
|
||||
GetStructPtr(taskId)->aiViewState = 0;
|
||||
}
|
||||
|
||||
static void SwitchToAiPartyView(u8 taskId)
|
||||
{
|
||||
gTasks[taskId].func = Task_ShowAiParty;
|
||||
GetStructPtr(taskId)->aiViewState = 0;
|
||||
}
|
||||
|
||||
static void SwitchToDebugViewFromAiParty(u8 taskId)
|
||||
{
|
||||
u32 i;
|
||||
struct BattleDebugMenu *data = GetStructPtr(taskId);
|
||||
|
||||
FreeMonIconPalettes();
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
if (data->spriteIds.aiPartyIcons[i] != 0xFF)
|
||||
FreeAndDestroyMonIconSprite(&gSprites[data->spriteIds.aiPartyIcons[i]]);
|
||||
}
|
||||
RemoveWindow(data->aiMovesWindowId);
|
||||
|
||||
gTasks[taskId].func = Task_DebugMenuProcessInput;
|
||||
}
|
||||
|
||||
static void SwitchToDebugView(u8 taskId)
|
||||
{
|
||||
u32 i;
|
||||
@ -961,8 +1068,8 @@ static void SwitchToDebugView(u8 taskId)
|
||||
FreeMonIconPalettes();
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
{
|
||||
if (data->aiIconSpriteIds[i] != 0xFF)
|
||||
FreeAndDestroyMonIconSprite(&gSprites[data->aiIconSpriteIds[i]]);
|
||||
if (data->spriteIds.aiIconSpriteIds[i] != 0xFF)
|
||||
FreeAndDestroyMonIconSprite(&gSprites[data->spriteIds.aiIconSpriteIds[i]]);
|
||||
}
|
||||
FreeAndDestroyMonPicSprite(data->aiMonSpriteId);
|
||||
RemoveWindow(data->aiMovesWindowId);
|
||||
@ -1019,6 +1126,11 @@ static void Task_DebugMenuProcessInput(u8 taskId)
|
||||
SwitchToAiInfoView(taskId);
|
||||
return;
|
||||
}
|
||||
else if (listItemId == LIST_ITEM_AI_PARTY && gMain.newKeys & A_BUTTON)
|
||||
{
|
||||
SwitchToAiPartyView(taskId);
|
||||
return;
|
||||
}
|
||||
data->currentMainListItemId = listItemId;
|
||||
|
||||
// Create the secondary menu list.
|
||||
@ -2040,7 +2152,7 @@ static const u8 sText_HoldEffectRoomService[] = _("Room Service");
|
||||
static const u8 sText_HoldEffectBlunderPolicy[] = _("Blunder Policy");
|
||||
static const u8 sText_HoldEffectHeavyDutyBoots[] = _("Heavy Duty Boots");
|
||||
static const u8 sText_HoldEffectThroatSpray[] = _("Throat Spray");
|
||||
static const u8 *const sHoldEffectNames[] =
|
||||
static const u8 *const sHoldEffectNames[] =
|
||||
{
|
||||
[HOLD_EFFECT_NONE] = sText_HoldEffectNone,
|
||||
[HOLD_EFFECT_RESTORE_HP] = sText_HoldEffectRestoreHp,
|
||||
|
@ -947,7 +947,7 @@ static void CB2_HandleStartBattle(void)
|
||||
// Recv Pokémon 5-6
|
||||
ResetBlockReceivedFlags();
|
||||
memcpy(&gEnemyParty[4], gBlockRecvBuffer[enemyMultiplayerId], sizeof(struct Pokemon) * 2);
|
||||
|
||||
|
||||
TryCorrectShedinjaLanguage(&gEnemyParty[0]);
|
||||
TryCorrectShedinjaLanguage(&gEnemyParty[1]);
|
||||
TryCorrectShedinjaLanguage(&gEnemyParty[2]);
|
||||
@ -2856,7 +2856,7 @@ static void SpriteCB_TrainerThrowObject_Main(struct Sprite *sprite)
|
||||
sprite->callback = SpriteCB_Idle;
|
||||
}
|
||||
|
||||
// Sprite callback for a trainer back pic to throw an object
|
||||
// Sprite callback for a trainer back pic to throw an object
|
||||
// (Wally throwing a ball, throwing Pokéblocks/balls in the Safari Zone)
|
||||
void SpriteCB_TrainerThrowObject(struct Sprite *sprite)
|
||||
{
|
||||
@ -2991,10 +2991,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;
|
||||
@ -3094,7 +3094,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
|
||||
|
||||
@ -3110,14 +3110,12 @@ void SwitchInClearSetData(void)
|
||||
gBattleResources->flags->flags[gActiveBattler] = 0;
|
||||
gCurrentMove = 0;
|
||||
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;
|
||||
|
||||
ClearBattlerMoveHistory(gActiveBattler);
|
||||
ClearBattlerAbilityHistory(gActiveBattler);
|
||||
ClearBattlerItemEffectHistory(gActiveBattler);
|
||||
Ai_UpdateSwitchInData(gActiveBattler);
|
||||
}
|
||||
|
||||
void FaintClearSetData(void)
|
||||
@ -3194,7 +3192,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
|
||||
|
||||
@ -3572,7 +3570,8 @@ static void DoBattleIntro(void)
|
||||
gBattleStruct->switchInAbilitiesCounter = 0;
|
||||
gBattleStruct->switchInItemsCounter = 0;
|
||||
gBattleStruct->overworldWeatherDone = FALSE;
|
||||
|
||||
GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers
|
||||
Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield.
|
||||
gBattleMainFunc = TryDoEventsBeforeFirstTurn;
|
||||
}
|
||||
break;
|
||||
@ -3706,8 +3705,6 @@ static void TryDoEventsBeforeFirstTurn(void)
|
||||
gMoveResultFlags = 0;
|
||||
|
||||
gRandomTurnNumber = Random();
|
||||
|
||||
GetAiLogicData(); // get assumed abilities, hold effects, etc of all battlers
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
{
|
||||
@ -3914,7 +3911,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);
|
||||
@ -4574,7 +4571,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)
|
||||
|
@ -27,6 +27,7 @@ void AllocateBattleResources(void)
|
||||
gBattleResources->beforeLvlUp = AllocZeroed(sizeof(*gBattleResources->beforeLvlUp));
|
||||
gBattleResources->ai = AllocZeroed(sizeof(*gBattleResources->ai));
|
||||
gBattleResources->aiData = AllocZeroed(sizeof(*gBattleResources->aiData));
|
||||
gBattleResources->aiParty = AllocZeroed(sizeof(*gBattleResources->aiParty));
|
||||
gBattleResources->battleHistory = AllocZeroed(sizeof(*gBattleResources->battleHistory));
|
||||
|
||||
gLinkBattleSendBuffer = AllocZeroed(BATTLE_BUFFER_LINK_SIZE);
|
||||
@ -59,6 +60,7 @@ void FreeBattleResources(void)
|
||||
FREE_AND_SET_NULL(gBattleResources->beforeLvlUp);
|
||||
FREE_AND_SET_NULL(gBattleResources->ai);
|
||||
FREE_AND_SET_NULL(gBattleResources->aiData);
|
||||
FREE_AND_SET_NULL(gBattleResources->aiParty);
|
||||
FREE_AND_SET_NULL(gBattleResources->battleHistory);
|
||||
FREE_AND_SET_NULL(gBattleResources);
|
||||
|
||||
|
BIN
vbalink.ini
Normal file
BIN
vbalink.ini
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user