Files
infinitefusion-e18/AI references/Marin's AI/AI_ScoreHandler.rb
2022-08-10 18:38:20 +01:00

1491 lines
47 KiB
Ruby

# Evasion when Foresighted
# cont 120: self destruct
class MKAI
class ScoreHandler
@@GeneralCode = []
@@MoveCode = {}
@@StatusCode = []
@@DamagingCode = []
def self.add_status(&code)
@@StatusCode << code
end
def self.add_damaging(&code)
@@DamagingCode << code
end
def self.add(*moves, &code)
if moves.size == 0
@@GeneralCode << code
else
moves.each do |move|
if move.is_a?(Symbol) # Specific move
id = getConst(PBMoves, move)
raise "Invalid move #{move}" if id.nil? || id == 0
@@MoveCode[id] = code
elsif move.is_a?(String) # Function code
@@MoveCode[move] = code
end
end
end
end
def self.trigger(list, score, ai, user, target, move)
return score if list.nil?
list = [list] if !list.is_a?(Array)
list.each do |code|
next if code.nil?
newscore = code.call(score, ai, user, target, move)
score = newscore if newscore.is_a?(Numeric)
end
return score
end
def self.trigger_general(score, ai, user, target, move)
return self.trigger(@@GeneralCode, score, ai, user, target, move)
end
def self.trigger_status_moves(score, ai, user, target, move)
return self.trigger(@@StatusCode, score, ai, user, target, move)
end
def self.trigger_damaging_moves(score, ai, user, target, move)
return self.trigger(@@DamagingCode, score, ai, user, target, move)
end
def self.trigger_move(move, score, ai, user, target)
id = move.id
id = move.function if !@@MoveCode[id]
return self.trigger(@@MoveCode[id], score, ai, user, target, move)
end
end
end
#=============================================================================#
# #
# Multipliers #
# #
#=============================================================================#
# Effectiveness modifier
# For this to have a more dramatic effect, this block could be moved lower down
# so that it factors in more score modifications before multiplying.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
# Effectiveness doesn't add anything for fixed-damage moves.
next if move.is_a?(PokeBattle_FixedDamageMove) || move.statusMove?
# Add half the score times the effectiveness modifiers. Means super effective
# will be a 50% increase in score.
target_types = target.types
mod = move.pbCalcTypeMod(move.type, user, target) / PBTypeEffectiveness::NORMAL_EFFECTIVE.to_f
# If mod is 0, i.e. the target is immune to the move (based on type, at least),
# we do not multiply the score to 0, because immunity is handled as a final multiplier elsewhere.
if mod != 0 && mod != 1
score *= mod
MKAI.log("* #{mod} for effectiveness")
end
next score
end
#=============================================================================#
# #
# All Moves #
# #
#=============================================================================#
# Accuracy modifier to favor high-accuracy moves
MKAI::ScoreHandler.add do |score, ai, user, target, move|
next if user.battler == target.battler
accuracy = user.get_move_accuracy(move, target)
missing = 100 - accuracy
# (High) Jump Kick, a move that damages you when you miss
if move.function == "10B"
# Decrease the score more drastically if it has lower accuracy
missing *= 2.0
end
if missing > 0
score -= missing
MKAI.log("- #{missing} for accuracy")
end
next score
end
# Increase/decrease score for each positive/negative stat boost the move gives the user
MKAI::ScoreHandler.add do |score, ai, user, target, move|
next if !move.is_a?(PokeBattle_MultiStatUpMove) && !move.is_a?(PokeBattle_StatUpMove) &&
!move.is_a?(PokeBattle_StatDownMove)
boosts = 0
atkBoosts = 0
spAtkBoosts = 0
evBoosts = 0
stats = []
if move.statUp
for i in 0...move.statUp.size / 2
stat = move.statUp[i * 2]
incr = move.statUp[i * 2 + 1]
boosts += incr
atkBoosts += incr if stat == PBStats::ATTACK
spAtkBoosts += incr if stat == PBStats::SPATK
evBoosts += incr if stat == PBStats::EVASION
stats << stat
end
end
if move.statDown
for i in 0...move.statDown.size / 2
stat = move.statDown[i * 2]
decr = move.statDown[i * 2 + 1]
boosts -= decr if
atkBoosts -= decr if stat == PBStats::ATTACK
spAtkBoosts -= decr if stat == PBStats::SPATK
stats << stat if !stats.include?(stat)
end
end
# Increase score by 10 * (net stage differences)
# If attack is boosted and the user is a physical attacker,
# these stage increases are multiplied by 20 instead of 10.
if atkBoosts > 0 && user.is_physical_attacker?
atkIncr = (atkBoosts * 30 * (2 - (user.stages[PBStats::ATTACK] + 6) / 6.0)).round
if atkIncr > 0
score += atkIncr
MKAI.log("+ #{atkIncr} for attack boost and being a physical attacker")
boosts -= atkBoosts
end
end
# If spatk is boosted and the user is a special attacker,
# these stage increases are multiplied by 20 instead of 10.
if spAtkBoosts > 0 && user.is_special_attacker?
spatkIncr = (spAtkBoosts * 30 * (2 - (user.stages[PBStats::SPATK] + 6) / 6.0)).round
if spatkIncr > 0
score += spatkIncr
MKAI.log("+ #{spatkIncr} for spatk boost and being a special attacker")
boosts -= spAtkBoosts
end
end
# Boost to evasion
if evBoosts != 0
evIncr = (evBoosts * 50 * (2 - (user.stages[PBStats::EVASION] + 6) / 6.0)).round
if evIncr > 0
score += evIncr
MKAI.log("+ #{evIncr} for evasion boost")
boosts -= evBoosts
end
end
# All remaining stat increases (or decreases) are multiplied by 25 and added to the score.
if boosts != 0
total = 6 * stats.size
eff = total
user.stages.each_with_index do |value, stage|
if stats.include?(stage)
eff -= value
end
end
fact = 1.0
fact = eff / total.to_f if total != 0
incr = (boosts * 25 * fact).round
if incr > 0
score += incr
MKAI.log("+ #{incr} for general user buffs (#{eff}/#{total} effectiveness)")
end
end
next score
end
# Increase/decrease score for each positive/negative stat boost the move gives the target
MKAI::ScoreHandler.add do |score, ai, user, target, move|
next if !move.is_a?(PokeBattle_TargetStatDownMove) && !move.is_a?(PokeBattle_TargetMultiStatDownMove)
debuffs = 0
accDecreases = 0
stats = []
if move.statDown
for i in 0...move.statDown.size / 2
stat = move.statDown[i * 2]
decr = move.statDown[i * 2 + 1]
debuffs += decr
accDecreases += decr if stat == PBStats::ACCURACY
stats << stat if stat != PBStats::EVASION && stat != PBStats::ACCURACY
end
end
if accDecreases != 0 && target.stages[PBStats::ACCURACY] != -6
accIncr = (accDecreases * 50 * (target.stages[PBStats::ACCURACY] + 6) / 6.0).round
score += accIncr
debuffs -= accIncr
MKAI.log("+ #{accIncr} for target accuracy debuff")
end
# All remaining stat decrases are multiplied by 10 and added to the score.
if debuffs > 0
total = 6 * stats.size
eff = total
target.stages.each_with_index do |value, stage|
if stats.include?(stage)
eff += value
end
end
fact = 1.0
fact = eff / total.to_f if total != 0
incr = (debuffs * 25 * fact).round
score += incr
MKAI.log("+ #{incr} for general target debuffs (#{eff}/#{total} effectiveness)")
end
next score
end
# Prefer priority moves that deal enough damage to knock the target out.
# Use previous damage dealt to determine if it deals enough damage now,
# or make a rough estimate.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
# Apply this logic only for priority moves
next if move.priority <= 0 || move.function == "0D4" # Bide
prevDmg = target.get_damage_by_user_and_move(user, move)
if prevDmg.size > 0
# We have the previous damage this user has done with this move.
# Use the average of the previous damage dealt, and if it's more than the target's hp,
# we can likely use this move to knock out the target.
avg = (prevDmg.map { |e| e[2] }.sum / prevDmg.size.to_f).floor
if avg >= target.battler.hp
MKAI.log("+ 250 for priority move with average damage (#{avg}) >= target hp (#{target.battler.hp})")
score += 250
end
else
# Calculate the damage this priority move will do.
# The AI kind of cheats here, because this takes all items, berries, abilities, etc. into account.
# It is worth for the effect though; the AI using a priority move to prevent
# you from using one last move before you faint.
dmg = user.get_move_damage(target, move)
if dmg >= target.battler.hp
MKAI.log("+ 250 for priority move with predicted damage (#{dmg}) >= target hp (#{target.battler.hp})")
score += 250
end
end
next score
end
# Encourage using fixed-damage moves if the fixed damage is more than the target has HP
MKAI::ScoreHandler.add do |score, ai, user, target, move|
next if !move.is_a?(PokeBattle_FixedDamageMove) || move.function == "070" || move.function == "0D4"
dmg = move.pbFixedDamage(user, target)
if dmg >= target.hp
score += 175
MKAI.log("+ 175 for this move's fixed damage being enough to knock out the target")
end
next score
end
# See if any moves used in the past did enough damage to now kill the target,
# and if so, give that move slightly more preference.
# There can be more powerful moves that might also take out the user,
# but if this move will also take the user out, this is a safer option.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
next if move.function == "0D4" # Bide
# Get all times this move was used on the target
ary = target.get_damage_by_user_and_move(user, move)
# If this move has been used before, and the move is not a two-turn move
if ary.size > 0 && !move.chargingTurnMove? && move.function != "0C2" # Hyper Beam
# Calculate the average damage of every time this move was used on the target
avg = ary.map { |e| e[2] }.sum / ary.size.to_f
# If the average damage this move dealt is enough to kill the target, increase likelihood of choosing this move
if avg >= target.hp
score += 100
MKAI.log("+ 100 for this move being likely to take out the target")
end
end
next score
end
# Prefer moves that are usable while the user is asleep
MKAI::ScoreHandler.add do |score, ai, user, target, move|
# If the move is usable while asleep, and if the user won't wake up this turn
# Kind of cheating, but insignificant. This way the user can choose a more powerful move instead
if move.usableWhenAsleep?
if user.asleep? && user.statusCount > 1
score += 200
MKAI.log("+ 200 for being able to use this move while asleep")
else
score -= 50
MKAI.log("- 50 for this move will have no effect")
end
end
next score
end
# Prefer moves that can thaw the user if the user is frozen
MKAI::ScoreHandler.add do |score, ai, user, target, move|
# If the user is frozen and the move thaws the user
if user.frozen? && move.thawsUser?
score += 80
MKAI.log("+ 80 for being able to thaw the user")
end
next score
end
# Discourage using OHKO moves if the target is higher level or it has sturdy
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.function == "070" # OHKO Move
if target.has_ability?(:STURDY)
score -= 100
MKAI.log("- 100 for the target has Sturdy")
end
if target.level > user.level
score -= 80
MKAI.log("- 80 for the move will fail due to level difference")
end
score -= 50
MKAI.log("- 50 for OHKO moves are generally considered bad")
end
next score
end
# Encourage using trapping moves, since they're generally weak
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.function == "0CF" # Trapping Move
if target.effects[PBEffects::Trapping] == 0 # The target is not yet trapped
score += 60
MKAI.log("+ 60 for initiating a multi-turn trap")
end
end
next score
end
# Encourage using flinching moves if the user is faster
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.flinchingMove? && (user.faster_than?(target) || move.priority > 0)
score += 50
MKAI.log("+ 50 for being able to flinch the target")
end
next score
end
# Discourage using a multi-hit physical move if the target has an item or ability
# that will damage the user on each contact.
# Also slightly discourages physical moves if the target has a bad ability in general.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.pbContactMove?(user)
if user.discourage_making_contact_with?(target)
if move.multiHitMove?
score -= 60
MKAI.log("- 60 for the target has an item or ability that activates on each contact")
else
score -= 30
MKAI.log("- 30 for the target has an item or ability that activates on contact")
end
end
end
next score
end
# Encourage using moves that can cause a burn.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.is_a?(PokeBattle_BurnMove) && !target.burned? && target.can_burn?(user, move)
chance = move.pbAdditionalEffectChance(user, target)
chance = 100 if chance == 0
if chance > 0 && chance <= 100
if target.is_physical_attacker?
add = 30 + chance * 2
score += add
MKAI.log("+ #{add} for being able to burn the physical-attacking target")
else
score += chance
MKAI.log("+ #{chance} for being able to burn the target")
end
end
end
next score
end
# Encourage using moves that can cause freezing.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.is_a?(PokeBattle_FreezeMove) && !target.frozen? && target.can_freeze?(user, move)
chance = move.pbAdditionalEffectChance(user, target)
chance = 100 if chance == 0
if chance > 0 && chance <= 100
score += chance * 2
MKAI.log("+ #{chance} for being able to freeze the target")
end
end
next score
end
# Encourage using moves that can cause paralysis.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.is_a?(PokeBattle_ParalysisMove) && !target.paralyzed? && target.can_paralyze?(user, move)
chance = move.pbAdditionalEffectChance(user, target)
chance = 100 if chance == 0
if chance > 0 && chance <= 100
score += chance
MKAI.log("+ #{chance} for being able to paralyze the target")
end
end
next score
end
# Encourage using moves that can cause sleep.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.is_a?(PokeBattle_SleepMove) && !target.asleep? && target.can_sleep?(user, move)
chance = move.pbAdditionalEffectChance(user, target)
chance = 100 if chance == 0
if chance > 0 && chance <= 100
score += chance
MKAI.log("+ #{chance} for being able to put the target to sleep")
end
end
next score
end
# Encourage using moves that can cause poison.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.is_a?(PokeBattle_PoisonMove) && !target.poisoned? && target.can_poison?(user, move)
chance = move.pbAdditionalEffectChance(user, target)
chance = 100 if chance == 0
if chance > 0 && chance <= 100
if move.toxic
add = chance * 1.4 * move.pbNumHits(user, [target])
score += add
MKAI.log("+ #{add} for being able to badly poison the target")
else
add = chance * move.pbNumHits(user, [target])
score += add
MKAI.log("+ #{add} for being able to poison the target")
end
end
end
next score
end
# Encourage using moves that can cause confusion.
MKAI::ScoreHandler.add do |score, ai, user, target, move|
if move.is_a?(PokeBattle_ConfuseMove) && !target.confused?
chance = move.pbAdditionalEffectChance(user, target)
chance = 100 if chance == 0
if chance > 0 && chance <= 100
add = chance * move.pbNumHits(user, [target])
# The higher the target's attack stats, the more beneficial it is to confuse the target.
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]
stage = target.stages[PBStats::ATTACK] + 6
factor = stageMul[stage] / stageDiv[stage].to_f
add *= factor
score += add
MKAI.log("+ #{add} for being able to confuse the target")
end
end
next score
end
#=============================================================================#
# #
# Damaging Moves #
# #
#=============================================================================#
# STAB modifier
MKAI::ScoreHandler.add_damaging do |score, ai, user, target, move|
# STAB doesn't add anything for fixed-damage moves.
next if move.is_a?(PokeBattle_FixedDamageMove)
calcType = move.pbCalcType(user.battler)
if calcType >= 0 && user.has_type?(calcType)
if user.has_ability?(:ADAPTABILITY)
MKAI.log("+ 90 for STAB with Adaptability")
score += 90
else
MKAI.log("+ 50 for STAB")
score += 50
end
end
next score
end
# Stat stages and physical/special attacker label
MKAI::ScoreHandler.add_damaging do |score, ai, user, target, move|
# Stat boosts don't add anything for fixed-damage moves.
next if move.is_a?(PokeBattle_FixedDamageMove)
# If the move is physical
if move.physicalMove?
# Increase the score by 25 per stage increase/decrease
if user.stages[PBStats::ATTACK] != 0
add = user.stages[PBStats::ATTACK] * 25
score += add
MKAI.log("#{add < 0 ? "-" : "+"} #{add.abs} for attack stages")
end
# Make the move more likely to be chosen if this user is also considered a physical attacker.
if user.is_physical_attacker?
score += 30
MKAI.log("+ 30 for being a physical attacker")
end
end
# If the move is special
if move.specialMove?
# Increase the score by 25 per stage increase/decrease
if user.stages[PBStats::SPATK] != 0
add = user.stages[PBStats::SPATK] * 25
score += add
MKAI.log("#{add < 0 ? "-" : "+"} #{add.abs} for attack stages")
end
# Make the move more likely to be chosen if this user is also considered a special attacker.
if user.is_special_attacker?
score += 30
MKAI.log("+ 30 for being a special attacker")
end
end
next score
end
# Discourage using damaging moves if the target is semi-invulnerable and slower,
# and encourage using damaging moves if they can break through the semi-invulnerability
# (e.g. prefer earthquake when target is underground)
MKAI::ScoreHandler.add_damaging do |score, ai, user, target, move|
# Target is semi-invulnerable
if target.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0
encourage = false
discourage = false
# User will hit first while target is still semi-invulnerable.
# If this move will do extra damage because the target is semi-invulnerable,
# encourage using this move. If not, discourage using it.
if user.faster_than?(target)
if target.in_two_turn_attack?("0C9", "0CC", "0CE") # Fly, Bounce, Sky Drop
encourage = move.hitsFlyingTargets?
discourage = !encourage
elsif target.in_two_turn_attack?("0CA") # Dig
# Do not encourage using Fissure, even though it can hit digging targets, because it's an OHKO move
encourage = move.hitsDiggingTargets? && move.function != "070"
discourage = !encourage
elsif target.in_two_turn_attack?("0CB") # Dive
encourage = move.hitsDivingTargets?
discourage = !encourage
else
discourage = true
end
end
# If the user has No Guard
if user.has_ability?(:NOGUARD)
# Then any move would be able to hit the target, meaning this move wouldn't be anything special.
encourage = false
discourage = false
end
if encourage
score += 100
MKAI.log("+ 100 for being able to hit through a semi-invulnerable state")
elsif discourage
score -= 150
MKAI.log("- 150 for not being able to hit target because of semi-invulnerability")
end
end
next score
end
# Lower the score of multi-turn moves, because they likely have quite high power and thus score.
MKAI::ScoreHandler.add_damaging do |score, ai, user, target, move|
if !user.has_item?(:POWERHERB) && (move.chargingTurnMove? || move.function == "0C2") # Hyper Beam
score -= 70
MKAI.log("- 70 for requiring a charging turn")
end
next score
end
# Prefer using damaging moves based on the level difference between the user and target,
# because if the user will get one-shot, then there's no point in using set-up moves.
# Furthermore, if the target is more than 5 levels higher than the user, priority
# get an additional boost to ensure the user can get a hit in before being potentially one-shot.
# TODO: Make "underdog" method, also for use by moves like perish song or explode and such
MKAI::ScoreHandler.add_damaging do |score, ai, user, target, move|
# Start counting factor this when there's a level difference of greater than 5
if user.underdog?(target)
add = 5 * (target.level - user.level - 5)
if add > 0
score += add
MKAI.log("+ #{5 * (target.level - user.level - 5)} for preferring damaging moves due to being a low level")
end
if move.priority > 0
score += 30
MKAI.log("+ 30 for being a priority move and being and underdog")
end
end
next score
end
# Discourage using physical moves when the user is burned
MKAI::ScoreHandler.add_damaging do |score, ai, user, target, move|
if user.burned?
if move.physicalMove? && move.function != "07E"
score -= 50
MKAI.log("- 50 for being a physical move and being burned")
end
end
next score
end
# Encourage high-critical hit rate moves, or damaging moves in general
# if Laser Focus or Focus Energy has been used
MKAI::ScoreHandler.add_damaging do |score, ai, user, target, move|
next if !move.pbCouldBeCritical?(user.battler, target.battler)
if move.highCriticalRate? || user.effects[PBEffects::LaserFocus] > 0 ||
user.effects[PBEffects::FocusEnergy] > 0
score += 30
MKAI.log("+ 30 for having a high critical-hit rate")
end
next score
end
# Discourage recoil moves if they would knock the user out
MKAI::ScoreHandler.add_damaging do |score, ai, user, target, move|
if move.is_a?(PokeBattle_RecoilMove)
dmg = move.pbRecoilDamage(user.battler, target.battler)
if dmg >= user.hp
score -= 50
MKAI.log("- 50 for the recoil will knock the user out")
end
end
next score
end
#=============================================================================#
# #
# Move-specific #
# #
#=============================================================================#
# Facade
MKAI::ScoreHandler.add("07E") do |score, ai, user, target, move|
if user.burned? || user.poisoned? || user.paralyzed?
score += 50
MKAI.log("+ 50 for doing more damage with a status condition")
end
next score
end
# Aromatherapy, Heal Bell
MKAI::ScoreHandler.add("019") do |score, ai, user, target, move|
count = 0
user.side.battlers.each do |proj|
next if proj.nil?
# + 80 for each active battler with a status condition
count += 2.0 if proj.has_non_volatile_status?
end
user.side.party.each do |proj|
next if proj.battler # Skip battlers
# Inactive party members do not have a battler attached,
# so we can't use has_non_volatile_status?
count += 1.0 if proj.pokemon.status > 0
# + 40 for each inactive pokemon with a status condition in the party
end
if count != 0
add = count * 40.0
score += add
MKAI.log("+ #{add} for curing status condition(s)")
else
score -= 30
MKAI.log("- 30 for not curing any status conditions")
end
next score
end
# Psycho Shift
MKAI::ScoreHandler.add("01B") do |score, ai, user, target, move|
# If the user has a status condition that is not frozen,
if user.has_non_volatile_status? && !user.frozen?
# And the target doesn't have any status conditions
if !target.has_non_volatile_status?
# Then we can transfer our status condition
transferrable = true
transferrable = false if user.burned? && !target.can_burn?(user, move)
transferrable = false if user.poisoned? && !target.can_poison?(user, move)
transferrable = false if user.paralyzed? && !target.can_paralyze?(user, move)
transferrable = false if user.asleep? && !target.can_sleep?(user, move)
if transferrable
score += 120
MKAI.log("+ 120 for being able to pass on our status condition")
if user.burned? && target.is_physical_attacker?
score += 50
MKAI.log("+ 50 for being able to burn the physical-attacking target")
end
end
end
else
score -= 30
MKAI.log("- 30 for not having a transferrable status condition")
end
next score
end
# Purify
MKAI::ScoreHandler.add("15B") do |score, ai, user, target, move|
if target.has_non_volatile_status?
factor = 1 - user.hp / user.totalhp.to_f
# At full hp, factor is 0 (thus not encouraging this move)
# At half hp, factor is 0.5 (thus slightly encouraging this move)
# At 1 hp, factor is about 1.0 (thus encouraging this move)
if user.flags[:will_be_healed]
score -= 30
MKAI.log("- 30 for the user will already be healed by something")
elsif factor != 0
if user.is_healing_pointless?(0.5)
score -= 10
MKAI.log("- 10 for we will take more damage than we can heal if the target repeats their move")
elsif user.is_healing_necessary?(0.5)
add = (factor * 250).round
score += add
MKAI.log("+ #{add} for we will likely die without healing")
else
add = (factor * 125).round
score += add
MKAI.log("+ #{add} for we have lost some hp")
end
end
else
score -= 30
MKAI.log("- 30 for the move will fail since the target has no status condition")
end
next score
end
# Refresh
MKAI::ScoreHandler.add("018") do |score, ai, user, target, move|
if user.burned? || user.poisoned? || user.paralyzed?
score += 70
MKAI.log("+ 70 for being able to cure our status condition")
end
next score
end
# Rest
MKAI::ScoreHandler.add("0D9") do |score, ai, user, target, move|
factor = 1 - user.hp / user.totalhp.to_f
if user.flags[:will_be_healed]
score -= 30
MKAI.log("- 30 for the user will already be healed by something")
elsif factor != 0
# Not at full hp
if user.can_sleep?(user, move, true)
add = (factor * 100).round
score += add
MKAI.log("+ #{add} for we have lost some hp")
else
score -= 10
MKAI.log("- 10 for the move will fail")
end
end
next score
end
# Smelling Salts
MKAI::ScoreHandler.add("07C") do |score, ai, user, target, move|
if target.paralyzed?
score += 50
MKAI.log("+ 50 for doing double damage")
end
next score
end
# Wake-Up Slap
MKAI::ScoreHandler.add("07D") do |score, ai, user, target, move|
if target.asleep?
score += 50
MKAI.log("+ 50 for doing double damage")
end
next score
end
# Fire Fang, Flare Blitz
MKAI::ScoreHandler.add("00B", "0FE") do |score, ai, user, target, move|
if !target.burned? && target.can_burn?(user, move)
if target.is_physical_attacker?
score += 40
MKAI.log("+ 40 for being able to burn the physical-attacking target")
else
score += 10
MKAI.log("+ 10 for being able to burn the target")
end
end
next score
end
# Ice Fang
MKAI::ScoreHandler.add("00E") do |score, ai, user, target, move|
if !target.frozen? && target.can_freeze?(user, move)
score += 20
MKAI.log("+ 20 for being able to freeze the target")
end
next score
end
# Thunder Fang
MKAI::ScoreHandler.add("009") do |score, ai, user, target, move|
if !target.paralyzed? && target.can_paralyze?(user, move)
score += 10
MKAI.log("+ 10 for being able to paralyze the target")
end
next score
end
# Ice Burn
MKAI::ScoreHandler.add("0C6") do |score, ai, user, target, move|
if !target.burned? && target.can_burn?(user, move)
if target.is_physical_attacker?
score += 80
MKAI.log("+ 80 for being able to burn the physical-attacking target")
else
score += 30
MKAI.log("+ 30 for being able to burn the target")
end
end
next score
end
# Secret Power
MKAI::ScoreHandler.add("0A4") do |score, ai, user, target, move|
score += 40
MKAI.log("+ 40 for its potential side effects")
next score
end
# Tri Attack
MKAI::ScoreHandler.add("017") do |score, ai, user, target, move|
if !target.has_non_volatile_status?
score += 50
MKAI.log("+ 50 for being able to cause a status condition")
end
next score
end
# Freeze Shock, Bounce
MKAI::ScoreHandler.add("0C5", "0CC") do |score, ai, user, target, move|
if !target.paralyzed? && target.can_paralyze?(user, move)
score += 30
MKAI.log("+ 30 for being able to paralyze the target")
end
next score
end
# Volt Tackle
MKAI::ScoreHandler.add("0FD") do |score, ai, user, target, move|
if !target.paralyzed? && target.can_paralyze?(user, move)
score += 10
MKAI.log("+ 10 for being able to paralyze the target")
end
next score
end
# Toxic Thread
MKAI::ScoreHandler.add("159") do |score, ai, user, target, move|
if !target.paralyzed? && target.can_paralyze?(user, move)
score += 50
MKAI.log("+ 50 for being able to poison the target")
end
if target.battler.pbCanLowerStatStage?(PBStats::SPEED, user, move) &&
target.faster_than?(user)
score += 30
MKAI.log("+ 30 for being able to lower target speed")
end
next score
end
# Dark Void
MKAI::ScoreHandler.add(:DARKVOID) do |score, ai, user, target, move|
if user.is_species?(:DARKRAI)
if !target.asleep? && target.can_sleep?(user, move)
score += 120
MKAI.log("+ 120 for damaging the target with Nightmare if it is asleep")
end
else
score -= 100
MKAI.log("- 100 for this move will fail")
end
next score
end
# Yawn
MKAI::ScoreHandler.add("004") do |score, ai, user, target, move|
if !target.has_non_volatile_status? && target.effects[PBEffects::Yawn] == 0
score += 60
MKAI.log("+ 60 for putting the target to sleep")
end
next score
end
# Flatter
MKAI::ScoreHandler.add("040") do |score, ai, user, target, move|
if target.confused?
score -= 30
MKAI.log("- 30 for only raising target stats without being able to confuse it")
else
score += 30
MKAI.log("+ 30 for confusing the target")
end
next score
end
# Swagger
MKAI::ScoreHandler.add("041") do |score, ai, user, target, move|
if target.confused?
score -= 50
MKAI.log("- 50 for only raising target stats without being able to confuse it")
else
score += 50
MKAI.log("+ 50 for confusing the target")
if !target.is_physical_attacker?
score += 50
MKAI.log("+ 50 for the target also is not a physical attacker")
end
end
next score
end
# Attract
MKAI::ScoreHandler.add("016") do |score, ai, user, target, move|
# If the target can be attracted by the user
if target.can_attract?(user)
score += 150
MKAI.log("+ 150 for being able to attract the target")
end
next score
end
# Rage
MKAI::ScoreHandler.add("093") do |score, ai, user, target, move|
dmg = user.get_move_damage(target, move)
perc = dmg / target.totalhp.to_f
perc /= 1.5 if user.discourage_making_contact_with?(target)
score += perc * 150
next score
end
# Uproar, Thrash, Petal Dance, Outrage, Ice Ball, Rollout
MKAI::ScoreHandler.add("0D1", "0D2", "0D3") do |score, ai, user, target, move|
dmg = user.get_move_damage(target, move)
perc = dmg / target.totalhp.to_f
perc /= 1.5 if user.discourage_making_contact_with?(target) && move.pbContactMove?(user)
if perc != 0
add = perc * 80
score += add
MKAI.log("+ #{add} for dealing about #{(perc * 100).round}% dmg")
end
next score
end
# Stealth Rock, Spikes, Toxic Spikes
MKAI::ScoreHandler.add("103", "104", "105") do |score, ai, user, target, move|
if move.function == "103" && user.opposing_side.effects[PBEffects::Spikes] >= 3 ||
move.function == "104" && user.opposing_side.effects[PBEffects::ToxicSpikes] >= 2 ||
move.function == "105" && user.opposing_side.effects[PBEffects::StealthRock]
score -= 30
MKAI.log("- 30 for the opposing side already has max spikes")
else
inactive = user.opposing_side.party.size - user.opposing_side.battlers.compact.size
add = inactive * 30
add *= (3 - user.opposing_side.effects[PBEffects::Spikes]) / 3.0 if move.function == "103"
add *= 3 / 4.0 if user.opposing_side.effects[PBEffects::ToxicSpikes] == 1 && move.function == "104"
score += add
MKAI.log("+ #{add} for there are #{inactive} pokemon to be sent out at some point")
end
next score
end
# Disable
MKAI::ScoreHandler.add("0B9") do |score, ai, user, target, move|
# Already disabled one of the target's moves
if target.effects[PBEffects::Disable] > 1
score -= 30
MKAI.log("- 30 for the target is already disabled")
else
# Get previous damage done by the target
prevDmg = target.get_damage_by_user(user)
if prevDmg.size > 0
lastDmg = prevDmg[-1]
# If the last move did more than 50% damage and the target was faster,
# we can't disable the move in time thus using Disable is pointless.
if user.is_healing_pointless?(0.5) && target.faster_than?(user)
score -= 30
MKAI.log("- 30 for the target move is too strong and the target is faster")
else
add = (lastDmg[3] * 150).round
score += add
MKAI.log("+ #{add} for we disable a strong move")
end
else
# Target hasn't used a damaging move yet
score -= 30
MKAI.log("- 30 for the target hasn't used a damaging move yet.")
end
end
next score
end
# Counter
MKAI::ScoreHandler.add("071") do |score, ai, user, target, move|
expect = false
expect = true if target.is_physical_attacker? && !target.is_healing_necessary?(0.5)
prevDmg = user.get_damage_by_user(target)
if prevDmg.size > 0
lastDmg = prevDmg[-1]
lastMove = lastDmg[1]
expect = true if lastMove.physicalMove?
end
# If we can reasonably expect the target to use a physical move
if expect
score += 60
MKAI.log("+ 60 for we can reasonably expect the target to use a physical move")
end
next score
end
# Aqua Ring
MKAI::ScoreHandler.add("0DA") do |score, ai, user, target, move|
if !user.effects[PBEffects::AquaRing]
if !user.underdog?(target)
score += 80
MKAI.log("+ 80 for gaining hp each round")
else
# Underdogs are likely to die fast, so setting up healing for each round
# is likely useless and only a waste of a turn.
score += 40
MKAI.log("+ 40 for gaining hp each round despite being an underdog")
end
else
score -= 30
MKAI.log("- 30 for the user already has an aqua ring")
end
next score
end
# Ingrain
MKAI::ScoreHandler.add("0DB") do |score, ai, user, target, move|
if !user.effects[PBEffects::Ingrain]
if !user.underdog?(target)
score += 80
MKAI.log("+ 80 for gaining hp each round")
else
# Underdogs are likely to die fast, so setting up healing for each round
# is likely useless and only a waste of a turn.
score += 40
MKAI.log("+ 40 for gaining hp each round despite being an underdog")
end
else
score -= 30
MKAI.log("- 30 for the user is already ingrained")
end
next score
end
# Leech Seed
MKAI::ScoreHandler.add("0DC") do |score, ai, user, target, move|
if !user.underdog?(target) && !target.has_type?(:GRASS) && target.effects[PBEffects::LeechSeed] == 0
score += 60
MKAI.log("+ 60 for sapping hp from the target")
end
next score
end
# Leech Life, Parabolic Charge, Drain Punch, Giga Drain, Horn Leech, Mega Drain, Absorb
MKAI::ScoreHandler.add("0DD") do |score, ai, user, target, move|
dmg = user.get_move_damage(target, move)
add = dmg / 2
score += add
MKAI.log("+ #{add} for hp gained")
next score
end
# Dream Eater
MKAI::ScoreHandler.add("0DE") do |score, ai, user, target, move|
if target.asleep?
dmg = user.get_move_damage(target, move)
add = dmg / 2
score += add
MKAI.log("+ #{add} for hp gained")
else
score -= 30
MKAI.log("- 30 for the move will fail")
end
next score
end
# Heal Pulse
MKAI::ScoreHandler.add("0DF") do |score, ai, user, target, move|
# If the target is an ally
ally = false
target.battler.eachAlly do |battler|
ally = true if battler == user.battler
end
if ally
factor = 1 - target.hp / target.totalhp.to_f
# At full hp, factor is 0 (thus not encouraging this move)
# At half hp, factor is 0.5 (thus slightly encouraging this move)
# At 1 hp, factor is about 1.0 (thus encouraging this move)
if target.flags[:will_be_healed]
score -= 30
MKAI.log("- 30 for the target will already be healed by something")
elsif factor != 0
if target.is_healing_pointless?(0.5)
score -= 10
MKAI.log("- 10 for the target will take more damage than we can heal if the opponent repeats their move")
elsif target.is_healing_necessary?(0.5)
add = (factor * 250).round
score += add
MKAI.log("+ #{add} for the target will likely die without healing")
else
add = (factor * 125).round
score += add
MKAI.log("+ #{add} for the target has lost some hp")
end
else
score -= 30
MKAI.log("- 30 for the target is at full hp")
end
else
score -= 30
MKAI.log("- 30 for the target is not an ally")
end
next score
end
# Whirlwind, Roar, Circle Throw, Dragon Tail, U-Turn, Volt Switch
MKAI::ScoreHandler.add("0EB", "0EC", "0EE") do |score, ai, user, target, move|
if user.bad_against?(target) && user.level >= target.level &&
!target.has_ability?(:SUCTIONCUPS) && !target.effects[PBEffects::Ingrain]
score += 100
MKAI.log("+ 100 for forcing our target to switch and we're bad against our target")
end
next score
end
# Anchor Shot, Block, Mean Look, Spider Web, Spirit Shackle, Thousand Waves
MKAI::ScoreHandler.add("0EF") do |score, ai, user, target, move|
if target.bad_against?(user) && !target.has_type?(:GHOST)
score += 100
MKAI.log("+ 100 for locking our target in battle with us and they're bad against us")
end
next score
end
# Mimic
MKAI::ScoreHandler.add("05C") do |score, ai, user, target, move|
blacklisted = ["002", "014", "05C", "05D", "0B6"] # Struggle, Chatter, Mimic, Sketch, Metronome
last_move = pbGetMoveData(target.battler.lastRegularMoveUsed)
# Don't mimic if no move has been used or we can't mimic the move
if target.battler.lastRegularMoveUsed <= 0 || blacklisted.include?(last_move[MOVE_FUNCTION_CODE])
score -= 30
MKAI.log("- 30 for we can't mimic any move used prior")
else
move_id = last_move[MOVE_ID]
matchup = target.calculate_move_matchup(move_id)
# If our target used a move that would also be super effective against them,
# it would be a good idea to mimic that move now so we can use it against them.
if matchup > 1
add = (matchup * 75.0).round
score += add
MKAI.log("+ #{add} for we can mimic a move that would be super effective against the target too.")
end
end
next score
end
# Recover, Slack Off, Soft-Boiled, Heal Order, Milk Drink, Roost, Wish
MKAI::ScoreHandler.add("0D5", "0D6", "0D7") do |score, ai, user, target, move|
factor = 1 - user.hp / user.totalhp.to_f
# At full hp, factor is 0 (thus not encouraging this move)
# At half hp, factor is 0.5 (thus slightly encouraging this move)
# At 1 hp, factor is about 1.0 (thus encouraging this move)
if user.flags[:will_be_healed]
score -= 30
MKAI.log("- 30 for the user will already be healed by something")
elsif factor != 0
if user.is_healing_pointless?(0.5)
score -= 10
MKAI.log("- 10 for we will take more damage than we can heal if the target repeats their move")
elsif user.is_healing_necessary?(0.5)
add = (factor * 250).round
score += add
MKAI.log("+ #{add} for we will likely die without healing")
else
add = (factor * 125).round
score += add
MKAI.log("+ #{add} for we have lost some hp")
end
else
score -= 30
MKAI.log("- 30 for we are at full hp")
end
next score
end
# Moonlight, Morning Sun, Synthesis
MKAI::ScoreHandler.add("0D8") do |score, ai, user, target, move|
heal_factor = 0.5
case ai.battle.pbWeather
when PBWeather::Sun, PBWeather::HarshSun
heal_factor = 2.0 / 3.0
when PBWeather::None, PBWeather::StrongWinds
heal_factor = 0.5
else
heal_factor = 0.25
end
effi_factor = 1.0
effi_factor = 0.5 if heal_factor == 0.25
factor = 1 - user.hp / user.totalhp.to_f
# At full hp, factor is 0 (thus not encouraging this move)
# At half hp, factor is 0.5 (thus slightly encouraging this move)
# At 1 hp, factor is about 1.0 (thus encouraging this move)
if user.flags[:will_be_healed]
score -= 30
MKAI.log("- 30 for the user will already be healed by something")
elsif factor != 0
if user.is_healing_pointless?(heal_factor)
score -= 10
MKAI.log("- 10 for we will take more damage than we can heal if the target repeats their move")
elsif user.is_healing_necessary?(heal_factor)
add = (factor * 250 * effi_factor).round
score += add
MKAI.log("+ #{add} for we will likely die without healing")
else
add = (factor * 125 * effi_factor).round
score += add
MKAI.log("+ #{add} for we have lost some hp")
end
else
score -= 30
MKAI.log("- 30 for we are at full hp")
end
next score
end
# Minimize
MKAI::ScoreHandler.add("034") do |score, ai, user, target, move|
accuracy = false
double = false
target.side.battlers.any? do |proj|
accuracy = true if proj.moves.any? { |move| move.tramplesMinimize?(1) && !proj.target_is_immune?(move, user) }
double = true if proj.moves.any? { |move| move.tramplesMinimize?(2) && !proj.target_is_immune?(move, user) }
end
if accuracy
score -= 40
MKAI.log("- 40 for the target has moves that will hit with perfect accuracy against minimized targets")
end
if double
score -= 90
MKAI.log("- 90 for the target has moves that will deal double damage against minimized targets")
end
next score
end
# Lucky Chant
MKAI::ScoreHandler.add("0A1") do |score, ai, user, target, move|
if user.side.effects[PBEffects::LuckyChant] > 0
score -= 30
MKAI.log("- 30 for lucky chant is already active")
elsif user.side.flags[:will_luckychant]
score -= 30
MKAI.log("- 30 for another battler will already use lucky chant")
else
enemies = target.side.battlers.select { |proj| !proj.fainted? }.size
critenemies = target.side.battlers.select { |proj| proj.moves.any? { |m| m.highCriticalRate? } }.size
add = enemies * 20 + critenemies * 30
score += add
MKAI.log("+ #{add} based on enemy and high-crit-dealing enemy moves count")
end
next score
end
# Reflect
MKAI::ScoreHandler.add("0A2") do |score, ai, user, target, move|
if user.side.effects[PBEffects::Reflect] > 0
score -= 30
MKAI.log("- 30 for reflect is already active")
elsif user.side.flags[:will_reflect]
score -= 30
MKAI.log("- 30 for another battler will already use reflect")
else
enemies = target.side.battlers.select { |proj| !proj.fainted? }.size
physenemies = target.side.battlers.select { |proj| proj.is_physical_attacker? }.size
add = enemies * 20 + physenemies * 30
score += add
MKAI.log("+ #{add} based on enemy and physical enemy count")
end
next score
end
# Light Screen
MKAI::ScoreHandler.add("0A3") do |score, ai, user, target, move|
if user.side.effects[PBEffects::LightScreen] > 0
score -= 30
MKAI.log("- 30 for light screen is already active")
elsif user.side.flags[:will_lightscreen]
score -= 30
MKAI.log("- 30 for another battler will already use light screen")
else
enemies = target.side.battlers.select { |proj| !proj.fainted? }.size
specenemies = target.side.battlers.select { |proj| proj.is_special_attacker? }.size
add = enemies * 20 + specenemies * 30
score += add
MKAI.log("+ #{add} based on enemy and special enemy count")
end
next score
end
# Haze
MKAI::ScoreHandler.add("051") do |score, ai, user, target, move|
if user.side.flags[:will_haze]
score -= 30
MKAI.log("- 30 for another battler will already use haze")
else
net = 0
# User buffs: net goes up
# User debuffs: net goes down
# Target buffs: net goes down
# Target debuffs: net goes up
# The lower net is, the better Haze is to choose.
user.side.battlers.each do |proj|
PBStats.eachBattleStat { |s| net += proj.stages[s] }
end
target.side.battlers.each do |proj|
PBStats.eachBattleStat { |s| net -= proj.stages[s] }
end
# As long as the target's stat stages are more advantageous than ours (i.e. net < 0), Haze is a good choice
if net < 0
add = -net * 20
score += add
MKAI.log("+ #{add} to reset disadvantageous stat stages")
else
score -= 30
MKAI.log("- 30 for our stat stages are advantageous")
end
end
next score
end
# Bide
MKAI::ScoreHandler.add("0D4") do |score, ai, user, target, move|
# If we've been hit at least once, use Bide if we could take two hits of the last attack and survive
prevDmg = target.get_damage_by_user(user)
if prevDmg.size > 0
lastDmg = prevDmg[-1]
predDmg = lastDmg[2] * 2
# We would live if we took two hits of the last move
if user.hp - predDmg > 0
score += 120
MKAI.log("+ 120 for we can survive two subsequent attacks")
else
score -= 10
MKAI.log("- 10 for we would not survive two subsequent attacks")
end
else
score -= 10
MKAI.log("- 10 for we don't know whether we'd survive two subsequent attacks")
end
next score
end
# Metronome
MKAI::ScoreHandler.add("0B6") do |score, ai, user, target, move|
score += 20
MKAI.log("+ 20 to make this move an option if all other choices also have a low score")
next score
end
# Mirror Move
MKAI::ScoreHandler.add("0AE") do |score, ai, user, target, move|
if target.battler.lastRegularMoveUsed <= 0 || target.faster_than?(user)
score -= 10
MKAI.log("- 10 for we don't know what move our target will use")
elsif target.battler.lastRegularMoveUsed <= 0 && user.faster_than?(target)
score -= 30
MKAI.log("- 30 for our target has not made a move yet")
else
# Can Mirror Move
if pbGetMoveData(target.battler.lastRegularMoveUsed, MOVE_FLAGS)[/e/]
matchup = target.calculate_move_matchup(pbGetMoveData(target.battler.lastRegularMoveUsed, MOVE_ID))
# Super Effective
if matchup > 1
add = (matchup * 75.0).round
score += add
MKAI.log("+ #{add} for being able to mirror a super effective move")
else
score -= 30
MKAI.log("- 30 for we would likely mirror a not very effective move")
end
else
score -= 30
MKAI.log("- 30 for we would not be able to mirror the move the target will likely use")
end
end
next score
end