init.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  1. local specs = {
  2. normal = {
  3. offname = "mesecons_pistons:piston_normal_off",
  4. onname = "mesecons_pistons:piston_normal_on",
  5. pusher = "mesecons_pistons:piston_pusher_normal",
  6. },
  7. sticky = {
  8. offname = "mesecons_pistons:piston_sticky_off",
  9. onname = "mesecons_pistons:piston_sticky_on",
  10. pusher = "mesecons_pistons:piston_pusher_sticky",
  11. sticky = true,
  12. },
  13. }
  14. local function get_pistonspec_name(name, part)
  15. if part then
  16. for spec_name, spec in pairs(specs) do
  17. if name == spec[part] then
  18. return spec_name, part
  19. end
  20. end
  21. return
  22. end
  23. for spec_name, spec in pairs(specs) do
  24. for part, value in pairs(spec) do
  25. if name == value then
  26. return spec_name, part
  27. end
  28. end
  29. end
  30. end
  31. local function get_pistonspec(name, part)
  32. return specs[get_pistonspec_name(name, part)]
  33. end
  34. local max_push = mesecon.setting("piston_max_push", 15)
  35. local max_pull = mesecon.setting("piston_max_pull", 15)
  36. -- Get mesecon rules of pistons
  37. local function piston_get_rules(node)
  38. local dir = minetest.facedir_to_dir(node.param2)
  39. for k, v in pairs(dir) do
  40. if v ~= 0 then
  41. dir = {k, -v}
  42. break
  43. end
  44. end
  45. local rules = table.copy(mesecon.rules.default)
  46. for i, rule in ipairs(rules) do
  47. if rule[dir[1]] == dir[2] then
  48. table.remove(rules, i)
  49. end
  50. end
  51. return rules
  52. end
  53. local function piston_remove_pusher(pos, node, check_falling)
  54. local pistonspec = get_pistonspec(node.name, "onname")
  55. local dir = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
  56. local pusherpos = vector.add(pos, dir)
  57. local pushername = minetest.get_node(pusherpos).name
  58. -- make sure there actually is a pusher (for compatibility reasons mainly)
  59. if pushername ~= pistonspec.pusher then
  60. return
  61. end
  62. minetest.remove_node(pusherpos)
  63. minetest.sound_play("piston_retract", {
  64. pos = pos,
  65. max_hear_distance = 20,
  66. gain = 0.3,
  67. })
  68. if check_falling then
  69. minetest.check_for_falling(pusherpos)
  70. end
  71. end
  72. local function piston_after_dig(pos, node)
  73. piston_remove_pusher(pos, node, true)
  74. end
  75. local piston_on = function(pos, node)
  76. local pistonspec = get_pistonspec(node.name, "offname")
  77. local dir = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
  78. local pusher_pos = vector.add(pos, dir)
  79. local meta = minetest.get_meta(pos)
  80. local success, stack, oldstack = mesecon.mvps_push(pusher_pos, dir, max_push, meta:get_string("owner"))
  81. if not success then
  82. if stack == "protected" then
  83. meta:set_string("infotext", "Can't extend: protected area on the way")
  84. end
  85. return
  86. end
  87. minetest.swap_node(pos, {param2 = node.param2, name = pistonspec.onname})
  88. minetest.set_node(pusher_pos, {param2 = node.param2, name = pistonspec.pusher})
  89. minetest.sound_play("piston_extend", {
  90. pos = pos,
  91. max_hear_distance = 20,
  92. gain = 0.3,
  93. })
  94. mesecon.mvps_process_stack(stack)
  95. mesecon.mvps_move_objects(pusher_pos, dir, oldstack)
  96. end
  97. local function piston_off(pos, node)
  98. local pistonspec = get_pistonspec(node.name, "onname")
  99. minetest.swap_node(pos, {param2 = node.param2, name = pistonspec.offname})
  100. piston_remove_pusher(pos, node, not pistonspec.sticky) -- allow that even in protected area
  101. if not pistonspec.sticky then
  102. return
  103. end
  104. local dir = minetest.facedir_to_dir(node.param2)
  105. local pullpos = vector.add(pos, vector.multiply(dir, -2))
  106. local meta = minetest.get_meta(pos)
  107. local success, stack, oldstack = mesecon.mvps_pull_single(pullpos, dir, max_pull, meta:get_string("owner"))
  108. if success then
  109. mesecon.mvps_move_objects(pullpos, vector.multiply(dir, -1), oldstack, -1)
  110. end
  111. end
  112. local orientations = {
  113. [0] = { 4, 8},
  114. {13, 17},
  115. {10, 6},
  116. {20, 15},
  117. }
  118. local function piston_orientate(pos, placer)
  119. mesecon.mvps_set_owner(pos, placer)
  120. if not placer then
  121. return
  122. end
  123. local pitch = math.deg(placer:get_look_vertical())
  124. local node = minetest.get_node(pos)
  125. if pitch > 55 then
  126. node.param2 = orientations[node.param2][1]
  127. elseif pitch < -55 then
  128. node.param2 = orientations[node.param2][2]
  129. else
  130. return
  131. end
  132. minetest.swap_node(pos, node)
  133. -- minetest.after, because on_placenode for unoriented piston must be processed first
  134. minetest.after(0, mesecon.on_placenode, pos, node)
  135. end
  136. local rotations = {
  137. {0, 16, 20, 12},
  138. {2, 14, 22, 18},
  139. {1, 5, 23, 9},
  140. {3, 11, 21, 7},
  141. {4, 13, 10, 19},
  142. {6, 15, 8, 17},
  143. }
  144. local function get_rotation(param2)
  145. for a = 1, #rotations do
  146. for f = 1, #rotations[a] do
  147. if rotations[a][f] == param2 then
  148. return a, f
  149. end
  150. end
  151. end
  152. end
  153. local function rotate(param2, mode)
  154. local axis, face = get_rotation(param2)
  155. if mode == screwdriver.ROTATE_FACE then
  156. face = face + 1
  157. if face > 4 then
  158. face = 1
  159. end
  160. elseif mode == screwdriver.ROTATE_AXIS then
  161. axis = axis + 1
  162. if axis > 6 then
  163. axis = 1
  164. end
  165. face = 1
  166. else
  167. return param2
  168. end
  169. return rotations[axis][face]
  170. end
  171. local function piston_rotate(pos, node, _, mode)
  172. node.param2 = rotate(node.param2, mode)
  173. minetest.swap_node(pos, node)
  174. mesecon.execute_autoconnect_hooks_now(pos, node)
  175. return true
  176. end
  177. local function piston_rotate_on(pos, node, player, mode)
  178. local pistonspec = get_pistonspec(node.name, "onname")
  179. local dir = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
  180. local pusher_pos = vector.add(dir, pos)
  181. local pusher_node = minetest.get_node(pusher_pos)
  182. if pusher_node.name ~= pistonspec.pusher then
  183. return piston_rotate(pos, node, nil, mode)
  184. end
  185. if mode == screwdriver.ROTATE_FACE then
  186. piston_rotate(pusher_pos, pusher_node, nil, mode)
  187. return piston_rotate(pos, node, nil, mode)
  188. elseif mode ~= screwdriver.ROTATE_AXIS then
  189. return false
  190. end
  191. local player_name = player and player:is_player() and player:get_player_name() or ""
  192. local ok, dir_after, pusher_pos_after
  193. for i = 1, 5 do
  194. node.param2 = rotate(node.param2, mode)
  195. dir_after = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
  196. pusher_pos_after = vector.add(dir_after, pos)
  197. local pusher_pos_after_node_name = minetest.get_node(pusher_pos_after).name
  198. local pusher_pos_after_node_def = minetest.registered_nodes[pusher_pos_after_node_name]
  199. if pusher_pos_after_node_def and pusher_pos_after_node_def.buildable_to and
  200. not minetest.is_protected(pusher_pos_after, player_name) then
  201. ok = true
  202. break
  203. end
  204. end
  205. if not ok then
  206. return false
  207. end
  208. pusher_node.param2 = node.param2
  209. minetest.remove_node(pusher_pos)
  210. minetest.set_node(pusher_pos_after, pusher_node)
  211. minetest.swap_node(pos, node)
  212. mesecon.execute_autoconnect_hooks_now(pos, node)
  213. return true
  214. end
  215. local function piston_rotate_pusher(pos, node, player, mode)
  216. local pistonspec = get_pistonspec(node.name, "pusher")
  217. local piston_pos = vector.add(pos, minetest.facedir_to_dir(node.param2))
  218. local piston_node = minetest.get_node(piston_pos)
  219. if piston_node.name ~= pistonspec.onname then
  220. minetest.remove_node(pos) -- Make it possible to remove alone pushers.
  221. return false
  222. end
  223. return piston_rotate_on(piston_pos, piston_node, player, mode)
  224. end
  225. local function piston_punch(pos, node, player)
  226. local player_name = player and player.get_player_name and player:get_player_name()
  227. if mesecon.mvps_claim(pos, player_name) then
  228. minetest.chat_send_player(player_name, "Reclaimed piston")
  229. end
  230. end
  231. -- Boxes:
  232. local pt = 3/16 -- pusher thickness
  233. local piston_pusher_box = {
  234. type = "fixed",
  235. fixed = {
  236. {-2/16, -2/16, -.5 + pt, 2/16, 2/16, .5 + pt},
  237. {-.5 , -.5 , -.5 , .5 , .5 , -.5 + pt},
  238. },
  239. }
  240. local piston_on_box = {
  241. type = "fixed",
  242. fixed = {
  243. {-.5, -.5, -.5 + pt, .5, .5, .5}
  244. },
  245. }
  246. -- Normal (non-sticky) Pistons:
  247. -- offstate
  248. minetest.register_node("mesecons_pistons:piston_normal_off", {
  249. description = "Piston",
  250. tiles = {
  251. "mesecons_piston_top.png",
  252. "mesecons_piston_bottom.png",
  253. "mesecons_piston_left.png",
  254. "mesecons_piston_right.png",
  255. "mesecons_piston_back.png",
  256. "mesecons_piston_pusher_front.png"
  257. },
  258. groups = {cracky = 3},
  259. paramtype2 = "facedir",
  260. is_ground_content = false,
  261. after_place_node = piston_orientate,
  262. sounds = default.node_sound_wood_defaults(),
  263. mesecons = {effector={
  264. action_on = piston_on,
  265. rules = piston_get_rules,
  266. }},
  267. on_punch = piston_punch,
  268. on_rotate = piston_rotate,
  269. on_blast = mesecon.on_blastnode,
  270. })
  271. -- onstate
  272. minetest.register_node("mesecons_pistons:piston_normal_on", {
  273. description = "Activated Piston Base",
  274. drawtype = "nodebox",
  275. tiles = {
  276. "mesecons_piston_top.png",
  277. "mesecons_piston_bottom.png",
  278. "mesecons_piston_left.png",
  279. "mesecons_piston_right.png",
  280. "mesecons_piston_back.png",
  281. "mesecons_piston_on_front.png"
  282. },
  283. groups = {cracky = 3, not_in_creative_inventory = 1},
  284. paramtype = "light",
  285. paramtype2 = "facedir",
  286. is_ground_content = false,
  287. drop = "mesecons_pistons:piston_normal_off",
  288. after_dig_node = piston_after_dig,
  289. node_box = piston_on_box,
  290. selection_box = piston_on_box,
  291. sounds = default.node_sound_wood_defaults(),
  292. mesecons = {effector={
  293. action_off = piston_off,
  294. rules = piston_get_rules,
  295. }},
  296. on_rotate = piston_rotate_on,
  297. on_blast = mesecon.on_blastnode,
  298. })
  299. -- pusher
  300. minetest.register_node("mesecons_pistons:piston_pusher_normal", {
  301. description = "Piston Pusher",
  302. drawtype = "nodebox",
  303. tiles = {
  304. "mesecons_piston_pusher_top.png",
  305. "mesecons_piston_pusher_bottom.png",
  306. "mesecons_piston_pusher_left.png",
  307. "mesecons_piston_pusher_right.png",
  308. "mesecons_piston_pusher_back.png",
  309. "mesecons_piston_pusher_front.png"
  310. },
  311. groups = {not_in_creative_inventory = 1},
  312. paramtype = "light",
  313. paramtype2 = "facedir",
  314. is_ground_content = false,
  315. diggable = false,
  316. selection_box = piston_pusher_box,
  317. node_box = piston_pusher_box,
  318. on_rotate = piston_rotate_pusher,
  319. drop = "",
  320. sounds = default.node_sound_wood_defaults(),
  321. })
  322. -- Sticky ones
  323. -- offstate
  324. minetest.register_node("mesecons_pistons:piston_sticky_off", {
  325. description = "Sticky Piston",
  326. tiles = {
  327. "mesecons_piston_top.png",
  328. "mesecons_piston_bottom.png",
  329. "mesecons_piston_left.png",
  330. "mesecons_piston_right.png",
  331. "mesecons_piston_back.png",
  332. "mesecons_piston_pusher_front_sticky.png"
  333. },
  334. groups = {cracky = 3},
  335. paramtype2 = "facedir",
  336. is_ground_content = false,
  337. after_place_node = piston_orientate,
  338. sounds = default.node_sound_wood_defaults(),
  339. mesecons = {effector={
  340. action_on = piston_on,
  341. rules = piston_get_rules,
  342. }},
  343. on_punch = piston_punch,
  344. on_rotate = piston_rotate,
  345. on_blast = mesecon.on_blastnode,
  346. })
  347. -- onstate
  348. minetest.register_node("mesecons_pistons:piston_sticky_on", {
  349. description = "Activated Sticky Piston Base",
  350. drawtype = "nodebox",
  351. tiles = {
  352. "mesecons_piston_top.png",
  353. "mesecons_piston_bottom.png",
  354. "mesecons_piston_left.png",
  355. "mesecons_piston_right.png",
  356. "mesecons_piston_back.png",
  357. "mesecons_piston_on_front.png"
  358. },
  359. groups = {cracky = 3, not_in_creative_inventory = 1},
  360. paramtype = "light",
  361. paramtype2 = "facedir",
  362. is_ground_content = false,
  363. drop = "mesecons_pistons:piston_sticky_off",
  364. after_dig_node = piston_after_dig,
  365. node_box = piston_on_box,
  366. selection_box = piston_on_box,
  367. sounds = default.node_sound_wood_defaults(),
  368. mesecons = {effector={
  369. action_off = piston_off,
  370. rules = piston_get_rules,
  371. }},
  372. on_rotate = piston_rotate_on,
  373. on_blast = mesecon.on_blastnode,
  374. })
  375. -- pusher
  376. minetest.register_node("mesecons_pistons:piston_pusher_sticky", {
  377. description = "Sticky Piston Pusher",
  378. drawtype = "nodebox",
  379. tiles = {
  380. "mesecons_piston_pusher_top.png",
  381. "mesecons_piston_pusher_bottom.png",
  382. "mesecons_piston_pusher_left.png",
  383. "mesecons_piston_pusher_right.png",
  384. "mesecons_piston_pusher_back.png",
  385. "mesecons_piston_pusher_front_sticky.png"
  386. },
  387. groups = {not_in_creative_inventory = 1},
  388. paramtype = "light",
  389. paramtype2 = "facedir",
  390. is_ground_content = false,
  391. diggable = false,
  392. selection_box = piston_pusher_box,
  393. node_box = piston_pusher_box,
  394. on_rotate = piston_rotate_pusher,
  395. drop = "",
  396. sounds = default.node_sound_wood_defaults(),
  397. })
  398. -- Register pushers as stoppers if they would be seperated from the piston
  399. local function piston_pusher_get_stopper(node, dir, stack, stackid)
  400. if (stack[stackid + 1]
  401. and stack[stackid + 1].node.name == get_pistonspec(node.name, "pusher").onname
  402. and stack[stackid + 1].node.param2 == node.param2)
  403. or (stack[stackid - 1]
  404. and stack[stackid - 1].node.name == get_pistonspec(node.name, "pusher").onname
  405. and stack[stackid - 1].node.param2 == node.param2) then
  406. return false
  407. end
  408. return true
  409. end
  410. local function piston_pusher_up_down_get_stopper(node, dir, stack, stackid)
  411. if (stack[stackid + 1]
  412. and stack[stackid + 1].node.name == get_pistonspec(node.name, "pusher").onname)
  413. or (stack[stackid - 1]
  414. and stack[stackid - 1].node.name == get_pistonspec(node.name, "pusher").onname) then
  415. return false
  416. end
  417. return true
  418. end
  419. mesecon.register_mvps_stopper("mesecons_pistons:piston_pusher_normal", piston_pusher_get_stopper)
  420. mesecon.register_mvps_stopper("mesecons_pistons:piston_pusher_sticky", piston_pusher_get_stopper)
  421. -- Register pistons as stoppers if they would be seperated from the stopper
  422. local piston_up_down_get_stopper = function (node, dir, stack, stackid)
  423. if (stack[stackid + 1]
  424. and stack[stackid + 1].node.name == get_pistonspec(node.name, "onname").pusher)
  425. or (stack[stackid - 1]
  426. and stack[stackid - 1].node.name == get_pistonspec(node.name, "onname").pusher) then
  427. return false
  428. end
  429. return true
  430. end
  431. local function piston_get_stopper(node, dir, stack, stackid)
  432. local pistonspec = get_pistonspec(node.name, "onname")
  433. local dir = vector.multiply(minetest.facedir_to_dir(node.param2), -1)
  434. local pusherpos = vector.add(stack[stackid].pos, dir)
  435. local pushernode = minetest.get_node(pusherpos)
  436. if pistonspec.pusher == pushernode.name then
  437. for _, s in ipairs(stack) do
  438. if vector.equals(s.pos, pusherpos) -- pusher is also to be pushed
  439. and s.node.param2 == node.param2 then
  440. return false
  441. end
  442. end
  443. end
  444. return true
  445. end
  446. mesecon.register_mvps_stopper("mesecons_pistons:piston_normal_on", piston_get_stopper)
  447. mesecon.register_mvps_stopper("mesecons_pistons:piston_sticky_on", piston_get_stopper)
  448. --craft recipes
  449. minetest.register_craft({
  450. output = "mesecons_pistons:piston_normal_off 2",
  451. recipe = {
  452. {"group:wood", "group:wood", "group:wood"},
  453. {"default:cobble", "default:steel_ingot", "default:cobble"},
  454. {"default:cobble", "group:mesecon_conductor_craftable", "default:cobble"},
  455. }
  456. })
  457. minetest.register_craft({
  458. output = "mesecons_pistons:piston_sticky_off",
  459. recipe = {
  460. {"mesecons_materials:glue"},
  461. {"mesecons_pistons:piston_normal_off"},
  462. }
  463. })
  464. -- load legacy code
  465. dofile(minetest.get_modpath("mesecons_pistons")..DIR_DELIM.."legacy.lua")