More misc AI move effect calculation rewrites

This commit is contained in:
Maruno17
2022-09-02 00:24:56 +01:00
parent 429068f3cb
commit d870b027db
3 changed files with 284 additions and 44 deletions

View File

@@ -430,4 +430,120 @@ class Battle::AI
return score return score
end end
#=============================================================================
#
#=============================================================================
def get_score_for_terrain(terrain, move_user)
ret = 0
# Inherent effects of terrain
@battlers.each do |b|
next if !b || b.battler.fainted? || !b.battler.affectedByTerrain?
case terrain
when :Electric
# Immunity to sleep
# TODO: Check all battlers for sleep-inducing moves and other effects?
if b.status == :NONE
ret += (b.opposes?(move_user)) ? -5 : 5
end
if b.effects[PBEffects::Yawn] > 0
ret += (b.opposes?(move_user)) ? -10 : 10
end
# Check for Electric moves
if b.check_for_move { |move| move.type == :ELECTRIC && move.damagingMove? }
ret += (b.opposes?(move_user)) ? -15 : 15
end
when :Grassy
# End of round healing
ret += (b.opposes?(move_user)) ? -10 : 10
# Check for Grass moves
if b.check_for_move { |move| move.type == :GRASS && move.damagingMove? }
ret += (b.opposes?(move_user)) ? -15 : 15
end
when :Misty
# Immunity to status problems/confusion
# TODO: Check all battlers for status/confusion-inducing moves and other
# effects?
if b.status == :NONE || b.effects[PBEffects::Confusion] == 0
ret += (b.opposes?(move_user)) ? -5 : 5
end
# Check for Dragon moves
if b.check_for_move { |move| move.type == :DRAGON && move.damagingMove? }
ret += (b.opposes?(move_user)) ? 15 : -15
end
when :Psychic
# Check for priority moves
if b.check_for_move { |move| move.priority > 0 && move.pbTarget&.can_target_one_foe? }
ret += (b.opposes?(move_user)) ? 10 : -10
end
# Check for Psychic moves
if b.check_for_move { |move| move.type == :PSYCHIC && move.damagingMove? }
ret += (b.opposes?(move_user)) ? -15 : 15
end
end
end
# Held items relating to terrain
seed = {
:Electric => :ELECTRICSEED,
:Grassy => :GRASSYSEED,
:Misty => :MISTYSEED,
:Psychic => :PSYCHICSEED
}[terrain]
ai.battlers.each do |b|
next if !b || b.battler.fainted?
if b.has_active_item?(:TERRAINEXTENDER)
ret += (b.opposes?(move_user)) ? -15 : 15
elsif seed && b.has_active_item?(seed)
ret += (b.opposes?(move_user)) ? -15 : 15
end
end
# Check for abilities/moves affected by the terrain
if ai.trainer.medium_skill?
abils = {
:Electric => :SURGESURFER,
:Grassy => :GRASSPELT,
:Misty => nil,
:Psychic => nil
}[terrain]
good_moves = {
:Electric => ["DoublePowerInElectricTerrain"],
:Grassy => ["HealTargetDependingOnGrassyTerrain",
"HigherPriorityInGrassyTerrain"],
:Misty => ["UserFaintsPowersUpInMistyTerrainExplosive"],
:Psychic => ["HitsAllFoesAndPowersUpInPsychicTerrain"]
}[terrain]
bad_moves = {
:Electric => nil,
:Grassy => ["DoublePowerIfTargetUnderground",
"LowerTargetSpeed1WeakerInGrassyTerrain",
"RandomPowerDoublePowerIfTargetUnderground"],
:Misty => nil,
:Psychic => nil
}[terrain]
ai.battlers.each do |b|
next if !b || b.battler.fainted? || !b.battler.affectedByTerrain?
# Abilities
if b.has_active_ability?(:MIMICRY)
ret += (b.opposes?(move_user)) ? -5 : 5
end
if abils && b.has_active_ability?(abils)
ret += (b.opposes?(move_user)) ? -15 : 15
end
# Moves
if b.check_for_move { |move| ["EffectDependsOnEnvironment",
"SetUserTypesBasedOnEnvironment",
"TypeAndPowerDependOnTerrain",
"UseMoveDependingOnEnvironment"].include?(move.function) }
ret += (b.opposes?(move_user)) ? -10 : 10
end
if good_moves && b.check_for_move { |move| good_moves.include?(move.function) }
ret += (b.opposes?(move_user)) ? -10 : 10
end
if bad_moves && b.check_for_move { |move| bad_moves.include?(move.function) }
ret += (b.opposes?(move_user)) ? 10 : -10
end
end
end
return ret
end
end end

