diff --git a/conf.lua b/conf.lua index 826d89e..6fbf427 100644 --- a/conf.lua +++ b/conf.lua @@ -12,7 +12,7 @@ function love.conf(t) 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.width = 1000 -- The window width (number) + t.window.width = 1024 -- The window width (number) t.window.height = 640 -- The window height (number) t.window.borderless = false -- Remove all border visuals from the window (boolean) t.window.resizable = true -- Let the window be user-resizable (boolean) diff --git a/icons/check.png b/icons/check.png new file mode 100644 index 0000000..eabbead Binary files /dev/null and b/icons/check.png differ diff --git a/icons/layer-ainodes.png b/icons/layer-ainodes.png index 8901bea..20f938f 100644 Binary files a/icons/layer-ainodes.png and b/icons/layer-ainodes.png differ diff --git a/icons/layer-cities.png b/icons/layer-cities.png index 0bf6d6f..6c14460 100644 Binary files a/icons/layer-cities.png and b/icons/layer-cities.png differ diff --git a/icons/layer-sailable.png b/icons/layer-sailable.png index d80f52e..ebe10a4 100644 Binary files a/icons/layer-sailable.png and b/icons/layer-sailable.png differ diff --git a/icons/layer-travelnodes.png b/icons/layer-travelnodes.png index 1037d11..67ce08c 100644 Binary files a/icons/layer-travelnodes.png and b/icons/layer-travelnodes.png differ diff --git a/main.lua b/main.lua index 9ced19e..0451efc 100644 --- a/main.lua +++ b/main.lua @@ -58,10 +58,7 @@ end function love.mousepressed( x, y, mouseButton, istouch, presses ) local wx, wy = Camera.GetWorldCoordinate( x, y ) - if button.selected and button.selected:contains( x, y ) then - button.callback( button.selected ) - return button.selected:callback() - end + return button.mousepressed( x, y ) end function love.mousemoved( x, y, dx, dy, istouch ) @@ -89,9 +86,6 @@ function love.keypressed(key, code, isRepeat) end end - - -do - +function love.textinput() + end - diff --git a/map/cities.lua b/map/cities.lua index cb7b569..e8d2eba 100644 --- a/map/cities.lua +++ b/map/cities.lua @@ -53,6 +53,48 @@ function city:formatDisplayInfo() CAPITAL: %s]]):format( self.name, self.country, self.x, self.y, self.pop, tostring(self.capital) ) end +function city:delete() + +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() + self.capital = not( self.capital ) +end + +function t.newCity( tbl ) + return setmetatable({ + name = "", + country = "", + x = 0, + y = 0, + pop = 0, + capital = false, + }, citymt ) +end + function t.load( filename ) print( "=== LOADING CITIES. ===" ) @@ -72,7 +114,8 @@ function t.load( filename ) 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 + x = x, y = y, pop = pop, capital = capital, + n = n, points = idxPts, caps = capital and idxCaps }, citymt ) cities[n] = city n = n + 1 diff --git a/ui/button.lua b/ui/button.lua index 802003c..5c2385c 100644 --- a/ui/button.lua +++ b/ui/button.lua @@ -4,12 +4,13 @@ local t = { name = "", tooltip = "", icon = false, + lit = false, x = 8, y = 250, - w = 176, + w = 13 * 28 - 4, h = 24, - group = "", - visible = true, + group = false, + visible = false, 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 @@ -52,7 +53,7 @@ function t.draw( b ) 0, b.h / h ) end - if t.selected == b then + if b.lit or t.selected == b then b:highlight() end end @@ -92,15 +93,27 @@ function t.selectPrevInGroup() repeat t.selectPrev() until group == t.selected.group end -function t.displayGroup( group, show ) +--show/hide all buttons in a group +--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 repeat b = b.next - b.visible = ( b.group == group ) + local inGroup = (group == b.group) + if solo or inGroup then + b.visible = not(hide) and inGroup + end until b == t t.visible = true 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 ) t.selected = t end diff --git a/ui/loadmodal.lua b/ui/loadmodal.lua index c1a5c54..9f95f7f 100644 --- a/ui/loadmodal.lua +++ b/ui/loadmodal.lua @@ -8,7 +8,7 @@ local loadLocation = false local folder = love.graphics.newImage( "icons/load.png" ) folder:setFilter( "nearest", "nearest" ) local loadButton = button.new{ - group = "loadModal", + group = t, name = "load", callback = function() if not loadLocation then return end @@ -25,7 +25,7 @@ local loadButton = button.new{ local xIcon = love.graphics.newImage( "icons/x.png" ) xIcon:setFilter( "nearest", "nearest" ) local cancelButton = button.new{ - group = "loadModal", + group = t, name = "cancel load", visible = false, icon = xIcon, @@ -41,7 +41,7 @@ function t.start() loadLocation = loadLocation or map.path button.selected = loadButton loadButton.name = "save to "..loadLocation - button.displayGroup( "loadModal", true ) + button.displayGroup( t, true ) end function t.draw() diff --git a/ui/menu.lua b/ui/menu.lua deleted file mode 100644 index e69de29..0000000 diff --git a/ui/menu/cities.lua b/ui/menu/cities.lua index 28b4350..9e1c02e 100644 --- a/ui/menu/cities.lua +++ b/ui/menu/cities.lua @@ -1 +1,231 @@ -local textinput = require 'ui.textinput' \ No newline at end of file + +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{ cursor = 1 } +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 currentMode + +button.new{ name = "NEW CITY", + group = t, + icon = lg.newImage("icons/layer-cities.png"), + x = 615, + y = 0, + callback = function() + city = map.cities.newCity() + return editModal:start() + end +} + +moveModal.button = button.new{ name = "MOVE CITY", + group = t, + x = 615, + y = 28, + 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 = 28 * 2, + callback = function( self ) + self.lit = true + selectModal.mode = editModal + return selectModal:start() + end, +} + +deleteModal.button = button.new{ name = "DELETE CITY", + group = t, + x = 615, + y = 28 * 3, + icon = lg.newImage("icons/x.png"), + callback = function( self ) + self.lit = true + selectModal.mode = deleteModal + return selectModal:start() + end, +} + +--editButtons +local function editText( self ) + print( "editing: ", self.field, city.name ) + return textModal:start( self.field ) +end + +local function editNumber( self ) + local field = self.field +end + +local editButtons = { + + save = button.new{ + icon = lg.newImage( "icons/save.png" ), + callback = function() print( "stop editing city" ) 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 }, +} + +do + local i = 0 + for key, b in pairs( editButtons ) do + b.field = key + b.name = key + b.group = editModal + b.x = 0 + b.y = 28 * 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 = city[k] or b.name + end +end + +function editModal.draw() + return button:draw() +end + +function moveModal.update( dt ) + local x, y = love.mouse.getPosition() + if y > 200 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 < 200 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 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 +end + +function numberModal.textinput( char ) + local str = tostring( city[ numberModal.field ] ) + local plus = str..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" 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 < 200 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 + + + +return t \ No newline at end of file diff --git a/ui/menu/mainmenu.lua b/ui/menu/mainmenu.lua index 54f011a..7f8aeec 100644 --- a/ui/menu/mainmenu.lua +++ b/ui/menu/mainmenu.lua @@ -5,6 +5,7 @@ local savemodal = require 'ui.savemodal' local loadmodal = require 'ui.loadmodal' local Camera = require 'ui.camera' local map = require 'map.map' +local t = {} local loadImg = love.graphics.newImage local layers = { @@ -25,20 +26,23 @@ local layers = { button.new{ name = "LOAD", x = 250, y = 0, - w = 28 * 13, + group = t, callback = loadmodal.start, icon = love.graphics.newImage( "icons/load.png" )} button.new{ name = "SAVE", x = 250, y = 28, - w = 28 * 13, + group = t, callback = savemodal.start, icon = love.graphics.newImage( "icons/save.png" )} button.new{ name = "UNDO", x = 250, y = 2 * 28, - w = 28 * 13, + 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() @@ -57,9 +61,17 @@ end local activeLayerButton local function back( self ) - self.visible = false - map.setEditLayer() + activeLayerButton.lit = false activeLayerButton = nil + map.setEditLayer() + 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 @@ -68,13 +80,18 @@ local backButton = button.new{ visible = false, y = 5 * 28, x = 250, + group = false, icon = love.graphics.newImage( "icons/up.bmp" ), callback = back, } local function editLayer( self ) + self.lit = true map.setEditLayer( self.layer ) activeLayerButton = self + if self.menu then + button.displayGroup( self.menu ) + end backButton.visible = true return updateVisibilityIcons() end @@ -86,11 +103,7 @@ local function copy( i, target ) return target end - -local layerButtons = {} local x = 250 -local soloButtons = {} -local editButtons = {} for i = 1, #layers do editButtons[i] = button.new( copy( i, { @@ -98,7 +111,7 @@ for i = 1, #layers do x = x + (button.h + 4) * ( i - 1 ), w = 24, callback = editLayer, - group = "edit", + group = editButtons, tooltip = "edit "..layers[i].layer })) layerButtons[ 2 * i - 2 ] = editButtons[i] @@ -110,14 +123,13 @@ for i = 1, #layers do name = "", callback = toggleVisibleLayer, icon = visibilityIcon, - group = "show", + group = showButtons, tooltip = "show "..layers[i].layer })) layerButtons[ 2 * i - 1 ] = showButtons[i] end -local t = {} function t.draw() --Status bar. @@ -144,16 +156,22 @@ function t.draw() if map.selected then love.graphics.print( map.selected:formatDisplayInfo(), 0, 80 ) end if map.selectionLocked then end - love.graphics.setScissor( 0, 0, love.graphics.getWidth(), 200 ) + 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.setColor( 1, 1, 1, 0.8 ) button:draw() - love.graphics.setColor( 1, 0, 0, 0.4 ) - if activeLayerButton then activeLayerButton:highlight() end +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 \ No newline at end of file diff --git a/ui/modal.lua b/ui/modal.lua index f6e457e..57c7219 100644 --- a/ui/modal.lua +++ b/ui/modal.lua @@ -5,14 +5,17 @@ t.__index = t local i = 0 function t.start( self ) + print( "starting modal:", i + 1) love.graphics.setScissor( self.x or 0, self.y or 0, self.w or love.graphics.getWidth(), - self.h or love.graphics.getDimensions()) + self.h or love.graphics.getHeight()) i = i + 1 - t[i] = t[i] or {} + t[i] = { modal = self } + t.previous = t[i] + t.current = self --store callbacks for name in pairs( self ) do @@ -24,17 +27,18 @@ function t.start( self ) --store menus local b = button.next - repeat + repeat t[i][b] = b.visible b = b.next until b == button end function t.stop( self ) + print( "stopping modal:", i ) --restore callbacks for name in pairs( self ) do if love[name] then - love[name] = t[i][name] + love[name] = t[i][name] or love[name] end end @@ -43,12 +47,16 @@ function t.stop( self ) button.deselect() repeat b = b.next - b.visible = t[i][b] or false --accessing a button's nil field is an error, so make sure that b.visible is a boolean + b.visible = t[i][b] or false until b == button + + t.current = t[i].modal t[i] = nil i = i - 1 + t.previous = t[i - 1] + love.graphics.setScissor(0, 0, love.graphics.getDimensions()) end function t.new( modal ) diff --git a/ui/savemodal.lua b/ui/savemodal.lua index cce7052..780676d 100644 --- a/ui/savemodal.lua +++ b/ui/savemodal.lua @@ -8,7 +8,7 @@ local saveLocation = false local floppy = love.graphics.newImage( "icons/save.png" ) floppy:setFilter( "nearest", "nearest" ) local saveButton = button.new{ - group = "saveModal", + group = t, name = "save", callback = function() map.save(); return t:stop() end, visible = false, @@ -22,7 +22,7 @@ local saveButton = button.new{ local xIcon = love.graphics.newImage( "icons/x.png" ) xIcon:setFilter( "nearest", "nearest" ) local cancelButton = button.new{ - group = "saveModal", + group = t, name = "cancel", visible = false, icon = xIcon, @@ -38,7 +38,7 @@ function t.start() saveLocation = saveLocation or map.path button.selected = saveButton saveButton.name = "save to "..saveLocation - button.displayGroup( "saveModal", true ) + button.displayGroup( t, false, true ) end function t.draw() diff --git a/ui/textinput.lua b/ui/textinput.lua index 543f515..a17a7c2 100644 --- a/ui/textinput.lua +++ b/ui/textinput.lua @@ -54,7 +54,7 @@ function t.keypressed(key, code, isRepeat) 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). - text = string.sub(text, 1, byteoffset - 1) + t.currentModal.currentField = text:sub( 1, byteoffset - 1) end end if code == "escape" then