Feverish coding binge tonight.

Mouseover to find nearest city and display information.
Camera movement via arrow keys and scroll wheel.
Status bar displaying mouse location in world and window coordinates.
This commit is contained in:
wan-may 2023-07-28 02:17:00 -03:00
parent 7fa3e83a22
commit 8eed23c87d
12 changed files with 352 additions and 32 deletions

18
bmp.lua
View File

@ -1,5 +1,19 @@
--Load and save the bmp formats used by DEFCON. --Load and save the bmp formats used by DEFCON.
local bmp = {} local t = {}
local lfs = love.filesystem
--FFI bit-twiddling stuff.
local ffi = require 'ffi'
local bit = require 'bit'
return bmp function t.load( filename )
local bytes, size = lfs.read( filename )
print( "LOADED", filename, size)
end
function t.save( data, format )
end
return t

40
camera.lua Normal file
View File

@ -0,0 +1,40 @@
local tf = love.math.newTransform()
local lg = assert( love.graphics )
local Camera = { x = 0, y = 0, w = 360, h = 200, zoom = 1, tf = tf }
function Camera.GetWorldCoordinate( x, y )
return tf:inverseTransformPoint( x, y )
end
function Camera.Zoom( out )
local scale = out and 1.1 or 0.9
tf:scale( scale, scale )
local x = Camera.x
local y = Camera.y
return Camera.Set( x, y, Camera.w * scale, Camera.h * scale )
end
function Camera.Translate( x, y )
x = x or 0
y = y or 0
return Camera.Set( Camera.x + x, Camera.y + y, Camera.w, Camera.h)
end
--In world coordinates: top left corner at x, y, extent of w, h.
function Camera.Set( x, y, w, h )
print( ("CAMERA: %3.2f %3.2f %3.2f %3.2f"):format(x, y, w, h) )
Camera.x, Camera.y, Camera.w, Camera.h = x, y, w, h
Camera.zoom = w / 360
tf:reset()
tf:scale( w / 360, -h / 200 )
tf:translate( 180 - x, -y - 100 )
end
function Camera.Resize( w, h )
w, h = math.min( w, h * 360 / 200 ), math.min( h, w * 200 / 360 )
return Camera.Set( Camera.x, Camera.y, w, h )
end
Camera.Resize( lg.getDimensions() )
return Camera

View File

@ -1,31 +1,52 @@
--Load and save the fixed width plaintext data used by DEFCON. --Load and save the fixed width plaintext data used by DEFCON.
local t = {} local t = {}
local io = io local io = io
local math = math
local table = table local table = table
local tonumber = tonumber local tonumber = tonumber
local lfs = love.filesystem local lfs = love.filesystem
local lg = love.graphics local lg = love.graphics
local locationQuery = require 'locationQuery'
local cities local cities
local points = {} local points = {}
local caps = {} local caps = {}
t.selectedCity = nil
function t.draw() function t.draw()
lg.points( points ) lg.points( points )
end end
function t.drawSelected( r )
local c = t.selectedCity
if not c then return end
lg.circle( "fill", c.x, c.y, r )
end
function t.drawCapitals() function t.drawCapitals()
lg.points( caps ) lg.points( caps )
end end
function t.selectNearestCity(x, y)
t.selectedCity = cities:getClosestPoint(x, y)
if t.selectedCity then
local city = t.selectedCity
print( "SELECTED CITY", city.x, city.y, city.name, city.pop, city.capital)
else
print( "NO SELECTED CITY" )
end
end
function t.load( filename ) function t.load( filename )
cities = {} cities = {}
local n = 0 local n = 1
local idxPts = 1 local idxPts = 1
local idxCaps = 1 local idxCaps = 1
for line in assert( io.lines( filename ), "Error: could not open cities.dat" ) do for line in assert( lfs.lines( filename ), "Error: could not open cities.dat" ) do
n = n + 1
local _, _, x, y, pop, capital = line:sub( 83 ):find( "(%g+)%s+(%g+)%s+(%g+)%s+(%g+)" ) local _, _, x, y, pop, capital = line:sub( 83 ):find( "(%g+)%s+(%g+)%s+(%g+)%s+(%g+)" )
x, y, pop, capital = tonumber( x ), tonumber( y ), tonumber( pop ), ( tonumber( capital ) > 0) x, y, pop, capital = tonumber( x ), tonumber( y ), tonumber( pop ), ( tonumber( capital ) > 0)
local city = { local city = {
@ -34,6 +55,7 @@ function t.load( filename )
x = x, y = y, pop = pop, capital = capital x = x, y = y, pop = pop, capital = capital
} }
cities[n] = city cities[n] = city
n = n + 1
points[idxPts], points[idxPts + 1] = x, y points[idxPts], points[idxPts + 1] = x, y
idxPts = idxPts + 2 idxPts = idxPts + 2
@ -44,9 +66,12 @@ function t.load( filename )
end end
end end
print( "LOADED", filename, n ) --Multiple inheritance.
cities = locationQuery.New( cities )
setmetatable( getmetatable( cities ).__index, {__index = t } )
return setmetatable( cities, {__index = t } ) print( "LOADED", filename, n )
return cities
end end
function t.save( cities, filename ) function t.save( cities, filename )

