core.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446
  1. -------------------------
  2. -- Sky Layers: Core
  3. -- Git: https://gitlab.com/rautars/skylayer
  4. -- License: MIT
  5. -- Credits: rautars
  6. -- Thanks: Perkovec for colorise utils (github.com/Perkovec/colorise-lua)
  7. -------------------------
  8. local modpath = minetest.get_modpath("skylayer");
  9. local colorise = dofile(modpath.."/thirdparty/colorise-lua/colorise.lua")
  10. local core = {}
  11. core.settings = {}
  12. -- flag to disable skylayer at global step
  13. core.settings.enabled = true
  14. -- default gradient interval values
  15. core.settings.gradient_default_min_value = 0
  16. core.settings.gradient_default_max_value = 1000
  17. -- how often sky will be updated in seconds
  18. core.settings.update_interval = 4
  19. -- helps track total dtime
  20. core.timer = 0
  21. core.default_clouds = nil
  22. core.default_moon = nil
  23. core.default_sun = nil
  24. core.default_stars = nil
  25. core.default_sky_color = nil
  26. -- keeps player related data such as player itself and own sky layers
  27. core.sky_players = {}
  28. -- flag for minetest legacy version (< 5.1.1), value will be initialized lazily
  29. core.legacy = nil
  30. -- A helper function to imitate ternary operator for inline if/else checks
  31. core.ternary = function(condition, trueVal, falseVal)
  32. if condition then
  33. return trueVal
  34. end
  35. return falseVal
  36. end
  37. -- adds player to sky layer affected players list
  38. core.add_player = function(player)
  39. local data = {}
  40. data.id = player:get_player_name()
  41. data.player = player
  42. data.skylayers = {}
  43. table.insert(core.sky_players, data)
  44. end
  45. -- remove player from sky layer affected players list
  46. core.remove_player = function(player_name)
  47. if #core.sky_players == 0 then
  48. return
  49. end
  50. for k, player_data in ipairs(core.sky_players) do
  51. if player_data.id == player_name then
  52. reset_sky(player_data.player)
  53. table.remove(core.sky_players, k)
  54. return
  55. end
  56. end
  57. end
  58. core.get_player_by_name = function(player_name)
  59. if player_name == nil then
  60. return nil
  61. end
  62. if #minetest.get_connected_players() == 0 then
  63. return nil
  64. end
  65. for i, player in ipairs(minetest.get_connected_players()) do
  66. if player:get_player_name() == player_name then
  67. return player
  68. end
  69. end
  70. return nil
  71. end
  72. core.get_player_data = function(player_name)
  73. if #core.sky_players == 0 then
  74. return nil
  75. end
  76. for k, player_data in ipairs(core.sky_players) do
  77. if player_data.id == player_name then
  78. return player_data
  79. end
  80. end
  81. end
  82. core.create_new_player_data = function(player_name)
  83. local player_data = core.get_player_data(player_name)
  84. if player_data == nil then
  85. local player = core.get_player_by_name(player_name)
  86. if player == nil then
  87. minetest.log("error", "Fail to resolve player '" .. player_name .. "'")
  88. return
  89. end
  90. core.add_player(player)
  91. return core.get_player_data(player_name)
  92. end
  93. return player_data
  94. end
  95. -- sets default / regular sky for player
  96. core.reset_sky = function(player)
  97. player:set_clouds(core.default_clouds)
  98. if core.legacy then
  99. player:set_sky(nil, "regular", nil)
  100. else
  101. player:set_sky({
  102. base_color = nil,
  103. type = "regular",
  104. textures = nil,
  105. clouds = true,
  106. sky_color = core.default_sky_color
  107. })
  108. player:set_moon(core.default_moon)
  109. player:set_sun(core.default_sun)
  110. player:set_stars(core.default_stars)
  111. end
  112. end
  113. -- resolves latest skylayer based on added layer time
  114. core.get_latest_layer = function(layers)
  115. if #layers == 0 then
  116. return nil
  117. end
  118. local latest_layer = nil
  119. for k, layer in ipairs(layers) do
  120. if latest_layer == nil then
  121. latest_layer = layer
  122. else
  123. if layer.added_time >= latest_layer.added_time then
  124. latest_layer = layer
  125. end
  126. end
  127. end
  128. return latest_layer
  129. end
  130. core.convert_to_rgb = function(minval, maxval, current_val, colors)
  131. local max_index = #colors - 1
  132. local val = (current_val-minval) / (maxval-minval) * max_index + 1.0
  133. local index1 = math.floor(val)
  134. local index2 = math.min(math.floor(val) + 1, max_index + 1)
  135. local f = val - index1
  136. local c1 = colors[math.max(index1, 1)]
  137. local c2 = colors[index2]
  138. if c2 == nil then -- TODO need dig this case more carefully and improve handling
  139. c2 = colors[max_index]
  140. end
  141. return {
  142. r = math.floor(c1.r + f * (c2.r - c1.r)),
  143. g = math.floor(c1.g + f * (c2.g - c1.g)),
  144. b = math.floor(c1.b + f * (c2.b - c1.b))
  145. }
  146. end
  147. -- Returns current gradient color in {r, g, b} format
  148. core.calculate_current_gradient_color = function(gradient_colors, min_val, max_val)
  149. if gradient_colors == nil then return nil end
  150. if min_val == nil then
  151. min_val = core.settings.gradient_default_min_value
  152. end
  153. if max_val == nil then
  154. max_val = core.settings.gradient_default_max_value
  155. end
  156. local rounded_time = math.floor(minetest.get_timeofday() * max_val)
  157. return core.convert_to_rgb(min_val, max_val, rounded_time, gradient_colors)
  158. end
  159. -- Returns current sky color in {r, g, b} format
  160. core.get_current_layer_color = function(gradient_colors, min_val, max_val)
  161. return core.calculate_current_gradient_color(gradient_colors, min_val, max_val)
  162. end
  163. -- Returns current cloud color in hex format
  164. core.calculate_color_hex_value = function(gradient_colors, min_val, max_val)
  165. local rgb_color = core.calculate_current_gradient_color(gradient_colors, min_val, max_val)
  166. if rgb_color == nil then return nil end
  167. return colorise.rgb2hex({rgb_color.r, rgb_color.g, rgb_color.b})
  168. end
  169. core.resolve_sky_color = function(sky_data)
  170. local sky_color = sky_data.sky_color
  171. local gradient_sky = sky_data.gradient_sky
  172. if sky_color == nil and gradient_sky == nil then
  173. return core.default_sky_color
  174. end
  175. if sky_color == nil then
  176. sky_color = {}
  177. end
  178. -- merge user set color values with worlds defaults
  179. local merged_sky_color = {
  180. day_sky = sky_color.day_sky and sky_color.day_sky or core.default_sky_color.day_sky,
  181. day_horizon = sky_color.day_horizon and sky_color.day_horizon or core.default_sky_color.day_horizon,
  182. dawn_sky = sky_color.dawn_sky and sky_color.dawn_sky or core.default_sky_color.dawn_sky,
  183. dawn_horizon = sky_color.dawn_horizon and sky_color.dawn_horizon or core.default_sky_color.dawn_horizon,
  184. night_sky = sky_color.night_sky and sky_color.night_sky or core.default_sky_color.night_sky,
  185. night_horizon = sky_color.night_horizon and sky_color.night_horizon or core.default_sky_color.night_horizon,
  186. indoors = sky_color.indoors and sky_color.indoors or core.default_sky_color.indoors,
  187. fog_sun_tint = sky_color.fog_sun_tint and sky_color.fog_sun_tint or core.default_sky_color.fog_sun_tint,
  188. fog_moon_tint = sky_color.fog_moon_tint and sky_color.fog_moon_tint or core.default_sky_color.fog_moon_tint,
  189. fog_tint_type = sky_color.fog_tint_type and sky_color.fog_tint_type or core.default_sky_color.fog_tint_type
  190. }
  191. if gradient_sky == nil then
  192. return merged_sky_color
  193. end
  194. local time_of_day = math.floor(minetest.get_timeofday() * 1000)
  195. if gradient_sky.day_sky ~= nil and time_of_day > 190 and time_of_day < 800 then
  196. merged_sky_color.day_sky = core.calculate_color_hex_value(gradient_sky.day_sky, 200, 750)
  197. end
  198. if gradient_sky.day_horizon ~= nil and time_of_day > 190 and time_of_day < 800 then
  199. merged_sky_color.day_horizon = core.calculate_color_hex_value(gradient_sky.day_horizon, 200, 750)
  200. end
  201. if gradient_sky.dawn_sky ~= nil and time_of_day >= 750 and time_of_day <= 850 then
  202. merged_sky_color.dawn_sky = core.calculate_color_hex_value(gradient_sky.dawn_sky, 750, 850)
  203. end
  204. if gradient_sky.dawn_horizon ~= nil and time_of_day >= 750 and time_of_day <= 850 then
  205. merged_sky_color.dawn_horizon = core.calculate_color_hex_value(gradient_sky.dawn_horizon, 750, 850)
  206. end
  207. if time_of_day >= 800 or time_of_day <= 190 then
  208. local night_sky_min = time_of_day >= 800 and 800 or 0
  209. local night_sky_max = time_of_day >= 800 and 1000 or 200
  210. if gradient_sky.night_sky ~= nil then
  211. merged_sky_color.night_sky = core.calculate_color_hex_value(gradient_sky.night_sky, night_sky_min, night_sky_max)
  212. end
  213. if gradient_sky.night_horizon ~= nil then
  214. merged_sky_color.night_horizon = core.calculate_color_hex_value(gradient_sky.night_horizon, night_sky_min, night_sky_max)
  215. end
  216. if gradient_sky.fog_moon_tint ~= nil then
  217. merged_sky_color.fog_moon_tint = core.calculate_color_hex_value(gradient_sky.fog_moon_tint, night_sky_min, night_sky_max)
  218. end
  219. elseif gradient_sky.fog_sun_tint ~= nil then
  220. merged_sky_color.fog_sun_tint = core.calculate_color_hex_value(gradient_sky.fog_sun_tint, 200, 750)
  221. end
  222. if gradient_sky.indoors ~= nil then
  223. merged_sky_color.indoors = core.calculate_color_hex_value(gradient_sky.indoors)
  224. end
  225. return merged_sky_color
  226. end
  227. core.update_sky_details = function(player, sky_layer)
  228. local sky_data = sky_layer.sky_data
  229. if sky_data == nil then
  230. return
  231. end
  232. local bg_color = sky_data.base_color and sky_data.base_color or sky_data.bgcolor -- fallback to bgcolor legacy parameter
  233. if sky_data.gradient_colors ~= nil then
  234. bg_color = core.get_current_layer_color(
  235. sky_data.gradient_colors,
  236. sky_data.gradient_min_value,
  237. sky_data.gradient_max_value)
  238. end
  239. local sky_type = sky_data.type and sky_data.type or "plain"
  240. if sky_data.type == nil and (sky_data.sky_color ~= nil or sky_data.gradient_sky ~= nil) then
  241. sky_type = "regular"
  242. end
  243. if core.legacy then
  244. player:set_sky(
  245. bg_color,
  246. sky_type,
  247. sky_data.textures,
  248. sky_data.clouds
  249. )
  250. else
  251. player:set_sky({
  252. base_color = bg_color,
  253. type = sky_type,
  254. textures = sky_data.textures,
  255. clouds = sky_data.clouds,
  256. sky_color = core.resolve_sky_color(sky_data)
  257. })
  258. end
  259. end
  260. core.update_moon_details = function(player, sky_layer)
  261. local moon_data = sky_layer.moon_data
  262. if moon_data == nil then
  263. return
  264. end
  265. player:set_moon(moon_data)
  266. end
  267. core.update_sun_details = function(player, sky_layer)
  268. local sun_data = sky_layer.sun_data
  269. if sun_data == nil then
  270. return
  271. end
  272. player:set_sun(sun_data)
  273. end
  274. core.update_stars_details = function(player, sky_layer)
  275. local stars_data = sky_layer.stars_data
  276. if stars_data == nil then
  277. return
  278. end
  279. local _stars_color = core.calculate_color_hex_value(
  280. stars_data.gradient_star_colors,
  281. stars_data.gradient_star_min_value,
  282. stars_data.gradient_star_max_value)
  283. local star_brightness = stars_data.brightness and stars_data.brightness or "69"
  284. player:set_stars({
  285. visible = core.ternary(stars_data.visible, stars_data.visible, core.default_stars.visible),
  286. count = stars_data.count and stars_data.count or core.default_stars.count,
  287. star_color = _stars_color and _stars_color .. star_brightness or core.default_stars.star_color,
  288. scale = stars_data.scale and stars_data.scale or core.default_stars.scale
  289. })
  290. end
  291. core.update_clouds_details = function(player, sky_layer)
  292. local clouds_data = sky_layer.clouds_data
  293. if clouds_data == nil then
  294. return
  295. end
  296. local cloud_color = core.calculate_color_hex_value(
  297. clouds_data.gradient_colors,
  298. clouds_data.gradient_min_value,
  299. clouds_data.gradient_max_value)
  300. local ambient_color = core.calculate_color_hex_value(
  301. clouds_data.gradient_ambient_colors,
  302. clouds_data.gradient_ambient_min_value,
  303. clouds_data.gradient_ambient_max_value)
  304. player:set_clouds({
  305. color = cloud_color and clouds_data.color or core.default_clouds.color,
  306. density = clouds_data.density and clouds_data.density or core.default_clouds.density,
  307. ambient = ambient_color and clouds_data.ambient or core.default_clouds.ambient,
  308. height = clouds_data.height and clouds_data.height or core.default_clouds.height,
  309. thickness = clouds_data.thickness and clouds_data.thickness or core.default_clouds.thickness,
  310. speed = clouds_data.speed and clouds_data.speed or core.default_clouds.speed
  311. })
  312. end
  313. core.post_update_processing = function(player, player_data, sky_layer)
  314. if sky_layer.reset_defaults == true then
  315. core.reset_sky(player)
  316. sky_layer.reset_defaults = false
  317. end
  318. player_data.last_active_layer = sky_layer.name
  319. if player_data.last_active_layer == nil or player_data.last_active_layer ~= sky_layer.name then
  320. sky_layer.reset_defaults = true
  321. end
  322. end
  323. core.update_sky = function(player, timer)
  324. local player_data = core.get_player_data(player:get_player_name())
  325. if player_data == nil then return end
  326. local current_layer = core.get_latest_layer(player_data.skylayers)
  327. if current_layer == nil then return end
  328. if current_layer.updated == false or core.timer >= current_layer.update_interval then
  329. current_layer.updated = os.time()
  330. core.update_sky_details(player, current_layer)
  331. core.update_clouds_details(player, current_layer)
  332. if core.legacy == false then
  333. core.update_moon_details(player, current_layer)
  334. core.update_sun_details(player, current_layer)
  335. core.update_stars_details(player, current_layer)
  336. end
  337. core.post_update_processing(player, player_data, current_layer)
  338. end
  339. end
  340. minetest.register_on_joinplayer(function(player)
  341. if core.default_clouds == nil then
  342. core.default_clouds = player:get_clouds()
  343. end
  344. if core.legacy == nil then
  345. core.legacy = player.get_moon == nil and true or false
  346. end
  347. if core.default_moon == nil then
  348. core.default_moon = player.get_moon and player:get_moon() or {}
  349. core.default_moon.texture = "" -- according set_moon api description, empty string used for setting default texture.
  350. end
  351. if core.default_sun == nil then
  352. core.default_sun = player.get_sun and player:get_sun() or {}
  353. core.default_sun.texture = ""
  354. end
  355. if core.default_stars == nil then
  356. core.default_stars = player.get_stars and player:get_stars() or {}
  357. end
  358. if core.default_sky_color == nil then
  359. core.default_sky_color = player.get_sky_color and player:get_sky(true) or {}
  360. end
  361. end)
  362. minetest.register_globalstep(function(dtime)
  363. if core.settings.enabled == false then
  364. return
  365. end
  366. if #minetest.get_connected_players() == 0 then
  367. return
  368. end
  369. -- timer addition calculated outside of players loop
  370. core.timer = core.timer + dtime;
  371. for k, player in ipairs(minetest.get_connected_players()) do
  372. core.update_sky(player, core.timer)
  373. end
  374. -- reset timer outside of loop to make sure that all players sky will be updated
  375. if core.timer >= core.settings.update_interval then
  376. core.timer = 0
  377. end
  378. end)
  379. return core