├── main.lua ├── ppfov.lua ├── readme.txt ├── rotLove ├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── doc │ ├── index.html │ ├── ldoc.css │ └── modules │ │ ├── ROT.Color.html │ │ ├── ROT.DijkstraMap.html │ │ ├── ROT.Display.html │ │ ├── ROT.EventQueue.html │ │ ├── ROT.FOV.Bresenham.html │ │ ├── ROT.FOV.Precise.html │ │ ├── ROT.Lighting.html │ │ ├── ROT.Map.Arena.html │ │ ├── ROT.Map.Brogue.html │ │ ├── ROT.Map.BrogueRoom.html │ │ ├── ROT.Map.Cellular.html │ │ ├── ROT.Map.Corridor.html │ │ ├── ROT.Map.Digger.html │ │ ├── ROT.Map.DividedMaze.html │ │ ├── ROT.Map.Dungeon.html │ │ ├── ROT.Map.EllerMaze.html │ │ ├── ROT.Map.IceyMaze.html │ │ ├── ROT.Map.Rogue.html │ │ ├── ROT.Map.Room.html │ │ ├── ROT.Map.Uniform.html │ │ ├── ROT.Noise.Simplex.html │ │ ├── ROT.Path.AStar.html │ │ ├── ROT.Path.Dijkstra.html │ │ ├── ROT.RNG.LCG.html │ │ ├── ROT.RNG.MWC.html │ │ ├── ROT.RNG.Twister.html │ │ ├── ROT.RNG.html │ │ ├── ROT.Scheduler.Action.html │ │ ├── ROT.Scheduler.Simple.html │ │ ├── ROT.Scheduler.Speed.html │ │ ├── ROT.Scheduler.html │ │ ├── ROT.StringGenerator.html │ │ └── ROT.TextDisplay.html ├── rotLove │ ├── img │ │ └── cp437.png │ └── rotLove.lua └── src │ ├── action.lua │ ├── arena.lua │ ├── astar.lua │ ├── bresenham.lua │ ├── brogue.lua │ ├── brogueRoom.lua │ ├── cellular.lua │ ├── color.lua │ ├── corridor.lua │ ├── digger.lua │ ├── dijkstra.lua │ ├── dijkstraMap.lua │ ├── display.lua │ ├── dividedMaze.lua │ ├── dungeon.lua │ ├── ellerMaze.lua │ ├── engine.lua │ ├── eventQueue.lua │ ├── feature.lua │ ├── fov.lua │ ├── iceyMaze.lua │ ├── img │ └── cp437.png │ ├── lcg.lua │ ├── lighting.lua │ ├── line.lua │ ├── map.lua │ ├── mwc.lua │ ├── newFuncs.lua │ ├── noise.lua │ ├── path.lua │ ├── point.lua │ ├── precise.lua │ ├── rng.lua │ ├── rogue.lua │ ├── room.lua │ ├── rot.lua │ ├── scheduler.lua │ ├── simple.lua │ ├── simplex.lua │ ├── speed.lua │ ├── stringGenerator.lua │ ├── textDisplay.lua │ ├── twister.lua │ ├── uniform.lua │ └── vendor │ └── 30log.lua └── rsfov.lua /main.lua: -------------------------------------------------------------------------------- 1 | rot = require 'rotLove/rotLove/rotLove' 2 | fov_list = { 3 | {require 'ppfov','precise permissive'}, 4 | {require 'rsfov','recursive shadowcast'}, 5 | } 6 | 7 | fov_index = 1 8 | fov = fov_list[fov_index][1] 9 | 10 | tw,th = 8,8 11 | px,py = 1,1 12 | radius = 20 13 | radius_type= 'square' 14 | perm = 5 15 | angle = 0 16 | angle_size = 360 17 | delta = 5 18 | show_help = true 19 | width = 98 20 | height = 60 21 | 22 | run_symmetry_test = false 23 | fail_visible = {} 24 | 25 | function generateMap() 26 | bmap = rot.Map.Cellular(width,height) 27 | bmap:randomize(0.35) 28 | map = {} 29 | 30 | bmap:create(function(x,y,type) 31 | map[x] = map[x] or {} 32 | map[x][y] = type 33 | end,false) 34 | 35 | repeat 36 | px,py = math.random(1,width),math.random(1,height) 37 | until map[px][py] == 0 38 | end 39 | 40 | function generateVisible() 41 | visible = {} 42 | 43 | local isTransparent = function(x,y) 44 | return map[x] and map[x][y] == 0 45 | end 46 | 47 | local onVisible = function(x,y) 48 | local dx,dy = x-px,y-py 49 | if (dx*dx + dy*dy) > radius*radius + radius and radius_type == 'circle' then 50 | return 51 | end 52 | 53 | visible[x] = visible[x] or {} 54 | visible[x][y] = 1 55 | end 56 | 57 | fov(px,py,radius,isTransparent,onVisible,math.rad(angle-angle_size/2),math.rad(angle+angle_size/2),perm) 58 | 59 | if run_symmetry_test then 60 | local ex,ey = 0,0 61 | fail_visible = {} 62 | 63 | local onEnemyVision = function(x,y) 64 | local dx,dy = x-ex,y-ey 65 | if (dx*dx + dy*dy) > radius*radius + radius and radius_type == 'circle' then 66 | return 67 | end 68 | 69 | enemyVision[x] = enemyVision[x] or {} 70 | enemyVision[x][y] = 1 71 | end 72 | 73 | for x,t in pairs(visible) do 74 | for y,vis in pairs(t) do 75 | enemyVision = {} 76 | ex,ey = x,y 77 | fov(x,y,radius,isTransparent,onEnemyVision,nil,nil,perm) 78 | if not (enemyVision[px] and enemyVision[px][py]) then 79 | fail_visible[x] = fail_visible[x] or {} 80 | fail_visible[x][y] = 1 81 | end 82 | end 83 | end 84 | end 85 | end 86 | 87 | function love.load() 88 | math.randomseed(os.time()) 89 | generateMap() 90 | generateVisible() 91 | 92 | if love.keyboard.setKeyRepeat then 93 | love.keyboard.setKeyRepeat(0.3,0.1) 94 | end 95 | end 96 | 97 | function love.keypressed(k) 98 | local dx,dy = 0,0 99 | if k == 'f1' then 100 | show_help = not show_help 101 | end 102 | if k == 'f2' then 103 | run_symmetry_test = not run_symmetry_test 104 | end 105 | if k == 'f3' then 106 | fov_index = fov_index + 1 107 | if fov_index > #fov_list then fov_index = 1 end 108 | fov = fov_list[fov_index][1] 109 | end 110 | if k == 'tab' then 111 | radius_type = radius_type == 'circle' and 'square' or 'circle' 112 | end 113 | if k == '1' then 114 | radius = math.max(0,radius-1) 115 | end 116 | if k == '2' then 117 | radius = radius+1 118 | end 119 | if k == 'a' then 120 | angle = angle - delta 121 | end 122 | if k == 'd' then 123 | angle = angle + delta 124 | end 125 | if k == 's' then 126 | angle_size = angle_size - delta 127 | end 128 | if k == 'w' then 129 | angle_size = angle_size + delta 130 | end 131 | if k == 'kp4' or k == 'left' then 132 | dx = -1 133 | end 134 | if k == 'kp6' or k == 'right' then 135 | dx = 1 136 | end 137 | if k == 'kp8' or k == 'up' then 138 | dy = -1 139 | end 140 | if k == 'kp2' or k == 'down' then 141 | dy = 1 142 | end 143 | if k == 'kp1' then 144 | dx,dy = -1,1 145 | end 146 | if k == 'kp3' then 147 | dx,dy = 1,1 148 | end 149 | if k == 'kp7' then 150 | dx,dy = -1,-1 151 | end 152 | if k == 'kp9' then 153 | dx,dy = 1,-1 154 | end 155 | if k == 'insert' then 156 | map[px][py] = 1 157 | end 158 | if k == '+' or k == 'kp+' then 159 | perm = perm + 1 160 | perm = math.min(math.max(0,perm),10) 161 | end 162 | if k == '-' or k == 'kp-' then 163 | perm = perm - 1 164 | perm = math.min(math.max(0,perm),10) 165 | end 166 | if k == 'delete' then 167 | map[px-1][py] = 0 168 | map[px+1][py] = 0 169 | map[px][py-1] = 0 170 | map[px][py+1] = 0 171 | end 172 | if k == ' ' then generateMap() end 173 | 174 | if map[px+dx] and map[px+dx][py+dy] then 175 | px,py = px+dx,py+dy 176 | end 177 | 178 | angle = angle % 360 179 | angle_size = math.max(math.min(angle_size,360),0) 180 | 181 | generateVisible() 182 | end 183 | 184 | function love.draw() 185 | for x = 1,width do 186 | local col = map[x] 187 | for y = 1,height do 188 | -- draw map 189 | if map[x] and map[x][y] == 1 then 190 | love.graphics.setColor(0,0,0) 191 | else 192 | love.graphics.setColor(64,64,64) 193 | end 194 | love.graphics.rectangle('fill',x*tw,y*th,tw,th) 195 | 196 | -- vision color overlay 197 | if visible[x] and visible[x][y] == 1 then 198 | local dx,dy = x-px,y-py 199 | if map[x][y] == 1 then 200 | love.graphics.setColor(255,255,0,255) 201 | else 202 | love.graphics.setColor(255,255,0,64) 203 | end 204 | -- non-symmetric cell color overlay 205 | if run_symmetry_test and fail_visible[x] and fail_visible[x][y] then 206 | love.graphics.setColor(255,0,0) 207 | end 208 | end 209 | love.graphics.rectangle('fill',x*tw,y*th,tw,th) 210 | end 211 | end 212 | 213 | -- draw player 214 | love.graphics.setColor(0,255,0) 215 | love.graphics.rectangle('fill',px*tw,py*th,tw,th) 216 | 217 | if show_help then 218 | local t = { 219 | 'Press f1 to toggle help', 220 | 'Press f2 to enable symmetry test: '..tostring(run_symmetry_test), 221 | 'Press f3 to switch algorithm: '..fov_list[fov_index][2], 222 | 'Permissive level: '..perm, 223 | 'Press +/- to change permissiveness', 224 | 'Press space to randomize', 225 | 'Press insert/delete to insert/dig blocks', 226 | 'Press arrow keys or numpad to move', 227 | 'Press 1/2 to decrease/increase FOV radius: '..radius, 228 | 'Press tab to toggle FOV type: '..radius_type, 229 | 'Press a/d to change viewing angle: '..angle, 230 | 'Press s/w to change cone size: '..angle_size, 231 | } 232 | 233 | love.graphics.setColor(255,255,255) 234 | love.graphics.print(table.concat(t,'\n'),0,0) 235 | end 236 | end -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | Field of view algorithms in Lua. 2 | 3 | Two algorithms are available: 4 | -Recursive shadowcasting 5 | -Precise permissive 6 | 7 | The demo requires LOVE. 8 | 9 | Example code: 10 | ```````````````````````````````````````````````````````````````````````` 11 | fov = require 'fov' 12 | 13 | -- Required callbacks: 14 | function isTransparent(x,y) 15 | -- return true if the cell is non-blocking 16 | end 17 | 18 | function onVisible(x,y) 19 | -- gets called when a square is visible 20 | end 21 | 22 | -- Required: 23 | radius = 5 -- sight radius 24 | px,py = 0,0 -- position of light origin 25 | 26 | -- Optional: 27 | start_angle = 0 -- starting angle for FOV arc 28 | last_angle = math.pi*2 -- last angle for FOV arc 29 | -- default: 360 degrees FOV 30 | 31 | permissiveness= 10 -- 0-10, 10 being perfectly symmetric FOV 32 | -- default: 10 33 | -- not available for Recursive Shadowcasting 34 | 35 | -- Calculate fov: 36 | fov(px,py,radius,isTransparent,onVisible, 37 | start_angle,last_angle,permissiveness) 38 | ```````````````````````````````````````````````````````````````````````` 39 | 40 | More information can be found by searching: 41 | FOV using recursive shadowcasting - Björn Bergström 42 | Precise permissive field of view - Jonathon Duerig -------------------------------------------------------------------------------- /rotLove/.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /rotLove/.gitignore: -------------------------------------------------------------------------------- 1 | Demos/ 2 | *.js 3 | config.ld 4 | ################# 5 | ## Eclipse 6 | ################# 7 | 8 | *.pydevproject 9 | .project 10 | .metadata 11 | bin/ 12 | tmp/ 13 | *.tmp 14 | *.bak 15 | *.swp 16 | *~.nib 17 | local.properties 18 | .classpath 19 | .settings/ 20 | .loadpath 21 | 22 | # External tool builders 23 | .externalToolBuilders/ 24 | 25 | # Locally stored "Eclipse launch configurations" 26 | *.launch 27 | 28 | # CDT-specific 29 | .cproject 30 | 31 | # PDT-specific 32 | .buildpath 33 | 34 | 35 | ################# 36 | ## Visual Studio 37 | ################# 38 | 39 | ## Ignore Visual Studio temporary files, build results, and 40 | ## files generated by popular Visual Studio add-ons. 41 | 42 | # User-specific files 43 | *.suo 44 | *.user 45 | *.sln.docstates 46 | 47 | # Build results 48 | [Dd]ebug/ 49 | [Rr]elease/ 50 | *_i.c 51 | *_p.c 52 | *.ilk 53 | *.meta 54 | *.obj 55 | *.pch 56 | *.pdb 57 | *.pgc 58 | *.pgd 59 | *.rsp 60 | *.sbr 61 | *.tlb 62 | *.tli 63 | *.tlh 64 | *.tmp 65 | *.vspscc 66 | .builds 67 | *.dotCover 68 | 69 | ## TODO: If you have NuGet Package Restore enabled, uncomment this 70 | #packages/ 71 | 72 | # Visual C++ cache files 73 | ipch/ 74 | *.aps 75 | *.ncb 76 | *.opensdf 77 | *.sdf 78 | 79 | # Visual Studio profiler 80 | *.psess 81 | *.vsp 82 | 83 | # ReSharper is a .NET coding add-in 84 | _ReSharper* 85 | 86 | # Installshield output folder 87 | [Ee]xpress 88 | 89 | # DocProject is a documentation generator add-in 90 | DocProject/buildhelp/ 91 | DocProject/Help/*.HxT 92 | DocProject/Help/*.HxC 93 | DocProject/Help/*.hhc 94 | DocProject/Help/*.hhk 95 | DocProject/Help/*.hhp 96 | DocProject/Help/Html2 97 | DocProject/Help/html 98 | 99 | # Click-Once directory 100 | publish 101 | 102 | # Others 103 | [Bb]in 104 | [Oo]bj 105 | sql 106 | TestResults 107 | *.Cache 108 | ClientBin 109 | stylecop.* 110 | ~$* 111 | *.dbmdl 112 | Generated_Code #added for RIA/Silverlight projects 113 | 114 | # Backup & report files from converting an old project file to a newer 115 | # Visual Studio version. Backup files are not needed, because we have git ;-) 116 | _UpgradeReport_Files/ 117 | Backup*/ 118 | UpgradeLog*.XML 119 | 120 | 121 | 122 | ############ 123 | ## Windows 124 | ############ 125 | 126 | # Windows image file caches 127 | Thumbs.db 128 | 129 | # Folder config file 130 | Desktop.ini 131 | 132 | 133 | ############# 134 | ## Python 135 | ############# 136 | 137 | *.py[co] 138 | 139 | # Packages 140 | *.egg 141 | *.egg-info 142 | dist 143 | build 144 | eggs 145 | parts 146 | bin 147 | var 148 | sdist 149 | develop-eggs 150 | .installed.cfg 151 | 152 | # Installer logs 153 | pip-log.txt 154 | 155 | # Unit test / coverage reports 156 | .coverage 157 | .tox 158 | 159 | #Translations 160 | *.mo 161 | 162 | #Mr Developer 163 | .mr.developer.cfg 164 | 165 | # Mac crap 166 | .DS_Store 167 | -------------------------------------------------------------------------------- /rotLove/LICENSE.txt: -------------------------------------------------------------------------------- 1 | -- ROT.LOVE -- License 2 | --[[ 3 | rotLove is Copyright (c) 2013 Paul Lewis 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included 14 | in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 17 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | --]] 24 | 25 | -- ROT.JS -- License 26 | 27 | -- While not verbatim, MUCH (most (the vast majority)) of the code involved in this project is derived from rot.js 28 | -- rot.js is a wonderful javascript-based imagining of the libtcod library that many popular 29 | -- roguelike developers use to make their lives easier 30 | -- Keeping in mind the aforementioned derivation, I feel it necessary to include the rot.js license. 31 | --[[-------------------------------------------------------------------------------------- 32 | rot.js is Copyright (c) 2012-now(), Ondrej Zara 33 | All rights reserved. 34 | 35 | Redistribution and use in source and binary forms, with or without modification, 36 | are permitted provided that the following conditions are met: 37 | 38 | * Redistributions of source code must retain the above copyright notice, 39 | this list of conditions and the following disclaimer. 40 | * Redistributions in binary form must reproduce the above copyright notice, 41 | this list of conditions and the following disclaimer in the documentation 42 | and/or other materials provided with the distribution. 43 | * Neither the name of Ondrej Zara nor the names of its contributors may be used 44 | to endorse or promote products derived from this software without specific 45 | prior written permission. 46 | 47 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 48 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 49 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 50 | IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 51 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 52 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 53 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 54 | OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 55 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 56 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 57 | --]]-------------------------------------------------------------------------------------- 58 | 59 | -- Damn near all of this would be laughably disfunctional if not for 30log. 60 | -- 30log is a OOP solution for lua, created by Roland Yonaba. It's excellent for this sort of thing. 61 | --[[--------------------------------------------------------------------------- 62 | 30log is Copyright (c) 2012 Roland Yonaba 63 | 64 | Permission is hereby granted, free of charge, to any person obtaining a 65 | copy of this software and associated documentation files (the 66 | "Software"), to deal in the Software without restriction, including 67 | without limitation the rights to use, copy, modify, merge, publish, 68 | distribute, sublicense, and/or sell copies of the Software, and to 69 | permit persons to whom the Software is furnished to do so, subject to 70 | the following conditions: 71 | 72 | The above copyright notice and this permission notice shall be included 73 | in all copies or substantial portions of the Software. 74 | 75 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 76 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 77 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 78 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 79 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 80 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 81 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 82 | --]]--------------------------------------------------------------------------- 83 | 84 | -- ROT.RNG includes code from RandomLua (that is, it's basically RandomLua plus some stuff I added.) 85 | -- Following is the RandomLua license 86 | --[[------------------------------------ 87 | RandomLua v0.3.1 88 | Pure Lua Pseudo-Random Numbers Generator 89 | Under the MIT license. 90 | copyright(c) 2011 linux-man 91 | --]]------------------------------------ 92 | -------------------------------------------------------------------------------- /rotLove/README.md: -------------------------------------------------------------------------------- 1 | RogueLike Toolkit in Love 2 | ========= 3 | Bringing [rot.js](http://ondras.github.io/rot.js/hp/) functionality to Love2D. The only modules that require Love2D are the display modules. 4 | 5 | See [this page](http://paulofmandown.github.io/rotLove/) for a quick and dirty run down of all the functionality provided. 6 | 7 | Included: 8 | ``` 9 | * Display - via [rlLove](https://github.com/paulofmandown/rlLove), only supports cp437 emulation 10 | rather than full font support. 11 | * TextDisplay - Text based display, accepts supplied fonts 12 | * RNG - via [RandmLua](http://love2d.org/forums/viewtopic.php?f=5&t=3424). 13 | Multiply With Carry, Linear congruential generator, and Mersenne Twister. 14 | Extended with set/getState methods. 15 | * StringGenerator - Direct Port from [rot.js](http://ondras.github.io/rot.js/hp/) 16 | * Map - Arena, Divided/Icey/Eller Maze, Digger/Uniform/Rogue* Dungeons. 17 | Ported from [rot.js](http://ondras.github.io/rot.js/hp/). 18 | * Noise Generator - Simplex Noise 19 | * FOV - Bresenham Line based Ray Casting, Precise Shadow Casting 20 | * Color - 147 Predefined colors; generate valid colors from string; add, multiply, or interpolate colors; 21 | generate a random color from a reference and set of standard deviations. 22 | (straight port from [rot.js](http://ondras.github.io/rot.js/hp/)) 23 | * Path Finding - Dijkstra and AStar pathfinding ported from [rot.js](http://ondras.github.io/rot.js/hp/). 24 | * Lighting - compute light emission and blending, ported from [rot.js](http://ondras.github.io/rot.js/hp/). 25 | ``` 26 | Getting started 27 | ========== 28 | `git clone git://github.com/paulofmandown/rotLove.git` 29 | 30 | Add the rotLove folder/directory to your project and require the rotLove file. 31 | ```lua 32 | ROT=require 'rotLove/rotLove' 33 | function love.load() 34 | f=ROT.Display() 35 | f:writeCenter('You did it!', math.floor(f:getHeight()/2)) 36 | end 37 | function love.draw() f:draw() end 38 | ``` 39 | 40 | Demos Folder 41 | ========== 42 | In Demos/main.lua (you'll want the demos branch), uncomment the line for the demo you'd like to see. 43 | 44 | Then, from the Demos directory in the shell, run `love .` 45 | -------------------------------------------------------------------------------- /rotLove/doc/ldoc.css: -------------------------------------------------------------------------------- 1 | /* BEGIN RESET 2 | 3 | Copyright (c) 2010, Yahoo! Inc. All rights reserved. 4 | Code licensed under the BSD License: 5 | http://developer.yahoo.com/yui/license.html 6 | version: 2.8.2r1 7 | */ 8 | html { 9 | color: #000; 10 | background: #FFF; 11 | } 12 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,button,textarea,p,blockquote,th,td { 13 | margin: 0; 14 | padding: 0; 15 | } 16 | table { 17 | border-collapse: collapse; 18 | border-spacing: 0; 19 | } 20 | fieldset,img { 21 | border: 0; 22 | } 23 | address,caption,cite,code,dfn,em,strong,th,var,optgroup { 24 | font-style: inherit; 25 | font-weight: inherit; 26 | } 27 | del,ins { 28 | text-decoration: none; 29 | } 30 | li { 31 | list-style: disc; 32 | margin-left: 20px; 33 | } 34 | caption,th { 35 | text-align: left; 36 | } 37 | h1,h2,h3,h4,h5,h6 { 38 | font-size: 100%; 39 | font-weight: bold; 40 | } 41 | q:before,q:after { 42 | content: ''; 43 | } 44 | abbr,acronym { 45 | border: 0; 46 | font-variant: normal; 47 | } 48 | sup { 49 | vertical-align: baseline; 50 | } 51 | sub { 52 | vertical-align: baseline; 53 | } 54 | legend { 55 | color: #000; 56 | } 57 | input,button,textarea,select,optgroup,option { 58 | font-family: inherit; 59 | font-size: inherit; 60 | font-style: inherit; 61 | font-weight: inherit; 62 | } 63 | input,button,textarea,select {*font-size:100%; 64 | } 65 | /* END RESET */ 66 | 67 | body { 68 | margin-left: 1em; 69 | margin-right: 1em; 70 | font-family: arial, helvetica, geneva, sans-serif; 71 | background-color: #ffffff; margin: 0px; 72 | } 73 | 74 | code, tt { font-family: monospace; } 75 | span.parameter { font-family:monospace; } 76 | span.parameter:after { content:":"; } 77 | span.types:before { content:"("; } 78 | span.types:after { content:")"; } 79 | .type { font-weight: bold; font-style:italic } 80 | 81 | body, p, td, th { font-size: .95em; line-height: 1.2em;} 82 | 83 | p, ul { margin: 10px 0 0 0px;} 84 | 85 | strong { font-weight: bold;} 86 | 87 | em { font-style: italic;} 88 | 89 | h1 { 90 | font-size: 1.5em; 91 | margin: 0 0 20px 0; 92 | } 93 | h2, h3, h4 { margin: 15px 0 10px 0; } 94 | h2 { font-size: 1.25em; } 95 | h3 { font-size: 1.15em; } 96 | h4 { font-size: 1.06em; } 97 | 98 | a:link { font-weight: bold; color: #004080; text-decoration: none; } 99 | a:visited { font-weight: bold; color: #006699; text-decoration: none; } 100 | a:link:hover { text-decoration: underline; } 101 | 102 | hr { 103 | color:#cccccc; 104 | background: #00007f; 105 | height: 1px; 106 | } 107 | 108 | blockquote { margin-left: 3em; } 109 | 110 | ul { list-style-type: disc; } 111 | 112 | p.name { 113 | font-family: "Andale Mono", monospace; 114 | padding-top: 1em; 115 | } 116 | 117 | pre.example { 118 | background-color: rgb(245, 245, 245); 119 | border: 1px solid silver; 120 | padding: 10px; 121 | margin: 10px 0 10px 0; 122 | font-family: "Andale Mono", monospace; 123 | font-size: .85em; 124 | } 125 | 126 | pre { 127 | background-color: rgb(245, 245, 245); 128 | border: 1px solid silver; 129 | padding: 10px; 130 | margin: 10px 0 10px 0; 131 | overflow: auto; 132 | font-family: "Andale Mono", monospace; 133 | } 134 | 135 | 136 | table.index { border: 1px #00007f; } 137 | table.index td { text-align: left; vertical-align: top; } 138 | 139 | #container { 140 | margin-left: 1em; 141 | margin-right: 1em; 142 | background-color: #f0f0f0; 143 | } 144 | 145 | #product { 146 | text-align: center; 147 | border-bottom: 1px solid #cccccc; 148 | background-color: #ffffff; 149 | } 150 | 151 | #product big { 152 | font-size: 2em; 153 | } 154 | 155 | #main { 156 | background-color: #f0f0f0; 157 | border-left: 2px solid #cccccc; 158 | } 159 | 160 | #navigation { 161 | float: left; 162 | width: 18em; 163 | vertical-align: top; 164 | background-color: #f0f0f0; 165 | overflow: visible; 166 | } 167 | 168 | #navigation h2 { 169 | background-color:#e7e7e7; 170 | font-size:1.1em; 171 | color:#000000; 172 | text-align: left; 173 | padding:0.2em; 174 | border-top:1px solid #dddddd; 175 | border-bottom:1px solid #dddddd; 176 | } 177 | 178 | #navigation ul 179 | { 180 | font-size:1em; 181 | list-style-type: none; 182 | margin: 1px 1px 10px 1px; 183 | } 184 | 185 | #navigation li { 186 | text-indent: -1em; 187 | display: block; 188 | margin: 3px 0px 0px 22px; 189 | } 190 | 191 | #navigation li li a { 192 | margin: 0px 3px 0px -1em; 193 | } 194 | 195 | #content { 196 | margin-left: 18em; 197 | padding: 1em; 198 | width: 700px; 199 | border-left: 2px solid #cccccc; 200 | border-right: 2px solid #cccccc; 201 | background-color: #ffffff; 202 | } 203 | 204 | #about { 205 | clear: both; 206 | padding: 5px; 207 | border-top: 2px solid #cccccc; 208 | background-color: #ffffff; 209 | } 210 | 211 | @media print { 212 | body { 213 | font: 12pt "Times New Roman", "TimeNR", Times, serif; 214 | } 215 | a { font-weight: bold; color: #004080; text-decoration: underline; } 216 | 217 | #main { 218 | background-color: #ffffff; 219 | border-left: 0px; 220 | } 221 | 222 | #container { 223 | margin-left: 2%; 224 | margin-right: 2%; 225 | background-color: #ffffff; 226 | } 227 | 228 | #content { 229 | padding: 1em; 230 | background-color: #ffffff; 231 | } 232 | 233 | #navigation { 234 | display: none; 235 | } 236 | pre.example { 237 | font-family: "Andale Mono", monospace; 238 | font-size: 10pt; 239 | page-break-inside: avoid; 240 | } 241 | } 242 | 243 | table.module_list { 244 | border-width: 1px; 245 | border-style: solid; 246 | border-color: #cccccc; 247 | border-collapse: collapse; 248 | } 249 | table.module_list td { 250 | border-width: 1px; 251 | padding: 3px; 252 | border-style: solid; 253 | border-color: #cccccc; 254 | } 255 | table.module_list td.name { background-color: #f0f0f0; min-width: 200px; } 256 | table.module_list td.summary { width: 100%; } 257 | 258 | 259 | table.function_list { 260 | border-width: 1px; 261 | border-style: solid; 262 | border-color: #cccccc; 263 | border-collapse: collapse; 264 | } 265 | table.function_list td { 266 | border-width: 1px; 267 | padding: 3px; 268 | border-style: solid; 269 | border-color: #cccccc; 270 | } 271 | table.function_list td.name { background-color: #f0f0f0; min-width: 200px; } 272 | table.function_list td.summary { width: 100%; } 273 | 274 | ul.nowrap { 275 | overflow:auto; 276 | white-space:nowrap; 277 | } 278 | 279 | dl.table dt, dl.function dt {border-top: 1px solid #ccc; padding-top: 1em;} 280 | dl.table dd, dl.function dd {padding-bottom: 1em; margin: 10px 0 0 20px;} 281 | dl.table h3, dl.function h3 {font-size: .95em;} 282 | 283 | /* stop sublists from having initial vertical space */ 284 | ul ul { margin-top: 0px; } 285 | ol ul { margin-top: 0px; } 286 | ol ol { margin-top: 0px; } 287 | ul ol { margin-top: 0px; } 288 | 289 | /* styles for prettification of source */ 290 | pre .comment { color: #558817; } 291 | pre .constant { color: #a8660d; } 292 | pre .escape { color: #844631; } 293 | pre .keyword { color: #2239a8; font-weight: bold; } 294 | pre .library { color: #0e7c6b; } 295 | pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } 296 | pre .string { color: #a8660d; } 297 | pre .number { color: #f8660d; } 298 | pre .operator { color: #2239a8; font-weight: bold; } 299 | pre .preprocessor, pre .prepro { color: #a33243; } 300 | pre .global { color: #800080; } 301 | pre .prompt { color: #558817; } 302 | pre .url { color: #272fc2; text-decoration: underline; } 303 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.EventQueue.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.EventQueue

