AI function code rewrites

This commit is contained in:
Maruno17
2023-01-12 23:08:26 +00:00
parent a22f75f500
commit 84bdd1f60b
10 changed files with 330 additions and 180 deletions

View File

@@ -84,6 +84,7 @@ class Battle
attr_accessor :poke_ball_failed # Set after first_poke_ball to prevent it being set again
attr_reader :switching # True if during the switching phase of the round
attr_reader :futureSight # True if Future Sight is hitting
attr_reader :command_phase
attr_reader :endOfRound # True during the end of round
attr_accessor :moldBreaker # True if Mold Breaker applies
attr_reader :struggle # The Struggle move
@@ -159,6 +160,7 @@ class Battle
@lastMoveUser = -1
@switching = false
@futureSight = false
@command_phase = false
@endOfRound = false
@moldBreaker = false
@runCommand = 0

View File

@@ -172,6 +172,7 @@ class Battle
# Command phase
#=============================================================================
def pbCommandPhase
@command_phase = true
@scene.pbBeginCommandPhase
# Reset choices if commands can be shown
@battlers.each_with_index do |b, i|
@@ -186,8 +187,12 @@ class Battle
end
# Choose actions for the round (player first, then AI)
pbCommandPhaseLoop(true) # Player chooses their actions
return if @decision != 0 # Battle ended, stop choosing actions
if @decision != 0 # Battle ended, stop choosing actions
@command_phase = false
return
end
pbCommandPhaseLoop(false) # AI chooses their actions
@command_phase = false
end
def pbCommandPhaseLoop(isPlayer)

View File

@@ -1152,7 +1152,7 @@ class Battle::Move::CategoryDependsOnHigherDamagePoisonTarget < Battle::Move::Po
special_damage = real_special_attack.to_f / real_special_defense
# Determine move's category
if physical_damage == special_damage
@calcCategry = @battle.pbRandom(2)
@calcCategory = (@battle.command_phase) ? rand(2) : @battle.pbRandom(2)
else
@calcCategory = (physical_damage > special_damage) ? 0 : 1
end

View File

