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:
Maruno17
2023-02-20 23:33:09 +00:00
parent 0e4053f837
commit d0c99aa512
21 changed files with 610 additions and 241 deletions

View File

@@ -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
}
)

View File

@@ -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
}
)

View File

@@ -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

View File

@@ -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

View File

@@ -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")
}
)

View File

@@ -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