init.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. -- Localize for performance.
  2. local math_floor = math.floor
  3. if not minetest.global_exists("toolranks") then toolranks = {} end
  4. toolranks.modpath = minetest.get_modpath("toolranks")
  5. toolranks.players = toolranks.players or {}
  6. toolranks.tools = toolranks.tools or {}
  7. local players = toolranks.players
  8. if not toolranks.mod_storage then
  9. toolranks.mod_storage = minetest.get_mod_storage()
  10. end
  11. local mod_storage = toolranks.mod_storage
  12. toolranks.colors = {
  13. grey = minetest.get_color_escape_sequence("#9d9d9d"),
  14. green = minetest.get_color_escape_sequence("#1eff00"),
  15. gold = minetest.get_color_escape_sequence("#ffdf00"),
  16. white = minetest.get_color_escape_sequence("#ffffff")
  17. }
  18. function toolranks.get_tool_type(description)
  19. if string.find(description, "Pickaxe") then
  20. return "pickaxe"
  21. elseif string.find(description, "Axe") then
  22. return "axe"
  23. elseif string.find(description, "Shovel") then
  24. return "shovel"
  25. elseif string.find(description, "Hoe") then
  26. return "hoe"
  27. elseif string.find(description, "Sword") then
  28. return "sword"
  29. else
  30. return "tool"
  31. end
  32. end
  33. -- Used to apply multiple descriptions into a single description.
  34. -- Any mod wanting to change a tool's description meta should go through this.
  35. function toolranks.apply_description(itemmeta, def)
  36. local desc1 = itemmeta:get_string("en_desc") or ""
  37. local desc2 = itemmeta:get_string("tr_desc") or ""
  38. local desc3 = itemmeta:get_string("ar_desc") or ""
  39. if desc1 == "" then
  40. -- No custom description set by engraver, etc.?
  41. -- Use original description.
  42. desc1 = def.original_description or ""
  43. if desc1 == "" then
  44. desc1 = def.description or ""
  45. end
  46. end
  47. if desc2 == "" then
  48. -- Nothing assigned by TR yet? Use default TR desc, if any.
  49. desc2 = def.original_tr_description or ""
  50. end
  51. if desc3 ~= "" then
  52. desc3 = "Loaded: " .. desc3
  53. end
  54. if desc2 ~= "" then
  55. -- Have toolranks description? Seperate with newlines.
  56. desc1 = desc1 .. "\n\n"
  57. end
  58. if desc3 ~= "" then
  59. desc2 = desc2 .. "\n\n"
  60. end
  61. itemmeta:set_string("description", desc1 .. desc2 .. desc3)
  62. end
  63. function toolranks.create_description(name, uses, level)
  64. local description = utility.get_short_desc(name)
  65. local tooltype = toolranks.get_tool_type(description)
  66. local strpart = "Nodes dug"
  67. if tooltype == "sword" then
  68. strpart = "Blows struck"
  69. elseif tooltype == "pickaxe" then
  70. strpart = "Resources picked"
  71. elseif tooltype == "axe" then
  72. strpart = "Resources chopped"
  73. elseif tooltype == "shovel" then
  74. strpart = "Resources shoveled"
  75. end
  76. local newdesc = toolranks.colors.green .. description .. "\n" ..
  77. toolranks.colors.gold .. "Level " .. (level or 1) .. " " .. tooltype .. "\n" ..
  78. toolranks.colors.grey .. strpart .. ": " .. (uses or 0)
  79. return newdesc
  80. end
  81. function toolranks.get_level(uses, max_uses, old_level)
  82. -- Uncomment this to enable rapidly testing the tool-rank leveling function.
  83. --max_uses = 5
  84. local lvl = 1
  85. -- subtract 1 from max_uses so that the tool levels up right before it is broken
  86. if uses <= max_uses-1 then
  87. lvl = 1
  88. elseif uses < max_uses*2-1 then
  89. lvl = 2
  90. elseif uses < max_uses*3-1 then
  91. lvl = 3
  92. elseif uses < max_uses*4-1 then
  93. lvl = 4
  94. elseif uses < max_uses*5-1 then
  95. lvl = 5
  96. elseif uses < max_uses*7-1 then -- level 7 requires twice the number of nodes dug as previous levels
  97. lvl = 6
  98. else
  99. lvl = 7 -- at level 7, tool wear is cut in half
  100. end
  101. if lvl < old_level then
  102. lvl = old_level
  103. end
  104. return lvl
  105. end
  106. -- API function: allow to get the rank-level of a tool,
  107. -- (or 0, if the item is not a tool).
  108. -- The lowest possible (valid) rank is 1.
  109. function toolranks.get_tool_level(item)
  110. local name = item:get_name()
  111. local count = item:get_count()
  112. if count == 1 and toolranks.tools[name] then
  113. local meta = item:get_meta()
  114. local rank = tonumber(meta:get_string("tr_lastlevel")) or 1
  115. return rank
  116. end
  117. return 0
  118. end
  119. function toolranks.new_afteruse(itemstack, user, node, digparams)
  120. -- If either is not specified, then behave like builtin.
  121. if not user or not node then
  122. itemstack:add_wear(digparams.wear)
  123. return itemstack
  124. end
  125. local pname = user:get_player_name()
  126. -- Initialize data if not already done.
  127. local pdata = players[pname]
  128. if not pdata then
  129. players[pname] = {}
  130. pdata = players[pname]
  131. -- Default values.
  132. pdata.last_node = node.name -- Name of the last node dug, used for caching.
  133. pdata.last_ignore = false
  134. local ndef = minetest.registered_nodes[node.name]
  135. if ndef then
  136. if ndef._toolranks then
  137. if ndef._toolranks.ignore then
  138. pdata.last_ignore = true
  139. end
  140. end
  141. else
  142. -- Ignore unknown nodes.
  143. pdata.last_ignore = true
  144. end
  145. end
  146. -- Get cached player data.
  147. if pdata.last_node ~= node.name then
  148. pdata.last_node = node.name
  149. pdata.last_ignore = false
  150. -- If this node is different from the last node, update cached information.
  151. local ndef = minetest.registered_nodes[node.name]
  152. if ndef then
  153. if ndef._toolranks then
  154. if ndef._toolranks.ignore then
  155. pdata.last_ignore = true
  156. end
  157. end
  158. else
  159. -- Ignore unknown nodes.
  160. pdata.last_ignore = true
  161. end
  162. end
  163. local ignore_this_node = pdata.last_ignore
  164. local itemmeta = itemstack:get_meta() -- Metadata
  165. local itemdef = itemstack:get_definition() -- Item Definition
  166. local itemdesc = itemdef.original_description -- Original Description
  167. local dugnodes = tonumber(itemmeta:get_string("tr_dug")) or 0 -- Number of nodes dug
  168. local lastlevel = tonumber(itemmeta:get_string("tr_lastlevel")) or 1 -- Level the tool had
  169. -- Only count nodes that spend the tool
  170. if not ignore_this_node then
  171. if(digparams.wear > 0) then
  172. dugnodes = dugnodes + 1
  173. itemmeta:set_string("tr_dug", dugnodes)
  174. end
  175. end
  176. -- Don't print message more than once every 5 seconds.
  177. local os_time = os.time()
  178. if os_time > (pdata.last_alert or 0) + 5 then
  179. if(itemstack:get_wear() > 60135) then
  180. minetest.chat_send_player(pname, "# Server: Your tool is about to break!")
  181. ambiance.sound_play("default_tool_breaks", user:get_pos(), 1.0, 20)
  182. pdata.last_alert = os_time
  183. end
  184. end
  185. -- pass total number of nodes (of this type) that could be dug assuming no tool repairs, as second param to this function
  186. local level = toolranks.get_level(dugnodes, math_floor(65535 / digparams.wear), lastlevel)
  187. -- New level should never be less than the old level.
  188. if lastlevel < level then
  189. local levelup_text = "# Server: Your " .. utility.get_short_desc(itemdesc) .. " just leveled up!"
  190. ambiance.sound_play("toolranks_levelup", user:get_pos(), 1.0, 20)
  191. minetest.chat_send_player(pname, levelup_text)
  192. itemmeta:set_string("tr_lastlevel", level)
  193. end
  194. local newdesc = toolranks.create_description(itemdesc, dugnodes, level)
  195. itemmeta:set_string("tr_desc", newdesc)
  196. toolranks.apply_description(itemmeta, itemdef)
  197. local wear = digparams.wear
  198. if level > 1 then
  199. wear = digparams.wear / (1 + level / 4)
  200. end
  201. --minetest.chat_send_all("wear="..wear.."Original wear: "..digparams.wear.." 1+level/4="..1+level/4)
  202. -- Uncomment for testing ^
  203. itemstack:add_wear(wear)
  204. return itemstack
  205. end
  206. -- This code was used at one time to replace a valuable tool someone had lost
  207. -- due to a bug.
  208. --[[
  209. function toolranks.rebuild_tool()
  210. local player = minetest.get_player_by_name("MustTest")
  211. local inv = player:get_inventory()
  212. local item = inv:get_stack("main", 1)
  213. local meta = item:get_meta()
  214. meta:set_string("tr_dug", "58194")
  215. meta:set_string("tr_lastlevel", "7")
  216. meta:set_string("en_desc", "Trusted")
  217. local newdesc = toolranks.create_description(item:get_definition().original_description, 58194, 7)
  218. meta:set_string("tr_desc", newdesc)
  219. toolranks.apply_description(meta, item:get_definition())
  220. inv:set_stack("main", 1, item)
  221. end
  222. --]]
  223. if not toolranks.registered then
  224. local function override_item(name)
  225. --print(name)
  226. toolranks.tools[name] = true
  227. local itemdef = table.copy(minetest.registered_items[name])
  228. -- Found out by reading MT sources that these must both be nill'ed.
  229. -- Otherwise we get error: Attempt to redefine name of X to "X".
  230. itemdef.name = nil
  231. itemdef.type = nil
  232. local tr_desc = toolranks.create_description(itemdef.description, 0, 1)
  233. itemdef.original_description = itemdef.description
  234. itemdef.description = itemdef.description .. "\n\n" .. tr_desc
  235. itemdef.original_tr_description = tr_desc
  236. itemdef.after_use = function(...) return toolranks.new_afteruse(...) end
  237. minetest.override_item(name, itemdef)
  238. end
  239. override_item("default:pick_diamond")
  240. override_item("default:axe_diamond")
  241. override_item("default:shovel_diamond")
  242. override_item("default:pick_wood")
  243. --override_item("default:axe_wood")
  244. --override_item("default:shovel_wood")
  245. override_item("default:pick_steel")
  246. override_item("default:axe_steel")
  247. override_item("default:shovel_steel")
  248. override_item("default:pick_stone")
  249. override_item("default:axe_stone")
  250. override_item("default:shovel_stone")
  251. override_item("default:pick_bronze")
  252. override_item("default:axe_bronze")
  253. override_item("default:shovel_bronze")
  254. override_item("default:pick_bronze2")
  255. override_item("default:axe_bronze2")
  256. override_item("default:shovel_bronze2")
  257. override_item("default:pick_mese")
  258. override_item("default:axe_mese")
  259. override_item("default:shovel_mese")
  260. if minetest.get_modpath("moreores") then
  261. override_item("moreores:pick_mithril")
  262. override_item("moreores:axe_mithril")
  263. override_item("moreores:shovel_mithril")
  264. override_item("moreores:sword_mithril")
  265. override_item("moreores:pick_silver")
  266. override_item("moreores:axe_silver")
  267. override_item("moreores:shovel_silver")
  268. override_item("moreores:sword_silver")
  269. end
  270. -- add swords for snappy nodes
  271. --override_item("default:sword_wood")
  272. override_item("default:sword_stone")
  273. override_item("default:sword_steel")
  274. override_item("default:sword_bronze")
  275. override_item("default:sword_bronze2")
  276. override_item("default:sword_mese")
  277. override_item("default:sword_diamond")
  278. if minetest.get_modpath("titanium") then
  279. override_item("titanium:sword")
  280. override_item("titanium:axe")
  281. override_item("titanium:shovel")
  282. override_item("titanium:pick")
  283. end
  284. if minetest.get_modpath("gem_tools") then
  285. override_item("gems:sword_emerald")
  286. override_item("gems:axe_emerald")
  287. override_item("gems:shovel_emerald")
  288. override_item("gems:pick_emerald")
  289. override_item("gems:sword_ruby")
  290. override_item("gems:axe_ruby")
  291. override_item("gems:shovel_ruby")
  292. override_item("gems:pick_ruby")
  293. override_item("gems:sword_amethyst")
  294. override_item("gems:axe_amethyst")
  295. override_item("gems:shovel_amethyst")
  296. override_item("gems:pick_amethyst")
  297. override_item("gems:sword_sapphire")
  298. override_item("gems:axe_sapphire")
  299. override_item("gems:shovel_sapphire")
  300. override_item("gems:pick_sapphire")
  301. override_item("gems:rf_sword_emerald")
  302. override_item("gems:rf_axe_emerald")
  303. override_item("gems:rf_shovel_emerald")
  304. override_item("gems:rf_pick_emerald")
  305. override_item("gems:rf_sword_ruby")
  306. override_item("gems:rf_axe_ruby")
  307. override_item("gems:rf_shovel_ruby")
  308. override_item("gems:rf_pick_ruby")
  309. override_item("gems:rf_sword_amethyst")
  310. override_item("gems:rf_axe_amethyst")
  311. override_item("gems:rf_shovel_amethyst")
  312. override_item("gems:rf_pick_amethyst")
  313. override_item("gems:rf_sword_sapphire")
  314. override_item("gems:rf_axe_sapphire")
  315. override_item("gems:rf_shovel_sapphire")
  316. override_item("gems:rf_pick_sapphire")
  317. end
  318. local c = "toolranks:core"
  319. local f = toolranks.modpath .. "/init.lua"
  320. reload.register_file(c, f, false)
  321. toolranks.registered = true
  322. end