mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-21 13:09:54 +02:00
feat(mods): expose map tile shading
This commit is contained in:
+46
-3
@@ -6,6 +6,7 @@
|
|||||||
-- stays unsupported; anything a mod legitimately needs belongs here.
|
-- stays unsupported; anything a mod legitimately needs belongs here.
|
||||||
|
|
||||||
local Logger = require("src.core.Logger")
|
local Logger = require("src.core.Logger")
|
||||||
|
local Assets = require("src.render.Assets")
|
||||||
local MapLoader = require("src.world.MapLoader")
|
local MapLoader = require("src.world.MapLoader")
|
||||||
local Runtime = require("src.mods.Runtime")
|
local Runtime = require("src.mods.Runtime")
|
||||||
|
|
||||||
@@ -13,6 +14,45 @@ local WorldAPI = {}
|
|||||||
WorldAPI.__index = WorldAPI
|
WorldAPI.__index = WorldAPI
|
||||||
|
|
||||||
local NO_OVERWORLD = "no overworld"
|
local NO_OVERWORLD = "no overworld"
|
||||||
|
local overviewShades = {}
|
||||||
|
|
||||||
|
Assets.register(function() overviewShades = {} end)
|
||||||
|
|
||||||
|
local function mapTileRows(map)
|
||||||
|
local tileset = map.tileset
|
||||||
|
if not (tileset and tileset.image and tileset.tilesPerRow) then return nil end
|
||||||
|
local cached = overviewShades[tileset.image]
|
||||||
|
if not cached then
|
||||||
|
local ok, pixels = pcall(Assets.imageData, tileset.image)
|
||||||
|
if not ok then return nil end
|
||||||
|
cached = { pixels = pixels, shades = {} }
|
||||||
|
overviewShades[tileset.image] = cached
|
||||||
|
end
|
||||||
|
local rows, perRow = {}, tileset.tilesPerRow
|
||||||
|
for ty = 0, map.heightCells * 2 - 1 do
|
||||||
|
local row = {}
|
||||||
|
for tx = 0, map.widthCells * 2 - 1 do
|
||||||
|
local tile = map:tileAt(tx, ty)
|
||||||
|
local shade = cached.shades[tile]
|
||||||
|
if shade == nil then
|
||||||
|
local sum = 0
|
||||||
|
local ox, oy = (tile % perRow) * 8, math.floor(tile / perRow) * 8
|
||||||
|
for py = 0, 7 do
|
||||||
|
for px = 0, 7 do
|
||||||
|
local r, g, b = cached.pixels:getPixel(ox + px, oy + py)
|
||||||
|
sum = sum + r * 0.2126 + g * 0.7152 + b * 0.0722
|
||||||
|
end
|
||||||
|
end
|
||||||
|
shade = tostring(math.max(0, math.min(3,
|
||||||
|
math.floor((1 - sum / 64) * 3 + 0.5))))
|
||||||
|
cached.shades[tile] = shade
|
||||||
|
end
|
||||||
|
row[#row + 1] = shade
|
||||||
|
end
|
||||||
|
rows[#rows + 1] = table.concat(row)
|
||||||
|
end
|
||||||
|
return rows
|
||||||
|
end
|
||||||
|
|
||||||
function WorldAPI.new(game, modId)
|
function WorldAPI.new(game, modId)
|
||||||
return setmetatable({ game = game, modId = modId }, WorldAPI)
|
return setmetatable({ game = game, modId = modId }, WorldAPI)
|
||||||
@@ -44,8 +84,8 @@ function WorldAPI:current()
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- A compact, read-only view of the active map for minimaps and companion UIs.
|
-- A compact, read-only view of the active map for minimaps and companion UIs.
|
||||||
-- Rows use " " for blocked terrain, "." for walkable land, "~" for water
|
-- `rows` describes collision terrain; optional `tileRows` reduces each real
|
||||||
-- and "+" for a door or warp.
|
-- 8x8 map tile to its average Game Boy shade ("0" lightest, "3" darkest).
|
||||||
function WorldAPI:mapOverview()
|
function WorldAPI:mapOverview()
|
||||||
local ow = self:overworld()
|
local ow = self:overworld()
|
||||||
if not ow or not ow.map then return nil, NO_OVERWORLD end
|
if not ow or not ow.map then return nil, NO_OVERWORLD end
|
||||||
@@ -59,8 +99,11 @@ function WorldAPI:mapOverview()
|
|||||||
end
|
end
|
||||||
rows[#rows + 1] = table.concat(row)
|
rows[#rows + 1] = table.concat(row)
|
||||||
end
|
end
|
||||||
|
local tileRows = mapTileRows(map)
|
||||||
return { mapId = map.id, width = map.widthCells,
|
return { mapId = map.id, width = map.widthCells,
|
||||||
height = map.heightCells, rows = rows }
|
height = map.heightCells, rows = rows, tileRows = tileRows,
|
||||||
|
tileWidth = tileRows and map.widthCells * 2,
|
||||||
|
tileHeight = tileRows and map.heightCells * 2 }
|
||||||
end
|
end
|
||||||
|
|
||||||
-- opts.arrive = "fly" | "teleport" picks the arrival FX; anything else
|
-- opts.arrive = "fly" | "teleport" picks the arrival FX; anything else
|
||||||
|
|||||||
@@ -1,8 +1,16 @@
|
|||||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||||
|
|
||||||
local T = require("tests.harness")
|
local T = require("tests.harness")
|
||||||
|
local Assets = require("src.render.Assets")
|
||||||
local WorldAPI = require("src.world.WorldAPI")
|
local WorldAPI = require("src.world.WorldAPI")
|
||||||
|
|
||||||
|
Assets.imageData = function()
|
||||||
|
return { getPixel = function(_, x)
|
||||||
|
local shade = x < 8 and 1 or 0
|
||||||
|
return shade, shade, shade, 1
|
||||||
|
end }
|
||||||
|
end
|
||||||
|
|
||||||
local api = WorldAPI.new({ stack = { states = {} } }, "tester")
|
local api = WorldAPI.new({ stack = { states = {} } }, "tester")
|
||||||
local overview, err = api:mapOverview()
|
local overview, err = api:mapOverview()
|
||||||
T.eq(overview, nil, "map overview is unavailable outside the overworld")
|
T.eq(overview, nil, "map overview is unavailable outside the overworld")
|
||||||
@@ -12,6 +20,7 @@ local map = { id = "TEST_MAP", widthCells = 2, heightCells = 2 }
|
|||||||
function map:isWarpTileCell(x, y) return x == 1 and y == 0 end
|
function map:isWarpTileCell(x, y) return x == 1 and y == 0 end
|
||||||
function map:isWaterCell(x, y) return x == 0 and y == 1 end
|
function map:isWaterCell(x, y) return x == 0 and y == 1 end
|
||||||
function map:isWalkableCell(x, y) return x == 0 and y == 0 end
|
function map:isWalkableCell(x, y) return x == 0 and y == 0 end
|
||||||
|
function map:tileAt(x) return x % 2 end
|
||||||
|
|
||||||
api = WorldAPI.new({ stack = { states = {
|
api = WorldAPI.new({ stack = { states = {
|
||||||
{ isOverworld = true, map = map },
|
{ isOverworld = true, map = map },
|
||||||
@@ -22,5 +31,12 @@ T.eq(overview.width, 2, "map overview reports its width")
|
|||||||
T.eq(overview.height, 2, "map overview reports its height")
|
T.eq(overview.height, 2, "map overview reports its height")
|
||||||
T.eq(overview.rows[1], ".+", "walkable land and warps are distinct")
|
T.eq(overview.rows[1], ".+", "walkable land and warps are distinct")
|
||||||
T.eq(overview.rows[2], "~ ", "water and blocked terrain are distinct")
|
T.eq(overview.rows[2], "~ ", "water and blocked terrain are distinct")
|
||||||
|
T.eq(overview.tileRows, nil, "tile overview is optional")
|
||||||
|
|
||||||
|
map.tileset = { image = "test.png", tilesPerRow = 2 }
|
||||||
|
overview = api:mapOverview()
|
||||||
|
T.eq(overview.tileWidth, 4, "tile overview reports its width")
|
||||||
|
T.eq(overview.tileHeight, 4, "tile overview reports its height")
|
||||||
|
T.eq(overview.tileRows[1], "0303", "tile overview preserves map shading")
|
||||||
|
|
||||||
T.finish("world map overview")
|
T.finish("world map overview")
|
||||||
|
|||||||
Reference in New Issue
Block a user