123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843 |
- if not minetest.global_exists("beds") then beds = {} end
- local SLEEP_TIME_WO_NIGHTSKIP = 5
- -- Localize for performance.
- local vector_round = vector.round
- local math_random = math.random
- -- Reloadable file.
- if not beds.run_functions_once then
- local c = "beds:functions"
- local f = beds.modpath .. "/functions.lua"
- reload.register_file(c, f, false)
- beds.run_functions_once = true
- end
- local pi = math.pi
- --local player_in_bed = 0
- local enable_respawn = true
- local count_players_in_bed = function()
- local count = 0
- for k, v in pairs(beds.player) do
- local nobeds = minetest.check_player_privs(k, {nobeds=true})
- -- Ignore AFK folks.
- if afk.is_afk(k) then
- nobeds = true
- end
- local registered = passport.player_registered(k)
- if not nobeds and registered then
- count = count + 1
- end
- end
- return count
- end
- local get_participating_players = function()
- local players = minetest.get_connected_players()
- local outp = {}
- for k, v in ipairs(players) do
- local pname = v:get_player_name()
- local nobeds = minetest.check_player_privs(v, {nobeds=true})
- -- Ignore AFK folks.
- if afk.is_afk(pname) then
- nobeds = true
- end
- local registered = passport.player_registered(pname)
- if not nobeds and registered then
- outp[#outp+1] = v
- end
- end
- return outp
- end
- local function get_look_yaw(pos)
- local n = minetest.get_node(pos)
- if n.param2 == 1 then
- return pi / 2, n.param2
- elseif n.param2 == 3 then
- return -pi / 2, n.param2
- elseif n.param2 == 0 then
- return pi, n.param2
- else
- return 0, n.param2
- end
- end
- local function is_night_skip_enabled()
- local tod = minetest.get_timeofday()
- if tod > 0.2 and tod < 0.805 then
- -- Consider nobody in beds during daytime.
- return false
- end
- local enable_night_skip = minetest.settings:get_bool("enable_bed_night_skip")
- if enable_night_skip == nil then
- enable_night_skip = true
- end
- return enable_night_skip
- end
- local function check_in_beds()
- local in_bed = beds.player
- local players = get_participating_players()
- for n, player in ipairs(players) do
- local name = player:get_player_name()
- if not in_bed[name] then
- return false
- end
- end
- return #players > 0
- end
- local function lay_down(player, pos, bed_pos, state, skip)
- local name = player:get_player_name()
- local pmeta = player:get_meta()
- local hud_flags = player:hud_get_flags()
- if not player or not name then
- return
- end
- -- stand up
- if state ~= nil and not state then
- local p = beds.pos[name] or nil
- if beds.player[name] ~= nil then
- beds.player[name] = nil
- --player_in_bed = player_in_bed - 1
- end
- -- skip here to prevent sending player specific changes (used for leaving players)
- if skip then
- return
- end
- if p then
- player:set_pos(p)
- end
- -- physics, eye_offset, etc
- pova.remove_modifier(player, "eye_offset", "sleeping")
- player:set_look_horizontal(math_random(1, 180) / 100)
- default.player_attached[name] = false
- pova.remove_modifier(player, "physics", "sleeping")
- hud_flags.wielditem = true
- default.player_set_animation(player, "stand" , 30)
- local otime = pmeta:get_int("last_sleep_time")
- local ntime = os.time()
- local dtime = math.max(ntime - otime, 0)
- --minetest.chat_send_all("stayed in bed for " .. dtime .. " seconds.")
- -- Staying in bed long enough cures portal sickness even if nightskip not successful.
- if dtime > (60 * SLEEP_TIME_WO_NIGHTSKIP) then
- beds.player_finishes_sleep(name)
- end
- -- lay down
- else
- beds.player[name] = 1
- beds.pos[name] = pos
- --player_in_bed = player_in_bed + 1
- -- physics, eye_offset, etc
- pova.set_modifier(player, "eye_offset", {{x = 0, y = -13, z = 0}}, "sleeping")
- local yaw, param2 = get_look_yaw(bed_pos)
- player:set_look_horizontal(yaw)
- pmeta:set_int("last_sleep_time", os.time())
- local dir = minetest.facedir_to_dir(param2)
- local p = {
- x = bed_pos.x + dir.x / 2,
- y = bed_pos.y + 0.5,
- z = bed_pos.z + dir.z / 2,
- }
- pova.set_modifier(player, "physics", {speed = 0, jump = 0}, "sleeping")
- player:set_pos(p)
- default.player_attached[name] = true
- hud_flags.wielditem = false
- default.player_set_animation(player, "lay" , 0)
- end
- player:hud_set_flags(hud_flags)
- end
- local function update_formspecs(finished)
- local ges = #get_participating_players()
- local form_n
- local ppl_in_bed = count_players_in_bed()
- local is_majority = (ges / 2) < ppl_in_bed
- if finished then
- form_n = beds.formspec .. "label[3.0,11;Good morning.]"
- else
- form_n = beds.formspec .. "label[2.4,11;" .. tostring(ppl_in_bed) ..
- " of " .. tostring(ges) .. " players are in bed.]"
- if is_majority and is_night_skip_enabled() then
- form_n = form_n .. "button_exit[2,8;4,0.75;force;Force Night Skip]"
- end
- end
- for name,_ in pairs(beds.player) do
- local form_s = form_n
- if portal_sickness.is_sick_or_queasy(name) then
- form_s = form_s .. "label[1.2,10;You are ill. Sleep " .. SLEEP_TIME_WO_NIGHTSKIP .. " minutes or skip night to cure.]"
- if finished then
- --minetest.chat_send_all('test')
- minetest.after(1, function()
- --minetest.chat_send_all('after')
- if not portal_sickness.is_sick_or_queasy(name) then
- --minetest.chat_send_all('not sick')
- local form_s = form_n
- form_s = form_s .. "label[2.3,10;You don't feel ill anymore.]"
- minetest.show_formspec(name, "beds:detatched_formspec", form_s)
- end
- end)
- end
- end
- minetest.show_formspec(name, "beds:detatched_formspec", form_s)
- end
- end
- function beds.kick_players()
- for name, _ in pairs(beds.player) do
- local player = minetest.get_player_by_name(name)
- lay_down(player, nil, nil, false)
- end
- end
- function beds.kick_one_player(name)
- local player = minetest.get_player_by_name(name)
- if player and player:is_player() then
- if beds.player[name] ~= nil then
- beds.player[name] = nil
- lay_down(player, nil, nil, false)
- update_formspecs(false)
- return true
- end
- end
- end
- function beds.spawn_monsters_near(pos)
- pos = vector.round(pos)
- local minp = vector.offset(pos, -5, -2, -5)
- local maxp = vector.offset(pos, 5, 2, 5)
- local air = minetest.find_nodes_in_area(minp, maxp, "air")
- -- This will almost never happen.
- if not air or #air == 0 then
- return
- end
- local count = math.random(1, 5)
- for k = 1, count do
- local target = air[math.random(1, #air)]
- local success, luaentity = mob_spawn.spawn_mob_at(target, "stoneman:stoneman")
- if luaentity then
- --minetest.chat_send_all('setting drops to nil')
- -- Must set to empty table, instead of nil, because of how mobs API
- -- works.
- luaentity.drops = {}
- end
- end
- end
- -- This function runs after a successful night skip, for each bed that was used
- -- for sleeping.
- function beds.check_monsters_accessible(pos)
- pos = vector.round(pos)
- local minp = vector.offset(pos, -30, -10, -30)
- local maxp = vector.offset(pos, 30, 10, 30)
- local air = minetest.find_nodes_in_area(minp, maxp, "air")
- -- This will almost never happen.
- if not air or #air == 0 then
- return
- end
- local function find_ground(pos)
- local p2 = vector.offset(pos, 0, -1, 0)
- local n2 = minetest.get_node(p2)
- local count = 0
- while n2.name == "air" and count < 16 do
- pos = p2
- p2 = vector.offset(pos, 0, -1, 0)
- n2 = minetest.get_node(p2)
- count = count + 1
- end
- return pos
- end
- local startpos = find_ground(air[math.random(1, #air)])
- local count = 0
- while vector.distance(pos, startpos) < 20 and count < 30 do
- startpos = find_ground(air[math.random(1, #air)])
- count = count + 1
- end
- -- If start pos is too close, path could be starting in the same room.
- -- This is not allowed.
- if vector.distance(pos, startpos) < 20 then
- return
- end
- local path = minetest.find_path(startpos, pos, 16, 5, 5)
- if path then
- --minetest.chat_send_player("MustTest", "Path exists.")
- return true
- end
- end
- function beds.player_finishes_sleep(pname)
- local player = minetest.get_player_by_name(pname)
- if player then
- -- Heal player 4 HP, but not if the player is dead.
- if player:get_hp() > 0 then
- local hp_max = pova.get_active_modifier(player, "properties").hp_max
- player:set_hp(player:get_hp() + (hp_max * 0.2))
- end
- -- Increase player's hunger.
- hunger.increase_hunger(player, 6)
- -- Refill stamina.
- sprint.set_stamina(player, SPRINT_STAMINA)
- -- Notify portal sickness mod.
- --minetest.chat_send_player("MustTest", "# Server: <" .. rename.gpn(pname) .. ">!")
- portal_sickness.on_use_bed(pname)
- local pos = vector.round(utility.get_middle_pos(player:get_pos()))
- if beds.check_monsters_accessible(pos) then
- beds.spawn_monsters_near(pos)
- end
- end
- end
- function beds.skip_night()
- minetest.set_timeofday(0.23)
-
- -- This assumes that players aren't kicked out of beds until after this function runs.
- -- Thus the need for 'minetest.after'.
- for k, v in pairs(beds.player) do
- local pname = k
- minetest.after(0, function()
- beds.player_finishes_sleep(pname)
- end)
- end
- end
- function beds.report_respawn_status(name)
- --minetest.chat_send_player("MustTest", "# Server: Respawn report!")
- local good = false
- local pos = beds.spawn[name]
- if pos then
- local spawncount = beds.storage:get_int(name .. ":count")
- if spawncount > 0 then
- minetest.chat_send_player(name,
- "# Server: Your home position currently set in the " .. rc.realm_description_at_pos(pos) .. " @ " ..
- rc.pos_to_string(pos) .. " has " .. spawncount .. " respawn(s) left.")
- good = true
- end
- end
- if not good then
- minetest.chat_send_player(name, "# Server: You currently have no home/respawn position set.")
- end
- end
- function beds.get_respawn_count(pname)
- local pos = beds.spawn[pname]
- if pos then
- local spawncount = beds.storage:get_int(pname .. ":count")
- if spawncount > 0 then
- return spawncount
- end
- end
- return 0
- end
- local function node_blocks_bed(nn)
- if nn == "air" then return false end
- if string.find(nn, "ladder") or
- string.find(nn, "torch") or
- string.find(nn, "memorandum") then
- return false
- end
- local def = minetest.reg_ns_nodes[nn]
- if def then
- local dt = def.drawtype
- local pt2 = def.paramtype2
- if dt == "airlike" or
- dt == "signlike" or
- dt == "torchlike" or
- dt == "raillike" or
- dt == "plantlike" or
- (dt == "nodebox" and pt2 == "wallmounted") then
- return false
- end
- end
- -- All stairs nodes block bed respawning.
- return true
- end
- function beds.is_valid_bed_spawn(pos)
- local n1 = minetest.get_node(vector.add(pos, {x=0, y=1, z=0}))
- local n2 = minetest.get_node(vector.add(pos, {x=0, y=2, z=0}))
- if node_blocks_bed(n1.name) or node_blocks_bed(n2.name) then
- return false
- end
- return true
- end
- function beds.on_rightclick(pos, player)
- pos = vector_round(pos)
- local name = player:get_player_name()
- local meta = minetest.get_meta(pos)
- local owner = meta:get_string("owner") or ""
- -- Not while attached to something else!
- if default.player_attached[name] or player:get_attach() then
- return
- end
- if player:get_hp() == 0 then
- return
- end
- -- Check if player is moving.
- if vector.length(player:get_velocity()) > 0.001 then
- minetest.chat_send_player(name, "# Server: Stop moving before going to bed!")
- return
- end
- if owner == "" then
- -- If bed has no owner, and pos is not protected, player takes ownership.
- -- Note: this is to prevent player from taking ownership of an unowned bed
- -- in an area protected by someone else.
- if minetest.test_protection(pos, name) then
- minetest.chat_send_player(name, "# Server: You cannot take ownership of this bed due to protection.")
- return
- else
- local dname = rename.gpn(name)
- meta:set_string("owner", name)
- meta:set_string("rename", dname)
- meta:mark_as_private({"owner", "rename"})
- meta:set_string("infotext", "Bed (Owned by <" .. dname .. ">!)")
- end
- elseif owner == "server" then
- -- If owner is server, then bed is public and player may sleep here.
- -- But respawn position must not be set here.
- local others = minetest.get_connected_players()
- -- Check if bed is occupied.
- for k, v in ipairs(others) do
- if v:get_player_name() ~= name then
- if vector.distance(v:get_pos(), pos) < 0.75 then
- minetest.chat_send_player(name, "# Server: This bed is already occupied!")
- return
- end
- end
- end
- elseif owner ~= name then
- minetest.chat_send_player(name, "# Server: You cannot sleep here, this bed is not yours!")
- return
- end
- -- Otherwise, if bed is public OR the bed is owned by the player, then they
- -- are allowed to sleep, even if the bed is protected by someone else (and the
- -- protector wasn't shared, e.g., basic protection).
- if beds.monsters_nearby(pos, player) then
- minetest.chat_send_player(name, "# Server: You cannot sleep now, there are monsters nearby!")
- beds.report_respawn_status(name)
- return
- end
- if not beds.is_valid_bed_spawn(pos) then
- minetest.chat_send_player(name, "# Server: You cannot use this bed, there is not enough space above it to respawn!")
- beds.report_respawn_status(name)
- return
- end
-
- local ppos = player:get_pos()
- local tod = minetest.get_timeofday()
- -- Player can sleep in bed anytime in the nether.
- if ppos.y > -25000 then
- if tod > 0.2 and tod < 0.805 then
- if beds.player[name] then
- lay_down(player, nil, nil, false)
- end
- minetest.chat_send_player(name, "# Server: You can only sleep at night.")
- beds.report_respawn_status(name)
- return
- end
- end
- -- move to bed
- if not beds.player[name] then
- lay_down(player, ppos, pos)
- -- If the bed is public, then player doesn't sethome here, and respawn count is not changed.
- if owner ~= "server" then
- beds.set_spawn(vector_round(pos), name)
- -- Sleeping in a bed refreshes the respawn count for this player.
- -- The player will respawn at this bed as long as their count is
- -- greater than 0.
- local spawncount = 8
- beds.storage:set_int(name .. ":count", spawncount)
- minetest.chat_send_player(name, "# Server: You will respawn in your bed at " .. rc.pos_to_namestr(pos) .. " up to " .. spawncount .. " times.")
- minetest.chat_send_player(name, "# Server: Afterward you will need to sleep again to refresh your respawn position.")
- minetest.chat_send_player(name, "# Server: You may safely dig your previous bed, if you had one set.")
- if survivalist.game_in_progress(name) then
- minetest.chat_send_player(name, "# Server: If you die during the Survival Challenge you will respawn here instead of failing the Challenge.")
- end
- else
- minetest.chat_send_player(name, "# Server: This bed is public, you cannot set-home here.")
- end
- else
- lay_down(player, nil, nil, false)
- end
- update_formspecs(false)
- -- skip the night and let all players stand up
- if check_in_beds() then
- minetest.after(2, function()
- update_formspecs(is_night_skip_enabled())
- if is_night_skip_enabled() then
- beds.skip_night()
- beds.kick_players()
- end
- end)
- end
- end
- function beds.has_respawn_bed(pname)
- if beds.spawn[pname] then
- return true
- end
- end
- function beds.get_respawn_pos_or_nil(pname)
- return beds.spawn[pname]
- end
- function beds.clear_player_spawn(pname)
- beds.spawn[pname] = nil
- beds.save_spawns()
- end
- function beds.set_player_spawn(pname, pos)
- beds.spawn[pname] = pos
- beds.save_spawns()
- end
- -- Respawn player at bed if enabled and valid position is found.
- -- Note: this can also be called from /emergency_recall.
- function beds.on_respawnplayer(player)
- local pname = player:get_player_name()
- local pmeta = player:get_meta()
- local pos = beds.spawn[pname]
- -- Record the last respawn time.
- pmeta:set_string("last_respawn_time", tostring(os.time()))
- -- If the player died in MIDFELD, behave as if they don't have a bed, and send
- -- them to the OUTBACK. If they die in the outback after this flag is set, they'll
- -- keep respawning in the outback until they use the gate (bypassing their bed),
- -- at which point the outback gate will send them back to MIDFELD instead of the
- -- overworld.
- --
- -- Note: the point of this convoluted logic is to prevent player from being
- -- able to use flame staffs to cheese their way out of a Survival Challenge.
- -- The issue is that dying in MIDFELD is supposed to be an official means of
- -- re-entering the Outback (without losing your bed). But since that is the
- -- case, I need to make sure that if the player enters the Outback in that way,
- -- that they cannot leave the Outback EXCEPT by returning to MIDFELD.
- if player:get_meta():get_int("abyss_return_midfeld") == 1 then
- -- Unless player's bed is actually IN MIDFELD, in which case just clear the
- -- flag and respawn in their bed.
- if pos and rc.current_realm_at_pos(pos) == "midfeld" then
- -- Respawn in your bed in Midfeld, and clear the flag.
- player:get_meta():set_int("abyss_return_midfeld", 0)
- elseif pos and rc.current_realm_at_pos(pos) == "abyss" then
- -- Do nothing, respawn in the Outback in your bed.
- -- But don't clear the flag.
- else
- -- Respawn in the Outback as if a new player.
- pos = nil
- end
- end
- if pos then
- -- Don't preload area, that could allow a cheat.
- -- Update player's position immediately, without delay.
- wield3d.on_teleport()
- -- If player dies in a realm and their bed is in another, then they may
- -- change realms that way.
- rc.notify_realm_update(player, pos)
- player:set_pos(pos)
- local spawncount = beds.storage:get_int(pname .. ":count")
- if pmeta:get_int("was_assassinated") ~= 0 then
- pmeta:set_string("was_assassinated", "")
- spawncount = 0
- chat_core.alert_player_sound(pname)
- local RED = core.get_color_escape_sequence("#ff0000")
- minetest.chat_send_player(pname, RED .. "# Server: Your bed is lost! You were assassinated in the wilds.")
- end
- if spawncount == 1 then
- spawncount = 0
- beds.storage:set_int(pname .. ":count", spawncount)
- beds.spawn[pname] = nil
- beds.save_spawns()
- chat_core.alert_player_sound(pname)
- local RED = core.get_color_escape_sequence("#ff0000")
- minetest.chat_send_player(pname, RED .. "# Server: Warning! Your respawn position is lost. Sleep or die!")
- elseif spawncount > 1 then
- spawncount = spawncount - 1
- beds.storage:set_int(pname .. ":count", spawncount)
- if spawncount > 1 then
- minetest.chat_send_player(pname, "# Server: " .. spawncount .. " respawns left for that bed.")
- else
- chat_core.alert_player_sound(pname)
- local RED = core.get_color_escape_sequence("#ff0000")
- minetest.chat_send_player(pname, RED .. "# Server: Alert! Only 1 respawn left for that bed!")
- end
- elseif spawncount == 0 then
- beds.spawn[pname] = nil
- beds.save_spawns()
- end
- ambiance.sound_play("respawn", pos, 1.0, 10)
- else
- local death_pos = minetest.string_to_pos(pmeta:get_string("last_death_pos"))
- -- If the death position is not known, assume they died in the Abyss.
- -- This should normally never happen.
- if not death_pos then
- death_pos = rc.static_spawn("abyss")
- end
- -- Tests show that `on_respawnplayer` is only called for existing players
- -- that die and respawn, NOT for newly-joined players!
- --minetest.chat_send_all("death at " .. minetest.pos_to_string(death_pos))
- --minetest.after(1, function() minetest.chat_send_all("on_respawnplayer was called!") end)
- -- Shall place player in the Outback, ALWAYS.
- randspawn.reposition_player(pname, death_pos)
- -- If player died in a realm other than the abyss, then give them initial
- -- stuff upon respawning there.
- --
- -- Update: no, this allows an exploit to get lots of noob stuff quickly.
- -- Make them work for their keep!
- --
- -- Update #2: I obviously can't read my own code; this only applied if
- -- player died OUTSIDE the Outback. Putting it back. If you die OUTSIDE the
- -- outback, of course you should get the initial stuff. Initial stuff is
- -- only to be withheld from players who die INSIDE the Outback (b/c in that
- -- case it would be very easy to stack noob items).
- ---[[
- if rc.current_realm_at_pos(death_pos) ~= "abyss" then
- give_initial_stuff.give(player)
- end
- --]]
- end
- return true -- Disable regular player placement.
- end
- function beds.on_joinplayer(player)
- local name = player:get_player_name()
- beds.player[name] = nil
- if check_in_beds() then
- update_formspecs(is_night_skip_enabled())
- if is_night_skip_enabled() then
- beds.skip_night()
- beds.kick_players()
- end
- else
- update_formspecs(false)
- end
- end
- function beds.on_leaveplayer(player)
- -- Bugfix: if player leaves game while dead, and in bed,
- -- resurrect them. Maybe this avoids issues with ppl logging in dead
- -- and unable to do anything?
- local name = player:get_player_name()
- -- Note: although a player who knows about this code could theoretically
- -- use it to cheat, the cheat is not game-breaking because they would respawn
- -- in their bed anyway.
- if beds.player[name] then
- if player:get_hp() == 0 then
- player:set_hp(1)
- end
- end
- lay_down(player, nil, nil, false, true)
- beds.player[name] = nil
- -- Wrapping this in minetest.after() is necessary.
- minetest.after(0, function()
- if check_in_beds() then
- update_formspecs(is_night_skip_enabled())
- if is_night_skip_enabled() then
- beds.skip_night()
- beds.kick_players()
- end
- else
- update_formspecs(false)
- end
- end)
- end
- function beds.on_player_receive_fields(player, formname, fields)
- if formname ~= "beds:detatched_formspec" then
- return
- end
- -- Because "Force night skip" button is a button_exit, it will set fields.quit
- -- and lay_down call will change value of player_in_bed, so it must be taken
- -- earlier.
- local pib = count_players_in_bed()
- local ges = get_participating_players()
- local is_majority = ((#ges) / 2) < pib
- if (fields.quit or fields.leave) and not fields.force then
- lay_down(player, nil, nil, false)
- update_formspecs(false)
- portal_sickness.check_sick(player:get_player_name())
- end
- if fields.force then
- if is_majority and is_night_skip_enabled() then
- update_formspecs(true)
- beds.skip_night()
- beds.kick_players()
- else
- update_formspecs(false)
- end
- end
- end
- -- Detect nearby monsters.
- function beds.monsters_nearby(pos, player)
- -- `pos` is the position of the bed.
- -- `player` is the person trying to sleep in a bed.
- local radius_wide = 15
- local radius_small = 4
- local ents = minetest.get_objects_inside_radius(pos, radius_wide)
- local wide_count = 0
- local short_count = 0
- for k, v in ipairs(ents) do
- if not v:is_player() then
- local tb = v:get_luaentity()
- if tb and tb.mob then
- if tb.type and tb.type == "monster" then
- wide_count = wide_count + 1
- if vector.distance(v:get_pos(), pos) < radius_small then
- -- Found monster in (small) radius.
- short_count = short_count + 1
- end
- end
- end
- end
- end
- if short_count > 0 then
- return true
- end
- if wide_count > 0 then
- if beds.check_monsters_accessible(pos) then
- return true
- end
- end
- end
|