94 | 95 |

Stores and retrieves events based on time.

96 |

97 | 98 | 99 |

Functions

100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 |
EventQueue:getTime ()Get Time.
EventQueue:clear ()Clear.
EventQueue:add (event, time)Add.
EventQueue:get ()Get.
EventQueue:remove (event)Remove.
122 | 123 |
124 |
125 | 126 | 127 |

Functions

128 |
129 |
130 | 131 | EventQueue:getTime () 132 |
133 |
134 | Get Time. 135 | Get time counted since start 136 | 137 | 138 |

Returns:

139 |
    140 | 141 | int 142 | elapsed time 143 |
144 | 145 | 146 | 147 | 148 |
149 |
150 | 151 | EventQueue:clear () 152 |
153 |
154 | Clear. 155 | Remove all events from queue 156 | 157 | 158 |

Returns:

159 |
    160 | 161 | ROT.EventQueue 162 | self 163 |
164 | 165 | 166 | 167 | 168 |
169 |
170 | 171 | EventQueue:add (event, time) 172 |
173 |
174 | Add. 175 | Add an event 176 | 177 |

Parameters:

178 |
    179 |
  • event 180 | any 181 | Any object 182 |
  • 183 |
  • time 184 | int 185 | The number of time units that will elapse before this event is returned 186 |
  • 187 |
188 | 189 | 190 | 191 | 192 | 193 |
194 |
195 | 196 | EventQueue:get () 197 |
198 |
199 | Get. 200 | Get the next event from the queue and advance the appropriate amount time 201 | 202 | 203 |

Returns:

204 |
    205 | 206 | event or nil 207 | The event previously added by .add() or nil if none are queued 208 |
209 | 210 | 211 | 212 | 213 |
214 |
215 | 216 | EventQueue:remove (event) 217 |
218 |
219 | Remove. 220 | Find and remove an event from the queue 221 | 222 |

Parameters:

223 |
    224 |
  • event 225 | any 226 | The previously added event to be removed 227 |
  • 228 |
229 | 230 |

Returns:

231 |
    232 | 233 | boolean 234 | true if an event was removed from the queue 235 |
236 | 237 | 238 | 239 | 240 |
241 |
242 | 243 | 244 |
245 |
246 |
247 | 248 | 249 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.FOV.Precise.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.FOV.Precise

94 | 95 |

Precise Shadowcasting Field of View calculator.

96 |

97 | The Precise shadow casting algorithm developed by Ondřej Žára for rot.js. 98 | See http://roguebasin.roguelikedevelopment.org/index.php?title=Precise_Shadowcasting_in_JavaScript

99 | 100 | 101 |

Functions

102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
Precise:__init (lightPassesCallback, options)Constructor.
Precise:compute (x, y, R, callback)Compute.
112 | 113 |
114 |
115 | 116 | 117 |

Functions

118 |
119 |
120 | 121 | Precise:__init (lightPassesCallback, options) 122 |
123 |
124 | Constructor. 125 | Called with ROT.FOV.Precise:new() 126 | 127 |

Parameters:

128 |
    129 |
  • lightPassesCallback 130 | function 131 | A function with two parameters (x, y) that returns true if a map cell will allow light to pass through 132 |
  • 133 |
  • options Options 134 |
      135 |
    • topology 136 | int 137 | Direction for light movement Accepted values: (4 or 8) 138 |
    • 139 |
    140 |
141 | 142 | 143 | 144 | 145 | 146 |
147 |
148 | 149 | Precise:compute (x, y, R, callback) 150 |
151 |
152 | Compute. 153 | Get visibility from a given point 154 | 155 |

Parameters:

156 |
    157 |
  • x 158 | int 159 | x-position of center of FOV 160 |
  • 161 |
  • y 162 | int 163 | y-position of center of FOV 164 |
  • 165 |
  • R 166 | int 167 | radius of FOV (i.e.: At most, I can see for R cells) 168 |
  • 169 |
  • callback A function that is called for every cell in view. Must accept four parameters. 170 |
      171 |
    • x 172 | int 173 | x-position of cell that is in view 174 |
    • 175 |
    • y 176 | int 177 | y-position of cell that is in view 178 |
    • 179 |
    • r 180 | int 181 | The cell's distance from center of FOV 182 |
    • 183 |
    • visibility 184 | number 185 | The cell's visibility rating (from 0-1). How well can you see this cell? 186 |
    • 187 |
    188 |
189 | 190 | 191 | 192 | 193 | 194 |
195 |
196 | 197 | 198 |
199 |
200 |
201 | 202 | 203 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Map.Arena.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Map.Arena

94 | 95 |

The Arena map generator.

96 |

97 | Generates an arena style map. All cells except for the extreme borders are floors. The borders are walls.

98 | 99 | 100 |

Functions

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
Arena:__init (width, height)Constructor.
Arena:create (callback)Create.
111 | 112 |
113 |
114 | 115 | 116 |

Functions

117 |
118 |
119 | 120 | Arena:__init (width, height) 121 |
122 |
123 | Constructor. 124 | Called with ROT.Map.Arena:new(width, height) 125 | 126 |

Parameters:

127 |
    128 |
  • width 129 | int 130 | Width in cells of the map 131 |
  • 132 |
  • height 133 | int 134 | Height in cells of the map 135 |
  • 136 |
137 | 138 | 139 | 140 | 141 | 142 |
143 |
144 | 145 | Arena:create (callback) 146 |
147 |
148 | Create. 149 | Creates a map. 150 | 151 |

Parameters:

