From 2627d68782c2823e3ba84306a5a17097b517c270 Mon Sep 17 00:00:00 2001 From: Maruno17 Date: Mon, 16 Jan 2023 19:29:28 +0000 Subject: [PATCH] Added more debug logging to AI, fixed some bugs in AI --- .../001_Debugging/001_PBDebug.rb | 21 +++++ .../001_Battle/005_Battle_ActionSwitching.rb | 2 +- .../001_Battle/009_Battle_CommandPhase.rb | 2 +- .../001_Battle/010_Battle_AttackPhase.rb | 1 - .../003_Move/007_MoveEffects_BattlerOther.rb | 13 ++- .../003_Move/009_MoveEffects_MultiHit.rb | 11 ++- .../003_Move/010_MoveEffects_Healing.rb | 17 ++-- .../011_Battle/005_AI/001_Battle_AI.rb | 1 + Data/Scripts/011_Battle/005_AI/002_AI_Item.rb | 4 +- .../011_Battle/005_AI/003_AI_Switch.rb | 5 +- .../011_Battle/005_AI/004_AI_ChooseMove.rb | 58 +++++++++---- .../011_Battle/005_AI/005_AI_MegaEvolve.rb | 2 +- .../005_AI/020_AI_Move_EffectScoresGeneric.rb | 60 +++++++++++--- .../052_AI_MoveHandlers_BattlerStats.rb | 50 +++++------ .../053_AI_MoveHandlers_BattlerOther.rb | 2 +- .../005_AI/056_AI_MoveHandlers_Healing.rb | 2 +- .../058_AI_MoveHandlers_ChangeMoveEffect.rb | 23 ++--- .../070_AI_MoveHandlers_GeneralModifiers.rb | 83 ++++++++++++++----- .../011_Battle/005_AI/102_AIBattler.rb | 8 +- Data/Scripts/011_Battle/005_AI/103_AIMove.rb | 2 +- .../debug battle tests.rb} | 28 +++++-- 21 files changed, 280 insertions(+), 115 deletions(-) rename Data/Scripts/{999_Main/002_debug battle test.rb => 022_Maruno/debug battle tests.rb} (80%) diff --git a/Data/Scripts/001_Technical/001_Debugging/001_PBDebug.rb b/Data/Scripts/001_Technical/001_Debugging/001_PBDebug.rb index 459d2c723..9aaf9733e 100644 --- a/Data/Scripts/001_Technical/001_Debugging/001_PBDebug.rb +++ b/Data/Scripts/001_Technical/001_Debugging/001_PBDebug.rb @@ -48,6 +48,27 @@ module PBDebug end end + def self.log_ai(msg) + if $DEBUG && $INTERNAL + msg = "[AI] " + msg + echoln msg.gsub("%", "%%") + @@log.push(msg + "\r\n") + PBDebug.flush # if @@log.length > 1024 + end + end + + def self.log_score_change(amt, msg) + return if amt == 0 + if $DEBUG && $INTERNAL + sign = (amt > 0) ? "+" : "-" + amt_text = sprintf("%3d", amt.abs) + msg = " #{sign}#{amt_text}: #{msg}" + echoln msg.gsub("%", "%%") + @@log.push(msg + "\r\n") + PBDebug.flush # if @@log.length > 1024 + end + end + def self.dump(msg) if $DEBUG && $INTERNAL File.open("Data/dumplog.txt", "a+b") { |f| f.write("#{msg}\r\n") } diff --git a/Data/Scripts/011_Battle/001_Battle/005_Battle_ActionSwitching.rb b/Data/Scripts/011_Battle/001_Battle/005_Battle_ActionSwitching.rb index 7a80aba15..25cc5ef8c 100644 --- a/Data/Scripts/011_Battle/001_Battle/005_Battle_ActionSwitching.rb +++ b/Data/Scripts/011_Battle/001_Battle/005_Battle_ActionSwitching.rb @@ -129,7 +129,7 @@ class Battle # For choosing a replacement Pokémon when prompted in the middle of other # things happening (U-turn, Baton Pass, in def pbEORSwitch). def pbSwitchInBetween(idxBattler, checkLaxOnly = false, canCancel = false) - return pbPartyScreen(idxBattler, checkLaxOnly, canCancel) if pbOwnedByPlayer?(idxBattler) + return pbPartyScreen(idxBattler, checkLaxOnly, canCancel) if !@controlPlayer && pbOwnedByPlayer?(idxBattler) return @battleAI.pbDefaultChooseNewEnemy(idxBattler, pbParty(idxBattler)) end diff --git a/Data/Scripts/011_Battle/001_Battle/009_Battle_CommandPhase.rb b/Data/Scripts/011_Battle/001_Battle/009_Battle_CommandPhase.rb index 99f4b6e7c..4ec07771a 100644 --- a/Data/Scripts/011_Battle/001_Battle/009_Battle_CommandPhase.rb +++ b/Data/Scripts/011_Battle/001_Battle/009_Battle_CommandPhase.rb @@ -207,7 +207,7 @@ class Battle next if !@battlers[idxBattler] || pbOwnedByPlayer?(idxBattler) != isPlayer if @choices[idxBattler][0] != :None || !pbCanShowCommands?(idxBattler) # Action is forced, can't choose one - PBDebug.log("[AI] #{@battlers[idxBattler].pbThis} (#{idxBattler}) is forced into using a multi-turn move") + PBDebug.log_ai("#{@battlers[idxBattler].pbThis} (#{idxBattler}) is forced to use a multi-turn move") next end # AI controls this battler diff --git a/Data/Scripts/011_Battle/001_Battle/010_Battle_AttackPhase.rb b/Data/Scripts/011_Battle/001_Battle/010_Battle_AttackPhase.rb index ab53de1a0..02ddb9674 100644 --- a/Data/Scripts/011_Battle/001_Battle/010_Battle_AttackPhase.rb +++ b/Data/Scripts/011_Battle/001_Battle/010_Battle_AttackPhase.rb @@ -187,7 +187,6 @@ class Battle b.effects[PBEffects::Rage] = false if !pbChoseMoveFunctionCode?(i, "StartRaiseUserAtk1WhenDamaged") end # Calculate move order for this round - PBDebug.log("") pbCalculatePriority(true) PBDebug.log("") # Perform actions diff --git a/Data/Scripts/011_Battle/003_Move/007_MoveEffects_BattlerOther.rb b/Data/Scripts/011_Battle/003_Move/007_MoveEffects_BattlerOther.rb index 7b9ef3dfc..673c7dea8 100644 --- a/Data/Scripts/011_Battle/003_Move/007_MoveEffects_BattlerOther.rb +++ b/Data/Scripts/011_Battle/003_Move/007_MoveEffects_BattlerOther.rb @@ -100,11 +100,18 @@ end # Poisons the target and decreases its Speed by 1 stage. (Toxic Thread) #=============================================================================== class Battle::Move::PoisonTargetLowerTargetSpeed1 < Battle::Move + attr_reader :statDown + + def initialize(battle, move) + super + @statDown = [:SPEED, 1] + end + def canMagicCoat?; return true; end def pbFailsAgainstTarget?(user, target, show_message) if !target.pbCanPoison?(user, false, self) && - !target.pbCanLowerStatStage?(:SPEED, user, self) + !target.pbCanLowerStatStage?(@statDown[0], user, self) @battle.pbDisplay(_INTL("But it failed!")) if show_message return true end @@ -113,8 +120,8 @@ class Battle::Move::PoisonTargetLowerTargetSpeed1 < Battle::Move def pbEffectAgainstTarget(user, target) target.pbPoison(user) if target.pbCanPoison?(user, false, self) - if target.pbCanLowerStatStage?(:SPEED, user, self) - target.pbLowerStatStage(:SPEED, 1, user) + if target.pbCanLowerStatStage?(@statDown[0], user, self) + target.pbLowerStatStage(@statDown[0], @statDown[1], user) end end end diff --git a/Data/Scripts/011_Battle/003_Move/009_MoveEffects_MultiHit.rb b/Data/Scripts/011_Battle/003_Move/009_MoveEffects_MultiHit.rb index 1112c2dc7..e6e2b2745 100644 --- a/Data/Scripts/011_Battle/003_Move/009_MoveEffects_MultiHit.rb +++ b/Data/Scripts/011_Battle/003_Move/009_MoveEffects_MultiHit.rb @@ -334,13 +334,20 @@ end # (Skull Bash) #=============================================================================== class Battle::Move::TwoTurnAttackChargeRaiseUserDefense1 < Battle::Move::TwoTurnMove + attr_reader :statUp + + def initialize(battle, move) + super + @statUp = [:DEFENSE, 1] + end + def pbChargingTurnMessage(user, targets) @battle.pbDisplay(_INTL("{1} tucked in its head!", user.pbThis)) end def pbChargingTurnEffect(user, target) - if user.pbCanRaiseStatStage?(:DEFENSE, user, self) - user.pbRaiseStatStage(:DEFENSE, 1, user) + if user.pbCanRaiseStatStage?(@statUp[0], user, self) + user.pbRaiseStatStage(@statUp[0], @statUp[1], user) end end end diff --git a/Data/Scripts/011_Battle/003_Move/010_MoveEffects_Healing.rb b/Data/Scripts/011_Battle/003_Move/010_MoveEffects_Healing.rb index 16a9dbed3..eae6ba8c2 100644 --- a/Data/Scripts/011_Battle/003_Move/010_MoveEffects_Healing.rb +++ b/Data/Scripts/011_Battle/003_Move/010_MoveEffects_Healing.rb @@ -109,6 +109,13 @@ end # it). (Strength Sap) #=============================================================================== class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move + attr_reader :statDown + + def initialize(battle, move) + super + @statDown = [:ATTACK, 1] + end + def healingMove?; return true; end def canMagicCoat?; return true; end @@ -119,11 +126,11 @@ class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move # works even if the stat stage cannot be changed due to an ability or # other effect. if !@battle.moldBreaker && target.hasActiveAbility?(:CONTRARY) - if target.statStageAtMax?(:ATTACK) + if target.statStageAtMax?(@statDown[0]) @battle.pbDisplay(_INTL("But it failed!")) if show_message return true end - elsif target.statStageAtMin?(:ATTACK) + elsif target.statStageAtMin?(@statDown[0]) @battle.pbDisplay(_INTL("But it failed!")) if show_message return true end @@ -135,11 +142,11 @@ class Battle::Move::HealUserByTargetAttackLowerTargetAttack1 < Battle::Move stageMul = [2, 2, 2, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8] stageDiv = [8, 7, 6, 5, 4, 3, 2, 2, 2, 2, 2, 2, 2] atk = target.attack - atkStage = target.stages[:ATTACK] + 6 + atkStage = target.stages[@statDown[0]] + 6 healAmt = (atk.to_f * stageMul[atkStage] / stageDiv[atkStage]).floor # Reduce target's Attack stat - if target.pbCanLowerStatStage?(:ATTACK, user, self) - target.pbLowerStatStage(:ATTACK, 1, user) + if target.pbCanLowerStatStage?(@statDown[0], user, self) + target.pbLowerStatStage(@statDown[0], @statDown[1], user) end # Heal user if target.hasActiveAbility?(:LIQUIDOOZE, true) diff --git a/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb b/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb index df7957e6b..d10031bb1 100644 --- a/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb +++ b/Data/Scripts/011_Battle/005_AI/001_Battle_AI.rb @@ -57,6 +57,7 @@ class Battle::AI @battle.pbRegisterMegaEvolution(idxBattler) if pbEnemyShouldMegaEvolve? choices = pbGetMoveScores pbChooseMove(choices) + PBDebug.log("") end end diff --git a/Data/Scripts/011_Battle/005_AI/002_AI_Item.rb b/Data/Scripts/011_Battle/005_AI/002_AI_Item.rb index ffee02b2f..465e939bd 100644 --- a/Data/Scripts/011_Battle/005_AI/002_AI_Item.rb +++ b/Data/Scripts/011_Battle/005_AI/002_AI_Item.rb @@ -1,6 +1,8 @@ class Battle::AI #============================================================================= # Decide whether the opponent should use an item on the Pokémon + # TODO: Maybe don't cure a status problem if the Pokémon has an ability or + # something that makes it benefit from having that problem. #============================================================================= def pbEnemyShouldUseItem? item = nil @@ -14,7 +16,7 @@ class Battle::AI end # Register use of item @battle.pbRegisterItem(@user.index, item, idxTarget) - PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will use item #{GameData::Item.get(item).name}") + PBDebug.log_ai("#{@user.name} will use item #{GameData::Item.get(item).name}") return true end diff --git a/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb b/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb index ada1b6079..d27d46517 100644 --- a/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb +++ b/Data/Scripts/011_Battle/005_AI/003_AI_Switch.rb @@ -133,12 +133,11 @@ class Battle::AI end if list.length > 0 if batonPass >= 0 && @battle.pbRegisterMove(battler.index, batonPass, false) - PBDebug.log("[AI] #{battler.pbThis} (#{battler.index}) will use Baton Pass to avoid Perish Song") + PBDebug.log_ai("#{@user.name} will use Baton Pass to avoid Perish Song") return true end if @battle.pbRegisterSwitch(battler.index, list[0]) - PBDebug.log("[AI] #{battler.pbThis} (#{battler.index}) will switch with " + - @battle.pbParty(battler.index)[list[0]].name) + PBDebug.log_ai("#{@user.name} will switch with #{@battle.pbParty(battler.index)[list[0]].name}") return true end end diff --git a/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb b/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb index a899c9611..39daeecf0 100644 --- a/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb +++ b/Data/Scripts/011_Battle/005_AI/004_AI_ChooseMove.rb @@ -23,16 +23,18 @@ class Battle::AI # Unchoosable moves aren't considered if !@battle.pbCanChooseMove?(@user.index, idxMove, false) if move.pp == 0 && move.total_pp > 0 - PBDebug.log("[AI] #{@user.battler.pbThis} (#{@user.index}) cannot use move #{move.name} as it has no PP left") + PBDebug.log_ai("#{@user.name} cannot use #{move.name} (no PP left)") else - PBDebug.log("[AI] #{@user.battler.pbThis} (#{@user.index}) cannot choose to use #{move.name}") + PBDebug.log_ai("#{@user.name} cannot choose to use #{move.name}") end next end + PBDebug.log_ai("#{@user.name} is considering using #{move.name}...") # Set up move in class variables set_up_move_check(move) # Predict whether the move will fail (generally) if @trainer.has_skill_flag?("PredictMoveFailure") && pbPredictMoveFailure + PBDebug.log_score_change(MOVE_FAIL_SCORE - MOVE_BASE_SCORE, "move will fail") add_move_to_choices(choices, idxMove, MOVE_FAIL_SCORE) next end @@ -49,6 +51,7 @@ class Battle::AI # TODO: Figure out first which targets are valid. Includes the call to # pbMoveCanTarget?, but also includes move-redirecting effects # like Lightning Rod. Skip any battlers that can't be targeted. + num_targets = 0 @battle.allBattlers.each do |b| next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data) # TODO: Should this sometimes consider targeting an ally? See def @@ -57,7 +60,9 @@ class Battle::AI score = MOVE_BASE_SCORE PBDebug.logonerr { score = pbGetMoveScore([b]) } add_move_to_choices(choices, idxMove, score, b.index) + num_targets += 1 end + PBDebug.log(" no valid targets") if num_targets == 0 else # Multiple targets at once # Includes: AllAllies, AllBattlers, AllFoes, AllNearFoes, AllNearOthers, UserAndAllies targets = [] @@ -100,7 +105,7 @@ class Battle::AI move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(target.battler.lastRegularMoveUsed)) end when "UseMoveDependingOnEnvironment" - move.move.pbOnStartUse(@user.battler, []) # Determine which move is used instead + move.pbOnStartUse(@user.battler, []) # Determine which move is used instead move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(move.npMove)) end @move.set_up(move, @user) @@ -197,13 +202,18 @@ class Battle::AI end # Score based on how many targets were affected if affected_targets == 0 && @trainer.has_skill_flag?("PredictMoveFailure") - return MOVE_FAIL_SCORE if !@move.move.worksWithNoTargets? + if !@move.move.worksWithNoTargets? + PBDebug.log_score_change(MOVE_FAIL_SCORE - MOVE_BASE_SCORE, "move will fail") + return MOVE_FAIL_SCORE + end else # TODO: Can this accounting for multiple targets be improved somehow? score /= affected_targets if affected_targets > 1 # Average the score against multiple targets # Bonus for affecting multiple targets if @trainer.has_skill_flag?("PreferMultiTargetMoves") && affected_targets > 1 + old_score = score score += (affected_targets - 1) * 10 + PBDebug.log_score_change(score - old_score, "affects multiple battlers") end end end @@ -212,8 +222,10 @@ class Battle::AI # Self-Destruct) if @trainer.has_skill_flag?("ScoreMoves") # Modify the score according to the move's effect + old_score = score score = Battle::AI::Handlers.apply_move_effect_score(@move.function, score, @move, @user, self, @battle) + PBDebug.log_score_change(score - old_score, "function code modifier (generic)") # Modify the score according to various other effects score = Battle::AI::Handlers.apply_general_move_score_modifiers( score, @move, @user, self, @battle) @@ -240,15 +252,18 @@ class Battle::AI #============================================================================= def pbGetMoveScoreAgainstTarget # Predict whether the move will fail against the target - if @trainer.has_skill_flag?("PredictMoveFailure") - return -1 if pbPredictMoveFailureAgainstTarget + if @trainer.has_skill_flag?("PredictMoveFailure") && pbPredictMoveFailureAgainstTarget + PBDebug.log(" move will not affect #{@target.name}") + return -1 end # Score the move score = MOVE_BASE_SCORE if @trainer.has_skill_flag?("ScoreMoves") # Modify the score according to the move's effect against the target + old_score = score score = Battle::AI::Handlers.apply_move_effect_against_target_score(@move.function, MOVE_BASE_SCORE, @move, @user, @target, self, @battle) + PBDebug.log_score_change(score - old_score, "function code modifier (against target)") # Modify the score according to various other effects against the target score = Battle::AI::Handlers.apply_general_move_against_target_score_modifiers( score, @move, @user, @target, self, @battle) @@ -256,9 +271,14 @@ class Battle::AI # Add the score against the target to the overall score target_data = @move.pbTarget(@user.battler) if target_data.targets_foe && !@target.opposes?(@user) && @target.index != @user.index - return -1 if score == MOVE_USELESS_SCORE + if score == MOVE_USELESS_SCORE + PBDebug.log(" move is useless against #{@target.name}") + return -1 + end # TODO: Is this reversal of the score okay? + old_score = score score = 175 - score + PBDebug.log_score_change(score - old_score, "score inverted (move targets ally but can target foe)") end return score end @@ -272,7 +292,7 @@ class Battle::AI # If no moves can be chosen, auto-choose a move or Struggle if choices.length == 0 @battle.pbAutoChooseMove(user_battler.index) - PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will auto-use a move or Struggle") + PBDebug.log_ai("#{@user.name} will auto-use a move or Struggle") return end # Figure out useful information about the choices @@ -289,9 +309,10 @@ class Battle::AI badMoves = choices.none? { |c| user_battler.moves[c[0]].damagingMove? } badMoves = false if badMoves && pbAIRandom(100) < 10 end - if badMoves && pbEnemyShouldWithdrawEx?(true) - PBDebug.log("[AI] #{user_battler.pbThis} (#{user_battler.index}) will switch due to terrible moves") - return + if badMoves + PBDebug.log_ai("#{@user.name} wants to switch due to terrible moves") + return if pbEnemyShouldWithdrawEx?(true) + PBDebug.log_ai("#{@user.name} won't switch after all") end end # Calculate a minimum score threshold and reduce all move scores by it @@ -300,12 +321,12 @@ class Battle::AI total_score = choices.sum { |c| c[3] } # Log the available choices if $INTERNAL - PBDebug.log("[AI] Move choices for #{user_battler.pbThis(true)} (#{user_battler.index}):") + PBDebug.log_ai("Move choices for #{@user.name}:") choices.each_with_index do |c, i| chance = sprintf("%5.1f", (c[3] > 0) ? 100.0 * c[3] / total_score : 0) - log_msg = " * #{chance}% chance: #{user_battler.moves[c[0]].name}" - log_msg += " (against target #{c[2]})" if c[2] >= 0 - log_msg += " = score #{c[1]}" + log_msg = " * #{chance}% to use #{user_battler.moves[c[0]].name}" + log_msg += " (target #{c[2]})" if c[2] >= 0 + log_msg += ": score #{c[1]}" PBDebug.log(log_msg) end end @@ -320,7 +341,12 @@ class Battle::AI end # Log the result if @battle.choices[user_battler.index][2] - PBDebug.log(" => will use #{@battle.choices[user_battler.index][2].name}") + move_name = @battle.choices[user_battler.index][2].name + if @battle.choices[user_battler.index][3] >= 0 + PBDebug.log(" => will use #{move_name} (target #{@battle.choices[user_battler.index][3]})") + else + PBDebug.log(" => will use #{move_name}") + end end end end diff --git a/Data/Scripts/011_Battle/005_AI/005_AI_MegaEvolve.rb b/Data/Scripts/011_Battle/005_AI/005_AI_MegaEvolve.rb index 6909ee008..758bb82c8 100644 --- a/Data/Scripts/011_Battle/005_AI/005_AI_MegaEvolve.rb +++ b/Data/Scripts/011_Battle/005_AI/005_AI_MegaEvolve.rb @@ -6,7 +6,7 @@ class Battle::AI # be. def pbEnemyShouldMegaEvolve? if @battle.pbCanMegaEvolve?(@user.index) # Simple "always should if possible" - PBDebug.log("[AI] #{@user.pbThis} (#{@user.index}) will Mega Evolve") + PBDebug.log_ai("#{@user.name} will Mega Evolve") return true end return false diff --git a/Data/Scripts/011_Battle/005_AI/020_AI_Move_EffectScoresGeneric.rb b/Data/Scripts/011_Battle/005_AI/020_AI_Move_EffectScoresGeneric.rb index 3d8ad1903..b867aaaa2 100644 --- a/Data/Scripts/011_Battle/005_AI/020_AI_Move_EffectScoresGeneric.rb +++ b/Data/Scripts/011_Battle/005_AI/020_AI_Move_EffectScoresGeneric.rb @@ -18,12 +18,16 @@ class Battle::AI # Discard status move/don't prefer damaging move if target has Contrary # TODO: Maybe this should return get_score_for_target_stat_drop if Contrary # applies and desire_mult < 1. - if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1 - return (whole_effect) ? MOVE_USELESS_SCORE : score - 20 + if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 0 + ret = (whole_effect) ? MOVE_USELESS_SCORE : score - 20 + PBDebug.log_score_change(ret - score, "don't prefer raising target's stats (it has Contrary)") + return ret end # Don't make score changes if target will faint from EOR damage if target.rough_end_of_round_damage >= target.hp - return (whole_effect) ? MOVE_USELESS_SCORE : score + ret = (whole_effect) ? MOVE_USELESS_SCORE : score + PBDebug.log(" ignore stat change (target predicted to faint this round)") + return ret end # Don't make score changes if foes have Unaware and target can't make use of # extra stat stages @@ -33,7 +37,9 @@ class Battle::AI foe_is_aware = true if !b.has_active_ability?(:UNAWARE) end if !foe_is_aware - return (whole_effect) ? MOVE_USELESS_SCORE : score + ret = (whole_effect) ? MOVE_USELESS_SCORE : score + PBDebug.log(" ignore stat change (target's foes have Unaware)") + return ret end end @@ -41,7 +47,14 @@ class Battle::AI real_stat_changes = [] stat_changes.each_with_index do |stat, idx| next if idx.odd? - next if !stat_raise_worthwhile?(target, stat, fixed_change) + if !stat_raise_worthwhile?(target, stat, fixed_change) + if target.index == @user.index + PBDebug.log(" raising the user's #{GameData::Stat.get(stat).name} isn't worthwhile") + else + PBDebug.log(" raising the target's #{GameData::Stat.get(stat).name} isn't worthwhile") + end + next + end # Calculate amount that stat will be raised by increment = stat_changes[idx + 1] increment *= 2 if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:SIMPLE) @@ -60,7 +73,13 @@ class Battle::AI # Make score changes based on the specific changes to each stat that will be # raised real_stat_changes.each do |change| + old_score = score score = get_target_stat_raise_score_one(score, target, change[0], change[1], desire_mult) + if target.index == @user.index + PBDebug.log_score_change(score - old_score, "raising the user's #{GameData::Stat.get(change[0]).name} by #{change[1]}") + else + PBDebug.log_score_change(score - old_score, "raising the target's #{GameData::Stat.get(change[0]).name} by #{change[1]}") + end end return score @@ -601,12 +620,16 @@ class Battle::AI # Discard status move/don't prefer damaging move if target has Contrary # TODO: Maybe this should return get_score_for_target_stat_raise if Contrary # applies and desire_mult < 1. - if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1 - return (whole_effect) ? MOVE_USELESS_SCORE : score - 20 + if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 0 + ret = (whole_effect) ? MOVE_USELESS_SCORE : score - 20 + PBDebug.log_score_change(ret - score, "don't prefer lowering target's stats (it has Contrary)") + return ret end # Don't make score changes if target will faint from EOR damage if target.rough_end_of_round_damage >= target.hp - return (whole_effect) ? MOVE_USELESS_SCORE : score + ret = (whole_effect) ? MOVE_USELESS_SCORE : score + PBDebug.log(" ignore stat change (target predicted to faint this round)") + return ret end # Don't make score changes if foes have Unaware and target can't make use of # its lowered stat stages @@ -615,14 +638,23 @@ class Battle::AI foe_is_aware = true if !b.has_active_ability?(:UNAWARE) end if !foe_is_aware - return (whole_effect) ? MOVE_USELESS_SCORE : score + ret = (whole_effect) ? MOVE_USELESS_SCORE : score + PBDebug.log(" ignore stat change (target's foes have Unaware)") + return ret end # Figure out which stat raises can happen real_stat_changes = [] stat_changes.each_with_index do |stat, idx| next if idx.odd? - next if !stat_drop_worthwhile?(target, stat, fixed_change) + if !stat_drop_worthwhile?(target, stat, fixed_change) + if target.index == @user.index + PBDebug.log(" lowering the user's #{GameData::Stat.get(stat).name} isn't worthwhile") + else + PBDebug.log(" lowering the target's #{GameData::Stat.get(stat).name} isn't worthwhile") + end + next + end # Calculate amount that stat will be raised by decrement = stat_changes[idx + 1] decrement *= 2 if !fixed_change && !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE) @@ -641,7 +673,13 @@ class Battle::AI # Make score changes based on the specific changes to each stat that will be # lowered real_stat_changes.each do |change| + old_score = score score = get_target_stat_drop_score_one(score, target, change[0], change[1], desire_mult) + if target.index == @user.index + PBDebug.log_score_change(score - old_score, "lowering the user's #{GameData::Stat.get(change[0]).name} by #{change[1]}") + else + PBDebug.log_score_change(score - old_score, "lowering the target's #{GameData::Stat.get(change[0]).name} by #{change[1]}") + end end return score @@ -886,7 +924,7 @@ class Battle::AI end when :Psychic # Check for priority moves - if b.check_for_move { |m| m.priority > 0 && m.pbTarget&.can_target_one_foe? } + if b.check_for_move { |m| m.priority > 0 && m.pbTarget(b.battler)&.can_target_one_foe? } ret += (b.opposes?(move_user)) ? 10 : -10 end # Check for Psychic moves diff --git a/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb b/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb index 9ec1b0fd6..0d3807dd3 100644 --- a/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb +++ b/Data/Scripts/011_Battle/005_AI/052_AI_MoveHandlers_BattlerStats.rb @@ -719,7 +719,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetRandomStat2", score += 8 end # Don't prefer if any foe has Punishment - each_foe_battler(target.side) do |b, i| + ai.each_foe_battler(target.side) do |b, i| next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" } score -= 5 end @@ -1182,7 +1182,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaisePlusMinusUserAndAll #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseGroundedGrassBattlersAtkSpAtk1", proc { |move, user, target, ai, battle| - next true if !b.pbHasType?(:GRASS) || b.airborne? || b.semiInvulnerable? + next true if !target.has_type?(:GRASS) || target.battler.airborne? || target.battler.semiInvulnerable? next !target.battler.pbCanRaiseStatStage?(:ATTACK, user.battler, move.move) && !target.battler.pbCanRaiseStatStage?(:SPECIAL_ATTACK, user.battler, move.move) } @@ -1198,7 +1198,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseGroundedGrassBattle #=============================================================================== Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseGrassBattlersDef1", proc { |move, user, target, ai, battle| - next true if !b.pbHasType?(:GRASS) || b.semiInvulnerable? + next true if !target.has_type?(:GRASS) || target.battler.semiInvulnerable? next !target.battler.pbCanRaiseStatStage?(:DEFENSE, user.battler, move.move) } ) @@ -1226,10 +1226,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapAtkSpAtkSt end end next Battle::AI::MOVE_USELESS_SCORE if raises.length == 0 # No stat raises - score += ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0 - score += ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0 next score } ) @@ -1252,10 +1252,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapDefSpDefSt end end next Battle::AI::MOVE_USELESS_SCORE if raises.length == 0 # No stat raises - score += ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0 - score += ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0 next score } ) @@ -1278,10 +1278,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapStatStages end end next Battle::AI::MOVE_USELESS_SCORE if raises.length == 0 # No stat raises - score += ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0 - score += ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, target, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, target, drops, false, true) if drops.length > 0 next score } ) @@ -1304,8 +1304,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserCopyTargetStatStages end end next Battle::AI::MOVE_USELESS_SCORE if raises.length == 0 # No stat raises - score += ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, user, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, user, drops, false, true) if drops.length > 0 if Settings::NEW_CRITICAL_HIT_RATE_MECHANICS if user.effects[PBEffects::FocusEnergy] > 0 && target.effects[PBEffects::FocusEnergy] == 0 score -= 4 @@ -1337,8 +1337,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserStealTargetPositiveS raises.push(target.stages[s.id]) end if raises.length > 0 - score += ai.get_score_for_target_stat_raise(score, user, raises, false) - score += ai.get_score_for_target_stat_drop(score, target, raises, false, true) + score = ai.get_score_for_target_stat_raise(score, user, raises, false) + score = ai.get_score_for_target_stat_drop(score, target, raises, false, true) end next score } @@ -1366,8 +1366,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("InvertTargetStatStages", end end next Battle::AI::MOVE_USELESS_SCORE if drops.length == 0 # No stats will drop - score += ai.get_score_for_target_stat_raise(score, target, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, target, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, target, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, target, drops, false, true) if drops.length > 0 next score } ) @@ -1388,8 +1388,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ResetTargetStatStages", raises.push(target.stages[s.id]) end end - score += ai.get_score_for_target_stat_raise(score, target, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, target, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, target, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, target, drops, false, true) if drops.length > 0 next score } ) @@ -1416,8 +1416,8 @@ Battle::AI::Handlers::MoveEffectScore.add("ResetAllBattlersStatStages", raises.push(b.stages[s.id]) end end - score += ai.get_score_for_target_stat_raise(score, b, raises, false, true) if raises.length > 0 - score += ai.get_score_for_target_stat_drop(score, b, drops, false, true) if drops.length > 0 + score = ai.get_score_for_target_stat_raise(score, b, raises, false, true) if raises.length > 0 + score = ai.get_score_for_target_stat_drop(score, b, drops, false, true) if drops.length > 0 end next score } diff --git a/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb b/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb index 15d3ccabe..64c1e78d7 100644 --- a/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb +++ b/Data/Scripts/011_Battle/005_AI/053_AI_MoveHandlers_BattlerOther.rb @@ -783,7 +783,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("SetUserTypesToTargetTyp next true if !user.battler.canChangeType? next true if target.battler.pbTypes(true).empty? next true if user.battler.pbTypes == target.battler.pbTypes && - user.effects[PBEffects::Type3] == target.effects[PBEffects::Type3] + user.effects[PBEffects::ExtraType] == target.effects[PBEffects::ExtraType] next false } ) diff --git a/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb b/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb index eb6daf0e9..ae060fadd 100644 --- a/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb +++ b/Data/Scripts/011_Battle/005_AI/056_AI_MoveHandlers_Healing.rb @@ -157,7 +157,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("HealUserByTargetAttackLo proc { |score, move, user, target, ai, battle| # Check whether lowering the target's Attack will have any impact if ai.trainer.medium_skill? - score = ai.get_score_for_target_stat_drop(score, target, [:ATTACK, 1]) + score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown) end # Consider how much HP will be restored heal_amt = target.rough_stat(:ATTACK) diff --git a/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb b/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb index 76113f1ba..2ae1fe638 100644 --- a/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb +++ b/Data/Scripts/011_Battle/005_AI/058_AI_MoveHandlers_ChangeMoveEffect.rb @@ -506,21 +506,24 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseRandomUserMoveIfAsleep", #=============================================================================== # #=============================================================================== +Battle::AI::Handlers::MoveFailureCheck.add("ReplaceMoveThisBattleWithTargetLastMoveUsed", + proc { |move, user, ai, battle| + next user.effects[PBEffects::Transform] || !user.battler.pbHasMove?(move.id) + } +) Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("ReplaceMoveThisBattleWithTargetLastMoveUsed", proc { |move, user, target, ai, battle| - next true if user.effects[PBEffects::Transform] || !user.battler.pbHasMove?(move.id) - if user.faster_than?(target) - last_move_data = GameData::Move.try_get(target.battler.lastRegularMoveUsed) - next true if !last_move_data || - user.battler.pbHasMove?(target.battler.lastRegularMoveUsed) || - move.move.moveBlacklist.include?(last_move_data.function_code) || - last_move_data.type == :SHADOW - end + next false if !user.faster_than?(target) + last_move_data = GameData::Move.try_get(target.battler.lastRegularMoveUsed) + next true if !last_move_data || + user.battler.pbHasMove?(target.battler.lastRegularMoveUsed) || + move.move.moveBlacklist.include?(last_move_data.function_code) || + last_move_data.type == :SHADOW next false } ) -Battle::AI::Handlers::MoveEffectScore.add("ReplaceMoveThisBattleWithTargetLastMoveUsed", - proc { |score, move, user, ai, battle| +Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ReplaceMoveThisBattleWithTargetLastMoveUsed", + proc { |score, move, user, target, ai, battle| # Generally don't prefer, as this wastes the user's turn just to gain a move # of unknown utility score -= 8 diff --git a/Data/Scripts/011_Battle/005_AI/070_AI_MoveHandlers_GeneralModifiers.rb b/Data/Scripts/011_Battle/005_AI/070_AI_MoveHandlers_GeneralModifiers.rb index f5830f8b6..621d10cfd 100644 --- a/Data/Scripts/011_Battle/005_AI/070_AI_MoveHandlers_GeneralModifiers.rb +++ b/Data/Scripts/011_Battle/005_AI/070_AI_MoveHandlers_GeneralModifiers.rb @@ -28,7 +28,10 @@ #=============================================================================== Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shiny_target, proc { |score, move, user, target, ai, battle| - score -= 40 if target.wild? && target.battler.shiny? + if target.wild? && target.battler.shiny? + PBDebug.log_score_change(-40, "avoid attacking a shiny wild Pokémon") + score -= 40 + end next score } ) @@ -47,10 +50,16 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage, proc { |score, move, user, target, ai, battle| if move.damagingMove? dmg = move.rough_damage - score += [20.0 * dmg / target.hp, 25].min - score += 10 if dmg > target.hp * 1.1 # Predicted to KO the target + old_score = score + score += ([30.0 * dmg / target.hp, 35].min).to_i + PBDebug.log_score_change(score - old_score, "damaging move (predicted damage #{dmg} = #{100 * dmg / target.hp}% of target's HP)") + if dmg > target.hp * 1.1 # Predicted to KO the target + old_score = score + score += 10 + PBDebug.log_score_change(score - old_score, "predicted to KO the target") + end end - next score.to_i + next score } ) @@ -60,7 +69,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:add_predicted_damage, #=============================================================================== Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:move_accuracy, proc { |score, move, user, target, ai, battle| - next score * move.rough_accuracy / 100.0 + acc = move.rough_accuracy.to_i + if acc < 90 + old_score = score + score -= (0.2 * (100 - acc)).to_i # -2 (89%) to -19 (1%) + PBDebug.log_score_change(score - old_score, "accuracy (predicted #{acc}%)") + end + next score } ) @@ -89,7 +104,11 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_semi_invulnerabl miss = false if move.move.hitsDivingTargets? end end - next Battle::AI::MOVE_USELESS_SCORE if miss + if miss + old_score = score + score = Battle::AI::MOVE_USELESS_SCORE + PBDebug.log_score_change(score - old_score, "target is semi-invulnerable") + end end next score } @@ -109,7 +128,9 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_fr proc { |score, move, user, target, ai, battle| if ai.trainer.medium_skill? && target.status == :FROZEN if move.rough_type == :FIRE || (Settings::MECHANICS_GENERATION >= 6 && move.move.thawsUser?) - score -= 30 + old_score = score + score -= 15 + PBDebug.log_score_change(score - old_score, "thaws the target") end end next score @@ -137,7 +158,9 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:flinching_effects, (move.damagingMove? && (user.has_active_item?([:KINGSROCK, :RAZORFANG]) || user.has_active_ability?(:STENCH))) - score += 20 + old_score = score + score += 8 + PBDebug.log_score_change(score - old_score, "flinching") end end end @@ -166,7 +189,11 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:flinching_effects, #=============================================================================== Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:dance_move_against_dancer, proc { |score, move, user, target, ai, battle| - score /= 2 if move.move.danceMove? && target.has_active_ability?(:DANCER) + if move.move.danceMove? && target.has_active_ability?(:DANCER) + old_score = score + score -= 12 + PBDebug.log_score_change(score - old_score, "don't want to use a dance move on a target with Dancer") + end next score } ) @@ -199,8 +226,10 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest if ai.trainer.medium_skill? && move.damagingMove? && target.effects[PBEffects::DestinyBond] dmg = move.rough_damage if dmg > target.hp * 1.05 # Predicted to KO the target - score -= 25 - score -= 20 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 + old_score = score + score -= 15 + score -= 10 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 + PBDebug.log_score_change(score - old_score, "don't want to KO the Destiny Bonding target") end end next score @@ -243,14 +272,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen, proc { |score, move, user, ai, battle| if ai.trainer.medium_skill? && user.status == :FROZEN + old_score = score if move.move.thawsUser? score += 30 - else - user.battler.eachMove do |m| - next unless m.thawsUser? - score -= 30 # Don't prefer this move if user knows another move that thaws - break - end + PBDebug.log_score_change(score - old_score, "move will thaw the user") + elsif user.check_for_move { |m| m.thawsUser? } + score -= 30 # Don't prefer this move if user knows another move that thaws + PBDebug.log_score_change(score - old_score, "user knows another move will thaw it") end end next score @@ -267,7 +295,11 @@ Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item, if user.has_active_item?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF]) || user.has_active_ability?(:GORILLATACTICS) # Really don't prefer status moves (except Trick) - score *= 0.1 if move.statusMove? && move.function != "UserTargetSwapItems" + if move.statusMove? && move.function != "UserTargetSwapItems" + old_score = score + score -= 25 + PBDebug.log_score_change(score - old_score, "move is not suitable to be Choiced into") + end # Don't prefer moves of certain types move_type = move.rough_type # Most unpreferred types are 0x effective against another type, except @@ -280,11 +312,13 @@ Battle::AI::Handlers::GeneralMoveScore.add(:good_move_for_choice_item, # very effective against Dragon. unpreferred_types = [:NORMAL, :FIGHTING, :POISON, :GROUND, :GHOST, :FIRE, :WATER, :GRASS, :ELECTRIC, :PSYCHIC, :DRAGON] - score *= 0.95 if unpreferred_types.include?(move_type) + old_score = score + score -= 5 if unpreferred_types.include?(move_type) # Don't prefer moves with lower accuracy - score *= move.accuracy / 100.0 if move.accuracy > 0 + score = score * move.accuracy / 100 if move.accuracy > 0 # Don't prefer moves with low PP - score *= 0.9 if move.move.pp < 6 + score -= 10 if move.move.pp < 6 + PBDebug.log_score_change(score - old_score, "move is less suitable to be Choiced into") end end next score @@ -305,10 +339,13 @@ Battle::AI::Handlers::GeneralMoveScore.add(:prefer_damaging_moves_if_last_pokemo # Don't mess with scores just because a move is damaging; need to play well next score if ai.trainer.high_skill? && foes > reserves # AI is outnumbered # Prefer damaging moves depending on remaining Pokémon + old_score = score if foes == 0 # Foe is down to their last Pokémon - score *= 1.1 # => Go for the kill + score += 10 # => Go for the kill + PBDebug.log_score_change(score - old_score, "prefer damaging moves (no foe party Pokémon left)") elsif reserves == 0 # AI is down to its last Pokémon, foe has reserves - score *= 1.05 # => Go out with a bang + score += 5 # => Go out with a bang + PBDebug.log_score_change(score - old_score, "prefer damaging moves (no ally party Pokémon left)") end end next score diff --git a/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb b/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb index fa7799abe..a48791aa8 100644 --- a/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb +++ b/Data/Scripts/011_Battle/005_AI/102_AIBattler.rb @@ -40,6 +40,10 @@ class Battle::AI::AIBattler return @ai.battle.wildBattle? && opposes? end + def name + return sprintf("%s (%d)", @battler.name, @index) + end + def opposes?(other = nil) return @side == 1 if other.nil? return other.side != @side @@ -184,7 +188,7 @@ class Battle::AI::AIBattler #============================================================================= def types; return @battler.types; end - def pbTypes(withType3 = false); return @battler.pbTypes(withType3); end + def pbTypes(withExtraType = false); return @battler.pbTypes(withExtraType); end def has_type?(type) return false if !type @@ -208,7 +212,7 @@ class Battle::AI::AIBattler # TODO: Need to check the move's pbCalcTypeModSingle. ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user) end - ret *= 2 if target.effects[PBEffects::TarShot] && type == :FIRE + ret *= 2 if @battler.effects[PBEffects::TarShot] && type == :FIRE end return ret end diff --git a/Data/Scripts/011_Battle/005_AI/103_AIMove.rb b/Data/Scripts/011_Battle/005_AI/103_AIMove.rb index 96e82415d..1af8ad728 100644 --- a/Data/Scripts/011_Battle/005_AI/103_AIMove.rb +++ b/Data/Scripts/011_Battle/005_AI/103_AIMove.rb @@ -396,7 +396,7 @@ class Battle::AI::AIMove # OHKO move accuracy if @move.is_a?(Battle::Move::OHKO) ret = self.accuracy + user.level - target.level - ret -= 10 if function == "OHKOIce" && !user.pbHasType?(:ICE) + ret -= 10 if function == "OHKOIce" && !user.has_type?(:ICE) return [ret, 0].max end # "Always hit" effects and "always hit" accuracy diff --git a/Data/Scripts/999_Main/002_debug battle test.rb b/Data/Scripts/022_Maruno/debug battle tests.rb similarity index 80% rename from Data/Scripts/999_Main/002_debug battle test.rb rename to Data/Scripts/022_Maruno/debug battle tests.rb index 7ee33cef5..9e33c6291 100644 --- a/Data/Scripts/999_Main/002_debug battle test.rb +++ b/Data/Scripts/022_Maruno/debug battle tests.rb @@ -1,3 +1,11 @@ +# TODO: Better randomisation of moves, including tracking of how many times each +# function code has been tested (note that some Pokémon may not be used in +# battle, so their moves won't be score). +# TODO: Add held items. + +#=============================================================================== +# +#=============================================================================== def debug_set_up_trainer # Values to return trainer_array = [] @@ -6,7 +14,7 @@ def debug_set_up_trainer party_starts = [0] # Choose random trainer type and trainer name - trainer_type = GameData::TrainerType.keys.sample + trainer_type = :CHAMPION # GameData::TrainerType.keys.sample trainer_name = ["Alpha", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot", "Golf", "Hotel", "India", "Juliette", "Kilo", "Lima", "Mike", "November", "Oscar", @@ -24,8 +32,12 @@ def debug_set_up_trainer GameData::Species.each_species { |sp| valid_species.push(sp.species) } Settings::MAX_PARTY_SIZE.times do |i| this_species = valid_species.sample - this_level = rand(1, Settings::MAXIMUM_LEVEL) - pkmn = Pokemon.new(this_species, this_level, trainer) + this_level = 100 # rand(1, Settings::MAXIMUM_LEVEL) + pkmn = Pokemon.new(this_species, this_level, trainer, false) + all_moves = pkmn.getMoveList.map { |m| m[1] } + all_moves.uniq! + moves = all_moves.sample(4) + moves.each { |m| pkmn.learn_move(m) } trainer.party.push(pkmn) pokemon_array.push(pkmn) end @@ -37,7 +49,7 @@ end def debug_test_auto_battle(logging = false) old_internal = $INTERNAL $INTERNAL = logging - echoln "Start of testing auto battle." + echoln "Start of testing auto-battle." echoln "" if !$INTERNAL PBDebug.log("") PBDebug.log("================================================================") @@ -51,7 +63,7 @@ def debug_test_auto_battle(logging = false) trainer_txt = "[Trainer #{index}] #{trainer.full_name} [skill: #{trainer.skill_level}]" ($INTERNAL) ? PBDebug.log_header(trainer_txt) : echoln(trainer_txt) party.each do |pkmn| - pkmn_txt = "* #{pkmn.name}, Lv.#{pkmn.level}" + pkmn_txt = "#{pkmn.name}, Lv.#{pkmn.level}" pkmn_txt += " [Ability: #{pkmn.ability&.name || "---"}]" pkmn_txt += " [Item: #{pkmn.item&.name || "---"}]" ($INTERNAL) ? PBDebug.log(pkmn_txt) : echoln(pkmn_txt) @@ -85,18 +97,19 @@ def debug_test_auto_battle(logging = false) # Perform the battle itself outcome = battle.pbStartBattle # End - echoln ["Undecided", + text = ["Undecided", "Trainer 1 #{player_trainers[0].name} won", "Trainer 2 #{foe_trainers[0].name} won", "Ran/forfeited", "Wild Pokémon caught", "Draw"][outcome] + echoln sprintf("%s after %d rounds", text, battle.turnCount + 1) echoln "" $INTERNAL = old_internal end #=============================================================================== -# Add to Debug menu +# Add to Debug menu. #=============================================================================== MenuHandlers.add(:debug_menu, :test_auto_battle, { "name" => _INTL("Test Auto Battle"), @@ -115,5 +128,6 @@ MenuHandlers.add(:debug_menu, :test_auto_battle_logging, { "always_show" => false, "effect" => proc { debug_test_auto_battle(true) + pbMessage(_INTL("Battle transcript was logged in Data/debuglog.txt.")) } })