init.lua 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. if not minetest.global_exists("sky") then sky = {} end
  2. sky.modpath = minetest.get_modpath("sky")
  3. sky.players = sky.players or {}
  4. -- Disable underground skycolors in singleplayer -- black sky gets in the way of
  5. -- mapgen development, esp regarding caves.
  6. local IS_SINGLEPLAYER = minetest.is_singleplayer()
  7. -- Localize for speed.
  8. local get_node = minetest.get_node
  9. local all_nodes = minetest.registered_nodes
  10. local ns_nodes = minetest.reg_ns_nodes
  11. local vector_distance = vector.distance
  12. local vector_round = vector.round
  13. local vector_equals = vector.equals
  14. local get_connected_players = minetest.get_connected_players
  15. local random = math.random
  16. local string_find = string.find
  17. -- Public API function.
  18. -- Used in the ambiance mod to determine the surface a player stands on.
  19. function sky.get_last_walked_node(pname)
  20. local data = sky.players[pname]
  21. if data then
  22. return data.snode
  23. end
  24. return ""
  25. end
  26. function sky.get_last_walked_nodeabove(pname)
  27. local data = sky.players[pname]
  28. if data then
  29. return data.wnode
  30. end
  31. return ""
  32. end
  33. -- Private function!
  34. -- This is the default `on_walkover' action.
  35. local function default_on_walkover(pos, name, player)
  36. local pname = player:get_player_name()
  37. -- Admin doesn't trigger default actions.
  38. if gdac.player_is_admin(pname) then
  39. return
  40. end
  41. -- Do not trigger if position is protected.
  42. if minetest.test_protection(pos, "") then
  43. return
  44. end
  45. if get_node(pos).name ~= name then
  46. return
  47. end
  48. local test_and_drop = function(p2)
  49. -- Don't drop if protected.
  50. if minetest.test_protection(p2, "") then
  51. return
  52. end
  53. local overhang = true
  54. for i = 1, 4, 1 do
  55. local node = get_node({x=p2.x, y=p2.y-i, z=p2.z})
  56. if node.name ~= "air" then
  57. overhang = false
  58. break
  59. end
  60. end
  61. if overhang then
  62. sfn.drop_node(p2)
  63. core.check_for_falling(p2)
  64. return true
  65. end
  66. end
  67. -- Drop several nodes under the player to ensure a likelihood of
  68. -- causing the player to fall down.
  69. local positions = {
  70. -- Test and drop lower nodes first.
  71. {x=pos.x, y=pos.y-1, z=pos.z},
  72. {x=pos.x+1, y=pos.y-1, z=pos.z},
  73. {x=pos.x-1, y=pos.y-1, z=pos.z},
  74. {x=pos.x, y=pos.y-1, z=pos.z+1},
  75. {x=pos.x, y=pos.y-1, z=pos.z-1},
  76. {x=pos.x, y=pos.y, z=pos.z},
  77. {x=pos.x+1, y=pos.y, z=pos.z},
  78. {x=pos.x-1, y=pos.y, z=pos.z},
  79. {x=pos.x, y=pos.y, z=pos.z+1},
  80. {x=pos.x, y=pos.y, z=pos.z-1},
  81. }
  82. local play_sound = false
  83. for k, v in ipairs(positions) do
  84. if test_and_drop(v) then
  85. play_sound = true
  86. end
  87. end
  88. if play_sound then
  89. ambiance.sound_play("default_gravel_footstep", pos, 1, 20)
  90. end
  91. end
  92. -- Private function!
  93. --
  94. -- This handles skycolor updates and `on_player_walk_over' calls.
  95. -- The walk-over calls are used in plenty of places, so do not break this!
  96. -- Also, we handle movement speed based on current node walked on.
  97. local function update_player(player, pname, pdata, playerpos, nodepos)
  98. -- Player doesn't walk over nodes if attached to some vehicle.
  99. if not default.player_attached[pname] then
  100. -- Get node player is standing ON.
  101. local snode = get_node(nodepos)
  102. local sname = snode.name
  103. -- Don't modify movement or call walk-over callbacks if node is air.
  104. -- This prevents players from getting better movement speed by hopping constantly.
  105. if sname ~= "air" and sname ~= "ignore" then
  106. local sdef = ns_nodes[sname] or all_nodes[sname] or {}
  107. -- Get node player is walking IN, not ON.
  108. -- Plants shall slow players down!
  109. local wnode = get_node(vector.add(nodepos, {x=0, y=1, z=0}))
  110. local wname = wnode.name
  111. -- Recompute movement speed only if either walked nodename changes.
  112. if sname ~= pdata.snode or wname ~= pdata.wnode then
  113. if sdef.movement_speed_depends then
  114. -- Assume node is slab-like and has a standard 'flat' orientation.
  115. local p2 = snode.param2
  116. local is_flat = false
  117. if p2 >= 0 and p2 <= 3 then
  118. is_flat = true
  119. elseif p2 >= 20 and p2 <= 23 then
  120. is_flat = true
  121. end
  122. if is_flat then
  123. -- If slab is flat and has a parent type, use the parent type.
  124. local def2 = ns_nodes[sdef.movement_speed_depends] or {}
  125. if def2 then sdef = def2 end
  126. end
  127. elseif sdef.move_speed_stair then
  128. -- Assume node is stair-like and has a standard 'flat' orientation.
  129. local p2 = snode.param2
  130. local is_flat = false
  131. if p2 >= 20 and p2 <= 23 then
  132. is_flat = true
  133. end
  134. if is_flat then
  135. -- If stair is flat and has a parent type, use the parent type.
  136. local def2 = ns_nodes[sdef.move_speed_stair] or {}
  137. if def2 then sdef = def2 end
  138. end
  139. end
  140. local smult = sdef.movement_speed_multiplier or default.NORM_SPEED
  141. local jmult = sdef.movement_jump_multiplier or default.NORM_JUMP
  142. pova.set_modifier(player, "physics", {speed=smult, jump=jmult}, "walking")
  143. if wname ~= "air" then
  144. -- But ignore doors.
  145. if not string.find(wname, "^doors:") then
  146. local wdef = ns_nodes[wname] or all_nodes[wname] or {}
  147. local smult2 = wdef.movement_speed_multiplier or default.NORM_SPEED
  148. local jmult2 = wdef.movement_jump_multiplier or default.NORM_JUMP
  149. pova.set_modifier(player, "physics", {speed=smult2, jump=jmult2}, "walking")
  150. end
  151. end
  152. -- Record the name of the last walked node.
  153. -- This is used by the ambiance mod to determine walked surface type.
  154. pdata.snode = sname
  155. pdata.wnode = wname
  156. end
  157. if not gdac_invis.is_invisible(pname) then
  158. -- Execute `on_walkover' callback for current walked node.
  159. -- Note, this must only be called ONCE for the walked node!
  160. -- This is ensured because we are only called max once per position.
  161. if sdef.walkable and sdef.on_player_walk_over then
  162. sdef.on_player_walk_over(nodepos, player)
  163. end
  164. -- The default action is only rarely taken.
  165. if not sdef._no_collapse_on_walkover then
  166. if random(1, 500) == 1 then
  167. default_on_walkover(nodepos, sname, player)
  168. end
  169. end
  170. end
  171. end -- Air/ignore check.
  172. end
  173. -- Update player's sky colors. Use flags to avoid extra calls.
  174. if vector_distance(playerpos, pdata.ppos) > 5 or pdata.sky == -1 then
  175. local pos_underground = rc.position_underground(playerpos)
  176. if pos_underground then
  177. local light = minetest.get_node_light(playerpos, 0.5)
  178. if light and light >= 15 then
  179. pos_underground = false
  180. end
  181. end
  182. if pos_underground and pdata.sky <= 0 then
  183. if not IS_SINGLEPLAYER then
  184. if playerpos.y > -25000 and pdata.sky ~= 1 then
  185. -- Cave (natural) background.
  186. player:set_sky({base_color={a=255, r=0, g=0, b=0}, type="plain", clouds=false})
  187. player:set_sun({visible=false, sunrise_visible=false})
  188. player:set_moon({visible=false})
  189. player:set_stars({visible=false})
  190. pdata.sky = 1
  191. elseif pdata.sky ~= 2 then
  192. -- Nether (cave) background.
  193. player:set_sky({base_color={a=255, r=10, g=0, b=0}, type="plain", clouds=false})
  194. player:set_sun({visible=false, sunrise_visible=false})
  195. player:set_moon({visible=false})
  196. player:set_stars({visible=false})
  197. pdata.sky = 2
  198. end
  199. end
  200. elseif not pos_underground and pdata.sky ~= 0 then
  201. player:set_sky(rc.get_realm_sky(playerpos))
  202. player:set_sun(rc.get_realm_sun(playerpos))
  203. player:set_moon(rc.get_realm_moon(playerpos))
  204. player:set_stars(rc.get_realm_stars(playerpos))
  205. player:set_clouds(rc.get_realm_clouds(playerpos))
  206. pdata.sky = 0
  207. end
  208. pdata.ppos = playerpos
  209. end
  210. end
  211. -- Private function! (Registered callback.)
  212. local timer = 0
  213. function sky.on_globalstep(dtime)
  214. timer = timer + dtime
  215. if timer < 0.25 then return end
  216. timer = 0
  217. local players = get_connected_players()
  218. local datas = sky.players
  219. for i=1, #players do
  220. local player = players[i]
  221. local pname = player:get_player_name()
  222. local ppos = player:get_pos()
  223. local rpos = vector_round(ppos)
  224. local pdata = datas[pname]
  225. if not vector_equals(pdata.rpos, rpos) then
  226. local npos = utility.node_under_pos(ppos)
  227. update_player(player, pname, pdata, ppos, npos)
  228. pdata.rpos = rpos
  229. end
  230. end
  231. end
  232. function sky.on_joinplayer(player)
  233. local ppos = player:get_pos()
  234. local rpos = vector_round(ppos)
  235. local npos = utility.node_under_pos(ppos)
  236. -- Initialize player data.
  237. local pname = player:get_player_name()
  238. sky.players[pname] = {
  239. ppos = {x=0, y=0, z=0}, -- Last known player position.
  240. rpos = rpos, -- Last known player position, rounded.
  241. snode = "", -- Name of last walked node.
  242. wnode = "", -- Name of last node above walked node.
  243. sky = -1, -- Current sky colors flag.
  244. }
  245. local pdata = sky.players[pname]
  246. -- Update player on first join.
  247. update_player(player, pname, pdata, ppos, npos)
  248. end
  249. function sky.on_leaveplayer(player, timeout)
  250. local pname = player:get_player_name()
  251. sky.players[pname] = nil
  252. end
  253. function sky.notify_sky_update_needed(pname)
  254. -- "-1" indicates that the sky needs updating for this player.
  255. sky.players[pname].sky = -1
  256. end
  257. if not sky.run_once then
  258. minetest.register_on_joinplayer(function(...)
  259. sky.on_joinplayer(...)
  260. end)
  261. minetest.register_on_leaveplayer(function(...)
  262. return sky.on_leaveplayer(...)
  263. end)
  264. minetest.register_globalstep(function(...)
  265. sky.on_globalstep(...)
  266. end)
  267. local c = "sky:core"
  268. local f = sky.modpath .. "/init.lua"
  269. reload.register_file(c, f, false)
  270. sky.run_once = true
  271. end