mirror of
https://gitlab.com/Deukhoofd/BattleSim.git
synced 2026-10-31 14:01:40 +00:00
Lots of stuff
This commit is contained in:
commit
d7316d5799
6681 files changed
+527969
No files matched your search
@@ -0,0 +1,987 @@
|
||||
{_} = require 'underscore'
|
||||
{Attachment, Status, VolatileAttachment} = require '../attachment'
|
||||
{Weather} = require '../../../shared/weather'
|
||||
util = require '../util'
|
||||
|
||||
@Ability = Ability = {}
|
||||
|
||||
makeAbility = (name, func) ->
|
||||
condensed = name.replace(/\s+/g, '')
|
||||
class Ability[condensed] extends VolatileAttachment
|
||||
@displayName: name
|
||||
displayName: name
|
||||
ability: true
|
||||
func?.call(this)
|
||||
|
||||
# TODO: Implement.
|
||||
makeAbility 'Pickup'
|
||||
|
||||
# Ability templates
|
||||
|
||||
makeWeatherPreventionAbility = (name) ->
|
||||
makeAbility name, ->
|
||||
@preventsWeather = true
|
||||
|
||||
this::switchIn = ->
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('WEATHER_DISABLED')
|
||||
|
||||
makeWeatherPreventionAbility("Air Lock")
|
||||
makeWeatherPreventionAbility("Cloud Nine")
|
||||
|
||||
makeCriticalHitPreventionAbility = (name) ->
|
||||
makeAbility name, ->
|
||||
@preventsCriticalHits = true
|
||||
|
||||
makeCriticalHitPreventionAbility("Battle Armor")
|
||||
makeCriticalHitPreventionAbility("Shell Armor")
|
||||
|
||||
makeBoostProtectionAbility = (name, protection) ->
|
||||
makeAbility name, ->
|
||||
this::transformBoosts = (boosts, source) ->
|
||||
return boosts if source == @pokemon
|
||||
didProtect = false
|
||||
for stat of boosts
|
||||
if (!protection || stat in protection) && boosts[stat] < 0
|
||||
didProtect = true
|
||||
boosts[stat] = 0
|
||||
@pokemon.activateAbility() if didProtect
|
||||
boosts
|
||||
|
||||
makeBoostProtectionAbility("Big Pecks", [ "defense" ])
|
||||
makeBoostProtectionAbility("Clear Body")
|
||||
makeBoostProtectionAbility("Hyper Cutter", [ "attack" ])
|
||||
makeBoostProtectionAbility("Keen Eye", [ "accuracy" ])
|
||||
makeBoostProtectionAbility("White Smoke")
|
||||
|
||||
makeWeatherSpeedAbility = (name, weather) ->
|
||||
makeAbility name, ->
|
||||
this::switchIn = ->
|
||||
@doubleSpeed = @battle.hasWeather(weather)
|
||||
|
||||
this::informWeather = (newWeather) ->
|
||||
@doubleSpeed = (weather == newWeather)
|
||||
|
||||
this::editSpeed = (speed) ->
|
||||
if @doubleSpeed then 2 * speed else speed
|
||||
|
||||
this::isWeatherDamageImmune = (currentWeather) ->
|
||||
return true if weather == currentWeather
|
||||
|
||||
makeWeatherSpeedAbility("Chlorophyll", Weather.SUN)
|
||||
makeWeatherSpeedAbility("Swift Swim", Weather.RAIN)
|
||||
makeWeatherSpeedAbility("Sand Rush", Weather.SAND)
|
||||
|
||||
makeLowHealthAbility = (name, type) ->
|
||||
makeAbility name, ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
return 0x1000 if move.getType(@battle, @pokemon, target) != type
|
||||
return 0x1000 if @pokemon.currentHP > Math.floor(@pokemon.stat('hp') / 3)
|
||||
return 0x1800
|
||||
|
||||
makeLowHealthAbility("Blaze", "Fire")
|
||||
makeLowHealthAbility("Torrent", "Water")
|
||||
makeLowHealthAbility("Overgrow", "Grass")
|
||||
makeLowHealthAbility("Swarm", "Bug")
|
||||
|
||||
makeWeatherAbility = makeWeatherAbility ? (name, weather) ->
|
||||
makeAbility name, ->
|
||||
this::switchIn = ->
|
||||
@pokemon.activateAbility()
|
||||
@battle.setWeather(weather)
|
||||
|
||||
makeWeatherAbility("Drizzle", Weather.RAIN)
|
||||
makeWeatherAbility("Drought", Weather.SUN)
|
||||
makeWeatherAbility("Sand Stream", Weather.SAND)
|
||||
makeWeatherAbility("Snow Warning", Weather.HAIL)
|
||||
|
||||
makeFilterAbility = (name) ->
|
||||
makeAbility name, ->
|
||||
this::modifyDamageTarget = (move, user) ->
|
||||
if util.typeEffectiveness(move.type, user.types) > 1
|
||||
0xC00
|
||||
else
|
||||
0x1000
|
||||
|
||||
makeFilterAbility("Filter")
|
||||
makeFilterAbility("Solid Rock")
|
||||
|
||||
makeContactStatusAbility = (name, attachment) ->
|
||||
makeAbility name, ->
|
||||
this::isAliveCheck = -> true
|
||||
|
||||
this::afterBeingHit = (move, user, target, damage, isDirect) ->
|
||||
return if !move.hasFlag("contact")
|
||||
return if @battle.rng.next("contact status") >= .3
|
||||
return if !isDirect
|
||||
@pokemon.activateAbility()
|
||||
user.attach(attachment, source: @pokemon)
|
||||
|
||||
makeContactStatusAbility("Cute Charm", Attachment.Attract)
|
||||
makeContactStatusAbility("Flame Body", Status.Burn)
|
||||
makeContactStatusAbility("Poison Point", Status.Poison)
|
||||
makeContactStatusAbility("Static", Status.Paralyze)
|
||||
|
||||
makeStatusBoostAbility = (name, statuses, spectra) ->
|
||||
makeAbility name, ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
if move.spectra == spectra && statuses.some((s) => @pokemon.has(s))
|
||||
0x1800
|
||||
else
|
||||
0x1000
|
||||
|
||||
makeStatusBoostAbility("Flare Boost", [Status.Burn], 'special')
|
||||
makeStatusBoostAbility("Toxic Boost", [Status.Poison, Status.Toxic], 'physical')
|
||||
|
||||
makeHugePowerAbility = (name) ->
|
||||
makeAbility name, ->
|
||||
this::modifyAttack = (move) ->
|
||||
if move.isPhysical() then 0x2000 else 0x1000
|
||||
|
||||
makeHugePowerAbility("Huge Power")
|
||||
makeHugePowerAbility("Pure Power")
|
||||
|
||||
makeAttachmentImmuneAbility = (name, immuneAttachments, options = {}) ->
|
||||
makeAbility name, ->
|
||||
this::shouldAttach = (attachment) ->
|
||||
if attachment in immuneAttachments
|
||||
@pokemon.activateAbility()
|
||||
return false
|
||||
return true
|
||||
|
||||
shouldCure = options.cure ? true
|
||||
if shouldCure
|
||||
this::update = ->
|
||||
for attachment in immuneAttachments
|
||||
if @pokemon.has(attachment)
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.unattach(attachment)
|
||||
|
||||
makeAttachmentImmuneAbility("Immunity", [Status.Poison, Status.Toxic])
|
||||
makeAttachmentImmuneAbility("Inner Focus", [Attachment.Flinch], cure: false)
|
||||
makeAttachmentImmuneAbility("Insomnia", [Status.Sleep])
|
||||
makeAttachmentImmuneAbility("Limber", [Status.Paralyze])
|
||||
makeAttachmentImmuneAbility("Magma Armor", [Status.Freeze])
|
||||
makeAttachmentImmuneAbility("Oblivious", [Attachment.Attract])
|
||||
makeAttachmentImmuneAbility("Own Tempo", [Attachment.Confusion])
|
||||
makeAttachmentImmuneAbility("Vital Spirit", [Status.Sleep])
|
||||
makeAttachmentImmuneAbility("Water Veil", [Status.Burn])
|
||||
|
||||
makeContactHurtAbility = (name) ->
|
||||
makeAbility name, ->
|
||||
this::isAliveCheck = -> true
|
||||
|
||||
this::afterBeingHit = (move, user, target, damage, isDirect) ->
|
||||
return unless move.hasFlag('contact')
|
||||
return unless isDirect
|
||||
amount = user.stat('hp') >> 3
|
||||
@pokemon.activateAbility()
|
||||
if user.damage(amount)
|
||||
@battle.cannedText('POKEMON_HURT', user)
|
||||
|
||||
makeContactHurtAbility("Iron Barbs")
|
||||
makeContactHurtAbility("Rough Skin")
|
||||
|
||||
makeRedirectAndBoostAbility = (name, type) ->
|
||||
makeAbility name, ->
|
||||
# TODO: This should be implemented as isImmune instead.
|
||||
# TODO: Type-immunities should come before ability immunities.
|
||||
this::shouldBlockExecution = (move, user) ->
|
||||
return if move.getType(@battle, user, @pokemon) != type || user == @pokemon
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.boost(specialAttack: 1) unless @pokemon.isImmune(type)
|
||||
return true
|
||||
|
||||
makeRedirectAndBoostAbility("Lightningrod", "Electric")
|
||||
makeRedirectAndBoostAbility("Storm Drain", "Water")
|
||||
|
||||
makeTypeImmuneAbility = (name, type, stat) ->
|
||||
makeAbility name, ->
|
||||
this::shouldBlockExecution = (move, user) ->
|
||||
return if move.getType(@battle, user, @pokemon) != type || user == @pokemon
|
||||
@pokemon.activateAbility()
|
||||
@battle.message "#{@pokemon.name}'s #{name} increased its #{stat}!"
|
||||
hash = {}
|
||||
hash[stat] = 1
|
||||
@pokemon.boost(hash)
|
||||
return true
|
||||
|
||||
makeTypeImmuneAbility("Motor Drive", "Electric", "speed")
|
||||
makeTypeImmuneAbility("Sap Sipper", "Grass", "attack")
|
||||
|
||||
makeTypeAbsorbMove = (name, type) ->
|
||||
makeAbility name, ->
|
||||
this::shouldBlockExecution = (move, user) ->
|
||||
return if move.getType(@battle, user, @pokemon) != type || user == @pokemon
|
||||
@pokemon.activateAbility()
|
||||
amount = @pokemon.stat('hp') >> 2
|
||||
if @pokemon.heal(amount)
|
||||
@battle.cannedText('RECOVER_HP', @pokemon)
|
||||
return true
|
||||
|
||||
makeTypeAbsorbMove("Water Absorb", "Water")
|
||||
makeTypeAbsorbMove("Volt Absorb", "Electric")
|
||||
|
||||
makeAbilityCancelAbility = (name, cannedText) ->
|
||||
makeAbility name, ->
|
||||
this::switchIn = ->
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText(cannedText, @pokemon)
|
||||
|
||||
this::beforeMove = (move, pokemon, targets) ->
|
||||
for target in targets
|
||||
continue if !@battle.isPokemon(target)
|
||||
target.attach(Attachment.AbilityCancel)
|
||||
|
||||
this::afterMove = (move, pokemon, targets) ->
|
||||
for target in targets
|
||||
continue if !@battle.isPokemon(target)
|
||||
target.unattach(Attachment.AbilityCancel)
|
||||
|
||||
makeAbilityCancelAbility('Mold Breaker', 'MOLD_BREAKER')
|
||||
makeAbilityCancelAbility('Teravolt', 'TERAVOLT')
|
||||
makeAbilityCancelAbility('Turboblaze', 'TURBOBLAZE')
|
||||
|
||||
# Unique Abilities
|
||||
|
||||
makeAbility "Adaptability"
|
||||
|
||||
makeAbility "Aftermath", ->
|
||||
this::isAliveCheck = -> true
|
||||
|
||||
this::afterFaint = ->
|
||||
hit = @pokemon.lastHitBy
|
||||
return if !hit
|
||||
{team, slot, damage, move, turn} = hit
|
||||
pokemon = team.at(slot)
|
||||
if move.hasFlag('contact')
|
||||
amount = (pokemon.stat('hp') >> 2)
|
||||
@pokemon.activateAbility()
|
||||
pokemon.damage(amount)
|
||||
@battle.cannedText('POKEMON_HURT', pokemon)
|
||||
|
||||
makeAbility 'Analytic', ->
|
||||
this::modifyBasePower = ->
|
||||
if !@battle.hasActionsLeft() then 0x14CD else 0x1000
|
||||
|
||||
makeAbility "Anger Point", ->
|
||||
this::informCriticalHit = ->
|
||||
@pokemon.activateAbility()
|
||||
@battle.message "#{@pokemon.name} maxed its Attack!"
|
||||
@pokemon.boost(attack: 12)
|
||||
|
||||
makeAbility "Anticipation", ->
|
||||
this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
moves = _(opponent.moves for opponent in opponents).flatten()
|
||||
for move in moves
|
||||
effectiveness = util.typeEffectiveness(move.type, @pokemon.types) > 1
|
||||
if effectiveness || move.hasFlag("ohko")
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('ANTICIPATION', @pokemon)
|
||||
break
|
||||
|
||||
makeAbility "Arena Trap", ->
|
||||
this::beginTurn = this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
for opponent in opponents
|
||||
opponent.blockSwitch() unless opponent.isImmune("Ground")
|
||||
|
||||
makeAbility "Bad Dreams", ->
|
||||
this::endTurn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
for opponent in opponents
|
||||
continue unless opponent.has(Status.Sleep)
|
||||
amount = opponent.stat('hp') >> 3
|
||||
@pokemon.activateAbility()
|
||||
if opponent.damage(amount)
|
||||
@battle.cannedText('BAD_DREAMS', opponent)
|
||||
|
||||
makeAbility "Color Change", ->
|
||||
this::afterBeingHit = (move, user, target, damage) ->
|
||||
{type} = move
|
||||
if !move.isNonDamaging() && !target.hasType(type)
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('COLOR_CHANGE', target, type)
|
||||
target.types = [ type ]
|
||||
|
||||
makeAbility "Compoundeyes", ->
|
||||
this::editAccuracy = (accuracy) ->
|
||||
Math.floor(1.3 * accuracy)
|
||||
|
||||
# Hardcoded in Pokemon#boost
|
||||
makeAbility "Contrary"
|
||||
|
||||
makeAbility "Cursed Body", ->
|
||||
this::isAliveCheck = -> true
|
||||
|
||||
this::afterBeingHit = (move, user, target, damage, isDirect) ->
|
||||
return if !isDirect
|
||||
return if user == target
|
||||
return if user.has(Attachment.Substitute)
|
||||
return if @battle.rng.next("cursed body") >= .3
|
||||
return if user.has(Attachment.Disable)
|
||||
@pokemon.activateAbility()
|
||||
user.attach(Attachment.Disable, {move})
|
||||
|
||||
# Implementation is done in moves.coffee, specifically makeExplosionMove.
|
||||
makeAbility 'Damp'
|
||||
|
||||
makeAbility 'Defeatist', ->
|
||||
this::modifyAttack = ->
|
||||
halfHP = (@pokemon.stat('hp') >> 1)
|
||||
if @pokemon.currentHP <= halfHP then 0x800 else 0x1000
|
||||
|
||||
makeAbility 'Defiant', ->
|
||||
this::afterEachBoost = (boostAmount, source) ->
|
||||
return if source.team == @pokemon.team
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.boost(attack: 2) if boostAmount < 0
|
||||
|
||||
makeAbility 'Download', ->
|
||||
this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
return if opponents.length == 0
|
||||
totalDef = opponents.reduce(((s, p) -> s + p.stat('defense')), 0)
|
||||
totalSpDef = opponents.reduce(((s, p) -> s + p.stat('specialDefense')), 0)
|
||||
@pokemon.activateAbility()
|
||||
if totalSpDef <= totalDef
|
||||
@pokemon.boost(specialAttack: 1)
|
||||
else
|
||||
@pokemon.boost(attack: 1)
|
||||
|
||||
makeAbility 'Dry Skin', ->
|
||||
this::modifyBasePowerTarget = (move, user) ->
|
||||
if move.getType(@battle, user, @pokemon) == 'Fire' then 0x1400 else 0x1000
|
||||
|
||||
this::endTurn = ->
|
||||
amount = (@pokemon.stat('hp') >> 3)
|
||||
if @battle.hasWeather(Weather.SUN)
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.damage(amount)
|
||||
else if @battle.hasWeather(Weather.RAIN)
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.heal(amount)
|
||||
|
||||
this::shouldBlockExecution = (move, user) ->
|
||||
return if move.getType(@battle, user, @pokemon) != 'Water' || user == @pokemon
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.heal((@pokemon.stat('hp') >> 2))
|
||||
return true
|
||||
|
||||
# Implementation is in Attachment.Sleep
|
||||
makeAbility 'Early Bird'
|
||||
|
||||
makeAbility 'Effect Spore', ->
|
||||
this::isAliveCheck = -> true
|
||||
|
||||
this::afterBeingHit = (move, user, target, damage) ->
|
||||
return unless move.hasFlag("contact")
|
||||
switch @battle.rng.randInt(1, 10, "effect spore")
|
||||
when 1
|
||||
if user.attach(Status.Sleep)
|
||||
@pokemon.activateAbility()
|
||||
when 2
|
||||
if user.attach(Status.Paralyze)
|
||||
@pokemon.activateAbility()
|
||||
when 3
|
||||
if user.attach(Status.Poison)
|
||||
@pokemon.activateAbility()
|
||||
|
||||
makeAbility 'Flash Fire', ->
|
||||
this::shouldBlockExecution = (move, user) ->
|
||||
return if move.getType(@battle, user, @pokemon) != 'Fire' || user == @pokemon
|
||||
if @pokemon.attach(Attachment.FlashFire)
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('FLASH_FIRE', @pokemon)
|
||||
else
|
||||
@battle.cannedText('IMMUNITY', @pokemon)
|
||||
return true
|
||||
|
||||
makeAbility 'Forecast'
|
||||
|
||||
makeAbility 'Forewarn', ->
|
||||
VariablePowerMoves =
|
||||
'Crush Grip' : true
|
||||
'Dragon Rage' : true
|
||||
'Endeavor' : true
|
||||
'Flail' : true
|
||||
'Frustration' : true
|
||||
'Grass Knot' : true
|
||||
'Gyro Ball' : true
|
||||
'SonicBoom' : true
|
||||
'Hidden Power' : true
|
||||
'Low Kick' : true
|
||||
'Natural Gift' : true
|
||||
'Night Shade' : true
|
||||
'Psywave' : true
|
||||
'Return' : true
|
||||
'Reversal' : true
|
||||
'Seismic Toss' : true
|
||||
'Trump Card' : true
|
||||
'Wring Out' : true
|
||||
|
||||
CounterMoves =
|
||||
"Counter" : true
|
||||
"Mirror Coat" : true
|
||||
"Metal Burst" : true
|
||||
|
||||
@consider = consider = (move) ->
|
||||
if move.hasFlag('ohko')
|
||||
160
|
||||
else if CounterMoves[move.name]
|
||||
120
|
||||
else if VariablePowerMoves[move.name]
|
||||
80
|
||||
else
|
||||
move.power
|
||||
|
||||
this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
return if opponents.length == 0
|
||||
moves = _(opponent.moves for opponent in opponents).flatten()
|
||||
maxPower = Math.max(moves.map((m) -> consider(m))...)
|
||||
possibles = moves.filter((m) -> consider(m) == maxPower)
|
||||
finalMove = @battle.rng.choice(possibles, "forewarn")
|
||||
pokemon = _(opponents).find((p) -> finalMove in p.moves)
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('FOREWARN', pokemon, finalMove)
|
||||
|
||||
makeAbility 'Friend Guard', ->
|
||||
this::modifyDamageTarget = (move, user) ->
|
||||
return 0xC00 if user.team == @pokemon.team
|
||||
return 0x1000
|
||||
|
||||
makeAbility "Frisk", ->
|
||||
this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
return if opponents.length == 0
|
||||
# TODO: Do you select from opponents with items, or all alive opponents?
|
||||
opponent = @battle.rng.choice(opponents, "frisk")
|
||||
if opponent.hasItem()
|
||||
@pokemon.activateAbility()
|
||||
item = opponent.getItem()
|
||||
@battle.cannedText('FRISK', @pokemon, item)
|
||||
|
||||
# Implemented in items.coffee; makePinchBerry
|
||||
makeAbility "Gluttony"
|
||||
|
||||
makeAbility "Guts", ->
|
||||
this::modifyAttack = (move, target) ->
|
||||
return 0x1800 if @pokemon.hasStatus() && move.isPhysical()
|
||||
return 0x1000
|
||||
|
||||
makeAbility 'Harvest', ->
|
||||
this::endTurn = ->
|
||||
return unless @pokemon.lastItem?.type == 'berries'
|
||||
shouldHarvest = @battle.hasWeather(Weather.SUN)
|
||||
shouldHarvest ||= @battle.rng.randInt(0, 1, "harvest") == 1
|
||||
if shouldHarvest
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('HARVEST', @pokemon, @pokemon.lastItem)
|
||||
@pokemon.setItem(@pokemon.lastItem, clearLastItem: true)
|
||||
|
||||
makeAbility 'Healer', ->
|
||||
this::endTurn = ->
|
||||
for adjacent in @pokemon.team.getAdjacent(@pokemon)
|
||||
if @battle.rng.randInt(1, 10, "healer") <= 3
|
||||
@pokemon.activateAbility()
|
||||
adjacent.cureStatus()
|
||||
|
||||
makeAbility 'Heatproof', ->
|
||||
this::modifyBasePowerTarget = (move, user) ->
|
||||
return 0x800 if move.getType(@battle, user, @pokemon) == 'Fire'
|
||||
return 0x1000
|
||||
|
||||
makeAbility 'Heavy Metal', ->
|
||||
this::calculateWeight = (weight) ->
|
||||
2 * weight
|
||||
|
||||
makeAbility 'Honey Gather'
|
||||
|
||||
makeAbility 'Hustle', ->
|
||||
this::modifyAttack = (move, target) ->
|
||||
return 0x1800 if move.isPhysical()
|
||||
return 0x1000
|
||||
|
||||
this::editAccuracy = (accuracy, move) ->
|
||||
return Math.floor(0.8 * accuracy) if move.isPhysical()
|
||||
return accuracy
|
||||
|
||||
makeAbility "Hydration", ->
|
||||
this::endTurn = ->
|
||||
if @battle.hasWeather(Weather.RAIN) && @pokemon.hasStatus()
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.cureStatus()
|
||||
|
||||
makeAbility 'Ice Body', ->
|
||||
this::endTurn = ->
|
||||
if @battle.hasWeather(Weather.HAIL)
|
||||
@pokemon.activateAbility()
|
||||
amount = @pokemon.stat('hp') >> 4
|
||||
@pokemon.heal(amount)
|
||||
|
||||
this::isWeatherDamageImmune = (weather) ->
|
||||
return true if weather == Weather.HAIL
|
||||
|
||||
makeAbility 'Illuminate'
|
||||
|
||||
makeAbility 'Imposter', ->
|
||||
this::switchIn = ->
|
||||
opponents = @battle.getAllOpponents(@pokemon)
|
||||
index = @team.indexOf(@pokemon)
|
||||
opponent = opponents[index]
|
||||
return if !opponent
|
||||
return if opponent.isFainted() || opponent.has(Attachment.Substitute)
|
||||
@pokemon.attach(Attachment.Transform, target: opponent)
|
||||
|
||||
# Hardcoded in Move#isDirectHit
|
||||
# Hardcoded in Attachment.Reflect and Attachment.LightScreen
|
||||
makeAbility 'Infiltrator'
|
||||
|
||||
makeAbility 'Intimidate', ->
|
||||
this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
for opponent in opponents
|
||||
unless opponent.has(Attachment.Substitute)
|
||||
@pokemon.activateAbility()
|
||||
opponent.boost(attack: -1, @pokemon)
|
||||
|
||||
makeAbility 'Iron Fist', ->
|
||||
this::modifyBasePower = (move) ->
|
||||
if move.hasFlag('punch') then 0x1333 else 0x1000
|
||||
|
||||
makeAbility 'Justified', ->
|
||||
this::afterBeingHit = (move, user, target, damage, isDirect) ->
|
||||
if !move.isNonDamaging() && move.getType(@battle, user, @pokemon) == 'Dark' && isDirect
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.boost(attack: 1)
|
||||
|
||||
makeAbility 'Klutz', ->
|
||||
this::beginTurn = this::switchIn = ->
|
||||
@pokemon.blockItem()
|
||||
|
||||
makeAbility 'Leaf Guard', ->
|
||||
this::shouldAttach = (attachment) ->
|
||||
if attachment.status && @battle.hasWeather(Weather.SUN)
|
||||
@pokemon.activateAbility()
|
||||
return false
|
||||
return true
|
||||
|
||||
makeAbility 'Levitate', ->
|
||||
this::isImmune = (type) ->
|
||||
return true if type == 'Ground'
|
||||
|
||||
makeAbility 'Light Metal', ->
|
||||
this::calculateWeight = (weight) ->
|
||||
weight >> 1
|
||||
|
||||
# Implemented in Pokemon#drain
|
||||
makeAbility 'Liquid Ooze'
|
||||
|
||||
makeAbility 'Magic Bounce', ->
|
||||
this::beginTurn = this::switchIn = ->
|
||||
@pokemon.attach(Attachment.MagicCoat)
|
||||
@team.attach(Attachment.MagicCoat)
|
||||
|
||||
makeAbility 'Magic Guard', ->
|
||||
this::transformHealthChange = (damage, options) ->
|
||||
switch options.source
|
||||
when 'move' then return damage
|
||||
else return 0
|
||||
|
||||
makeAbility 'Magnet Pull', ->
|
||||
this::beginTurn = this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
opponents = opponents.filter((p) -> p.hasType("Steel"))
|
||||
opponent.blockSwitch() for opponent in opponents
|
||||
|
||||
makeAbility 'Marvel Scale', ->
|
||||
this::editDefense = (defense) ->
|
||||
if @pokemon.hasStatus() then Math.floor(1.5 * defense) else defense
|
||||
|
||||
makeAbility 'Minus', ->
|
||||
this::modifyAttack = (move, target) ->
|
||||
allies = @team.getActiveAlivePokemon()
|
||||
if move.isSpecial() && allies.some((p) -> p.has(Ability.Plus))
|
||||
0x1800
|
||||
else
|
||||
0x1000
|
||||
|
||||
makeAbility 'Moody', ->
|
||||
allBoosts = [ "attack", "defense", "speed", "specialAttack",
|
||||
"specialDefense", "accuracy", "evasion" ]
|
||||
this::endTurn = ->
|
||||
possibleRaises = allBoosts.filter (stat) =>
|
||||
@pokemon.stages[stat] < 6
|
||||
raiseStat = @battle.rng.choice(possibleRaises, "moody raise")
|
||||
|
||||
possibleLowers = allBoosts.filter (stat) =>
|
||||
@pokemon.stages[stat] > -6 && stat != raiseStat
|
||||
lowerStat = @battle.rng.choice(possibleLowers, "moody lower")
|
||||
|
||||
boosts = {}
|
||||
boosts[raiseStat] = 2 if raiseStat
|
||||
boosts[lowerStat] = -1 if lowerStat
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.boost(boosts)
|
||||
|
||||
makeAbility 'Moxie', ->
|
||||
this::afterSuccessfulHit = (move, user, target) ->
|
||||
if target.isFainted()
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.boost(attack: 1)
|
||||
|
||||
makeAbility 'Multiscale', ->
|
||||
this::modifyDamageTarget = ->
|
||||
return 0x800 if @pokemon.currentHP == @pokemon.stat('hp')
|
||||
return 0x1000
|
||||
|
||||
makeAbility 'Multitype'
|
||||
|
||||
makeAbility 'Mummy', ->
|
||||
this::isAliveCheck = -> true
|
||||
|
||||
this::afterBeingHit = (move, user) ->
|
||||
if move.hasFlag("contact") && user.hasChangeableAbility() && !user.hasAbility("Mummy")
|
||||
@pokemon.activateAbility()
|
||||
user.copyAbility(@constructor)
|
||||
@battle.cannedText('MUMMY', user)
|
||||
|
||||
makeAbility 'Natural Cure', ->
|
||||
this::switchOut = ->
|
||||
@pokemon.cureStatus(message: false)
|
||||
|
||||
# Hardcoded in Move#willMiss
|
||||
makeAbility 'No Guard'
|
||||
|
||||
makeAbility 'Normalize', ->
|
||||
this::editMoveType = (type, target) ->
|
||||
return "Normal" if @pokemon != target
|
||||
return type
|
||||
|
||||
makeAbility 'Overcoat', ->
|
||||
this::isWeatherDamageImmune = -> true
|
||||
|
||||
makeAbility 'Pickpocket', ->
|
||||
this::afterBeingHit = (move, user, target, damage) ->
|
||||
return if !move.hasFlag("contact") || target.hasItem() || !user.canLoseItem()
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('PICKPOCKET', target, user, user.item)
|
||||
target.setItem(user.item)
|
||||
user.removeItem()
|
||||
|
||||
makeAbility 'Plus', ->
|
||||
this::modifyAttack = (move, target) ->
|
||||
allies = @team.getActiveAlivePokemon()
|
||||
if move.isSpecial() && allies.some((p) -> p.has(Ability.Minus))
|
||||
0x1800
|
||||
else
|
||||
0x1000
|
||||
|
||||
makeAbility 'Poison Heal', ->
|
||||
# Poison damage neutralization is hardcoded in Attachment.Poison and Toxic.
|
||||
this::endTurn = ->
|
||||
# Return early so that:
|
||||
# 1. We don't trigger ability activation if the pokemon won't be healed.
|
||||
# 2. Ability activation must happen before HP animation.
|
||||
return if @pokemon.currentHP == @pokemon.stat('hp')
|
||||
if @pokemon.has(Status.Poison) || @pokemon.has(Status.Toxic)
|
||||
@pokemon.activateAbility()
|
||||
amount = @pokemon.stat('hp') >> 3
|
||||
@pokemon.heal(amount)
|
||||
|
||||
makeAbility 'Prankster', ->
|
||||
this::editPriority = (priority, move) ->
|
||||
return priority + 1 if move.isNonDamaging()
|
||||
return priority
|
||||
|
||||
# PP deduction hardcoded in Battle
|
||||
makeAbility 'Pressure', ->
|
||||
this::switchIn = ->
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('PRESSURE', @pokemon)
|
||||
|
||||
# Speed drop negation hardcoded into Attachment.Paralyze
|
||||
makeAbility 'Quick Feet', ->
|
||||
this::editSpeed = (speed) ->
|
||||
if @pokemon.hasStatus() then Math.floor(1.5 * speed) else speed
|
||||
|
||||
makeAbility 'Rain Dish', ->
|
||||
this::endTurn = ->
|
||||
return unless @battle.hasWeather(Weather.RAIN)
|
||||
@pokemon.activateAbility()
|
||||
amount = @pokemon.stat('hp') >> 4
|
||||
@pokemon.heal(amount)
|
||||
|
||||
makeAbility 'Rattled', ->
|
||||
this::afterBeingHit = (move, user, target, damage, isDirect) ->
|
||||
type = move.getType(@battle, user, @pokemon)
|
||||
if type in [ "Bug", "Ghost", "Dark" ] && !move.isNonDamaging() && isDirect
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.boost(speed: 1)
|
||||
|
||||
makeAbility 'Reckless', ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
kickMoves = [ @battle.getMove("Jump Kick"), @battle.getMove("Hi Jump Kick")]
|
||||
if move.recoil < 0 || move in kickMoves
|
||||
0x1333
|
||||
else
|
||||
0x1000
|
||||
|
||||
makeAbility 'Rivalry', ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
return 0x1400 if @pokemon.gender == target.gender
|
||||
return 0xC00 if (@pokemon.gender == 'F' && target.gender == 'M') ||
|
||||
(@pokemon.gender == 'M' && target.gender == 'F')
|
||||
return 0x1000
|
||||
|
||||
makeAbility 'Regenerator', ->
|
||||
this::switchOut = ->
|
||||
amount = Math.floor(@pokemon.stat('hp') / 3)
|
||||
# Uses setHP directly to bypass Heal Block's effect
|
||||
@pokemon.setHP(@pokemon.currentHP + amount)
|
||||
|
||||
# Hardcoded in move.coffee
|
||||
makeAbility 'Rock Head'
|
||||
|
||||
makeAbility 'Run Away'
|
||||
|
||||
makeAbility 'Sand Force', ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
return 0x1000 unless @battle.hasWeather(Weather.SAND)
|
||||
type = move.getType(@battle, @pokemon, target)
|
||||
return 0x14CD if type in ['Rock', 'Ground', 'Steel']
|
||||
return 0x1000
|
||||
|
||||
this::isWeatherDamageImmune = (weather) ->
|
||||
return true if weather == Weather.SAND
|
||||
|
||||
makeAbility 'Sand Veil', ->
|
||||
this::editEvasion = (accuracy) ->
|
||||
if @battle.hasWeather(Weather.SAND)
|
||||
Math.floor(.8 * accuracy)
|
||||
else
|
||||
accuracy
|
||||
|
||||
this::isWeatherDamageImmune = (weather) ->
|
||||
return true if weather == Weather.SAND
|
||||
|
||||
makeAbility 'Scrappy', ->
|
||||
this::shouldIgnoreImmunity = (moveType, target) ->
|
||||
return target.hasType('Ghost') && moveType in [ 'Normal', 'Fighting' ]
|
||||
|
||||
# Hardcoded in server/bw/data/moves
|
||||
makeAbility 'Serene Grace'
|
||||
|
||||
makeAbility 'Shadow Tag', ->
|
||||
this::beginTurn = this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
for opponent in opponents
|
||||
opponent.blockSwitch() unless opponent.hasAbility('Shadow Tag')
|
||||
|
||||
makeAbility 'Shed Skin', ->
|
||||
this::endTurn = ->
|
||||
return unless @pokemon.hasStatus()
|
||||
if @battle.rng.randInt(1, 10, "shed skin") <= 3
|
||||
@pokemon.cureStatus()
|
||||
|
||||
makeAbility 'Sheer Force', ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
return 0x14CD if move.hasSecondaryEffect()
|
||||
return 0x1000
|
||||
|
||||
# Hardcoded in Move#shouldTriggerSecondary
|
||||
makeAbility 'Shield Dust'
|
||||
|
||||
makeAbility 'Simple', ->
|
||||
this::transformBoosts = (boosts) ->
|
||||
newBoosts = {}
|
||||
for stat, boost of boosts
|
||||
newBoosts[stat] = 2 * boost
|
||||
newBoosts
|
||||
|
||||
makeAbility "Skill Link", ->
|
||||
this::calculateNumberOfHits = (move, targets) ->
|
||||
move.maxHits
|
||||
|
||||
makeAbility 'Slow Start', ->
|
||||
this::initialize = ->
|
||||
@turns = 5
|
||||
|
||||
this::switchIn = ->
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('SLOW_START_START', @pokemon)
|
||||
|
||||
this::endTurn = ->
|
||||
@turns -= 1
|
||||
if @turns == 0
|
||||
@battle.cannedText('SLOW_START_END', @pokemon)
|
||||
|
||||
this::modifyAttack = (move, target) ->
|
||||
return 0x800 if move.isPhysical() && @turns > 0
|
||||
return 0x1000
|
||||
|
||||
this::editSpeed = (speed) ->
|
||||
return speed >> 1 if @turns > 0
|
||||
return speed
|
||||
|
||||
makeAbility 'Sniper', ->
|
||||
this::modifyDamage = (move, target) ->
|
||||
return 0x1800 if @pokemon.crit
|
||||
return 0x1000
|
||||
|
||||
makeAbility 'Snow Cloak', ->
|
||||
this::editEvasion = (accuracy) ->
|
||||
if @battle.hasWeather(Weather.HAIL)
|
||||
Math.floor(.8 * accuracy)
|
||||
else
|
||||
accuracy
|
||||
|
||||
this::isWeatherDamageImmune = (weather) ->
|
||||
return true if weather == Weather.HAIL
|
||||
|
||||
makeAbility 'Solar Power', ->
|
||||
this::modifyAttack = (move, target) ->
|
||||
return 0x1800 if move.isSpecial() && @battle.hasWeather(Weather.SUN)
|
||||
return 0x1000
|
||||
|
||||
this::endTurn = ->
|
||||
if @battle.hasWeather(Weather.SUN)
|
||||
amount = (@pokemon.stat('hp') >> 3)
|
||||
@pokemon.activateAbility()
|
||||
@pokemon.damage(amount)
|
||||
@battle.cannedText('POKEMON_HURT', @pokemon)
|
||||
|
||||
makeAbility 'Soundproof', ->
|
||||
this::isImmune = (type, move) ->
|
||||
return true if move?.hasFlag('sound')
|
||||
|
||||
makeAbility 'Speed Boost', ->
|
||||
this::endTurn = ->
|
||||
return if @pokemon.turnsActive <= 0
|
||||
@pokemon.boost(speed: 1)
|
||||
|
||||
makeAbility 'Stall', ->
|
||||
this::afterTurnOrder = ->
|
||||
@battle.delay(@pokemon)
|
||||
|
||||
# Hardcoded in Attachment.Flinch
|
||||
makeAbility 'Steadfast'
|
||||
|
||||
# Hardcoded in Pokemon#canLoseItem
|
||||
makeAbility 'Sticky Hold'
|
||||
|
||||
makeAbility 'Sturdy', ->
|
||||
this::transformHealthChange = (amount, options) ->
|
||||
if @pokemon.currentHP == @pokemon.stat('hp')
|
||||
if amount >= @pokemon.currentHP && options.source == 'move'
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('ENDURE', @pokemon)
|
||||
return @pokemon.currentHP - 1
|
||||
return amount
|
||||
|
||||
makeAbility 'Suction Cups', ->
|
||||
this::shouldPhase = (phaser) ->
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('ANCHOR', @pokemon)
|
||||
return false
|
||||
|
||||
# Hardcoded in Move#criticalHitLevel
|
||||
makeAbility 'Super Luck'
|
||||
|
||||
# Hardcoded in status.coffee
|
||||
makeAbility 'Synchronize'
|
||||
|
||||
makeAbility 'Tangled Feet', ->
|
||||
this::editEvasion = (evasion) ->
|
||||
if @pokemon.has(Attachment.Confusion) then evasion >> 1 else evasion
|
||||
|
||||
makeAbility 'Technician', ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
return 0x1800 if move.basePower(@battle, @pokemon, target) <= 60
|
||||
return 0x1000
|
||||
|
||||
makeAbility 'Telepathy', ->
|
||||
this::shouldBlockExecution = (move, user) ->
|
||||
return if move.isNonDamaging() || user == @pokemon
|
||||
return if user not in @team.pokemon
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('AVOID_ALLIES', @pokemon)
|
||||
return true
|
||||
|
||||
makeAbility 'Thick Fat', ->
|
||||
this::modifyAttackTarget = (move, user) ->
|
||||
return 0x800 if move.getType(@battle, user, @pokemon) in [ 'Fire', 'Ice' ]
|
||||
return 0x1000
|
||||
|
||||
makeAbility 'Tinted Lens', ->
|
||||
this::modifyDamage = (move, target) ->
|
||||
return 0x2000 if move.typeEffectiveness(@battle, @pokemon, target) < 1
|
||||
return 0x1000
|
||||
|
||||
makeAbility 'Trace', ->
|
||||
bannedAbilities =
|
||||
"Flower Gift" : true
|
||||
"Forecast" : true
|
||||
"Illusion" : true
|
||||
"Imposter" : true
|
||||
"Multitype" : true
|
||||
"Trace" : true
|
||||
"Zen Mode" : true
|
||||
|
||||
this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
abilities = _(opponent.ability for opponent in opponents).compact()
|
||||
abilities = abilities.filter((a) -> a.displayName not of bannedAbilities)
|
||||
return if abilities.length == 0
|
||||
ability = @battle.rng.choice(abilities, "trace")
|
||||
# TODO: Display whose ability it traced.
|
||||
shouldshow = {reveal: true}
|
||||
shouldshow.reveal = false if @pokemon.has(Attachment.Illusion)
|
||||
@pokemon.copyAbility(ability, shouldshow)
|
||||
@battle.cannedText('TRACE', ability) if !@pokemon.has(Attachment.Illusion)
|
||||
|
||||
makeAbility 'Truant', ->
|
||||
this::initialize = ->
|
||||
@truanted = true
|
||||
|
||||
this::beforeMove = ->
|
||||
@truanted = !@truanted
|
||||
if @truanted
|
||||
@pokemon.activateAbility()
|
||||
@battle.cannedText('TRUANT', @pokemon)
|
||||
return false
|
||||
|
||||
# Hardcoded in Move
|
||||
makeAbility "Unaware"
|
||||
|
||||
# Hardcoded in Pokemon#removeItem
|
||||
makeAbility 'Unburden'
|
||||
|
||||
makeAbility 'Unnerve', ->
|
||||
this::beginTurn = this::switchIn = ->
|
||||
opponents = @battle.getOpponents(@pokemon)
|
||||
# TODO: Unnerve likely doesn't last until the end of the turn.
|
||||
# More research is needed here.
|
||||
for opponent in opponents
|
||||
opponent.blockItem() if opponent.item?.type == 'berries'
|
||||
|
||||
makeAbility 'Victory Star', ->
|
||||
this::editAccuracy = (accuracy) ->
|
||||
Math.floor(accuracy * 1.1)
|
||||
|
||||
makeAbility 'Weak Armor', ->
|
||||
this::afterBeingHit = (move, user) ->
|
||||
if move.isPhysical() then @pokemon.boost(defense: -1, speed: 1)
|
||||
|
||||
makeAbility 'Wonder Guard', ->
|
||||
this::shouldBlockExecution = (move, user) ->
|
||||
return if move == @battle.getMove("Struggle")
|
||||
return if move.isNonDamaging() || user == @pokemon
|
||||
return if move.typeEffectiveness(@battle, user, @pokemon) > 1
|
||||
@pokemon.activateAbility()
|
||||
return true
|
||||
|
||||
# Hardcoded in Move#chanceToHit
|
||||
makeAbility 'Wonder Skin'
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,4 @@
|
||||
{@Moves, @MoveData, @MoveList} = require './moves'
|
||||
{@Ability} = require './abilities'
|
||||
{@Item, @ItemData} = require './items'
|
||||
{@SpeciesData, @FormeData} = require './pokemon'
|
||||
@@ -0,0 +1,633 @@
|
||||
@ItemData = ItemData = require './data_items.json'
|
||||
{Attachment, Status, VolatileAttachment} = require('../attachment')
|
||||
{Weather} = require '../../../shared/weather'
|
||||
{Protocol} = require '../../../shared/protocol'
|
||||
util = require '../util'
|
||||
|
||||
@Item = Item = {}
|
||||
|
||||
makeItem = (name, func) ->
|
||||
if name not of ItemData
|
||||
throw new Error("Cannot extend Item '#{name}' because it does not exist.")
|
||||
condensed = name.replace(/\s+/g, '')
|
||||
class Item[condensed] extends VolatileAttachment
|
||||
@displayName: name
|
||||
displayName: name
|
||||
item: true
|
||||
(this[property] = value for property, value of ItemData[name])
|
||||
func?.call(this)
|
||||
|
||||
makePinchBerry = (name, hookName, func) ->
|
||||
if !func?
|
||||
func = hookName
|
||||
hookName = "update"
|
||||
|
||||
makeItem name, ->
|
||||
this.eat = (battle, eater) ->
|
||||
func.call(this, battle, eater)
|
||||
|
||||
this::[hookName] = ->
|
||||
fraction = (if @pokemon.hasAbility("Gluttony") then 1 else 2)
|
||||
activationHP = @pokemon.stat('hp') >> fraction
|
||||
if @pokemon.currentHP <= activationHP
|
||||
@constructor.eat(@battle, @pokemon)
|
||||
@pokemon.useItem()
|
||||
|
||||
# TODO: If the stat is maxed, does anything special happen?
|
||||
# Is the berry still consumed?
|
||||
makeStatBoostBerry = (name, boosts) ->
|
||||
makePinchBerry name, (battle, eater) ->
|
||||
boostedStats = eater.boost(boosts)
|
||||
|
||||
makeFlavorHealingBerry = (name, stat) ->
|
||||
makeItem name, ->
|
||||
this.eat = (battle, owner) ->
|
||||
if owner.heal(Math.floor(owner.stat('hp') / 8))
|
||||
battle.cannedText('BERRY_RESTORE', owner, this)
|
||||
if owner.natureBoost(stat) < 1.0
|
||||
owner.attach(Attachment.Confusion)
|
||||
|
||||
this::update = ->
|
||||
if @pokemon.currentHP <= Math.floor(@pokemon.stat('hp') / 2) && @pokemon.canHeal()
|
||||
@constructor.eat(@battle, @pokemon)
|
||||
@pokemon.useItem()
|
||||
|
||||
makeHealingBerry = (name, func) ->
|
||||
makeItem name, ->
|
||||
this.eat = (battle, owner) ->
|
||||
if owner.heal(func(owner))
|
||||
battle.cannedText('BERRY_RESTORE', owner, this)
|
||||
|
||||
this::update = ->
|
||||
if @pokemon.currentHP <= Math.floor(@pokemon.stat('hp') / 2) && @pokemon.canHeal()
|
||||
@constructor.eat(@battle, @pokemon)
|
||||
@pokemon.useItem()
|
||||
|
||||
makeTypeResistBerry = (name, type) ->
|
||||
makeItem name, ->
|
||||
this.eat = ->
|
||||
this::modifyBasePowerTarget = (move, user) ->
|
||||
return 0x1000 if move.getType(@battle, user, @pokemon) != type
|
||||
return 0x1000 if util.typeEffectiveness(type, @pokemon.types) <= 1 && type != 'Normal'
|
||||
@battle.cannedText('ITEM_WEAKEN', @constructor, @pokemon)
|
||||
@pokemon.useItem()
|
||||
return 0x800
|
||||
|
||||
makeFeedbackDamageBerry = (name, klass) ->
|
||||
makeItem name, ->
|
||||
this.eat = ->
|
||||
this::afterBeingHit = (move, user, target) ->
|
||||
return if !move[klass]()
|
||||
return if target.isFainted()
|
||||
if user.damage(Math.floor(user.stat('hp') / 8))
|
||||
@battle.cannedText('POKEMON_HURT_BY_ITEM', user, target, @constructor)
|
||||
target.useItem()
|
||||
|
||||
makeStatusCureBerry = (name, statuses...) ->
|
||||
makeItem name, ->
|
||||
this.eat = (battle, owner) ->
|
||||
for attachment in statuses
|
||||
if owner.cureAttachment(attachment, message: name)
|
||||
return true
|
||||
return false
|
||||
|
||||
this::update = ->
|
||||
if @constructor.eat(@battle, @pokemon) then @pokemon.useItem()
|
||||
|
||||
makeOrbItem = (name, species) ->
|
||||
makeItem name, ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
if @pokemon.species == species && move.type in @pokemon.types
|
||||
0x1333
|
||||
else
|
||||
0x1000
|
||||
|
||||
makeStatusOrbItem = (name, status) ->
|
||||
makeItem name, ->
|
||||
this::endTurn = ->
|
||||
@pokemon.attach(status)
|
||||
|
||||
makeTypeBoostItem = (name, type) ->
|
||||
makeItem name, ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
if move.type == type
|
||||
0x1333
|
||||
else
|
||||
0x1000
|
||||
|
||||
# Same as makeTypeBoostItem, but sets item.plate = type.
|
||||
makePlateItem = (name, type) ->
|
||||
makeTypeBoostItem(name, type)
|
||||
makeItem(name, -> @plate = type)
|
||||
|
||||
# Gem items are one-time use.
|
||||
GEM_BOOST_AMOUNT = GEM_BOOST_AMOUNT ? 0x1800
|
||||
makeGemItem = (name, type) ->
|
||||
makeItem name, ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
if move.type == type
|
||||
GEM_BOOST_AMOUNT
|
||||
else
|
||||
0x1000
|
||||
|
||||
this::afterSuccessfulHit = (move, user, target) ->
|
||||
if move.type == type
|
||||
@battle.cannedText('GEM_BOOST', @constructor, move)
|
||||
user.useItem()
|
||||
|
||||
makeChoiceItem = (name, func) ->
|
||||
makeItem name, ->
|
||||
this::initialize = ->
|
||||
@move = null
|
||||
|
||||
this::beforeMove = (move, user, targets) ->
|
||||
@move = move
|
||||
true
|
||||
|
||||
this::beginTurn = ->
|
||||
@pokemon.lockMove(@move) if @move?
|
||||
|
||||
func.call(this)
|
||||
|
||||
makeWeatherItem = (name, weather) ->
|
||||
makeItem name, ->
|
||||
@lengthensWeather = weather
|
||||
|
||||
makeSpeciesBoostingItem = (name, speciesArray, statsHash) ->
|
||||
makeItem name, ->
|
||||
for stat, boost of statsHash
|
||||
capitalizedStat = stat[0].toUpperCase() + stat.substr(1)
|
||||
# TODO: Use modifiers
|
||||
this::["edit#{capitalizedStat}"] = (stat) ->
|
||||
isTransformed = @pokemon.has(Attachment.Transform)
|
||||
if @pokemon.species in speciesArray && !isTransformed
|
||||
Math.floor(stat * boost)
|
||||
else
|
||||
stat
|
||||
|
||||
makeSpeciesCriticalItem = (name, species) ->
|
||||
makeItem name, ->
|
||||
this::criticalModifier = (sum) ->
|
||||
sum + (if @pokemon.species == species then 2 else 0)
|
||||
|
||||
makeDelayItem = (name) ->
|
||||
makeItem name, ->
|
||||
this::afterTurnOrder = ->
|
||||
@battle.delay(@pokemon)
|
||||
|
||||
makeEvasionItem = (name, ratio=0.9) ->
|
||||
makeItem name, ->
|
||||
this::editEvasion = (accuracy) ->
|
||||
Math.floor(accuracy * ratio)
|
||||
|
||||
makeFlinchItem = (name) ->
|
||||
makeItem name, ->
|
||||
this::afterSuccessfulHit = (move, user, target) ->
|
||||
multiplier = (if user.hasAbility("Serene Grace") then 2 else 1)
|
||||
if move.flinchChance == 0 && !move.isNonDamaging() &&
|
||||
@battle.rng.next("flinch item chance") < .1 * multiplier
|
||||
target.attach(Attachment.Flinch)
|
||||
|
||||
makeCriticalBoostItem = (name) ->
|
||||
makeItem name, ->
|
||||
this::criticalModifier = (sum) -> sum + 1
|
||||
|
||||
makeBoostOnTypeItem = (name, type, boosts) ->
|
||||
stats = Object.keys(boosts)
|
||||
length = stats.length
|
||||
stats = stats.map (stat) ->
|
||||
stat[0].toUpperCase() + stat[1...length].replace(/[A-Z]/g, " $1")
|
||||
stats[length - 1] = "and #{stats[length - 1]}" if length >= 2
|
||||
stats = stats.join(", ") if length >= 3
|
||||
stats = stats.join(" ") if length == 2
|
||||
makeItem name, ->
|
||||
this::afterBeingHit = (move, user, target) ->
|
||||
if move.type == type
|
||||
@battle.cannedText('BERRY_RAISE_STAT', @constructor, user, stats)
|
||||
target.boost(boosts)
|
||||
target.useItem()
|
||||
|
||||
makeBoostOnTypeItem 'Absorb Bulb', 'Water', specialAttack: 1
|
||||
|
||||
makeOrbItem 'Adamant Orb', 'Dialga'
|
||||
makeFlavorHealingBerry 'Aguav Berry', "specialDefense"
|
||||
|
||||
makeItem 'Air Balloon', ->
|
||||
this::initialize = ->
|
||||
@pokemon.tell(Protocol.POKEMON_ATTACH, @displayName)
|
||||
|
||||
this::afterBeingHit = (move, user, target) ->
|
||||
return if move.isNonDamaging()
|
||||
@pokemon.tell(Protocol.POKEMON_UNATTACH, @displayName)
|
||||
target.removeItem()
|
||||
|
||||
this::isImmune = (type) ->
|
||||
return true if type == 'Ground'
|
||||
|
||||
makeStatBoostBerry 'Apicot Berry', specialDefense: 1
|
||||
makeStatusCureBerry 'Aspear Berry', Status.Freeze
|
||||
makeTypeResistBerry 'Babiri Berry', 'Steel'
|
||||
makeHealingBerry 'Berry Juice', -> 20
|
||||
makeTypeBoostItem 'Black Belt', 'Fighting'
|
||||
makeTypeBoostItem 'BlackGlasses', 'Dark'
|
||||
|
||||
makeItem 'Black Sludge', ->
|
||||
this::endTurn = ->
|
||||
maxHP = @pokemon.stat('hp')
|
||||
if @pokemon.hasType('Poison')
|
||||
return if maxHP == @pokemon.currentHP
|
||||
amount = Math.floor(maxHP / 16)
|
||||
amount = 1 if amount == 0
|
||||
if @pokemon.heal(amount)
|
||||
@battle.cannedText('ITEM_RESTORE', @pokemon, @constructor)
|
||||
else
|
||||
amount = Math.floor(maxHP / 8)
|
||||
amount = 1 if amount == 0
|
||||
if @pokemon.damage(amount)
|
||||
@battle.cannedText('ITEM_SELF_HURT', @pokemon, @constructor)
|
||||
|
||||
makeEvasionItem 'BrightPowder', 0.9
|
||||
makeGemItem 'Bug Gem', 'Bug'
|
||||
makeBoostOnTypeItem 'Cell Battery', 'Electric', attack: 1
|
||||
makeTypeBoostItem 'Charcoal', 'Fire'
|
||||
makeTypeResistBerry 'Charti Berry', 'Rock'
|
||||
makeStatusCureBerry 'Cheri Berry', Status.Paralyze
|
||||
makeStatusCureBerry 'Chesto Berry', Status.Sleep
|
||||
makeTypeResistBerry 'Chilan Berry', 'Normal'
|
||||
makeChoiceItem 'Choice Band', ->
|
||||
this::modifyAttack = (move) ->
|
||||
if move.isPhysical() then 0x1800 else 0x1000
|
||||
|
||||
makeChoiceItem 'Choice Specs', ->
|
||||
this::modifyAttack = (move) ->
|
||||
if move.isSpecial() then 0x1800 else 0x1000
|
||||
|
||||
makeChoiceItem 'Choice Scarf', ->
|
||||
this::editSpeed = (stat) ->
|
||||
Math.floor(stat * 1.5)
|
||||
|
||||
makeTypeResistBerry 'Chople Berry', 'Fighting'
|
||||
makeTypeResistBerry 'Coba Berry', 'Flying'
|
||||
makeTypeResistBerry 'Colbur Berry', 'Dark'
|
||||
|
||||
makePinchBerry 'Custap Berry', 'afterTurnOrder', (battle, eater) ->
|
||||
battle.cannedText('MOVE_FIRST', eater, this)
|
||||
battle.bump(eater)
|
||||
|
||||
makeWeatherItem 'Damp Rock', Weather.RAIN
|
||||
makeWeatherItem 'Dark Rock', Weather.MOON
|
||||
makeGemItem 'Dark Gem', 'Dark'
|
||||
makeTypeBoostItem 'Dragon Fang', 'Dragon'
|
||||
makeGemItem 'Dragon Gem', 'Dragon'
|
||||
makePlateItem 'Draco Plate', 'Dragon'
|
||||
makePlateItem 'Dread Plate', 'Dark'
|
||||
makePlateItem 'Earth Plate', 'Ground'
|
||||
|
||||
makeItem 'Eject Button', ->
|
||||
this::afterAllHitsTarget = (move, user) ->
|
||||
return if move.isNonDamaging()
|
||||
return if !@battle.forceSwitch(@pokemon)
|
||||
@battle.cannedText('EJECT_BUTTON', @pokemon)
|
||||
@pokemon.useItem()
|
||||
|
||||
makeGemItem 'Electric Gem', 'Electric'
|
||||
|
||||
makeItem 'Enigma Berry', ->
|
||||
this.eat = ->
|
||||
this::afterBeingHit = (move, user, target) ->
|
||||
return if util.typeEffectiveness(move.type, target.types) <= 1
|
||||
if target.heal(Math.floor(target.stat('hp') / 4))
|
||||
@battle.cannedText('BERRY_RESTORE', target, @constructor)
|
||||
target.useItem()
|
||||
|
||||
makeItem 'Eviolite', ->
|
||||
this::editDefense = this::editSpecialDefense = (defense) ->
|
||||
return Math.floor(1.5 * defense) if @pokemon.nfe
|
||||
return defense
|
||||
|
||||
makeItem 'Expert Belt', ->
|
||||
this::modifyAttack = (move, target) ->
|
||||
effectiveness = move.typeEffectiveness(@battle, @pokemon, target)
|
||||
return 0x1333 if effectiveness > 1
|
||||
return 0x1000
|
||||
|
||||
makeGemItem 'Fighting Gem', 'Fighting'
|
||||
makeFlavorHealingBerry 'Figy Berry', "attack"
|
||||
makeGemItem 'Fire Gem', 'Fire'
|
||||
makePlateItem 'Fist Plate', 'Fighting'
|
||||
makeStatusOrbItem 'Flame Orb', Status.Burn
|
||||
makePlateItem 'Flame Plate', 'Fire'
|
||||
makeItem 'Float Stone', ->
|
||||
this::calculateWeight = (weight) ->
|
||||
Math.floor(weight / 2)
|
||||
makeGemItem 'Flying Gem', 'Flying'
|
||||
|
||||
makeItem 'Focus Band', ->
|
||||
this::transformHealthChange = (amount, options) ->
|
||||
if amount >= @pokemon.currentHP && @battle.rng.randInt(0, 9, "focus band") == 0 &&
|
||||
options.source == 'move'
|
||||
@battle.cannedText('HANG_ON', @pokemon, @constructor)
|
||||
@pokemon.useItem()
|
||||
return @pokemon.currentHP - 1
|
||||
return amount
|
||||
|
||||
makeItem 'Focus Sash', ->
|
||||
this::transformHealthChange = (amount, options) ->
|
||||
maxHP = @pokemon.stat('hp')
|
||||
if @pokemon.currentHP == maxHP && amount >= maxHP && options.source == 'move'
|
||||
@battle.cannedText('HANG_ON', @pokemon, @constructor)
|
||||
@pokemon.useItem()
|
||||
return maxHP - 1
|
||||
return amount
|
||||
|
||||
makeDelayItem 'Full Incense'
|
||||
makeStatBoostBerry 'Ganlon Berry', defense: 1
|
||||
makeGemItem 'Ghost Gem', 'Ghost'
|
||||
makeGemItem 'Grass Gem', 'Grass'
|
||||
makeOrbItem 'Griseous Orb', 'Giratina'
|
||||
makeGemItem 'Ground Gem', 'Ground'
|
||||
makeTypeResistBerry 'Haban Berry', 'Dragon'
|
||||
makeTypeBoostItem 'Hard Stone', 'Rock'
|
||||
makeWeatherItem 'Heat Rock', Weather.SUN
|
||||
makeFlavorHealingBerry 'Iapapa Berry', "defense"
|
||||
makeGemItem 'Ice Gem', 'Ice'
|
||||
makePlateItem 'Icicle Plate', 'Ice'
|
||||
makeWeatherItem 'Icy Rock', Weather.HAIL
|
||||
makePlateItem 'Insect Plate', 'Bug'
|
||||
makePlateItem 'Iron Plate', 'Steel'
|
||||
makeFeedbackDamageBerry 'Jaboca Berry', 'isPhysical'
|
||||
makeTypeResistBerry 'Kasib Berry', 'Ghost'
|
||||
makeTypeResistBerry 'Kebia Berry', 'Poison'
|
||||
makeFlinchItem "King's Rock"
|
||||
makeDelayItem 'Lagging Tail'
|
||||
|
||||
# TODO: What happens if the Pokemon already has Focus Energy?
|
||||
# Does the berry still get eaten? Same goes for the other stat berries.
|
||||
makePinchBerry 'Lansat Berry', (battle, eater) ->
|
||||
eater.attach(Attachment.FocusEnergy)
|
||||
|
||||
makeEvasionItem 'Lax Incense', 0.9
|
||||
|
||||
makeItem 'Leftovers', ->
|
||||
this::endTurn = ->
|
||||
maxHP = @pokemon.stat('hp')
|
||||
return if maxHP == @pokemon.currentHP
|
||||
amount = Math.floor(maxHP / 16)
|
||||
amount = 1 if amount == 0
|
||||
if @pokemon.heal(amount)
|
||||
@battle.cannedText('ITEM_RESTORE', @pokemon, @constructor)
|
||||
|
||||
makeStatBoostBerry 'Liechi Berry', attack: 1
|
||||
|
||||
makeItem 'Life Orb', ->
|
||||
this::modifyAttack = ->
|
||||
0x14CC
|
||||
|
||||
this::afterAllHits = (move) ->
|
||||
return if move.isNonDamaging()
|
||||
if @pokemon.damage(Math.floor(@pokemon.stat('hp') / 10))
|
||||
@battle.cannedText('ITEM_SELF_HURT', @pokemon, @constructor)
|
||||
|
||||
makeItem 'Light Clay' # Hardcoded in Attachment.Screen
|
||||
|
||||
makeStatusCureBerry 'Lum Berry', Status.Paralyze, Status.Sleep, Status.Poison,
|
||||
Status.Toxic, Status.Burn, Status.Freeze, Attachment.Confusion
|
||||
makeOrbItem 'Lustrous Orb', 'Palkia'
|
||||
makeItem 'Macho Brace', ->
|
||||
this::editSpeed = (stat) ->
|
||||
Math.floor(stat / 2)
|
||||
makeTypeBoostItem 'Magnet', 'Electric'
|
||||
makeFlavorHealingBerry 'Mago Berry', "speed"
|
||||
makePlateItem 'Meadow Plate', 'Grass'
|
||||
|
||||
makeItem 'Mental Herb', ->
|
||||
this.activate = (battle, pokemon) ->
|
||||
for effectName in [ 'Attract', 'Taunt', 'Encore', 'Torment', 'Disable' ]
|
||||
attachment = Attachment[effectName]
|
||||
if pokemon.has(attachment)
|
||||
battle.cannedText('MENTAL_HERB', pokemon)
|
||||
pokemon.unattach(attachment)
|
||||
return true
|
||||
return false
|
||||
|
||||
this::update = ->
|
||||
if @constructor.activate(@battle, @pokemon)
|
||||
@pokemon.useItem()
|
||||
|
||||
makeTypeBoostItem 'Metal Coat', 'Steel'
|
||||
|
||||
makeItem 'Metronome', ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
attachment = @pokemon.get(Attachment.Metronome)
|
||||
layers = attachment?.layers || 0
|
||||
0x1000 + layers * 0x333
|
||||
|
||||
this::afterSuccessfulHit = (move, user, target) ->
|
||||
user.attach(Attachment.Metronome, {move})
|
||||
|
||||
makePinchBerry 'Micle Berry', (battle, eater) ->
|
||||
eater.attach(Attachment.MicleBerry)
|
||||
|
||||
makePlateItem 'Mind Plate', 'Psychic'
|
||||
makeTypeBoostItem 'Miracle Seed', 'Grass'
|
||||
|
||||
makeItem 'Muscle Band', ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
if move.isPhysical()
|
||||
0x1199
|
||||
else
|
||||
0x1000
|
||||
|
||||
makeTypeBoostItem 'Mystic Water', 'Water'
|
||||
makeTypeBoostItem 'NeverMeltIce', 'Ice'
|
||||
makeGemItem 'Normal Gem', 'Normal'
|
||||
makeTypeResistBerry 'Occa Berry', 'Fire'
|
||||
makeTypeBoostItem 'Odd Incense', 'Psychic'
|
||||
makeHealingBerry 'Oran Berry', -> 10
|
||||
makeTypeResistBerry 'Passho Berry', 'Water'
|
||||
makeTypeResistBerry 'Payapa Berry', 'Psychic'
|
||||
makeStatusCureBerry 'Pecha Berry', Status.Toxic, Status.Poison
|
||||
makeStatusCureBerry 'Persim Berry', Attachment.Confusion
|
||||
makeStatBoostBerry 'Petaya Berry', specialAttack: 1
|
||||
makeTypeBoostItem 'Poison Barb', 'Poison'
|
||||
makeGemItem 'Poison Gem', 'Poison'
|
||||
makeGemItem 'Psychic Gem', 'Psychic'
|
||||
|
||||
makeItem 'Quick Claw', ->
|
||||
this::afterTurnOrder = ->
|
||||
if @battle.rng.next("quick claw") < .2
|
||||
@battle.cannedText('MOVE_FIRST', @pokemon, @constructor)
|
||||
@battle.bump(@pokemon)
|
||||
|
||||
makeStatusCureBerry 'Rawst Berry', Status.Burn
|
||||
makeFlinchItem "Razor Fang"
|
||||
|
||||
makeItem 'Red Card', ->
|
||||
this::afterAllHitsTarget = (move, user) ->
|
||||
return if move.isNonDamaging()
|
||||
benched = user.team.getAliveBenchedPokemon()
|
||||
return if benched.length == 0
|
||||
@battle.cannedText('RED_CARD', @pokemon, user)
|
||||
@pokemon.useItem()
|
||||
return if user.shouldPhase(@battle, @pokemon) == false
|
||||
pokemon = @battle.rng.choice(benched)
|
||||
index = user.team.indexOf(pokemon)
|
||||
user.team.switch(user, index)
|
||||
|
||||
makeTypeResistBerry 'Rindo Berry', 'Grass'
|
||||
makeGemItem 'Rock Gem', 'Rock'
|
||||
|
||||
makeItem 'Rocky Helmet', ->
|
||||
this::isAliveCheck = -> true
|
||||
|
||||
this::afterBeingHit = (move, user, target) ->
|
||||
if move.hasFlag("contact")
|
||||
amount = Math.floor(user.stat('hp') / 6)
|
||||
if user.damage(amount)
|
||||
@battle.cannedText('POKEMON_HURT_BY_ITEM', user, target, @constructor)
|
||||
|
||||
makeTypeBoostItem 'Rock Incense', 'Rock'
|
||||
makeTypeBoostItem 'Rose Incense', 'Grass'
|
||||
makeFeedbackDamageBerry 'Rowap Berry', 'isSpecial'
|
||||
makeStatBoostBerry 'Salac Berry', speed: 1
|
||||
makeTypeBoostItem 'Sea Incense', 'Water'
|
||||
makeTypeBoostItem 'Sharp Beak', 'Flying'
|
||||
|
||||
makeItem 'Shell Bell', ->
|
||||
this::afterSuccessfulHit = (move, user, target, damage) ->
|
||||
return if damage == 0
|
||||
if user.heal(Math.floor(damage / 8))
|
||||
@battle.cannedText('ITEM_RESTORE', user, @constructor)
|
||||
|
||||
makeTypeResistBerry 'Shuca Berry', 'Ground'
|
||||
makeTypeBoostItem 'Silk Scarf', 'Normal'
|
||||
makeTypeBoostItem 'SilverPowder', 'Bug'
|
||||
makeHealingBerry 'Sitrus Berry', (owner) -> Math.floor(owner.stat('hp') / 4)
|
||||
makePlateItem 'Sky Plate', 'Flying'
|
||||
makeWeatherItem 'Smooth Rock', Weather.SAND
|
||||
makeTypeBoostItem 'Soft Sand', 'Ground'
|
||||
makeSpeciesBoostingItem 'Soul Dew', ["Latias", "Latios"],
|
||||
specialAttack: 1.5, specialDefense: 1.5
|
||||
makeTypeBoostItem 'Spell Tag', 'Ghost'
|
||||
makePlateItem 'Splash Plate', 'Water'
|
||||
makePlateItem 'Spooky Plate', 'Ghost'
|
||||
|
||||
# TODO: If there is no stat left to boost, is it still consumed?
|
||||
makePinchBerry 'Starf Berry', (battle, eater) ->
|
||||
stats = ["attack", "defense", "specialAttack", "specialDefense", "speed"]
|
||||
stats = stats.filter((stat) -> eater.stages[stat] != 6)
|
||||
return if stats.length == 0
|
||||
index = battle.rng.randInt(0, stats.length - 1, "starf berry stat")
|
||||
boosts = {}
|
||||
boosts[stats[index]] = 2
|
||||
boostedStats = eater.boost(boosts)
|
||||
|
||||
makeItem 'Sticky Barb', ->
|
||||
this::afterBeingHit = (move, user, target) ->
|
||||
return unless move.hasFlag("contact")
|
||||
return if user.hasItem()
|
||||
user.setItem(@constructor)
|
||||
target.useItem()
|
||||
|
||||
this::endTurn = ->
|
||||
@pokemon.damage(Math.floor(@pokemon.stat('hp') / 8))
|
||||
|
||||
makeGemItem 'Steel Gem', 'Steel'
|
||||
makePlateItem 'Stone Plate', 'Rock'
|
||||
makeTypeResistBerry 'Tanga Berry', 'Bug'
|
||||
makeStatusOrbItem 'Toxic Orb', Status.Toxic
|
||||
makePlateItem 'Toxic Plate', 'Poison'
|
||||
makeTypeBoostItem 'TwistedSpoon', 'Psychic'
|
||||
makeTypeResistBerry 'Wacan Berry', 'Electric'
|
||||
makeGemItem 'Water Gem', 'Water'
|
||||
makeTypeBoostItem 'Wave Incense', 'Water'
|
||||
|
||||
# TODO: What if White Herb is tricked onto a Pokemon? Are all boosts negated?
|
||||
makeItem 'White Herb', ->
|
||||
this.activate = (battle, pokemon) ->
|
||||
triggered = false
|
||||
boosts = {}
|
||||
for stat, boost of pokemon.stages
|
||||
if boost < 0
|
||||
triggered = true
|
||||
boosts[stat] = 0
|
||||
if triggered
|
||||
pokemon.setBoosts(boosts)
|
||||
battle.cannedText('WHITE_HERB', pokemon)
|
||||
return triggered
|
||||
|
||||
this::update = ->
|
||||
if @constructor.activate(@battle, @pokemon)
|
||||
@pokemon.useItem()
|
||||
|
||||
makeItem "Wide Lens", ->
|
||||
this::editAccuracy = (accuracy) ->
|
||||
Math.floor(accuracy * 1.1)
|
||||
|
||||
makeFlavorHealingBerry 'Wiki Berry', "specialAttack"
|
||||
|
||||
makeItem 'Wise Glasses', ->
|
||||
this::modifyBasePower = (move, target) ->
|
||||
if move.isSpecial()
|
||||
0x1199
|
||||
else
|
||||
0x1000
|
||||
|
||||
makeTypeResistBerry 'Yache Berry', 'Ice'
|
||||
makePlateItem 'Zap Plate', 'Electric'
|
||||
|
||||
makeItem 'Zoom Lens', ->
|
||||
this::editAccuracy = (accuracy, move, target) ->
|
||||
return Math.floor(accuracy * 1.2) if @battle.willMove(target)
|
||||
return accuracy
|
||||
|
||||
makeSpeciesBoostingItem("DeepSeaTooth", ["Clamperl"], specialAttack: 2)
|
||||
makeSpeciesBoostingItem("DeepSeaScale", ["Clamperl"], specialDefense: 2)
|
||||
makeSpeciesBoostingItem("Light Ball", ["Pikachu"], attack: 2, specialAttack: 2)
|
||||
makeSpeciesBoostingItem("Thick Club", ["Cubone", "Marowak"], attack: 2)
|
||||
makeSpeciesBoostingItem("Metal Powder", ["Ditto"],
|
||||
defense: 2, specialDefense: 2)
|
||||
makeSpeciesBoostingItem("Quick Powder", ["Ditto"], speed: 2)
|
||||
|
||||
makeSpeciesCriticalItem "Lucky Punch", "Chansey"
|
||||
makeSpeciesCriticalItem "Stick", "Farfetch'd"
|
||||
|
||||
makeCriticalBoostItem 'Razor Claw'
|
||||
makeCriticalBoostItem 'Scope Lens'
|
||||
|
||||
makeItem 'Iron Ball', ->
|
||||
this::editSpeed = (stat) ->
|
||||
Math.floor(stat / 2)
|
||||
|
||||
this::isImmune = (type) ->
|
||||
return false if type == 'Ground'
|
||||
|
||||
makeItem 'Leppa Berry', ->
|
||||
this.eat = (battle, eater) ->
|
||||
for move in eater.moves
|
||||
if eater.pp(move) == 0
|
||||
eater.setPP(move, 10)
|
||||
break
|
||||
|
||||
this::update = ->
|
||||
if @pokemon.lastMove? && @pokemon.pp(@pokemon.lastMove) == 0
|
||||
@constructor.eat(@battle, @pokemon)
|
||||
@pokemon.useItem()
|
||||
|
||||
# TODO: Implement Nature Power and implement eat there.
|
||||
for berry in "Belue Berry, Bluk Berry, Cornn Berry, Durin Berry, Grepa Berry,
|
||||
Hondew Berry, Kelpsy Berry, Magost Berry, Nanab Berry,
|
||||
Nomel Berry, Pamtre Berry, Pinap Berry, Pomeg Berry, Qualot Berry,
|
||||
Rabuta Berry, Razz Berry, Spelon Berry, Tamato Berry,
|
||||
Watmel Berry, Wepear Berry".split(/,\s+/)
|
||||
makeItem berry, ->
|
||||
this.eat = ->
|
||||
|
||||
# Ensure we aren't purposefully missing berries that need an `eat` function.
|
||||
for name, item of Item
|
||||
if item.type == 'berries' && 'eat' not of item
|
||||
console.warn "Note: Item '#{item.displayName}' does not have `eat` implemented."
|
||||
|
||||
# Make all leftover items
|
||||
for itemName of ItemData
|
||||
makeItem(itemName) if itemName.replace(/\s+/, '') not of Item
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,2 @@
|
||||
@SpeciesData = require('./data_species.json')
|
||||
@FormeData = require('./data_formes.json')
|
||||
Reference in new issue
Block a user