-- engine/battle/effect_commands.asm:1553-1614 BattleCommand_CheckHit

package.path = "./?.lua;./?/init.lua;" .. package.path

local T = require("tests.harness")
local Battle = require("src.battle.gen2.Battle")
local Effects = require("src.battle.gen2.Effects")
local Mon = require("src.battle.gen2.Mon")

local TYPES = {
  NORMAL = { id = "NORMAL", index = 0, category = "physical" },
  FIGHTING = { id = "FIGHTING", index = 1, category = "physical" },
}

local MOVES = {
  TACKLE = { id = "TACKLE", name = "TACKLE", power = 35, type = "NORMAL",
    accuracy = 100, pp = 35, effect = "EFFECT_NORMAL_HIT" },
  SWIFT = { id = "SWIFT", name = "SWIFT", power = 60, type = "NORMAL",
    accuracy = 100, pp = 20, effect = "EFFECT_ALWAYS_HIT" },
  VITAL_THROW = { id = "VITAL_THROW", name = "VITAL THROW", power = 70,
    type = "FIGHTING", accuracy = 100, pp = 10,
    effect = "EFFECT_ALWAYS_HIT" },
}

local POKEMON = {
  growthRates = {
    GROWTH_MEDIUM_FAST = { numerator = 1, denominator = 1, squared = 0,
      linear = 0, constant = 0 },
  },
  MACHOP = {
    id = "MACHOP", index = 66, name = "MACHOP",
    baseStats = { hp = 70, attack = 80, defense = 50, speed = 35,
      specialAttack = 35, specialDefense = 35 },
    types = { "NORMAL", "NORMAL" }, catchRate = 180, baseExp = 75,
    growthRate = "GROWTH_MEDIUM_FAST", genderRatio = 63,
    levelMoves = { { level = 1, move = "TACKLE" } }, evolutions = {},
  },
}

local DATA = {
  pokemon = POKEMON,
  moves = MOVES,
  type_chart = { types = TYPES, matchups = {} },
  items = {
    BRIGHTPOWDER = { id = "BRIGHTPOWDER", name = "BRIGHTPOWDER",
      heldEffect = "HELD_BRIGHTPOWDER", heldParameter = 51 },
  },
}

local perfect = { attack = 15, defense = 15, speed = 15, special = 15 }
perfect.hp = Mon.hpDV(perfect)

local function alwaysHighRoll(n)
  return 99 % math.max(1, n or 1)
end

local function newBattle()
  local player = Mon.new(DATA, "MACHOP", 15, { dvs = perfect })
  player.moves = {
    { id = "TACKLE", pp = 35, maxPp = 35 },
    { id = "SWIFT", pp = 20, maxPp = 20 },
    { id = "VITAL_THROW", pp = 10, maxPp = 10 },
  }
  local wild = Mon.new(DATA, "MACHOP", 15, { dvs = perfect })
  wild.moves = { { id = "TACKLE", pp = 35, maxPp = 35 } }
  return Battle.new({ data = DATA, party = { player }, wild = wild,
    random = alwaysHighRoll }), player, wild
end

local function moveEvent(events)
  for _, event in ipairs(events or {}) do
    if event.kind == "move" then return event end
  end
  return nil
end

local function findText(events, text)
  for _, event in ipairs(events or {}) do
    if event.kind == "message" and event.text == text then return true end
  end
  return false
end

local function use(battle, player, wild, moveId)
  wild.hp = wild.maxHp
  battle:useMove(player, wild, moveId)
  local events = battle:takeEvents()
  return (moveEvent(events) or {}).missed == true, events
end

do
  local battle, player, wild = newBattle()
  battle.stages.enemy.evasion = Effects.MAX_STAGE
  T.check(use(battle, player, wild, "TACKLE"),
    "at +6 evasion a 100-accuracy move misses on a high roll")
  T.check(not use(battle, player, wild, "SWIFT"),
    "SWIFT ignores the evasion stage")
  T.check(wild.hp < wild.maxHp, "and the hit lands damage")
  T.check(not use(battle, player, wild, "VITAL_THROW"),
    "VITAL THROW carries the same effect")
end

do
  local battle, player, wild = newBattle()
  battle.stages.player.accuracy = -Effects.MAX_STAGE
  T.check(use(battle, player, wild, "TACKLE"),
    "at -6 accuracy a 100-accuracy move misses on a high roll")
  T.check(not use(battle, player, wild, "SWIFT"),
    "SWIFT ignores the accuracy stage")
end

do
  local battle, player, wild = newBattle()
  wild.item = "BRIGHTPOWDER"
  T.check(use(battle, player, wild, "TACKLE"),
    "BRIGHTPOWDER alone is enough to miss on a 99 roll")
  T.check(not use(battle, player, wild, "SWIFT"),
    "the `ret z` sits ahead of .BrightPowder too")
end

do
  local battle, player, wild = newBattle()
  battle:volatile(wild).vanished = true
  battle:volatile(wild).chargeMove = "FLY"
  local missed, events = use(battle, player, wild, "SWIFT")
  T.check(missed, "a FLY target still dodges SWIFT")
  T.check(findText(events, "MACHOP's attack missed!"), "with CheckHit's .Miss")
  T.eq(wild.hp, wild.maxHp, "and takes nothing")
end

do
  local battle, player, wild = newBattle()
  battle:volatile(wild).vanished = true
  battle:volatile(wild).chargeMove = "DIG"
  T.check(use(battle, player, wild, "SWIFT"), "a DIG target dodges it too")
end

do
  local battle, player, wild = newBattle()
  battle:volatile(wild).protect = true
  local missed, events = use(battle, player, wild, "SWIFT")
  T.check(missed, "PROTECT still turns SWIFT aside")
  T.check(findText(events, "MACHOP protected itself!"), "with .Protect's line")
  T.eq(wild.hp, wild.maxHp, "and takes nothing")
end

do
  local battle, player, wild = newBattle()
  T.check(not use(battle, player, wild, "SWIFT"),
    "with no stage moved at all SWIFT still hits")
  T.check(not use(battle, player, wild, "TACKLE"),
    "and a plain move only fails the 99 roll once a stage has moved")
end

T.finish("gen2 always-hit bug 1272")
