init.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  1. if not minetest.global_exists("itempickup") then itempickup = {} end
  2. itempickup.modpath = minetest.get_modpath("itempickup")
  3. -- Localize vector.distance() for performance.
  4. local vector_distance = vector.distance
  5. local math_random = math.random
  6. local math_min = math.min
  7. local math_max = math.max
  8. local abs = math.abs
  9. -- custom particle effects
  10. local function effect(pos, amount, texture, min_size, max_size, radius, gravity, glow)
  11. radius = radius or 2
  12. min_size = min_size or 2.0
  13. max_size = max_size or 6.0
  14. gravity = gravity or -10
  15. glow = glow or 0
  16. minetest.add_particlespawner({
  17. amount = amount,
  18. time = 0.25,
  19. minpos = pos,
  20. maxpos = pos,
  21. minvel = {x = -radius, y = -radius, z = -radius},
  22. maxvel = {x = radius, y = radius, z = radius},
  23. minacc = {x = 0, y = gravity, z = 0},
  24. maxacc = {x = 0, y = gravity, z = 0},
  25. minexptime = 0.1,
  26. maxexptime = 1,
  27. minsize = min_size,
  28. maxsize = max_size,
  29. texture = texture,
  30. glow = glow,
  31. })
  32. end
  33. itempickup.sound = function(pos)
  34. ambiance.sound_play("itempickup_pickup", pos, 0.07, 20)
  35. end
  36. local pickuptimer = 0
  37. itempickup.update = function(dtime)
  38. -- Time delay 0.5 second.
  39. pickuptimer = pickuptimer + dtime
  40. if pickuptimer < 0.5 then
  41. return
  42. end
  43. pickuptimer = 0
  44. --collectgarbage("step") -- Do not enable - causes huge issues.
  45. local players = minetest.get_connected_players()
  46. for k, v in ipairs(players) do
  47. rc.check_position(v) -- Check position before calling `get_objects_inside_radius'.
  48. local pname = v:get_player_name()
  49. if v:is_player() and v:get_hp() > 0 and not gdac_invis.is_invisible(pname) then
  50. -- Basic range, when player is standing still.
  51. local range = 0
  52. local sneak = false
  53. local control = v:get_player_control()
  54. -- Sneaking increases pickup range.
  55. if control.sneak or control.aux1 then
  56. range = 3.5
  57. sneak = true
  58. end
  59. -- Range is increased a bit when digging.
  60. -- This is because items are dropped when nodes are dug,
  61. -- but it would be too annoying if even nodes right next
  62. -- to the player were dropped on the ground.
  63. if control.LMB then
  64. range = 2.5
  65. end
  66. local pos -- Initialized only if items are searched.
  67. local items = {}
  68. if range > 0 then
  69. pos = utility.get_middle_pos(v:get_pos())
  70. items = minetest.get_objects_inside_radius(pos, range)
  71. end
  72. local inv
  73. if #items > 0 then
  74. inv = v:get_inventory()
  75. end
  76. for m, n in ipairs(items) do
  77. -- Ensure object found is an item-drop.
  78. local luaent = n:get_luaentity()
  79. if not n:is_player() and luaent and luaent.name == "__builtin:item" then
  80. local name = luaent.itemstring
  81. if name ~= "" then -- Some itemstacks have empty names.
  82. -- Don't pick up drops which were dropped by the picking player,
  83. -- unless the player is holding their 'E' key.
  84. if not control.aux1 then
  85. if luaent.dropped_by and type(luaent.dropped_by) == "string" then
  86. if luaent.dropped_by == pname then
  87. goto next_drop
  88. end
  89. end
  90. end
  91. local item = ItemStack(name)
  92. local ndef = minetest.registered_items[item:get_name()]
  93. if ndef and (not ndef._no_auto_pop or control.aux1) then
  94. -- Disable pickup for items inside fire.
  95. -- Disable pickup for moving items.
  96. local ip = vector.round(n:get_pos())
  97. local np = minetest.get_node(ip).name
  98. local moving = false
  99. local vel = n:get_velocity()
  100. if abs(vel.x) > 0.01 or abs(vel.y) > 0.01 or abs(vel.z) > 0.01 then
  101. moving = true
  102. end
  103. if minetest.get_item_group(np, "fire") == 0 and not moving then
  104. -- Ensure player's inventory has enough room.
  105. if inv and inv:room_for_item("main", item) then
  106. inv:add_item("main", item)
  107. itempickup.sound(pos)
  108. luaent.itemstring = "" -- Prevents item duplication.
  109. n:remove()
  110. end
  111. end
  112. end
  113. end
  114. end
  115. ::next_drop::
  116. end
  117. end
  118. end
  119. end
  120. -- Anything not listed here is assumed to have an XP value of 0.
  121. -- Be carefull not to include nodes that can be dug over and over again.
  122. -- This is a list of *drops* that, when obtained, increase XP.
  123. local drop_xp_list = {
  124. ["akalin:lump"] = 1.0,
  125. ["alatro:lump"] = 1.0,
  126. ["arol:lump"] = 1.0,
  127. ["chromium:lump"] = 1.0,
  128. ["gems:raw_ruby"] = 10.0,
  129. ["gems:raw_emerald"] = 10.0,
  130. ["gems:raw_sapphire"] = 10.0,
  131. ["gems:raw_amethyst"] = 10.0,
  132. ["kalite:lump"] = 0.1,
  133. ["lead:lump"] = 1.0,
  134. ["default:coal_lump"] = 0.1,
  135. ["default:iron_lump"] = 1.0,
  136. ["default:copper_lump"] = 1.0,
  137. ["default:gold_lump"] = 2.0,
  138. ["default:mese_crystal"] = 2.0,
  139. ["default:diamond"] = 5.0,
  140. ["moreores:tin_lump"] = 0.5,
  141. ["moreores:silver_lump"] = 1.0,
  142. ["moreores:mithril_lump"] = 20.0,
  143. ["glowstone:glowing_dust"] = 1.0,
  144. ["quartz:quartz_crystal"] = 0.5,
  145. ["talinite:lump"] = 1.0,
  146. ["thorium:lump"] = 1.0,
  147. ["titanium:titanium"] = 1.0,
  148. ["uranium:lump"] = 1.0,
  149. ["zinc:lump"] = 1.0,
  150. ["sulfur:lump"] = 0.1,
  151. -- Naraxen resources.
  152. ["xtraores:nickel_ore"] = 7.0,
  153. ["xtraores:platinum_ore"] = 7.0,
  154. ["xtraores:palladium_ore"] = 7.0,
  155. ["xtraores:cobalt_ore"] = 7.0,
  156. }
  157. local drop_xp_multiplier = 1.0
  158. for k, v in pairs(drop_xp_list) do
  159. drop_xp_list[k] = v * drop_xp_multiplier
  160. end
  161. -- Stuff listed here is what player can actually get, if XP is near max.
  162. local drop_extra_item_list = {
  163. ["akalin:lump"] = true,
  164. ["alatro:lump"] = true,
  165. ["arol:lump"] = true,
  166. ["chromium:lump"] = true,
  167. ["gems:raw_ruby"] = true,
  168. ["gems:raw_emerald"] = true,
  169. ["gems:raw_sapphire"] = true,
  170. ["gems:raw_amethyst"] = true,
  171. ["kalite:lump"] = true,
  172. ["lead:lump"] = true,
  173. ["default:coal_lump"] = true,
  174. ["default:iron_lump"] = true,
  175. ["default:copper_lump"] = true,
  176. ["default:gold_lump"] = true,
  177. ["default:mese_crystal"] = true,
  178. ["default:diamond"] = true,
  179. ["default:mese"] = true,
  180. ["mese_crystals:mese_crystal_ore1"] = true,
  181. ["mese_crystals:mese_crystal_ore2"] = true,
  182. ["mese_crystals:mese_crystal_ore3"] = true,
  183. ["mese_crystals:mese_crystal_ore4"] = true,
  184. ["moreores:tin_lump"] = true,
  185. ["moreores:silver_lump"] = true,
  186. ["moreores:mithril_lump"] = true,
  187. ["glowstone:glowing_dust"] = true,
  188. ["quartz:quartz_crystal"] = true,
  189. ["talinite:lump"] = true,
  190. ["thorium:lump"] = true,
  191. ["titanium:titanium"] = true,
  192. ["uranium:lump"] = true,
  193. ["zinc:lump"] = true,
  194. ["sulfur:lump"] = true,
  195. -- Naraxen resources.
  196. ["xtraores:nickel_ore"] = true,
  197. ["xtraores:platinum_ore"] = true,
  198. ["xtraores:palladium_ore"] = true,
  199. ["xtraores:cobalt_ore"] = true,
  200. }
  201. -- List of nodes capable of providing extra drops.
  202. -- Player can only get extra drop for mineral XP if they dug one of these nodes.
  203. -- Also, node must be one of these in order to increase mining XP.
  204. local drop_node_list = {
  205. ["default:stone_with_coal"] = true,
  206. ["default:desert_stone_with_coal"] = true,
  207. ["default:stone_with_iron"] = true,
  208. ["default:stone_with_copper"] = true,
  209. ["default:desert_stone_with_copper"] = true,
  210. ["default:desert_stone_with_iron"] = true,
  211. ["default:desert_stone_with_diamond"] = true,
  212. ["default:stone_with_mese"] = true,
  213. ["default:stone_with_gold"] = true,
  214. ["default:stone_with_diamond"] = true,
  215. ["rackstone:redrack_with_coal"] = true,
  216. ["rackstone:redrack_with_iron"] = true,
  217. ["rackstone:redrack_with_copper"] = true,
  218. ["rackstone:redrack_with_tin"] = true,
  219. ["akalin:ore"] = true,
  220. ["alatro:ore"] = true,
  221. ["arol:ore"] = true,
  222. ["talinite:ore"] = true,
  223. ["thorium:ore"] = true,
  224. ["titanium:ore"] = true,
  225. ["uranium:ore"] = true,
  226. ["sulfur:ore"] = true,
  227. ["kalite:ore"] = true,
  228. ["chromium:ore"] = true,
  229. ["zinc:ore"] = true,
  230. ["lead:ore"] = true,
  231. ["glowstone:luxore"] = true,
  232. ["glowstone:minerals"] = true,
  233. ["glowstone:glowstone"] = true,
  234. ["moreores:mineral_tin"] = true,
  235. ["moreores:mineral_silver"] = true,
  236. ["moreores:mineral_mithril"] = true,
  237. ["gems:ruby_ore"] = true,
  238. ["gems:emerald_ore"] = true,
  239. ["gems:sapphire_ore"] = true,
  240. ["gems:amethyst_ore"] = true,
  241. ["mese_crystals:mese_crystal_ore1"] = true,
  242. ["mese_crystals:mese_crystal_ore2"] = true,
  243. ["mese_crystals:mese_crystal_ore3"] = true,
  244. ["mese_crystals:mese_crystal_ore4"] = true,
  245. ["quartz:quartz_ore"] = true,
  246. -- Naraxen ores.
  247. ["xtraores:basalt_with_nickel"] = true,
  248. ["xtraores:basalt_with_platinum"] = true,
  249. ["xtraores:basalt_with_palladium"] = true,
  250. ["xtraores:basalt_with_cobalt"] = true,
  251. ["stoneworld:basalt_with_gold"] = true,
  252. ["stoneworld:basalt_with_diamond"] = true,
  253. ["stoneworld:basalt_with_mese"] = true,
  254. ["stoneworld:basalt_with_iron"] = true,
  255. ["stoneworld:basalt_with_coal"] = true,
  256. ["stoneworld:basalt_with_copper"] = true,
  257. ["stoneworld:basalt_with_tin"] = true,
  258. }
  259. function itempickup.drop_an_item(pos, stack, digger, tool_capabilities)
  260. local pp = utility.get_middle_pos(digger:get_pos())
  261. -- Some tools always make their drops go directly to player's inventory.
  262. local direct = tool_capabilities.direct_to_inventory
  263. -- Stack goes directly into inventory if player close enough.
  264. if vector_distance(pp, pos) < 3.5 or direct then
  265. local inv = digger:get_inventory()
  266. if inv then
  267. stack = inv:add_item("main", stack)
  268. -- If stack couldn't be added because of full inventory, then material is sometimes lost.
  269. if not stack:is_empty() and math_random(0, 3) == 0 then
  270. -- Don't drop anything on the ground, 25% chance.
  271. -- Give particle feedback to player.
  272. digger:set_hp(digger:get_hp() - (1*500))
  273. effect(pos, math_random(2, 5), "tnt_smoke.png")
  274. return
  275. end
  276. end
  277. end
  278. if not stack:is_empty() then
  279. local obj = minetest.add_item(pos, stack)
  280. -- Make the drop fly a bit.
  281. if obj then
  282. obj:set_velocity({
  283. x=math_random(-10, 10) / 5,
  284. y=3,
  285. z=math_random(-10, 10) / 5,
  286. })
  287. end
  288. end
  289. end
  290. -- Table of XP gain multipliers based on tool's rank (toolranks mod).
  291. local xp_gain_ranks = {
  292. [1] = 0.1,
  293. [2] = 0.3,
  294. [3] = 0.5,
  295. [4] = 1.0,
  296. [5] = 1.1,
  297. [6] = 1.2,
  298. [7] = 1.5,
  299. }
  300. function itempickup.handle_node_drops(pos, drops, digger)
  301. -- Nil check.
  302. if not digger or not digger:is_player() then
  303. return
  304. end
  305. -- Node hasn't been removed yet, we can make use of it. GOOD!
  306. local node = minetest.get_node(pos) -- Node to be dug.
  307. local tool = digger:get_wielded_item()
  308. local tool_meta = tool:get_meta()
  309. local tool_level = tonumber(tool_meta:get_string("tr_lastlevel")) or 1
  310. local tn = tool:get_name()
  311. local xp_drop_enabled = true
  312. -- Node definition.
  313. local ndef = minetest.reg_ns_nodes[node.name] or minetest.registered_nodes[node.name]
  314. -- Nil check.
  315. if not ndef then
  316. return
  317. end
  318. local ngroups = ndef.groups or {}
  319. -- We have to get tool capabilities directly from the itemdef in order to access custom data.
  320. -- If tool capabilities are not present, use those from the HAND. Note: not doing this
  321. -- properly was the cause of an embarrassing bug where players could not get items that they
  322. -- had dug with the hand while wielding another non-tool item.
  323. local idef = tool:get_definition()
  324. if not idef then
  325. return
  326. end
  327. local tool_capabilities = idef.tool_capabilities
  328. if not tool_capabilities then
  329. tool_capabilities = tooldata["hand_hand"]
  330. assert(tool_capabilities)
  331. end
  332. if tool_capabilities.node_overrides then
  333. if tool_capabilities.node_overrides[node.name] then
  334. tool_capabilities = tool_capabilities.node_overrides[node.name]
  335. end
  336. end
  337. -- Max level (toolranks) tool gets its `max_drop_level` improved by 1!
  338. local max_drop_level = (tool_capabilities.max_drop_level or 0)
  339. if tool_level >= 7 then
  340. max_drop_level = max_drop_level + 1
  341. --minetest.log("max_drop_level increased!") -- Tested, works.
  342. end
  343. -- Player does not get node drop if tool doesn't have sufficient level.
  344. if (max_drop_level) < (ndef.groups.level or 0) then
  345. -- 1 in 4 chance player will get the node anyway.
  346. if math_random(1, 4) > 1 then
  347. -- Particle feedback to player.
  348. effect(pos, math_random(2, 5), "tnt_smoke.png")
  349. return
  350. end
  351. end
  352. -- Test tool's chance to destroy node regardless of node/tool levels.
  353. if tool_capabilities.destroy_chance then
  354. if math_random(1, 1000) < tool_capabilities.destroy_chance then
  355. -- Particle feedback to player.
  356. effect(pos, math_random(2, 5), "tnt_smoke.png")
  357. return
  358. end
  359. end
  360. -- Tool's (toolranks) rank modifies the mineral XP gain rate!
  361. local xp_gain_multiplier = (tool_capabilities.xp_gain or 1.0)
  362. if xp_gain_ranks[tool_level] then
  363. xp_gain_multiplier = (xp_gain_multiplier * xp_gain_ranks[tool_level])
  364. --minetest.log("xp gain: " .. xp_gain_multiplier) -- Tested, works!
  365. end
  366. local is_basic_tool = (tn:find("pick_") or tn:find("sword_") or tn:find("shovel_") or tn:find("axe_") or tn:find(":axe"))
  367. -- If node has a drop string/table for silver tools, override drop table.
  368. -- Player doesn't get XP for nodes dug this way, but that's good (prevents exploit).
  369. if is_basic_tool and tn:find("silver") then
  370. if ndef.silverpick_drop then
  371. local newdrop = ndef.silverpick_drop
  372. if type(newdrop) == "table" then
  373. drops = newdrop
  374. elseif type(newdrop) == "string" then
  375. drops = {newdrop}
  376. elseif type(newdrop) == "boolean" and newdrop == true then
  377. drops = {node.name}
  378. end
  379. end
  380. elseif tn:find("shears") then
  381. if ndef.shears_drop then
  382. local newdrop = ndef.shears_drop
  383. if type(newdrop) == "table" then
  384. drops = newdrop
  385. elseif type(newdrop) == "string" then
  386. drops = {newdrop}
  387. elseif type(newdrop) == "boolean" and newdrop == true then
  388. drops = {node.name}
  389. end
  390. end
  391. end
  392. --minetest.chat_send_all(dump(drops))
  393. for _, item in ipairs(drops) do
  394. local stack = ItemStack(item) -- Itemstring to itemstack.
  395. local sname = stack:get_name()
  396. -- Give drop to player, or drop on ground.
  397. itempickup.drop_an_item(pos, stack, digger, tool_capabilities)
  398. if xp_drop_enabled and drop_xp_list[sname] then
  399. local value = drop_xp_list[sname]
  400. local pname = digger:get_player_name()
  401. local digxp = xp.get_xp(pname, "digxp")
  402. -- Reward player more when Mineral XP is higher.
  403. -- But only for ore nodes.
  404. if drop_node_list[node.name] then
  405. -- Both X's should be in range [0, 1].
  406. -- If player's XP is > 1000, then clamp to 1000 for purposes of this calculation.
  407. local max = 1000
  408. local x1 = math_min(math_max(0, digxp), max) / max
  409. local x2 = math_random(0, 10000)/10000
  410. if x1*x1 >= x2 then
  411. if drop_extra_item_list[sname] then
  412. -- Give drop to player, or drop on ground.
  413. itempickup.drop_an_item(pos, stack, digger, tool_capabilities)
  414. end
  415. end
  416. end
  417. -- Increase player's XP if not at max yet.
  418. if drop_node_list[node.name] then
  419. if digxp < xp.digxp_max then
  420. digxp = digxp + (value * xp_gain_multiplier)
  421. if digxp > xp.digxp_max then
  422. digxp = xp.digxp_max
  423. end
  424. xp.set_xp(pname, "digxp", digxp)
  425. hud_clock.update_xp(pname)
  426. end
  427. end
  428. end -- If item in drop_xp list.
  429. end
  430. end
  431. -- First-time initialization code.
  432. if not itempickup.run_once then
  433. minetest.register_globalstep(function(...) itempickup.update(...) end)
  434. local name = "itempickup:core"
  435. local file = itempickup.modpath .. "/init.lua"
  436. reload.register_file(name, file, false)
  437. function minetest.handle_node_drops(pos, drops, player)
  438. return itempickup.handle_node_drops(pos, drops, player)
  439. end
  440. itempickup.run_once = true
  441. end