functions.lua 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860
  1. -- This file is reloadable.
  2. bones = bones or {}
  3. -- Contains the positions of last known player deaths, indexed by player name.
  4. bones.last_known_death_locations = bones.last_known_death_locations or {}
  5. -- Localize for performance.
  6. local vector_round = vector.round
  7. local math_random = math.random
  8. local get_public_time = function()
  9. return os.date("!%Y/%m/%d, %H:%M:%S UTC")
  10. end
  11. local share_bones_time = 1200
  12. local share_bones_time_early = (share_bones_time * 0.75)
  13. local share_bones_time_city = (share_bones_time * 10.0)
  14. local function is_owner(pos, name)
  15. local owner = minetest.get_meta(pos):get_string("owner")
  16. -- Don't let dead players pick bones.
  17. local player = minetest.get_player_by_name(name)
  18. if not player or not player:is_player() then return false end
  19. if player:get_hp() <= 0 then return false end
  20. if owner == "" or owner == name or minetest.check_player_privs(name, "protection_bypass") then
  21. return true
  22. end
  23. return false
  24. end
  25. local function may_replace(pos, player)
  26. local nn = minetest.get_node(pos).name
  27. local ndef = minetest.reg_ns_nodes[nn]
  28. -- If the node is unknown, we return false.
  29. if not ndef then
  30. return false
  31. end
  32. local pname = player and player:get_player_name() or ""
  33. local protected = minetest.test_protection(pos, pname)
  34. -- Allow replacing air and (non-walkable) liquids.
  35. if nn == "air" or (ndef.liquidtype ~= "none" and not ndef.walkable) then
  36. return true
  37. end
  38. -- Don't replace filled chests and other nodes that don't allow it.
  39. -- Includes bedrock, admin TNT, etc.
  40. local can_dig = ndef.can_dig
  41. if can_dig and not can_dig(pos, player) then
  42. return false
  43. end
  44. -- Default to each nodes buildable_to; if a placed block would replace it, why
  45. -- shouldn't bones? Flowers being squished by bones are more realistical than
  46. -- a squished stone, too. Exception are of course any protected buildable_to.
  47. return ndef.buildable_to and not protected
  48. end
  49. local drop = function(pos, itemstack)
  50. local obj = minetest.add_item(pos, itemstack:take_item(itemstack:get_count()))
  51. if obj then
  52. obj:setvelocity({
  53. x = math_random(-10, 10) / 9,
  54. y = 5,
  55. z = math_random(-10, 10) / 9,
  56. })
  57. end
  58. end
  59. local player_inventory_empty = function(inv, name)
  60. local list = inv:get_list(name)
  61. -- Nil check.
  62. if not list then
  63. -- Could not get list, list does not exist.
  64. -- This is true in the case of bags.
  65. return true -- Inventory list doesn't exist.
  66. end
  67. for i = 1, #list do
  68. local stack = list[i]
  69. if not stack:is_empty() then
  70. if not passport.is_passport(stack:get_name()) then
  71. return false -- Not empty.
  72. end
  73. end
  74. end
  75. return true -- Inventory list is empty.
  76. end
  77. local find_ground = function(pos, player)
  78. local p = {x=pos.x, y=pos.y, z=pos.z}
  79. local count = 0
  80. local cr1 = may_replace({x=p.x, y=p.y, z=p.z}, player)
  81. local cr2 = may_replace({x=p.x, y=p.y-1, z=p.z}, player)
  82. while (not (cr1 and not cr2)) or (cr1 and cr2) do
  83. p.y = p.y - 1
  84. count = count + 1
  85. if count > 10 then return pos end
  86. cr1 = may_replace({x=p.x, y=p.y, z=p.z}, player)
  87. cr2 = may_replace({x=p.x, y=p.y-1, z=p.z}, player)
  88. end
  89. -- Return position, if we can replace to it, but not to the node under it.
  90. if cr1 and not cr2 then
  91. return p
  92. end
  93. return pos
  94. end
  95. local function find_suitable_bone_location(pos, player)
  96. -- Locate position directly above ground level.
  97. local air = find_ground(pos, player)
  98. -- Check whether the initial location is suitable.
  99. if not may_replace(air, player) then
  100. air = nil
  101. end
  102. -- Is the initial location not suitable? Try to locate a suitable location on
  103. -- a horizontal plane from the actual death location.
  104. if not air then
  105. local sidepos = {
  106. {x=pos.x, y=pos.y, z=pos.z},
  107. {x=pos.x+1, y=pos.y, z=pos.z},
  108. {x=pos.x-1, y=pos.y, z=pos.z},
  109. {x=pos.x, y=pos.y, z=pos.z+1},
  110. {x=pos.x, y=pos.y, z=pos.z-1},
  111. }
  112. for k, v in ipairs(sidepos) do
  113. if may_replace(v, player) then
  114. air = v
  115. break
  116. end
  117. end
  118. end
  119. -- Didn't find a replaceable node that way, try a short-range search for air.
  120. if not air then
  121. air = minetest.find_node_near(pos, 2, {"air"})
  122. end
  123. -- Still didn't find air? Try a longer range search,
  124. -- and include liquid/fire/etc nodes that player can die in.
  125. if not air then
  126. -- If we couldn't find air, fallback to finding a location in these
  127. -- substances. Search 1 meter farther than at first.
  128. air = minetest.find_node_near(pos, 3, {
  129. "air",
  130. "default:water_source",
  131. "default:water_flowing",
  132. "default:river_water_source",
  133. "default:river_water_flowing",
  134. "default:lava_source",
  135. "default:lava_flowing",
  136. "lbrim:lava_source",
  137. "lbrim:lava_flowing",
  138. "group:gas",
  139. "rackstone:nether_grit",
  140. "rackstone:void",
  141. "fire:basic_flame",
  142. })
  143. end
  144. -- If we found air, try to make sure we found a replaceable node directly
  145. -- above ground.
  146. if air then
  147. air = find_ground(air, player)
  148. end
  149. -- By now, we have either found air or nothing.
  150. return air -- May be nil.
  151. end
  152. -- This function may be called from mods to dump player bones EXACTLY as if the
  153. -- player had died, including any side-effects. The sole exception is that the
  154. -- player's health is not actually changed. You might need to update a few other
  155. -- datums too, to get exactly the right result.
  156. function bones.dump_bones(pname, preserve_xp)
  157. local player = minetest.get_player_by_name(pname)
  158. if player then
  159. bones.on_dieplayer(player, {}, preserve_xp)
  160. end
  161. end
  162. bones.on_dieplayer = function(player, reason, preserve_xp)
  163. local bones_mode = "bones"
  164. local player_inv = player:get_inventory()
  165. local pname = player:get_player_name()
  166. -- If player died while attached to cart/boat/etc, they must be detached.
  167. default.detach_player_if_attached(player)
  168. -- Record position of player on death.
  169. -- This is needed because this information is lost on respawn.
  170. -- We must record this info *always*, even if player does not leave bones.
  171. bones.last_known_death_locations[pname] = utility.get_foot_pos(player:get_pos())
  172. -- Record all player deaths, whether they leave bones or not.
  173. minetest.log("action", "player <" .. pname .. "> died @ " .. minetest.pos_to_string(player:get_pos()))
  174. -- Death sound.
  175. coresounds.play_death_sound(player, pname)
  176. -- Notify of death.
  177. chat_colorize.notify_death(pname)
  178. jail.notify_player_death(player)
  179. -- Special case for admin. This helps prevent accidentally leaving
  180. -- admin/cheated stuff lying around.
  181. if gdac.player_is_admin(pname) then
  182. return
  183. end
  184. -- Don't make bones if player doesn't have anything.
  185. -- This also means that player won't lose XP. Keep this, it is a feature!
  186. if player_inventory_empty(player_inv, "main") and
  187. player_inventory_empty(player_inv, "craft") and
  188. player_inventory_empty(player_inv, "bag1contents") and
  189. player_inventory_empty(player_inv, "bag2contents") and
  190. player_inventory_empty(player_inv, "bag3contents") and
  191. player_inventory_empty(player_inv, "bag4contents") and
  192. player_inventory_empty(player_inv, "bag1") and
  193. player_inventory_empty(player_inv, "bag2") and
  194. player_inventory_empty(player_inv, "bag3") and
  195. player_inventory_empty(player_inv, "bag4") and
  196. player_inventory_empty(player_inv, "armor") then
  197. return
  198. end
  199. local pos = vector_round(utility.get_middle_pos(player:get_pos()))
  200. -- Check if it's possible to place bones, if not find space near player.
  201. if bones_mode == "bones" then
  202. local boneloc = find_suitable_bone_location(pos, player)
  203. if boneloc then
  204. pos = boneloc
  205. else
  206. bones_mode = "drop"
  207. end
  208. end
  209. if not rc.is_valid_realm_pos(pos) then
  210. bones_mode = "drop"
  211. end
  212. -- If player died and ought to leave bones because they have stuff in their
  213. -- inventory, BUT we cannot actually place bones (no suitable location found),
  214. -- then player doesn't lose their items. This is a safety feature since it is
  215. -- possible to die in locations where bones cannot physically be placed.
  216. -- E.g., player dies in a 1x1 mineshaft occupied by ladder nodes.
  217. if bones_mode ~= "bones" then
  218. -- Cannot create bones, therefore we don't modify player inventories.
  219. minetest.log("action", "Player <" .. pname .. "> died @ " .. minetest.pos_to_string(pos) .. ", but cannot create bones!")
  220. if not preserve_xp then
  221. -- Reduce player's mining XP without storing it anywhere.
  222. -- Prevents player from being able to use this as an exploit.
  223. -- Death should always have a cost!
  224. local xp_amount = xp.get_xp(pname, "digxp")
  225. xp_amount = (xp_amount / 3) * 2
  226. xp.set_xp(pname, "digxp", xp_amount)
  227. hud_clock.update_xp(pname)
  228. end
  229. return
  230. end
  231. local xp_for_bones = 0.0
  232. -- Halve player XP!
  233. if not preserve_xp then
  234. local xp_amount = xp.get_xp(pname, "digxp")
  235. xp_amount = xp_amount/2
  236. xp_for_bones = (xp_amount/3)*2
  237. xp.set_xp(pname, "digxp", xp_amount)
  238. end
  239. -- Note: portal sickness only removed if player would leave bones.
  240. portal_sickness.on_die_player(pname)
  241. -- These inventories are always cleared.
  242. player_inv:set_list("craftpreview", {})
  243. player_inv:set_list("craftresult", {})
  244. -- Clear the virtual bags inventories.
  245. do
  246. local bags_inv = minetest.get_inventory({type="detached", name=pname .. "_bags"})
  247. bags_inv:set_list("bag1", {})
  248. bags_inv:set_list("bag2", {})
  249. bags_inv:set_list("bag3", {})
  250. bags_inv:set_list("bag4", {})
  251. end
  252. -- The player can die while holding something. Remove it.
  253. minetest.after(0.1, function()
  254. local ply = minetest.get_player_by_name(pname)
  255. if not ply then return end
  256. local inv = ply:get_inventory()
  257. if not inv then return end
  258. if inv:is_empty("hand") then return end
  259. inv:set_list("hand", {}) -- Fixes a bug if the player dies while holding something.
  260. end)
  261. -- Preload map just in case.
  262. local minp = vector.add(pos, -16)
  263. local maxp = vector.add(pos, 16)
  264. utility.ensure_map_loaded(minp, maxp)
  265. local param2 = minetest.dir_to_facedir(player:get_look_dir())
  266. minetest.add_node(pos, {name = "bones:bones", param2 = param2})
  267. local meta = minetest.get_meta(pos)
  268. meta:set_float("digxp", xp_for_bones)
  269. meta:set_string("diedate", get_public_time())
  270. local inv = meta:get_inventory()
  271. inv:set_size("main", 200) -- Enuf space for everything!
  272. -- Keep track of how many stacks are stored in bones.
  273. local location = minetest.pos_to_string(pos)
  274. local num_stacks = 0
  275. minetest.log("action", "Put " .. xp_for_bones .. " XP in bones @ " .. location .. ".")
  276. -- Note: clear player inventory slot-by-slot to avoid clobbering PoC/KoC items.
  277. -- Empty the player's main inv. We must not to clobber any passports.
  278. do
  279. local list = player_inv:get_list("main")
  280. if list then -- Nil check necessary.
  281. for i = 1, #list do
  282. local stack = list[i]
  283. if not passport.is_passport(stack:get_name()) then
  284. if stack:get_count() > 0 and inv:room_for_item("main", stack) then
  285. inv:add_item("main", stack)
  286. minetest.log("action", "Put " .. stack:to_string() .. " in bones @ " .. location .. ".")
  287. num_stacks = num_stacks + 1
  288. -- Stack was added to bones inventory, remove it from list.
  289. list[i]:set_count(0)
  290. list[i]:set_name("")
  291. else
  292. drop(pos, stack)
  293. end
  294. end
  295. end
  296. player_inv:set_list("main", list)
  297. end
  298. end
  299. -- Empty the player's craft-grid. Passports are not preserved, here.
  300. do
  301. local list = player_inv:get_list("craft")
  302. if list then -- Nil check necessary.
  303. for i = 1, #list do
  304. local stack = list[i]
  305. if stack:get_count() > 0 and inv:room_for_item("main", stack) then
  306. inv:add_item("main", stack)
  307. minetest.log("action", "Put " .. stack:to_string() .. " in bones @ " .. location .. ".")
  308. num_stacks = num_stacks + 1
  309. else
  310. drop(pos, stack)
  311. end
  312. end
  313. player_inv:set_list("craft", {})
  314. end
  315. end
  316. -- Armor goes into bones after main and crafting grid items.
  317. armor.on_dieplayer(player, pos)
  318. -- Empty the bag slots. Passports are not preserved, here. (It should not be possible to store a passport in here, anyway.)
  319. for j = 1, 4, 1 do
  320. local bag = "bag" .. j
  321. local list = player_inv:get_list(bag)
  322. if list then -- Nil check necessary.
  323. for i = 1, #list do
  324. local stack = list[i]
  325. if stack:get_count() > 0 and inv:room_for_item("main", stack) then
  326. inv:add_item("main", stack)
  327. minetest.log("action", "Put " .. stack:to_string() .. " in bones @ " .. location .. ".")
  328. num_stacks = num_stacks + 1
  329. else
  330. drop(pos, stack)
  331. end
  332. end
  333. player_inv:set_list(bag, {})
  334. end
  335. end
  336. -- Empty the bag inventories. Passports are not preserved, here.
  337. for j = 1, 4, 1 do
  338. local bag = "bag" .. j .. "contents"
  339. local list = player_inv:get_list(bag)
  340. if list then -- Nil check necessary.
  341. for i = 1, #list do
  342. local stack = list[i]
  343. if stack:get_count() > 0 and inv:room_for_item("main", stack) then
  344. inv:add_item("main", stack)
  345. minetest.log("action", "Put " .. stack:to_string() .. " in bones @ " .. location .. ".")
  346. num_stacks = num_stacks + 1
  347. else
  348. drop(pos, stack)
  349. end
  350. end
  351. player_inv:set_list(bag, {})
  352. end
  353. end
  354. -- We use on_rightclick instead.
  355. --meta:set_string("formspec", bones_formspec)
  356. meta:set_string("owner", pname)
  357. meta:set_int("numstacks", num_stacks)
  358. -- Notify the mapping code it needs to recalculate the mapkit cache.
  359. minetest.after(0, function()
  360. map.clear_inventory_info(pname)
  361. end)
  362. if share_bones_time ~= 0 then
  363. meta:set_string("infotext",
  364. "Unfortunate <" .. rename.gpn(pname) ..
  365. ">'s Undecayed Bones\nMineral XP: " .. string.format("%.2f", xp_for_bones) .. "\n" ..
  366. "Died On " .. meta:get_string("diedate"))
  367. meta:set_int("time", 0)
  368. minetest.get_node_timer(pos):start(10)
  369. else
  370. meta:set_string("infotext",
  371. "Unfortunate <" .. rename.gpn(pname) ..
  372. ">'s Bones\nMineral XP: " .. string.format("%.2f", xp_for_bones) .. "\n" ..
  373. "Died On " .. meta:get_string("diedate"))
  374. end
  375. hud_clock.update_xp(pname)
  376. if bones_mode == "bones" then
  377. if bones and bones.do_messages then
  378. bones.do_messages(pos, pname, num_stacks)
  379. end
  380. if minetest.get_node(pos).name == "bones:bones" then
  381. minetest.log("action", "Successfully spawned bones @ " .. minetest.pos_to_string(pos) .. "!")
  382. end
  383. end
  384. -- Check if bones still exist after 1 second.
  385. minetest.after(1, function()
  386. local name = minetest.get_node(pos).name
  387. if name == "bones:bones" then
  388. minetest.log("action", "Bones exist @ " .. minetest.pos_to_string(pos) .. " after 1 second check!")
  389. elseif name == "ignore" then
  390. minetest.log("action", "Got ignore @ " .. minetest.pos_to_string(pos) .. " after 1 second bone check!")
  391. else
  392. minetest.log("action", "Got " .. name .. " @ " .. minetest.pos_to_string(pos) .. " after 1 second bone check!")
  393. end
  394. end)
  395. end
  396. bones.can_dig = function(pos, player)
  397. local inv = minetest.get_meta(pos):get_inventory()
  398. local name = ""
  399. if player then
  400. name = player:get_player_name()
  401. end
  402. return is_owner(pos, name) and inv:is_empty("main")
  403. end
  404. bones.allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  405. if is_owner(pos, player:get_player_name()) then
  406. return count
  407. end
  408. return 0
  409. end
  410. bones.allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  411. minetest.chat_send_player(player:get_player_name(), "# Server: Bones are not a place to store items!")
  412. return 0
  413. end
  414. bones.allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  415. -- Prevent picking bones right after respawn, before player is repositioned.
  416. if bones.nohack.on_hackdetect(player) then
  417. local pname = player:get_player_name()
  418. minetest.chat_send_player(pname, "# Server: Wait a bit before taking from bones.")
  419. return 0
  420. end
  421. if is_owner(pos, player:get_player_name()) then
  422. return stack:get_count()
  423. end
  424. return 0
  425. end
  426. bones.on_metadata_inventory_take = function(pos, listname, index, stack, player)
  427. local pname = player:get_player_name()
  428. local meta = minetest.get_meta(pos)
  429. if meta:get_inventory():is_empty("main") then
  430. bones.reward_xp(meta, pname)
  431. bones.do_grab_bones_message(pname, pos, meta)
  432. minetest.after(0, function() minetest.close_formspec(pname, "bones:main") end)
  433. minetest.log("action", "Player " .. pname .. " took all from bones @ " .. minetest.pos_to_string(pos) .. ". Removing bones.")
  434. minetest.remove_node(pos)
  435. end
  436. end
  437. bones.reward_xp = function(meta, pname)
  438. -- Give XP stored in bones to player.
  439. -- But only if player removed the bones!
  440. local digxp = meta:get_float("digxp")
  441. local current_xp = xp.get_xp(pname, "digxp")
  442. current_xp = current_xp + digxp
  443. if current_xp > xp.digxp_max then
  444. current_xp = xp.digxp_max
  445. end
  446. xp.set_xp(pname, "digxp", current_xp)
  447. hud_clock.update_xp(pname)
  448. meta:set_float("digxp", 0)
  449. end
  450. bones.on_punch = function(pos, node, player)
  451. -- Fix stopped timers.
  452. minetest.get_node_timer(pos):start(10)
  453. local pname = player:get_player_name()
  454. if not is_owner(pos, pname) then
  455. return
  456. end
  457. if sheriff.is_cheater(pname) then
  458. if sheriff.punish_probability(pname) then
  459. sheriff.punish_player(pname)
  460. return
  461. end
  462. end
  463. -- Prevent picking bones right after respawn, before player is repositioned.
  464. if bones.nohack.on_hackdetect(player) then
  465. minetest.chat_send_player(pname, "# Server: Wait a bit before picking bones.")
  466. return
  467. end
  468. -- Dead players cannot pick bones.
  469. if player:get_hp() <= 0 then
  470. return
  471. end
  472. local meta = minetest.get_meta(pos)
  473. -- Bones that are neither fresh nor old aren't 'punchable'.
  474. if meta:get_string("infotext") == "" then
  475. return
  476. end
  477. local inv = meta:get_inventory()
  478. local player_inv = player:get_inventory()
  479. local has_space = true
  480. local added_map = false
  481. for i = 1, inv:get_size("main") do
  482. local stk = inv:get_stack("main", i)
  483. if player_inv:room_for_item("main", stk) then
  484. inv:set_stack("main", i, nil)
  485. player_inv:add_item("main", stk)
  486. -- Notify if a mapping kit was added.
  487. if map.is_mapping_kit(stk:get_name()) then
  488. added_map = true
  489. end
  490. else
  491. has_space = false
  492. break
  493. end
  494. end
  495. -- Notify if a mapping kit was added.
  496. if added_map then
  497. map.update_inventory_info(pname)
  498. end
  499. -- remove bones if player emptied them
  500. if has_space then
  501. -- Give player the bones.
  502. if player_inv:room_for_item("main", {name = "bones:bones_type2"}) then
  503. player_inv:add_item("main", {name = "bones:bones_type2"})
  504. else
  505. minetest.add_item(pos,"bones:bones_type2")
  506. end
  507. bones.reward_xp(meta, pname)
  508. bones.do_grab_bones_message(pname, pos, meta)
  509. -- Remove the bones from the world.
  510. minetest.log("action", "Bones @ " .. minetest.pos_to_string(pos) .. " punched by " .. pname .. ". Removing bones.")
  511. minetest.remove_node(pos)
  512. end
  513. end
  514. bones.do_grab_bones_message = function(pname, pos, meta)
  515. -- Send chat messages about bones retrieval.
  516. -- Don't spam the chatstream.
  517. if not bones.players[pname] then
  518. local public = true
  519. if player_labels.query_nametag_onoff(pname) == false then
  520. public = false
  521. end
  522. if cloaking.is_cloaked(pname) then
  523. public = false
  524. end
  525. local numstacks = meta:get_int("numstacks")
  526. local stacks = "stacks"
  527. if numstacks == 1 then
  528. stacks = "stack"
  529. elseif numstacks == 0 then
  530. numstacks = "an unknown number of"
  531. end
  532. local boneowner = meta:get_string("owner")
  533. if boneowner == "" then
  534. boneowner = meta:get_string("oldowner")
  535. end
  536. local ownerstring = "<" .. rename.gpn(boneowner) .. ">'s"
  537. if pname == boneowner then
  538. local sex = skins.get_gender_strings(pname)
  539. ownerstring = sex.his .. " own"
  540. end
  541. if boneowner == "" then
  542. ownerstring = "someone's"
  543. end
  544. local agestring = "fresh"
  545. if meta:get_string("owner") == "" then
  546. agestring = "old"
  547. end
  548. if public then
  549. minetest.chat_send_all(
  550. "# Server: Player <" ..
  551. rename.gpn(pname) ..
  552. "> claimed " .. ownerstring .. " " .. agestring .. " bones at " ..
  553. rc.pos_to_namestr(vector_round(pos)) ..
  554. " with " .. numstacks .. " " .. stacks .. ".")
  555. else
  556. minetest.chat_send_all(
  557. "# Server: Someone claimed " .. agestring .. " bones with " .. numstacks ..
  558. " " .. stacks .. ".")
  559. end
  560. bones.players[pname] = true
  561. minetest.after(60, bones.release_player, pname)
  562. end
  563. end
  564. bones.on_timer = function(pos, elapsed)
  565. local meta = minetest.get_meta(pos)
  566. local time = meta:get_int("time") + elapsed
  567. local share_time = share_bones_time
  568. -- If bones are in a protected area, they can be shared earlier than normal.
  569. if minetest.test_protection(pos, "") then
  570. share_time = share_bones_time_early
  571. end
  572. -- But if bones are in city, preserve them a lot longer.
  573. if city_block:in_city(pos) then
  574. share_time = share_bones_time_city
  575. end
  576. if time >= share_time then
  577. -- Function may have been called twice or more. This prevents an issue.
  578. if meta:get_string("owner") == "" then
  579. return
  580. end
  581. -- Bones will NOT decay as long as cheaters are present on the server. This
  582. -- prevents cheaters from being able to steal other player's stuff. If the
  583. -- player that died is themself a cheater, they don't get this protection.
  584. if not sheriff.is_cheater(meta:get_string("owner")) then
  585. local cheaters_are_present = false
  586. local all_players = minetest.get_connected_players()
  587. for k, v in ipairs(all_players) do
  588. if sheriff.is_cheater(v:get_player_name()) then
  589. cheaters_are_present = true
  590. break
  591. end
  592. end
  593. if cheaters_are_present then
  594. local timer = minetest.get_node_timer(pos)
  595. timer:start(60*math_random(10, 60))
  596. return
  597. end
  598. end
  599. minetest.log("action", "Fresh bones @ " .. minetest.pos_to_string(pos) .. " decay into old bones.")
  600. local diedate = meta:get_string("diedate")
  601. if diedate == "" then
  602. diedate = "An Unknown Date"
  603. end
  604. local digxp = string.format("%.2f", meta:get_float("digxp"))
  605. meta:set_string("infotext",
  606. "Unfortunate <" .. rename.gpn(meta:get_string("owner")) ..
  607. ">'s Old Bones\nMineral XP: " .. digxp .. "\n" ..
  608. "Died On " .. diedate)
  609. meta:set_string("oldowner", meta:get_string("owner"))
  610. meta:set_string("owner", "")
  611. else
  612. meta:set_int("time", time)
  613. return true
  614. end
  615. end
  616. bones.on_blast = function(pos)
  617. minetest.log("action", "Bones @ " .. minetest.pos_to_string(pos) .. " experienced explosion blast.")
  618. end
  619. bones.on_destruct = function(pos)
  620. minetest.log("action", "Bones @ " .. minetest.pos_to_string(pos) .. " destructor called. Bones removed.")
  621. local meta = minetest.get_meta(pos)
  622. local inv = meta:get_inventory()
  623. if inv:is_empty("main") then
  624. return
  625. end
  626. -- If the inventory is not empty, we must respawn the bones.
  627. -- This is a workaround for a bug that just won't go away.
  628. local node = minetest.get_node(pos)
  629. local list = inv:get_list("main")
  630. local infotext = meta:get_string("infotext")
  631. local owner = meta:get_string("owner")
  632. local oldowner = meta:get_string("oldowner")
  633. local numstacks = meta:get_int("numstacks")
  634. local time = meta:get_int("time")
  635. local digxp = meta:get_float("digxp")
  636. local diedate = meta:get_string("diedate")
  637. minetest.after(0, function()
  638. minetest.log("action", "Bones @ " .. minetest.pos_to_string(pos) .. " were not empty! Attempting to restore bones.")
  639. minetest.set_node(pos, {name="bones:bones", param2=node.param2})
  640. local meta2 = minetest.get_meta(pos)
  641. local inv2 = meta2:get_inventory()
  642. inv2:set_size("main", 200)
  643. inv2:set_list("main", list)
  644. meta2:set_string("infotext", infotext)
  645. meta2:set_string("owner", owner)
  646. meta2:set_string("oldowner", oldowner)
  647. meta2:set_int("numstacks", numstacks)
  648. meta2:set_int("time", time)
  649. meta2:set_float("digxp", digxp)
  650. meta2:set_string("diedate", diedate)
  651. if time < share_bones_time then
  652. minetest.get_node_timer(pos):start(10)
  653. end
  654. end)
  655. end
  656. -- Show bones inventory.
  657. function bones.on_rightclick(pos, node, clicker, itemstack, pt)
  658. local meta = minetest.get_meta(pos)
  659. if meta:get_string("infotext") == "" then
  660. return
  661. end
  662. -- Put all inventory contents toward the front of the list.
  663. local inv = meta:get_inventory()
  664. local list = inv:get_list("main") or {}
  665. local tmp = {}
  666. for index, stack in ipairs(list) do
  667. if not stack:is_empty() then
  668. tmp[#tmp+1] = stack
  669. end
  670. end
  671. inv:set_list("main", {})
  672. for index, stack in ipairs(tmp) do
  673. if inv:room_for_item("main", stack) then
  674. inv:add_item("main", stack)
  675. else
  676. minetest.add_item(pos, stack)
  677. end
  678. end
  679. local pname = clicker:get_player_name()
  680. minetest.log("action", "Player " .. pname .. " opens bones @ " .. minetest.pos_to_string(pos) .. ".")
  681. local spos = pos.x .. "," .. pos.y .. "," .. pos.z
  682. local formspec =
  683. "size[8,9]" ..
  684. default.gui_bg ..
  685. default.gui_bg_img ..
  686. default.gui_slots ..
  687. "list[nodemeta:" .. spos .. ";main;0,0.3;8,4;]" ..
  688. "list[current_player;main;0,4.85;8,1;]" ..
  689. "list[current_player;main;0,6.08;8,3;8]" ..
  690. "listring[nodemeta:" .. spos .. ";main]" ..
  691. "listring[current_player;main]" ..
  692. default.get_hotbar_bg(0,4.85)
  693. minetest.show_formspec(pname, "bones:main", formspec)
  694. end
  695. function bones.kill_bully_on_leaveplayer(pref, timeout)
  696. end