controller.lua 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. --[[
  2. Minetest Mod Storage Drawers - A Mod adding storage drawers
  3. Copyright (C) 2017-2019 Linus Jahn <lnj@kaidan.im>
  4. Copyright (C) 2018 isaiah658
  5. MIT License
  6. Permission is hereby granted, free of charge, to any person obtaining a copy
  7. of this software and associated documentation files (the "Software"), to deal
  8. in the Software without restriction, including without limitation the rights
  9. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  10. copies of the Software, and to permit persons to whom the Software is
  11. furnished to do so, subject to the following conditions:
  12. The above copyright notice and this permission notice shall be included in all
  13. copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. SOFTWARE.
  21. ]]--
  22. --[[ The gist of how the drawers mod stores data is that there are entities
  23. and the drawer node itself. The entities are needed to allow having multiple
  24. drawers in one node. The entities and node each store metadata about the item
  25. counts and such. It is necessary to change both at once otherwise in some cases
  26. the entity values are used and in other cases the node metadata is used.
  27. The gist of how the controller works is this. The drawer controller scans the
  28. adjacent tiles and puts the item names and other info such as coordinates and
  29. the visualid of the entity in a table. That table is saved in the controllers
  30. metadata. The table is used to help prevent needing to scan all the drawers to
  31. deposit an item in certain situations. The table is only updated on an as needed
  32. basis, not by a specific time/interval. Controllers that have no items will not
  33. continue scanning drawers. ]]--
  34. -- Load support for intllib.
  35. local MP = core.get_modpath(core.get_current_modname())
  36. local S, NS = dofile(MP.."/intllib.lua")
  37. local default_loaded = core.get_modpath("default") and default
  38. local mcl_loaded = core.get_modpath("mcl_core") and mcl_core
  39. local pipeworks_loaded = core.get_modpath("pipeworks") and pipeworks
  40. local controller_interval = tonumber(core.settings:get("drawers_controller_interval")) or 1.0
  41. local function controller_formspec(pos, meta_current_state)
  42. local formspec =
  43. "size[8,8.5]"..
  44. drawers.gui_bg..
  45. drawers.gui_bg_img..
  46. drawers.gui_slots..
  47. "label[0,0;" .. S("Current State: ") .. meta_current_state .. "]" ..
  48. "list[current_name;src;3.5,1.75;1,1;]"..
  49. "list[current_player;main;0,4.25;8,1;]"..
  50. "list[current_player;main;0,5.5;8,3;8]"..
  51. "listring[current_player;main]"..
  52. "listring[current_name;src]"..
  53. "listring[current_player;main]"
  54. return formspec
  55. end
  56. local function controller_index_slot(pos, visualid)
  57. return {
  58. drawer_pos_x = pos.x,
  59. drawer_pos_y = pos.y,
  60. drawer_pos_z = pos.z,
  61. visualid = visualid
  62. }
  63. end
  64. local function compare_pos(pos1, pos2)
  65. return pos1.x == pos2.x and pos1.y == pos2.y and pos1.z == pos2.z
  66. end
  67. local function contains_pos(list, p)
  68. for _,v in ipairs(list) do
  69. if compare_pos(v, p) then
  70. return true
  71. end
  72. end
  73. return false
  74. end
  75. -- iterator for iterating from 1 -> to
  76. local function range(to)
  77. local i = 0
  78. return function()
  79. if i == to then
  80. return nil
  81. end
  82. i = i + 1
  83. return i, i
  84. end
  85. end
  86. local function pos_in_range(pos1, pos2)
  87. local diff = {
  88. pos1.x - pos2.x,
  89. pos1.y - pos2.y,
  90. pos1.z - pos2.z
  91. }
  92. for _,v in ipairs(diff) do
  93. if v < 0 then
  94. v = v * -1
  95. end
  96. if v > drawers.CONTROLLER_RANGE then
  97. return false
  98. end
  99. end
  100. return true
  101. end
  102. local function add_drawer_to_inventory(controllerInventory, pos)
  103. -- the number of slots is saved as drawer group
  104. local slots = core.get_item_group(core.get_node(pos).name, "drawer")
  105. if not slots then
  106. return
  107. end
  108. local meta = core.get_meta(pos)
  109. if not meta then
  110. return
  111. end
  112. local i = 1
  113. while i <= slots do
  114. -- nothing is appended in case the drawer has only one slot
  115. local slot_id = ""
  116. if slots ~= 1 then
  117. slot_id = tostring(i)
  118. end
  119. local item_id = meta:get_string("name" .. slot_id)
  120. local drawer_meta_entity_infotext = meta:get_string("entity_infotext" .. slot_id)
  121. if item_id == "" and not controllerInventory["empty"] then
  122. controllerInventory["empty"] = controller_index_slot(pos, slot_id)
  123. elseif item_id ~= "" then
  124. -- If we already indexed this item previously, check which drawer
  125. -- has the most space and have that one be the one indexed
  126. if controllerInventory[item_id] then
  127. local indexed_drawer_meta = core.get_meta({
  128. x = controllerInventory[item_id]["drawer_pos_x"],
  129. y = controllerInventory[item_id]["drawer_pos_y"],
  130. z = controllerInventory[item_id]["drawer_pos_z"]}
  131. )
  132. local indexed_drawer_meta_count = indexed_drawer_meta:get_int(
  133. "count" .. controllerInventory[item_id]["visualid"])
  134. local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int(
  135. "max_count" .. controllerInventory[item_id]["visualid"])
  136. local drawer_meta_count = meta:get_int("count" .. slot_id)
  137. local drawer_meta_max_count = meta:get_int("max_count" .. slot_id)
  138. -- If the already indexed drawer has less space, we override the table index for that item with the new drawer
  139. if (indexed_drawer_meta_max_count - indexed_drawer_meta_count)
  140. < (drawer_meta_max_count - drawer_meta_count) then
  141. controllerInventory[item_id] = controller_index_slot(pos, slot_id)
  142. end
  143. else
  144. controllerInventory[item_id] = controller_index_slot(pos, slot_id)
  145. end
  146. end
  147. i = i + 1
  148. end
  149. end
  150. local function find_connected_drawers(controller_pos, pos, foundPositions)
  151. foundPositions = foundPositions or {}
  152. pos = pos or controller_pos
  153. local newPositions = core.find_nodes_in_area(
  154. {x = pos.x - 1, y = pos.y - 1, z = pos.z - 1},
  155. {x = pos.x + 1, y = pos.y + 1, z = pos.z + 1},
  156. {"group:drawer", "group:drawer_connector"}
  157. )
  158. for _,p in ipairs(newPositions) do
  159. -- check that this node hasn't been scanned yet
  160. if not compare_pos(pos, p) and not contains_pos(foundPositions, p)
  161. and pos_in_range(controller_pos, pos) then
  162. -- add new position
  163. table.insert(foundPositions, p)
  164. -- search for other drawers from the new pos
  165. find_connected_drawers(controller_pos, p, foundPositions)
  166. end
  167. end
  168. return foundPositions
  169. end
  170. local function index_drawers(pos)
  171. --[[
  172. The pos parameter is the controllers position
  173. We store the item name as a string key and the value is a table with position x,
  174. position y, position z, and visualid. Those are all strings as well with the
  175. values assigned to them that way we don't need to worry about the ordering of
  176. the table. The count and max count are not stored as those values have a high
  177. potential of being outdated quickly. It's better to grab the values from the
  178. drawer when needed so you know you are working with accurate numbers.
  179. ]]
  180. local controllerInventory = {}
  181. for _,drawerPos in ipairs(find_connected_drawers(pos)) do
  182. add_drawer_to_inventory(controllerInventory, drawerPos)
  183. end
  184. return controllerInventory
  185. end
  186. local function controller_node_timer(pos, elapsed)
  187. -- Inizialize metadata
  188. local meta = core.get_meta(pos)
  189. local meta_current_state = meta:get_string("current_state")
  190. local meta_times_ran_while_jammed = meta:get_float("times_ran_while_jammed")
  191. local meta_jammed_item_name = meta:get_string("jammed_item_name")
  192. local inv = meta:get_inventory()
  193. local src = inv:get_stack("src", 1)
  194. local src_name = src:get_name()
  195. --[[
  196. There are four scenarios for the item slot in the controller.
  197. 1: No item is in the controller.
  198. 2: Item is not stackable.
  199. 3. Item is allowed and there is either an existing drawer for that item with room or an empty drawer.
  200. 4: Item is allowed, but there is no room.
  201. There are three different possibilities for "current_state".
  202. 1: "running" which means means it's operating normally.
  203. 2: "stopped" meaning the controller makes no attempt to put in the item possibly due to being unallowed for various reasons.
  204. 3: "jammed" meaning the item is allowed in to drawers, but there was no space to deposit it last time it ran.
  205. ]]
  206. --[[
  207. If current state is jammed, the item that jammed it is the same item in the
  208. src inv slot, and the amount of times ran while jammed is 8 or higher, we
  209. set the current state to stopped. Will possibly want to make an option in the
  210. formspec to ignore this an continue running if the user plans on using the
  211. system in a way that may cause frequent jams making it a hassle to manually
  212. clear it each time
  213. ]]
  214. if meta_current_state == "jammed" and meta_jammed_item_name == src_name and meta_times_ran_while_jammed >= 2 then
  215. meta:set_string("current_state", "stopped")
  216. meta:set_string("formspec", controller_formspec(pos, S("Stopped")))
  217. return true
  218. end
  219. -- If current state is stopped, and the item that jammed it is the same
  220. -- item in the src inv slot, we don't do anything
  221. if meta_current_state == "stopped" and meta_jammed_item_name == src_name then
  222. return true
  223. end
  224. -- If current state is stopped, and the item that jammed it is not the
  225. -- same item in the src inv slot, we set the current state to running and
  226. -- clear the jam counter.
  227. if meta_current_state == "stopped" and meta_jammed_item_name ~= src_name then
  228. meta:set_string("current_state", "running")
  229. meta:set_string("formspec", controller_formspec(pos, S("Running")))
  230. meta:set_float("times_ran_while_jammed", 0)
  231. end
  232. -- If no item is in the controller, nothing is searched and current_state
  233. -- is set to running and no jams.
  234. if inv:is_empty("src") then
  235. meta:set_string("current_state", "running")
  236. meta:set_string("formspec", controller_formspec(pos, S("Running")))
  237. meta:set_float("times_ran_while_jammed", 0)
  238. return true
  239. end
  240. -- If a non stackable item is in the controller, such as a written book,
  241. -- set the current_state to stopped because they are not allowed in drawers
  242. if src:get_stack_max() == 1 then
  243. meta:set_string("current_state", "stopped")
  244. meta:set_string("formspec", controller_formspec(pos, S("Stopped")))
  245. meta:set_string("jammed_item_name", src_name)
  246. meta:set_float("times_ran_while_jammed", 1)
  247. return true
  248. end
  249. -- If the index has not been created, the item isn't in the index, the
  250. -- item in the drawer is no longer the same item in the index, or the item
  251. -- is in the index but it's full, run the index_drawers function.
  252. local drawers_table_index = core.deserialize(meta:get_string("drawers_table_index"))
  253. -- If the index has not been created
  254. if not drawers_table_index then
  255. drawers_table_index = index_drawers(pos)
  256. meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
  257. -- If the item isn't in the index
  258. elseif not drawers_table_index[src_name] then
  259. drawers_table_index = index_drawers(pos)
  260. meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
  261. -- If the item is in the index but either the name that was indexed is not
  262. -- the same as what is currently in the drawer or the drawer is full
  263. elseif drawers_table_index[src_name] then
  264. local visualid = drawers_table_index[src_name]["visualid"]
  265. local indexed_drawer_meta = core.get_meta({x = drawers_table_index[src_name]["drawer_pos_x"], y = drawers_table_index[src_name]["drawer_pos_y"], z = drawers_table_index[src_name]["drawer_pos_z"]})
  266. local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
  267. local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. visualid)
  268. local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. visualid)
  269. if indexed_drawer_meta_name ~= src_name or indexed_drawer_meta_count >= indexed_drawer_meta_max_count then
  270. drawers_table_index = index_drawers(pos)
  271. meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
  272. end
  273. end
  274. -- This might not be needed, but my concern is if the above indexing takes
  275. -- enough time, there could be a "race condition" where the item in the src
  276. -- inventory is no longer the same item when we checked before or the
  277. -- quantity of the items changed so I'm having it grab the item stack again
  278. -- just in case.
  279. -- If a race condition does occur, items could be lost or duplicated
  280. src = inv:get_stack("src", 1)
  281. src_name = src:get_name()
  282. local src_count = src:get_count()
  283. local src_stack_max = src:get_stack_max()
  284. -- At this point, the item either was in the index or everything was reindexed so we check again
  285. -- If there is a drawer with the item and it isn't full, we will put the items we can in to it
  286. if drawers_table_index[src_name] then
  287. local indexed_drawer_pos = {x = drawers_table_index[src_name]["drawer_pos_x"], y = drawers_table_index[src_name]["drawer_pos_y"], z = drawers_table_index[src_name]["drawer_pos_z"]}
  288. local visualid = drawers_table_index[src_name]["visualid"]
  289. local indexed_drawer_meta = core.get_meta(indexed_drawer_pos)
  290. local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
  291. local indexed_drawer_meta_count = indexed_drawer_meta:get_int("count" .. visualid)
  292. local indexed_drawer_meta_max_count = indexed_drawer_meta:get_int("max_count" .. visualid)
  293. -- If the the item in the drawer is the same as the one we are trying to store, the drawer is not full, and the drawer entity is loaded, we will put the items in the drawer
  294. if indexed_drawer_meta_name == src_name and indexed_drawer_meta_count < indexed_drawer_meta_max_count and drawers.drawer_visuals[core.serialize(indexed_drawer_pos)] then
  295. local leftover = drawers.drawer_insert_object(indexed_drawer_pos, nil, src, nil)
  296. inv:set_stack("src", 1, leftover)
  297. -- Set the controller metadata
  298. meta:set_string("current_state", "running")
  299. meta:set_string("formspec", controller_formspec(pos, S("Running")))
  300. meta:set_float("times_ran_while_jammed", 0)
  301. else
  302. meta:set_string("current_state", "jammed")
  303. meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
  304. meta:set_string("jammed_item_name", src_name)
  305. meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
  306. end
  307. elseif drawers_table_index["empty"] then
  308. local indexed_drawer_pos = {x = drawers_table_index["empty"]["drawer_pos_x"], y = drawers_table_index["empty"]["drawer_pos_y"], z = drawers_table_index["empty"]["drawer_pos_z"]}
  309. local visualid = drawers_table_index["empty"]["visualid"]
  310. local indexed_drawer_meta = core.get_meta(indexed_drawer_pos)
  311. local indexed_drawer_meta_name = indexed_drawer_meta:get_string("name" .. visualid)
  312. -- If the drawer is still empty and the drawer entity is loaded, we will put the items in the drawer
  313. if indexed_drawer_meta_name == "" and drawers.drawer_visuals[core.serialize(indexed_drawer_pos)] then
  314. local leftover = drawers.drawer_insert_object(indexed_drawer_pos, nil, src, nil)
  315. inv:set_stack("src", 1, leftover)
  316. -- Add the item to the drawers table index and set the empty one to nil
  317. drawers_table_index["empty"] = nil
  318. drawers_table_index[src_name] = {drawer_pos_x = indexed_drawer_pos.x, drawer_pos_y = indexed_drawer_pos.y, drawer_pos_z = indexed_drawer_pos.z, visualid = visualid}
  319. -- Set the controller metadata
  320. meta:set_string("current_state", "running")
  321. meta:set_string("formspec", controller_formspec(pos, S("Running")))
  322. meta:set_float("times_ran_while_jammed", 0)
  323. meta:set_string("drawers_table_index", core.serialize(drawers_table_index))
  324. else
  325. meta:set_string("current_state", "jammed")
  326. meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
  327. meta:set_string("jammed_item_name", src_name)
  328. meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
  329. end
  330. else
  331. meta:set_string("current_state", "jammed")
  332. meta:set_string("formspec", controller_formspec(pos, S("Jammed")))
  333. meta:set_string("jammed_item_name", src_name)
  334. meta:set_float("times_ran_while_jammed", meta_times_ran_while_jammed + 1)
  335. end
  336. return true
  337. end
  338. local function controller_can_dig(pos, player)
  339. local meta = core.get_meta(pos);
  340. local inv = meta:get_inventory()
  341. return inv:is_empty("src")
  342. end
  343. local function controller_on_construct(pos)
  344. local meta = core.get_meta(pos)
  345. meta:set_string("current_state", "running")
  346. meta:set_float("times_ran_while_jammed", 0)
  347. meta:set_string("jammed_item_name", "")
  348. meta:set_string("drawers_table_index", "")
  349. meta:set_string("formspec", controller_formspec(pos, S("Running")))
  350. meta:get_inventory():set_size("src", 1)
  351. core.get_node_timer(pos):start(controller_interval)
  352. end
  353. local function controller_on_blast(pos)
  354. local drops = {}
  355. default.get_inventory_drops(pos, "src", drops)
  356. drops[#drops+1] = "drawers:controller"
  357. core.remove_node(pos)
  358. return drops
  359. end
  360. local function controller_allow_metadata_inventory_put(pos, listname, index, stack, player)
  361. if core.is_protected(pos, player:get_player_name()) then
  362. return 0
  363. end
  364. if listname == "src" then
  365. return stack:get_count()
  366. end
  367. end
  368. local function controller_allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
  369. local meta = core.get_meta(pos)
  370. local inv = meta:get_inventory()
  371. local stack = inv:get_stack(from_list, from_index)
  372. return controller_allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
  373. end
  374. local function controller_allow_metadata_inventory_take(pos, listname, index, stack, player)
  375. if core.is_protected(pos, player:get_player_name()) then
  376. return 0
  377. end
  378. return stack:get_count()
  379. end
  380. -- Registers the drawer controller
  381. local function register_controller()
  382. -- Set the controller definition using a table to allow for pipeworks and
  383. -- potentially other mod support
  384. local def = {}
  385. def.description = S("Drawer Controller")
  386. def.drawtype = "nodebox"
  387. def.node_box = { type = "fixed", fixed = drawers.node_box_simple }
  388. def.collision_box = { type = "regular" }
  389. def.selection_box = { type = "regular" }
  390. def.paramtype = "light"
  391. def.paramtype2 = "facedir"
  392. def.legacy_facedir_simple = true
  393. -- add pipe connectors, if pipeworks is enabled
  394. if pipeworks_loaded then
  395. def.tiles = {
  396. "drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
  397. "drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
  398. "drawers_controller_side.png^pipeworks_tube_connection_metallic.png",
  399. "drawers_controller_side.png^pipeworks_tube_connection_metallic.png",
  400. "drawers_controller_top.png^pipeworks_tube_connection_metallic.png",
  401. "drawers_controller_front.png"
  402. }
  403. else
  404. def.tiles = {
  405. "drawers_controller_top.png",
  406. "drawers_controller_top.png",
  407. "drawers_controller_side.png",
  408. "drawers_controller_side.png",
  409. "drawers_controller_top.png",
  410. "drawers_controller_front.png"
  411. }
  412. end
  413. -- MCL2 requires a few different groups and parameters that MTG does not
  414. if mcl_loaded then
  415. def.groups = {
  416. pickaxey = 1, stone = 1, building_block = 1, material_stone = 1
  417. }
  418. def._mcl_blast_resistance = 30
  419. def._mcl_hardness = 1.5
  420. else
  421. def.groups = {
  422. cracky = 3, level = 2
  423. }
  424. end
  425. def.can_dig = controller_can_dig
  426. def.on_construct = controller_on_construct
  427. def.on_blast = controller_on_blast
  428. def.on_timer = controller_node_timer
  429. def.allow_metadata_inventory_put = controller_allow_metadata_inventory_put
  430. def.allow_metadata_inventory_move = controller_allow_metadata_inventory_move
  431. def.allow_metadata_inventory_take = controller_allow_metadata_inventory_take
  432. if pipeworks_loaded then
  433. def.groups.tubedevice = 1
  434. def.groups.tubedevice_receiver = 1
  435. def.tube = {}
  436. def.tube.insert_object = function(pos, node, stack, tubedir)
  437. -- add stack to inventory
  438. local remaining_stack = core.get_meta(pos):get_inventory():add_item("src", stack)
  439. -- kick off controller work
  440. controller_node_timer(pos)
  441. return remaining_stack
  442. end
  443. def.tube.can_insert = function(pos, node, stack, tubedir)
  444. return core.get_meta(pos):get_inventory():room_for_item("src", stack)
  445. end
  446. def.tube.connect_sides = {
  447. left = 1, right = 1, back = 1, top = 1, bottom = 1
  448. }
  449. def.after_place_node = pipeworks.after_place
  450. def.after_dig_node = pipeworks.after_dig
  451. end
  452. core.register_node("drawers:controller", def)
  453. end
  454. -- register drawer controller
  455. register_controller()
  456. if default_loaded then
  457. core.register_craft({
  458. output = 'drawers:controller',
  459. recipe = {
  460. {'default:steel_ingot', 'default:diamond', 'default:steel_ingot'},
  461. {'default:tin_ingot', 'group:drawer', 'default:copper_ingot'},
  462. {'default:steel_ingot', 'default:diamond', 'default:steel_ingot'},
  463. }
  464. })
  465. elseif mcl_loaded then
  466. core.register_craft({
  467. output = 'drawers:controller',
  468. recipe = {
  469. {'mcl_core:iron_ingot', 'mcl_core:diamond', 'mcl_core:iron_ingot'},
  470. {'mcl_core:gold_ingot', 'group:drawer', 'mcl_core:gold_ingot'},
  471. {'mcl_core:iron_ingot', 'mcl_core:diamond', 'mcl_core:iron_ingot'},
  472. }
  473. })
  474. else
  475. -- Because the rest of the drawers mod doesn't have a hard depend on
  476. -- default, I changed the recipe to have an alternative
  477. core.register_craft({
  478. output = 'drawers:controller',
  479. recipe = {
  480. {'group:stone', 'group:stone', 'group:stone'},
  481. {'group:stone', 'group:drawer', 'group:stone'},
  482. {'group:stone', 'group:stone', 'group:stone'},
  483. }
  484. })
  485. end