Compare commits
No commits in common. "main" and "bmploading" have entirely different histories.
main
...
bmploading
|
@ -1,7 +1,7 @@
|
||||||
--Manage the AI nodes used by DEFCON.
|
--Manage the AI nodes used by DEFCON.
|
||||||
local bmp = require 'map.bmp'
|
local bmp = require 'bmp'
|
||||||
local lg = assert( love.graphics )
|
local lg = assert( love.graphics )
|
||||||
local locationQuery = require 'map.locationQuery'
|
local locationQuery = require 'locationQuery'
|
||||||
|
|
||||||
local t = setmetatable( {}, {__index = locationQuery } )
|
local t = setmetatable( {}, {__index = locationQuery } )
|
||||||
local print = print
|
local print = print
|
||||||
|
@ -17,14 +17,6 @@ function aiNode:formatDisplayInfo()
|
||||||
]]):format( self.idx, self.x, self.y, tostring(self.attacking) )
|
]]):format( self.idx, self.x, self.y, tostring(self.attacking) )
|
||||||
end
|
end
|
||||||
|
|
||||||
function aiNode:add()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function aiNode:moveTo( x, y )
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.load( filename )
|
function t.load( filename )
|
||||||
local img, imgd = bmp.load( filename )
|
local img, imgd = bmp.load( filename )
|
||||||
local nodes = {
|
local nodes = {
|
||||||
|
@ -90,8 +82,4 @@ function t.save( nodes )
|
||||||
return bmp.ai( nodes.all )
|
return bmp.ai( nodes.all )
|
||||||
end
|
end
|
||||||
|
|
||||||
function t.newNode( isAttacking )
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
return t
|
return t
|
|
@ -16,7 +16,6 @@ function test.load( filename )
|
||||||
print( "LOADING BITMAP: ", filename, imgd:getSize(), imgd:getFormat(), imgd:getDimensions() )
|
print( "LOADING BITMAP: ", filename, imgd:getSize(), imgd:getFormat(), imgd:getDimensions() )
|
||||||
local img = love.graphics.newImage( imgd )
|
local img = love.graphics.newImage( imgd )
|
||||||
img:setFilter( "nearest", "nearest" )
|
img:setFilter( "nearest", "nearest" )
|
||||||
img:setWrap( "repeat", "clampzero" )
|
|
||||||
return img, imgd
|
return img, imgd
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,6 +51,7 @@ end
|
||||||
local function getHeader( filename )
|
local function getHeader( filename )
|
||||||
local offset = 2 + love.data.unpack( "<I4", assert(love.filesystem.read( "data", filename, 14 )), 11 )
|
local offset = 2 + love.data.unpack( "<I4", assert(love.filesystem.read( "data", filename, 14 )), 11 )
|
||||||
local header, size = assert( love.filesystem.read( filename, offset ) )
|
local header, size = assert( love.filesystem.read( filename, offset ) )
|
||||||
|
print( "BMP HEADER", filename, size, "\n", string.byte( header, 1, size ) )
|
||||||
return header
|
return header
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -74,7 +74,7 @@ local formats = {
|
||||||
header = getHeader( "data/earth/sailable.bmp" ),
|
header = getHeader( "data/earth/sailable.bmp" ),
|
||||||
w = 512,
|
w = 512,
|
||||||
h = 285,
|
h = 285,
|
||||||
--technically this information is in the header already but I don't want to write code to parse it we only use one palette ever
|
--technically this information is in the header already but I don't want to write code to parse it since we only use this one header
|
||||||
palette = {
|
palette = {
|
||||||
[0] = 0,
|
[0] = 0,
|
||||||
[8] = 1,
|
[8] = 1,
|
||||||
|
@ -104,16 +104,6 @@ local formats = {
|
||||||
w = 512,
|
w = 512,
|
||||||
h = 285,
|
h = 285,
|
||||||
},
|
},
|
||||||
["blur"] = {
|
|
||||||
header = getHeader( "data/graphics/blur.bmp" ),
|
|
||||||
w = 512,
|
|
||||||
h = 285,
|
|
||||||
},
|
|
||||||
["screenshot"] = {
|
|
||||||
header = getHeader( "screenshot.bmp" ),
|
|
||||||
w = 256,
|
|
||||||
h = 128,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function formats.ai:test( )
|
function formats.ai:test( )
|
||||||
|
@ -169,6 +159,7 @@ function formats.ai:encode( data )
|
||||||
local x = math.floor(self.w * (wx + 180) / 360)
|
local x = math.floor(self.w * (wx + 180) / 360)
|
||||||
--idk why exactly I need to round the value here instead of truncating it
|
--idk why exactly I need to round the value here instead of truncating it
|
||||||
local y = math.floor(0.5 + self.h * (wy + 100) / 200)
|
local y = math.floor(0.5 + self.h * (wy + 100) / 200)
|
||||||
|
print( "export ai marker", i, x, y, wx, wy )
|
||||||
--get index into byte array
|
--get index into byte array
|
||||||
local idx = 2 + y * self.w + x
|
local idx = 2 + y * self.w + x
|
||||||
--in-editor we could have two points in the same place, possibly of the same or different types
|
--in-editor we could have two points in the same place, possibly of the same or different types
|
||||||
|
@ -233,6 +224,7 @@ function formats.travel:encode( points )
|
||||||
local offset = math.floor( y * self.w + x - 2 )
|
local offset = math.floor( y * self.w + x - 2 )
|
||||||
--1 := white
|
--1 := white
|
||||||
bitmap[offset] = 1
|
bitmap[offset] = 1
|
||||||
|
print( "encoded:", point.idx, wx, wy, x, y )
|
||||||
end
|
end
|
||||||
|
|
||||||
--fold into 4-bit pixel array
|
--fold into 4-bit pixel array
|
||||||
|
@ -255,63 +247,40 @@ function formats.sailable:test()
|
||||||
print "sailable OK"
|
print "sailable OK"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function formats.sailable:encode( data )
|
||||||
|
local w, h = self.w, self.h
|
||||||
|
local bytes = { self.header:sub( 1, -2 ) }
|
||||||
|
local i = 2
|
||||||
|
|
||||||
do --sailable
|
--y coordinates are written top to bottom
|
||||||
|
for y = h - 1, 0, -1 do
|
||||||
local reversePalette = {}
|
for x = 0, w - 1 do
|
||||||
for eight, four in pairs( formats.sailable.palette ) do
|
bytes[i] = assert( self.palette[ math.floor( data:getPixel(x, y) * 255 )] )
|
||||||
reversePalette[ four ] = eight
|
i = i + 1
|
||||||
end
|
|
||||||
|
|
||||||
--take the red channel in float [0, 1] format
|
|
||||||
--expand it to a byte, then quantize it to 4 bits
|
|
||||||
--according to the sailable palette
|
|
||||||
local function sailableQuantize( r )
|
|
||||||
if r == 0 then return 0 end
|
|
||||||
if r >= 1 then return 15 end
|
|
||||||
r = math.floor( r * 255 )
|
|
||||||
|
|
||||||
for four = 1, #reversePalette do
|
|
||||||
if reversePalette[ four ] > r then return four end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
error( "Could not quantize sailable.bmp!" )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function formats.sailable:encode( data )
|
--fold into 4-bit pixel array
|
||||||
local w, h = self.w, self.h
|
local nybbles = { bytes[1] }
|
||||||
local bytes = { self.header:sub( 1, -2 ) }
|
for j = 2, #bytes / 2 do
|
||||||
local i = 2
|
local a, b = bytes[ 2 * j ], bytes[ 2 * j + 1 ]
|
||||||
|
nybbles[j] = string.char( 16 * a + b )
|
||||||
--y coordinates are written top to bottom
|
|
||||||
for y = h - 1, 0, -1 do
|
|
||||||
for x = 0, w - 1 do
|
|
||||||
bytes[i] = sailableQuantize( data:getPixel(x, y) )
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--fold into 4-bit pixel array
|
|
||||||
local nybbles = { bytes[1] }
|
|
||||||
for j = 2, #bytes / 2 do
|
|
||||||
local a, b = bytes[ 2 * j ], bytes[ 2 * j + 1 ]
|
|
||||||
nybbles[j] = string.char( 16 * a + b )
|
|
||||||
end
|
|
||||||
|
|
||||||
return table.concat( nybbles )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function formats.africa:test()
|
return table.concat( nybbles )
|
||||||
print "testing africa"
|
|
||||||
local filename = "data/earth/africa.bmp"
|
|
||||||
local img, imgd = test.load( filename )
|
|
||||||
local encoded = self:encode( imgd )
|
|
||||||
love.filesystem.write( "africa_out.bmp", encoded )
|
|
||||||
test.compareData( love.filesystem.read( filename ), encoded )
|
|
||||||
print "africa OK"
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function formats.africa:test()
|
||||||
|
print "testing africa"
|
||||||
|
local filename = "data/earth/africa.bmp"
|
||||||
|
local img, imgd = test.load( filename )
|
||||||
|
local encoded = self:encode( imgd )
|
||||||
|
love.filesystem.write( "africa_out.bmp", encoded )
|
||||||
|
test.compareData( love.filesystem.read( filename ), encoded )
|
||||||
|
print "africa OK"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function formats.africa:encode( data )
|
function formats.africa:encode( data )
|
||||||
local w, h = self.w, self.h
|
local w, h = self.w, self.h
|
||||||
local bytes = { self.header:sub( 1, -3 ) }
|
local bytes = { self.header:sub( 1, -3 ) }
|
||||||
|
@ -327,24 +296,11 @@ function formats.africa:encode( data )
|
||||||
return table.concat( bytes )
|
return table.concat( bytes )
|
||||||
end
|
end
|
||||||
|
|
||||||
function formats.screenshot:encode( data )
|
|
||||||
return formats.territory.encode( self, data )
|
|
||||||
end
|
|
||||||
|
|
||||||
--this one was 8-bit grayscale in the original game
|
|
||||||
--but we're going to increase the bit depth to 24 because
|
|
||||||
--the game can render colours in blur.bmp just fine
|
|
||||||
--and we want to render tinted radar ranges into that file
|
|
||||||
function formats.blur:encode( data )
|
|
||||||
return formats.territory.encode( self, data )
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.load( filename )
|
function t.load( filename )
|
||||||
local imgd = love.image.newImageData( filename )
|
local imgd = love.image.newImageData( filename )
|
||||||
print( "LOADING BITMAP: ", filename, imgd:getSize(), imgd:getFormat(), imgd:getDimensions() )
|
print( "LOADING BITMAP: ", filename, imgd:getSize(), imgd:getFormat(), imgd:getDimensions() )
|
||||||
local img = love.graphics.newImage( imgd )
|
local img = love.graphics.newImage( imgd )
|
||||||
img:setFilter( "nearest", "nearest" )
|
img:setFilter( "nearest", "nearest" )
|
||||||
img:setWrap( "repeat", "clampzero" )
|
|
||||||
return img, imgd
|
return img, imgd
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -357,7 +313,7 @@ formats.territory = formats.africa
|
||||||
for fmt, tbl in pairs( formats ) do
|
for fmt, tbl in pairs( formats ) do
|
||||||
t[fmt] = function( data ) return tbl:encode( data ) end
|
t[fmt] = function( data ) return tbl:encode( data ) end
|
||||||
--TESTING
|
--TESTING
|
||||||
--tbl:test()
|
tbl:test()
|
||||||
end
|
end
|
||||||
|
|
||||||
return t
|
return t
|
|
@ -2,16 +2,14 @@ local lg = love.graphics
|
||||||
|
|
||||||
local t = {
|
local t = {
|
||||||
name = "",
|
name = "",
|
||||||
tooltip = "",
|
tooltip = "button",
|
||||||
icon = false,
|
icon = false,
|
||||||
lit = false,
|
|
||||||
x = 8,
|
x = 8,
|
||||||
y = 250,
|
y = 250,
|
||||||
w = 13 * 28 - 4,
|
w = 176,
|
||||||
h = 24,
|
h = 24,
|
||||||
group = false,
|
group = false,
|
||||||
visible = false,
|
visible = true,
|
||||||
align = "center",
|
|
||||||
callback = function( self ) return print( "clicked button: ", self.name, self.x, self.y, self.w, self.h, self.visible ) end
|
callback = function( self ) return print( "clicked button: ", self.name, self.x, self.y, self.w, self.h, self.visible ) end
|
||||||
}
|
}
|
||||||
t.selected, t.next, t.prev = t, t, t
|
t.selected, t.next, t.prev = t, t, t
|
||||||
|
@ -31,22 +29,18 @@ function t.new( b )
|
||||||
return b
|
return b
|
||||||
end
|
end
|
||||||
|
|
||||||
function t.highlight( b )
|
|
||||||
lg.rectangle( "fill", b.x, b.y, b.w, b.h )
|
|
||||||
end
|
|
||||||
|
|
||||||
local drawPassOngoing = false
|
local drawPassOngoing = false
|
||||||
function t.draw( b )
|
function t.draw( b )
|
||||||
if b == t then
|
if b == t then
|
||||||
drawPassOngoing = not( drawPassOngoing )
|
drawPassOngoing = not( drawPassOngoing )
|
||||||
if not drawPassOngoing then return end
|
if not drawPassOngoing then return end
|
||||||
elseif b.visible then
|
elseif b.visible then
|
||||||
lg.rectangle( "line", b.x, b.y, b.w, b.h )
|
lg.rectangle( "line", b.x, b.y, b.w, b.h, 6 )
|
||||||
lg.printf( b.name,
|
lg.printf( b.name,
|
||||||
b.x + (b.icon and b.h or 0),
|
b.x + (b.icon and b.h or 0),
|
||||||
b.y + 0.5 * ( b.h - lg.getFont():getHeight() ),
|
b.y + 0.5 * ( b.h - lg.getFont():getHeight() ),
|
||||||
b.w - (b.icon and b.h or 0),
|
b.w - (b.icon and b.h or 0),
|
||||||
b.align )
|
"center" )
|
||||||
if b.icon then
|
if b.icon then
|
||||||
local h = b.icon:getHeight()
|
local h = b.icon:getHeight()
|
||||||
lg.draw( b.icon,
|
lg.draw( b.icon,
|
||||||
|
@ -54,8 +48,8 @@ function t.draw( b )
|
||||||
0,
|
0,
|
||||||
b.h / h )
|
b.h / h )
|
||||||
end
|
end
|
||||||
if b.lit or t.selected == b then
|
if t.selected == b then
|
||||||
b:highlight()
|
lg.rectangle( "fill", b.x, b.y, b.w, b.h, 6 )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return t.draw( b.next )
|
return t.draw( b.next )
|
||||||
|
@ -80,9 +74,7 @@ end
|
||||||
|
|
||||||
function t.selectNextInGroup()
|
function t.selectNextInGroup()
|
||||||
--make sure our group is visible, otherwise the loop doesn't end
|
--make sure our group is visible, otherwise the loop doesn't end
|
||||||
local group = t.selected
|
local group = t.selected and t.selected.visible and t.selected.group
|
||||||
group = group and t.selected.visible
|
|
||||||
group = group and t.selected.group
|
|
||||||
if not group then return t.selectNext() end
|
if not group then return t.selectNext() end
|
||||||
repeat t.selectNext() until group == t.selected.group
|
repeat t.selectNext() until group == t.selected.group
|
||||||
end
|
end
|
||||||
|
@ -94,27 +86,15 @@ function t.selectPrevInGroup()
|
||||||
repeat t.selectPrev() until group == t.selected.group
|
repeat t.selectPrev() until group == t.selected.group
|
||||||
end
|
end
|
||||||
|
|
||||||
--show/hide all buttons in a group
|
function t.displayGroup( group, show )
|
||||||
--passing hide=true will hide the group, hide=false or nil will show the group
|
|
||||||
--solo=true will hide all buttons outside the group
|
|
||||||
function t.displayGroup( group, hide, solo )
|
|
||||||
local b = t
|
local b = t
|
||||||
repeat
|
repeat
|
||||||
b = b.next
|
b = b.next
|
||||||
local inGroup = (group == b.group)
|
b.visible = ( b.group == group )
|
||||||
if solo or inGroup then
|
|
||||||
b.visible = not(hide) and inGroup
|
|
||||||
end
|
|
||||||
until b == t
|
until b == t
|
||||||
t.visible = true
|
t.visible = true
|
||||||
end
|
end
|
||||||
|
|
||||||
function t.mousepressed( x, y )
|
|
||||||
if t.selected and t.selected:contains( x, y ) then
|
|
||||||
return t.selected:callback()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.deselect( b )
|
function t.deselect( b )
|
||||||
t.selected = t
|
t.selected = t
|
||||||
end
|
end
|
|
@ -3,7 +3,7 @@ local tfTerritory = love.math.newTransform()
|
||||||
local tfNodes = love.math.newTransform()
|
local tfNodes = love.math.newTransform()
|
||||||
local lg = assert( love.graphics )
|
local lg = assert( love.graphics )
|
||||||
local Camera = {
|
local Camera = {
|
||||||
x = 0, y = 70,
|
x = -90, y = 45,
|
||||||
w = 360, h = 200,
|
w = 360, h = 200,
|
||||||
zoom = 1, tf = tf,
|
zoom = 1, tf = tf,
|
||||||
tfTerritory = tfTerritory, tfNodes = tfNodes }
|
tfTerritory = tfTerritory, tfNodes = tfNodes }
|
||||||
|
@ -37,9 +37,7 @@ end
|
||||||
function Camera.Translate( x, y )
|
function Camera.Translate( x, y )
|
||||||
x = x or 0
|
x = x or 0
|
||||||
y = y or 0
|
y = y or 0
|
||||||
return Camera.Set(
|
return Camera.Set( math.max(-180, math.min(360, Camera.x + x)), math.min(100, Camera.y + y), Camera.w, Camera.h)
|
||||||
math.max(-360, math.min(360, Camera.x + x)),
|
|
||||||
math.max(-140, math.min(140, Camera.y + y)), Camera.w, Camera.h)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
--In world coordinates: top left corner at x, y, extent of 1/w, 1/h.
|
--In world coordinates: top left corner at x, y, extent of 1/w, 1/h.
|
||||||
|
@ -50,17 +48,14 @@ function Camera.Set( x, y, w, h )
|
||||||
tf:scale( w / 360, -h / 200 )
|
tf:scale( w / 360, -h / 200 )
|
||||||
tf:translate( 180 - x, -y - 100 )
|
tf:translate( 180 - x, -y - 100 )
|
||||||
|
|
||||||
|
|
||||||
tfTerritory:reset()
|
tfTerritory:reset()
|
||||||
tfTerritory:scale( w / 512, h / 285 )
|
tfTerritory:scale( w / 512, h / 285 )
|
||||||
tfTerritory:translate( -x * 512 / 360, y * 512 / 360 )
|
tfTerritory:translate( -x * 512 / 360, y * 512 / 360 )
|
||||||
|
|
||||||
|
|
||||||
tfNodes:reset()
|
tfNodes:reset()
|
||||||
tfNodes:scale( w / 360, -h / 200 )
|
tfNodes:scale( w / 360, -h / 200 )
|
||||||
tfNodes:translate( 180 - x , -y - 100 )
|
tfNodes:translate( 180 - x , -y - 100 )
|
||||||
--tfNodes:translate( -x * 800 / 360, y * 400 / 200 )
|
--tfNodes:translate( -x * 800 / 360, y * 400 / 200 )
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Camera.Resize( w, h )
|
function Camera.Resize( w, h )
|
|
@ -6,14 +6,13 @@ 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 'map.locationQuery'
|
local locationQuery = require 'locationQuery'
|
||||||
local cities
|
local cities
|
||||||
local points = {}
|
local points = {}
|
||||||
local caps = {}
|
local caps = {}
|
||||||
|
|
||||||
t.selected = nil
|
t.selected = nil
|
||||||
t.selectionLocked = false
|
t.selectionLocked = false
|
||||||
local invisible = 10000 --sentinel value outside the draw rectangle
|
|
||||||
|
|
||||||
function t.lockSelection()
|
function t.lockSelection()
|
||||||
t.selectionLocked = true
|
t.selectionLocked = true
|
||||||
|
@ -54,65 +53,6 @@ function city:formatDisplayInfo()
|
||||||
CAPITAL: %s]]):format( self.name, self.country, self.x, self.y, self.pop, tostring(self.capital) )
|
CAPITAL: %s]]):format( self.name, self.country, self.x, self.y, self.pop, tostring(self.capital) )
|
||||||
end
|
end
|
||||||
|
|
||||||
function city:delete()
|
|
||||||
print( "deleting city:", self.name )
|
|
||||||
self.deleted = true
|
|
||||||
if self.capital then
|
|
||||||
caps[ self.caps ] = invisible
|
|
||||||
caps[ self.caps + 1] = invisible
|
|
||||||
end
|
|
||||||
points[ self.points ] = invisible
|
|
||||||
points[ self.points + 1] = invisible
|
|
||||||
end
|
|
||||||
|
|
||||||
function city:add()
|
|
||||||
local n = #cities + 1
|
|
||||||
cities[ n ] = self
|
|
||||||
self.n = n
|
|
||||||
|
|
||||||
local idxPoints = #points + 1
|
|
||||||
self.points = idxPoints
|
|
||||||
points[ idxPoints ], points[ idxPoints + 1 ] = self.x, self.y
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function city:moveTo(x, y)
|
|
||||||
self.x, self.y = x, y
|
|
||||||
if self.points then
|
|
||||||
points[ self.points ] = x
|
|
||||||
points[ self.points + 1 ] = y
|
|
||||||
end
|
|
||||||
if self.capital then
|
|
||||||
caps[ self.caps ] = x
|
|
||||||
caps[ self.caps + 1 ] = y
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function city:toggleCapital()
|
|
||||||
if self.capital then
|
|
||||||
self.capital = false
|
|
||||||
caps[ self.caps ] = invisible
|
|
||||||
caps[ self.caps + 1 ] = invisible
|
|
||||||
else
|
|
||||||
self.capital = true
|
|
||||||
local idx = #caps + 1
|
|
||||||
caps[ idx ] = self.x
|
|
||||||
caps[ idx + 1 ] = self.y
|
|
||||||
self.caps = idx
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.newCity( tbl )
|
|
||||||
return setmetatable({
|
|
||||||
name = "",
|
|
||||||
country = "",
|
|
||||||
x = 0,
|
|
||||||
y = 0,
|
|
||||||
pop = 0,
|
|
||||||
capital = false,
|
|
||||||
}, citymt )
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.load( filename )
|
function t.load( filename )
|
||||||
|
|
||||||
print( "=== LOADING CITIES. ===" )
|
print( "=== LOADING CITIES. ===" )
|
||||||
|
@ -130,11 +70,10 @@ function t.load( filename )
|
||||||
if capital then --check against empty or malformed line
|
if capital then --check against empty or malformed line
|
||||||
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 = setmetatable({
|
local city = setmetatable({
|
||||||
name = line:sub( 1, 39 ):gsub("%s+$",""),
|
name = line:sub( 1, 39 ):gsub("%s+$",""),
|
||||||
country = line:sub( 42, 82 ):gsub("%s+$",""),
|
country = line:sub( 42, 82 ):gsub("%s+$",""),
|
||||||
x = x, y = y, pop = pop, capital = capital,
|
x = x, y = y, pop = pop, capital = capital
|
||||||
n = n, points = idxPts, caps = capital and idxCaps
|
}, citymt )
|
||||||
}, citymt )
|
|
||||||
cities[n] = city
|
cities[n] = city
|
||||||
n = n + 1
|
n = n + 1
|
||||||
|
|
||||||
|
@ -160,13 +99,9 @@ end
|
||||||
|
|
||||||
function t.save( cities )
|
function t.save( cities )
|
||||||
local str = {}
|
local str = {}
|
||||||
local i = 1
|
for n, city in ipairs( cities ) do
|
||||||
for _, city in ipairs( cities ) do
|
str[n] = ("%-41s%-41s%-14f%-14f%-19d %d"):format(
|
||||||
if not city.deleted then
|
city.name, city.country, city.x, city.y, city.pop, city.capital and 1 or 0 )
|
||||||
str[i] = ("%-41s%-41s%-14f%-14f%-19d %d"):format(
|
|
||||||
city.name, city.country, city.x, city.y, city.pop, city.capital and 1 or 0 )
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return assert(table.concat( str, "\n" ))
|
return assert(table.concat( str, "\n" ))
|
||||||
end
|
end
|
2
conf.lua
|
@ -12,7 +12,7 @@ function love.conf(t)
|
||||||
|
|
||||||
t.window.title = "dcEarth" -- The window title (string)
|
t.window.title = "dcEarth" -- The window title (string)
|
||||||
t.window.icon = "icons/favicon.png" -- Filepath to an image to use as the window's icon (string)
|
t.window.icon = "icons/favicon.png" -- Filepath to an image to use as the window's icon (string)
|
||||||
t.window.width = 1024 -- The window width (number)
|
t.window.width = 800 -- The window width (number)
|
||||||
t.window.height = 640 -- The window height (number)
|
t.window.height = 640 -- The window height (number)
|
||||||
t.window.borderless = false -- Remove all border visuals from the window (boolean)
|
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.resizable = true -- Let the window be user-resizable (boolean)
|
||||||
|
|
Before Width: | Height: | Size: 428 KiB After Width: | Height: | Size: 144 KiB |
BIN
icons/check.png
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 391 B |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 357 B |
BIN
icons/eye.bmp
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 77 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 357 B |
Before Width: | Height: | Size: 341 B |
Before Width: | Height: | Size: 424 B |
Before Width: | Height: | Size: 383 B |
Before Width: | Height: | Size: 390 B |
Before Width: | Height: | Size: 381 B |
BIN
icons/load.png
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.1 KiB |
Before Width: | Height: | Size: 3.1 KiB |
BIN
icons/undo.bmp
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
103
lib/mkdir.lua
|
@ -1,103 +0,0 @@
|
||||||
-- mkdir only
|
|
||||||
-- A portable filesystem API using LuaJIT's FFI
|
|
||||||
-- Retrieved 2024-07-13 from https://gist.githubusercontent.com/Techcable/503f35ceea9554fb81cf3a5c1aa550da/raw/33a29f59207335b743824fbb657e4721a12ce280/fs.lua
|
|
||||||
local ffi = require("ffi")
|
|
||||||
local table = require("table")
|
|
||||||
require("string")
|
|
||||||
-- Cache needed functions and locals
|
|
||||||
local C, errno, string = ffi.C, ffi.errno, ffi.string
|
|
||||||
local concat, insert = table.concat, table.insert
|
|
||||||
|
|
||||||
-- "Standard" C99 functions
|
|
||||||
ffi.cdef[[
|
|
||||||
char *strerror(int errnum);
|
|
||||||
]]
|
|
||||||
|
|
||||||
local exists, mkdir, PATH_SEPARATOR
|
|
||||||
if ffi.os == "Windows" then
|
|
||||||
ffi.cdef[[
|
|
||||||
bool CreateDirectoryA(const char *path, void *lpSecurityAttributes);
|
|
||||||
]]
|
|
||||||
function mkdir(path, _)
|
|
||||||
assert(type(path) == "string", "path isn't a string")
|
|
||||||
if not C.CreateDirectoryA(path, nil) then
|
|
||||||
local message = string(C.strerror(errno()))
|
|
||||||
error("Unable to create directory " .. path .. ": " .. message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
PATH_SEPARATOR = "\\"
|
|
||||||
elseif ffi.os == "Linux" or ffi.os == "OSX" then
|
|
||||||
ffi.cdef[[
|
|
||||||
int mkdir(const char *path, int mode);
|
|
||||||
]]
|
|
||||||
function mkdir(path, mode)
|
|
||||||
assert(type(path) == "string", "path isn't a string")
|
|
||||||
local mode = tonumber(mode or "755", 8)
|
|
||||||
if C.mkdir(path, mode) ~= 0 then
|
|
||||||
local message = string(C.strerror(errno()))
|
|
||||||
error("Unable to create directory " .. path .. ": " .. message)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
PATH_SEPARATOR = "/"
|
|
||||||
else
|
|
||||||
error("Unsupported operating system: " .. ffi.os)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function join(...)
|
|
||||||
local parts = {}
|
|
||||||
for i = 1, select("#", ...) do
|
|
||||||
local part = select(i, ...)
|
|
||||||
insert(parts, part)
|
|
||||||
end
|
|
||||||
return concat(parts, PATH_SEPARATOR)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function splitPath(path)
|
|
||||||
assert(type(path) == "string", "path isn't a string!")
|
|
||||||
local parts = {}
|
|
||||||
local lastIndex = 0
|
|
||||||
for i = 1, path:len() do
|
|
||||||
if path:sub(i, i) == PATH_SEPARATOR then
|
|
||||||
insert(parts, path:sub(lastIndex, i - 1))
|
|
||||||
lastIndex = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
insert(parts, path:sub(lastIndex))
|
|
||||||
return parts
|
|
||||||
end
|
|
||||||
|
|
||||||
local function mkdirs(path)
|
|
||||||
local parts = splitPath(path)
|
|
||||||
local currentPath = parts[1]
|
|
||||||
for i=2, #parts do
|
|
||||||
if not exists(currentPath) then
|
|
||||||
mkdir(currentPath)
|
|
||||||
end
|
|
||||||
-- Note: This isn't suboptimal, since we really do need the intermediate results
|
|
||||||
currentPath = currentPath .. PATH_SEPARATOR .. parts[i]
|
|
||||||
end
|
|
||||||
if not exists(path) then
|
|
||||||
mkdir(path)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Check if a file or directory exists in this path
|
|
||||||
function exists(file)
|
|
||||||
local ok, err, code = os.rename(file, file)
|
|
||||||
if not ok then
|
|
||||||
if code == 13 then
|
|
||||||
-- Permission denied, but it exists
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return ok, err
|
|
||||||
end
|
|
||||||
|
|
||||||
return {
|
|
||||||
exists = exists,
|
|
||||||
join = join,
|
|
||||||
mkdir = mkdir,
|
|
||||||
mkdirs = mkdirs,
|
|
||||||
splitPath = splitPath,
|
|
||||||
PATH_SEPERATOR = PATH_SEPARATOR
|
|
||||||
}
|
|
|
@ -12,7 +12,7 @@ function polygon:formatDisplayInfo()
|
||||||
y: %f
|
y: %f
|
||||||
X: %f
|
X: %f
|
||||||
Y: %f
|
Y: %f
|
||||||
Length: %d]]):format( self.x, self.y, self.X, self.Y, #self / 4 )
|
N: %d]]):format( self.x, self.y, self.X, self.Y, #self )
|
||||||
end
|
end
|
||||||
|
|
||||||
function polygon:drawDirection()
|
function polygon:drawDirection()
|
||||||
|
@ -73,9 +73,8 @@ function t.selectNearest( lines, wx, wy )
|
||||||
poly.X + 5 > wx and
|
poly.X + 5 > wx and
|
||||||
poly.y - 5 < wy and
|
poly.y - 5 < wy and
|
||||||
poly.Y + 5 > wy then
|
poly.Y + 5 > wy then
|
||||||
for k = 1, #poly - 3, 4 do
|
for k = 1, #poly, 2 do
|
||||||
--find the midpoint of each line segment
|
local x, y = poly[k], poly[k + 1]
|
||||||
local x, y = 0.5 * (poly[k] + poly[k + 2]), 0.5 * (poly[k + 1] + poly[k + 3])
|
|
||||||
local r = ( x - wx ) * ( x - wx ) + ( y - wy ) * ( y - wy )
|
local r = ( x - wx ) * ( x - wx ) + ( y - wy ) * ( y - wy )
|
||||||
if r < d then
|
if r < d then
|
||||||
d = r
|
d = r
|
||||||
|
@ -88,14 +87,11 @@ function t.selectNearest( lines, wx, wy )
|
||||||
end
|
end
|
||||||
|
|
||||||
function t.save( lines )
|
function t.save( lines )
|
||||||
local str = { "b" } --initial B
|
local str = { "b" }
|
||||||
for i, poly in ipairs( lines ) do
|
for i, poly in ipairs( lines ) do
|
||||||
str[i + 1] = table.concat( poly, " " )
|
str[i + 1] = table.concat( poly, " " )
|
||||||
end
|
end
|
||||||
--concatenate into one big string, one line per polygon
|
str = table.concat( str, "\nb\n" ):gsub("(%S+) (%S+) ", "%1 %2\n")
|
||||||
--then put each pair of numbers on their own line (without concatenating)
|
|
||||||
--we use CRLF line breaks here because that's what's in the original game files
|
|
||||||
str = table.concat( str, "\13\nb\13\n" ):gsub("(%S+) (%S+) ", "%1 %2\13\n")
|
|
||||||
return str
|
return str
|
||||||
end
|
end
|
||||||
|
|
69
main.lua
|
@ -1,9 +1,9 @@
|
||||||
local love = assert( love, "This tool requires LOVE: love2d.org" )
|
local love = assert( love, "This tool requires LOVE: love2d.org" )
|
||||||
--assert( require('mobdebug') ).start() --remote debugger
|
--assert( require('mobdebug') ).start() --remote debugger
|
||||||
local map = require 'map.map'
|
local map = require 'map'
|
||||||
local button = require 'ui.button'
|
local button = require 'button'
|
||||||
local mainmenu = require 'ui.menu.mainmenu'
|
require 'mainmenu'
|
||||||
local Camera = require 'ui.camera'
|
local Camera = require 'camera'
|
||||||
|
|
||||||
function love.load()
|
function love.load()
|
||||||
love.filesystem.setIdentity( "dcearth", false )
|
love.filesystem.setIdentity( "dcearth", false )
|
||||||
|
@ -20,10 +20,6 @@ function love.directorydropped( path )
|
||||||
return map.load( path )
|
return map.load( path )
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.filedropped( path )
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function love.update( dt )
|
function love.update( dt )
|
||||||
local tx, ty = 0, 0
|
local tx, ty = 0, 0
|
||||||
local moveCamera = false
|
local moveCamera = false
|
||||||
|
@ -48,7 +44,29 @@ function love.draw()
|
||||||
map.draw()
|
map.draw()
|
||||||
love.graphics.pop()
|
love.graphics.pop()
|
||||||
|
|
||||||
mainmenu.draw()
|
--Status bar.
|
||||||
|
local x, y = love.mouse.getPosition()
|
||||||
|
local wx, wy = Camera.GetWorldCoordinate( x, y )
|
||||||
|
local bx, by = Camera.GetBitmapCoordinate( x, y )
|
||||||
|
local h = love.graphics.getHeight() - 60
|
||||||
|
love.graphics.setColor( 0, 0, 0, 0.9 )
|
||||||
|
love.graphics.rectangle( "fill", 0, 0, 250, love.graphics.getHeight() )
|
||||||
|
love.graphics.setColor( 1, 1, 1, 1 )
|
||||||
|
love.graphics.print(([[
|
||||||
|
SCREEN %-12d %-12d
|
||||||
|
WORLD %-12.2f%-12.2f
|
||||||
|
BITMAP %-12d %-12d
|
||||||
|
%s]]):format(x, y, wx, wy, bx, by, map.editLayer and map.editLayer.filename or ""), 0, 0)
|
||||||
|
|
||||||
|
if map.selected then love.graphics.print( map.selected:formatDisplayInfo(), 0, 80 ) end
|
||||||
|
if map.selectionLocked then end
|
||||||
|
|
||||||
|
|
||||||
|
love.graphics.rectangle( "line", 0, 0 , 250, 218 )
|
||||||
|
love.graphics.rectangle( "line", 0, 218, 250, love.graphics.getHeight() )
|
||||||
|
|
||||||
|
love.graphics.setColor( 1, 1, 1, 0.6 )
|
||||||
|
button:draw()
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.resize(w, h)
|
function love.resize(w, h)
|
||||||
|
@ -62,29 +80,31 @@ end
|
||||||
function love.mousepressed( x, y, mouseButton, istouch, presses )
|
function love.mousepressed( x, y, mouseButton, istouch, presses )
|
||||||
local wx, wy = Camera.GetWorldCoordinate( x, y )
|
local wx, wy = Camera.GetWorldCoordinate( x, y )
|
||||||
|
|
||||||
return button.mousepressed( x, y )
|
if button.selected and button.selected:contains( x, y ) then
|
||||||
|
print( ("MOUSE\tx %f\ty %f\twx %f\twy %f"):format(x, y, wx, wy) )
|
||||||
|
button.callback( button.selected )
|
||||||
|
return button.selected:callback()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.mousemoved( x, y, dx, dy, istouch )
|
function love.mousemoved( x, y, dx, dy, istouch )
|
||||||
if not map.loaded then return end
|
if not map.loaded then return end
|
||||||
--mouse over menu
|
--mouse over menu
|
||||||
if y < mainmenu.menuHeight then
|
button.selectIn( x, y )
|
||||||
button.selectIn( x, y )
|
|
||||||
--mouse on map
|
--mouse on map
|
||||||
else
|
if map.selectionLocked then return end
|
||||||
if map.selectionLocked then return end
|
if map.editLayer and map.editLayer.selectNearest then
|
||||||
if map.editLayer and map.editLayer.selectNearest then
|
map.selected = map.editLayer:selectNearest( Camera.GetWorldCoordinate( x, y ) )
|
||||||
map.selected = map.editLayer:selectNearest( Camera.GetWorldCoordinate( x, y ) )
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.keypressed(key, code, isRepeat)
|
function love.keypressed(key, code, isRepeat)
|
||||||
|
|
||||||
if code == "up" then return button.selectPrev() end
|
if code == "left" then return button.selectPrev() end
|
||||||
if code == "down" then return button.selectNext() end
|
if code == "right" then return button.selectNext() end
|
||||||
if code == "right" then return button.selectNextInGroup() end
|
if code == "down" then return button.selectNextInGroup() end
|
||||||
if code == "left" then return button.selectPrevInGroup() end
|
if code == "up" then return button.selectPrevInGroup() end
|
||||||
if code == "return" then return button.selected:callback() end
|
if code == "return" then return button.selected:callback() end
|
||||||
|
|
||||||
if key == "c" then
|
if key == "c" then
|
||||||
|
@ -92,6 +112,9 @@ function love.keypressed(key, code, isRepeat)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function love.textinput()
|
|
||||||
|
|
||||||
|
do
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
|
||||||
|
local love = assert( love )
|
||||||
|
local button = require 'button'
|
||||||
|
local savemodal = require 'savemodal'
|
||||||
|
local map = require 'map'
|
||||||
|
|
||||||
|
button.new{ name = "SAVE", y = 222, callback = savemodal.start, icon = love.graphics.newImage( "icons/save.png" )}
|
||||||
|
button.new{ name = "UNDO", y = 250, callback = map.undo, icon = love.graphics.newImage( "icons/undo.bmp" ) }
|
||||||
|
|
||||||
|
local tools
|
||||||
|
local layerButtons = {}
|
||||||
|
local function back( self )
|
||||||
|
for k, button in pairs( tools ) do button.visible = false end
|
||||||
|
for k, button in pairs( layerButtons ) do button.visible = true end
|
||||||
|
self.visible = false
|
||||||
|
map.editLayer = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local backButton = button.new{
|
||||||
|
name = "UP",
|
||||||
|
visible = false,
|
||||||
|
y = 250 + button.h + 4,
|
||||||
|
icon = love.graphics.newImage( "icons/up.bmp" ),
|
||||||
|
callback = back,
|
||||||
|
}
|
||||||
|
|
||||||
|
local function toolCallback( self )
|
||||||
|
local f = (map.layers[self.layer])[self.name]
|
||||||
|
if f then return f(self) end
|
||||||
|
end
|
||||||
|
|
||||||
|
tools = {
|
||||||
|
button.new{ name = "SELECT"},
|
||||||
|
button.new{ name = "ERASE",},
|
||||||
|
button.new{ name = "MOVE", },
|
||||||
|
button.new{ name = "ADD", },
|
||||||
|
button.new{ name = "EDIT", },
|
||||||
|
button.new{ name = "DRAW", },
|
||||||
|
}
|
||||||
|
for i, v in ipairs( tools ) do
|
||||||
|
v.callback = toolCallback
|
||||||
|
v.y = 250 + (v.h + 4) * ( i + 1 )
|
||||||
|
v.visible = false
|
||||||
|
end
|
||||||
|
|
||||||
|
local layers = {
|
||||||
|
{ name = "AF", layer = "africa" },
|
||||||
|
{ name = "EU", layer = "europe" },
|
||||||
|
{ name = "NA", layer = "northamerica" },
|
||||||
|
{ name = "SA", layer = "southamerica" },
|
||||||
|
{ name = "AS", layer = "southasia" },
|
||||||
|
{ name = "RU", layer = "russia" },
|
||||||
|
{ name = "PATH", layer = "travelnodes" },
|
||||||
|
{ name = "AI", layer = "ainodes" },
|
||||||
|
{ name = "CITY", layer = "cities" },
|
||||||
|
{ name = "COAST", layer = "coastlines" },
|
||||||
|
{ name = "LOW", layer = "coastlinesLow"},
|
||||||
|
{ name = "INT", layer = "international"},
|
||||||
|
{ name = "SAIL", layer = "sailable" },
|
||||||
|
}
|
||||||
|
|
||||||
|
local showButtons = {}
|
||||||
|
local visibilityIcon = love.graphics.newImage( "icons/eye.bmp" )
|
||||||
|
local function updateVisibilityIcons()
|
||||||
|
for i = 1, #showButtons do
|
||||||
|
showButtons[i].icon = map.layers[ showButtons[i].layer ].visible and visibilityIcon
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function toggleVisibleLayer( self )
|
||||||
|
if not (self and self.layer) then return end
|
||||||
|
local ml = map.layers[ self.layer ]
|
||||||
|
ml.visible = not( ml.visible )
|
||||||
|
return updateVisibilityIcons()
|
||||||
|
end
|
||||||
|
|
||||||
|
local soloIcon = false--love.graphics.newImage( "icons/eye.bmp" )
|
||||||
|
local function soloVisibleLayer( self )
|
||||||
|
for k, layer in pairs( map.layers ) do
|
||||||
|
print( "invisible layer, map:", k, layer)
|
||||||
|
layer.visible = false
|
||||||
|
end
|
||||||
|
map.layers[ self.layer ].visible = true
|
||||||
|
return updateVisibilityIcons()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function editLayer( self )
|
||||||
|
map.editLayer = map.layers[ self.layer ]
|
||||||
|
map.editLayer.visible = true
|
||||||
|
for k, button in pairs( layerButtons ) do button.visible = false end
|
||||||
|
for k, button in pairs( tools ) do
|
||||||
|
button.visible = true
|
||||||
|
button.layer = self.layer
|
||||||
|
end
|
||||||
|
backButton.visible = true
|
||||||
|
print( "EDITING LAYER", self.layer )
|
||||||
|
return updateVisibilityIcons()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function copy( i, target )
|
||||||
|
for k, v in pairs( layers[i] ) do
|
||||||
|
target[k] = target[k] or v
|
||||||
|
end
|
||||||
|
return target
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local y = 250
|
||||||
|
local soloButtons = {}
|
||||||
|
local editButtons = {}
|
||||||
|
for i = 1, #layers do
|
||||||
|
editButtons[i] = button.new( copy( i, {
|
||||||
|
x = 8,
|
||||||
|
y = y + (button.h + 4) * i,
|
||||||
|
w = 112,
|
||||||
|
callback = editLayer,
|
||||||
|
group = "edit",
|
||||||
|
}))
|
||||||
|
layerButtons[ 3 * i - 2 ] = editButtons[i]
|
||||||
|
|
||||||
|
showButtons[i] = button.new( copy( i, {
|
||||||
|
x = 128,
|
||||||
|
y = y + (button.h + 4) * i,
|
||||||
|
w = 24,
|
||||||
|
name = "",
|
||||||
|
callback = toggleVisibleLayer,
|
||||||
|
icon = visibilityIcon,
|
||||||
|
group = "show",
|
||||||
|
}))
|
||||||
|
layerButtons[ 3 * i - 1 ] = showButtons[i]
|
||||||
|
|
||||||
|
soloButtons[i] = button.new( copy( i, {
|
||||||
|
x = 160,
|
||||||
|
y = y + (button.h + 4) * i,
|
||||||
|
w = 24,
|
||||||
|
name = "S",
|
||||||
|
callback = soloVisibleLayer,
|
||||||
|
icon = soloIcon,
|
||||||
|
group = "solo",
|
||||||
|
}))
|
||||||
|
layerButtons[ 3 * i ] = soloButtons[i]
|
||||||
|
end
|
|
@ -1,15 +1,10 @@
|
||||||
local love = assert( love )
|
|
||||||
local io = io
|
|
||||||
local coroutine = coroutine
|
|
||||||
local mkdir = assert( require 'lib.mkdir' )
|
|
||||||
local lg = love.graphics
|
local lg = love.graphics
|
||||||
local AI = require 'map.ai'
|
local AI = require 'ai'
|
||||||
local Cities = require 'map.cities'
|
local Cities = require 'cities'
|
||||||
local Lines = require 'map.lines'
|
local Lines = require 'lines'
|
||||||
local Nodes = require 'map.travelNodes'
|
local Nodes = require 'travelNodes'
|
||||||
local Camera = require 'ui.camera'
|
local Camera = require 'camera'
|
||||||
local Territory = require 'map.territory'
|
local Territory = require 'territory'
|
||||||
local Blur = require 'map.blur'
|
|
||||||
|
|
||||||
--flat list of editable layers for convenience
|
--flat list of editable layers for convenience
|
||||||
local layers = {
|
local layers = {
|
||||||
|
@ -26,7 +21,6 @@ local layers = {
|
||||||
sailable = false,
|
sailable = false,
|
||||||
ainodes = false,
|
ainodes = false,
|
||||||
cities = false,
|
cities = false,
|
||||||
blur = false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local map = {
|
local map = {
|
||||||
|
@ -53,28 +47,9 @@ local map = {
|
||||||
travelnodes = false,
|
travelnodes = false,
|
||||||
sailable = false,
|
sailable = false,
|
||||||
ainodes = false,
|
ainodes = false,
|
||||||
cities = false,
|
cities = false
|
||||||
blur = false,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function map.reloadLayer( path )
|
|
||||||
--Shouldn't call this before the map loads, but just in case
|
|
||||||
if not map.loaded then return end
|
|
||||||
for name, layer in pairs( layers ) do
|
|
||||||
if layer.filename and
|
|
||||||
(layer.filename:gsub( ".+[\\/]", "") == path.filename:gsub( ".+[\\/]", "" )) then
|
|
||||||
local newLayer = layer:load( path )
|
|
||||||
newLayer.filename = layer.filename --we don't store the full path in there
|
|
||||||
map[ name ] = newLayer
|
|
||||||
layers[ name ] = newLayer
|
|
||||||
return layer.filename
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
function map.load( path )
|
function map.load( path )
|
||||||
map.background = lg.newImage( "/data/graphics/blur.bmp" )
|
map.background = lg.newImage( "/data/graphics/blur.bmp" )
|
||||||
map.cities = Cities.load( "/data/earth/cities.dat" )
|
map.cities = Cities.load( "/data/earth/cities.dat" )
|
||||||
|
@ -84,7 +59,6 @@ function map.load( path )
|
||||||
map.sailable = Territory.load( "/data/earth/sailable.bmp", "sailable" )
|
map.sailable = Territory.load( "/data/earth/sailable.bmp", "sailable" )
|
||||||
map.travelnodes = Nodes.load( "/data/earth/travel_nodes.bmp", map.sailable.isSailable ) --travel node adjacency matrix depends on sailable bitmap
|
map.travelnodes = Nodes.load( "/data/earth/travel_nodes.bmp", map.sailable.isSailable ) --travel node adjacency matrix depends on sailable bitmap
|
||||||
map.ainodes = AI.load( "/data/earth/ai_markers.bmp" )
|
map.ainodes = AI.load( "/data/earth/ai_markers.bmp" )
|
||||||
map.blur = Blur.load( "/data/graphics/blur.bmp", map )
|
|
||||||
for k, v in pairs(map.territory) do
|
for k, v in pairs(map.territory) do
|
||||||
map.territory[k] = Territory.load( "/data/earth/"..k..".bmp", k )
|
map.territory[k] = Territory.load( "/data/earth/"..k..".bmp", k )
|
||||||
end
|
end
|
||||||
|
@ -98,7 +72,6 @@ function map.load( path )
|
||||||
end
|
end
|
||||||
|
|
||||||
function map.draw()
|
function map.draw()
|
||||||
love.graphics.setScissor( 0, 200, love.graphics.getWidth(), love.graphics.getHeight() - 200 )
|
|
||||||
lg.clear( 0, 0, 0, 1 )
|
lg.clear( 0, 0, 0, 1 )
|
||||||
if not map.loaded then return end
|
if not map.loaded then return end
|
||||||
|
|
||||||
|
@ -108,31 +81,31 @@ function map.draw()
|
||||||
lg.setBlendMode( "add" )
|
lg.setBlendMode( "add" )
|
||||||
|
|
||||||
lg.setColor( 1, 1, 1, 0.2 )
|
lg.setColor( 1, 1, 1, 0.2 )
|
||||||
--lg.draw( map.background )
|
lg.draw( map.background )
|
||||||
lg.setColor( 1, 1, 1, 0.5 )
|
lg.setColor( 1, 1, 1, 0.5 )
|
||||||
local sh = require 'shaders.sailable'
|
|
||||||
lg.setShader( sh )
|
|
||||||
sh:send( "lowBorder", 60 )
|
|
||||||
sh:send( "highBorder", 130 )
|
|
||||||
for k, v in pairs(map.territory) do
|
for k, v in pairs(map.territory) do
|
||||||
if v.visible then
|
if v.visible then
|
||||||
|
|
||||||
v:draw()
|
v:draw()
|
||||||
--[[lg.setLineWidth( 1 / Camera.zoom )
|
lg.setLineWidth( 1 / Camera.zoom )
|
||||||
v:drawBorder( "land" )
|
v:drawBorder( "land" )
|
||||||
lg.setLineWidth( 3 / Camera.zoom )
|
lg.setLineWidth( 3 / Camera.zoom )
|
||||||
v:drawBorder( "sea" )]]
|
v:drawBorder( "sea" )
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if map.sailable.visible then
|
if map.sailable.visible then
|
||||||
sh:send( "lowBorder", 20 )
|
|
||||||
sh:send( "highBorder", 60 )
|
|
||||||
lg.setShader( require 'shaders.sailable' )
|
|
||||||
map.sailable:draw()
|
map.sailable:draw()
|
||||||
|
|
||||||
|
lg.setLineJoin( "none" )
|
||||||
|
lg.setLineWidth( 1 / Camera.zoom )
|
||||||
|
|
||||||
|
lg.setColor( 1, 1, 1, 0.5)
|
||||||
|
map.sailable:drawBorder( "sailable" )
|
||||||
|
|
||||||
|
lg.setLineWidth( 3 / Camera.zoom )
|
||||||
|
map.sailable:drawBorder( "placeable" )
|
||||||
end
|
end
|
||||||
lg.setShader()
|
|
||||||
lg.setBlendMode( "alpha" )
|
lg.setBlendMode( "alpha" )
|
||||||
|
|
||||||
lg.setColor( 1, 1, 1, 1 )
|
lg.setColor( 1, 1, 1, 1 )
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -205,42 +178,20 @@ function map.draw()
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
do
|
local function write( filename, string )
|
||||||
local function write( filename, string )
|
--
|
||||||
local file = assert( io.open( filename, "wb" ) )
|
print( "Writing", string:len(), "bytes to", filename )
|
||||||
assert( file:write( string ) )
|
os.rename( filename, filename..".bak" ) --just in case :^)
|
||||||
assert( file:flush() ) --your toilet is set to stun, not kill
|
local file = assert( io.open( filename, "wb+" ) )
|
||||||
file:close()
|
assert( file:write( string ) )
|
||||||
|
assert( file:flush() ) --your toilet is set to stun, not kill
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function map.save()
|
||||||
|
for k, layer in pairs( layers ) do
|
||||||
|
write( map.path..tostring( layer.filename ), assert( layer:save() ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
local function save()
|
|
||||||
--should be cross platform-ish.
|
|
||||||
--race condition, unfortunately.
|
|
||||||
--maybe we should do this on part on load, then keep a lockfile open in each of these folders
|
|
||||||
for _, folder in ipairs{ "/data/", "/data/earth/", "/data/graphics/" } do
|
|
||||||
coroutine.yield( "Creating folder ".. folder )
|
|
||||||
assert( mkdir.exists( map.path ), map.path )
|
|
||||||
local path = map.path..folder
|
|
||||||
if not mkdir.exists( path ) then mkdir.mkdir( path ) end
|
|
||||||
end
|
|
||||||
|
|
||||||
local files = {}
|
|
||||||
--Write everything to strings first, in case there are errors we don't want to half-write the map
|
|
||||||
for k, layer in pairs( layers ) do
|
|
||||||
coroutine.yield( "Exporting layer ".. k )
|
|
||||||
files[ map.path..tostring( layer.filename ) ] = assert( layer:save() )
|
|
||||||
end
|
|
||||||
for filename, str in pairs( files ) do
|
|
||||||
coroutine.yield( "Writing ".. filename )
|
|
||||||
write( filename, str )
|
|
||||||
end
|
|
||||||
|
|
||||||
coroutine.yield() --yield nothing to indicate we're done
|
|
||||||
return save() --save again
|
|
||||||
end
|
|
||||||
|
|
||||||
map.save = coroutine.wrap( save )
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function map.hover(x, y)
|
function map.hover(x, y)
|
||||||
|
@ -251,17 +202,4 @@ function map.undo()
|
||||||
print( "=== UNDO ===" )
|
print( "=== UNDO ===" )
|
||||||
end
|
end
|
||||||
|
|
||||||
function map.setEditLayer( layerName )
|
|
||||||
if not layerName then
|
|
||||||
map.editLayer = nil
|
|
||||||
for name, layer in pairs( layers ) do layer.visible = true end
|
|
||||||
else
|
|
||||||
for name, layer in pairs( layers ) do
|
|
||||||
layer.visible = false
|
|
||||||
end
|
|
||||||
map.editLayer = layers[ layerName ]
|
|
||||||
if map.editLayer then map.editLayer.visible = true end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return map
|
return map
|
113
map/blur.lua
|
@ -1,113 +0,0 @@
|
||||||
local love = assert( love )
|
|
||||||
local lg = love.graphics
|
|
||||||
local lt = love.timer
|
|
||||||
local coroutine = assert( coroutine )
|
|
||||||
local bmp = require 'map.bmp'
|
|
||||||
|
|
||||||
local scale = 1
|
|
||||||
local w, h = scale * 512, scale * 285
|
|
||||||
|
|
||||||
local dilateShader = require 'shaders.dilate'
|
|
||||||
local finalCanvas = lg.newCanvas( w, h )
|
|
||||||
local canvas = lg.newCanvas( w, h )
|
|
||||||
local identityTransform = love.math.newTransform()
|
|
||||||
|
|
||||||
local tiles = {}
|
|
||||||
do
|
|
||||||
local sideLength = 4
|
|
||||||
local i = 1
|
|
||||||
for x = 1, sideLength do
|
|
||||||
for y = 1, sideLength do
|
|
||||||
local width = math.floor( w / sideLength )
|
|
||||||
local height = math.floor( h / sideLength )
|
|
||||||
tiles[i] = function() return
|
|
||||||
(x - 1) * width, (y - 1) * height,
|
|
||||||
width, height
|
|
||||||
end
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
--the coroutine library doesn't reset all the global graphics state for us
|
|
||||||
--so this yield function does
|
|
||||||
local function y( s, currentTile )
|
|
||||||
lg.pop( "all" )
|
|
||||||
coroutine.yield( s )
|
|
||||||
lg.push( "all" )
|
|
||||||
lg.setCanvas( canvas )
|
|
||||||
lg.setShader( dilateShader )
|
|
||||||
lg.setScissor( currentTile() )
|
|
||||||
lg.setBlendMode( "add" )
|
|
||||||
end
|
|
||||||
|
|
||||||
local function renderRadar( map, filename )
|
|
||||||
local statusString = "%s: rendering %s radius\n %s\n %d/%d"
|
|
||||||
--we need this first push to the stack to keep y() simple
|
|
||||||
lg.push( "all" )
|
|
||||||
y( filename, tiles[1] )
|
|
||||||
--then we clear the whole canvas to opaque black, pause, and select the first tile
|
|
||||||
lg.setScissor()
|
|
||||||
lg.clear( 0, 0, 0, 1 )
|
|
||||||
dilateShader:send( "sailable", map.sailable.img )
|
|
||||||
for tile = 1, #tiles do
|
|
||||||
|
|
||||||
--dilate placeable land area by 30 degrees (radar radius)
|
|
||||||
dilateShader:send( "radius", math.floor( 512 * 30 / 360 ) )
|
|
||||||
|
|
||||||
for name, terr in pairs( map.territory ) do
|
|
||||||
y( statusString:format( filename, "radar", name, tile, #tiles ), tiles[tile] )
|
|
||||||
lg.setColor(
|
|
||||||
0.9 + terr.colour[1],
|
|
||||||
0.9 + terr.colour[2],
|
|
||||||
0.9 + terr.colour[3],
|
|
||||||
1 / 6 )
|
|
||||||
lg.draw( terr.img, 0, 0, 0, scale, scale )
|
|
||||||
end
|
|
||||||
|
|
||||||
--dilate placeable land area by 45 degrees (sub launch radius)
|
|
||||||
dilateShader:send( "radius", math.floor( 512 * 45 / 360 ) )
|
|
||||||
for name, terr in pairs( map.territory ) do
|
|
||||||
y( statusString:format( filename, "sub", name, tile, #tiles ), tiles[tile] )
|
|
||||||
lg.setColor(
|
|
||||||
0.9 + terr.colour[1],
|
|
||||||
0.9 + terr.colour[2],
|
|
||||||
0.9 + terr.colour[3],
|
|
||||||
1 / 12 )
|
|
||||||
lg.draw( terr.img, 0, 0, 0, scale, scale )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
lg.setCanvas( finalCanvas )
|
|
||||||
lg.setScissor()
|
|
||||||
lg.setShader()
|
|
||||||
lg.clear( 0, 0, 0, 1 )
|
|
||||||
lg.setColor( 1, 1, 1, 1 )
|
|
||||||
lg.draw( canvas )
|
|
||||||
lg.pop( "all" )
|
|
||||||
|
|
||||||
coroutine.yield( ("%s: encoding image data"):format( filename ))
|
|
||||||
|
|
||||||
return bmp.blur( finalCanvas:newImageData() )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
local t = {}
|
|
||||||
|
|
||||||
function t:save( )
|
|
||||||
return renderRadar( self.map, self.filename )
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.load( filename, map )
|
|
||||||
t.filename = filename
|
|
||||||
t.map = map
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.draw()
|
|
||||||
lg.setScissor()
|
|
||||||
return lg.draw( canvas )
|
|
||||||
end
|
|
||||||
|
|
||||||
return t
|
|
|
@ -1,19 +0,0 @@
|
||||||
local t = {}
|
|
||||||
t.options = {
|
|
||||||
Name = "New DEFCON Mod",
|
|
||||||
Version = "0.1",
|
|
||||||
Author = "DeFacto",
|
|
||||||
Website = "https://wan-may.art/dev/",
|
|
||||||
Comment = "DEFCON map made with dcEarth",
|
|
||||||
radarExport = true,
|
|
||||||
screenGenerator = true,
|
|
||||||
highDetail = true,
|
|
||||||
}
|
|
||||||
|
|
||||||
--parse and load metadata from mod.txt
|
|
||||||
function t.load( filename )
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return t
|
|
5
mod.txt
|
@ -1,5 +0,0 @@
|
||||||
Name New DEFCON Mod
|
|
||||||
Version 0.1
|
|
||||||
Author DeFacto
|
|
||||||
Website https://wan-may.art/dev/
|
|
||||||
Comment DEFCON map made with dcEarth
|
|
|
@ -1,22 +1,12 @@
|
||||||
local love = assert( love )
|
local love = assert( love )
|
||||||
local button = require( "ui.button" )
|
local button = require( "button" )
|
||||||
local t = {}
|
local t = {}
|
||||||
t.__index = t
|
t.__index = t
|
||||||
|
|
||||||
local i = 0
|
local i = 0
|
||||||
function t.start( self )
|
function t.start( self )
|
||||||
print( "starting modal:", i + 1)
|
|
||||||
love.graphics.push( "all" )
|
|
||||||
love.graphics.setScissor(
|
|
||||||
self.x or 0,
|
|
||||||
self.y or 0,
|
|
||||||
self.w or love.graphics.getWidth(),
|
|
||||||
self.h or love.graphics.getHeight())
|
|
||||||
|
|
||||||
i = i + 1
|
i = i + 1
|
||||||
t[i] = { modal = self }
|
t[i] = t[i] or {}
|
||||||
t.previous = t[i]
|
|
||||||
t.current = self
|
|
||||||
|
|
||||||
--store callbacks
|
--store callbacks
|
||||||
for name in pairs( self ) do
|
for name in pairs( self ) do
|
||||||
|
@ -38,31 +28,21 @@ function t.stop( self )
|
||||||
--restore callbacks
|
--restore callbacks
|
||||||
for name in pairs( self ) do
|
for name in pairs( self ) do
|
||||||
if love[name] then
|
if love[name] then
|
||||||
love[name] = t[i][name] or love[name]
|
love[name] = t[i][name]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--restore menus
|
--restore menus
|
||||||
local b = button
|
local b = button
|
||||||
button.deselect()
|
button.selected = button
|
||||||
repeat
|
repeat
|
||||||
b = b.next
|
b = b.next
|
||||||
b.visible = t[i][b] or false
|
b.visible = t[i][b]
|
||||||
until b == button
|
until b == button
|
||||||
|
|
||||||
|
|
||||||
t.current = t[i].modal
|
|
||||||
t[i] = nil
|
t[i] = nil
|
||||||
i = i - 1
|
i = i - 1
|
||||||
t.previous = t[i - 1]
|
|
||||||
|
|
||||||
love.graphics.pop( "all" )
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.exitAll()
|
|
||||||
if i < 1 then return end
|
|
||||||
i = 1
|
|
||||||
return t.stop( love )
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function t.new( modal )
|
function t.new( modal )
|
|
@ -1,5 +1 @@
|
||||||
Map editor for DEFCON, the strategy game, written in LOVE2D, the engine.
|
Map editor for DEFCON, the strategy game, written in LOVE2D, the engine.
|
||||||
|
|
||||||
Currently does not do anything besides read the files and write them back out.
|
|
||||||
|
|
||||||
Need to make a more structured UI, rewriting menu.lua and button.lua for this purpose.
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
local love = assert( love )
|
||||||
|
local modal = require( "modal" )
|
||||||
|
local button = require( "button" )
|
||||||
|
local map = require( "map" )
|
||||||
|
local t = {}
|
||||||
|
|
||||||
|
local saveLocation = map.path
|
||||||
|
local floppy = love.graphics.newImage( "icons/save.png" )
|
||||||
|
floppy:setFilter( "nearest", "nearest" )
|
||||||
|
local saveButton = button.new{
|
||||||
|
group = "saveModal",
|
||||||
|
name = "save",
|
||||||
|
callback = function() map.save(); return t:stop() end,
|
||||||
|
visible = false,
|
||||||
|
icon = floppy,
|
||||||
|
x = love.graphics.getWidth() / 2 - 300,
|
||||||
|
y = love.graphics.getHeight() / 2 - 150,
|
||||||
|
w = 600,
|
||||||
|
h = 100,
|
||||||
|
}
|
||||||
|
|
||||||
|
local xIcon = love.graphics.newImage( "icons/x.png" )
|
||||||
|
xIcon:setFilter( "nearest", "nearest" )
|
||||||
|
local cancelButton = button.new{
|
||||||
|
group = "saveModal",
|
||||||
|
name = "cancel",
|
||||||
|
visible = false,
|
||||||
|
icon = xIcon,
|
||||||
|
callback = function() return t:stop() end,
|
||||||
|
x = love.graphics.getWidth() / 2 - 300,
|
||||||
|
y = love.graphics.getHeight() / 2,
|
||||||
|
w = 600,
|
||||||
|
h = 100
|
||||||
|
}
|
||||||
|
|
||||||
|
function t.start()
|
||||||
|
modal.start( t )
|
||||||
|
button.selected = saveButton
|
||||||
|
saveButton.name = "save to "..map.path
|
||||||
|
button.displayGroup( "saveModal", true )
|
||||||
|
end
|
||||||
|
|
||||||
|
function t.draw()
|
||||||
|
love.graphics.clear( 0,0,0,1 )
|
||||||
|
love.graphics.setColor( 1, 0, 0, 0.4 )
|
||||||
|
button:draw()
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
function t.directorydropped( path )
|
||||||
|
saveLocation = path
|
||||||
|
map.path = path
|
||||||
|
saveButton.name = "save to "..map.path
|
||||||
|
return love.filesystem.mount( path, "" )
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return modal.new( t )
|
BIN
screenshot.bmp
Before Width: | Height: | Size: 96 KiB |
|
@ -1,46 +0,0 @@
|
||||||
return love.graphics.newShader[[
|
|
||||||
#pragma language glsl3
|
|
||||||
uniform int radius;
|
|
||||||
uniform Image sailable;
|
|
||||||
|
|
||||||
bool own( float x )
|
|
||||||
{
|
|
||||||
if( x * 255.0 > 130.0 ){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool land( float x )
|
|
||||||
{
|
|
||||||
if( x * 255.0 <= 60.0 ){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool placeable( Image tex, vec2 uv )
|
|
||||||
{
|
|
||||||
return own(Texel(tex, uv).r) && land(Texel(sailable, uv).r);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 effect( vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords )
|
|
||||||
{
|
|
||||||
for(int i = -radius; i <= radius; ++i) {
|
|
||||||
for(int j = -radius; j <= radius; ++j) {
|
|
||||||
vec2 offset = vec2( ivec2( i, j ) ) / vec2( textureSize(tex, 0) );
|
|
||||||
int r = ((i * i) + (j * j));
|
|
||||||
if ( (r < (radius * radius) )
|
|
||||||
&& placeable( tex, texture_coords + offset )){
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return vec4( 0.0, 0.0, 0.0, 0.0 );
|
|
||||||
}
|
|
||||||
]]
|
|
|
@ -1,36 +0,0 @@
|
||||||
return love.graphics.newShader[[
|
|
||||||
#pragma language glsl3
|
|
||||||
|
|
||||||
uniform float highBorder;
|
|
||||||
uniform float lowBorder;
|
|
||||||
|
|
||||||
#define p(x) ((x) * 255.0 > highBorder)
|
|
||||||
#define t(x) ((x) * 255.0 > lowBorder)
|
|
||||||
|
|
||||||
vec4 effect( vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords )
|
|
||||||
{
|
|
||||||
vec2 s = vec2( 1.0, 1.0 ) / vec2( textureSize(tex, 0) );
|
|
||||||
float c = Texel( tex, texture_coords ).r;
|
|
||||||
vec4 g = vec4(
|
|
||||||
Texel( tex, texture_coords + s * vec2(1.0, 0.0)).r,
|
|
||||||
Texel( tex, texture_coords + s * vec2(0.0, 1.0)).r,
|
|
||||||
Texel( tex, texture_coords + s * vec2(-1.0, 0.0)).r,
|
|
||||||
Texel( tex, texture_coords + s * vec2(0.0, -1.0)).r
|
|
||||||
);
|
|
||||||
bvec4 place = bvec4( p(g.r), p(g.g), p(g.b), p(g.a) );
|
|
||||||
bvec4 traverse = bvec4( t(g.r), t(g.g), t(g.b), t(g.a) );
|
|
||||||
float a;
|
|
||||||
if ( p(c) ){
|
|
||||||
if( all( place ) ) { a = 1.0; }
|
|
||||||
else { a = 2.0; }
|
|
||||||
}
|
|
||||||
else if( t(c) ){
|
|
||||||
if( all( traverse ) ) { a = 0.5; }
|
|
||||||
else { a = 0.75; }
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
a = 0.0;
|
|
||||||
}
|
|
||||||
return vec4( color.rgb, a * color.a );
|
|
||||||
}
|
|
||||||
]]
|
|
|
@ -1,54 +0,0 @@
|
||||||
--[[
|
|
||||||
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
|
|
||||||
]]
|
|
||||||
|
|
||||||
return love.graphics.newShader[[
|
|
||||||
#pragma language glsl3
|
|
||||||
uniform int radius;
|
|
||||||
uniform Image sailable;
|
|
||||||
|
|
||||||
bool seaPlace( float x, float y )
|
|
||||||
{
|
|
||||||
return ( x * 255.0 > 60.0 ) && ( y * 255.0 > 60.0 );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool own( float x )
|
|
||||||
{
|
|
||||||
if( x * 255.0 > 130.0 ){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool land( float x )
|
|
||||||
{
|
|
||||||
if( x * 255.0 <= 60.0 ){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool placeable( Image tex, vec2 uv )
|
|
||||||
{
|
|
||||||
return own(Texel(tex, uv).r) && land(Texel(sailable, uv).r);
|
|
||||||
}
|
|
||||||
|
|
||||||
vec4 effect( vec4 color, Image tex, vec2 texture_coords, vec2 screen_coords )
|
|
||||||
{
|
|
||||||
if (placeable( tex, texture_coords + offset )){
|
|
||||||
return color;
|
|
||||||
}
|
|
||||||
return vec4( 0.0, 0.0, 0.0, 0.0 );
|
|
||||||
}
|
|
||||||
]]
|
|
|
@ -1,5 +1,5 @@
|
||||||
local t = {}
|
local t = {}
|
||||||
local bmp = require 'map.bmp'
|
local bmp = require 'bmp'
|
||||||
local lg = assert( love.graphics )
|
local lg = assert( love.graphics )
|
||||||
|
|
||||||
local colours = {
|
local colours = {
|
||||||
|
@ -144,6 +144,7 @@ end
|
||||||
|
|
||||||
function t.save( territory )
|
function t.save( territory )
|
||||||
local fmt = (territory.name == "sailable") and "sailable" or "territory"
|
local fmt = (territory.name == "sailable") and "sailable" or "territory"
|
||||||
|
print( "saving bitmap: ", territory.name, fmt )
|
||||||
return bmp[fmt]( territory.imgd )
|
return bmp[fmt]( territory.imgd )
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
local love = assert( love )
|
local love = assert( love )
|
||||||
local utf8 = require("utf8")
|
local utf8 = require("utf8")
|
||||||
local modal = require( "ui.modal" )
|
local modal = require( "modal" )
|
||||||
local t = modal.new{ }
|
local t = modal.new{ }
|
||||||
|
|
||||||
function t.setCurrentModal( fields )
|
function t.setCurrentModal( fields )
|
||||||
|
@ -47,17 +47,16 @@ function t.keypressed(key, code, isRepeat)
|
||||||
end
|
end
|
||||||
|
|
||||||
if key == "backspace" then
|
if key == "backspace" then
|
||||||
local text = t.currentModal.currentField
|
|
||||||
-- get the byte offset to the last UTF-8 character in the string.
|
-- get the byte offset to the last UTF-8 character in the string.
|
||||||
local byteoffset = utf8.offset(text, -1)
|
local byteoffset = utf8.offset(text, -1)
|
||||||
|
|
||||||
if byteoffset then
|
if byteoffset then
|
||||||
-- remove the last UTF-8 character.
|
-- remove the last UTF-8 character.
|
||||||
-- string.sub operates on bytes rather than UTF-8 characters, so we couldn't do string.sub(text, 1, -2).
|
-- string.sub operates on bytes rather than UTF-8 characters, so we couldn't do string.sub(text, 1, -2).
|
||||||
t.currentModal.currentField = text:sub( 1, byteoffset - 1)
|
text = string.sub(text, 1, byteoffset - 1)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
if code == "escape" then
|
if key == "escape" then
|
||||||
return t:stop()
|
return t:stop()
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -2,8 +2,8 @@
|
||||||
--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 bmp = require 'map.bmp'
|
local bmp = require 'bmp'
|
||||||
local locationQuery = require 'map.locationQuery'
|
local locationQuery = require 'locationQuery'
|
||||||
local lg = assert( love.graphics )
|
local lg = assert( love.graphics )
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ end
|
||||||
|
|
||||||
function t.load( filename, sailable )
|
function t.load( filename, sailable )
|
||||||
|
|
||||||
isSailable = sailable or isSailable
|
isSailable = sailable
|
||||||
local img, imgd = bmp.load( filename )
|
local img, imgd = bmp.load( filename )
|
||||||
local nodes = { filename = filename, visible = true, nodes = {}, points = {}, connections = {}, img = img }
|
local nodes = { filename = filename, visible = true, nodes = {}, points = {}, connections = {}, img = img }
|
||||||
t.nodes = nodes
|
t.nodes = nodes
|
||||||
|
@ -107,6 +107,7 @@ end
|
||||||
|
|
||||||
local function dfs( idx, adj, unvisited )
|
local function dfs( idx, adj, unvisited )
|
||||||
if not unvisited[idx] then return end
|
if not unvisited[idx] then return end
|
||||||
|
print( "visiting node", idx )
|
||||||
unvisited[ idx ] = nil
|
unvisited[ idx ] = nil
|
||||||
for i in pairs( adj[idx] ) do dfs( i, adj, unvisited ) end
|
for i in pairs( adj[idx] ) do dfs( i, adj, unvisited ) end
|
||||||
end
|
end
|
|
@ -1,65 +0,0 @@
|
||||||
local love = assert( love )
|
|
||||||
local modal = require( "ui.modal" )
|
|
||||||
local button = require( "ui.button" )
|
|
||||||
local map = require( "map.map" )
|
|
||||||
local t = {}
|
|
||||||
|
|
||||||
local loadLocation = false
|
|
||||||
local folder = love.graphics.newImage( "icons/load.png" )
|
|
||||||
folder:setFilter( "nearest", "nearest" )
|
|
||||||
local loadButton = button.new{
|
|
||||||
group = t,
|
|
||||||
name = "load",
|
|
||||||
callback = function()
|
|
||||||
if not loadLocation then return end
|
|
||||||
map.load( loadLocation )
|
|
||||||
return t:stop() end,
|
|
||||||
visible = false,
|
|
||||||
icon = folder,
|
|
||||||
x = love.graphics.getWidth() / 2 - 300,
|
|
||||||
y = love.graphics.getHeight() / 2 - 150,
|
|
||||||
w = 600,
|
|
||||||
h = 100,
|
|
||||||
}
|
|
||||||
|
|
||||||
local xIcon = love.graphics.newImage( "icons/x.png" )
|
|
||||||
xIcon:setFilter( "nearest", "nearest" )
|
|
||||||
local cancelButton = button.new{
|
|
||||||
group = t,
|
|
||||||
name = "cancel load",
|
|
||||||
visible = false,
|
|
||||||
icon = xIcon,
|
|
||||||
callback = function() return t:stop() end,
|
|
||||||
x = love.graphics.getWidth() / 2 - 300,
|
|
||||||
y = love.graphics.getHeight() / 2,
|
|
||||||
w = 600,
|
|
||||||
h = 100
|
|
||||||
}
|
|
||||||
|
|
||||||
function t.start()
|
|
||||||
modal.start( t )
|
|
||||||
loadLocation = loadLocation or map.path
|
|
||||||
button.selected = loadButton
|
|
||||||
loadButton.name = "load from "..loadLocation
|
|
||||||
button.displayGroup( t, false, true )
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.draw()
|
|
||||||
love.graphics.clear( 0,0,0,1 )
|
|
||||||
love.graphics.setColor( 0, 0, 1, 0.4 )
|
|
||||||
button:draw()
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.directorydropped( path )
|
|
||||||
loadLocation = path
|
|
||||||
map.path = path
|
|
||||||
loadButton.name = "load from "..map.path
|
|
||||||
return love.filesystem.mount( path, "" )
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.filedropped( path )
|
|
||||||
return map.reloadLayer( path )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return modal.new( t )
|
|
|
@ -1,45 +0,0 @@
|
||||||
local t = {}
|
|
||||||
local lg = assert( love ).graphics
|
|
||||||
local modal = require 'ui.modal'
|
|
||||||
local button = require 'ui.button'
|
|
||||||
local camera = require 'ui.camera'
|
|
||||||
local map = require 'map.map'
|
|
||||||
|
|
||||||
local node
|
|
||||||
local moveModal = modal.new{}
|
|
||||||
local selectModal = modal.new{}
|
|
||||||
|
|
||||||
button.new{ name = "ATTACK NODE",
|
|
||||||
group = t,
|
|
||||||
icon = lg.newImage("icons/node-attack.png"),
|
|
||||||
x = 615,
|
|
||||||
y = 0,
|
|
||||||
callback = function()
|
|
||||||
node = map.ainodes.newNode( true )
|
|
||||||
return moveModal:start()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
button.new{ name = "PLACEMENT NODE",
|
|
||||||
group = t,
|
|
||||||
icon = lg.newImage("icons/node-place.png"),
|
|
||||||
x = 615,
|
|
||||||
y = 1 * (4 + button.h),
|
|
||||||
callback = function()
|
|
||||||
node = map.ainodes.newNode( true )
|
|
||||||
return moveModal:start()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
button.new{ name = "MOVE NODE",
|
|
||||||
group = t,
|
|
||||||
y = 2 * (4 + button.h),
|
|
||||||
x = 615,
|
|
||||||
}
|
|
||||||
|
|
||||||
button.new{ name = "DELETE NODE",
|
|
||||||
group = t,
|
|
||||||
y = 3 * (4 + button.h),
|
|
||||||
x = 615,
|
|
||||||
}
|
|
||||||
return t
|
|
|
@ -1,279 +0,0 @@
|
||||||
|
|
||||||
local love = assert( love )
|
|
||||||
local lg = assert( love ).graphics
|
|
||||||
local utf8 = require 'utf8'
|
|
||||||
|
|
||||||
local button = require 'ui.button'
|
|
||||||
local textinput = require 'ui.textinput'
|
|
||||||
local modal = require 'ui.modal'
|
|
||||||
local map = require 'map.map'
|
|
||||||
local camera = require 'ui.camera'
|
|
||||||
|
|
||||||
local t = {}
|
|
||||||
local city
|
|
||||||
|
|
||||||
|
|
||||||
local function keypressed( key, code, isRepeat )
|
|
||||||
if code == 'escape' then return modal.current:stop() end
|
|
||||||
if modal.previous then return modal.previous.keypressed( key, code, isRepeat ) end
|
|
||||||
end
|
|
||||||
|
|
||||||
--select modal: as normal, but clicking a city will proceed to the previously selected mode,
|
|
||||||
--and clicking escape will clear the previously selected mode
|
|
||||||
local selectModal = modal.new{}
|
|
||||||
local textModal = modal.new{}
|
|
||||||
local numberModal = modal.new{}
|
|
||||||
local editModal = modal.new{ mousepressed = button.mousepressed, keypressed = keypressed, }
|
|
||||||
local moveModal = modal.new{ mousepressed = button.mousepressed, keypressed = keypressed, mousemoved = button.selectIn }
|
|
||||||
local deleteModal = modal.new{ keypressed = keypressed }
|
|
||||||
local currentMode
|
|
||||||
|
|
||||||
button.new{ name = "NEW CITY",
|
|
||||||
group = t,
|
|
||||||
icon = lg.newImage("icons/city-new.png"),
|
|
||||||
x = 615,
|
|
||||||
y = 0,
|
|
||||||
callback = function()
|
|
||||||
city = map.cities.newCity()
|
|
||||||
city:add()
|
|
||||||
return editModal:start()
|
|
||||||
end
|
|
||||||
}
|
|
||||||
|
|
||||||
moveModal.button = button.new{ name = "MOVE CITY",
|
|
||||||
group = t,
|
|
||||||
icon = lg.newImage("icons/city-move.png"),
|
|
||||||
x = 615,
|
|
||||||
y = button.h + 4,
|
|
||||||
callback = function( self )
|
|
||||||
self.lit = true
|
|
||||||
selectModal.mode = moveModal
|
|
||||||
return selectModal:start()
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
editModal.button = button.new{ name = "EDIT CITY",
|
|
||||||
group = t,
|
|
||||||
x = 615,
|
|
||||||
y = (button.h + 4) * 2,
|
|
||||||
callback = function( self )
|
|
||||||
self.lit = true
|
|
||||||
selectModal.mode = editModal
|
|
||||||
return selectModal:start()
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteModal.button = button.new{ name = "DELETE CITY",
|
|
||||||
group = t,
|
|
||||||
icon = lg.newImage("icons/city-delete.png"),
|
|
||||||
x = 615,
|
|
||||||
y = (button.h + 4) * 3,
|
|
||||||
callback = function( self )
|
|
||||||
self.lit = true
|
|
||||||
return deleteModal:start()
|
|
||||||
end,
|
|
||||||
}
|
|
||||||
|
|
||||||
--editButtons
|
|
||||||
local function editText( self )
|
|
||||||
print( "editing: ", self.field, city.name )
|
|
||||||
self.lit = true
|
|
||||||
return textModal:start( self.field )
|
|
||||||
end
|
|
||||||
|
|
||||||
local function editNumber( self )
|
|
||||||
print( "editing: ", self.field, city.name )
|
|
||||||
self.lit = true
|
|
||||||
return numberModal:start( self.field )
|
|
||||||
end
|
|
||||||
|
|
||||||
local editButtons = {
|
|
||||||
|
|
||||||
save = button.new{
|
|
||||||
icon = lg.newImage( "icons/check.png" ),
|
|
||||||
callback = function() return editModal:stop() end,},
|
|
||||||
|
|
||||||
|
|
||||||
name = button.new{ callback = editText },
|
|
||||||
country = button.new{ callback = editText },
|
|
||||||
x = button.new{ callback = editNumber },
|
|
||||||
y = button.new{ callback = editNumber },
|
|
||||||
capital = button.new{ callback = function( self )
|
|
||||||
if city then
|
|
||||||
self.name = tostring( not( city.capital ) )
|
|
||||||
return city:toggleCapital()
|
|
||||||
end
|
|
||||||
end },
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
local i = 0
|
|
||||||
for _, key in ipairs{ "save", "name", "country", "x", "y", "capital" } do
|
|
||||||
local b = assert( editButtons[ key ] )
|
|
||||||
b.align = "right"
|
|
||||||
b.field = key
|
|
||||||
b.name = tostring( key ) --bools must be cast to string before getting passed to printf
|
|
||||||
b.group = editModal
|
|
||||||
b.x = lg.getWidth() / 2 - button.w / 2
|
|
||||||
b.y = (button.h + 4) * i
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function editModal:start()
|
|
||||||
modal.start( self )
|
|
||||||
button.displayGroup( self, false, true )
|
|
||||||
for k, b in pairs( editButtons ) do
|
|
||||||
b.name = tostring( city[k] or b.name )
|
|
||||||
end
|
|
||||||
if city.capital == false then editButtons.capital.name = "false" end
|
|
||||||
end
|
|
||||||
|
|
||||||
function editModal:stop()
|
|
||||||
return modal.stop( self )
|
|
||||||
end
|
|
||||||
|
|
||||||
function editModal.draw()
|
|
||||||
lg.setColor( 1, 1, 1, 0.5 )
|
|
||||||
return button:draw()
|
|
||||||
end
|
|
||||||
|
|
||||||
function moveModal.update( dt )
|
|
||||||
local x, y = love.mouse.getPosition()
|
|
||||||
if y > t.menuHeight and love.mouse.isDown( 1 ) then
|
|
||||||
local wx, wy = camera.GetWorldCoordinate( x, y )
|
|
||||||
city:moveTo( wx, wy )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function moveModal.mousemoved( x, y, dx, dy, istouch )
|
|
||||||
return button.selectIn( x, y )
|
|
||||||
end
|
|
||||||
|
|
||||||
function moveModal.mousepressed( x, y, mouseButton, istouch, presses )
|
|
||||||
if y < t.menuHeight then
|
|
||||||
moveModal:stop()
|
|
||||||
return button.mousepressed( x, y, mouseButton, istouch, presses )
|
|
||||||
end
|
|
||||||
if map.selected and mouseButton == 2 then
|
|
||||||
city = map.selected
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function deleteModal.mousepressed( x, y, mouseButton, istouch, presses )
|
|
||||||
if map.selected then
|
|
||||||
map.selected:delete()
|
|
||||||
end
|
|
||||||
if y < t.menuHeight then
|
|
||||||
deleteModal.button.lit = false
|
|
||||||
deleteModal:stop()
|
|
||||||
return button.mousepressed( x, y, mouseButton, istouch, presses )
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function numberModal:start( field )
|
|
||||||
self.field = field
|
|
||||||
return modal.start( self )
|
|
||||||
end
|
|
||||||
|
|
||||||
function numberModal.keypressed( key, code, isrepeat )
|
|
||||||
if code == "backspace" then
|
|
||||||
local text = tostring( city[numberModal.field] )
|
|
||||||
-- get the byte offset to the last UTF-8 character in the string.
|
|
||||||
local byteoffset = utf8.offset(text, -1)
|
|
||||||
print( "textmodal: backspace", byteoffset )
|
|
||||||
|
|
||||||
if byteoffset then
|
|
||||||
-- remove the last UTF-8 character.
|
|
||||||
-- string.sub operates on bytes rather than UTF-8 characters, so we couldn't do string.sub(text, 1, -2).
|
|
||||||
local newstr = text:sub( 1, byteoffset - 1)
|
|
||||||
if newstr == "" then newstr = 0 end
|
|
||||||
city[numberModal.field] = tonumber( newstr ) or city[numberModal.field]
|
|
||||||
editButtons[numberModal.field].name = city[numberModal.field]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if code == "escape" or code == "return" then
|
|
||||||
return numberModal:stop()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function numberModal.textinput( char )
|
|
||||||
local str = tostring( city[ numberModal.field ] )
|
|
||||||
local plus = str..char
|
|
||||||
print( "text input: ", char )
|
|
||||||
if tonumber( plus ) then
|
|
||||||
city[ numberModal.field ] = plus
|
|
||||||
editButtons[ numberModal.field ].name = plus
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function textModal.textinput( char )
|
|
||||||
print( "text input: ", char )
|
|
||||||
city[textModal.field] = city[textModal.field] .. char
|
|
||||||
editButtons[textModal.field].name = city[textModal.field]
|
|
||||||
end
|
|
||||||
|
|
||||||
function textModal.mousepressed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function textModal:stop()
|
|
||||||
self.field = nil
|
|
||||||
return modal.stop( self )
|
|
||||||
end
|
|
||||||
|
|
||||||
function textModal:start( field )
|
|
||||||
self.field = field
|
|
||||||
return modal.start( self )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function textModal.keypressed( key, code, isRepeat )
|
|
||||||
if code == "backspace" then
|
|
||||||
local text = city[textModal.field]
|
|
||||||
-- get the byte offset to the last UTF-8 character in the string.
|
|
||||||
local byteoffset = utf8.offset(text, -1)
|
|
||||||
print( "textmodal: backspace", byteoffset )
|
|
||||||
|
|
||||||
if byteoffset then
|
|
||||||
-- remove the last UTF-8 character.
|
|
||||||
-- string.sub operates on bytes rather than UTF-8 characters, so we couldn't do string.sub(text, 1, -2).
|
|
||||||
city[textModal.field] = text:sub( 1, byteoffset - 1)
|
|
||||||
editButtons[textModal.field].name = city[textModal.field]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if code == "escape" or code == "return" then
|
|
||||||
return textModal:stop()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function selectModal.keypressed( key, code, isRepeat )
|
|
||||||
if code == 'escape' then return selectModal:stop() end
|
|
||||||
if modal.previous then return modal.previous.keypressed( key, code, isRepeat ) end
|
|
||||||
end
|
|
||||||
|
|
||||||
function selectModal:stop()
|
|
||||||
local mode = selectModal.mode
|
|
||||||
if mode then
|
|
||||||
mode.button.lit = false
|
|
||||||
end
|
|
||||||
return modal.stop( self )
|
|
||||||
end
|
|
||||||
|
|
||||||
function selectModal.mousepressed( x, y, mouseButton, istouch, presses )
|
|
||||||
if y < t.menuHeight then
|
|
||||||
selectModal:stop()
|
|
||||||
return button.mousepressed( x, y, mouseButton, istouch, presses )
|
|
||||||
end
|
|
||||||
|
|
||||||
if map.selected then
|
|
||||||
city = map.selected
|
|
||||||
return selectModal.mode:start()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.setMenuHeight( h )
|
|
||||||
t.menuHeight = h
|
|
||||||
end
|
|
||||||
|
|
||||||
return t
|
|
|
@ -1,3 +0,0 @@
|
||||||
local t = {}
|
|
||||||
|
|
||||||
return t
|
|
|
@ -1,186 +0,0 @@
|
||||||
|
|
||||||
local love = assert( love )
|
|
||||||
local button = require 'ui.button'
|
|
||||||
local modal = require 'ui.modal'
|
|
||||||
local camera = require 'ui.camera'
|
|
||||||
local map = require 'map.map'
|
|
||||||
local t = { menuHeight = 200 }
|
|
||||||
|
|
||||||
local loadImg = love.graphics.newImage
|
|
||||||
local layers = {
|
|
||||||
{ name = "AF", layer = "africa" , menu = require 'ui.menu.territory' },
|
|
||||||
{ name = "EU", layer = "europe" , menu = require 'ui.menu.territory' },
|
|
||||||
{ name = "NA", layer = "northamerica" , menu = require 'ui.menu.territory' },
|
|
||||||
{ name = "SA", layer = "southamerica" , menu = require 'ui.menu.territory' },
|
|
||||||
{ name = "AS", layer = "southasia" , menu = require 'ui.menu.territory' },
|
|
||||||
{ name = "RU", layer = "russia" , menu = require 'ui.menu.territory' },
|
|
||||||
{ name = "PATH", layer = "travelnodes" , menu = require 'ui.menu.travelnodes', icon = loadImg( "icons/layer-travelnodes.png" )},
|
|
||||||
{ name = "AI", layer = "ainodes" , menu = require 'ui.menu.ainodes', icon = loadImg( "icons/layer-ainodes.png" )},
|
|
||||||
{ name = "CITY", layer = "cities" , menu = require 'ui.menu.cities', icon = loadImg( "icons/layer-cities.png" )},
|
|
||||||
{ name = "COAST", layer = "coastlines" , menu = require 'ui.menu.lines', icon = loadImg( "icons/layer-coastlines.png" )},
|
|
||||||
{ name = "LOW", layer = "coastlinesLow", menu = require 'ui.menu.lines', icon = loadImg( "icons/layer-coastlines-low.png" )},
|
|
||||||
{ name = "INT", layer = "international", menu = require 'ui.menu.lines', icon = loadImg( "icons/layer-international.png" )},
|
|
||||||
{ name = "SAIL", layer = "sailable" , menu = require 'ui.menu.territory', icon = loadImg( "icons/layer-sailable.png" )},
|
|
||||||
}
|
|
||||||
|
|
||||||
button.new{
|
|
||||||
name = "LOAD", x = 250, y = 0,
|
|
||||||
group = t,
|
|
||||||
callback = require( 'ui.loadmodal' ).start,
|
|
||||||
icon = love.graphics.newImage( "icons/load.png" )}
|
|
||||||
button.new{
|
|
||||||
name = "SAVE", x = 250, y = 28,
|
|
||||||
group = t,
|
|
||||||
callback = require( 'ui.savemodal' ).start,
|
|
||||||
icon = love.graphics.newImage( "icons/save.png" )}
|
|
||||||
button.new{
|
|
||||||
name = "UNDO", x = 250, y = 2 * 28,
|
|
||||||
group = t,
|
|
||||||
callback = map.undo,
|
|
||||||
icon = love.graphics.newImage( "icons/undo.bmp" ) }
|
|
||||||
|
|
||||||
|
|
||||||
local editButtons = {}
|
|
||||||
local layerButtons = {}
|
|
||||||
local showButtons = {}
|
|
||||||
local visibilityIcon = love.graphics.newImage( "icons/eye.bmp" )
|
|
||||||
local function updateVisibilityIcons()
|
|
||||||
for i = 1, #showButtons do
|
|
||||||
showButtons[i].icon = map.layers[ showButtons[i].layer ].visible and visibilityIcon
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function toggleVisibleLayer( self )
|
|
||||||
if not (self and self.layer) then return end
|
|
||||||
local ml = map.layers[ self.layer ]
|
|
||||||
ml.visible = not( ml.visible )
|
|
||||||
return updateVisibilityIcons()
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local activeLayerButton
|
|
||||||
local function back( self )
|
|
||||||
print( "back button clicked" )
|
|
||||||
if activeLayerButton then
|
|
||||||
activeLayerButton.lit = false
|
|
||||||
end
|
|
||||||
activeLayerButton = nil
|
|
||||||
map.setEditLayer()
|
|
||||||
modal.exitAll()
|
|
||||||
button.displayGroup( t, false, true )
|
|
||||||
for i, b in ipairs( editButtons ) do
|
|
||||||
b.visible = true
|
|
||||||
end
|
|
||||||
for i, b in ipairs( showButtons ) do
|
|
||||||
b.visible = true
|
|
||||||
end
|
|
||||||
self.visible = false
|
|
||||||
return updateVisibilityIcons()
|
|
||||||
end
|
|
||||||
|
|
||||||
local backButton = button.new{
|
|
||||||
name = "UP",
|
|
||||||
visible = false,
|
|
||||||
y = 5 * 28,
|
|
||||||
x = 250,
|
|
||||||
group = false,
|
|
||||||
icon = love.graphics.newImage( "icons/up.bmp" ),
|
|
||||||
callback = back,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function editLayer( self )
|
|
||||||
back( backButton )
|
|
||||||
self.lit = true
|
|
||||||
map.setEditLayer( self.layer )
|
|
||||||
activeLayerButton = self
|
|
||||||
if self.menu then
|
|
||||||
button.displayGroup( self.menu )
|
|
||||||
end
|
|
||||||
backButton.visible = true
|
|
||||||
return updateVisibilityIcons()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function copy( i, target )
|
|
||||||
for k, v in pairs( layers[i] ) do
|
|
||||||
target[k] = target[k] or v
|
|
||||||
end
|
|
||||||
return target
|
|
||||||
end
|
|
||||||
|
|
||||||
local x = 250
|
|
||||||
for i = 1, #layers do
|
|
||||||
|
|
||||||
layers[i].menu.menuHeight = t.menuHeight
|
|
||||||
|
|
||||||
editButtons[i] = button.new( copy( i, {
|
|
||||||
y = 3 * 28,
|
|
||||||
x = x + (button.h + 4) * ( i - 1 ),
|
|
||||||
w = 24,
|
|
||||||
callback = editLayer,
|
|
||||||
group = editButtons,
|
|
||||||
tooltip = "edit "..layers[i].layer
|
|
||||||
}))
|
|
||||||
layerButtons[ 2 * i - 2 ] = editButtons[i]
|
|
||||||
|
|
||||||
showButtons[i] = button.new( copy( i, {
|
|
||||||
y = 4 * 28,
|
|
||||||
x = x + (button.h + 4) * ( i - 1 ),
|
|
||||||
w = 24,
|
|
||||||
name = "",
|
|
||||||
callback = toggleVisibleLayer,
|
|
||||||
icon = visibilityIcon,
|
|
||||||
group = showButtons,
|
|
||||||
tooltip = "show "..layers[i].layer
|
|
||||||
}))
|
|
||||||
|
|
||||||
layerButtons[ 2 * i - 1 ] = showButtons[i]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
function t.draw()
|
|
||||||
--Status bar.
|
|
||||||
love.graphics.setScissor( 0, 0, 250, t.menuHeight )
|
|
||||||
local x, y = love.mouse.getPosition()
|
|
||||||
local wx, wy = camera.GetWorldCoordinate( x, y )
|
|
||||||
local bx, by = camera.GetBitmapCoordinate( x, y )
|
|
||||||
local h = love.graphics.getHeight() - 60
|
|
||||||
love.graphics.setColor( 0, 0, 0, 1 )
|
|
||||||
love.graphics.rectangle( "fill", 0, 0, 250, love.graphics.getHeight() )
|
|
||||||
love.graphics.setColor( 1, 1, 1, 1 )
|
|
||||||
love.graphics.print(([[
|
|
||||||
SCREEN %-12d %-12d
|
|
||||||
WORLD %-12.2f%-12.2f
|
|
||||||
BITMAP %-12d %-12d
|
|
||||||
%s
|
|
||||||
%s]]):format(
|
|
||||||
x, y,
|
|
||||||
wx, wy,
|
|
||||||
bx, by,
|
|
||||||
button.selected and button.selected.tooltip or "",
|
|
||||||
map.editLayer and map.editLayer.filename or ""), 0, 0)
|
|
||||||
|
|
||||||
if map.selected then love.graphics.print( map.selected:formatDisplayInfo(), 0, 80 ) end
|
|
||||||
if map.selectionLocked then end
|
|
||||||
|
|
||||||
love.graphics.setScissor( 250, 0, love.graphics.getWidth() - 250, t.menuHeight)
|
|
||||||
love.graphics.rectangle( "line", 0, 0 , 250, t.menuHeight )
|
|
||||||
love.graphics.rectangle( "line", 250, 0, love.graphics.getWidth() - 250, t.menuHeight )
|
|
||||||
love.graphics.rectangle( "line", 250, 0, button.w, t.menuHeight )
|
|
||||||
|
|
||||||
|
|
||||||
love.graphics.setColor( 1, 1, 1, 0.8 )
|
|
||||||
button:draw()
|
|
||||||
love.graphics.setColor( 1, 0, 0, 0.4 )
|
|
||||||
end
|
|
||||||
|
|
||||||
do --button visibility
|
|
||||||
button.displayGroup( t, false, true )
|
|
||||||
button.displayGroup( editButtons, false, false )
|
|
||||||
button.displayGroup( layerButtons, false, false )
|
|
||||||
button.displayGroup( showButtons, false, false )
|
|
||||||
end
|
|
||||||
|
|
||||||
return t
|
|
|
@ -1,3 +0,0 @@
|
||||||
local t = {}
|
|
||||||
|
|
||||||
return t
|
|
|
@ -1,3 +0,0 @@
|
||||||
local t = {}
|
|
||||||
|
|
||||||
return t
|
|
|
@ -1,61 +0,0 @@
|
||||||
--Modal for setting save options.
|
|
||||||
local love = assert( love )
|
|
||||||
local modal = require( "ui.modal" )
|
|
||||||
local button = require( "ui.button" )
|
|
||||||
local map = require( "map.map" )
|
|
||||||
local save = require( "ui.saveprogress" )
|
|
||||||
local t = { w = love.graphics.getWidth(), h = 200 }
|
|
||||||
|
|
||||||
local saveLocation = false
|
|
||||||
local floppy = love.graphics.newImage( "icons/save.png" )
|
|
||||||
floppy:setFilter( "nearest", "nearest" )
|
|
||||||
local saveButton = button.new{
|
|
||||||
group = t,
|
|
||||||
name = "save",
|
|
||||||
align = "left",
|
|
||||||
callback = function() save:start() end,
|
|
||||||
visible = false,
|
|
||||||
icon = floppy,
|
|
||||||
w = 400 - button.x,
|
|
||||||
h = 64,
|
|
||||||
y = 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
local xIcon = love.graphics.newImage( "icons/x.png" )
|
|
||||||
xIcon:setFilter( "nearest", "nearest" )
|
|
||||||
local cancelButton = button.new{
|
|
||||||
group = t,
|
|
||||||
name = " cancel",
|
|
||||||
align = "left",
|
|
||||||
visible = false,
|
|
||||||
icon = xIcon,
|
|
||||||
callback = function() return t:stop() end,
|
|
||||||
y = 68,
|
|
||||||
w = 400 - button.x,
|
|
||||||
h = 64,
|
|
||||||
}
|
|
||||||
|
|
||||||
function t.start()
|
|
||||||
modal.start( t )
|
|
||||||
saveLocation = saveLocation or map.path
|
|
||||||
button.selected = saveButton
|
|
||||||
saveButton.name = " save to "..saveLocation
|
|
||||||
button.displayGroup( t, false, true )
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.draw()
|
|
||||||
love.graphics.clear( 0,0,0,1 )
|
|
||||||
love.graphics.setColor( 1, 1, 1, 0.9 )
|
|
||||||
button:draw()
|
|
||||||
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.directorydropped( path )
|
|
||||||
saveLocation = path
|
|
||||||
map.path = path
|
|
||||||
saveButton.name = " save to "..map.path
|
|
||||||
return love.filesystem.mount( path, "" )
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
return modal.new( t )
|
|
|
@ -1,36 +0,0 @@
|
||||||
--What to display when saving is in progress.
|
|
||||||
local love = assert( love )
|
|
||||||
local modal = require( "ui.modal" )
|
|
||||||
local button = require( "ui.button" )
|
|
||||||
local map = require( "map.map" )
|
|
||||||
local timer = love.timer
|
|
||||||
local time = 0
|
|
||||||
|
|
||||||
local t = { w = 400, h = 200 }
|
|
||||||
local progressMessage = ""
|
|
||||||
|
|
||||||
function t.start()
|
|
||||||
time = timer.getTime()
|
|
||||||
progressMessage = ""
|
|
||||||
return modal.start( t )
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.update( dt )
|
|
||||||
local msg = map.save()
|
|
||||||
if not msg then return t:stop() end
|
|
||||||
progressMessage = msg
|
|
||||||
end
|
|
||||||
|
|
||||||
function t.draw()
|
|
||||||
love.graphics.push( "all" )
|
|
||||||
love.graphics.setCanvas()
|
|
||||||
love.graphics.setShader()
|
|
||||||
love.graphics.setScissor( 0, 0, t.w, t.h )
|
|
||||||
love.graphics.clear()
|
|
||||||
love.graphics.setColor( 1, 1, 1, 1 )
|
|
||||||
love.graphics.print( timer.getTime() - time )
|
|
||||||
love.graphics.printf( progressMessage, 0, love.graphics.getFont():getHeight(), t.w, "left")
|
|
||||||
love.graphics.pop( "all" )
|
|
||||||
end
|
|
||||||
|
|
||||||
return modal.new( t )
|
|