@@ -7,7 +7,7 @@ class Battle::AI
# could target a foe but is targeting an ally, the score is also inverted, but
# only because it is inverted again in def pbGetMoveScoreAgainstTarget.
#=============================================================================
def get_score_for_target_stat_raise(score, target, stat_changes, whole_effect = true)
def get_score_for_target_stat_raise(score, target, stat_changes, whole_effect = true, fixed_change = false)
whole_effect = false if @move.damagingMove?
# Decide whether the target raising its stat(s) is a good thing
desire_mult = 1
@@ -18,11 +18,11 @@ 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 !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
return (whole_effect) ? MOVE_USELESS_SCORE : score - 20
end
# Don't make score changes if target will faint from EOR damage
if target.rough_end_of_round_damage > target.hp
if target.rough_end_of_round_damage >= target.hp
return (whole_effect) ? MOVE_USELESS_SCORE : score
end
# Don't make score changes if foes have Unaware and target can't make use of
@@ -41,14 +41,10 @@ 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)
next if !stat_raise_worthwhile?(target, stat, fixed_change)
# Calculate amount that stat will be raised by
increment = stat_changes[idx + 1]
if @move.function == "RaiseUserAtkSpAtk1Or2InSun"
increment = 1
increment = 2 if [:Sun, :HarshSun].include?(target.battler.effectiveWeather)
end
increment *= 2 if !@battle.moldBreaker && target.has_active_ability?(:SIMPLE)
increment *= 2 if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:SIMPLE)
increment = [increment, 6 - target.stages[stat]].min # The actual stages gained
# Count this as a valid stat raise
real_stat_changes.push([stat, increment]) if increment > 0
@@ -76,8 +72,10 @@ class Battle::AI
# i.e. CategoryDependsOnHigherDamagePoisonTarget and
# CategoryDependsOnHigherDamageIgnoreTargetAbility.
#=============================================================================
def stat_raise_worthwhile?(target, stat)
def stat_raise_worthwhile?(target, stat, fixed_change = false)
if !fixed_change
return false if !target.battler.pbCanRaiseStatStage?(stat, @user.battler, @move.move)
end
# Check if target won't benefit from the stat being raised
# TODO: Exception if target knows Baton Pass/Stored Power?
case stat
@@ -589,8 +587,10 @@ class Battle::AI
# ally, but only because it is inverted in def pbGetMoveScoreAgainstTarget
# instead.
# TODO: Revisit this method as parts may need rewriting.
# TODO: fixed_change should make this ignore Mist/Clear Body/other effects
# that prevent increments/decrements to stat stages.
#=============================================================================
def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true)
def get_score_for_target_stat_drop(score, target, stat_changes, whole_effect = true, fixed_change = false)
whole_effect = false if @move.damagingMove?
# Decide whether the target raising its stat(s) is a good thing
desire_mult = -1
@@ -601,11 +601,11 @@ 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 !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
if !fixed_change && !@battle.moldBreaker && target.has_active_ability?(:CONTRARY) && desire_mult > 1
return (whole_effect) ? MOVE_USELESS_SCORE : score - 20
end
# Don't make score changes if target will faint from EOR damage
if target.rough_end_of_round_damage > target.hp
if target.rough_end_of_round_damage >= target.hp
return (whole_effect) ? MOVE_USELESS_SCORE : score
end
# Don't make score changes if foes have Unaware and target can't make use of
@@ -622,10 +622,10 @@ class Battle::AI
real_stat_changes = []
stat_changes.each_with_index do |stat, idx|
next if idx.odd?
next if !stat_drop_worthwhile?(target, stat)
next if !stat_drop_worthwhile?(target, stat, fixed_change)
# Calculate amount that stat will be raised by
decrement = stat_changes[idx + 1]
decrement *= 2 if !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE)
decrement *= 2 if !fixed_change && !@battle.moldBreaker && @user.has_active_ability?(:SIMPLE)
decrement = [decrement, 6 + target.stages[stat]].min # The actual stages lost
# Count this as a valid stat drop
real_stat_changes.push([stat, decrement]) if decrement > 0
@@ -654,8 +654,10 @@ class Battle::AI
# CategoryDependsOnHigherDamageIgnoreTargetAbility.
# TODO: Revisit this method as parts may need rewriting.
#=============================================================================
def stat_drop_worthwhile?(target, stat)
def stat_drop_worthwhile?(target, stat, fixed_change = false)
if !fixed_change
return false if !target.battler.pbCanLowerStatStage?(stat, @user.battler, @move.move)
end
# Check if target won't benefit from the stat being lowered
case stat
when :ATTACK
@@ -983,8 +985,11 @@ class Battle::AI
:TOXICORB
]
ret = 0
ret = 2 if preferred_items.include?(item)
ret = -2 if unpreferred_items.include?(item)
if preferred_items.include?(item)
ret = 2
elsif unpreferred_items.include?(item)
ret = -2
end
# Don't prefer if the battler knows Acrobatics
if battler.check_for_move { |m| m.function == "DoublePowerIfUserHasNoItem" }
ret += (item == :NONE) ? 1 : -1

View File

