Lignum

ccLightSim

Apr 16th, 2014
137
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 9.31 KB | None | 0 0
  1. --[[ This program is a simple simulation of light.
  2.      It's not supposed to have a special purpose.
  3.      It was made for other people to use in their programs. ]]
  4.  
  5. os.unloadAPI(shell.resolveProgram("redirect"))
  6. os.loadAPI(shell.resolveProgram("redirect"))
  7.  
  8. local map = {{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1},{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}}
  9.  
  10. local scrWidth,scrHeight = term.getSize()
  11. local buffer = redirect.createRedirectBuffer(scrWidth, scrHeight)
  12.  
  13. local args = { ... }
  14.  
  15. if #args >= 1 then
  16.     local mapFile = args[1]
  17.  
  18.     if fs.exists(mapFile) then
  19.         map = paintutils.loadImage(shell.resolve(mapFile))
  20.     end
  21.  
  22.     for y=1,#map do
  23.         for x=1,#map[y] do
  24.             if map[y][x] == colours.black or map[y][x] == 0 then
  25.                 map[y][x] = 0
  26.             elseif map[y][x] == colours.white then
  27.                 map[y][x] = 1
  28.             elseif map[y][x] == colours.purple then
  29.                 map[y][x] = 2
  30.             end
  31.         end
  32.     end
  33. end
  34.  
  35. local function canMoveTo(x, y)
  36.     if x > #map[1] or x < 1 or y > #map or y < 1 then return false end
  37.     return map[y][x] == 0
  38. end
  39.  
  40. local function isReflective(x, y)
  41.     return (not canMoveTo(x, y)) and map[y][x] == 2
  42. end
  43.  
  44. local function swap(a, b)
  45.     local temp = b
  46.     b = a
  47.     a = temp
  48.     return a, b
  49. end
  50.  
  51. --[[ A modified version of paintutils's drawLine. ]]
  52. local function line(startX, startY, endX, endY)
  53.     local points = {}
  54.  
  55.     startX = math.floor(startX)
  56.     startY = math.floor(startY)
  57.     endX = math.floor(endX)
  58.     endY = math.floor(endY)
  59.  
  60.     if startX == endX and startY == endY then
  61.         points[#points + 1] = { posX = startX, posY = startY }
  62.         return points
  63.     end
  64.  
  65.     local minX = math.min(startX, endX)
  66.     local minY, maxX, maxY
  67.  
  68.     if minX == startX then
  69.         minY = startY
  70.         maxX = endX
  71.         maxY = endY
  72.     else
  73.         minY = endY
  74.         maxX = startX
  75.         maxY = startY
  76.     end
  77.  
  78.     local xDiff = maxX - minX
  79.     local yDiff = maxY - minY
  80.  
  81.     if xDiff > math.abs(yDiff) then
  82.         local y = minY
  83.         local dy = yDiff / xDiff
  84.  
  85.         for x=minX,maxX do
  86.             points[#points + 1] = { posX = x, posY = math.floor(y + 0.5) }
  87.             y = y + dy
  88.         end
  89.     else
  90.         local x = minX
  91.         local dx = xDiff / yDiff
  92.  
  93.         if maxY >= minY then
  94.             for y=minY, maxY do
  95.                 points[#points + 1] = { posX = math.floor(x + 0.5), posY = y }
  96.                 x = x + dx
  97.             end
  98.         else
  99.             for y=minY,maxY,-1 do
  100.                 points[#points + 1] = { posX = math.floor(x + 0.5), posY = y }
  101.                 x = x - dx
  102.             end
  103.         end
  104.     end
  105.  
  106.     return points
  107. end
  108.  
  109. local lineBuffer = {}
  110.  
  111. local function addToLineBuffer(pt)
  112.     if pt == nil or pt.x0 == nil or pt.x1 == nil or
  113.         pt.y0 == nil or pt.y1 == nil then return end
  114.  
  115.     for _,v in ipairs(lineBuffer) do
  116.         if v.x0 == pt.x0 and v.y0 == pt.y0 and
  117.             v.x1 == pt.x1 and v.y1 == pt.y1 then
  118.             return
  119.         end
  120.     end
  121.  
  122.     lineBuffer[#lineBuffer + 1] = pt
  123. end
  124.  
  125. local function calculateDirectionalAngles(x, y, dirX, dirY, coneAngle)
  126.     local angle = math.deg(math.atan2(dirY - y, dirX - x))
  127.     local startAngle = angle - coneAngle / 2
  128.     local endAngle = angle + coneAngle / 2
  129.     return startAngle, endAngle
  130. end
  131.  
  132. --[[
  133.  
  134. WIP!
  135. function reflect(x, y, angle, length)
  136.     local refAngle = 0
  137.  
  138.     if (angle <= 90 and angle > -90) or (angle <= 360 and angle > 270) then
  139.         refAngle = angle - 90
  140.     elseif (angle <= 180 and angle > 90) or (angle <= 270 and angle > 180) then
  141.         refAngle = angle + 90
  142.     end
  143.  
  144.     local _x0, _y0, _x1, _y1 = calculateLightRay(x, y, refAngle, length, true)
  145.     addToLineBuffer({ x0 = _x0, y0 = _y0, x1 = _x1, y1 = _y1 })
  146. end
  147.  
  148. ]]
  149.  
  150. function calculateLightRay(x, y, angle, length, fromReflect)
  151.     if fromReflect == nil then fromReflect = false end
  152.  
  153.     local tx = x + math.cos(math.rad(angle)) * length
  154.     local ty = y + math.sin(math.rad(angle)) * length
  155.  
  156.     local pts = line(x, y, tx, ty)
  157.  
  158.     if angle == 0 then return nil end
  159.  
  160.     if (angle > -89 and angle <= 90) or angle > 270 then
  161.         for i=1,#pts do
  162.             if i + 1 < #pts and not canMoveTo(pts[i + 1].posX, pts[i + 1].posY) then
  163.                 --[[if (not fromReflect) and isReflective(pts[i + 1].posX, pts[i + 1].posY) then
  164.                     reflect(pts[i].posX, pts[i].posY, angle, length)
  165.                 end]]
  166.                 return x, y, pts[i].posX, pts[i].posY  
  167.             end
  168.         end
  169.     end
  170.  
  171.     if (angle < 270 and angle > 90) or angle < -90 then
  172.         for i=#pts,1,-1 do
  173.             if i - 1 > 0 and not canMoveTo(pts[i - 1].posX, pts[i - 1].posY) then
  174.                 --[[if (not fromReflect) and isReflective(pts[i - 1].posX, pts[i - 1].posY) then
  175.                     reflect(pts[i].posX, pts[i].posY, angle, length)
  176.                 end]]
  177.                 return pts[i].posX, pts[i].posY, x, y
  178.             end
  179.         end
  180.     end
  181.  
  182.     return nil
  183. end
  184.  
  185. local pointAt = { x = 0, y = 0 }
  186. local lightStartAngle = 0
  187. local lightEndAngle = 360
  188. local lightConeSize = 90
  189.  
  190. local player = {
  191.     x = 7,
  192.     y = 2,
  193. }
  194.  
  195. local function drawPixel(x, y, colour)
  196.     buffer.setCursorPos(x, y)
  197.     buffer.setBackgroundColour(colour)
  198.     buffer.write(" ")
  199. end
  200.  
  201. local function drawLine(x0, y0, x1, y1, colour)
  202.     local ln = line(x0, y0, x1, y1, colour)
  203.     for _,v in ipairs(ln) do
  204.         drawPixel(v.posX, v.posY, colour)
  205.     end
  206. end
  207.  
  208. local function hasBeenPlaced(t, pt)
  209.     for _,v in ipairs(t) do
  210.         if v.posX == pt.posX and v.posY == pt.posY then
  211.             return true
  212.         end
  213.     end
  214.  
  215.     return false
  216. end
  217.  
  218. local function hasBeenPlacedC(t, x, y)
  219.     return hasBeenPlaced(t, { posX = x, posY = y })
  220. end
  221.  
  222. local function renderLineBuffer()
  223.     for _,v in ipairs(lineBuffer) do
  224.         if v.x0 ~= nil then
  225.             drawLine(v.x0, v.y0, v.x1, v.y1, colours.yellow)
  226.         end
  227.     end
  228. end
  229.  
  230. local function clearLineBuffer()
  231.     lineBuffer = {}
  232. end
  233.  
  234. local function render()
  235.     buffer.setBackgroundColour(colours.grey)
  236.     buffer.clear()
  237.  
  238.     clearLineBuffer()
  239.  
  240.     for i=lightStartAngle,lightEndAngle do
  241.         local _x0, _y0, _x1, _y1 = calculateLightRay(player.x, player.y, i, #map[1] + 1)
  242.         addToLineBuffer({ x0 = _x0, y0 = _y0, x1 = _x1, y1 = _y1 })
  243.     end
  244.  
  245.     renderLineBuffer()
  246.  
  247.     for y=1,#map do
  248.         for x=1,#map[y] do
  249.             if map[y][x] == 1 then
  250.                 drawPixel(x, y, colours.white)
  251.             elseif map[y][x] == 2 then
  252.                 drawPixel(x, y, colours.purple)
  253.             end
  254.         end
  255.     end
  256.  
  257.     if pointAt.x > 0 and pointAt.y > 0 and pointAt.x <= scrWidth and pointAt.y <= scrHeight then
  258.         drawPixel(pointAt.x, pointAt.y, colours.green)
  259.     end
  260.  
  261.     buffer.setBackgroundColour(colours.grey)
  262.     buffer.setTextColour(colours.white)
  263.     buffer.setCursorPos(1, scrHeight - 2)
  264.     buffer.write("Q: Quit")
  265.     buffer.setCursorPos(1, scrHeight - 1)
  266.     buffer.write("Arrow keys: Move light; Cone size: " .. lightConeSize .. "deg (+/-)")
  267.     buffer.setCursorPos(1, scrHeight)
  268.     buffer.write("Middle Mouse: Point to click pos.; R: Reset")
  269.  
  270.     drawPixel(player.x, player.y, colours.orange)
  271.  
  272.     buffer.blit(1, 1, 1, 1, scrWidth, scrHeight)
  273. end
  274.  
  275. local function update(e, p1, p2, p3, p4, p5)
  276.     if e == "key" then
  277.         if p1 == keys.right and canMoveTo(player.x + 1, player.y) then
  278.             player.x = player.x  + 1
  279.         end
  280.  
  281.         if p1 == keys.left and canMoveTo(player.x - 1, player.y) then
  282.             player.x = player.x - 1
  283.         end
  284.  
  285.         if p1 == keys.up and canMoveTo(player.x, player.y - 1) then
  286.             player.y = player.y - 1
  287.         end
  288.  
  289.         if p1 == keys.down and canMoveTo(player.x, player.y + 1) then
  290.             player.y = player.y + 1
  291.         end
  292.     end
  293.  
  294.     if e == "char" then
  295.         if p1 == 'q' then
  296.             return false
  297.         end
  298.  
  299.         if p1 == 'r' then
  300.             pointAt.x = 0
  301.             pointAt.y = 0
  302.         end
  303.  
  304.         if p1 == 'f' then
  305.             fancyGraphics = not fancyGraphics
  306.         end
  307.  
  308.         if p1 == '+' then
  309.             lightConeSize = lightConeSize + 10
  310.  
  311.             if lightConeSize >= 360 then
  312.                 lightConeSize = 360
  313.                 pointAt.x = 0
  314.                 pointAt.y = 0
  315.             end
  316.         end
  317.  
  318.         if p1 == '-' then
  319.             lightConeSize = lightConeSize - 10
  320.  
  321.             if lightConeSize < 10 then
  322.                 lightConeSize = 10
  323.             end
  324.         end
  325.     end
  326.  
  327.     if e == "mouse_click" then
  328.         if p1 == 3 then
  329.             pointAt.x = p2
  330.             pointAt.y = p3
  331.         end
  332.     end
  333.  
  334.     if pointAt.x ~= 0 and pointAt.y ~= 0 then
  335.         lightStartAngle, lightEndAngle = calculateDirectionalAngles(player.x, player.y, pointAt.x, pointAt.y, lightConeSize)
  336.     else
  337.         lightStartAngle = 0
  338.         lightEndAngle = 360
  339.     end
  340.  
  341.     render()
  342.     return true
  343. end
  344.  
  345. update("init")
  346. while update(os.pullEvent()) do end
  347.  
  348. term.setBackgroundColour(colours.black)
  349. term.clear()
  350. term.setCursorPos(1, 1)
  351.  
  352. os.unloadAPI(shell.resolveProgram("redirect"))
Advertisement
Add Comment
Please, Sign In to add comment