init.lua 27 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151
  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. elseif minetest.find_node_near(pos, 2, "group:lava") then
  623. repair_amount = 5000
  624. end
  625. -- Max wear is 65535 (16 bit unsigned).
  626. wear = wear - repair_amount
  627. if wear < 0 then wear = 0 end
  628. stack:set_wear(wear)
  629. list[index] = stack
  630. inv:set_list("input", list)
  631. meta:set_int("strike", 1)
  632. meta:set_int("heat", meta:get_int("heat") + 10)
  633. anvil.update_infotext(pos)
  634. anvil.start_timer_if_needed(pos)
  635. return true
  636. end
  637. end
  638. -- Nothing repaired.
  639. return false
  640. end
  641. -- Timer fires.
  642. function anvil.on_timer(pos, elapsed)
  643. local meta = minetest.get_meta(pos)
  644. local heat = meta:get_int("heat")
  645. meta:set_int("strike", 0)
  646. -- Cool off over time.
  647. if heat > 0 then
  648. local loss = 1
  649. if minetest.find_node_near(pos, 2, "group:water") then
  650. loss = 3
  651. end
  652. heat = heat - loss
  653. if heat < 0 then
  654. heat = 0
  655. end
  656. meta:set_int("heat", heat)
  657. anvil.update_infotext(pos)
  658. local p2 = vector.add(pos, anvil.entity_offset)
  659. p2.y = p2.y - 0.2
  660. minetest.add_particlespawner({
  661. amount = 6,
  662. time = 1,
  663. minpos = {x = p2.x - 0.1, y = p2.y + 0.1, z = p2.z - 0.1 },
  664. maxpos = {x = p2.x + 0.1, y = p2.y + 0.2, z = p2.z + 0.1 },
  665. minvel = {x = -3, y = 0.5, z = -3},
  666. maxvel = {x = 3, y = 0.5, z = 3},
  667. minacc = {x = -0.15, y = -8, z = -0.15},
  668. maxacc = {x = 0.15, y = -8, z = 0.15},
  669. minexptime = 0.2,
  670. maxexptime = 0.5,
  671. minsize = 0.1,
  672. maxsize = 0.1,
  673. collisiondetection = true,
  674. texture = "anvil_particle_heat.png",
  675. glow = 13,
  676. })
  677. local timer = minetest.get_node_timer(pos)
  678. timer:start(1.0)
  679. end
  680. end
  681. -- Check if diggable.
  682. function anvil.can_dig(pos, user)
  683. if not user or not user:is_player() then
  684. return false
  685. end
  686. if not anvil.player_can_use(pos, user) then
  687. return false
  688. end
  689. return true
  690. end
  691. -- Make sparks fly!
  692. function anvil.sparks_and_sound(pos)
  693. local pos = vector.add(pos, {
  694. x = math.random(-10, 10) / 30,
  695. y = 0.2,
  696. z = math.random(-10, 10) / 30
  697. })
  698. minetest.add_particlespawner({
  699. amount = 6,
  700. time = 0.2,
  701. collisiondetection = true,
  702. collision_removal = true,
  703. texture = "anvil_particle_spark.png",
  704. glow = 13,
  705. minsize = 0.5,
  706. maxsize = 0.5,
  707. minpos = vector.add(pos, {x=0, y=0, z=0}),
  708. maxpos = vector.add(pos, {x=0, y=0, z=0}),
  709. minvel = {x=-4, y=1, z=-4},
  710. maxvel = {x=4, y=2, z=4},
  711. minacc = {x=0, y=-8, z=0},
  712. maxacc = {x=0, y=-8, z=0},
  713. minexptime = 0.5,
  714. maxexptime = 1.0,
  715. })
  716. ambiance.sound_play("anvil_clang", pos, 0.3, 40)
  717. end
  718. -- Rotate the anvil (and the display entity).
  719. function anvil.on_rotate(pos, node, user, mode, new_param2)
  720. if mode ~= screwdriver.ROTATE_FACE then
  721. return false
  722. end
  723. node.param2 = new_param2
  724. minetest.swap_node(pos, node)
  725. minetest.check_for_falling(pos)
  726. -- Rotate the display entity if we have one.
  727. anvil.update_entity(pos)
  728. -- Returning 'true' means we did the rotation ourselves.
  729. return true
  730. end
  731. -- Burn player and play a sound.
  732. function anvil.burn_user(pos, user)
  733. utility.damage_player(user, "heat", 1*250)
  734. minetest.sound_play("default_item_smoke", {pos=pos, max_hear_distance=16, gain=0.5}, true)
  735. local p2 = vector.add(pos, anvil.entity_offset)
  736. minetest.add_particlespawner({
  737. amount = 3,
  738. time = 0.1,
  739. minpos = {x = p2.x - 0.1, y = p2.y + 0.1, z = p2.z - 0.1 },
  740. maxpos = {x = p2.x + 0.1, y = p2.y + 0.2, z = p2.z + 0.1 },
  741. minvel = {x = 0, y = 2.5, z = 0},
  742. maxvel = {x = 0, y = 2.5, z = 0},
  743. minacc = {x = -0.15, y = -0.02, z = -0.15},
  744. maxacc = {x = 0.15, y = -0.01, z = 0.15},
  745. minexptime = 4,
  746. maxexptime = 6,
  747. minsize = 5,
  748. maxsize = 5,
  749. collisiondetection = false,
  750. texture = "default_item_smoke.png"
  751. })
  752. end
  753. -- Restart the cooldown timer if needed.
  754. function anvil.start_timer_if_needed(pos, time)
  755. local meta = minetest.get_meta(pos)
  756. if meta:get_int("heat") > 0 or meta:get_int("strike") > 0 then
  757. local timer = minetest.get_node_timer(pos)
  758. if not timer:is_started() then
  759. if time then
  760. timer:start(time)
  761. else
  762. timer:start(1.0)
  763. end
  764. end
  765. end
  766. end
  767. if not anvil.registered then
  768. anvil.registered = true
  769. minetest.register_on_player_receive_fields(function(...)
  770. return anvil.on_player_receive_fields(...) end)
  771. -- Display entity.
  772. minetest.register_entity("anvil:item", {
  773. initial_properties = {
  774. visual = "item",
  775. wield_item = "default:coal_lump",
  776. visual_size = {x=0.3, y=0.3, z=0.2},
  777. collide_with_objects = false,
  778. pointable = false,
  779. collisionbox = {0},
  780. selectionbox = {0},
  781. },
  782. on_activate = function(...) return anvil.on_activate(...) end,
  783. get_staticdata = function(...) return anvil.get_staticdata(...) end,
  784. set_display_item = function(...) return anvil.set_display_item(...) end,
  785. })
  786. local function box(x1, y1, z1, x2, y2, z2)
  787. return {
  788. x1 / 16 - 0.5,
  789. y1 / 16 - 0.5,
  790. z1 / 16 - 0.5,
  791. x2 / 16 - 0.5,
  792. y2 / 16 - 0.5,
  793. z2 / 16 - 0.5,
  794. }
  795. end
  796. -- The node.
  797. minetest.register_node("anvil:anvil", {
  798. description = "Blacksmithing Anvil",
  799. drawtype = "nodebox",
  800. tiles = {
  801. {name="anvil_tool_anvil.png"},
  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. },
  808. paramtype = "light",
  809. paramtype2 = "facedir",
  810. on_rotate = function(...) return anvil.on_rotate(...) end,
  811. node_box = {
  812. type = "fixed",
  813. fixed = {
  814. box(-3, 8, 7, 0, 10, 9),
  815. box(0, 7, 6, 3, 10, 10),
  816. box(3, 7, 5, 16, 10, 11),
  817. box(16, 8, 6, 18, 10, 10),
  818. box(3, 6, 6, 15, 7, 10),
  819. box(5, 3, 6, 11, 6, 10),
  820. box(3, 2, 5, 13, 3, 11),
  821. box(2, 0, 5, 14, 2, 11),
  822. box(2, 0, 3, 4, 2, 13),
  823. box(12, 0, 3, 14, 2, 13),
  824. },
  825. },
  826. selection_box = {
  827. type = "fixed",
  828. fixed = {
  829. box(-3, 7, 5, 18, 10, 11),
  830. },
  831. },
  832. collision_box = {
  833. type = "fixed",
  834. fixed = {
  835. box(0, 0, 4, 16, 10, 12),
  836. },
  837. },
  838. groups = utility.dig_groups("machine", {falling_node=1}),
  839. drop = 'anvil:anvil',
  840. sounds = default.node_sound_metal_defaults({dig={name="default_silence", gain=1.0}}),
  841. stack_max = 1,
  842. crushing_damage = 19*500,
  843. on_construct = function(...) return anvil.on_construct(...) end,
  844. on_destruct = function(...) return anvil.on_destruct(...) end,
  845. on_blast = function(...) return anvil.on_blast(...) end,
  846. on_collapse_to_entity = function(...) return anvil.on_collapse_to_entity(...) end,
  847. on_finish_collapse = function(...) return anvil.on_finish_collapse(...) end,
  848. on_rightclick = function(...) return anvil.on_rightclick(...) end,
  849. allow_metadata_inventory_move = function(...) return anvil.allow_metadata_inventory_move(...) end,
  850. allow_metadata_inventory_put = function(...) return anvil.allow_metadata_inventory_put(...) end,
  851. allow_metadata_inventory_take = function(...) return anvil.allow_metadata_inventory_take(...) end,
  852. on_metadata_inventory_move = function(...) return anvil.on_metadata_inventory_move(...) end,
  853. on_metadata_inventory_put = function(...) return anvil.on_metadata_inventory_put(...) end,
  854. on_metadata_inventory_take = function(...) return anvil.on_metadata_inventory_take(...) end,
  855. after_place_node = function(...) return anvil.after_place_node(...) end,
  856. on_punch = function(...) return anvil.on_punch(...) end,
  857. on_timer = function(...) return anvil.on_timer(...) end,
  858. can_dig = function(...) return anvil.can_dig(...) end,
  859. _on_update_infotext = function(...) return anvil.update_infotext(...) end,
  860. _on_update_formspec = function(...) return anvil.update_formspec(...) end,
  861. _on_update_entity = function(...) return anvil.update_entity(...) end,
  862. _on_pre_fall = function(...) return anvil.on_pre_fall(...) end,
  863. })
  864. -- Hammering tool.
  865. minetest.register_tool("anvil:hammer", {
  866. description = "Blacksmithing Hammer",
  867. inventory_image = "anvil_tool_steelhammer.png",
  868. wield_image = "anvil_tool_steelhammer.png",
  869. tool_capabilities = tooldata["hammer_hammer"],
  870. sounds = {
  871. breaks = "basictools_tool_breaks",
  872. },
  873. groups = {not_repaired_by_anvil=1},
  874. })
  875. minetest.register_craft({
  876. output = "anvil:hammer",
  877. recipe = {
  878. {"carbon_steel:ingot", "darkage:iron_stick", "carbon_steel:ingot"},
  879. {"carbon_steel:ingot", "darkage:iron_stick", "carbon_steel:ingot"},
  880. {"", "darkage:iron_stick", ""},
  881. },
  882. })
  883. minetest.register_craft({
  884. output = "anvil:anvil",
  885. recipe = {
  886. {"carbon_steel:ingot", "carbon_steel:ingot", "carbon_steel:ingot"},
  887. {"", "cast_iron:ingot", ""},
  888. {"default:steel_ingot", "default:steel_ingot", "default:steel_ingot"},
  889. },
  890. })
  891. -- Register mod reloadable.
  892. local c = "anvil:core"
  893. local f = anvil.modpath .. "/init.lua"
  894. reload.register_file(c, f, false)
  895. end