functions.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. beds = beds or {}
  2. -- Localize for performance.
  3. local vector_round = vector.round
  4. local math_random = math.random
  5. -- Reloadable file.
  6. if not beds.run_functions_once then
  7. local c = "beds:functions"
  8. local f = beds.modpath .. "/functions.lua"
  9. reload.register_file(c, f, false)
  10. beds.run_functions_once = true
  11. end
  12. local pi = math.pi
  13. --local player_in_bed = 0
  14. local enable_respawn = true
  15. local count_players_in_bed = function()
  16. local count = 0
  17. for k, v in pairs(beds.player) do
  18. local nobeds = minetest.check_player_privs(k, {nobeds=true})
  19. -- Ignore AFK folks.
  20. if afk.is_afk(k) then
  21. nobeds = true
  22. end
  23. local registered = passport.player_registered(k)
  24. if not nobeds and registered then
  25. count = count + 1
  26. end
  27. end
  28. return count
  29. end
  30. local get_participating_players = function()
  31. local players = minetest.get_connected_players()
  32. local outp = {}
  33. for k, v in ipairs(players) do
  34. local pname = v:get_player_name()
  35. local nobeds = minetest.check_player_privs(v, {nobeds=true})
  36. -- Ignore AFK folks.
  37. if afk.is_afk(pname) then
  38. nobeds = true
  39. end
  40. local registered = passport.player_registered(pname)
  41. if not nobeds and registered then
  42. outp[#outp+1] = v
  43. end
  44. end
  45. return outp
  46. end
  47. local function get_look_yaw(pos)
  48. local n = minetest.get_node(pos)
  49. if n.param2 == 1 then
  50. return pi / 2, n.param2
  51. elseif n.param2 == 3 then
  52. return -pi / 2, n.param2
  53. elseif n.param2 == 0 then
  54. return pi, n.param2
  55. else
  56. return 0, n.param2
  57. end
  58. end
  59. local function is_night_skip_enabled()
  60. local tod = minetest.get_timeofday()
  61. if tod > 0.2 and tod < 0.805 then
  62. -- Consider nobody in beds during daytime.
  63. return false
  64. end
  65. local enable_night_skip = minetest.setting_getbool("enable_bed_night_skip")
  66. if enable_night_skip == nil then
  67. enable_night_skip = true
  68. end
  69. return enable_night_skip
  70. end
  71. local function check_in_beds()
  72. local in_bed = beds.player
  73. local players = get_participating_players()
  74. for n, player in ipairs(players) do
  75. local name = player:get_player_name()
  76. if not in_bed[name] then
  77. return false
  78. end
  79. end
  80. return #players > 0
  81. end
  82. local function lay_down(player, pos, bed_pos, state, skip)
  83. local name = player:get_player_name()
  84. local hud_flags = player:hud_get_flags()
  85. if not player or not name then
  86. return
  87. end
  88. -- stand up
  89. if state ~= nil and not state then
  90. local p = beds.pos[name] or nil
  91. if beds.player[name] ~= nil then
  92. beds.player[name] = nil
  93. --player_in_bed = player_in_bed - 1
  94. end
  95. -- skip here to prevent sending player specific changes (used for leaving players)
  96. if skip then
  97. return
  98. end
  99. if p then
  100. player:set_pos(p)
  101. end
  102. -- physics, eye_offset, etc
  103. player:set_eye_offset({x = 0, y = 0, z = 0}, {x = 0, y = 0, z = 0})
  104. player:set_look_horizontal(math_random(1, 180) / 100)
  105. default.player_attached[name] = false
  106. player:set_physics_override(1, 1, 1)
  107. hud_flags.wielditem = true
  108. default.player_set_animation(player, "stand" , 30)
  109. -- lay down
  110. else
  111. beds.player[name] = 1
  112. beds.pos[name] = pos
  113. --player_in_bed = player_in_bed + 1
  114. -- physics, eye_offset, etc
  115. player:set_eye_offset({x = 0, y = -13, z = 0}, {x = 0, y = 0, z = 0})
  116. local yaw, param2 = get_look_yaw(bed_pos)
  117. player:set_look_horizontal(yaw)
  118. local dir = minetest.facedir_to_dir(param2)
  119. local p = {x = bed_pos.x + dir.x / 2, y = bed_pos.y, z = bed_pos.z + dir.z / 2}
  120. player:set_physics_override(0, 0, 0)
  121. player:set_pos(p)
  122. default.player_attached[name] = true
  123. hud_flags.wielditem = false
  124. default.player_set_animation(player, "lay" , 0)
  125. end
  126. player:hud_set_flags(hud_flags)
  127. end
  128. local function update_formspecs(finished)
  129. local ges = #get_participating_players()
  130. local form_n
  131. local ppl_in_bed = count_players_in_bed()
  132. local is_majority = (ges / 2) < ppl_in_bed
  133. if finished then
  134. form_n = beds.formspec .. "label[2.7,11;Good morning.]"
  135. else
  136. form_n = beds.formspec .. "label[2.2,11;" .. tostring(ppl_in_bed) ..
  137. " of " .. tostring(ges) .. " players are in bed.]"
  138. if is_majority and is_night_skip_enabled() then
  139. form_n = form_n .. "button_exit[2,8;4,0.75;force;Force Night Skip]"
  140. end
  141. end
  142. for name,_ in pairs(beds.player) do
  143. minetest.show_formspec(name, "beds:detatched_formspec", form_n)
  144. end
  145. end
  146. function beds.kick_players()
  147. for name, _ in pairs(beds.player) do
  148. local player = minetest.get_player_by_name(name)
  149. lay_down(player, nil, nil, false)
  150. end
  151. end
  152. function beds.kick_one_player(name)
  153. local player = minetest.get_player_by_name(name)
  154. if player and player:is_player() then
  155. if beds.player[name] ~= nil then
  156. beds.player[name] = nil
  157. lay_down(player, nil, nil, false)
  158. update_formspecs(false)
  159. return true
  160. end
  161. end
  162. end
  163. function beds.skip_night()
  164. minetest.set_timeofday(0.23)
  165. -- This assumes that players aren't kicked out of beds until after this function runs.
  166. for k, v in pairs(beds.player) do
  167. local pname = k
  168. -- HUD update is ignored if minetest.after() isn't used?
  169. -- Oh well. A little delay is nice, too.
  170. minetest.after(0.5, function()
  171. local player = minetest.get_player_by_name(pname)
  172. if player then
  173. -- Heal player 4 HP, but not if the player is dead.
  174. if player:get_hp() > 0 then
  175. player:set_hp(player:get_hp() + 4)
  176. end
  177. -- Increase player's hunger.
  178. hunger.increase_hunger(player, 6)
  179. -- Refill stamina.
  180. sprint.set_stamina(player, SPRINT_STAMINA)
  181. -- Notify portal sickness mod.
  182. --minetest.chat_send_player("MustTest", "# Server: <" .. rename.gpn(pname) .. ">!")
  183. portal_sickness.on_use_bed(pname)
  184. end
  185. end)
  186. end
  187. end
  188. function beds.report_respawn_status(name)
  189. --minetest.chat_send_player("MustTest", "# Server: Respawn report!")
  190. local good = false
  191. local pos = beds.spawn[name]
  192. if pos then
  193. local spawncount = beds.storage:get_int(name .. ":count")
  194. if spawncount > 0 then
  195. minetest.chat_send_player(name,
  196. "# Server: Your home position currently set in the " .. rc.realm_description_at_pos(pos) .. " @ " ..
  197. rc.pos_to_string(pos) .. " has " .. spawncount .. " respawn(s) left.")
  198. good = true
  199. end
  200. end
  201. if not good then
  202. minetest.chat_send_player(name, "# Server: You currently have no home/respawn position set.")
  203. end
  204. end
  205. function beds.get_respawn_count(pname)
  206. local pos = beds.spawn[pname]
  207. if pos then
  208. local spawncount = beds.storage:get_int(pname .. ":count")
  209. if spawncount > 0 then
  210. return spawncount
  211. end
  212. end
  213. return 0
  214. end
  215. local function node_blocks_bed(nn)
  216. if nn == "air" then return false end
  217. if string.find(nn, "ladder") or
  218. string.find(nn, "torch") or
  219. string.find(nn, "memorandum") then
  220. return false
  221. end
  222. local def = minetest.reg_ns_nodes[nn]
  223. if def then
  224. local dt = def.drawtype
  225. local pt2 = def.paramtype2
  226. if dt == "airlike" or
  227. dt == "signlike" or
  228. dt == "torchlike" or
  229. dt == "raillike" or
  230. dt == "plantlike" or
  231. (dt == "nodebox" and pt2 == "wallmounted") then
  232. return false
  233. end
  234. end
  235. -- All stairs nodes block bed respawning.
  236. return true
  237. end
  238. function beds.is_valid_bed_spawn(pos)
  239. local n1 = minetest.get_node(vector.add(pos, {x=0, y=1, z=0}))
  240. local n2 = minetest.get_node(vector.add(pos, {x=0, y=2, z=0}))
  241. if node_blocks_bed(n1.name) or node_blocks_bed(n2.name) then
  242. return false
  243. end
  244. return true
  245. end
  246. function beds.on_rightclick(pos, player)
  247. pos = vector_round(pos)
  248. local name = player:get_player_name()
  249. local meta = minetest.get_meta(pos)
  250. local owner = meta:get_string("owner") or ""
  251. -- Not while attached to something else!
  252. if default.player_attached[name] then
  253. return
  254. end
  255. if player:get_hp() == 0 then
  256. return
  257. end
  258. if owner == "" then
  259. -- If bed has no owner, and pos is not protected, player takes ownership.
  260. -- Note: this is to prevent player from taking ownership of an unowned bed
  261. -- in an area protected by someone else.
  262. if minetest.test_protection(pos, name) then
  263. minetest.chat_send_player(name, "# Server: You cannot take ownership of this bed due to protection.")
  264. return
  265. else
  266. local dname = rename.gpn(name)
  267. meta:set_string("owner", name)
  268. meta:set_string("rename", dname)
  269. meta:mark_as_private({"owner", "rename"})
  270. meta:set_string("infotext", "Bed (Owned by <" .. dname .. ">!)")
  271. end
  272. elseif owner == "server" then
  273. -- No code needed here so far.
  274. -- If owner is server, then bed is public and player may sleep here.
  275. -- But respawn position must not be set here.
  276. elseif owner ~= name then
  277. minetest.chat_send_player(name, "# Server: You cannot sleep here, this bed is not yours!")
  278. return
  279. end
  280. -- Otherwise, if bed is public OR the bed is owned by the player, then they
  281. -- are allowed to sleep, even if the bed is protected by someone else (and the
  282. -- protector wasn't shared, e.g., basic protection).
  283. if beds.monsters_nearby(pos, player) then
  284. minetest.chat_send_player(name, "# Server: You cannot sleep now, there are monsters nearby!")
  285. beds.report_respawn_status(name)
  286. return
  287. end
  288. if not beds.is_valid_bed_spawn(pos) then
  289. minetest.chat_send_player(name, "# Server: You cannot use this bed, there is not enough space above it to respawn!")
  290. beds.report_respawn_status(name)
  291. return
  292. end
  293. local ppos = player:get_pos()
  294. local tod = minetest.get_timeofday()
  295. -- Player can sleep in bed anytime in the nether.
  296. if ppos.y > -25000 then
  297. if tod > 0.2 and tod < 0.805 then
  298. if beds.player[name] then
  299. lay_down(player, nil, nil, false)
  300. end
  301. minetest.chat_send_player(name, "# Server: You can only sleep at night.")
  302. beds.report_respawn_status(name)
  303. return
  304. end
  305. end
  306. -- move to bed
  307. if not beds.player[name] then
  308. lay_down(player, ppos, pos)
  309. -- If the bed is public, then player doesn't sethome here, and respawn count is not changed.
  310. if owner ~= "server" then
  311. beds.set_spawn(vector_round(pos), name)
  312. -- Sleeping in a bed refreshes the respawn count for this player.
  313. -- The player will respawn at this bed as long as their count is
  314. -- greater than 0.
  315. local spawncount = 8
  316. beds.storage:set_int(name .. ":count", spawncount)
  317. minetest.chat_send_player(name, "# Server: You will respawn in your bed at " .. rc.pos_to_namestr(pos) .. " up to " .. spawncount .. " times.")
  318. minetest.chat_send_player(name, "# Server: Afterward you will need to sleep again to refresh your respawn position.")
  319. minetest.chat_send_player(name, "# Server: You may safely dig your previous bed, if you had one set.")
  320. if survivalist.game_in_progress(name) then
  321. minetest.chat_send_player(name, "# Server: If you die during the Survival Challenge you will respawn here instead of failing the Challenge.")
  322. end
  323. else
  324. minetest.chat_send_player(name, "# Server: This bed is public, you cannot set-home here.")
  325. end
  326. else
  327. lay_down(player, nil, nil, false)
  328. end
  329. update_formspecs(false)
  330. -- skip the night and let all players stand up
  331. if check_in_beds() then
  332. minetest.after(2, function()
  333. update_formspecs(is_night_skip_enabled())
  334. if is_night_skip_enabled() then
  335. beds.skip_night()
  336. beds.kick_players()
  337. end
  338. end)
  339. end
  340. end
  341. function beds.has_respawn_bed(pname)
  342. if beds.spawn[pname] then
  343. return true
  344. end
  345. end
  346. function beds.clear_player_spawn(pname)
  347. beds.spawn[pname] = nil
  348. beds.save_spawns()
  349. end
  350. -- respawn player at bed if enabled and valid position is found
  351. function beds.on_respawnplayer(player)
  352. local name = player:get_player_name()
  353. local pos = beds.spawn[name]
  354. if pos then
  355. -- Don't preload area, that could allow a cheat.
  356. -- Update player's position immediately, without delay.
  357. wield3d.on_teleport()
  358. player:set_pos(pos)
  359. -- If player dies in a realm and their bed is in another, then they may
  360. -- change realms that way.
  361. rc.notify_realm_update(player, pos)
  362. local spawncount = beds.storage:get_int(name .. ":count")
  363. if spawncount <= 1 then
  364. beds.spawn[name] = nil
  365. beds.save_spawns()
  366. chat_core.alert_player_sound(name)
  367. local RED = core.get_color_escape_sequence("#ff0000")
  368. minetest.chat_send_player(name, RED .. "# Server: Warning! Your respawn position is lost!")
  369. else
  370. spawncount = spawncount - 1
  371. beds.storage:set_int(name .. ":count", spawncount)
  372. if spawncount > 1 then
  373. minetest.chat_send_player(name, "# Server: " .. spawncount .. " respawns left for that bed.")
  374. else
  375. chat_core.alert_player_sound(name)
  376. local RED = core.get_color_escape_sequence("#ff0000")
  377. minetest.chat_send_player(name, RED .. "# Server: Alert! Only 1 respawn left for that bed!")
  378. end
  379. end
  380. ambiance.sound_play("respawn", pos, 1.0, 10)
  381. else
  382. -- If the death position is not known, assume they died in the Abyss.
  383. local death_pos = rc.static_spawn("abyss")
  384. if bones.last_known_death_locations[name] then
  385. death_pos = bones.last_known_death_locations[name]
  386. end
  387. -- Tests show that `on_respawnplayer` is only called for existing players
  388. -- that die and respawn, NOT for newly-joined players!
  389. --minetest.chat_send_all("death at " .. minetest.pos_to_string(death_pos))
  390. --minetest.after(1, function() minetest.chat_send_all("on_respawnplayer was called!") end)
  391. -- Shall place player in the Outback, ALWAYS.
  392. randspawn.reposition_player(name, death_pos)
  393. -- If player died in a realm other than the abyss, then give them initial
  394. -- stuff upon respawning there.
  395. if rc.current_realm_at_pos(death_pos) ~= "abyss" then
  396. give_initial_stuff.give(player)
  397. end
  398. end
  399. return true -- Disable regular player placement.
  400. end
  401. function beds.on_joinplayer(player)
  402. local name = player:get_player_name()
  403. beds.player[name] = nil
  404. if check_in_beds() then
  405. update_formspecs(is_night_skip_enabled())
  406. if is_night_skip_enabled() then
  407. beds.skip_night()
  408. beds.kick_players()
  409. end
  410. else
  411. update_formspecs(false)
  412. end
  413. end
  414. function beds.on_leaveplayer(player)
  415. -- Bugfix: if player leaves game while dead, and in bed,
  416. -- resurrect them. Maybe this avoids issues with ppl logging in dead
  417. -- and unable to do anything?
  418. local name = player:get_player_name()
  419. -- Note: although a player who knows about this code could theoretically
  420. -- use it to cheat, the cheat is not game-breaking because they would respawn
  421. -- in their bed anyway.
  422. if beds.player[name] then
  423. if player:get_hp() == 0 then
  424. player:set_hp(1)
  425. end
  426. end
  427. lay_down(player, nil, nil, false, true)
  428. beds.player[name] = nil
  429. -- Wrapping this in minetest.after() is necessary.
  430. minetest.after(0, function()
  431. if check_in_beds() then
  432. update_formspecs(is_night_skip_enabled())
  433. if is_night_skip_enabled() then
  434. beds.skip_night()
  435. beds.kick_players()
  436. end
  437. else
  438. update_formspecs(false)
  439. end
  440. end)
  441. end
  442. function beds.on_player_receive_fields(player, formname, fields)
  443. if formname ~= "beds:detatched_formspec" then
  444. return
  445. end
  446. -- Because "Force night skip" button is a button_exit, it will set fields.quit
  447. -- and lay_down call will change value of player_in_bed, so it must be taken
  448. -- earlier.
  449. local pib = count_players_in_bed()
  450. local ges = get_participating_players()
  451. local is_majority = ((#ges) / 2) < pib
  452. if (fields.quit or fields.leave) and not fields.force then
  453. lay_down(player, nil, nil, false)
  454. update_formspecs(false)
  455. portal_sickness.check_sick(player:get_player_name())
  456. end
  457. if fields.force then
  458. if is_majority and is_night_skip_enabled() then
  459. update_formspecs(true)
  460. beds.skip_night()
  461. beds.kick_players()
  462. else
  463. update_formspecs(false)
  464. end
  465. end
  466. end
  467. -- Detect nearby monsters.
  468. function beds.monsters_nearby(pos, player)
  469. -- `pos` is the position of the bed.
  470. -- `player` is the person trying to sleep in a bed.
  471. local ents = minetest.get_objects_inside_radius(pos, 10)
  472. for k, v in ipairs(ents) do
  473. if not v:is_player() then
  474. local tb = v:get_luaentity()
  475. if tb and tb.mob then
  476. if tb.type and tb.type == "monster" then
  477. -- Found monster in radius.
  478. return true
  479. end
  480. end
  481. end
  482. end
  483. end