init.lua 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
  1. -- Anvil mod for Enyekala
  2. -- Author of source code: MustTest/BlueBird51
  3. -- License of source code: MIT
  4. if not minetest.global_exists("anvil") then anvil = {} end
  5. anvil.modpath = minetest.get_modpath("anvil")
  6. -- Place entity exactly on anvil surface.
  7. -- Search for entities inside anvil node ONLY.
  8. anvil.entity_offset = {x=0, y=0.15, z=0}
  9. anvil.entity_radius = 0.45
  10. -- Check protection, check access rights.
  11. function anvil.player_can_use(pos, player)
  12. local pname = player:get_player_name()
  13. local meta = minetest.get_meta(pos)
  14. local owner = meta:get_string("owner")
  15. if owner == "" then
  16. return true
  17. end
  18. -- Note: this is really only effective if anvil is ALSO inside protection.
  19. if owner == pname then
  20. return true
  21. end
  22. -- Player can use the anvil if not protected, even if they aren't owner.
  23. -- This also lets anvils be shared, by sharing the protection.
  24. if not minetest.test_protection(pos, pname) then
  25. return true
  26. end
  27. return false
  28. end
  29. -- Check if itemstack is a hammer.
  30. function anvil.is_hammer(itemstack)
  31. if itemstack:is_empty() then
  32. return false
  33. end
  34. local sn = itemstack:get_name()
  35. if sn == "anvil:hammer" or sn == "xdecor:hammer" then
  36. return true
  37. end
  38. return false
  39. end
  40. -- Apply wear to the hammer.
  41. function anvil.wear_hammer(pos, stack)
  42. if stack:is_empty() then
  43. return stack
  44. end
  45. local uses = {
  46. ["anvil:hammer"] = 1000,
  47. ["xdecor:hammer"] = 100,
  48. }
  49. local sdef = stack:get_definition()
  50. if sdef and uses[stack:get_name()] then
  51. stack:add_wear_by_uses(uses[stack:get_name()])
  52. if stack:is_empty() then
  53. if sdef.sounds and sdef.sounds.breaks then
  54. ambiance.sound_play(sdef.sounds.breaks, pos, 1.0, 16)
  55. end
  56. end
  57. end
  58. return stack
  59. end
  60. -- Check whether itemstack is repairable by anvils.
  61. function anvil.item_repairable_or_craftable(itemstack)
  62. if minetest.get_item_group(itemstack:get_name(), "not_repaired_by_anvil") ~= 0 then
  63. return false
  64. end
  65. -- Must NOT have a 'wear_represents' key, that means wear is NOT durability.
  66. local idef = minetest.registered_items[itemstack:get_name()]
  67. if idef and idef.wear_represents then
  68. return false
  69. end
  70. -- Allow stuff that's used in anvil recipes.
  71. if minetest.get_craft_result({
  72. method = "anvil",
  73. width = 1,
  74. items = {itemstack}
  75. }).time ~= 0 then
  76. return true
  77. end
  78. if minetest.registered_tools[itemstack:get_name()] then
  79. -- Permit worn tools only.
  80. local wear = itemstack:get_wear()
  81. if wear > 0 then
  82. return true
  83. end
  84. end
  85. return false
  86. end
  87. -- Do craft action.
  88. function anvil.craft_something(pos)
  89. local meta = minetest.get_meta(pos)
  90. local inv = meta:get_inventory()
  91. if meta:get_int("strike") >= 1 then
  92. return false
  93. end
  94. for index = 1, inv:get_size("input"), 1 do
  95. local stack = inv:get_stack("input", index)
  96. local craft, aftercraft = minetest.get_craft_result({
  97. method = "anvil",
  98. width = 1,
  99. items = {stack}
  100. })
  101. if craft.time ~= 0 and inv:room_for_item("input", craft.item) then
  102. stack = aftercraft.items[1]
  103. inv:set_stack("input", index, stack)
  104. inv:add_item("input", ItemStack(craft.item))
  105. meta:set_int("strike", 1)
  106. anvil.update_infotext(pos)
  107. anvil.update_entity(pos)
  108. anvil.start_timer_if_needed(pos, 0.1)
  109. return true
  110. end
  111. end
  112. return false
  113. end
  114. -- Anvil item entity activation function.
  115. -- Note: this is also call for NEW entities, not just reactivated ones.
  116. function anvil.on_activate(self, staticdata)
  117. local pos = vector.round(self.object:get_pos())
  118. local node = minetest.get_node(pos)
  119. -- Clean up orphaned.
  120. if node.name ~= "anvil:anvil" then
  121. self.object:remove()
  122. return
  123. end
  124. if staticdata and staticdata ~= "" then
  125. local fields = minetest.deserialize(staticdata)
  126. if fields and fields.wield_name then
  127. self:set_display_item(pos, fields.wield_name)
  128. else
  129. -- Clean up the ones with bad data.
  130. self.object:remove()
  131. end
  132. end
  133. end
  134. -- Anvil item entity staticdata function.
  135. function anvil.get_staticdata(self)
  136. -- Note: can't access self.object here because it appears this function gets
  137. -- called after the object has already been removed.
  138. local fields = {
  139. -- If we don't have a wield item name, you get a lump of coal instead.
  140. wield_name = self._wield_item or "default:coal_lump",
  141. }
  142. return minetest.serialize(fields)
  143. end
  144. -- Set the display item on the display entity.
  145. function anvil.set_display_item(self, pos, stackname)
  146. local p2 = vector.add(pos, anvil.entity_offset)
  147. local node = minetest.get_node(pos)
  148. -- Rotations determined by visual inspection (trial-and-error).
  149. local param2_tab = {
  150. [0] = math.pi,
  151. [1] = math.pi / 2,
  152. [2] = 0,
  153. [3] = -(math.pi / 2),
  154. }
  155. local y_rot = param2_tab[node.param2] or 0
  156. local entity = self.object
  157. entity:set_pos(p2)
  158. entity:set_rotation({x = math.pi / 2, y=y_rot, z=0})
  159. entity:set_properties({
  160. wield_item = stackname,
  161. })
  162. -- Also store stack name in the luaentity.
  163. -- This lets us access the data in the 'get_staticdata' function.
  164. self._wield_item = stackname
  165. end
  166. -- Updates infotext according to current state.
  167. function anvil.update_infotext(pos)
  168. local info = ""
  169. local workpiece = ""
  170. local itemstack
  171. local meta = minetest.get_meta(pos)
  172. local owner = meta:get_string("owner")
  173. local inv = meta:get_inventory()
  174. local list = inv:get_list("input") or {}
  175. for index, stack in ipairs(list) do
  176. if not stack:is_empty() then
  177. local sdef = stack:get_definition() or {}
  178. workpiece = utility.get_short_desc(sdef.description or "Unknown")
  179. itemstack = stack
  180. break
  181. end
  182. end
  183. if owner ~= "" then
  184. info = info .. "<" .. rename.gpn(owner) .. ">'s Anvil\n"
  185. else
  186. info = info .. "Blacksmithing Anvil\n"
  187. end
  188. if workpiece ~= "" then
  189. info = info .. "Workpiece: " .. workpiece .. "\n"
  190. end
  191. if itemstack then
  192. local tdef = minetest.registered_items[itemstack:get_name()]
  193. if tdef and tdef.stack_max == 1 then
  194. -- Must NOT have a 'wear_represents' key, that implies wear is NOT default.
  195. if not tdef.wear_represents then
  196. local wear = itemstack:get_wear()
  197. if wear == 0 then
  198. info = info .. "Durability: 100%\n"
  199. else
  200. info = info .. "Durability: " .. math.floor(((wear / 65535) * -1 + 1) * 100) .. "%\n"
  201. end
  202. end
  203. end
  204. end
  205. local heat = meta:get_int("heat")
  206. if heat > 0 then
  207. info = info .. "Let cool before take (" .. heat .. ")\n"
  208. end
  209. meta:set_string("infotext", info)
  210. end
  211. -- Updates formspec according to current state.
  212. function anvil.update_formspec(pos)
  213. local smeta = "nodemeta:" .. pos.x .. "," .. pos.y .. "," .. pos.z
  214. local formspec = "size[8,8]" ..
  215. default.gui_bg ..
  216. default.gui_bg_img ..
  217. default.gui_slots ..
  218. "label[2.5,1;Workspace]" ..
  219. "list[" .. smeta .. ";input;2.5,1.5;3,1;]" ..
  220. "list[current_player;main;0,3.75;8,1;]" ..
  221. "list[current_player;main;0,5;8,3;8]" ..
  222. "listring[" .. smeta .. ";input]" ..
  223. "listring[current_player;main]" ..
  224. default.get_hotbar_bg(0, 3.75)
  225. -- Note: using a NON-standard name because we do NOT want special
  226. -- engine/client handling. This is just a storage space for the formspec
  227. -- string, but we send it to the client manually.
  228. local meta = minetest.get_meta(pos)
  229. meta:set_string("formspec2", formspec)
  230. local inv = meta:get_inventory()
  231. inv:set_size("input", 3)
  232. end
  233. -- Node constructor.
  234. function anvil.on_construct(pos)
  235. anvil.update_infotext(pos)
  236. anvil.update_formspec(pos)
  237. anvil.update_entity(pos)
  238. end
  239. -- Node destructor.
  240. function anvil.on_destruct(pos)
  241. anvil.on_pre_fall(pos)
  242. anvil.remove_entity(pos)
  243. end
  244. -- Pre-fall callback. Node is being converted to falling node.
  245. function anvil.on_pre_fall(pos)
  246. local meta = minetest.get_meta(pos)
  247. local heat = meta:get_int("heat")
  248. local inv = meta:get_inventory()
  249. local list = inv:get_list("input") or {}
  250. for index, stack in ipairs(list) do
  251. if not stack:is_empty() then
  252. -- In anvil is removed/destroyed while hot items are on it, damage them.
  253. if heat > 0 then
  254. local idef = minetest.registered_tools[stack:get_name()]
  255. if idef and idef.stack_max == 1 and not idef.wear_represents then
  256. -- Add 2/3rds wear. This has a high chance to destroy the tool unless
  257. -- it had good durability.
  258. stack:add_wear((65535 / 3) * 2)
  259. if stack:is_empty() then
  260. if idef.sounds and idef.sounds.breaks then
  261. ambiance.sound_play(idef.sounds.breaks, pos, 1.0, 16)
  262. end
  263. end
  264. end
  265. end
  266. list[index] = ItemStack("")
  267. if not stack:is_empty() then
  268. minetest.add_item(pos, stack)
  269. end
  270. end
  271. end
  272. -- Have to explicitly clear the inventory.
  273. -- Otherwise it will be duplicated when the node actually falls.
  274. inv:set_list("input", list)
  275. end
  276. -- Anvil gets blasted.
  277. function anvil.on_blast(pos)
  278. anvil.on_pre_fall(pos)
  279. minetest.remove_node(pos)
  280. end
  281. -- Anvil fell but cannot be placed as a node for some reason.
  282. function anvil.on_collapse_to_entity(pos, node)
  283. minetest.add_item(pos, ItemStack("anvil:anvil"))
  284. end
  285. -- Anvil fell and was reconstructed as a node.
  286. function anvil.on_finish_collapse(pos, node)
  287. anvil.update_entity(pos)
  288. anvil.update_infotext(pos)
  289. anvil.update_formspec(pos)
  290. -- If there was a timer running, restart it.
  291. anvil.start_timer_if_needed(pos)
  292. end
  293. -- Formspec received fields.
  294. function anvil.on_player_receive_fields(player, formname, fields)
  295. local fn = formname:sub(1, 14)
  296. if fn ~= "anvil:formspec" then
  297. return
  298. end
  299. local pos = minetest.string_to_pos(formname:sub(16))
  300. if not pos then
  301. return
  302. end
  303. if not player or not player:is_player() then
  304. return
  305. end
  306. local pname = player:get_player_name()
  307. local plpos = player:get_pos()
  308. -- Require close interactions (needed for security, since owner can be blank).
  309. if vector.distance(pos, plpos) > 10 then
  310. return
  311. end
  312. if not anvil.player_can_use(pos, player) then
  313. return
  314. end
  315. -- We handled it.
  316. return true
  317. end
  318. -- Function to show player the formspec.
  319. function anvil.show_formspec(pname, pos)
  320. local meta = minetest.get_meta(pos)
  321. local fs = meta:get_string("formspec2")
  322. local sp = minetest.pos_to_string(pos)
  323. minetest.show_formspec(pname, "anvil:formspec_" .. sp, fs)
  324. end
  325. -- Player rightclicks on anvil.
  326. function anvil.on_rightclick(pos, node, user, itemstack, pt)
  327. if not user or not user:is_player() then
  328. return
  329. end
  330. anvil.start_timer_if_needed(pos)
  331. local pname = user:get_player_name()
  332. local meta = minetest.get_meta(pos)
  333. local inv = meta:get_inventory()
  334. -- Rightclicking with an empty hand takes from anvil, if there is something to take.
  335. if anvil.player_can_use(pos, user) then
  336. if itemstack:is_empty() and not inv:is_empty("input") then
  337. if meta:get_int("heat") > 0 then
  338. anvil.burn_user(pos, user)
  339. return
  340. end
  341. return anvil.put_or_take(pos, user, itemstack, false)
  342. end
  343. end
  344. -- Player does not need access rights to open the formspec.
  345. if itemstack:is_empty() or (not anvil.item_repairable_or_craftable(itemstack)
  346. and not anvil.is_water_bucket(itemstack)) then
  347. anvil.show_formspec(pname, pos)
  348. return
  349. end
  350. -- Otherwise, player needs access to be able to put or take from anvil.
  351. if anvil.player_can_use(pos, user) then
  352. if anvil.is_water_bucket(itemstack) then
  353. return anvil.cool_with_bucket(pos, user, itemstack)
  354. end
  355. if anvil.item_repairable_or_craftable(itemstack) then
  356. if meta:get_int("heat") > 0 then
  357. anvil.burn_user(pos, user)
  358. return
  359. end
  360. return anvil.put_or_take(pos, user, itemstack, true)
  361. end
  362. end
  363. end
  364. -- Query whether this itemstack is a water bucket with water.
  365. function anvil.is_water_bucket(itemstack)
  366. local sname = itemstack:get_name()
  367. if sname == "bucket:bucket_water" or sname == "bucket:bucket_river_water" then
  368. return true
  369. end
  370. if sname == "cans:water_can" or sname == "cans:river_water_can" then
  371. if cans.get_can_level(itemstack) > 0 then
  372. return true
  373. end
  374. end
  375. return false
  376. end
  377. -- Cool this anvil.
  378. function anvil.cool_with_bucket(pos, user, itemstack)
  379. local meta = minetest.get_meta(pos)
  380. local sname = itemstack:get_name()
  381. if meta:get_int("heat") == 0 then
  382. return itemstack
  383. end
  384. if sname == "bucket:bucket_water" or sname == "bucket:bucket_river_water" then
  385. itemstack = ItemStack("bucket:bucket_empty")
  386. elseif sname == "cans:water_can" or sname == "cans:river_water_can" then
  387. local level = cans.get_can_level(itemstack)
  388. level = level - 1
  389. cans.set_can_level(itemstack, level)
  390. end
  391. meta:set_int("heat", 0)
  392. ambiance.sound_play("default_cool_lava", pos, 0.1, 32)
  393. anvil.update_infotext(pos)
  394. return itemstack
  395. end
  396. -- Put or take user's currently-wielded item.
  397. function anvil.put_or_take(pos, user, itemstack, put)
  398. local meta = minetest.get_meta(pos)
  399. local inv = meta:get_inventory()
  400. if put then
  401. -- Putting onto anvil.
  402. if inv:room_for_item("input", itemstack) then
  403. inv:add_item("input", itemstack)
  404. anvil.update_entity(pos)
  405. anvil.update_infotext(pos)
  406. anvil.update_formspec(pos)
  407. -- Must return the ORIGINAL itemstack, not a new one.
  408. itemstack:take_item(itemstack:get_count())
  409. return itemstack
  410. end
  411. else
  412. -- Taking from anvil.
  413. local list = inv:get_list("input") or {}
  414. for index, stack in ipairs(list) do
  415. if not stack:is_empty() then
  416. inv:set_stack("input", index, ItemStack(""))
  417. anvil.update_entity(pos)
  418. anvil.update_infotext(pos)
  419. anvil.update_formspec(pos)
  420. return stack
  421. end
  422. end
  423. end
  424. return itemstack
  425. end
  426. -- Function to update entity display.
  427. function anvil.update_entity(pos)
  428. local meta = minetest.get_meta(pos)
  429. local node = minetest.get_node(pos)
  430. local inv = meta:get_inventory()
  431. local stack
  432. local ilist = inv:get_list("input")
  433. for index, item in ipairs(ilist) do
  434. if not item:is_empty() then
  435. stack = item
  436. break
  437. end
  438. end
  439. local p2 = vector.add(pos, anvil.entity_offset)
  440. local ents = minetest.get_objects_inside_radius(p2, anvil.entity_radius)
  441. local count = 0
  442. local entity
  443. local luaent
  444. for k, v in ipairs(ents) do
  445. local ent = v:get_luaentity()
  446. if ent and ent.name == "anvil:item" then
  447. -- If there are multiple entities, remove the extras.
  448. -- Remove all entities if there is nothing on the anvil.
  449. if not stack or count >= 1 then
  450. v:remove()
  451. else
  452. entity = v
  453. luaent = ent
  454. count = count + 1
  455. end
  456. end
  457. end
  458. -- If there are no entities and something is on the anvil, create one.
  459. if count == 0 and stack then
  460. local ent = minetest.add_entity(p2, "anvil:item")
  461. if ent then
  462. local lent = ent:get_luaentity()
  463. if not lent then
  464. ent:remove()
  465. return
  466. end
  467. entity = ent
  468. luaent = lent
  469. end
  470. end
  471. -- We must have entity, lua-entity, and stack.
  472. if not entity or not luaent or not stack then
  473. return
  474. end
  475. luaent:set_display_item(pos, stack:get_name())
  476. end
  477. -- Remove the entity display.
  478. function anvil.remove_entity(pos)
  479. local p2 = vector.add(pos, anvil.entity_offset)
  480. local ents = minetest.get_objects_inside_radius(p2, anvil.entity_radius)
  481. for k, v in ipairs(ents) do
  482. local luaent = v:get_luaentity()
  483. if luaent and luaent.name == "anvil:item" then
  484. v:remove()
  485. end
  486. end
  487. end
  488. -- Can player move stuff in inventory.
  489. function anvil.allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, user)
  490. local meta = minetest.get_meta(pos)
  491. if meta:get_int("heat") > 0 then
  492. anvil.burn_user(pos, user)
  493. return 0
  494. end
  495. if not anvil.player_can_use(pos, user) then
  496. return 0
  497. end
  498. local meta = minetest.get_meta(pos)
  499. local inv = meta:get_inventory()
  500. return count
  501. end
  502. -- After inventory move.
  503. function anvil.on_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, user)
  504. anvil.update_entity(pos)
  505. anvil.update_infotext(pos)
  506. anvil.update_formspec(pos)
  507. end
  508. -- Can player put stuff in inventory.
  509. function anvil.allow_metadata_inventory_put(pos, listname, index, stack, user)
  510. local meta = minetest.get_meta(pos)
  511. if meta:get_int("heat") > 0 then
  512. anvil.burn_user(pos, user)
  513. return 0
  514. end
  515. if not anvil.player_can_use(pos, user) then
  516. return 0
  517. end
  518. if not anvil.item_repairable_or_craftable(stack) then
  519. return 0
  520. end
  521. return stack:get_count()
  522. end
  523. -- After inventory put.
  524. function anvil.on_metadata_inventory_put(pos, listname, index, stack, user)
  525. anvil.update_entity(pos)
  526. anvil.update_infotext(pos)
  527. anvil.update_formspec(pos)
  528. end
  529. -- Can player take stuff from inventory.
  530. function anvil.allow_metadata_inventory_take(pos, listname, index, stack, user)
  531. local meta = minetest.get_meta(pos)
  532. if meta:get_int("heat") > 0 then
  533. anvil.burn_user(pos, user)
  534. return 0
  535. end
  536. if not anvil.player_can_use(pos, user) then
  537. return 0
  538. end
  539. return stack:get_count()
  540. end
  541. -- After inventory take.
  542. function anvil.on_metadata_inventory_take(pos, listname, index, stack, user)
  543. anvil.update_entity(pos)
  544. anvil.update_infotext(pos)
  545. anvil.update_formspec(pos)
  546. end
  547. -- After place node.
  548. function anvil.after_place_node(pos, user, itemstack, pt)
  549. if not user or not user:is_player() then
  550. return
  551. end
  552. local pname = user:get_player_name()
  553. local control = user:get_player_control()
  554. local meta = minetest.get_meta(pos)
  555. -- Hold 'E' to set owner, otherwise anyone can use.
  556. if control.aux1 then
  557. meta:set_string("owner", pname)
  558. anvil.update_infotext(pos)
  559. anvil.update_formspec(pos)
  560. end
  561. end
  562. -- Player punches anvil.
  563. function anvil.on_punch(pos, node, user, pt)
  564. if not user or not user:is_player() then
  565. return
  566. end
  567. anvil.start_timer_if_needed(pos)
  568. local stack = user:get_wielded_item()
  569. local sname = stack:get_name()
  570. -- Note: hammering sound shall be played even if player cannot actually use the anvil.
  571. if sname:find("hammer") or sname:find("pick") or sname:find("axe") or sname:find("sword") then
  572. anvil.sparks_and_sound(pos)
  573. else
  574. ambiance.sound_play("default_dig_metal", pos, 0.5, 16)
  575. end
  576. if not anvil.player_can_use(pos, user) then
  577. return
  578. end
  579. -- Punching with empty hand takes item, if possible.
  580. if stack:is_empty() then
  581. local meta = minetest.get_meta(pos)
  582. if meta:get_int("heat") > 0 then
  583. anvil.burn_user(pos, user)
  584. return
  585. end
  586. stack = anvil.put_or_take(pos, user, stack, false)
  587. user:set_wielded_item(stack)
  588. return
  589. end
  590. -- We are going to repair or craft. Player must be wielding a hammer.
  591. if not anvil.is_hammer(stack) then
  592. return
  593. end
  594. if anvil.repair_tool(pos) then
  595. user:set_wielded_item(anvil.wear_hammer(pos, stack))
  596. return
  597. end
  598. if anvil.craft_something(pos) then
  599. user:set_wielded_item(anvil.wear_hammer(pos, stack))
  600. return
  601. end
  602. end
  603. -- Repair tool.
  604. function anvil.repair_tool(pos)
  605. local meta = minetest.get_meta(pos)
  606. local inv = meta:get_inventory()
  607. local list = inv:get_list("input") or {}
  608. if meta:get_int("strike") >= 1 then
  609. return false
  610. end
  611. for index, stack in ipairs(list) do
  612. local idef = minetest.registered_tools[stack:get_name()]
  613. if idef and idef.stack_max == 1 and not idef.wear_represents then
  614. local wear = stack:get_wear()
  615. if wear == 0 then
  616. return false
  617. end
  618. -- A heat source makes repairs faster.
  619. local repair_amount = 500
  620. if minetest.find_node_near(pos, 2, "group:fire") then
  621. repair_amount = 2000
  622. end
  623. if minetest.find_node_near(pos, 2, "group:lava") then
  624. repair_amount = 5000
  625. end
  626. -- Max wear is 65535 (16 bit unsigned aka uint16_t).
  627. wear = wear - repair_amount
  628. if wear < 0 then wear = 0 end
  629. stack:set_wear(wear)
  630. list[index] = stack
  631. inv:set_list("input", list)
  632. meta:set_int("strike", 1)
  633. meta:set_int("heat", meta:get_int("heat") + 10)
  634. anvil.update_infotext(pos)
  635. anvil.start_timer_if_needed(pos)
  636. return true
  637. end
  638. end
  639. -- Nothing repaired.
  640. return false
  641. end
  642. -- Timer fires.
  643. function anvil.on_timer(pos, elapsed)
  644. local meta = minetest.get_meta(pos)
  645. local heat = meta:get_int("heat")
  646. meta:set_int("strike", 0)
  647. -- Cool off over time.
  648. if heat > 0 then
  649. local loss = 1
  650. if minetest.find_node_near(pos, 2, "group:water") then
  651. loss = 3
  652. end
  653. heat = heat - loss
  654. if heat < 0 then
  655. heat = 0
  656. end
  657. meta:set_int("heat", heat)
  658. anvil.update_infotext(pos)
  659. local p2 = vector.add(pos, anvil.entity_offset)
  660. p2.y = p2.y - 0.2
  661. minetest.add_particlespawner({
  662. amount = 6,
  663. time = 1,
  664. minpos = {x = p2.x - 0.1, y = p2.y + 0.1, z = p2.z - 0.1 },
  665. maxpos = {x = p2.x + 0.1, y = p2.y + 0.2, z = p2.z + 0.1 },
  666. minvel = {x = -3, y = 0.5, z = -3},
  667. maxvel = {x = 3, y = 0.5, z = 3},
  668. minacc = {x = -0.15, y = -8, z = -0.15},
  669. maxacc = {x = 0.15, y = -8, z = 0.15},
  670. minexptime = 0.2,
  671. maxexptime = 0.5,
  672. minsize = 0.1,
  673. maxsize = 0.1,
  674. collisiondetection = true,
  675. texture = "anvil_particle_heat.png",
  676. glow = 13,
  677. })
  678. local timer = minetest.get_node_timer(pos)
  679. timer:start(1.0)
  680. end
  681. end
  682. -- Check if diggable.
  683. function anvil.can_dig(pos, user)
  684. if not user or not user:is_player() then
  685. return false
  686. end
  687. if not anvil.player_can_use(pos, user) then
  688. return false
  689. end
  690. return true
  691. end
  692. -- Make sparks fly!
  693. function anvil.sparks_and_sound(pos)
  694. local pos = vector.add(pos, {
  695. x = math.random(-10, 10) / 30,
  696. y = 0.2,
  697. z = math.random(-10, 10) / 30
  698. })
  699. minetest.add_particlespawner({
  700. amount = 6,
  701. time = 0.2,
  702. collisiondetection = true,
  703. collision_removal = true,
  704. texture = "anvil_particle_spark.png",
  705. glow = 13,
  706. minsize = 0.5,
  707. maxsize = 0.5,
  708. minpos = vector.add(pos, {x=0, y=0, z=0}),
  709. maxpos = vector.add(pos, {x=0, y=0, z=0}),
  710. minvel = {x=-4, y=1, z=-4},
  711. maxvel = {x=4, y=2, z=4},
  712. minacc = {x=0, y=-8, z=0},
  713. maxacc = {x=0, y=-8, z=0},
  714. minexptime = 0.5,
  715. maxexptime = 1.0,
  716. })
  717. ambiance.sound_play("anvil_clang", pos, 0.3, 40)
  718. end
  719. -- Rotate the anvil (and the display entity).
  720. function anvil.on_rotate(pos, node, user, mode, new_param2)
  721. if mode ~= screwdriver.ROTATE_FACE then
  722. return false
  723. end
  724. node.param2 = new_param2
  725. minetest.swap_node(pos, node)
  726. minetest.check_for_falling(pos)
  727. -- Rotate the display entity if we have one.
  728. anvil.update_entity(pos)
  729. -- Returning 'true' means we did the rotation ourselves.
  730. return true
  731. end
  732. -- Burn player and play a sound.
  733. function anvil.burn_user(pos, user)
  734. utility.damage_player(user, "heat", 1*250)
  735. minetest.sound_play("default_item_smoke", {pos=pos, max_hear_distance=16, gain=0.5}, true)
  736. local p2 = vector.add(pos, anvil.entity_offset)
  737. minetest.add_particlespawner({
  738. amount = 3,
  739. time = 0.1,
  740. minpos = {x = p2.x - 0.1, y = p2.y + 0.1, z = p2.z - 0.1 },
  741. maxpos = {x = p2.x + 0.1, y = p2.y + 0.2, z = p2.z + 0.1 },
  742. minvel = {x = 0, y = 2.5, z = 0},
  743. maxvel = {x = 0, y = 2.5, z = 0},
  744. minacc = {x = -0.15, y = -0.02, z = -0.15},
  745. maxacc = {x = 0.15, y = -0.01, z = 0.15},
  746. minexptime = 4,
  747. maxexptime = 6,
  748. minsize = 5,
  749. maxsize = 5,
  750. collisiondetection = false,
  751. texture = "default_item_smoke.png"
  752. })
  753. end
  754. -- Restart the cooldown timer if needed.
  755. function anvil.start_timer_if_needed(pos, time)
  756. local meta = minetest.get_meta(pos)
  757. if meta:get_int("heat") > 0 or meta:get_int("strike") > 0 then
  758. local timer = minetest.get_node_timer(pos)
  759. if not timer:is_started() then
  760. if time then
  761. timer:start(time)
  762. else
  763. timer:start(1.0)
  764. end
  765. end
  766. end
  767. end
  768. if not anvil.registered then
  769. anvil.registered = true
  770. minetest.register_on_player_receive_fields(function(...)
  771. return anvil.on_player_receive_fields(...) end)
  772. -- Display entity.
  773. minetest.register_entity("anvil:item", {
  774. initial_properties = {
  775. visual = "item",
  776. wield_item = "default:coal_lump",
  777. visual_size = {x=0.3, y=0.3, z=0.2},
  778. collide_with_objects = false,
  779. pointable = false,
  780. collisionbox = {0},
  781. selectionbox = {0},
  782. },
  783. on_activate = function(...) return anvil.on_activate(...) end,
  784. get_staticdata = function(...) return anvil.get_staticdata(...) end,
  785. set_display_item = function(...) return anvil.set_display_item(...) end,
  786. })
  787. local function box(x1, y1, z1, x2, y2, z2)
  788. return {
  789. x1 / 16 - 0.5,
  790. y1 / 16 - 0.5,
  791. z1 / 16 - 0.5,
  792. x2 / 16 - 0.5,
  793. y2 / 16 - 0.5,
  794. z2 / 16 - 0.5,
  795. }
  796. end
  797. -- The node.
  798. minetest.register_node("anvil:anvil", {
  799. description = "Blacksmithing Anvil",
  800. drawtype = "nodebox",
  801. tiles = {
  802. {name="anvil_tool_anvil.png"},
  803. {name="anvil_tool_anvil.png"},
  804. {name="anvil_tool_anvil.png"},
  805. {name="anvil_tool_anvil.png"},
  806. {name="anvil_tool_anvil.png"},
  807. {name="anvil_tool_anvil.png"},
  808. },
  809. paramtype = "light",
  810. paramtype2 = "facedir",
  811. on_rotate = function(...) return anvil.on_rotate(...) end,
  812. node_box = {
  813. type = "fixed",
  814. fixed = {
  815. box(-3, 8, 7, 0, 10, 9),
  816. box(0, 7, 6, 3, 10, 10),
  817. box(3, 7, 5, 16, 10, 11),
  818. box(16, 8, 6, 18, 10, 10),
  819. box(3, 6, 6, 15, 7, 10),
  820. box(5, 3, 6, 11, 6, 10),
  821. box(3, 2, 5, 13, 3, 11),
  822. box(2, 0, 5, 14, 2, 11),
  823. box(2, 0, 3, 4, 2, 13),
  824. box(12, 0, 3, 14, 2, 13),
  825. },
  826. },
  827. selection_box = {
  828. type = "fixed",
  829. fixed = {
  830. box(-3, 7, 5, 18, 10, 11),
  831. },
  832. },
  833. collision_box = {
  834. type = "fixed",
  835. fixed = {
  836. box(0, 0, 4, 16, 10, 12),
  837. },
  838. },
  839. groups = utility.dig_groups("machine", {falling_node=1}),
  840. drop = 'anvil:anvil',
  841. sounds = default.node_sound_metal_defaults({dig={name="default_silence", gain=1.0}}),
  842. stack_max = 1,
  843. crushing_damage = 19*500,
  844. on_construct = function(...) return anvil.on_construct(...) end,
  845. on_destruct = function(...) return anvil.on_destruct(...) end,
  846. on_blast = function(...) return anvil.on_blast(...) end,
  847. on_collapse_to_entity = function(...) return anvil.on_collapse_to_entity(...) end,
  848. on_finish_collapse = function(...) return anvil.on_finish_collapse(...) end,
  849. on_rightclick = function(...) return anvil.on_rightclick(...) end,
  850. allow_metadata_inventory_move = function(...) return anvil.allow_metadata_inventory_move(...) end,
  851. allow_metadata_inventory_put = function(...) return anvil.allow_metadata_inventory_put(...) end,
  852. allow_metadata_inventory_take = function(...) return anvil.allow_metadata_inventory_take(...) end,
  853. on_metadata_inventory_move = function(...) return anvil.on_metadata_inventory_move(...) end,
  854. on_metadata_inventory_put = function(...) return anvil.on_metadata_inventory_put(...) end,
  855. on_metadata_inventory_take = function(...) return anvil.on_metadata_inventory_take(...) end,
  856. after_place_node = function(...) return anvil.after_place_node(...) end,
  857. on_punch = function(...) return anvil.on_punch(...) end,
  858. on_timer = function(...) return anvil.on_timer(...) end,
  859. can_dig = function(...) return anvil.can_dig(...) end,
  860. _on_update_infotext = function(...) return anvil.update_infotext(...) end,
  861. _on_update_formspec = function(...) return anvil.update_formspec(...) end,
  862. _on_update_entity = function(...) return anvil.update_entity(...) end,
  863. _on_pre_fall = function(...) return anvil.on_pre_fall(...) end,
  864. })
  865. -- Hammering tool.
  866. minetest.register_tool("anvil:hammer", {
  867. description = "Blacksmithing Hammer",
  868. inventory_image = "anvil_tool_steelhammer.png",
  869. wield_image = "anvil_tool_steelhammer.png",
  870. tool_capabilities = tooldata["hammer_hammer"],
  871. sounds = {
  872. breaks = "basictools_tool_breaks",
  873. },
  874. groups = {not_repaired_by_anvil=1},
  875. })
  876. minetest.register_craft({
  877. output = "anvil:hammer",
  878. recipe = {
  879. {"carbon_steel:ingot", "darkage:iron_stick", "carbon_steel:ingot"},
  880. {"carbon_steel:ingot", "darkage:iron_stick", "carbon_steel:ingot"},
  881. {"", "darkage:iron_stick", ""},
  882. },
  883. })
  884. minetest.register_craft({
  885. output = "anvil:anvil",
  886. recipe = {
  887. {"carbon_steel:ingot", "carbon_steel:ingot", "carbon_steel:ingot"},
  888. {"", "cast_iron:ingot", ""},
  889. {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
  890. },
  891. })
  892. -- Register mod reloadable.
  893. local c = "anvil:core"
  894. local f = anvil.modpath .. "/init.lua"
  895. reload.register_file(c, f, false)
  896. end