51
conf.lua Normal file
View File

@ -0,0 +1,51 @@
function love.conf(t)
t.identity = "dcEarth" -- The name of the save directory (string)
t.appendidentity = true -- Search files in source directory before save directory (boolean)
t.version = "11.3" -- The LÖVE version this game was made for (string)
t.console = true -- Attach a console (boolean, Windows only)
t.accelerometerjoystick = true -- Enable the accelerometer on iOS and Android by exposing it as a Joystick (boolean)
t.externalstorage = false -- True to save files (and read from the save directory) in external storage on Android (boolean)
t.gammacorrect = false -- Enable gamma-correct rendering, when supported by the system (boolean)
t.audio.mic = false -- Request and use microphone capabilities in Android (boolean)
t.audio.mixwithsystem = true -- Keep background music playing when opening LOVE (boolean, iOS and Android only)
t.window.title = "dcEarth" -- The window title (string)
t.window.icon = "favicon.png" -- Filepath to an image to use as the window's icon (string)
t.window.width = 800 -- The window width (number)
t.window.height = 600 -- The window height (number)
t.window.borderless = false -- Remove all border visuals from the window (boolean)
t.window.resizable = true -- Let the window be user-resizable (boolean)
t.window.minwidth = 1 -- Minimum window width if the window is resizable (number)
t.window.minheight = 1 -- Minimum window height if the window is resizable (number)
t.window.fullscreen = false -- Enable fullscreen (boolean)
t.window.fullscreentype = "desktop" -- Choose between "desktop" fullscreen or "exclusive" fullscreen mode (string)
t.window.vsync = 0 -- Vertical sync mode (number)
t.window.msaa = 0 -- The number of samples to use with multi-sampled antialiasing (number)
t.window.depth = nil -- The number of bits per sample in the depth buffer
t.window.stencil = nil -- The number of bits per sample in the stencil buffer
t.window.display = 1 -- Index of the monitor to show the window in (number)
t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean)
t.window.usedpiscale = true -- Enable automatic DPI scaling when highdpi is set to true as well (boolean)
t.window.x = nil -- The x-coordinate of the window's position in the specified display (number)
t.window.y = nil -- The y-coordinate of the window's position in the specified display (number)
t.modules.audio = true -- Enable the audio module (boolean)
t.modules.data = true -- Enable the data module (boolean)
t.modules.event = true -- Enable the event module (boolean)
t.modules.font = true -- Enable the font module (boolean)
t.modules.graphics = true -- Enable the graphics module (boolean)
t.modules.image = true -- Enable the image module (boolean)
t.modules.joystick = false -- Enable the joystick module (boolean)
t.modules.keyboard = true -- Enable the keyboard module (boolean)
t.modules.math = true -- Enable the math module (boolean)
t.modules.mouse = true -- Enable the mouse module (boolean)
t.modules.physics = false -- Enable the physics module (boolean)
t.modules.sound = true -- Enable the sound module (boolean)
t.modules.system = true -- Enable the system module (boolean)
t.modules.thread = true -- Enable the thread module (boolean)
t.modules.timer = true -- Enable the timer module (boolean), Disabling it will result 0 delta time in love.update
t.modules.touch = false -- Enable the touch module (boolean)
t.modules.video = false -- Enable the video module (boolean)
t.modules.window = true -- Enable the window module (boolean)
end

BIN
favicon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

65
locationQuery.lua Normal file
View File

