2 local player_in_bed
= 0
3 local is_sp
= minetest
.is_singleplayer()
4 local weather_mod
= minetest
.get_modpath("mcl_weather") ~= nil
8 local function get_look_yaw(pos
)
9 local n
= minetest
.get_node(pos
)
11 return pi
/ 2, n
.param2
12 elseif n
.param2
== 3 then
13 return -pi
/ 2, n
.param2
14 elseif n
.param2
== 0 then
21 local function is_night_skip_enabled()
22 local enable_night_skip
= minetest
.settings
:get_bool("enable_bed_night_skip")
23 if enable_night_skip
== nil then
24 enable_night_skip
= true
26 return enable_night_skip
29 local function check_in_beds(players
)
30 local in_bed
= mcl_beds
.player
32 players
= minetest
.get_connected_players()
35 for n
, player
in ipairs(players
) do
36 local name
= player
:get_player_name()
37 if not in_bed
[name
] then
45 -- These monsters do not prevent sleep
46 local monster_exceptions
= {
47 ["mobs_mc:ghast"] = true,
48 ["mobs_mc:enderdragon"] = true,
49 ["mobs_mc:killer_bunny"] = true,
50 ["mobs_mc:slime_big"] = true,
51 ["mobs_mc:slime_small"] = true,
52 ["mobs_mc:slime_tiny"] = true,
53 ["mobs_mc:magma_cube_big"] = true,
54 ["mobs_mc:magma_cube_small"] = true,
55 ["mobs_mc:magma_cube_tiny"] = true,
56 ["mobs_mc:shulker"] = true,
59 local function lay_down(player
, pos
, bed_pos
, state
, skip
)
60 local name
= player
:get_player_name()
61 local hud_flags
= player
:hud_get_flags()
63 if not player
or not name
then
68 -- No sleeping if too far away
69 if vector
.distance(bed_pos
, pos
) > 2 then
70 minetest
.chat_send_player(name
, "You can't sleep, the bed's too far away!")
74 -- No sleeping if monsters nearby.
75 -- The exceptions above apply.
76 -- Zombie pigmen only prevent sleep while they are hostle.
77 local objs
= minetest
.get_objects_inside_radius(bed_pos
, 8)
78 for _
, obj
in ipairs(objs
) do
79 if obj
~= nil and not obj
:is_player() then
80 local ent
= obj
:get_luaentity()
81 local mobname
= ent
.name
82 local def
= minetest
.registered_entities
[mobname
]
83 -- Approximation of monster detection range
84 if def
._cmi_is_mob
and ((mobname
~= "mobs_mc:pigman" and def
.type == "monster" and not monster_exceptions
[mobname
]) or (mobname
== "mobs_mc:pigman" and ent
.state
== "attack")) then
85 if math
.abs(bed_pos
.y
- obj
:get_pos().y
) <= 5 then
86 minetest
.chat_send_player(name
, "You can't sleep now, monsters are nearby!")
95 if state
~= nil and not state
then
96 local p
= mcl_beds
.pos
[name
] or nil
97 if mcl_beds
.player
[name
] ~= nil then
98 mcl_beds
.player
[name
] = nil
99 player_in_bed
= player_in_bed
- 1
101 -- skip here to prevent sending player specific changes (used for leaving players)
109 -- physics, eye_offset, etc
110 player
:set_eye_offset({x
= 0, y
= 0, z
= 0}, {x
= 0, y
= 0, z
= 0})
111 player
:set_look_horizontal(math
.random(1, 180) / 100)
112 mcl_player
.player_attached
[name
] = false
113 playerphysics
.remove_physics_factor(player
, "speed", "mcl_beds:sleeping")
114 playerphysics
.remove_physics_factor(player
, "jump", "mcl_beds:sleeping")
115 player
:set_attribute("mcl_beds:sleeping", "false")
116 hud_flags
.wielditem
= true
117 mcl_player
.player_set_animation(player
, "stand" , 30)
121 mcl_beds
.player
[name
] = 1
122 mcl_beds
.pos
[name
] = pos
123 player_in_bed
= player_in_bed
+ 1
125 -- physics, eye_offset, etc
126 player
:set_eye_offset({x
= 0, y
= -13, z
= 0}, {x
= 0, y
= 0, z
= 0})
127 local yaw
, param2
= get_look_yaw(bed_pos
)
128 player
:set_look_horizontal(yaw
)
129 local dir
= minetest
.facedir_to_dir(param2
)
130 local p
= {x
= bed_pos
.x
+ dir
.x
/ 2, y
= bed_pos
.y
, z
= bed_pos
.z
+ dir
.z
/ 2}
131 player
:set_attribute("mcl_beds:sleeping", "true")
132 playerphysics
.add_physics_factor(player
, "speed", "mcl_beds:sleeping", 0)
133 playerphysics
.add_physics_factor(player
, "jump", "mcl_beds:sleeping", 0)
135 mcl_player
.player_attached
[name
] = true
136 hud_flags
.wielditem
= false
137 mcl_player
.player_set_animation(player
, "lay" , 0)
140 player
:hud_set_flags(hud_flags
)
143 local function update_formspecs(finished
)
144 local ges
= #minetest
.get_connected_players()
146 local all_in_bed
= ges
== player_in_bed
149 form_n
= mcl_beds
.formspec
.. "label[2.7,11; Good morning.]"
151 form_n
= mcl_beds
.formspec
.. "label[2.2,11;" .. tostring(player_in_bed
) ..
152 " of " .. tostring(ges
) .. " players are in bed]"
155 for name
,_
in pairs(mcl_beds
.player
) do
156 minetest
.show_formspec(name
, "mcl_beds_form", form_n
)
162 -- Handle environment stuff related to sleeping: skip night and thunderstorm
163 function mcl_beds
.sleep()
164 local storm_skipped
= mcl_beds
.skip_thunderstorm()
165 -- Always clear weather
167 mcl_weather
.change_weather("none")
169 if is_night_skip_enabled() then
170 if not storm_skipped
then
171 mcl_beds
.skip_night()
173 mcl_beds
.kick_players()
177 function mcl_beds
.kick_players()
178 for name
, _
in pairs(mcl_beds
.player
) do
179 local player
= minetest
.get_player_by_name(name
)
180 lay_down(player
, nil, nil, false)
184 function mcl_beds
.skip_night()
185 minetest
.set_timeofday(0.25) -- tod = 6000
188 function mcl_beds
.skip_thunderstorm()
190 if weather_mod
and mcl_weather
.get_weather() == "thunder" then
191 -- Sleep for a half day (=minimum thunderstorm duration)
192 minetest
.set_timeofday((minetest
.get_timeofday() + 0.5) % 1)
198 function mcl_beds
.on_rightclick(pos
, player
)
199 -- Anti-Inception: Don't allow to sleep while you're sleeping
200 if player
:get_attribute("mcl_beds:sleeping") == "true" then
203 if minetest
.get_modpath("mcl_worlds") then
204 local dim
= mcl_worlds
.pos_to_dimension(pos
)
205 if dim
== "nether" or dim
== "end" then
206 -- Bed goes BOOM in the Nether or End.
207 minetest
.remove_node(pos
)
208 if minetest
.get_modpath("mcl_tnt") then
209 tnt
.boom(pos
, {radius
= 4, damage_radius
= 4})
214 local name
= player
:get_player_name()
215 local ppos
= player
:get_pos()
216 local tod
= minetest
.get_timeofday() * 24000
218 -- Values taken from Minecraft Wiki with offset of +6000
219 if tod
< 18541 and tod
> 5458 and (not weather_mod
or (mcl_weather
.get_weather() ~= "thunder")) then
220 if mcl_beds
.player
[name
] then
221 lay_down(player
, nil, nil, false)
223 minetest
.chat_send_player(name
, "You can only sleep at night or during a thunderstorm.")
228 if not mcl_beds
.player
[name
] then
229 lay_down(player
, ppos
, pos
)
230 if minetest
.get_modpath("mcl_spawn") then
231 mcl_spawn
.set_spawn_pos(player
, player
:get_pos()) -- save respawn position when entering bed
234 lay_down(player
, nil, nil, false)
238 update_formspecs(false)
241 -- skip the night and let all players stand up
242 if check_in_beds() then
243 minetest
.after(5, function()
245 update_formspecs(is_night_skip_enabled())
254 minetest
.register_on_joinplayer(function(player
)
255 if player
:get_attribute("mcl_beds:sleeping") == "true" then
256 player
:set_attribute("mcl_beds:sleeping", "false")
260 minetest
.register_on_leaveplayer(function(player
)
261 local name
= player
:get_player_name()
262 lay_down(player
, nil, nil, false, true)
263 mcl_beds
.player
[name
] = nil
264 if check_in_beds() then
265 minetest
.after(5, function()
266 update_formspecs(is_night_skip_enabled())
272 minetest
.register_on_player_receive_fields(function(player
, formname
, fields
)
273 if formname
~= "mcl_beds_form" then
276 if fields
.quit
or fields
.leave
then
277 lay_down(player
, nil, nil, false)
278 update_formspecs(false)
282 update_formspecs(is_night_skip_enabled())