DramaticShapeVoxelMod archival mirror · read-only
lib/ExpPanel.lua

ExpPanel.lua

lib/ExpPanel.lua3.4 KB100644c967027424
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
-- LET'S GO: the whole party's experience, on one card.
--
-- Sharing experience to everybody has a cost the original game never had
-- to pay: six Pokemon means six "X gained N EXP. Points!" boxes for every
-- knockout, each needing its own press. That is the same information the
-- player wanted, delivered in the most tiring possible way -- and it is
-- worse than a wall of text, because the numbers arrive one at a time so
-- the one thing a shared payout is FOR (comparing them: the little one
-- gained five times what the big one did) can never be seen at once.
--
-- So the per-Pokemon lines are suppressed and this card is shown in their
-- place: one box, one press, every gain side by side, with a level-up
-- called out on the row it happened to. What the card cannot cover still
-- plays as it always did -- "X grew to level 6!", the stats window, and
-- any move learned -- because those are events, not a tally.
--
-- Drawn with the engine's own font and box, in the GB's own frame, so it
-- sits in a staged 3D battle exactly like every other battle panel.

-- the mod namespace (see main.lua): V.require loads a sibling module
local V = ...

local ExpPanel = {}
ExpPanel.__index = ExpPanel

local Font = nil
local function font()
  if Font ~= nil then return Font or nil end
  local ok, F = pcall(require, "src.render.Font")
  Font = ok and F or false
  return Font or nil
end

-- the GB frame, in 8-pixel tiles
local TILE = 8
local COLS, ROWS = 20, 18
local ROW_H = 12          -- pixels between listed Pokemon

-- `rows` is filled by the award loop and read HERE, at draw time: the
-- loop runs to completion long before the battle queue reaches this
-- panel, so the table is always complete by the time it is shown.
function ExpPanel.new(game, rows)
  return setmetatable({ game = game, rows = rows or {}, t = 0 }, ExpPanel)
end

function ExpPanel:update(dt)
  self.t = (self.t or 0) + (dt or 0)
  local input = self.game and self.game.input
  if not input then return end
  -- a beat of deafness: the press that dismissed whatever came before
  -- must not dismiss this card in the same breath
  if self.t < 0.12 then return end
  if input:wasPressed("a") or input:wasPressed("b") then
    self.game.stack:pop()
    if self.onDone then self.onDone() end
  end
end

function ExpPanel:draw()
  local F = font()
  if not F then return end
  local rows = self.rows or {}
  local n = #rows
  if n == 0 then return end

  -- bottom-anchored and only as tall as it needs to be, so a two-Pokemon
  -- party does not black out the fight behind it
  local th = 3 + math.ceil(n * ROW_H / TILE)
  th = math.min(th, ROWS - 1)
  local ty = ROWS - th
  F.drawBox(0, ty, COLS, th)

  love.graphics.setColor(0, 0, 0, 1)
  local x0 = TILE
  local y0 = (ty + 1) * TILE + 2
  F.draw("EXP GAINED", x0, y0)

  for i, r in ipairs(rows) do
    local y = y0 + ROW_H + (i - 1) * ROW_H
    if y > (ROWS - 1) * TILE then break end
    local mon = r.mon
    local name = mon.nickname
                 or (self.game.data.pokemon[mon.species] or {}).name
                 or tostring(mon.species)
    F.draw(name, x0, y)
    -- a level-up is called out where it happened rather than left to the
    -- message that follows, so the card reads as the whole story
    if (r.to or 0) > (r.from or 0) then
      local up = ("L%d"):format(r.to)
      F.draw(up, 108 - F.width(up), y)
    end
    local amt = ("+%d"):format(r.gained or 0)
    F.draw(amt, 152 - F.width(amt), y)
  end
  love.graphics.setColor(1, 1, 1, 1)
end

return ExpPanel