@ -0,0 +1,65 @@
--Shitty acceleration structure: get closest point in set.
--Assumed to be in world coordinates: ( -180, 180 ) x ( -100, 100 )
local t = {}
local hashes = {}
local math = math
local hash = function( x, y, debug )
local s = "h"..("%2d%2d"):format(math.floor( 0.5 + (180 + x) / 10 ), math.floor( 0.5 + (100 + y) / 10 ) )
if debug then print( "HASH: ", s, x, y) end
return s
end
function t.getClosestPoint( points, x, y )
local closePoints = hashes[hash( x, y )]
if not closePoints then return end
local distance = math.huge
local px, py, point
for k, v in pairs(closePoints) do
px, py = v.x, v.y
local d = (x - px) * (x - px) + (y - py) * (y - py)
if d < distance then
distance = d
point = v
end
end
print( #closePoints, distance )
return point
end
function t.Edit( points, point, x, y )
local h = hashes[hash( point.x, point.y )]
if h then
for i, p in pairs(h) do
if p == point then
table.remove( h, i )
break
end
end
end
table.insert( hashes[ hash( x, y ) ], point )
point.x = x
point.y = y
end
function t.Add( points )
end
function t.New( points )
for i = 1, #points do
local x, y = points[i].x, points[i].y
local h = hash( x, y )
hashes[h] = hashes[h] or {}
hashes[h][#hashes[h] + 1] = points[i]
end
return setmetatable( points, {__index = t } )
end
return t

View File

@ -1,29 +1,78 @@
local love = assert( love, "This tool requires LOVE: love2d.org" ) local love = assert( love, "This tool requires LOVE: love2d.org" )
local map = require 'map' local map = require 'map'
local SAVEDIRECTORY = "out/" local SAVEDIRECTORY = "out/"
local Camera = require 'camera'
local wasKeyPressed
function love.load() function love.load()
local lfs = assert( love.filesystem ) local lfs = assert( love.filesystem )
lfs.setIdentity( "dcearth", false ) lfs.setIdentity( "dcearth", false )
assert( lfs.createDirectory( SAVEDIRECTORY.."data/earth" )) assert( lfs.createDirectory( SAVEDIRECTORY.."data/earth" ))
assert( lfs.createDirectory( SAVEDIRECTORY.."data/graphics" )) assert( lfs.createDirectory( SAVEDIRECTORY.."data/graphics" ))
map.load() map.load()
love.graphics.setNewFont( 12, "mono" )
end end
local canvas = love.graphics.newCanvas()
function love.update( dt ) function love.update( dt )
love.graphics.setCanvas( canvas ) local tx, ty = 0, 0
map.draw() local moveCamera = false
love.graphics.setCanvas() if love.keyboard.isScancodeDown( "w" ) then moveCamera = true; ty = ty + 1 end
if love.keyboard.isScancodeDown( "a" ) then moveCamera = true; tx = tx - 1 end
if love.keyboard.isScancodeDown( "s" ) then moveCamera = true; ty = ty - 1 end
if love.keyboard.isScancodeDown( "d" ) then moveCamera = true; tx = tx + 1 end
if moveCamera then Camera.Translate( tx, ty ) end
end end
function love.draw() function love.draw()
love.graphics.draw( canvas ) love.graphics.push( "all" )
map.draw()
love.graphics.pop()
--Status bar.
local x, y = love.mouse.getPosition()
local wx, wy = Camera.GetWorldCoordinate( x, y )
local h = love.graphics.getHeight() - 30
love.graphics.setColor( 0.2, 0.1, 0.1, 0.5 )
love.graphics.rectangle( "fill", 0, h, love.graphics.getWidth() / 2, 30 )
love.graphics.setColor( 1, 1, 1, 1 )
love.graphics.rectangle( "line", 0, h, love.graphics.getWidth() / 2, 30 )
love.graphics.print(("SCREEN\t%d\t%d\nWORLD \t%5.2f\t%5.2f"):format(x, y, wx, wy), 0, h)
--Edit box.
love.graphics.rectangle( "line", love.graphics.getWidth() / 2, h, love.graphics.getWidth() / 2, 30 )
if map.cities.selectedCity then
local c = map.cities.selectedCity
love.graphics.print( ("NAME: %s\nX: %3.2f\nY: %3.2f\nPOP: %d\nCAPITAL: %s\nCOUNTRY: %s"):format(c.name, c.x, c.y, c.pop, tostring(c.capital), c.country), 0, 0 )
end
end
function love.resize(w, h)
Camera.Resize( w, h )
end
function love.wheelmoved(x, y)
Camera.Zoom( (y > 0) and true or false )
end
function love.mousepressed( x, y, button, istouch, presses )
local wx, wy = Camera.GetWorldCoordinate( x, y )
print( ("MOUSE\tx %f\ty %f\twx %f\twy %f"):format(x, y, wx, wy) )
end
function love.mousemoved( x, y, dx, dy, istouch )
map.cities.selectNearestCity( Camera.GetWorldCoordinate( x, y ) )
end end
function love.keypressed(key) function love.keypressed(key)
if key == "s" then if key == "l" then
-- To open a file or folder, "file://" must be prepended to the path. -- To open a file or folder, "file://" must be prepended to the path.
love.system.openURL("file://"..love.filesystem.getSaveDirectory()) love.system.openURL("file://"..love.filesystem.getSaveDirectory())
end end
wasKeyPressed = true
end end

38
map.lua
View File

@ -1,7 +1,9 @@
local lg = love.graphics local lg = love.graphics
local Cities = require 'cities' local Cities = require 'cities'
local Lines = require 'lines' local Lines = require 'lines'
local Nodes = require 'nodes'
local Bitmap = require 'bmp' local Bitmap = require 'bmp'
local Camera = require 'camera'
local map = { local map = {
coastlines = false, coastlines = false,
@ -26,41 +28,49 @@ function map.load()
map.coastlines = Lines.load( "data/earth/coastlines.dat" ) map.coastlines = Lines.load( "data/earth/coastlines.dat" )
map.coastlinesLow = Lines.load( "data/earth/coastlines-low.dat" ) map.coastlinesLow = Lines.load( "data/earth/coastlines-low.dat" )
map.international = Lines.load( "data/earth/international.dat" ) map.international = Lines.load( "data/earth/international.dat" )
map.travelnodes = Nodes.load( "data/earth/travel_nodes.bmp" )
end end
function map.draw() function map.draw()
lg.clear( 0, 0, 0, 1 ) lg.clear( 0, 0, 0, 1 )
do --all this stuff is drawn in world coordinates, ( -180, 180 ) x ( -100, 100 ) do --all this stuff is drawn in world coordinates, ( -180, 180 ) x ( -100, 100 )
lg.push( "transform" )
lg.scale( lg.getCanvas():getDimensions() )
lg.scale( 1 / 360, - 1 / 200 )
lg.translate( 180, -100 )
lg.replaceTransform( Camera.tf )
do --points do --points
lg.setColor( 1, 0, 0, 0.5 ) lg.setColor( 1, 0, 0, 0.5 )
lg.setPointSize( 2 ) lg.setPointSize( 0.5 * Camera.zoom )
map.cities:draw() map.cities.draw()
lg.setColor( 1, 0, 0, 1.0 ) lg.setColor( 1, 1, 1.0, 0.5 )
lg.setPointSize( 4 ) lg.setPointSize( 1.0 * Camera.zoom )
map.cities:drawCapitals() map.cities.drawCapitals()
lg.setColor( 1, 0, 1, 0.5 )
map.cities.drawSelected( 22.0 / Camera.zoom )
end end
do --line stuff do --line stuff
lg.setColor(1, 1, 1, 0.2 ) lg.setColor(1, 1, 1, 0.2 )
lg.setLineWidth( 0.2 )
lg.setLineJoin( "miter" )
lg.setLineWidth( 0.2 / Camera.zoom )
map.international:draw() map.international:draw()
lg.setColor(1, 1, 1, 1 ) lg.setColor(1, 1, 1, 0.5 )
lg.setLineWidth( 0.3 )
map.coastlines:draw() map.coastlines:draw()
map.coastlinesLow:draw() map.coastlinesLow:draw()
--International Date Line
lg.line( -180, -100, -180, 100 )
lg.line( 180, -100, 180, 100 )
lg.line( -180, 90, 180, 90 )
lg.line( -180, -90, 180, -90 )
lg.line( -180, 100, 180, 100 )
lg.line( -180, -100, 180, -100 )
end end
lg.pop( )
end end
end end

View File

@ -1,7 +1,29 @@
--Manage the pathfinding nodes used by DEFCON. --Manage the pathfinding nodes used by DEFCON.
--This is important for a mapping tool because the DEFCON client will not load a map unless --This is important for a mapping tool because the DEFCON client will not load a map unless
--the pathfinding nodes form a connected graph. --the pathfinding nodes form a connected graph.
local nodes = {} local t = {}
local bmp = require 'bmp'
function t.load( filename )
local nodes = { points = {}, connections = {}, img = bmp.load( filename ) }
return nodes return setmetatable( nodes, {__index = t } )
end
function t.isConnected( nodes )
end
function t.draw( nodes )
end
function t.drawConnections( nodes )
end
function t.save( nodes, filename )
end
return t

2
scratch.lua Normal file
View File

@ -0,0 +1,2 @@
local jit = require 'jit'
for k, v in pairs( jit.opt ) do print(k , v ) end

42
territory.lua Normal file
View File

@ -0,0 +1,42 @@
local t = {}
local bmp = require 'bmp'
function t.load( filename, name )
local territory = { img = bmp.load( filename ) }
return setmetatable( territory, {__index = t } )
end
--World space coordinate.
function t.isValid( x, y )
end
function t.getPixel( territory, x, y )
--ZERO INDEXED
return territory.img[math.floor( 512 * ( x + 180 ) / 360 ) ][ math.floor( 285 * ( y + 100 ) / 200 ) ]
end
--[[
0
20 -- once sailable.bmp is brighter than this, the area is traversable by ships
60 -- once sailable.bmp and territory.bmp are brighter than this, ships can be placed here
130 -- if territory.bmp is brighter than this and sailable is darker than 60, structures are placeable.
SO:
SAILABLE: 0 (not), 21 (traverse not place), 61 ( traverse and place )
TERRITORY: 131 ( place land if sailable <= 60 ), 61 ( place sea ), 0
]]
function t.draw( nodes )
end
function t.drawConnections( nodes )
end
function t.save( nodes, filename )
end
return t