392 lines
13 KiB
CoffeeScript
392 lines
13 KiB
CoffeeScript
|
{Weather} = require '../../../shared/weather'
|
||
|
|
||
|
# Retcon weather abilities to only last 5 turns.
|
||
|
makeWeatherAbility = (name, weather) ->
|
||
|
makeAbility name, ->
|
||
|
this::switchIn = ->
|
||
|
return if @battle.hasWeather(weather)
|
||
|
moveName = switch weather
|
||
|
when Weather.SUN then "Sunny Day"
|
||
|
when Weather.RAIN then "Rain Dance"
|
||
|
when Weather.SAND then "Sandstorm"
|
||
|
when Weather.HAIL then "Hail"
|
||
|
when Weather.MOON then "New Moon"
|
||
|
else throw new Error("#{weather} ability not supported.")
|
||
|
|
||
|
@pokemon.activateAbility()
|
||
|
move = @battle.getMove(moveName)
|
||
|
move.changeWeather(@battle, @pokemon)
|
||
|
|
||
|
# Import old abilities
|
||
|
|
||
|
coffee = require 'coffee-script'
|
||
|
path = require('path').resolve(__dirname, '../../bw/data/abilities.coffee')
|
||
|
eval(coffee.compile(require('fs').readFileSync(path, 'utf8'), bare: true))
|
||
|
|
||
|
# Retcon old abilities
|
||
|
|
||
|
# Effect Spore now does not affect Grass-type Pokemon,
|
||
|
# Pokemon with Overcoat, or Pokemon holding Safety Goggles
|
||
|
oldEffectSpore = Ability.EffectSpore::afterBeingHit
|
||
|
Ability.EffectSpore::afterBeingHit = (move, user, target, damage) ->
|
||
|
unless user.hasType("Grass") || user.hasAbility("Overcoat") || user.hasItem("Safety Goggles")
|
||
|
oldEffectSpore.apply(this, arguments)
|
||
|
|
||
|
# Oblivious now also prevents and cures Taunt
|
||
|
makeAttachmentImmuneAbility("Oblivious", [Attachment.Attract, Attachment.Taunt])
|
||
|
|
||
|
# Overcoat now also prevents powder moves from working.
|
||
|
Ability.Overcoat::shouldBlockExecution = (move, user) ->
|
||
|
if move.hasFlag("powder")
|
||
|
@pokemon.activateAbility()
|
||
|
return true
|
||
|
|
||
|
# New ability interfaces
|
||
|
|
||
|
makeNormalTypeChangeAbility = (name, newType) ->
|
||
|
makeAbility name, ->
|
||
|
this::editMoveType = (type, target) ->
|
||
|
return newType if type == 'Normal' && @pokemon != target
|
||
|
return type
|
||
|
|
||
|
this::modifyBasePower = (move, target) ->
|
||
|
return 0x14CD if move.type == 'Normal'
|
||
|
return 0x1000
|
||
|
|
||
|
makeNormalTypeChangeAbility("Aerilate", "Flying")
|
||
|
makeNormalTypeChangeAbility("Pixilate", "Fairy")
|
||
|
makeNormalTypeChangeAbility("Refrigerate", "Ice")
|
||
|
|
||
|
makeAuraAbility = (name, type) ->
|
||
|
makeAbility name, ->
|
||
|
this::modifyBasePower = (move, target) ->
|
||
|
return 0x1000 if move.getType(@battle, @pokemon, target) != type
|
||
|
for pokemon in @battle.getActiveAlivePokemon()
|
||
|
return 0xC00 if pokemon.hasAbility("Aura Break")
|
||
|
return 0x1547
|
||
|
|
||
|
makeAuraAbility("Dark Aura", "Dark")
|
||
|
makeAuraAbility("Fairy Aura", "Fairy")
|
||
|
|
||
|
# New unique abilities
|
||
|
|
||
|
makeAttachmentImmuneAbility("Aroma Veil", [Attachment.Attract, Attachment.Disable,
|
||
|
Attachment.Encore, Attachment.Taunt, Attachment.Torment], cure: false) # TODO: Add Heal Block
|
||
|
|
||
|
# Implemented in makeAuraAbility
|
||
|
makeAbility "Aura Break"
|
||
|
|
||
|
makeAbility 'Bulletproof', ->
|
||
|
this::isImmune = (type, move) ->
|
||
|
if move?.hasFlag('bullet')
|
||
|
@pokemon.activateAbility()
|
||
|
return true
|
||
|
|
||
|
# TODO: Cheek Pouch
|
||
|
makeAbility "Cheek Pouch"
|
||
|
|
||
|
makeAbility "Competitive", ->
|
||
|
this::afterEachBoost = (boostAmount, source) ->
|
||
|
return if source.team == @pokemon.team
|
||
|
@pokemon.activateAbility()
|
||
|
@pokemon.boost(specialAttack: 2) if boostAmount < 0
|
||
|
|
||
|
# TODO: Flower Veil
|
||
|
makeAbility "Flower Veil"
|
||
|
|
||
|
makeAbility "Fur Coat", ->
|
||
|
this::modifyBasePowerTarget = (move) ->
|
||
|
if move.isPhysical() then 0x800 else 0x1000
|
||
|
|
||
|
makeAbility 'Gale Wings', ->
|
||
|
this::editPriority = (priority, move) ->
|
||
|
# TODO: Test if Gale Wings works with Hidden Power Flying.
|
||
|
return priority + 1 if move.type == 'Flying'
|
||
|
return priority
|
||
|
|
||
|
makeAbility "Gooey", ->
|
||
|
this::isAliveCheck = -> true
|
||
|
|
||
|
this::afterBeingHit = (move, user) ->
|
||
|
if move.hasFlag("contact")
|
||
|
user.boost(speed: -1, @pokemon)
|
||
|
@pokemon.activateAbility()
|
||
|
|
||
|
# TODO: Grass Pelt
|
||
|
makeAbility "Grass Pelt"
|
||
|
|
||
|
# TODO: Magician
|
||
|
makeAbility "Magician"
|
||
|
|
||
|
makeAbility 'Mega Launcher', ->
|
||
|
this::modifyBasePower = (move, target) ->
|
||
|
return 0x1800 if move.hasFlag("pulse")
|
||
|
return 0x1000
|
||
|
|
||
|
makeAbility 'Parental Bond', ->
|
||
|
this::calculateNumberOfHits = (move, targets) ->
|
||
|
# Do nothing if this move is multi-hit, has multiple targets, or is status.
|
||
|
return if move.minHits != 1 || targets.length > 1 || move.isNonDamaging()
|
||
|
return 2
|
||
|
|
||
|
this::modifyDamage = (move, target, hitNumber) ->
|
||
|
return 0x800 if hitNumber == 2 && move.maxHits == 1
|
||
|
return 0x1000
|
||
|
|
||
|
makeAbility 'Protean', ->
|
||
|
this::beforeMove = (move, user, targets) ->
|
||
|
type = move.getType(@battle, user, targets[0])
|
||
|
return if user.types.length == 1 && user.types[0] == type
|
||
|
user.types = [ type ]
|
||
|
@pokemon.activateAbility()
|
||
|
@battle.cannedText('TRANSFORM_TYPE', user, type)
|
||
|
|
||
|
makeAbility 'Stance Change', ->
|
||
|
this::beforeMove = (move, user, targets) ->
|
||
|
newForme = switch
|
||
|
when !move.isNonDamaging() then "blade"
|
||
|
when move == @battle.getMove("King's Shield") then "default"
|
||
|
if newForme && !@pokemon.isInForme(newForme) && @pokemon.species == 'Aegislash'
|
||
|
@pokemon.activateAbility()
|
||
|
@pokemon.changeForme(newForme)
|
||
|
humanized = (if newForme == "blade" then "Blade" else "Shield")
|
||
|
@battle.message("Changed to #{humanized} Forme!")
|
||
|
true
|
||
|
|
||
|
makeAbility "Strong Jaw", ->
|
||
|
this::modifyBasePower = (move) ->
|
||
|
return 0x1800 if move.hasFlag("bite")
|
||
|
return 0x1000
|
||
|
|
||
|
# TODO: Sweet Veil (2v2)
|
||
|
makeAttachmentImmuneAbility("Sweet Veil", [Status.Sleep], cure: false)
|
||
|
|
||
|
# TODO: Symbiosis
|
||
|
makeAbility "Symbiosis"
|
||
|
|
||
|
makeAbility "Tough Claws", ->
|
||
|
this::modifyBasePower = (move) ->
|
||
|
return 0x14CD if move.hasFlag("contact")
|
||
|
return 0x1000
|
||
|
|
||
|
makeWeatherAbility("Noctem", Weather.MOON)
|
||
|
|
||
|
makeAbility 'Heliophobia', ->
|
||
|
this::endTurn = ->
|
||
|
amount = Math.floor(@pokemon.stat('hp') / 8)
|
||
|
if @battle.hasWeather(Weather.SUN)
|
||
|
@pokemon.setHP(@pokemon.currentHP - amount)
|
||
|
else if @battle.hasWeather(Weather.MOON)
|
||
|
@pokemon.setHP(@pokemon.currentHP + amount)
|
||
|
|
||
|
makeWeatherSpeedAbility("Shadow Dance", Weather.MOON)
|
||
|
|
||
|
makeAbility "Amplifier", ->
|
||
|
this::modifyBasePower = (move) ->
|
||
|
return 0x1800 if move.hasFlag("sound")
|
||
|
return 0x1000
|
||
|
|
||
|
makeAbility "Athenian", ->
|
||
|
this::modifyAttack = (move) ->
|
||
|
if move.isSpecial() then 0x2000 else 0x1000
|
||
|
|
||
|
makeTypeImmuneAbility("Castle Moat", "Water", "specialDefense")
|
||
|
|
||
|
makeAbility 'Ethereal Shroud', ->
|
||
|
this::typeEffectiveness = (move, user) ->
|
||
|
movetype = move.type
|
||
|
usertypes = user.types
|
||
|
effect = util.typeEffectiveness(movetype, usertypes)
|
||
|
ghosteffect = util.typeEffectiveness(movetype, ["Ghost"])
|
||
|
if ghosteffect < 1
|
||
|
effect *= ghosteffect
|
||
|
return effect
|
||
|
|
||
|
makeAbility 'Foundry', ->
|
||
|
this::editMoveType = (type, target) ->
|
||
|
return "Fire" if type == 'Rock' && @pokemon != target
|
||
|
return type
|
||
|
|
||
|
this::modifyBasePower = (move, target) ->
|
||
|
return 0x14CD if move.type == 'Rock'
|
||
|
return 0x1000
|
||
|
|
||
|
makeAbility 'Hubris', ->
|
||
|
this::afterSuccessfulHit = (move, user, target) ->
|
||
|
if target.isFainted()
|
||
|
@pokemon.activateAbility()
|
||
|
@pokemon.boost(specialAttack: 1)
|
||
|
|
||
|
makeWeatherSpeedAbility("Ice Cleats", Weather.HAIL)
|
||
|
|
||
|
makeAbility 'Irrelephant', ->
|
||
|
this::shouldIgnoreImmunity = (moveType, target) ->
|
||
|
return true
|
||
|
|
||
|
|
||
|
makeAbility 'Pendulum', ->
|
||
|
this::modifyBasePower = (move, target) ->
|
||
|
attachment = @pokemon.get(Attachment.Pendulum)
|
||
|
layers = attachment?.layers || 0
|
||
|
0x1000 + layers * 0x333
|
||
|
|
||
|
this::afterSuccessfulHit = (move, user, target) ->
|
||
|
user.attach(Attachment.Pendulum, {move})
|
||
|
|
||
|
makeAbility 'Prism Guard', ->
|
||
|
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)
|
||
|
|
||
|
# TODO: Protean Maxima
|
||
|
makeAbility "Protean Maxima", ->
|
||
|
this::beforeMove = (move, user, targets) ->
|
||
|
type = move.getType(@battle, user, targets[0])
|
||
|
newForme = switch
|
||
|
when type == "Normal" then "mega"
|
||
|
when type == "Water" then "mega-water"
|
||
|
when type == "Electric" then "mega-electric"
|
||
|
when type == "Fire" then "mega-fire"
|
||
|
when type == "Psychic" then "mega-psychic"
|
||
|
when type == "Dark" then "mega-dark"
|
||
|
when type == "Grass" then "mega-grass"
|
||
|
when type == "Ice" then "mega-ice"
|
||
|
when type == "Fairy" then "mega-fairy"
|
||
|
else ""
|
||
|
if newForme != "" && !@pokemon.isInForme(newForme) && @pokemon.species == 'Eevee'
|
||
|
@pokemon.activateAbility()
|
||
|
@pokemon.changeForme(newForme)
|
||
|
@battle.message("Changed to #{type} Forme!")
|
||
|
if newForme == 'mega-psychic'
|
||
|
@pokemon.attach(Attachment.MagicCoat)
|
||
|
@team.attach(Attachment.MagicCoat)
|
||
|
true
|
||
|
|
||
|
this::shouldBlockExecution = (move, user) ->
|
||
|
forme = @pokemon.getForme()
|
||
|
console.log(forme)
|
||
|
if @pokemon.isInForme("mega-water") && @pokemon.species == 'Eevee'
|
||
|
return if move.getType(@battle, user, @pokemon) != "Water" || user == @pokemon
|
||
|
@pokemon.activateAbility()
|
||
|
amount = @pokemon.stat('hp') >> 2
|
||
|
if @pokemon.heal(amount)
|
||
|
@battle.cannedText('RECOVER_HP', @pokemon)
|
||
|
return true
|
||
|
else if @pokemon.isInForme("mega-electric") && @pokemon.species == 'Eevee'
|
||
|
return if move.getType(@battle, user, @pokemon) != "Electric" || user == @pokemon
|
||
|
@pokemon.activateAbility()
|
||
|
amount = @pokemon.stat('hp') >> 2
|
||
|
if @pokemon.heal(amount)
|
||
|
@battle.cannedText('RECOVER_HP', @pokemon)
|
||
|
return true
|
||
|
else if @pokemon.isInForme("mega-fire") and @pokemon.species == 'Eevee'
|
||
|
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
|
||
|
|
||
|
this::beginTurn = this::switchIn = ->
|
||
|
forme = @pokemon.getForme()
|
||
|
if @pokemon.isInForme("mega-psychic") and @pokemon.species == 'Eevee'
|
||
|
@pokemon.attach(Attachment.MagicCoat)
|
||
|
@team.attach(Attachment.MagicCoat)
|
||
|
|
||
|
this::switchIn = ->
|
||
|
forme = @pokemon.getForme()
|
||
|
@doubleSpeed = @battle.hasWeather(Weather.SUN) if @pokemon.isInForme("mega-grass") and @pokemon.species == 'Eevee'
|
||
|
|
||
|
this::informWeather = (newWeather) ->
|
||
|
forme = @pokemon.getForme()
|
||
|
@doubleSpeed = (Weather.SUN == newWeather) if @pokemon.isInForme("mega-grass") and @pokemon.species == 'Eevee'
|
||
|
|
||
|
this::editSpeed = (speed) ->
|
||
|
forme = @pokemon.getForme()
|
||
|
if @doubleSpeed and @pokemon.isInForme("mega-grass") and @pokemon.species == 'Eevee'
|
||
|
2 * speed
|
||
|
else
|
||
|
speed
|
||
|
|
||
|
this::isWeatherDamageImmune = (currentWeather) ->
|
||
|
forme = @pokemon.getForme()
|
||
|
return true if Weather.SUN == currentWeather and @pokemon.isInForme("mega-grass")
|
||
|
return true if Weather.HAIL == currentWeather and forme == @pokemon.isInForme("mega-ice")
|
||
|
|
||
|
this::editEvasion = (accuracy) ->
|
||
|
forme = @pokemon.getForme()
|
||
|
if @battle.hasWeather(Weather.HAIL) and @pokemon.isInForme("mega-ice") and @pokemon.species == 'Eevee'
|
||
|
Math.floor(.8 * accuracy)
|
||
|
else
|
||
|
accuracy
|
||
|
|
||
|
this::afterBeingHit = (move, user, target, damage, isDirect) ->
|
||
|
forme = @pokemon.getForme()
|
||
|
return if @pokemon.isInForme("mega-ice") or @pokemon.species != 'Eevee'
|
||
|
return if !move.hasFlag("contact")
|
||
|
return if @battle.rng.next("contact status") >= .3
|
||
|
return if !isDirect
|
||
|
@pokemon.activateAbility()
|
||
|
user.attach(Attachment.Attract, source: @pokemon)
|
||
|
|
||
|
|
||
|
#mega-dark is hardcoded in bw/attachments
|
||
|
|
||
|
makeLowHealthAbility("Psycho Call", "Psychic")
|
||
|
makeLowHealthAbility("Shadow Call", "Dark")
|
||
|
makeLowHealthAbility("Spirit Call", "Ghost")
|
||
|
|
||
|
|
||
|
# TODO: Just kidding, this is done
|
||
|
makeAbility "Regurgitation"
|
||
|
|
||
|
makeAbility 'Spectral Jaws', ->
|
||
|
this::modifyBasePower = (move) ->
|
||
|
return 0x14CD if move.hasFlag("bite")
|
||
|
return 0x1000
|
||
|
|
||
|
makeAbility 'Speed Swap', ->
|
||
|
this::switchIn = ->
|
||
|
if @battle.attach(Attachment.TrickRoom)
|
||
|
@battle.cannedText('TRICK_ROOM_START', @pokemon)
|
||
|
else
|
||
|
@battle.unattach(Attachment.TrickRoom)
|
||
|
|
||
|
makeAbility 'Synthetic Alloy', ->
|
||
|
this::typeEffectiveness = (move) ->
|
||
|
return 1 if move.type = "Fire"
|
||
|
|
||
|
makeAbility 'Venomous', ->
|
||
|
this::afterSuccessfulHit = (move, user, target) ->
|
||
|
if move.shouldTriggerSecondary && move.ailmentId == "poison"
|
||
|
target.attach(Status.Toxic)
|
||
|
|
||
|
makeTypeImmuneAbility("Wind Force", "Flying", "speed")
|
||
|
|
||
|
makeAbility 'Winter Joy', ->
|
||
|
this::modifyBasePower = (move) ->
|
||
|
boostmonths = [1,2,11,12]
|
||
|
badmonths = [5,6,7,8]
|
||
|
today = new Date
|
||
|
mm = today.getMonth() + 1
|
||
|
if mm in boostmonths
|
||
|
return 0x1666
|
||
|
else if mm in badmonths
|
||
|
return 0xB33
|
||
|
else
|
||
|
return 0x1000
|
||
|
|
||
|
makeAbility 'Illusion', ->
|
||
|
this::initialize = ->
|
||
|
alivemons = @pokemon.team.getAlivePokemon()
|
||
|
lastalivemon = alivemons[alivemons.length-1]
|
||
|
|
||
|
@pokemon.attach(Attachment.Illusion, target: lastalivemon)
|
||
|
|