View File

@@ -346,52 +346,97 @@ Battle::AI::Handlers::MoveEffectScore.add("StartHailWeather",
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("StartElectricTerrain", Battle::AI::Handlers::MoveFailureCheck.add("StartElectricTerrain",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
next true if battle.field.terrain == :Electric next true if battle.field.terrain == :Electric
} }
) )
Battle::AI::Handlers::MoveEffectScore.add("StartElectricTerrain",
proc { |score, move, user, target, ai, battle|
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
if battle.field.terrain != :None
score -= ai.get_score_for_terrain(battle.field.terrain, user)
end
score += ai.get_score_for_terrain(:Electric, user)
next score
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("StartGrassyTerrain", Battle::AI::Handlers::MoveFailureCheck.add("StartGrassyTerrain",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
next true if battle.field.terrain == :Grassy next true if battle.field.terrain == :Grassy
} }
) )
Battle::AI::Handlers::MoveEffectScore.add("StartGrassyTerrain",
proc { |score, move, user, target, ai, battle|
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
if battle.field.terrain != :None
score -= ai.get_score_for_terrain(battle.field.terrain, user)
end
score += ai.get_score_for_terrain(:Grassy, user)
next score
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("StartMistyTerrain", Battle::AI::Handlers::MoveFailureCheck.add("StartMistyTerrain",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
next true if battle.field.terrain == :Misty next true if battle.field.terrain == :Misty
} }
) )
Battle::AI::Handlers::MoveEffectScore.add("StartMistyTerrain",
proc { |score, move, user, target, ai, battle|
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
if battle.field.terrain != :None
score -= ai.get_score_for_terrain(battle.field.terrain, user)
end
score += ai.get_score_for_terrain(:Misty, user)
next score
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("StartPsychicTerrain", Battle::AI::Handlers::MoveFailureCheck.add("StartPsychicTerrain",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
next true if battle.field.terrain == :Psychic next true if battle.field.terrain == :Psychic
} }
) )
Battle::AI::Handlers::MoveEffectScore.add("StartPsychicTerrain",
proc { |score, move, user, target, ai, battle|
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
if battle.field.terrain != :None
score -= ai.get_score_for_terrain(battle.field.terrain, user)
end
score += ai.get_score_for_terrain(:Psychic, user)
next score
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("RemoveTerrain", Battle::AI::Handlers::MoveFailureCheck.add("RemoveTerrain",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
next true if battle.field.terrain == :None next true if battle.field.terrain == :None
} }
) )
Battle::AI::Handlers::MoveEffectScore.add("RemoveTerrain",
proc { |score, move, user, target, ai, battle|
next score - ai.get_score_for_terrain(battle.field.terrain, user)
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("AddSpikesToFoeSide", Battle::AI::Handlers::MoveFailureCheck.add("AddSpikesToFoeSide",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -400,18 +445,34 @@ Battle::AI::Handlers::MoveFailureCheck.add("AddSpikesToFoeSide",
) )
Battle::AI::Handlers::MoveEffectScore.add("AddSpikesToFoeSide", Battle::AI::Handlers::MoveEffectScore.add("AddSpikesToFoeSide",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if user.battler.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) } inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
next score - 90 # Opponent can't switch in any Pokemon foe_reserves = []
else battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
score += 10 * battle.pbAbleNonActiveCount(user.idxOpposingSide) next if !pkmn || !pkmn.able? || inBattleIndices.include(idxParty)
score += [40, 26, 13][user.pbOpposingSide.effects[PBEffects::Spikes]] if ai.trainer.medium_skill?
next score # Check affected by entry hazard
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
# Check can take indirect damage
next if pkmn.hasAbility?(:MAGICGUARD)
# Check airborne
if !pkmn.hasItem?(:IRONBALL) &&
battle.field.effects[PBEffects::Gravity] == 0
next if pkmn.hasType?(:FLYING)
next if pkmn.hasAbility?(:LEVITATE)
next if pkmn.hasItem?(:AIRBALLOON)
end
end
foe_reserves.push(pkmn) # pkmn will be affected by Spikes
end end
next score - 40 if foe_reserves.empty?
multiplier = [8, 5, 3][user.pbOpposingSide.effects[PBEffects::Spikes]]
score += multiplier * foe_reserves.length
next score
} }
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("AddToxicSpikesToFoeSide", Battle::AI::Handlers::MoveFailureCheck.add("AddToxicSpikesToFoeSide",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -420,18 +481,36 @@ Battle::AI::Handlers::MoveFailureCheck.add("AddToxicSpikesToFoeSide",
) )
Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide", Battle::AI::Handlers::MoveEffectScore.add("AddToxicSpikesToFoeSide",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if user.battler.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) } inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
next score - 90 # Opponent can't switch in any Pokemon foe_reserves = []
else battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
score += 8 * battle.pbAbleNonActiveCount(user.idxOpposingSide) next if !pkmn || !pkmn.able? || inBattleIndices.include(idxParty)
score += [26, 13][user.pbOpposingSide.effects[PBEffects::ToxicSpikes]] if ai.trainer.medium_skill?
next score # Check affected by entry hazard
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
# TODO: Check pkmn's immunity to being poisoned.
next if battle.field.terrain == :Misty
next if pkmn.hasType?(:POISON)
next if pkmn.hasType?(:STEEL)
# Check airborne
if !pkmn.hasItem?(:IRONBALL) &&
battle.field.effects[PBEffects::Gravity] == 0
next if pkmn.hasType?(:FLYING)
next if pkmn.hasAbility?(:LEVITATE)
next if pkmn.hasItem?(:AIRBALLOON)
end
end
foe_reserves.push(pkmn) # pkmn will be affected by Toxic Spikes
end end
next score - 40 if foe_reserves.empty?
multiplier = [6, 4][user.pbOpposingSide.effects[PBEffects::ToxicSpikes]]
score += multiplier * foe_reserves.length
next score
} }
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("AddStealthRocksToFoeSide", Battle::AI::Handlers::MoveFailureCheck.add("AddStealthRocksToFoeSide",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -440,25 +519,57 @@ Battle::AI::Handlers::MoveFailureCheck.add("AddStealthRocksToFoeSide",
) )
Battle::AI::Handlers::MoveEffectScore.add("AddStealthRocksToFoeSide", Battle::AI::Handlers::MoveEffectScore.add("AddStealthRocksToFoeSide",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if user.battler.allOpposing.none? { |b| battle.pbCanChooseNonActive?(b.index) } inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
next score - 90 # Opponent can't switch in any Pokemon foe_reserves = []
else battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
next score + 10 * battle.pbAbleNonActiveCount(user.idxOpposingSide) next if !pkmn || !pkmn.able? || inBattleIndices.include(idxParty)
if ai.trainer.medium_skill?
# Check affected by entry hazard
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
# Check can take indirect damage
next if pkmn.hasAbility?(:MAGICGUARD)
end
foe_reserves.push(pkmn) # pkmn will be affected by Stealth Rock
end end
next score - 40 if foe_reserves.empty?
next score + 8 * foe_reserves.length
} }
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("AddStickyWebToFoeSide", Battle::AI::Handlers::MoveFailureCheck.add("AddStickyWebToFoeSide",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
next true if user.pbOpposingSide.effects[PBEffects::StickyWeb] next true if user.pbOpposingSide.effects[PBEffects::StickyWeb]
} }
) )
Battle::AI::Handlers::MoveEffectScore.add("AddStickyWebToFoeSide",
proc { |score, move, user, target, ai, battle|
inBattleIndices = battle.allSameSideBattlers(user.idxOpposingSide).map { |b| b.pokemonIndex }
foe_reserves = []
battle.pbParty(user.idxOpposingSide).each_with_index do |pkmn, idxParty|
next if !pkmn || !pkmn.able? || inBattleIndices.include(idxParty)
if ai.trainer.medium_skill?
# Check affected by entry hazard
next if pkmn.hasItem?(:HEAVYDUTYBOOTS)
# Check airborne
if !pkmn.hasItem?(:IRONBALL) &&
battle.field.effects[PBEffects::Gravity] == 0
next if pkmn.hasType?(:FLYING)
next if pkmn.hasAbility?(:LEVITATE)
next if pkmn.hasItem?(:AIRBALLOON)
end
end
foe_reserves.push(pkmn) # pkmn will be affected by Sticky Web
end
next score - 40 if foe_reserves.empty?
next score + 7 * foe_reserves.length
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("SwapSideEffects", Battle::AI::Handlers::MoveFailureCheck.add("SwapSideEffects",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -484,10 +595,9 @@ Battle::AI::Handlers::MoveFailureCheck.add("SwapSideEffects",
Battle::AI::Handlers::MoveEffectScore.add("SwapSideEffects", Battle::AI::Handlers::MoveEffectScore.add("SwapSideEffects",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if ai.trainer.medium_skill? if ai.trainer.medium_skill?
good_effects = [:Reflect, :LightScreen, :AuroraVeil, :SeaOfFire, good_effects = [:AuroraVeil, :LightScreen, :Mist, :Rainbow, :Reflect,
:Swamp, :Rainbow, :Mist, :Safeguard, :Safeguard, :SeaOfFire, :Swamp, :Tailwind].map! { |e| PBEffects.const_get(e) }
:Tailwind].map! { |e| PBEffects.const_get(e) } bad_effects = [:Spikes, :StealthRock, :StickyWeb, :ToxicSpikes].map! { |e| PBEffects.const_get(e) }
bad_effects = [:Spikes, :StickyWeb, :ToxicSpikes, :StealthRock].map! { |e| PBEffects.const_get(e) }
bad_effects.each do |e| bad_effects.each do |e|
score += 10 if ![0, false, nil].include?(user.pbOwnSide.effects[e]) score += 10 if ![0, false, nil].include?(user.pbOwnSide.effects[e])
score -= 10 if ![0, 1, false, nil].include?(user.pbOpposingSide.effects[e]) score -= 10 if ![0, 1, false, nil].include?(user.pbOpposingSide.effects[e])
@@ -514,23 +624,24 @@ Battle::AI::Handlers::MoveFailureCheck.add("UserMakeSubstitute",
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("RemoveUserBindingAndEntryHazards", Battle::AI::Handlers::MoveEffectScore.add("RemoveUserBindingAndEntryHazards",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
score += 30 if user.effects[PBEffects::Trapping] > 0 score += 10 if user.effects[PBEffects::Trapping] > 0
score += 30 if user.effects[PBEffects::LeechSeed] >= 0 score += 15 if user.effects[PBEffects::LeechSeed] >= 0
if battle.pbAbleNonActiveCount(user.idxOwnSide) > 0 if battle.pbAbleNonActiveCount(user.idxOwnSide) > 0
score += 80 if user.pbOwnSide.effects[PBEffects::Spikes] > 0 score += 15 if user.pbOwnSide.effects[PBEffects::Spikes] > 0
score += 80 if user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0 score += 15 if user.pbOwnSide.effects[PBEffects::ToxicSpikes] > 0
score += 80 if user.pbOwnSide.effects[PBEffects::StealthRock] score += 20 if user.pbOwnSide.effects[PBEffects::StealthRock]
score += 15 if user.pbOwnSide.effects[PBEffects::StickyWeb]
end end
next score next score
} }
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("AttackTwoTurnsLater", Battle::AI::Handlers::MoveFailureCheck.add("AttackTwoTurnsLater",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -539,15 +650,13 @@ Battle::AI::Handlers::MoveFailureCheck.add("AttackTwoTurnsLater",
) )
Battle::AI::Handlers::MoveEffectScore.add("AttackTwoTurnsLater", Battle::AI::Handlers::MoveEffectScore.add("AttackTwoTurnsLater",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0 # Future Sight tends to be wasteful if down to last Pokémon
# Future Sight tends to be wasteful if down to last Pokemon next score - 20 if battle.pbAbleNonActiveCount(user.idxOwnSide) == 0
next score - 70
end
} }
) )
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveFailureCheck.add("UserSwapsPositionsWithAlly", Battle::AI::Handlers::MoveFailureCheck.add("UserSwapsPositionsWithAlly",
proc { |move, user, target, ai, battle| proc { |move, user, target, ai, battle|
@@ -561,12 +670,26 @@ Battle::AI::Handlers::MoveFailureCheck.add("UserSwapsPositionsWithAlly",
next num_targets != 1 next num_targets != 1
} }
) )
Battle::AI::Handlers::MoveEffectScore.add("UserSwapsPositionsWithAlly",
proc { |score, move, user, target, ai, battle|
next score - 30 # Usually no point in using this
}
)
#=============================================================================== #===============================================================================
# TODO: Review score modifiers. #
#=============================================================================== #===============================================================================
Battle::AI::Handlers::MoveEffectScore.add("BurnAttackerBeforeUserActs", Battle::AI::Handlers::MoveEffectScore.add("BurnAttackerBeforeUserActs",
proc { |score, move, user, target, ai, battle| proc { |score, move, user, target, ai, battle|
next score + 20 # Because of possible burning ai.battlers.each do |b|
next if !b || !b.opposes?(user)
next if !b.battler.affectedByContactEffect?
next if !b.battler.pbCanBurn?(user.battler, false, move.move)
if ai.trainer.high_skill?
next if !b.check_for_move { |move| move.pbContactMove?(b.battler) }
end
score += 10 # Possible to burn
end
next score
} }
) )

View File

@@ -79,6 +79,7 @@ class Battle::AI::AIMove
return @move.type return @move.type
end end
# TODO: Should this exist, or should all type checks go through rough_type?
def pbCalcType(user); return @move.pbCalcType(user); end def pbCalcType(user); return @move.pbCalcType(user); end
#============================================================================= #=============================================================================