152 |
    153 |
  • callback This function will be called for every cell. It must accept the following parameters: 154 |
      155 |
    • x 156 | int 157 | The x-position of a cell in the map 158 |
    • 159 |
    • y 160 | int 161 | The y-position of a cell in the map 162 |
    • 163 |
    • value 164 | int 165 | A value representing the cell-type. 0==floor, 1==wall 166 |
    • 167 |
    168 |
169 | 170 |

Returns:

171 |
    172 | 173 | ROT.Map.Arena 174 | self 175 |
176 | 177 | 178 | 179 | 180 |
181 |
182 | 183 | 184 |
185 |
186 |
187 | 188 | 189 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Map.DividedMaze.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Map.DividedMaze

94 | 95 |

The Divided Maze Map Generator.

96 |

97 | Recursively divided maze, http://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_division_method

98 | 99 | 100 |

Functions

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
DividedMaze:__init (width, height)Constructor.
DividedMaze:create (callback)Create.
111 | 112 |
113 |
114 | 115 | 116 |

Functions

117 |
118 |
119 | 120 | DividedMaze:__init (width, height) 121 |
122 |
123 | Constructor. 124 | Called with ROT.Map.DividedMaze:new(width, height) 125 | 126 |

Parameters:

127 |
    128 |
  • width 129 | int 130 | Width in cells of the map 131 |
  • 132 |
  • height 133 | int 134 | Height in cells of the map 135 |
  • 136 |
137 | 138 | 139 | 140 | 141 | 142 |
143 |
144 | 145 | DividedMaze:create (callback) 146 |
147 |
148 | Create. 149 | Creates a map. 150 | 151 |

Parameters:

152 |
    153 |
  • callback This function will be called for every cell. It must accept the following parameters: 154 |
      155 |
    • x 156 | int 157 | The x-position of a cell in the map 158 |
    • 159 |
    • y 160 | int 161 | The y-position of a cell in the map 162 |
    • 163 |
    • value 164 | int 165 | A value representing the cell-type. 0==floor, 1==wall 166 |
    • 167 |
    168 |
169 | 170 |

Returns:

171 |
    172 | 173 | ROT.Map.DividedMaze 174 | self 175 |
176 | 177 | 178 | 179 | 180 |
181 |
182 | 183 | 184 |
185 |
186 |
187 | 188 | 189 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Map.Dungeon.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Map.Dungeon

94 | 95 |

The Dungeon-style map Prototype.

96 |

97 | This class is extended by ROT.Map.Digger and ROT.Map.Uniform

98 | 99 | 100 |

Functions

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 110 | 111 | 112 | 113 | 115 | 116 | 117 | 118 | 120 | 121 |
Dungeon:__init (width, height)Constructor.
Dungeon:getRooms ()Get rooms 109 | Get a table of rooms on the map
Dungeon:getDoors ()Get doors 114 | Get a table of doors on the map
Dungeon:getCorridors ()Get corridors 119 | Get a table of corridors on the map
122 | 123 |
124 |
125 | 126 | 127 |

Functions

128 |
129 |
130 | 131 | Dungeon:__init (width, height) 132 |
133 |
134 | Constructor. 135 | Called with ROT.Map.Cellular:new() 136 | 137 |

Parameters:

138 |
    139 |
  • width 140 | int 141 | Width in cells of the map 142 |
  • 143 |
  • height 144 | int 145 | Height in cells of the map 146 |
  • 147 |
148 | 149 | 150 | 151 | 152 | 153 |
154 |
155 | 156 | Dungeon:getRooms () 157 |
158 |
159 | Get rooms 160 | Get a table of rooms on the map 161 | 162 | 163 |

Returns:

164 |
    165 | 166 | table 167 | A table containing objects of the type ROT.Map.Room 168 |
169 | 170 | 171 | 172 | 173 |
174 |
175 | 176 | Dungeon:getDoors () 177 |
178 |
179 | Get doors 180 | Get a table of doors on the map 181 | 182 | 183 |

Returns:

184 |
    185 | 186 | table 187 | A table {{x=int, y=int},...} for doors. 188 |
189 | 190 | 191 | 192 | 193 |
194 |
195 | 196 | Dungeon:getCorridors () 197 |
198 |
199 | Get corridors 200 | Get a table of corridors on the map 201 | 202 | 203 |

Returns:

204 |
    205 | 206 | table 207 | A table containing objects of the type ROT.Map.Corridor 208 |
209 | 210 | 211 | 212 | 213 |
214 |
215 | 216 | 217 |
218 |
219 |
220 | 221 | 222 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Map.EllerMaze.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Map.EllerMaze

94 | 95 |

The Eller Maze Map Generator.

96 |

97 | See http://homepages.cwi.nl/~tromp/maze.html for explanation

98 | 99 | 100 |

Functions

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
EllerMaze:__init (width, height, rng)Constructor.
EllerMaze:create (callback)Create.
111 | 112 |
113 |
114 | 115 | 116 |

Functions

117 |
118 |
119 | 120 | EllerMaze:__init (width, height, rng) 121 |
122 |
123 | Constructor. 124 | Called with ROT.Map.EllerMaze:new(width, height) 125 | 126 |

Parameters:

127 |
    128 |
  • width 129 | int 130 | Width in cells of the map 131 |
  • 132 |
  • height 133 | int 134 | Height in cells of the map 135 |
  • 136 |
  • rng 137 | userdata 138 | Userdata with a .random(self, min, max) function 139 |
  • 140 |
141 | 142 | 143 | 144 | 145 | 146 |
147 |
148 | 149 | EllerMaze:create (callback) 150 |
151 |
152 | Create. 153 | Creates a map. 154 | 155 |

Parameters:

156 |
    157 |
  • callback This function will be called for every cell. It must accept the following parameters: 158 |
      159 |
    • x 160 | int 161 | The x-position of a cell in the map 162 |
    • 163 |
    • y 164 | int 165 | The y-position of a cell in the map 166 |
    • 167 |
    • value 168 | int 169 | A value representing the cell-type. 0==floor, 1==wall 170 |
    • 171 |
    172 |
173 | 174 |

Returns:

175 |
    176 | 177 | ROT.Map.EllerMaze 178 | self 179 |
180 | 181 | 182 | 183 | 184 |
185 |
186 | 187 | 188 |
189 |
190 |
191 | 192 | 193 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Map.IceyMaze.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Map.IceyMaze

94 | 95 |

The Icey Maze Map Generator.

96 |

97 | See http://www.roguebasin.roguelikedevelopment.org/index.php?title=Simple_maze for explanation

98 | 99 | 100 |

Functions

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
IceyMaze:__init (width, height, rng, regularity)Constructor.
IceyMaze:create (callback)Create.
111 | 112 |
113 |
114 | 115 | 116 |

Functions

117 |
118 |
119 | 120 | IceyMaze:__init (width, height, rng, regularity) 121 |
122 |
123 | Constructor. 124 | Called with ROT.Map.IceyMaze:new(width, height, regularity) 125 | 126 |

Parameters:

127 |
    128 |
  • width 129 | int 130 | Width in cells of the map 131 |
  • 132 |
  • height 133 | int 134 | Height in cells of the map 135 |
  • 136 |
  • rng 137 | userdata 138 | Userdata with a .random(self, min, max) function 139 |
  • 140 |
  • regularity 141 | int[opt=0] 142 | A value used to determine the 'randomness' of the map, 0= more random 143 |
  • 144 |
145 | 146 | 147 | 148 | 149 | 150 |
151 |
152 | 153 | IceyMaze:create (callback) 154 |
155 |
156 | Create. 157 | Creates a map. 158 | 159 |

Parameters:

160 |
    161 |
  • callback This function will be called for every cell. It must accept the following parameters: 162 |
      163 |
    • x 164 | int 165 | The x-position of a cell in the map 166 |
    • 167 |
    • y 168 | int 169 | The y-position of a cell in the map 170 |
    • 171 |
    • value 172 | int 173 | A value representing the cell-type. 0==floor, 1==wall 174 |
    • 175 |
    176 |
177 | 178 |

Returns:

179 |
    180 | 181 | ROT.Map.IceyMaze 182 | self 183 |
184 | 185 | 186 | 187 | 188 |
189 |
190 | 191 | 192 |
193 |
194 |
195 | 196 | 197 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Map.Rogue.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Map.Rogue

94 | 95 |

Rogue Map Generator.

96 |

97 | A map generator based on the original Rogue map gen algorithm 98 | See http://kuoi.com/~kamikaze/GameDesign/art07_rogue_dungeon.php

99 | 100 | 101 |

Functions

102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 |
Rogue:__init (width, height[, options], rng)Constructor.
Rogue:create (callback)Create.
112 | 113 |
114 |
115 | 116 | 117 |

Functions

118 |
119 |
120 | 121 | Rogue:__init (width, height[, options], rng) 122 |
123 |
124 | Constructor. 125 | 126 |

Parameters:

127 |
    128 |
  • width 129 | int 130 | Width in cells of the map 131 |
  • 132 |
  • height 133 | int 134 | Height in cells of the map 135 |
  • 136 |
  • options Options 137 |
      138 |
    • cellWidth 139 | int 140 | Number of cells to create on the horizontal (number of rooms horizontally) 141 |
    • 142 |
    • cellHeight 143 | int 144 | Number of cells to create on the vertical (number of rooms vertically) 145 |
    • 146 |
    • roomWidth 147 | int 148 | Room min and max width 149 |
    • 150 |
    • roomHeight 151 | int 152 | Room min and max height 153 |
    • 154 |
    155 |
  • rng 156 | userdata 157 | Userdata with a .random(self, min, max) function 158 |
  • 159 |
160 | 161 | 162 | 163 | 164 | 165 |
166 |
167 | 168 | Rogue:create (callback) 169 |
170 |
171 | Create. 172 | Creates a map. 173 | 174 |

Parameters:

175 |
    176 |
  • callback This function will be called for every cell. It must accept the following parameters: 177 |
      178 |
    • x 179 | int 180 | The x-position of a cell in the map 181 |
    • 182 |
    • y 183 | int 184 | The y-position of a cell in the map 185 |
    • 186 |
    • value 187 | int 188 | A value representing the cell-type. 0==floor, 1==wall 189 |
    • 190 |
    191 |
192 | 193 |

Returns:

194 |
    195 | 196 | ROT.Map.Cellular or nil 197 | self or nil if time limit is reached 198 |
199 | 200 | 201 | 202 | 203 |
204 |
205 | 206 | 207 |
208 |
209 |
210 | 211 | 212 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Noise.Simplex.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Noise.Simplex

94 | 95 |

Simplex Noise Generator.

96 |

97 | Based on a simple 2d implementation of simplex noise by Ondrej Zara 98 | Which is based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java. 99 | Which is based on example code by Stefan Gustavson (stegu@itn.liu.se). 100 | With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu). 101 | Better rank ordering method by Stefan Gustavson in 2012.

102 | 103 | 104 |

Functions

105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 114 | 115 |
Simplex:__init (gradients)Constructor.
Simplex:get (xin, yin)Get noise for a cell 113 | Iterate over this function to retrieve noise values
116 | 117 |
118 |
119 | 120 | 121 |

Functions

122 |
123 |
124 | 125 | Simplex:__init (gradients) 126 |
127 |
128 | Constructor. 129 | 2D simplex noise generator. 130 | 131 |

Parameters:

132 |
    133 |
  • gradients 134 | int 135 | The random values for the noise. 136 |
  • 137 |
138 | 139 | 140 | 141 | 142 | 143 |
144 |
145 | 146 | Simplex:get (xin, yin) 147 |
148 |
149 | Get noise for a cell 150 | Iterate over this function to retrieve noise values 151 | 152 |

Parameters:

153 |
    154 |
  • xin 155 | int 156 | x-position of noise value 157 |
  • 158 |
  • yin 159 | int 160 | y-position of noise value 161 |
  • 162 |
163 | 164 | 165 | 166 | 167 | 168 |
169 |
170 | 171 | 172 |
173 |
174 |
175 | 176 | 177 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Path.AStar.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Path.AStar

94 | 95 |

A* Pathfinding.

96 |

97 | Simplified A* algorithm: all edges have a value of 1

98 | 99 | 100 |

Functions

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
AStar:__init (toX, toY, passableCallback, options)Constructor.
AStar:compute (fromX, fromY, callback)Compute the path from a starting point
111 | 112 |
113 |
114 | 115 | 116 |

Functions

117 |
118 |
119 | 120 | AStar:__init (toX, toY, passableCallback, options) 121 |
122 |
123 | Constructor. 124 | 125 |

Parameters:

126 |
    127 |
  • toX 128 | int 129 | x-position of destination cell 130 |
  • 131 |
  • toY 132 | int 133 | y-position of destination cell 134 |
  • 135 |
  • passableCallback 136 | function 137 | Function with two parameters (x, y) that returns true if the cell at x,y is able to be crossed 138 |
  • 139 |
  • options Options 140 |
      141 |
    • topology 142 | int 143 | Directions for movement Accepted values (4 or 8) 144 | (default 8) 145 |
    • 146 |
    147 |
148 | 149 | 150 | 151 | 152 | 153 |
154 |
155 | 156 | AStar:compute (fromX, fromY, callback) 157 |
158 |
159 | Compute the path from a starting point 160 | 161 |

Parameters:

162 |
    163 |
  • fromX 164 | int 165 | x-position of starting point 166 |
  • 167 |
  • fromY 168 | int 169 | y-position of starting point 170 |
  • 171 |
  • callback 172 | function 173 | Will be called for every path item with arguments "x" and "y" 174 |
  • 175 |
176 | 177 | 178 | 179 | 180 | 181 |
182 |
183 | 184 | 185 |
186 |
187 |
188 | 189 | 190 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.Path.Dijkstra.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.Path.Dijkstra

94 | 95 |

Dijkstra Pathfinding.

96 |

97 | Simplified Dijkstra's algorithm: all edges have a value of 1

98 | 99 | 100 |

Functions

101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 |
Dijkstra:__init (toX, toY, passableCallback, options)Constructor.
Dijkstra:compute (fromX, fromY, callback)Compute the path from a starting point
111 | 112 |
113 |
114 | 115 | 116 |

Functions

117 |
118 |
119 | 120 | Dijkstra:__init (toX, toY, passableCallback, options) 121 |
122 |
123 | Constructor. 124 | 125 |

Parameters:

126 |
    127 |
  • toX 128 | int 129 | x-position of destination cell 130 |
  • 131 |
  • toY 132 | int 133 | y-position of destination cell 134 |
  • 135 |
  • passableCallback 136 | function 137 | Function with two parameters (x, y) that returns true if the cell at x,y is able to be crossed 138 |
  • 139 |
  • options Options 140 |
      141 |
    • topology 142 | int 143 | Directions for movement Accepted values (4 or 8) 144 | (default 8) 145 |
    • 146 |
    147 |
148 | 149 | 150 | 151 | 152 | 153 |
154 |
155 | 156 | Dijkstra:compute (fromX, fromY, callback) 157 |
158 |
159 | Compute the path from a starting point 160 | 161 |

Parameters:

162 |
    163 |
  • fromX 164 | int 165 | x-position of starting point 166 |
  • 167 |
  • fromY 168 | int 169 | y-position of starting point 170 |
  • 171 |
  • callback 172 | function 173 | Will be called for every path item with arguments "x" and "y" 174 |
  • 175 |
176 | 177 | 178 | 179 | 180 | 181 |
182 |
183 | 184 | 185 |
186 |
187 |
188 | 189 | 190 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.RNG.Twister.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.RNG.Twister

94 | 95 |

Mersenne Twister.

96 |

A random number generator based on RandomLua

97 | 98 | 99 |

Functions

100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 113 | 114 | 115 | 116 | 118 | 119 |
Twister:randomseed ([s=os.clock()])Seed.
Twister:random ([a=0[, b=1]])Random.
Twister:getState ()Get current rng state 112 | Returns a table that can be given to the rng to return it to this state.
Twister:setState (stateTable)Set current rng state 117 | used to return an rng to a known/previous state
120 | 121 |
122 |
123 | 124 | 125 |

Functions

126 |
127 |
128 | 129 | Twister:randomseed ([s=os.clock()]) 130 |
131 |
132 | Seed. 133 | seed the rng 134 | 135 |

Parameters:

136 |
    137 |
  • s 138 | number 139 | A number to base the rng from 140 | (default os.clock()) 141 |
  • 142 |
143 | 144 | 145 | 146 | 147 | 148 |
149 |
150 | 151 | Twister:random ([a=0[, b=1]]) 152 |
153 |
154 | Random. 155 | get a random number 156 | 157 |

Parameters:

158 |
    159 |
  • a 160 | int 161 | lower threshold for random numbers 162 | (default 0) 163 |
  • 164 |
  • b 165 | int 166 | upper threshold for random numbers 167 | (default 1) 168 |
  • 169 |
170 | 171 |

Returns:

172 |
    173 | 174 | number 175 | a random number 176 |
177 | 178 | 179 | 180 | 181 |
182 |
183 | 184 | Twister:getState () 185 |
186 |
187 | Get current rng state 188 | Returns a table that can be given to the rng to return it to this state. 189 | Any RNG of the same type will always produce the same values from this state. 190 | 191 | 192 |

Returns:

193 |
    194 | 195 | table 196 | A table that represents the current state of the rng 197 |
198 | 199 | 200 | 201 | 202 |
203 |
204 | 205 | Twister:setState (stateTable) 206 |
207 |
208 | Set current rng state 209 | used to return an rng to a known/previous state 210 | 211 |

Parameters:

212 |
    213 |
  • stateTable 214 | table 215 | The table retrieved from .getState() 216 |
  • 217 |
218 | 219 | 220 | 221 | 222 | 223 |
224 |
225 | 226 | 227 |
228 |
229 |
230 | 231 | 232 | -------------------------------------------------------------------------------- /rotLove/doc/modules/ROT.RNG.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | rotLove Reference 7 | 8 | 21 | 22 | 23 | 24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 | 36 | 37 | 38 | 90 | 91 |
92 | 93 |

Module ROT.RNG

94 | 95 |

The RNG Prototype.

96 |

