Merge pull request #1835 from campavao/rfc-0019-level-visible

engine: pokemon.level_visible, a level a mode can take off the screen (RFC 0019)
This commit is contained in:
bryanthaboi
2026-08-27 13:38:59 -04:00
committed by GitHub
7 changed files with 318 additions and 6 deletions
+3 -2
View File
@@ -16,6 +16,7 @@ local Damage = require("src.battle.Damage")
local EffectRegistry = require("src.battle.EffectRegistry")
local Experience = require("src.battle.Experience")
local Font = require("src.render.Font")
local LevelDisplay = require("src.ui.LevelDisplay")
local Logger = require("src.core.Logger")
local MoveEffects = require("src.battle.MoveEffects")
local Party = require("src.pokemon.Party")
@@ -6337,7 +6338,7 @@ function BattleState:drawHUDs(slide)
end
if self.enemy.shownStatus then
Font.draw(self:statusLabel({ status = self.enemy.shownStatus }), 40, 8)
else
elseif LevelDisplay.visible(self.enemy.mon, "battle.enemy", self.game) then
hudTile(0x6E, 32, 8) -- <LV>
Font.draw(tostring(self.enemy.mon.level), 40, 8)
end
@@ -6421,7 +6422,7 @@ function BattleState:drawHUDs(slide)
Font.draw(self.player.name, nameX(10, self.player.name), 56)
if self.player.shownStatus then
Font.draw(self:statusLabel({ status = self.player.shownStatus }), 120, 64)
else
elseif LevelDisplay.visible(self.player.mon, "battle.player", self.game) then
hudTile(0x6E, 112, 64) -- <LV>
Font.draw(tostring(self.player.mon.level), 120, 64)
end
+33
View File
@@ -0,0 +1,33 @@
-- Whether a Pokémon's level is printed on a screen that would normally
-- print it (RFC 0019).
--
-- Every Gen 1 screen that shows a level does it with the same pokered rule
-- -- home/pokemon.asm:335-345 PrintLevel: the <LV> tile, then the digits
-- left-aligned after it, with a level of 100 writing its third digit back
-- over the tile -- and each screen hand-rolls that rule against its own
-- coordinates. This module does not touch any of that. It answers one
-- question, in one place, so that a mode which wants the number off does
-- not have to know four call sites and a glyph code.
--
-- Default is true, and `Runtime.wantsHook` is checked first, so a build
-- with no mod wrapping the hook prints exactly what it always did and pays
-- nothing for the seam on a per-frame draw path.
--
-- `where` names the surface rather than the widget, because the number
-- means different things on different screens: on the battle HUD an
-- opponent's level is information about them, on the party and status
-- screens your own level is information about you. A mode can hide one and
-- keep the other.
local Runtime = require("src.mods.Runtime")
local LevelDisplay = {}
-- where: "battle.enemy" | "battle.player" | "party" | "summary"
function LevelDisplay.visible(mon, where, game)
if not Runtime.wantsHook("pokemon.level_visible") then return true end
return Runtime.call("pokemon.level_visible", function() return true end,
mon, { where = where, game = game }) ~= false
end
return LevelDisplay
+6 -1
View File
@@ -11,6 +11,7 @@
local Assets = require("src.render.Assets")
local Font = require("src.render.Font")
local LevelDisplay = require("src.ui.LevelDisplay")
local Logger = require("src.core.Logger")
local Runtime = require("src.mods.Runtime")
local Screens = require("src.ui.Screens")
@@ -794,7 +795,11 @@ function PartyMenu:draw()
-- level at column 13 (<LV> tile + digits, PrintLevel) AND the
-- status/FNT text at column 17 (PrintStatusCondition), like the
-- original rows -- statused mons keep their level display
if mon.level < 100 then
if not LevelDisplay.visible(mon, "party", self.game) then -- RFC 0019
-- the level column is simply empty; the status/FNT column at 17 is a
-- separate field and still prints, exactly as it does for a mon whose
-- level is on screen
elseif mon.level < 100 then
HudTiles.tile(0x6E, 104, y) -- <LV>
Font.draw(tostring(mon.level), 112, y)
else
+10 -3
View File
@@ -12,6 +12,7 @@ local Font = require("src.render.Font")
-- TypeChart.displayName maps it back to "PSYCHIC", like HallOfFame and the
-- battle move-type box already do (#214).
local TypeChart = require("src.battle.TypeChart")
local LevelDisplay = require("src.ui.LevelDisplay")
local Strings = require("src.core.Strings")
local Stats = require("src.pokemon.Stats")
local Status = require("src.battle.Status")
@@ -137,7 +138,9 @@ function SummaryMenu:draw()
if self.page == 1 then
-- level is page 1 only: StatusScreen2 opens with ClearScreenArea over
-- (9,2) 5x10 (status_screen.asm:303-305). #280
printLevel(14, 2, mon.level)
if LevelDisplay.visible(mon, "summary", self.game) then -- RFC 0019
printLevel(14, 2, mon.level)
end
drawLineBox(19, 1, 6, 10)
-- engine/pokemon/status_screen.asm:120-125
local PaletteFX = require("src.render.PaletteFX")
@@ -196,8 +199,12 @@ function SummaryMenu:draw()
local nextExp = mon.level < 100
and (Growth.expForLevel(def.growthRate, mon.level + 1) - mon.exp) or 0
Font.draw(("%7d"):format(math.max(0, nextExp)), 56, 48)
HudTiles.statusTile(0x70, 112, 48) -- '<to>' at (14,6), was missing (#280)
printLevel(16, 6, math.min(100, mon.level + 1))
if LevelDisplay.visible(mon, "summary", self.game) then -- RFC 0019
-- the '<to>' arrow is half a sentence without the level it points at,
-- so the pair is hidden together
HudTiles.statusTile(0x70, 112, 48) -- '<to>' at (14,6), was missing (#280)
printLevel(16, 6, math.min(100, mon.level + 1))
end
Font.drawBox(0, 8, 20, 10)
for i = 1, 4 do
local mv = mon.moves[i]