functions.lua 27 KB

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