--Load and save the fixed width plaintext data used by DEFCON. local t = {} local io = io local math = math local table = table local tonumber = tonumber local lfs = love.filesystem local lg = love.graphics local locationQuery = require 'map.locationQuery' local cities local points = {} local caps = {} t.selected = nil t.selectionLocked = false local invisible = 10000 --sentinel value outside the draw rectangle function t.lockSelection() t.selectionLocked = true end function t.unlockSelection() t.selectionLocked = false end function t.draw() return lg.points( points ) end function t.drawSelected( ) local c = t.selected if not c then return end lg.circle( "line", c.x, c.y, 1.0 ) end function t.drawCapitals() if cities.visible then lg.points( caps ) end end function t.selectNearest( cities, x, y ) return cities:getClosestPoint(x, y) --defer to locationQuery end local city = {} local citymt = {__index = city} function city:formatDisplayInfo() return ( [[ NAME: %s COUNTRY: %s LONGITUDE: %3.2f LATITUDE: %3.2f POP: %d CAPITAL: %s]]):format( self.name, self.country, self.x, self.y, self.pop, tostring(self.capital) ) 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 ) print( "=== LOADING CITIES. ===" ) cities = { visible = true, active = false, filename = filename } local n = 1 local idxPts = 1 local idxCaps = 1 points = {} caps = {} for line in assert( lfs.lines( filename ), "Error: could not open cities.dat" ) do local _, _, x, y, pop, capital = line:sub( 83 ):find( "(%g+)%s+(%g+)%s+(%g+)%s+(%g+)" ) 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 ) cities[n] = city n = n + 1 points[idxPts], points[idxPts + 1] = x, y idxPts = idxPts + 2 if capital then caps[idxCaps], caps[idxCaps + 1] = x, y idxCaps = idxCaps + 2 end else print( "CITIES: malformed line:", line ) end end --Multiple inheritance. cities = locationQuery.New( cities ) setmetatable( getmetatable( cities ).__index, {__index = t } ) print( "=== CITIES LOADED:", filename, n, "===" ) return cities end function t.save( cities ) local str = {} 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 return t