mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-08 05:34:58 +00:00
Rewrote AI item usage (inc. adding Revives), various fixes/changes to AI, removed Struggle from PBS files, some bug fixes
This commit is contained in:
@@ -165,11 +165,7 @@ class Battle
|
|||||||
@moldBreaker = false
|
@moldBreaker = false
|
||||||
@runCommand = 0
|
@runCommand = 0
|
||||||
@nextPickupUse = 0
|
@nextPickupUse = 0
|
||||||
if GameData::Move.exists?(:STRUGGLE)
|
@struggle = Move::Struggle.new(self, nil)
|
||||||
@struggle = Move.from_pokemon_move(self, Pokemon::Move.new(:STRUGGLE))
|
|
||||||
else
|
|
||||||
@struggle = Move::Struggle.new(self, nil)
|
|
||||||
end
|
|
||||||
@mega_rings = []
|
@mega_rings = []
|
||||||
GameData::Item.each { |item| @mega_rings.push(item.id) if item.has_flag?("MegaRing") }
|
GameData::Item.each { |item| @mega_rings.push(item.id) if item.has_flag?("MegaRing") }
|
||||||
@battleAI = AI.new(self)
|
@battleAI = AI.new(self)
|
||||||
|
|||||||
@@ -171,7 +171,7 @@ class Battle::Battler
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
def pbCanSynchronizeStatus?(newStatus, target)
|
def pbCanSynchronizeStatus?(newStatus, user)
|
||||||
return false if fainted?
|
return false if fainted?
|
||||||
# Trying to replace a status problem with another one
|
# Trying to replace a status problem with another one
|
||||||
return false if self.status != :NONE
|
return false if self.status != :NONE
|
||||||
@@ -181,8 +181,8 @@ class Battle::Battler
|
|||||||
hasImmuneType = false
|
hasImmuneType = false
|
||||||
case newStatus
|
case newStatus
|
||||||
when :POISON
|
when :POISON
|
||||||
# NOTE: target will have Synchronize, so it can't have Corrosion.
|
# NOTE: user will have Synchronize, so it can't have Corrosion.
|
||||||
if !(target && target.hasActiveAbility?(:CORROSION))
|
if !(user && user.hasActiveAbility?(:CORROSION))
|
||||||
hasImmuneType |= pbHasType?(:POISON)
|
hasImmuneType |= pbHasType?(:POISON)
|
||||||
hasImmuneType |= pbHasType?(:STEEL)
|
hasImmuneType |= pbHasType?(:STEEL)
|
||||||
end
|
end
|
||||||
@@ -205,6 +205,7 @@ class Battle::Battler
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
# Safeguard immunity
|
# Safeguard immunity
|
||||||
|
# NOTE: user will have Synchronize, so it can't have Infiltrator.
|
||||||
if pbOwnSide.effects[PBEffects::Safeguard] > 0 &&
|
if pbOwnSide.effects[PBEffects::Safeguard] > 0 &&
|
||||||
!(user && user.hasActiveAbility?(:INFILTRATOR))
|
!(user && user.hasActiveAbility?(:INFILTRATOR))
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -43,19 +43,21 @@ class Battle::Battler
|
|||||||
# Choice Band/Gorilla Tactics
|
# Choice Band/Gorilla Tactics
|
||||||
@effects[PBEffects::ChoiceBand] = nil if !pbHasMove?(@effects[PBEffects::ChoiceBand])
|
@effects[PBEffects::ChoiceBand] = nil if !pbHasMove?(@effects[PBEffects::ChoiceBand])
|
||||||
if @effects[PBEffects::ChoiceBand] && move.id != @effects[PBEffects::ChoiceBand]
|
if @effects[PBEffects::ChoiceBand] && move.id != @effects[PBEffects::ChoiceBand]
|
||||||
choiced_move_name = GameData::Move.get(@effects[PBEffects::ChoiceBand]).name
|
choiced_move = GameData::Move.try_get(@effects[PBEffects::ChoiceBand])
|
||||||
if hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
|
if choiced_move
|
||||||
if showMessages
|
if hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
|
||||||
msg = _INTL("The {1} only allows the use of {2}!", itemName, choiced_move_name)
|
if showMessages
|
||||||
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
|
msg = _INTL("The {1} only allows the use of {2}!", itemName, choiced_move.name)
|
||||||
|
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
elsif hasActiveAbility?(:GORILLATACTICS)
|
||||||
|
if showMessages
|
||||||
|
msg = _INTL("{1} can only use {2}!", pbThis, choiced_move.name)
|
||||||
|
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
|
||||||
|
end
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
return false
|
|
||||||
elsif hasActiveAbility?(:GORILLATACTICS)
|
|
||||||
if showMessages
|
|
||||||
msg = _INTL("{1} can only use {2}!", pbThis, choiced_move_name)
|
|
||||||
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Taunt
|
# Taunt
|
||||||
@@ -85,7 +87,7 @@ class Battle::Battler
|
|||||||
end
|
end
|
||||||
# Assault Vest (prevents choosing status moves but doesn't prevent
|
# Assault Vest (prevents choosing status moves but doesn't prevent
|
||||||
# executing them)
|
# executing them)
|
||||||
if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && move.id != :MEFIRST && commandPhase
|
if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && move.function != "UseMoveTargetIsAboutToUse" && commandPhase
|
||||||
if showMessages
|
if showMessages
|
||||||
msg = _INTL("The effects of the {1} prevent status moves from being used!", itemName)
|
msg = _INTL("The effects of the {1} prevent status moves from being used!", itemName)
|
||||||
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
|
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
|
||||||
|
|||||||
@@ -43,11 +43,10 @@ class Battle::Move
|
|||||||
@category = move.category
|
@category = move.category
|
||||||
@accuracy = move.accuracy
|
@accuracy = move.accuracy
|
||||||
@pp = move.pp # Can be changed with Mimic/Transform
|
@pp = move.pp # Can be changed with Mimic/Transform
|
||||||
@addlEffect = move.effect_chance
|
|
||||||
@target = move.target
|
@target = move.target
|
||||||
@priority = move.priority
|
@priority = move.priority
|
||||||
@flags = move.flags.clone
|
@flags = move.flags.clone
|
||||||
@calcType = nil
|
@addlEffect = move.effect_chance
|
||||||
@powerBoost = false # For Aerilate, Pixilate, Refrigerate, Galvanize
|
@powerBoost = false # For Aerilate, Pixilate, Refrigerate, Galvanize
|
||||||
@snatched = false
|
@snatched = false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -285,7 +285,7 @@ class Battle::Move
|
|||||||
if (@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
|
if (@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
|
||||||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY)
|
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY)
|
||||||
if @battle.pbCheckGlobalAbility(:AURABREAK)
|
if @battle.pbCheckGlobalAbility(:AURABREAK)
|
||||||
multipliers[:power_multiplier] *= 2 / 3.0
|
multipliers[:power_multiplier] *= 3 / 4.0
|
||||||
else
|
else
|
||||||
multipliers[:power_multiplier] *= 4 / 3.0
|
multipliers[:power_multiplier] *= 4 / 3.0
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ class Battle::Move::Confusion < Battle::Move
|
|||||||
@priority = 0
|
@priority = 0
|
||||||
@flags = []
|
@flags = []
|
||||||
@addlEffect = 0
|
@addlEffect = 0
|
||||||
@calcType = nil
|
|
||||||
@powerBoost = false
|
@powerBoost = false
|
||||||
@snatched = false
|
@snatched = false
|
||||||
end
|
end
|
||||||
@@ -53,19 +52,18 @@ class Battle::Move::Struggle < Battle::Move
|
|||||||
def initialize(battle, move)
|
def initialize(battle, move)
|
||||||
@battle = battle
|
@battle = battle
|
||||||
@realMove = nil # Not associated with a move
|
@realMove = nil # Not associated with a move
|
||||||
@id = (move) ? move.id : :STRUGGLE
|
@id = :STRUGGLE
|
||||||
@name = (move) ? move.name : _INTL("Struggle")
|
@name = _INTL("Struggle")
|
||||||
@function = "Struggle"
|
@function = "Struggle"
|
||||||
@power = 50
|
@power = 50
|
||||||
@type = nil
|
@type = nil
|
||||||
@category = 0
|
@category = 0
|
||||||
@accuracy = 0
|
@accuracy = 0
|
||||||
@pp = -1
|
@pp = -1
|
||||||
@target = :NearOther
|
@target = :RandomNearFoe
|
||||||
@priority = 0
|
@priority = 0
|
||||||
@flags = ["Contact", "CanProtect"]
|
@flags = ["Contact", "CanProtect"]
|
||||||
@addlEffect = 0
|
@addlEffect = 0
|
||||||
@calcType = nil
|
|
||||||
@powerBoost = false
|
@powerBoost = false
|
||||||
@snatched = false
|
@snatched = false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1372,7 +1372,7 @@ class Battle::Move::LowerPoisonedTargetAtkSpAtkSpd1 < Battle::Move
|
|||||||
next if !b.poisoned?
|
next if !b.poisoned?
|
||||||
failed = true
|
failed = true
|
||||||
(@statDown.length / 2).times do |i|
|
(@statDown.length / 2).times do |i|
|
||||||
next if !target.pbCanLowerStatStage?(@statDown[i * 2], user, self)
|
next if !b.pbCanLowerStatStage?(@statDown[i * 2], user, self)
|
||||||
failed = false
|
failed = false
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -768,6 +768,7 @@ class Battle::Move::UseLastMoveUsed < Battle::Move
|
|||||||
|
|
||||||
def pbMoveFailed?(user, targets)
|
def pbMoveFailed?(user, targets)
|
||||||
if !@copied_move ||
|
if !@copied_move ||
|
||||||
|
!GameData::Move.exists?(@copied_move) ||
|
||||||
@moveBlacklist.include?(GameData::Move.get(@copied_move).function_code)
|
@moveBlacklist.include?(GameData::Move.get(@copied_move).function_code)
|
||||||
@battle.pbDisplay(_INTL("But it failed!"))
|
@battle.pbDisplay(_INTL("But it failed!"))
|
||||||
return true
|
return true
|
||||||
@@ -789,6 +790,7 @@ class Battle::Move::UseLastMoveUsedByTarget < Battle::Move
|
|||||||
|
|
||||||
def pbFailsAgainstTarget?(user, target, show_message)
|
def pbFailsAgainstTarget?(user, target, show_message)
|
||||||
if !target.lastRegularMoveUsed ||
|
if !target.lastRegularMoveUsed ||
|
||||||
|
!GameData::Move.exists?(target.lastRegularMoveUsed) ||
|
||||||
!GameData::Move.get(target.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
!GameData::Move.get(target.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
||||||
@battle.pbDisplay(_INTL("The mirror move failed!")) if show_message
|
@battle.pbDisplay(_INTL("The mirror move failed!")) if show_message
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -606,7 +606,8 @@ class Battle::Move::TargetUsesItsLastUsedMoveAgain < Battle::Move
|
|||||||
end
|
end
|
||||||
|
|
||||||
def pbFailsAgainstTarget?(user, target, show_message)
|
def pbFailsAgainstTarget?(user, target, show_message)
|
||||||
if !target.lastRegularMoveUsed || !target.pbHasMove?(target.lastRegularMoveUsed)
|
if !target.lastRegularMoveUsed || !target.pbHasMove?(target.lastRegularMoveUsed) ||
|
||||||
|
!GameData::Move.exists?(target.lastRegularMoveUsed)
|
||||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
@@ -814,6 +815,7 @@ class Battle::Move::DisableTargetUsingDifferentMove < Battle::Move
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
if !target.lastRegularMoveUsed ||
|
if !target.lastRegularMoveUsed ||
|
||||||
|
!GameData::Move.exists?(target.lastRegularMoveUsed) ||
|
||||||
@moveBlacklist.include?(GameData::Move.get(target.lastRegularMoveUsed).function_code)
|
@moveBlacklist.include?(GameData::Move.get(target.lastRegularMoveUsed).function_code)
|
||||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ class Battle::Scene
|
|||||||
# Returns the animation ID to use for a given move/user. Returns nil if that
|
# Returns the animation ID to use for a given move/user. Returns nil if that
|
||||||
# move has no animations defined for it.
|
# move has no animations defined for it.
|
||||||
def pbFindMoveAnimDetails(move2anim, moveID, idxUser, hitNum = 0)
|
def pbFindMoveAnimDetails(move2anim, moveID, idxUser, hitNum = 0)
|
||||||
real_move_id = GameData::Move.get(moveID).id
|
real_move_id = GameData::Move.try_get(moveID)&.id || moveID
|
||||||
noFlip = false
|
noFlip = false
|
||||||
if (idxUser & 1) == 0 # On player's side
|
if (idxUser & 1) == 0 # On player's side
|
||||||
anim = move2anim[0][real_move_id]
|
anim = move2anim[0][real_move_id]
|
||||||
|
|||||||
@@ -5,12 +5,11 @@ class Battle::AI
|
|||||||
attr_reader :battle
|
attr_reader :battle
|
||||||
attr_reader :trainer
|
attr_reader :trainer
|
||||||
attr_reader :battlers
|
attr_reader :battlers
|
||||||
|
attr_reader :roles
|
||||||
attr_reader :user, :target, :move
|
attr_reader :user, :target, :move
|
||||||
|
|
||||||
def initialize(battle)
|
def initialize(battle)
|
||||||
@battle = battle
|
@battle = battle
|
||||||
|
|
||||||
# TODO: Move this elsewhere?
|
|
||||||
@roles = [Array.new(@battle.pbParty(0).length) { |i| determine_roles(0, i) },
|
@roles = [Array.new(@battle.pbParty(0).length) { |i| determine_roles(0, i) },
|
||||||
Array.new(@battle.pbParty(1).length) { |i| determine_roles(1, i) }]
|
Array.new(@battle.pbParty(1).length) { |i| determine_roles(1, i) }]
|
||||||
end
|
end
|
||||||
@@ -57,7 +56,9 @@ class Battle::AI
|
|||||||
PBDebug.log("")
|
PBDebug.log("")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
if pbEnemyShouldUseItem?
|
ret = false
|
||||||
|
PBDebug.logonerr { ret = pbChooseToUseItem }
|
||||||
|
if ret
|
||||||
PBDebug.log("")
|
PBDebug.log("")
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,173 +1,224 @@
|
|||||||
class Battle::AI
|
class Battle::AI
|
||||||
#=============================================================================
|
HP_HEAL_ITEMS = {
|
||||||
# Decide whether the opponent should use an item on the Pokémon
|
:POTION => 20,
|
||||||
# TODO: Maybe don't cure a status problem if the Pokémon has an ability or
|
:SUPERPOTION => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
|
||||||
# something that makes it benefit from having that problem.
|
:HYPERPOTION => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 120 : 200,
|
||||||
#=============================================================================
|
:MAXPOTION => 999,
|
||||||
def pbEnemyShouldUseItem?
|
:BERRYJUICE => 20,
|
||||||
|
:SWEETHEART => 20,
|
||||||
|
:FRESHWATER => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 30 : 50,
|
||||||
|
:SODAPOP => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 50 : 60,
|
||||||
|
:LEMONADE => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 70 : 80,
|
||||||
|
:MOOMOOMILK => 100,
|
||||||
|
:ORANBERRY => 10,
|
||||||
|
:SITRUSBERRY => 1, # Actual amount is determined below (pkmn.totalhp / 4)
|
||||||
|
:ENERGYPOWDER => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
|
||||||
|
:ENERGYROOT => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 120 : 200
|
||||||
|
}
|
||||||
|
HP_HEAL_ITEMS[:RAGECANDYBAR] = 20 if !Settings::RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS
|
||||||
|
FULL_RESTORE_ITEMS = [
|
||||||
|
:FULLRESTORE
|
||||||
|
]
|
||||||
|
ONE_STATUS_CURE_ITEMS = [ # Preferred over items that heal all status problems
|
||||||
|
:AWAKENING, :CHESTOBERRY, :BLUEFLUTE,
|
||||||
|
:ANTIDOTE, :PECHABERRY,
|
||||||
|
:BURNHEAL, :RAWSTBERRY,
|
||||||
|
:PARALYZEHEAL, :PARLYZHEAL, :CHERIBERRY,
|
||||||
|
:ICEHEAL, :ASPEARBERRY
|
||||||
|
]
|
||||||
|
ALL_STATUS_CURE_ITEMS = [
|
||||||
|
:FULLHEAL, :LAVACOOKIE, :OLDGATEAU, :CASTELIACONE, :LUMIOSEGALETTE,
|
||||||
|
:SHALOURSABLE, :BIGMALASADA, :PEWTERCRUNCHIES, :LUMBERRY, :HEALPOWDER
|
||||||
|
]
|
||||||
|
ALL_STATUS_CURE_ITEMS.push(:RAGECANDYBAR) if Settings::RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS
|
||||||
|
ONE_STAT_RAISE_ITEMS = {
|
||||||
|
:XATTACK => [:ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
||||||
|
:XATTACK2 => [:ATTACK, 2],
|
||||||
|
:XATTACK3 => [:ATTACK, 3],
|
||||||
|
:XATTACK6 => [:ATTACK, 6],
|
||||||
|
:XDEFENSE => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
||||||
|
:XDEFENSE2 => [:DEFENSE, 2],
|
||||||
|
:XDEFENSE3 => [:DEFENSE, 3],
|
||||||
|
:XDEFENSE6 => [:DEFENSE, 6],
|
||||||
|
:XDEFEND => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
||||||
|
:XDEFEND2 => [:DEFENSE, 2],
|
||||||
|
:XDEFEND3 => [:DEFENSE, 3],
|
||||||
|
:XDEFEND6 => [:DEFENSE, 6],
|
||||||
|
:XSPATK => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
||||||
|
:XSPATK2 => [:SPECIAL_ATTACK, 2],
|
||||||
|
:XSPATK3 => [:SPECIAL_ATTACK, 3],
|
||||||
|
:XSPATK6 => [:SPECIAL_ATTACK, 6],
|
||||||
|
:XSPECIAL => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
||||||
|
:XSPECIAL2 => [:SPECIAL_ATTACK, 2],
|
||||||
|
:XSPECIAL3 => [:SPECIAL_ATTACK, 3],
|
||||||
|
:XSPECIAL6 => [:SPECIAL_ATTACK, 6],
|
||||||
|
:XSPDEF => [:SPECIAL_DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
||||||
|
:XSPDEF2 => [:SPECIAL_DEFENSE, 2],
|
||||||
|
:XSPDEF3 => [:SPECIAL_DEFENSE, 3],
|
||||||
|
:XSPDEF6 => [:SPECIAL_DEFENSE, 6],
|
||||||
|
:XSPEED => [:SPEED, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
||||||
|
:XSPEED2 => [:SPEED, 2],
|
||||||
|
:XSPEED3 => [:SPEED, 3],
|
||||||
|
:XSPEED6 => [:SPEED, 6],
|
||||||
|
:XACCURACY => [:ACCURACY, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
||||||
|
:XACCURACY2 => [:ACCURACY, 2],
|
||||||
|
:XACCURACY3 => [:ACCURACY, 3],
|
||||||
|
:XACCURACY6 => [:ACCURACY, 6]
|
||||||
|
}
|
||||||
|
ALL_STATS_RAISE_ITEMS = [
|
||||||
|
:MAXMUSHROOMS
|
||||||
|
]
|
||||||
|
REVIVE_ITEMS = {
|
||||||
|
:REVIVE => 5,
|
||||||
|
:MAXREVIVE => 7,
|
||||||
|
:REVIVALHERB => 7,
|
||||||
|
:MAXHONEY => 7
|
||||||
|
}
|
||||||
|
# TODO: Add more items for the AI to use from their Bag:
|
||||||
|
# Confusion healing (Yellow Flute, Persim Berry)
|
||||||
|
# Infatuation healing (Red Flute)
|
||||||
|
# PP (Either, Max Ether, Leppa Berry, Elixir, Max Elixir)
|
||||||
|
# Dire Hit (and 2 and 3)
|
||||||
|
# Guard Spec.
|
||||||
|
# Poké Flute (awakens all battlers)
|
||||||
|
|
||||||
|
#-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Decide whether the opponent should use an item on the Pokémon.
|
||||||
|
def pbChooseToUseItem
|
||||||
item = nil
|
item = nil
|
||||||
idxTarget = nil
|
idxTarget = nil # Party index (battle_use type 1/2/3) or battler index
|
||||||
PBDebug.logonerr { item, idxTarget = pbEnemyItemToUse }
|
idxMove = nil
|
||||||
|
item, idxTarget, idxMove = choose_item_to_use
|
||||||
return false if !item
|
return false if !item
|
||||||
# Determine target of item (always the Pokémon choosing the action)
|
|
||||||
useType = GameData::Item.get(item).battle_use
|
|
||||||
if [1, 2, 3].include?(useType) # Use on Pokémon
|
|
||||||
idxTarget = @battle.battlers[idxTarget].pokemonIndex # Party Pokémon
|
|
||||||
end
|
|
||||||
# Register use of item
|
# Register use of item
|
||||||
@battle.pbRegisterItem(@user.index, item, idxTarget)
|
@battle.pbRegisterItem(@user.index, item, idxTarget, idxMove)
|
||||||
PBDebug.log_ai("#{@user.name} will use item #{GameData::Item.get(item).name}")
|
PBDebug.log_ai("#{@user.name} will use item #{GameData::Item.get(item).name}")
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
# NOTE: The AI will only consider using an item on the Pokémon it's currently
|
# Return values are:
|
||||||
# choosing an action for.
|
# item ID
|
||||||
def pbEnemyItemToUse
|
# target index (party index for items with a battle use of 1/2/3, battler
|
||||||
|
# index otherwise)
|
||||||
|
# move index (for items usable on moves only)
|
||||||
|
def choose_item_to_use
|
||||||
return nil if !@battle.internalBattle
|
return nil if !@battle.internalBattle
|
||||||
items = @battle.pbGetOwnerItems(@user.index)
|
items = @battle.pbGetOwnerItems(@user.index)
|
||||||
return nil if !items || items.length == 0
|
return nil if !items || items.length == 0
|
||||||
# Determine target of item (always the Pokémon choosing the action)
|
# Find all items usable on the Pokémon choosing this action
|
||||||
idxTarget = @user.index # Battler using the item
|
pkmn = @user.battler.pokemon
|
||||||
battler = @user.battler
|
usable_items = {}
|
||||||
pkmn = battler.pokemon
|
items.each do |item|
|
||||||
# Item categories
|
usage = get_usability_of_item_on_pkmn(item, @user.party_index, @user.side)
|
||||||
hpItems = {
|
usage.each_pair do |key, vals|
|
||||||
:POTION => 20,
|
usable_items[key] ||= []
|
||||||
:SUPERPOTION => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
|
usable_items[key] += vals
|
||||||
:HYPERPOTION => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 120 : 200,
|
|
||||||
:MAXPOTION => 999,
|
|
||||||
:BERRYJUICE => 20,
|
|
||||||
:SWEETHEART => 20,
|
|
||||||
:FRESHWATER => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 30 : 50,
|
|
||||||
:SODAPOP => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 50 : 60,
|
|
||||||
:LEMONADE => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 70 : 80,
|
|
||||||
:MOOMOOMILK => 100,
|
|
||||||
:ORANBERRY => 10,
|
|
||||||
:SITRUSBERRY => battler.totalhp / 4,
|
|
||||||
:ENERGYPOWDER => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
|
|
||||||
:ENERGYROOT => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 120 : 200
|
|
||||||
}
|
|
||||||
hpItems[:RAGECANDYBAR] = 20 if !Settings::RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS
|
|
||||||
fullRestoreItems = [
|
|
||||||
:FULLRESTORE
|
|
||||||
]
|
|
||||||
oneStatusItems = [ # Preferred over items that heal all status problems
|
|
||||||
:AWAKENING, :CHESTOBERRY, :BLUEFLUTE,
|
|
||||||
:ANTIDOTE, :PECHABERRY,
|
|
||||||
:BURNHEAL, :RAWSTBERRY,
|
|
||||||
:PARALYZEHEAL, :PARLYZHEAL, :CHERIBERRY,
|
|
||||||
:ICEHEAL, :ASPEARBERRY
|
|
||||||
]
|
|
||||||
allStatusItems = [
|
|
||||||
:FULLHEAL, :LAVACOOKIE, :OLDGATEAU, :CASTELIACONE, :LUMIOSEGALETTE,
|
|
||||||
:SHALOURSABLE, :BIGMALASADA, :PEWTERCRUNCHIES, :LUMBERRY, :HEALPOWDER
|
|
||||||
]
|
|
||||||
allStatusItems.push(:RAGECANDYBAR) if Settings::RAGE_CANDY_BAR_CURES_STATUS_PROBLEMS
|
|
||||||
xItems = {
|
|
||||||
:XATTACK => [:ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
|
||||||
:XATTACK2 => [:ATTACK, 2],
|
|
||||||
:XATTACK3 => [:ATTACK, 3],
|
|
||||||
:XATTACK6 => [:ATTACK, 6],
|
|
||||||
:XDEFENSE => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
|
||||||
:XDEFENSE2 => [:DEFENSE, 2],
|
|
||||||
:XDEFENSE3 => [:DEFENSE, 3],
|
|
||||||
:XDEFENSE6 => [:DEFENSE, 6],
|
|
||||||
:XDEFEND => [:DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
|
||||||
:XDEFEND2 => [:DEFENSE, 2],
|
|
||||||
:XDEFEND3 => [:DEFENSE, 3],
|
|
||||||
:XDEFEND6 => [:DEFENSE, 6],
|
|
||||||
:XSPATK => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
|
||||||
:XSPATK2 => [:SPECIAL_ATTACK, 2],
|
|
||||||
:XSPATK3 => [:SPECIAL_ATTACK, 3],
|
|
||||||
:XSPATK6 => [:SPECIAL_ATTACK, 6],
|
|
||||||
:XSPECIAL => [:SPECIAL_ATTACK, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
|
||||||
:XSPECIAL2 => [:SPECIAL_ATTACK, 2],
|
|
||||||
:XSPECIAL3 => [:SPECIAL_ATTACK, 3],
|
|
||||||
:XSPECIAL6 => [:SPECIAL_ATTACK, 6],
|
|
||||||
:XSPDEF => [:SPECIAL_DEFENSE, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
|
||||||
:XSPDEF2 => [:SPECIAL_DEFENSE, 2],
|
|
||||||
:XSPDEF3 => [:SPECIAL_DEFENSE, 3],
|
|
||||||
:XSPDEF6 => [:SPECIAL_DEFENSE, 6],
|
|
||||||
:XSPEED => [:SPEED, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
|
||||||
:XSPEED2 => [:SPEED, 2],
|
|
||||||
:XSPEED3 => [:SPEED, 3],
|
|
||||||
:XSPEED6 => [:SPEED, 6],
|
|
||||||
:XACCURACY => [:ACCURACY, (Settings::X_STAT_ITEMS_RAISE_BY_TWO_STAGES) ? 2 : 1],
|
|
||||||
:XACCURACY2 => [:ACCURACY, 2],
|
|
||||||
:XACCURACY3 => [:ACCURACY, 3],
|
|
||||||
:XACCURACY6 => [:ACCURACY, 6]
|
|
||||||
}
|
|
||||||
losthp = battler.totalhp - battler.hp
|
|
||||||
preferFullRestore = (battler.hp <= battler.totalhp * 2 / 3 &&
|
|
||||||
(battler.status != :NONE || battler.effects[PBEffects::Confusion] > 0))
|
|
||||||
# Find all usable items
|
|
||||||
usableHPItems = []
|
|
||||||
usableStatusItems = []
|
|
||||||
usableXItems = []
|
|
||||||
items.each do |i|
|
|
||||||
next if !i
|
|
||||||
next if !@battle.pbCanUseItemOnPokemon?(i, pkmn, battler, @battle.scene, false)
|
|
||||||
next if !ItemHandlers.triggerCanUseInBattle(i, pkmn, battler, nil,
|
|
||||||
false, self, @battle.scene, false)
|
|
||||||
# Log HP healing items
|
|
||||||
if losthp > 0
|
|
||||||
power = hpItems[i]
|
|
||||||
if power
|
|
||||||
usableHPItems.push([i, 5, power])
|
|
||||||
next
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# Log Full Restores (HP healer and status curer)
|
|
||||||
if fullRestoreItems.include?(i)
|
|
||||||
usableHPItems.push([i, (preferFullRestore) ? 3 : 7, 999]) if losthp > 0
|
|
||||||
usableStatusItems.push([i, (preferFullRestore) ? 3 : 9]) if battler.status != :NONE ||
|
|
||||||
battler.effects[PBEffects::Confusion] > 0
|
|
||||||
next
|
|
||||||
end
|
|
||||||
# Log single status-curing items
|
|
||||||
if oneStatusItems.include?(i)
|
|
||||||
usableStatusItems.push([i, 5])
|
|
||||||
next
|
|
||||||
end
|
|
||||||
# Log Full Heal-type items
|
|
||||||
if allStatusItems.include?(i)
|
|
||||||
usableStatusItems.push([i, 7])
|
|
||||||
next
|
|
||||||
end
|
|
||||||
# Log stat-raising items
|
|
||||||
if xItems[i]
|
|
||||||
data = xItems[i]
|
|
||||||
usableXItems.push([i, battler.stages[data[0]], data[1]])
|
|
||||||
next
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Prioritise using a HP restoration item
|
# Prioritise using a HP restoration item
|
||||||
if usableHPItems.length > 0 && (battler.hp <= battler.totalhp / 4 ||
|
if usable_items[:hp_heal] && (pkmn.hp <= pkmn.totalhp / 4 ||
|
||||||
(battler.hp <= battler.totalhp / 2 && pbAIRandom(100) < 30))
|
(pkmn.hp <= pkmn.totalhp / 2 && pbAIRandom(100) < 30))
|
||||||
usableHPItems.sort! { |a, b| (a[1] == b[1]) ? a[2] <=> b[2] : a[1] <=> b[1] }
|
usable_items[:hp_heal].sort! { |a, b| (a[2] == b[2]) ? a[3] <=> b[3] : a[2] <=> b[2] }
|
||||||
prevItem = nil
|
usable_items[:hp_heal].each do |item|
|
||||||
usableHPItems.each do |i|
|
return item[0], item[1] if item[3] >= (pkmn.totalhp - pkmn.hp) * 0.75
|
||||||
return i[0], idxTarget if i[2] >= losthp
|
|
||||||
prevItem = i
|
|
||||||
end
|
end
|
||||||
return prevItem[0], idxTarget
|
return usable_items[:hp_heal].last[0], usable_items[:hp_heal].last[1]
|
||||||
end
|
end
|
||||||
# Next prioritise using a status-curing item
|
# Next prioritise using a status-curing item
|
||||||
if usableStatusItems.length > 0 && pbAIRandom(100) < 40
|
if usable_items[:status_cure] &&
|
||||||
usableStatusItems.sort! { |a, b| a[1] <=> b[1] }
|
([:SLEEP, :FROZEN].include?(pkmn.status) || pbAIRandom(100) < 40)
|
||||||
return usableStatusItems[0][0], idxTarget
|
usable_items[:status_cure].sort! { |a, b| a[2] <=> b[2] }
|
||||||
|
return usable_items[:status_cure].first[0], usable_items[:status_cure].first[1]
|
||||||
|
end
|
||||||
|
# Next try using an item that raises all stats (Max Mushrooms)
|
||||||
|
if usable_items[:all_stats_raise] && pbAIRandom(100) < 30
|
||||||
|
return usable_items[:stat_raise].first[0], usable_items[:stat_raise].first[1]
|
||||||
end
|
end
|
||||||
# Next try using an X item
|
# Next try using an X item
|
||||||
if usableXItems.length > 0 && pbAIRandom(100) < 30
|
if usable_items[:stat_raise] && pbAIRandom(100) < 30
|
||||||
usableXItems.sort! { |a, b| (a[1] == b[1]) ? a[2] <=> b[2] : a[1] <=> b[1] }
|
usable_items[:stat_raise].sort! { |a, b| (a[2] == b[2]) ? a[3] <=> b[3] : a[2] <=> b[2] }
|
||||||
prevItem = nil
|
return usable_items[:stat_raise].last[0], usable_items[:stat_raise].last[1]
|
||||||
usableXItems.each do |i|
|
end
|
||||||
break if prevItem && i[1] > prevItem[1]
|
# Find items usable on other Pokémon in the user's team
|
||||||
return i[0], idxTarget if i[1] + i[2] >= 6
|
# NOTE: Currently only checks Revives.
|
||||||
prevItem = i
|
usable_items = {}
|
||||||
|
@battle.eachInTeamFromBattlerIndex(@user.index) do |pkmn, i|
|
||||||
|
next if !pkmn.fainted? # Remove this line to check unfainted Pokémon too
|
||||||
|
items.each do |item|
|
||||||
|
usage = get_usability_of_item_on_pkmn(item, i, @user.side)
|
||||||
|
usage.each_pair do |key, vals|
|
||||||
|
usable_items[key] ||= []
|
||||||
|
usable_items[key] += vals
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return prevItem[0], idxTarget
|
end
|
||||||
|
# Try using a Revive (prefer Max Revive-type items over Revive)
|
||||||
|
if usable_items[:revive] &&
|
||||||
|
(@battle.pbAbleNonActiveCount(@user.index) == 0 || pbAIRandom(100) < 40)
|
||||||
|
usable_items[:revive].sort! { |a, b| (a[2] == b[2]) ? a[1] <=> b[1] : a[2] <=> b[2] }
|
||||||
|
return usable_items[:revive].last[0], usable_items[:revive].last[1]
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_usability_of_item_on_pkmn(item, party_index, side)
|
||||||
|
pkmn = @battle.pbParty(side)[party_index]
|
||||||
|
battler = @battle.pbFindBattler(party_index, side)
|
||||||
|
ret = {}
|
||||||
|
return ret if !@battle.pbCanUseItemOnPokemon?(item, pkmn, battler, @battle.scene, false)
|
||||||
|
return ret if !ItemHandlers.triggerCanUseInBattle(item, pkmn, battler, nil,
|
||||||
|
false, self, @battle.scene, false)
|
||||||
|
want_to_cure_status = (pkmn.status != :NONE)
|
||||||
|
if battler
|
||||||
|
if want_to_cure_status
|
||||||
|
want_to_cure_status = @battlers[battler.index].wants_status_problem?(pkmn.status)
|
||||||
|
want_to_cure_status = false if pkmn.status == :SLEEP && pkmn.statusCount <= 2
|
||||||
|
end
|
||||||
|
want_to_cure_status ||= (battler.effects[PBEffects::Confusion] > 0)
|
||||||
|
end
|
||||||
|
if HP_HEAL_ITEMS.include?(item)
|
||||||
|
if pkmn.hp < pkmn.totalhp
|
||||||
|
heal_amount = HP_HEAL_ITEMS[item]
|
||||||
|
heal_amount = pkmn.totalhp / 4 if item == :SITURUSBERRY
|
||||||
|
ret[:hp_heal] ||= []
|
||||||
|
ret[:hp_heal].push([item, party_index, 5, heal_amount])
|
||||||
|
end
|
||||||
|
elsif FULL_RESTORE_ITEMS.include?(item)
|
||||||
|
prefer_full_restore = (pkmn.hp <= pkmn.totalhp * 2 / 3 && want_to_cure_status)
|
||||||
|
if pkmn.hp < pkmn.totalhp
|
||||||
|
ret[:hp_heal] ||= []
|
||||||
|
ret[:hp_heal].push([item, party_index, (prefer_full_restore) ? 3 : 7, 999])
|
||||||
|
end
|
||||||
|
if want_to_cure_status
|
||||||
|
ret[:status_cure] ||= []
|
||||||
|
ret[:status_cure].push([item, party_index, (prefer_full_restore) ? 3 : 9])
|
||||||
|
end
|
||||||
|
elsif ONE_STATUS_CURE_ITEMS.include?(item)
|
||||||
|
if want_to_cure_status
|
||||||
|
ret[:status_cure] ||= []
|
||||||
|
ret[:status_cure].push([item, party_index, 5])
|
||||||
|
end
|
||||||
|
elsif ALL_STATUS_CURE_ITEMS.include?(item)
|
||||||
|
if want_to_cure_status
|
||||||
|
ret[:status_cure] ||= []
|
||||||
|
ret[:status_cure].push([item, party_index, 7])
|
||||||
|
end
|
||||||
|
elsif ONE_STAT_RAISE_ITEMS.include?(item)
|
||||||
|
stat_data = ONE_STAT_RAISE_ITEMS[item]
|
||||||
|
if battler && stat_raise_worthwhile?(@battlers[battler.index], stat_data[0])
|
||||||
|
ret[:stat_raise] ||= []
|
||||||
|
ret[:stat_raise].push([item, party_index, battler.stages[stat_data[0]], stat_data[1]])
|
||||||
|
end
|
||||||
|
elsif ALL_STATS_RAISE_ITEMS.include?(item)
|
||||||
|
if battler
|
||||||
|
ret[:all_stats_raise] ||= []
|
||||||
|
ret[:all_stats_raise].push([item, party_index])
|
||||||
|
end
|
||||||
|
elsif REVIVE_ITEMS.include?(item)
|
||||||
|
ret[:revive] ||= []
|
||||||
|
ret[:revive].push([item, party_index, REVIVE_ITEMS[item]])
|
||||||
|
end
|
||||||
|
return ret
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
class Battle::AI
|
class Battle::AI
|
||||||
#=============================================================================
|
|
||||||
# Decide whether the opponent should switch Pokémon
|
|
||||||
#=============================================================================
|
|
||||||
# Called by the AI's def pbDefaultChooseEnemyCommand, and by def pbChooseMove
|
# Called by the AI's def pbDefaultChooseEnemyCommand, and by def pbChooseMove
|
||||||
# if the only moves known are bad ones (the latter forces a switch). Also
|
# if the only moves known are bad ones (the latter forces a switch). Also
|
||||||
# aliased by the Battle Palace and Battle Arena.
|
# aliased by the Battle Palace and Battle Arena.
|
||||||
@@ -119,8 +116,8 @@ class Battle::AI
|
|||||||
# Predict effectiveness of foe's last used move against pkmn
|
# Predict effectiveness of foe's last used move against pkmn
|
||||||
each_foe_battler(@user.side) do |b, i|
|
each_foe_battler(@user.side) do |b, i|
|
||||||
next if !b.battler.lastMoveUsed
|
next if !b.battler.lastMoveUsed
|
||||||
move_data = GameData::Move.get(b.battler.lastMoveUsed)
|
move_data = GameData::Move.try_get(b.battler.lastMoveUsed)
|
||||||
next if move_data.status?
|
next if !move_data || move_data.status?
|
||||||
move_type = move_data.type
|
move_type = move_data.type
|
||||||
eff = Effectiveness.calculate(move_type, *pkmn_types)
|
eff = Effectiveness.calculate(move_type, *pkmn_types)
|
||||||
score -= move_data.power * eff / 5
|
score -= move_data.power * eff / 5
|
||||||
@@ -146,6 +143,7 @@ class Battle::AI
|
|||||||
ret = 0
|
ret = 0
|
||||||
# Stealth Rock
|
# Stealth Rock
|
||||||
if @battle.sides[side].effects[PBEffects::StealthRock] && GameData::Type.exists?(:ROCK)
|
if @battle.sides[side].effects[PBEffects::StealthRock] && GameData::Type.exists?(:ROCK)
|
||||||
|
pkmn_types = pkmn.types
|
||||||
eff = Effectiveness.calculate(:ROCK, *pkmn_types)
|
eff = Effectiveness.calculate(:ROCK, *pkmn_types)
|
||||||
ret += pkmn.totalhp * eff / 8 if !Effectiveness.ineffective?(eff)
|
ret += pkmn.totalhp * eff / 8 if !Effectiveness.ineffective?(eff)
|
||||||
end
|
end
|
||||||
@@ -227,7 +225,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:cure_status_problem_by_switching_out,
|
|||||||
next false if !battler.ability_active?
|
next false if !battler.ability_active?
|
||||||
# Don't try to cure a status problem/heal a bit of HP if entry hazards will
|
# Don't try to cure a status problem/heal a bit of HP if entry hazards will
|
||||||
# KO the battler if it switches back in
|
# KO the battler if it switches back in
|
||||||
entry_hazard_damage = ai.calculate_entry_hazard_damage(battler.pkmn, battler.side)
|
entry_hazard_damage = ai.calculate_entry_hazard_damage(battler.pokemon, battler.side)
|
||||||
next false if entry_hazard_damage >= battler.hp
|
next false if entry_hazard_damage >= battler.hp
|
||||||
# Check specific abilities
|
# Check specific abilities
|
||||||
single_status_cure = {
|
single_status_cure = {
|
||||||
@@ -247,8 +245,8 @@ Battle::AI::Handlers::ShouldSwitch.add(:cure_status_problem_by_switching_out,
|
|||||||
# Don't bother curing a poisoning if Toxic Spikes will just re-poison the
|
# Don't bother curing a poisoning if Toxic Spikes will just re-poison the
|
||||||
# battler when it switches back in
|
# battler when it switches back in
|
||||||
if battler.status == :POISON && reserves.none? { |pkmn| pkmn.hasType?(:POISON) }
|
if battler.status == :POISON && reserves.none? { |pkmn| pkmn.hasType?(:POISON) }
|
||||||
next false if battle.field.effects[PBEffectS::ToxicSpikes] == 2
|
next false if battle.field.effects[PBEffects::ToxicSpikes] == 2
|
||||||
next false if battle.field.effects[PBEffectS::ToxicSpikes] == 1 && battler.statusCount == 0
|
next false if battle.field.effects[PBEffects::ToxicSpikes] == 1 && battler.statusCount == 0
|
||||||
end
|
end
|
||||||
# Not worth curing status problems that still allow actions if at high HP
|
# Not worth curing status problems that still allow actions if at high HP
|
||||||
next false if battler.hp >= battler.totalhp / 2 && ![:SLEEP, :FROZEN].include?(battler.status)
|
next false if battler.hp >= battler.totalhp / 2 && ![:SLEEP, :FROZEN].include?(battler.status)
|
||||||
@@ -281,7 +279,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:wish_healing,
|
|||||||
next false if battler.totalhp - battler.hp >= amt * 2 / 3
|
next false if battler.totalhp - battler.hp >= amt * 2 / 3
|
||||||
reserve_wants_healing_more = false
|
reserve_wants_healing_more = false
|
||||||
reserves.each do |pkmn|
|
reserves.each do |pkmn|
|
||||||
entry_hazard_damage = calculate_entry_hazard_damage(pkmn, battler.index & 1)
|
entry_hazard_damage = ai.calculate_entry_hazard_damage(pkmn, battler.index & 1)
|
||||||
next if entry_hazard_damage >= pkmn.hp
|
next if entry_hazard_damage >= pkmn.hp
|
||||||
reserve_wants_healing_more = (pkmn.totalhp - pkmn.hp - entry_hazard_damage >= amt * 2 / 3)
|
reserve_wants_healing_more = (pkmn.totalhp - pkmn.hp - entry_hazard_damage >= amt * 2 / 3)
|
||||||
break if reserve_wants_healing_more
|
break if reserve_wants_healing_more
|
||||||
@@ -331,7 +329,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:yawning,
|
|||||||
next if b.ability_active? && Battle::AbilityEffects.triggerCertainSwitching(b.ability, b, battle)
|
next if b.ability_active? && Battle::AbilityEffects.triggerCertainSwitching(b.ability, b, battle)
|
||||||
next if b.item_active? && Battle::ItemEffects.triggerCertainSwitching(b.item, b, battle)
|
next if b.item_active? && Battle::ItemEffects.triggerCertainSwitching(b.item, b, battle)
|
||||||
next if Settings::MORE_TYPE_EFFECTS && b.has_type?(:GHOST)
|
next if Settings::MORE_TYPE_EFFECTS && b.has_type?(:GHOST)
|
||||||
next if b.trappedInBattle? # Relevant trapping effects are checked above
|
next if b.battler.trappedInBattle? # Relevant trapping effects are checked above
|
||||||
if battler.ability_active?
|
if battler.ability_active?
|
||||||
trapping = Battle::AbilityEffects.triggerTrappingByTarget(battler.ability, b, battler.battler, battle)
|
trapping = Battle::AbilityEffects.triggerTrappingByTarget(battler.ability, b, battler.battler, battle)
|
||||||
break if trapping
|
break if trapping
|
||||||
@@ -344,7 +342,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:yawning,
|
|||||||
next false if trapping
|
next false if trapping
|
||||||
end
|
end
|
||||||
# Doesn't have sufficiently raised stats that would be lost by switching
|
# Doesn't have sufficiently raised stats that would be lost by switching
|
||||||
next false if battler.stages.any? { |val| val >= 2 }
|
next false if battler.stages.any? { |key, val| val >= 2 }
|
||||||
PBDebug.log_ai("#{battler.name} wants to switch because it is yawning and can't do anything while asleep")
|
PBDebug.log_ai("#{battler.name} wants to switch because it is yawning and can't do anything while asleep")
|
||||||
next true
|
next true
|
||||||
}
|
}
|
||||||
@@ -376,7 +374,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:asleep,
|
|||||||
next if b.ability_active? && Battle::AbilityEffects.triggerCertainSwitching(b.ability, b, battle)
|
next if b.ability_active? && Battle::AbilityEffects.triggerCertainSwitching(b.ability, b, battle)
|
||||||
next if b.item_active? && Battle::ItemEffects.triggerCertainSwitching(b.item, b, battle)
|
next if b.item_active? && Battle::ItemEffects.triggerCertainSwitching(b.item, b, battle)
|
||||||
next if Settings::MORE_TYPE_EFFECTS && b.has_type?(:GHOST)
|
next if Settings::MORE_TYPE_EFFECTS && b.has_type?(:GHOST)
|
||||||
next if b.trappedInBattle? # Relevant trapping effects are checked above
|
next if b.battler.trappedInBattle? # Relevant trapping effects are checked above
|
||||||
if battler.ability_active?
|
if battler.ability_active?
|
||||||
trapping = Battle::AbilityEffects.triggerTrappingByTarget(battler.ability, b, battler.battler, battle)
|
trapping = Battle::AbilityEffects.triggerTrappingByTarget(battler.ability, b, battler.battler, battle)
|
||||||
break if trapping
|
break if trapping
|
||||||
@@ -389,7 +387,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:asleep,
|
|||||||
next false if trapping
|
next false if trapping
|
||||||
end
|
end
|
||||||
# Doesn't have sufficiently raised stats that would be lost by switching
|
# Doesn't have sufficiently raised stats that would be lost by switching
|
||||||
next false if battler.stages.any? { |val| val >= 2 }
|
next false if battler.stages.any? { |key, val| val >= 2 }
|
||||||
# 50% chance to not bother
|
# 50% chance to not bother
|
||||||
next false if ai.pbAIRandom(100) < 50
|
next false if ai.pbAIRandom(100) < 50
|
||||||
PBDebug.log_ai("#{battler.name} wants to switch because it is asleep and can't do anything")
|
PBDebug.log_ai("#{battler.name} wants to switch because it is asleep and can't do anything")
|
||||||
@@ -430,7 +428,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:foe_has_wonder_guard,
|
|||||||
next false if battler.battler.hasMoldBreaker?
|
next false if battler.battler.hasMoldBreaker?
|
||||||
non_wonder_guard_foe_exists = false
|
non_wonder_guard_foe_exists = false
|
||||||
has_super_effective_move = false
|
has_super_effective_move = false
|
||||||
foe_types = b.pbTypes(true)
|
foe_types = battler.pbTypes(true)
|
||||||
next false if foe_types.length == 0
|
next false if foe_types.length == 0
|
||||||
ai.each_foe_battler(battler.side) do |b, i|
|
ai.each_foe_battler(battler.side) do |b, i|
|
||||||
if !b.has_active_ability?(:WONDERGUARD)
|
if !b.has_active_ability?(:WONDERGUARD)
|
||||||
@@ -479,14 +477,14 @@ Battle::AI::Handlers::ShouldSwitch.add(:foe_has_wonder_guard,
|
|||||||
# Check reserves for super-effective moves; only switch if there are any
|
# Check reserves for super-effective moves; only switch if there are any
|
||||||
reserve_has_super_effective_move = false
|
reserve_has_super_effective_move = false
|
||||||
reserves.each do |pkmn|
|
reserves.each do |pkmn|
|
||||||
pkmn.moves.each do |m|
|
pkmn.moves.each do |move|
|
||||||
next if m.status_move?
|
next if move.status_move?
|
||||||
if ["IgnoreTargetAbility",
|
if ["IgnoreTargetAbility",
|
||||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
|
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
|
||||||
reserve_has_super_effective_move = true
|
reserve_has_super_effective_move = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
eff = Effectiveness.calculate(m.type, *foe_types)
|
eff = Effectiveness.calculate(move.type, *foe_types)
|
||||||
if Effectiveness.super_effective?(eff)
|
if Effectiveness.super_effective?(eff)
|
||||||
reserve_has_super_effective_move = true
|
reserve_has_super_effective_move = true
|
||||||
break
|
break
|
||||||
@@ -525,15 +523,15 @@ Battle::AI::Handlers::ShouldSwitch.add(:absorb_foe_move,
|
|||||||
# Get the foe move with the highest power (or a random damaging move)
|
# Get the foe move with the highest power (or a random damaging move)
|
||||||
foe_moves = []
|
foe_moves = []
|
||||||
ai.each_foe_battler(battler.side) do |b, i|
|
ai.each_foe_battler(battler.side) do |b, i|
|
||||||
b.moves.each do |m|
|
b.moves.each do |move|
|
||||||
next if m.statusMove?
|
next if move.statusMove?
|
||||||
# TODO: Improve on m_power with STAB and attack stat/stages and certain
|
# TODO: Improve on m_power with STAB and attack stat/stages and certain
|
||||||
# other damage-altering effects, including base power calculations
|
# other damage-altering effects, including base power calculations
|
||||||
# for moves with variable power.
|
# for moves with variable power.
|
||||||
m_power = m.power
|
m_power = move.power
|
||||||
m_power = battler.hp if m.is_a?(Battle::Move::OHKO)
|
m_power = battler.hp if move.is_a?(Battle::Move::OHKO)
|
||||||
m_type = move.pbCalcType(b.battler)
|
m_type = move.pbCalcType(b.battler)
|
||||||
foe_moves.push([m_power, m_type, m])
|
foe_moves.push([m_power, m_type, move])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
next false if foe_moves.empty?
|
next false if foe_moves.empty?
|
||||||
@@ -612,7 +610,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:high_damage_from_foe,
|
|||||||
big_threat = false
|
big_threat = false
|
||||||
ai.each_foe_battler(battler.side) do |b, i|
|
ai.each_foe_battler(battler.side) do |b, i|
|
||||||
next if (b.level - battler.level).abs > 5
|
next if (b.level - battler.level).abs > 5
|
||||||
next if !b.battler.lastMoveUsed
|
next if !b.battler.lastMoveUsed || !GameData::Move.exists?(b.battler.lastMoveUsed)
|
||||||
move_data = GameData::Move.get(b.battler.lastMoveUsed)
|
move_data = GameData::Move.get(b.battler.lastMoveUsed)
|
||||||
next if move_data.status?
|
next if move_data.status?
|
||||||
eff = battler.effectiveness_of_type_against_battler(move_data.type, b)
|
eff = battler.effectiveness_of_type_against_battler(move_data.type, b)
|
||||||
@@ -643,7 +641,7 @@ Battle::AI::Handlers::ShouldNotSwitch.add(:lethal_entry_hazards,
|
|||||||
proc { |battler, reserves, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
next false if battle.rules["suddendeath"]
|
next false if battle.rules["suddendeath"]
|
||||||
# Check whether battler will faint from entry hazard(s)
|
# Check whether battler will faint from entry hazard(s)
|
||||||
entry_hazard_damage = ai.calculate_entry_hazard_damage(battler.pkmn, battler.side)
|
entry_hazard_damage = ai.calculate_entry_hazard_damage(battler.pokemon, battler.side)
|
||||||
next false if entry_hazard_damage < battler.hp
|
next false if entry_hazard_damage < battler.hp
|
||||||
# Check for Rapid Spin
|
# Check for Rapid Spin
|
||||||
reserve_can_remove_hazards = false
|
reserve_can_remove_hazards = false
|
||||||
@@ -668,7 +666,7 @@ Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_super_effective_move,
|
|||||||
proc { |battler, reserves, ai, battle|
|
proc { |battler, reserves, ai, battle|
|
||||||
next false if battle.rules["suddendeath"]
|
next false if battle.rules["suddendeath"]
|
||||||
has_super_effective_move = false
|
has_super_effective_move = false
|
||||||
battler.eachMove do |move|
|
battler.battler.eachMove do |move|
|
||||||
next if move.pp == 0 && move.total_pp > 0
|
next if move.pp == 0 && move.total_pp > 0
|
||||||
next if move.statusMove?
|
next if move.statusMove?
|
||||||
# TODO: next if move is unusable? This would be complicated to implement.
|
# TODO: next if move is unusable? This would be complicated to implement.
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ class Battle::AI
|
|||||||
strength = b.effects[PBEffects::FollowMe]
|
strength = b.effects[PBEffects::FollowMe]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return new_target if new_target
|
return new_target if new_target >= 0
|
||||||
calc_type = @move.rough_type
|
calc_type = @move.rough_type
|
||||||
priority.each do |b|
|
priority.each do |b|
|
||||||
next if b.index == @user.index
|
next if b.index == @user.index
|
||||||
@@ -122,7 +122,7 @@ class Battle::AI
|
|||||||
end
|
end
|
||||||
break if new_target >= 0
|
break if new_target >= 0
|
||||||
end
|
end
|
||||||
return new_target
|
return (new_target >= 0) ? new_target : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_move_to_choices(choices, idxMove, score, idxTarget = -1)
|
def add_move_to_choices(choices, idxMove, score, idxTarget = -1)
|
||||||
@@ -142,6 +142,7 @@ class Battle::AI
|
|||||||
case move.function
|
case move.function
|
||||||
when "UseLastMoveUsed"
|
when "UseLastMoveUsed"
|
||||||
if @battle.lastMoveUsed &&
|
if @battle.lastMoveUsed &&
|
||||||
|
GameData::Move.exists?(@battle.lastMoveUsed) &&
|
||||||
!move.moveBlacklist.include?(GameData::Move.get(@battle.lastMoveUsed).function_code)
|
!move.moveBlacklist.include?(GameData::Move.get(@battle.lastMoveUsed).function_code)
|
||||||
move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@battle.lastMoveUsed))
|
move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@battle.lastMoveUsed))
|
||||||
end
|
end
|
||||||
@@ -159,9 +160,10 @@ class Battle::AI
|
|||||||
@target&.refresh_battler
|
@target&.refresh_battler
|
||||||
if @target && @move.function == "UseLastMoveUsedByTarget"
|
if @target && @move.function == "UseLastMoveUsedByTarget"
|
||||||
if @target.battler.lastRegularMoveUsed &&
|
if @target.battler.lastRegularMoveUsed &&
|
||||||
|
GameData::Move.exists?(@target.battler.lastRegularMoveUsed) &&
|
||||||
GameData::Move.get(@target.battler.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
GameData::Move.get(@target.battler.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
||||||
mov = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@target.battler.lastRegularMoveUsed))
|
|
||||||
@battle.moldBreaker = @user.has_mold_breaker?
|
@battle.moldBreaker = @user.has_mold_breaker?
|
||||||
|
mov = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@target.battler.lastRegularMoveUsed))
|
||||||
@move.set_up(mov)
|
@move.set_up(mov)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -6,24 +6,24 @@ class Battle::AI
|
|||||||
# opponent (only when deciding whether to switch mon in) - this
|
# opponent (only when deciding whether to switch mon in) - this
|
||||||
# comparison should be calculated when needed instead of being a role.
|
# comparison should be calculated when needed instead of being a role.
|
||||||
module BattleRole
|
module BattleRole
|
||||||
PHAZER = 0
|
PHAZER = 0
|
||||||
CLERIC = 1
|
CLERIC = 1
|
||||||
STALLBREAKER = 2
|
STALL_BREAKER = 2
|
||||||
STATUSABSORBER = 3
|
STATUS_ABSORBER = 3
|
||||||
BATONPASSER = 4
|
BATON_PASSER = 4
|
||||||
SPINNER = 5
|
SPINNER = 5
|
||||||
FIELDSETTER = 6
|
FIELD_SETTER = 6
|
||||||
WEATHERSETTER = 7
|
WEATHER_SETTER = 7
|
||||||
SWEEPER = 8
|
SWEEPER = 8
|
||||||
PIVOT = 9
|
PIVOT = 9
|
||||||
PHYSICALWALL = 10
|
PHYSICAL_WALL = 10
|
||||||
SPECIALWALL = 11
|
SPECIAL_WALL = 11
|
||||||
TANK = 12
|
TANK = 12
|
||||||
TRAPPER = 13
|
TRAPPER = 13
|
||||||
SCREENER = 14
|
SCREENER = 14
|
||||||
ACE = 15
|
ACE = 15
|
||||||
LEAD = 16
|
LEAD = 16
|
||||||
SECOND = 17
|
SECOND = 17
|
||||||
end
|
end
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
@@ -51,22 +51,22 @@ class Battle::AI
|
|||||||
when "CureUserPartyStatus" # Aromatherapy/Heal Bell
|
when "CureUserPartyStatus" # Aromatherapy/Heal Bell
|
||||||
ret.push(BattleRole::CLERIC)
|
ret.push(BattleRole::CLERIC)
|
||||||
when "DisableTargetStatusMoves" # Taunt
|
when "DisableTargetStatusMoves" # Taunt
|
||||||
ret.push(BattleRole::STALLBREAKER)
|
ret.push(BattleRole::STALL_BREAKER)
|
||||||
when "HealUserPositionNextTurn" # Wish
|
when "HealUserPositionNextTurn" # Wish
|
||||||
ret.push(BattleRole::CLERIC) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
ret.push(BattleRole::CLERIC) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
||||||
when "HealUserFullyAndFallAsleep" # Rest
|
when "HealUserFullyAndFallAsleep" # Rest
|
||||||
ret.push(BattleRole::STATUSABSORBER)
|
ret.push(BattleRole::STATUS_ABSORBER)
|
||||||
when "SwitchOutUserPassOnEffects" # Baton Pass
|
when "SwitchOutUserPassOnEffects" # Baton Pass
|
||||||
ret.push(BattleRole::BATONPASSER)
|
ret.push(BattleRole::BATON_PASSER)
|
||||||
when "SwitchOutUserStatusMove", "SwitchOutUserDamagingMove" # Teleport (Gen 8+), U-turn
|
when "SwitchOutUserStatusMove", "SwitchOutUserDamagingMove" # Teleport (Gen 8+), U-turn
|
||||||
hasPivotMove = true
|
hasPivotMove = true
|
||||||
when "RemoveUserBindingAndEntryHazards" # Rapid Spin
|
when "RemoveUserBindingAndEntryHazards" # Rapid Spin
|
||||||
ret.push(BattleRole::SPINNER)
|
ret.push(BattleRole::SPINNER)
|
||||||
when "StartElectricTerrain", "StartGrassyTerrain",
|
when "StartElectricTerrain", "StartGrassyTerrain",
|
||||||
"StartMistyTerrain", "StartPsychicTerrain" # Terrain moves
|
"StartMistyTerrain", "StartPsychicTerrain" # Terrain moves
|
||||||
ret.push(BattleRole::FIELDSETTER)
|
ret.push(BattleRole::FIELD_SETTER)
|
||||||
else
|
else
|
||||||
ret.push(BattleRole::WEATHERSETTER) if move.is_a?(Battle::Move::WeatherMove)
|
ret.push(BattleRole::WEATHER_SETTER) if move.is_a?(Battle::Move::WeatherMove)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -81,10 +81,10 @@ class Battle::AI
|
|||||||
ret.push(BattleRole::PIVOT) if hasPivotMove
|
ret.push(BattleRole::PIVOT) if hasPivotMove
|
||||||
if pkmn.nature.stat_changes.any? { |change| change[0] == :DEFENSE && change[1] > 0 } &&
|
if pkmn.nature.stat_changes.any? { |change| change[0] == :DEFENSE && change[1] > 0 } &&
|
||||||
!pkmn.nature.stat_changes.any? { |change| change[0] == :DEFENSE && change[1] < 0 }
|
!pkmn.nature.stat_changes.any? { |change| change[0] == :DEFENSE && change[1] < 0 }
|
||||||
ret.push(BattleRole::PHYSICALWALL) if pkmn.ev[:DEFENSE] == Pokemon::EV_STAT_LIMIT
|
ret.push(BattleRole::PHYSICAL_WALL) if pkmn.ev[:DEFENSE] == Pokemon::EV_STAT_LIMIT
|
||||||
elsif pkmn.nature.stat_changes.any? { |change| change[0] == :SPECIAL_DEFENSE && change[1] > 0 } &&
|
elsif pkmn.nature.stat_changes.any? { |change| change[0] == :SPECIAL_DEFENSE && change[1] > 0 } &&
|
||||||
!pkmn.nature.stat_changes.any? { |change| change[0] == :SPECIAL_DEFENSE && change[1] < 0 }
|
!pkmn.nature.stat_changes.any? { |change| change[0] == :SPECIAL_DEFENSE && change[1] < 0 }
|
||||||
ret.push(BattleRole::SPECIALWALL) if pkmn.ev[:SPECIAL_DEFENSE] == Pokemon::EV_STAT_LIMIT
|
ret.push(BattleRole::SPECIAL_WALL) if pkmn.ev[:SPECIAL_DEFENSE] == Pokemon::EV_STAT_LIMIT
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
ret.push(BattleRole::TANK) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
ret.push(BattleRole::TANK) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
||||||
@@ -96,14 +96,14 @@ class Battle::AI
|
|||||||
ret.push(BattleRole::PIVOT)
|
ret.push(BattleRole::PIVOT)
|
||||||
when :GUTS, :QUICKFEET, :FLAREBOOST, :TOXICBOOST, :NATURALCURE, :MAGICGUARD,
|
when :GUTS, :QUICKFEET, :FLAREBOOST, :TOXICBOOST, :NATURALCURE, :MAGICGUARD,
|
||||||
:MAGICBOUNCE, :HYDRATION
|
:MAGICBOUNCE, :HYDRATION
|
||||||
ret.push(BattleRole::STATUSABSORBER)
|
ret.push(BattleRole::STATUS_ABSORBER)
|
||||||
when :SHADOWTAG, :ARENATRAP, :MAGNETPULL
|
when :SHADOWTAG, :ARENATRAP, :MAGNETPULL
|
||||||
ret.push(BattleRole::TRAPPER)
|
ret.push(BattleRole::TRAPPER)
|
||||||
when :DROUGHT, :DRIZZLE, :SANDSTREAM, :SNOWWARNING, :PRIMORDIALSEA,
|
when :DROUGHT, :DRIZZLE, :SANDSTREAM, :SNOWWARNING, :PRIMORDIALSEA,
|
||||||
:DESOLATELAND, :DELTASTREAM
|
:DESOLATELAND, :DELTASTREAM
|
||||||
ret.push(BattleRole::WEATHERSETTER)
|
ret.push(BattleRole::WEATHER_SETTER)
|
||||||
when :GRASSYSURGE, :ELECTRICSURGE, :MISTYSURGE, :PSYCHICSURGE
|
when :GRASSYSURGE, :ELECTRICSURGE, :MISTYSURGE, :PSYCHICSURGE
|
||||||
ret.push(BattleRole::FIELDSETTER)
|
ret.push(BattleRole::FIELD_SETTER)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for items indicative of particular roles
|
# Check for items indicative of particular roles
|
||||||
@@ -113,14 +113,14 @@ class Battle::AI
|
|||||||
when :ASSAULTVEST
|
when :ASSAULTVEST
|
||||||
ret.push(BattleRole::TANK)
|
ret.push(BattleRole::TANK)
|
||||||
when :CHOICEBAND, :CHOICESPECS
|
when :CHOICEBAND, :CHOICESPECS
|
||||||
ret.push(BattleRole::STALLBREAKER)
|
ret.push(BattleRole::STALL_BREAKER)
|
||||||
ret.push(BattleRole::SWEEPER) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
ret.push(BattleRole::SWEEPER) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
||||||
when :CHOICESCARF
|
when :CHOICESCARF
|
||||||
ret.push(BattleRole::SWEEPER) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
ret.push(BattleRole::SWEEPER) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
||||||
when :TOXICORB, :FLAMEORB
|
when :TOXICORB, :FLAMEORB
|
||||||
ret.push(BattleRole::STATUSABSORBER)
|
ret.push(BattleRole::STATUS_ABSORBER)
|
||||||
when :TERRAINEXTENDER
|
when :TERRAINEXTENDER
|
||||||
ret.push(BattleRole::FIELDSETTER)
|
ret.push(BattleRole::FIELD_SETTER)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Check for position in team, level relative to other levels in team
|
# Check for position in team, level relative to other levels in team
|
||||||
|
|||||||
@@ -509,9 +509,9 @@ class Battle::AI
|
|||||||
next if target_speed > b_speed * 2.5 # Much too fast to reasonably be overtaken
|
next if target_speed > b_speed * 2.5 # Much too fast to reasonably be overtaken
|
||||||
if target_speed > b_speed
|
if target_speed > b_speed
|
||||||
if target_speed < b_speed * 2 / (decrement + 2)
|
if target_speed < b_speed * 2 / (decrement + 2)
|
||||||
score += 15 * inc_mult # Target will become slower than b
|
score += 15 * dec_mult # Target will become slower than b
|
||||||
else
|
else
|
||||||
score += 8 * inc_mult
|
score += 8 * dec_mult
|
||||||
end
|
end
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shiny_target,
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
# Prefer Shadow moves (for flavour).
|
# Prefer Shadow moves (for flavour).
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shadow_moves,
|
Battle::AI::Handlers::GeneralMoveScore.add(:shadow_moves,
|
||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, ai, battle|
|
||||||
if move.rough_type == :SHADOW
|
if move.rough_type == :SHADOW
|
||||||
old_score = score
|
old_score = score
|
||||||
score += 10
|
score += 10
|
||||||
@@ -51,8 +51,8 @@ Battle::AI::Handlers::GeneralMoveScore.add(:thawing_move_when_frozen,
|
|||||||
# - the target is predicted to be knocked out by the move.
|
# - the target is predicted to be knocked out by the move.
|
||||||
# TODO: Less prefer a priority move if any foe knows Quick Guard?
|
# TODO: Less prefer a priority move if any foe knows Quick Guard?
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::GeneralMoveScore.add(:priority_move_against_faster_target,
|
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:priority_move_against_faster_target,
|
||||||
proc { |score, move, user, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
if ai.trainer.high_skill? && target.faster_than?(user) && move.rough_priority(user) > 0
|
if ai.trainer.high_skill? && target.faster_than?(user) && move.rough_priority(user) > 0
|
||||||
# User is at risk of being knocked out
|
# User is at risk of being knocked out
|
||||||
if ai.trainer.has_skill_flag?("HPAware") && user.hp < user.totalhp / 3
|
if ai.trainer.has_skill_flag?("HPAware") && user.hp < user.totalhp / 3
|
||||||
@@ -61,7 +61,7 @@ Battle::AI::Handlers::GeneralMoveScore.add(:priority_move_against_faster_target,
|
|||||||
PBDebug.log_score_change(score - old_score, "user at low HP and move has priority over faster target")
|
PBDebug.log_score_change(score - old_score, "user at low HP and move has priority over faster target")
|
||||||
end
|
end
|
||||||
# Target is predicted to be knocked out by the move
|
# Target is predicted to be knocked out by the move
|
||||||
if move.damaging_move? && move.rough_damage >= target.hp
|
if move.damagingMove? && move.rough_damage >= target.hp
|
||||||
old_score = score
|
old_score = score
|
||||||
score += 8
|
score += 8
|
||||||
PBDebug.log_score_change(score - old_score, "target at low HP and move has priority over faster target")
|
PBDebug.log_score_change(score - old_score, "target at low HP and move has priority over faster target")
|
||||||
@@ -221,7 +221,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:target_semi_invulnerabl
|
|||||||
if ai.trainer.medium_skill? && move.rough_accuracy > 0 &&
|
if ai.trainer.medium_skill? && move.rough_accuracy > 0 &&
|
||||||
(target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
|
(target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
|
||||||
next score if user.has_active_ability?(:NOGUARD) || target.has_active_ability?(:NOGUARD)
|
next score if user.has_active_ability?(:NOGUARD) || target.has_active_ability?(:NOGUARD)
|
||||||
priority = move.rough_priority
|
priority = move.rough_priority(user)
|
||||||
if priority > 0 || (priority == 0 && user.faster_than?(target)) # User goes first
|
if priority > 0 || (priority == 0 && user.faster_than?(target)) # User goes first
|
||||||
miss = true
|
miss = true
|
||||||
if ai.trainer.high_skill?
|
if ai.trainer.high_skill?
|
||||||
@@ -328,6 +328,13 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:thawing_move_against_fr
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
#===============================================================================
|
||||||
|
#
|
||||||
|
#===============================================================================
|
||||||
|
# TODO: Check all effects that trigger upon using a move, including per-hit
|
||||||
|
# stuff in def pbEffectsOnMakingHit and end-of-move stuff in def
|
||||||
|
# pbEffectsAfterMove.
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
@@ -350,7 +357,7 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:knocking_out_a_destiny_
|
|||||||
proc { |score, move, user, target, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
if (ai.trainer.has_skill_flag?("HPAware") || ai.trainer.high_skill?) && move.damagingMove? &&
|
if (ai.trainer.has_skill_flag?("HPAware") || ai.trainer.high_skill?) && move.damagingMove? &&
|
||||||
(target.effects[PBEffects::DestinyBond] || target.effects[PBEffects::Grudge])
|
(target.effects[PBEffects::DestinyBond] || target.effects[PBEffects::Grudge])
|
||||||
priority = move.rough_priority
|
priority = move.rough_priority(user)
|
||||||
if priority > 0 || (priority == 0 && user.faster_than?(target)) # User goes first
|
if priority > 0 || (priority == 0 && user.faster_than?(target)) # User goes first
|
||||||
if move.rough_damage > target.hp * 1.1 # Predicted to KO the target
|
if move.rough_damage > target.hp * 1.1 # Predicted to KO the target
|
||||||
old_score = score
|
old_score = score
|
||||||
|
|||||||
@@ -60,6 +60,32 @@ class Battle::AI::AIBattler
|
|||||||
# Returns how much damage this battler will take at the end of this round.
|
# Returns how much damage this battler will take at the end of this round.
|
||||||
def rough_end_of_round_damage
|
def rough_end_of_round_damage
|
||||||
ret = 0
|
ret = 0
|
||||||
|
# Weather
|
||||||
|
weather = battler.effectiveWeather
|
||||||
|
if @ai.battle.field.weatherDuration == 1
|
||||||
|
weather = @ai.battle.field.defaultWeather
|
||||||
|
weather = :None if @ai.battle.allBattlers.any? { |b| b.hasActiveAbility?([:CLOUDNINE, :AIRLOCK]) }
|
||||||
|
weather = :None if [:Sun, :Rain, :HarshSun, :HeavyRain].include?(weather) && has_active_item?(:UTILITYUMBRELLA)
|
||||||
|
end
|
||||||
|
case weather
|
||||||
|
when :Sandstorm
|
||||||
|
ret += [self.totalhp / 16, 1].max if battler.takesSandstormDamage?
|
||||||
|
when :Hail
|
||||||
|
ret += [self.totalhp / 16, 1].max if battler.takesHailDamage?
|
||||||
|
when :ShadowSky
|
||||||
|
ret += [self.totalhp / 16, 1].max if battler.takesShadowSkyDamage?
|
||||||
|
end
|
||||||
|
case ability_id
|
||||||
|
when :DRYSKIN
|
||||||
|
ret += [self.totalhp / 8, 1].max if [:Sun, :HarshSun].include?(weather) && battler.takesIndirectDamage?
|
||||||
|
ret -= [self.totalhp / 8, 1].max if [:Rain, :HeavyRain].include?(weather) && battler.canHeal?
|
||||||
|
when :ICEBODY
|
||||||
|
ret -= [self.totalhp / 16, 1].max if weather == :Hail && battler.canHeal?
|
||||||
|
when :RAINDISH
|
||||||
|
ret -= [self.totalhp / 16, 1].max if [:Rain, :HeavyRain].include?(weather) && battler.canHeal?
|
||||||
|
when :SOLARPOWER
|
||||||
|
ret += [self.totalhp / 8, 1].max if [:Sun, :HarshSun].include?(weather) && battler.takesIndirectDamage?
|
||||||
|
end
|
||||||
# Future Sight/Doom Desire
|
# Future Sight/Doom Desire
|
||||||
# TODO
|
# TODO
|
||||||
# Wish
|
# Wish
|
||||||
@@ -69,38 +95,38 @@ class Battle::AI::AIBattler
|
|||||||
# Sea of Fire
|
# Sea of Fire
|
||||||
if @ai.battle.sides[@side].effects[PBEffects::SeaOfFire] > 1 &&
|
if @ai.battle.sides[@side].effects[PBEffects::SeaOfFire] > 1 &&
|
||||||
battler.takesIndirectDamage? && !has_type?(:FIRE)
|
battler.takesIndirectDamage? && !has_type?(:FIRE)
|
||||||
ret += self.totalhp / 8
|
ret += [self.totalhp / 8, 1].max
|
||||||
end
|
end
|
||||||
# Grassy Terrain (healing)
|
# Grassy Terrain (healing)
|
||||||
if @ai.battle.field.terrain == :Grassy && battler.affectedByTerrain? && battler.canHeal?
|
if @ai.battle.field.terrain == :Grassy && battler.affectedByTerrain? && battler.canHeal?
|
||||||
ret -= [battler.totalhp / 16, 1].max
|
ret -= [self.totalhp / 16, 1].max
|
||||||
end
|
end
|
||||||
# Leftovers/Black Sludge
|
# Leftovers/Black Sludge
|
||||||
if has_active_item?(:BLACKSLUDGE)
|
if has_active_item?(:BLACKSLUDGE)
|
||||||
if has_type?(:POISON)
|
if has_type?(:POISON)
|
||||||
ret -= [battler.totalhp / 16, 1].max if battler.canHeal?
|
ret -= [self.totalhp / 16, 1].max if battler.canHeal?
|
||||||
else
|
else
|
||||||
ret += [battler.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
ret += [self.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
||||||
end
|
end
|
||||||
elsif has_active_item?(:LEFTOVERS)
|
elsif has_active_item?(:LEFTOVERS)
|
||||||
ret -= [battler.totalhp / 16, 1].max if battler.canHeal?
|
ret -= [self.totalhp / 16, 1].max if battler.canHeal?
|
||||||
end
|
end
|
||||||
# Aqua Ring
|
# Aqua Ring
|
||||||
if self.effects[PBEffects::AquaRing] && battler.canHeal?
|
if self.effects[PBEffects::AquaRing] && battler.canHeal?
|
||||||
amt = battler.totalhp / 16
|
amt = self.totalhp / 16
|
||||||
amt = (amt * 1.3).floor if has_active_item?(:BIGROOT)
|
amt = (amt * 1.3).floor if has_active_item?(:BIGROOT)
|
||||||
ret -= [amt, 1].max
|
ret -= [amt, 1].max
|
||||||
end
|
end
|
||||||
# Ingrain
|
# Ingrain
|
||||||
if self.effects[PBEffects::Ingrain] && battler.canHeal?
|
if self.effects[PBEffects::Ingrain] && battler.canHeal?
|
||||||
amt = battler.totalhp / 16
|
amt = self.totalhp / 16
|
||||||
amt = (amt * 1.3).floor if has_active_item?(:BIGROOT)
|
amt = (amt * 1.3).floor if has_active_item?(:BIGROOT)
|
||||||
ret -= [amt, 1].max
|
ret -= [amt, 1].max
|
||||||
end
|
end
|
||||||
# Leech Seed
|
# Leech Seed
|
||||||
if self.effects[PBEffects::LeechSeed] >= 0
|
if self.effects[PBEffects::LeechSeed] >= 0
|
||||||
if battler.takesIndirectDamage?
|
if battler.takesIndirectDamage?
|
||||||
ret += [battler.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
ret += [self.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ai.each_battler do |b, i|
|
@ai.each_battler do |b, i|
|
||||||
@@ -112,16 +138,16 @@ class Battle::AI::AIBattler
|
|||||||
end
|
end
|
||||||
# Hyper Mode (Shadow Pokémon)
|
# Hyper Mode (Shadow Pokémon)
|
||||||
if battler.inHyperMode?
|
if battler.inHyperMode?
|
||||||
ret += [battler.totalhp / 24, 1].max
|
ret += [self.totalhp / 24, 1].max
|
||||||
end
|
end
|
||||||
# Poison/burn/Nightmare
|
# Poison/burn/Nightmare
|
||||||
if self.status == :POISON
|
if self.status == :POISON
|
||||||
if has_active_ability?(:POISONHEAL)
|
if has_active_ability?(:POISONHEAL)
|
||||||
ret -= [battler.totalhp / 8, 1].max if battler.canHeal?
|
ret -= [self.totalhp / 8, 1].max if battler.canHeal?
|
||||||
elsif battler.takesIndirectDamage?
|
elsif battler.takesIndirectDamage?
|
||||||
mult = 2
|
mult = 2
|
||||||
mult = [self.effects[PBEffects::Toxic] + 1, 16].min if self.statusCount > 0 # Toxic
|
mult = [self.effects[PBEffects::Toxic] + 1, 16].min if self.statusCount > 0 # Toxic
|
||||||
ret += [mult * battler.totalhp / 16, 1].max
|
ret += [mult * self.totalhp / 16, 1].max
|
||||||
end
|
end
|
||||||
elsif self.status == :BURN
|
elsif self.status == :BURN
|
||||||
if battler.takesIndirectDamage?
|
if battler.takesIndirectDamage?
|
||||||
@@ -130,11 +156,11 @@ class Battle::AI::AIBattler
|
|||||||
ret += [amt, 1].max
|
ret += [amt, 1].max
|
||||||
end
|
end
|
||||||
elsif battler.asleep? && self.statusCount > 1 && self.effects[PBEffects::Nightmare]
|
elsif battler.asleep? && self.statusCount > 1 && self.effects[PBEffects::Nightmare]
|
||||||
ret += [battler.totalhp / 4, 1].max if battler.takesIndirectDamage?
|
ret += [self.totalhp / 4, 1].max if battler.takesIndirectDamage?
|
||||||
end
|
end
|
||||||
# Curse
|
# Curse
|
||||||
if self.effects[PBEffects::Curse]
|
if self.effects[PBEffects::Curse]
|
||||||
ret += [battler.totalhp / 4, 1].max if battler.takesIndirectDamage?
|
ret += [self.totalhp / 4, 1].max if battler.takesIndirectDamage?
|
||||||
end
|
end
|
||||||
# Trapping damage
|
# Trapping damage
|
||||||
if self.effects[PBEffects::Trapping] > 1 && battler.takesIndirectDamage?
|
if self.effects[PBEffects::Trapping] > 1 && battler.takesIndirectDamage?
|
||||||
@@ -150,12 +176,12 @@ class Battle::AI::AIBattler
|
|||||||
if battler.asleep? && self.statusCount > 1 && battler.takesIndirectDamage?
|
if battler.asleep? && self.statusCount > 1 && battler.takesIndirectDamage?
|
||||||
@ai.each_battler do |b, i|
|
@ai.each_battler do |b, i|
|
||||||
next if i == @index || !b.battler.near?(battler) || !b.has_active_ability?(:BADDREAMS)
|
next if i == @index || !b.battler.near?(battler) || !b.has_active_ability?(:BADDREAMS)
|
||||||
ret += [battler.totalhp / 8, 1].max
|
ret += [self.totalhp / 8, 1].max
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Sticky Barb
|
# Sticky Barb
|
||||||
if has_active_item?(:STICKYBARB) && battler.takesIndirectDamage?
|
if has_active_item?(:STICKYBARB) && battler.takesIndirectDamage?
|
||||||
ret += [battler.totalhp / 8, 1].max
|
ret += [self.totalhp / 8, 1].max
|
||||||
end
|
end
|
||||||
return ret
|
return ret
|
||||||
end
|
end
|
||||||
@@ -242,7 +268,7 @@ class Battle::AI::AIBattler
|
|||||||
end
|
end
|
||||||
|
|
||||||
def has_mold_breaker?
|
def has_mold_breaker?
|
||||||
return @ai.move.function == "IgnoreTargetAbility" || battler.hasMoldBreaker?
|
return battler.hasMoldBreaker?
|
||||||
end
|
end
|
||||||
|
|
||||||
#-----------------------------------------------------------------------------
|
#-----------------------------------------------------------------------------
|
||||||
@@ -321,7 +347,7 @@ class Battle::AI::AIBattler
|
|||||||
# Other certain trapping effects
|
# Other certain trapping effects
|
||||||
return false if battler.trappedInBattle?
|
return false if battler.trappedInBattle?
|
||||||
# Trapping abilities/items
|
# Trapping abilities/items
|
||||||
ai.each_foe_battler(side) do |b, i|
|
@ai.each_foe_battler(side) do |b, i|
|
||||||
if b.ability_active? &&
|
if b.ability_active? &&
|
||||||
Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b.battler, @ai.battle)
|
Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b.battler, @ai.battle)
|
||||||
return false
|
return false
|
||||||
@@ -342,20 +368,21 @@ class Battle::AI::AIBattler
|
|||||||
case ability_id
|
case ability_id
|
||||||
when :GUTS
|
when :GUTS
|
||||||
return true if ![:SLEEP, :FROZEN].include?(new_status) &&
|
return true if ![:SLEEP, :FROZEN].include?(new_status) &&
|
||||||
stat_raise_worthwhile?(self, :ATTACK, true)
|
@ai.stat_raise_worthwhile?(self, :ATTACK, true)
|
||||||
when :MARVELSCALE
|
when :MARVELSCALE
|
||||||
return true if stat_raise_worthwhile?(self, :DEFENSE, true)
|
return true if @ai.stat_raise_worthwhile?(self, :DEFENSE, true)
|
||||||
when :QUICKFEET
|
when :QUICKFEET
|
||||||
return true if ![:SLEEP, :FROZEN].include?(new_status) &&
|
return true if ![:SLEEP, :FROZEN].include?(new_status) &&
|
||||||
stat_raise_worthwhile?(self, :SPEED, true)
|
@ai.stat_raise_worthwhile?(self, :SPEED, true)
|
||||||
when :FLAREBOOST
|
when :FLAREBOOST
|
||||||
return true if new_status == :BURN && stat_raise_worthwhile?(self, :SPECIAL_ATTACK, true)
|
return true if new_status == :BURN && @ai.stat_raise_worthwhile?(self, :SPECIAL_ATTACK, true)
|
||||||
when :TOXICBOOST
|
when :TOXICBOOST
|
||||||
return true if new_status == :POISON && stat_raise_worthwhile?(self, :ATTACK, true)
|
return true if new_status == :POISON && @ai.stat_raise_worthwhile?(self, :ATTACK, true)
|
||||||
when :POISONHEAL
|
when :POISONHEAL
|
||||||
return true if new_status == :POISON
|
return true if new_status == :POISON
|
||||||
when :MAGICGUARD # Want a harmless status problem to prevent getting a harmful one
|
when :MAGICGUARD # Want a harmless status problem to prevent getting a harmful one
|
||||||
return true if new_status == :POISON || (new_status == :BURN && !stat_raise_worthwhile?(self, :ATTACK, true))
|
return true if new_status == :POISON ||
|
||||||
|
(new_status == :BURN && !@ai.stat_raise_worthwhile?(self, :ATTACK, true))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return true if new_status == :SLEEP && check_for_move { |m| m.usableWhenAsleep? }
|
return true if new_status == :SLEEP && check_for_move { |m| m.usableWhenAsleep? }
|
||||||
@@ -924,12 +951,12 @@ class Battle::AI::AIBattler
|
|||||||
ret = 0 if gender == 2
|
ret = 0 if gender == 2
|
||||||
when :FRIENDGUARD, :HEALER, :SYMBOISIS, :TELEPATHY
|
when :FRIENDGUARD, :HEALER, :SYMBOISIS, :TELEPATHY
|
||||||
has_ally = false
|
has_ally = false
|
||||||
each_ally(@side) { |b, i| has_ally = true }
|
@ai.each_ally(@side) { |b, i| has_ally = true }
|
||||||
ret = 0 if !has_ally
|
ret = 0 if !has_ally
|
||||||
when :GALEWINGS
|
when :GALEWINGS
|
||||||
ret = 0 if !check_for_move { |m| m.type == :FLYING }
|
ret = 0 if !check_for_move { |m| m.type == :FLYING }
|
||||||
when :HUGEPOWER, :PUREPOWER
|
when :HUGEPOWER, :PUREPOWER
|
||||||
ret = 0 if !ai.stat_raise_worthwhile?(self, :ATTACK, true)
|
ret = 0 if !@ai.stat_raise_worthwhile?(self, :ATTACK, true)
|
||||||
when :IRONFIST
|
when :IRONFIST
|
||||||
ret = 0 if !check_for_move { |m| m.punchingMove? }
|
ret = 0 if !check_for_move { |m| m.punchingMove? }
|
||||||
when :LIQUIDVOICE
|
when :LIQUIDVOICE
|
||||||
@@ -953,7 +980,7 @@ class Battle::AI::AIBattler
|
|||||||
when :SKILLLINK
|
when :SKILLLINK
|
||||||
ret = 0 if !check_for_move { |m| m.is_a?(Battle::Move::HitTwoToFiveTimes) }
|
ret = 0 if !check_for_move { |m| m.is_a?(Battle::Move::HitTwoToFiveTimes) }
|
||||||
when :STEELWORKER
|
when :STEELWORKER
|
||||||
ret = 0 if !has_damaging_move_of_type?(:GRASS)
|
ret = 0 if !has_damaging_move_of_type?(:STEEL)
|
||||||
when :SWARM
|
when :SWARM
|
||||||
ret = 0 if !has_damaging_move_of_type?(:BUG)
|
ret = 0 if !has_damaging_move_of_type?(:BUG)
|
||||||
when :TORRENT
|
when :TORRENT
|
||||||
|
|||||||
@@ -149,17 +149,40 @@ class Battle::AI::AIMove
|
|||||||
((@ai.battle.pbCheckGlobalAbility(:DARKAURA) && calc_type == :DARK) ||
|
((@ai.battle.pbCheckGlobalAbility(:DARKAURA) && calc_type == :DARK) ||
|
||||||
(@ai.battle.pbCheckGlobalAbility(:FAIRYAURA) && calc_type == :FAIRY))
|
(@ai.battle.pbCheckGlobalAbility(:FAIRYAURA) && calc_type == :FAIRY))
|
||||||
if @ai.battle.pbCheckGlobalAbility(:AURABREAK)
|
if @ai.battle.pbCheckGlobalAbility(:AURABREAK)
|
||||||
multipliers[:power_multiplier] *= 2 / 3.0
|
multipliers[:power_multiplier] *= 3 / 4.0
|
||||||
else
|
else
|
||||||
multipliers[:power_multiplier] *= 4 / 3.0
|
multipliers[:power_multiplier] *= 4 / 3.0
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Ability effects that alter damage
|
# Ability effects that alter damage
|
||||||
if user.ability_active?
|
if user.ability_active?
|
||||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
case user.ability_id
|
||||||
# round.
|
when :AERILATE, :GALVANIZE, :PIXILATE, :REFRIGERATE
|
||||||
abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE]
|
multipliers[:power_multiplier] *= 1.2 if type == :NORMAL # NOTE: Not calc_type.
|
||||||
if !abilityBlacklist.include?(user.ability_id)
|
when :ANALYTIC
|
||||||
|
if rough_priority(user) <= 0
|
||||||
|
user_faster = false
|
||||||
|
@ai.each_battler do |b, i|
|
||||||
|
user_faster = (i != user.index && user.faster_than?(b))
|
||||||
|
break if user_faster
|
||||||
|
end
|
||||||
|
multipliers[:power_multiplier] *= 1.3 if !user_faster
|
||||||
|
end
|
||||||
|
when :NEUROFORCE
|
||||||
|
if Effectiveness.super_effective_type?(calc_type, *target.pbTypes(true))
|
||||||
|
multipliers[:final_damage_multiplier] *= 1.25
|
||||||
|
end
|
||||||
|
when :NORMALIZE
|
||||||
|
multipliers[:power_multiplier] *= 1.2 if Settings::MECHANICS_GENERATION >= 7
|
||||||
|
when :SNIPER
|
||||||
|
multipliers[:final_damage_multiplier] *= 1.5 if is_critical
|
||||||
|
when :STAKEOUT
|
||||||
|
# TODO: multipliers[:attack_multiplier] *= 2 if target switches out
|
||||||
|
when :TINTEDLENS
|
||||||
|
if Effectiveness.resistant_type?(calc_type, *target.pbTypes(true))
|
||||||
|
multipliers[:final_damage_multiplier] *= 2
|
||||||
|
end
|
||||||
|
else
|
||||||
Battle::AbilityEffects.triggerDamageCalcFromUser(
|
Battle::AbilityEffects.triggerDamageCalcFromUser(
|
||||||
user.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
user.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||||
)
|
)
|
||||||
@@ -173,10 +196,12 @@ class Battle::AI::AIMove
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
if target.ability_active?
|
if target.ability_active?
|
||||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
case target.ability_id
|
||||||
# round.
|
when :FILTER, :SOLIDROCK
|
||||||
abilityBlacklist = [:FILTER, :SOLIDROCK]
|
if Effectiveness.super_effective_type?(calc_type, *target.pbTypes(true))
|
||||||
if !abilityBlacklist.include?(target.ability_id)
|
multipliers[:final_damage_multiplier] *= 0.75
|
||||||
|
end
|
||||||
|
else
|
||||||
Battle::AbilityEffects.triggerDamageCalcFromTarget(
|
Battle::AbilityEffects.triggerDamageCalcFromTarget(
|
||||||
target.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
target.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||||
)
|
)
|
||||||
@@ -197,13 +222,15 @@ class Battle::AI::AIMove
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
# Item effects that alter damage
|
# Item effects that alter damage
|
||||||
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
|
|
||||||
# round.
|
|
||||||
if user.item_active?
|
if user.item_active?
|
||||||
# NOTE: These items aren't suitable for checking at the start of the
|
case user.item_id
|
||||||
# round.
|
when :EXPERTBELT
|
||||||
itemBlacklist = [:EXPERTBELT, :LIFEORB]
|
if Effectiveness.super_effective_type?(calc_type, *target.pbTypes(true))
|
||||||
if !itemBlacklist.include?(user.item_id)
|
multipliers[:final_damage_multiplier] *= 1.2
|
||||||
|
end
|
||||||
|
when :LIFEORB
|
||||||
|
multipliers[:final_damage_multiplier] *= 1.3
|
||||||
|
else
|
||||||
Battle::ItemEffects.triggerDamageCalcFromUser(
|
Battle::ItemEffects.triggerDamageCalcFromUser(
|
||||||
user.item, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
user.item, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||||
)
|
)
|
||||||
@@ -443,7 +470,9 @@ class Battle::AI::AIMove
|
|||||||
# Item effects that alter accuracy calculation
|
# Item effects that alter accuracy calculation
|
||||||
if user.item_active?
|
if user.item_active?
|
||||||
if user.item == :ZOOMLENS
|
if user.item == :ZOOMLENS
|
||||||
mods[:accuracy_multiplier] *= 1.2 if target.faster_than?(user)
|
if rough_priority(user) <= 0
|
||||||
|
mods[:accuracy_multiplier] *= 1.2 if target.faster_than?(user)
|
||||||
|
end
|
||||||
else
|
else
|
||||||
Battle::ItemEffects.triggerAccuracyCalcFromUser(
|
Battle::ItemEffects.triggerAccuracyCalcFromUser(
|
||||||
user.item, modifiers, user_battler, target_battler, @move, calc_type
|
user.item, modifiers, user_battler, target_battler, @move, calc_type
|
||||||
@@ -534,12 +563,12 @@ class Battle::AI::AIMove
|
|||||||
# 0: Regular additional effect chance or isn't an additional effect
|
# 0: Regular additional effect chance or isn't an additional effect
|
||||||
# -999: Additional effect will be negated
|
# -999: Additional effect will be negated
|
||||||
# Other: Amount to add to a move's score
|
# Other: Amount to add to a move's score
|
||||||
def get_score_change_for_additional_effect(user, target)
|
def get_score_change_for_additional_effect(user, target = nil)
|
||||||
# Doesn't have an additional effect
|
# Doesn't have an additional effect
|
||||||
return 0 if @move.addlEffect == 0
|
return 0 if @move.addlEffect == 0
|
||||||
# Additional effect will be negated
|
# Additional effect will be negated
|
||||||
return -999 if user.has_active_ability?(:SHEERFORCE)
|
return -999 if user.has_active_ability?(:SHEERFORCE)
|
||||||
return -999 if user.index != target.index &&
|
return -999 if target && user.index != target.index &&
|
||||||
target.has_active_ability?(:SHIELDDUST) && !@ai.battle.moldBreaker
|
target.has_active_ability?(:SHIELDDUST) && !@ai.battle.moldBreaker
|
||||||
# Prefer if the additional effect will have an increased chance of working
|
# Prefer if the additional effect will have an increased chance of working
|
||||||
return 5 if @move.addlEffect < 100 &&
|
return 5 if @move.addlEffect < 100 &&
|
||||||
|
|||||||
@@ -1,8 +1,3 @@
|
|||||||
#===============================================================================
|
|
||||||
#
|
|
||||||
#===============================================================================
|
|
||||||
# Struggle
|
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
@@ -574,9 +569,12 @@ Battle::AI::Handlers::MoveEffectScore.add("UserMakeSubstitute",
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveEffectScore.add("RemoveUserBindingAndEntryHazards",
|
Battle::AI::Handlers::MoveEffectScore.add("RemoveUserBindingAndEntryHazards",
|
||||||
proc { |score, move, user, ai, battle|
|
proc { |score, move, user, ai, battle|
|
||||||
add_effect = move.get_score_change_for_additional_effect(user, target)
|
# Score for raising user's Speed
|
||||||
next score if add_effect == -999 # Additional effect will be negated
|
if Settings::MECHANICS_GENERATION >= 8
|
||||||
score += add_effect
|
score = Battle::AI::Handlers.apply_move_effect_score("RaiseUserSpeed1",
|
||||||
|
score, move, user, ai, battle)
|
||||||
|
end
|
||||||
|
# Score for removing various effects
|
||||||
score += 10 if user.effects[PBEffects::Trapping] > 0
|
score += 10 if user.effects[PBEffects::Trapping] > 0
|
||||||
score += 15 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
|
||||||
|
|||||||
@@ -445,7 +445,7 @@ Battle::AI::Handlers::MoveEffectScore.add("EnsureNextCriticalHit",
|
|||||||
# critical hits are impossible (e.g. via Lucky Chant)
|
# critical hits are impossible (e.g. via Lucky Chant)
|
||||||
crit_stage = 0
|
crit_stage = 0
|
||||||
crit_stage = -1 if user.battler.pbOwnSide.effects[PBEffects::LuckyChant] > 0
|
crit_stage = -1 if user.battler.pbOwnSide.effects[PBEffects::LuckyChant] > 0
|
||||||
if crit_stage >= 0 && user.ability_active? && ![:MERCILESS].include?(user.ability)
|
if crit_stage >= 0 && user.ability_active? && ![:MERCILESS].include?(user.ability_id)
|
||||||
crit_stage = Battle::AbilityEffects.triggerCriticalCalcFromUser(user.battler.ability,
|
crit_stage = Battle::AbilityEffects.triggerCriticalCalcFromUser(user.battler.ability,
|
||||||
user.battler, user.battler, crit_stage)
|
user.battler, user.battler, crit_stage)
|
||||||
end
|
end
|
||||||
@@ -660,6 +660,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenPhysicalDamageAgainstUserS
|
|||||||
score += 10
|
score += 10
|
||||||
score += 8 if !b.check_for_move { |m| m.specialMove?(m.type) }
|
score += 8 if !b.check_for_move { |m| m.specialMove?(m.type) }
|
||||||
end
|
end
|
||||||
|
# Prefer if user has Light Clay
|
||||||
|
score += 5 if user.has_active_item?(:LIGHTCLAY)
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -688,6 +690,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenSpecialDamageAgainstUserSi
|
|||||||
score += 10
|
score += 10
|
||||||
score += 8 if !b.check_for_move { |m| m.physicalMove?(m.type) }
|
score += 8 if !b.check_for_move { |m| m.physicalMove?(m.type) }
|
||||||
end
|
end
|
||||||
|
# Prefer if user has Light Clay
|
||||||
|
score += 5 if user.has_active_item?(:LIGHTCLAY)
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -713,6 +717,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenDamageAgainstUserSideIfHai
|
|||||||
score -= (20 * (0.75 - (user.hp.to_f / user.totalhp))).to_i # -5 to -15
|
score -= (20 * (0.75 - (user.hp.to_f / user.totalhp))).to_i # -5 to -15
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
# Prefer if user has Light Clay
|
||||||
|
score += 5 if user.has_active_item?(:LIGHTCLAY)
|
||||||
next score + 15
|
next score + 15
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -883,7 +889,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesKingsShie
|
|||||||
# less likely to work
|
# less likely to work
|
||||||
score -= (user.effects[PBEffects::ProtectRate] - 1) * ((Settings::MECHANICS_GENERATION >= 6) ? 15 : 10)
|
score -= (user.effects[PBEffects::ProtectRate] - 1) * ((Settings::MECHANICS_GENERATION >= 6) ? 15 : 10)
|
||||||
# Aegislash
|
# Aegislash
|
||||||
score += 10 if user.battler.isSpecies?(:AEGISLASH) && user.form == 1 &&
|
score += 10 if user.battler.isSpecies?(:AEGISLASH) && user.battler.form == 1 &&
|
||||||
user.ability == :STANCECHANGE
|
user.ability == :STANCECHANGE
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
@@ -1533,7 +1539,7 @@ Battle::AI::Handlers::MoveEffectScore.add("NormalMovesBecomeElectric",
|
|||||||
normal_type_better = 0
|
normal_type_better = 0
|
||||||
electric_type_better = 0
|
electric_type_better = 0
|
||||||
ai.each_foe_battler(user.side) do |b, i|
|
ai.each_foe_battler(user.side) do |b, i|
|
||||||
next if move.pbPriority(b.battler) <= 0 && b.faster_than?(user)
|
next if move.rough_priority(b) <= 0 && b.faster_than?(user)
|
||||||
next if !b.has_damaging_move_of_type?(:NORMAL)
|
next if !b.has_damaging_move_of_type?(:NORMAL)
|
||||||
# Normal's effectiveness
|
# Normal's effectiveness
|
||||||
eff = user.effectiveness_of_type_against_battler(:NORMAL, b)
|
eff = user.effectiveness_of_type_against_battler(:NORMAL, b)
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("UserTargetSwapItems",
|
|||||||
score += target_old_item_preference - target_new_item_preference
|
score += target_old_item_preference - target_new_item_preference
|
||||||
# Don't prefer if user used this move in the last round
|
# Don't prefer if user used this move in the last round
|
||||||
score -= 15 if user.battler.lastMoveUsed &&
|
score -= 15 if user.battler.lastMoveUsed &&
|
||||||
|
GameData::Move.exists?(user.battler.lastMoveUsed) &&
|
||||||
GameData::Move.get(user.battler.lastMoveUsed).function_code == move.function
|
GameData::Move.get(user.battler.lastMoveUsed).function_code == move.function
|
||||||
next score
|
next score
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -265,7 +265,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CounterPhysicalDamage",
|
|||||||
score += 5 if b.rough_stat(:ATTACK) > b.rough_stat(:SPECIAL_ATTACK)
|
score += 5 if b.rough_stat(:ATTACK) > b.rough_stat(:SPECIAL_ATTACK)
|
||||||
# Prefer if the last move the foe used was physical
|
# Prefer if the last move the foe used was physical
|
||||||
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
||||||
score += 8 if GameData::Move.get(b.battler.lastMoveUsed).physical?
|
score += 8 if GameData::Move.try_get(b.battler.lastMoveUsed)&.physical?
|
||||||
end
|
end
|
||||||
# Prefer if the foe is taunted into using a damaging move
|
# Prefer if the foe is taunted into using a damaging move
|
||||||
score += 5 if b.effects[PBEffects::Taunt] > 0
|
score += 5 if b.effects[PBEffects::Taunt] > 0
|
||||||
@@ -297,7 +297,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CounterSpecialDamage",
|
|||||||
score += 5 if b.rough_stat(:SPECIAL_ATTACK) > b.rough_stat(:ATTACK)
|
score += 5 if b.rough_stat(:SPECIAL_ATTACK) > b.rough_stat(:ATTACK)
|
||||||
# Prefer if the last move the foe used was special
|
# Prefer if the last move the foe used was special
|
||||||
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
||||||
score += 8 if GameData::Move.get(b.battler.lastMoveUsed).special?
|
score += 8 if GameData::Move.try_get(b.battler.lastMoveUsed)&.special?
|
||||||
end
|
end
|
||||||
# Prefer if the foe is taunted into using a damaging move
|
# Prefer if the foe is taunted into using a damaging move
|
||||||
score += 5 if b.effects[PBEffects::Taunt] > 0
|
score += 5 if b.effects[PBEffects::Taunt] > 0
|
||||||
@@ -327,7 +327,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CounterDamagePlusHalf",
|
|||||||
has_damaging_move = true
|
has_damaging_move = true
|
||||||
# Prefer if the last move the foe used was damaging
|
# Prefer if the last move the foe used was damaging
|
||||||
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
if ai.trainer.medium_skill? && b.battler.lastMoveUsed
|
||||||
score += 8 if GameData::Move.get(b.battler.lastMoveUsed).damaging?
|
score += 8 if GameData::Move.try_get(b.battler.lastMoveUsed)&.damaging?
|
||||||
end
|
end
|
||||||
# Prefer if the foe is taunted into using a damaging move
|
# Prefer if the foe is taunted into using a damaging move
|
||||||
score += 5 if b.effects[PBEffects::Taunt] > 0
|
score += 5 if b.effects[PBEffects::Taunt] > 0
|
||||||
@@ -454,7 +454,7 @@ Battle::AI::Handlers::MoveEffectScore.add("WaterPledge",
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsed",
|
Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsed",
|
||||||
proc { |move, user, ai, battle|
|
proc { |move, user, ai, battle|
|
||||||
next true if !battle.lastMoveUsed
|
next true if !battle.lastMoveUsed || !GameData::Move.exists?(battle.lastMoveUsed)
|
||||||
next move.move.moveBlacklist.include?(GameData::Move.get(battle.lastMoveUsed).function_code)
|
next move.move.moveBlacklist.include?(GameData::Move.get(battle.lastMoveUsed).function_code)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -468,6 +468,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsed",
|
|||||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("UseLastMoveUsedByTarget",
|
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("UseLastMoveUsedByTarget",
|
||||||
proc { |move, user, target, ai, battle|
|
proc { |move, user, target, ai, battle|
|
||||||
next true if !target.battler.lastRegularMoveUsed
|
next true if !target.battler.lastRegularMoveUsed
|
||||||
|
next true if !GameData::Move.exists?(target.battler.lastRegularMoveUsed)
|
||||||
next !GameData::Move.get(target.battler.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
next !GameData::Move.get(target.battler.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -434,8 +434,8 @@ Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetActsNext",
|
|||||||
#===============================================================================
|
#===============================================================================
|
||||||
#
|
#
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
Battle::AI::Handlers::MoveEffectScore.add("TargetActsLast",
|
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetActsLast",
|
||||||
proc { |score, move, user, ai, battle|
|
proc { |score, move, user, target, ai, battle|
|
||||||
# Useless if the target is an ally
|
# Useless if the target is an ally
|
||||||
next Battle::AI::MOVE_USELESS_SCORE if !target.opposes?(user)
|
next Battle::AI::MOVE_USELESS_SCORE if !target.opposes?(user)
|
||||||
# Useless if the user has no ally (the point of this move is to let the ally
|
# Useless if the user has no ally (the point of this move is to let the ally
|
||||||
@@ -643,6 +643,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingDiffe
|
|||||||
proc { |move, user, target, ai, battle|
|
proc { |move, user, target, ai, battle|
|
||||||
next true if target.effects[PBEffects::Encore] > 0
|
next true if target.effects[PBEffects::Encore] > 0
|
||||||
next true if !target.battler.lastRegularMoveUsed ||
|
next true if !target.battler.lastRegularMoveUsed ||
|
||||||
|
!GameData::Move.exists?(target.battler.lastRegularMoveUsed) ||
|
||||||
move.move.moveBlacklist.include?(GameData::Move.get(target.battler.lastRegularMoveUsed).function_code)
|
move.move.moveBlacklist.include?(GameData::Move.get(target.battler.lastRegularMoveUsed).function_code)
|
||||||
next true if target.effects[PBEffects::ShellTrap]
|
next true if target.effects[PBEffects::ShellTrap]
|
||||||
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
next true if move.move.pbMoveFailedAromaVeil?(user.battler, target.battler, false)
|
||||||
|
|||||||
@@ -1200,7 +1200,7 @@ Battle::AbilityEffects::DamageCalcFromUser.add(:AERILATE,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Battle::AbilityEffects::DamageCalcFromUser.copy(:AERILATE, :PIXILATE, :REFRIGERATE, :GALVANIZE, :NORMALIZE)
|
Battle::AbilityEffects::DamageCalcFromUser.copy(:AERILATE, :GALVANIZE, :NORMALIZE, :PIXILATE, :REFRIGERATE)
|
||||||
|
|
||||||
Battle::AbilityEffects::DamageCalcFromUser.add(:ANALYTIC,
|
Battle::AbilityEffects::DamageCalcFromUser.add(:ANALYTIC,
|
||||||
proc { |ability, user, target, move, mults, power, type|
|
proc { |ability, user, target, move, mults, power, type|
|
||||||
@@ -1372,6 +1372,12 @@ Battle::AbilityEffects::DamageCalcFromUser.add(:SLOWSTART,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::AbilityEffects::DamageCalcFromUser.add(:SNIPER,
|
||||||
|
proc { |ability, user, target, move, mults, power, type|
|
||||||
|
mults[:final_damage_multiplier] *= 1.5 if target.damageState.critical
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::AbilityEffects::DamageCalcFromUser.add(:SOLARPOWER,
|
Battle::AbilityEffects::DamageCalcFromUser.add(:SOLARPOWER,
|
||||||
proc { |ability, user, target, move, mults, power, type|
|
proc { |ability, user, target, move, mults, power, type|
|
||||||
if move.specialMove? && [:Sun, :HarshSun].include?(user.effectiveWeather)
|
if move.specialMove? && [:Sun, :HarshSun].include?(user.effectiveWeather)
|
||||||
@@ -1380,12 +1386,6 @@ Battle::AbilityEffects::DamageCalcFromUser.add(:SOLARPOWER,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Battle::AbilityEffects::DamageCalcFromUser.add(:SNIPER,
|
|
||||||
proc { |ability, user, target, move, mults, power, type|
|
|
||||||
mults[:final_damage_multiplier] *= 1.5 if target.damageState.critical
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
Battle::AbilityEffects::DamageCalcFromUser.add(:STAKEOUT,
|
Battle::AbilityEffects::DamageCalcFromUser.add(:STAKEOUT,
|
||||||
proc { |ability, user, target, move, mults, power, type|
|
proc { |ability, user, target, move, mults, power, type|
|
||||||
mults[:attack_multiplier] *= 2 if target.battle.choices[target.index][0] == :SwitchOut
|
mults[:attack_multiplier] *= 2 if target.battle.choices[target.index][0] == :SwitchOut
|
||||||
@@ -1420,7 +1420,7 @@ Battle::AbilityEffects::DamageCalcFromUser.add(:SWARM,
|
|||||||
|
|
||||||
Battle::AbilityEffects::DamageCalcFromUser.add(:TECHNICIAN,
|
Battle::AbilityEffects::DamageCalcFromUser.add(:TECHNICIAN,
|
||||||
proc { |ability, user, target, move, mults, power, type|
|
proc { |ability, user, target, move, mults, power, type|
|
||||||
if user.index != target.index && move && move.id != :STRUGGLE &&
|
if user.index != target.index && move && move.function != "Struggle" &&
|
||||||
power * mults[:power_multiplier] <= 60
|
power * mults[:power_multiplier] <= 60
|
||||||
mults[:power_multiplier] *= 1.5
|
mults[:power_multiplier] *= 1.5
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -224,6 +224,12 @@ Battle::ItemEffects::SpeedCalc.add(:CHOICESCARF,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Battle::ItemEffects::SpeedCalc.add(:IRONBALL,
|
||||||
|
proc { |item, battler, mult|
|
||||||
|
next mult / 2
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
Battle::ItemEffects::SpeedCalc.add(:MACHOBRACE,
|
Battle::ItemEffects::SpeedCalc.add(:MACHOBRACE,
|
||||||
proc { |item, battler, mult|
|
proc { |item, battler, mult|
|
||||||
next mult / 2
|
next mult / 2
|
||||||
@@ -241,12 +247,6 @@ Battle::ItemEffects::SpeedCalc.add(:QUICKPOWDER,
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Battle::ItemEffects::SpeedCalc.add(:IRONBALL,
|
|
||||||
proc { |item, battler, mult|
|
|
||||||
next mult / 2
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
# WeightCalc handlers
|
# WeightCalc handlers
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ ItemHandlers::CanUseInBattle.add(:FULLRESTORE, proc { |item, pokemon, battler, m
|
|||||||
})
|
})
|
||||||
|
|
||||||
ItemHandlers::CanUseInBattle.add(:REVIVE, proc { |item, pokemon, battler, move, firstAction, battle, scene, showMessages|
|
ItemHandlers::CanUseInBattle.add(:REVIVE, proc { |item, pokemon, battler, move, firstAction, battle, scene, showMessages|
|
||||||
if pokemon.able? || pokemon.egg?
|
if !pokemon.fainted?
|
||||||
scene.pbDisplay(_INTL("It won't have any effect.")) if showMessages
|
scene.pbDisplay(_INTL("It won't have any effect.")) if showMessages
|
||||||
next false
|
next false
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def pbRandomMove
|
|||||||
loop do
|
loop do
|
||||||
move_id = keys.sample
|
move_id = keys.sample
|
||||||
move = GameData::Move.get(move_id)
|
move = GameData::Move.get(move_id)
|
||||||
next if move.id == :SKETCH || move.id == :STRUGGLE
|
next if ["Struggle", "ReplaceMoveWithTargetLastMoveUsed"].include?(move.function_code)
|
||||||
return move.id
|
return move.id
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
def debug_set_up_trainer
|
def debug_set_up_trainer
|
||||||
# Values to return
|
# Values to return
|
||||||
trainer_array = []
|
trainer_array = []
|
||||||
foe_items = [] # Intentionally left blank (for now)
|
foe_items = [] # Items can't be used except in internal battles
|
||||||
pokemon_array = []
|
pokemon_array = []
|
||||||
party_starts = [0]
|
party_starts = [0]
|
||||||
|
|
||||||
@@ -25,6 +25,10 @@ def debug_set_up_trainer
|
|||||||
trainer = NPCTrainer.new(trainer_name, trainer_type)
|
trainer = NPCTrainer.new(trainer_name, trainer_type)
|
||||||
trainer.id = $player.make_foreign_ID
|
trainer.id = $player.make_foreign_ID
|
||||||
trainer.lose_text = "I lost."
|
trainer.lose_text = "I lost."
|
||||||
|
# [:MAXPOTION, :FULLHEAL, :MAXREVIVE, :REVIVE].each do |item|
|
||||||
|
# trainer.items.push(item)
|
||||||
|
# end
|
||||||
|
# foe_items.push(trainer.items)
|
||||||
trainer_array.push(trainer)
|
trainer_array.push(trainer)
|
||||||
|
|
||||||
# Generate party
|
# Generate party
|
||||||
@@ -46,11 +50,13 @@ def debug_set_up_trainer
|
|||||||
return trainer_array, foe_items, pokemon_array, party_starts
|
return trainer_array, foe_items, pokemon_array, party_starts
|
||||||
end
|
end
|
||||||
|
|
||||||
def debug_test_auto_battle(logging = false)
|
def debug_test_auto_battle(logging = false, console_messages = true)
|
||||||
old_internal = $INTERNAL
|
old_internal = $INTERNAL
|
||||||
$INTERNAL = logging
|
$INTERNAL = logging
|
||||||
echoln "Start of testing auto-battle."
|
if console_messages
|
||||||
echoln "" if !$INTERNAL
|
echoln "Start of testing auto-battle."
|
||||||
|
echoln "" if !$INTERNAL
|
||||||
|
end
|
||||||
PBDebug.log("")
|
PBDebug.log("")
|
||||||
PBDebug.log("================================================================")
|
PBDebug.log("================================================================")
|
||||||
PBDebug.log("")
|
PBDebug.log("")
|
||||||
@@ -75,11 +81,13 @@ def debug_test_auto_battle(logging = false)
|
|||||||
($INTERNAL) ? PBDebug.log(moves_msg) : echoln(moves_msg)
|
($INTERNAL) ? PBDebug.log(moves_msg) : echoln(moves_msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
echo_participant.call(player_trainers[0], player_party, 1)
|
echo_participant.call(player_trainers[0], player_party, 1) if console_messages
|
||||||
PBDebug.log("")
|
PBDebug.log("")
|
||||||
echoln "" if !$INTERNAL
|
if console_messages
|
||||||
echo_participant.call(foe_trainers[0], foe_party, 2)
|
echoln "" if !$INTERNAL
|
||||||
echoln "" if !$INTERNAL
|
echo_participant.call(foe_trainers[0], foe_party, 2)
|
||||||
|
echoln "" if !$INTERNAL
|
||||||
|
end
|
||||||
# Create the battle scene (the visual side of it)
|
# Create the battle scene (the visual side of it)
|
||||||
scene = Battle::DebugSceneNoVisuals.new(logging)
|
scene = Battle::DebugSceneNoVisuals.new(logging)
|
||||||
# Create the battle class (the mechanics side of it)
|
# Create the battle class (the mechanics side of it)
|
||||||
@@ -97,14 +105,16 @@ def debug_test_auto_battle(logging = false)
|
|||||||
# Perform the battle itself
|
# Perform the battle itself
|
||||||
outcome = battle.pbStartBattle
|
outcome = battle.pbStartBattle
|
||||||
# End
|
# End
|
||||||
text = ["Undecided",
|
if console_messages
|
||||||
"Trainer 1 #{player_trainers[0].name} won",
|
text = ["Undecided",
|
||||||
"Trainer 2 #{foe_trainers[0].name} won",
|
"Trainer 1 #{player_trainers[0].name} won",
|
||||||
"Ran/forfeited",
|
"Trainer 2 #{foe_trainers[0].name} won",
|
||||||
"Wild Pokémon caught",
|
"Ran/forfeited",
|
||||||
"Draw"][outcome]
|
"Wild Pokémon caught",
|
||||||
echoln sprintf("%s after %d rounds", text, battle.turnCount + 1)
|
"Draw"][outcome]
|
||||||
echoln ""
|
echoln sprintf("%s after %d rounds", text, battle.turnCount + 1)
|
||||||
|
echoln ""
|
||||||
|
end
|
||||||
$INTERNAL = old_internal
|
$INTERNAL = old_internal
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -112,9 +122,9 @@ end
|
|||||||
# Add to Debug menu.
|
# Add to Debug menu.
|
||||||
#===============================================================================
|
#===============================================================================
|
||||||
MenuHandlers.add(:debug_menu, :test_auto_battle, {
|
MenuHandlers.add(:debug_menu, :test_auto_battle, {
|
||||||
"name" => _INTL("Test Auto Battle"),
|
"name" => "Test Auto Battle",
|
||||||
"parent" => :main,
|
"parent" => :main,
|
||||||
"description" => _INTL("Runs an AI-controlled battle with no visuals."),
|
"description" => "Runs an AI-controlled battle with no visuals.",
|
||||||
"always_show" => false,
|
"always_show" => false,
|
||||||
"effect" => proc {
|
"effect" => proc {
|
||||||
debug_test_auto_battle
|
debug_test_auto_battle
|
||||||
@@ -122,12 +132,27 @@ MenuHandlers.add(:debug_menu, :test_auto_battle, {
|
|||||||
})
|
})
|
||||||
|
|
||||||
MenuHandlers.add(:debug_menu, :test_auto_battle_logging, {
|
MenuHandlers.add(:debug_menu, :test_auto_battle_logging, {
|
||||||
"name" => _INTL("Test Auto Battle with Logging"),
|
"name" => "Test Auto Battle with Logging",
|
||||||
"parent" => :main,
|
"parent" => :main,
|
||||||
"description" => _INTL("Runs an AI-controlled battle with no visuals. Logs messages."),
|
"description" => "Runs an AI-controlled battle with no visuals. Logs messages.",
|
||||||
"always_show" => false,
|
"always_show" => false,
|
||||||
"effect" => proc {
|
"effect" => proc {
|
||||||
debug_test_auto_battle(true)
|
debug_test_auto_battle(true)
|
||||||
pbMessage(_INTL("Battle transcript was logged in Data/debuglog.txt."))
|
pbMessage("Battle transcript was logged in Data/debuglog.txt.")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
MenuHandlers.add(:debug_menu, :bulk_test_auto_battle, {
|
||||||
|
"name" => "Bulk Test Auto Battle",
|
||||||
|
"parent" => :main,
|
||||||
|
"description" => "Runs 50 AI-controlled battles with no visuals.",
|
||||||
|
"always_show" => false,
|
||||||
|
"effect" => proc {
|
||||||
|
echoln "Running 50 battles.."
|
||||||
|
50.times do |i|
|
||||||
|
echoln "#{i + 1}..."
|
||||||
|
debug_test_auto_battle(false, false)
|
||||||
|
end
|
||||||
|
echoln "Done!"
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -3634,18 +3634,6 @@ FunctionCode = None
|
|||||||
Flags = Contact,CanProtect,CanMirrorMove
|
Flags = Contact,CanProtect,CanMirrorMove
|
||||||
Description = The target is cut with a scythe or a claw. It can also be used to cut down thin trees.
|
Description = The target is cut with a scythe or a claw. It can also be used to cut down thin trees.
|
||||||
#-------------------------------
|
#-------------------------------
|
||||||
[STRUGGLE]
|
|
||||||
Name = Struggle
|
|
||||||
Type = NORMAL
|
|
||||||
Category = Physical
|
|
||||||
Power = 50
|
|
||||||
Accuracy = 0
|
|
||||||
TotalPP = 1
|
|
||||||
Target = RandomNearFoe
|
|
||||||
FunctionCode = Struggle
|
|
||||||
Flags = Contact,CanProtect
|
|
||||||
Description = An attack that is used in desperation only if the user has no PP. It also hurts the user slightly.
|
|
||||||
#-------------------------------
|
|
||||||
[TACKLE]
|
[TACKLE]
|
||||||
Name = Tackle
|
Name = Tackle
|
||||||
Type = NORMAL
|
Type = NORMAL
|
||||||
|
|||||||
@@ -4230,18 +4230,6 @@ Flags = CanProtect,CanMirrorMove,Sound
|
|||||||
EffectChance = 30
|
EffectChance = 30
|
||||||
Description = An attack that can be used only if the user is asleep. The harsh noise may also make the target flinch.
|
Description = An attack that can be used only if the user is asleep. The harsh noise may also make the target flinch.
|
||||||
#-------------------------------
|
#-------------------------------
|
||||||
[STRUGGLE]
|
|
||||||
Name = Struggle
|
|
||||||
Type = NORMAL
|
|
||||||
Category = Physical
|
|
||||||
Power = 50
|
|
||||||
Accuracy = 0
|
|
||||||
TotalPP = 1
|
|
||||||
Target = RandomNearFoe
|
|
||||||
FunctionCode = Struggle
|
|
||||||
Flags = Contact,CanProtect
|
|
||||||
Description = An attack that is used in desperation only if the user has no PP. It also hurts the user slightly.
|
|
||||||
#-------------------------------
|
|
||||||
[WEATHERBALL]
|
[WEATHERBALL]
|
||||||
Name = Weather Ball
|
Name = Weather Ball
|
||||||
Type = NORMAL
|
Type = NORMAL
|
||||||
|
|||||||
@@ -4654,18 +4654,6 @@ Flags = CanProtect,CanMirrorMove,Sound
|
|||||||
EffectChance = 30
|
EffectChance = 30
|
||||||
Description = An attack that can be used only if the user is asleep. The harsh noise may also make the target flinch.
|
Description = An attack that can be used only if the user is asleep. The harsh noise may also make the target flinch.
|
||||||
#-------------------------------
|
#-------------------------------
|
||||||
[STRUGGLE]
|
|
||||||
Name = Struggle
|
|
||||||
Type = NORMAL
|
|
||||||
Category = Physical
|
|
||||||
Power = 50
|
|
||||||
Accuracy = 0
|
|
||||||
TotalPP = 1
|
|
||||||
Target = RandomNearFoe
|
|
||||||
FunctionCode = Struggle
|
|
||||||
Flags = Contact,CanProtect
|
|
||||||
Description = An attack that is used in desperation only if the user has no PP. It also hurts the user slightly.
|
|
||||||
#-------------------------------
|
|
||||||
[WEATHERBALL]
|
[WEATHERBALL]
|
||||||
Name = Weather Ball
|
Name = Weather Ball
|
||||||
Type = NORMAL
|
Type = NORMAL
|
||||||
|
|||||||
@@ -5200,18 +5200,6 @@ Flags = CanProtect,CanMirrorMove,Sound
|
|||||||
EffectChance = 30
|
EffectChance = 30
|
||||||
Description = An attack that can be used only if the user is asleep. The harsh noise may also make the target flinch.
|
Description = An attack that can be used only if the user is asleep. The harsh noise may also make the target flinch.
|
||||||
#-------------------------------
|
#-------------------------------
|
||||||
[STRUGGLE]
|
|
||||||
Name = Struggle
|
|
||||||
Type = NORMAL
|
|
||||||
Category = Physical
|
|
||||||
Power = 50
|
|
||||||
Accuracy = 0
|
|
||||||
TotalPP = 1
|
|
||||||
Target = RandomNearFoe
|
|
||||||
FunctionCode = Struggle
|
|
||||||
Flags = Contact,CanProtect
|
|
||||||
Description = An attack that is used in desperation only if the user has no PP. It also hurts the user slightly.
|
|
||||||
#-------------------------------
|
|
||||||
[TERRAINPULSE]
|
[TERRAINPULSE]
|
||||||
Name = Terrain Pulse
|
Name = Terrain Pulse
|
||||||
Type = NORMAL
|
Type = NORMAL
|
||||||
|
|||||||
@@ -5200,18 +5200,6 @@ Flags = CanProtect,CanMirrorMove,Sound
|
|||||||
EffectChance = 30
|
EffectChance = 30
|
||||||
Description = An attack that can be used only if the user is asleep. The harsh noise may also make the target flinch.
|
Description = An attack that can be used only if the user is asleep. The harsh noise may also make the target flinch.
|
||||||
#-------------------------------
|
#-------------------------------
|
||||||
[STRUGGLE]
|
|
||||||
Name = Struggle
|
|
||||||
Type = NORMAL
|
|
||||||
Category = Physical
|
|
||||||
Power = 50
|
|
||||||
Accuracy = 0
|
|
||||||
TotalPP = 1
|
|
||||||
Target = RandomNearFoe
|
|
||||||
FunctionCode = Struggle
|
|
||||||
Flags = Contact,CanProtect
|
|
||||||
Description = An attack that is used in desperation only if the user has no PP. It also hurts the user slightly.
|
|
||||||
#-------------------------------
|
|
||||||
[TERRAINPULSE]
|
[TERRAINPULSE]
|
||||||
Name = Terrain Pulse
|
Name = Terrain Pulse
|
||||||
Type = NORMAL
|
Type = NORMAL
|
||||||
|
|||||||
Reference in New Issue
Block a user