API.lua 60 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403
  1. -- liblevelup mod for Minetest
  2. -- Copyright © 2017-2021 Alex Yst <mailto:copyright@y.st>
  3. -- This program is free software; you can redistribute it and/or
  4. -- modify it under the terms of the GNU Lesser General Public
  5. -- License as published by the Free Software Foundation; either
  6. -- version 2.1 of the License, or (at your option) any later version.
  7. -- This software is distributed in the hope that it will be useful,
  8. -- but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  10. -- Lesser General Public License for more details.
  11. -- You should have received a copy of the GNU Lesser General Public
  12. -- License along with this program. If not, see
  13. -- <https://www.gnu.org./licenses/>.
  14. -----------------------------------------------------------------------
  15. ----------------- Deprecated mod name database import -----------------
  16. -----------------------------------------------------------------------
  17. -- liblevelup used to be called "minestats". If the older database
  18. -- under the deprecated name still exists, we should import that to the
  19. -- new database. Usually, I keep all code related to supporting
  20. -- deprecated features at the end of the file, but this database import
  21. -- has to come before everything else. Otherwise, when liblevelup
  22. -- requests its storage object, the data the storage object contains
  23. -- will be incorrect.
  24. --
  25. -- Legacy support in this mod is only enabled when overall Minetest
  26. -- legacy support is enabled in minetest.conf. By default, legacy
  27. -- support is enabled for release versions but not development
  28. -- versions.
  29. --
  30. -- Deprecated on 2020-02-29; DO NOT REMOVE FOR AT LEAST TWO YEARS.
  31. if minetest.settings:get("deprecated_lua_api_handling") ~= "error" then
  32. local liblevelup_database = io.open(minetest.get_worldpath().."/mod_storage/liblevelup", "r")
  33. if liblevelup_database then
  34. liblevelup_database:close()
  35. else
  36. local minestats_database = io.open(minetest.get_worldpath().."/mod_storage/minestats", "r")
  37. if minestats_database then
  38. local liblevelup_database = io.open(minetest.get_worldpath().."/mod_storage/liblevelup", "w")
  39. liblevelup_database:write(minestats_database:read())
  40. liblevelup_database:close()
  41. minestats_database:close()
  42. end
  43. end
  44. end
  45. -----------------------------------------------------------------------
  46. ------------------------- Internal Variables: -------------------------
  47. -----------------------------------------------------------------------
  48. -- Anything in this table is a countable node/drop pair. Anything else
  49. -- is, well, not.
  50. --
  51. -- Third-party mod developers need not worry about the structure of
  52. -- this table because the table's not made public. However, if you're
  53. -- editing this file's code, the structure is:
  54. --
  55. -- registered_countables[node_name][drop_key(drop_list)] == true
  56. --
  57. -- A single countable node might be able to drop multiple countable
  58. -- drops, so this table structure allows us to account for that fact.
  59. local registered_countables = {}
  60. -- We need a way to store our data, so let's try this:
  61. local storage = minetest.get_mod_storage()
  62. -- This table is used internally to keep track of the alphabetical
  63. -- order of all registered drops.
  64. local sorted_drops = {}
  65. -- This is the maximum number of items that can be in a Minetest
  66. -- ItemStack (16 bits, unsigned). I know the value off the top of my
  67. -- head because I use it so often, to be honest, but assigning it a
  68. -- variable name makes it more clear to readers of the code why that
  69. -- value is even there. Like, where did 65535 come from? Well, here you
  70. -- go. It's used in a few places due to being the most items that can
  71. -- ever fit into a stack of items in Minetest.
  72. local max_stack_size = 65535
  73. -- This table contains all the functions registered to run once
  74. -- liblevelup has been initialised.
  75. local startup_functions = {}
  76. -- This table contains all the functions to be called whenever a stat
  77. -- is updated.
  78. local update_functions = {}
  79. -- This table contains all the functions to be called whenever a
  80. -- level-up occurs.
  81. local levelup_functions = {}
  82. -- A third-party mod may tell liblevelup that a particular node drop is
  83. -- countable. The functions registered by mods to check each drop are
  84. -- stored in this table.
  85. local is_countable_callbacks = {}
  86. -- When the determining proficiency level of a player with a given
  87. -- material, that material's own levelling curve needs to be accounted
  88. -- for. In the majority of cases, a material's levelling exponent is
  89. -- 0.414335358827271738046960081192082725465297698974609375. Yes, the
  90. -- exponent is *that* finely tuned. That aside though, not all
  91. -- materials have to have this specific levelling curve, so this table
  92. -- keeps track of the levelling curves of counted materials. It gets
  93. -- populated just before the game begins.
  94. local levelling_exponent = {}
  95. -- The reverse levelling exponent is used in determining how many
  96. -- stacks of a material are needed to get to a specific level, instead
  97. -- of determining what level you are at given a specific number of
  98. -- stacks. It's used to build "next level at" counters.
  99. local reverse_levelling_exponent = {}
  100. -- Like the table above, this table deals with levelling mechanics and
  101. -- is populated just before the game begins. The the maximum stack size
  102. -- for each material is usually 99, but again, we shouldn't assume that
  103. -- limit and use this table so we don't have to.
  104. local drop_max_stack_size = {}
  105. -- Level calculation's more costly than I'd like it to be. Let's just
  106. -- cache the most expensive-to-compute parts. But also, we want to be
  107. -- able to compare new values to old values to see if there was a
  108. -- change, and the cache lets us do that.
  109. local unscaled_level_cache = {}
  110. -- The scaled levels are pretty costly too, and the full set of scaled
  111. -- levels have to be calculated every single time the player collects a
  112. -- drop from a node even if that node only dropped something related
  113. -- to one of the levels. We might as well cache the levels at that time
  114. -- to avoid recalculating them when other mods query these levels.
  115. local scaled_level_cache = {}
  116. -- If a craft-predicting function returns an empty ItemStack, Minetest
  117. -- now disables the craft. This means we can prevent items from being
  118. -- crafted based on arbitrary criteria. The keys in this table
  119. -- represent items that the player isn't allowed to craft unless
  120. -- they've met certain level requirements, and the values are tables of
  121. -- level requirements with the drop the player must have levels in
  122. -- being the keys and the level in that particular drop being the
  123. -- values.
  124. local craft_locks = {}
  125. -- In implementing craft locks, we send messages to players that
  126. -- haven't met the unlocking requirements, so we should probably be
  127. -- sending translated strings.
  128. local S = minetest.get_translator("liblevelup")
  129. -----------------------------------------------------------------------
  130. ------------------------- Internal functions: -------------------------
  131. -----------------------------------------------------------------------
  132. -- This method normalises drops so we can better handle them.
  133. -- Sometimes, a node will drop multiple stacks of the same item or will
  134. -- drop unnormalised stacks, such as in the form of legacy item
  135. -- strings. This function lets us properly handle these cases.
  136. local function normalise_drops(drops)
  137. local by_name = {}
  138. local normalised = {}
  139. for _, item in next, drops do
  140. local stack = ItemStack(item)
  141. if not stack:is_empty() then
  142. local count = stack:get_count()
  143. stack:set_count(1)
  144. local name = stack:to_string()
  145. if by_name[name] then
  146. by_name[name] = by_name[name] + count
  147. else
  148. by_name[name] = count
  149. end
  150. end
  151. end
  152. -- We need to return drops in a consistent order for comparison later
  153. -- in the script.
  154. local sorted = {}
  155. for name, _ in next, by_name do
  156. sorted[#sorted+1] = name
  157. end
  158. table.sort(sorted)
  159. for _, name in next, sorted do
  160. local count = by_name[name]
  161. if ItemStack(name):get_definition().type == "tool" then
  162. -- The engine doesn't allow stacking multiple tools, even via code in a
  163. -- mod. Tools just simply cannot be stacked. Ever. If we try to stack
  164. -- the tools, we'll end up deleting all but one of them.
  165. while count > 0 do
  166. normalised[#normalised+1] = name
  167. count = count - 1
  168. end
  169. else
  170. -- A node might drop so many items that we can't store them all in a
  171. -- stack. In practice, this should never happen, but we'd be fools to
  172. -- fail to account for this possibility.
  173. while count > max_stack_size do
  174. normalised[#normalised+1] = ItemStack(name.." "..max_stack_size):to_string()
  175. count = count - max_stack_size
  176. end
  177. end
  178. -- Who knows how many bugs converting to an ItemStack and back fixes?
  179. -- The need for this process was discovered because of the common
  180. -- possibility that an item drop contains a stack of a single item.
  181. -- In the original code, we simply append the size of the stack to the
  182. -- ID of the item. In cases of one item, that's incorrect behaviour and
  183. -- results in unnormalised item strings. We could account for
  184. -- single-item stacks manually, but there's a chance I messed something
  185. -- else up along the way too. Converting to an ItemStack and back
  186. -- ensures that the item string is in the canonically-normalised form
  187. -- as defined by the engine.
  188. --
  189. -- I'm almost certain another bug is present somewhere, too. Flint and
  190. -- cotton seeds from Minetest Game weren't displaying in the stats
  191. -- menu, along with potentially other items. Converting to an ItemStack
  192. -- and back mysteriously fixed that though.
  193. normalised[#normalised+1] = ItemStack(name.." "..count):to_string()
  194. end
  195. return normalised
  196. end
  197. -- Checking to see if a given list of items would be counted as a drop
  198. -- requires putting the entire drop list into a single string to check
  199. -- against a table.
  200. local function drop_key(drop_list)
  201. -- Multiple stacks in the same drop list might be the same type of
  202. -- item, which should be treated the same as if the same quantity of
  203. -- items in the drop list were present but as a single stack. Stack
  204. -- order also shouldn't influence how drop lists are treated, so we
  205. -- need to sort the drop list. Normalisation of the drop list takes
  206. -- care of both.
  207. local drops = normalise_drops(drop_list)
  208. -- Te list could be empty. If so, the list isn't counted. Return an
  209. -- empty string anyway instead of nil to avoid errors caused by
  210. -- assuming the return value of this function is a string. If not,
  211. -- build the table key needed to check.
  212. if drops[1] then
  213. local key = drops[1]
  214. if drops[2] then
  215. for i = 2, #drops do
  216. key = key..";"..drops[i]
  217. end
  218. end
  219. return key
  220. else
  221. return ""
  222. end
  223. end
  224. -- Need to know if something's counted? Look no further! Keep in mind
  225. -- that drops are counted based on context, so the full drop list needs
  226. -- to be passed to this function.
  227. local function is_counted(node_name, drop_list)
  228. local key = drop_key(drop_list)
  229. if registered_countables[node_name]
  230. and registered_countables[node_name][key] then
  231. return true
  232. else
  233. return false
  234. end
  235. end
  236. -- This method simply gets a string from the database and casts it to
  237. -- an integer. All other functions that get data from the database
  238. -- aren't interacting with the database directly, but are merely
  239. -- calling this function and using the return value in some way.
  240. local function get_stat(key)
  241. -- Sometimes tonumber() returns nil instead of a number.
  242. return tonumber(storage:get_string(key)) or 0
  243. end
  244. -- This method returns a stat in terms of how many of an item have been
  245. -- harvested, regardless of number dropped in a single dig and
  246. -- regardless of what node dropped it.
  247. local function get_drop_stat(player_name, drop)
  248. return get_stat(player_name..";"..drop)
  249. end
  250. -- This method returns the number of stacks of the drop a player has
  251. -- mined. Fractional values are returned, to allow for levels to
  252. -- account for everything a player has mined in a unified way.
  253. local function get_stacks_mined(player_name, drop_name)
  254. return get_drop_stat(player_name, drop_name) / drop_max_stack_size[drop_name]
  255. end
  256. -- We'll overwrite these functions later in the script (see "External
  257. -- API"), so we need to create local copies of the old versions to call
  258. -- from our new versions.
  259. local builtin_get_node_drops = minetest.get_node_drops
  260. -- This second function needs to be copied after the game begins
  261. -- though, not now. That makes it more likely our version is the
  262. -- outermost version of the function. If ours is not the outermost one,
  263. -- we can't even make sure our version is called at all. Specifically,
  264. -- Minetest Game's creative mod overwrites the node drop handler (iff
  265. -- creative mode is enabled) and doesn't call the previous version of
  266. -- it. If we don't wait to put our own version of the function in
  267. -- place, liblevelup will be incompatible with the Minetest Game
  268. -- creative mod.
  269. local builtin_handle_node_drops
  270. -- Actually solving this problem is incredibly difficult, and I don't
  271. -- know how to do it yet. For now, this function is dummied out.
  272. --
  273. -- Eventually, this function should verify that there could exist a
  274. -- string that matches all patterns and doesn't match any
  275. -- "antipatterns".
  276. local function vague_tool_is_consistent(drop_possibility)
  277. return true
  278. end
  279. -- The next two functions actually both need to be able to call one
  280. -- another, so the second function's variable needs to be declared
  281. -- local before the first function is defined. Otherwise, when calling
  282. -- the second function from within the first, Lua will try and fail to
  283. -- find the function using the global variable name.
  284. local handle_affirmative_drop_state
  285. -- Recursion seems sort of like overkill for the problem at hand, but
  286. -- I can't seem to figure out what else can do the job. Anyway, this
  287. -- function takes a drop table from a node definition and spits out a
  288. -- table of tables, with each inner table representing a list of items
  289. -- that the node could drop if the node is dug. All possibilities are
  290. -- enumerated, but their exact probabilities are not calculated.
  291. local function drop_possibilities(node_drop_table, palette_index, return_table, current_state)
  292. if type(node_drop_table) == "string" then
  293. return_table[1] = {node_drop_table}
  294. elseif not node_drop_table.items then
  295. return_table[1] = {}
  296. else
  297. if current_state.drop_index > #node_drop_table.items
  298. or current_state.number_drops == node_drop_table.max_items then
  299. if current_state.tool
  300. or vague_tool_is_consistent(node_drop_table) then
  301. return_table[#return_table+1] = current_state.drops
  302. end
  303. else
  304. -- This value may be updated before this variable is checked.
  305. local might_not_drop = false
  306. local dropped_state = table.copy(current_state)
  307. dropped_state.drop_index = dropped_state.drop_index + 1
  308. local undropped_state = table.copy(dropped_state)
  309. if node_drop_table.items[current_state.drop_index].tools then
  310. -- If the item definitely drops with the right tool, we have to handle
  311. -- it here. Otherwise, if there's a chance the item wouldn't drop even
  312. -- *with* the right tool, no special treatment is needed. We'll just
  313. -- consider the chance that luck wasn't on the player's side.
  314. if not node_drop_table.items[current_state.drop_index].rarity
  315. or node_drop_table.items[current_state.drop_index].rarity <= 1 then
  316. might_not_drop = true
  317. -- We're holding a specific tool. Compare the tool to the list of tool
  318. -- names and tool name patterns that allow the drop to occur.
  319. if undropped_state.tool then
  320. for _, disallowed_tool in next, node_drop_table.items[current_state.drop_index].tools do
  321. if disallowed_tool:sub(1, 1) == "~" then
  322. if undropped_state.tool:find(disallowed_tool:sub(2)) ~= nil then
  323. might_not_drop = false
  324. end
  325. else
  326. if disallowed_tool == undropped_state.tool then
  327. might_not_drop = false
  328. end
  329. end
  330. end
  331. -- We have limited information on the tool we're holding. Check to see
  332. -- if there are any direct conflicts that would prevent us from missing
  333. -- this drop.
  334. else
  335. -- See if we can miss the drop.
  336. for _, pattern in next, node_drop_table.items[current_state.drop_index].tools do
  337. if undropped_state.toolname_patterns[pattern] then
  338. might_not_drop = false
  339. end
  340. end
  341. -- If we can, add the list of tool patterns that would cause the drop
  342. -- to the list of patterns that don't match the tool we're holding.
  343. if not might_not_drop then
  344. for _, pattern in next, node_drop_table.items[current_state.drop_index].tools do
  345. undropped_state.toolname_antipatterns[pattern] = true
  346. end
  347. end
  348. end
  349. end
  350. end
  351. -- If the item is dropped based on probability, we might not get the
  352. -- drop. It's as simple as that.
  353. if node_drop_table.items[current_state.drop_index].rarity
  354. and node_drop_table.items[current_state.drop_index].rarity > 1 then
  355. might_not_drop = true
  356. end
  357. -- We fork the state here if there's a chance the next set of items
  358. -- might not be dropped.
  359. if might_not_drop then
  360. drop_possibilities(node_drop_table, palette_index, return_table, undropped_state)
  361. end
  362. -- Done forking, let's continue. This branch accounts for if the set of
  363. -- items did drop after all.
  364. dropped_state.number_drops = dropped_state.number_drops + 1
  365. -- First of all, *can* the item drop after all? If we're holding the
  366. -- wrong tool, maybe not.
  367. if node_drop_table.items[current_state.drop_index].tools then
  368. -- If we know what tool we're holding, check it against the list of
  369. -- tool patterns that trigger the drop.
  370. if dropped_state.tool then
  371. local might_drop = false
  372. for _, pattern in next, node_drop_table.items[current_state.drop_index].tools do
  373. if pattern:sub(1, 1) == "~" then
  374. if dropped_state.tool:find(pattern:sub(2)) ~= nil then
  375. might_drop = true
  376. end
  377. else
  378. if pattern == dropped_state.tool then
  379. might_drop = true
  380. end
  381. end
  382. end
  383. if might_drop then
  384. handle_affirmative_drop_state(node_drop_table, palette_index, return_table, dropped_state)
  385. end
  386. -- If we don't know what tool we're holding, compare what would cause
  387. -- the drop with what we know for a fact we don't have.
  388. else
  389. for _, pattern in next, node_drop_table.items[current_state.drop_index].tools do
  390. -- If the pattern is a pattern and not a full tool name, we still don't
  391. -- know what we're holding. Check to see if the pattern directly
  392. -- conflicts with something we know we don't have. If it doesn't, fork,
  393. -- then add it to the list of pattern we know we know our tool matches.
  394. if pattern:sub(1, 1) == "~" then
  395. local might_drop = true
  396. for tool, _ in next, dropped_state.toolname_antipatterns do
  397. if pattern == tool then
  398. might_drop = false
  399. end
  400. end
  401. if might_drop then
  402. local forked_state = table.copy(dropped_state)
  403. forked_state.toolname_patterns[pattern] = true
  404. handle_affirmative_drop_state(node_drop_table, palette_index, return_table, forked_state)
  405. end
  406. -- Finally!! At this point in the script, we don't know for sure what
  407. -- tool we have, but we may or may not have a list of patterns our tool
  408. -- matches against and/or a list of patterns our tool does not match
  409. -- against. Now, we've finally gotten a concrete tool name to match
  410. -- against. Check the tool name against the lists. If the tool name is
  411. -- compatible, fork here, set the tool name, and remove the
  412. -- now-redundant lists.
  413. else
  414. local matches_pattens = true
  415. local does_not_match_antipaterns = true
  416. for tool, _ in next, dropped_state.toolname_patterns do
  417. if pattern:find(tool:sub(2)) == nil then
  418. matches_pattens = false
  419. end
  420. end
  421. for tool, _ in next, dropped_state.toolname_antipatterns do
  422. if tool:sub(1, 1) == "~" then
  423. if pattern:find(tool:sub(2)) ~= nil then
  424. does_not_match_antipaterns = false
  425. end
  426. else
  427. if pattern == tool then
  428. does_not_match_antipaterns = false
  429. end
  430. end
  431. end
  432. if matches_pattens
  433. and does_not_match_antipaterns then
  434. local forked_state = table.copy(dropped_state)
  435. forked_state.toolname_patterns = nil
  436. forked_state.toolname_antipatterns = nil
  437. forked_state.tool = pattern
  438. handle_affirmative_drop_state(node_drop_table, palette_index, return_table, forked_state)
  439. end
  440. end
  441. end
  442. end
  443. else
  444. -- If there are no tool requirements, there's a definite possibility
  445. -- that we can get the drop.
  446. handle_affirmative_drop_state(node_drop_table, palette_index, return_table, dropped_state)
  447. end
  448. end
  449. end
  450. end
  451. -- The function above has two branches that require this logic, and I'd
  452. -- rather not code this same thing twice in case a later Minetest
  453. -- update requires me to modify it, so I made it a separate function.
  454. -- Being separate, I added this function call to a third branch as well
  455. -- that otherwise would have hooked into an existing branch using a set
  456. -- variable.
  457. --
  458. -- This function's variable name was already declared local before. If
  459. -- we declare it local a second time, the previous function somehow
  460. -- can't access this function. I think Lua somehow sets up a second
  461. -- local variable with the same name or something, presenting the
  462. -- former variable to functions that bound to the variable before this
  463. -- point and the newer variable to functions that bind to the variable
  464. -- after this point. Very unintuitive. In any case, the easy solution
  465. -- is to just not declare the function name local here, then write a
  466. -- note here in the comments explaining that not declaring the function
  467. -- local was intentional.
  468. function handle_affirmative_drop_state(node_drop_table, palette_index, return_table, dropped_state)
  469. if node_drop_table.items[dropped_state.drop_index-1].inherit_color
  470. and palette_index then
  471. for _, item in next, node_drop_table.items[dropped_state.drop_index-1].items do
  472. local stack = ItemStack(item)
  473. stack:get_meta():set_int("palette_index", palette_index)
  474. dropped_state.drops[#dropped_state.drops+1] = stack:to_string()
  475. end
  476. else
  477. for _, item in next, node_drop_table.items[dropped_state.drop_index-1].items do
  478. dropped_state.drops[#dropped_state.drops+1] = item
  479. end
  480. end
  481. drop_possibilities(node_drop_table, palette_index, return_table, dropped_state)
  482. end
  483. -- Adding a wrapper to that last function will make our API cleaner.
  484. local function node_drop_possibilities(node_name, palette_index)
  485. -- The above function modifies tables passed into it to make recursion
  486. -- a bit more efficient. Instead of building a bunch of tables and
  487. -- having to combine their data, we can just put all the data into a
  488. -- single table to begin with.
  489. --
  490. -- Likewise, the current_state argument is only used internally, so
  491. -- we'll set that as well.
  492. local return_table = {}
  493. local current_state = {
  494. drop_index = 1,
  495. number_drops = 0,
  496. drops = {},
  497. toolname_patterns = {},
  498. toolname_antipatterns = {},
  499. }
  500. drop_possibilities(minetest.registered_nodes[node_name].drop, palette_index, return_table, current_state)
  501. return return_table
  502. end
  503. -- This function calls any functions registered by mods to be called
  504. -- any time a player's stats are updated. This could be used, for
  505. -- example, to build an automatically-updating inventory page for
  506. -- players.
  507. local function call_update_functions(player, drops)
  508. for modname, funct in next, update_functions do
  509. funct(player, drops)
  510. end
  511. end
  512. -- Calculating uses exponents, which is a bit more
  513. -- computationally-expensive than I'd like it to be. As such,
  514. -- get_unscaled_partial_level() just returns data from the cache. When
  515. -- we want to actually recalculate the data, we need to use
  516. -- calculate_unscaled_partial_level() instead.
  517. local function calculate_unscaled_partial_level(player_name, material)
  518. if levelling_exponent[material] then
  519. return math.min(get_stacks_mined(player_name, material)^levelling_exponent[material], max_stack_size)
  520. else
  521. return 0
  522. end
  523. end
  524. -- This just conveniently adds up the unscaled levels, which needs to
  525. -- be done in order to calculate the scaled level or calculate the size
  526. -- of the level progress bar.
  527. local function get_unscaled_floating_point_level(player_name)
  528. local total_level = 0
  529. for material, partial_level in next, unscaled_level_cache[player_name] do
  530. total_level = total_level + partial_level
  531. end
  532. return total_level
  533. end
  534. -- Given a player name and a material, this function returns a sort of
  535. -- "level" representing that player's experience mining or farming that
  536. -- material. Rather than an outright number of stacks mined, like
  537. -- get_stacks_mined() returns, this function calculates on a curve,
  538. -- requiring players to mine/farm more and more of the material to
  539. -- reach the next level.
  540. local function get_unscaled_partial_level(player_name, material)
  541. -- We've got a cache, so we should use it.
  542. if unscaled_level_cache[player_name] then
  543. if unscaled_level_cache[player_name][material] then
  544. return unscaled_level_cache[player_name][material]
  545. else
  546. -- if the player is in the cache but the level isn't, that material
  547. -- must not be associated with a stat, and thus not associated with a
  548. -- level. Return zero.
  549. return 0
  550. end
  551. else
  552. -- If the player has been online this server session, their data will
  553. -- be in the cache, but if their data isn't in the cache, we still want
  554. -- this function to return the correct answer. We'll calculate the
  555. -- level and return it.
  556. return calculate_unscaled_partial_level(player_name, material)
  557. end
  558. end
  559. -- The first time that a player's level is queried, this function needs
  560. -- to be called to calculate all of their levels. Each time the player
  561. -- gets a drop, this function needs to be called again to calculate all
  562. -- the levels and see if the player levelled up and the level-up
  563. -- callbacks need to be called.
  564. local function calculate_scaled_partial_levels(player_name)
  565. local raw_value = {}
  566. for _, drop in next, sorted_drops do
  567. raw_value[drop] = math.sqrt(
  568. get_unscaled_partial_level(player_name, drop) * (get_unscaled_floating_point_level(player_name) / #sorted_drops)
  569. )
  570. end
  571. rawset(scaled_level_cache, player_name, raw_value)
  572. end
  573. -- Calculating a player's full level all at once is more expensive than
  574. -- I'd like it to be, not to mention that with the new on-level-up
  575. -- callback feature, a player's level has to be recalculated *every
  576. -- time* one of their stats is raised so as to see if the level has
  577. -- changed. We greatly alleviate the problem by using a cache to store
  578. -- the current proficiency levels, which we can initialise the first
  579. -- time the data is needed or the first time the data changes,
  580. -- whichever happens first. From there, we only have to recalculate the
  581. -- parts of the player's level referring to individual stats that
  582. -- changed and compare them to the previous values for those
  583. -- components. If there's a change, we can use the new values along
  584. -- with even more data from the cache to calculate the new total level
  585. -- and call any registered on-level-up callbacks.
  586. --
  587. -- Previously, the generation of cached data was done when a player
  588. -- logged in. However, I found that sometimes, a player's level might
  589. -- get checked even if that player hasn't logged in since the last
  590. -- restart. One example of this is if a sfinv page displays a player's
  591. -- level. In one case, I found that sfinv was generated before the
  592. -- registered on_join function from this mod was run, causing the game
  593. -- to crash because the sfinv page was trying to access data from
  594. -- liblevelup that wasn't yet available but would be a fraction of a
  595. -- second later when the on_join function from this mod would get run.
  596. -- This is only one example though. A mod should be able to check the
  597. -- level of absolutely any player without fear of crashing the game and
  598. -- without getting a zeroed-out result. To handle that, the
  599. -- cache-generation functionality was moved to this metatable. The
  600. -- first time that the data is needed, it'll be generated.
  601. setmetatable(unscaled_level_cache, {
  602. __index = function(unscaled_level_cache, player_name)
  603. local raw_value = rawget(unscaled_level_cache, player_name)
  604. if not raw_value then
  605. raw_value = {}
  606. for _, material in next, sorted_drops do
  607. raw_value[material] = calculate_unscaled_partial_level(player_name, material)
  608. end
  609. rawset(unscaled_level_cache, player_name, raw_value)
  610. end
  611. return raw_value
  612. end
  613. })
  614. -- Same logic as above. If the data is queried but hasn't been
  615. -- calculated, let's calculate it.
  616. setmetatable(scaled_level_cache, {
  617. __index = function(scaled_level_cache, player_name)
  618. local raw_value = rawget(scaled_level_cache, player_name)
  619. if not raw_value then
  620. calculate_scaled_partial_levels(player_name)
  621. raw_value = rawget(scaled_level_cache, player_name)
  622. end
  623. return raw_value
  624. end
  625. })
  626. -- Mods can register update functions to be alerted when a player's
  627. -- stats change.
  628. local function register_update_function(funct)
  629. update_functions[minetest.get_current_modname()] = funct
  630. end
  631. -- Mods can also remove the update functions of other mods, if need be.
  632. local function unregister_update_function(modname)
  633. update_functions[modname] = nil
  634. end
  635. -- Mods can register update functions to be alerted when a player's
  636. -- level changes.
  637. local function register_levelup_function(funct)
  638. levelup_functions[minetest.get_current_modname()] = funct
  639. end
  640. -- Mods can also remove the update functions of other mods, if need be.
  641. local function unregister_levelup_function(modname)
  642. levelup_functions[modname] = nil
  643. end
  644. -- Functions may be registered by other mods for telling liblevelup to
  645. -- count more types of drops using this function.
  646. local function register_is_countable(funct)
  647. is_countable_callbacks[minetest.get_current_modname()] = funct
  648. end
  649. -- Functions may be unregistered by other mods so their behaviour can
  650. -- be overridden.
  651. local function unregister_is_countable(modname)
  652. is_countable_callbacks[modname] = nil
  653. end
  654. -- Mods can register craft locks, which prevent items from being
  655. -- crafted if level requirements have not been met. Aliases, item
  656. -- groups, and item quantities are not handles here. Only specific
  657. -- known item names should be registered. If multiple locks on the same
  658. -- item are registered - for example, by different mods - they'll be
  659. -- merged and the level requirements for all registered locks will need
  660. -- to be met in order to craft the item.
  661. local function register_craft_lock(name, requirements)
  662. craft_locks[name] = craft_locks[name] or {}
  663. for drop, level in next, requirements do
  664. -- We attach "or level" here to the existing value because a value
  665. -- might not yet be registered, at which point the level will simply be
  666. -- compared to itself by math.max().
  667. craft_locks[name][drop] = math.max(craft_locks[name][drop] or level, level)
  668. end
  669. end
  670. -- All requirements for unlocking the craft will be removed, and the
  671. -- player will have that craft available to them from the start.
  672. local function remove_craft_lock(name)
  673. craft_locks[name] = nil
  674. end
  675. -- If even one registered function says a drop is valid, this function
  676. -- will reply that the drop is valid.
  677. local function is_countable(node_name, drop_list, drop_opts, drop_key)
  678. drop_list = normalise_drops(drop_list)
  679. for mod_name, funct in next, is_countable_callbacks do
  680. if funct(node_name, drop_list, drop_opts, drop_key) then
  681. return true
  682. end
  683. end
  684. return false
  685. end
  686. -- Mods developers should really think about whether they actually need
  687. -- per-material player levels before making use of this feature. In
  688. -- most cases, a unified level would be a much cleaner experience for
  689. -- players. In fact, I originally planned not to provide a per-material
  690. -- level-retrieval function specifically so it wouldn't be misused.
  691. -- However, there are some cases in which per-material levels make
  692. -- sense. I'm not saying not to use this function, I'm just saying you
  693. -- should consider unified levels first and make sure you've ruled them
  694. -- out for a good reason before resorting to this feature.
  695. --
  696. -- To make this function much more balanced, it heavily takes into
  697. -- account a the average drop counts across all possible drops. As a
  698. -- result, players with a low overall level will always have low
  699. -- per-material levels even if they've maxed out the amount of that
  700. -- material that will affect levels. While this doesn't fully alleviate
  701. -- the concerns expressed in the paragraph above, it does greatly
  702. -- reduce them.
  703. local function get_scaled_per_material_player_level(player_name, drop)
  704. local level = scaled_level_cache[player_name][drop] or 0
  705. local cap = drop_max_stack_size[drop] or 0
  706. return math.min(cap, math.floor(level))
  707. end
  708. -- For mods that use liblevelup to add a sense of progression to the
  709. -- game, it can be useful to know a player's level.
  710. local function get_scaled_player_level(player_name)
  711. local level = 0
  712. for drop_name, drop_level in next, scaled_level_cache[player_name] do
  713. level = level + math.min(drop_max_stack_size[drop_name], math.floor(drop_level))
  714. end
  715. return level
  716. end
  717. -- I was running into a lot of bugs in the code related to next level
  718. -- meters, and I wasn't sure how much of the issue is caused by
  719. -- mistaken inconsistency in the code, so I just dumped all the
  720. -- relevant code into this function. The other two functions can call
  721. -- this one as need be, and I can be sure that they're both doing the
  722. -- same thing as one another. It turned out inconsistency had nothing
  723. -- to do with the issue, but this still makes the code easier to debug.
  724. local function get_per_level_material_scaled_level_at(player_name, material, target_level)
  725. -- Due to precision errors, the exact starting point isn't getting
  726. -- accurately determined. When at level zero, this can cause this
  727. -- extrapolation algorithm to predict that the current level would have
  728. -- been reached a drop before or after it really would be. I have no
  729. -- idea what to do about that; there's no way to have infinite
  730. -- precision, so we can't eliminate precision errors. I think most of
  731. -- the time, the effect is unnoticeable, especially because of the
  732. -- ever-shifting denominators that result from the complexities of the
  733. -- levelling system. Importantly, this rounding issue affects only
  734. -- level predictions (and not even the goal state, but the extrapolated
  735. -- starting state), and not the actual level of the player. However,
  736. -- there's a corner case: level zero. There are two odd behaviours that
  737. -- are *highly* noticeable for players at level zero. The most obvious
  738. -- error is when -nan is is returned instead of the valid starting
  739. -- point of zero drops. This seems to happen when precision errors set
  740. -- the extrapolated starting point to be below zero. The second is when
  741. -- the extrapolated starting point is instead estimated to be one
  742. -- instead of zero. This sets causes progress pages, for example, to
  743. -- say the progress toward the next level - level 1 - is one less than
  744. -- the total number of drops obtained. It makes absolutely no sense to
  745. -- the player and only serves to confuse them. I'm utterly confused as
  746. -- to even one semantically correct thing I can try in attempting to
  747. -- resolve the issue.
  748. --
  749. -- Out of desperation, I'm just checking for level zero, and if level
  750. -- zero isn't the case, having liblevelup actually do the maths. This
  751. -- results in correct return values for players at level zero and
  752. -- almost-correct values for everyone else.
  753. if target_level == 0 then
  754. return 0
  755. elseif reverse_levelling_exponent[material] then
  756. local current_material_level = get_unscaled_partial_level(player_name, material)
  757. if current_material_level == drop_max_stack_size[material] then
  758. return math.huge
  759. else
  760. local target = target_level^2
  761. local average_material_level = get_unscaled_floating_point_level(player_name) / #sorted_drops
  762. -- BEGIN COMPLEX, UNVERIFIED MATHS
  763. --
  764. -- This part's a bit above me. Basically, the intent is that this
  765. -- equality be true:
  766. --
  767. -- (current_material_level + X) * (average_material_level + X / #sorted_drops) == target
  768. --
  769. -- We then figure out how many of the drop is needed to get to level
  770. -- (current_material_level + X) and call it a day. I couldn't figure
  771. -- out where to go after I'd expressed my intent in that form though. I
  772. -- ended up asking an algebra solver hoping that I could walk through
  773. -- the steps it provided an arrive at the same solution it did, but it
  774. -- solved it using quadratic equations and at the moment, I'm far too
  775. -- stressed out by other things in my life to keep the variables
  776. -- straight in my head enough to work with quadratic equations. When
  777. -- I'm in a better place mentally and emotionally, I need to come back
  778. -- and verify that this equation actually does what it's supposed to.
  779. -- Until then, I'm just going to trust that the computerised algebra
  780. -- solver knows what it's doing and that this equation does actually
  781. -- solve for X so I can continue development.
  782. local X = (
  783. math.sqrt(
  784. 4 * #sorted_drops * target
  785. - 2 * current_material_level * average_material_level * #sorted_drops
  786. + average_material_level^2 * (#sorted_drops)^2
  787. + current_material_level^2
  788. )
  789. - current_material_level
  790. - average_material_level * #sorted_drops
  791. ) / 2
  792. -- END COMPLEX, UNVERIFIED MATHS
  793. local target_material_level = current_material_level + X
  794. return math.ceil(target_material_level^reverse_levelling_exponent[material] * drop_max_stack_size[material])
  795. end
  796. else
  797. return math.huge
  798. end
  799. end
  800. -- Given a player name and a material, this function returns the number
  801. -- of the material left the player needs to mine or farm to reach the
  802. -- next per-material level. If there are no more levels to gain, this
  803. -- function returns infinity. Consider using next_scaled_level_at()
  804. -- instead, when reasonable.
  805. local function next_level_at(player_name, material)
  806. return get_per_level_material_scaled_level_at(player_name, material, math.floor(scaled_level_cache[player_name][material] or 0) + 1)
  807. - get_drop_stat(player_name, material)
  808. end
  809. -- This helper function determines the length of a player's progress
  810. -- progress bar for things such as level-up progress bars. In
  811. -- combination with the next_level_at(), it can be used to determine
  812. -- what a user's progress bar percentage and progress bar text should
  813. -- be set to.
  814. --
  815. -- progress_percent == (length - next_scaled_level_at) / length
  816. local function player_material_progress_bar_length(player_name, material)
  817. local current_level = math.floor(scaled_level_cache[player_name][material] or 0)
  818. return get_per_level_material_scaled_level_at(player_name, material, current_level + 1)
  819. - get_per_level_material_scaled_level_at(player_name, material, current_level)
  820. end
  821. -- Incrementing is handled differently depending on a setting that is
  822. -- read only when the game starts.
  823. local increment
  824. -- If unlimited counting is enabled, we count on and on without end and
  825. -- without overflow. In this case, we work with the number fifteen
  826. -- digits at a time, which allows us to work not at the integer
  827. -- precision limit, but instead at the RAM limit.
  828. --
  829. -- If we weren't using the default Minetest implementation by default,
  830. -- and thus didn't need the default Minetest implementation to be
  831. -- compatible with our own implementation, we could save space by using
  832. -- hexadecimal instead of decimal. The default Minetest implementation
  833. -- uses decimal though, so we must use decimal too. It doesn't actually
  834. -- matter though. You shouldn't need to even turn on our
  835. -- implementation, so the default Minetest implementation is the one
  836. -- that matters here.
  837. if Settings(minetest.get_worldpath().."/world.conf"):get_bool("liblevelup.enable_infinite_counter", false) or true then
  838. function increment(key, quantity)
  839. local count = storage:get_string(key)
  840. local continue = true
  841. local offset = 0
  842. while continue do
  843. local prefix = count:sub(1, offset - 16)
  844. -- I'm not sure how to correctly set the suffix without a bizarre
  845. -- exception for the first working segment. I guess for now, a bizarre
  846. -- exception is going to have to do.
  847. --
  848. -- To sum up the situation simply, the problem seems to be that I have
  849. -- no idea what input would yield an empty string when no suffix is
  850. -- available.
  851. local suffix = ""
  852. if offset ~= 0 then
  853. suffix = count:sub(offset)
  854. end
  855. local working_segment = count:sub(offset - 15, offset - 1)
  856. -- An empty segment can't appear in the middle of a string, so if the
  857. -- segment is empty, we can ignore the prefix, which must also be
  858. -- empty. We also need to treat the empty segment as zero, but there's
  859. -- no need to set it to zero and increment, because incrementing zero
  860. -- will always yield the value we incremented by. So we skip right to
  861. -- using the value we're adding as the final value, and append the
  862. -- suffix to it. Here, we terminate the loop, as we've finished the
  863. -- incrementing.
  864. if working_segment == "" then
  865. count = quantity .. suffix
  866. continue = false
  867. -- if the segment isn't empty, we need to add the value to it.
  868. else
  869. working_segment = working_segment + quantity
  870. -- If there's no prefix, we're working with the most-significant
  871. -- segment and can just write it as-is. Overflow, if any, into the next
  872. -- segment can just be dealt with by writing this segment longer than
  873. -- it should be. As a side note, a more-robust check would be needed if
  874. -- arbitrarily-large values could be added, but in fact, the value
  875. -- added will never be more than the maximum stack size of 65535, so
  876. -- the value to carry to the next segment, if any, will always be
  877. -- exactly one. No need for a special check.
  878. if prefix == "" then
  879. count = prefix .. string.format("%d", working_segment) .. suffix
  880. continue = false
  881. -- If the segment is maxed out, we need to set it back to all zeros and
  882. -- increment the next segment by one. Here, we change the offset, but
  883. -- the incrementing hasn't completed yet, so the loop continues.
  884. elseif working_segment > 999999999999999 then
  885. quantity = 1
  886. count = prefix .. string.format("%015d", working_segment - 1000000000000000) .. suffix
  887. offset = offset - 15
  888. -- If it's not the most-significant segment, we need to pad the segment
  889. -- out with zeros.
  890. else
  891. count = prefix .. string.format("%015d", working_segment) .. suffix
  892. continue = false
  893. end
  894. end
  895. end
  896. -- Finally, we save the incremented value.
  897. storage:set_string(key, count)
  898. end
  899. -- If unlimited counting is disabled though, we just add the quantity
  900. -- to the current value, without doing anything special. The Minetest
  901. -- mod storage API will automatically overflow for us at the 32-bit
  902. -- signed integer limit. Realistically, no one will hit this limit, so
  903. -- this is just fine for normal gameplay.
  904. else
  905. function increment(key, quantity)
  906. storage:set_int(key, storage:get_int(key) + quantity)
  907. end
  908. end
  909. -- This function calls any functions registered by mods to be called
  910. -- any time a player levels up. This could be used, for example, to
  911. -- reward the player in some way or update their abilities.
  912. local function call_levelup_functions(player)
  913. -- First, we'll add up the level, then we'll call the functions and
  914. -- pass that level to them.
  915. local level = get_scaled_player_level(player:get_player_name())
  916. for modname, funct in next, levelup_functions do
  917. funct(player, level)
  918. end
  919. end
  920. -- We want this function to be the last to override
  921. -- minetest.handle_node_drops(). We can define our implementation now,
  922. -- but we can't put it in place until later.
  923. function liblevelup_handle_node_drops(pos, original_drops, digger)
  924. local update_stats = false
  925. local update_level = false
  926. -- If we have information on what node was dug, let's use it.
  927. -- Otherwise, this function must have been called not by a dig, but by
  928. -- some other mod.
  929. if original_drops.node_dug then
  930. -- Is this drop even counted?
  931. if is_counted(original_drops.node_dug, original_drops) then
  932. update_stats = true
  933. local player_name = digger:get_player_name()
  934. for _, item in next, original_drops do
  935. local stack = ItemStack(item)
  936. local count = stack:get_count()
  937. stack:set_count(1)
  938. local material_name = stack:to_string()
  939. increment(player_name..";"..material_name, count)
  940. -- We need to check the player's level now, before updating it, so we
  941. -- can compare it to the new value.
  942. local previous_level = get_scaled_player_level(player_name)
  943. -- Now we can recalculate the player's level.
  944. unscaled_level_cache[player_name][material_name] = calculate_unscaled_partial_level(player_name, material_name)
  945. calculate_scaled_partial_levels(player_name)
  946. -- With both the new and old level available, we can check to see if
  947. -- there was an increase.
  948. if previous_level ~= get_scaled_player_level(player_name, material_name) then
  949. update_level = true
  950. end
  951. end
  952. end
  953. end
  954. -- Whether called by a dig or by another mod, we should call the
  955. -- default implementation to finish up.
  956. builtin_handle_node_drops(pos, original_drops, digger)
  957. -- Is stats have actually changed, we should notify mods that have
  958. -- requested to be notified.
  959. if update_stats then
  960. -- First, notify mods about changed stats.
  961. call_update_functions(digger, original_drops)
  962. -- Then, if the level has changed as well, notify mods about that as
  963. -- too.
  964. if update_level then
  965. call_levelup_functions(digger)
  966. end
  967. end
  968. end
  969. -- Again, this function returns an empty table if called at load time.
  970. -- Wait until run time to call it.
  971. local function get_all_drops()
  972. return table.copy(sorted_drops)
  973. end
  974. -- Sometimes, a mod may need code to run when the game begins, but
  975. -- after liblevelup has initialised. Using minetest.after() is no
  976. -- guarantee unless you pass it a time longer than zero seconds.
  977. -- Instead of registering a function with minetest.after, you can
  978. -- register one with this function and it will run as soon as the
  979. -- liblevelup API is fully functional.
  980. local function register_startup_function(funct)
  981. startup_functions[minetest.get_current_modname()] = funct
  982. end
  983. -- A "node definition" might actually be several node definitions
  984. -- rolled into one.
  985. local function node_forms(node_name)
  986. local palette = minetest.registered_nodes[node_name].palette
  987. local paramtype2 = minetest.registered_nodes[node_name].paramtype2
  988. local increment
  989. if paramtype2 == "color" then
  990. increment = 1
  991. elseif paramtype2 == "colorwallmounted" then
  992. increment = 8
  993. elseif paramtype2 == "colorfacedir" then
  994. increment = 32
  995. end
  996. if palette and increment then
  997. local array = {}
  998. local index = 0
  999. while index < 256 do
  1000. array[#array+1] = {string=node_name..' 1 0 "\\0001palette_index\\0002'..index..'\\0003"', palette_index=index}
  1001. index = index + increment
  1002. end
  1003. return array
  1004. else
  1005. return {{string=node_name}}
  1006. end
  1007. end
  1008. -----------------------------------------------------------------------
  1009. ---------------------------- External API: ----------------------------
  1010. -----------------------------------------------------------------------
  1011. -- To grant drop points to the player, we need to know which player
  1012. -- dug the node, what node was dug, and what item was dropped. The
  1013. -- Minetest API doesn't give us all that information at once. Instead,
  1014. -- it tells us what node was dug when we need to find the drops, then
  1015. -- tells us the drops and tells us what player they're for. In order to
  1016. -- get all the information we need, we need to sneak the dug node's
  1017. -- name into the drop table.
  1018. function minetest.get_node_drops(node, tool_name)
  1019. local drops = builtin_get_node_drops(node, tool_name)
  1020. -- When a player digs, we get a chance to remove this item from the
  1021. -- drop list before passing it to Minetest's handlers. When drops are
  1022. -- gotten for some other reason, we don't. If we add the information on
  1023. -- what node was dug by simply setting this value to the name of the
  1024. -- node, it causes item duplication in some cases (for example, when
  1025. -- attached nodes fall). However, if we store this value in a meta
  1026. -- table, we can retrieve the value later without Minetest doing
  1027. -- anything silly with it in the mean time.
  1028. local paramtype2 = ItemStack(node.name):get_definition().paramtype2
  1029. local palette_index = minetest.strip_param2_color(node.param2, paramtype2)
  1030. if palette_index then
  1031. setmetatable(drops, {__index = {node_dug = node.name..' 1 0 "\\0001palette_index\\0002'..palette_index..'\\0003"'}})
  1032. else
  1033. setmetatable(drops, {__index = {node_dug = node.name}})
  1034. end
  1035. return drops
  1036. end
  1037. minetest.register_on_mods_loaded(function()
  1038. -- We'll need to iterate over a list of the drops later on so we can
  1039. -- build the sorted_drops table, so we should build that list as we go.
  1040. -- Once this function completes though, the list will no longer be
  1041. -- needed in this form and can be discarded. From there, we'll only
  1042. -- need the sorted_drops table to get this information if we need to
  1043. -- later iterate over it again.
  1044. local registered_drops = {}
  1045. -- After all mods have loaded, we need to go through the node table to
  1046. -- figure out which countable node/drop pairs have been defined in the
  1047. -- game.
  1048. for node_name, def in next, minetest.registered_nodes do
  1049. -- No drop property means the node drops itself. It's not countable.
  1050. if def.drop
  1051. -- If the node's not pointable, how are we supposed to mine it?
  1052. and minetest.registered_nodes[node_name].pointable
  1053. -- If the node is not diggable, we shouldn't be tormenting players with
  1054. -- a visible stat in their menus that they'll never be able to raise.
  1055. and def.diggable
  1056. -- If the can_dig() method is defined, the node either can never be dug
  1057. -- (so again, we shouldn't torment players) of the node is likely some
  1058. -- unnatural node such as a furnace or chest. We shouldn't be keeping
  1059. -- stats for these kinds of nodes.
  1060. and not def.can_dig then
  1061. -- For now, node forms just refers to the different versions of
  1062. -- coloured nodes. If some other type of node variant needs to be
  1063. -- accounted for later, that can be added in as well.
  1064. for _, form in next, node_forms(node_name) do
  1065. local possibilities = node_drop_possibilities(node_name, form.palette_index)
  1066. local blacklisted = {}
  1067. -- If one of the recipes matches one of the drops, that drop is
  1068. -- basically just the node dropping itself in another form. Nodes that
  1069. -- do this include stone, which drops cobble that can be smelted back
  1070. -- into stone, and clay blocks, which drop four clay lumps that can be
  1071. -- crafted back into one clay block.
  1072. local recipes = minetest.get_all_craft_recipes(node_name)
  1073. if recipes then
  1074. for _, recipe in next, recipes do
  1075. blacklisted[drop_key(recipe.items)] = true
  1076. end
  1077. end
  1078. for _, possibility in next, possibilities do
  1079. local key = drop_key(possibility)
  1080. if not blacklisted[key] and form.string ~= key and key ~= ""
  1081. and is_countable(form.string, possibility, possibilities, key) then
  1082. if not registered_countables[form.string] then
  1083. registered_countables[form.string] = {}
  1084. end
  1085. registered_countables[form.string][key] = true
  1086. for _, item in next, possibility do
  1087. local stack = ItemStack(item)
  1088. if not stack:is_empty() then
  1089. stack:set_count(1)
  1090. registered_drops[stack:to_string()] = stack:get_name()
  1091. end
  1092. end
  1093. end
  1094. end
  1095. end
  1096. end
  1097. end
  1098. -- Here, we set up several important tables. It's quicker to work with
  1099. -- them all at once instead of building them separately, as the server
  1100. -- doesn't have to cycle through the drop materials as many times.
  1101. -- First, we have the sorted_drops drops table, of which a copy of is
  1102. -- given to any mod that requests it. While we're building this table,
  1103. -- we also take the opportunity to build the levelling_exponent table,
  1104. -- used to provide a levelling mechanic in which reaching the next
  1105. -- level requires exponentially higher numbers of drops. We also build
  1106. -- the drop_max_stack_size table here, used in the levelling equation
  1107. -- of each drop stat.
  1108. local denominator = math.log(max_stack_size)
  1109. for drop_string, drop_name in next, registered_drops do
  1110. sorted_drops[#sorted_drops+1] = drop_string
  1111. if minetest.registered_items[drop_name] then
  1112. drop_max_stack_size[drop_string] = minetest.registered_items[drop_name].stack_max
  1113. else
  1114. drop_max_stack_size[drop_string] = minetest.registered_items.unknown.stack_max
  1115. end
  1116. local numerator = math.log(drop_max_stack_size[drop_string])
  1117. levelling_exponent[drop_string] = numerator/denominator
  1118. reverse_levelling_exponent[drop_string] = denominator/numerator
  1119. end
  1120. table.sort(sorted_drops)
  1121. -- It's time! Let's put our implementation of the drop handler in
  1122. -- place.
  1123. builtin_handle_node_drops = minetest.handle_node_drops
  1124. minetest.handle_node_drops = liblevelup_handle_node_drops
  1125. -- It's too late now to register startup functions. These should be
  1126. -- registered at load time, not run time, as it'd be too late for them
  1127. -- to get called if registered later. The same applies to the
  1128. -- is_countable functions. However, it's not even just these two
  1129. -- registrations that don't work after the game starts. Due to the way
  1130. -- registration is implemented, no registration will be successful once
  1131. -- the game starts. We might as well remove the whole registration
  1132. -- function table, as none of it is useful at this point in execution.
  1133. liblevelup.register = nil
  1134. -- We should remove the unregistration table as well.
  1135. liblevelup.unregister = nil
  1136. -- We're now completely done finding drop possibilities to count for
  1137. -- stats. We can delete the registered handlers while we're at it, as
  1138. -- we're done with them.
  1139. is_countable_callbacks = nil
  1140. -- The liblevelup API is now fully functional. Let's run the functions
  1141. -- other mods have requested we run.
  1142. for modname, funct in next, startup_functions do
  1143. funct()
  1144. end
  1145. -- We've already run the startup functions. We can now remove them.
  1146. startup_functions = nil
  1147. end)
  1148. -- To lock a craft, we simply return an empty ItemStack() if the
  1149. -- unlocking requirements have not been met after reporting to the
  1150. -- player what the unmet requirements are.
  1151. minetest.register_craft_predict(function(itemstack, player, old_craft_grid, craft_inv)
  1152. local lock = craft_locks[itemstack:get_name()]
  1153. if lock then
  1154. local disallowed = false
  1155. local name = player:get_player_name()
  1156. for drop, level in next, lock do
  1157. if get_scaled_per_material_player_level(name, drop) < level then
  1158. disallowed = true
  1159. minetest.chat_send_player(name, S("@1 level @2 is required for this craft.", ItemStack(drop):get_description(), level))
  1160. end
  1161. end
  1162. if disallowed then
  1163. return ItemStack("")
  1164. end
  1165. end
  1166. end)
  1167. -- Make the API visible to other mods:
  1168. liblevelup = {
  1169. -- Both for this mod's main purpose and for other purposes, it can be
  1170. -- useful to register callbacks that will be used when certain events
  1171. -- occur. If you register multiple callbacks of the same type from the
  1172. -- same mod, only the last one registered will be called by liblevelup.
  1173. -- If you need multiple things to happen, put them all in one function
  1174. -- or register a callback that calls all the functions you need called.
  1175. register = {
  1176. is_countable = register_is_countable ,
  1177. levelup_function = register_levelup_function,
  1178. startup_function = register_startup_function,
  1179. update_function = register_update_function ,
  1180. craft_lock = register_craft_lock ,
  1181. },
  1182. -- Sometimes though, you want to override the functionality of a mod,
  1183. -- so you don't want the old mod's callbacks called. These functions
  1184. -- let you unregister most callbacks given by other mods. The argument
  1185. -- to pass to these functions is always the name of the mod you wish to
  1186. -- unregister a callback provided by, and it doesn't prevent that mod
  1187. -- from later registering a callback. What that means is that you'll
  1188. -- want to depend on (either a hard dependency or a soft dependency)
  1189. -- the mod you want to restrict from having a registered callback, so
  1190. -- you can wait for it to register the callback and you can
  1191. -- successfully unregister that callback.
  1192. unregister = {
  1193. is_countable = unregister_is_countable ,
  1194. levelup_function = unregister_levelup_function,
  1195. update_function = unregister_update_function ,
  1196. craft_lock = remove_craft_lock ,
  1197. },
  1198. -- These methods use data queried form the database, process it, and
  1199. -- return it to the caller. liblevelup.get.pair_stat() is the only one
  1200. -- that returns data directly form the database unprocessed. All other
  1201. -- functions in this sub-table are built up from this one.
  1202. --
  1203. -- For any method taking a player name, the player name argument comes
  1204. -- first. Node names are always specified before drop names, if
  1205. -- specified at all. Part of the API overhaul was to ensure not only
  1206. -- organisation, but also consistency, so all future functions will
  1207. -- follow this convention as well.
  1208. get = {
  1209. stat = get_drop_stat ,
  1210. next_level_at = next_level_at ,
  1211. player_level = get_scaled_player_level ,
  1212. player_material_level = get_scaled_per_material_player_level,
  1213. player_material_progress_bar_length = player_material_progress_bar_length ,
  1214. },
  1215. -- These methods return metadata about how liblevelup is interacting
  1216. -- with the current subgame.
  1217. meta = {
  1218. drops_list = get_all_drops,
  1219. is_counted = is_counted ,
  1220. },
  1221. }
  1222. -----------------------------------------------------------------------
  1223. -------------------------- Deprecated Stuff: --------------------------
  1224. -----------------------------------------------------------------------
  1225. -- Legacy support in this mod is only enabled when overall Minetest
  1226. -- legacy support is enabled in minetest.conf. By default, legacy
  1227. -- support is enabled for release versions but not development
  1228. -- versions.
  1229. if minetest.settings:get("deprecated_lua_api_handling") ~= "error" then
  1230. -- I deprecated the entire API. It was a bit of a mess, and the mod's
  1231. -- name wasn't very fitting of its purpose either, so I renamed the mod
  1232. -- and rebuilt the API from the ground up with consistency and
  1233. -- organisation in mind. This is the old API table, kept for backwards
  1234. -- compatibility for at least two years, as is the usual time frame for
  1235. -- keeping deprecated code around in this mod.
  1236. --
  1237. -- Deprecated on 2020-02-29; DO NOT REMOVE FOR AT LEAST TWO YEARS.
  1238. __minestats__ = {
  1239. get_all_drops = get_all_drops ,
  1240. get_all_pairs = function()
  1241. return {}
  1242. end,
  1243. get_drop_stat = get_drop_stat ,
  1244. get_pair_stat = get_drop_stat ,
  1245. get_total_level = get_scaled_player_level ,
  1246. is_counted = is_counted ,
  1247. next_level_at = get_scaled_player_level ,
  1248. register_is_countable = register_is_countable ,
  1249. register_levelup_function = register_levelup_function ,
  1250. register_startup_function = register_startup_function ,
  1251. register_update_function = register_update_function ,
  1252. unregister_is_countable = unregister_is_countable ,
  1253. unregister_levelup_function = unregister_levelup_function,
  1254. unregister_update_function = unregister_update_function ,
  1255. get_proficiency_level = function(player_name, material)
  1256. return math.floor(get_unscaled_partial_level(player_name, material))
  1257. end,
  1258. get_drop_limit_multiplier = function()
  1259. return 1
  1260. end,
  1261. get_engine_stacks_mined = function(player_name, drop_name)
  1262. if minetest.registered_items[drop_name].type == "tool" then
  1263. return get_drop_stat(player_name, drop_name)
  1264. else
  1265. local stat = get_drop_stat(player_name, drop_name)
  1266. return math.floor(stat / max_stack_size)
  1267. end
  1268. end,
  1269. get_stacks_mined = function(player_name, drop_name)
  1270. if drop_max_stack_size[drop_name] then
  1271. return math.floor(get_stacks_mined(player_name, drop_name))
  1272. else
  1273. return 0
  1274. end
  1275. end,
  1276. }
  1277. -- Unscaled levels are no longer queriable by other mods, as scaled
  1278. -- levels should be used instead in every case so that players who
  1279. -- engage in a variety of activities are rewarded more than players
  1280. -- that learn to perform one activity endlessly but quickly.
  1281. --
  1282. -- Also, full player levels are now only earned by earning a level in
  1283. -- a specific material. That is, you can't earn half a level in one
  1284. -- material and half in another to earn a full player level. This
  1285. -- feature was removed to make player-visible level displays more
  1286. -- intuitive to players, but also provides an easy way to deal with the
  1287. -- fact that progress bars using partial levels along with the new
  1288. -- scaled level system require complex mathematics that I was never
  1289. -- able to figure out.
  1290. --
  1291. -- Deprecated on 2020-12-16; DO NOT REMOVE FOR AT LEAST TWO YEARS.
  1292. liblevelup.get.scaled_player_level = get_scaled_player_level
  1293. liblevelup.get.next_material_level_at = next_level_at
  1294. -- Node data is no longer recorded, as it's not useful to the purpose
  1295. -- of liblevelup and merely inflates the database size.
  1296. --
  1297. -- Deprecated on 2021-09-30; DO NOT REMOVE FOR AT LEAST TWO YEARS.
  1298. liblevelup.get.pair_stat = liblevelup.get.stat
  1299. liblevelup.get.drop_stat = liblevelup.get.stat
  1300. function liblevelup.meta.pairs_list()
  1301. return {}
  1302. end
  1303. startup_functions["*DEPRECATED 2021-09-30*"] = function()
  1304. local updated = false
  1305. local new_fields = {}
  1306. local fields = storage:to_table().fields
  1307. for key, value in next, fields do
  1308. local parts = string.split(key, ";")
  1309. if #parts == 2 then
  1310. new_fields[key] = (new_fields[key] or 0) + value
  1311. else
  1312. local stack = ItemStack(parts[#parts])
  1313. local count = stack:get_count()
  1314. stack:set_count(1)
  1315. local new_key = parts[1]..";"..stack:to_string()
  1316. new_fields[new_key] = (new_fields[new_key] or 0) + value * count
  1317. updated = true
  1318. end
  1319. end
  1320. if updated then
  1321. storage:from_table({
  1322. fields = new_fields,
  1323. })
  1324. end
  1325. end
  1326. end