mirror of
https://github.com/infinitefusion/infinitefusion-e18.git
synced 2025-12-07 13:15:01 +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
|
||||
@runCommand = 0
|
||||
@nextPickupUse = 0
|
||||
if GameData::Move.exists?(:STRUGGLE)
|
||||
@struggle = Move.from_pokemon_move(self, Pokemon::Move.new(:STRUGGLE))
|
||||
else
|
||||
@struggle = Move::Struggle.new(self, nil)
|
||||
end
|
||||
@struggle = Move::Struggle.new(self, nil)
|
||||
@mega_rings = []
|
||||
GameData::Item.each { |item| @mega_rings.push(item.id) if item.has_flag?("MegaRing") }
|
||||
@battleAI = AI.new(self)
|
||||
|
||||
@@ -171,7 +171,7 @@ class Battle::Battler
|
||||
return true
|
||||
end
|
||||
|
||||
def pbCanSynchronizeStatus?(newStatus, target)
|
||||
def pbCanSynchronizeStatus?(newStatus, user)
|
||||
return false if fainted?
|
||||
# Trying to replace a status problem with another one
|
||||
return false if self.status != :NONE
|
||||
@@ -181,8 +181,8 @@ class Battle::Battler
|
||||
hasImmuneType = false
|
||||
case newStatus
|
||||
when :POISON
|
||||
# NOTE: target will have Synchronize, so it can't have Corrosion.
|
||||
if !(target && target.hasActiveAbility?(:CORROSION))
|
||||
# NOTE: user will have Synchronize, so it can't have Corrosion.
|
||||
if !(user && user.hasActiveAbility?(:CORROSION))
|
||||
hasImmuneType |= pbHasType?(:POISON)
|
||||
hasImmuneType |= pbHasType?(:STEEL)
|
||||
end
|
||||
@@ -205,6 +205,7 @@ class Battle::Battler
|
||||
return false
|
||||
end
|
||||
# Safeguard immunity
|
||||
# NOTE: user will have Synchronize, so it can't have Infiltrator.
|
||||
if pbOwnSide.effects[PBEffects::Safeguard] > 0 &&
|
||||
!(user && user.hasActiveAbility?(:INFILTRATOR))
|
||||
return false
|
||||
|
||||
@@ -43,19 +43,21 @@ class Battle::Battler
|
||||
# Choice Band/Gorilla Tactics
|
||||
@effects[PBEffects::ChoiceBand] = nil if !pbHasMove?(@effects[PBEffects::ChoiceBand])
|
||||
if @effects[PBEffects::ChoiceBand] && move.id != @effects[PBEffects::ChoiceBand]
|
||||
choiced_move_name = GameData::Move.get(@effects[PBEffects::ChoiceBand]).name
|
||||
if hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
|
||||
if showMessages
|
||||
msg = _INTL("The {1} only allows the use of {2}!", itemName, choiced_move_name)
|
||||
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
|
||||
choiced_move = GameData::Move.try_get(@effects[PBEffects::ChoiceBand])
|
||||
if choiced_move
|
||||
if hasActiveItem?([:CHOICEBAND, :CHOICESPECS, :CHOICESCARF])
|
||||
if showMessages
|
||||
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
|
||||
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
|
||||
# Taunt
|
||||
@@ -85,7 +87,7 @@ class Battle::Battler
|
||||
end
|
||||
# Assault Vest (prevents choosing status moves but doesn't prevent
|
||||
# executing them)
|
||||
if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && move.id != :MEFIRST && commandPhase
|
||||
if hasActiveItem?(:ASSAULTVEST) && move.statusMove? && move.function != "UseMoveTargetIsAboutToUse" && commandPhase
|
||||
if showMessages
|
||||
msg = _INTL("The effects of the {1} prevent status moves from being used!", itemName)
|
||||
(commandPhase) ? @battle.pbDisplayPaused(msg) : @battle.pbDisplay(msg)
|
||||
|
||||
@@ -43,11 +43,10 @@ class Battle::Move
|
||||
@category = move.category
|
||||
@accuracy = move.accuracy
|
||||
@pp = move.pp # Can be changed with Mimic/Transform
|
||||
@addlEffect = move.effect_chance
|
||||
@target = move.target
|
||||
@priority = move.priority
|
||||
@flags = move.flags.clone
|
||||
@calcType = nil
|
||||
@addlEffect = move.effect_chance
|
||||
@powerBoost = false # For Aerilate, Pixilate, Refrigerate, Galvanize
|
||||
@snatched = false
|
||||
end
|
||||
|
||||
@@ -285,7 +285,7 @@ class Battle::Move
|
||||
if (@battle.pbCheckGlobalAbility(:DARKAURA) && type == :DARK) ||
|
||||
(@battle.pbCheckGlobalAbility(:FAIRYAURA) && type == :FAIRY)
|
||||
if @battle.pbCheckGlobalAbility(:AURABREAK)
|
||||
multipliers[:power_multiplier] *= 2 / 3.0
|
||||
multipliers[:power_multiplier] *= 3 / 4.0
|
||||
else
|
||||
multipliers[:power_multiplier] *= 4 / 3.0
|
||||
end
|
||||
|
||||
@@ -36,7 +36,6 @@ class Battle::Move::Confusion < Battle::Move
|
||||
@priority = 0
|
||||
@flags = []
|
||||
@addlEffect = 0
|
||||
@calcType = nil
|
||||
@powerBoost = false
|
||||
@snatched = false
|
||||
end
|
||||
@@ -53,19 +52,18 @@ class Battle::Move::Struggle < Battle::Move
|
||||
def initialize(battle, move)
|
||||
@battle = battle
|
||||
@realMove = nil # Not associated with a move
|
||||
@id = (move) ? move.id : :STRUGGLE
|
||||
@name = (move) ? move.name : _INTL("Struggle")
|
||||
@id = :STRUGGLE
|
||||
@name = _INTL("Struggle")
|
||||
@function = "Struggle"
|
||||
@power = 50
|
||||
@type = nil
|
||||
@category = 0
|
||||
@accuracy = 0
|
||||
@pp = -1
|
||||
@target = :NearOther
|
||||
@target = :RandomNearFoe
|
||||
@priority = 0
|
||||
@flags = ["Contact", "CanProtect"]
|
||||
@addlEffect = 0
|
||||
@calcType = nil
|
||||
@powerBoost = false
|
||||
@snatched = false
|
||||
end
|
||||
|
||||
@@ -1372,7 +1372,7 @@ class Battle::Move::LowerPoisonedTargetAtkSpAtkSpd1 < Battle::Move
|
||||
next if !b.poisoned?
|
||||
failed = true
|
||||
(@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
|
||||
break
|
||||
end
|
||||
|
||||
@@ -768,6 +768,7 @@ class Battle::Move::UseLastMoveUsed < Battle::Move
|
||||
|
||||
def pbMoveFailed?(user, targets)
|
||||
if !@copied_move ||
|
||||
!GameData::Move.exists?(@copied_move) ||
|
||||
@moveBlacklist.include?(GameData::Move.get(@copied_move).function_code)
|
||||
@battle.pbDisplay(_INTL("But it failed!"))
|
||||
return true
|
||||
@@ -789,6 +790,7 @@ class Battle::Move::UseLastMoveUsedByTarget < Battle::Move
|
||||
|
||||
def pbFailsAgainstTarget?(user, target, show_message)
|
||||
if !target.lastRegularMoveUsed ||
|
||||
!GameData::Move.exists?(target.lastRegularMoveUsed) ||
|
||||
!GameData::Move.get(target.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
||||
@battle.pbDisplay(_INTL("The mirror move failed!")) if show_message
|
||||
return true
|
||||
|
||||
@@ -606,7 +606,8 @@ class Battle::Move::TargetUsesItsLastUsedMoveAgain < Battle::Move
|
||||
end
|
||||
|
||||
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
|
||||
return true
|
||||
end
|
||||
@@ -814,6 +815,7 @@ class Battle::Move::DisableTargetUsingDifferentMove < Battle::Move
|
||||
return true
|
||||
end
|
||||
if !target.lastRegularMoveUsed ||
|
||||
!GameData::Move.exists?(target.lastRegularMoveUsed) ||
|
||||
@moveBlacklist.include?(GameData::Move.get(target.lastRegularMoveUsed).function_code)
|
||||
@battle.pbDisplay(_INTL("But it failed!")) if show_message
|
||||
return true
|
||||
|
||||
@@ -410,7 +410,7 @@ class Battle::Scene
|
||||
# Returns the animation ID to use for a given move/user. Returns nil if that
|
||||
# move has no animations defined for it.
|
||||
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
|
||||
if (idxUser & 1) == 0 # On player's side
|
||||
anim = move2anim[0][real_move_id]
|
||||
|
||||
@@ -5,12 +5,11 @@ class Battle::AI
|
||||
attr_reader :battle
|
||||
attr_reader :trainer
|
||||
attr_reader :battlers
|
||||
attr_reader :roles
|
||||
attr_reader :user, :target, :move
|
||||
|
||||
def initialize(battle)
|
||||
@battle = battle
|
||||
|
||||
# TODO: Move this elsewhere?
|
||||
@roles = [Array.new(@battle.pbParty(0).length) { |i| determine_roles(0, i) },
|
||||
Array.new(@battle.pbParty(1).length) { |i| determine_roles(1, i) }]
|
||||
end
|
||||
@@ -57,7 +56,9 @@ class Battle::AI
|
||||
PBDebug.log("")
|
||||
return
|
||||
end
|
||||
if pbEnemyShouldUseItem?
|
||||
ret = false
|
||||
PBDebug.logonerr { ret = pbChooseToUseItem }
|
||||
if ret
|
||||
PBDebug.log("")
|
||||
return
|
||||
end
|
||||
|
||||
@@ -1,173 +1,224 @@
|
||||
class Battle::AI
|
||||
#=============================================================================
|
||||
# Decide whether the opponent should use an item on the Pokémon
|
||||
# TODO: Maybe don't cure a status problem if the Pokémon has an ability or
|
||||
# something that makes it benefit from having that problem.
|
||||
#=============================================================================
|
||||
def pbEnemyShouldUseItem?
|
||||
HP_HEAL_ITEMS = {
|
||||
:POTION => 20,
|
||||
:SUPERPOTION => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
|
||||
: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 => 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
|
||||
idxTarget = nil
|
||||
PBDebug.logonerr { item, idxTarget = pbEnemyItemToUse }
|
||||
idxTarget = nil # Party index (battle_use type 1/2/3) or battler index
|
||||
idxMove = nil
|
||||
item, idxTarget, idxMove = choose_item_to_use
|
||||
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
|
||||
@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}")
|
||||
return true
|
||||
end
|
||||
|
||||
# NOTE: The AI will only consider using an item on the Pokémon it's currently
|
||||
# choosing an action for.
|
||||
def pbEnemyItemToUse
|
||||
# Return values are:
|
||||
# item ID
|
||||
# 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
|
||||
items = @battle.pbGetOwnerItems(@user.index)
|
||||
return nil if !items || items.length == 0
|
||||
# Determine target of item (always the Pokémon choosing the action)
|
||||
idxTarget = @user.index # Battler using the item
|
||||
battler = @user.battler
|
||||
pkmn = battler.pokemon
|
||||
# Item categories
|
||||
hpItems = {
|
||||
:POTION => 20,
|
||||
:SUPERPOTION => (Settings::REBALANCED_HEALING_ITEM_AMOUNTS) ? 60 : 50,
|
||||
: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
|
||||
# Find all items usable on the Pokémon choosing this action
|
||||
pkmn = @user.battler.pokemon
|
||||
usable_items = {}
|
||||
items.each do |item|
|
||||
usage = get_usability_of_item_on_pkmn(item, @user.party_index, @user.side)
|
||||
usage.each_pair do |key, vals|
|
||||
usable_items[key] ||= []
|
||||
usable_items[key] += vals
|
||||
end
|
||||
end
|
||||
# Prioritise using a HP restoration item
|
||||
if usableHPItems.length > 0 && (battler.hp <= battler.totalhp / 4 ||
|
||||
(battler.hp <= battler.totalhp / 2 && pbAIRandom(100) < 30))
|
||||
usableHPItems.sort! { |a, b| (a[1] == b[1]) ? a[2] <=> b[2] : a[1] <=> b[1] }
|
||||
prevItem = nil
|
||||
usableHPItems.each do |i|
|
||||
return i[0], idxTarget if i[2] >= losthp
|
||||
prevItem = i
|
||||
if usable_items[:hp_heal] && (pkmn.hp <= pkmn.totalhp / 4 ||
|
||||
(pkmn.hp <= pkmn.totalhp / 2 && pbAIRandom(100) < 30))
|
||||
usable_items[:hp_heal].sort! { |a, b| (a[2] == b[2]) ? a[3] <=> b[3] : a[2] <=> b[2] }
|
||||
usable_items[:hp_heal].each do |item|
|
||||
return item[0], item[1] if item[3] >= (pkmn.totalhp - pkmn.hp) * 0.75
|
||||
end
|
||||
return prevItem[0], idxTarget
|
||||
return usable_items[:hp_heal].last[0], usable_items[:hp_heal].last[1]
|
||||
end
|
||||
# Next prioritise using a status-curing item
|
||||
if usableStatusItems.length > 0 && pbAIRandom(100) < 40
|
||||
usableStatusItems.sort! { |a, b| a[1] <=> b[1] }
|
||||
return usableStatusItems[0][0], idxTarget
|
||||
if usable_items[:status_cure] &&
|
||||
([:SLEEP, :FROZEN].include?(pkmn.status) || pbAIRandom(100) < 40)
|
||||
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
|
||||
# Next try using an X item
|
||||
if usableXItems.length > 0 && pbAIRandom(100) < 30
|
||||
usableXItems.sort! { |a, b| (a[1] == b[1]) ? a[2] <=> b[2] : a[1] <=> b[1] }
|
||||
prevItem = nil
|
||||
usableXItems.each do |i|
|
||||
break if prevItem && i[1] > prevItem[1]
|
||||
return i[0], idxTarget if i[1] + i[2] >= 6
|
||||
prevItem = i
|
||||
if usable_items[:stat_raise] && pbAIRandom(100) < 30
|
||||
usable_items[:stat_raise].sort! { |a, b| (a[2] == b[2]) ? a[3] <=> b[3] : a[2] <=> b[2] }
|
||||
return usable_items[:stat_raise].last[0], usable_items[:stat_raise].last[1]
|
||||
end
|
||||
# Find items usable on other Pokémon in the user's team
|
||||
# NOTE: Currently only checks Revives.
|
||||
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
|
||||
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
|
||||
return nil
|
||||
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
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
class Battle::AI
|
||||
#=============================================================================
|
||||
# Decide whether the opponent should switch Pokémon
|
||||
#=============================================================================
|
||||
# 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
|
||||
# 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
|
||||
each_foe_battler(@user.side) do |b, i|
|
||||
next if !b.battler.lastMoveUsed
|
||||
move_data = GameData::Move.get(b.battler.lastMoveUsed)
|
||||
next if move_data.status?
|
||||
move_data = GameData::Move.try_get(b.battler.lastMoveUsed)
|
||||
next if !move_data || move_data.status?
|
||||
move_type = move_data.type
|
||||
eff = Effectiveness.calculate(move_type, *pkmn_types)
|
||||
score -= move_data.power * eff / 5
|
||||
@@ -146,6 +143,7 @@ class Battle::AI
|
||||
ret = 0
|
||||
# Stealth Rock
|
||||
if @battle.sides[side].effects[PBEffects::StealthRock] && GameData::Type.exists?(:ROCK)
|
||||
pkmn_types = pkmn.types
|
||||
eff = Effectiveness.calculate(:ROCK, *pkmn_types)
|
||||
ret += pkmn.totalhp * eff / 8 if !Effectiveness.ineffective?(eff)
|
||||
end
|
||||
@@ -227,7 +225,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:cure_status_problem_by_switching_out,
|
||||
next false if !battler.ability_active?
|
||||
# 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
|
||||
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
|
||||
# Check specific abilities
|
||||
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
|
||||
# battler when it switches back in
|
||||
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] == 1 && battler.statusCount == 0
|
||||
next false if battle.field.effects[PBEffects::ToxicSpikes] == 2
|
||||
next false if battle.field.effects[PBEffects::ToxicSpikes] == 1 && battler.statusCount == 0
|
||||
end
|
||||
# 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)
|
||||
@@ -281,7 +279,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:wish_healing,
|
||||
next false if battler.totalhp - battler.hp >= amt * 2 / 3
|
||||
reserve_wants_healing_more = false
|
||||
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
|
||||
reserve_wants_healing_more = (pkmn.totalhp - pkmn.hp - entry_hazard_damage >= amt * 2 / 3)
|
||||
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.item_active? && Battle::ItemEffects.triggerCertainSwitching(b.item, b, battle)
|
||||
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?
|
||||
trapping = Battle::AbilityEffects.triggerTrappingByTarget(battler.ability, b, battler.battler, battle)
|
||||
break if trapping
|
||||
@@ -344,7 +342,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:yawning,
|
||||
next false if trapping
|
||||
end
|
||||
# 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")
|
||||
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.item_active? && Battle::ItemEffects.triggerCertainSwitching(b.item, b, battle)
|
||||
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?
|
||||
trapping = Battle::AbilityEffects.triggerTrappingByTarget(battler.ability, b, battler.battler, battle)
|
||||
break if trapping
|
||||
@@ -389,7 +387,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:asleep,
|
||||
next false if trapping
|
||||
end
|
||||
# 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
|
||||
next false if ai.pbAIRandom(100) < 50
|
||||
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?
|
||||
non_wonder_guard_foe_exists = false
|
||||
has_super_effective_move = false
|
||||
foe_types = b.pbTypes(true)
|
||||
foe_types = battler.pbTypes(true)
|
||||
next false if foe_types.length == 0
|
||||
ai.each_foe_battler(battler.side) do |b, i|
|
||||
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
|
||||
reserve_has_super_effective_move = false
|
||||
reserves.each do |pkmn|
|
||||
pkmn.moves.each do |m|
|
||||
next if m.status_move?
|
||||
pkmn.moves.each do |move|
|
||||
next if move.status_move?
|
||||
if ["IgnoreTargetAbility",
|
||||
"CategoryDependsOnHigherDamageIgnoreTargetAbility"].include?(move.function_code)
|
||||
reserve_has_super_effective_move = true
|
||||
break
|
||||
end
|
||||
eff = Effectiveness.calculate(m.type, *foe_types)
|
||||
eff = Effectiveness.calculate(move.type, *foe_types)
|
||||
if Effectiveness.super_effective?(eff)
|
||||
reserve_has_super_effective_move = true
|
||||
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)
|
||||
foe_moves = []
|
||||
ai.each_foe_battler(battler.side) do |b, i|
|
||||
b.moves.each do |m|
|
||||
next if m.statusMove?
|
||||
b.moves.each do |move|
|
||||
next if move.statusMove?
|
||||
# TODO: Improve on m_power with STAB and attack stat/stages and certain
|
||||
# other damage-altering effects, including base power calculations
|
||||
# for moves with variable power.
|
||||
m_power = m.power
|
||||
m_power = battler.hp if m.is_a?(Battle::Move::OHKO)
|
||||
m_power = move.power
|
||||
m_power = battler.hp if move.is_a?(Battle::Move::OHKO)
|
||||
m_type = move.pbCalcType(b.battler)
|
||||
foe_moves.push([m_power, m_type, m])
|
||||
foe_moves.push([m_power, m_type, move])
|
||||
end
|
||||
end
|
||||
next false if foe_moves.empty?
|
||||
@@ -612,7 +610,7 @@ Battle::AI::Handlers::ShouldSwitch.add(:high_damage_from_foe,
|
||||
big_threat = false
|
||||
ai.each_foe_battler(battler.side) do |b, i|
|
||||
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)
|
||||
next if move_data.status?
|
||||
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|
|
||||
next false if battle.rules["suddendeath"]
|
||||
# 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
|
||||
# Check for Rapid Spin
|
||||
reserve_can_remove_hazards = false
|
||||
@@ -668,7 +666,7 @@ Battle::AI::Handlers::ShouldNotSwitch.add(:battler_has_super_effective_move,
|
||||
proc { |battler, reserves, ai, battle|
|
||||
next false if battle.rules["suddendeath"]
|
||||
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.statusMove?
|
||||
# TODO: next if move is unusable? This would be complicated to implement.
|
||||
|
||||
@@ -109,7 +109,7 @@ class Battle::AI
|
||||
strength = b.effects[PBEffects::FollowMe]
|
||||
end
|
||||
end
|
||||
return new_target if new_target
|
||||
return new_target if new_target >= 0
|
||||
calc_type = @move.rough_type
|
||||
priority.each do |b|
|
||||
next if b.index == @user.index
|
||||
@@ -122,7 +122,7 @@ class Battle::AI
|
||||
end
|
||||
break if new_target >= 0
|
||||
end
|
||||
return new_target
|
||||
return (new_target >= 0) ? new_target : nil
|
||||
end
|
||||
|
||||
def add_move_to_choices(choices, idxMove, score, idxTarget = -1)
|
||||
@@ -142,6 +142,7 @@ class Battle::AI
|
||||
case move.function
|
||||
when "UseLastMoveUsed"
|
||||
if @battle.lastMoveUsed &&
|
||||
GameData::Move.exists?(@battle.lastMoveUsed) &&
|
||||
!move.moveBlacklist.include?(GameData::Move.get(@battle.lastMoveUsed).function_code)
|
||||
move = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@battle.lastMoveUsed))
|
||||
end
|
||||
@@ -159,9 +160,10 @@ class Battle::AI
|
||||
@target&.refresh_battler
|
||||
if @target && @move.function == "UseLastMoveUsedByTarget"
|
||||
if @target.battler.lastRegularMoveUsed &&
|
||||
GameData::Move.exists?(@target.battler.lastRegularMoveUsed) &&
|
||||
GameData::Move.get(@target.battler.lastRegularMoveUsed).has_flag?("CanMirrorMove")
|
||||
mov = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@target.battler.lastRegularMoveUsed))
|
||||
@battle.moldBreaker = @user.has_mold_breaker?
|
||||
mov = Battle::Move.from_pokemon_move(@battle, Pokemon::Move.new(@target.battler.lastRegularMoveUsed))
|
||||
@move.set_up(mov)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,24 +6,24 @@ class Battle::AI
|
||||
# opponent (only when deciding whether to switch mon in) - this
|
||||
# comparison should be calculated when needed instead of being a role.
|
||||
module BattleRole
|
||||
PHAZER = 0
|
||||
CLERIC = 1
|
||||
STALLBREAKER = 2
|
||||
STATUSABSORBER = 3
|
||||
BATONPASSER = 4
|
||||
SPINNER = 5
|
||||
FIELDSETTER = 6
|
||||
WEATHERSETTER = 7
|
||||
SWEEPER = 8
|
||||
PIVOT = 9
|
||||
PHYSICALWALL = 10
|
||||
SPECIALWALL = 11
|
||||
TANK = 12
|
||||
TRAPPER = 13
|
||||
SCREENER = 14
|
||||
ACE = 15
|
||||
LEAD = 16
|
||||
SECOND = 17
|
||||
PHAZER = 0
|
||||
CLERIC = 1
|
||||
STALL_BREAKER = 2
|
||||
STATUS_ABSORBER = 3
|
||||
BATON_PASSER = 4
|
||||
SPINNER = 5
|
||||
FIELD_SETTER = 6
|
||||
WEATHER_SETTER = 7
|
||||
SWEEPER = 8
|
||||
PIVOT = 9
|
||||
PHYSICAL_WALL = 10
|
||||
SPECIAL_WALL = 11
|
||||
TANK = 12
|
||||
TRAPPER = 13
|
||||
SCREENER = 14
|
||||
ACE = 15
|
||||
LEAD = 16
|
||||
SECOND = 17
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
@@ -51,22 +51,22 @@ class Battle::AI
|
||||
when "CureUserPartyStatus" # Aromatherapy/Heal Bell
|
||||
ret.push(BattleRole::CLERIC)
|
||||
when "DisableTargetStatusMoves" # Taunt
|
||||
ret.push(BattleRole::STALLBREAKER)
|
||||
ret.push(BattleRole::STALL_BREAKER)
|
||||
when "HealUserPositionNextTurn" # Wish
|
||||
ret.push(BattleRole::CLERIC) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
||||
when "HealUserFullyAndFallAsleep" # Rest
|
||||
ret.push(BattleRole::STATUSABSORBER)
|
||||
ret.push(BattleRole::STATUS_ABSORBER)
|
||||
when "SwitchOutUserPassOnEffects" # Baton Pass
|
||||
ret.push(BattleRole::BATONPASSER)
|
||||
ret.push(BattleRole::BATON_PASSER)
|
||||
when "SwitchOutUserStatusMove", "SwitchOutUserDamagingMove" # Teleport (Gen 8+), U-turn
|
||||
hasPivotMove = true
|
||||
when "RemoveUserBindingAndEntryHazards" # Rapid Spin
|
||||
ret.push(BattleRole::SPINNER)
|
||||
when "StartElectricTerrain", "StartGrassyTerrain",
|
||||
"StartMistyTerrain", "StartPsychicTerrain" # Terrain moves
|
||||
ret.push(BattleRole::FIELDSETTER)
|
||||
ret.push(BattleRole::FIELD_SETTER)
|
||||
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
|
||||
|
||||
@@ -81,10 +81,10 @@ class Battle::AI
|
||||
ret.push(BattleRole::PIVOT) if hasPivotMove
|
||||
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 }
|
||||
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 } &&
|
||||
!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
|
||||
else
|
||||
ret.push(BattleRole::TANK) if pkmn.ev[:HP] == Pokemon::EV_STAT_LIMIT
|
||||
@@ -96,14 +96,14 @@ class Battle::AI
|
||||
ret.push(BattleRole::PIVOT)
|
||||
when :GUTS, :QUICKFEET, :FLAREBOOST, :TOXICBOOST, :NATURALCURE, :MAGICGUARD,
|
||||
:MAGICBOUNCE, :HYDRATION
|
||||
ret.push(BattleRole::STATUSABSORBER)
|
||||
ret.push(BattleRole::STATUS_ABSORBER)
|
||||
when :SHADOWTAG, :ARENATRAP, :MAGNETPULL
|
||||
ret.push(BattleRole::TRAPPER)
|
||||
when :DROUGHT, :DRIZZLE, :SANDSTREAM, :SNOWWARNING, :PRIMORDIALSEA,
|
||||
:DESOLATELAND, :DELTASTREAM
|
||||
ret.push(BattleRole::WEATHERSETTER)
|
||||
ret.push(BattleRole::WEATHER_SETTER)
|
||||
when :GRASSYSURGE, :ELECTRICSURGE, :MISTYSURGE, :PSYCHICSURGE
|
||||
ret.push(BattleRole::FIELDSETTER)
|
||||
ret.push(BattleRole::FIELD_SETTER)
|
||||
end
|
||||
|
||||
# Check for items indicative of particular roles
|
||||
@@ -113,14 +113,14 @@ class Battle::AI
|
||||
when :ASSAULTVEST
|
||||
ret.push(BattleRole::TANK)
|
||||
when :CHOICEBAND, :CHOICESPECS
|
||||
ret.push(BattleRole::STALLBREAKER)
|
||||
ret.push(BattleRole::STALL_BREAKER)
|
||||
ret.push(BattleRole::SWEEPER) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
||||
when :CHOICESCARF
|
||||
ret.push(BattleRole::SWEEPER) if pkmn.ev[:SPEED] == Pokemon::EV_STAT_LIMIT
|
||||
when :TOXICORB, :FLAMEORB
|
||||
ret.push(BattleRole::STATUSABSORBER)
|
||||
ret.push(BattleRole::STATUS_ABSORBER)
|
||||
when :TERRAINEXTENDER
|
||||
ret.push(BattleRole::FIELDSETTER)
|
||||
ret.push(BattleRole::FIELD_SETTER)
|
||||
end
|
||||
|
||||
# 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
|
||||
if target_speed > b_speed
|
||||
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
|
||||
score += 8 * inc_mult
|
||||
score += 8 * dec_mult
|
||||
end
|
||||
break
|
||||
end
|
||||
|
||||
@@ -15,8 +15,8 @@ Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shiny_target,
|
||||
#===============================================================================
|
||||
# Prefer Shadow moves (for flavour).
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:shadow_moves,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:shadow_moves,
|
||||
proc { |score, move, user, ai, battle|
|
||||
if move.rough_type == :SHADOW
|
||||
old_score = score
|
||||
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.
|
||||
# TODO: Less prefer a priority move if any foe knows Quick Guard?
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::GeneralMoveScore.add(:priority_move_against_faster_target,
|
||||
proc { |score, move, user, ai, battle|
|
||||
Battle::AI::Handlers::GeneralMoveAgainstTargetScore.add(:priority_move_against_faster_target,
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
if ai.trainer.high_skill? && target.faster_than?(user) && move.rough_priority(user) > 0
|
||||
# User is at risk of being knocked out
|
||||
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")
|
||||
end
|
||||
# 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
|
||||
score += 8
|
||||
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 &&
|
||||
(target.battler.semiInvulnerable? || target.effects[PBEffects::SkyDrop] >= 0)
|
||||
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
|
||||
miss = true
|
||||
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|
|
||||
if (ai.trainer.has_skill_flag?("HPAware") || ai.trainer.high_skill?) && move.damagingMove? &&
|
||||
(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 move.rough_damage > target.hp * 1.1 # Predicted to KO the target
|
||||
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.
|
||||
def rough_end_of_round_damage
|
||||
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
|
||||
# TODO
|
||||
# Wish
|
||||
@@ -69,38 +95,38 @@ class Battle::AI::AIBattler
|
||||
# Sea of Fire
|
||||
if @ai.battle.sides[@side].effects[PBEffects::SeaOfFire] > 1 &&
|
||||
battler.takesIndirectDamage? && !has_type?(:FIRE)
|
||||
ret += self.totalhp / 8
|
||||
ret += [self.totalhp / 8, 1].max
|
||||
end
|
||||
# Grassy Terrain (healing)
|
||||
if @ai.battle.field.terrain == :Grassy && battler.affectedByTerrain? && battler.canHeal?
|
||||
ret -= [battler.totalhp / 16, 1].max
|
||||
ret -= [self.totalhp / 16, 1].max
|
||||
end
|
||||
# Leftovers/Black Sludge
|
||||
if has_active_item?(:BLACKSLUDGE)
|
||||
if has_type?(:POISON)
|
||||
ret -= [battler.totalhp / 16, 1].max if battler.canHeal?
|
||||
ret -= [self.totalhp / 16, 1].max if battler.canHeal?
|
||||
else
|
||||
ret += [battler.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
||||
ret += [self.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
||||
end
|
||||
elsif has_active_item?(:LEFTOVERS)
|
||||
ret -= [battler.totalhp / 16, 1].max if battler.canHeal?
|
||||
ret -= [self.totalhp / 16, 1].max if battler.canHeal?
|
||||
end
|
||||
# Aqua Ring
|
||||
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)
|
||||
ret -= [amt, 1].max
|
||||
end
|
||||
# Ingrain
|
||||
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)
|
||||
ret -= [amt, 1].max
|
||||
end
|
||||
# Leech Seed
|
||||
if self.effects[PBEffects::LeechSeed] >= 0
|
||||
if battler.takesIndirectDamage?
|
||||
ret += [battler.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
||||
ret += [self.totalhp / 8, 1].max if battler.takesIndirectDamage?
|
||||
end
|
||||
else
|
||||
@ai.each_battler do |b, i|
|
||||
@@ -112,16 +138,16 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
# Hyper Mode (Shadow Pokémon)
|
||||
if battler.inHyperMode?
|
||||
ret += [battler.totalhp / 24, 1].max
|
||||
ret += [self.totalhp / 24, 1].max
|
||||
end
|
||||
# Poison/burn/Nightmare
|
||||
if self.status == :POISON
|
||||
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?
|
||||
mult = 2
|
||||
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
|
||||
elsif self.status == :BURN
|
||||
if battler.takesIndirectDamage?
|
||||
@@ -130,11 +156,11 @@ class Battle::AI::AIBattler
|
||||
ret += [amt, 1].max
|
||||
end
|
||||
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
|
||||
# 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
|
||||
# Trapping damage
|
||||
if self.effects[PBEffects::Trapping] > 1 && battler.takesIndirectDamage?
|
||||
@@ -150,12 +176,12 @@ class Battle::AI::AIBattler
|
||||
if battler.asleep? && self.statusCount > 1 && battler.takesIndirectDamage?
|
||||
@ai.each_battler do |b, i|
|
||||
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
|
||||
# Sticky Barb
|
||||
if has_active_item?(:STICKYBARB) && battler.takesIndirectDamage?
|
||||
ret += [battler.totalhp / 8, 1].max
|
||||
ret += [self.totalhp / 8, 1].max
|
||||
end
|
||||
return ret
|
||||
end
|
||||
@@ -242,7 +268,7 @@ class Battle::AI::AIBattler
|
||||
end
|
||||
|
||||
def has_mold_breaker?
|
||||
return @ai.move.function == "IgnoreTargetAbility" || battler.hasMoldBreaker?
|
||||
return battler.hasMoldBreaker?
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------------------
|
||||
@@ -321,7 +347,7 @@ class Battle::AI::AIBattler
|
||||
# Other certain trapping effects
|
||||
return false if battler.trappedInBattle?
|
||||
# Trapping abilities/items
|
||||
ai.each_foe_battler(side) do |b, i|
|
||||
@ai.each_foe_battler(side) do |b, i|
|
||||
if b.ability_active? &&
|
||||
Battle::AbilityEffects.triggerTrappingByTarget(b.ability, battler, b.battler, @ai.battle)
|
||||
return false
|
||||
@@ -342,20 +368,21 @@ class Battle::AI::AIBattler
|
||||
case ability_id
|
||||
when :GUTS
|
||||
return true if ![:SLEEP, :FROZEN].include?(new_status) &&
|
||||
stat_raise_worthwhile?(self, :ATTACK, true)
|
||||
@ai.stat_raise_worthwhile?(self, :ATTACK, true)
|
||||
when :MARVELSCALE
|
||||
return true if stat_raise_worthwhile?(self, :DEFENSE, true)
|
||||
return true if @ai.stat_raise_worthwhile?(self, :DEFENSE, true)
|
||||
when :QUICKFEET
|
||||
return true if ![:SLEEP, :FROZEN].include?(new_status) &&
|
||||
stat_raise_worthwhile?(self, :SPEED, true)
|
||||
@ai.stat_raise_worthwhile?(self, :SPEED, true)
|
||||
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
|
||||
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
|
||||
return true if new_status == :POISON
|
||||
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
|
||||
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
|
||||
when :FRIENDGUARD, :HEALER, :SYMBOISIS, :TELEPATHY
|
||||
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
|
||||
when :GALEWINGS
|
||||
ret = 0 if !check_for_move { |m| m.type == :FLYING }
|
||||
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
|
||||
ret = 0 if !check_for_move { |m| m.punchingMove? }
|
||||
when :LIQUIDVOICE
|
||||
@@ -953,7 +980,7 @@ class Battle::AI::AIBattler
|
||||
when :SKILLLINK
|
||||
ret = 0 if !check_for_move { |m| m.is_a?(Battle::Move::HitTwoToFiveTimes) }
|
||||
when :STEELWORKER
|
||||
ret = 0 if !has_damaging_move_of_type?(:GRASS)
|
||||
ret = 0 if !has_damaging_move_of_type?(:STEEL)
|
||||
when :SWARM
|
||||
ret = 0 if !has_damaging_move_of_type?(:BUG)
|
||||
when :TORRENT
|
||||
|
||||
@@ -149,17 +149,40 @@ class Battle::AI::AIMove
|
||||
((@ai.battle.pbCheckGlobalAbility(:DARKAURA) && calc_type == :DARK) ||
|
||||
(@ai.battle.pbCheckGlobalAbility(:FAIRYAURA) && calc_type == :FAIRY))
|
||||
if @ai.battle.pbCheckGlobalAbility(:AURABREAK)
|
||||
multipliers[:power_multiplier] *= 2 / 3.0
|
||||
multipliers[:power_multiplier] *= 3 / 4.0
|
||||
else
|
||||
multipliers[:power_multiplier] *= 4 / 3.0
|
||||
end
|
||||
end
|
||||
# Ability effects that alter damage
|
||||
if user.ability_active?
|
||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||
# round.
|
||||
abilityBlacklist = [:ANALYTIC, :SNIPER, :TINTEDLENS, :AERILATE, :PIXILATE, :REFRIGERATE]
|
||||
if !abilityBlacklist.include?(user.ability_id)
|
||||
case user.ability_id
|
||||
when :AERILATE, :GALVANIZE, :PIXILATE, :REFRIGERATE
|
||||
multipliers[:power_multiplier] *= 1.2 if type == :NORMAL # NOTE: Not calc_type.
|
||||
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(
|
||||
user.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
@@ -173,10 +196,12 @@ class Battle::AI::AIMove
|
||||
)
|
||||
end
|
||||
if target.ability_active?
|
||||
# NOTE: These abilities aren't suitable for checking at the start of the
|
||||
# round.
|
||||
abilityBlacklist = [:FILTER, :SOLIDROCK]
|
||||
if !abilityBlacklist.include?(target.ability_id)
|
||||
case target.ability_id
|
||||
when :FILTER, :SOLIDROCK
|
||||
if Effectiveness.super_effective_type?(calc_type, *target.pbTypes(true))
|
||||
multipliers[:final_damage_multiplier] *= 0.75
|
||||
end
|
||||
else
|
||||
Battle::AbilityEffects.triggerDamageCalcFromTarget(
|
||||
target.ability, user_battler, target_battler, @move, multipliers, base_dmg, calc_type
|
||||
)
|
||||
@@ -197,13 +222,15 @@ class Battle::AI::AIMove
|
||||
end
|
||||
end
|
||||
# Item effects that alter damage
|
||||
# NOTE: Type-boosting gems aren't suitable for checking at the start of the
|
||||
# round.
|
||||
if user.item_active?
|
||||
# NOTE: These items aren't suitable for checking at the start of the
|
||||
# round.
|
||||
itemBlacklist = [:EXPERTBELT, :LIFEORB]
|
||||
if !itemBlacklist.include?(user.item_id)
|
||||
case user.item_id
|
||||
when :EXPERTBELT
|
||||
if Effectiveness.super_effective_type?(calc_type, *target.pbTypes(true))
|
||||
multipliers[:final_damage_multiplier] *= 1.2
|
||||
end
|
||||
when :LIFEORB
|
||||
multipliers[:final_damage_multiplier] *= 1.3
|
||||
else
|
||||
Battle::ItemEffects.triggerDamageCalcFromUser(
|
||||
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
|
||||
if user.item_active?
|
||||
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
|
||||
Battle::ItemEffects.triggerAccuracyCalcFromUser(
|
||||
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
|
||||
# -999: Additional effect will be negated
|
||||
# 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
|
||||
return 0 if @move.addlEffect == 0
|
||||
# Additional effect will be negated
|
||||
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
|
||||
# Prefer if the additional effect will have an increased chance of working
|
||||
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",
|
||||
proc { |score, move, user, ai, battle|
|
||||
add_effect = move.get_score_change_for_additional_effect(user, target)
|
||||
next score if add_effect == -999 # Additional effect will be negated
|
||||
score += add_effect
|
||||
# Score for raising user's Speed
|
||||
if Settings::MECHANICS_GENERATION >= 8
|
||||
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 += 15 if user.effects[PBEffects::LeechSeed] >= 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)
|
||||
crit_stage = 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,
|
||||
user.battler, user.battler, crit_stage)
|
||||
end
|
||||
@@ -660,6 +660,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenPhysicalDamageAgainstUserS
|
||||
score += 10
|
||||
score += 8 if !b.check_for_move { |m| m.specialMove?(m.type) }
|
||||
end
|
||||
# Prefer if user has Light Clay
|
||||
score += 5 if user.has_active_item?(:LIGHTCLAY)
|
||||
next score
|
||||
}
|
||||
)
|
||||
@@ -688,6 +690,8 @@ Battle::AI::Handlers::MoveEffectScore.add("StartWeakenSpecialDamageAgainstUserSi
|
||||
score += 10
|
||||
score += 8 if !b.check_for_move { |m| m.physicalMove?(m.type) }
|
||||
end
|
||||
# Prefer if user has Light Clay
|
||||
score += 5 if user.has_active_item?(:LIGHTCLAY)
|
||||
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
|
||||
end
|
||||
end
|
||||
# Prefer if user has Light Clay
|
||||
score += 5 if user.has_active_item?(:LIGHTCLAY)
|
||||
next score + 15
|
||||
}
|
||||
)
|
||||
@@ -883,7 +889,7 @@ Battle::AI::Handlers::MoveEffectScore.add("ProtectUserFromDamagingMovesKingsShie
|
||||
# less likely to work
|
||||
score -= (user.effects[PBEffects::ProtectRate] - 1) * ((Settings::MECHANICS_GENERATION >= 6) ? 15 : 10)
|
||||
# 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
|
||||
next score
|
||||
}
|
||||
@@ -1533,7 +1539,7 @@ Battle::AI::Handlers::MoveEffectScore.add("NormalMovesBecomeElectric",
|
||||
normal_type_better = 0
|
||||
electric_type_better = 0
|
||||
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)
|
||||
# Normal's effectiveness
|
||||
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
|
||||
# Don't prefer if user used this move in the last round
|
||||
score -= 15 if user.battler.lastMoveUsed &&
|
||||
GameData::Move.exists?(user.battler.lastMoveUsed) &&
|
||||
GameData::Move.get(user.battler.lastMoveUsed).function_code == move.function
|
||||
next score
|
||||
}
|
||||
|
||||
@@ -265,7 +265,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CounterPhysicalDamage",
|
||||
score += 5 if b.rough_stat(:ATTACK) > b.rough_stat(:SPECIAL_ATTACK)
|
||||
# Prefer if the last move the foe used was physical
|
||||
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
|
||||
# Prefer if the foe is taunted into using a damaging move
|
||||
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)
|
||||
# Prefer if the last move the foe used was special
|
||||
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
|
||||
# Prefer if the foe is taunted into using a damaging move
|
||||
score += 5 if b.effects[PBEffects::Taunt] > 0
|
||||
@@ -327,7 +327,7 @@ Battle::AI::Handlers::MoveEffectScore.add("CounterDamagePlusHalf",
|
||||
has_damaging_move = true
|
||||
# Prefer if the last move the foe used was damaging
|
||||
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
|
||||
# Prefer if the foe is taunted into using a damaging move
|
||||
score += 5 if b.effects[PBEffects::Taunt] > 0
|
||||
@@ -454,7 +454,7 @@ Battle::AI::Handlers::MoveEffectScore.add("WaterPledge",
|
||||
#===============================================================================
|
||||
Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsed",
|
||||
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)
|
||||
}
|
||||
)
|
||||
@@ -468,6 +468,7 @@ Battle::AI::Handlers::MoveFailureCheck.add("UseLastMoveUsed",
|
||||
Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("UseLastMoveUsedByTarget",
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if !target.battler.lastRegularMoveUsed
|
||||
next true if !GameData::Move.exists?(target.battler.lastRegularMoveUsed)
|
||||
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",
|
||||
proc { |score, move, user, ai, battle|
|
||||
Battle::AI::Handlers::MoveEffectAgainstTargetScore.add("TargetActsLast",
|
||||
proc { |score, move, user, target, ai, battle|
|
||||
# Useless if the target is an ally
|
||||
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
|
||||
@@ -643,6 +643,7 @@ Battle::AI::Handlers::MoveFailureAgainstTargetCheck.add("DisableTargetUsingDiffe
|
||||
proc { |move, user, target, ai, battle|
|
||||
next true if target.effects[PBEffects::Encore] > 0
|
||||
next true if !target.battler.lastRegularMoveUsed ||
|
||||
!GameData::Move.exists?(target.battler.lastRegularMoveUsed) ||
|
||||
move.move.moveBlacklist.include?(GameData::Move.get(target.battler.lastRegularMoveUsed).function_code)
|
||||
next true if target.effects[PBEffects::ShellTrap]
|
||||
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,
|
||||
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,
|
||||
proc { |ability, user, target, move, mults, power, type|
|
||||
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,
|
||||
proc { |ability, user, target, move, mults, power, type|
|
||||
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,
|
||||
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
|
||||
mults[:power_multiplier] *= 1.5
|
||||
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,
|
||||
proc { |item, battler, mult|
|
||||
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
|
||||
#===============================================================================
|
||||
|
||||
@@ -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|
|
||||
if pokemon.able? || pokemon.egg?
|
||||
if !pokemon.fainted?
|
||||
scene.pbDisplay(_INTL("It won't have any effect.")) if showMessages
|
||||
next false
|
||||
end
|
||||
|
||||
@@ -42,7 +42,7 @@ def pbRandomMove
|
||||
loop do
|
||||
move_id = keys.sample
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
def debug_set_up_trainer
|
||||
# Values to return
|
||||
trainer_array = []
|
||||
foe_items = [] # Intentionally left blank (for now)
|
||||
foe_items = [] # Items can't be used except in internal battles
|
||||
pokemon_array = []
|
||||
party_starts = [0]
|
||||
|
||||
@@ -25,6 +25,10 @@ def debug_set_up_trainer
|
||||
trainer = NPCTrainer.new(trainer_name, trainer_type)
|
||||
trainer.id = $player.make_foreign_ID
|
||||
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)
|
||||
|
||||
# Generate party
|
||||
@@ -46,11 +50,13 @@ def debug_set_up_trainer
|
||||
return trainer_array, foe_items, pokemon_array, party_starts
|
||||
end
|
||||
|
||||
def debug_test_auto_battle(logging = false)
|
||||
def debug_test_auto_battle(logging = false, console_messages = true)
|
||||
old_internal = $INTERNAL
|
||||
$INTERNAL = logging
|
||||
echoln "Start of testing auto-battle."
|
||||
echoln "" if !$INTERNAL
|
||||
if console_messages
|
||||
echoln "Start of testing auto-battle."
|
||||
echoln "" if !$INTERNAL
|
||||
end
|
||||
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)
|
||||
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("")
|
||||
echoln "" if !$INTERNAL
|
||||
echo_participant.call(foe_trainers[0], foe_party, 2)
|
||||
echoln "" if !$INTERNAL
|
||||
if console_messages
|
||||
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)
|
||||
scene = Battle::DebugSceneNoVisuals.new(logging)
|
||||
# Create the battle class (the mechanics side of it)
|
||||
@@ -97,14 +105,16 @@ def debug_test_auto_battle(logging = false)
|
||||
# Perform the battle itself
|
||||
outcome = battle.pbStartBattle
|
||||
# End
|
||||
text = ["Undecided",
|
||||
"Trainer 1 #{player_trainers[0].name} won",
|
||||
"Trainer 2 #{foe_trainers[0].name} won",
|
||||
"Ran/forfeited",
|
||||
"Wild Pokémon caught",
|
||||
"Draw"][outcome]
|
||||
echoln sprintf("%s after %d rounds", text, battle.turnCount + 1)
|
||||
echoln ""
|
||||
if console_messages
|
||||
text = ["Undecided",
|
||||
"Trainer 1 #{player_trainers[0].name} won",
|
||||
"Trainer 2 #{foe_trainers[0].name} won",
|
||||
"Ran/forfeited",
|
||||
"Wild Pokémon caught",
|
||||
"Draw"][outcome]
|
||||
echoln sprintf("%s after %d rounds", text, battle.turnCount + 1)
|
||||
echoln ""
|
||||
end
|
||||
$INTERNAL = old_internal
|
||||
end
|
||||
|
||||
@@ -112,9 +122,9 @@ end
|
||||
# Add to Debug menu.
|
||||
#===============================================================================
|
||||
MenuHandlers.add(:debug_menu, :test_auto_battle, {
|
||||
"name" => _INTL("Test Auto Battle"),
|
||||
"name" => "Test Auto Battle",
|
||||
"parent" => :main,
|
||||
"description" => _INTL("Runs an AI-controlled battle with no visuals."),
|
||||
"description" => "Runs an AI-controlled battle with no visuals.",
|
||||
"always_show" => false,
|
||||
"effect" => proc {
|
||||
debug_test_auto_battle
|
||||
@@ -122,12 +132,27 @@ MenuHandlers.add(:debug_menu, :test_auto_battle, {
|
||||
})
|
||||
|
||||
MenuHandlers.add(:debug_menu, :test_auto_battle_logging, {
|
||||
"name" => _INTL("Test Auto Battle with Logging"),
|
||||
"name" => "Test Auto Battle with Logging",
|
||||
"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,
|
||||
"effect" => proc {
|
||||
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
|
||||
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]
|
||||
Name = Tackle
|
||||
Type = NORMAL
|
||||
|
||||
@@ -4230,18 +4230,6 @@ Flags = CanProtect,CanMirrorMove,Sound
|
||||
EffectChance = 30
|
||||
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]
|
||||
Name = Weather Ball
|
||||
Type = NORMAL
|
||||
|
||||
@@ -4654,18 +4654,6 @@ Flags = CanProtect,CanMirrorMove,Sound
|
||||
EffectChance = 30
|
||||
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]
|
||||
Name = Weather Ball
|
||||
Type = NORMAL
|
||||
|
||||
@@ -5200,18 +5200,6 @@ Flags = CanProtect,CanMirrorMove,Sound
|
||||
EffectChance = 30
|
||||
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]
|
||||
Name = Terrain Pulse
|
||||
Type = NORMAL
|
||||
|
||||
@@ -5200,18 +5200,6 @@ Flags = CanProtect,CanMirrorMove,Sound
|
||||
EffectChance = 30
|
||||
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]
|
||||
Name = Terrain Pulse
|
||||
Type = NORMAL
|
||||
|
||||
Reference in New Issue
Block a user