238 lines
5.7 KiB
Lua
238 lines
5.7 KiB
Lua
|
-- an experimental lighting engine for df
|
||
|
local gui = require 'gui'
|
||
|
local guidm = require 'gui.dwarfmode'
|
||
|
local render = require 'plugins.rendermax'
|
||
|
|
||
|
local levelDim=0.05
|
||
|
local tile_attrs = df.tiletype.attrs
|
||
|
|
||
|
local args={...}
|
||
|
|
||
|
function setCell(x,y,cell)
|
||
|
cell=cell or {}
|
||
|
cell.fm=cell.fm or {r=1,g=1,b=1}
|
||
|
cell.bm=cell.bm or {r=1,g=1,b=1}
|
||
|
cell.fo=cell.fo or {r=0,g=0,b=0}
|
||
|
cell.bo=cell.bo or {r=0,g=0,b=0}
|
||
|
render.setCell(x,y,cell)
|
||
|
end
|
||
|
function getCursorPos()
|
||
|
local g_cursor=df.global.cursor
|
||
|
if g_cursor.x ~= -30000 then
|
||
|
return copyall(g_cursor)
|
||
|
end
|
||
|
end
|
||
|
function falloff(color,sqDist,maxdist)
|
||
|
local v1=1/(sqDist/maxdist+1)
|
||
|
local v2=v1-1/(1+maxdist*maxdist)
|
||
|
local v=v2/(1-1/(1+maxdist*maxdist))
|
||
|
return {r=v*color.r,g=v*color.g,b=v*color.b}
|
||
|
end
|
||
|
function blend(c1,c2)
|
||
|
return {r=math.max(c1.r,c2.r),
|
||
|
g=math.max(c1.g,c2.g),
|
||
|
b=math.max(c1.b,c2.b)}
|
||
|
end
|
||
|
LightOverlay=defclass(LightOverlay,guidm.DwarfOverlay)
|
||
|
LightOverlay.ATTRS {
|
||
|
lightMap={},
|
||
|
dynamic=true
|
||
|
}
|
||
|
function LightOverlay.init(args)
|
||
|
end
|
||
|
|
||
|
function lightPassable(shape)
|
||
|
if shape==df.tiletype_shape.WALL or
|
||
|
shape==df.tiletype_shape.BROOK_BED or
|
||
|
shape==df.tiletype_shape.TREE then
|
||
|
return false
|
||
|
else
|
||
|
return true
|
||
|
end
|
||
|
end
|
||
|
function LightOverlay:placeLightFov(pos,radius,color,f,rays)
|
||
|
f=f or falloff
|
||
|
local raycount=rays or 25
|
||
|
local vp=self:getViewport()
|
||
|
local map = self.df_layout.map
|
||
|
local off=math.random(0,math.pi)
|
||
|
local done={}
|
||
|
for d=0,math.pi*2,math.pi*2/raycount do
|
||
|
local dx,dy
|
||
|
dx=math.cos(d+off)
|
||
|
dy=math.sin(d+off)
|
||
|
local cx=0
|
||
|
local cy=0
|
||
|
|
||
|
for dt=0,radius,0.01 do
|
||
|
if math.abs(math.floor(dt*dx)-cx)>0 or math.abs(math.floor(dt*dy)-cy)> 0then
|
||
|
local x=cx+pos.x
|
||
|
local y=cy+pos.y
|
||
|
|
||
|
if x>0 and y>0 and x<=map.width and y<=map.height and not done[tile] then
|
||
|
local tile=x+y*map.width
|
||
|
done[tile]=true
|
||
|
local ncol=f(color,dt*dt,radius)
|
||
|
local ocol=self.lightMap[tile] or {r=0,g=0,b=0}
|
||
|
ncol=blend(ncol,ocol)
|
||
|
self.lightMap[tile]=ncol
|
||
|
|
||
|
|
||
|
if --(ncol.r==ocol.r and ncol.g==ocol.g and ncol.b==ocol.b) or
|
||
|
not self.ocupancy[tile] then
|
||
|
break
|
||
|
end
|
||
|
end
|
||
|
cx=math.floor(dt*dx)
|
||
|
cy=math.floor(dt*dy)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
function LightOverlay:placeLight(pos,radius,color,f)
|
||
|
f=f or falloff
|
||
|
local vp=self:getViewport()
|
||
|
local map = self.df_layout.map
|
||
|
|
||
|
for i=-radius,radius do
|
||
|
for j=-radius,radius do
|
||
|
local x=pos.x+i+1
|
||
|
local y=pos.y+j+1
|
||
|
if x>0 and y>0 and x<=map.width and y<=map.height then
|
||
|
local tile=x+y*map.width
|
||
|
local ncol=f(color,(i*i+j*j),radius)
|
||
|
local ocol=self.lightMap[tile] or {r=0,g=0,b=0}
|
||
|
self.lightMap[tile]=blend(ncol,ocol)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
function LightOverlay:calculateLightLava()
|
||
|
local vp=self:getViewport()
|
||
|
local map = self.df_layout.map
|
||
|
for i=map.x1,map.x2 do
|
||
|
for j=map.y1,map.y2 do
|
||
|
local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z}
|
||
|
local pos2={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z-1}
|
||
|
local t1=dfhack.maps.getTileFlags(pos)
|
||
|
local shape=tile_attrs[dfhack.maps.getTileType(pos)].shape
|
||
|
local t2=dfhack.maps.getTileFlags(pos2)
|
||
|
if (t1 and t1.liquid_type and t1.flow_size>0) or
|
||
|
(shape==df.tiletype_shape.EMPTY and t2 and t2.liquid_type and t2.flow_size>0) then
|
||
|
--self:placeLight({x=i,y=j},5,{r=0.8,g=0.2,b=0.2})
|
||
|
self:placeLightFov({x=i,y=j},5,{r=0.8,g=0.2,b=0.2},nil,5)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
function LightOverlay:calculateLightSun()
|
||
|
local vp=self:getViewport()
|
||
|
local map = self.df_layout.map
|
||
|
for i=map.x1,map.x2+1 do
|
||
|
for j=map.y1,map.y2+1 do
|
||
|
local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z}
|
||
|
|
||
|
local t1=dfhack.maps.getTileFlags(pos)
|
||
|
|
||
|
if (t1 and t1.outside ) then
|
||
|
|
||
|
self:placeLightFov({x=i,y=j},7,{r=1,g=1,b=1},nil,3)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
function LightOverlay:calculateLightCursor()
|
||
|
local c=getCursorPos()
|
||
|
|
||
|
if c then
|
||
|
|
||
|
local vp=self:getViewport()
|
||
|
local pos=vp:tileToScreen(c)
|
||
|
--self:placeLight(pos,11,{r=0.96,g=0.84,b=0.03})
|
||
|
self:placeLightFov({x=pos.x+1,y=pos.y+1},11,{r=0.96,g=0.84,b=0.03})
|
||
|
|
||
|
end
|
||
|
end
|
||
|
function LightOverlay:buildOcupancy()
|
||
|
self.ocupancy={}
|
||
|
local vp=self:getViewport()
|
||
|
local map = self.df_layout.map
|
||
|
for i=map.x1,map.x2+1 do
|
||
|
for j=map.y1,map.y2+1 do
|
||
|
local pos={x=i+vp.x1-1,y=j+vp.y1-1,z=vp.z}
|
||
|
local tile=i+j*map.width
|
||
|
local tt=dfhack.maps.getTileType(pos)
|
||
|
|
||
|
if tt then
|
||
|
local shape=tile_attrs[tt].shape
|
||
|
self.ocupancy[tile]=lightPassable(shape)
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
end
|
||
|
function LightOverlay:makeLightMap()
|
||
|
self.lightMap={}
|
||
|
self:buildOcupancy()
|
||
|
self:calculateLightCursor()
|
||
|
self:calculateLightLava()
|
||
|
self:calculateLightSun()
|
||
|
end
|
||
|
function LightOverlay:onIdle()
|
||
|
self._native.parent:logic()
|
||
|
if self.dynamic then
|
||
|
self:makeLightMap()
|
||
|
end
|
||
|
end
|
||
|
function LightOverlay:render(dc)
|
||
|
|
||
|
|
||
|
self:renderParent()
|
||
|
local vp=self:getViewport()
|
||
|
local map = self.df_layout.map
|
||
|
|
||
|
self.lightMap=self.lightMap or {}
|
||
|
render.lockGrids()
|
||
|
df.global.gps.force_full_display_count=df.global.gps.force_full_display_count+1
|
||
|
render.resetGrids()
|
||
|
for i=map.x1,map.x2 do
|
||
|
for j=map.y1,map.y2 do
|
||
|
local v=self.lightMap[i+j*map.width]
|
||
|
if v then
|
||
|
setCell(i,j,{fm=v,bm=v})
|
||
|
else
|
||
|
local dimRgb={r=levelDim,g=levelDim,b=levelDim}
|
||
|
setCell(i,j,{fm=dimRgb,bm=dimRgb})
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
render.unlockGrids()
|
||
|
|
||
|
end
|
||
|
function LightOverlay:onDismiss()
|
||
|
render.lockGrids()
|
||
|
render.resetGrids()
|
||
|
render.unlockGrids()
|
||
|
df.global.gps.force_full_display_count=df.global.gps.force_full_display_count+1
|
||
|
end
|
||
|
function LightOverlay:onInput(keys)
|
||
|
if keys.LEAVESCREEN then
|
||
|
self:dismiss()
|
||
|
else
|
||
|
self:sendInputToParent(keys)
|
||
|
|
||
|
if keys.CHANGETAB then
|
||
|
self:updateLayout()
|
||
|
end
|
||
|
if keys.STRING_A126 and not self.dynamic then
|
||
|
self:makeLightMap()
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
if not render.isEnabled() then
|
||
|
qerror("Lua rendermode not enabled!")
|
||
|
end
|
||
|
local dyn=true
|
||
|
if #args>0 and args[1]=="static" then dyn=false end
|
||
|
local lview = LightOverlay{ dynamic=dyn}
|
||
|
lview:show()
|