init.lua 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. local update_interval = 0.2
  2. local level_delta = 2
  3. local shiny_items = {}
  4. --- Shining API ---
  5. wielded_light = {}
  6. function wielded_light.update_light(pos, light_level)
  7. local around_vector = {
  8. {x=0, y=0, z=0},
  9. {x=0, y=1, z=0}, {x=0, y=-1, z=0},
  10. {x=1, y=0, z=0}, {x=-1, y=0, z=0},
  11. {x=0, y=0, z=1}, {x=0, y=0, z=1},
  12. }
  13. local do_update = false
  14. local old_value = 0
  15. local timer
  16. local light_pos
  17. for _, around in ipairs(around_vector) do
  18. light_pos = vector.add(pos, around)
  19. local name = minetest.get_node(light_pos).name
  20. if name == "air" and (minetest.get_node_light(light_pos) or 0) < light_level then
  21. do_update = true
  22. break
  23. elseif name:sub(1,13) == "wielded_light" then -- Update existing light node and timer
  24. old_value = tonumber(name:sub(15))
  25. if light_level > old_value then
  26. do_update = true
  27. else
  28. timer = minetest.get_node_timer(light_pos)
  29. local elapsed = timer:get_elapsed()
  30. if elapsed > (update_interval * 1.5) then
  31. -- The timer is set to 3x update_interval
  32. -- This node was not updated the last interval and may
  33. -- is disabled before the next step
  34. -- Therefore the light should be re-set to avoid flicker
  35. do_update = true
  36. end
  37. end
  38. break
  39. end
  40. end
  41. if do_update then
  42. timer = timer or minetest.get_node_timer(light_pos)
  43. if light_level ~= old_value then
  44. minetest.swap_node(light_pos, {name = "wielded_light:"..light_level})
  45. end
  46. timer:start(update_interval*3)
  47. end
  48. end
  49. function wielded_light.update_light_by_item(item, pos)
  50. local stack = ItemStack(item)
  51. local light_level = shiny_items[stack:get_name()]
  52. local itemdef = stack:get_definition()
  53. if not light_level and not itemdef then
  54. return
  55. end
  56. light_level = light_level or ((itemdef.light_source or 0) - level_delta)
  57. if light_level > 0 then
  58. wielded_light.update_light(pos, light_level)
  59. end
  60. end
  61. function wielded_light.register_item_light(itemname, light_level)
  62. shiny_items[itemname] = light_level
  63. end
  64. -- Register helper nodes
  65. for i=1, 14 do
  66. minetest.register_node("wielded_light:"..i, {
  67. drawtype = "airlike",
  68. groups = {not_in_creative_inventory = 1},
  69. walkable = false,
  70. paramtype = "light",
  71. sunlight_propagates = true,
  72. light_source = i,
  73. pointable = false,
  74. buildable_to = true,
  75. drop = {},
  76. on_timer = function(pos, elapsed)
  77. minetest.swap_node(pos, {name = "air"})
  78. end,
  79. })
  80. end
  81. -- Wielded item shining globalstep
  82. local timer = 0
  83. minetest.register_globalstep(function(dtime)
  84. timer = timer + dtime;
  85. if timer < update_interval then
  86. return
  87. end
  88. timer = 0
  89. for _, player in pairs(minetest.get_connected_players()) do
  90. -- predict where the player will be the next time we place the light
  91. -- assume that on average we're slightly past 1/2 of the next interval, hence 1.5
  92. -- (since the scheduling is a bit behind)
  93. -- experimentally this also works nicely
  94. local pos = vector.add (
  95. vector.add({x = 0, y = 1, z = 0}, vector.round(player:get_pos())),
  96. vector.round(vector.multiply(player:get_velocity(), update_interval * 1.5))
  97. )
  98. wielded_light.update_light_by_item(player:get_wielded_item(), pos)
  99. end
  100. end)
  101. -- Dropped item on_step override
  102. -- https://github.com/minetest/minetest/issues/6909
  103. local builtin_item = minetest.registered_entities["__builtin:item"]
  104. local item = {
  105. on_step = function(self, dtime, moveresult)
  106. builtin_item.on_step(self, dtime, moveresult)
  107. self.shining_timer = (self.shining_timer or 0) + dtime
  108. if self.shining_timer >= update_interval then
  109. self.shining_timer = 0
  110. local pos = self.object:get_pos()
  111. if pos then
  112. wielded_light.update_light_by_item(self.itemstring, pos)
  113. end
  114. end
  115. end
  116. }
  117. setmetatable(item, {__index = builtin_item})
  118. minetest.register_entity(":__builtin:item", item)
  119. ---TEST
  120. --wielded_light.register_item_light('default:dirt', 14)