init.lua 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. ---------------------------------------------------------------------------------------
  2. -- simple anvil that can be used to repair tools
  3. ---------------------------------------------------------------------------------------
  4. -- * can be used to repair tools
  5. -- * the hammer gets dammaged a bit at each repair step
  6. ---------------------------------------------------------------------------------------
  7. anvil = anvil or {}
  8. anvil.modpath = minetest.get_modpath("anvil")
  9. -- Localize for performance.
  10. local math_floor = math.floor
  11. local math_random = math.random
  12. anvil.tmp = anvil.tmp or {}
  13. -- Item entity's displacement above the anvil.
  14. local item_displacement = 7/16
  15. local remove_item = function(pos, node)
  16. local objs = minetest.env:get_objects_inside_radius({x = pos.x, y = pos.y + item_displacement, z = pos.z}, .5)
  17. if objs then
  18. for _, obj in ipairs(objs) do
  19. if obj and obj:get_luaentity() and obj:get_luaentity().name == "anvil:item" then
  20. obj:remove()
  21. end
  22. end
  23. end
  24. end
  25. local update_item = function(pos, node)
  26. local meta = minetest.env:get_meta(pos)
  27. local inv = meta:get_inventory()
  28. if not inv:is_empty("input") then
  29. pos.y = pos.y + item_displacement
  30. anvil.tmp.nodename = node.name
  31. anvil.tmp.texture = inv:get_stack("input", 1):get_name()
  32. local e = minetest.env:add_entity(pos,"anvil:item")
  33. local yaw = math.pi*2 - node.param2 * math.pi/2
  34. e:set_yaw(yaw)
  35. else
  36. remove_item(pos, node)
  37. end
  38. end
  39. local update_item_if_needed = function(pos, node)
  40. local test_pos = {x=pos.x, y=pos.y + item_displacement, z=pos.z}
  41. if #minetest.get_objects_inside_radius(test_pos, 0.5) > 0 then
  42. return
  43. end
  44. update_item(pos, node)
  45. end
  46. function anvil.on_activate(self, staticdata)
  47. if anvil.tmp.nodename ~= nil and anvil.tmp.texture ~= nil then
  48. self.nodename = anvil.tmp.nodename
  49. anvil.tmp.nodename = nil
  50. self.texture = anvil.tmp.texture
  51. anvil.tmp.texture = nil
  52. else
  53. if staticdata ~= nil and staticdata ~= "" then
  54. local data = staticdata:split(';')
  55. if data and data[1] and data[2] then
  56. self.nodename = data[1]
  57. self.texture = data[2]
  58. end
  59. end
  60. end
  61. if self.texture ~= nil then
  62. self.object:set_properties({textures={self.texture}})
  63. end
  64. end
  65. function anvil.get_staticdata(self)
  66. if self.nodename ~= nil and self.texture ~= nil then
  67. return self.nodename .. ';' .. self.texture
  68. end
  69. return ""
  70. end
  71. function anvil.on_construct(pos)
  72. local meta = minetest.get_meta(pos)
  73. local inv = meta:get_inventory()
  74. inv:set_size("input", 1)
  75. end
  76. function anvil.on_destruct(pos)
  77. local meta = minetest.get_meta(pos)
  78. local inv = meta:get_inventory()
  79. local item = inv:get_stack("input", 1)
  80. if item:get_count() == 1 then
  81. minetest.add_item(pos, item)
  82. inv:set_stack("input", 1, ItemStack(""))
  83. end
  84. remove_item(pos, minetest.get_node(pos))
  85. end
  86. function anvil.on_finish_collapse(pos, node)
  87. local meta = minetest.get_meta(pos)
  88. local inv = meta:get_inventory()
  89. inv:set_stack("input", 1, ItemStack(""))
  90. end
  91. function anvil.after_place_node(pos, placer)
  92. local meta = minetest.get_meta(pos)
  93. meta:set_string("owner", placer:get_player_name() or "")
  94. meta:set_string("infotext", "Blacksmithing Anvil")
  95. end
  96. function anvil.can_dig(pos, player)
  97. local meta = minetest.get_meta(pos)
  98. local inv = meta:get_inventory()
  99. if not inv:is_empty("input") then
  100. return false
  101. end
  102. return true
  103. end
  104. function anvil.allow_metadata_inventory_put(pos, listname, index, stack, player)
  105. if not player or not player:is_player() then
  106. return 0
  107. end
  108. if minetest.test_protection(pos, player:get_player_name()) then
  109. return 0
  110. end
  111. if listname ~= "input" then
  112. return 0
  113. end
  114. -- Enable use with anvil/hammer craft recipes.
  115. local is_recipe = minetest.get_craft_result({method="anvil", width=1, items={stack}}).time ~= 0
  116. if not is_recipe then
  117. -- If not a recipe, it must be a valid tool type that can be repaired.
  118. if (listname == 'input'
  119. and (stack:get_wear() == 0
  120. or minetest.get_item_group(stack:get_name(), "not_repaired_by_anvil") ~= 0
  121. or stack:get_name() == "cans:water_can"
  122. or stack:get_name() == "cans:lava_can" )) then
  123. -- Report error if tool is not the wieldhand.
  124. if stack:get_name() ~= "" then
  125. local pname = player:get_player_name()
  126. minetest.chat_send_player(pname, '# Server: Not a hammerable item or tool needing repair.')
  127. end
  128. return 0
  129. end
  130. end
  131. local meta = minetest.get_meta(pos)
  132. if meta:get_inventory():room_for_item("input", stack) then
  133. return stack:get_count()
  134. end
  135. return 0
  136. end
  137. function anvil.allow_metadata_inventory_move()
  138. return 0
  139. end
  140. function anvil.allow_metadata_inventory_take(pos, listname, index, stack, player)
  141. if not player or not player:is_player() then
  142. return 0
  143. end
  144. if minetest.test_protection(pos, player:get_player_name()) then
  145. return 0
  146. end
  147. if listname~="input" then
  148. return 0
  149. end
  150. return stack:get_count()
  151. end
  152. function anvil.on_rightclick(pos, node, clicker, itemstack)
  153. if not clicker or not clicker:is_player() then
  154. return
  155. end
  156. if minetest.test_protection(pos, clicker:get_player_name()) then
  157. return itemstack
  158. end
  159. -- If player is wielding nothing.
  160. if itemstack:get_count() == 0 then
  161. local meta = minetest.env:get_meta(pos)
  162. local inv = meta:get_inventory()
  163. if not inv:is_empty("input") then
  164. local return_stack = inv:get_stack("input", 1)
  165. inv:set_stack("input", 1, nil)
  166. --clicker:get_inventory():add_item("main", return_stack)
  167. remove_item(pos, node)
  168. return return_stack
  169. --return itemstack
  170. end
  171. end
  172. local this_def = minetest.reg_ns_nodes[node.name]
  173. if this_def.allow_metadata_inventory_put(pos, "input", 1, itemstack, clicker) > 0 then
  174. local s = itemstack:take_item(itemstack:get_count())
  175. local meta = minetest.env:get_meta(pos)
  176. local inv = meta:get_inventory()
  177. inv:add_item("input", s)
  178. update_item(pos,node)
  179. end
  180. return itemstack
  181. end
  182. function anvil.on_punch(pos, node, puncher)
  183. if( not( pos ) or not( node ) or not( puncher )) then
  184. return
  185. end
  186. if minetest.test_protection(pos, puncher:get_player_name()) then
  187. return
  188. end
  189. update_item_if_needed(pos, node)
  190. local wielded = puncher:get_wielded_item()
  191. local meta = minetest.get_meta(pos)
  192. local inv = meta:get_inventory()
  193. if wielded:get_count() == 0 then
  194. if not inv:is_empty("input") then
  195. local return_stack = inv:get_stack("input", 1)
  196. inv:set_stack("input", 1, nil)
  197. puncher:get_inventory():add_item("main", return_stack)
  198. remove_item(pos, node)
  199. end
  200. end
  201. -- Only punching with the hammer is supposed to work.
  202. local wieldname = wielded:get_name()
  203. if wieldname ~= 'anvil:hammer' and wieldname ~= "xdecor:hammer" then
  204. return
  205. end
  206. local input = inv:get_stack('input', 1)
  207. local cr, ci = minetest.get_craft_result({method="anvil", width=1, items={input}})
  208. local is_recipe = cr.time ~= 0
  209. -- Only tools can be repaired.
  210. -- Also allow recipe input.
  211. if not is_recipe then
  212. if ( not( input )
  213. or input:is_empty()
  214. or input:get_name() == "cans:water_can"
  215. or input:get_name() == "cans:lava_can" ) then
  216. return
  217. end
  218. end
  219. -- Show wear-status HUD element only if repairing a tool.
  220. if not is_recipe and input:get_wear() > 0 then
  221. -- 65535 is max damage.
  222. local damage_state = 40-math_floor(input:get_wear()/1638)
  223. local hud2 = nil
  224. local hud3 = nil
  225. local pname = puncher:get_player_name()
  226. hud2 = puncher:hud_add({
  227. hud_elem_type = "statbar",
  228. text = "default_cloud.png^[colorize:#ff0000:256",
  229. number = 40,
  230. direction = 0, -- left to right
  231. position = {x=0.5, y=0.65},
  232. alignment = {x = 0, y = 0},
  233. offset = {x = -320, y = 0},
  234. size = {x=32, y=32},
  235. })
  236. hud3 = puncher:hud_add({
  237. hud_elem_type = "statbar",
  238. text = "default_cloud.png^[colorize:#00ff00:256",
  239. number = damage_state,
  240. direction = 0, -- left to right
  241. position = {x=0.5, y=0.65},
  242. alignment = {x = 0, y = 0},
  243. offset = {x = -320, y = 0},
  244. size = {x=32, y=32},
  245. })
  246. minetest.after(2, function()
  247. local puncher = minetest.get_player_by_name(pname)
  248. if puncher and puncher:is_player() then
  249. puncher:hud_remove(hud2)
  250. puncher:hud_remove(hud3)
  251. end
  252. end)
  253. end
  254. -- Tell the player when the job is done.
  255. local tool_name = input:get_name()
  256. if minetest.registered_tools[tool_name] then
  257. if not is_recipe and input:get_wear() == 0 then
  258. local tool_desc = "Unknown Tool"
  259. if minetest.registered_items[tool_name] and minetest.registered_items[tool_name].description then
  260. tool_desc = utility.get_short_desc(minetest.registered_items[tool_name].description)
  261. end
  262. local pname = puncher:get_player_name()
  263. minetest.chat_send_player(pname, '# Server: Your "' .. tool_desc .. '" has been repaired successfully.')
  264. return
  265. end
  266. end
  267. -- Show sparks if repairing a tool or crafting an item.
  268. if is_recipe or input:get_wear() > 0 then
  269. pos.y = pos.y + item_displacement
  270. ambiance.sound_play("anvil_clang", pos, 1.0, 30)
  271. minetest.add_particlespawner({
  272. amount = math_random(3, 10),
  273. time = 0.1,
  274. minpos = pos,
  275. maxpos = pos,
  276. minvel = {x=2, y=3, z=2},
  277. maxvel = {x=-2, y=1, z=-2},
  278. minacc = {x=0, y= -10, z=0},
  279. maxacc = {x=0, y= -10, z=0},
  280. minexptime = 0.5,
  281. maxexptime = 1,
  282. minsize = 1,
  283. maxsize = 1,
  284. collisiondetection = true,
  285. vertical = false,
  286. texture = "anvil_spark.png",
  287. })
  288. end
  289. if not is_recipe then
  290. -- Do the actual repair.
  291. input:add_wear( -1500 )
  292. inv:set_stack("input", 1, input)
  293. else
  294. -- Hammer is being used for crafting!
  295. local p = {x=pos.x, y=pos.y + 0.5, z=pos.z}
  296. local r = minetest.add_item(p, cr.item)
  297. if r then
  298. r:add_velocity({
  299. x = math_random(-1, 1),
  300. y = 2,
  301. z = math_random(-1, 1),
  302. })
  303. local rs = ci.items[1]
  304. if rs:get_count() > 0 then
  305. inv:set_stack("input", 1, rs)
  306. update_item_if_needed(pos, node)
  307. else
  308. inv:set_stack("input", 1, ItemStack(""))
  309. remove_item(pos, node)
  310. end
  311. end
  312. end
  313. -- Damage the hammer slightly.
  314. local hammerwear = 300
  315. -- The xdecor hammer wears out faster (it is cheaper to craft).
  316. if wieldname == "xdecor:hammer" then
  317. hammerwear = 1000
  318. end
  319. -- Wear is less if hammer is used for crafting.
  320. if is_recipe then hammerwear = hammerwear / 3 end
  321. -- Now apply damage to hammer and update wielded item.
  322. wielded:add_wear(hammerwear)
  323. if wielded:is_empty() then
  324. ambiance.sound_play("default_tool_breaks", pos, 1.0, 10)
  325. end
  326. puncher:set_wielded_item(wielded)
  327. end
  328. if not anvil.registered then
  329. minetest.register_alias("castle:anvil", "anvil:anvil")
  330. -- The hammer for the anvil.
  331. minetest.register_tool("anvil:hammer", {
  332. description = "Steel Blacksmithing Hammer",
  333. groups = {not_repaired_by_anvil = 1},
  334. sound = {breaks = "default_tool_breaks"},
  335. image = "anvil_tool_steelhammer.png",
  336. inventory_image = "anvil_tool_steelhammer.png",
  337. tool_capabilities = tooldata["hammer_hammer"],
  338. })
  339. -- The anvil itself.
  340. minetest.register_node("anvil:anvil", {
  341. drawtype = "nodebox",
  342. description = "Anvil",
  343. tiles = {"chains_iron.png", "default_stone.png"},
  344. paramtype = "light",
  345. paramtype2 = "facedir",
  346. groups = utility.dig_groups("machine", {falling_node=1}),
  347. sounds = default.node_sound_metal_defaults(),
  348. is_ground_content = false,
  349. -- The nodebox model comes from realtest.
  350. -- It has been modified to fit this game.
  351. node_box = {
  352. type = "fixed",
  353. fixed = {
  354. -- Base
  355. {-0.5,-0.5,-0.3,0.5,-0.4,0.3},
  356. -- Column
  357. {-0.35,-0.4,-0.25,0.35,-0.3,0.25},
  358. {-0.3,-0.3,-0.15,0.3,-0.1,0.15},
  359. -- Top
  360. {-0.5,-0.1,-0.2,0.5,0.1,0.2},
  361. },
  362. },
  363. selection_box = {
  364. type = "fixed",
  365. fixed = {
  366. -- Base
  367. {-0.5,-0.5,-0.3,0.5,-0.4,0.3},
  368. -- Column
  369. {-0.35,-0.4,-0.25,0.35,-0.3,0.25},
  370. {-0.3,-0.3,-0.15,0.3,-0.1,0.15},
  371. -- Top
  372. {-0.5,-0.1,-0.2,0.5,0.1,0.2},
  373. }
  374. },
  375. on_construct = function(...)
  376. return anvil.on_construct(...)
  377. end,
  378. on_destruct = function(...)
  379. return anvil.on_destruct(...)
  380. end,
  381. on_finish_collapse = function(...)
  382. return anvil.on_finish_collapse(...)
  383. end,
  384. after_place_node = function(...)
  385. return anvil.after_place_node(...)
  386. end,
  387. can_dig = function(...)
  388. return anvil.can_dig(...)
  389. end,
  390. allow_metadata_inventory_put = function(...)
  391. return anvil.allow_metadata_inventory_put(...)
  392. end,
  393. allow_metadata_inventory_take = function(...)
  394. return anvil.allow_metadata_inventory_take(...)
  395. end,
  396. allow_metadata_inventory_move = function(...)
  397. return anvil.allow_metadata_inventory_move(...)
  398. end,
  399. on_rightclick = function(...)
  400. return anvil.on_rightclick(...)
  401. end,
  402. on_punch = function(...)
  403. return anvil.on_punch(...)
  404. end,
  405. })
  406. minetest.register_entity("anvil:item", {
  407. hp_max = 1,
  408. visual="wielditem",
  409. visual_size={x=.33,y=.33},
  410. collisionbox = {0,0,0,0,0,0},
  411. physical=false,
  412. textures={"air"},
  413. on_activate = function(...)
  414. return anvil.on_activate(...)
  415. end,
  416. get_staticdata = function(...)
  417. return anvil.get_staticdata(...)
  418. end,
  419. })
  420. minetest.register_craft({
  421. output = "anvil:anvil",
  422. recipe = {
  423. {"carbon_steel:ingot","carbon_steel:ingot","carbon_steel:ingot"},
  424. {'', "cast_iron:ingot",'' },
  425. {"default:steel_ingot","default:steel_ingot","default:steel_ingot"},
  426. },
  427. })
  428. minetest.register_craft({
  429. output = "anvil:hammer",
  430. recipe = {
  431. {"carbon_steel:ingot","group:stick","carbon_steel:ingot"},
  432. {"carbon_steel:ingot","group:stick","carbon_steel:ingot"},
  433. {'', "group:stick", '' },
  434. },
  435. })
  436. local c = "anvil:core"
  437. local f = anvil.modpath .. "/init.lua"
  438. reload.register_file(c, f, false)
  439. anvil.registered = true
  440. end