2023-04-28 17:35:52 +00:00
|
|
|
--Load and save the bmp formats used by DEFCON.
|
2023-07-28 05:17:00 +00:00
|
|
|
local t = {}
|
2023-07-28 23:23:08 +00:00
|
|
|
local love = assert( love )
|
2023-07-28 05:17:00 +00:00
|
|
|
local lfs = love.filesystem
|
2023-04-28 17:35:52 +00:00
|
|
|
|
2023-07-28 05:17:00 +00:00
|
|
|
--FFI bit-twiddling stuff.
|
|
|
|
local ffi = require 'ffi'
|
|
|
|
local bit = require 'bit'
|
2023-04-28 17:35:52 +00:00
|
|
|
|
2024-04-29 01:21:32 +00:00
|
|
|
local function getHeader( filename )
|
|
|
|
local offset = love.data.unpack( "<I4", assert(love.filesystem.read( "data", filename, 14 )), 11 )
|
|
|
|
local header, size = assert( love.filesystem.read( filename, offset ) )
|
2024-05-25 02:43:39 +00:00
|
|
|
print( "BMP HEADER", filename, size, "\n", string.byte( header, 1, size ) )
|
2024-04-29 01:21:32 +00:00
|
|
|
return header
|
|
|
|
end
|
|
|
|
|
|
|
|
local formats = {
|
|
|
|
["512rgb24"] = {
|
|
|
|
header = getHeader( "data/earth/africa.bmp" ),
|
|
|
|
encode = function( r ) return math.floor( r * 255 ) end,
|
|
|
|
bytesPerPixel = 3,
|
|
|
|
w = 512,
|
|
|
|
h = 285,
|
|
|
|
},
|
|
|
|
["512r4"] = {
|
|
|
|
header = getHeader( "data/earth/sailable.bmp" ),
|
|
|
|
encode = function( r ) return math.floor( r * 255 / 16 ) end,
|
|
|
|
bytesPerPixel = 0.5,
|
|
|
|
w = 512,
|
|
|
|
h = 285,
|
|
|
|
},
|
|
|
|
["800r4"] = {
|
|
|
|
header = getHeader( "data/earth/travel_nodes.bmp" ),
|
|
|
|
encode = function( r ) return math.floor( r * 255 / 16 ) end,
|
|
|
|
bytesPerPixel = 0.5,
|
2024-05-25 02:43:39 +00:00
|
|
|
w = 800,
|
|
|
|
h = 400,
|
2024-04-29 01:21:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function t.header( format )
|
|
|
|
return formats[format] and formats[format].header
|
|
|
|
end
|
|
|
|
|
2023-07-28 05:17:00 +00:00
|
|
|
function t.load( filename )
|
2023-07-28 23:23:08 +00:00
|
|
|
local imgd = love.image.newImageData( filename )
|
|
|
|
print( "LOADING BITMAP: ", filename, imgd:getSize(), imgd:getFormat(), imgd:getDimensions() )
|
2023-08-28 23:12:43 +00:00
|
|
|
local img = love.graphics.newImage( imgd )
|
|
|
|
img:setFilter( "nearest", "nearest" )
|
|
|
|
return img, imgd
|
2023-07-28 05:17:00 +00:00
|
|
|
end
|
|
|
|
|
2024-04-29 01:21:32 +00:00
|
|
|
--maps an array of 1-byte pixel values (numbers 0 to 15 inclusive)
|
|
|
|
--to an array of half-byte pixel values (which can be concatenated into a string)
|
|
|
|
--BUT don't touch the first element (which is assumed to be a header or something)
|
|
|
|
local function foldByteArray( bytes )
|
|
|
|
local nybbles = { bytes[1] }
|
|
|
|
for j = 2, #bytes / 2 do
|
|
|
|
local a, b = bytes[ 2 * j - 2 ], bytes[ 2 * j - 1 ]
|
|
|
|
nybbles[j] = string.char( 16 * a + b )
|
|
|
|
end
|
|
|
|
return nybbles
|
|
|
|
end
|
|
|
|
|
|
|
|
local function packTwentyFour( bytes )
|
|
|
|
local twentyFour = { bytes[1] }
|
|
|
|
for j = 2, #bytes do
|
2024-05-25 02:43:39 +00:00
|
|
|
twentyFour[j] = string.char( bytes[j], bytes[j], bytes[j] )
|
2024-04-29 01:21:32 +00:00
|
|
|
end
|
2024-05-25 02:43:39 +00:00
|
|
|
return twentyFour
|
2024-04-29 01:21:32 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
function t.save( data, format )
|
2024-04-21 14:10:53 +00:00
|
|
|
local w, h = data:getDimensions()
|
2024-04-29 01:21:32 +00:00
|
|
|
format = assert( formats[format] )
|
|
|
|
local bytes = { format.header }
|
|
|
|
format.byte = 0
|
|
|
|
local i = 2
|
2024-05-25 02:43:39 +00:00
|
|
|
for y = h - 1, 0, -1 do
|
|
|
|
for x = 0, w - 1 do
|
2024-04-29 01:21:32 +00:00
|
|
|
bytes[i] = format.encode( data:getPixel(x, y) )
|
|
|
|
i = i + 1
|
2024-04-28 01:38:23 +00:00
|
|
|
end
|
|
|
|
end
|
2024-04-29 01:21:32 +00:00
|
|
|
if format.bytesPerPixel < 1 then bytes = foldByteArray( bytes )
|
2024-05-25 02:43:39 +00:00
|
|
|
else bytes = packTwentyFour( bytes ) end
|
2024-04-29 01:21:32 +00:00
|
|
|
return table.concat( bytes )
|
2023-07-28 05:17:00 +00:00
|
|
|
end
|
|
|
|
|
2024-04-29 01:21:32 +00:00
|
|
|
--takes an array of world-space points in [-180, 180] x [-100, 100]
|
|
|
|
--e.g { { x = 0.5, y = 0.5 }, { x = 0.4, y = 0.6 }, }, etc.
|
|
|
|
function t.savePoints( points, format )
|
|
|
|
format = assert( formats[format] )
|
|
|
|
|
|
|
|
--set up bitmap as an array of pixels
|
|
|
|
local w, h = format.w, format.h
|
|
|
|
local size = 2 + format.w * format.h
|
|
|
|
local bitmap = { format.header }
|
|
|
|
for j = 2, size do bitmap[j] = 0 end
|
|
|
|
|
|
|
|
--this is black-and-white only. easy case
|
|
|
|
if format.bytesPerPixel < 1 then
|
|
|
|
for i, point in ipairs( points ) do
|
|
|
|
local wx, wy = point.x, point.y
|
2024-05-25 02:43:39 +00:00
|
|
|
-- get bitmap coordinates:
|
|
|
|
-- N.B., y's divided by 190 rather than 200 because of an idiosyncracy in the travel node image
|
|
|
|
local x, y = math.floor(format.w * (wx + 180) / 360), math.floor(format.h * (wy + 100) / 190)
|
|
|
|
|
2024-04-29 01:21:32 +00:00
|
|
|
--get index into byte array
|
2024-05-25 02:43:39 +00:00
|
|
|
local idx = 2 + y * format.w + x
|
|
|
|
print( "SAVING POINT:", wx, wy, x, format.h - y - 1, idx )
|
|
|
|
bitmap[idx] = 1 --white: this header uses an indexed palette
|
2024-04-29 01:21:32 +00:00
|
|
|
end
|
|
|
|
--now map pixels to 4-bit strings, slap on the header, and pass it back up
|
|
|
|
return table.concat( foldByteArray( bitmap ) )
|
|
|
|
end
|
|
|
|
|
|
|
|
--cf. ai.lua: these points can be red or green
|
|
|
|
for i, point in ipairs( points ) do
|
|
|
|
local wx, wy = point.x, point.y
|
|
|
|
--get bitmap coordinates
|
|
|
|
local x, y = math.floor(format.w * (wx + 180) / 360), math.floor(format.h * (1.0 - (wy + 100) / 200))
|
|
|
|
--get index into byte array
|
|
|
|
local idx = 2 + x * format.h + y
|
|
|
|
--in-editor we could have two points in the same place, possibly of the same or different types
|
|
|
|
--in case we miss something upstream, don't corrupt these duplicate points,
|
|
|
|
--just saturate the attack/defense value
|
|
|
|
if bitmap[idx] == 0 then bitmap[idx] = { attack = point.attacking, place = not(point.attacking)}
|
|
|
|
else
|
|
|
|
bitmap[idx].attack = bitmap[idx].attack or point.attacking
|
|
|
|
bitmap[idx].place = bitmap[idx].place or not(point.attacking)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--now map pixels to 3-byte strings
|
|
|
|
for j = 2, size do
|
|
|
|
if bitmap[j] == 0
|
|
|
|
then bitmap[j] = "\0\0\0"
|
2024-05-25 02:43:39 +00:00
|
|
|
else bitmap[j] = string.char( 0, bitmap[j].place and 0xff or 0, bitmap[j].attack and 0xff or 0 ) --bgr24
|
2024-04-29 01:21:32 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
return table.concat( bitmap )
|
|
|
|
end
|
2023-07-28 05:17:00 +00:00
|
|
|
return t
|