{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)