init.lua 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. engraver = engraver or {}
  2. engraver.modpath = minetest.get_modpath("engraver")
  3. -- API function to allow caller to check if an item has a custom description.
  4. function engraver.item_has_custom_description(item)
  5. if item:get_count() ~= 1 then
  6. return false
  7. end
  8. local meta = item:get_meta()
  9. local en_desc = meta:get_string("en_desc") or ""
  10. local ar_desc = meta:get_string("ar_desc") or ""
  11. return en_desc ~= "" or ar_desc ~= ""
  12. end
  13. local function player_wields_tools(user)
  14. local chisel_index = user:get_wield_index()
  15. local hammer_index = chisel_index + 1
  16. local inv = user:get_inventory()
  17. local chisel_stack = inv:get_stack("main", chisel_index)
  18. local chisel_name = chisel_stack:get_name()
  19. local hammer_stack = inv:get_stack("main", hammer_index)
  20. local hammer_name = hammer_stack:get_name()
  21. if chisel_name ~= "engraver:chisel" then
  22. return false
  23. end
  24. if hammer_name ~= "xdecor:hammer" and hammer_name ~= "anvil:hammer" then
  25. return false
  26. end
  27. return true
  28. end
  29. local function node_can_be_chiseled(pos)
  30. local node = minetest.get_node(pos)
  31. local ndef = minetest.registered_nodes[node.name]
  32. if not ndef then
  33. return false
  34. end
  35. -- Check node drawtype (must be full node).
  36. local dt = ndef.drawtype
  37. if dt ~= "normal" and dt ~= "glasslike" and dt ~= "glasslike_framed" and dt ~= "glasslike_framed_optional" and dt ~= "allfaces" and dt ~= "allfaces_optional" then
  38. return false
  39. end
  40. -- Check node groups (must be stone, brick or block).
  41. local groups = ndef.groups or {}
  42. if (groups.stone and groups.stone > 0) or (groups.brick and groups.brick > 0) or (groups.block and groups.block > 0) then
  43. -- Do nothing.
  44. else
  45. return false
  46. end
  47. -- Check meta (cannot have infotext or formspec, or must have been previously chiseled).
  48. local meta = minetest.get_meta(pos)
  49. local data = meta:to_table() or {fields={}, inventory={}}
  50. -- Any inventory fields means this node can't be engraved.
  51. for k, v in pairs(data.inventory) do
  52. return false
  53. end
  54. local was_engraved = false
  55. local was_polished = false
  56. local has_other_fields = false
  57. local has_infotext = false
  58. for k, v in pairs(data.fields) do
  59. if k == "engraver_chiseled" then
  60. was_engraved = true
  61. elseif k == "infotext" then
  62. has_infotext = true
  63. elseif k == "chiseled_text" or k == "chiseled_date" then
  64. -- Nothing to be done. Ignore these fields.
  65. elseif k == "chiseled_polished" then
  66. was_polished = true
  67. else
  68. has_other_fields = true
  69. end
  70. end
  71. if has_infotext and not was_engraved then
  72. return false
  73. end
  74. if has_other_fields or was_polished then
  75. return false
  76. end
  77. return true
  78. end
  79. local function show_chisel_formspec(pos, user)
  80. local pname = user:get_player_name()
  81. local node = minetest.get_node(pos)
  82. local text = minetest.get_meta(pos):get_string("chiseled_text")
  83. local formspec = "size[5,2.3]" ..
  84. default.gui_bg ..
  85. default.gui_bg_img ..
  86. default.gui_slots ..
  87. "item_image[1,1;1,1;" .. minetest.formspec_escape(node.name) .. "]" ..
  88. "field[0.3,0.3;5,1;text;;" .. minetest.formspec_escape(text) .. "]" ..
  89. "button_exit[2,1;2,1;proceed;Chisel Text]" ..
  90. "label[0,2;`%n' inserts a new line.]"
  91. local formname = "engraver:chisel_" .. minetest.pos_to_string(pos)
  92. minetest.show_formspec(pname, formname, formspec)
  93. end
  94. -- Must be a tool for the wear bar to work.
  95. minetest.register_tool("engraver:chisel", {
  96. description = "Chisel",
  97. groups = {not_repaired_by_anvil = 1},
  98. inventory_image = "engraver_chisel.png",
  99. wield_image = "engraver_chisel.png",
  100. on_use = function(itemstack, user, pt)
  101. if not user or not user:is_player() then
  102. return
  103. end
  104. if pt.type ~= "node" then
  105. return
  106. end
  107. if not player_wields_tools(user) then
  108. return
  109. end
  110. if not node_can_be_chiseled(pt.under) then
  111. return
  112. end
  113. ambiance.sound_play("anvil_clang", pt.under, 1.0, 30)
  114. show_chisel_formspec(pt.under, user)
  115. end,
  116. })
  117. local function handle_engraver_use(player, formname, fields)
  118. if not string.find(formname, "^engraver:chisel_") then
  119. return
  120. end
  121. if not player or not player:is_player() then
  122. return true
  123. end
  124. local pname = player:get_player_name()
  125. local pos = minetest.string_to_pos(string.sub(formname, string.len("engraver:chisel_") + 1))
  126. if not pos then
  127. return true
  128. end
  129. if not player_wields_tools(player) then
  130. return true
  131. end
  132. if not node_can_be_chiseled(pos) then
  133. return true
  134. end
  135. if not fields.text or type(fields.text) ~= "string" then
  136. return true
  137. end
  138. local message = utility.trim_remove_special_chars(fields.text)
  139. if anticurse.check(pname, message, "foul") then
  140. anticurse.log(pname, message)
  141. minetest.chat_send_player(pname, "# Server: Don't use a chisel for naughty talk!")
  142. return true
  143. elseif anticurse.check(pname, message, "curse") then
  144. anticurse.log(pname, message)
  145. minetest.chat_send_player(pname, "# Server: Please do not curse with a chisel.")
  146. return true
  147. end
  148. if string.len(message) > 256 then
  149. minetest.chat_send_player(pname, "# Server: Message is too long. Put something shorter.")
  150. return true
  151. end
  152. -- Add wear to the chisel.
  153. local got_chisel = false
  154. local inv = player:get_inventory()
  155. local index = player:get_wield_index()
  156. local chisel = inv:get_stack("main", index)
  157. if chisel:get_name() == "engraver:chisel" then
  158. chisel:add_wear(300)
  159. inv:set_stack("main", index, chisel)
  160. if chisel:is_empty() == 0 then
  161. ambiance.sound_play("default_tool_breaks", pos, 1.0, 10)
  162. end
  163. got_chisel = true
  164. end
  165. if got_chisel then
  166. local meta = minetest.get_meta(pos)
  167. meta:set_string("chiseled_text", message)
  168. meta:set_string("chiseled_date", os.time())
  169. meta:set_int("engraver_chiseled", 1)
  170. meta:mark_as_private({"chiseled_text", "chiseled_date", "engraver_chiseled"})
  171. -- Translate escape sequences.
  172. message = string.gsub(message, "%%[nN]", "\n")
  173. if message ~= "" then
  174. meta:set_string("infotext", message)
  175. else
  176. meta:set_string("infotext", "")
  177. meta:set_int("engraver_chiseled", 0)
  178. end
  179. minetest.chat_send_player(pname, "# Server: Text chiseled successfully.")
  180. ambiance.sound_play("anvil_clang", pos, 1.0, 30)
  181. end
  182. return true
  183. end
  184. minetest.register_on_player_receive_fields(handle_engraver_use)
  185. minetest.register_craft({
  186. output = "engraver:chisel",
  187. recipe = {
  188. {"carbon_steel:ingot"},
  189. {"default:stick"},
  190. },
  191. })
  192. -- Code by 'octacian'
  193. --
  194. -- Formspec
  195. --
  196. local function get_workbench_formspec(pos, error)
  197. local msg = "Rename Item"
  198. local text = minetest.get_meta(pos):get_string("text")
  199. if error then
  200. msg = minetest.colorize("red", error)
  201. end
  202. return
  203. "size[8,7]" ..
  204. default.gui_bg ..
  205. default.gui_bg_img ..
  206. default.gui_slots ..
  207. "field[0.5,0.5;6.2,1;text;"..msg..";"..minetest.formspec_escape(text).."]" ..
  208. "button[6.5,0.2;1.5,1;rename;Rename]" ..
  209. "list[context;input;1.5,1.4;1,1;]" ..
  210. "image[2.5,1.4;1,1;gui_workbench_plus.png]" ..
  211. "image[3.5,1.4;1,1;default_nametag_slot.png]" ..
  212. "list[context;nametag;3.5,1.4;1,1;]" ..
  213. "image[4.5,1.4;1,1;gui_furnace_arrow_bg.png^[transformR270]" ..
  214. "list[context;output;5.5,1.4;1,1]" ..
  215. "list[current_player;main;0,2.85;8,1;]" ..
  216. "list[current_player;main;0,4.08;8,3;8]" ..
  217. "field_close_on_enter[text;false]" ..
  218. default.get_hotbar_bg(0,2.85)
  219. end
  220. local function get_item_desc(stack)
  221. if not stack:is_known() then
  222. return
  223. end
  224. local desc = stack:get_meta():get_string("description")
  225. if desc == "" then
  226. desc = minetest.registered_items[stack:get_name()].description or ""
  227. end
  228. desc = utility.get_short_desc(desc)
  229. return desc
  230. end
  231. local function workbench_update_text(pos, stack)
  232. local meta = minetest.get_meta(pos)
  233. meta:set_string("text", get_item_desc(stack))
  234. meta:set_string("formspec", get_workbench_formspec(pos))
  235. end
  236. local function workbench_update_help(pos, type, string)
  237. local meta = minetest.get_meta(pos)
  238. meta:set_string("formspec", get_workbench_formspec(pos, string))
  239. meta:set_string("error", type)
  240. end
  241. --
  242. -- Node definition
  243. --
  244. minetest.register_node(":engraver:bench", {
  245. description = "Engraving Bench",
  246. tiles = {"default_workbench_top.png", "default_wood.png", "default_workbench_sides.png",
  247. "default_workbench_sides.png", "default_workbench_sides.png", "default_workbench_sides.png"},
  248. groups = utility.dig_groups("furniture", {flammable = 3}),
  249. sounds = default.node_sound_wood_defaults(),
  250. drawtype = "nodebox",
  251. paramtype = "light",
  252. node_box = {
  253. type = "fixed",
  254. fixed = {
  255. {-0.5, 3/16, -0.5, 0.5, 0.5, 0.5},
  256. {-7/16, -0.5, 1/4, -1/4, 0.5, 7/16},
  257. {-7/16, -0.5, -7/16, -1/4, 0.5, -1/4},
  258. {1/4, -0.5, 1/4, 7/16, 0.5, 7/16},
  259. {1/4, -0.5, -7/16, 7/16, 0.5, -1/4},
  260. }
  261. },
  262. on_construct = function(pos)
  263. local meta = minetest.get_meta(pos)
  264. meta:set_string("formspec", get_workbench_formspec(pos))
  265. local inv = meta:get_inventory()
  266. inv:set_size("input", 1)
  267. inv:set_size("nametag", 1)
  268. inv:set_size("output", 1)
  269. end,
  270. can_dig = function(pos, player)
  271. local inv = minetest.get_meta(pos):get_inventory()
  272. if inv:is_empty("input") and inv:is_empty("nametag") and
  273. inv:is_empty("output") then
  274. return true
  275. else
  276. return false
  277. end
  278. end,
  279. on_blast = function(pos)
  280. local inv = minetest.get_meta(pos):get_inventory()
  281. local drops = {
  282. inv:get_list("input")[1],
  283. inv:get_list("nametag")[1],
  284. inv:get_list("output")[1],
  285. "engraver:bench",
  286. }
  287. minetest.remove_node(pos)
  288. return drops
  289. end,
  290. allow_metadata_inventory_put = function(pos, listname, index, stack, player)
  291. local pname = player:get_player_name()
  292. if minetest.test_protection(pos, pname) then
  293. return 0
  294. end
  295. if not stack:is_known() then
  296. return 0
  297. end
  298. if listname == "nametag" then
  299. if stack:get_name() ~= "engraver:plate" then
  300. return 0
  301. else
  302. return stack:get_count()
  303. end
  304. elseif listname == "output" then
  305. return 0
  306. elseif listname == "input" then
  307. if minetest.get_item_group(stack:get_name(), "not_renamable") > 0 then
  308. return 0
  309. end
  310. if stack:get_stack_max() > 1 then
  311. return 0
  312. end
  313. return stack:get_count()
  314. end
  315. return 0
  316. end,
  317. allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
  318. return 0
  319. end,
  320. allow_metadata_inventory_take = function(pos, listname, index, stack, player)
  321. local pname = player:get_player_name()
  322. if minetest.test_protection(pos, pname) then
  323. return 0
  324. end
  325. return stack:get_count()
  326. end,
  327. on_metadata_inventory_put = function(pos, listname, index, stack)
  328. local meta = minetest.get_meta(pos)
  329. local inv = meta:get_inventory()
  330. local error = meta:get_string("error")
  331. if error == "input" and not inv:is_empty("input") then
  332. meta:set_string("formspec", get_workbench_formspec(pos))
  333. elseif error == "nametag" and not inv:is_empty("nametag") then
  334. meta:set_string("formspec", get_workbench_formspec(pos))
  335. end
  336. if listname == "input" then
  337. workbench_update_text(pos, stack)
  338. end
  339. end,
  340. on_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index)
  341. -- Moving is not allowed.
  342. end,
  343. on_metadata_inventory_take = function(pos, listname)
  344. local meta = minetest.get_meta(pos)
  345. local inv = meta:get_inventory()
  346. local error = meta:get_string("error")
  347. if error == "output" and inv:is_empty("output") then
  348. meta:set_string("formspec", get_workbench_formspec(pos))
  349. end
  350. if listname == "input" then
  351. meta:set_string("text", "")
  352. meta:set_string("formspec", get_workbench_formspec(pos))
  353. end
  354. end,
  355. on_receive_fields = function(pos, formname, fields, sender)
  356. local meta = minetest.get_meta(pos)
  357. local inv = meta:get_inventory()
  358. local pname = sender:get_player_name()
  359. if fields.rename or fields.key_enter_field == "text" then
  360. meta:set_string("text", fields.text)
  361. if inv:is_empty("input") then
  362. workbench_update_help(pos, "input", "Missing input item!")
  363. elseif inv:is_empty("nametag") then
  364. workbench_update_help(pos, "nametag", "Missing nameplate!")
  365. elseif not inv:is_empty("output") then
  366. workbench_update_help(pos, "output", "No room in output!")
  367. else
  368. local new_stack = inv:get_stack("input", 1)
  369. if not new_stack:is_known() then
  370. workbench_update_help(pos, nil, "Cannot rename unknown item!")
  371. return
  372. end
  373. local item = minetest.registered_items[new_stack:get_name()]
  374. local renameable = item.groups.renameable ~= 0
  375. if not renameable then
  376. workbench_update_help(pos, nil, "Item cannot be renamed!")
  377. return
  378. elseif new_stack:get_stack_max() > 1 then
  379. workbench_update_help(pos, nil, "Item cannot be renamed!")
  380. return
  381. elseif fields.text == "" then
  382. workbench_update_help(pos, nil, "Description cannot be blank!")
  383. return
  384. elseif anticurse.check(pname, fields.text, "foul") then
  385. workbench_update_help(pos, nil, "No foul language!")
  386. return
  387. elseif anticurse.check(pname, fields.text, "curse") then
  388. workbench_update_help(pos, nil, "No cursing!")
  389. return
  390. elseif fields.text:len() > 256 then
  391. workbench_update_help(pos, nil, "Description too long (max 256 characters)!")
  392. return
  393. elseif fields.text == get_item_desc(inv:get_stack("input", 1)) then
  394. workbench_update_help(pos, nil, "Description not changed!")
  395. end
  396. local itemmeta = new_stack:get_meta()
  397. itemmeta:set_string("en_desc", fields.text)
  398. toolranks.apply_description(itemmeta, new_stack:get_definition())
  399. minetest.log("action", pname .. " renames "
  400. ..inv:get_stack("input", 1):get_name().." to "..fields.text)
  401. inv:remove_item("input", inv:get_stack("input", 1))
  402. inv:remove_item("nametag", inv:get_stack("nametag", 1):take_item(1))
  403. inv:set_stack("output", 1, new_stack)
  404. meta:set_string("text", "")
  405. workbench_update_help(pos)
  406. end
  407. end
  408. end,
  409. })
  410. minetest.register_craft({
  411. output = "engraver:bench",
  412. recipe = {
  413. {'default:bronze_ingot', 'default:bronze_ingot', 'default:bronze_ingot'},
  414. {'basictrees:tree_wood', '', 'basictrees:tree_wood'},
  415. {'basictrees:tree_wood', '', 'basictrees:tree_wood'},
  416. }
  417. })
  418. minetest.register_craftitem(":engraver:plate", {
  419. description = "Nameplate",
  420. inventory_image = "default_nametag.png",
  421. groups = {not_renamable = 1}
  422. })
  423. minetest.register_craft({
  424. type = "compressing",
  425. output = "engraver:plate 4",
  426. recipe = "default:bronze_ingot",
  427. time = 10,
  428. })