pushing_chest.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. --[[
  2. Tubelib Addons 3
  3. ================
  4. Copyright (C) 2017-2020 Joachim Stolberg
  5. AGPL v3
  6. See LICENSE.txt for more information
  7. pushing_chest.lua
  8. A high performance pushing chest
  9. ]]--
  10. -- Load support for I18n
  11. local S = tubelib_addons3.S
  12. -- tubelib aging feature
  13. local AGING_LEVEL1 = 50 * tubelib.machine_aging_value
  14. local AGING_LEVEL2 = 150 * tubelib.machine_aging_value
  15. local Cache = {}
  16. local function allow_metadata_inventory_put(pos, listname, index, stack, player)
  17. if minetest.is_protected(pos, player:get_player_name()) then
  18. return 0
  19. end
  20. Cache[minetest.get_meta(pos):get_string("number")] = nil
  21. minetest.log("action", player:get_player_name().." moves "..stack:get_name()..
  22. " to chest at "..minetest.pos_to_string(pos))
  23. return stack:get_count()
  24. end
  25. local function allow_metadata_inventory_take(pos, listname, index, stack, player)
  26. if minetest.is_protected(pos, player:get_player_name()) then
  27. return 0
  28. end
  29. Cache[minetest.get_meta(pos):get_string("number")] = nil
  30. minetest.log("action", player:get_player_name().." takes "..stack:get_name()..
  31. " from chest at "..minetest.pos_to_string(pos))
  32. return stack:get_count()
  33. end
  34. local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
  35. if minetest.is_protected(pos, player:get_player_name()) then
  36. return 0
  37. end
  38. Cache[minetest.get_meta(pos):get_string("number")] = nil
  39. return count
  40. end
  41. local function keep_the_rest(meta, list, taken)
  42. if taken then
  43. local inv = meta:get_inventory()
  44. local rest = ItemStack(taken:get_name())
  45. if not inv:contains_item(list, rest) then
  46. inv:add_item(list, rest)
  47. if taken:get_count() > 1 then
  48. taken:set_count(taken:get_count() - 1)
  49. return taken
  50. end
  51. else
  52. return taken
  53. end
  54. end
  55. end
  56. local function aging(pos, meta)
  57. local cnt = meta:get_int("tubelib_aging") + 1
  58. meta:set_int("tubelib_aging", cnt)
  59. if cnt > AGING_LEVEL1 and math.random(AGING_LEVEL2) == 1 then
  60. minetest.get_node_timer(pos):stop()
  61. local node = minetest.get_node(pos)
  62. node.name = "tubelib_addons3:pushing_chest_defect"
  63. minetest.swap_node(pos, node)
  64. end
  65. end
  66. local function set_state(meta, state)
  67. local number = meta:get_string("number")
  68. meta:set_string("infotext", S("HighPerf Pushing Chest").." "..number..": "..state)
  69. meta:set_string("state", state)
  70. end
  71. local function configured(pos, item)
  72. local meta = minetest.get_meta(pos)
  73. local inv = meta:get_inventory()
  74. local number = meta:get_string("number")
  75. if not Cache[number] then
  76. Cache[number] = {}
  77. for _,items in ipairs(inv:get_list("main")) do
  78. Cache[number][items:get_name()] = true
  79. end
  80. end
  81. return Cache[number][item:get_name()] == true
  82. end
  83. local function shift_items(pos, elapsed)
  84. if tubelib.data_not_corrupted(pos) then
  85. local meta = minetest.get_meta(pos)
  86. local inv = meta:get_inventory()
  87. if not inv:is_empty("shift") then
  88. local number = meta:get_string("number")
  89. local player_name = meta:get_string("player_name")
  90. local offs = meta:get_int("offs")
  91. meta:set_int("offs", offs + 1)
  92. for i = 0,7 do
  93. local idx = ((i + offs) % 8) + 1
  94. local stack = inv:get_stack("shift", idx)
  95. local count = stack:get_count()
  96. if count > 0 then
  97. if tubelib.push_items(pos, "R", stack, player_name) then
  98. -- The effort is needed here for the case the
  99. -- pusher pushes into its own chest.
  100. local num = stack:get_count()
  101. stack = inv:get_stack("shift", idx)
  102. stack:take_item(num)
  103. inv:set_stack("shift", idx, stack)
  104. aging(pos, meta)
  105. return true
  106. else
  107. -- Complete stack rejected
  108. if count == stack:get_count() then
  109. set_state(meta, "blocked")
  110. else
  111. inv:set_stack("shift", idx, stack)
  112. end
  113. end
  114. end
  115. end
  116. end
  117. return true
  118. end
  119. return false
  120. end
  121. local function formspec()
  122. return "size[9,9.2]"..
  123. default.gui_bg..
  124. default.gui_bg_img..
  125. default.gui_slots..
  126. "list[context;shift;0.5,0;8,1;]"..
  127. "list[context;main;0.5,1.2;8,4;]"..
  128. "image[0.5,0;1,1;tubelib_gui_arrow.png]"..
  129. "image[7.5,0;1,1;tubelib_gui_arrow.png]"..
  130. "list[current_player;main;0.5,5.5;8,4;]"..
  131. "image[0.5,1.2;1,1;tubelib_gui_arrow.png^[transformR270]"..
  132. "listring[context;main]"..
  133. "listring[current_player;main]"
  134. end
  135. -- necessary function for a quick bugfix, doubles some code from local function "aging"
  136. local function defect(pos, meta)
  137. minetest.get_node_timer(pos):stop()
  138. local node = minetest.get_node(pos)
  139. node.name = "tubelib_addons3:pushing_chest_defect"
  140. minetest.swap_node(pos, node)
  141. return true
  142. end
  143. -- code duplication of method "NodeStates:on_dig_node" (node_states.lua)
  144. -- (in contrast to (all?) other tubelib nodes that can go defect class "NodeStates" isn't applied here)
  145. function on_dig_node(pos, node, player)
  146. local meta = minetest.get_meta(pos)
  147. local cnt = tonumber(meta:get_string("tubelib_aging"))
  148. if (not cnt or cnt < 1) then
  149. cnt = 1
  150. end
  151. local is_defect = (cnt > AGING_LEVEL1) and ( math.random(math.max(1, math.floor(AGING_LEVEL2 / cnt))) == 1 )
  152. if is_defect then
  153. defect(pos, meta) -- replace node with defect one
  154. node = minetest.get_node(pos)
  155. end
  156. minetest.node_dig(pos, node, player) -- default behaviour (this function is called automatically if on_dig() callback isn't set)
  157. end
  158. minetest.register_node("tubelib_addons3:pushing_chest", {
  159. description = S("HighPerf Pushing Chest"),
  160. tiles = {
  161. -- up, down, right, left, back, front
  162. {
  163. image = "tubelib_addons3_pusher_active.png",
  164. backface_culling = false,
  165. animation = {
  166. type = "vertical_frames",
  167. aspect_w = 32,
  168. aspect_h = 32,
  169. length = 2.0,
  170. },
  171. },
  172. 'tubelib_addons3_chest_bottom.png',
  173. "tubelib_addons3_chest_out.png",
  174. "tubelib_addons3_chest_side.png",
  175. "tubelib_addons3_chest_side.png",
  176. "tubelib_addons3_chest_front.png",
  177. },
  178. on_construct = function(pos)
  179. local meta = minetest.get_meta(pos)
  180. local inv = meta:get_inventory()
  181. inv:set_size('main', 32)
  182. inv:set_size('shift', 8)
  183. end,
  184. after_place_node = function(pos, placer)
  185. local meta = minetest.get_meta(pos)
  186. local number = tubelib.add_node(pos, "tubelib_addons3:pushing_chest")
  187. meta:set_string("player_name", placer:get_player_name())
  188. meta:set_string("number", number)
  189. meta:set_string("formspec", formspec())
  190. set_state(meta, "empty")
  191. minetest.get_node_timer(pos):start(2)
  192. end,
  193. can_dig = function(pos, player)
  194. if minetest.is_protected(pos, player:get_player_name()) then
  195. return false
  196. end
  197. local meta = minetest.get_meta(pos)
  198. local inv = meta:get_inventory()
  199. return inv:is_empty("main") and inv:is_empty("shift")
  200. end,
  201. on_dig = function(pos, node, player)
  202. on_dig_node(pos, node, player)
  203. tubelib.remove_node(pos)
  204. end,
  205. allow_metadata_inventory_put = allow_metadata_inventory_put,
  206. allow_metadata_inventory_take = allow_metadata_inventory_take,
  207. allow_metadata_inventory_move = allow_metadata_inventory_move,
  208. on_timer = shift_items,
  209. on_rotate = screwdriver.disallow,
  210. paramtype = "light",
  211. sunlight_propagates = true,
  212. paramtype2 = "facedir",
  213. groups = {choppy=2, cracky=2, crumbly=2},
  214. is_ground_content = false,
  215. sounds = default.node_sound_wood_defaults(),
  216. })
  217. minetest.register_node("tubelib_addons3:pushing_chest_defect", {
  218. description = S("HighPerf Pushing Chest"),
  219. tiles = {
  220. -- up, down, right, left, back, front
  221. 'tubelib_pusher1.png^tubelib_addons3_node_frame4.png',
  222. 'tubelib_addons3_chest_bottom.png',
  223. "tubelib_addons3_chest_out.png^tubelib_defect.png",
  224. "tubelib_addons3_chest_side.png^tubelib_defect.png",
  225. "tubelib_addons3_chest_side.png^tubelib_defect.png",
  226. "tubelib_addons3_chest_front.png^tubelib_defect.png",
  227. },
  228. on_construct = function(pos)
  229. local meta = minetest.get_meta(pos)
  230. local inv = meta:get_inventory()
  231. inv:set_size('main', 32)
  232. inv:set_size('shift', 8)
  233. end,
  234. after_place_node = function(pos, placer)
  235. local meta = minetest.get_meta(pos)
  236. local number = tubelib.add_node(pos, "tubelib_addons3:pushing_chest")
  237. meta:set_string("player_name", placer:get_player_name())
  238. meta:set_string("number", number)
  239. meta:set_string("formspec", formspec())
  240. set_state(meta, "empty")
  241. end,
  242. can_dig = function(pos, player)
  243. if minetest.is_protected(pos, player:get_player_name()) then
  244. return false
  245. end
  246. local inv = minetest.get_meta(pos):get_inventory()
  247. return inv:is_empty("main") and inv:is_empty("shift")
  248. end,
  249. after_dig_node = function(pos, oldnode, oldmetadata, digger)
  250. tubelib.remove_node(pos)
  251. end,
  252. allow_metadata_inventory_put = allow_metadata_inventory_put,
  253. allow_metadata_inventory_take = allow_metadata_inventory_take,
  254. allow_metadata_inventory_move = allow_metadata_inventory_move,
  255. on_rotate = screwdriver.disallow,
  256. paramtype = "light",
  257. sunlight_propagates = true,
  258. paramtype2 = "facedir",
  259. groups = {choppy=2, cracky=2, crumbly=2, not_in_creative_inventory=1},
  260. is_ground_content = false,
  261. sounds = default.node_sound_wood_defaults(),
  262. })
  263. minetest.register_craft({
  264. output = "tubelib_addons3:pushing_chest",
  265. recipe = {
  266. {"default:tin_ingot", "tubelib_addons3:pusher", ""},
  267. {"tubelib_addons1:chest", "default:gold_ingot", ""},
  268. {"", "", ""},
  269. },
  270. })
  271. tubelib.register_node("tubelib_addons3:pushing_chest",
  272. {"tubelib_addons3:pushing_chest_defect"}, {
  273. on_recv_message = function(pos, topic, payload)
  274. local node = minetest.get_node(pos)
  275. if topic == "state" then
  276. local meta = minetest.get_meta(pos)
  277. local inv = meta:get_inventory()
  278. if inv:is_empty("main") then
  279. return "empty"
  280. end
  281. return meta:get_string("state")
  282. elseif topic == "aging" then
  283. return minetest.get_meta(pos):get_int("tubelib_aging")
  284. else
  285. return "not supported"
  286. end
  287. end,
  288. on_pull_stack = function(pos, side)
  289. local meta = minetest.get_meta(pos)
  290. local taken = tubelib.get_stack(meta, "main")
  291. return keep_the_rest(meta, "main", taken)
  292. end,
  293. on_pull_item = function(pos, side)
  294. local meta = minetest.get_meta(pos)
  295. local items = tubelib.get_num_items(meta, "main", 2)
  296. if items then
  297. -- return only one
  298. items:set_count(1)
  299. -- don't remove the potentally last item (recipe)
  300. tubelib.put_item(meta, "main", items)
  301. return items
  302. end
  303. end,
  304. on_push_item = function(pos, side, item)
  305. local meta = minetest.get_meta(pos)
  306. if configured(pos, item) then
  307. if tubelib.put_item(meta, "main", item) then
  308. set_state(meta, "loaded")
  309. return true
  310. else
  311. set_state(meta, "full")
  312. return tubelib.put_item(meta, "shift", item)
  313. end
  314. else
  315. return tubelib.put_item(meta, "shift", item)
  316. end
  317. end,
  318. on_unpull_item = function(pos, side, item)
  319. local meta = minetest.get_meta(pos)
  320. return tubelib.put_item(meta, "main", item)
  321. end,
  322. on_node_load = function(pos)
  323. minetest.get_node_timer(pos):start(2)
  324. end,
  325. on_node_repair = function(pos)
  326. local meta = minetest.get_meta(pos)
  327. meta:set_int("tubelib_aging", 0)
  328. meta:set_int("idx", 2)
  329. meta:set_string("formspec", formspec())
  330. set_state(meta, "empty")
  331. local node = minetest.get_node(pos)
  332. node.name = "tubelib_addons3:pushing_chest"
  333. minetest.swap_node(pos, node)
  334. minetest.get_node_timer(pos):start(2)
  335. return true
  336. end,
  337. })