diff --git a/bmp.lua b/bmp.lua index 450b05c..961739f 100644 --- a/bmp.lua +++ b/bmp.lua @@ -1,5 +1,19 @@ --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 \ No newline at end of file +function t.load( filename ) + local bytes, size = lfs.read( filename ) + + print( "LOADED", filename, size) +end + +function t.save( data, format ) + +end + +return t \ No newline at end of file diff --git a/camera.lua b/camera.lua new file mode 100644 index 0000000..3fafcfa --- /dev/null +++ b/camera.lua @@ -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 \ No newline at end of file diff --git a/cities.lua b/cities.lua index c3dca75..1892cfd 100644 --- a/cities.lua +++ b/cities.lua @@ -1,31 +1,52 @@ --Load and save the fixed width plaintext data used by DEFCON. local t = {} local io = io +local math = math local table = table local tonumber = tonumber local lfs = love.filesystem local lg = love.graphics +local locationQuery = require 'locationQuery' local cities local points = {} local caps = {} +t.selectedCity = nil + function t.draw() lg.points( points ) 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() lg.points( caps ) 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 ) cities = {} - local n = 0 + local n = 1 local idxPts = 1 local idxCaps = 1 - for line in assert( io.lines( filename ), "Error: could not open cities.dat" ) do - n = n + 1 + for line in assert( lfs.lines( filename ), "Error: could not open cities.dat" ) do + 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) local city = { @@ -33,7 +54,8 @@ function t.load( filename ) country = line:sub( 42, 82 ):gsub("%s+$",""), x = x, y = y, pop = pop, capital = capital } - cities[n] = city + cities[n] = city + n = n + 1 points[idxPts], points[idxPts + 1] = x, y idxPts = idxPts + 2 @@ -44,9 +66,12 @@ function t.load( filename ) end end + --Multiple inheritance. + cities = locationQuery.New( cities ) + setmetatable( getmetatable( cities ).__index, {__index = t } ) + print( "LOADED", filename, n ) - - return setmetatable( cities, {__index = t } ) + return cities end function t.save( cities, filename ) diff --git a/conf.lua b/conf.lua new file mode 100644 index 0000000..92c8889 --- /dev/null +++ b/conf.lua @@ -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 \ No newline at end of file diff --git a/data/earth/cities.dat b/data/earth/cities.dat index 26cc1ff..84b9829 100644 --- a/data/earth/cities.dat +++ b/data/earth/cities.dat @@ -1,4 +1,4 @@ -Cambridge United Kingdom 6.166700 35.566700 106673 0 +Cambridge United Kingdom 6.166700 35.566700 106673 0 Gloucester United Kingdom 6.666700 36.366699 108150 0 Hastings/Bexhill United Kingdom 5.400000 36.183300 112080 0 Slough United Kingdom 13.250000 -8.833300 106882 0 diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000..018fb5d Binary files /dev/null and b/favicon.png differ diff --git a/locationQuery.lua b/locationQuery.lua new file mode 100644 index 0000000..d677f22 --- /dev/null +++ b/locationQuery.lua @@ -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 \ No newline at end of file diff --git a/main.lua b/main.lua index d49f13c..04ef67a 100644 --- a/main.lua +++ b/main.lua @@ -1,29 +1,78 @@ local love = assert( love, "This tool requires LOVE: love2d.org" ) local map = require 'map' local SAVEDIRECTORY = "out/" +local Camera = require 'camera' +local wasKeyPressed function love.load() + local lfs = assert( love.filesystem ) lfs.setIdentity( "dcearth", false ) assert( lfs.createDirectory( SAVEDIRECTORY.."data/earth" )) assert( lfs.createDirectory( SAVEDIRECTORY.."data/graphics" )) map.load() + + + love.graphics.setNewFont( 12, "mono" ) end -local canvas = love.graphics.newCanvas() function love.update( dt ) - love.graphics.setCanvas( canvas ) - map.draw() - love.graphics.setCanvas() + local tx, ty = 0, 0 + local moveCamera = false + 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 + 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 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. love.system.openURL("file://"..love.filesystem.getSaveDirectory()) end + wasKeyPressed = true end diff --git a/map.lua b/map.lua index a08fd9a..c483f55 100644 --- a/map.lua +++ b/map.lua @@ -1,7 +1,9 @@ local lg = love.graphics local Cities = require 'cities' local Lines = require 'lines' +local Nodes = require 'nodes' local Bitmap = require 'bmp' +local Camera = require 'camera' local map = { coastlines = false, @@ -26,41 +28,49 @@ function map.load() map.coastlines = Lines.load( "data/earth/coastlines.dat" ) map.coastlinesLow = Lines.load( "data/earth/coastlines-low.dat" ) map.international = Lines.load( "data/earth/international.dat" ) + map.travelnodes = Nodes.load( "data/earth/travel_nodes.bmp" ) end function map.draw() lg.clear( 0, 0, 0, 1 ) 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 lg.setColor( 1, 0, 0, 0.5 ) - lg.setPointSize( 2 ) - map.cities:draw() + lg.setPointSize( 0.5 * Camera.zoom ) + map.cities.draw() - lg.setColor( 1, 0, 0, 1.0 ) - lg.setPointSize( 4 ) - map.cities:drawCapitals() + lg.setColor( 1, 1, 1.0, 0.5 ) + lg.setPointSize( 1.0 * Camera.zoom ) + map.cities.drawCapitals() + + lg.setColor( 1, 0, 1, 0.5 ) + map.cities.drawSelected( 22.0 / Camera.zoom ) end do --line stuff lg.setColor(1, 1, 1, 0.2 ) - lg.setLineWidth( 0.2 ) + + lg.setLineJoin( "miter" ) + lg.setLineWidth( 0.2 / Camera.zoom ) map.international:draw() - lg.setColor(1, 1, 1, 1 ) - lg.setLineWidth( 0.3 ) + lg.setColor(1, 1, 1, 0.5 ) map.coastlines: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 - lg.pop( ) end end diff --git a/nodes.lua b/nodes.lua index f42e45c..30cbc19 100644 --- a/nodes.lua +++ b/nodes.lua @@ -1,7 +1,29 @@ --Manage the pathfinding nodes used by DEFCON. --This is important for a mapping tool because the DEFCON client will not load a map unless --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 setmetatable( nodes, {__index = t } ) +end -return nodes \ No newline at end of file +function t.isConnected( nodes ) + +end + +function t.draw( nodes ) + +end + +function t.drawConnections( nodes ) + +end + +function t.save( nodes, filename ) + +end + +return t \ No newline at end of file diff --git a/scratch.lua b/scratch.lua new file mode 100644 index 0000000..39c2b53 --- /dev/null +++ b/scratch.lua @@ -0,0 +1,2 @@ +local jit = require 'jit' +for k, v in pairs( jit.opt ) do print(k , v ) end \ No newline at end of file diff --git a/territory.lua b/territory.lua new file mode 100644 index 0000000..5d79516 --- /dev/null +++ b/territory.lua @@ -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 \ No newline at end of file