register.lua 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. --------------------------------------------------------------------------------------------------------------------
  2. -- Local functions
  3. -- Finds the greatest common divisor of the two input parameters
  4. local function greatest_common_divisor(a, b)
  5. local temp
  6. while(b > 0) do
  7. temp = b
  8. b = a % b
  9. a = temp
  10. end
  11. return a
  12. end
  13. -- Finds the greatest common divisor of an arbitrarily long list of numbers
  14. local function gcd_list(list)
  15. if #list == 1 then
  16. return list[1]
  17. end
  18. local gcd = list[1]
  19. for i=2, #list do
  20. gcd = greatest_common_divisor(gcd, list[i])
  21. end
  22. return gcd
  23. end
  24. -- divides input, output and returns values by their greatest common divisor
  25. -- does *not* modify time or any other values
  26. local function reduce_recipe(def)
  27. local list = {}
  28. for _, count in pairs(def.input) do
  29. table.insert(list, count)
  30. end
  31. if def.output then
  32. table.insert(list, ItemStack(def.output):get_count())
  33. end
  34. if def.returns then
  35. for _, count in pairs(def.returns) do
  36. table.insert(list, count)
  37. end
  38. end
  39. local gcd = gcd_list(list)
  40. if gcd ~= 1 then
  41. for item, count in pairs(def.input) do
  42. def.input[item] = count/gcd
  43. end
  44. if def.output then
  45. def.output:set_count(def.output:get_count()/gcd)
  46. end
  47. if def.returns then
  48. for item, count in pairs(def.returns) do
  49. def.returns[item] = count/gcd
  50. end
  51. end
  52. end
  53. end
  54. -- Strip group: from group names to simplify comparison later
  55. local function strip_groups(def)
  56. local groups = {}
  57. for item, count in pairs(def.input) do
  58. local group = string.match(item, "^group:(%S+)$")
  59. if group then
  60. groups[group] = count
  61. end
  62. end
  63. -- must be done in two steps like this in case the recipe has more than one group item
  64. -- doing it in the first loop could invalidate the pairs iterator and miss one
  65. for group, count in pairs(groups) do
  66. def.input[group] = count
  67. def.input["group:"..group] = nil
  68. end
  69. return def
  70. end
  71. -- Deep equals, used to check for duplicate recipes during registration
  72. local deep_equals
  73. deep_equals = function(test1, test2)
  74. if test1 == test2 then
  75. return true
  76. end
  77. if type(test1) ~= "table" or type(test2) ~= "table" then
  78. return false
  79. end
  80. local value2
  81. for key, value1 in pairs(test1) do
  82. value2 = test2[key]
  83. if deep_equals(value1, value2) == false then
  84. return false
  85. end
  86. end
  87. for key, _ in pairs(test2) do
  88. if test1[key] == nil then
  89. return false
  90. end
  91. end
  92. return true
  93. end
  94. --------------------------------------------------------------------------------------------------------------------
  95. -- Public API
  96. simplecrafting_lib.recipe_equals = function(recipe1, recipe2)
  97. for key, value1 in pairs(recipe1) do
  98. local value2 = recipe2[key]
  99. -- Special handling of output ItemStack
  100. if key == "output" then
  101. if type(value1) == "userdata" then
  102. value1 = value1:to_string()
  103. end
  104. if type(value2) == "userdata" then
  105. value2 = value2:to_string()
  106. end
  107. if value1 ~= value2 then
  108. return false
  109. end
  110. else
  111. if not deep_equals(value1, value2) then
  112. return false
  113. end
  114. end
  115. end
  116. for key, _ in pairs(recipe2) do
  117. if recipe1[key] == nil then
  118. return false
  119. end
  120. end
  121. return true
  122. end
  123. simplecrafting_lib.register = function(craft_type, def)
  124. def.input = def.input or {}
  125. reduce_recipe(def)
  126. strip_groups(def)
  127. local crafting_info = simplecrafting_lib.get_crafting_info(craft_type)
  128. -- Check if this recipe has already been registered. Many different old-style recipes
  129. -- can reduce down to equivalent recipes in this system, so this is a useful step
  130. -- to keep things tidy and efficient.
  131. local output_name
  132. if def.output then
  133. def.output = ItemStack(def.output)
  134. output_name = def.output:get_name()
  135. else
  136. output_name = "none" -- special value for recipes with no output. Shouldn't conflict with group:none since output can't be a group
  137. end
  138. local existing_recipes = crafting_info.recipes_by_out[output_name]
  139. if existing_recipes ~= nil then
  140. for _, existing_recipe in pairs(existing_recipes) do
  141. if simplecrafting_lib.recipe_equals(def, existing_recipe) then
  142. return nil
  143. end
  144. end
  145. end
  146. table.insert(crafting_info.recipes, def)
  147. local recipes_by_out = crafting_info.recipes_by_out
  148. recipes_by_out[output_name] = recipes_by_out[output_name] or {}
  149. recipes_by_out[output_name][#recipes_by_out[output_name]+1] = def
  150. local recipes_by_in = crafting_info.recipes_by_in
  151. for item, _ in pairs(def.input) do
  152. recipes_by_in[item] = recipes_by_in[item] or {}
  153. recipes_by_in[item][#recipes_by_in[item]+1] = def
  154. end
  155. return def
  156. end
  157. -- Registers the provided crafting recipe, and also
  158. -- automatically creates and registers a "reverse" craft of the same type.
  159. -- This should generally only be done with craft that turns one type of item into
  160. -- one other type of item (for example, metal ingots <-> metal blocks), but
  161. -- it will still work if there are multiple inputs.
  162. -- If there's more than one input type it will use "returns" to give them to the
  163. -- player in the reverse craft.
  164. -- Don't use a recipe that has a "group:" input with this, because obviously that
  165. -- can't be turned into an output. The mod will assert if you try to do this.
  166. simplecrafting_lib.register_reversible = function(craft_type, forward_def)
  167. local reverse_def = simplecrafting_lib.deep_copy(forward_def) -- copy before registering, registration messes with "group:" prefixes
  168. simplecrafting_lib.register(craft_type, forward_def)
  169. local forward_in = reverse_def.input
  170. reverse_def.input = simplecrafting_lib.count_list_add({[reverse_def.output:get_name()] = reverse_def.output:get_count()}, reverse_def.returns)
  171. local most_common_in_name = ""
  172. local most_common_in_count = 0
  173. for item, count in pairs(forward_in) do
  174. assert(string.sub(item, 1, 6) ~= "group:")
  175. if count > most_common_in_count then
  176. most_common_in_name = item
  177. most_common_in_count = count
  178. end
  179. end
  180. local reverse_output = ItemStack()
  181. reverse_output:set_name(most_common_in_name)
  182. reverse_output:set_count(most_common_in_count)
  183. reverse_def.output = reverse_output
  184. forward_in[most_common_in_name] = nil
  185. if next(forward_in) ~= nil then -- if there are any items left, they become returns
  186. reverse_def.returns = forward_in
  187. end
  188. simplecrafting_lib.register(craft_type, reverse_def)
  189. end