mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-06 06:01:46 +00:00
Improved AI code for weather-causing moves, added AI getting flags from trainer types, added Legendary/Mythical flags to pokemon.txt, added Setting to make wild Legendary/Mythical Pokémon smarter
This commit is contained in:
@@ -104,4 +104,11 @@ module Settings
|
||||
CHECK_EVOLUTION_AFTER_ALL_BATTLES = (MECHANICS_GENERATION >= 6)
|
||||
# Whether fainted Pokémon can try to evolve after a battle.
|
||||
CHECK_EVOLUTION_FOR_FAINTED_POKEMON = true
|
||||
|
||||
#=============================================================================
|
||||
|
||||
# Whether wild Pokémon with the "Legendary" or "Mythical" flag (as defined in
|
||||
# pokemon.txt) have a smarter AI. Their skill level is set to 32, which is a
|
||||
# medium skill level.
|
||||
SMARTER_WILD_LEGENDARY_POKEMON = true
|
||||
end
|
||||
|
||||
@@ -788,7 +788,7 @@ class Battle::Move::UseLastMoveUsedByTarget < Battle::Move
|
||||
|
||||
def pbFailsAgainstTarget?(user, target, show_message)
|
||||
if !target.lastRegularMoveUsed ||
|
||||
GameData::Move.get(target.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] }
|
||||
!GameData::Move.get(target.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
||||
@battle.pbDisplay(_INTL("The mirror move failed!")) if show_message
|
||||
return true
|
||||
end
|
||||
|
||||
@@ -105,7 +105,7 @@ class Battle::AI
|
||||
end
|
||||
# Spikes
|
||||
if battler_side.effects[PBEffects::Spikes] > 0 && !pkmn.hasAbility?(:MAGICGUARD) &&
|
||||
!battler.airborne? && !pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
!pkmn.hasItem?(:HEAVYDUTYBOOTS)
|
||||
if @battle.field.effects[PBEffects::Gravity] > 0 || pkmn.hasItem?(:IRONBALL) ||
|
||||
!(pkmn.hasType?(:FLYING) || pkmn.hasItem?(:LEVITATE) || pkmn.hasItem?(:AIRBALLOON))
|
||||
spikes_div = [8, 6, 4][battler_side.effects[PBEffects::Spikes] - 1]
|
||||
|
||||
@@ -19,21 +19,21 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
def pbGetMoveScores
|
||||
choices = []
|
||||
@user.battler.eachMoveWithIndex do |move, idxMove|
|
||||
@user.battler.eachMoveWithIndex do |orig_move, idxMove|
|
||||
# Unchoosable moves aren't considered
|
||||
if !@battle.pbCanChooseMove?(@user.index, idxMove, false)
|
||||
if move.pp == 0 && move.total_pp > 0
|
||||
PBDebug.log_ai("#{@user.name} cannot use #{move.name} (no PP left)")
|
||||
if orig_move.pp == 0 && orig_move.total_pp > 0
|
||||
PBDebug.log_ai("#{@user.name} cannot use #{orig_move.name} (no PP left)")
|
||||
else
|
||||
PBDebug.log_ai("#{@user.name} cannot choose to use #{move.name}")
|
||||
PBDebug.log_ai("#{@user.name} cannot choose to use #{orig_move.name}")
|
||||
end
|
||||
next
|
||||
end
|
||||
# Set up move in class variables
|
||||
set_up_move_check(move)
|
||||
set_up_move_check(orig_move)
|
||||
# Predict whether the move will fail (generally)
|
||||
if @trainer.has_skill_flag?("PredictMoveFailure") && pbPredictMoveFailure
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{orig_move.name}...")
|
||||
PBDebug.log_score_change(MOVE_FAIL_SCORE - MOVE_BASE_SCORE, "move will fail")
|
||||
add_move_to_choices(choices, idxMove, MOVE_FAIL_SCORE)
|
||||
next
|
||||
@@ -47,7 +47,7 @@ class Battle::AI
|
||||
case target_data.num_targets
|
||||
when 0 # No targets, affects the user or a side or the whole field
|
||||
# Includes: BothSides, FoeSide, None, User, UserSide
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{orig_move.name}...")
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore }
|
||||
add_move_to_choices(choices, idxMove, score)
|
||||
@@ -61,7 +61,7 @@ class Battle::AI
|
||||
# TODO: Should this sometimes consider targeting an ally? See def
|
||||
# pbGetMoveScoreAgainstTarget for more information.
|
||||
next if target_data.targets_foe && !@user.battler.opposes?(b)
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{@move.name} against #{b.name} (#{b.index})...")
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{orig_move.name} against #{b.name} (#{b.index})...")
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore([b]) }
|
||||
add_move_to_choices(choices, idxMove, score, b.index)
|
||||
@@ -75,7 +75,7 @@ class Battle::AI
|
||||
next if !@battle.pbMoveCanTarget?(@user.battler.index, b.index, target_data)
|
||||
targets.push(b)
|
||||
end
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{@move.name}...")
|
||||
PBDebug.log_ai("#{@user.name} is considering using #{orig_move.name}...")
|
||||
score = MOVE_BASE_SCORE
|
||||
PBDebug.logonerr { score = pbGetMoveScore(targets) }
|
||||
add_move_to_choices(choices, idxMove, score)
|
||||
@@ -143,11 +143,6 @@ class Battle::AI
|
||||
!move.moveBlacklist.include?(GameData::Move.get(@battle.lastMoveUsed).function_code)
|
||||
move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@battle.lastMoveUsed))
|
||||
end
|
||||
when "UseLastMoveUsedByTarget"
|
||||
if target.battler.lastRegularMoveUsed &&
|
||||
GameData::Move.get(target.battler.lastRegularMoveUsed).flags.any? { |f| f[/^CanMirrorMove$/i] }
|
||||
move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(target.battler.lastRegularMoveUsed))
|
||||
end
|
||||
when "UseMoveDependingOnEnvironment"
|
||||
move.pbOnStartUse(@user.battler, []) # Determine which move is used instead
|
||||
move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(move.npMove))
|
||||
@@ -159,6 +154,13 @@ class Battle::AI
|
||||
def set_up_move_check_target(target)
|
||||
@target = (target) ? @battlers[target.index] : nil
|
||||
@target&.refresh_battler
|
||||
if @target && @move.function == "UseLastMoveUsedByTarget"
|
||||
if @target.battler.lastRegularMoveUsed &&
|
||||
GameData::Move.get(@target.battler.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
||||
mov = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@target.battler.lastRegularMoveUsed))
|
||||
@move.set_up(mov)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
@@ -231,7 +233,9 @@ class Battle::AI
|
||||
# TODO: Distinguish between affected foes and affected allies?
|
||||
affected_targets = 0
|
||||
# Get a score for the move against each target in turn
|
||||
orig_move = @move.move # In case move is Mirror Move and changes depending on the target
|
||||
targets.each do |target|
|
||||
set_up_move_check(orig_move)
|
||||
set_up_move_check_target(target)
|
||||
t_score = pbGetMoveScoreAgainstTarget
|
||||
next if t_score < 0
|
||||
|
||||
@@ -557,8 +557,127 @@ class Battle::AI
|
||||
#=============================================================================
|
||||
#
|
||||
#=============================================================================
|
||||
def get_score_for_terrain(terrain, move_user)
|
||||
def get_score_for_weather(weather, move_user, starting = false)
|
||||
return 0 if @battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
@battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
ret = 0
|
||||
if starting
|
||||
weather_extender = {
|
||||
:Sun => :HEATROCK,
|
||||
:Rain => :DAMPROCK,
|
||||
:Sandstorm => :SMOOTHROCK,
|
||||
:Hail => :ICYROCK
|
||||
}[weather]
|
||||
ret += 4 if weather_extender && move_user.has_active_item?(weather_extender)
|
||||
end
|
||||
each_battler do |b, i|
|
||||
# Check each battler for weather-specific effects
|
||||
case weather
|
||||
when :Sun
|
||||
# Check for Fire/Water moves
|
||||
if b.has_damaging_move_of_type?(:FIRE)
|
||||
ret += (b.opposes?(move_user)) ? -8 : 8
|
||||
end
|
||||
if b.has_damaging_move_of_type?(:WATER)
|
||||
ret += (b.opposes?(move_user)) ? 8 : -8
|
||||
end
|
||||
# TODO: Check for freezing moves.
|
||||
when :Rain
|
||||
# Check for Fire/Water moves
|
||||
if b.has_damaging_move_of_type?(:WATER)
|
||||
ret += (b.opposes?(move_user)) ? -8 : 8
|
||||
end
|
||||
if b.has_damaging_move_of_type?(:FIRE)
|
||||
ret += (b.opposes?(move_user)) ? 8 : -8
|
||||
end
|
||||
when :Sandstorm
|
||||
# Check for battlers affected by sandstorm's effects
|
||||
if b.battler.takesSandstormDamage? # End of round damage
|
||||
ret += (b.opposes?(move_user)) ? 8 : -8
|
||||
end
|
||||
if b.has_type?(:ROCK) # +SpDef for Rock types
|
||||
ret += (b.opposes?(move_user)) ? -8 : 8
|
||||
end
|
||||
when :Hail
|
||||
# Check for battlers affected by hail's effects
|
||||
if b.battler.takesHailDamage? # End of round damage
|
||||
ret += (b.opposes?(move_user)) ? 8 : -8
|
||||
end
|
||||
when :ShadowSky
|
||||
# Check for battlers affected by Shadow Sky's effects
|
||||
if b.has_damaging_move_of_type?(:SHADOW)
|
||||
ret += (b.opposes?(move_user)) ? 8 : -8
|
||||
end
|
||||
if b.battler.takesShadowSkyDamage? # End of round damage
|
||||
ret += (b.opposes?(move_user)) ? 8 : -8
|
||||
end
|
||||
end
|
||||
# Check each battler's abilities/other moves affected by the new weather
|
||||
if @trainer.medium_skill? && !b.has_active_item?(:UTILITYUMBRELLA)
|
||||
beneficial_abilities = {
|
||||
:Sun => [:CHLOROPHYLL, :FLOWERGIFT, :FORECAST, :HARVEST, :LEAFGUARD, :SOLARPOWER],
|
||||
:Rain => [:DRYSKIN, :FORECAST, :HYDRATION, :RAINDISH, :SWIFTSWIM],
|
||||
:Sandstorm => [:SANDFORCE, :SANDRUSH, :SANDVEIL],
|
||||
:Hail => [:FORECAST, :ICEBODY, :SLUSHRUSH, :SNOWCLOAK]
|
||||
}[weather]
|
||||
if beneficial_abilities && beneficial_abilities.length > 0 &&
|
||||
b.has_active_ability?(beneficial_abilities)
|
||||
ret += (b.opposes?(move_user)) ? -5 : 5
|
||||
end
|
||||
if weather == :Hail && b.ability == :ICEFACE
|
||||
ret += (b.opposes?(move_user)) ? -5 : 5
|
||||
end
|
||||
negative_abilities = {
|
||||
:Sun => [:DRYSKIN]
|
||||
}[weather]
|
||||
if negative_abilities && negative_abilities.length > 0 &&
|
||||
b.has_active_ability?(negative_abilities)
|
||||
ret += (b.opposes?(move_user)) ? 5 : -5
|
||||
end
|
||||
beneficial_moves = {
|
||||
:Sun => ["HealUserDependingOnWeather",
|
||||
"RaiseUserAtkSpAtk1Or2InSun",
|
||||
"TwoTurnAttackOneTurnInSun",
|
||||
"TypeAndPowerDependOnWeather"],
|
||||
:Rain => ["ConfuseTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"ParalyzeTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"TypeAndPowerDependOnWeather"],
|
||||
:Sandstorm => ["HealUserDependingOnSandstorm",
|
||||
"TypeAndPowerDependOnWeather"],
|
||||
:Hail => ["FreezeTargetAlwaysHitsInHail",
|
||||
"StartWeakenDamageAgainstUserSideIfHail",
|
||||
"TypeAndPowerDependOnWeather"],
|
||||
:ShadowSky => ["TypeAndPowerDependOnWeather"]
|
||||
}[weather]
|
||||
if beneficial_moves && beneficial_moves.length > 0 &&
|
||||
b.has_move_with_function?(*beneficial_moves)
|
||||
ret += (b.opposes?(move_user)) ? -5 : 5
|
||||
end
|
||||
negative_moves = {
|
||||
:Sun => ["ConfuseTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"ParalyzeTargetAlwaysHitsInRainHitsTargetInSky"],
|
||||
:Rain => ["HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun"],
|
||||
:Sandstorm => ["HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun"],
|
||||
:Hail => ["HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun"]
|
||||
}[weather]
|
||||
if negative_moves && negative_moves.length > 0 &&
|
||||
b.has_move_with_function?(*negative_moves)
|
||||
ret += (b.opposes?(move_user)) ? 5 : -5
|
||||
end
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
#
|
||||
#=============================================================================
|
||||
def get_score_for_terrain(terrain, move_user, starting = false)
|
||||
ret = 0
|
||||
ret += 4 if starting && terrain != :None && move_user.has_active_item?(:TERRAINEXTENDER)
|
||||
# Inherent effects of terrain
|
||||
each_battler do |b, i|
|
||||
next if !b.battler.affectedByTerrain?
|
||||
@@ -613,9 +732,7 @@ class Battle::AI
|
||||
:Psychic => :PSYCHICSEED
|
||||
}[terrain]
|
||||
each_battler do |b, i|
|
||||
if b.has_active_item?(:TERRAINEXTENDER)
|
||||
ret += (b.opposes?(move_user)) ? -15 : 15
|
||||
elsif seed && b.has_active_item?(seed)
|
||||
if seed && b.has_active_item?(seed)
|
||||
ret += (b.opposes?(move_user)) ? -15 : 15
|
||||
end
|
||||
end
|
||||
@@ -623,9 +740,7 @@ class Battle::AI
|
||||
if @trainer.medium_skill?
|
||||
abils = {
|
||||
:Electric => :SURGESURFER,
|
||||
:Grassy => :GRASSPELT,
|
||||
:Misty => nil,
|
||||
:Psychic => nil
|
||||
:Grassy => :GRASSPELT
|
||||
}[terrain]
|
||||
good_moves = {
|
||||
:Electric => ["DoublePowerInElectricTerrain"],
|
||||
@@ -635,12 +750,9 @@ class Battle::AI
|
||||
:Psychic => ["HitsAllFoesAndPowersUpInPsychicTerrain"]
|
||||
}[terrain]
|
||||
bad_moves = {
|
||||
:Electric => nil,
|
||||
:Grassy => ["DoublePowerIfTargetUnderground",
|
||||
"LowerTargetSpeed1WeakerInGrassyTerrain",
|
||||
"RandomPowerDoublePowerIfTargetUnderground"],
|
||||
:Misty => nil,
|
||||
:Psychic => nil
|
||||
:Grassy => ["DoublePowerIfTargetUnderground",
|
||||
"LowerTargetSpeed1WeakerInGrassyTerrain",
|
||||
"RandomPowerDoublePowerIfTargetUnderground"]
|
||||
}[terrain]
|
||||
each_battler do |b, i|
|
||||
next if !b.battler.affectedByTerrain?
|
||||
|
||||
@@ -258,7 +258,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_knocking_out_dest
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_targeting_bouncable_move_against_Magic_Bouncer,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if move.statusMove? && move.canMagicCoat? &&
|
||||
if move.statusMove? && move.move.canMagicCoat? &&
|
||||
!battle.moldBreaker && target.has_active_ability?(:MAGICBOUNCE)
|
||||
old_score = score
|
||||
score = Battle::AI::MOVE_USELESS_SCORE
|
||||
@@ -270,7 +270,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:avoid_targeting_bouncab
|
||||
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:avoid_bouncable_move_with_foe_Magic_Bouncer,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if move.statusMove? && move.canMagicCoat? &&
|
||||
if move.statusMove? && move.move.canMagicCoat? &&
|
||||
move.pbTarget(user.battler).num_targets == 0 && !battle.moldBreaker
|
||||
has_magic_bounce = false
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
|
||||
@@ -28,10 +28,27 @@ class Battle::AI::AITrainer
|
||||
@skill = 0
|
||||
@skill_flags = []
|
||||
set_up_skill
|
||||
set_up_skill_flags
|
||||
sanitize_skill_flags
|
||||
end
|
||||
|
||||
def set_up_skill
|
||||
@skill = @trainer.skill_level if @trainer
|
||||
if @trainer
|
||||
@skill = @trainer.skill_level
|
||||
elsif Settings::SMARTER_WILD_LEGENDARY_POKEMON
|
||||
# Give wild legendary/mythical Pokémon a higher skill
|
||||
wild_battler = @ai.battle.battlers[@side]
|
||||
sp_data = wild_battler.pokemon.species_data
|
||||
if sp_data.has_flag?("Legendary") || sp_data.has_flag?("Mythical")
|
||||
@skill = 32 # Medium skill
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def set_up_skill_flags
|
||||
if @trainer
|
||||
@trainer.flags.each { |flag| @skill_flags.push(flag) }
|
||||
end
|
||||
# TODO: Add skill flags depending on @skill.
|
||||
if @skill > 0
|
||||
@skill_flags.push("PredictMoveFailure")
|
||||
@@ -41,11 +58,17 @@ class Battle::AI::AITrainer
|
||||
if !medium_skill?
|
||||
@skill_flags.push("UsePokemonInOrder")
|
||||
elsif best_skill?
|
||||
# TODO: Also have flag "DontReserveLastPokemon" which negates this.
|
||||
@skill_flags.push("ReserveLastPokemon")
|
||||
end
|
||||
end
|
||||
|
||||
def sanitize_skill_flags
|
||||
# NOTE: Any skill flag which is shorthand for multiple other skill flags
|
||||
# should be "unpacked" here.
|
||||
# TODO: Have a bunch of "AntiX" flags that negate the corresponding "X" flags.
|
||||
# TODO: Have flag "DontReserveLastPokemon" which negates "ReserveLastPokemon".
|
||||
end
|
||||
|
||||
def has_skill_flag?(flag)
|
||||
return @skill_flags.include?(flag)
|
||||
end
|
||||
|
||||
@@ -201,6 +201,8 @@ class Battle::AI::AIBattler
|
||||
return active_types.include?(GameData::Type.get(type).id)
|
||||
end
|
||||
|
||||
# TODO: Also make a def effectiveness_of_move_against_battler which calls
|
||||
# pbCalcTypeModSingle instead of effectiveness_of_type_against_single_battler_type.
|
||||
def effectiveness_of_type_against_battler(type, user = nil)
|
||||
ret = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
return ret if !type
|
||||
@@ -214,8 +216,6 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
else
|
||||
@battler.pbTypes(true).each do |defend_type|
|
||||
# TODO: Need to check the move's pbCalcTypeModSingle because particular
|
||||
# moves can modify that method to give different effectivenesses.
|
||||
ret *= effectiveness_of_type_against_single_battler_type(type, defend_type, user)
|
||||
end
|
||||
ret *= 2 if self.effects[PBEffects::TarShot] && type == :FIRE
|
||||
|
||||
@@ -32,6 +32,7 @@ class Battle::AI::AIMove
|
||||
# ignoresReflect?
|
||||
|
||||
def id; return @move.id; end
|
||||
def name; return @move.name; end
|
||||
def physicalMove?(thisType = nil); return @move.physicalMove?(thisType); end
|
||||
def specialMove?(thisType = nil); return @move.specialMove?(thisType); end
|
||||
def damagingMove?; return @move.damagingMove?; end
|
||||
|
||||
@@ -175,38 +175,11 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSunWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score += 15 if user.has_active_item?(:HEATROCK)
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for Fire/Water moves
|
||||
ai.each_battler do |b, i|
|
||||
if b.has_damaging_move_of_type?(:FIRE)
|
||||
score += (b.opposes?(user)) ? -15 : 15
|
||||
end
|
||||
if b.has_damaging_move_of_type?(:WATER)
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
end
|
||||
# TODO: Check for freezing moves.
|
||||
# Check for abilities/other moves affected by sun
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_active_ability?([:CHLOROPHYLL, :FLOWERGIFT, :FORECAST, :HARVEST, :LEAFGUARD, :SOLARPOWER])
|
||||
score += 15
|
||||
elsif user.has_active_ability?(:DRYSKIN)
|
||||
score -= 10
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnWeather",
|
||||
"RaiseUserAtkSpAtk1Or2InSun",
|
||||
"TwoTurnAttackOneTurnInSun",
|
||||
"TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if user.has_move_with_function?("ConfuseTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"ParalyzeTargetAlwaysHitsInRainHitsTargetInSky")
|
||||
score -= 10
|
||||
end
|
||||
if ai.trainer.high_skill? && battle.field.weather != :None
|
||||
score -= ai.get_score_for_weather(battle.field.weather, user)
|
||||
end
|
||||
score += ai.get_score_for_weather(:Sun, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -220,34 +193,11 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRainWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score += 15 if user.has_active_item?(:DAMPROCK)
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for Fire/Water moves
|
||||
ai.each_battler do |b, i|
|
||||
if b.has_damaging_move_of_type?(:WATER)
|
||||
score += (b.opposes?(user)) ? -15 : 15
|
||||
end
|
||||
if b.has_damaging_move_of_type?(:FIRE)
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
end
|
||||
# Check for abilities/other moves affected by rain
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_active_ability?([:DRYSKIN, :FORECAST, :HYDRATION, :RAINDISH, :SWIFTSWIM])
|
||||
score += 15
|
||||
end
|
||||
if user.has_move_with_function?("ConfuseTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"ParalyzeTargetAlwaysHitsInRainHitsTargetInSky",
|
||||
"TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun")
|
||||
score -= 10
|
||||
end
|
||||
if ai.trainer.high_skill? && battle.field.weather != :None
|
||||
score -= ai.get_score_for_weather(battle.field.weather, user)
|
||||
end
|
||||
score += ai.get_score_for_weather(:Rain, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -261,33 +211,11 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSandstormWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score += 15 if user.has_active_item?(:SMOOTHROCK)
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for battlers affected by sandstorm's effects
|
||||
ai.each_battler do |b, i|
|
||||
if b.battler.takesSandstormDamage? # End of round damage
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
if b.has_type?(:ROCK) # +SpDef for Rock types
|
||||
score += (b.opposes?(user)) ? -15 : 15
|
||||
end
|
||||
end
|
||||
# Check for abilities/moves affected by sandstorm
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_active_ability?([:SANDFORCE, :SANDRUSH, :SANDVEIL])
|
||||
score += 15
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnSandstorm",
|
||||
"TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun")
|
||||
score -= 10
|
||||
end
|
||||
if ai.trainer.high_skill? && battle.field.weather != :None
|
||||
score -= ai.get_score_for_weather(battle.field.weather, user)
|
||||
end
|
||||
score += ai.get_score_for_weather(:Sandstorm, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -301,33 +229,11 @@ Battle::AI::Handlers::MoveEffectScore.add("StartHailWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score += 15 if user.has_active_item?(:ICYROCK)
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for battlers affected by hail's effects
|
||||
ai.each_battler do |b, i|
|
||||
if b.battler.takesHailDamage? # End of round damage
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
end
|
||||
# Check for abilities/moves affected by hail
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_active_ability?([:FORECAST, :ICEBODY, :SLUSHRUSH, :SNOWCLOAK])
|
||||
score += 15
|
||||
elsif user.ability == :ICEFACE
|
||||
score += 15
|
||||
end
|
||||
if user.has_move_with_function?("FreezeTargetAlwaysHitsInHail",
|
||||
"StartWeakenDamageAgainstUserSideIfHail",
|
||||
"TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if user.has_move_with_function?("HealUserDependingOnWeather",
|
||||
"TwoTurnAttackOneTurnInSun")
|
||||
score -= 10
|
||||
end
|
||||
if ai.trainer.high_skill? && battle.field.weather != :None
|
||||
score -= ai.get_score_for_weather(battle.field.weather, user)
|
||||
end
|
||||
score += ai.get_score_for_weather(:Hail, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -343,10 +249,10 @@ Battle::AI::Handlers::MoveFailureCheck.add("StartElectricTerrain",
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartElectricTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
if battle.field.terrain != :None
|
||||
if ai.trainer.high_skill? && battle.field.terrain != :None
|
||||
score -= ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
end
|
||||
score += ai.get_score_for_terrain(:Electric, user)
|
||||
score += ai.get_score_for_terrain(:Electric, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -362,10 +268,10 @@ Battle::AI::Handlers::MoveFailureCheck.add("StartGrassyTerrain",
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartGrassyTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
if battle.field.terrain != :None
|
||||
if ai.trainer.high_skill? && battle.field.terrain != :None
|
||||
score -= ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
end
|
||||
score += ai.get_score_for_terrain(:Grassy, user)
|
||||
score += ai.get_score_for_terrain(:Grassy, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -381,10 +287,10 @@ Battle::AI::Handlers::MoveFailureCheck.add("StartMistyTerrain",
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartMistyTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
if battle.field.terrain != :None
|
||||
if ai.trainer.high_skill? && battle.field.terrain != :None
|
||||
score -= ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
end
|
||||
score += ai.get_score_for_terrain(:Misty, user)
|
||||
score += ai.get_score_for_terrain(:Misty, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -400,10 +306,10 @@ Battle::AI::Handlers::MoveFailureCheck.add("StartPsychicTerrain",
|
||||
Battle::AI::Handlers::MoveEffectScore.add("StartPsychicTerrain",
|
||||
proc { |score, move, user, ai, battle|
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
if battle.field.terrain != :None
|
||||
if ai.trainer.high_skill? && battle.field.terrain != :None
|
||||
score -= ai.get_score_for_terrain(battle.field.terrain, user)
|
||||
end
|
||||
score += ai.get_score_for_terrain(:Psychic, user)
|
||||
score += ai.get_score_for_terrain(:Psychic, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -745,24 +651,11 @@ Battle::AI::Handlers::MoveEffectScore.add("StartShadowSkyWeather",
|
||||
proc { |score, move, user, ai, battle|
|
||||
next Battle::AI::MOVE_USELESS_SCORE if battle.pbCheckGlobalAbility(:AIRLOCK) ||
|
||||
battle.pbCheckGlobalAbility(:CLOUDNINE)
|
||||
score += 10 if battle.field.weather != :None # Prefer replacing another weather
|
||||
score -= 10 if user.hp < user.totalhp / 2 # Not worth it at lower HP
|
||||
# Check for battlers affected by Shadow Sky's effects
|
||||
ai.each_battler do |b, i|
|
||||
if b.has_damaging_move_of_type?(:SHADOW)
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
if b.battler.takesShadowSkyDamage? # End of round damage
|
||||
score += (b.opposes?(user)) ? 15 : -15
|
||||
end
|
||||
end
|
||||
# Check for moves affected by Shadow Sky
|
||||
# TODO: Check other battlers for these as well?
|
||||
if ai.trainer.medium_skill? && !user.has_active_item?(:UTILITYUMBRELLA)
|
||||
if user.has_move_with_function?("TypeAndPowerDependOnWeather")
|
||||
score += 10
|
||||
end
|
||||
if ai.trainer.high_skill? && battle.field.weather != :None
|
||||
score -= ai.get_score_for_weather(battle.field.weather, user)
|
||||
end
|
||||
score += ai.get_score_for_weather(:ShadowSky, user, true)
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
@@ -515,13 +515,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartRaiseUserAtk1WhenDamaged",
|
||||
# TODO: Check whether any foe has damaging moves that will trigger the stat
|
||||
# raise?
|
||||
# Prefer if user benefits from a raised Attack stat
|
||||
if user.check_for_move { |m| m.physicalMove?(m.type) &&
|
||||
m.function != "UseUserDefenseInsteadOfUserAttack" &&
|
||||
m.function != "UseTargetAttackInsteadOfUserAttack" }
|
||||
score += 8
|
||||
elsif user.has_move_with_function?("PowerHigherWithUserPositiveStatStages")
|
||||
score += 4
|
||||
end
|
||||
score += 8 if ai.stat_raise_worthwhile?(user, :ATTACK)
|
||||
score += 4 if user.has_move_with_function?("PowerHigherWithUserPositiveStatStages")
|
||||
next score
|
||||
}
|
||||
)
|
||||
|
||||
@@ -581,8 +581,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("FlinchTarget",
|
||||
end
|
||||
# Inherent preference
|
||||
score += 10
|
||||
# Prefer if the target is paralysed, confused or infatuated, to compound the turn skipping
|
||||
# TODO: Also prefer if the target is trapped in battle or can't switch out?
|
||||
# Prefer if the target is paralysed, confused or infatuated, to compound the
|
||||
# turn skipping
|
||||
score += 5 if target.status == :PARALYSIS ||
|
||||
target.effects[PBEffects::Confusion] > 1 ||
|
||||
target.effects[PBEffects::Attract] >= 0
|
||||
|
||||
@@ -76,13 +76,9 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("OHKO",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Don't prefer if the target has less HP and user has a non-OHKO damaging move
|
||||
if user.check_for_move { |m| !m.is_a?(Battle::Move::OHKO) && m.damagingMove? }
|
||||
score -= 10 if target.hp <= target.totalhp / 2
|
||||
score -= 10 if target.hp <= target.totalhp / 4
|
||||
score -= 8 if target.hp <= target.totalhp / 2
|
||||
score -= 8 if target.hp <= target.totalhp / 4
|
||||
end
|
||||
# TODO: Maybe predict dealt damage of all user's other moves, and score this
|
||||
# move useless if another one can KO the target. Might also need to
|
||||
# take into account whether those moves will fail. Might need to do
|
||||
# this specially after all move scores are determined.
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -506,7 +502,7 @@ Battle::AI::Handlers::MoveEffectScore.add("StartPreventCriticalHitsAgainstUserSi
|
||||
if crit_stage >= 0 && crit_stage < 50
|
||||
crit_stage += b.effects[PBEffects::FocusEnergy]
|
||||
crit_stage += 1 if b.check_for_move { |m| m.highCriticalRate? }
|
||||
crit_stage = 99 if m.check_for_move { |m| m.pbCritialOverride(b.battler, user.battler) > 0 }
|
||||
crit_stage = 99 if b.check_for_move { |m| m.pbCritialOverride(b.battler, user.battler) > 0 }
|
||||
crit_stage = [crit_stage, Battle::Move::CRITICAL_HIT_RATIOS.length - 1].min
|
||||
end
|
||||
# TODO: Change the score depending on how much of an effect a critical hit
|
||||
@@ -1509,10 +1505,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetMovesBecomeElectri
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveEffectScore.add("NormalMovesBecomeElectric",
|
||||
proc { |score, move, user, ai, battle|
|
||||
# Get Electric's effectiveness against the user
|
||||
electric_eff = user.effectiveness_of_type_against_battler(:ELECTRIC, target)
|
||||
electric_eff *= 1.5 if target.has_type?(:ELECTRIC) # STAB
|
||||
electric_eff = 0 if user.has_active_ability?([:LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB])
|
||||
base_electric_eff = Effectiveness::NORMAL_EFFECTIVE_MULTIPLIER
|
||||
base_electric_eff = 0 if user.has_active_ability?([:LIGHTNINGROD, :MOTORDRIVE, :VOLTABSORB])
|
||||
# Check all affected foe battlers for Normal moves, get their effectiveness
|
||||
# against the user and decide whether it is better or worse than Electric's
|
||||
# effectiveness
|
||||
@@ -1521,11 +1515,17 @@ Battle::AI::Handlers::MoveEffectScore.add("NormalMovesBecomeElectric",
|
||||
ai.each_foe_battler(user.side) do |b, i|
|
||||
next if move.pbPriority(b.battler) <= 0 && b.faster_than?(user)
|
||||
next if !b.has_damaging_move_of_type?(:NORMAL)
|
||||
# Normal's effectiveness
|
||||
eff = user.effectiveness_of_type_against_battler(:NORMAL, b)
|
||||
eff *= 1.5 if b.has_type?(:NORMAL) # STAB
|
||||
if eff > electric_eff
|
||||
# Electric's effectiveness
|
||||
elec_eff = user.effectiveness_of_type_against_battler(:ELECTRIC, b)
|
||||
elec_eff *= 1.5 if b.has_type?(:ELECTRIC) # STAB
|
||||
elec_eff *= base_electric_eff
|
||||
# Compare the two
|
||||
if eff > elec_eff
|
||||
electric_type_better += 1
|
||||
elsif eff < electric_eff
|
||||
elsif eff < elec_eff
|
||||
normal_type_better += 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -469,7 +469,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsed",
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("UseLastMoveUsedByTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !target.battler.lastRegularMoveUsed
|
||||
next GameData::Move.get(target.battler.lastRegularMoveUsed).flags.none? { |f| f[/^CanMirrorMove$/i] }
|
||||
next !GameData::Move.get(target.battler.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -421,11 +421,11 @@ Battle::AI::Handlers::MoveEffectScore.add("StartSlowerBattlersActFirst",
|
||||
foe_speeds = []
|
||||
ai.each_battler do |b, i|
|
||||
if b.opposes?(user)
|
||||
foe_speeds.push(rough_stat(:SPEED))
|
||||
foe_speeds.push(b.rough_stat(:SPEED))
|
||||
foe_speeds.last *= 2 if user.pbOpposingSide.effects[PBEffects::Tailwind] > 1
|
||||
foe_speeds.last /= 2 if user.pbOpposingSide.effects[PBEffects::Swamp] > 1
|
||||
else
|
||||
ally_speeds.push(rough_stat(:SPEED))
|
||||
ally_speeds.push(b.rough_stat(:SPEED))
|
||||
ally_speeds.last *= 2 if user.pbOwnSide.effects[PBEffects::Tailwind] > 1
|
||||
ally_speeds.last /= 2 if user.pbOwnSide.effects[PBEffects::Swamp] > 1
|
||||
end
|
||||
|
||||
@@ -20,7 +20,7 @@ class Trainer
|
||||
end
|
||||
|
||||
def skill_level
|
||||
return GameData::TrainerType.try_get(:trainer_type)&.skill_level || 0
|
||||
return GameData::TrainerType.try_get(self.trainer_type)&.skill_level || 0
|
||||
end
|
||||
|
||||
#=============================================================================
|
||||
|
||||
Reference in New Issue
Block a user