@@ -364,8 +364,16 @@ Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAtkDef1",
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.copy("RaiseUserAtkSpAtk1",
"RaiseUserAtkSpAtk1Or2InSun")
Battle::AI::Handlers::MoveEffectScore.copy("RaiseUserAtkSpAtk1",
"RaiseUserAtkSpAtk1Or2InSun")
Battle::AI::Handlers::MoveEffectScore.add("RaiseUserAtkSpAtk1Or2InSun",
proc { |score, move, user, ai, battle|
raises = move.move.statUp.clone
if [:Sun, :HarshSun].include?(user.battler.effectiveWeather)
raises[1] = 2
raises[3] = 2
end
next ai.get_score_for_target_stat_raise(score, user, raises)
}
)
#===============================================================================
#
@@ -682,7 +690,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetSpDef1",
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetRandomStat2",
proc { |move, user, target, ai, battle|
@@ -696,12 +704,26 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("RaiseTargetRandomStat2"
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseTargetRandomStat2",
proc { |score, move, user, target, ai, battle|
avgStat = 0
GameData::Stat.each_battle do |s|
avgStat -= target.stages[s.id] if !target.statStageAtMax?(s.id)
next Battle::AI::MOVE_USELESS_SCORE if !battle.moldBreaker && target.has_active_ability?(:CONTRARY)
next Battle::AI::MOVE_USELESS_SCORE if target.rough_end_of_round_damage >= target.hp
score -= 5 if target.index != user.index # Less likely to use on ally
score += 5 if target.has_active_ability?(:SIMPLE)
# Prefer if target is at high HP, don't prefer if target is at low HP
if target.hp >= target.totalhp * 0.7
score += 8
else
score += ((100 * target.hp / target.totalhp) - 50) / 4 # +5 to -12
end
avgStat = avgStat / 2 if avgStat < 0 # More chance of getting even better
next + avgStat * 10
# Prefer if target has Stored Power
if target.check_for_move { |m| m.function == "PowerHigherWithUserPositiveStatStages" }
score += 8
end
# Don't prefer if any foe has Punishment
each_foe_battler(target.side) do |b, i|
next if !b.check_for_move { |m| m.function == "PowerHigherWithTargetPositiveStatStages" }
score -= 5
end
next score
}
)
@@ -881,7 +903,7 @@ Battle::AI::Handlers::MoveBasePower.add("LowerTargetSpeed1WeakerInGrassyTerrain"
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetSpeed1MakeTargetWeakerToFire",
proc { |move, user, target, ai, battle|
@@ -897,7 +919,10 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetSpeed1MakeTar
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
# Score for adding weakness to Fire
if !target.effects[PBEffects::TarShot]
score += 20 if user.battler.moves.any? { |m| m.damagingMove? && m.pbCalcType(user.battler) == :FIRE }
eff = target.effectiveness_of_type_against_battler(:FIRE)
if !Effectiveness.ineffective?(eff)
score += 8 * eff if user.check_for_move { |m| m.damagingMove? && m.pbCalcType(user.battler) == :FIRE }
end
end
next score
}
@@ -952,7 +977,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("LowerTargetAccuracy1",
"LowerTargetEvasion1")
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("LowerTargetEvasion1RemoveSideEffects",
proc { |move, user, target, ai, battle|
@@ -982,14 +1007,26 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("LowerTargetEvasion1Remov
# Score for stat drop
score = ai.get_score_for_target_stat_drop(score, target, move.move.statDown)
# Score for removing side effects/terrain
score += 30 if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 0 ||
target.pbOwnSide.effects[PBEffects::Reflect] > 0 ||
target.pbOwnSide.effects[PBEffects::LightScreen] > 0 ||
target.pbOwnSide.effects[PBEffects::Mist] > 0 ||
target.pbOwnSide.effects[PBEffects::Safeguard] > 0
score -= 30 if target.pbOwnSide.effects[PBEffects::Spikes] > 0 ||
score += 8 if target.pbOwnSide.effects[PBEffects::AuroraVeil] > 1 ||
target.pbOwnSide.effects[PBEffects::Reflect] > 1 ||
target.pbOwnSide.effects[PBEffects::LightScreen] > 1 ||
target.pbOwnSide.effects[PBEffects::Mist] > 1 ||
target.pbOwnSide.effects[PBEffects::Safeguard] > 1
if target.can_switch_lax?
score -= 10 if target.pbOwnSide.effects[PBEffects::Spikes] > 0 ||
target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 ||
target.pbOwnSide.effects[PBEffects::StealthRock]
target.pbOwnSide.effects[PBEffects::StealthRock] ||
target.pbOwnSide.effects[PBEffects::StickyWeb]
end
if user.opposes?(target) && user.can_switch_lax? && Settings::MECHANICS_GENERATION >= 6
score += 10 if target.pbOpposingSide.effects[PBEffects::Spikes] > 0 ||
target.pbOpposingSide.effects[PBEffects::ToxicSpikes] > 0 ||
target.pbOpposingSide.effects[PBEffects::StealthRock] ||
target.pbOpposingSide.effects[PBEffects::StickyWeb]
end
if Settings::MECHANICS_GENERATION >= 8 && battle.field.terrain != :None
score -= ai.get_score_for_terrain(battle.field.terrain, user)
end
next score
}
)
@@ -1176,118 +1213,139 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("RaiseGrassBattlersDef1",
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapAtkSpAtkStages",
proc { |score, move, user, target, ai, battle|
user_attack = user.stages[:ATTACK]
user_spatk = user.stages[:SPECIAL_ATTACK]
target_attack = target.stages[:ATTACK]
target_spatk = target.stages[:SPECIAL_ATTACK]
# Useless if both of the user's stats are already higher than the target's
next Battle::AI::MOVE_USELESS_SCORE if user_attack >= target_attack && user_spatk >= target_spatk
# Useless if neither the user nor the target make use of these stats
useless_attack = !user.check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserDefenseInsteadOfUserAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" }
useless_attack = false if useless_attack &&
target.check_for_move { |m| m.physicalMove?(m.type) &&
m.function != "UseUserDefenseInsteadOfUserAttack" &&
m.function != "UseTargetAttackInsteadOfUserAttack" }
useless_spatk = !user.check_for_move { |m| m.specialMove?(m.type) }
useless_spatk = false if useless_spatk && target.check_for_move { |m| m.specialMove?(m.type) }
next Battle::AI::MOVE_USELESS_SCORE if useless_attack && useless_spatk
# Apply score modifiers
score += (target_attack - user_attack) * 5 if !useless_attack
score += (target_spatk - user_spatk) * 5 if !useless_spatk
raises = []
drops = []
[:ATTACK, :SPECIAL_ATTACK].each do |stat|
stage_diff = target.stages[stat] - user.stages[stat]
if stage_diff > 0
raises.push(stat)
raises.push(stage_diff)
elsif stage_diff < 0
drops.push(stat)
drops.push(stage_diff)
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
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
#
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapDefSpDefStages",
proc { |score, move, user, target, ai, battle|
user_def = user.stages[:DEFENSE]
user_spdef = user.stages[:SPECIAL_DEFENSE]
target_def = target.stages[:DEFENSE]
target_spdef = target.stages[:SPECIAL_DEFENSE]
next Battle::AI::MOVE_USELESS_SCORE if user_def >= target_def && user_spdef >= target_spdef
next score - 20 if user_def + user_spdef >= target_def + target_spdef
# TODO: Check whether the target has physical/special moves that will be
# more resisted after the swap, and vice versa for the user?
score += (target_def - user_def) * 5
score += (target_spdef - user_spdef) * 5
raises = []
drops = []
[:DEFENSE, :SPECIAL_DEFENSE].each do |stat|
stage_diff = target.stages[stat] - user.stages[stat]
if stage_diff > 0
raises.push(stat)
raises.push(stage_diff)
elsif stage_diff < 0
drops.push(stat)
drops.push(stage_diff)
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
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
#
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapStatStages",
proc { |score, move, user, target, ai, battle|
user_stages = 0
target_stages = 0
target_stage_better = false
raises = []
drops = []
GameData::Stat.each_battle do |s|
user_stages += user.stages[s.id]
target_stages += target.stages[s.id]
target_stage_better = true if target.stages[s.id] > user.stages[s.id]
stage_diff = target.stages[s.id] - user.stages[s.id]
if stage_diff > 0
raises.push(s.id)
raises.push(stage_diff)
elsif stage_diff < 0
drops.push(s.id)
drops.push(stage_diff)
end
next Battle::AI::MOVE_USELESS_SCORE if !target_stage_better
score += (target_stages - user_stages) * 10
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
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
#
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserCopyTargetStatStages",
proc { |score, move, user, target, ai, battle|
equal = true
raises = []
drops = []
GameData::Stat.each_battle do |s|
stagediff = target.stages[s.id] - user.stages[s.id]
score += stagediff * 10
equal = false if stagediff != 0
stage_diff = target.stages[s.id] - user.stages[s.id]
if stage_diff > 0
raises.push(s.id)
raises.push(stage_diff)
elsif stage_diff < 0
drops.push(s.id)
drops.push(stage_diff)
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
if Settings::NEW_CRITICAL_HIT_RATE_MECHANICS
if user.effects[PBEffects::FocusEnergy] > 0 && target.effects[PBEffects::FocusEnergy] == 0
score -= 4
elsif user.effects[PBEffects::FocusEnergy] == 0 && target.effects[PBEffects::FocusEnergy] > 0
score += 4
end
if user.effects[PBEffects::LaserFocus] > 0 && target.effects[PBEffects::LaserFocus] == 0
score -= 3
elsif user.effects[PBEffects::LaserFocus] == 0 && target.effects[PBEffects::LaserFocus] > 0
score += 3
end
end
next Battle::AI::MOVE_USELESS_SCORE if equal # No stat changes
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: Account for stat theft before damage calculation.
# TODO: target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
# TODO: Account for stat theft before damage calculation. This would be complex,
# involving pbCanRaiseStatStage? and Contrary and Simple; do I want to
# account for all that or simplify things?
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserStealTargetPositiveStatStages",
proc { |score, move, user, target, ai, battle|
num_stages = 0
raises = []
GameData::Stat.each_battle do |s|
num_stages += target.stages[s.id] if target.stages[s.id] > 0
next if target.stages[s.id] <= 0
raises.push(s.id)
raises.push(target.stages[s.id])
end
if num_stages > 0
next Battle::AI::MOVE_USELESS_SCORE if user.has_active_ability?(:CONTRARY)
score += num_stages * 5
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)
end
next score
}
)
#===============================================================================
# TODO: target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("InvertTargetStatStages",
proc { |move, user, target, ai, battle|
@@ -1296,41 +1354,48 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("InvertTargetStatStages"
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("InvertTargetStatStages",
proc { |score, move, user, target, ai, battle|
pos_stages = 0
neg_stages = 0
raises = []
drops = []
GameData::Stat.each_battle do |s|
pos_stages += target.stages[s.id] if target.stages[s.id] > 0
neg_stages += target.stages[s.id] if target.stages[s.id] < 0
if target.stages[s.id] > 0
drops.push(s.id)
drops.push(target.stages[s.id] * 2)
elsif target.stages[s.id] < 0
raises.push(s.id)
raises.push(target.stages[s.id] * 2)
end
next Battle::AI::MOVE_USELESS_SCORE if pos_stages == 0
next score + (pos_stages - neg_stages) * 10
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
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: target should probably be treated as an enemy when deciding the score,
# since the score will be inverted elsewhere due to the target being an
# ally.
#
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("ResetTargetStatStages",
proc { |score, move, user, target, ai, battle|
avg = 0
pos_change = false
any_change = false
raises = []
drops = []
GameData::Stat.each_battle do |s|
next if target.stages[s.id] == 0
avg += target.stages[s.id]
pos_change = true if target.stages[s.id] > 0
any_change = true
if target.stages[s.id] > 0
drops.push(s.id)
drops.push(target.stages[s.id])
elsif target.stages[s.id] < 0
raises.push(s.id)
raises.push(target.stages[s.id])
end
next Battle::AI::MOVE_USELESS_SCORE if !any_change || !pos_change
next score + avg * 5
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
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("ResetAllBattlersStatStages",
proc { |move, user, ai, battle|
@@ -1339,17 +1404,22 @@ Battle::AI::Handlers::MoveFailureCheck.add("ResetAllBattlersStatStages",
)
Battle::AI::Handlers::MoveEffectScore.add("ResetAllBattlersStatStages",
proc { |score, move, user, ai, battle|
stages = 0
battle.allBattlers.each do |b|
totalStages = 0
GameData::Stat.each_battle { |s| totalStages += b.stages[s.id] }
if b.opposes?(user.battler)
stages += totalStages
else
stages -= totalStages
ai.each_battler do |b|
raises = []
drops = []
GameData::Stat.each_battle do |s|
if b.stages[s.id] > 0
drops.push(s.id)
drops.push(b.stages[s.id])
elsif b.stages[s.id] < 0
raises.push(s.id)
raises.push(b.stages[s.id])
end
end
next score + stages * 10
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
}
)

View File

@@ -912,16 +912,13 @@ Battle::AI::Handlers::MoveBasePower.add("EffectivenessIncludesFlyingType",
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CategoryDependsOnHigherDamagePoisonTarget",
proc { |score, move, user, target, ai, battle|
next score + 5 if target.battler.pbCanPoison?(user.battler, false)
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.copy("PoisonTarget",
"CategoryDependsOnHigherDamagePoisonTarget")
#===============================================================================
# TODO: Review score modifiers.
# TODO: Review score modifiers. Category part is already accounted for.
#===============================================================================
# CategoryDependsOnHigherDamageIgnoreTargetAbility
@@ -1095,7 +1092,7 @@ Battle::AI::Handlers::MoveBasePower.copy("TypeAndPowerDependOnWeather",
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetMovesBecomeElectric",
proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if user.faster_than?(target)
next Battle::AI::MOVE_USELESS_SCORE if !user.faster_than?(target)
}
)

View File

@@ -125,7 +125,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DestroyTargetBerryOrGem"
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CorrodeTargetItem",
proc { |move, user, target, ai, battle|
@@ -137,11 +137,9 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("CorrodeTargetItem",
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("CorrodeTargetItem",
proc { |score, move, user, target, ai, battle|
if target.item_active?
score += 30
else
score -= 50
end
target_item_preference = ai.battler_wants_item?(target, target.item_id)
target_no_item_preference = ai.battler_wants_item?(target, :NONE)
score += (target_item_preference - target_no_item_preference) * 8
next score
}
)

View File

@@ -201,7 +201,8 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("PowerUpAllyMove",
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("PowerUpAllyMove",
proc { |score, move, user, target, ai, battle|
next score + 15
next Battle::AI::MOVE_USELESS_SCORE if !target.check_for_move { |m| m.damagingMove? }
next score + 8
}
)
@@ -269,7 +270,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CounterDamagePlusHalf",
)
#===============================================================================
# TODO: Review score modifiers.
#
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("UserAddStockpileRaiseDefSpDef1",
proc { |move, user, ai, battle|
@@ -280,14 +281,17 @@ Battle::AI::Handlers::MoveEffectScore.add("UserAddStockpileRaiseDefSpDef1",
proc { |score, move, user, ai, battle|
score = ai.get_score_for_target_stat_raise(score, user, [:DEFENSE, 1, :SPECIAL_DEFENSE, 1], false)
# More preferable if user also has Spit Up/Swallow
score += 20 if user.battler.pbHasMoveFunction?("PowerDependsOnUserStockpile",
if user.battler.pbHasMoveFunction?("PowerDependsOnUserStockpile",
"HealUserDependingOnUserStockpile")
score += [10, 8, 5, 3][user.effects[PBEffects::Stockpile]]
end
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
# NOTE: Don't worry about the stat drops caused by losing the stockpile, because
# if these moves are known, they want to be used.
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("PowerDependsOnUserStockpile",
proc { |move, user, ai, battle|
@@ -299,9 +303,17 @@ Battle::AI::Handlers::MoveBasePower.add("PowerDependsOnUserStockpile",
next move.move.pbBaseDamage(power, user.battler, target.battler)
}
)
Battle::AI::Handlers::MoveEffectScore.add("PowerDependsOnUserStockpile",
proc { |score, move, user, ai, battle|
# Slightly prefer to hold out for another Stockpile to make this move stronger
score -= 5 if user.effects[PBEffects::Stockpile] < 2
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
# NOTE: Don't worry about the stat drops caused by losing the stockpile, because
# if these moves are known, they want to be used.
#===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("HealUserDependingOnUserStockpile",
proc { |move, user, ai, battle|
@@ -313,9 +325,15 @@ Battle::AI::Handlers::MoveFailureCheck.add("HealUserDependingOnUserStockpile",
)
Battle::AI::Handlers::MoveEffectScore.add("HealUserDependingOnUserStockpile",
proc { |score, move, user, ai, battle|
mult = [0, 25, 50, 100][user.effects[PBEffects::Stockpile]]
score += mult
score -= user.hp * mult * 2 / user.totalhp
next Battle::AI::MOVE_USELESS_SCORE if !user.battler.canHeal?
# Consider how much HP will be restored
if user.hp >= user.totalhp * 0.5
score -= 10
else
# Slightly prefer to hold out for another Stockpile to make this move stronger
score -= 5 if user.effects[PBEffects::Stockpile] < 2
score += 20 * (user.totalhp - user.hp) / user.totalhp
end
next score
}
)

View File

@@ -271,19 +271,16 @@ Battle::AI::Handlers::MoveFailureCheck.add("TrapAllBattlersInBattleForOneTurn",
#===============================================================================
# TODO: Review score modifiers.
# TODO: Consider all foes rather than just target. Can't use "target".
#===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("UsedAfterUserTakesPhysicalDamage",
proc { |score, move, user, ai, battle|
if ai.trainer.medium_skill?
hasPhysicalAttack = false
# target.battler.eachMove do |m|
# next if !m.physicalMove?(m.type)
# hasPhysicalAttack = true
# break
# end
score -= 50 if !hasPhysicalAttack
found_physical_move = false
ai.each_foe_battler(user.side) do |b, i|
next if !b.check_for_move { |m| m.physicalMove?(m.type) }
found_physical_move = true
break
end
next Battle::AI::MOVE_USELESS_SCORE if !found_physical_move
next score
}
)
@@ -304,14 +301,62 @@ Battle::AI::Handlers::MoveEffectScore.add("UsedAfterAllyRoundWithDoublePower",
)
#===============================================================================
# TODO: Review score modifiers.
# TODO: Review score modifiers. This could reasonably used on an ally.
#===============================================================================
# TargetActsNext
Battle::AI::Handlers::MoveEffectScore.add("TargetActsNext",
proc { |score, move, user, ai, battle|
# Useless if the user has no ally
has_ally = false
ai.each_ally(user.index) { |b, i| has_ally = true }
next Battle::AI::MOVE_USELESS_SCORE if !has_ally
# Useless if the target is a foe
next Battle::AI::MOVE_USELESS_SCORE if target.opposes?(user)
# Compare the speeds of all battlers
speeds = []
ai.each_battler { |b, i| speeds.push([i, rough_stat(:SPEED)]) }
if battle.field.effects[PBEffects::TrickRoom] > 0
speeds.sort! { |a, b| a[1] <=> b[1] }
else
speeds.sort! { |a, b| b[1] <=> a[1] }
end
idx_user = speeds.index { |ele| ele[0] == user.index }
idx_target = speeds.index { |ele| ele[0] == target.index }
# Useless if the target is faster than the user
next Battle::AI::MOVE_USELESS_SCORE if idx_target < idx_user
# Useless if the target will move next anyway
next Battle::AI::MOVE_USELESS_SCORE if idx_target - idx_user <= 1
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
#===============================================================================
# TargetActsLast
Battle::AI::Handlers::MoveEffectScore.add("TargetActsLast",
proc { |score, move, user, ai, battle|
# Useless if the user has no ally
has_ally = false
ai.each_ally(user.index) { |b, i| has_ally = true }
next Battle::AI::MOVE_USELESS_SCORE if !has_ally
# Useless if the target is an ally
next Battle::AI::MOVE_USELESS_SCORE if !target.opposes?(user)
# Compare the speeds of all battlers
speeds = []
ai.each_battler { |b, i| speeds.push([i, rough_stat(:SPEED)]) }
if battle.field.effects[PBEffects::TrickRoom] > 0
speeds.sort! { |a, b| a[1] <=> b[1] }
else
speeds.sort! { |a, b| b[1] <=> a[1] }
end
idx_user = speeds.index { |ele| ele[0] == user.index }
idx_target = speeds.index { |ele| ele[0] == target.index }
# Useless if the target is faster than the user
next Battle::AI::MOVE_USELESS_SCORE if idx_target < idx_user
# Useless if the target will move last anyway
next Battle::AI::MOVE_USELESS_SCORE if idx_target == speeds.length - 1
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
@@ -474,6 +519,12 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetStatusMove
!battle.moldBreaker && target.has_active_ability?(:OBLIVIOUS)
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetStatusMoves",
proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if !target.check_for_move { |m| m.statusMove? }
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
@@ -484,20 +535,20 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetHealingMov
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
}
)
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetHealingMoves",
proc { |score, move, user, target, ai, battle|
next Battle::AI::MOVE_USELESS_SCORE if !target.check_for_move { |m| m.healingMove? }
next score
}
)
#===============================================================================
# TODO: Review score modifiers.
#===============================================================================
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("DisableTargetSoundMoves",
proc { |score, move, user, target, ai, battle|
if target.effects[PBEffects::ThroatChop] == 0 && ai.trainer.high_skill?
hasSoundMove = false
user.battler.eachMove do |m|
next if !m.soundMove?
hasSoundMove = true
break
end
score += 40 if hasSoundMove
if target.effects[PBEffects::ThroatChop] == 0
score += 10 if target.check_for_move { |m| m.soundMove? }
end
next score
}

View File

@@ -113,6 +113,10 @@ class Battle::AI::AIMove
is_critical = rough_critical_hit_stage >= Battle::Move::CRITICAL_HIT_RATIOS.length
##### Calculate user's attack stat #####
if ["CategoryDependsOnHigherDamagePoisonTarget",
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(function)
@move.pbOnStartUse(user.battler, [target.battler]) # Calculate category
end
atk, atk_stage = @move.pbGetAttackStats(user.battler, target.battler)
if !target.has_active_ability?(:UNAWARE) || @ai.battle.moldBreaker
atk_stage = 6 if is_critical && atk_stage < 6