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
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.
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 |
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
--------------------------------------------------------------------------------