97 | The base class that is extended by all rng classes

98 | 99 | 100 |

Functions

101 | 102 | 103 | 104 | 105 | 106 |
RNG:seed ()Seed.
107 | 108 |
109 |
110 | 111 | 112 |

Functions

113 |
114 |
115 | 116 | RNG:seed () 117 |
118 |
119 | Seed. 120 | get the host system's time in milliseconds as a positive 32 bit number 121 | 122 | 123 |

Returns:

124 |
    125 | 126 | number 127 |
128 | 129 | 130 | 131 | 132 |
133 |
134 | 135 | 136 |
137 |
138 |
139 | 140 | 141 | -------------------------------------------------------------------------------- /rotLove/rotLove/img/cp437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markandgo/Lua-FOV/0e76592ba33d247266aecb254df30fc0ed05a61d/rotLove/rotLove/img/cp437.png -------------------------------------------------------------------------------- /rotLove/src/action.lua: -------------------------------------------------------------------------------- 1 | --- Action based turn scheduler. 2 | -- @module ROT.Scheduler.Action 3 | 4 | local Action_Path =({...})[1]:gsub("[%.\\/]action$", "") .. '/' 5 | local class =require (Action_Path .. 'vendor/30log') 6 | 7 | local Action= ROT.Scheduler:extends { _defaultDuration, _duration } 8 | Action.__name='Action' 9 | function Action:__init() 10 | Action.super.__init(self) 11 | self._defaultDuration=1 12 | self._duration=self._defaultDuration 13 | end 14 | 15 | --- Add. 16 | -- Add an item to the scheduler. 17 | -- @tparam any item The item that is returned when this turn comes up 18 | -- @tparam boolean repeating If true, when this turn comes up, it will be added to the queue again 19 | -- @tparam[opt=1] int time an initial delay time 20 | -- @treturn ROT.Scheduler.Action self 21 | function Action:add(item, repeating, time) 22 | self._queue:add(item, time and time or self._defaultDuration) 23 | return Action.super.add(self, item, repeating) 24 | end 25 | 26 | --- Clear. 27 | -- empties this scheduler's event queue, no items will be returned by .next() until more are added with .add() 28 | -- @treturn ROT.Scheduler.Action self 29 | function Action:clear() 30 | self._duration = self._defaultDuration 31 | return Action.super.clear(self) 32 | end 33 | 34 | --- Remove. 35 | -- Looks for the next instance of item in the event queue 36 | -- @treturn ROT.Scheduler.Action self 37 | function Action:remove(item) 38 | if item==self._current then self._duration=self._defaultDuration end 39 | return Action.super.remove(self, item) 40 | end 41 | 42 | --- Next. 43 | -- returns the next item based on that item's last action's duration 44 | -- @return item 45 | function Action:next() 46 | if self._current and table.indexOf(self._repeat, self._current)~=0 then 47 | self._queue:add(self._current, self._duration and self._duration or self._defaultDuration) 48 | self._duration=self._defaultDuration 49 | end 50 | return Action.super.next(self) 51 | end 52 | 53 | --- set duration for the active item 54 | -- after calling next() this function defines the duration of that item's action 55 | -- @tparam int time The amount of time that the current item's action should last. 56 | -- @treturn ROT.Scheduler.Action self 57 | function Action:setDuration(time) 58 | if self._current then self._duration=time end 59 | return self 60 | end 61 | 62 | return Action 63 | -------------------------------------------------------------------------------- /rotLove/src/arena.lua: -------------------------------------------------------------------------------- 1 | --- The Arena map generator. 2 | -- Generates an arena style map. All cells except for the extreme borders are floors. The borders are walls. 3 | -- @module ROT.Map.Arena 4 | local Arena_PATH =({...})[1]:gsub("[%.\\/]arena$", "") .. '/' 5 | local class =require (Arena_PATH .. 'vendor/30log') 6 | 7 | local Arena = ROT.Map:extends { } 8 | Arena.__name='Arena' 9 | --- Constructor. 10 | -- Called with ROT.Map.Arena:new(width, height) 11 | -- @tparam int width Width in cells of the map 12 | -- @tparam int height Height in cells of the map 13 | function Arena:__init(width, height) 14 | Arena.super.__init(self, width, height) 15 | end 16 | 17 | --- Create. 18 | -- Creates a map. 19 | -- @tparam function callback This function will be called for every cell. It must accept the following parameters: 20 | -- @tparam int callback.x The x-position of a cell in the map 21 | -- @tparam int callback.y The y-position of a cell in the map 22 | -- @tparam int callback.value A value representing the cell-type. 0==floor, 1==wall 23 | -- @treturn ROT.Map.Arena self 24 | function Arena:create(callback) 25 | local w=self._width 26 | local h=self._height 27 | for i=1,w do 28 | for j=1,h do 29 | local empty= i>1 and j>1 and i0 do 36 | local item=table.remove(self._todo, 1) 37 | if item.x == fromX and item.y == fromY then break end 38 | local neighbors=self:_getNeighbors(item.x, item.y) 39 | 40 | for i=1,#neighbors do 41 | local x = neighbors[i][1] 42 | local y = neighbors[i][2] 43 | if not self._done[x] then self._done[x]={} end 44 | if not self._done[x][y] then 45 | self:_add(x, y, item) 46 | end 47 | end 48 | end 49 | 50 | local item=self._done[self._fromX] and self._done[self._fromX][self._fromY] or nil 51 | if not item then return end 52 | 53 | while item do 54 | callback(tonumber(item.x), tonumber(item.y)) 55 | item=item.prev 56 | end 57 | end 58 | 59 | function AStar:_add(x, y, prev) 60 | local obj={} 61 | obj.x =x 62 | obj.y =y 63 | obj.prev=prev 64 | obj.g =prev and prev.g+1 or 0 65 | obj.h =self:_distance(x, y) 66 | self._done[x][y]=obj 67 | 68 | local f=obj.g+obj.h 69 | 70 | for i=1,#self._todo do 71 | local item=self._todo[i] 72 | if f c 150 | else 151 | return math.abs(x)+math.abs(y)>r 152 | end 153 | end 154 | 155 | return Bresenham 156 | -------------------------------------------------------------------------------- /rotLove/src/cellular.lua: -------------------------------------------------------------------------------- 1 | --- Cellular Automaton Map Generator 2 | -- @module ROT.Map.Cellular 3 | 4 | local Cellular_PATH =({...})[1]:gsub("[%.\\/]cellular$", "") .. '/' 5 | local class =require (Cellular_PATH .. 'vendor/30log') 6 | 7 | local Cellular = ROT.Map:extends { _rng, _options, _map } 8 | Cellular.__name='Cellular' 9 | --- Constructor. 10 | -- Called with ROT.Map.Cellular:new() 11 | -- @tparam int width Width in cells of the map 12 | -- @tparam int height Height in cells of the map 13 | -- @tparam[opt] table options Options 14 | -- @tparam table options.born List of neighbor counts for a new cell to be born in empty space 15 | -- @tparam table options.survive List of neighbor counts for an existing cell to survive 16 | -- @tparam int options.topology Topology. Accepted values: 4, 8 17 | -- @tparam userdata rng Userdata with a .random(self, min, max) function 18 | function Cellular:__init(width, height, options, rng) 19 | assert(ROT, 'must require rot') 20 | Cellular.super.__init(self, width, height) 21 | self._options={ 22 | born ={5,6,7,8}, 23 | survive ={4,5,6,7,8}, 24 | topology=8 25 | } 26 | if options then 27 | for k,v in pairs(options) do 28 | self._options[k]=v 29 | end 30 | end 31 | local t=self._options.topology 32 | assert(t==8 or t==4, 'topology must be 8 or 4') 33 | self._dirs = t==8 and ROT.DIRS.EIGHT or t==4 and ROT.DIRS.FOUR 34 | 35 | self._rng = rng and rng or ROT.RNG.Twister:new() 36 | if not rng then self._rng:randomseed() end 37 | end 38 | 39 | --- Randomize cells. 40 | -- Random fill map with 0 or 1. Call this first when creating a map. 41 | -- @tparam number prob Probability that a cell will be a floor (0). Accepts values between 0 and 1 42 | -- @treturn ROT.Map.Cellular self 43 | function Cellular:randomize(prob) 44 | if not self._map then self._map = self:_fillMap(0) end 45 | for i=1,self._width do 46 | for j=1,self._height do 47 | self._map[i][j]= self._rng:random() < prob and 1 or 0 48 | end 49 | end 50 | return self 51 | end 52 | 53 | --- Set. 54 | -- Assign a value (0 or 1) to a cell on the map 55 | -- @tparam int x x-position of the cell 56 | -- @tparam int y y-position of the cell 57 | -- @tparam int value Value to be assigned 0-Floor 1-Wall 58 | function Cellular:set(x, y, value) 59 | self._map[x][y]=value 60 | end 61 | 62 | --- Create. 63 | -- Creates a map. 64 | -- @tparam function callback This function will be called for every cell. It must accept the following parameters: 65 | -- @tparam int callback.x The x-position of a cell in the map 66 | -- @tparam int callback.y The y-position of a cell in the map 67 | -- @tparam int callback.value A value representing the cell-type. 0==floor, 1==wall 68 | -- @treturn ROT.Map.Cellular self 69 | function Cellular:create(callback) 70 | local newMap =self:_fillMap(0) 71 | local born =self._options.born 72 | local survive=self._options.survive 73 | local changed=false 74 | 75 | for j=1,self._height do 76 | for i=1,self._width do 77 | local cur =self._map[i][j] 78 | local ncount=self:_getNeighbors(i, j) 79 | if cur>0 and table.indexOf(survive, ncount)>0 then 80 | newMap[i][j]=1 81 | elseif cur<=0 and table.indexOf(born, ncount)>0 then 82 | newMap[i][j]=1 83 | end 84 | if callback then callback(i, j, newMap[i][j]) end 85 | if not changed and newMap[i][j]~=self._map[i][j] then changed=true end 86 | end 87 | end 88 | self._map=newMap 89 | return changed 90 | end 91 | 92 | function Cellular:_getNeighbors(cx, cy) 93 | local rst=0 94 | for i=1,#self._dirs do 95 | local dir=self._dirs[i] 96 | local x =cx+dir[1] 97 | local y =cy+dir[2] 98 | if x>0 and x<=self._width and y>0 and y<=self._height then 99 | rst= self._map[x][y]==1 and rst+1 or rst 100 | end 101 | end 102 | return rst 103 | end 104 | 105 | return Cellular 106 | -------------------------------------------------------------------------------- /rotLove/src/corridor.lua: -------------------------------------------------------------------------------- 1 | --- Corridor object. 2 | -- Used by ROT.Map.Uniform and ROT.Map.Digger to create maps 3 | -- @module ROT.Map.Corridor 4 | local Corridor_PATH =({...})[1]:gsub("[%.\\/]corridor$", "") .. '/' 5 | local class =require (Corridor_PATH .. 'vendor/30log') 6 | 7 | local Corridor = ROT.Map.Feature:extends { _startX, _startY, _endX, _endY, _rng } 8 | Corridor.__name='Corridor' 9 | --- Constructor. 10 | -- Called with ROT.Map.Corridor:new() 11 | -- @tparam int startX x-position of first floospace in corridor 12 | -- @tparam int startY y-position of first floospace in corridor 13 | -- @tparam int endX x-position of last floospace in corridor 14 | -- @tparam int endY y-position of last floospace in corridor 15 | -- @tparam userdata rng Userdata with a .random(self, min, max) function 16 | function Corridor:__init(startX, startY, endX, endY, rng) 17 | self._startX =startX 18 | self._startY =startY 19 | self._endX =endX 20 | self._endY =endY 21 | self._endsWithAWall=true 22 | self._rng = rng and rng or ROT.RNG.Twister:new() 23 | if not rng then self._rng:randomseed() end 24 | end 25 | 26 | --- Create random with position. 27 | -- @tparam int x x-position of first floospace in corridor 28 | -- @tparam int y y-position of first floospace in corridor 29 | -- @tparam int dx x-direction of corridor (-1, 0, 1) for (left, none, right) 30 | -- @tparam int dy y-direction of corridor (-1, 0, 1) for (up, none, down) 31 | -- @tparam table options Options 32 | -- @tparam table options.corridorLength a table for the min and max corridor lengths {min, max} 33 | -- @tparam[opt] userData rng A user defined object with a .random(min, max) method 34 | function Corridor:createRandomAt(x, y, dx, dy, options, rng) 35 | rng=rng and rng or math.random 36 | local min =options.corridorLength[1] 37 | local max =options.corridorLength[2] 38 | local length=math.floor(rng:random(min, max)) 39 | return Corridor:new(x, y, x+dx*length, y+dy*length) 40 | end 41 | 42 | --- Write various information about this corridor to the console. 43 | function Corridor:debug() 44 | local command = write and write or io.write 45 | local debugString= 'corridor: '..self._startX..','..self._startY..','..self._endX..','..self._endY 46 | command(debugString) 47 | end 48 | 49 | --- Use two callbacks to confirm corridor validity. 50 | -- @tparam userdata gen The map generator calling this function. Lack of bind() function requires this. This is mainly so the map generator can hava a self reference in the two callbacks. 51 | -- @tparam function isWallCallback A function with three parameters (gen, x, y) that will return true if x, y represents a wall space in a map. 52 | -- @tparam function canBeDugCallback A function with three parameters (gen, x, y) that will return true if x, y represents a map cell that can be made into floorspace. 53 | -- @treturn boolean true if corridor is valid. 54 | function Corridor:isValid(gen, isWallCallback, canBeDugCallback) 55 | local sx =self._startX 56 | local sy =self._startY 57 | local dx =self._endX-sx 58 | local dy =self._endY-sy 59 | local length=1+math.max(math.abs(dx), math.abs(dy)) 60 | 61 | if dx>0 then dx=dx/math.abs(dx) end 62 | if dy>0 then dy=dy/math.abs(dy) end 63 | local nx=dy 64 | local ny=-dx 65 | 66 | local ok=true 67 | 68 | for i=0,length-1 do 69 | local x=sx+i*dx 70 | local y=sy+i*dy 71 | 72 | if not canBeDugCallback(gen, x, y) then ok=false end 73 | if not isWallCallback (gen, x+nx, y+ny) then ok=false end 74 | if not isWallCallback (gen, x-nx, y-ny) then ok=false end 75 | 76 | if not ok then 77 | length=i 78 | self._endX=x-dx 79 | self._endY=y-dy 80 | break 81 | end 82 | end 83 | 84 | if length==0 then return false end 85 | if length==1 and isWallCallback(gen, self._endX+dx, self._endY+dy) then return false end 86 | 87 | local firstCornerBad=not isWallCallback(gen, self._endX+dx+nx, self._endY+dy+ny) 88 | local secondCornrBad=not isWallCallback(gen, self._endX+dx-nx, self._endY+dy-ny) 89 | self._endsWithAWall = isWallCallback(gen, self._endX+dx , self._endY+dy ) 90 | if (firstCornerBad or secondCornrBad) and self._endsWithAWall then return false end 91 | 92 | return true 93 | end 94 | 95 | --- Create. 96 | -- Function runs a callback to dig the corridor into a map 97 | -- @tparam userdata gen The map generator calling this function. Passed as self to the digCallback 98 | -- @tparam function digCallback The function responsible for digging the corridor into a map. 99 | function Corridor:create(gen, digCallback) 100 | local sx =self._startX 101 | local sy =self._startY 102 | local dx =self._endX-sx 103 | local dy =self._endY-sy 104 | 105 | local length=1+math.max(math.abs(dx), math.abs(dy)) 106 | if dx~=0 then dx=dx/math.abs(dx) end 107 | if dy~=0 then dy=dy/math.abs(dy) end 108 | 109 | for i=0,length-1 do 110 | local x=sx+i*dx 111 | local y=sy+i*dy 112 | digCallback(gen, x, y, 0) 113 | end 114 | return true 115 | end 116 | 117 | --- Mark walls as priority for a future feature. 118 | -- Use this for storing the three points at the end of the corridor that you probably want to make sure gets a room attached. 119 | -- @tparam userdata gen The map generator calling this function. Passed as self to the digCallback 120 | -- @tparam function priorityWallCallback The function responsible for receiving and processing the priority walls 121 | function Corridor:createPriorityWalls(gen, priorityWallCallback) 122 | if not self._endsWithAWall then return end 123 | 124 | local sx =self._startX 125 | local sy =self._startY 126 | local dx =self._endX-sx 127 | local dy =self._endY-sy 128 | local length=1+math.max(math.abs(dx), math.abs(dy)) 129 | 130 | if dx>0 then dx=dx/math.abs(dx) end 131 | if dy>0 then dy=dy/math.abs(dy) end 132 | local nx=dy 133 | local ny=-dx 134 | 135 | priorityWallCallback(gen, self._endX+dx, self._endY+dy) 136 | priorityWallCallback(gen, self._endX+nx, self._endY+ny) 137 | priorityWallCallback(gen, self._endX-nx, self._endY-ny) 138 | end 139 | 140 | return Corridor 141 | -------------------------------------------------------------------------------- /rotLove/src/digger.lua: -------------------------------------------------------------------------------- 1 | --- The Digger Map Generator. 2 | -- See http://www.roguebasin.roguelikedevelopment.org/index.php?title=Dungeon-Building_Algorithm. 3 | -- @module ROT.Map.Digger 4 | local Digger_PATH =({...})[1]:gsub("[%.\\/]digger$", "") .. '/' 5 | local class =require (Digger_PATH .. 'vendor/30log') 6 | 7 | local Digger=ROT.Map.Dungeon:extends { _options, _rng } 8 | Digger.__name='Digger' 9 | --- Constructor. 10 | -- Called with ROT.Map.Digger:new() 11 | -- @tparam int width Width in cells of the map 12 | -- @tparam int height Height in cells of the map 13 | -- @tparam[opt] table options Options 14 | -- @tparam[opt={3,8}] table options.roomWidth room minimum and maximum width 15 | -- @tparam[opt={3,5}] table options.roomHeight room minimum and maximum height 16 | -- @tparam[opt={3,7}] table options.corridorLength corridor minimum and maximum length 17 | -- @tparam[opt=0.2] number options.dugPercentage we stop after this percentage of level area has been dug out 18 | -- @tparam[opt=1000] int options.timeLimit stop after this much time has passed (msec) 19 | -- @tparam[opt=false] boolean options.nocorridorsmode If true, do not use corridors to generate this map 20 | -- @tparam userdata rng Userdata with a .random(self, min, max) function 21 | function Digger:__init(width, height, options, rng) 22 | Digger.super.__init(self, width, height) 23 | 24 | self._options={ 25 | roomWidth={3,8}, 26 | roomHeight={3,5}, 27 | corridorLength={3,7}, 28 | dugPercentage=0.2, 29 | timeLimit=1000, 30 | nocorridorsmode=false 31 | } 32 | if options then 33 | for k,_ in pairs(options) do 34 | self._options[k]=options[k] 35 | end 36 | end 37 | 38 | self._features={Room=4, Corridor=4} 39 | if self._options.nocorridorsmode then 40 | self._features.Corridor=nil 41 | end 42 | self._featureAttempts=20 43 | self._walls={} 44 | 45 | self._rng = rng and rng or ROT.RNG.Twister:new() 46 | if not rng then self._rng:randomseed() end 47 | end 48 | 49 | --- Create. 50 | -- Creates a map. 51 | -- @tparam function callback This function will be called for every cell. It must accept the following parameters: 52 | -- @tparam int callback.x The x-position of a cell in the map 53 | -- @tparam int callback.y The y-position of a cell in the map 54 | -- @tparam int callback.value A value representing the cell-type. 0==floor, 1==wall 55 | -- @treturn ROT.Map.Digger self 56 | function Digger:create(callback) 57 | self._rooms ={} 58 | self._corridors={} 59 | self._map =self:_fillMap(1) 60 | self._walls ={} 61 | self._dug =0 62 | local area =(self._width-2)*(self._height-2) 63 | 64 | self:_firstRoom() 65 | 66 | local t1=os.clock()*1000 67 | local priorityWalls=0 68 | repeat 69 | local t2=os.clock()*1000 70 | if t2-t1>self._options.timeLimit then break end 71 | 72 | local wall=self:_findWall() 73 | if not wall then break end 74 | 75 | local parts=wall:split(',') 76 | local x =tonumber(parts[1]) 77 | local y =tonumber(parts[2]) 78 | local dir =self:_getDiggingDirection(x, y) 79 | if dir then 80 | local featureAttempts=0 81 | repeat 82 | featureAttempts=featureAttempts+1 83 | if self:_tryFeature(x, y, dir[1], dir[2]) then 84 | self:_removeSurroundingWalls(x, y) 85 | self:_removeSurroundingWalls(x-dir[1], y-dir[2]) 86 | break 87 | end 88 | until featureAttempts>=self._featureAttempts 89 | priorityWalls=0 90 | for k,_ in pairs(self._walls) do 91 | if self._walls[k] > 1 then 92 | priorityWalls=priorityWalls+1 93 | end 94 | end 95 | end 96 | until self._dug/area > self._options.dugPercentage and priorityWalls<1 97 | 98 | self:_addDoors() 99 | 100 | if callback then 101 | for i=1,self._width do 102 | for j=1,self._height do 103 | callback(i, j, self._map[i][j]) 104 | end 105 | end 106 | end 107 | self._walls={} 108 | self._map=nil 109 | return self 110 | end 111 | 112 | function Digger:_digCallback(x, y, value) 113 | if value==0 or value==2 then 114 | self._map[x][y]=0 115 | self._dug=self._dug+1 116 | else 117 | self._walls[x..','..y]=1 118 | end 119 | end 120 | 121 | function Digger:_isWallCallback(x, y) 122 | if x<1 or y<1 or x>self._width or y>self._height then return false end 123 | return self._map[x][y]==1 124 | end 125 | 126 | function Digger:_canBeDugCallback(x, y) 127 | if x<2 or y<2 or x>=self._width or y>=self._height then return false end 128 | return self._map[x][y]==1 129 | end 130 | 131 | function Digger:_priorityWallCallback(x, y) 132 | self._walls[x..','..y]=2 133 | end 134 | 135 | function Digger:_firstRoom() 136 | local cx =math.floor(self._width/2) 137 | local cy =math.floor(self._height/2) 138 | local room=ROT.Map.Room:new():createRandomCenter(cx, cy, self._options, self._rng) 139 | table.insert(self._rooms, room) 140 | room:create(self, self._digCallback) 141 | end 142 | 143 | function Digger:_findWall() 144 | local prio1={} 145 | local prio2={} 146 | for k,_ in pairs(self._walls) do 147 | if self._walls[k]>1 then table.insert(prio2, k) 148 | else table.insert(prio1, k) end 149 | end 150 | local arr=#prio2>0 and prio2 or prio1 151 | if #arr<1 then return nil end 152 | local id=table.random(arr) 153 | self._walls[id]=nil 154 | return id 155 | end 156 | 157 | function Digger:_tryFeature(x, y, dx, dy) 158 | local type=self._rng:getWeightedValue(self._features) 159 | local feature=ROT.Map[type]:createRandomAt(x,y,dx,dy,self._options,self._rng) 160 | 161 | if not feature:isValid(self, self._isWallCallback, self._canBeDugCallback) then 162 | return false 163 | end 164 | 165 | feature:create(self, self._digCallback) 166 | 167 | if type=='Room' then 168 | table.insert(self._rooms, feature) 169 | elseif type=='Corridor' then 170 | feature:createPriorityWalls(self, self._priorityWallCallback) 171 | table.insert(self._corridors, feature) 172 | end 173 | 174 | return true 175 | end 176 | 177 | function Digger:_removeSurroundingWalls(cx, cy) 178 | local deltas=ROT.DIRS.FOUR 179 | for i=1,#deltas do 180 | local delta=deltas[i] 181 | local x =delta[1] 182 | local y =delta[2] 183 | self._walls[x..','..y]=nil 184 | x=2*delta[1] 185 | y=2*delta[2] 186 | self._walls[x..','..y]=nil 187 | end 188 | end 189 | 190 | function Digger:_getDiggingDirection(cx, cy) 191 | local deltas=ROT.DIRS.FOUR 192 | local result=nil 193 | 194 | for i=1,#deltas do 195 | local delta=deltas[i] 196 | local x =cx+delta[1] 197 | local y =cy+delta[2] 198 | if x<1 or y<1 or x>self._width or y>self._height then return nil end 199 | if self._map[x][y]==0 then 200 | if result and #result>0 then return nil end 201 | result=delta 202 | end 203 | end 204 | if not result or #result<1 then return nil end 205 | 206 | return {-result[1], -result[2]} 207 | end 208 | 209 | function Digger:_addDoors() 210 | for i=1,#self._rooms do 211 | local room=self._rooms[i] 212 | room:clearDoors() 213 | room:addDoors(self, self._isWallCallback) 214 | end 215 | end 216 | 217 | return Digger 218 | -------------------------------------------------------------------------------- /rotLove/src/dijkstra.lua: -------------------------------------------------------------------------------- 1 | --- Dijkstra Pathfinding. 2 | -- Simplified Dijkstra's algorithm: all edges have a value of 1 3 | -- @module ROT.Path.Dijkstra 4 | local Dijkstra_PATH=({...})[1]:gsub("[%.\\/]dijkstra$", "") .. '/' 5 | local class =require (Dijkstra_PATH .. 'vendor/30log') 6 | 7 | local Dijkstra=ROT.Path:extends { _toX, _toY, _fromX, _fromY, _computed, _todo, _passableCallback, _options, _dirs} 8 | Dijkstra.__name='Dijkstra' 9 | --- Constructor. 10 | -- @tparam int toX x-position of destination cell 11 | -- @tparam int toY y-position of destination cell 12 | -- @tparam function passableCallback Function with two parameters (x, y) that returns true if the cell at x,y is able to be crossed 13 | -- @tparam table options Options 14 | -- @tparam[opt=8] int options.topology Directions for movement Accepted values (4 or 8) 15 | function Dijkstra:__init(toX, toY, passableCallback, options) 16 | Dijkstra.super.__init(self, toX, toY, passableCallback, options) 17 | 18 | self._computed={} 19 | self._todo ={} 20 | 21 | local obj = {x=toX, y=toY, prev=nil} 22 | self._computed[toX]={} 23 | self._computed[toX][toY] = obj 24 | table.insert(self._todo, obj) 25 | end 26 | 27 | --- Compute the path from a starting point 28 | -- @tparam int fromX x-position of starting point 29 | -- @tparam int fromY y-position of starting point 30 | -- @tparam function callback Will be called for every path item with arguments "x" and "y" 31 | function Dijkstra:compute(fromX, fromY, callback) 32 | self._fromX=tonumber(fromX) 33 | self._fromY=tonumber(fromY) 34 | 35 | if not self._computed[self._fromX] then self._computed[self._fromX]={} end 36 | if not self._computed[self._fromX][self._fromY] then self:_compute(self._fromX, self._fromY) end 37 | if not self._computed[self._fromX][self._fromY] then return end 38 | 39 | local item=self._computed[self._fromX][self._fromY] 40 | while item do 41 | callback(tonumber(item.x), tonumber(item.y)) 42 | item=item.prev 43 | end 44 | end 45 | 46 | function Dijkstra:_compute(fromX, fromY) 47 | while #self._todo>0 do 48 | local item=table.remove(self._todo, 1) 49 | if item.x == fromX and item.y == fromY then return end 50 | 51 | local neighbors=self:_getNeighbors(item.x, item.y) 52 | 53 | for i=1,#neighbors do 54 | local x=neighbors[i][1] 55 | local y=neighbors[i][2] 56 | if not self._computed[x] then self._computed[x]={} end 57 | if not self._computed[x][y] then 58 | self:_add(x, y, item) 59 | end 60 | end 61 | end 62 | end 63 | 64 | function Dijkstra:_add(x, y, prev) 65 | local obj={} 66 | obj.x =x 67 | obj.y =y 68 | obj.prev=prev 69 | 70 | self._computed[x][y]=obj 71 | table.insert(self._todo, obj) 72 | end 73 | 74 | return Dijkstra 75 | -------------------------------------------------------------------------------- /rotLove/src/dividedMaze.lua: -------------------------------------------------------------------------------- 1 | --- The Divided Maze Map Generator. 2 | -- Recursively divided maze, http://en.wikipedia.org/wiki/Maze_generation_algorithm#Recursive_division_method 3 | -- @module ROT.Map.DividedMaze 4 | local DividedMaze_PATH =({...})[1]:gsub("[%.\\/]dividedMaze$", "") .. '/' 5 | local class =require (DividedMaze_PATH .. 'vendor/30log') 6 | 7 | local DividedMaze = ROT.Map:extends { } 8 | DividedMaze.__name='DividedMaze' 9 | --- Constructor. 10 | -- Called with ROT.Map.DividedMaze:new(width, height) 11 | -- @tparam int width Width in cells of the map 12 | -- @tparam int height Height in cells of the map 13 | function DividedMaze:__init(width, height) 14 | DividedMaze.super.__init(self, width, height) 15 | end 16 | 17 | --- Create. 18 | -- Creates a map. 19 | -- @tparam function callback This function will be called for every cell. It must accept the following parameters: 20 | -- @tparam int callback.x The x-position of a cell in the map 21 | -- @tparam int callback.y The y-position of a cell in the map 22 | -- @tparam int callback.value A value representing the cell-type. 0==floor, 1==wall 23 | -- @treturn ROT.Map.DividedMaze self 24 | function DividedMaze:create(callback) 25 | local w=self._width 26 | local h=self._height 27 | self._map = {} 28 | 29 | for i=1,w do 30 | table.insert(self._map, {}) 31 | for j=1,h do 32 | local border= i==1 or j==1 or i==w or j==h 33 | table.insert(self._map[i], border and 1 or 0) 34 | end 35 | end 36 | self._stack = { {2,2,w-1,h-1} } 37 | self:_process() 38 | for i=1,w do 39 | for j=1,h do 40 | callback(i,j,self._map[i][j]) 41 | end 42 | end 43 | self._map=nil 44 | return self 45 | end 46 | 47 | function DividedMaze:_process() 48 | while #self._stack>0 do 49 | local room=table.remove(self._stack, 1) 50 | self:_partitionRoom(room) 51 | end 52 | end 53 | 54 | function DividedMaze:_partitionRoom(room) 55 | local availX={} 56 | local availY={} 57 | 58 | for i=room[1]+1,room[3]-1 do 59 | local top =self._map[i][room[2]-1] 60 | local bottom=self._map[i][room[4]+1] 61 | if top>0 and bottom>0 and i%2==0 then table.insert(availX, i) end 62 | end 63 | 64 | for j=room[2]+1,room[4]-1 do 65 | local left =self._map[room[1]-1][j] 66 | local right=self._map[room[3]+1][j] 67 | if left>0 and right>0 and j%2==0 then table.insert(availY, j) end 68 | end 69 | 70 | if #availX==0 or #availY==0 then return end 71 | 72 | local x=table.random(availX) 73 | local y=table.random(availY) 74 | 75 | self._map[x][y]=1 76 | 77 | local walls={} 78 | 79 | local w={} 80 | table.insert(walls, w) 81 | for i=room[1],x-1,1 do 82 | self._map[i][y]=1 83 | table.insert(w, {i,y}) 84 | end 85 | 86 | local w={} 87 | table.insert(walls, w) 88 | for i=x+1,room[3],1 do 89 | self._map[i][y]=1 90 | table.insert(w,{i,y}) 91 | end 92 | 93 | local w={} 94 | table.insert(walls, w) 95 | for j=room[2],y-1,1 do 96 | self._map[x][j]=1 97 | table.insert(w,{x,j}) 98 | end 99 | 100 | local w={} 101 | table.insert(walls, w) 102 | for j=y+1,room[4] do 103 | self._map[x][j]=1 104 | table.insert(w,{x,j}) 105 | end 106 | 107 | local solid= table.random(walls) 108 | for i=1,#walls do 109 | local w=walls[i] 110 | if w~=solid then 111 | hole=table.random(w) 112 | self._map[hole[1]][hole[2]]=0 113 | end 114 | end 115 | table.insert(self._stack, {room[1], room[2], x-1, y-1}) 116 | table.insert(self._stack, {x+1, room[2], room[3], y-1}) 117 | table.insert(self._stack, {room[1], y+1, x-1, room[4]}) 118 | table.insert(self._stack, {x+1, y+1, room[3], room[4]}) 119 | 120 | end 121 | 122 | return DividedMaze 123 | -------------------------------------------------------------------------------- /rotLove/src/dungeon.lua: -------------------------------------------------------------------------------- 1 | --- The Dungeon-style map Prototype. 2 | -- This class is extended by ROT.Map.Digger and ROT.Map.Uniform 3 | -- @module ROT.Map.Dungeon 4 | 5 | local Dungeon_PATH =({...})[1]:gsub("[%.\\/]dungeon$", "") .. '/' 6 | local class =require (Dungeon_PATH .. 'vendor/30log') 7 | 8 | local Dungeon = ROT.Map:extends { _rooms, _corridors } 9 | Dungeon.__name='Dungeon' 10 | --- Constructor. 11 | -- Called with ROT.Map.Cellular:new() 12 | -- @tparam int width Width in cells of the map 13 | -- @tparam int height Height in cells of the map 14 | function Dungeon:__init(width, height) 15 | Dungeon.super.__init(self, width, height) 16 | self._rooms ={} 17 | self._corridors={} 18 | end 19 | 20 | --- Get rooms 21 | -- Get a table of rooms on the map 22 | -- @treturn table A table containing objects of the type ROT.Map.Room 23 | function Dungeon:getRooms() return self._rooms end 24 | 25 | --- Get doors 26 | -- Get a table of doors on the map 27 | -- @treturn table A table {{x=int, y=int},...} for doors. 28 | function Dungeon:getDoors() 29 | local result={} 30 | for k,v in pairs(self._rooms) do 31 | for l,w in pairs(v._doors) do 32 | local s=l:split(',') 33 | table.insert(result, {x=tonumber(s[1]), y=tonumber(s[2])}) 34 | end 35 | end 36 | return result 37 | end 38 | 39 | --- Get corridors 40 | -- Get a table of corridors on the map 41 | -- @treturn table A table containing objects of the type ROT.Map.Corridor 42 | function Dungeon:getCorridors() return self._corridors end 43 | 44 | return Dungeon 45 | -------------------------------------------------------------------------------- /rotLove/src/ellerMaze.lua: -------------------------------------------------------------------------------- 1 | --- The Eller Maze Map Generator. 2 | -- See http://homepages.cwi.nl/~tromp/maze.html for explanation 3 | -- @module ROT.Map.EllerMaze 4 | 5 | local EllerMaze_PATH =({...})[1]:gsub("[%.\\/]ellerMaze$", "") .. '/' 6 | local class =require (EllerMaze_PATH .. 'vendor/30log') 7 | 8 | local EllerMaze = ROT.Map:extends { _rng } 9 | EllerMaze.__name='EllerMaze' 10 | 11 | --- Constructor. 12 | -- Called with ROT.Map.EllerMaze:new(width, height) 13 | -- @tparam int width Width in cells of the map 14 | -- @tparam int height Height in cells of the map 15 | -- @tparam userdata rng Userdata with a .random(self, min, max) function 16 | function EllerMaze:__init(width, height, rng) 17 | EllerMaze.super.__init(self, width, height) 18 | self._rng = rng and rng or ROT.RNG.Twister:new() 19 | if not rng then self._rng:randomseed() end 20 | end 21 | 22 | --- Create. 23 | -- Creates a map. 24 | -- @tparam function callback This function will be called for every cell. It must accept the following parameters: 25 | -- @tparam int callback.x The x-position of a cell in the map 26 | -- @tparam int callback.y The y-position of a cell in the map 27 | -- @tparam int callback.value A value representing the cell-type. 0==floor, 1==wall 28 | -- @treturn ROT.Map.EllerMaze self 29 | function EllerMaze:create(callback) 30 | local map =self:_fillMap(1) 31 | local w =math.ceil((self._width-2)/2) 32 | local rand=9/24 33 | local L ={} 34 | local R ={} 35 | 36 | for i=1,w do 37 | table.insert(L,i) 38 | table.insert(R,i) 39 | end 40 | table.insert(L,w) 41 | local j=2 42 | while jrand then 49 | self:_addToList(i, L, R) 50 | map[x+1][y]=0 51 | end 52 | 53 | if i~=L[i] and self._rng:random()>rand then 54 | self:_removeFromList(i, L, R) 55 | else 56 | map[x][y+1]=0 57 | end 58 | end 59 | j=j+2 60 | end 61 | --j=self._height%2==1 and self._height-2 or self._height-3 62 | for i=1,w do 63 | local x=2*i 64 | local y=j 65 | map[x][y]=0 66 | 67 | if i~=L[i+1] and (i==L[i] or self._rng:random()>rand) then 68 | self:_addToList(i, L, R) 69 | map[x+1][y]=0 70 | end 71 | 72 | self:_removeFromList(i, L, R) 73 | end 74 | for i=1,self._width do 75 | for j=1,self._height do 76 | callback(i, j, map[i][j]) 77 | end 78 | end 79 | return self 80 | end 81 | 82 | function EllerMaze:_removeFromList(i, L, R) 83 | R[L[i]]=R[i] 84 | L[R[i]]=L[i] 85 | R[i] =i 86 | L[i] =i 87 | end 88 | 89 | function EllerMaze:_addToList(i, L, R) 90 | R[L[i+1]]=R[i] 91 | L[R[i]] =L[i+1] 92 | R[i] =i+1 93 | L[i+1] =i 94 | end 95 | 96 | return EllerMaze 97 | -------------------------------------------------------------------------------- /rotLove/src/engine.lua: -------------------------------------------------------------------------------- 1 | local Engine_PATH =({...})[1]:gsub("[%.\\/]engine$", "") .. '/' 2 | local class =require (Engine_PATH .. 'vendor/30log') 3 | 4 | local Engine = class { _scheduler, _lock } 5 | Engine.__name='Engine' 6 | function Engine:__init(scheduler) 7 | self._scheduler=scheduler 8 | self._lock =1 9 | end 10 | 11 | function Engine:start() 12 | return self:unlock() 13 | end 14 | 15 | function Engine:lock() 16 | self._lock=self._lock+1 17 | end 18 | 19 | function Engine:unlock() 20 | assert(self._lock>0, 'Cannot unlock unlocked Engine') 21 | self._lock=self._lock-1 22 | while self._lock<1 do 23 | local actor=self._scheduler:next() 24 | if not actor then return self:lock() end 25 | actor:act() 26 | end 27 | return self 28 | end 29 | 30 | return Engine 31 | -------------------------------------------------------------------------------- /rotLove/src/eventQueue.lua: -------------------------------------------------------------------------------- 1 | --- Stores and retrieves events based on time. 2 | -- @module ROT.EventQueue 3 | 4 | local EventQueue_Path =({...})[1]:gsub("[%.\\/]eventQueue$", "") .. '/' 5 | local class =require (EventQueue_Path .. 'vendor/30log') 6 | 7 | local EventQueue = class { 8 | _time =0, 9 | _events ={}, 10 | _eventTimes={} 11 | } 12 | EventQueue.__name='EventQueue', 13 | --- Get Time. 14 | -- Get time counted since start 15 | -- @treturn int elapsed time 16 | function EventQueue:getTime() 17 | return self._time 18 | end 19 | 20 | --- Clear. 21 | -- Remove all events from queue 22 | -- @treturn ROT.EventQueue self 23 | function EventQueue:clear() 24 | self._events ={} 25 | self._eventTimes={} 26 | return self 27 | end 28 | 29 | --- Add. 30 | -- Add an event 31 | -- @tparam any event Any object 32 | -- @tparam int time The number of time units that will elapse before this event is returned 33 | function EventQueue:add(event, time) 34 | local index= 1 35 | if self._eventTimes then 36 | for i=1,#self._eventTimes do 37 | if self._eventTimes[i]>time then 38 | index=i 39 | break 40 | end 41 | index=i+1 42 | end 43 | end 44 | table.insert(self._events, index, event) 45 | table.insert(self._eventTimes, index, time) 46 | end 47 | 48 | --- Get. 49 | -- Get the next event from the queue and advance the appropriate amount time 50 | -- @treturn event|nil The event previously added by .add() or nil if none are queued 51 | function EventQueue:get() 52 | if #self._events<1 then return nil end 53 | local time = table.remove(self._eventTimes, 1) 54 | if time>0 then 55 | self._time=self._time+time 56 | for i=1,#self._eventTimes do 57 | self._eventTimes[i]=self._eventTimes[i]-time 58 | end 59 | end 60 | return table.remove(self._events, 1) 61 | end 62 | 63 | --- Remove. 64 | -- Find and remove an event from the queue 65 | -- @tparam any event The previously added event to be removed 66 | -- @treturn boolean true if an event was removed from the queue 67 | function EventQueue:remove(event) 68 | local index=table.indexOf(self._events, event) 69 | if index==0 then return false end 70 | self:_remove(index) 71 | return true 72 | end 73 | 74 | function EventQueue:_remove(index) 75 | table.remove(self._events, index) 76 | table.remove(self._eventTimes, index) 77 | end 78 | 79 | return EventQueue 80 | -------------------------------------------------------------------------------- /rotLove/src/feature.lua: -------------------------------------------------------------------------------- 1 | local Feature_PATH =({...})[1]:gsub("[%.\\/]feature$", "") .. '/' 2 | local class =require (Feature_PATH .. 'vendor/30log') 3 | 4 | local Feature = class { } 5 | Feature.__name='Feature' 6 | function Feature:isValid(gen, canBeDugCallback) end 7 | function Feature:create(gen, digCallback) end 8 | function Feature:debug() end 9 | function Feature:createRandomAt(x, y, dx, dy, options) end 10 | return Feature 11 | -------------------------------------------------------------------------------- /rotLove/src/fov.lua: -------------------------------------------------------------------------------- 1 | local FOV_PATH =({...})[1]:gsub("[%.\\/]fov$", "") .. '/' 2 | local class =require (FOV_PATH .. 'vendor/30log') 3 | 4 | local FOV=class{ __name, _lightPasses, _options } 5 | FOV.__name='FOV' 6 | function FOV:__init(lightPassesCallback, options) 7 | self._lightPasses=lightPassesCallback 8 | self._options={topology=8} 9 | if options then for k,_ in pairs(options) do self._options[k]=options[k] end end 10 | end 11 | 12 | function FOV:compute(x, y, R, callback) end 13 | 14 | function FOV:_getCircle(cx, cy, r) 15 | local result={} 16 | local dirs, countFactor, startOffset 17 | local topo=self._options.topology 18 | if topo==4 then 19 | countFactor=1 20 | startOffset={0,1} 21 | dirs={ 22 | ROT.DIRS.EIGHT[8], 23 | ROT.DIRS.EIGHT[2], 24 | ROT.DIRS.EIGHT[4], 25 | ROT.DIRS.EIGHT[6] 26 | } 27 | elseif topo==8 then 28 | dirs=ROT.DIRS.FOUR 29 | countFactor=2 30 | startOffset={-1,1} 31 | end 32 | 33 | local x=cx+startOffset[1]*r 34 | local y=cy+startOffset[2]*r 35 | 36 | for i=1,#dirs do 37 | for j=1,r*countFactor do 38 | table.insert(result, {x, y}) 39 | x=x+dirs[i][1] 40 | y=y+dirs[i][2] 41 | end 42 | end 43 | return result 44 | end 45 | 46 | function FOV:_getRealCircle(cx, cy, r) 47 | local i=0 48 | local result={} 49 | while i<2*math.pi do 50 | i=i+0.05 51 | x = cx + r * math.cos(i) 52 | y = cy + r * math.sin(i) 53 | table.insert(result, {x,y}) 54 | end 55 | return result 56 | end 57 | 58 | return FOV 59 | -------------------------------------------------------------------------------- /rotLove/src/iceyMaze.lua: -------------------------------------------------------------------------------- 1 | --- The Icey Maze Map Generator. 2 | -- See http://www.roguebasin.roguelikedevelopment.org/index.php?title=Simple_maze for explanation 3 | -- @module ROT.Map.IceyMaze 4 | 5 | local IceyMaze_PATH =({...})[1]:gsub("[%.\\/]iceyMaze$", "") .. '/' 6 | local class =require (IceyMaze_PATH .. 'vendor/30log') 7 | 8 | local IceyMaze = ROT.Map:extends { _regularity, _rng } 9 | IceyMaze.__name ='IceyMaze' 10 | --- Constructor. 11 | -- Called with ROT.Map.IceyMaze:new(width, height, regularity) 12 | -- @tparam int width Width in cells of the map 13 | -- @tparam int height Height in cells of the map 14 | -- @tparam userdata rng Userdata with a .random(self, min, max) function 15 | -- @tparam int[opt=0] regularity A value used to determine the 'randomness' of the map, 0= more random 16 | function IceyMaze:__init(width, height, rng, regularity) 17 | IceyMaze.super.__init(self, width, height) 18 | self._regularity= regularity and regularity or 0 19 | self._rng =rng and rng or ROT.RNG.Twister:new() 20 | if not rng then self._rng:randomseed() end 21 | end 22 | 23 | --- Create. 24 | -- Creates a map. 25 | -- @tparam function callback This function will be called for every cell. It must accept the following parameters: 26 | -- @tparam int callback.x The x-position of a cell in the map 27 | -- @tparam int callback.y The y-position of a cell in the map 28 | -- @tparam int callback.value A value representing the cell-type. 0==floor, 1==wall 29 | -- @treturn ROT.Map.IceyMaze self 30 | function IceyMaze:create(callback) 31 | local w=self._width 32 | local h=self._height 33 | local map=self:_fillMap(1) 34 | w= w%2==1 and w-1 or w-2 35 | h= h%2==1 and h-1 or h-2 36 | 37 | local cx, cy, nx, ny = 1, 1, 1, 1 38 | local done =0 39 | local blocked=false 40 | local dirs={ 41 | {0,0}, 42 | {0,0}, 43 | {0,0}, 44 | {0,0} 45 | } 46 | repeat 47 | cx=2+2*math.floor(self._rng:random()*(w-1)/2) 48 | cy=2+2*math.floor(self._rng:random()*(h-1)/2) 49 | if done==0 then map[cx][cy]=0 end 50 | if map[cx][cy]==0 then 51 | self:_randomize(dirs) 52 | repeat 53 | if math.floor(self._rng:random()*(self._regularity+1))==0 then self:_randomize(dirs) end 54 | blocked=true 55 | for i=1,4 do 56 | nx=cx+dirs[i][1]*2 57 | ny=cy+dirs[i][2]*2 58 | if self:_isFree(map, nx, ny, w, h) then 59 | map[nx][ny]=0 60 | map[cx+dirs[i][1]][cy+dirs[i][2]]=0 61 | 62 | cx=nx 63 | cy=ny 64 | blocked=false 65 | done=done+1 66 | break 67 | end 68 | end 69 | until blocked 70 | end 71 | until done+1>=w*h/4 72 | 73 | for i=1,self._width do 74 | for j=1,self._height do 75 | callback(i, j, map[i][j]) 76 | end 77 | end 78 | self._map=nil 79 | return self 80 | end 81 | 82 | function IceyMaze:_randomize(dirs) 83 | for i=1,4 do 84 | dirs[i][1]=0 85 | dirs[i][2]=0 86 | end 87 | local rand=math.floor(self._rng:random()*4) 88 | if rand==0 then 89 | dirs[1][1]=-1 90 | dirs[3][2]=-1 91 | dirs[2][1]= 1 92 | dirs[4][2]= 1 93 | elseif rand==1 then 94 | dirs[4][1]=-1 95 | dirs[2][2]=-1 96 | dirs[3][1]= 1 97 | dirs[1][2]= 1 98 | elseif rand==2 then 99 | dirs[3][1]=-1 100 | dirs[1][2]=-1 101 | dirs[4][1]= 1 102 | dirs[2][2]= 1 103 | elseif rand==3 then 104 | dirs[2][1]=-1 105 | dirs[4][2]=-1 106 | dirs[1][1]= 1 107 | dirs[3][2]= 1 108 | end 109 | end 110 | 111 | function IceyMaze:_isFree(map, x, y, w, h) 112 | if x<2 or y<2 or x>w or y>h then return false end 113 | return map[x][y]~=0 114 | end 115 | 116 | return IceyMaze 117 | -------------------------------------------------------------------------------- /rotLove/src/img/cp437.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markandgo/Lua-FOV/0e76592ba33d247266aecb254df30fc0ed05a61d/rotLove/src/img/cp437.png -------------------------------------------------------------------------------- /rotLove/src/lcg.lua: -------------------------------------------------------------------------------- 1 | --- Linear Congruential Generator. A random number generator based on RandomLua 2 | -- @module ROT.RNG.LCG 3 | 4 | local LCG_PATH =({...})[1]:gsub("[%.\\/]lcg$", "") .. '/' 5 | local class =require (LCG_PATH .. 'vendor/30log') 6 | 7 | local LCG=ROT.RNG:extends { __name, mt, index, a, c, m, x, _seed } 8 | LCG.__name='LCG' 9 | 10 | --- Constructor. 11 | -- Called with ROT.RNG.LCG:new(r) 12 | -- @tparam[opt] string r Choose to populate the rng with values from numerical recipes or mvc as opposed to Ansi C. Accepted values 'nr', 'mvc' 13 | function LCG:__init(r) 14 | self.a= 1103515245 -- Ansi C 15 | self.c= 12345 16 | self.m= 0x10000 17 | 18 | if r=='nr' then self.a, self.c, self.m = 1664525, 1013904223, 0x10000 -- Numerical Recipes 19 | elseif r=='mvc' then self.a, self.c, self.m = 214013, 2531011, 0x10000 end -- MVC 20 | end 21 | 22 | --- Random. 23 | -- get a random number 24 | -- @tparam[opt=0] int a lower threshold for random numbers 25 | -- @tparam[opt=1] int b upper threshold for random numbers 26 | -- @treturn number a random number 27 | function LCG:random(a, b) 28 | local y = (self.a * self.x + self.c) % self.m 29 | self.x = y 30 | if not a then return y / 0x10000 31 | elseif not b then 32 | if a == 0 then return y 33 | else return 1 + (y % a) end 34 | else 35 | return a + (y % (b - a + 1)) 36 | end 37 | end 38 | 39 | --- Seed. 40 | -- seed the rng 41 | -- @tparam[opt=os.clock()] number s A number to base the rng from 42 | function LCG:randomseed(s) 43 | if not s then s = self:seed() end 44 | self._seed=s 45 | self.x = self:normalize(s) 46 | end 47 | 48 | --- Get current rng state 49 | -- Returns a table that can be given to the rng to return it to this state. 50 | -- Any RNG of the same type will always produce the same values from this state. 51 | -- @treturn table A table that represents the current state of the rng 52 | function LCG:getState() 53 | return { a=self.a, c=self.c, m=self.m, x=self.x, _seed=self._seed} 54 | end 55 | 56 | --- Set current rng state 57 | -- used to return an rng to a known/previous state 58 | -- @tparam table stateTable The table retrieved from .getState() 59 | function LCG:setState(stateTable) 60 | assert(stateTable.a, 'bad stateTable: need stateTable.a') 61 | assert(stateTable.c, 'bad stateTable: need stateTable.c') 62 | assert(stateTable.m, 'bad stateTable: need stateTable.m') 63 | assert(stateTable.x, 'bad stateTable: need stateTable.x') 64 | assert(stateTable._seed, 'bad stateTable: need stateTable._seed') 65 | 66 | self.a=stateTable.a 67 | self.c=stateTable.c 68 | self.m=stateTable.m 69 | self.x=stateTable.x 70 | self._seed=stateTable._seed 71 | end 72 | 73 | return LCG 74 | -------------------------------------------------------------------------------- /rotLove/src/lighting.lua: -------------------------------------------------------------------------------- 1 | --- Lighting Calculator. 2 | -- based on a traditional FOV for multiple light sources and multiple passes. 3 | -- @module ROT.Lighting 4 | local Lighting_PATH=({...})[1]:gsub("[%.\\/]lighting$", "") .. '/' 5 | local class =require (Lighting_PATH .. 'vendor/30log') 6 | 7 | local Lighting=class { } 8 | Lighting.__name='Lighting' 9 | --- Constructor. 10 | -- Called with ROT.Color:new() 11 | -- @tparam function reflectivityCallback Callback to retrieve cell reflectivity must return float(0..1) 12 | -- @tparam int reflectivityCallback.x x-position of cell 13 | -- @tparam int reflectivityCallback.y y-position of cell 14 | -- @tparam table options Options 15 | -- @tparam[opt=1] int options.passes Number of passes. 1 equals to simple FOV of all light sources, >1 means a *highly simplified* radiosity-like algorithm. 16 | -- @tparam[opt=100] int options.emissionThreshold Cells with emissivity > threshold will be treated as light source in the next pass. 17 | -- @tparam[opt=10] int options.range Max light range 18 | function Lighting:__init(reflectivityCallback, options) 19 | self._reflectivityCallback=reflectivityCallback 20 | self._options={passes=1, emissionThreshold=100, range=10} 21 | self._fov=nil 22 | self._lights={} 23 | self._reflectivityCache={} 24 | self._fovCache={} 25 | 26 | self._colorHandler=ROT.Color:new() 27 | 28 | if options then for k,_ in pairs(options) do self._options[k]=options[k] end end 29 | end 30 | 31 | --- Set FOV 32 | -- Set the Field of View algorithm used to calculate light emission 33 | -- @tparam userdata fov Class/Module used to calculate fov Must have compute(x, y, range, cb) method. Typically you would supply ROT.FOV.Precise:new() here. 34 | -- @treturn ROT.Lighting self 35 | -- @see ROT.FOV.Precise 36 | -- @see ROT.FOV.Bresenham 37 | function Lighting:setFOV(fov) 38 | self._fov=fov 39 | self._fovCache={} 40 | return self 41 | end 42 | 43 | --- Add or remove a light source 44 | -- @tparam int x x-position of light source 45 | -- @tparam int y y-position of light source 46 | -- @tparam nil|string|table color An string accepted by Color:fromString(str) or a color table. A nil value here will remove the light source at x, y 47 | -- @treturn ROT.Lighting self 48 | -- @see ROT.Color 49 | function Lighting:setLight(x, y, color) 50 | local key=x..','..y 51 | if color then 52 | self._lights[key]=type(color)=='string' and self._colorHandler:fromString(color) or color 53 | else 54 | self._lights[key]=nil 55 | end 56 | return self 57 | end 58 | 59 | --- Compute. 60 | -- Compute the light sources and lit cells 61 | -- @tparam function lightingCallback Will be called with (x, y, color) for every lit cell 62 | -- @treturn ROT.Lighting self 63 | function Lighting:compute(lightingCallback) 64 | local doneCells={} 65 | local emittingCells={} 66 | local litCells={} 67 | 68 | for k,_ in pairs(self._lights) do 69 | local light=self._lights[k] 70 | if not emittingCells[k] then emittingCells[k]={r=0,g=0,b=0,a=255} end 71 | self._colorHandler:add_(emittingCells[k], light) 72 | end 73 | 74 | for i=1,self._options.passes do 75 | self:_emitLight(emittingCells, litCells, doneCells) 76 | if i0 then 122 | local emission ={} 123 | local intensity=0 124 | for l,_ in pairs(color) do 125 | if l~='a' then 126 | local part=math.round(color[l]*reflectivity) 127 | emission[l]=part 128 | intensity=intensity+part 129 | end 130 | end 131 | if intensity>self._options.emissionThreshold then 132 | result[k]=emission 133 | end 134 | end 135 | end 136 | end 137 | 138 | return result 139 | end 140 | 141 | function Lighting:_emitLightFromCell(x, y, color, litCells) 142 | local key=x..','..y 143 | local fov 144 | if self._fovCache[key] then fov=self._fovCache[key] 145 | else fov=self:_updateFOV(x, y) 146 | end 147 | local result 148 | local formFactor 149 | for k,_ in pairs(fov) do 150 | formFactor=fov[k] 151 | if not litCells[k] then 152 | litCells[k]={r=0,g=0,b=0,a=255} 153 | end 154 | for l,_ in pairs(color) do 155 | if l~='a' then 156 | litCells[k][l]=litCells[k][l]+math.round(color[l]*formFactor) 157 | end 158 | end 159 | end 160 | return self 161 | end 162 | 163 | function Lighting:_updateFOV(x, y) 164 | local key1=x..','..y 165 | local cache={} 166 | self._fovCache[key1]=cache 167 | local range=self._options.range 168 | local function cb(x, y, r, vis) 169 | local key2=x..','..y 170 | local formFactor=vis*(1-r/range) 171 | if formFactor==0 then return end 172 | cache[key2]=formFactor 173 | end 174 | self._fov:compute(x, y, range, cb) 175 | 176 | return cache 177 | end 178 | 179 | return Lighting 180 | -------------------------------------------------------------------------------- /rotLove/src/line.lua: -------------------------------------------------------------------------------- 1 | local Line_PATH =({...})[1]:gsub("[%.\\/]line$", "") .. '/' 2 | local class =require (Line_PATH .. 'vendor/30log') 3 | 4 | local Line=class{ x1, y1, x2, y2, points } 5 | Line.__name='Line' 6 | function Line:__init(x1, y1, x2, y2) 7 | self.x1=x1 8 | self.y1=y1 9 | self.x2=x2 10 | self.y2=y2 11 | self.points={} 12 | end 13 | function Line:getPoints() 14 | local dx =math.abs(self.x2-self.x1) 15 | local dy =math.abs(self.y2-self.y1) 16 | local sx =self.x1-dx then 25 | err=err-dy 26 | self.x1 =self.x1+sx 27 | end 28 | if e20 do 19 | table.insert(result, table.remove(theTable, table.randomi(theTable))) 20 | end 21 | return result 22 | end 23 | -- add js slice function 24 | function table.slice (values,i1,i2) 25 | local res = {} 26 | local n = #values 27 | -- default values for range 28 | i1 = i1 or 1 29 | i2 = i2 or n 30 | if i2 < 0 then 31 | i2 = n + i2 + 1 32 | elseif i2 > n then 33 | i2 = n 34 | end 35 | if i1 < 1 or i1 > n then 36 | return {} 37 | end 38 | local k = 1 39 | for i = i1,i2 do 40 | res[k] = values[i] 41 | k = k + 1 42 | end 43 | return res 44 | end 45 | -- add js indexOf function 46 | function table.indexOf(values,value) 47 | if values then 48 | for i=1,#values do 49 | if values[i] == value then return i end 50 | end 51 | end 52 | if type(value)=='table' then return table.indexOfTable(values, value) end 53 | return 0 54 | end 55 | 56 | -- extended for use with tables of tables 57 | function table.indexOfTable(values, value) 58 | if type(value)~='table' then return 0 end 59 | for k,v in ipairs(values) do 60 | if #v==#value then 61 | local match=true 62 | for i=1,#v do 63 | if v[i]~=value[i] then match=false end 64 | end 65 | if match then return k end 66 | end 67 | end 68 | return 0 69 | end 70 | 71 | -- asserts the type of 'theTable' is table 72 | function isATable(theTable) 73 | assert(type(theTable)=='table', "bad argument #1 to 'random' (table expected got "..type(theTable)..")") 74 | end 75 | 76 | -- New String functions 77 | -- first letter capitalized 78 | function string:capitalize() 79 | return self:sub(1,1):upper() .. self:sub(2) 80 | end 81 | -- returns string of length n consisting of only char c 82 | function charNTimes(c, n) 83 | assert(#c==1, 'character must be a string of length 1') 84 | s='' 85 | for i=1,n and n or 2 do 86 | s=s..c 87 | end 88 | return s 89 | end 90 | -- left pad with c char, repeated n times 91 | function string:lpad(c, n) 92 | c=c and c or '0' 93 | n=n and n or 2 94 | local s='' 95 | while #s < n-#self do s=s..c end 96 | return s..self 97 | end 98 | -- right pad with c char, repeated n times 99 | function string:rpad(c, n) 100 | c=c and c or '0' 101 | n=n and n or 2 102 | local s='' 103 | while #s < n-#self do s=s..c end 104 | return self..s 105 | end 106 | -- add js split function 107 | function string:split(delim, maxNb) 108 | -- Eliminate bad cases... 109 | if string.find(self, delim) == nil then 110 | return { self } 111 | end 112 | local result = {} 113 | if delim == '' or not delim then 114 | for i=1,#self do 115 | result[i]=self:sub(i,i) 116 | end 117 | return result 118 | end 119 | if maxNb == nil or maxNb < 1 then 120 | maxNb = 0 -- No limit 121 | end 122 | local pat = "(.-)" .. delim .. "()" 123 | local nb = 0 124 | local lastPos 125 | for part, pos in string.gfind(self, pat) do 126 | nb = nb + 1 127 | result[nb] = part 128 | lastPos = pos 129 | if nb == maxNb then break end 130 | end 131 | -- Handle the last field 132 | if nb ~= maxNb then 133 | result[nb + 1] = string.sub(self, lastPos) 134 | end 135 | return result 136 | end 137 | 138 | function math.round(n, mult) 139 | mult = mult or 1 140 | return math.floor((n + mult/2)/mult) * mult 141 | end 142 | 143 | -- io.write(arg..'\n') 144 | function write(str) 145 | io.write(str..'\n') 146 | end 147 | -------------------------------------------------------------------------------- /rotLove/src/noise.lua: -------------------------------------------------------------------------------- 1 | local Noise_PATH =({...})[1]:gsub("[%.\\/]noise$", "") .. '/' 2 | local class =require (Noise_PATH .. 'vendor/30log') 3 | 4 | local Noise=class{ __name } 5 | Noise.__name='Noise' 6 | 7 | function Noise:get(x, y) end 8 | 9 | return Noise 10 | -------------------------------------------------------------------------------- /rotLove/src/path.lua: -------------------------------------------------------------------------------- 1 | local Path_PATH=({...})[1]:gsub("[%.\\/]path$", "") .. '/' 2 | local class =require (Path_PATH .. 'vendor/30log') 3 | 4 | local Path=class { _toX, _toY, _fromX, _fromY, _passableCallback, _options, _dirs} 5 | Path.__name='Path' 6 | function Path:__init(toX, toY, passableCallback, options) 7 | self._toX =toX 8 | self._toY =toY 9 | self._fromX=nil 10 | self._fromY=nil 11 | self._passableCallback=passableCallback 12 | self._options= { topology=8 } 13 | 14 | if options then for k,_ in pairs(options) do self._options[k]=options[k] end end 15 | 16 | self._dirs= self._options.topology==8 and ROT.DIRS.EIGHT or ROT.DIRS.FOUR 17 | if self._options.topology==8 then 18 | self._dirs ={self._dirs[1], 19 | self._dirs[3], 20 | self._dirs[5], 21 | self._dirs[7], 22 | self._dirs[2], 23 | self._dirs[4], 24 | self._dirs[6], 25 | self._dirs[8] } 26 | end 27 | end 28 | 29 | function Path:compute(fromX, fromY, callback) end 30 | 31 | function Path:_getNeighbors(cx, cy) 32 | local result={} 33 | for i=1,#self._dirs do 34 | local dir=self._dirs[i] 35 | local x=cx+dir[1] 36 | local y=cy+dir[2] 37 | if self._passableCallback(x, y) then 38 | table.insert(result, {x, y}) 39 | end 40 | end 41 | return result 42 | end 43 | 44 | return Path 45 | -------------------------------------------------------------------------------- /rotLove/src/point.lua: -------------------------------------------------------------------------------- 1 | local Point_PATH =({...})[1]:gsub("[%.\\/]point$", "") .. '/' 2 | local class =require (Point_PATH .. 'vendor/30log') 3 | 4 | local Point=class { x, y } 5 | Point.__name='Point' 6 | function Point:__init(x, y) 7 | self.x=x 8 | self.y=y 9 | end 10 | 11 | function Point:hashCode() 12 | local prime =31 13 | local result=1 14 | result=prime*result+self.x 15 | result=prime*result+self.y 16 | return result 17 | end 18 | 19 | function Point:equals(other) 20 | if self==other then return true end 21 | if other==nil or 22 | not other.is_a(Point) or 23 | (other.x and other.x ~= self.x) or 24 | (other.y and other.y ~= self.y) then return false end 25 | return true 26 | end 27 | 28 | function Point:adjacentPoints() 29 | local points={} 30 | local i =1 31 | for ox=-1,1 do for oy=-1,1 do 32 | points[i]=Point(self.x+ox,self.y+oy) 33 | i=i+1 34 | end end 35 | return points 36 | end 37 | 38 | return Point 39 | -------------------------------------------------------------------------------- /rotLove/src/precise.lua: -------------------------------------------------------------------------------- 1 | --- Precise Shadowcasting Field of View calculator. 2 | -- The Precise shadow casting algorithm developed by Ondřej Žára for rot.js. 3 | -- See http://roguebasin.roguelikedevelopment.org/index.php?title=Precise_Shadowcasting_in_JavaScript 4 | -- @module ROT.FOV.Precise 5 | local Precise_PATH =({...})[1]:gsub("[%.\\/]precise$", "") .. '/' 6 | local class =require (Precise_PATH .. 'vendor/30log') 7 | 8 | local Precise=ROT.FOV:extends{ __name, _lightPasses, _options } 9 | Precise.__name='Precise' 10 | --- Constructor. 11 | -- Called with ROT.FOV.Precise:new() 12 | -- @tparam function lightPassesCallback A function with two parameters (x, y) that returns true if a map cell will allow light to pass through 13 | -- @tparam table options Options 14 | -- @tparam int options.topology Direction for light movement Accepted values: (4 or 8) 15 | function Precise:__init(lightPassesCallback, options) 16 | Precise.super.__init(self, lightPassesCallback, options) 17 | end 18 | 19 | --- Compute. 20 | -- Get visibility from a given point 21 | -- @tparam int x x-position of center of FOV 22 | -- @tparam int y y-position of center of FOV 23 | -- @tparam int R radius of FOV (i.e.: At most, I can see for R cells) 24 | -- @tparam function callback A function that is called for every cell in view. Must accept four parameters. 25 | -- @tparam int callback.x x-position of cell that is in view 26 | -- @tparam int callback.y y-position of cell that is in view 27 | -- @tparam int callback.r The cell's distance from center of FOV 28 | -- @tparam number callback.visibility The cell's visibility rating (from 0-1). How well can you see this cell? 29 | function Precise:compute(x, y, R, callback) 30 | callback(x, y, 0, 1) 31 | local SHADOWS={} 32 | 33 | local cx, cy, blocks, A1, A2, visibility 34 | 35 | for r=1,R do 36 | local neighbors=self:_getCircle(x, y, r) 37 | local neighborCount=#neighbors 38 | 39 | for i=0,neighborCount-1 do 40 | local cx=neighbors[i+1][1] 41 | local cy=neighbors[i+1][2] 42 | A1={i>1 and 2*i-1 or 2*neighborCount-1, 2*neighborCount} 43 | A2={2*i+1, 2*neighborCount} 44 | 45 | blocks =not self:_lightPasses(cx, cy) 46 | visibility=self:_checkVisibility(A1, A2, blocks, SHADOWS) 47 | if visibility>0 then callback(cx, cy, r, visibility) end 48 | if #SHADOWS==2 and SHADOWS[1][1]==0 and SHADOWS[2][1]==SHADOWS[2][2] then 49 | break 50 | end 51 | end 52 | end 53 | end 54 | 55 | function Precise:_checkVisibility(A1, A2, blocks, SHADOWS) 56 | if A1[1]>A2[1] then 57 | local v1=self:_checkVisibility(A1, {A1[2], A1[2]}, blocks, SHADOWS) 58 | local v2=self:_checkVisibility({0, 1}, A2, blocks, SHADOWS) 59 | return (v1+v2)/2 60 | end 61 | local index1=1 62 | local edge1 =false 63 | while index1<=#SHADOWS do 64 | local old =SHADOWS[index1] 65 | local diff=old[1]*A1[2] - A1[1]*old[2] 66 | if diff>=0 then 67 | if diff==0 and (index1)%2==1 then edge1=true end 68 | break 69 | end 70 | index1=index1+1 71 | end 72 | 73 | local index2=#SHADOWS 74 | local edge2=false 75 | while index2>0 do 76 | local old =SHADOWS[index2] 77 | local diff=A2[1]*old[2] - old[1]*A2[2] 78 | if diff >= 0 then 79 | if diff==0 and (index2)%2==0 then edge2=true end 80 | break 81 | end 82 | index2=index2-1 83 | end 84 | local visible=true 85 | if index1==index2 and (edge1 or edge2) then 86 | visible=false 87 | elseif edge1 and edge2 and index1+1==index2 and (index2)%2==0 then 88 | visible=false 89 | elseif index1>index2 and (index1)%2==0 then 90 | visible=false 91 | end 92 | if not visible then return 0 end 93 | local visibleLength=0 94 | local remove=index2-index1+1 95 | if remove%2==1 then 96 | if (index1)%2==0 then 97 | if #SHADOWS>0 then 98 | local P=SHADOWS[index1] 99 | visibleLength=(A2[1]*P[2] - P[1]*A2[2]) / (P[2]*A2[2]) 100 | end 101 | if blocks then splice(SHADOWS, index1, remove, {A2}) end 102 | else 103 | if #SHADOWS>0 then 104 | local P=SHADOWS[index2] 105 | visibleLength=(P[1]*A1[2] - A1[1]*P[2]) / (A1[2]*P[2]) 106 | end 107 | if blocks then splice(SHADOWS, index1, remove, {A1}) end 108 | end 109 | else 110 | if (index1)%2==0 then 111 | if #SHADOWS>0 then 112 | local P1=SHADOWS[index1] 113 | local P2=SHADOWS[index2] 114 | visibleLength=(P2[1]*P1[2] - P1[1]*P2[2]) / (P1[2]*P2[2]) 115 | end 116 | if blocks then splice(SHADOWS, index1, remove) end 117 | else 118 | if blocks then splice(SHADOWS, index1, remove, {A1, A2}) end 119 | return 1 120 | end 121 | end 122 | 123 | local arcLength=(A2[1]*A1[2] - A1[1]*A2[2]) / (A1[2]*A2[2]) 124 | return visibleLength/arcLength 125 | end 126 | 127 | function splice(t, i, rn, it) -- table, index, numberToRemove, insertTable 128 | if rn>0 then 129 | for j=1,rn do 130 | table.remove(t, i) 131 | end 132 | end 133 | if it and #it>0 then 134 | for idx=i,i+#it-1 do 135 | local el=table.remove(it, 1) 136 | if el then table.insert(t, idx, el) end 137 | end 138 | end 139 | end 140 | 141 | return Precise 142 | -------------------------------------------------------------------------------- /rotLove/src/rng.lua: -------------------------------------------------------------------------------- 1 | --- The RNG Prototype. 2 | -- The base class that is extended by all rng classes 3 | -- @module ROT.RNG 4 | 5 | local RNG_PATH =({...})[1]:gsub("[%.\\/]rng$", "") .. '/' 6 | local class =require (RNG_PATH .. 'vendor/30log') 7 | 8 | local RNG=class { } 9 | RNG.__name='RNG' 10 | 11 | function RNG:normalize(n) --keep numbers at (positive) 32 bits 12 | return n % 0x80000000 13 | end 14 | 15 | function RNG:bit_and(a, b) 16 | local r = 0 17 | local m = 0 18 | for m = 0, 31 do 19 | if (a % 2 == 1) and (b % 2 == 1) then r = r + 2^m end 20 | if a % 2 ~= 0 then a = a - 1 end 21 | if b % 2 ~= 0 then b = b - 1 end 22 | a = a / 2 b = b / 2 23 | end 24 | return self:normalize(r) 25 | end 26 | 27 | function RNG:bit_or(a, b) 28 | local r = 0 29 | local m = 0 30 | for m = 0, 31 do 31 | if (a % 2 == 1) or (b % 2 == 1) then r = r + 2^m end 32 | if a % 2 ~= 0 then a = a - 1 end 33 | if b % 2 ~= 0 then b = b - 1 end 34 | a = a / 2 b = b / 2 35 | end 36 | return self:normalize(r) 37 | end 38 | 39 | function RNG:bit_xor(a, b) 40 | local r = 0 41 | local m = 0 42 | for m = 0, 31 do 43 | if a % 2 ~= b % 2 then r = r + 2^m end 44 | if a % 2 ~= 0 then a = a - 1 end 45 | if b % 2 ~= 0 then b = b - 1 end 46 | a = a / 2 b = b / 2 47 | end 48 | return self:normalize(r) 49 | end 50 | 51 | function RNG:random(a,b) 52 | return math.random(a,b) 53 | end 54 | 55 | function RNG:getWeightedValue(tbl) 56 | local total=0 57 | for _,v in pairs(tbl) do 58 | total=total+v 59 | end 60 | local rand=self:random()*total 61 | local part=0 62 | for k,v in pairs(tbl) do 63 | part=part+v 64 | if randy0 then 70 | i1=1 71 | j1=0 72 | else 73 | i1=0 74 | j1=1 75 | end 76 | 77 | local x1=x0-i1+G2 78 | local y1=y0-j1+G2 79 | local x2=x0-1+2*G2 80 | local y2=y0-1+2*G2 81 | 82 | local ii=i%count+1 83 | local jj=j%count+1 84 | 85 | local t0=.5- x0*x0 - y0*y0 86 | if t0>=0 then 87 | t0=t0*t0 88 | gi=indexes[ii+perms[jj]] 89 | local grad=self._gradients[gi] 90 | n0=t0*t0*(grad[1]*x0+grad[2]*y0) 91 | end 92 | 93 | local t1=.5- x1*x1 - y1*y1 94 | if t1>=0 then 95 | t1=t1*t1 96 | gi=indexes[ii+i1+perms[jj+j1]] 97 | local grad=self._gradients[gi] 98 | n1=t1*t1*(grad[1]*x1+grad[2]*y1) 99 | end 100 | 101 | local t2=.5- x2*x2 - y2*y2 102 | if t2>=0 then 103 | t2=t2*t2 104 | gi=indexes[ii+1+perms[jj+1]] 105 | local grad=self._gradients[gi] 106 | n2=t2*t2*(grad[1]*x2+grad[2]*y2) 107 | end 108 | return 70*(n0+n1+n2) 109 | end 110 | 111 | return Simplex 112 | -------------------------------------------------------------------------------- /rotLove/src/speed.lua: -------------------------------------------------------------------------------- 1 | --- The Speed based scheduler 2 | -- @module ROT.Scheduler.Speed 3 | local Speed_Path =({...})[1]:gsub("[%.\\/]speed$", "") .. '/' 4 | local class =require (Speed_Path .. 'vendor/30log') 5 | 6 | local Speed= ROT.Scheduler:extends { } 7 | Speed.__name='Speed' 8 | --- Add. 9 | -- Add an item to the schedule 10 | -- @tparam userdata item Any class/module/userdata with a :getSpeed() function. The value returned by getSpeed() should be a number. 11 | -- @tparam boolean repeating If true, this item will be rescheduled once it is returned by .next() 12 | -- @treturn ROT.Scheduler.Speed self 13 | function Speed:add(item, repeating) 14 | self._queue:add(item, 1/item:getSpeed()) 15 | return Speed.super.add(self, item, repeating) 16 | end 17 | 18 | --- Next. 19 | -- Get the next item from the scheduler and advance the appropriate amount time 20 | -- @treturn item|nil The item previously added by .add() or nil if none are queued 21 | function Speed:next() 22 | if self._current and table.indexOf(self._repeat, self._current)~=0 then 23 | self._queue:add(self._current, 1/self._current:getSpeed()) 24 | end 25 | return Speed.super.next(self) 26 | end 27 | 28 | return Speed 29 | 30 | --- Get Time. 31 | -- Get time counted since start 32 | -- @treturn int elapsed time 33 | -- @function Speed:getTime() 34 | 35 | --- Clear. 36 | -- Remove all items from scheduler 37 | -- @treturn ROT.Scheduler.Speed self 38 | -- @function Speed:clear() 39 | 40 | --- Remove. 41 | -- Find and remove an item from the scheduler 42 | -- @tparam any item The previously added item to be removed 43 | -- @treturn boolean true if an item was removed from the scheduler 44 | -- @function Speed:remove(item) 45 | -------------------------------------------------------------------------------- /rotLove/src/stringGenerator.lua: -------------------------------------------------------------------------------- 1 | --- Random String Generator. 2 | -- Learns from provided strings, and generates similar strings. 3 | -- @module ROT.StringGenerator 4 | local StringGen_Path =({...})[1]:gsub("[%.\\/]stringGenerator$", "") .. '/' 5 | local class =require (StringGen_Path .. 'vendor/30log') 6 | 7 | local StringGenerator = class { 8 | __name, 9 | _options, 10 | _boundary, 11 | _suffix, 12 | _prefix, 13 | _priorValues, 14 | _data, 15 | _rng 16 | } 17 | StringGenerator.__name='StringGenerator' 18 | 19 | --- Constructor. 20 | -- Called with ROT.StringGenerator:new() 21 | -- @tparam table options A table with the following fields: 22 | -- @tparam[opt=false] boolean options.words Use word mode 23 | -- @tparam[opt=3] int options.order Number of letters/words to be used as context 24 | -- @tparam[opt=0.001] number options.prior A default priority for characters/words 25 | -- @tparam userdata rng Userdata with a .random(self, min, max) function 26 | function StringGenerator:__init(options, rng) 27 | self._options = {words=false, 28 | order=3, 29 | prior=0.001 30 | } 31 | self._boundary=string.char(0) 32 | self._suffix =string.char(0) 33 | self._prefix ={} 34 | self._priorValues={} 35 | self._data ={} 36 | if options then 37 | for k,v in pairs(options) do 38 | self._options.k=v 39 | end 40 | end 41 | for i=1,self._options.order do 42 | table.insert(self._prefix, self._boundary) 43 | end 44 | self._priorValues[self._boundary]=self._options.prior 45 | 46 | self._rng=rng and rng or ROT.RNG.Twister:new() 47 | if not rng then self._rng:randomseed() end 48 | end 49 | 50 | --- Remove all learned data 51 | function StringGenerator:clear() 52 | self._data={} 53 | self._priorValues={} 54 | end 55 | 56 | --- Generate a string 57 | -- @treturn string The generated string 58 | function StringGenerator:generate() 59 | local result={self:_sample(self._prefix)} 60 | local i=0 61 | while result[#result] ~= self._boundary do 62 | table.insert(result, self:_sample(result)) 63 | end 64 | table.remove(result) 65 | return table.concat(result) 66 | end 67 | 68 | --- Observe 69 | -- Learn from a string 70 | -- @tparam string s The string to observe 71 | function StringGenerator:observe(s) 72 | local tokens = self:_split(s) 73 | for i=1,#tokens do 74 | self._priorValues[tokens[i]] = self._options.prior 75 | end 76 | local i=1 77 | for k,v in pairs(self._prefix) do 78 | table.insert(tokens, i, v) 79 | i=i+1 80 | end 81 | table.insert(tokens, self._suffix) 82 | for i=self._options.order,#tokens-1 do 83 | local context=table.slice(tokens, i-self._options.order+1, i) 84 | local evt = tokens[i+1] 85 | for j=1,#context do 86 | local subcon=table.slice(context, j) 87 | self:_observeEvent(subcon, evt) 88 | end 89 | end 90 | end 91 | 92 | --- get Stats 93 | -- Get info about learned strings 94 | -- @treturn string Number of observed strings, number of contexts, number of possible characters/words 95 | function StringGenerator:getStats() 96 | local parts={} 97 | local prC=0 98 | for k,_ in pairs(self._priorValues) do 99 | prC = prC + 1 100 | end 101 | prC=prC-1 102 | table.insert(parts, 'distinct samples: '..prC) 103 | local dataC=0 104 | local evtCount=0 105 | for k,_ in pairs(self._data) do 106 | dataC=dataC+1 107 | for _,_ in pairs(self._data[k]) do 108 | evtCount=evtCount+1 109 | end 110 | end 111 | table.insert(parts, 'dict size(cons): '..dataC) 112 | table.insert(parts, 'dict size(evts): '..evtCount) 113 | return table.concat(parts, ', ') 114 | end 115 | 116 | function StringGenerator:_split(str) 117 | return str:split(self._options.words and " " or "") 118 | end 119 | 120 | function StringGenerator:_join(arr) 121 | return table.concat(arr, self._options.words and " " or "") 122 | end 123 | 124 | function StringGenerator:_observeEvent(context, event) 125 | local key=self:_join(context) 126 | if not self._data[key] then 127 | self._data[key] = {} 128 | end 129 | if not self._data[key][event] then 130 | self._data[key][event] = 0 131 | end 132 | self._data[key][event]=self._data[key][event]+1 133 | end 134 | function StringGenerator:_sample(context) 135 | context =self:_backoff(context) 136 | local key =self:_join(context) 137 | local data=self._data[key] 138 | local avail={} 139 | if self._options.prior then 140 | for k,_ in pairs(self._priorValues) do 141 | avail[k] = self._priorValues[k] 142 | end 143 | for k,_ in pairs(data) do 144 | avail[k] = avail[k]+data[k] 145 | end 146 | else 147 | avail=data 148 | end 149 | return self:_pickRandom(avail) 150 | end 151 | 152 | function StringGenerator:_backoff(context) 153 | local ctx = {} 154 | for i=1,#context do ctx[i]=context[i] end 155 | if #ctx > self._options.order then 156 | while #ctx > self._options.order do table.remove(ctx, 1) end 157 | elseif #ctx < self._options.order then 158 | while #ctx < self._options.order do table.insert(ctx,1,self._boundary) end 159 | end 160 | while not self._data[self:_join(ctx)] and #ctx>0 do 161 | ctx=table.slice(ctx, 2) 162 | end 163 | 164 | return ctx 165 | end 166 | 167 | function StringGenerator:_pickRandom(data) 168 | local total =0 169 | for k,_ in pairs(data) do 170 | total=total+data[k] 171 | end 172 | rand=self._rng:random()*total 173 | i=0 174 | for k,_ in pairs(data) do 175 | i=i+data[k] 176 | if (rand'):format((rawget(getmetatable(self),'__name') or 'Unnamed'), _instances[self]) end 46 | return _classes[self] and ('class (%s): <%s>'):format((rawget(self,'__name') or 'Unnamed'),_classes[self]) or self 47 | end} 48 | class = function(attr) 49 | local c = deep_copy(attr) ; _classes[c] = tostring(c) 50 | c.new, c.extends, c.__index, c.__call, c.__tostring = instantiate, extends, c, baseMt.__call, baseMt.__tostring 51 | return setmetatable(c,baseMt) 52 | end 53 | return class 54 | -------------------------------------------------------------------------------- /rsfov.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | zlib License: 3 | 4 | Copyright (c) 2014 Minh Ngo 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 22 | 3. This notice may not be removed or altered from any source 23 | distribution. 24 | --]] 25 | 26 | -- Based on recursive shadowcasting by Björn Bergström 27 | 28 | local octants = { 29 | function(x,y) return x,y end, 30 | function(x,y) return y,x end, 31 | function(x,y) return -y,x end, 32 | function(x,y) return -x,y end, 33 | function(x,y) return -x,-y end, 34 | function(x,y) return -y,-x end, 35 | function(x,y) return y,-x end, 36 | function(x,y) return x,-y end, 37 | } 38 | 39 | local tau = 2*math.pi 40 | local octant_angle= math.pi / 4 41 | local epsilon = 1e-5 42 | 43 | 44 | local fov = function(x0,y0,radius,isTransparent,onVisible,start_angle,last_angle) 45 | -- **NOTE** Assumed orientation in notes is x+ right, y+ up 46 | 47 | --[[ 48 | Octant designation 49 | \ | / 50 | 4\3|2/1 51 | ____\|/____ 52 | /|\ 53 | 5/6|7\8 54 | / | \ 55 | 56 | All calculations are done on the first octant 57 | To calculate FOV on other octants, reflect the cells onto the first octant 58 | 59 | The bottom left corner is the coordinates of a cell: 60 | 61 | (0,1)------(1,1) 62 | |Cell| 63 | |0,0 | 64 | (0,0)------(1,0) 65 | 66 | **ARC NOTE** 67 | 68 | The arc angle of vision defaults to 360 degrees. 69 | Arc angle is measured counterclockwise from starting to last. 70 | The shortest arc is used so if start = 0 and last = 370 deg then arc angle = 10 deg. 71 | To get full field of view, add 2*math.pi to starting angle. This is 72 | the only way to get a full view. Any other case will result in the 73 | smallest arc possible. 74 | 75 | For example: 76 | start = 0 ; last = 0 --> line field of view 77 | start = 0 ; last = 2*math.pi --> full field of view 78 | ]] 79 | 80 | start_angle = start_angle or 0 81 | last_angle = last_angle or tau 82 | local arc_angle= (last_angle-start_angle) 83 | -- Clamp angles or else some checks won't work correctly 84 | if arc_angle - tau > epsilon or arc_angle < 0 then arc_angle = arc_angle % tau end 85 | start_angle = start_angle % tau 86 | last_angle = last_angle % tau 87 | 88 | -- Angle interval of first octant [inclusive,exclusive) 89 | -- Touching the end of the interval moves onto the next octant 90 | -- Example: [0,pi/4) 91 | local first_octant = (math.floor(start_angle / octant_angle) % 8) + 1 92 | -- Angle interval of last octant (exclusive,inclusive] 93 | -- Touching the beginning of the interval moves to the prev octant 94 | -- Example: (0,pi/4] 95 | local last_octant = ((math.ceil(last_angle / octant_angle) - 1) % 8) + 1 96 | 97 | -- Hack to make large angles work when start/last are in the same octant 98 | if last_octant == first_octant and arc_angle > octant_angle then 99 | first_octant = (first_octant % 8) + 1 100 | end 101 | 102 | local octant = first_octant - 1 103 | 104 | -- Always see the origin 105 | onVisible(x0,y0) 106 | 107 | repeat 108 | octant = (octant % 8) + 1 109 | local coords = octants[octant] 110 | local views = {} 111 | -- A view is represented by two lines (steep & shallow) 112 | views[1] = { 113 | -- {x,y,x2,y2} 114 | steep = {0.5,0.5,0,radius}, 115 | shallow = {0.5,0.5,radius,0}, 116 | } 117 | for x = 1,radius do 118 | if not views[1] then break end 119 | -- Process all remaining views 120 | -- Iterate backward to be able to delete views 121 | for i = #views,1,-1 do 122 | local prev_cell_solid= false 123 | local view = views[i] 124 | local steep,shallow = view.steep,view.shallow 125 | 126 | -- Calculate the maxmimum and minimum height of the column to scan 127 | -- y = slope * dx + y0 128 | local yi,yf 129 | 130 | -- Don't calculate if the view lines didn't change 131 | if steep[3] > steep[1] then 132 | local steep_slope = (steep[4]-steep[2]) / (steep[3]-steep[1]) 133 | yi = math.floor( steep[2] + steep_slope*(x-steep[1]) ) 134 | else 135 | yi = x 136 | end 137 | 138 | if shallow[4] > shallow[2] then 139 | local shallow_slope = (shallow[4]-shallow[2]) / (shallow[3]-shallow[1]) 140 | yf = math.floor( shallow[2] + shallow_slope*(x-shallow[1]) ) 141 | else 142 | yf = 0 143 | end 144 | 145 | for y = yi,yf,-1 do 146 | local tx,ty = coords(x,y) 147 | 148 | -- The tile is visible if it is within the cone field of view 149 | if arc_angle >= tau or arc_angle >= (math.atan2(ty,tx)-start_angle) % tau then 150 | onVisible( x0+tx,y0+ty ) 151 | end 152 | -- Found a blocking cell 153 | if not isTransparent( x0+tx,y0+ty ) then 154 | -- If the previous cell is non blocking 155 | -- and it is not the first cell then 156 | -- add another view for the remaining columns 157 | if not prev_cell_solid and y < yi then 158 | local new_view = { 159 | -- Inherit the current view steep line 160 | steep = {steep[1],steep[2],steep[3],steep[4]}, 161 | -- Shallow line bumps into top left corner of block 162 | shallow = {shallow[1],shallow[2],x,y+1}, 163 | } 164 | 165 | table.insert(views,new_view) 166 | end 167 | 168 | prev_cell_solid = true 169 | elseif prev_cell_solid then 170 | -- Cell is transparent and moving from blocking to non-blocking 171 | -- Readjust steep line to steep bump 172 | steep[3],steep[4]= x+1,y+1 173 | prev_cell_solid = false 174 | end 175 | end 176 | 177 | -- Remove the view if the last cell is blocking 178 | if prev_cell_solid then 179 | table.remove(views,i) 180 | end 181 | end 182 | end 183 | until octant == last_octant 184 | end 185 | 186 | return fov --------------------------------------------------------------------------------