├── JSONMap.json ├── LICENSE ├── README.md ├── conf.lua ├── dkjson.lua ├── isomap.lua ├── lovebird.lua ├── main.lua ├── minimalDemo.lua ├── props ├── pixelTree.png └── praca.png └── textures ├── DirtTile.png ├── GrassTile.png ├── SelectTile.png ├── WaterTile.png ├── ls.png ├── rd.png ├── rdl.png ├── rdr.png ├── rdrd.png ├── rdv.png └── water.png /JSONMap.json: -------------------------------------------------------------------------------- 1 | { 2 | "textures": 3 | [ 4 | { 5 | "file": "ls.png", 6 | "mnemonic": "grass" 7 | }, 8 | { 9 | "file": "rd.png", 10 | "mnemonic": "road" 11 | }, 12 | { 13 | "file":"rdl.png", 14 | "mnemonic":"roadL" 15 | }, 16 | { 17 | "file":"rdv.png", 18 | "mnemonic":"roadV" 19 | }, 20 | { 21 | "file":"rdr.png", 22 | "mnemonic":"roadR" 23 | }, 24 | { 25 | "file":"rdrd.png", 26 | "mnemonic":"roadD" 27 | }, 28 | { 29 | "file":"water.png", 30 | "mnemonic":"water" 31 | } 32 | ], 33 | 34 | "props": 35 | [ 36 | { 37 | "file": "pixelTree.png", 38 | "mnemonic": "tree", 39 | "origin" : "72|245" 40 | }, 41 | { 42 | "file":"praca.png", 43 | "mnemonic":"post", 44 | "origin":"15|65" 45 | } 46 | ], 47 | 48 | "data": 49 | [ 50 | [["water"],["water"],["water"],["water"],["water"],["water"],["water"],["water"],["water"],["water"],["water"],["water"],["water"],["water"],["water"]] 51 | [["grass", "tree"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass", "tree"],["grass"],["grass"],["grass"],["grass"]] 52 | [["grass"],["grass", "tree"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"]] 53 | [["grass"],["grass"],["roadD"],["road"], ["road"], ["road"], ["road"], ["road"], ["road"], ["road"], ["road"], ["roadR"],["grass"],["grass"],["grass"]] 54 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass", "post"],["grass"],["roadV"],["grass"],["grass", "tree"],["grass"]] 55 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"]] 56 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass", "tree"],["grass"],["grass"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass"],["grass", "tree"]] 57 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass", "tree"],["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"]] 58 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"]] 59 | [["grass", "tree"],["grass"],["roadV"],["grass", "tree"],["grass"],["grass"],["grass"],["grass", "tree"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass", "tree"],["grass"]] 60 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"]] 61 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass", "tree"],["grass"],["roadV"],["grass"],["grass"],["grass"]] 62 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass", "tree"],["grass"],["grass"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"]] 63 | [["grass", "tree"],["grass"],["roadV"],["grass"],["grass"],["grass"],["grass"],["grass", "tree"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"]] 64 | [["grass"],["grass"],["roadV"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass"],["grass", "tree"]] 65 | [["grass"],["grass"],["roadV"],["grass", "tree"],["grass"],["grass"],["grass"],["grass", "tree"],["grass"],["grass"],["grass"],["roadV"],["grass"],["grass", "tree"],["grass"]] 66 | ], 67 | 68 | "general": 69 | [ 70 | { 71 | "name":"Simple road", 72 | "version":"v0.1", 73 | "lighting":"255|255|255" 74 | } 75 | ] 76 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Pedro Polez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # isomap-love2d 2 | Isometric map engine/library for Love2D 3 | 4 | 5 | 6 | ## What's this? 7 | This is a simple isometric map decoder and renderer. It can load maps with information stored on a JSON file and render the isometric map to the screen with a simple function call and a few parameters. 8 | The example uses mouse wheel to zoom in and zoom out, and arrow keys to move the map around. 9 | 10 | ## What now? 11 | I'll keep on adding things, such as elevation and maybe programmable tiles. I'll make a nice level editor too if needed. Won't be fast though. 12 | 13 | ### Modules used: 14 | * [DKJson](https://github.com/LuaDist/dkjson) for json parsing. Used by the library itself. 15 | * [Lovebird](https://github.com/rxi/lovebird) since it helps wonders with table content viewing. Used in the demo included. 16 | * [Kenney's isometric sprites](https://kenney.nl/) since I suck at drawing. Used in the demo. 17 | * [segfaultd](https://github.com/danielpontello) for drawing an example notice board, can be seen in the demo. 18 | 19 | # Great, but how do I use it? 20 | * First, create a JSON file, a "props" new folder, and a "textures" new folder in the root of your love project. Your project will look like this: 21 | 22 | 23 | * Add all textures you want to use for the ground layer to the *"textures"* folder. They **need** to have absolutely the same dimensions. 24 | 25 | * Add all prop textures you will place on your map to the *"props"* folder. These can be any dimensions you'd like. 26 | 27 | And you should be set up to create everything you need. 28 | 29 | # JSON map structure 30 | The basic JSON map will look a bit like this: 31 | ```JSON 32 | { 33 | "textures": 34 | [ 35 | { 36 | "file": "myTexture.*", 37 | "mnemonic": "myTexture" 38 | } 39 | ], 40 | 41 | "props": 42 | [ 43 | { 44 | "file": "myProp.*", 45 | "mnemonic": "myProp", 46 | "origin" : "50|72" 47 | } 48 | ], 49 | 50 | "data": 51 | [ 52 | [["myTexture"],["myTexture"],["myTexture"]], 53 | [["myTexture"],["myTexture", "myProp"],["myTexture"]], 54 | [["myTexture"],["myTexture"],["myTexture"]] 55 | ], 56 | 57 | "general": 58 | [ 59 | { 60 | "name":"Map name", 61 | "version":"string with map version", 62 | "lighting":"255|255|255" 63 | } 64 | ] 65 | } 66 | ``` 67 | Lets analyze it's parts. 68 | 69 | ### Textures 70 | ```JSON 71 | "textures": 72 | [ 73 | { 74 | "file": "myTexture.png", 75 | "mnemonic": "myTexture" 76 | }, 77 | { 78 | "file": "myTexture2.jpg", 79 | "mnemonic": "myTexture2" 80 | }, 81 | { 82 | "And so on":"for all textures you'll use" 83 | } 84 | ], 85 | ``` 86 | This is where you'll load your ground layer textures. It has 2 fields: 87 | * name: Specifies the texture filename. Can be any kind of image file Love loads. Examples are .PNG and .JPG files. 88 | * mnemonic: Specifies a more friendly name given to the texture. Used in the map matrix. (See below). 89 | 90 | **All ground textures need to have the same exact dimensions!** 91 | 92 | ### Props 93 | ```JSON 94 | "textures": 95 | [ 96 | { 97 | "file": "myProp.png", 98 | "mnemonic": "myProp", 99 | "origin": "X|Y" 100 | }, 101 | { 102 | "file": "myProp2.jpg", 103 | "mnemonic": "myProp2" 104 | "origin": "X|Y" 105 | }, 106 | { 107 | "And so on":"for all props you'll use" 108 | } 109 | ], 110 | ``` 111 | This is where you'll load your map props or objects. It has 3 fields: 112 | * name: Specifies the texture used in the prop. Can be any kind of image file Love loads. 113 | * mnemonic: Specifies a more friendly name given to the prop. Used in the map matrix. (See below). 114 | * origin: Specifies the offset used to draw the prop on the tile where it should be. Values should be X and Y separated by a pipe ("|"). To find out this offset, use any image editor you want! Then you modify your values until you can get it right. 115 | 116 | Prop textures can be any size you want. 117 | 118 | ### Data 119 | ```JSON 120 | "data": 121 | [ 122 | [["myTexture"],["myMnemonic"],["myTexture"]], 123 | [["myTexture"],["myTexture", "myProp"],["myTexture"]], 124 | [["myTexture"],["myTexture"],["myTexture"]] 125 | ], 126 | ``` 127 | Pretty straightforward. Use the *mnemonics* you defined for your props and textures above and put then into a JSON matrix. I'm not sure, but I think only one prop can be placed per tile. In this example, a map with a 3x3 size would be rendered with texture *myTexture*, and *myProp* prop would be placed in the middle tile[2][2]. Obviously, larger data matrixes will yield bigger maps. 128 | 129 | ### General 130 | ```JSON 131 | "general": 132 | [ 133 | { 134 | "name":"Map name", 135 | "version":"string with map version", 136 | "lighting":"R|G|B" 137 | } 138 | ] 139 | ``` 140 | Specifies general map information, as well as other extras. 141 | * name: Specifies the map name. 142 | * version: Specifies a map version. 143 | * lighting: A small trick. Values are RGB values separated by a pipe ("|"). Defines the color you want to use to draw your map. If you specify, say, a dark blue, it'll give the map a blue tone, faking a night environment. Be creative! Set this value to "255|255|255" if you don't want to use this feature. 144 | 145 | # Isomap methods 146 | ## isomap.decodeJson(filename) 147 | Decodes a given JSON file. 148 | 149 | *Arguments:* 150 | * filename: The JSON filepath. 151 | 152 | *Example*: 153 | ```Lua 154 | isomap.decodeJson("Jsonfile.json") 155 | ``` 156 | ## isomap.generatePlayfield() 157 | Generates the playfield with information provided by the preovously loaded JSON file. 158 | 159 | *No arguments.* 160 | 161 | *Example:* 162 | ```Lua 163 | isomap.generatePlayfield() 164 | ``` 165 | ## isomap.drawGround(x, y, zoomLevel) 166 | Draws the map with a *X* and a *Y* offset scaled by *zoomLevel*. 167 | 168 | *Arguments:* 169 | * **x** and **y**: X and Y offset for map drawing. 170 | * zoomLevel: a number specifiying the scale the map should be rendered. Used primarily for zooming in and out. 171 | 172 | *Example:* 173 | ```Lua 174 | isomap.drawGround(300, 200, 1.5) 175 | ``` 176 | 177 | ## isomap.drawObjects(x, y, zoomLevel) 178 | Sorts ZBuffer and draws objects on the map with a *X* and a *Y* offset scaled by *zoomLevel*. 179 | 180 | *Arguments:* 181 | * **x** and **y**: X and Y offset for object drawing. 182 | * zoomLevel: a number specifiying the scale the objects should be rendered. Used primarily for zooming in and out. 183 | 184 | *Example:* 185 | ```Lua 186 | isomap.drawObjects(300, 200, 1.5) 187 | ``` 188 | ## isomap.toIso(x, y) 189 | Converts *x* and *y* from cartesian (2D) to isometric coordinates. 190 | 191 | *Arguments:* 192 | * **x** and **y**: X and Y values to be converted. 193 | 194 | *Example:* 195 | ```Lua 196 | isomap.toIso(420, 350) 197 | ``` 198 | ## isomap.toCartesian(x, y) 199 | Converts *x* and *y* from isometric to cartesian (2D) coordinates. 200 | 201 | *Arguments:* 202 | * **x** and **y**: X and Y values to be converted. 203 | 204 | *Example:* 205 | ```Lua 206 | isomap.toCartesian(5, 8) 207 | ``` 208 | # Minimal usage example 209 | ```Lua 210 | isomap = require ("isomap") 211 | function love.load() 212 | --Variables 213 | x = 0 214 | y = 0 215 | 216 | --Set background to deep blue 217 | love.graphics.setBackgroundColor(0, 0, 69) 218 | 219 | --Decode JSON map file 220 | isomap.decodeJson("JSONMap.json") 221 | 222 | --Generate map from JSON file (loads assets and creates tables) 223 | isomap.generatePlayField() 224 | end 225 | 226 | function love.update(dt) 227 | --Get player input so map moves around. 228 | if love.keyboard.isDown("left") then x = x + 900*dt end 229 | if love.keyboard.isDown("right") then x = x - 900*dt end 230 | if love.keyboard.isDown("up") then y = y+900*dt end 231 | if love.keyboard.isDown("down") then y = y-900*dt end 232 | end 233 | 234 | function love.draw() 235 | isomap.drawGround(x, y, 1) 236 | isomap.drawObjects(x, y, 1) 237 | end 238 | 239 | ``` 240 | 241 | # Personal recommendations 242 | I recommend using [rxi's autobatch](https://github.com/rxi/autobatch) library to reduce draw calls made by this library to draw your map! 243 | 244 | # Contact 245 | Rants, questions, suggestions and anything else, [drop me an email here](mailto:pedrorocha@gec.inatel.br). 246 | -------------------------------------------------------------------------------- /conf.lua: -------------------------------------------------------------------------------- 1 | function love.conf(t) 2 | t.identity = nil -- The name of the save directory (string) 3 | t.version = "0.10.1" -- The L�VE version this game was made for (string) 4 | t.console = true -- Attach a console (boolean, Windows only) 5 | t.accelerometerjoystick = true -- Enable the accelerometer on iOS and Android by exposing it as a Joystick (boolean) 6 | t.externalstorage = false -- True to save files (and read from the save directory) in external storage on Android (boolean) 7 | t.gammacorrect = false -- Enable gamma-correct rendering, when supported by the system (boolean) 8 | 9 | t.window.title = "IsoMap" -- The window title (string) 10 | t.window.icon = nil -- Filepath to an image to use as the window's icon (string) 11 | t.window.width = 800 -- The window width (number) 12 | t.window.height = 600 -- The window height (number) 13 | t.window.borderless = false -- Remove all border visuals from the window (boolean) 14 | t.window.resizable = true -- Let the window be user-resizable (boolean) 15 | t.window.minwidth = 1 -- Minimum window width if the window is resizable (number) 16 | t.window.minheight = 1 -- Minimum window height if the window is resizable (number) 17 | t.window.fullscreen = false -- Enable fullscreen (boolean) 18 | t.window.fullscreentype = "desktop" -- Choose between "desktop" fullscreen or "exclusive" fullscreen mode (string) 19 | t.window.vsync = false -- Enable vertical sync (boolean) 20 | t.window.msaa = 0 -- The number of samples to use with multi-sampled antialiasing (number) 21 | t.window.display = 1 -- Index of the monitor to show the window in (number) 22 | t.window.highdpi = false -- Enable high-dpi mode for the window on a Retina display (boolean) 23 | t.window.x = nil -- The x-coordinate of the window's position in the specified display (number) 24 | t.window.y = nil -- The y-coordinate of the window's position in the specified display (number) 25 | 26 | t.modules.audio = true -- Enable the audio module (boolean) 27 | t.modules.event = true -- Enable the event module (boolean) 28 | t.modules.graphics = true -- Enable the graphics module (boolean) 29 | t.modules.image = true -- Enable the image module (boolean) 30 | t.modules.joystick = true -- Enable the joystick module (boolean) 31 | t.modules.keyboard = true -- Enable the keyboard module (boolean) 32 | t.modules.math = true -- Enable the math module (boolean) 33 | t.modules.mouse = true -- Enable the mouse module (boolean) 34 | t.modules.physics = true -- Enable the physics module (boolean) 35 | t.modules.sound = true -- Enable the sound module (boolean) 36 | t.modules.system = true -- Enable the system module (boolean) 37 | t.modules.timer = true -- Enable the timer module (boolean), Disabling it will result 0 delta time in love.update 38 | t.modules.touch = true -- Enable the touch module (boolean) 39 | t.modules.video = true -- Enable the video module (boolean) 40 | t.modules.window = true -- Enable the window module (boolean) 41 | t.modules.thread = true -- Enable the thread module (boolean) 42 | end 43 | -------------------------------------------------------------------------------- /dkjson.lua: -------------------------------------------------------------------------------- 1 | -- Module options: 2 | local always_try_using_lpeg = true 3 | local register_global_module_table = false 4 | local global_module_name = 'json' 5 | 6 | --[==[ 7 | 8 | David Kolf's JSON module for Lua 5.1/5.2 9 | 10 | Version 2.5 11 | 12 | 13 | For the documentation see the corresponding readme.txt or visit 14 | . 15 | 16 | You can contact the author by sending an e-mail to 'david' at the 17 | domain 'dkolf.de'. 18 | 19 | 20 | Copyright (C) 2010-2013 David Heiko Kolf 21 | 22 | Permission is hereby granted, free of charge, to any person obtaining 23 | a copy of this software and associated documentation files (the 24 | "Software"), to deal in the Software without restriction, including 25 | without limitation the rights to use, copy, modify, merge, publish, 26 | distribute, sublicense, and/or sell copies of the Software, and to 27 | permit persons to whom the Software is furnished to do so, subject to 28 | the following conditions: 29 | 30 | The above copyright notice and this permission notice shall be 31 | included in all copies or substantial portions of the Software. 32 | 33 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 34 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 35 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 36 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 37 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 38 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 39 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 40 | SOFTWARE. 41 | 42 | --]==] 43 | 44 | -- global dependencies: 45 | local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = 46 | pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset 47 | local error, require, pcall, select = error, require, pcall, select 48 | local floor, huge = math.floor, math.huge 49 | local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = 50 | string.rep, string.gsub, string.sub, string.byte, string.char, 51 | string.find, string.len, string.format 52 | local strmatch = string.match 53 | local concat = table.concat 54 | 55 | local json = { version = "dkjson 2.5" } 56 | 57 | if register_global_module_table then 58 | _G[global_module_name] = json 59 | end 60 | 61 | local _ENV = nil -- blocking globals in Lua 5.2 62 | 63 | pcall (function() 64 | -- Enable access to blocked metatables. 65 | -- Don't worry, this module doesn't change anything in them. 66 | local debmeta = require "debug".getmetatable 67 | if debmeta then getmetatable = debmeta end 68 | end) 69 | 70 | json.null = setmetatable ({}, { 71 | __tojson = function () return "null" end 72 | }) 73 | 74 | local function isarray (tbl) 75 | local max, n, arraylen = 0, 0, 0 76 | for k,v in pairs (tbl) do 77 | if k == 'n' and type(v) == 'number' then 78 | arraylen = v 79 | if v > max then 80 | max = v 81 | end 82 | else 83 | if type(k) ~= 'number' or k < 1 or floor(k) ~= k then 84 | return false 85 | end 86 | if k > max then 87 | max = k 88 | end 89 | n = n + 1 90 | end 91 | end 92 | if max > 10 and max > arraylen and max > n * 2 then 93 | return false -- don't create an array with too many holes 94 | end 95 | return true, max 96 | end 97 | 98 | local escapecodes = { 99 | ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", 100 | ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" 101 | } 102 | 103 | local function escapeutf8 (uchar) 104 | local value = escapecodes[uchar] 105 | if value then 106 | return value 107 | end 108 | local a, b, c, d = strbyte (uchar, 1, 4) 109 | a, b, c, d = a or 0, b or 0, c or 0, d or 0 110 | if a <= 0x7f then 111 | value = a 112 | elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then 113 | value = (a - 0xc0) * 0x40 + b - 0x80 114 | elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then 115 | value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 116 | elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then 117 | value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 118 | else 119 | return "" 120 | end 121 | if value <= 0xffff then 122 | return strformat ("\\u%.4x", value) 123 | elseif value <= 0x10ffff then 124 | -- encode as UTF-16 surrogate pair 125 | value = value - 0x10000 126 | local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) 127 | return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) 128 | else 129 | return "" 130 | end 131 | end 132 | 133 | local function fsub (str, pattern, repl) 134 | -- gsub always builds a new string in a buffer, even when no match 135 | -- exists. First using find should be more efficient when most strings 136 | -- don't contain the pattern. 137 | if strfind (str, pattern) then 138 | return gsub (str, pattern, repl) 139 | else 140 | return str 141 | end 142 | end 143 | 144 | local function quotestring (value) 145 | -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js 146 | value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) 147 | if strfind (value, "[\194\216\220\225\226\239]") then 148 | value = fsub (value, "\194[\128-\159\173]", escapeutf8) 149 | value = fsub (value, "\216[\128-\132]", escapeutf8) 150 | value = fsub (value, "\220\143", escapeutf8) 151 | value = fsub (value, "\225\158[\180\181]", escapeutf8) 152 | value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) 153 | value = fsub (value, "\226\129[\160-\175]", escapeutf8) 154 | value = fsub (value, "\239\187\191", escapeutf8) 155 | value = fsub (value, "\239\191[\176-\191]", escapeutf8) 156 | end 157 | return "\"" .. value .. "\"" 158 | end 159 | json.quotestring = quotestring 160 | 161 | local function replace(str, o, n) 162 | local i, j = strfind (str, o, 1, true) 163 | if i then 164 | return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) 165 | else 166 | return str 167 | end 168 | end 169 | 170 | -- locale independent num2str and str2num functions 171 | local decpoint, numfilter 172 | 173 | local function updatedecpoint () 174 | decpoint = strmatch(tostring(0.5), "([^05+])") 175 | -- build a filter that can be used to remove group separators 176 | numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" 177 | end 178 | 179 | updatedecpoint() 180 | 181 | local function num2str (num) 182 | return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") 183 | end 184 | 185 | local function str2num (str) 186 | local num = tonumber(replace(str, ".", decpoint)) 187 | if not num then 188 | updatedecpoint() 189 | num = tonumber(replace(str, ".", decpoint)) 190 | end 191 | return num 192 | end 193 | 194 | local function addnewline2 (level, buffer, buflen) 195 | buffer[buflen+1] = "\n" 196 | buffer[buflen+2] = strrep (" ", level) 197 | buflen = buflen + 2 198 | return buflen 199 | end 200 | 201 | function json.addnewline (state) 202 | if state.indent then 203 | state.bufferlen = addnewline2 (state.level or 0, 204 | state.buffer, state.bufferlen or #(state.buffer)) 205 | end 206 | end 207 | 208 | local encode2 -- forward declaration 209 | 210 | local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) 211 | local kt = type (key) 212 | if kt ~= 'string' and kt ~= 'number' then 213 | return nil, "type '" .. kt .. "' is not supported as a key by JSON." 214 | end 215 | if prev then 216 | buflen = buflen + 1 217 | buffer[buflen] = "," 218 | end 219 | if indent then 220 | buflen = addnewline2 (level, buffer, buflen) 221 | end 222 | buffer[buflen+1] = quotestring (key) 223 | buffer[buflen+2] = ":" 224 | return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) 225 | end 226 | 227 | local function appendcustom(res, buffer, state) 228 | local buflen = state.bufferlen 229 | if type (res) == 'string' then 230 | buflen = buflen + 1 231 | buffer[buflen] = res 232 | end 233 | return buflen 234 | end 235 | 236 | local function exception(reason, value, state, buffer, buflen, defaultmessage) 237 | defaultmessage = defaultmessage or reason 238 | local handler = state.exception 239 | if not handler then 240 | return nil, defaultmessage 241 | else 242 | state.bufferlen = buflen 243 | local ret, msg = handler (reason, value, state, defaultmessage) 244 | if not ret then return nil, msg or defaultmessage end 245 | return appendcustom(ret, buffer, state) 246 | end 247 | end 248 | 249 | function json.encodeexception(reason, value, state, defaultmessage) 250 | return quotestring("<" .. defaultmessage .. ">") 251 | end 252 | 253 | encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) 254 | local valtype = type (value) 255 | local valmeta = getmetatable (value) 256 | valmeta = type (valmeta) == 'table' and valmeta -- only tables 257 | local valtojson = valmeta and valmeta.__tojson 258 | if valtojson then 259 | if tables[value] then 260 | return exception('reference cycle', value, state, buffer, buflen) 261 | end 262 | tables[value] = true 263 | state.bufferlen = buflen 264 | local ret, msg = valtojson (value, state) 265 | if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end 266 | tables[value] = nil 267 | buflen = appendcustom(ret, buffer, state) 268 | elseif value == nil then 269 | buflen = buflen + 1 270 | buffer[buflen] = "null" 271 | elseif valtype == 'number' then 272 | local s 273 | if value ~= value or value >= huge or -value >= huge then 274 | -- This is the behaviour of the original JSON implementation. 275 | s = "null" 276 | else 277 | s = num2str (value) 278 | end 279 | buflen = buflen + 1 280 | buffer[buflen] = s 281 | elseif valtype == 'boolean' then 282 | buflen = buflen + 1 283 | buffer[buflen] = value and "true" or "false" 284 | elseif valtype == 'string' then 285 | buflen = buflen + 1 286 | buffer[buflen] = quotestring (value) 287 | elseif valtype == 'table' then 288 | if tables[value] then 289 | return exception('reference cycle', value, state, buffer, buflen) 290 | end 291 | tables[value] = true 292 | level = level + 1 293 | local isa, n = isarray (value) 294 | if n == 0 and valmeta and valmeta.__jsontype == 'object' then 295 | isa = false 296 | end 297 | local msg 298 | if isa then -- JSON array 299 | buflen = buflen + 1 300 | buffer[buflen] = "[" 301 | for i = 1, n do 302 | buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) 303 | if not buflen then return nil, msg end 304 | if i < n then 305 | buflen = buflen + 1 306 | buffer[buflen] = "," 307 | end 308 | end 309 | buflen = buflen + 1 310 | buffer[buflen] = "]" 311 | else -- JSON object 312 | local prev = false 313 | buflen = buflen + 1 314 | buffer[buflen] = "{" 315 | local order = valmeta and valmeta.__jsonorder or globalorder 316 | if order then 317 | local used = {} 318 | n = #order 319 | for i = 1, n do 320 | local k = order[i] 321 | local v = value[k] 322 | if v then 323 | used[k] = true 324 | buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 325 | prev = true -- add a seperator before the next element 326 | end 327 | end 328 | for k,v in pairs (value) do 329 | if not used[k] then 330 | buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 331 | if not buflen then return nil, msg end 332 | prev = true -- add a seperator before the next element 333 | end 334 | end 335 | else -- unordered 336 | for k,v in pairs (value) do 337 | buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) 338 | if not buflen then return nil, msg end 339 | prev = true -- add a seperator before the next element 340 | end 341 | end 342 | if indent then 343 | buflen = addnewline2 (level - 1, buffer, buflen) 344 | end 345 | buflen = buflen + 1 346 | buffer[buflen] = "}" 347 | end 348 | tables[value] = nil 349 | else 350 | return exception ('unsupported type', value, state, buffer, buflen, 351 | "type '" .. valtype .. "' is not supported by JSON.") 352 | end 353 | return buflen 354 | end 355 | 356 | function json.encode (value, state) 357 | state = state or {} 358 | local oldbuffer = state.buffer 359 | local buffer = oldbuffer or {} 360 | state.buffer = buffer 361 | updatedecpoint() 362 | local ret, msg = encode2 (value, state.indent, state.level or 0, 363 | buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) 364 | if not ret then 365 | error (msg, 2) 366 | elseif oldbuffer == buffer then 367 | state.bufferlen = ret 368 | return true 369 | else 370 | state.bufferlen = nil 371 | state.buffer = nil 372 | return concat (buffer) 373 | end 374 | end 375 | 376 | local function loc (str, where) 377 | local line, pos, linepos = 1, 1, 0 378 | while true do 379 | pos = strfind (str, "\n", pos, true) 380 | if pos and pos < where then 381 | line = line + 1 382 | linepos = pos 383 | pos = pos + 1 384 | else 385 | break 386 | end 387 | end 388 | return "line " .. line .. ", column " .. (where - linepos) 389 | end 390 | 391 | local function unterminated (str, what, where) 392 | return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) 393 | end 394 | 395 | local function scanwhite (str, pos) 396 | while true do 397 | pos = strfind (str, "%S", pos) 398 | if not pos then return nil end 399 | local sub2 = strsub (str, pos, pos + 1) 400 | if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then 401 | -- UTF-8 Byte Order Mark 402 | pos = pos + 3 403 | elseif sub2 == "//" then 404 | pos = strfind (str, "[\n\r]", pos + 2) 405 | if not pos then return nil end 406 | elseif sub2 == "/*" then 407 | pos = strfind (str, "*/", pos + 2) 408 | if not pos then return nil end 409 | pos = pos + 2 410 | else 411 | return pos 412 | end 413 | end 414 | end 415 | 416 | local escapechars = { 417 | ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", 418 | ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" 419 | } 420 | 421 | local function unichar (value) 422 | if value < 0 then 423 | return nil 424 | elseif value <= 0x007f then 425 | return strchar (value) 426 | elseif value <= 0x07ff then 427 | return strchar (0xc0 + floor(value/0x40), 428 | 0x80 + (floor(value) % 0x40)) 429 | elseif value <= 0xffff then 430 | return strchar (0xe0 + floor(value/0x1000), 431 | 0x80 + (floor(value/0x40) % 0x40), 432 | 0x80 + (floor(value) % 0x40)) 433 | elseif value <= 0x10ffff then 434 | return strchar (0xf0 + floor(value/0x40000), 435 | 0x80 + (floor(value/0x1000) % 0x40), 436 | 0x80 + (floor(value/0x40) % 0x40), 437 | 0x80 + (floor(value) % 0x40)) 438 | else 439 | return nil 440 | end 441 | end 442 | 443 | local function scanstring (str, pos) 444 | local lastpos = pos + 1 445 | local buffer, n = {}, 0 446 | while true do 447 | local nextpos = strfind (str, "[\"\\]", lastpos) 448 | if not nextpos then 449 | return unterminated (str, "string", pos) 450 | end 451 | if nextpos > lastpos then 452 | n = n + 1 453 | buffer[n] = strsub (str, lastpos, nextpos - 1) 454 | end 455 | if strsub (str, nextpos, nextpos) == "\"" then 456 | lastpos = nextpos + 1 457 | break 458 | else 459 | local escchar = strsub (str, nextpos + 1, nextpos + 1) 460 | local value 461 | if escchar == "u" then 462 | value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) 463 | if value then 464 | local value2 465 | if 0xD800 <= value and value <= 0xDBff then 466 | -- we have the high surrogate of UTF-16. Check if there is a 467 | -- low surrogate escaped nearby to combine them. 468 | if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then 469 | value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) 470 | if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then 471 | value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 472 | else 473 | value2 = nil -- in case it was out of range for a low surrogate 474 | end 475 | end 476 | end 477 | value = value and unichar (value) 478 | if value then 479 | if value2 then 480 | lastpos = nextpos + 12 481 | else 482 | lastpos = nextpos + 6 483 | end 484 | end 485 | end 486 | end 487 | if not value then 488 | value = escapechars[escchar] or escchar 489 | lastpos = nextpos + 2 490 | end 491 | n = n + 1 492 | buffer[n] = value 493 | end 494 | end 495 | if n == 1 then 496 | return buffer[1], lastpos 497 | elseif n > 1 then 498 | return concat (buffer), lastpos 499 | else 500 | return "", lastpos 501 | end 502 | end 503 | 504 | local scanvalue -- forward declaration 505 | 506 | local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) 507 | local len = strlen (str) 508 | local tbl, n = {}, 0 509 | local pos = startpos + 1 510 | if what == 'object' then 511 | setmetatable (tbl, objectmeta) 512 | else 513 | setmetatable (tbl, arraymeta) 514 | end 515 | while true do 516 | pos = scanwhite (str, pos) 517 | if not pos then return unterminated (str, what, startpos) end 518 | local char = strsub (str, pos, pos) 519 | if char == closechar then 520 | return tbl, pos + 1 521 | end 522 | local val1, err 523 | val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) 524 | if err then return nil, pos, err end 525 | pos = scanwhite (str, pos) 526 | if not pos then return unterminated (str, what, startpos) end 527 | char = strsub (str, pos, pos) 528 | if char == ":" then 529 | if val1 == nil then 530 | return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" 531 | end 532 | pos = scanwhite (str, pos + 1) 533 | if not pos then return unterminated (str, what, startpos) end 534 | local val2 535 | val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) 536 | if err then return nil, pos, err end 537 | tbl[val1] = val2 538 | pos = scanwhite (str, pos) 539 | if not pos then return unterminated (str, what, startpos) end 540 | char = strsub (str, pos, pos) 541 | else 542 | n = n + 1 543 | tbl[n] = val1 544 | end 545 | if char == "," then 546 | pos = pos + 1 547 | end 548 | end 549 | end 550 | 551 | scanvalue = function (str, pos, nullval, objectmeta, arraymeta) 552 | pos = pos or 1 553 | pos = scanwhite (str, pos) 554 | if not pos then 555 | return nil, strlen (str) + 1, "no valid JSON value (reached the end)" 556 | end 557 | local char = strsub (str, pos, pos) 558 | if char == "{" then 559 | return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) 560 | elseif char == "[" then 561 | return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) 562 | elseif char == "\"" then 563 | return scanstring (str, pos) 564 | else 565 | local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) 566 | if pstart then 567 | local number = str2num (strsub (str, pstart, pend)) 568 | if number then 569 | return number, pend + 1 570 | end 571 | end 572 | pstart, pend = strfind (str, "^%a%w*", pos) 573 | if pstart then 574 | local name = strsub (str, pstart, pend) 575 | if name == "true" then 576 | return true, pend + 1 577 | elseif name == "false" then 578 | return false, pend + 1 579 | elseif name == "null" then 580 | return nullval, pend + 1 581 | end 582 | end 583 | return nil, pos, "no valid JSON value at " .. loc (str, pos) 584 | end 585 | end 586 | 587 | local function optionalmetatables(...) 588 | if select("#", ...) > 0 then 589 | return ... 590 | else 591 | return {__jsontype = 'object'}, {__jsontype = 'array'} 592 | end 593 | end 594 | 595 | function json.decode (str, pos, nullval, ...) 596 | local objectmeta, arraymeta = optionalmetatables(...) 597 | return scanvalue (str, pos, nullval, objectmeta, arraymeta) 598 | end 599 | 600 | function json.use_lpeg () 601 | local g = require ("lpeg") 602 | 603 | if g.version() == "0.11" then 604 | error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" 605 | end 606 | 607 | local pegmatch = g.match 608 | local P, S, R = g.P, g.S, g.R 609 | 610 | local function ErrorCall (str, pos, msg, state) 611 | if not state.msg then 612 | state.msg = msg .. " at " .. loc (str, pos) 613 | state.pos = pos 614 | end 615 | return false 616 | end 617 | 618 | local function Err (msg) 619 | return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) 620 | end 621 | 622 | local SingleLineComment = P"//" * (1 - S"\n\r")^0 623 | local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" 624 | local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 625 | 626 | local PlainChar = 1 - S"\"\\\n\r" 627 | local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars 628 | local HexDigit = R("09", "af", "AF") 629 | local function UTF16Surrogate (match, pos, high, low) 630 | high, low = tonumber (high, 16), tonumber (low, 16) 631 | if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then 632 | return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) 633 | else 634 | return false 635 | end 636 | end 637 | local function UTF16BMP (hex) 638 | return unichar (tonumber (hex, 16)) 639 | end 640 | local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) 641 | local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP 642 | local Char = UnicodeEscape + EscapeSequence + PlainChar 643 | local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string") 644 | local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) 645 | local Fractal = P"." * R"09"^0 646 | local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 647 | local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num 648 | local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1) 649 | local SimpleValue = Number + String + Constant 650 | local ArrayContent, ObjectContent 651 | 652 | -- The functions parsearray and parseobject parse only a single value/pair 653 | -- at a time and store them directly to avoid hitting the LPeg limits. 654 | local function parsearray (str, pos, nullval, state) 655 | local obj, cont 656 | local npos 657 | local t, nt = {}, 0 658 | repeat 659 | obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) 660 | if not npos then break end 661 | pos = npos 662 | nt = nt + 1 663 | t[nt] = obj 664 | until cont == 'last' 665 | return pos, setmetatable (t, state.arraymeta) 666 | end 667 | 668 | local function parseobject (str, pos, nullval, state) 669 | local obj, key, cont 670 | local npos 671 | local t = {} 672 | repeat 673 | key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) 674 | if not npos then break end 675 | pos = npos 676 | t[key] = obj 677 | until cont == 'last' 678 | return pos, setmetatable (t, state.objectmeta) 679 | end 680 | 681 | local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected") 682 | local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected") 683 | local Value = Space * (Array + Object + SimpleValue) 684 | local ExpectedValue = Value + Space * Err "value expected" 685 | ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() 686 | local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue) 687 | ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() 688 | local DecodeValue = ExpectedValue * g.Cp () 689 | 690 | function json.decode (str, pos, nullval, ...) 691 | local state = {} 692 | state.objectmeta, state.arraymeta = optionalmetatables(...) 693 | local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) 694 | if state.msg then 695 | return nil, state.pos, state.msg 696 | else 697 | return obj, retpos 698 | end 699 | end 700 | 701 | -- use this function only once: 702 | json.use_lpeg = function () return json end 703 | 704 | json.using_lpeg = true 705 | 706 | return json -- so you can get the module using json = require "dkjson".use_lpeg() 707 | end 708 | 709 | if always_try_using_lpeg then 710 | pcall (json.use_lpeg) 711 | end 712 | 713 | return json 714 | 715 | -------------------------------------------------------------------------------- /isomap.lua: -------------------------------------------------------------------------------- 1 | --[[MIT License 2 | 3 | Copyright (c) 2016 Pedro Polez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE.]]-- 22 | 23 | local json = require("dkjson") 24 | --TODO: Load dkjson relative to mapDecoder's path. 25 | 26 | 27 | map = {} 28 | mapDec = {} 29 | local mapTextures = {} 30 | mapPositions = {} 31 | mapProps = {} 32 | local mapLighting = {} 33 | mapPropsfield = {} 34 | local tileWidth = 0 35 | local tileHeight = 0 36 | 37 | local mapPlayfieldWidthInTiles = 0 38 | local mapPlayfieldHeightInTiles = 0 39 | 40 | local objectListSize = 0 41 | 42 | local zoomLevel = 1 43 | 44 | function map.decodeJson(filename) 45 | assert(filename, "Filename is nil!") 46 | if not love.filesystem.isFile(filename) then error("Given filename is not a file! Is it a directory? Does it exist?") end 47 | 48 | --Reads file 49 | mapJson = love.filesystem.read(filename) 50 | 51 | --Attempts to decode file 52 | mapDec = json.decode(mapJson) 53 | 54 | end 55 | 56 | 57 | function map.generatePlayField() 58 | --TODO: Maps will be packed as renamed ZIP file extensios and will be able to be installed in users machines. So, textures and props have to be loaded from this directory. 59 | --Currently, the mapDecoder will look for textures in folder named textures in the root of the project, and props in a props folder. 60 | 61 | print("Current map information:") 62 | print("General information: =-=-=-=-=-=-=") 63 | if mapDec.general ~= nil then 64 | print("Map name: "..mapDec.general[1].name) 65 | print("Map version: "..mapDec.general[1].version) 66 | print("Map lighting: "..mapDec.general[1].lighting) 67 | if mapDec.general[1].lighting ~= nil then 68 | mapLighting = string.split_(mapDec.general[1].lighting, "|") 69 | end 70 | print("----") 71 | end 72 | 73 | print("Ground textures: =-=-=-=-=-=-=-=") 74 | for i, texture in ipairs(mapDec.textures) do 75 | --Print table contents for now 76 | print(texture.file) 77 | print(texture.mnemonic) 78 | print("---") 79 | 80 | table.insert(mapTextures, {file = texture.file, mnemonic = texture.mnemonic, image = love.graphics.newImage("textures/"..texture.file)}) 81 | 82 | end 83 | 84 | --Get ground texture dimensions 85 | tileWidth = mapTextures[1].image:getWidth()/2 86 | tileHeight = mapTextures[1].image:getHeight()/2 87 | 88 | print("Playfield props: =-=-=-=-=-=-=-=") 89 | if mapDec.props ~= nil then 90 | for i, props in ipairs(mapDec.props) do 91 | print(props.file) 92 | print(props.mnemonic) 93 | print(props.origin) 94 | print("----") 95 | table.insert(mapProps, {file = props.file, mnemonic = props.mnemonic, image = love.graphics.newImage("props/"..props.file), origins = string.split_(props.origin, "|")}) 96 | end 97 | else 98 | print("No props found on current map!") 99 | end 100 | 101 | 102 | --Add each ground tile to a table according to their texture 103 | --TODO: the following should be done on a separate thread. I have not tested the performance of the following lines on a colossal map. 104 | timerStart = love.timer.getTime() 105 | for i, groundTexture in ipairs(mapTextures) do 106 | for colunas in ipairs(mapDec.data) do 107 | for linhas in ipairs(mapDec.data[colunas]) do 108 | for i, properties in ipairs(mapDec.data[colunas][linhas]) do 109 | 110 | --Add ground texture if mnemonic is found 111 | if properties == groundTexture.mnemonic then 112 | local xPos = linhas 113 | local yPos = colunas 114 | if mapPositions[colunas] == nil then 115 | mapPositions[colunas] = {} 116 | end 117 | if mapPositions[colunas][linhas] == nil then 118 | mapPositions[colunas][linhas] = {} 119 | end 120 | table.insert(mapPositions[colunas][linhas], {texture = groundTexture.image, x=xPos, y=yPos}) 121 | end 122 | 123 | end 124 | end 125 | end 126 | end 127 | 128 | --TODO: Merge these loops, since both save stuff to the same table? 129 | --Add object to map accordingly 130 | for i, props in ipairs(mapProps) do --For each object 131 | 132 | --Loop through map terrain information 133 | for colunas in ipairs(mapDec.data) do 134 | for linhas in ipairs(mapDec.data[colunas]) do 135 | 136 | --Iterate over the objects in a given 2D position 137 | for i, objects in ipairs(mapDec.data[colunas][linhas]) do 138 | if objects == props.mnemonic then 139 | --table.insert(mapPositions[colunas][linhas], {texture=props.image, x=linhas, y=colunas, offX=props.origins[1], offY=props.origins[2]}) 140 | 141 | --VERY IMPORTANT NOTE ABOUT THE FOLLOWING LINES 142 | --these control the ZBuffer in some *dark manner*. IT WORKS. I **really** have to figure out why. 143 | pX, pY = map.toIso(linhas, colunas) 144 | 145 | colX = linhas * (tileWidth*zoomLevel) 146 | colY = colunas * (tileWidth*zoomLevel) 147 | colX, colY = map.toIso(colX, colY) 148 | table.insert(mapPropsfield,{texture=props.image, x=linhas, y=colunas, offX=props.origins[1], offY=props.origins[2], mapY = pY, mapX = pX, colX = colX, colY = colY, width = props.image:getWidth(), height = props.image:getHeight(), alpha = false}) 149 | end 150 | end 151 | 152 | end 153 | end 154 | 155 | end 156 | --Calculate map dimensions 157 | mapPlayfieldWidthInTiles = #mapPositions 158 | mapPlayfieldHeightInTiles = #mapPositions[1] 159 | 160 | --Store map original object list size without any extra dynamic objects 161 | objectListSize = #mapPropsfield 162 | 163 | timerEnd = love.timer.getTime() 164 | print("Decode loop took "..((timerEnd-timerStart)*100).."ms") 165 | 166 | end 167 | 168 | function map.drawGround(xOff, yOff, size) 169 | assert(xOff) 170 | assert(yOff) 171 | assert(size) 172 | zoomLevel = size 173 | --Apply lighting 174 | love.graphics.setColor(tonumber(mapLighting[1]), tonumber(mapLighting[2]), tonumber(mapLighting[3]), 255) 175 | 176 | --Draw the flat ground layer for the map, without elevation or props. 177 | for i in ipairs(mapPositions) do 178 | for j=1,#mapPositions[i], 1 do 179 | local xPos = mapPositions[i][j][1].x * (tileWidth*zoomLevel) 180 | local yPos = mapPositions[i][j][1].y * (tileWidth*zoomLevel) 181 | local xPos, yPos = map.toIso(xPos, yPos) 182 | love.graphics.draw(mapPositions[i][j][1].texture,xPos+xOff, yPos+yOff, 0, size, size, mapPositions[i][j][1].texture:getWidth()/2, mapPositions[i][j][1].texture:getHeight()/2 ) 183 | end 184 | end 185 | 186 | end 187 | 188 | function map.drawObjects(xOff, yOff, size) 189 | 190 | --Figure out dynamic object occlusion 191 | if #mapPropsfield > objectListSize then 192 | for i=objectListSize+1, #mapPropsfield do 193 | for j=1, objectListSize do 194 | if CheckCollision(mapPropsfield[j].colX, mapPropsfield[j].colY, mapPropsfield[j].width, mapPropsfield[j].height, mapPropsfield[i].colX, mapPropsfield[i].colY, mapPropsfield[i].width, mapPropsfield[i].height) and mapPropsfield[i].y < mapPropsfield[j].y and mapPropsfield[i].x < mapPropsfield[j].x then 195 | mapPropsfield[j].alpha = true 196 | end 197 | end 198 | end 199 | end 200 | 201 | --Sort ZBuffer and draw objects. 202 | for k,v in spairs(mapPropsfield, function(t,a,b) return t[b].mapY > t[a].mapY end) do 203 | local xPos = v.x * (tileWidth*zoomLevel) 204 | local yPos = v.y * (tileWidth*zoomLevel) 205 | local xPos, yPos = map.toIso(xPos, yPos) 206 | 207 | if v.alpha then 208 | love.graphics.setColor(255, 255, 255, 90) 209 | else 210 | love.graphics.setColor(255, 255, 255, 255) 211 | end 212 | love.graphics.draw(v.texture, xPos+xOff, yPos+yOff, 0, size, size, v.offX, v.offY) 213 | 214 | --Update values in order to minimize for loops 215 | v.alpha = false 216 | v.colX = xPos-v.offX 217 | v.colY = yPos-v.offY 218 | v.mapX, v.mapY = map.toIso(v.x, v.y) 219 | end 220 | end 221 | 222 | 223 | function map.getTileCoordinates2D(i, j) 224 | local xP = mapPositions[i][j][1].x * (tileWidth*zoomLevel) 225 | local yP = mapPositions[i][j][1].y * (tileWidth*zoomLevel) 226 | xP, yP = map.toIso(xP, yP) 227 | return xP, yP 228 | end 229 | 230 | function map.getPlayfieldWidth() 231 | return mapPlayfieldWidthInTiles 232 | end 233 | 234 | function map.getPlayfieldHeight() 235 | return mapPlayfieldHeightInTiles 236 | end 237 | 238 | function map.getGroundTileWidth() 239 | return tileWidth 240 | end 241 | 242 | --Links used whilst searching for information on isometric maps: 243 | --http://stackoverflow.com/questions/892811/drawing-isometric-game-worlds 244 | --https://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511 245 | --Give it a good read if you don't understand whats happening over here. 246 | 247 | function map.toIso(x, y) 248 | assert(x, "Position X is nil!") 249 | assert(y, "Position Y is nil!") 250 | 251 | newX = x-y 252 | newY = (x + y)/2 253 | return newX, newY 254 | end 255 | 256 | function map.toCartesian(x, y) 257 | assert(x, "Position X is nil!") 258 | assert(y, "Position Y is nil!") 259 | x = (2 * y + x)/2 260 | y = (2 * y - x)/2 261 | return x, y 262 | end 263 | 264 | function map.insertNewObject(textureI, isoX, isoY, offXR, offYR) 265 | --User checks 266 | if offXR == nil then offXR = 0 end 267 | if offYR == nil then offYR = 0 end 268 | assert(textureI, "Invalid texture file for object!") 269 | assert(isoX, "No X position for object! (Isometric coordinates)") 270 | assert(isoY, "No Y position for object! (Isometric coordinates)") 271 | assert(mapPlayfieldWidthInTiles>=isoX, "Insertion coordinates out of map bounds! (X)") 272 | assert(mapPlayfieldWidthInTiles>=isoY, "Insertion coordinates out of map bounds! (Y)") 273 | local rx, ry = map.toIso(isoX, isoY) 274 | 275 | local colX = isoX * (tileWidth*zoomLevel) 276 | local colY = isoY * (tileWidth*zoomLevel) 277 | colX, colY = map.toIso(colX, colY) 278 | --Insert object on map 279 | table.insert(mapPropsfield, {texture=textureI, x=isoY, y=isoX+0.001, offX=offXR, offY = offYR, mapY = ry, mapX = rx, colX = colX, colY = colY, width = textureI:getWidth(), height = textureI:getHeight(), alpha = false}) 280 | end 281 | 282 | function map.removeObject(x, y) 283 | if #mapPositions[x][y] > 1 then 284 | table.remove(mapPositions[x][y], #mapPositions[x][y]) 285 | end 286 | end 287 | 288 | 289 | --This next function had the underscore added to avoid collisions with 290 | --any other possible split function the user may want to use. 291 | function string:split_(sSeparator, nMax, bRegexp) 292 | assert(sSeparator ~= '') 293 | assert(nMax == nil or nMax >= 1) 294 | 295 | local aRecord = {} 296 | 297 | if self:len() > 0 then 298 | local bPlain = not bRegexp 299 | nMax = nMax or -1 300 | 301 | local nField, nStart = 1, 1 302 | local nFirst,nLast = self:find(sSeparator, nStart, bPlain) 303 | while nFirst and nMax ~= 0 do 304 | aRecord[nField] = self:sub(nStart, nFirst-1) 305 | nField = nField+1 306 | nStart = nLast+1 307 | nFirst,nLast = self:find(sSeparator, nStart, bPlain) 308 | nMax = nMax-1 309 | end 310 | aRecord[nField] = self:sub(nStart) 311 | end 312 | 313 | return aRecord 314 | --Credit goes to JoanOrdinas @ lua-users.org 315 | end 316 | 317 | function spairs(t, order) 318 | -- collect the keys 319 | local keys = {} 320 | for k in pairs(t) do keys[#keys+1] = k end 321 | 322 | -- if order function given, sort by it by passing the table and keys a, b, 323 | -- otherwise just sort the keys 324 | if order then 325 | table.sort(keys, function(a,b) return order(t, a, b) end) 326 | else 327 | table.sort(keys) 328 | end 329 | 330 | -- return the iterator function 331 | local i = 0 332 | return function() 333 | i = i + 1 334 | if keys[i] then 335 | return keys[i], t[keys[i]] 336 | end 337 | end 338 | --https://stackoverflow.com/questions/15706270/sort-a-table-in-lua 339 | --Function "spairs" by Michal Kottman. 340 | end 341 | 342 | -- Collision detection function; 343 | -- Returns true if two boxes overlap, false if they don't; 344 | -- x1,y1 are the top-left coords of the first box, while w1,h1 are its width and height; 345 | -- x2,y2,w2 & h2 are the same, but for the second box. 346 | function CheckCollision(x1,y1,w1,h1, x2,y2,w2,h2) 347 | return x1 < x2+w2 and 348 | x2 < x1+w1 and 349 | y1 < y2+h2 and 350 | y2 < y1+h1 351 | end 352 | 353 | return map 354 | -------------------------------------------------------------------------------- /lovebird.lua: -------------------------------------------------------------------------------- 1 | -- 2 | -- lovebird 3 | -- 4 | -- Copyright (c) 2017 rxi 5 | -- 6 | -- This library is free software; you can redistribute it and/or modify it 7 | -- under the terms of the MIT license. See LICENSE for details. 8 | -- 9 | 10 | local socket = require "socket" 11 | 12 | local lovebird = { _version = "0.4.2" } 13 | 14 | lovebird.loadstring = loadstring or load 15 | lovebird.inited = false 16 | lovebird.host = "*" 17 | lovebird.buffer = "" 18 | lovebird.lines = {} 19 | lovebird.connections = {} 20 | lovebird.pages = {} 21 | 22 | lovebird.wrapprint = true 23 | lovebird.timestamp = true 24 | lovebird.allowhtml = false 25 | lovebird.echoinput = true 26 | lovebird.port = 8000 27 | lovebird.whitelist = { "127.0.0.1" } 28 | lovebird.maxlines = 200 29 | lovebird.updateinterval = .5 30 | 31 | 32 | lovebird.pages["index"] = [[ 33 | 47 | 48 | 49 | 50 | 51 | 52 | lovebird 53 | 187 | 188 | 189 | 196 |
197 |
198 |
199 |
200 |
203 | 205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 | 375 | 376 | 377 | ]] 378 | 379 | 380 | lovebird.pages["buffer"] = [[ ]] 381 | 382 | 383 | lovebird.pages["env.json"] = [[ 384 | 399 | { 400 | "valid": true, 401 | "path": "", 402 | "vars": [ 403 | 414 | { 415 | "key": "", 416 | "value": , 421 | "type": "", 422 | }, 423 | 424 | ] 425 | } 426 | ]] 427 | 428 | 429 | 430 | function lovebird.init() 431 | -- Init server 432 | lovebird.server = assert(socket.bind(lovebird.host, lovebird.port)) 433 | lovebird.addr, lovebird.port = lovebird.server:getsockname() 434 | lovebird.server:settimeout(0) 435 | -- Wrap print 436 | lovebird.origprint = print 437 | if lovebird.wrapprint then 438 | local oldprint = print 439 | print = function(...) 440 | oldprint(...) 441 | lovebird.print(...) 442 | end 443 | end 444 | -- Compile page templates 445 | for k, page in pairs(lovebird.pages) do 446 | lovebird.pages[k] = lovebird.template(page, "lovebird, req", 447 | "pages." .. k) 448 | end 449 | lovebird.inited = true 450 | end 451 | 452 | 453 | function lovebird.template(str, params, chunkname) 454 | params = params and ("," .. params) or "" 455 | local f = function(x) return string.format(" echo(%q)", x) end 456 | str = ("?>"..str.."(.-)<%?lua", f) 457 | str = "local echo " .. params .. " = ..." .. str 458 | local fn = assert(lovebird.loadstring(str, chunkname)) 459 | return function(...) 460 | local output = {} 461 | local echo = function(str) table.insert(output, str) end 462 | fn(echo, ...) 463 | return table.concat(lovebird.map(output, tostring)) 464 | end 465 | end 466 | 467 | 468 | function lovebird.map(t, fn) 469 | local res = {} 470 | for k, v in pairs(t) do res[k] = fn(v) end 471 | return res 472 | end 473 | 474 | 475 | function lovebird.trace(...) 476 | local str = "[lovebird] " .. table.concat(lovebird.map({...}, tostring), " ") 477 | print(str) 478 | if not lovebird.wrapprint then lovebird.print(str) end 479 | end 480 | 481 | 482 | function lovebird.unescape(str) 483 | local f = function(x) return string.char(tonumber("0x"..x)) end 484 | return (str:gsub("%+", " "):gsub("%%(..)", f)) 485 | end 486 | 487 | 488 | function lovebird.parseurl(url) 489 | local res = {} 490 | res.path, res.search = url:match("/([^%?]*)%??(.*)") 491 | res.query = {} 492 | for k, v in res.search:gmatch("([^&^?]-)=([^&^#]*)") do 493 | res.query[k] = lovebird.unescape(v) 494 | end 495 | return res 496 | end 497 | 498 | 499 | function lovebird.htmlescape(str) 500 | return str:gsub("<", "<") 501 | end 502 | 503 | 504 | function lovebird.truncate(str, len) 505 | if #str <= len then 506 | return str 507 | end 508 | return str:sub(1, len - 3) .. "..." 509 | end 510 | 511 | 512 | function lovebird.compare(a, b) 513 | local na, nb = tonumber(a), tonumber(b) 514 | if na then 515 | if nb then return na < nb end 516 | return false 517 | elseif nb then 518 | return true 519 | end 520 | return tostring(a) < tostring(b) 521 | end 522 | 523 | 524 | function lovebird.checkwhitelist(addr) 525 | if lovebird.whitelist == nil then return true end 526 | for _, a in pairs(lovebird.whitelist) do 527 | local ptn = "^" .. a:gsub("%.", "%%."):gsub("%*", "%%d*") .. "$" 528 | if addr:match(ptn) then return true end 529 | end 530 | return false 531 | end 532 | 533 | 534 | function lovebird.clear() 535 | lovebird.lines = {} 536 | lovebird.buffer = "" 537 | end 538 | 539 | 540 | function lovebird.pushline(line) 541 | line.time = os.time() 542 | line.count = 1 543 | table.insert(lovebird.lines, line) 544 | if #lovebird.lines > lovebird.maxlines then 545 | table.remove(lovebird.lines, 1) 546 | end 547 | lovebird.recalcbuffer() 548 | end 549 | 550 | 551 | function lovebird.recalcbuffer() 552 | local function doline(line) 553 | local str = line.str 554 | if not lovebird.allowhtml then 555 | str = lovebird.htmlescape(line.str):gsub("\n", "
") 556 | end 557 | if line.type == "input" then 558 | str = '' .. str .. '' 559 | else 560 | if line.type == "error" then 561 | str = '! ' .. str 562 | str = '' .. str .. '' 563 | end 564 | if line.count > 1 then 565 | str = '' .. line.count .. ' ' .. str 566 | end 567 | if lovebird.timestamp then 568 | str = os.date('%H:%M:%S ', line.time) .. 569 | str 570 | end 571 | end 572 | return str 573 | end 574 | lovebird.buffer = table.concat(lovebird.map(lovebird.lines, doline), "
") 575 | end 576 | 577 | 578 | function lovebird.print(...) 579 | local t = {} 580 | for i = 1, select("#", ...) do 581 | table.insert(t, tostring(select(i, ...))) 582 | end 583 | local str = table.concat(t, " ") 584 | local last = lovebird.lines[#lovebird.lines] 585 | if last and str == last.str then 586 | -- Update last line if this line is a duplicate of it 587 | last.time = os.time() 588 | last.count = last.count + 1 589 | lovebird.recalcbuffer() 590 | else 591 | -- Create new line 592 | lovebird.pushline({ type = "output", str = str }) 593 | end 594 | end 595 | 596 | 597 | function lovebird.onerror(err) 598 | lovebird.pushline({ type = "error", str = err }) 599 | if lovebird.wrapprint then 600 | lovebird.origprint("[lovebird] ERROR: " .. err) 601 | end 602 | end 603 | 604 | 605 | function lovebird.onrequest(req, client) 606 | local page = req.parsedurl.path 607 | page = page ~= "" and page or "index" 608 | -- Handle "page not found" 609 | if not lovebird.pages[page] then 610 | return "HTTP/1.1 404\r\nContent-Length: 8\r\n\r\nBad page" 611 | end 612 | -- Handle page 613 | local str 614 | xpcall(function() 615 | local data = lovebird.pages[page](lovebird, req) 616 | str = "HTTP/1.1 200 OK\r\n" .. 617 | "Content-Length: " .. #data .. "\r\n" .. 618 | "\r\n" .. data 619 | end, lovebird.onerror) 620 | return str 621 | end 622 | 623 | 624 | function lovebird.receive(client, pattern) 625 | while 1 do 626 | local data, msg = client:receive(pattern) 627 | if not data then 628 | if msg == "timeout" then 629 | -- Wait for more data 630 | coroutine.yield(true) 631 | else 632 | -- Disconnected -- yielding nil means we're done 633 | coroutine.yield(nil) 634 | end 635 | else 636 | return data 637 | end 638 | end 639 | end 640 | 641 | 642 | function lovebird.send(client, data) 643 | local idx = 1 644 | while idx < #data do 645 | local res, msg = client:send(data, idx) 646 | if not res and msg == "closed" then 647 | -- Handle disconnect 648 | coroutine.yield(nil) 649 | else 650 | idx = idx + res 651 | coroutine.yield(true) 652 | end 653 | end 654 | end 655 | 656 | 657 | function lovebird.onconnect(client) 658 | -- Create request table 659 | local requestptn = "(%S*)%s*(%S*)%s*(%S*)" 660 | local req = {} 661 | req.socket = client 662 | req.addr, req.port = client:getsockname() 663 | req.request = lovebird.receive(client, "*l") 664 | req.method, req.url, req.proto = req.request:match(requestptn) 665 | req.headers = {} 666 | while 1 do 667 | local line, msg = lovebird.receive(client, "*l") 668 | if not line or #line == 0 then break end 669 | local k, v = line:match("(.-):%s*(.*)$") 670 | req.headers[k] = v 671 | end 672 | if req.headers["Content-Length"] then 673 | req.body = lovebird.receive(client, req.headers["Content-Length"]) 674 | end 675 | -- Parse body 676 | req.parsedbody = {} 677 | if req.body then 678 | for k, v in req.body:gmatch("([^&]-)=([^&^#]*)") do 679 | req.parsedbody[k] = lovebird.unescape(v) 680 | end 681 | end 682 | -- Parse request line's url 683 | req.parsedurl = lovebird.parseurl(req.url) 684 | -- Handle request; get data to send and send 685 | local data = lovebird.onrequest(req) 686 | lovebird.send(client, data) 687 | -- Clear up 688 | client:close() 689 | end 690 | 691 | 692 | function lovebird.update() 693 | if not lovebird.inited then lovebird.init() end 694 | -- Handle new connections 695 | while 1 do 696 | -- Accept new connections 697 | local client = lovebird.server:accept() 698 | if not client then break end 699 | client:settimeout(0) 700 | local addr = client:getsockname() 701 | if lovebird.checkwhitelist(addr) then 702 | -- Connection okay -- create and add coroutine to set 703 | local conn = coroutine.wrap(function() 704 | xpcall(function() lovebird.onconnect(client) end, function() end) 705 | end) 706 | lovebird.connections[conn] = true 707 | else 708 | -- Reject connection not on whitelist 709 | lovebird.trace("got non-whitelisted connection attempt: ", addr) 710 | client:close() 711 | end 712 | end 713 | -- Handle existing connections 714 | for conn in pairs(lovebird.connections) do 715 | -- Resume coroutine, remove if it has finished 716 | local status = conn() 717 | if status == nil then 718 | lovebird.connections[conn] = nil 719 | end 720 | end 721 | end 722 | 723 | 724 | return lovebird 725 | -------------------------------------------------------------------------------- /main.lua: -------------------------------------------------------------------------------- 1 | isomap = require ("isomap") 2 | function love.load() 3 | --Variables 4 | x = 0 5 | y = 0 6 | zoomL = 1 7 | zoom = 1 8 | 9 | --Set background to deep blue 10 | love.graphics.setBackgroundColor(0, 0, 69) 11 | love.graphics.setDefaultFilter("linear", "linear", 8) 12 | 13 | 14 | --Decode JSON map file 15 | isomap.decodeJson("JSONMap.json") 16 | 17 | --Generate map from JSON file (loads assets and creates tables) 18 | isomap.generatePlayField() 19 | end 20 | 21 | function love.update(dt) 22 | require("lovebird").update() 23 | if love.keyboard.isDown("left") then x = x + 900*dt end 24 | if love.keyboard.isDown("right") then x = x - 900*dt end 25 | if love.keyboard.isDown("up") then y = y+900*dt end 26 | if love.keyboard.isDown("down") then y = y-900*dt end 27 | zoomL = lerp(zoomL, zoom, 0.05*(dt*300)) 28 | end 29 | 30 | function love.draw() 31 | isomap.drawGround(x, y, zoomL) 32 | isomap.drawObjects(x, y, zoomL) 33 | info = love.graphics.getStats() 34 | love.graphics.print("FPS: "..love.timer.getFPS()) 35 | love.graphics.print("Draw calls: "..info.drawcalls, 0, 12) 36 | love.graphics.print("Texture memory: "..((info.texturememory/1024)/1024).."mb", 0, 24) 37 | love.graphics.print("Zoom level: "..zoom, 0, 36) 38 | love.graphics.print("X: "..math.floor(x).." Y: "..math.floor(y), 0, 48) 39 | end 40 | 41 | function love.wheelmoved(x, y) 42 | if y > 0 then 43 | zoom = zoom + 0.1 44 | elseif y < 0 then 45 | zoom = zoom - 0.1 46 | end 47 | 48 | if zoom < 0.1 then zoom = 0.1 end 49 | end 50 | 51 | function lerp(a, b, rate) --EMPLOYEE OF THE MONTH 52 | local result = (1-rate)*a + rate*b 53 | return result 54 | end 55 | -------------------------------------------------------------------------------- /minimalDemo.lua: -------------------------------------------------------------------------------- 1 | isomap = require ("isomap") 2 | function love.load() 3 | --Variables 4 | x = 0 5 | y = 0 6 | 7 | --Set background to deep blue 8 | love.graphics.setBackgroundColor(0, 0, 69) 9 | 10 | --Decode JSON map file 11 | isomap.decodeJson("JSONMap.json") 12 | 13 | --Generate map from JSON file (loads assets and creates tables) 14 | isomap.generatePlayField() 15 | end 16 | 17 | function love.update(dt) 18 | --Get player input so map moves around. 19 | if love.keyboard.isDown("left") then x = x + 900*dt end 20 | if love.keyboard.isDown("right") then x = x - 900*dt end 21 | if love.keyboard.isDown("up") then y = y+900*dt end 22 | if love.keyboard.isDown("down") then y = y-900*dt end 23 | end 24 | 25 | function love.draw() 26 | isomap.draw(x, y, 1) 27 | end 28 | -------------------------------------------------------------------------------- /props/pixelTree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/props/pixelTree.png -------------------------------------------------------------------------------- /props/praca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/props/praca.png -------------------------------------------------------------------------------- /textures/DirtTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/DirtTile.png -------------------------------------------------------------------------------- /textures/GrassTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/GrassTile.png -------------------------------------------------------------------------------- /textures/SelectTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/SelectTile.png -------------------------------------------------------------------------------- /textures/WaterTile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/WaterTile.png -------------------------------------------------------------------------------- /textures/ls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/ls.png -------------------------------------------------------------------------------- /textures/rd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/rd.png -------------------------------------------------------------------------------- /textures/rdl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/rdl.png -------------------------------------------------------------------------------- /textures/rdr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/rdr.png -------------------------------------------------------------------------------- /textures/rdrd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/rdrd.png -------------------------------------------------------------------------------- /textures/rdv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/rdv.png -------------------------------------------------------------------------------- /textures/water.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sulunia/isomap-love2d/a08498efef310289617411b8bafb19f36331313a/textures/water.png --------------------------------------------------------------------------------