async_craft.lua 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. -- Crafting Mod - semi-realistic crafting in minetest
  2. -- Copyright (C) 2018 rubenwardy <rw@rubenwardy.com>
  3. --
  4. -- This library is free software; you can redistribute it and/or
  5. -- modify it under the terms of the GNU Lesser General Public
  6. -- License as published by the Free Software Foundation; either
  7. -- version 2.1 of the License, or (at your option) any later version.
  8. --
  9. -- This library is distributed in the hope that it will be useful,
  10. -- but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. -- Lesser General Public License for more details.
  13. --
  14. -- You should have received a copy of the GNU Lesser General Public
  15. -- License along with this library; if not, write to the Free Software
  16. -- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  17. local default_def = {}
  18. function default_def:start_craft(pos, recipe)
  19. -- TODO: check for space in output
  20. local node = minetest.get_node(pos)
  21. node.name = self.active_name
  22. minetest.swap_node(pos, node)
  23. local meta = minetest.get_meta(pos)
  24. meta:set_int("recipe_idx", recipe.id)
  25. meta:set_int("work_remaining", recipe.work or 10)
  26. meta:set_int("work_total", recipe.work or 10)
  27. minetest.get_node_timer(pos):start(1.0)
  28. default_def.set_formspec(pos)
  29. end
  30. function default_def:make_inactive(pos)
  31. local node = minetest.get_node(pos)
  32. node.name = self.inactive_name
  33. minetest.swap_node(pos, node)
  34. minetest.get_node_timer(pos):start(1.0)
  35. local meta = minetest.get_meta(pos)
  36. meta:set_string("recipe_idx", nil)
  37. default_def.set_formspec(pos)
  38. end
  39. local function get_fuel_time(name)
  40. local fuel, _ = minetest.get_craft_result({
  41. method = "fuel",
  42. width = 1,
  43. items = { ItemStack(name) },
  44. })
  45. return fuel.time
  46. end
  47. function default_def.on_timer(pos)
  48. local meta = minetest.get_meta(pos)
  49. local player_name = meta:get_string("user")
  50. local inv = meta:get_inventory()
  51. local def = minetest.registered_items[minetest.get_node(pos).name]
  52. if player_name == "" or not def then
  53. return
  54. end
  55. -- Look and start a craft if possible
  56. -- Called after finishing a craft or every timer call when no craft
  57. local function check_for_craft()
  58. local item_hash = {}
  59. crafting.set_item_hashes_from_list(inv, "input", item_hash)
  60. local unlocked = crafting.get_unlocked(player_name)
  61. local recipes = crafting.get_all(def.craft_type, def.craft_level, item_hash, unlocked)
  62. -- TODO: unlocked crafts
  63. -- Find recipe with most inputs (ie: prioritise alloys)
  64. local best_recipe = nil
  65. for _, recipe in pairs(recipes) do
  66. if recipe.craftable and (not best_recipe or #best_recipe.recipe.items < #recipe.recipe.items) then
  67. best_recipe = recipe
  68. end
  69. end
  70. -- Check we have enough fuel
  71. if best_recipe then
  72. local fuel_remaining = meta:get_int("fuel_remaining") or 0
  73. local fuel_possible = fuel_remaining
  74. local fuel_stack = inv:get_stack("fuel", 1)
  75. if not fuel_stack:is_empty() then
  76. fuel_possible = fuel_possible +
  77. fuel_stack:get_count() * get_fuel_time(fuel_stack:get_name())
  78. end
  79. if fuel_possible < (best_recipe.work or 10) then
  80. best_recipe = nil
  81. end
  82. end
  83. -- If found, start crafting
  84. if best_recipe then
  85. def:start_craft(pos, best_recipe.recipe)
  86. elseif def.is_active then
  87. def:make_inactive(pos)
  88. end
  89. end
  90. -- Consume fuel even when the crafter is inactive
  91. local fuel_remaining = meta:get_int("fuel_remaining") or 0
  92. if fuel_remaining > 0 then
  93. meta:set_int("fuel_remaining", fuel_remaining - 1)
  94. default_def.set_formspec(pos)
  95. end
  96. -- Try to find craft if none active
  97. if not def.is_active then
  98. check_for_craft(pos)
  99. return
  100. end
  101. -- Check craft complete, produce item
  102. local work_remaining = meta:get_int("work_remaining")
  103. if work_remaining <= 0 then
  104. local idx = meta:get_int("recipe_idx")
  105. local recipe = crafting.get_recipe(idx)
  106. if not crafting.perform_craft(player_name, inv, "input", "main", recipe) then
  107. minetest.log("error", "Async station " ..
  108. def.name .. " at " .. minetest.pos_to_string(pos) ..
  109. " was unable to finish craft due to missing inputs")
  110. end
  111. check_for_craft(pos)
  112. return
  113. end
  114. -- Refill fuel if we've run out, or stop
  115. if fuel_remaining <= 0 then
  116. local stack = inv:get_stack("fuel", 1)
  117. if stack:is_empty() then
  118. def:make_inactive(pos)
  119. default_def.set_formspec(pos)
  120. return
  121. end
  122. local total = get_fuel_time(stack:get_name())
  123. if total <= 0 then
  124. def:make_inactive(pos)
  125. default_def.set_formspec(pos)
  126. return
  127. end
  128. stack:take_item()
  129. inv:set_stack("fuel", 1, stack)
  130. meta:set_int("fuel_remaining", total)
  131. meta:set_int("fuel_total", total)
  132. end
  133. -- Do work
  134. meta:set_int("work_remaining", work_remaining - 1)
  135. minetest.get_node_timer(pos):start(1.0)
  136. default_def.set_formspec(pos)
  137. end
  138. function default_def.set_formspec(pos)
  139. local meta = minetest.get_meta(pos)
  140. local item_percent = 0
  141. if meta:get_int("recipe_idx") > 0 then
  142. local remaining = meta:get_int("work_remaining")
  143. local total = meta:get_int("work_total")
  144. item_percent = 100 * (1 - remaining / total)
  145. end
  146. local fuel_remaining = meta:get_int("fuel_remaining")
  147. local fuel_total = meta:get_int("fuel_total")
  148. if fuel_total == 0 then
  149. fuel_total = 1
  150. end
  151. local fuel_percent = 100 * fuel_remaining / fuel_total
  152. local formspec = [[
  153. size[8,8]
  154. list[context;input;1,0.3;2,1;]
  155. list[context;fuel;1.5,2.5;2,1;]
  156. list[context;main;5,0.93;2,2;]
  157. list[current_player;main;0,4.1;8,1;]
  158. list[current_player;main;0,5.25;8,3;8]
  159. image[3.5,1.35;1,1;gui_furnace_arrow_bg.png^[lowpart:]] ..
  160. item_percent .. ":gui_furnace_arrow_fg.png^[transformR270]" ..
  161. "image[1.5,1.35;1,1;crafting_furnace_fire_bg.png^[lowpart:"..
  162. fuel_percent ..":crafting_furnace_fire_fg.png]"
  163. meta:set_string("formspec", formspec)
  164. return formspec
  165. end
  166. function default_def.can_dig(pos)
  167. local inv = minetest.get_inventory({ type = "node", pos = pos })
  168. return inv:is_empty("input") and inv:is_empty("fuel") and inv:is_empty("main")
  169. end
  170. function default_def.on_construct(pos)
  171. default_def.set_formspec(pos)
  172. local inv = minetest.get_inventory({ type = "node", pos = pos })
  173. inv:set_size("input", 2)
  174. inv:set_width("input", 2)
  175. inv:set_size("fuel", 1)
  176. inv:set_width("fuel", 1)
  177. inv:set_size("main", 4)
  178. inv:set_width("main", 2)
  179. end
  180. function default_def.on_metadata_inventory_move(pos, from_list, from_index,
  181. to_list, to_index, count, player)
  182. local meta = minetest.get_meta(pos)
  183. meta:set_string("user", player:get_player_name())
  184. minetest.get_node_timer(pos):start(1.0)
  185. end
  186. function default_def.on_metadata_inventory_put(pos, listname, index, stack, player)
  187. local meta = minetest.get_meta(pos)
  188. meta:set_string("user", player:get_player_name())
  189. minetest.get_node_timer(pos):start(1.0)
  190. end
  191. function crafting.create_async_station(name, type, level, def, def_active)
  192. local setboth = {
  193. craft_type = type,
  194. craft_level = level,
  195. active_name = name .. "_active",
  196. inactive_name = name,
  197. }
  198. for key, value in pairs(setboth) do
  199. def[key] = value
  200. def_active[key] = value
  201. end
  202. def.is_active = false
  203. def_active.is_active = true
  204. for key, value in pairs(default_def) do
  205. local d = def[key]
  206. def[key] = d ~= nil and d or default_def[key]
  207. local d_active = def_active[key]
  208. def_active[key] = d_active ~= nil and d_active or default_def[key]
  209. end
  210. minetest.register_node(name, def)
  211. minetest.register_node(name .. "_active", def_active)
  212. end