init.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. -- Localize for performance.
  2. local math_floor = math.floor
  3. toolranks = toolranks or {}
  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 = "Weapon strikes"
  69. elseif tooltype == "pickaxe" then
  70. strpart = "Nodes picked"
  71. elseif tooltype == "axe" then
  72. strpart = "Nodes chopped"
  73. elseif tooltype == "shovel" then
  74. strpart = "Nodes 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. local lvl = 1
  83. -- subtract 1 from max_uses so that the tool levels up right before it is broken
  84. if uses <= max_uses-1 then
  85. lvl = 1
  86. elseif uses < max_uses*2-1 then
  87. lvl = 2
  88. elseif uses < max_uses*3-1 then
  89. lvl = 3
  90. elseif uses < max_uses*4-1 then
  91. lvl = 4
  92. elseif uses < max_uses*5-1 then
  93. lvl = 5
  94. elseif uses < max_uses*7-1 then -- level 7 requires twice the number of nodes dug as previous levels
  95. lvl = 6
  96. else
  97. lvl = 7 -- at level 7, tool wear is cut in half
  98. end
  99. if lvl < old_level then
  100. lvl = old_level
  101. end
  102. return lvl
  103. end
  104. -- API function: allow to get the rank-level of a tool,
  105. -- (or 0, if the item is not a tool).
  106. -- The lowest possible (valid) rank is 1.
  107. function toolranks.get_tool_level(item)
  108. local name = item:get_name()
  109. local count = item:get_count()
  110. if count == 1 and toolranks.tools[name] then
  111. local meta = item:get_meta()
  112. local rank = tonumber(meta:get_string("tr_lastlevel")) or 1
  113. return rank
  114. end
  115. return 0
  116. end
  117. function toolranks.new_afteruse(itemstack, user, node, digparams)
  118. -- If either is not specified, then behave like builtin.
  119. if not user or not node then
  120. itemstack:add_wear(digparams.wear)
  121. return itemstack
  122. end
  123. local pname = user:get_player_name()
  124. -- Initialize data if not already done.
  125. local pdata = players[pname]
  126. if not pdata then
  127. players[pname] = {}
  128. pdata = players[pname]
  129. -- Default values.
  130. pdata.last_node = node.name -- Name of the last node dug, used for caching.
  131. pdata.last_ignore = false
  132. local ndef = minetest.registered_nodes[node.name]
  133. if ndef then
  134. if ndef._toolranks then
  135. if ndef._toolranks.ignore then
  136. pdata.last_ignore = true
  137. end
  138. end
  139. else
  140. -- Ignore unknown nodes.
  141. pdata.last_ignore = true
  142. end
  143. end
  144. -- Get cached player data.
  145. if pdata.last_node ~= node.name then
  146. pdata.last_node = node.name
  147. pdata.last_ignore = false
  148. -- If this node is different from the last node, update cached information.
  149. local ndef = minetest.registered_nodes[node.name]
  150. if ndef then
  151. if ndef._toolranks then
  152. if ndef._toolranks.ignore then
  153. pdata.last_ignore = true
  154. end
  155. end
  156. else
  157. -- Ignore unknown nodes.
  158. pdata.last_ignore = true
  159. end
  160. end
  161. local ignore_this_node = pdata.last_ignore
  162. local itemmeta = itemstack:get_meta() -- Metadata
  163. local itemdef = itemstack:get_definition() -- Item Definition
  164. local itemdesc = itemdef.original_description -- Original Description
  165. local dugnodes = tonumber(itemmeta:get_string("tr_dug")) or 0 -- Number of nodes dug
  166. local lastlevel = tonumber(itemmeta:get_string("tr_lastlevel")) or 1 -- Level the tool had
  167. -- Only count nodes that spend the tool
  168. if not ignore_this_node then
  169. if(digparams.wear > 0) then
  170. dugnodes = dugnodes + 1
  171. itemmeta:set_string("tr_dug", dugnodes)
  172. end
  173. end
  174. -- Don't print message more than once every 5 seconds.
  175. local os_time = os.time()
  176. if os_time > (pdata.last_alert or 0) + 5 then
  177. if(itemstack:get_wear() > 60135) then
  178. minetest.chat_send_player(pname, "# Server: Your tool is about to break!")
  179. ambiance.sound_play("default_tool_breaks", user:get_pos(), 1.0, 20)
  180. pdata.last_alert = os_time
  181. end
  182. end
  183. -- pass total number of nodes (of this type) that could be dug assuming no tool repairs, as second param to this function
  184. local level = toolranks.get_level(dugnodes, math_floor(65535 / digparams.wear), lastlevel)
  185. -- New level should never be less than the old level.
  186. if lastlevel < level then
  187. local levelup_text = "# Server: Your " .. utility.get_short_desc(itemdesc) .. " just leveled up!"
  188. ambiance.sound_play("toolranks_levelup", user:get_pos(), 1.0, 20)
  189. minetest.chat_send_player(pname, levelup_text)
  190. itemmeta:set_string("tr_lastlevel", level)
  191. end
  192. local newdesc = toolranks.create_description(itemdesc, dugnodes, level)
  193. itemmeta:set_string("tr_desc", newdesc)
  194. toolranks.apply_description(itemmeta, itemdef)
  195. local wear = digparams.wear
  196. if level > 1 then
  197. wear = digparams.wear / (1 + level / 4)
  198. end
  199. --minetest.chat_send_all("wear="..wear.."Original wear: "..digparams.wear.." 1+level/4="..1+level/4)
  200. -- Uncomment for testing ^
  201. itemstack:add_wear(wear)
  202. return itemstack
  203. end
  204. function toolranks.rebuild_tool()
  205. local player = minetest.get_player_by_name("MustTest")
  206. local inv = player:get_inventory()
  207. local item = inv:get_stack("main", 1)
  208. local meta = item:get_meta()
  209. meta:set_string("tr_dug", "58194")
  210. meta:set_string("tr_lastlevel", "7")
  211. meta:set_string("en_desc", "Trusted")
  212. local newdesc = toolranks.create_description(item:get_definition().original_description, 58194, 7)
  213. meta:set_string("tr_desc", newdesc)
  214. toolranks.apply_description(meta, item:get_definition())
  215. inv:set_stack("main", 1, item)
  216. end
  217. if not toolranks.registered then
  218. local function override_item(name)
  219. --print(name)
  220. toolranks.tools[name] = true
  221. local itemdef = table.copy(minetest.registered_items[name])
  222. -- Found out by reading MT sources that these must both be nill'ed.
  223. -- Otherwise we get error: Attempt to redefine name of X to "X".
  224. itemdef.name = nil
  225. itemdef.type = nil
  226. local tr_desc = toolranks.create_description(itemdef.description, 0, 1)
  227. itemdef.original_description = itemdef.description
  228. itemdef.description = itemdef.description .. "\n\n" .. tr_desc
  229. itemdef.original_tr_description = tr_desc
  230. itemdef.after_use = function(...) return toolranks.new_afteruse(...) end
  231. minetest.override_item(name, itemdef)
  232. end
  233. override_item("default:pick_diamond")
  234. override_item("default:axe_diamond")
  235. override_item("default:shovel_diamond")
  236. override_item("default:pick_wood")
  237. --override_item("default:axe_wood")
  238. --override_item("default:shovel_wood")
  239. override_item("default:pick_steel")
  240. override_item("default:axe_steel")
  241. override_item("default:shovel_steel")
  242. override_item("default:pick_stone")
  243. override_item("default:axe_stone")
  244. override_item("default:shovel_stone")
  245. override_item("default:pick_bronze")
  246. override_item("default:axe_bronze")
  247. override_item("default:shovel_bronze")
  248. override_item("default:pick_bronze2")
  249. override_item("default:axe_bronze2")
  250. override_item("default:shovel_bronze2")
  251. override_item("default:pick_mese")
  252. override_item("default:axe_mese")
  253. override_item("default:shovel_mese")
  254. if minetest.get_modpath("moreores") then
  255. override_item("moreores:pick_mithril")
  256. override_item("moreores:axe_mithril")
  257. override_item("moreores:shovel_mithril")
  258. override_item("moreores:sword_mithril")
  259. override_item("moreores:pick_silver")
  260. override_item("moreores:axe_silver")
  261. override_item("moreores:shovel_silver")
  262. override_item("moreores:sword_silver")
  263. end
  264. -- add swords for snappy nodes
  265. --override_item("default:sword_wood")
  266. override_item("default:sword_stone")
  267. override_item("default:sword_steel")
  268. override_item("default:sword_bronze")
  269. override_item("default:sword_bronze2")
  270. override_item("default:sword_mese")
  271. override_item("default:sword_diamond")
  272. if minetest.get_modpath("titanium") then
  273. override_item("titanium:sword")
  274. override_item("titanium:axe")
  275. override_item("titanium:shovel")
  276. override_item("titanium:pick")
  277. end
  278. if minetest.get_modpath("gem_tools") then
  279. override_item("gems:sword_emerald")
  280. override_item("gems:axe_emerald")
  281. override_item("gems:shovel_emerald")
  282. override_item("gems:pick_emerald")
  283. override_item("gems:sword_ruby")
  284. override_item("gems:axe_ruby")
  285. override_item("gems:shovel_ruby")
  286. override_item("gems:pick_ruby")
  287. override_item("gems:sword_amethyst")
  288. override_item("gems:axe_amethyst")
  289. override_item("gems:shovel_amethyst")
  290. override_item("gems:pick_amethyst")
  291. override_item("gems:sword_sapphire")
  292. override_item("gems:axe_sapphire")
  293. override_item("gems:shovel_sapphire")
  294. override_item("gems:pick_sapphire")
  295. override_item("gems:rf_sword_emerald")
  296. override_item("gems:rf_axe_emerald")
  297. override_item("gems:rf_shovel_emerald")
  298. override_item("gems:rf_pick_emerald")
  299. override_item("gems:rf_sword_ruby")
  300. override_item("gems:rf_axe_ruby")
  301. override_item("gems:rf_shovel_ruby")
  302. override_item("gems:rf_pick_ruby")
  303. override_item("gems:rf_sword_amethyst")
  304. override_item("gems:rf_axe_amethyst")
  305. override_item("gems:rf_shovel_amethyst")
  306. override_item("gems:rf_pick_amethyst")
  307. override_item("gems:rf_sword_sapphire")
  308. override_item("gems:rf_axe_sapphire")
  309. override_item("gems:rf_shovel_sapphire")
  310. override_item("gems:rf_pick_sapphire")
  311. end
  312. local c = "toolranks:core"
  313. local f = toolranks.modpath .. "/init.lua"
  314. reload.register_file(c, f, false)
  315. toolranks.registered = true
  316. end