Guest User

world.lua

a guest
Dec 16th, 2012
77
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 14.56 KB | None | 0 0
  1. Entity = require "entity"
  2. Friendly = require "friendly"
  3. Enemy = require "enemy"
  4. LCG = require "lcg"
  5. AudioCtl = require "audio"
  6.  
  7. World = {}
  8. World.__index = World
  9.  
  10. function World.new(seed)
  11.     local inst = {}
  12.  
  13.     setmetatable(inst, World)
  14.  
  15.     inst.audioCtlr = AudioCtl.new()
  16.     inst.audioCtlr:playSong("derpy")
  17.     inst.entities = {}
  18.     inst.friendlies = {}
  19.     inst.enemies = {}
  20.     inst.nodes = {}
  21.     inst.lcg = LCG.new(seed)
  22.     inst.tiles = {}
  23.     inst.width = 100
  24.     inst.height = 100
  25.     inst.rooms = {}
  26.     inst.state = nil
  27.  
  28.     return inst
  29. end
  30.  
  31. function World:generate()
  32.     if self.state == nil then
  33.         gui.state = "Digging..."
  34.         self.state = "dig"
  35.     elseif self.state == "dig" then
  36.         self:dig()
  37.  
  38.         gui.state = "Placing..."
  39.         self.state = "place"
  40.     elseif self.state == "place" then
  41.         self:place()
  42.  
  43.         gui.state = "Finalizing..."
  44.         self.state = "finalize"
  45.     elseif self.state == "finalize" then
  46.         self:finalize()
  47.     end
  48. end
  49.  
  50. function World:dig()
  51.     local width = self.width
  52.     local height = self.height
  53.     local rooms = self.rooms
  54.     local lcg = self.lcg
  55.     local roomCount = 16 + math.floor(lcg:random() * (width + height) / 2) * 2
  56.  
  57.     -- Initialize the initial tilemap to all solid
  58.     for x = 1, width do
  59.         for y = 1, height, 1 do
  60.             self.tiles[x .. "_" .. y] = false
  61.         end
  62.     end
  63.  
  64.     -- Complete two passes, first pass generating room positions
  65.     -- then hollow out hallways
  66.  
  67.     for j = 1, 2 do
  68.         if j == 1 then
  69.             for i = 0, roomCount do
  70.                 local x, y, radius, size
  71.  
  72.                 repeat
  73.                     size = 5 + math.floor(lcg:random() * 7)
  74.                     radius = math.floor(size / 2)
  75.                     x = math.floor(lcg:random() * width)
  76.                     y = math.floor(lcg:random() * height)
  77.                 until not (x + radius + 2 > width or y + radius + 2 > height or x - radius - 2 < 0 or y - radius - 2 < 0)
  78.  
  79.                 room = { x = x, y = y, size = size, radius = radius}
  80.  
  81.                 local kx = x - radius
  82.                 local ky = y - radius
  83.  
  84.                 for k = 0, size do
  85.                     for l = 0, size do
  86.                         self:setTile(kx + k, ky + l, true)
  87.                     end
  88.                 end
  89.  
  90.                 table.insert(rooms, room)
  91.             end
  92.         else
  93.             for k, room in pairs(rooms) do
  94.                 local next = rooms[k + 1]
  95.                 local x = room.x
  96.                 local y = room.y
  97.  
  98.                 if next then
  99.                     local nextX = next.x
  100.                     local nextY = next.y
  101.                     local nextSize = next.size
  102.                     local nextRadius = next.radius
  103.                     local targets
  104.  
  105.                     -- Possibly choose
  106.  
  107.                     if lcg:random() >= 0.5 then
  108.                         targets = {{
  109.                             x = x,
  110.                             y = nextY,
  111.                             radius = 1
  112.                         }, {
  113.                             x = nextX,
  114.                             y = nextY,
  115.                             radius = nextRadius
  116.                         }}
  117.                     else
  118.                         targets = {{
  119.                             x = nextX,
  120.                             y = y,
  121.                             radius = 1
  122.                         }, {
  123.                             x = nextX,
  124.                             y = nextY,
  125.                             radius = nextRadius
  126.                         }}
  127.                     end
  128.  
  129.                     repeat
  130.                         target = targets[1]
  131.                         targetX = target.x
  132.                         targetY = target.y
  133.                         targetRadius = target.radius
  134.  
  135.                         self:setTile(x, y, true)
  136.                         self:setTile(x + 1, y, true)
  137.                         self:setTile(x - 1, y, true)
  138.                         self:setTile(x, y + 1, true)
  139.                         self:setTile(x, y - 1, true)
  140.                         self:setTile(x - 1, y - 1, true)
  141.                         self:setTile(x + 1, y - 1, true)
  142.                         self:setTile(x - 1, y + 1, true)
  143.                         self:setTile(x + 1, y + 1, true)
  144.  
  145.                         if math.sqrt((targetX - x) ^ 2 + (targetY - y) ^ 2) < targetRadius then
  146.                             table.remove(targets, 1)
  147.                         end
  148.  
  149.                         local north = math.sqrt((targetX - x) ^ 2 + (targetY - (y + 1)) ^ 2)
  150.                         local south = math.sqrt((targetX - x) ^ 2 + (targetY - (y - 1)) ^ 2)
  151.                         local east = math.sqrt((targetX - (x + 1)) ^ 2 + (targetY - y) ^ 2)
  152.                         local west = math.sqrt((targetX - (x - 1)) ^ 2 + (targetY - y) ^ 2)
  153.  
  154.                         if north <= west and north <= south and north <= east then
  155.                             y = y + 1
  156.                         elseif south <= west and south <= north and south <= east then
  157.                             y = y - 1
  158.                         elseif east <= west and east <= south and east <= north then
  159.                             x = x + 1
  160.                         elseif west <= east and west <= south and west <= north then
  161.                             x = x - 1
  162.                         end
  163.                     until #targets < 1
  164.                 end
  165.             end
  166.         end
  167.     end
  168. end
  169.  
  170. function World:place()
  171.     function tile(x, y)
  172.         return love.graphics.newQuad(x * 32, y * 32, 32, 32, tileset:getWidth(), tileset:getHeight())
  173.     end
  174.  
  175.     local nodes = self.nodes
  176.     local width = self.width
  177.     local height = self.height
  178.     local tilesetQuads = {
  179.         wall = tile(4, 1),
  180.         wallLeft = tile(4, 0),
  181.         wallRight = tile(4, 2),
  182.         roof = tile(5, 3),
  183.         floor = tile(0, 1)
  184.     }
  185.     local tilesBatch = love.graphics.newSpriteBatch(tileset, width * self.height)
  186.     local walls = {}
  187.  
  188.     function setWall(x, y)
  189.         walls[x .. "_" .. y] = true
  190.     end
  191.  
  192.     function isWall(x, y)
  193.         return walls[x .. "_" .. y] == true
  194.     end
  195.  
  196.     for x = 0, width do
  197.         for y = 0, height do
  198.             local curr = not self:getTile(x, y)
  199.             local north = not self:getTile(x, y - 1)
  200.             local northEast = not self:getTile(x + 1, y - 1)
  201.             local northWest = not self:getTile(x - 1, y - 1)
  202.             local south = not self:getTile(x, y + 1)
  203.             local southEast = not self:getTile(x + 1, y + 1)
  204.             local southEastSouth = not self:getTile(x + 1, y + 2)
  205.             local southWest = not self:getTile(x - 1, y + 1)
  206.             local southWestSouth = not self:getTile(x - 1, y + 2)
  207.             local southSouth = not self:getTile(x, y + 2)
  208.             local southSouthWest = not self:getTile(x - 1, y + 2)
  209.             local southSouthEast = not self:getTile(x + 1, y + 2)
  210.             local east = not self:getTile(x + 1, y)
  211.             local eastEast = not self:getTile(x + 2, y)
  212.             local west = not self:getTile(x - 1, y)
  213.             local westWest = not self:getTile(x - 2, y)
  214.  
  215.             -- This logic is definitely broken in some places, so fix it where necessary.
  216.             -- WARNING: Trying to understand this logic can cause suicidal thoughts.
  217.  
  218.             if not curr then
  219.                 if (not north and not east and northEast) or
  220.                    (not south and not east and southEast) or
  221.                    (not north and not west and northWest) or
  222.                    (not south and not west and southWest) then
  223.                    table.insert(nodes, {x = x * 32 + 16, y = y * 32 + 16, neighbors = {}})
  224.                 end
  225.  
  226.                 tilesBatch:addq(tilesetQuads.floor, x * 32, y * 32)
  227.             elseif (not south and not north) or (not east and not west) then
  228.                 -- Clear 1x walls cause they look shit
  229.                 self:setTile(x, y, true)
  230.                 tilesBatch:addq(tilesetQuads.floor, x * 32, y * 32)
  231.             else
  232.                 setWall(x, y)
  233.  
  234.                 if not south then
  235.                     tilesBatch:addq(tilesetQuads.wall, x * 32, y * 32)
  236.                 elseif (not north or not east or not south or not west) or
  237.                        (not southEast or not southWest or not northEast or not northWest) or
  238.                        (not southSouth or not southSouthEast or not southSouthWest) then
  239.                     tilesBatch:addq(tilesetQuads.roof, x * 32, y * 32)
  240.                 end
  241.             end
  242.         end
  243.     end
  244.  
  245.     -- Wall Rectangle Optimization --
  246.     local rectangles = {}
  247.  
  248.     for x = 0, width do
  249.         local start_y
  250.         local end_y
  251.  
  252.         for y = 0, height do
  253.             if isWall(x, y) then
  254.                 if not start_y then
  255.                     start_y = y
  256.                 end
  257.                 end_y = y
  258.  
  259.                 if y == height then
  260.                     local overlaps = {}
  261.                     for _, r in ipairs(rectangles) do
  262.                         if (r.end_x == x - 1)
  263.                           and (start_y <= r.start_y)
  264.                           and (end_y >= r.end_y) then
  265.                             table.insert(overlaps, r)
  266.                         end
  267.                     end
  268.                     table.sort(
  269.                         overlaps,
  270.                         function (a, b)
  271.                             return a.start_y < b.start_y
  272.                         end
  273.                     )
  274.  
  275.                     for _, r in ipairs(overlaps) do
  276.                         if start_y == r.start_y then
  277.                             r.end_x = r.end_x + 1
  278.  
  279.                             if end_y == r.end_y then
  280.                                 start_y = nil
  281.                                 end_y = nil
  282.                             elseif end_y > r.end_y then
  283.                                 start_y = r.end_y + 1
  284.                             end
  285.                         end
  286.                     end
  287.                 end
  288.             elseif start_y then
  289.                 local overlaps = {}
  290.                 for _, r in ipairs(rectangles) do
  291.                     if (r.end_x == x - 1)
  292.                       and (start_y <= r.start_y)
  293.                       and (end_y >= r.end_y) then
  294.                         table.insert(overlaps, r)
  295.                     end
  296.                 end
  297.                 table.sort(
  298.                     overlaps,
  299.                     function (a, b)
  300.                         return a.start_y < b.start_y
  301.                     end
  302.                 )
  303.  
  304.                 for _, r in ipairs(overlaps) do
  305.                     if start_y < r.start_y then
  306.                         local new_rect = {
  307.                             start_x = x,
  308.                             start_y = start_y,
  309.                             end_x = x,
  310.                             end_y = r.start_y - 1
  311.                         }
  312.                         table.insert(rectangles, new_rect)
  313.                         start_y = r.start_y
  314.                     end
  315.  
  316.                     if start_y == r.start_y then
  317.                         r.end_x = r.end_x + 1
  318.  
  319.                         if end_y == r.end_y then
  320.                             start_y = nil
  321.                             end_y = nil
  322.                         elseif end_y > r.end_y then
  323.                             start_y = r.end_y + 1
  324.                         end
  325.                     end
  326.                 end
  327.  
  328.                 if start_y then
  329.                     local new_rect = {
  330.                         start_x = x,
  331.                         start_y = start_y,
  332.                         end_x = x,
  333.                         end_y = end_y
  334.                     }
  335.                     table.insert(rectangles, new_rect)
  336.                     start_y = nil
  337.                     end_y = nil
  338.                 end
  339.             end
  340.         end
  341.  
  342.         if start_y then
  343.             local new_rect = {
  344.                 start_x = x,
  345.                 start_y = start_y,
  346.                 end_x = x,
  347.                 end_y = end_y
  348.             }
  349.             table.insert(rectangles, new_rect)
  350.             start_y = nil
  351.             end_y = nil
  352.         end
  353.     end
  354.  
  355.     -- Creation of Wall Collision Boxes --
  356.     for _, r in ipairs(rectangles) do
  357.         local start_x = r.start_x * 32
  358.         local start_y = r.start_y * 32
  359.         local width = (r.end_x - r.start_x + 1) * 32
  360.         local height = (r.end_y - r.start_y + 1) * 32
  361.  
  362.         local box = collider:addRectangle(start_x, start_y, width, height)
  363.  
  364.         collider:setPassive(box)
  365.     end
  366.  
  367.     self.tilesBatch = tilesBatch
  368. end
  369.  
  370. function World:finalize()
  371.     -- Neighorhoo Generation for Navigation Nodes --
  372.     local nodes = self.nodes
  373.     local checked = {}
  374.  
  375.     for a, node in pairs(nodes) do
  376.         checked[a] = {}
  377.     end
  378.  
  379.     for a, nodeA in pairs(nodes) do
  380.         for b, nodeB in pairs(nodes) do
  381.             if a ~= b and checked[a][b] == nil then
  382.                 if # raycast(nodeA.x, nodeA.y, nodeB.x, nodeB.y) == 0 then
  383.                     checked[a][b] = 1
  384.                     checked[b][a] = 1
  385.                     table.insert(nodeA.neighbors, b)
  386.                     table.insert(nodeB.neighbors, a)
  387.                 end
  388.             end
  389.         end
  390.     end
  391.  
  392.     self:populate()
  393.  
  394.     gui.loaded = true
  395.     gui:renderMap()
  396. end
  397.  
  398. function World:populate()
  399.     local x = self.rooms[1].x
  400.     local y = self.rooms[1].y
  401.     local debugPlayer = Friendly.new(x * 32, y * 32, 32, 64)
  402.     self.cameraX = -x * 32
  403.     self.cameraY = -y * 32
  404. end
  405.  
  406. function World:render()
  407.     love.graphics.push()
  408.     love.graphics.translate(math.floor(self.cameraX + love.graphics.getWidth() / 2), math.floor(self.cameraY + love.graphics.getHeight() / 2))
  409.     love.graphics.setColor(255, 255, 255)
  410.     love.graphics.draw(self.tilesBatch)
  411.  
  412.     -- TODO: optimize
  413.     function isControlled(entity)
  414.         for i, controlled in pairs(control.controlling) do
  415.             if controlled == entity then
  416.                 return true
  417.             end
  418.         end
  419.  
  420.         return false
  421.     end
  422.  
  423.     for i, entity in pairs(self.entities) do
  424.         love.graphics.setColor(255, 255, 255)
  425.         if isControlled(entity) then
  426.             love.graphics.setColor(189, 129, 171)
  427.         end
  428.  
  429.         entity:render()
  430.     end
  431.  
  432.     love.graphics.pop()
  433. end
  434.  
  435. function World:setTile(x, y, bit)
  436.     self.tiles[x .. "_" .. y] = bit
  437. end
  438.  
  439. function World:getTile(x, y)
  440.     return self.tiles[x .. "_" .. y]
  441. end
  442.  
  443. function World:update(dt)
  444.     for a, entity in pairs(self.entities) do
  445.         entity:think(dt)
  446.     end
  447. end
  448.  
  449. return World
Advertisement
Add Comment
Please, Sign In to add comment