mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-07 13:15:01 +00:00
AI function code rewrites
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
return false if !target.battler.pbCanRaiseStatStage?(stat, @user.battler, @move.move)
|
||||
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)
|
||||
return false if !target.battler.pbCanLowerStatStage?(stat, @user.battler, @move.move)
|
||||
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
|
||||
|
||||
@@ -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 ||
|
||||
target.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 ||
|
||||
target.pbOwnSide.effects[PBEffects::StealthRock]
|
||||
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::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
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !target_stage_better
|
||||
score += (target_stages - user_stages) * 10
|
||||
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
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if pos_stages == 0
|
||||
next score + (pos_stages - neg_stages) * 10
|
||||
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
|
||||
end
|
||||
next Battle::AI::MOVE_USELESS_SCORE if !any_change || !pos_change
|
||||
next score + avg * 5
|
||||
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
|
||||
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 + stages * 10
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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",
|
||||
"HealUserDependingOnUserStockpile")
|
||||
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
|
||||
}
|
||||
)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user