1
0
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:
Deukhoofd
2016-02-01 23:19:30 +01:00
commit d7316d5799
6681 changed files with 527969 additions and 0 deletions

View 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'

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

11270
server/bw/data/data_moves.json Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,2 @@
@SpeciesData = require('./data_species.json')
@FormeData = require('./data_formes.json')