Advertisement
Guest User

Untitled

a guest
Jan 26th, 2020
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 39.15 KB | None | 0 0
  1. --- Gameworld main loop, controls rendering, input, and physics of terrain and entities.
  2. -- <u>This script really needs to be split up</u>
  3. -- @author Joshua O'Leary
  4. -- @copyright 2019 Conarium Software
  5.  
  6.  
  7. local config            = require("config")
  8.  
  9. local tiles             = require("src.tiles")
  10. local jutils            = require("src.jutils")
  11. local rendering         = require("src.rendering")
  12. local items             = require("src.items")
  13. local json              = require("src.json")
  14. local noise             = require("src.noise")
  15. local input             = require("src.input")
  16. local chunking          = require("src.chunking")
  17. local grid              = require("src.grid")
  18. local backgrounds       = require("src.backgrounds")
  19. local particlesystem    = require("src.particlesystem")
  20. local terrainMath       = require("src.terrain")
  21. local collision         = require("src.collision")
  22.  
  23. -- list of all entities that the world "can" spawn under varying conditions
  24. -- NOTE: this is the list that the "summon" command uses.
  25. -- NOTE: this is also what world:addEntity(...) uses.
  26.  
  27. local entitylist = {
  28.     player          = require("src.entities.player"),
  29.     -- hostile enemy entities
  30.     bee             = require("src.entities.hostile.bee"),
  31.     zombie          = require("src.entities.hostile.zombie"),
  32.     bat             = require("src.entities.hostile.bat"),
  33.     slime           = require("src.entities.hostile.slime"),
  34.     flower          = require("src.entities.hostile.flower"),
  35.     skull           = require("src.entities.hostile.skull"),
  36.     caster          = require("src.entities.hostile.caster"),
  37.     tortured        = require("src.entities.hostile.tortured"),
  38.     -- misc game objects
  39.     itemstack       = require("src.entities.itemstack"),
  40.     explosion       = require("src.entities.explosion"),
  41.     chest           = require("src.entities.chestentity"),
  42.     floatingtext    = require("src.entities.floatingtext"),
  43.     -- projectiles
  44.     casterball      = require("src.entities.projectiles.casterball"),
  45.     bullet          = require("src.entities.projectiles.bullet"),
  46.     bombentity      = require("src.entities.projectiles.bombentity"),
  47.     stickybomb      = require("src.entities.projectiles.stickybombentity"),
  48.     dynamite        = require("src.entities.projectiles.dynamiteentity"),
  49.     glowstick       = require("src.entities.projectiles.glowstick"),
  50.     magicball       = require("src.entities.projectiles.magicball"),
  51.     arrow           = require("src.entities.projectiles.arrow"),
  52.     --
  53.     laser           = require("src.entities.projectiles.laser"),   
  54. }
  55.  
  56. local channels = {
  57.     addchunk    = love.thread.getChannel("addchunk"),
  58.     dropchunk   = love.thread.getChannel("dropchunk"),
  59.     tilechange  = love.thread.getChannel("tilechange"),
  60.     bgchange    = love.thread.getChannel("bgchange"),
  61.     newlights   = love.thread.getChannel("newlights"),
  62.     setambient  = love.thread.getChannel("setambient"),
  63.     setlight    = love.thread.getChannel("setlight"),
  64.     light_kill  = love.thread.getChannel("light_kill"),
  65.     -- worldloading
  66.     loadchunk   = love.thread.getChannel("loadchunk"),
  67.     savechunk   = love.thread.getChannel("savechunk"),
  68.     returnchunk = love.thread.getChannel("returnchunk"),
  69.     finished    = love.thread.getChannel("finished"),
  70.     io_kill     = love.thread.getChannel("io_kill"),
  71. }
  72.  
  73. -- these get LERP'd between for sky colors
  74. local sky_color_table = {
  75.     [0] = {0, 0, 0},
  76.     [3] = {0.01, 0, 0},
  77.     [6] = {0, 0.05, 0.15},
  78.     [9] = {0.05, 0.2, 0.6},
  79.     [12] = {0.05, 0.3, 0.7},
  80.     [15] = {0.05, 0.2, 0.55},
  81.     [18] = {0.1, 0, 0.4},
  82.     [21] = {0.025, 0, 0.01},
  83. }
  84.  
  85. local biome_bg_textures = {
  86.     forest  = love.graphics.newImage("assets/backgrounds/forest.png"),
  87.     desert  = love.graphics.newImage("assets/backgrounds/desert.png"),
  88.     plains  = love.graphics.newImage("assets/backgrounds/plains.png"),
  89.     welands = love.graphics.newImage("assets/backgrounds/wetlands.png")
  90. }
  91.  
  92. local function get_daylight(worldtime)
  93.     local light = 0.15
  94.     local timeDial = worldtime
  95.  
  96.     if timeDial > (5.5*60)  and timeDial < (20*60)    then light = light + 0.15 end
  97.     if timeDial > (5.75*60) and timeDial < (19.75*60) then light = light + 0.10 end
  98.     if timeDial > (6*60)    and timeDial < (19.5*60)  then light = light + 0.10 end
  99.     if timeDial > (6.25*60) and timeDial < (19.25*60) then light = light + 0.10 end
  100.     if timeDial > (6.5*60)  and timeDial < (18.75*60) then light = light + 0.10 end
  101.     if timeDial > (6.75*60) and timeDial < (18.5*60)  then light = light + 0.10 end
  102.     if timeDial > (7.25*60) and timeDial < (18.25*60) then light = light + 0.10 end
  103.     if timeDial > (7.5*60)  and timeDial < (18*60)    then light = light + 0.10 end
  104.     return light
  105. end
  106.  
  107. --[[
  108.     NOTE:
  109.     world file format:
  110.  
  111.     worldname/
  112.         chunks/
  113.         idmaps/
  114.  
  115.         metadata.json
  116.             worldname
  117.             seed
  118.         entities.json
  119. ]]
  120.  
  121. local world = {} -- TODO: make world a class object instead of ghetto-rigged metatable
  122.  
  123. world.__index = world
  124.  
  125.  
  126. --- Generates a new world instance.
  127. -- Worldname is the name of the folder where it will read and save data to.
  128. -- @param worldname folder to store world in
  129. -- @param seed doesn't work
  130. -- @return World Instance
  131. function world.new(worldname, seed)
  132.  
  133.     -- world constructor
  134.     local self = setmetatable({}, world)
  135.  
  136.     -- make sure world directories exist
  137.     love.filesystem.createDirectory("worlds/"..worldname)
  138.     love.filesystem.createDirectory("conversionmaps")
  139.     love.filesystem.createDirectory("worlds/"..worldname.."/chunks")
  140.  
  141.     -- generate game threads
  142.     self.lightthread = love.thread.newThread("src/lighting.lua")
  143.     self.lightthread:start()
  144.  
  145.     self.chunkthread = love.thread.newThread("src/worldloading.lua")
  146.     self.chunkthread:start(worldname)
  147.     love.thread.getChannel("setambient"):push(self.ambientlight)
  148.  
  149.     self.worldname = worldname
  150.     self.seed = seed
  151.  
  152.     self.camera = {
  153.         position = jutils.vec2.new(0, 0),
  154.         zoom = 2.5
  155.     }
  156.  
  157.     -- ambience
  158.     self.ambientlight = 0
  159.     self.worldtime = 6*60
  160.     self.dayspassed = 0
  161.  
  162.     self.player = nil
  163.     self.spawnPointY = (terrainMath.getSurfaceLevel(0)*config.TILE_SIZE)
  164.     self.focuspoint = jutils.vec2.new(0, 0)
  165.    
  166.     self.entities = {}
  167.     self.tileentitymap = {}
  168.     self.waitinglist = {}
  169.    
  170.     self.terrainGenerator = require("src.terraingenerator")
  171.     self.structureGenerator = require("src.structuregenerator")
  172.  
  173.     self.chunks = {}
  174.     self.finishedTerrain = false
  175.  
  176.     -- if the world is trying to kill
  177.     self.tryingToEnd = false
  178.    
  179.     self.update_tick = 0
  180.     self.random_tick_speed = 1
  181.     self.no_save = false
  182.  
  183.     -- generate ID mappings for this version of the game if they don't yet exists
  184.     local info = love.filesystem.getInfo("conversionmaps/"..config.DATA_VERSION)
  185.  
  186.     if info == nil then
  187.  
  188.         local map = { tiles = {}, backgrounds = {}}
  189.         for name, data in pairs(tiles:getList()) do
  190.             map.tiles[name] = data.id
  191.         end
  192.         for name, data in pairs(backgrounds:getList()) do
  193.             map.backgrounds[name] = data.id
  194.         end
  195.  
  196.         local data = json.encode(map)
  197.         love.filesystem.write("conversionmaps/"..config.DATA_VERSION, data)
  198.     end
  199.  
  200.  
  201.     -- world metadata
  202.     local info = love.filesystem.getInfo("worlds/"..self.worldname.."/metadata.json", "file")
  203.     if info then
  204.         local contents = love.filesystem.read("string", "worlds/"..self.worldname.."/metadata.json")
  205.         if contents then
  206.             local data = json.decode(contents)
  207.  
  208.             self.worldtime = data.worldtime
  209.             self.dayspassed = data.dayspassed
  210.             self.seed = data.seed
  211.         end
  212.     end
  213.  
  214.     local madeplayer = false
  215.  
  216.     -- read entity save file
  217.     local info = love.filesystem.getInfo("worlds/"..self.worldname.."/entities.json", "file")
  218.     if info ~= nil then
  219.         --print("existing worldfile found... lastmodified: "..info.modtime)
  220.         local contents = love.filesystem.read("string", "worlds/"..self.worldname.."/entities.json")
  221.         if contents then
  222.             local data = json.decode(contents)
  223.  
  224.             for idx, edata in pairs(data) do
  225.  
  226.                 --print(idx, edata.type)
  227.                 if edata.type then
  228.                     print(edata.type)
  229.                     local entity = self:addEntity(edata.type)
  230.                     entity:deserialize(edata)
  231.  
  232.                     if edata.type == "player" then madeplayer = true end
  233.                 end
  234.  
  235.             end
  236.         end
  237.     end
  238.  
  239.     -- new player creation
  240.     if madeplayer == false then
  241.         local player = self:addEntity("player")
  242.         player:teleport(jutils.vec2.new(0, self.spawnPointY))
  243.         player.gui.inventory:addItem(items.HANDMADE_PICKAXE.id, 1)
  244.         player.gui.inventory:addItem(items.RUSTY_SWORD.id, 1)
  245.         player.gui.inventory:addItem(items.ROPE_TILE.id, 999)
  246.         player.gui.inventory:addItem(items.HEALING_POTION.id, 30)
  247.         player.gui.inventory:addItem(items.BOMB.id, 99)
  248.         player.gui.inventory:addItem(items.TORCH_TILE.id, 99)
  249.     end
  250.  
  251.     noise.setSeed(self.seed)
  252.  
  253.     return self
  254. end
  255.  
  256.  
  257. ---
  258. function world:savedata()
  259.     if self.no_save == true then return end
  260.  
  261.     for key, chunk in pairs(self.chunks) do
  262.         channels.savechunk:push({key, chunk})
  263.     end
  264.  
  265.     local metadata = json.encode({
  266.         worldtime = self.worldtime,
  267.         dayspassed = self.dayspassed,
  268.         seed = self.seed
  269.     })
  270.  
  271.     love.filesystem.write("worlds/"..self.worldname.."/metadata.json", metadata)
  272.  
  273.     local entityTable = {}
  274.     for _, entity in pairs(self.entities) do
  275.        
  276.         if entity.save == true then
  277.             local data = entity:serialize()
  278.             table.insert(entityTable, data)
  279.         end
  280.     end
  281.  
  282.  
  283.     local jsondata = json.encode(entityTable)
  284.  
  285.     love.filesystem.write("worlds/"..self.worldname.."/entities.json", jsondata)
  286.  
  287.  
  288.     love.timer.sleep(1/10)
  289.     channels.light_kill:push(true)
  290.     channels.io_kill:push(true)
  291.    
  292.     channels.finished:demand(5)
  293.  
  294.     self.lightthread:release()
  295.     self.chunkthread:release()
  296.    
  297.     self.chunks = nil
  298.     print("Finished saving...")
  299. end
  300.  
  301. ---
  302. function world:playerExitRequest()
  303.     self.tryingToEnd = true
  304. end
  305.  
  306. ---
  307. function world:getPlayer()
  308.     if self.player then return self.player end
  309.     for index, entity in pairs(self.entities) do
  310.         if entity:isA("Player") then
  311.             self.player = entity
  312.             return entity
  313.         end
  314.     end
  315. end
  316.  
  317. ---
  318. function world:addEntity(entityname, ...)
  319.     if not entityname then return end
  320.     if not entitylist[entityname] then return end
  321.     local entity = entitylist[entityname]:new(...)
  322.     entity.world = self
  323.     table.insert(self.entities, entity)
  324.     return entity
  325. end
  326.  
  327.  
  328.  
  329. function world:castRay(origin, direction, raydistance, rayaccuracy)
  330.     local max_ray_search_distance = raydistance
  331.  
  332.     local last_point = origin
  333.  
  334.     for i = 2, max_ray_search_distance, 1 do
  335.         local current_point = origin + (direction*i)   
  336.         local raytx, rayty = grid.pixelToTileXY(current_point.x, current_point.y)      
  337.         local tileat = self:getTile(raytx, rayty)
  338.  
  339.         if tileat ~= 0 then
  340.             local tiledata = tiles:getByID(tileat)
  341.             if tiledata.collide == true then
  342.  
  343.                 local center = current_point - (current_point - (last_point))*0.5
  344.                 local size = jutils.vec2.new(math.abs(current_point.x - last_point.x), math.abs(current_point.y - last_point.y))
  345.                 local tx, ty, tw, th = (raytx*config.TILE_SIZE)+(config.TILE_SIZE/2), (rayty*config.TILE_SIZE)+(config.TILE_SIZE/2), config.TILE_SIZE/2, config.TILE_SIZE/2
  346.                 local sx, sy = collision.test(center.x, center.y, 2, 2, tx, ty, tw, th)
  347.  
  348.                 if (sx and sy) then
  349.                     local normalx, normaly = collision.solve(sx, sy, direction.x, direction.y)
  350.                     --print("YES", tiledata.name)
  351.                     return true, current_point.x, current_point.y, sx, sy, normalx, normaly, tileat
  352.                 end
  353.             end
  354.         end
  355.         last_point = current_point
  356.     end
  357.     return false
  358. end
  359.  
  360.  
  361. ---------------------------------------
  362. -- World's map functionality
  363.  
  364. ---
  365. function world:rawget(tilex, tiley, field, unsafe)
  366.  
  367.     assert(type(tilex) == "number")
  368.     assert(type(tiley) == "number")
  369.     local cx, cy, lx, ly = grid.getLocalCoordinates(tilex, tiley)
  370.     local chunk = self:getchunk(cx, cy)
  371.  
  372.     if chunk then
  373.  
  374.         if chunk.loaded == false then
  375.             if not (unsafe) then return -1 end
  376.         end
  377.  
  378.         local exists = chunk[field]
  379.  
  380.         if exists then
  381.             return chunk[field][lx][ly]
  382.         end
  383.     end
  384.     return -1
  385. end
  386.  
  387. ---
  388. function world:getTile(tilex, tiley)
  389.     return self:rawget(tilex, tiley, "tiles", true)
  390. end
  391.  
  392. ---
  393. function world:getTileState(tilex, tiley)
  394.     return self:rawget(tilex, tiley, "states")
  395. end
  396.  
  397. ---
  398. function world:getTileDamage(tilex, tiley)
  399.     return self:rawget(tilex, tiley, "damage")
  400. end
  401.  
  402. ---
  403. function world:getLight(tilex, tiley)
  404.     assert(type(tilex) == "number")
  405.     assert(type(tiley) == "number")
  406.     local cx, cy, lx, ly = grid.getLocalCoordinates(tilex, tiley)
  407.     local chunk = self:getchunk(cx, cy)
  408.  
  409.     if chunk then
  410.  
  411.         if chunk.loaded == false then
  412.             return {-1, -1, -1}
  413.         end
  414.  
  415.         local exists = chunk["light"]
  416.  
  417.         if exists then
  418.             return {chunk["light"][1][lx][ly], chunk["light"][2][lx][ly], chunk["light"][3][lx][ly]}
  419.         end
  420.     end
  421.     return {-1, -1, -1}
  422. end
  423.  
  424. function world:setLight(tilex, tiley, r, g, b)
  425.     assert(type(tilex) == "number")
  426.     assert(type(tiley) == "number")
  427.     local cx, cy, lx, ly = grid.getLocalCoordinates(tilex, tiley)
  428.     local chunk = self:getchunk(cx, cy)
  429.  
  430.     if chunk then
  431.  
  432.         if chunk.loaded == false then
  433.             return
  434.         end
  435.  
  436.         local exists = chunk["light"]
  437.  
  438.         if exists then
  439.             chunk["light"][1][lx][ly] = r
  440.             chunk["light"][2][lx][ly] = g
  441.             chunk["light"][3][lx][ly] = b
  442.  
  443.         end
  444.     end
  445. end
  446.  
  447. ---
  448. function world:getBackground(tilex, tiley)
  449.     return self:rawget(tilex, tiley, "backgrounds")
  450. end
  451.  
  452. ---
  453. function world:damageTile(tilex, tiley, additionaldamage)
  454.     local tile = self:rawget(tilex, tiley, "tiles")
  455.     local data = tiles:getByID(tile)
  456.     local maxdamage = data.hardness
  457.     local damage = self:rawget(tilex, tiley, "damage")
  458.  
  459.     local newdamage = damage + additionaldamage
  460.  
  461.     if tile == tiles.WATER.id or tile == tiles.LAVA.id or tile == tiles.AIR.id then return end
  462.  
  463.     if newdamage >= maxdamage then
  464.        
  465.        
  466.  
  467.         self:setTile(tilex, tiley, 0, true)
  468.        
  469.     else
  470.         self:rawset(tilex, tiley, "damage", newdamage)
  471.     end
  472. end
  473.  
  474. --- Directly sets a field without triggering updates.
  475. -- @param field "tiles" | "damage" | "states" | "light"
  476. function world:rawset(tilex, tiley, field, value, unsafe)
  477.     local cx, cy, lx, ly = grid.getLocalCoordinates(tilex, tiley)
  478.     local chunk = self:getchunk(cx, cy)
  479.  
  480.     if chunk then
  481.         if chunk.loaded == false then
  482.             if not (unsafe) then return -1 end
  483.         end
  484.  
  485.         local exists = chunk[field]
  486.  
  487.         if exists then
  488.             chunk[field][lx][ly] = value
  489.             return
  490.         else
  491.             print("field doesn't exist")
  492.         end
  493.     end
  494.     return -1
  495. end
  496.  
  497. --- Sets the damage value of the tile. if damage exceeds the tile's hardness, it will break and drop an item.
  498. function world:setTileDamage(tilex, tiley, damage)
  499.     self:rawset(tilex, tiley, "damage", damage)
  500. end
  501.  
  502. --- Sets the state of the tile and triggers tileupdates.
  503. function world:setTileState(tilex, tiley, state)
  504.     self:rawset(tilex, tiley, "states", state)
  505.  
  506.     self:tileUpdate(tilex, tiley - 1)
  507.     self:tileUpdate(tilex, tiley + 1)
  508.     self:tileUpdate(tilex + 1, tiley)
  509.     self:tileUpdate(tilex - 1, tiley)
  510.     self:tileUpdate(tilex, tiley)
  511.  
  512. end
  513.  
  514. --- Sets the background and fills the tileupdate flag for that tile.
  515. function world:setBackground(tilex, tiley, id)
  516.     self:rawset(tilex, tiley, "backgrounds", id)
  517.     self:tileUpdate(tilex, tiley)
  518.     channels.bgchange:push({tilex, tiley, id})
  519. end
  520.  
  521. --- Sets the tile and fills the tileupdate flags of adjacent tiles.
  522. -- @param tilex
  523. -- @param tiley
  524. -- @param tile
  525. -- @param drop drop the previous tile.
  526. -- @param ignorecallback should the onPlace callback be called for this tile
  527.  
  528. function world:setTile(tilex, tiley, tile, drop, ignorecallback)
  529.     assert(type(tilex) == "number")
  530.     assert(type(tiley) == "number")
  531.     assert(type(tile)  == "number")
  532.  
  533.     local current = self:getTile(tilex, tiley)
  534.     if current ~= tile then
  535.  
  536.         if not ignorecallback then
  537.             local currentdata = tiles:getByID(current)
  538.             if currentdata.onbreak then
  539.                 currentdata.onbreak(self, tilex, tiley)
  540.             end
  541.         end
  542.  
  543.         self:setTileState(tilex, tiley, 0)
  544.         self:setTileDamage(tilex, tiley, 0)
  545.            
  546.         if drop then
  547.             local tiledata = tiles:getByID(current)
  548.             if tiledata.drop ~= false then
  549.                 if type(tiledata.drop) == "string" then
  550.                     local item = items[tiledata.drop]
  551.                     local e = self:addEntity("itemstack", item.id, 1)
  552.                     e:teleport(jutils.vec2.new(tilex*config.TILE_SIZE, tiley*config.TILE_SIZE))
  553.                 end
  554.  
  555.                 if type(tiledata.drop) == "function" then
  556.                     local itemname, amount = tiledata.drop()
  557.  
  558.                     local item = items[itemname]
  559.                     local e = self:addEntity("itemstack", item.id, amount)
  560.                     e:teleport(jutils.vec2.new(tilex*config.TILE_SIZE, tiley*config.TILE_SIZE))
  561.                 end
  562.             end
  563.         end
  564.            
  565.         self:rawset(tilex, tiley, "tiles", tile, true)
  566.  
  567.         local tiledata = tiles:getByID(tile)
  568.  
  569.         if not ignorecallback then
  570.             if tiledata.onplace then
  571.                 tiledata.onplace(self, tilex, tiley)
  572.             end
  573.         end
  574.        
  575.         self:tileUpdate(tilex, tiley)
  576.         self:tileUpdate(tilex, tiley - 1)
  577.         self:tileUpdate(tilex, tiley + 1)
  578.         self:tileUpdate(tilex + 1, tiley)
  579.         self:tileUpdate(tilex - 1, tiley)
  580.         channels.tilechange:push({tilex, tiley, tile})
  581.     end
  582. end
  583.  
  584. --- Get chunk at chunk region (cx, cy)
  585. -- @param cx
  586. -- @param cy
  587. -- @return chunk
  588. function world:getchunk(cx, cy)
  589.     return self.chunks[grid.coordinatesToKey(cx, cy)]
  590. end
  591.  
  592. ---
  593. function world:setchunkloaded(cx, cy)
  594.     self.loaded[grid.coordinatesToKey(cx, cy)] = true
  595. end
  596.  
  597. ---
  598. function world:tileUpdate(tilex, tiley)
  599.    
  600.     self:rawset(tilex, tiley, "tileupdate", true)
  601. end
  602.  
  603. ---
  604. function world:randomTick(wx, wy)
  605.     local tileid = self:getTile(wx, wy)
  606.     local data = tiles:getByID(tileid)
  607.  
  608.     if data.randomupdate then
  609.         data.randomupdate(self, wx, wy)
  610.     end
  611. end
  612.  
  613. ---
  614. function world:chunkRandomTileUpdates(chunk)
  615.     local wx, wy = chunk.position.x, chunk.position.y
  616.     for i = 1, self.random_tick_speed do
  617.         local randx = math.random(1, config.CHUNK_SIZE)
  618.         local randy = math.random(1, config.CHUNK_SIZE)
  619.         self:randomTick((wx * config.CHUNK_SIZE) + randx, (wy * config.CHUNK_SIZE) + randy)
  620.     end
  621. end
  622.  
  623. ---
  624. function world:chunkTileUpdates(chunk)
  625.     local count = 0
  626.     local lightcount = 0
  627.     local wx, wy = chunk.position.x, chunk.position.y
  628.    
  629.     for dx = 1, config.CHUNK_SIZE do
  630.         for dy = 1, config.CHUNK_SIZE do
  631.             -- the tile update
  632.             if count > config.MAX_TILE_UPDATES_PER_FRAME then
  633.                 return
  634.             end
  635.  
  636.             if chunk.tileupdate[dx][dy] == true then
  637.                 chunk.tileupdate[dx][dy] = false
  638.                 local tile = chunk.tiles[dx][dy]
  639.                 if tile > 0 then
  640.                     local data = tiles:getByID(tile)
  641.  
  642.                     if data.tileupdate then
  643.                         data.tileupdate(self, (wx * config.CHUNK_SIZE) + dx - 1, (wy * config.CHUNK_SIZE) + dy - 1)
  644.                         if tile ~= tiles.WATER.id and tile ~= tiles.LAVA.id then
  645.                             count = count + 1
  646.                         else
  647.                             count = count + 0.25
  648.                         end
  649.                     end
  650.                 end
  651.             end
  652.         end
  653.     end
  654. end
  655.  
  656. ---
  657. function world:map_clearChunkFields()
  658.     for key, chunk in pairs(self.chunks) do
  659.        
  660.         chunk.requested = false
  661.         chunk.visible = false
  662.         chunk.requestedFully = false
  663.     end
  664. end
  665.  
  666. ---
  667. function world:map_pullLoadedChunks()
  668.     local newChunk = channels.returnchunk:pop()
  669.  
  670.     if newChunk then
  671.            
  672.         local key = newChunk[1]
  673.         local chunk = newChunk[2]
  674.        
  675.         if self.waitinglist[key] == true then
  676.             self.waitinglist[key] = nil
  677.         else
  678.             print("was not expecting???", key, chunk)
  679.         end
  680.         chunk.requested = true
  681.         chunk.visible = true
  682.         self.chunks[key] = chunk
  683.     end
  684. end
  685.  
  686. ---
  687. function world:map_fillChunkFields()
  688.     local fx, fy = self.focuspoint.x, self.focuspoint.y
  689.  
  690.     local genradius = config.CHUNK_DRAW_RADIUS + config.CHUNK_SIMULATION_RADIUS + config.CHUNK_BUFFER_RADIUS
  691.  
  692.     for cx = -genradius, genradius do
  693.         for cy = -genradius, genradius do
  694.             local chunk = self:getchunk(fx+cx, fy+cy)
  695.  
  696.             if not chunk then
  697.                 local key = grid.coordinatesToKey(fx+cx, fy+cy)
  698.  
  699.                 if self.waitinglist[key] ~= true then
  700.                     --self.chunks[key] = chunking:newstruct(fx+cx, fy+cy)
  701.                     --chunk = self.chunks[key]
  702.                     channels.loadchunk:push({key})
  703.                     self.waitinglist[key] = true
  704.                 end
  705.             else
  706.                 chunk.requested = true
  707.             end
  708.             --
  709.         end
  710.     end
  711.  
  712.     local simradius = config.CHUNK_DRAW_RADIUS + config.CHUNK_SIMULATION_RADIUS
  713.     for cx = -simradius, simradius do
  714.         for cy = -simradius, simradius do
  715.             local chunk = self:getchunk(fx+cx, fy+cy)
  716.             if chunk then
  717.                 chunk.requestedFully = true
  718.             end
  719.         end
  720.     end
  721.  
  722.     local drawradius = config.CHUNK_DRAW_RADIUS
  723.     for cx = -drawradius, drawradius do
  724.         for cy = -drawradius, drawradius do
  725.             local chunk = self:getchunk(fx+cx, fy+cy)
  726.             if chunk then
  727.                 if chunk.structurePass == true and chunk.beingThreaded == false then
  728.                     chunk.beingThreaded = true
  729.                     channels.addchunk:push({grid.coordinatesToKey(fx+cx, fy+cy),
  730.                         {
  731.                             tiles = chunk.tiles,
  732.                             backgrounds = chunk.backgrounds
  733.                         }})
  734.                 end
  735.                 chunk.visible = true
  736.             end
  737.         end
  738.     end
  739. end
  740.  
  741. ---
  742. function world:map_unloadOldChunks()
  743.     -- cull chunks that are no longer "loaded"
  744.     for key, chunk in pairs(self.chunks) do
  745.         if chunk then
  746.             if chunk.visible == false and chunk.requested == false then
  747.                 if chunk.beingThreaded == true then
  748.                     chunk.beingThreaded = false
  749.                    
  750.                     channels.dropchunk:push({key})
  751.                 end
  752.                 if self.no_save == false then
  753.                     channels.savechunk:push({key, chunk})
  754.                 end
  755.                 self.chunks[key] = nil
  756.             end
  757.         end
  758.     end
  759. end
  760.  
  761. ---
  762. function world:map_loadNewChunks()
  763.     self.finishedTerrain = true
  764.     for key, chunk in pairs(self.chunks) do
  765.         if chunk.requested == true and chunk.terrainPass == false then
  766.             self.finishedTerrain = false
  767.         end
  768.     end
  769.  
  770.     for key, chunk in pairs(self.chunks) do
  771.         -- run terrainPass on chunks with no terrain
  772.         local wx, wy = chunk.position.x, chunk.position.y
  773.         if chunk.requested == true and chunk.terrainPass == false then
  774.             self.terrainGenerator(chunk, self.seed)
  775.             chunk.terrainPass = true
  776.             return
  777.         end
  778.  
  779.         if self.finishedTerrain then
  780.             if chunk.requestedFully == true and chunk.terrainPass == true and chunk.structurePass == false then
  781.                
  782.                 for x = 1, config.CHUNK_SIZE do
  783.                     for y = 1, config.CHUNK_SIZE do
  784.                         self.structureGenerator(self, ((wx) * config.CHUNK_SIZE) + x - 1, ((wy) * config.CHUNK_SIZE) + y - 1)
  785.                     end
  786.                 end
  787.                 chunk.structurePass = true
  788.                 chunk.loaded = true
  789.  
  790.                 for i = 1, 5 do
  791.                     self:chunkTileUpdates(chunk)
  792.                 end
  793.                 for i = 1, 32 do
  794.                     self:chunkRandomTileUpdates(chunk)
  795.                 end
  796.                 return
  797.             end
  798.         end
  799.     end
  800. end
  801.  
  802. ------------------------------------------------------
  803.  
  804. local function is_solid(world, tx, ty)
  805.     local t = world:getTile(tx, ty)
  806.  
  807.     if t == tiles.AIR.id or t == tiles.OVERGROWTH.id or t == tiles.VINE.id then
  808.         return false
  809.     end
  810.  
  811.     return true
  812. end
  813.  
  814. local function try_zombie_spawn(gameworld, tx, ty)
  815.  
  816.     -- NOTE: zombie spawn mechanics only check if there is a 1x3 region availible for spawning
  817.     -- must be night time
  818.     --if not (gameworld.worldtime > (60*18) or gameworld.worldtime < (60*6)) then return end
  819.  
  820.     local light = gameworld:getLight(tx, ty)
  821.  
  822.     -- must be fairly dark in this spot
  823.     if (light[1]+light[2]+light[3]) > 0.2 then return end
  824.  
  825.     -- selected tile must be solid, and there must be 3 pockets of air above
  826.     if is_solid(gameworld, tx, ty) == false then return end
  827.  
  828.     if is_solid(gameworld, tx, ty-1) then return end
  829.     if is_solid(gameworld, tx, ty-2) then return end
  830.     if is_solid(gameworld, tx, ty-3) then return end
  831.  
  832.  
  833.     local mob = gameworld:addEntity("zombie")
  834.     local rx, ry = tx*config.TILE_SIZE, ty*config.TILE_SIZE
  835.  
  836.     -- make sure zombie doesn't spawn halfway in the ground
  837.     ry = ry - mob.boundingbox.y
  838.                    
  839.     mob:teleport(jutils.vec2.new(rx, ry))
  840. end
  841.  
  842.  
  843. local function try_flower_spawn(gameworld, tx, ty)
  844.  
  845.     -- must be daytime
  846.     if not (gameworld.worldtime > (7*60) and gameworld.worldtime < (60*17)) then return end
  847.  
  848.     -- needs to be on grass
  849.     if gameworld:getTile(tx, ty) ~= tiles.GRASS.id then return end
  850.     if gameworld:getTile(tx+1, ty) ~= tiles.GRASS.id then return end
  851.  
  852.     -- needs a 2x2 of free space above
  853.     if is_solid(gameworld, tx, ty-1) then return end
  854.     if is_solid(gameworld, tx, ty-2) then return end
  855.     if is_solid(gameworld, tx+1, ty-1) then return end
  856.     if is_solid(gameworld, tx+1, ty-2) then return end
  857.  
  858.     local mob = gameworld:addEntity("flower")
  859.  
  860.     mob:teleport(jutils.vec2.new(tx*config.TILE_SIZE, ty*config.TILE_SIZE))
  861. end
  862.  
  863. local function try_tortured_spawn(gameworld, tx, ty)
  864.  
  865.     if ty < 300 then return end
  866.     local light = gameworld:getLight(tx, ty)
  867.     if (light[2]+light[3]) > 0.15 then return end
  868.  
  869.     -- needs a 2x2 of free space
  870.     if is_solid(gameworld, tx, ty) then return end
  871.     if is_solid(gameworld, tx, ty-1) then return end
  872.     if is_solid(gameworld, tx+1, ty) then return end
  873.     if is_solid(gameworld, tx+1, ty+1) then return end
  874.  
  875.     local mob = gameworld:addEntity("tortured")
  876.  
  877.     mob:teleport(jutils.vec2.new(tx*config.TILE_SIZE, ty*config.TILE_SIZE))
  878. end
  879.  
  880. local function try_caster_spawn(gameworld, tx, ty)
  881.  
  882.     if ty > 100 then return end
  883.  
  884.     -- must be night time
  885.     if (gameworld.worldtime > (7*60) and gameworld.worldtime < (60*17)) then return end
  886.  
  887.     local light = gameworld:getLight(tx, ty)
  888.     if (light[1]+light[2]+light[3]) > 0.2 then return end
  889.  
  890.  
  891.  
  892.     if is_solid(gameworld, tx, ty-1) == false then return end
  893.     -- needs a 2x3 of free space
  894.     if is_solid(gameworld, tx, ty) then return end
  895.     if is_solid(gameworld, tx, ty+1) then return end
  896.     if is_solid(gameworld, tx+1, ty) then return end
  897.     if is_solid(gameworld, tx+1, ty+1) then return end
  898.     if is_solid(gameworld, tx, ty+2) then return end
  899.     if is_solid(gameworld, tx+1, ty+2) then return end
  900.  
  901.     local mob = gameworld:addEntity("caster")
  902.  
  903.     mob:teleport(jutils.vec2.new(tx*config.TILE_SIZE, ty*config.TILE_SIZE))
  904. end
  905.  
  906. local function try_slime_spawn(gameworld, tx, ty)
  907.  
  908.     -- must be day time
  909.     if not (gameworld.worldtime > (7*60) and gameworld.worldtime < (60*17)) then return end
  910.  
  911.     --local light = gameworld:getLight(tx, ty)
  912.     --if (light[1]+light[2]+light[3]) < 0.6 then return end
  913.  
  914.     -- needs a 2x3 of free space
  915.     if is_solid(gameworld, tx, ty) then return end
  916.     if is_solid(gameworld, tx, ty+1) then return end
  917.     if is_solid(gameworld, tx+1, ty) then return end
  918.     if is_solid(gameworld, tx+1, ty+1) then return end
  919.     if is_solid(gameworld, tx, ty+2) then return end
  920.     if is_solid(gameworld, tx+1, ty+2) then return end
  921.  
  922.     if gameworld:getTile(tx, ty-1) ~= tiles.GRASS.id then return end
  923.  
  924.     local mob = gameworld:addEntity("slime")
  925.  
  926.     mob:teleport(jutils.vec2.new(tx*config.TILE_SIZE, ty*config.TILE_SIZE))
  927. end
  928.  
  929. local mob_weights = {
  930.     [1] = {
  931.         weight = 0.27,
  932.         func = try_zombie_spawn,
  933.     },
  934.     [2] = {
  935.         weight = 0.15,
  936.         func = try_flower_spawn,
  937.     },
  938.     [3] = {
  939.         weight = 0.01,
  940.         func = try_tortured_spawn,
  941.     },
  942.     [4] = {
  943.         weight = 0.005,
  944.         func = try_caster_spawn,
  945.     },
  946.     [5] = {
  947.         weight = 0.05,
  948.         func = try_slime_spawn,
  949.     }
  950. }
  951.  
  952. local function mob_spawn_pass(gameworld, tx, ty)
  953.  
  954.     local selected_mob = math.random(#mob_weights)
  955.     local mob_roll = math.random()
  956.     local weight = mob_weights[selected_mob].weight
  957.  
  958.     if mob_roll < weight then
  959.         local spawn_func = mob_weights[selected_mob].func
  960.         spawn_func(gameworld, tx, ty)
  961.     end
  962. end
  963.  
  964. ---
  965. function world:map_updateCurrentChunks()
  966.    
  967.     for key, chunk in pairs(self.chunks) do
  968.         if chunk.visible == true and chunk.loaded == true then
  969.             self:chunkTileUpdates(chunk)
  970.             self:chunkRandomTileUpdates(chunk)
  971.         end
  972.  
  973.         -- entity spawning
  974.         -- chunk must be off screen and fully generated
  975.         if chunk.visible == false and chunk.requestedFully == true then
  976.             -- max 50 entities
  977.             -- random roll chance to spawn
  978.             if math.random() > 0.95 and #self.entities < 50 then
  979.                 --print("entity spawn attempt")
  980.                 -- pick random point in chunk
  981.                 local randx, randy = math.random(1, config.CHUNK_SIZE), math.random(1, config.CHUNK_SIZE)
  982.                 -- get chunk's world position
  983.                 local wx, wy = chunk.position.x*config.CHUNK_SIZE, chunk.position.y*config.CHUNK_SIZE
  984.                 -- add the two together to get the world position of the chosen spawnpoint
  985.                 local tx, ty = wx+randx, wy+randy
  986.  
  987.                 mob_spawn_pass(self, tx, ty)
  988.             end
  989.         end
  990.     end
  991. end
  992. ----------------------------------------------------------------------------------
  993. -- ENTITY AND COLLISION HANDLING
  994.  
  995. local lightthing = 0
  996. local emitlighttimer = 0
  997.  
  998. ---
  999. function world:updateEntities(dt)
  1000.     lightthing = lightthing + dt
  1001.     emitlighttimer = emitlighttimer + dt
  1002.     for idx, entity in pairs(self.entities) do
  1003.         entity:update(dt)
  1004.  
  1005.         -- update camera position to player
  1006.         if entity:isA("Player") then
  1007.             local player = entity -- (cast)
  1008.             local camera_pos = self.camera.position
  1009.            
  1010.             --self.camera.position = player.position
  1011.  
  1012.             -- if camera is falling behind the player
  1013.             -- we should lerp much faster
  1014.             if camera_pos:distance(player.position) > 500 then
  1015.                 self.camera.position = player.position
  1016.             else
  1017.                 self.camera.position = camera_pos:lerp(player.position, (dt*5))
  1018.             end
  1019.         end
  1020.  
  1021.         if emitlighttimer > (1/20) then
  1022.             if entity.lightemitter then
  1023.                 local tx, ty = grid.pixelToTileXY(entity.position.x, entity.position.y)
  1024.                 channels.setlight:push({tx, ty, entity.lightemitter[1], entity.lightemitter[2], entity.lightemitter[3]})
  1025.             end
  1026.         end
  1027.  
  1028.         -- remove entity references if they are dead (allow garbage collection)
  1029.         -- determine if entity is outside of loaded chunks (so we can kill it)
  1030.         local x, y = self.focuspoint.x*config.CHUNK_SIZE*config.TILE_SIZE, self.focuspoint.y*config.CHUNK_SIZE*config.TILE_SIZE
  1031.         local radius = config.CHUNK_DRAW_RADIUS+config.CHUNK_SIMULATION_RADIUS+config.CHUNK_BUFFER_RADIUS
  1032.  
  1033.         radius = (radius * config.CHUNK_SIZE) * config.TILE_SIZE
  1034.  
  1035.         if entity:isA("PhysicalEntity") and (entity:isA("Player")==false) then
  1036.             if entity.position:distance(jutils.vec2.new(x, y)) > radius then
  1037.                 entity.unloadtimer = entity.unloadtimer + dt
  1038.             else
  1039.                 entity.unloadtimer = 0
  1040.             end
  1041.  
  1042.             if entity.unloadtimer > 1 then
  1043.                 entity.dead = true
  1044.             end
  1045.         end
  1046.  
  1047.         local tx, ty = grid.pixelToTileXY(entity.position.x, entity.position.y)
  1048.  
  1049.         if lightthing > (1/10) then
  1050.             entity.light = self:getLight(tx, ty)
  1051.         end
  1052.  
  1053.         if entity.dead then
  1054.             entity:dies()
  1055.             if entity:isA("Player") == false then
  1056.                 entity.world = nil
  1057.                 self.entities[idx] = nil
  1058.                 entity = nil
  1059.             end
  1060.         end
  1061.     end
  1062.     if lightthing > (1/10) then
  1063.         lightthing = 0
  1064.     end
  1065.     if emitlighttimer > (1/20) then
  1066.         emitlighttimer = 0
  1067.     end
  1068. end
  1069.  
  1070. ---
  1071. function world:drawchunks()
  1072.     for _, chunk in pairs(self.chunks) do
  1073.         if chunk.visible == true then
  1074.             local wx, wy = chunk.position.x, chunk.position.y
  1075.  
  1076.             local c_tiles = chunk.tiles
  1077.             local c_backgrounds = chunk.backgrounds
  1078.             local c_states = chunk.states
  1079.             local c_damages = chunk.damage
  1080.             local c_lights = chunk.light
  1081.  
  1082.             for x = 1, config.CHUNK_SIZE do
  1083.                 for y = 1, config.CHUNK_SIZE do
  1084.                     local tileid = c_tiles[x][y]
  1085.                     local bgid = c_backgrounds[x][y]
  1086.                     local r = c_lights[1][x][y]
  1087.                     local g = c_lights[2][x][y]
  1088.                     local b = c_lights[3][x][y]
  1089.  
  1090.                     -- cheat mode used to look at the entire world
  1091.                     if _G.FULLBRIGHT then
  1092.                         r = 1 g = 1 b = 1
  1093.                     end
  1094.  
  1095.                     if bgid > 0 then
  1096.                         rendering.queuebackground(bgid, r, g, b, ((wx) * config.CHUNK_SIZE) + x - 1, ((wy) * config.CHUNK_SIZE) + y - 1)
  1097.                     end
  1098.                     --if type(tileid) ~= "number" then error(tileid) end
  1099.                     if tileid > 0 then
  1100.                         rendering.queuetile(tileid, c_states[x][y], c_damages[x][y], r, g, b, ((wx) * config.CHUNK_SIZE) + x - 1, ((wy) * config.CHUNK_SIZE) + y - 1)
  1101.                     end
  1102.                 end
  1103.             end
  1104.         end
  1105.     end
  1106. end
  1107.  
  1108. local tileUpdateDelta = 0
  1109. local redrawTick = 0
  1110.  
  1111. ---
  1112.  
  1113. local biometransition = 0
  1114.  
  1115. local lastbiome = nil
  1116. local currentbiome = nil
  1117.  
  1118. ---
  1119. function world:update(dt)
  1120.  
  1121.     -- thread error checking
  1122.     local err = self.lightthread:getError()
  1123.     assert( not err, err )
  1124.  
  1125.     local err = self.chunkthread:getError()
  1126.     assert( not err, err )
  1127.    
  1128.     local num = channels.newlights:getCount()
  1129.  
  1130.     for i = 1, num do
  1131.         local newlight = channels.newlights:pop()
  1132.         if i < (num-100) then return end
  1133.         if newlight then
  1134.            
  1135.             local key = newlight[1]
  1136.             local data = newlight[2]
  1137.             if self.chunks[key] then
  1138.                 self.chunks[key].light = data
  1139.             end
  1140.         end
  1141.     end
  1142.  
  1143.     self.worldtime = self.worldtime + (dt)
  1144.  
  1145.     if self.worldtime > 1440 then
  1146.         self.worldtime = 0
  1147.         self.dayspassed = self.dayspassed + 1
  1148.         print("days passed", self.dayspassed)
  1149.     end
  1150.  
  1151.     self:map_clearChunkFields()
  1152.     self:map_pullLoadedChunks()
  1153.     self:map_fillChunkFields()
  1154.     self:map_loadNewChunks()
  1155.     self:map_unloadOldChunks()
  1156.  
  1157.     tileUpdateDelta = tileUpdateDelta + dt
  1158.     if tileUpdateDelta >= 1/60 then
  1159.         self:map_updateCurrentChunks()
  1160.         tileUpdateDelta = tileUpdateDelta - (1/60)
  1161.     end
  1162.  
  1163.     redrawTick = redrawTick + dt
  1164.     if redrawTick > (1/config.TILE_REDRAWS_PER_SECOND) then
  1165.         if self.ambientlight ~= get_daylight(self.worldtime) then
  1166.             self.ambientlight = get_daylight(self.worldtime)
  1167.             channels.setambient:push(self.ambientlight)
  1168.         end
  1169.        
  1170.         redrawTick = 0
  1171.         rendering.clearqueue()
  1172.         self:drawchunks()
  1173.     end
  1174.  
  1175.     self:updateEntities(dt)
  1176.  
  1177.     particlesystem.update(dt)
  1178.  
  1179.     -- need to set world focus point
  1180.     -- for chunk loading origin
  1181.     local px, py = grid.pixelToTileXY(self.camera.position.x, self.camera.position.y)
  1182.     local camx, camy = grid.tileToChunkXY(px, py)
  1183.     local fp = jutils.vec2.new(camx, camy)
  1184.  
  1185.     self.focuspoint = fp
  1186.  
  1187.     local biome = terrainMath.getBiomeAt(math.floor(self:getPlayer().position.x/8))
  1188.     currentbiome = biome
  1189.  
  1190.     if currentbiome ~= lastbiome then
  1191.         biometransition = biometransition + (dt/2)
  1192.  
  1193.  
  1194.         if biometransition >= 1 then
  1195.             lastbiome = currentbiome
  1196.         end
  1197.     else
  1198.         biometransition = 0
  1199.     end
  1200. end
  1201.  
  1202. function world:drawEntities()
  1203.     for _, entity in pairs(self.entities) do
  1204.         entity:draw()
  1205.     end
  1206. end
  1207.  
  1208. -- a color is assigned for each 3-hour segment of the day
  1209. -- and the colors are lerped between
  1210.  
  1211. local SHOW_ENTITY_LIST = false
  1212.  
  1213.  
  1214. local cloud_bg_texture = love.graphics.newImage("assets/clouds.png")
  1215. local star_bg_texture = love.graphics.newImage("assets/stars.png")
  1216. local cave_bg_texture = love.graphics.newImage("assets/cavebg.png")
  1217.  
  1218. function world:draw()
  1219.  
  1220.     -- store graphics coordinate info to reset later
  1221.     love.graphics.push()
  1222.  
  1223.     local sky_color = {0, 0, 0}
  1224.  
  1225.     local world_time_hour = self.worldtime/60
  1226.  
  1227.     local daytime_color = {0.05, 0.3, 0.85}
  1228.     local daytime_color_lower = {0, 0.2, 0.5}
  1229.  
  1230.     -- daytime
  1231.     if world_time_hour >= 7 and world_time_hour <= 19 then
  1232.         sky_color = daytime_color
  1233.        
  1234.         love.graphics.setColor(daytime_color)
  1235.         love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
  1236.  
  1237.         for slice = 0, 20 do
  1238.             love.graphics.setColor(jutils.color.lerp(daytime_color, daytime_color_lower, slice/20))
  1239.             love.graphics.rectangle("fill", 0, (love.graphics.getHeight()/20)*slice, love.graphics.getWidth(), love.graphics.getHeight()/20)
  1240.         end
  1241.     end
  1242.  
  1243.     -- night time
  1244.     if world_time_hour >= 20 or world_time_hour <= 6 then
  1245.  
  1246.         sky_color = {0, 0, 0.01}
  1247.         love.graphics.setColor(sky_color)
  1248.         love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
  1249.     end
  1250.  
  1251.     -- sunrise
  1252.     if world_time_hour >= 6 and world_time_hour <= 7 then
  1253.  
  1254.         local diff_time = ((self.worldtime/60)-6)
  1255.         local top_color = jutils.color.multiply({diff_time, diff_time, diff_time}, daytime_color)
  1256.         local bottom_color = jutils.color.multiply({diff_time, diff_time, diff_time}, {0.85, 0.85, 0.15})
  1257.  
  1258.         local cucr = 0
  1259.  
  1260.         if world_time_hour > 6.75 then
  1261.             cucr = (world_time_hour-6.75)*2
  1262.         end
  1263.  
  1264.         for slice = 0, 20 do
  1265.  
  1266.             love.graphics.setColor(jutils.color.lerp(top_color, bottom_color, (slice/20)-cucr))
  1267.             love.graphics.rectangle("fill", 0, (love.graphics.getHeight()/20)*slice, love.graphics.getWidth(), love.graphics.getHeight()/20)
  1268.         end
  1269.     end
  1270.  
  1271.     -- sunset
  1272.     if world_time_hour >= 19 and world_time_hour <= 20 then
  1273.         local diff_time = 1-((self.worldtime/60)-19)
  1274.         local top_color = jutils.color.multiply({diff_time, diff_time, diff_time}, daytime_color)
  1275.         local bottom_color = jutils.color.multiply({diff_time, diff_time, diff_time}, {0.75, 0.35, 0.15})
  1276.  
  1277.         local cucr = 0
  1278.  
  1279.         if world_time_hour < 19.25 then cucr = -((world_time_hour-19.25)*2) end
  1280.  
  1281.         if world_time_hour > 19.75 then cucr = (world_time_hour-19.75)*2 end
  1282.  
  1283.         for slice = 0, 20 do
  1284.             love.graphics.setColor(jutils.color.lerp(top_color, bottom_color, (slice/20)-cucr))
  1285.             love.graphics.rectangle("fill", 0, (love.graphics.getHeight()/20)*slice, love.graphics.getWidth(), love.graphics.getHeight()/20)
  1286.         end
  1287.     end
  1288.  
  1289.     --love.graphics.setColor(sky_color)
  1290.     --love.graphics.rectangle("fill", 0, 0, love.graphics.getWidth(), love.graphics.getHeight())
  1291.  
  1292.     -- make screen center the origin of screen coordinates
  1293.     love.graphics.translate(math.floor(love.graphics.getWidth() / 2), math.floor(love.graphics.getHeight() / 2))
  1294.     -- zoom in coordinates
  1295.     love.graphics.scale(self.camera.zoom, self.camera.zoom)
  1296.  
  1297.     -- rounding the camera position is nessecary
  1298.     -- to eliminate line flickering between
  1299.     -- grid objects (tiles and backgrounds)
  1300.     local camx = jutils.math.round(self.camera.position.x, 2)
  1301.     local camy = jutils.math.round(self.camera.position.y, 2)
  1302.    
  1303.     -- move to camera position (make camx and camy center of screen)
  1304.     love.graphics.push()
  1305.     love.graphics.translate(-camx, -camy)
  1306.  
  1307.     local mx, my = love.graphics.inverseTransformPoint(love.mouse.getX(), love.mouse.getY())
  1308.     input.setTransformedMouse(math.floor(mx), math.floor(my))
  1309.  
  1310.     local camera_pos = self.camera.position
  1311.  
  1312.     -- draw cloud background
  1313.     -- TODO: make cloud layer scroll at 1.25 speed correctly!
  1314.     local bgscroll = 2
  1315.     local texsize = 512
  1316.    
  1317.     local x = (camera_pos.x) / bgscroll
  1318.     local y = camera_pos.y / bgscroll
  1319.  
  1320.     local posx = math.floor(x/texsize)
  1321.     local posy = math.floor(y/texsize)
  1322.    
  1323.     for dx = -4, 4 do
  1324.         for dy = -3, 3 do
  1325.                
  1326.             local shiftx = x + ( (posx+dx)*texsize)
  1327.             local shifty = y + ( (posy+dy)*texsize)
  1328.  
  1329.             if self.camera.position.y/8 > (config.UNDERGROUND_DEPTH)-50 then
  1330.  
  1331.                 if self:getPlayer() ~= nil then
  1332.                     local tx, ty = grid.pixelToTileXY(shiftx, shifty)
  1333.  
  1334.                     local color = self:getPlayer().light
  1335.  
  1336.                     -- TODO: make the bgcolor darker for the farther out tiles
  1337.                     --! or make it reflect the color of that area
  1338.                     love.graphics.setColor(color)
  1339.                     love.graphics.draw(cave_bg_texture, shiftx, shifty, 0, 2, 2)
  1340.                 end
  1341.             else
  1342.                 love.graphics.setColor(self.ambientlight, self.ambientlight, self.ambientlight, self.ambientlight)
  1343.                 love.graphics.draw(cloud_bg_texture, shiftx, shifty, 0, 2, 2)
  1344.                 love.graphics.setColor(1-self.ambientlight, 1-self.ambientlight, 1-self.ambientlight, 1-self.ambientlight)
  1345.                 love.graphics.draw(star_bg_texture, shiftx, shifty, 0, 2, 2)
  1346.             end
  1347.         end
  1348.     end
  1349.  
  1350.     local bgscroll = 2
  1351.     local texsize = 512
  1352.    
  1353.     local x = (camera_pos.x) / bgscroll
  1354.     local y = camera_pos.y / bgscroll
  1355.  
  1356.     local posx = math.floor(x/256)
  1357.     local posy = math.floor(0)
  1358.  
  1359.     -- TODO: make background transition when moving biomes
  1360.     -- biome background
  1361.  
  1362.    
  1363.     if currentbiome and biome_bg_textures[currentbiome] ~= nil then
  1364.         for dx = -3, 3 do
  1365.  
  1366.             local shiftx = x + ( (posx+dx)*256)
  1367.             local shifty = y + ( (posy)*512)
  1368.             love.graphics.setColor(self.ambientlight-0.1, self.ambientlight-0.1, self.ambientlight-0.1, (biometransition~=0) and biometransition or 1)
  1369.  
  1370.             love.graphics.draw(biome_bg_textures[currentbiome], shiftx, shifty, 0, 2, 2)
  1371.         end
  1372.     end
  1373.  
  1374.     if lastbiome ~= currentbiome and biome_bg_textures[lastbiome] ~= nil then
  1375.         for dx = -3, 3 do
  1376.  
  1377.             local shiftx = x + ( (posx+dx)*256)
  1378.             local shifty = y + ( (posy)*512)
  1379.             love.graphics.setColor(self.ambientlight-0.1, self.ambientlight-0.1, self.ambientlight-0.1, 1-biometransition)
  1380.  
  1381.             love.graphics.draw(biome_bg_textures[lastbiome], shiftx, shifty, 0, 2, 2)
  1382.         end
  1383.     end
  1384.  
  1385.     love.graphics.setColor(1,1,1)
  1386.     -- draw foreground and background tiles
  1387.     love.graphics.pop()
  1388.     rendering.drawqueue(-self.camera.position.x, -self.camera.position.y)
  1389.     love.graphics.translate(-camx, -camy)
  1390.     -- draw entities
  1391.     self:drawEntities()
  1392.     particlesystem.draw()
  1393.    
  1394.     love.graphics.pop()
  1395.  
  1396.     if SHOW_ENTITY_LIST then
  1397.         love.graphics.setColor(0,0,0)
  1398.         local inc = 0
  1399.         for _, entity in pairs(self.entities) do
  1400.             love.graphics.print(entity.classname..", ".. tostring(entity), 800, inc*12)
  1401.             inc = inc + 1
  1402.         end
  1403.     end
  1404. end
  1405.  
  1406. return world
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement