From db5ce8b4f789c9c7cfc8a633fb81378ff8907fdc Mon Sep 17 00:00:00 2001 From: wan-may Date: Sat, 20 Jul 2024 14:43:09 -0300 Subject: [PATCH] sketch out more menus, implement editing and deleting cities, quantize 8-bit sailables back to 4 --- icons/check.png | Bin 3126 -> 3126 bytes icons/city-delete.png | Bin 0 -> 391 bytes icons/city-move.png | Bin 0 -> 3126 bytes icons/city-new.png | Bin 0 -> 357 bytes icons/node-attack.png | Bin 0 -> 3126 bytes icons/node-place.png | Bin 0 -> 3126 bytes main.lua | 16 +++--- map/ai.lua | 12 +++++ map/bmp.lua | 77 ++++++++++++++++++---------- map/cities.lua | 58 ++++++++++++++------- map/map.lua | 14 ++++-- ui/button.lua | 3 +- ui/loadmodal.lua | 4 +- ui/menu/ainodes.lua | 45 +++++++++++++++++ ui/menu/cities.lua | 109 +++++++++++++++++++++++++++++++--------- ui/menu/lines.lua | 3 ++ ui/menu/mainmenu.lua | 37 ++++++++------ ui/menu/territory.lua | 3 ++ ui/menu/travelnodes.lua | 3 ++ ui/modal.lua | 9 +++- 20 files changed, 293 insertions(+), 100 deletions(-) create mode 100644 icons/city-delete.png create mode 100644 icons/city-move.png create mode 100644 icons/city-new.png create mode 100644 icons/node-attack.png create mode 100644 icons/node-place.png diff --git a/icons/check.png b/icons/check.png index eabbeadd10fc880281b1cfe7faf5a05045be729a..30dad7e030420712965a85fd629ea86b28777a70 100644 GIT binary patch literal 3126 zcmeH{!3}^Q3`M_p2k_|82^_$?`;UW(5dA5n5hx@YU&6)0D{YC}zK%Fq0W1LKum(&% z5aNvcDWz!}Sq|8bN%(D3#PS#Ulb83DzLXn9Zqg#*cP{azm{@M;7jo_fhiotJVL#{1 zf+gZ0hwA-8?lDQB(AsbKkQ7>1(c1rwE{Q6XdDXzPsPX7rS@dl+^h{CwcX~pHLVj{N E0Lw@t{{R30 literal 3126 zcmeH^u?c`M6a}BX16W!*fdkmP|2PqhNC>BxXG9AR(p<=WKAGmR)ygPokkoIdQ5P?^ z`;_qW0n#6-Lbg+tX38u5Y^X=vB72L{15+qHFon_sQz$(!h0+64C_ONR(gRZ{JurpR z15+qHFon_sQz$(!h0+64C_OM0<#kR*SLsWKBd}k@z9zo9-w)wzM3sIz0>RZv!E&TD H_45!5l-P@j diff --git a/icons/city-delete.png b/icons/city-delete.png new file mode 100644 index 0000000000000000000000000000000000000000..1e8f5372ad37d1f89a4f24e97727c9121b334f57 GIT binary patch literal 391 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 NSRUA)X;+~2{{{_WF9-kt literal 0 HcmV?d00001 diff --git a/icons/city-new.png b/icons/city-new.png new file mode 100644 index 0000000000000000000000000000000000000000..8dafd621db042ae8b0a4db88fda0313161cd1fa4 GIT binary patch literal 357 zcmV-r0h<1aP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!~g&e!~vBn4jTXf0QyNpK~z{r#h1Yj z!ypVpVgLWn+gvr31_{K5cJHzsz<#71((3z5`6I9MQ>NB>e$!YIu<{iNS)eUdB_x5U zOp%}kW+kiyC6EngB+>%x$+GKH0&KaF**Cq2q7ZKzv(^|+Ks|ZOC?^!yRc=IiuE6ec z!^`ai?kYFh{GXRFDxbWkLuQ%R1nxe0i}f7>PnR3$Jp@)pyaIZ!NAR?S^>sI4_HlT^ z%HHm0=mqbj&F=)jT@qfdD*$#&M0ueA*d;N_N&PQaxhH1v!wDGbBr^L_0)Umsu1^bq zY$zj334mERJ1JTKL?vuFkOV+mrj!F&09dJ7UP>t++r@V)M1p7=00000NkvXXu0mjf Dk9&#( literal 0 HcmV?d00001 diff --git a/icons/node-attack.png b/icons/node-attack.png new file mode 100644 index 0000000000000000000000000000000000000000..eb42d6f4668781d93e7814af76938560c527b591 GIT binary patch literal 3126 zcmeH}F$#b%5CqrW2UuGA0v}-S|DU4}<&aDxY|bJ$Am{|<)D*hy>!`viX^}MF$Drw2 zxYx;%$u%x>LO&x?-_key+x?PwQBu~vXODQ^`SZL94APh|NMphvjR}J^CJfS;Fi2y< zAdLxwG$stvm@r6V!XS+agES@#(wJ1i>aqz{v$||Te2I?ox7MUdVoJs%1Tm!pPJT&0 A@c;k- literal 0 HcmV?d00001 diff --git a/icons/node-place.png b/icons/node-place.png new file mode 100644 index 0000000000000000000000000000000000000000..8b82b26ab99fab2939a9fe63229833745e308eb7 GIT binary patch literal 3126 zcmeIxF$#b%5Jb_j_W+ibo*)OX_x{IG&=gjmfhEQ2K1en2A%yHln{{iFHc4wgla^=b z``h^&qi literal 0 HcmV?d00001 diff --git a/main.lua b/main.lua index 0451efc..dbf65ef 100644 --- a/main.lua +++ b/main.lua @@ -64,12 +64,14 @@ end function love.mousemoved( x, y, dx, dy, istouch ) if not map.loaded then return end --mouse over menu - button.selectIn( x, y ) - - --mouse on map - if map.selectionLocked then return end - if map.editLayer and map.editLayer.selectNearest then - map.selected = map.editLayer:selectNearest( Camera.GetWorldCoordinate( x, y ) ) + if y < mainmenu.menuHeight then + button.selectIn( x, y ) + --mouse on map + else + if map.selectionLocked then return end + if map.editLayer and map.editLayer.selectNearest then + map.selected = map.editLayer:selectNearest( Camera.GetWorldCoordinate( x, y ) ) + end end end @@ -87,5 +89,5 @@ function love.keypressed(key, code, isRepeat) end function love.textinput() - + end diff --git a/map/ai.lua b/map/ai.lua index 43bb5d2..ecedeb8 100644 --- a/map/ai.lua +++ b/map/ai.lua @@ -17,6 +17,14 @@ function aiNode:formatDisplayInfo() ]]):format( self.idx, self.x, self.y, tostring(self.attacking) ) end +function aiNode:add() + +end + +function aiNode:moveTo( x, y ) + +end + function t.load( filename ) local img, imgd = bmp.load( filename ) local nodes = { @@ -82,4 +90,8 @@ function t.save( nodes ) return bmp.ai( nodes.all ) end +function t.newNode( isAttacking ) + +end + return t \ No newline at end of file diff --git a/map/bmp.lua b/map/bmp.lua index 103c827..0db1ba7 100644 --- a/map/bmp.lua +++ b/map/bmp.lua @@ -244,40 +244,63 @@ function formats.sailable:test() print "sailable OK" end -function formats.sailable:encode( data ) - local w, h = self.w, self.h - local bytes = { self.header:sub( 1, -2 ) } - local i = 2 - --y coordinates are written top to bottom - for y = h - 1, 0, -1 do - for x = 0, w - 1 do - bytes[i] = assert( self.palette[ math.floor( data:getPixel(x, y) * 255 )] ) - i = i + 1 - end +do --sailable + + local reversePalette = {} + for eight, four in pairs( formats.sailable.palette ) do + reversePalette[ four ] = eight + 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 + + error( "Could not quantize sailable.bmp!" ) 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 ) + function formats.sailable:encode( data ) + local w, h = self.w, self.h + local bytes = { self.header:sub( 1, -2 ) } + local i = 2 + + --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 - return table.concat( nybbles ) + 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 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 ) local w, h = self.w, self.h local bytes = { self.header:sub( 1, -3 ) } diff --git a/map/cities.lua b/map/cities.lua index e8d2eba..fbfda65 100644 --- a/map/cities.lua +++ b/map/cities.lua @@ -13,6 +13,7 @@ local caps = {} t.selected = nil t.selectionLocked = false +local invisible = 10000 --sentinel value outside the draw rectangle function t.lockSelection() t.selectionLocked = true @@ -54,14 +55,21 @@ function city:formatDisplayInfo() 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 @@ -81,22 +89,32 @@ function city:moveTo(x, y) end function city:toggleCapital() - self.capital = not( self.capital ) + 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, + name = "", + country = "", + x = 0, + y = 0, + pop = 0, + capital = false, }, citymt ) end function t.load( filename ) - + print( "=== LOADING CITIES. ===" ) cities = { visible = true, active = false, filename = filename } @@ -112,11 +130,11 @@ function t.load( filename ) if capital then --check against empty or malformed line x, y, pop, capital = tonumber( x ), tonumber( y ), tonumber( pop ), ( tonumber( capital ) > 0) local city = setmetatable({ - name = line:sub( 1, 39 ):gsub("%s+$",""), - country = line:sub( 42, 82 ):gsub("%s+$",""), - x = x, y = y, pop = pop, capital = capital, - n = n, points = idxPts, caps = capital and idxCaps - }, citymt ) + name = line:sub( 1, 39 ):gsub("%s+$",""), + country = line:sub( 42, 82 ):gsub("%s+$",""), + x = x, y = y, pop = pop, capital = capital, + n = n, points = idxPts, caps = capital and idxCaps + }, citymt ) cities[n] = city n = n + 1 @@ -142,9 +160,13 @@ end function t.save( cities ) local str = {} - for n, city in ipairs( cities ) do - str[n] = ("%-41s%-41s%-14f%-14f%-19d %d"):format( - city.name, city.country, city.x, city.y, city.pop, city.capital and 1 or 0 ) + local i = 1 + for _, city in ipairs( cities ) do + if not city.deleted then + 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 return assert(table.concat( str, "\n" )) end diff --git a/map/map.lua b/map/map.lua index 4dd42ca..81c8d6c 100644 --- a/map/map.lua +++ b/map/map.lua @@ -191,18 +191,24 @@ local function write( filename, string ) end function map.save() - --should be cross platform-ish + --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 - --getInfo checks if a directory exists assert( mkdir.exists( map.path ), map.path ) local path = map.path..folder if not mkdir.exists( path ) then mkdir.mkdir( path ) end end - --OK back to normal + 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 - write( map.path..tostring( layer.filename ), assert( layer:save() ) ) + files[ map.path..tostring( layer.filename ) ] = assert( layer:save() ) end + for filename, str in pairs( files ) do + write( filename, str ) + end + end function map.hover(x, y) diff --git a/ui/button.lua b/ui/button.lua index 5c2385c..b3f3273 100644 --- a/ui/button.lua +++ b/ui/button.lua @@ -11,6 +11,7 @@ local t = { h = 24, group = false, visible = false, + align = "center", 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 @@ -45,7 +46,7 @@ function t.draw( b ) b.x + (b.icon and b.h or 0), b.y + 0.5 * ( b.h - lg.getFont():getHeight() ), b.w - (b.icon and b.h or 0), - "center" ) + b.align ) if b.icon then local h = b.icon:getHeight() lg.draw( b.icon, diff --git a/ui/loadmodal.lua b/ui/loadmodal.lua index 9f95f7f..be75b8b 100644 --- a/ui/loadmodal.lua +++ b/ui/loadmodal.lua @@ -40,8 +40,8 @@ function t.start() modal.start( t ) loadLocation = loadLocation or map.path button.selected = loadButton - loadButton.name = "save to "..loadLocation - button.displayGroup( t, true ) + loadButton.name = "load from "..loadLocation + button.displayGroup( t, false, true ) end function t.draw() diff --git a/ui/menu/ainodes.lua b/ui/menu/ainodes.lua index e69de29..b3ac15b 100644 --- a/ui/menu/ainodes.lua +++ b/ui/menu/ainodes.lua @@ -0,0 +1,45 @@ +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 \ No newline at end of file diff --git a/ui/menu/cities.lua b/ui/menu/cities.lua index 9e1c02e..d8023cb 100644 --- a/ui/menu/cities.lua +++ b/ui/menu/cities.lua @@ -22,27 +22,29 @@ end --and clicking escape will clear the previously selected mode local selectModal = modal.new{} local textModal = modal.new{} -local numberModal = modal.new{ cursor = 1 } +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{ mousepressed = button.mousepressed, keypressed = keypressed } +local deleteModal = modal.new{ keypressed = keypressed } local currentMode button.new{ name = "NEW CITY", group = t, - icon = lg.newImage("icons/layer-cities.png"), + 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 = 28, + y = button.h + 4, callback = function( self ) self.lit = true selectModal.mode = moveModal @@ -53,7 +55,7 @@ moveModal.button = button.new{ name = "MOVE CITY", editModal.button = button.new{ name = "EDIT CITY", group = t, x = 615, - y = 28 * 2, + y = (button.h + 4) * 2, callback = function( self ) self.lit = true selectModal.mode = editModal @@ -63,48 +65,57 @@ editModal.button = button.new{ name = "EDIT CITY", deleteModal.button = button.new{ name = "DELETE CITY", group = t, + icon = lg.newImage("icons/city-delete.png"), x = 615, - y = 28 * 3, - icon = lg.newImage("icons/x.png"), + y = (button.h + 4) * 3, callback = function( self ) self.lit = true - selectModal.mode = deleteModal - return selectModal:start() + 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 ) - local field = self.field + print( "editing: ", self.field, city.name ) + self.lit = true + return numberModal:start( self.field ) end local editButtons = { save = button.new{ - icon = lg.newImage( "icons/save.png" ), - callback = function() print( "stop editing city" ) return editModal:stop() end,}, + 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() if city then return city:toggleCapital() end end }, + 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, b in pairs( editButtons ) do + for _, key in ipairs{ "save", "name", "country", "x", "y", "capital" } do + local b = assert( editButtons[ key ] ) + b.align = "right" b.field = key - b.name = key + b.name = tostring( key ) --bools must be cast to string before getting passed to printf b.group = editModal - b.x = 0 - b.y = 28 * i + b.x = lg.getWidth() / 2 - button.w / 2 + b.y = (button.h + 4) * i i = i + 1 end end @@ -113,17 +124,34 @@ function editModal:start() modal.start( self ) button.displayGroup( self, false, true ) for k, b in pairs( editButtons ) do - b.name = city[k] or b.name + 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 editModal.resize( w, h ) + local i = 0 + local high = h / 6 + for key, b in pairs( editButtons ) do + b.x = w / 2 - button.w / 2 + b.y = ( high + 4 ) * i + b.h = high + i = i + 1 + end +end + function moveModal.update( dt ) local x, y = love.mouse.getPosition() - if y > 200 and love.mouse.isDown( 1 ) then + if y > t.menuHeight and love.mouse.isDown( 1 ) then local wx, wy = camera.GetWorldCoordinate( x, y ) city:moveTo( wx, wy ) end @@ -134,7 +162,7 @@ function moveModal.mousemoved( x, y, dx, dy, istouch ) end function moveModal.mousepressed( x, y, mouseButton, istouch, presses ) - if y < 200 then + if y < t.menuHeight then moveModal:stop() return button.mousepressed( x, y, mouseButton, istouch, presses ) end @@ -143,19 +171,48 @@ function moveModal.mousepressed( x, y, mouseButton, istouch, presses ) 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 end - if code == 'escape' then return numberModal:stop() end + 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 @@ -196,7 +253,7 @@ function textModal.keypressed( key, code, isRepeat ) editButtons[textModal.field].name = city[textModal.field] end end - if code == "escape" then + if code == "escape" or code == "return" then return textModal:stop() end end @@ -215,7 +272,7 @@ function selectModal:stop() end function selectModal.mousepressed( x, y, mouseButton, istouch, presses ) - if y < 200 then + if y < t.menuHeight then selectModal:stop() return button.mousepressed( x, y, mouseButton, istouch, presses ) end @@ -226,6 +283,8 @@ function selectModal.mousepressed( x, y, mouseButton, istouch, presses ) end end - +function t.setMenuHeight( h ) + t.menuHeight = h +end return t \ No newline at end of file diff --git a/ui/menu/lines.lua b/ui/menu/lines.lua index e69de29..b8617ae 100644 --- a/ui/menu/lines.lua +++ b/ui/menu/lines.lua @@ -0,0 +1,3 @@ +local t = {} + +return t \ No newline at end of file diff --git a/ui/menu/mainmenu.lua b/ui/menu/mainmenu.lua index 7f8aeec..34db08e 100644 --- a/ui/menu/mainmenu.lua +++ b/ui/menu/mainmenu.lua @@ -1,11 +1,10 @@ local love = assert( love ) local button = require 'ui.button' -local savemodal = require 'ui.savemodal' -local loadmodal = require 'ui.loadmodal' -local Camera = require 'ui.camera' +local modal = require 'ui.modal' +local camera = require 'ui.camera' local map = require 'map.map' -local t = {} +local t = { menuHeight = 200 } local loadImg = love.graphics.newImage local layers = { @@ -27,12 +26,12 @@ local layers = { button.new{ name = "LOAD", x = 250, y = 0, group = t, - callback = loadmodal.start, + callback = require( 'ui.loadmodal' ).start, icon = love.graphics.newImage( "icons/load.png" )} button.new{ name = "SAVE", x = 250, y = 28, group = t, - callback = savemodal.start, + callback = require( 'ui.savemodal' ).start, icon = love.graphics.newImage( "icons/save.png" )} button.new{ name = "UNDO", x = 250, y = 2 * 28, @@ -61,9 +60,13 @@ end local activeLayerButton local function back( self ) - activeLayerButton.lit = false + 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 @@ -86,6 +89,7 @@ local backButton = button.new{ } local function editLayer( self ) + back( backButton ) self.lit = true map.setEditLayer( self.layer ) activeLayerButton = self @@ -106,6 +110,8 @@ 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 ), @@ -126,17 +132,20 @@ for i = 1, #layers do 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, 200 ) + 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 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() ) @@ -156,10 +165,10 @@ function t.draw() 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, 200 ) - love.graphics.rectangle( "line", 0, 0 , 250, 200 ) - love.graphics.rectangle( "line", 250, 0, love.graphics.getWidth() - 250, 200 ) - love.graphics.rectangle( "line", 250, 0, button.w, 200 ) + 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 ) diff --git a/ui/menu/territory.lua b/ui/menu/territory.lua index e69de29..b8617ae 100644 --- a/ui/menu/territory.lua +++ b/ui/menu/territory.lua @@ -0,0 +1,3 @@ +local t = {} + +return t \ No newline at end of file diff --git a/ui/menu/travelnodes.lua b/ui/menu/travelnodes.lua index e69de29..b8617ae 100644 --- a/ui/menu/travelnodes.lua +++ b/ui/menu/travelnodes.lua @@ -0,0 +1,3 @@ +local t = {} + +return t \ No newline at end of file diff --git a/ui/modal.lua b/ui/modal.lua index 57c7219..18ea5b9 100644 --- a/ui/modal.lua +++ b/ui/modal.lua @@ -34,7 +34,6 @@ function t.start( self ) end function t.stop( self ) - print( "stopping modal:", i ) --restore callbacks for name in pairs( self ) do if love[name] then @@ -56,7 +55,13 @@ function t.stop( self ) i = i - 1 t.previous = t[i - 1] - love.graphics.setScissor(0, 0, love.graphics.getDimensions()) + love.graphics.setScissor(0, 0, love.graphics.getDimensions()) +end + +function t.exitAll() + if i < 1 then return end + i = 1 + return t.stop( love ) end function t.new( modal )