mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-01 07:40:29 -04:00
Merge pull request #2259 from Code-Grub/truecolor-menu-icons
This commit is contained in:
@@ -1819,14 +1819,19 @@ end
|
||||
|
||||
R.icons = {
|
||||
semantics = "record", target = "icons.bySpecies",
|
||||
value = f.union{ f.str, f.rec{ image = f.path, frames = f.opt(f.int(1)) } },
|
||||
-- trueColor is the same 4-shade opt-out the pokemon and trainers records
|
||||
-- carry: flagged art skips the OBP bake and reports its rect so the SGB
|
||||
-- pass re-blits it unshaded, instead of being bucketed by red channel.
|
||||
value = f.union{ f.str, f.rec{ image = f.path, frames = f.opt(f.int(1)),
|
||||
trueColor = f.opt(f.bool) } },
|
||||
gen2Value = f.union{
|
||||
-- the assignment form: a species id mapped to a sheet name
|
||||
f.str,
|
||||
-- the sheet form: width/height are the sheet's pixel size, and every
|
||||
-- vanilla sheet is a 16x32 two-frame strip
|
||||
f.rec{ id = f.opt(f.str), index = f.opt(f.int(0, 255)), image = f.path,
|
||||
width = f.int(1), height = f.int(1), frames = f.int(1) },
|
||||
width = f.int(1), height = f.int(1), frames = f.int(1),
|
||||
trueColor = f.opt(f.bool) },
|
||||
},
|
||||
gen2Extra = function(id, value)
|
||||
if gen2IconIsSheet(id) then
|
||||
|
||||
+14
-6
@@ -113,11 +113,17 @@ end
|
||||
-- Resolve a party-menu icon image path for `mon`.
|
||||
-- vanillaPath is the path PartyMenu already picked from icons.bySpecies /
|
||||
-- def.icon / icons.byDex; the hook may replace it.
|
||||
-- Returns path (possibly nil).
|
||||
-- opts.trueColor is the icon record's own flag: an icons.bySpecies table
|
||||
-- entry may carry one, the way a pokemon record carries it for a battle pic.
|
||||
-- A hook may also set ctx.trueColor, to flag art it substitutes for art that
|
||||
-- carried no flag -- the same ctx contract pokemon.sprite has, and the reason
|
||||
-- the flag is read back off ctx rather than trusted from opts alone.
|
||||
-- Returns path (possibly nil), trueColor.
|
||||
function Sprites.iconPath(data, mon, vanillaPath, opts)
|
||||
opts = opts or {}
|
||||
local trueColor = opts.trueColor and true or false
|
||||
if not vanillaPath and not Runtime.wantsHook("pokemon.icon") then
|
||||
return vanillaPath
|
||||
return vanillaPath, trueColor
|
||||
end
|
||||
local species = mon and mon.species
|
||||
local ctx = {
|
||||
@@ -126,12 +132,14 @@ function Sprites.iconPath(data, mon, vanillaPath, opts)
|
||||
name = opts.name,
|
||||
data = data,
|
||||
kind = "icon",
|
||||
trueColor = trueColor,
|
||||
}
|
||||
if not Runtime.wantsHook("pokemon.icon") then return vanillaPath end
|
||||
if not Runtime.wantsHook("pokemon.icon") then return vanillaPath, trueColor end
|
||||
local hooked = Runtime.call("pokemon.icon", samePath, vanillaPath, ctx)
|
||||
if type(hooked) == "string" and hooked ~= "" then return hooked end
|
||||
if hooked == nil or hooked == false then return nil end
|
||||
return vanillaPath
|
||||
trueColor = ctx.trueColor and true or false
|
||||
if type(hooked) == "string" and hooked ~= "" then return hooked, trueColor end
|
||||
if hooked == nil or hooked == false then return nil, false end
|
||||
return vanillaPath, trueColor
|
||||
end
|
||||
|
||||
return Sprites
|
||||
|
||||
+23
-4
@@ -221,18 +221,20 @@ function PartyMenu.drawIcon(game, mon, x, y, selected, counter, forceAlt)
|
||||
-- change its menu icon.
|
||||
local entry = (icons.bySpecies and icons.bySpecies[mon.species])
|
||||
or (def and def.icon)
|
||||
local name, path
|
||||
local name, path, trueColor
|
||||
if type(entry) == "string" then
|
||||
name = entry
|
||||
path = icons.icons and icons.icons[entry]
|
||||
elseif type(entry) == "table" then
|
||||
path = entry.image
|
||||
trueColor = entry.trueColor
|
||||
end
|
||||
if not path then
|
||||
name = def and def.dex and icons.byDex and icons.byDex[def.dex]
|
||||
path = name and icons.icons and icons.icons[name]
|
||||
end
|
||||
path = require("src.pokemon.Sprites").iconPath(game.data, mon, path, { name = name })
|
||||
path, trueColor = require("src.pokemon.Sprites")
|
||||
.iconPath(game.data, mon, path, { name = name, trueColor = trueColor })
|
||||
if not path then return end
|
||||
-- Built-in icon classes are DMG 2bpp OBJ art and get the OBP0 bake; a
|
||||
-- mod's own image (an entry table rather than an icon name) is authored
|
||||
@@ -240,13 +242,20 @@ function PartyMenu.drawIcon(game, mon, x, y, selected, counter, forceAlt)
|
||||
-- split PartyMenu.mirrorsIcon makes for the OAM mirror. Both live in one
|
||||
-- cache under different keys, so a mod pointing a table entry at a
|
||||
-- built-in path still gets its unbaked copy. #274
|
||||
local key = name and (path .. "#obp") or path
|
||||
--
|
||||
-- trueColor art is unbaked for the same reason it is unshaded: obpIcon is
|
||||
-- itself a 4-shade remap keyed off the red channel, so running it over
|
||||
-- full-colour art destroys exactly what the flag asks to keep. The flag
|
||||
-- overrides `name`, because a pokemon.icon hook can substitute full-colour
|
||||
-- art for a path that resolved to a built-in class and still carries one.
|
||||
local baked = name ~= nil and not trueColor
|
||||
local key = baked and (path .. "#obp") or path
|
||||
if iconImages[key] == nil then
|
||||
-- resolve through Assets so an overrides/ or transform-derived icon
|
||||
-- (e.g. a per-species image at assets/generated/icons/<name>.png) is
|
||||
-- picked up the same way battle sprites are
|
||||
local ok, img
|
||||
if name then
|
||||
if baked then
|
||||
ok, img = pcall(obpIcon, path)
|
||||
else
|
||||
ok, img = pcall(love.graphics.newImage, Assets.resolve(path))
|
||||
@@ -290,6 +299,16 @@ function PartyMenu.drawIcon(game, mon, x, y, selected, counter, forceAlt)
|
||||
-- whatever size the file is (unchanged path)
|
||||
love.graphics.draw(img, x, y)
|
||||
end
|
||||
-- Report the covering rect so Renderer:endFrame can re-blit it unshaded
|
||||
-- over the colorized pass. Every branch above lays a frame into a 16x16
|
||||
-- OAM block except the last, which draws the file at its own size.
|
||||
-- No vanilla icon record sets the flag, so this stays dead code without a
|
||||
-- mod and the zone lists are exactly the ones the states returned.
|
||||
if trueColor then
|
||||
local mw, mh = 16, 16
|
||||
if not (PartyMenu.mirrorsIcon(name) or ih > 16) then mw, mh = iw, ih end
|
||||
require("src.render.PaletteFX").markTrueColor(x, y, mw, mh)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -143,7 +143,8 @@ local savedLoaded = {}
|
||||
for _, name in ipairs({ "src.render.PaletteFX", "src.render.Renderer",
|
||||
"src.render.Font", "src.render.Assets",
|
||||
"src.render.SpriteRenderer",
|
||||
"src.render.TileRenderer", "src.mods.Loader" }) do
|
||||
"src.render.TileRenderer", "src.mods.Loader",
|
||||
"src.ui.PartyMenu" }) do
|
||||
savedLoaded[name] = package.loaded[name]
|
||||
package.loaded[name] = nil
|
||||
end
|
||||
@@ -157,6 +158,7 @@ local Font = require("src.render.Font")
|
||||
local Hooks = require("src.mods.Hooks")
|
||||
local HudTiles = require("src.render.HudTiles")
|
||||
local PaletteFX = require("src.render.PaletteFX")
|
||||
local PartyMenu = require("src.ui.PartyMenu")
|
||||
local Registry = require("src.mods.Registry")
|
||||
local Renderer = require("src.render.Renderer")
|
||||
local Runtime = require("src.mods.Runtime")
|
||||
@@ -409,6 +411,51 @@ check(BattleState.trainerTrueColor(trainerPicData,
|
||||
"a basePic reuse inherits the base portrait's trueColor flag")
|
||||
PaletteFX.setMode(savedColors)
|
||||
|
||||
-- ------- trueColor: mon menu icons take the same opt-out
|
||||
-- Icons were the one art class that never had it. Seven records in
|
||||
-- Schemas carry trueColor; icons.bySpecies did not, and Sprites.iconPath
|
||||
-- returned a path alone where every sibling returns path, trueColor. So
|
||||
-- no icon draw site could know its art was full colour, none reported a
|
||||
-- rect, and on any screen declaring an SGB zone the art met the shade-remap
|
||||
-- shader -- which buckets on the RED channel, so a warm pixel clears 0.83
|
||||
-- and comes back c0, white in every named palette.
|
||||
do
|
||||
local iconData = {
|
||||
pokemon = { PLAINMON = { dex = 1 }, FULLMON = { dex = 2 } },
|
||||
icons = {
|
||||
icons = { QUADRUPED = "assets/generated/icons/mon/quadruped.png" },
|
||||
byDex = { [1] = "QUADRUPED", [2] = "QUADRUPED" },
|
||||
bySpecies = {
|
||||
FULLMON = { image = "mods/skin/full_icon.png", trueColor = true },
|
||||
},
|
||||
},
|
||||
}
|
||||
local game = { data = iconData }
|
||||
|
||||
PaletteFX.clearTrueColor()
|
||||
PaletteFX.setPass("ui")
|
||||
|
||||
PartyMenu.drawIcon(game, { species = "PLAINMON" }, 24, 40, false, 0, false)
|
||||
check(#PaletteFX.trueColorRects("ui") == 0,
|
||||
"a built-in icon class reports no trueColor rect")
|
||||
|
||||
resetLog()
|
||||
PartyMenu.drawIcon(game, { species = "FULLMON" }, 24, 40, false, 0, false)
|
||||
local rects = PaletteFX.trueColorRects("ui")
|
||||
check(#rects == 1 and rects[1].x == 24 and rects[1].y == 40
|
||||
and rects[1].w == 16 and rects[1].h == 16,
|
||||
"a trueColor icon reports its rectangle so the shader skips it")
|
||||
-- the OBP bake is itself a 4-shade remap (obpIcon keys off the red
|
||||
-- channel exactly as the shader does), so full-colour art must not take
|
||||
-- it either. A baked icon is built from ImageData and carries .data;
|
||||
-- art loaded straight from its path does not.
|
||||
check(log.draws[1] and log.draws[1].what and log.draws[1].what.data == nil,
|
||||
"and it is not run through the OBP bake")
|
||||
|
||||
PaletteFX.setPass(nil)
|
||||
PaletteFX.clearTrueColor()
|
||||
end
|
||||
|
||||
-- ------- trueColor: the colors == false zone sentinel
|
||||
|
||||
check(PaletteFX.zone(nil, 0, 0, 1, 1) == nil, "nil colors is still no zone")
|
||||
|
||||
@@ -448,6 +448,29 @@ do
|
||||
check(Sprites.iconPath(data, mon, "assets/generated/icons/mon/quadruped.png")
|
||||
== "assets/generated/icons/mon/quadruped.png",
|
||||
"unwrapped pokemon.icon is vanilla")
|
||||
|
||||
-- the icon seam carries the same trueColor contract the sprite seam does:
|
||||
-- a hook substituting full-colour art flags it on ctx, and the flag comes
|
||||
-- back beside the path so the draw site can skip the OBP bake and report
|
||||
-- the rect. Without it a pack's icons are bucketed by red channel and
|
||||
-- come out white under any screen that declares an SGB zone.
|
||||
unsub = wrap("pokemon.icon", function(next, p, ctx)
|
||||
if ctx.mon and ctx.mon.skin == "alt" then
|
||||
ctx.trueColor = true
|
||||
return "mods/skinpicker/assets/pika_icon_hd.png"
|
||||
end
|
||||
return next(p, ctx)
|
||||
end)
|
||||
local hdIcon, hdTC = Sprites.iconPath(data, mon,
|
||||
"assets/generated/icons/mon/quadruped.png", { name = "QUADRUPED" })
|
||||
check(hdIcon == "mods/skinpicker/assets/pika_icon_hd.png" and hdTC == true,
|
||||
"pokemon.icon can swap path + trueColor from mon state")
|
||||
local plainIcon, plainTC = Sprites.iconPath(data, { species = "PIKACHU" },
|
||||
"assets/generated/icons/mon/quadruped.png", { name = "QUADRUPED" })
|
||||
check(plainIcon == "assets/generated/icons/mon/quadruped.png"
|
||||
and plainTC == false,
|
||||
"and an unflagged icon stays unflagged through the same hook")
|
||||
unsub()
|
||||
end
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user