808 lines
24 KiB
Lua
808 lines
24 KiB
Lua
|
||
--
|
||
-- Bed mod
|
||
--
|
||
|
||
local S = minetest.get_translator("rp_bed")
|
||
|
||
bed = {}
|
||
|
||
local DEFAULT_BED_COLOR = rp_paint.COLOR_AZURE_BLUE
|
||
|
||
-- Per-user data table
|
||
|
||
local bed_userdata = {}
|
||
bed_userdata.saved = {}
|
||
bed_userdata.temp = {}
|
||
|
||
-- List of occupied beds, indexed by node position hash
|
||
local occupied_beds = {}
|
||
|
||
-- Returns <spawn position> of `player` or
|
||
-- nil if if there is no spawn active
|
||
bed.get_spawn = function(player)
|
||
local name = player:get_player_name()
|
||
local spawn
|
||
if bed_userdata.saved[name].spawn_pos then
|
||
spawn = bed_userdata.saved[name].spawn_pos
|
||
end
|
||
return spawn
|
||
end
|
||
|
||
-- Sets the bed spawn position for `player`.
|
||
-- Returns true if spawn position was set and changed.
|
||
-- Returns false if spawn position was not changed because
|
||
-- it's already used by the player.
|
||
bed.set_spawn = function(player, spawn_pos)
|
||
local name = player:get_player_name()
|
||
local old_spawn_pos = bed_userdata.saved[name].spawn_pos
|
||
if old_spawn_pos and vector.equals(spawn_pos, old_spawn_pos) then
|
||
return false
|
||
end
|
||
bed_userdata.saved[name].spawn_pos = table.copy(spawn_pos)
|
||
minetest.log("action", "[rp_bed] Respawn position of "..name.." set to "..minetest.pos_to_string(spawn_pos, 1))
|
||
return true
|
||
end
|
||
|
||
-- Unsets the bed spawn position of `player`
|
||
bed.unset_spawn = function(player)
|
||
local name = player:get_player_name()
|
||
bed_userdata.saved[name].spawn_pos = nil
|
||
end
|
||
|
||
-- Returns true if pos has a valid bed
|
||
bed.is_valid_bed = function(pos)
|
||
local node = minetest.get_node(pos)
|
||
local dir = minetest.fourdir_to_dir(node.param2)
|
||
if node.name == "rp_bed:bed_head" then
|
||
local neighbor = vector.subtract(pos, dir)
|
||
local nnode = minetest.get_node(neighbor)
|
||
if nnode.name == "rp_bed:bed_foot" and nnode.param2 == node.param2 then
|
||
return true
|
||
end
|
||
elseif node.name == "rp_bed:bed_foot" then
|
||
local neighbor = vector.add(pos, dir)
|
||
local nnode = minetest.get_node(neighbor)
|
||
if nnode.name == "rp_bed:bed_head" and nnode.param2 == node.param2 then
|
||
return true
|
||
end
|
||
end
|
||
return false
|
||
end
|
||
|
||
-- Savefile
|
||
|
||
local bed_file = minetest.get_worldpath() .. "/bed.dat"
|
||
local saving = false
|
||
|
||
-- Timer
|
||
|
||
local timer_interval = 1
|
||
local timer = 0
|
||
|
||
local delay_daytime = false
|
||
|
||
local function is_bed_node(pos)
|
||
if pos == nil then
|
||
return false
|
||
end
|
||
|
||
local node = minetest.get_node(pos)
|
||
|
||
if node.name == "rp_bed:bed_foot" then
|
||
return true
|
||
end
|
||
|
||
return false
|
||
end
|
||
|
||
-- Returns name of player in bed at pos or nil if not occupied
|
||
local function get_player_in_bed(pos)
|
||
local hash = minetest.hash_node_position(pos)
|
||
local playername = occupied_beds[hash]
|
||
return playername
|
||
end
|
||
-- Assign a player to the bed at pos.
|
||
-- If playername==nil, bed will be unassigned.
|
||
local function set_bed_occupier(pos, playername)
|
||
local hash = minetest.hash_node_position(pos)
|
||
occupied_beds[hash] = playername
|
||
end
|
||
|
||
local function put_player_in_bed(player)
|
||
if player == nil then
|
||
return
|
||
end
|
||
|
||
local name = player:get_player_name()
|
||
|
||
if not is_bed_node(bed_userdata.temp[name].node_pos) then
|
||
return
|
||
end
|
||
|
||
player:set_pos(bed_userdata.temp[name].sleep_pos)
|
||
|
||
player_effects.apply_effect(player, "inbed")
|
||
|
||
player:set_eye_offset(vector.new(0, -13, 0), vector.new(0, -13, 0))
|
||
player:set_local_animation(
|
||
{x=162, y=166},
|
||
{x=162, y=166},
|
||
{x=162, y=166},
|
||
{x=162, y=168},
|
||
rp_player.player_animation_speed)
|
||
|
||
rp_player.player_set_animation(player, "lay", rp_player.player_animation_speed)
|
||
|
||
rp_player.player_attached[name] = true
|
||
|
||
minetest.log("action", "[rp_bed] "..name.." was put into bed")
|
||
end
|
||
|
||
local function clear_bed_status(player)
|
||
if player == nil then
|
||
return
|
||
end
|
||
local name = player:get_player_name()
|
||
|
||
bed_userdata.temp[name].in_bed = false
|
||
if bed_userdata.temp[name].node_pos then
|
||
set_bed_occupier(bed_userdata.temp[name].node_pos, nil)
|
||
end
|
||
bed_userdata.temp[name].node_pos = nil
|
||
|
||
player_effects.remove_effect(player, "inbed")
|
||
|
||
player:set_eye_offset(vector.new(0, 0, 0), vector.new(0, 0, 0))
|
||
player:set_local_animation(
|
||
{x=0, y=79},
|
||
{x=168, y=187},
|
||
{x=189, y=198},
|
||
{x=200, y=219},
|
||
rp_player.player_animation_speed)
|
||
|
||
rp_player.player_set_animation(player, "stand", rp_player.player_animation_speed)
|
||
|
||
rp_player.player_attached[name] = false
|
||
end
|
||
|
||
local function take_player_from_bed(player)
|
||
if player == nil then
|
||
return
|
||
end
|
||
local name = player:get_player_name()
|
||
|
||
local was_in_bed = bed_userdata.temp[name].in_bed == true
|
||
if was_in_bed then
|
||
minetest.log("action", "[rp_bed] "..name.." was taken from bed")
|
||
end
|
||
local spawn_pos = bed.get_spawn(player)
|
||
if spawn_pos then
|
||
player:set_pos(spawn_pos)
|
||
end
|
||
|
||
clear_bed_status(player)
|
||
end
|
||
|
||
local function save_bed()
|
||
local f = io.open(bed_file, "w")
|
||
|
||
f:write(minetest.serialize(bed_userdata.saved))
|
||
|
||
io.close(f)
|
||
|
||
saving = false
|
||
end
|
||
|
||
local function delayed_save()
|
||
if not saving then
|
||
saving = true
|
||
|
||
minetest.after(40, save_bed)
|
||
end
|
||
end
|
||
|
||
local function load_bed()
|
||
local f = io.open(bed_file, "r")
|
||
|
||
if f then
|
||
bed_userdata.saved = minetest.deserialize(f:read("*all"))
|
||
|
||
io.close(f)
|
||
else
|
||
save_bed()
|
||
end
|
||
end
|
||
|
||
-- Server start
|
||
|
||
local function on_load()
|
||
load_bed()
|
||
end
|
||
|
||
-- Server shutdown
|
||
|
||
local function on_shutdown()
|
||
save_bed()
|
||
end
|
||
|
||
-- Joining player
|
||
|
||
local function on_joinplayer(player)
|
||
local name = player:get_player_name()
|
||
|
||
if not bed_userdata.saved[name] then
|
||
bed_userdata.saved[name] = {
|
||
spawn_pos = nil,
|
||
}
|
||
end
|
||
bed_userdata.temp[name] = {
|
||
in_bed = false,
|
||
node_pos = nil,
|
||
sleep_pos = nil,
|
||
}
|
||
delayed_save()
|
||
end
|
||
|
||
-- Leaving player
|
||
|
||
local function on_leaveplayer(player)
|
||
local name = player:get_player_name()
|
||
if bed_userdata.temp[name] then
|
||
bed_userdata.temp[name].in_bed = false
|
||
if bed_userdata.temp[name].node_pos then
|
||
set_bed_occupier(bed_userdata.temp[name].node_pos, nil)
|
||
end
|
||
bed_userdata.temp[name].node_pos = nil
|
||
end
|
||
end
|
||
|
||
-- Returns true if players can spawn into the given node safely.
|
||
local function node_is_spawnable_in(node, is_upper)
|
||
-- All non-walkable, non-damaging, non-drowning nodes are safe.
|
||
-- Also the bed as a special case for the lower check.
|
||
if not node then
|
||
return false, "no_node"
|
||
end
|
||
if not is_upper and minetest.get_item_group(node.name, "bed") ~= 0 then
|
||
return true
|
||
end
|
||
local def = minetest.registered_nodes[node.name]
|
||
if not def.walkable and def.drowning <= 0 and def.damage_per_second <= 0 then
|
||
return true
|
||
end
|
||
local fail_reason
|
||
if def.walkable then
|
||
fail_reason = "blocked"
|
||
elseif def.damage_per_second > 0 then
|
||
fail_reason = "damage"
|
||
elseif def.drowning > 0 then
|
||
fail_reason = "drowning"
|
||
else
|
||
fail_reason = "blocked"
|
||
end
|
||
return false, fail_reason
|
||
end
|
||
|
||
-- Returns true if players can spawn on given node safely (without falling).
|
||
local function node_is_spawnable_on(node)
|
||
-- All walkable full cube nodes that don't disable jump are accepted
|
||
if not node then
|
||
return false
|
||
end
|
||
local def = minetest.registered_nodes[node.name]
|
||
if def.walkable and
|
||
((def.collision_box == nil and def.node_box == nil) or
|
||
(not def.collision_box and def.node_box and def.node_box.type == "regular") or
|
||
(not def.node_box and def.collision_box and def.collision_box.type == "regular")) and
|
||
(minetest.get_item_group(node.name, "disable_jump") == 0) then
|
||
return true
|
||
end
|
||
return false
|
||
end
|
||
|
||
local respawn_check_posses = {
|
||
vector.new(0, 0, 0 ),
|
||
vector.new(0, 0, -1 ),
|
||
vector.new(0, 0, 1 ),
|
||
vector.new(-1,0, 0 ),
|
||
vector.new(1, 0, 0 ),
|
||
vector.new(-1,0, -1 ),
|
||
vector.new(-1,0, 1 ),
|
||
vector.new(1, 0, -1 ),
|
||
vector.new(1, 0, 1 ),
|
||
}
|
||
|
||
local attempt_bed_respawn = function(player)
|
||
-- Place player on respawn position if set
|
||
local name = player:get_player_name()
|
||
local pos = bed.get_spawn(player)
|
||
if pos then
|
||
-- Load area around spawn pos to make sure
|
||
-- we don't get ignore nodes.
|
||
local load_offset = vector.new(1,1,1)
|
||
local load_min = vector.subtract(pos, load_offset)
|
||
local load_max = vector.add(pos, load_offset)
|
||
minetest.load_area(load_min, load_max)
|
||
-- Check if position is safe, if not, try to spawn to one of the
|
||
-- neighbor blocks
|
||
for n=1, #respawn_check_posses do
|
||
local cpos = vector.add(pos, respawn_check_posses[n])
|
||
local node = minetest.get_node(cpos)
|
||
if node_is_spawnable_in(node, false) then
|
||
local is_bed = minetest.get_item_group(node.name, "bed") ~= 0
|
||
-- Check posses above (must be free)
|
||
-- and below (must be walkable)
|
||
-- If bed, 2 posses must be free above
|
||
local acpos = { x=cpos.x, y=cpos.y+1, z=cpos.z }
|
||
local aacpos = { x=cpos.x, y=cpos.y+2, z=cpos.z }
|
||
local abpos = { x=cpos.x, y=cpos.y-1, z=cpos.z }
|
||
local anode = minetest.get_node(acpos)
|
||
local aanode = minetest.get_node(aacpos)
|
||
local bnode = minetest.get_node(abpos)
|
||
if node_is_spawnable_in(anode, true) and
|
||
((n == 1 and is_bed) or node_is_spawnable_on(bnode)) and
|
||
(not is_bed or node_is_spawnable_in(aanode, true)) then
|
||
local spos = cpos
|
||
if not is_bed then
|
||
spos.y = spos.y - 0.5
|
||
end
|
||
player:set_pos(spos)
|
||
return true
|
||
end
|
||
end
|
||
end
|
||
bed.unset_spawn(player)
|
||
minetest.chat_send_player(name, minetest.colorize("#FFFF00", S("Your respawn position was blocked or dangerous. You’ve lost your old respawn position.")))
|
||
return false
|
||
end
|
||
return false
|
||
end
|
||
|
||
local on_respawnplayer = function(player)
|
||
clear_bed_status(player)
|
||
return attempt_bed_respawn(player)
|
||
end
|
||
|
||
|
||
local function on_dieplayer(player)
|
||
local name = player:get_player_name()
|
||
if bed_userdata.temp[name] then
|
||
bed_userdata.temp[name].in_bed = false
|
||
if bed_userdata.temp[name].node_pos then
|
||
set_bed_occupier(bed_userdata.temp[name].node_pos, nil)
|
||
end
|
||
bed_userdata.temp[name].node_pos = nil
|
||
end
|
||
end
|
||
|
||
-- Update function
|
||
|
||
local function on_globalstep(dtime)
|
||
timer = timer + dtime
|
||
|
||
if timer < timer_interval then
|
||
return
|
||
end
|
||
|
||
timer = 0
|
||
|
||
local sleeping_players = 0
|
||
|
||
local in_bed = {}
|
||
for name, data in pairs(bed_userdata.temp) do
|
||
if data.in_bed then
|
||
local player = minetest.get_player_by_name(name)
|
||
if player then
|
||
table.insert(in_bed, name)
|
||
sleeping_players = sleeping_players + 1
|
||
end
|
||
end
|
||
end
|
||
|
||
local players = minetest.get_connected_players()
|
||
local player_count = #players
|
||
|
||
if player_count > 0 and (player_count / 2.0) < sleeping_players then
|
||
if minetest.get_timeofday() < 0.2 or minetest.get_timeofday() > 0.8 then
|
||
if not delay_daytime then
|
||
delay_daytime = true
|
||
|
||
minetest.after(
|
||
2,
|
||
function()
|
||
minetest.set_timeofday(0.23)
|
||
delay_daytime = false
|
||
|
||
local players = minetest.get_connected_players()
|
||
local msg
|
||
if #players == 1 then
|
||
msg = S("You have slept, rise and shine!")
|
||
else
|
||
msg = S("Players have slept, rise and shine!")
|
||
end
|
||
minetest.chat_send_all(minetest.colorize("#0ff", "*** " .. msg))
|
||
minetest.log("action", "[rp_bed] Players have slept; the night was skipped")
|
||
end)
|
||
|
||
delayed_save()
|
||
end
|
||
end
|
||
end
|
||
end
|
||
|
||
-- Force player to wake up when punched
|
||
local function on_punchplayer(player)
|
||
if player:get_hp() <= 0 then
|
||
return
|
||
end
|
||
local name = player:get_player_name()
|
||
if bed_userdata.temp[name].in_bed then
|
||
take_player_from_bed(player)
|
||
end
|
||
end
|
||
|
||
-- Force player to wake up when taking damage
|
||
local function on_player_hpchange(player, hp_change)
|
||
if player:get_hp() <= 0 or hp_change >= 0 then
|
||
return
|
||
end
|
||
local name = player:get_player_name()
|
||
if bed_userdata.temp[name].in_bed then
|
||
take_player_from_bed(player)
|
||
end
|
||
end
|
||
|
||
minetest.register_on_mods_loaded(on_load)
|
||
|
||
minetest.register_on_shutdown(on_shutdown)
|
||
|
||
minetest.register_on_joinplayer(on_joinplayer)
|
||
|
||
minetest.register_on_leaveplayer(on_joinplayer)
|
||
|
||
minetest.register_on_respawnplayer(on_respawnplayer)
|
||
|
||
minetest.register_on_dieplayer(on_dieplayer)
|
||
|
||
minetest.register_on_punchplayer(on_punchplayer)
|
||
|
||
minetest.register_on_player_hpchange(on_player_hpchange)
|
||
|
||
minetest.register_globalstep(on_globalstep)
|
||
|
||
-- Nodes
|
||
|
||
local sounds = rp_sounds.node_sound_planks_defaults({
|
||
footstep = {name="rp_sounds_footstep_fuzzy", gain=0.7},
|
||
dug = {name="rp_sounds_dug_planks", gain=1.0, pitch=0.8},
|
||
place = {name="rp_sounds_place_planks", gain=1.0, pitch=0.8},
|
||
})
|
||
|
||
minetest.register_node(
|
||
"rp_bed:bed_foot",
|
||
{
|
||
description = S("Bed"),
|
||
_tt_help = S("Sets the respawn position and allows to pass the night"),
|
||
drawtype = "nodebox",
|
||
paramtype = "light",
|
||
paramtype2 = "color4dir",
|
||
palette = "bed_palette.png",
|
||
sunlight_propagates = true,
|
||
wield_image = "bed_bed_inventory.png",
|
||
inventory_image = "bed_bed_inventory.png",
|
||
tiles = {
|
||
"bed_foot.png",
|
||
{name="default_wood.png",color="white"},
|
||
"bed_side_l.png",
|
||
"bed_side_r.png",
|
||
"bed_inside.png",
|
||
"bed_back.png",
|
||
},
|
||
overlay_tiles = {
|
||
{name="bed_foot_overlay.png",color="white"},
|
||
"",
|
||
{name="bed_side_l_overlay.png",color="white"},
|
||
{name="bed_side_r_overlay.png",color="white"},
|
||
{name="bed_inside_overlay.png",color="white"},
|
||
{name="bed_back_overlay.png",color="white"},
|
||
},
|
||
use_texture_alpha = "clip",
|
||
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 3, bed = 1, fall_damage_add_percent = -15, creative_decoblock = 1, interactive_node = 1, paintable = 1 },
|
||
is_ground_content = false,
|
||
sounds = sounds,
|
||
node_box = {
|
||
type = "fixed",
|
||
fixed = {-0.5, -0.5, -0.5, 0.5, 2/16, 0.5}
|
||
},
|
||
selection_box = {
|
||
type = "fixed",
|
||
fixed = {-0.5, -0.5, -0.5, 0.5, 2/16, 1.5}
|
||
},
|
||
|
||
node_placement_prediction = "",
|
||
on_place = function(itemstack, placer, pointed_thing)
|
||
local under = pointed_thing.under
|
||
|
||
-- Use pointed node's on_rightclick function first, if present
|
||
local node = minetest.get_node(under)
|
||
if placer and not placer:get_player_control().sneak then
|
||
if minetest.registered_nodes[node.name] and minetest.registered_nodes[node.name].on_rightclick then
|
||
return minetest.registered_nodes[node.name].on_rightclick(pointed_thing.under, node, placer, itemstack, pointed_thing) or itemstack
|
||
end
|
||
end
|
||
|
||
local pos
|
||
local undername = minetest.get_node(under).name
|
||
if minetest.registered_items[undername] and minetest.registered_items[undername].buildable_to then
|
||
pos = under
|
||
else
|
||
pos = pointed_thing.above
|
||
end
|
||
|
||
if minetest.is_protected(pos, placer:get_player_name()) and
|
||
not minetest.check_player_privs(placer, "protection_bypass") then
|
||
minetest.record_protection_violation(pos, placer:get_player_name())
|
||
return itemstack
|
||
end
|
||
|
||
local node_def = minetest.registered_nodes[minetest.get_node(pos).name]
|
||
if not node_def or not node_def.buildable_to then
|
||
rp_sounds.play_place_failed_sound(placer)
|
||
return itemstack
|
||
end
|
||
|
||
local dir = minetest.dir_to_fourdir(placer:get_look_dir())
|
||
local botpos = vector.add(pos, minetest.fourdir_to_dir(dir))
|
||
|
||
if minetest.is_protected(botpos, placer:get_player_name()) and
|
||
not minetest.check_player_privs(placer, "protection_bypass") then
|
||
minetest.record_protection_violation(botpos, placer:get_player_name())
|
||
return itemstack
|
||
end
|
||
|
||
local bot = minetest.get_node(botpos)
|
||
local botdef = minetest.registered_nodes[bot.name]
|
||
-- Check if the 2nd node for the bed is free or already a bed head.
|
||
if not (bot.name == "rp_bed:bed_head" and bot.param2 == dir) and (not botdef or not botdef.buildable_to) then
|
||
rp_sounds.play_place_failed_sound(placer)
|
||
return itemstack
|
||
end
|
||
|
||
local param2 = dir + (DEFAULT_BED_COLOR - 1) * 4
|
||
|
||
local footnode = {name = "rp_bed:bed_foot", param2 = param2}
|
||
local headnode = {name = "rp_bed:bed_head", param2 = param2}
|
||
minetest.set_node(pos, footnode)
|
||
minetest.set_node(botpos, headnode)
|
||
rp_sounds.play_node_sound(pos, footnode, "place")
|
||
|
||
if not minetest.is_creative_enabled(placer:get_player_name()) then
|
||
itemstack:take_item()
|
||
end
|
||
return itemstack
|
||
end,
|
||
|
||
on_destruct = function(pos)
|
||
local player_name = get_player_in_bed(pos)
|
||
if player_name then
|
||
local player = minetest.get_player_by_name(player_name)
|
||
take_player_from_bed(player)
|
||
end
|
||
|
||
set_bed_occupier(pos, nil)
|
||
local node = minetest.get_node(pos)
|
||
local dir = minetest.fourdir_to_dir(node.param2)
|
||
local head_pos = vector.add(pos, dir)
|
||
if minetest.get_node(head_pos).name == "rp_bed:bed_head" then
|
||
minetest.remove_node(head_pos)
|
||
minetest.check_for_falling({x=head_pos.x, y=head_pos.y+1, z=head_pos.z})
|
||
end
|
||
end,
|
||
on_blast = function(pos)
|
||
-- Needed to force on_destruct to be called
|
||
minetest.remove_node(pos)
|
||
minetest.check_for_falling({x=pos.x, y=pos.y+1, z=pos.z})
|
||
end,
|
||
|
||
on_rightclick = function(pos, node, clicker, itemstack)
|
||
if not clicker:is_player() then
|
||
return itemstack
|
||
end
|
||
|
||
local clicker_name = clicker:get_player_name()
|
||
|
||
local sleeper_name = get_player_in_bed(pos)
|
||
|
||
if clicker_name == sleeper_name then
|
||
take_player_from_bed(clicker)
|
||
elseif sleeper_name == nil and not rp_player.player_attached[clicker_name]
|
||
and bed_userdata.temp[clicker_name].in_bed == false then
|
||
if not minetest.settings:get_bool("bed_enable", true) then
|
||
minetest.chat_send_player(clicker_name, minetest.colorize("#FFFF00", S("Sleeping is disabled.")))
|
||
return itemstack
|
||
end
|
||
|
||
local dir = minetest.fourdir_to_dir(node.param2)
|
||
local above_posses = {
|
||
{x=pos.x, y=pos.y+1, z=pos.z},
|
||
vector.add({x=pos.x, y=pos.y+1, z=pos.z}, dir),
|
||
{x=pos.x, y=pos.y+2, z=pos.z},
|
||
vector.add({x=pos.x, y=pos.y+2, z=pos.z}, dir),
|
||
}
|
||
for a=1,#above_posses do
|
||
local apos = above_posses[a]
|
||
local anode = minetest.get_node(apos)
|
||
local is_spawnable, fail_reason = node_is_spawnable_in(anode, true)
|
||
if not is_spawnable then
|
||
local msg
|
||
if fail_reason == "damage" then
|
||
msg = S("It’s too painful to sleep here!")
|
||
elseif fail_reason == "drowning" then
|
||
msg = S("You can’t sleep while holding your breath!")
|
||
elseif fail_reason == "blocked" then
|
||
msg = S("Not enough space to sleep!")
|
||
else
|
||
msg = S("You can’t sleep here!")
|
||
end
|
||
minetest.chat_send_player(clicker_name, minetest.colorize("#FFFF00", msg))
|
||
return itemstack
|
||
end
|
||
end
|
||
|
||
-- No sleeping while moving
|
||
if vector.length(clicker:get_velocity()) > 0.001 then
|
||
minetest.chat_send_player(clicker_name, minetest.colorize("#FFFF00", S("You have to stop moving before going to bed!")))
|
||
return itemstack
|
||
end
|
||
|
||
local put_pos = table.copy(pos)
|
||
|
||
local yaw = (-(node.param2 / 2.0) * math.pi) + math.pi
|
||
|
||
bed_userdata.temp[clicker_name].in_bed = true
|
||
|
||
local changed = bed.set_spawn(clicker, put_pos)
|
||
if changed then
|
||
minetest.chat_send_player(clicker_name, minetest.colorize("#00FFFF", S("Respawn position set!")))
|
||
end
|
||
|
||
bed_userdata.temp[clicker_name].node_pos = pos
|
||
local sleep_pos = vector.add(pos, vector.divide(minetest.fourdir_to_dir(node.param2), 2))
|
||
bed_userdata.temp[clicker_name].sleep_pos = sleep_pos
|
||
|
||
set_bed_occupier(pos, clicker_name)
|
||
|
||
put_player_in_bed(clicker)
|
||
end
|
||
return itemstack
|
||
end,
|
||
|
||
can_dig = function(pos)
|
||
local sleeper = get_player_in_bed(pos)
|
||
return sleeper == nil
|
||
end,
|
||
|
||
-- Paint support for rp_paint mod
|
||
_on_paint = function(pos, new_param2)
|
||
local node = minetest.get_node(pos)
|
||
local dir = minetest.fourdir_to_dir(node.param2)
|
||
local head_pos = vector.add(pos, dir)
|
||
if minetest.get_node(head_pos).name == "rp_bed:bed_head" then
|
||
minetest.swap_node(head_pos, {name = "rp_bed:bed_head", param2=new_param2})
|
||
end
|
||
return true
|
||
end,
|
||
|
||
-- Drop itself, but without metadata
|
||
drop = "rp_bed:bed_foot",
|
||
})
|
||
|
||
minetest.register_node(
|
||
"rp_bed:bed_head",
|
||
{
|
||
drawtype = "nodebox",
|
||
paramtype = "light",
|
||
paramtype2 = "color4dir",
|
||
palette = "bed_palette.png",
|
||
is_ground_content = false,
|
||
pointable = false,
|
||
diggable = false,
|
||
|
||
tiles = {
|
||
"bed_head.png",
|
||
{name="default_wood.png",color="white"},
|
||
"bed_side_r.png",
|
||
"bed_side_l.png",
|
||
"bed_front.png",
|
||
"bed_inside.png",
|
||
},
|
||
overlay_tiles = {
|
||
{name="bed_head_overlay.png",color="white"},
|
||
"",
|
||
{name="bed_side_r_overlay.png",color="white"},
|
||
{name="bed_side_l_overlay.png",color="white"},
|
||
{name="bed_front_overlay.png",color="white"},
|
||
{name="bed_inside_overlay.png",color="white"},
|
||
},
|
||
use_texture_alpha = "clip",
|
||
groups = {choppy = 2, oddly_breakable_by_hand = 2, flammable = 3, bed = 1, fall_damage_add_percent = -15, not_in_creative_inventory = 1 },
|
||
sounds = sounds,
|
||
node_box = {
|
||
type = "fixed",
|
||
fixed = {-0.5, -0.5, -0.5, 0.5, 2/16, 0.5}
|
||
},
|
||
on_blast = function() end,
|
||
drop = "",
|
||
})
|
||
|
||
minetest.register_alias("rp_bed:bed", "rp_bed:bed_foot")
|
||
|
||
-- Crafting
|
||
|
||
crafting.register_craft(
|
||
{
|
||
output = "rp_bed:bed",
|
||
items = {
|
||
"group:fuzzy 3",
|
||
"group:planks 3",
|
||
}
|
||
})
|
||
minetest.register_craft({
|
||
type = "fuel",
|
||
recipe = "rp_bed:bed_foot",
|
||
burntime = 30,
|
||
})
|
||
|
||
-- Player effects
|
||
|
||
player_effects.register_effect(
|
||
"inbed",
|
||
{
|
||
title = S("In bed"),
|
||
description = S("You're in a bed"),
|
||
duration = -1,
|
||
physics = {
|
||
speed = 0,
|
||
jump = 0,
|
||
},
|
||
save = false,
|
||
icon = "bed_effect.png",
|
||
})
|
||
|
||
-- Achievements
|
||
|
||
achievements.register_achievement(
|
||
"bedtime",
|
||
{
|
||
title = S("Bed Time"),
|
||
description = S("Craft a bed."),
|
||
times = 1,
|
||
craftitem = "rp_bed:bed_foot",
|
||
difficulty = 4.1,
|
||
})
|
||
|
||
minetest.register_lbm({
|
||
label = "Clear legacy bed meta and initialize color param2",
|
||
name = "rp_bed:reset_beds_v3_10_0",
|
||
nodenames = {"rp_bed:bed_foot", "rp_bed:bed_head"},
|
||
action = function(pos, node)
|
||
-- Clear meta
|
||
if node.name == "rp_bed:bed_foot" then
|
||
local meta = minetest.get_meta(pos)
|
||
meta:set_string("player", "")
|
||
end
|
||
|
||
-- Set default color
|
||
if node.param2 <= 3 then
|
||
node.param2 = node.param2 + (DEFAULT_BED_COLOR - 1) * 4
|
||
minetest.swap_node(pos, node)
|
||
end
|
||
end,
|
||
})
|
||
|
||
-- Aliases
|
||
minetest.register_alias("bed:bed", "rp_bed:bed_foot")
|
||
minetest.register_alias("bed:bed_foot", "rp_bed:bed_foot")
|
||
minetest.register_alias("bed:bed_head", "rp_bed:bed_head")
|