123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- if not minetest.global_exists("sky") then sky = {} end
- sky.modpath = minetest.get_modpath("sky")
- sky.players = sky.players or {}
- -- Disable underground skycolors in singleplayer -- black sky gets in the way of
- -- mapgen development, esp regarding caves.
- local IS_SINGLEPLAYER = minetest.is_singleplayer()
- -- Localize for speed.
- local get_node = minetest.get_node
- local all_nodes = minetest.registered_nodes
- local ns_nodes = minetest.reg_ns_nodes
- local vector_distance = vector.distance
- local vector_round = vector.round
- local vector_equals = vector.equals
- local get_connected_players = minetest.get_connected_players
- local random = math.random
- local string_find = string.find
- -- Public API function.
- -- Used in the ambiance mod to determine the surface a player stands on.
- function sky.get_last_walked_node(pname)
- local data = sky.players[pname]
- if data then
- return data.snode
- end
- return ""
- end
- function sky.get_last_walked_nodeabove(pname)
- local data = sky.players[pname]
- if data then
- return data.wnode
- end
- return ""
- end
- -- Private function!
- -- This is the default `on_walkover' action.
- local function default_on_walkover(pos, name, player)
- local pname = player:get_player_name()
- -- Admin doesn't trigger default actions.
- if gdac.player_is_admin(pname) then
- return
- end
- -- Do not trigger if position is protected.
- if minetest.test_protection(pos, "") then
- return
- end
- if get_node(pos).name ~= name then
- return
- end
- local test_and_drop = function(p2)
- -- Don't drop if protected.
- if minetest.test_protection(p2, "") then
- return
- end
- local overhang = true
- for i = 1, 4, 1 do
- local node = get_node({x=p2.x, y=p2.y-i, z=p2.z})
- if node.name ~= "air" then
- overhang = false
- break
- end
- end
- if overhang then
- sfn.drop_node(p2)
- core.check_for_falling(p2)
- return true
- end
- end
- -- Drop several nodes under the player to ensure a likelihood of
- -- causing the player to fall down.
- local positions = {
- -- Test and drop lower nodes first.
- {x=pos.x, y=pos.y-1, z=pos.z},
- {x=pos.x+1, y=pos.y-1, z=pos.z},
- {x=pos.x-1, y=pos.y-1, z=pos.z},
- {x=pos.x, y=pos.y-1, z=pos.z+1},
- {x=pos.x, y=pos.y-1, z=pos.z-1},
- {x=pos.x, y=pos.y, z=pos.z},
- {x=pos.x+1, y=pos.y, z=pos.z},
- {x=pos.x-1, y=pos.y, z=pos.z},
- {x=pos.x, y=pos.y, z=pos.z+1},
- {x=pos.x, y=pos.y, z=pos.z-1},
- }
- local play_sound = false
- for k, v in ipairs(positions) do
- if test_and_drop(v) then
- play_sound = true
- end
- end
- if play_sound then
- ambiance.sound_play("default_gravel_footstep", pos, 1, 20)
- end
- end
- -- Private function!
- --
- -- This handles skycolor updates and `on_player_walk_over' calls.
- -- The walk-over calls are used in plenty of places, so do not break this!
- -- Also, we handle movement speed based on current node walked on.
- local function update_player(player, pname, pdata, playerpos, nodepos)
- -- Player doesn't walk over nodes if attached to some vehicle.
- if not default.player_attached[pname] then
- -- Get node player is standing ON.
- local snode = get_node(nodepos)
- local sname = snode.name
- -- Don't modify movement or call walk-over callbacks if node is air.
- -- This prevents players from getting better movement speed by hopping constantly.
- if sname ~= "air" and sname ~= "ignore" then
- local sdef = ns_nodes[sname] or all_nodes[sname] or {}
- -- Get node player is walking IN, not ON.
- -- Plants shall slow players down!
- local wnode = get_node(vector.add(nodepos, {x=0, y=1, z=0}))
- local wname = wnode.name
- -- Recompute movement speed only if either walked nodename changes.
- if sname ~= pdata.snode or wname ~= pdata.wnode then
- if sdef.movement_speed_depends then
- -- Assume node is slab-like and has a standard 'flat' orientation.
- local p2 = snode.param2
- local is_flat = false
- if p2 >= 0 and p2 <= 3 then
- is_flat = true
- elseif p2 >= 20 and p2 <= 23 then
- is_flat = true
- end
- if is_flat then
- -- If slab is flat and has a parent type, use the parent type.
- local def2 = ns_nodes[sdef.movement_speed_depends] or {}
- if def2 then sdef = def2 end
- end
- elseif sdef.move_speed_stair then
- -- Assume node is stair-like and has a standard 'flat' orientation.
- local p2 = snode.param2
- local is_flat = false
- if p2 >= 20 and p2 <= 23 then
- is_flat = true
- end
- if is_flat then
- -- If stair is flat and has a parent type, use the parent type.
- local def2 = ns_nodes[sdef.move_speed_stair] or {}
- if def2 then sdef = def2 end
- end
- end
- local smult = sdef.movement_speed_multiplier or default.NORM_SPEED
- local jmult = sdef.movement_jump_multiplier or default.NORM_JUMP
- pova.set_modifier(player, "physics", {speed=smult, jump=jmult}, "walking")
- if wname ~= "air" then
- -- But ignore doors.
- if not string.find(wname, "^doors:") then
- local wdef = ns_nodes[wname] or all_nodes[wname] or {}
- local smult2 = wdef.movement_speed_multiplier or default.NORM_SPEED
- local jmult2 = wdef.movement_jump_multiplier or default.NORM_JUMP
- pova.set_modifier(player, "physics", {speed=smult2, jump=jmult2}, "walking")
- end
- end
- -- Record the name of the last walked node.
- -- This is used by the ambiance mod to determine walked surface type.
- pdata.snode = sname
- pdata.wnode = wname
- end
- if not gdac_invis.is_invisible(pname) then
- -- Execute `on_walkover' callback for current walked node.
- -- Note, this must only be called ONCE for the walked node!
- -- This is ensured because we are only called max once per position.
- if sdef.walkable and sdef.on_player_walk_over then
- sdef.on_player_walk_over(nodepos, player)
- end
- -- The default action is only rarely taken.
- if not sdef._no_collapse_on_walkover then
- if random(1, 500) == 1 then
- default_on_walkover(nodepos, sname, player)
- end
- end
- end
- end -- Air/ignore check.
- end
- -- Update player's sky colors. Use flags to avoid extra calls.
- if vector_distance(playerpos, pdata.ppos) > 5 or pdata.sky == -1 then
- local pos_underground = rc.position_underground(playerpos)
- if pos_underground then
- local light = minetest.get_node_light(playerpos, 0.5)
- if light and light >= 15 then
- pos_underground = false
- end
- end
- if pos_underground and pdata.sky <= 0 then
- if not IS_SINGLEPLAYER then
- if playerpos.y > -25000 and pdata.sky ~= 1 then
- -- Cave (natural) background.
- player:set_sky({base_color={a=255, r=0, g=0, b=0}, type="plain", clouds=false})
- player:set_sun({visible=false, sunrise_visible=false})
- player:set_moon({visible=false})
- player:set_stars({visible=false})
- pdata.sky = 1
- elseif pdata.sky ~= 2 then
- -- Nether (cave) background.
- player:set_sky({base_color={a=255, r=10, g=0, b=0}, type="plain", clouds=false})
- player:set_sun({visible=false, sunrise_visible=false})
- player:set_moon({visible=false})
- player:set_stars({visible=false})
- pdata.sky = 2
- end
- end
- elseif not pos_underground and pdata.sky ~= 0 then
- player:set_sky(rc.get_realm_sky(playerpos))
- player:set_sun(rc.get_realm_sun(playerpos))
- player:set_moon(rc.get_realm_moon(playerpos))
- player:set_stars(rc.get_realm_stars(playerpos))
- player:set_clouds(rc.get_realm_clouds(playerpos))
- pdata.sky = 0
- end
- pdata.ppos = playerpos
- end
- end
- -- Private function! (Registered callback.)
- local timer = 0
- function sky.on_globalstep(dtime)
- timer = timer + dtime
- if timer < 0.25 then return end
- timer = 0
- local players = get_connected_players()
- local datas = sky.players
- for i=1, #players do
- local player = players[i]
- local pname = player:get_player_name()
- local ppos = player:get_pos()
- local rpos = vector_round(ppos)
- local pdata = datas[pname]
- if not vector_equals(pdata.rpos, rpos) then
- local npos = utility.node_under_pos(ppos)
- update_player(player, pname, pdata, ppos, npos)
- pdata.rpos = rpos
- end
- end
- end
- function sky.on_joinplayer(player)
- local ppos = player:get_pos()
- local rpos = vector_round(ppos)
- local npos = utility.node_under_pos(ppos)
- -- Initialize player data.
- local pname = player:get_player_name()
- sky.players[pname] = {
- ppos = {x=0, y=0, z=0}, -- Last known player position.
- rpos = rpos, -- Last known player position, rounded.
- snode = "", -- Name of last walked node.
- wnode = "", -- Name of last node above walked node.
- sky = -1, -- Current sky colors flag.
- }
- local pdata = sky.players[pname]
- -- Update player on first join.
- update_player(player, pname, pdata, ppos, npos)
- end
- function sky.on_leaveplayer(player, timeout)
- local pname = player:get_player_name()
- sky.players[pname] = nil
- end
- function sky.notify_sky_update_needed(pname)
- -- "-1" indicates that the sky needs updating for this player.
- sky.players[pname].sky = -1
- end
- if not sky.run_once then
- minetest.register_on_joinplayer(function(...)
- sky.on_joinplayer(...)
- end)
- minetest.register_on_leaveplayer(function(...)
- return sky.on_leaveplayer(...)
- end)
- minetest.register_globalstep(function(...)
- sky.on_globalstep(...)
- end)
- local c = "sky:core"
- local f = sky.modpath .. "/init.lua"
- reload.register_file(c, f, false)
- sky.run_once = true
- end
|