init.lua 8.2 KB

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