-- Audio check for the silent Pewter NIDORAN (#247).  pokered
-- scripts/PewterNidoranHouse.asm types the line, then `ld a, NIDORAN_M / call
-- PlayCry / call WaitForSoundToFinish`, and the box still waits for a button;
-- the port ported the dialogue row and not the cry row.
-- Don't add POKEPORT_SPEED: audio runs on its own real-time accumulator.
--   POKEPORT_DRIVER=tests/drivers/nidoran_cry_bug247_test.lua POKEPORT_IDENTITY=bug247 POKEPORT_TOUCH=0 POKEPORT_VERSION=red love .
return function(game)
  local U = dofile("tests/drivers/util.lua")
  local mapScripts = require("data.scripts.init")

  local MAP = "PEWTER_NIDORAN_HOUSE"
  local NPC = "PEWTERNIDORANHOUSE_NIDORAN"
  local TEXT = "TEXT_PEWTERNIDORANHOUSE_NIDORAN"
  local SPECIES = "NIDORAN_M"

  local function check(label, ok)
    U.log(ok and "PASS" or "FAIL", label)
    return ok
  end

  -- Commands.play_cry only stashes ctx.pendingCry; Commands.show_text is what
  -- turns it into the box's opts.auto, so the two rows have to be adjacent and
  -- in that order or the cry goes to the wrong box, or to none at all
  local rows = mapScripts.talkScript(MAP, TEXT)
  local cryAt, textAt
  for i, row in ipairs(rows or {}) do
    if row[1] == "play_cry" and not cryAt then cryAt = i end
    if row[1] == "show_text" and not textAt then textAt = i end
  end
  local cry = cryAt and rows[cryAt]
  check("talk script carries a play_cry row", cry ~= nil)
  check("cry species is " .. SPECIES, cry ~= nil and cry[2] == SPECIES)
  -- the third arg is play_cry's waitForButton form, which keeps DisplayTextID's
  -- trailing WaitForTextScrollButtonPress instead of letting the box pop itself
  -- the instant the cry goes quiet
  check("cry row keeps the button wait", cry ~= nil and cry[3] == true)
  check("play_cry sits immediately before show_text",
        cryAt ~= nil and textAt == cryAt + 1)

  -- Sound.playCry reads data.audio.cries[species]; a missing or misspelled key
  -- is a silent no-op with no error, which sounds exactly like the bug
  local cries = game.data.audio and game.data.audio.cries
  check("data.audio.cries." .. SPECIES .. " resolves",
        cries ~= nil and cries[SPECIES] ~= nil)

  local vol = game.save.options and game.save.options.sfxVol
  U.log("audio device present:", love.audio ~= nil,
        "  SFX VOL (0-7):", tostring(vol))
  if not love.audio or vol == 0 then
    U.log("WARNING: sound output is off, so nothing below will be audible;",
          "raise SFX VOL in OPTION first")
  end

  -- pokered data/maps/objects/PewterNidoranHouse.asm puts the NIDORAN on (4, 5)
  -- facing LEFT at the little boy on (3, 5), so the free approach cell is the
  -- floor directly below it
  U.teleport(game, MAP, 4, 6, "up")
  U.wait(10)

  local function nidoranIn(ow)
    for _, n in ipairs(ow.npcs or {}) do
      if n.def and n.def.name == NPC then return n end
    end
    return nil
  end

  -- re-reads game.overworld every call: the fallback below teleports again and
  -- that rebuilds the state and its npc list
  local function facingTheMon()
    local ow = game.overworld
    local mon = ow and nidoranIn(ow)
    if not mon then return false end
    local fx, fy = ow.player:facingCell()
    return ow:npcAtCell(fx, fy) == mon
  end

  local ow = game.overworld
  local mon = ow and nidoranIn(ow)
  check("NIDORAN object loaded on " .. MAP, mon ~= nil)

  if mon and not facingTheMon() then
    -- a map edit or a mod moved the object: fall back to any free walkable
    -- neighbour.  {dx, dy, facing} is the offset from the mon to the stand cell
    -- plus the direction that looks back at it, so +1 on x means left.
    local sides = {
      { 0, 1, "up" }, { 1, 0, "left" }, { -1, 0, "right" }, { 0, -1, "down" },
    }
    for _, s in ipairs(sides) do
      local cx, cy = mon.cellX + s[1], mon.cellY + s[2]
      if ow.map:isWalkableCell(cx, cy) and not ow:npcAtCell(cx, cy) then
        U.log("approach cell (4, 6) is blocked, standing on",
              cx, cy, "facing", s[3])
        U.teleport(game, MAP, cx, cy, s[3])
        U.wait(10)
        break
      end
    end
  end
  check("player is standing in front of the NIDORAN", facingTheMon())

  U.log("Press A to talk to the NIDORAN in front of you.")
  U.log("The box types \"NIDORAN: Bowbow!\", then the cry sounds once after the")
  U.log("last character lands and the box waits for A or B; under #247 the line")
  U.log("typed and nothing followed.  The boy on the left is the silent control.")

  while true do
    coroutine.yield()
  end
end
