mirror of
https://gitlab.com/Deukhoofd/BattleSim.git
synced 2025-10-28 10:10:04 +00:00
Lots of stuff
This commit is contained in:
987
server/bw/data/abilities.coffee
Normal file
987
server/bw/data/abilities.coffee
Normal file
@@ -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'
|
||||
62404
server/bw/data/data_formes.json
Normal file
62404
server/bw/data/data_formes.json
Normal file
File diff suppressed because it is too large
Load Diff
1838
server/bw/data/data_items.json
Normal file
1838
server/bw/data/data_items.json
Normal file
File diff suppressed because it is too large
Load Diff
11270
server/bw/data/data_moves.json
Normal file
11270
server/bw/data/data_moves.json
Normal file
File diff suppressed because it is too large
Load Diff
6615
server/bw/data/data_species.json
Normal file
6615
server/bw/data/data_species.json
Normal file
File diff suppressed because it is too large
Load Diff
4
server/bw/data/index.coffee
Normal file
4
server/bw/data/index.coffee
Normal file
@@ -0,0 +1,4 @@
|
||||
{@Moves, @MoveData, @MoveList} = require './moves'
|
||||
{@Ability} = require './abilities'
|
||||
{@Item, @ItemData} = require './items'
|
||||
{@SpeciesData, @FormeData} = require './pokemon'
|
||||
633
server/bw/data/items.coffee
Normal file
633
server/bw/data/items.coffee
Normal file
@@ -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
|
||||
1956
server/bw/data/moves.coffee
Normal file
1956
server/bw/data/moves.coffee
Normal file
File diff suppressed because it is too large
Load Diff
2
server/bw/data/pokemon.coffee
Normal file
2
server/bw/data/pokemon.coffee
Normal file
@@ -0,0 +1,2 @@
|
||||
@SpeciesData = require('./data_species.json')
|
||||
@FormeData = require('./data_formes.json')
|
||||
Reference in New Issue
Block a user