functions.lua 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. -- returns an object to a door object or nil
  2. function doors.get(pos)
  3. local node_name = minetest.get_node(pos).name
  4. if doors.registered_doors[node_name] then
  5. -- A normal upright door
  6. return {
  7. pos = pos,
  8. open = function(self, player)
  9. if self:state() then
  10. return false
  11. end
  12. return doors.door_toggle(self.pos, nil, player)
  13. end,
  14. close = function(self, player)
  15. if not self:state() then
  16. return false
  17. end
  18. return doors.door_toggle(self.pos, nil, player)
  19. end,
  20. toggle = function(self, player)
  21. return doors.door_toggle(self.pos, nil, player)
  22. end,
  23. state = function(self)
  24. local state = minetest.get_meta(self.pos):get_int('state')
  25. return state %2 == 1
  26. end
  27. }
  28. elseif doors.registered_trapdoors[node_name] then
  29. -- A trapdoor
  30. return {
  31. pos = pos,
  32. open = function(self, player)
  33. if self:state() then
  34. return false
  35. end
  36. return doors.trapdoor_toggle(self.pos, nil, player)
  37. end,
  38. close = function(self, player)
  39. if not self:state() then
  40. return false
  41. end
  42. return doors.trapdoor_toggle(self.pos, nil, player)
  43. end,
  44. toggle = function(self, player)
  45. return doors.trapdoor_toggle(self.pos, nil, player)
  46. end,
  47. state = function(self)
  48. return minetest.get_node(self.pos).name:sub(-5) == '_open'
  49. end
  50. }
  51. else
  52. return nil
  53. end
  54. end
  55. -- this hidden node is placed on top of the bottom, and prevents
  56. -- nodes from being placed in the top half of the door.
  57. minetest.register_node('doors:hidden', {
  58. drawtype = 'airlike',
  59. paramtype = 'light',
  60. paramtype2 = 'facedir',
  61. sunlight_propagates = true,
  62. -- has to be walkable for falling nodes to stop falling.
  63. --walkable = true,
  64. pointable = false,
  65. diggable = false,
  66. buildable_to = false,
  67. floodable = false,
  68. drop = '',
  69. groups = {not_in_creative_inventory = 1},
  70. collision_box = {
  71. type = "fixed",
  72. fixed = {-15/32, 13/32, -15/32, -13/32, 1/2, -13/32},
  73. },
  74. })
  75. -- table used to aid door opening/closing
  76. local transform = {
  77. {
  78. {v = '_a', param2 = 3},
  79. {v = '_a', param2 = 0},
  80. {v = '_a', param2 = 1},
  81. {v = '_a', param2 = 2},
  82. },
  83. {
  84. {v = '_c', param2 = 1},
  85. {v = '_c', param2 = 2},
  86. {v = '_c', param2 = 3},
  87. {v = '_c', param2 = 0},
  88. },
  89. {
  90. {v = '_b', param2 = 1},
  91. {v = '_b', param2 = 2},
  92. {v = '_b', param2 = 3},
  93. {v = '_b', param2 = 0},
  94. },
  95. {
  96. {v = '_d', param2 = 3},
  97. {v = '_d', param2 = 0},
  98. {v = '_d', param2 = 1},
  99. {v = '_d', param2 = 2},
  100. },
  101. }
  102. function doors.door_toggle(pos, node, clicker, close)
  103. local meta = minetest.get_meta(pos)
  104. node = node or minetest.get_node(pos)
  105. local def = minetest.registered_nodes[node.name]
  106. local name = def.door.name
  107. local player_name = clicker:get_player_name()
  108. local lock_status = meta:get_int('lock_s')
  109. if not close then
  110. local wield = clicker:get_wielded_item()
  111. local wield_name = wield:get_name()
  112. if not minetest.is_protected(pos, player_name) and minetest.check_player_privs(player_name, { creative = true }) then
  113. else
  114. local map_id = lobby.game[player_name]
  115. local sabotage_level = lobby.sabotage_level[map_id] or 5
  116. local level = meta:get_int('level') or 0
  117. if level < sabotage_level then
  118. if lock_status == 1 and (minetest.is_protected(pos, player_name) or not minetest.check_player_privs(player_name, { creative = true })) then
  119. return
  120. elseif lock_status == 2 then
  121. local key = meta:get_string('key')
  122. if wield_name ~= key then
  123. local def = minetest.registered_items[key]
  124. local key_name = def.description
  125. minetest.chat_send_player(player_name, 'This door can be opened/closed with a '..key_name..'.')
  126. return
  127. else
  128. minetest.after(3, function()
  129. doors.door_toggle(pos, nil, clicker, true)
  130. end)
  131. end
  132. elseif lock_status >= 3 then
  133. minetest.chat_send_player(player_name, 'The lock looks cheap, you might be able to pick it.')
  134. return
  135. end
  136. else
  137. minetest.chat_send_player(player_name, 'This door can\'t be used right now.')
  138. return
  139. end
  140. end
  141. end
  142. local state = meta:get_string('state')
  143. if state == '' then
  144. -- fix up lvm-placed right-hinged doors, default closed
  145. if node.name:sub(-2) == '_b' then
  146. state = 2
  147. else
  148. state = 0
  149. end
  150. else
  151. state = tonumber(state)
  152. end
  153. -- until Lua-5.2 we have no bitwise operators :(
  154. if state % 2 == 1 then
  155. state = state - 1
  156. else
  157. state = state + 1
  158. end
  159. local dir = node.param2
  160. -- It's possible param2 is messed up, so, validate before using
  161. -- the input data. This indicates something may have rotated
  162. -- the door, even though that is not supported.
  163. if not transform[state + 1] or not transform[state + 1][dir + 1] then
  164. return false
  165. end
  166. if state % 2 == 0 then
  167. minetest.sound_play(def.door.sounds[1],
  168. {pos = pos, gain = 0.3, max_hear_distance = 10}, true)
  169. else
  170. minetest.sound_play(def.door.sounds[2],
  171. {pos = pos, gain = 0.3, max_hear_distance = 10}, true)
  172. end
  173. minetest.swap_node(pos, {
  174. name = 'doors:'..name .. transform[state + 1][dir+1].v,
  175. param2 = transform[state + 1][dir+1].param2
  176. })
  177. meta:set_int('state', state)
  178. return true
  179. end
  180. local function on_place_node(place_to, newnode,
  181. placer, oldnode, itemstack, pointed_thing)
  182. -- Run script hook
  183. for _, callback in ipairs(minetest.registered_on_placenodes) do
  184. -- Deepcopy pos, node and pointed_thing because callback can modify them
  185. local place_to_copy = {x = place_to.x, y = place_to.y, z = place_to.z}
  186. local newnode_copy =
  187. {name = newnode.name, param1 = newnode.param1, param2 = newnode.param2}
  188. local oldnode_copy =
  189. {name = oldnode.name, param1 = oldnode.param1, param2 = oldnode.param2}
  190. local pointed_thing_copy = {
  191. type = pointed_thing.type,
  192. above = vector.new(pointed_thing.above),
  193. under = vector.new(pointed_thing.under),
  194. ref = pointed_thing.ref,
  195. }
  196. callback(place_to_copy, newnode_copy, placer,
  197. oldnode_copy, itemstack, pointed_thing_copy)
  198. end
  199. end
  200. function doors.register(name, def)
  201. minetest.register_craftitem('doors:' .. name, {
  202. description = def.description,
  203. inventory_image = 'doors_'..name..'_inv.png',
  204. groups = {breakable=1},
  205. on_place = function(itemstack, placer, pointed_thing)
  206. local pos
  207. if not pointed_thing.type == 'node' then
  208. return itemstack
  209. end
  210. local node = minetest.get_node(pointed_thing.under)
  211. local pdef = minetest.registered_nodes[node.name]
  212. if pdef and pdef.on_rightclick and
  213. not (placer and placer:is_player() and
  214. placer:get_player_control().sneak) then
  215. return pdef.on_rightclick(pointed_thing.under,
  216. node, placer, itemstack, pointed_thing)
  217. end
  218. if pdef and pdef.buildable_to then
  219. pos = pointed_thing.under
  220. else
  221. pos = pointed_thing.above
  222. node = minetest.get_node(pos)
  223. pdef = minetest.registered_nodes[node.name]
  224. if not pdef or not pdef.buildable_to then
  225. return itemstack
  226. end
  227. end
  228. local above = {x = pos.x, y = pos.y + 1, z = pos.z}
  229. local top_node = minetest.get_node_or_nil(above)
  230. local topdef = top_node and minetest.registered_nodes[top_node.name]
  231. if not topdef or not topdef.buildable_to then
  232. return itemstack
  233. end
  234. local pn = placer and placer:get_player_name() or ''
  235. if minetest.is_protected(pos, pn) or minetest.is_protected(above, pn) then
  236. return itemstack
  237. end
  238. local dir = placer and minetest.dir_to_facedir(placer:get_look_dir()) or 0
  239. local ref = {
  240. {x = -1, y = 0, z = 0},
  241. {x = 0, y = 0, z = 1},
  242. {x = 1, y = 0, z = 0},
  243. {x = 0, y = 0, z = -1},
  244. }
  245. local aside = {
  246. x = pos.x + ref[dir + 1].x,
  247. y = pos.y + ref[dir + 1].y,
  248. z = pos.z + ref[dir + 1].z,
  249. }
  250. local state = 0
  251. if minetest.get_item_group(minetest.get_node(aside).name, 'door') == 1 then
  252. state = state + 2
  253. minetest.set_node(pos, {name = 'doors:'..name .. '_b', param2 = dir})
  254. minetest.set_node(above, {name = 'doors:hidden', param2 = (dir + 3) % 4})
  255. else
  256. minetest.set_node(pos, {name = 'doors:'..name .. '_a', param2 = dir})
  257. minetest.set_node(above, {name = 'doors:hidden', param2 = dir})
  258. end
  259. local meta = minetest.get_meta(pos)
  260. meta:set_int('state', state)
  261. if not minetest.is_creative_enabled(pn) then
  262. itemstack:take_item()
  263. end
  264. on_place_node(pos, minetest.get_node(pos),
  265. placer, node, itemstack, pointed_thing)
  266. return itemstack
  267. end
  268. })
  269. def.inventory_image = nil
  270. def.tiles = {{name = 'doors_'..name..'.png', backface_culling=true}}
  271. def.groups = {breakable=1, door=1, not_in_creative_inventory=1}
  272. --if not def.sounds then
  273. -- def.sounds = default.node_sound_wood_defaults()
  274. --end
  275. if not def.sound_open then
  276. def.sound_open = 'doors_door_open'
  277. end
  278. if not def.sound_close then
  279. def.sound_close = 'doors_door_close'
  280. end
  281. def.drop = 'doors:'..name
  282. def.door = {
  283. name = name,
  284. sounds = { def.sound_close, def.sound_open },
  285. }
  286. if not def.on_rightclick then
  287. def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
  288. doors.door_toggle(pos, node, clicker)
  289. return itemstack
  290. end
  291. end
  292. def.after_dig_node = function(pos, node, meta, digger)
  293. minetest.remove_node({x = pos.x, y = pos.y + 1, z = pos.z})
  294. end
  295. def.on_rotate = function(pos, node, user, mode, new_param2)
  296. return false
  297. end
  298. def.on_destruct = function(pos)
  299. minetest.remove_node({x = pos.x, y = pos.y + 1, z = pos.z})
  300. end
  301. def.drawtype = 'mesh'
  302. def.paramtype = 'light'
  303. def.paramtype2 = 'facedir'
  304. def.sunlight_propagates = true
  305. def.walkable = true
  306. def.is_ground_content = false
  307. def.buildable_to = false
  308. def.use_texture_alpha = 'clip'
  309. def.selection_box = {type = 'fixed', fixed = {-1/2,-1/2,-1/2,1/2,3/2,-6/16}}
  310. def.collision_box = {type = 'fixed', fixed = {-1/2,-1/2,-1/2,1/2,3/2,-6/16}}
  311. def.mesh = 'door_a.obj'
  312. minetest.register_node('doors:' .. name .. '_a', def)
  313. def.mesh = 'door_b.obj'
  314. minetest.register_node('doors:' .. name .. '_b', def)
  315. def.mesh = 'door_a2.obj'
  316. minetest.register_node('doors:' .. name .. '_c', def)
  317. def.mesh = 'door_b2.obj'
  318. minetest.register_node('doors:' .. name .. '_d', def)
  319. doors.registered_doors['doors:'..name .. '_a'] = true
  320. doors.registered_doors['doors:'..name .. '_b'] = true
  321. doors.registered_doors['doors:'..name .. '_c'] = true
  322. doors.registered_doors['doors:'..name .. '_d'] = true
  323. end
  324. ----trapdoor----
  325. function doors.trapdoor_toggle(pos, node, clicker, close)
  326. node = node or minetest.get_node(pos)
  327. local meta = minetest.get_meta(pos)
  328. local def = minetest.registered_nodes[node.name]
  329. local player_name = clicker:get_player_name()
  330. local lock_status = meta:get_int('lock_s')
  331. if not close then
  332. local wield = clicker:get_wielded_item()
  333. local wield_name = wield:get_name()
  334. if not minetest.is_protected(pos, player_name) and minetest.check_player_privs(player_name, { creative = true }) then
  335. else
  336. local map_id = lobby.game[player_name]
  337. local sabotage_level = lobby.sabotage_level[map_id] or 5
  338. local level = meta:get_int('level') or 0
  339. if level < sabotage_level then
  340. if lock_status == 1 and (minetest.is_protected(pos, player_name) or not minetest.check_player_privs(player_name, { creative = true })) then
  341. return
  342. elseif lock_status == 2 then
  343. local key = meta:get_string('key')
  344. if wield_name ~= key then
  345. local def = minetest.registered_items[key]
  346. local key_name = def.description
  347. minetest.chat_send_player(player_name, 'This door can be opened/closed with a '..key_name..'.')
  348. return
  349. else
  350. minetest.after(3, function()
  351. doors.trapdoor_toggle(pos, nil, clicker, true)
  352. end)
  353. end
  354. elseif lock_status >= 3 then
  355. minetest.chat_send_player(player_name, 'The lock looks cheap, you might be able to pick it.')
  356. return
  357. end
  358. else
  359. minetest.chat_send_player(player_name, 'This door can\'t be used right now.')
  360. return
  361. end
  362. end
  363. end
  364. if string.sub(node.name, -5) == '_open' then
  365. minetest.sound_play(def.sound_close,
  366. {pos = pos, gain = 0.3, max_hear_distance = 10}, true)
  367. minetest.swap_node(pos, {name = string.sub(node.name, 1,
  368. string.len(node.name) - 5), param1 = node.param1, param2 = node.param2})
  369. else
  370. minetest.sound_play(def.sound_open,
  371. {pos = pos, gain = 0.3, max_hear_distance = 10}, true)
  372. minetest.swap_node(pos, {name = node.name .. '_open',
  373. param1 = node.param1, param2 = node.param2})
  374. end
  375. end
  376. function doors.register_trapdoor(name, def)
  377. local name_closed = name
  378. local name_opened = name..'_open'
  379. def.on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
  380. doors.trapdoor_toggle(pos, node, clicker)
  381. return itemstack
  382. end
  383. -- Common trapdoor configuration
  384. def.inventory_image = 'doors_'..name..'.png'
  385. def.drawtype = 'nodebox'
  386. def.paramtype = 'light'
  387. def.paramtype2 = 'facedir'
  388. def.is_ground_content = false
  389. def.use_texture_alpha = 'clip'
  390. def.groups = {breakable=1, door=2}
  391. --if not def.sounds then
  392. -- def.sounds = default.node_sound_wood_defaults()
  393. --end
  394. if not def.sound_open then
  395. def.sound_open = 'doors_door_open'
  396. end
  397. if not def.sound_close then
  398. def.sound_close = 'doors_door_close'
  399. end
  400. local def_opened = table.copy(def)
  401. local def_closed = table.copy(def)
  402. def_closed.node_box = {
  403. type = 'fixed',
  404. fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
  405. }
  406. def_closed.selection_box = {
  407. type = 'fixed',
  408. fixed = {-0.5, -0.5, -0.5, 0.5, -6/16, 0.5}
  409. }
  410. def_closed.tiles = {
  411. 'doors_'..name..'.png',
  412. 'doors_'..name..'.png^[transformFY',
  413. 'doors_'..name..'_side.png',
  414. 'doors_'..name..'_side.png',
  415. 'doors_'..name..'_side.png',
  416. 'doors_'..name..'_side.png'
  417. }
  418. def_opened.groups.not_in_creative_inventory=1
  419. def_opened.node_box = {
  420. type = 'fixed',
  421. fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
  422. }
  423. def_opened.selection_box = {
  424. type = 'fixed',
  425. fixed = {-0.5, -0.5, 6/16, 0.5, 0.5, 0.5}
  426. }
  427. def_opened.tiles = {
  428. def.tile_side,
  429. 'doors_'..name..'_side.png^[transform2',
  430. 'doors_'..name..'_side.png^[transform3',
  431. 'doors_'..name..'_side.png^[transform1',
  432. 'doors_'..name..'.png^[transform46',
  433. 'doors_'..name..'.png^[transform6'
  434. }
  435. def_opened.drop = name_closed
  436. minetest.register_node('doors:'..name_opened, def_opened)
  437. minetest.register_node('doors:'..name_closed, def_closed)
  438. doors.registered_trapdoors['doors:'..name_opened] = true
  439. doors.registered_trapdoors['doors:'..name_closed] = true
  440. end
  441. function doors.lock(pos, name)
  442. local meta = minetest.get_meta(pos)
  443. local status = meta:get_int('lock_s')
  444. local key_i = meta:get_int('key_i')
  445. local level = meta:get_int('level')
  446. local infotext = meta:get_string('infotext')
  447. minetest.show_formspec(name, 'doors:configuration', doors.lock_formspec(status, key_i, level, infotext))
  448. end
  449. local keys = {
  450. 'doors:key_skeleton', 'doors:key_modern', 'doors:keycard_red', 'doors:keycard_green', 'doors:keycard_blue'
  451. }
  452. function doors.lock_formspec(status, key_i, level, infotext)
  453. local formspec =
  454. 'formspec_version[3]'..
  455. 'size[8,8]'..
  456. 'textarea[.5,.5;7,4.25;;;Set the lock status here, different numbers mean different things, the following breakdown should be helpful.\n'..
  457. '\n0: Standard door, anybody can open it.\n'..
  458. '1: Locked door, only people within the area protection can open. Not pickable.\n'..
  459. '2: Locked door, uses Key to open.\n'..
  460. '3-13: locked door, can be picked, higher values are harder to pick. Limited time is offered to pick a door. '..
  461. 'Formula is (20-lock Status)+Luck.\n'..
  462. '\nYou can ignore the level input if you aren\'t adding a node you can sabotage.]'..
  463. 'field[1,5;6,.5;infotext;Infotext;'..infotext..']'..
  464. 'field[1,6;2,.5;status;Lock Status;'..status..']'..
  465. 'dropdown[4,6;3,.5;key;'..table.concat(keys, ',')..';'..key_i..']'..
  466. 'field[1,7;2,.5;level;Level;'..level..']'..
  467. 'button_exit[5,7;2,.5;save;Submit]'
  468. return formspec
  469. end
  470. minetest.register_on_player_receive_fields(function(player, formname, fields)
  471. local name = player:get_player_name()
  472. if formname == 'doors:configuration' then
  473. if fields.save then
  474. local state = tonumber(fields.status) or 0
  475. if state >= 0 and state <= 13 then
  476. local pos = tasks.player_config[name]
  477. local meta = minetest.get_meta(pos)
  478. local info = fields.infotext
  479. local lower = string.lower(info)
  480. meta:set_int('lock_s', state)
  481. meta:set_int('level', math.min(fields.level, 4))
  482. if state == 0 then
  483. meta:set_string('infotext', info)
  484. elseif state == 2 then
  485. if string.find(lower, 'lock') then
  486. meta:set_string('infotext', info)
  487. else
  488. meta:set_string('infotext', info..' (locked)')
  489. end
  490. for i, index in ipairs(keys) do
  491. if index == fields.key then
  492. meta:set_string('key', fields.key)
  493. meta:set_int('key_i', i)
  494. end
  495. end
  496. else
  497. if string.find(lower, 'lock') then
  498. meta:set_string('infotext', info)
  499. else
  500. meta:set_string('infotext', info..' (locked)')
  501. end
  502. end
  503. else
  504. minetest.chat_send_player(name, 'Faulty input, try again.')
  505. end
  506. end
  507. end
  508. end)