armor.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  1. ARMOR_INIT_DELAY = 1
  2. ARMOR_INIT_TIMES = 1
  3. ARMOR_BONES_DELAY = 1
  4. ARMOR_UPDATE_TIME = 1
  5. ARMOR_DROP = minetest.get_modpath("bones") ~= nil
  6. ARMOR_DESTROY = false
  7. ARMOR_LEVEL_MULTIPLIER = 1
  8. ARMOR_HEAL_MULTIPLIER = 1
  9. ARMOR_RADIATION_MULTIPLIER = 1
  10. ARMOR_MATERIALS = {
  11. wood = "group:wood",
  12. cactus = "default:cactus",
  13. steel = "default:steel_ingot",
  14. bronze = "default:bronze_ingot",
  15. diamond = "default:diamond",
  16. gold = "default:gold_ingot",
  17. mithril = "moreores:mithril_ingot",
  18. crystal = "ethereal:crystal_ingot",
  19. }
  20. ARMOR_FIRE_PROTECT = minetest.get_modpath("ethereal") ~= nil
  21. ARMOR_FIRE_NODES = {
  22. {"default:lava_source", 5, 8},
  23. {"default:lava_flowing", 5, 8},
  24. {"fire:basic_flame", 3, 4},
  25. {"fire:permanent_flame", 3, 4},
  26. {"ethereal:crystal_spike", 2, 1},
  27. {"ethereal:fire_flower", 2, 1},
  28. {"default:torch", 1, 1},
  29. }
  30. local skin_mod = nil
  31. local inv_mod = nil
  32. local modpath = minetest.get_modpath(ARMOR_MOD_NAME)
  33. local worldpath = minetest.get_worldpath()
  34. local input = io.open(modpath.."/armor.conf", "r")
  35. if input then
  36. dofile(modpath.."/armor.conf")
  37. input:close()
  38. input = nil
  39. end
  40. input = io.open(worldpath.."/armor.conf", "r")
  41. if input then
  42. dofile(worldpath.."/armor.conf")
  43. input:close()
  44. input = nil
  45. end
  46. if not minetest.get_modpath("moreores") then
  47. ARMOR_MATERIALS.mithril = nil
  48. end
  49. if not minetest.get_modpath("ethereal") then
  50. ARMOR_MATERIALS.crystal = nil
  51. end
  52. armor = {
  53. timer = 0,
  54. elements = {"head", "torso", "legs", "feet"},
  55. physics = {"jump","speed","gravity"},
  56. --formspec = "size[8,8.5]image[2,0.75;2,4;armor_preview]"
  57. -- .."list[current_player;main;0,4.5;8,4;]"
  58. -- .."list[current_player;craft;4,1;3,3;]"
  59. -- .."list[current_player;craftpreview;7,2;1,1;]"
  60. -- .."listring[current_player;main]"
  61. -- .."listring[current_player;craft]",
  62. textures = {},
  63. default_skin = "character",
  64. version = "0.4.6",
  65. }
  66. if minetest.get_modpath("inventory_plus") then
  67. inv_mod = "inventory_plus"
  68. armor.formspec =
  69. "size[8,8.5]" ..
  70. default.gui_bg ..
  71. default.gui_bg_img ..
  72. default.gui_slots ..
  73. "button[0,0.5;2,0.5;main;Back]" ..
  74. "image[4,0.25;2,4;armor_preview]" ..
  75. "label[6,0.5;HP Max: hp_max]" ..
  76. "label[6,1;Level: armor_level]" ..
  77. "label[6,1.4;Heal: armor_heal]" ..
  78. --"label[6,1.8;Fire: armor_fire]" ..
  79. --"label[6,2.2;Radiation: armor_radiation]" ..
  80. "list[current_player;main;0,4.25;8,1;]" ..
  81. "list[current_player;main;0,5.5;8,3;8]" ..
  82. default.get_hotbar_bg(0, 4.25)
  83. end
  84. if minetest.get_modpath("skins") then
  85. skin_mod = "skins"
  86. elseif minetest.get_modpath("simple_skins") then
  87. skin_mod = "simple_skins"
  88. elseif minetest.get_modpath("u_skins") then
  89. skin_mod = "u_skins"
  90. elseif minetest.get_modpath("wardrobe") then
  91. skin_mod = "wardrobe"
  92. end
  93. armor.def = {
  94. state = 0,
  95. count = 0,
  96. }
  97. armor.update_player_visuals = function(self, player)
  98. if not player then
  99. return
  100. end
  101. local name = player:get_player_name()
  102. if self.textures[name] then
  103. default.player_set_textures(player, {
  104. self.textures[name].skin,
  105. self.textures[name].armor,
  106. self.textures[name].wielditem,
  107. })
  108. end
  109. end
  110. armor.set_player_armor = function(self, player)
  111. local name, player_inv = armor:get_valid_player(player, "[set_player_armor]")
  112. if not name then
  113. return
  114. end
  115. local armor_texture = "3d_armor_trans.png"
  116. local armor_level = 0
  117. local armor_heal = 0
  118. local armor_fire = 0
  119. local armor_water = 0
  120. local armor_radiation = 0
  121. local state = 0
  122. local items = 0
  123. local elements = {}
  124. local textures = {}
  125. local physics_o = {speed=1,gravity=1,jump=1}
  126. local material = {type=nil, count=1}
  127. local preview = armor:get_preview(name) or "character_preview.png"
  128. for _,v in ipairs(self.elements) do
  129. elements[v] = false
  130. end
  131. for i=1, 6 do
  132. local stack = player_inv:get_stack("armor", i)
  133. local item = stack:get_name()
  134. if stack:get_count() == 1 then
  135. local def = stack:get_definition()
  136. for k, v in pairs(elements) do
  137. if v == false then
  138. local level = def.groups["armor_"..k]
  139. if level then
  140. local texture = def.texture or item:gsub("%:", "_")
  141. table.insert(textures, texture..".png")
  142. preview = preview.."^"..texture.."_preview.png"
  143. armor_level = armor_level + level
  144. state = state + stack:get_wear()
  145. items = items + 1
  146. armor_heal = armor_heal + (def.groups["armor_heal"] or 0)
  147. armor_fire = armor_fire + (def.groups["armor_fire"] or 0)
  148. armor_water = armor_water + (def.groups["armor_water"] or 0)
  149. armor_radiation = armor_radiation + (def.groups["armor_radiation"] or 0)
  150. for kk,vv in ipairs(self.physics) do
  151. local o_value = def.groups["physics_"..vv]
  152. if o_value then
  153. physics_o[vv] = physics_o[vv] + o_value
  154. end
  155. end
  156. local mat = string.match(item, "%:.+_(.+)$")
  157. if material.type then
  158. if material.type == mat then
  159. material.count = material.count + 1
  160. end
  161. else
  162. material.type = mat
  163. end
  164. elements[k] = true
  165. end
  166. end
  167. end
  168. end
  169. end
  170. if minetest.get_modpath("shields") then
  171. armor_level = armor_level * 0.9
  172. end
  173. -- I guess this gives an armor bonus if all armors are the same material?
  174. -- MustTest.
  175. if material.type and material.count == #self.elements then
  176. armor_level = armor_level * 1.1
  177. end
  178. armor_level = armor_level * ARMOR_LEVEL_MULTIPLIER
  179. armor_heal = armor_heal * ARMOR_HEAL_MULTIPLIER
  180. armor_radiation = armor_radiation * ARMOR_RADIATION_MULTIPLIER
  181. if #textures > 0 then
  182. armor_texture = table.concat(textures, "^")
  183. end
  184. local armor_groups = {fleshy=100}
  185. if armor_level > 0 then
  186. armor_groups.level = math.floor(armor_level / 20)
  187. armor_groups.fleshy = 100 - armor_level
  188. armor_groups.radiation = 100 - armor_radiation
  189. end
  190. player:set_armor_groups(armor_groups)
  191. player:set_physics_override(physics_o)
  192. self.textures[name].armor = armor_texture
  193. self.textures[name].preview = preview
  194. self.def[name].state = state
  195. self.def[name].count = items
  196. self.def[name].level = armor_level
  197. self.def[name].heal = armor_heal
  198. self.def[name].jump = physics_o.jump
  199. self.def[name].speed = physics_o.speed
  200. self.def[name].gravity = physics_o.gravity
  201. self.def[name].fire = armor_fire
  202. self.def[name].water = armor_water
  203. self.def[name].radiation = armor_radiation
  204. self:update_player_visuals(player)
  205. end
  206. armor.update_armor = function(self, player)
  207. -- Legacy support: Called when armor levels are changed
  208. -- Other mods can hook on to this function, see hud mod for example
  209. end
  210. armor.get_player_skin = function(self, name)
  211. local skin = nil
  212. if skin_mod == "skins" or skin_mod == "simple_skins" then
  213. skin = skins.skins[name]
  214. elseif skin_mod == "u_skins" then
  215. skin = u_skins.u_skins[name]
  216. elseif skin_mod == "wardrobe" then
  217. skin = string.gsub(wardrobe.playerSkins[name], "%.png$","")
  218. end
  219. return skin or armor.default_skin
  220. end
  221. armor.get_preview = function(self, name)
  222. if skin_mod == "skins" then
  223. return armor:get_player_skin(name).."_preview.png"
  224. end
  225. end
  226. local function get_player_max_hp(name)
  227. local pref = minetest.get_player_by_name(name)
  228. if pref then
  229. return pref:get_properties().hp_max
  230. end
  231. return 20
  232. end
  233. armor.get_armor_formspec = function(self, name)
  234. if not armor.textures[name] then
  235. minetest.log("error", "3d_armor: Player texture["..name.."] is nil [get_armor_formspec]")
  236. return ""
  237. end
  238. if not armor.def[name] then
  239. minetest.log("error", "3d_armor: Armor def["..name.."] is nil [get_armor_formspec]")
  240. return ""
  241. end
  242. local formspec = armor.formspec .. "list[detached:"..name.."_armor;armor;0,1.5;3,2;]"
  243. formspec = formspec:gsub("armor_preview", armor.textures[name].preview)
  244. formspec = formspec:gsub("armor_level", armor.def[name].level)
  245. formspec = formspec:gsub("armor_heal", armor.def[name].heal)
  246. formspec = formspec:gsub("armor_fire", armor.def[name].fire)
  247. formspec = formspec:gsub("armor_radiation", armor.def[name].radiation)
  248. formspec = formspec:gsub("hp_max", tostring(get_player_max_hp(name)))
  249. return formspec
  250. end
  251. armor.update_inventory = function(self, player)
  252. local name = armor:get_valid_player(player, "[set_player_armor]")
  253. if not name or inv_mod == "inventory_enhanced" then
  254. return
  255. end
  256. if inv_mod == "unified_inventory" then
  257. if unified_inventory.current_page[name] == "armor" then
  258. unified_inventory.set_inventory_formspec(player, "armor")
  259. end
  260. else
  261. local formspec = armor:get_armor_formspec(name)
  262. if inv_mod == "inventory_plus" then
  263. formspec = formspec.."listring[current_player;main]"
  264. .."listring[detached:"..name.."_armor;armor]"
  265. local page = player:get_inventory_formspec()
  266. if page:find("detached:"..name.."_armor") then
  267. inventory_plus.set_inventory_formspec(player, formspec)
  268. end
  269. elseif not core.setting_getbool("creative_mode") then
  270. player:set_inventory_formspec(formspec)
  271. end
  272. end
  273. end
  274. armor.get_valid_player = function(self, player, msg)
  275. msg = msg or ""
  276. if not player then
  277. minetest.log("error", "3d_armor: Player reference is nil "..msg)
  278. return
  279. end
  280. local name = player:get_player_name()
  281. if not name then
  282. minetest.log("error", "3d_armor: Player name is nil "..msg)
  283. return
  284. end
  285. local pos = player:getpos()
  286. local player_inv = player:get_inventory()
  287. local armor_inv = minetest.get_inventory({type="detached", name=name.."_armor"})
  288. if not pos then
  289. minetest.log("error", "3d_armor: Player position is nil "..msg)
  290. return
  291. elseif not player_inv then
  292. minetest.log("error", "3d_armor: Player inventory is nil "..msg)
  293. return
  294. elseif not armor_inv then
  295. minetest.log("error", "3d_armor: Detached armor inventory is nil "..msg)
  296. return
  297. end
  298. return name, player_inv, armor_inv, pos
  299. end
  300. -- Register Player Model
  301. ---[===[
  302. default.player_register_model("3d_armor_character.b3d", {
  303. animation_speed = 30,
  304. textures = {
  305. armor.default_skin..".png",
  306. "3d_armor_trans.png",
  307. "3d_armor_trans.png",
  308. },
  309. animations = {
  310. stand = {x=0, y=79},
  311. lay = {x=162, y=166},
  312. walk = {x=168, y=187},
  313. mine = {x=189, y=198},
  314. walk_mine = {x=200, y=219},
  315. sit = {x=81, y=160},
  316. },
  317. })
  318. --]===]
  319. -- Register Callbacks
  320. minetest.register_on_player_receive_fields(function(player, formname, fields)
  321. local name = armor:get_valid_player(player, "[on_player_receive_fields]")
  322. if not name or inv_mod == "inventory_enhanced" then
  323. return
  324. end
  325. if inv_mod == "inventory_plus" and fields.armor then
  326. local formspec = armor:get_armor_formspec(name)
  327. inventory_plus.set_inventory_formspec(player, formspec)
  328. return
  329. end
  330. for field, _ in pairs(fields) do
  331. if string.find(field, "skins_set") then
  332. minetest.after(0, function(player)
  333. local skin = armor:get_player_skin(name)
  334. armor.textures[name].skin = skin..".png"
  335. armor:set_player_armor(player)
  336. end, player)
  337. end
  338. end
  339. end)
  340. minetest.register_on_joinplayer(function(player)
  341. default.player_set_model(player, "3d_armor_character.b3d")
  342. local name = player:get_player_name()
  343. local player_inv = player:get_inventory()
  344. local armor_inv = minetest.create_detached_inventory(name.."_armor", {
  345. on_put = function(inv, listname, index, stack, player)
  346. player:get_inventory():set_stack(listname, index, stack)
  347. armor:set_player_armor(player)
  348. armor:update_inventory(player)
  349. end,
  350. on_take = function(inv, listname, index, stack, player)
  351. player:get_inventory():set_stack(listname, index, nil)
  352. armor:set_player_armor(player)
  353. armor:update_inventory(player)
  354. end,
  355. on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
  356. local plaver_inv = player:get_inventory()
  357. local stack = inv:get_stack(to_list, to_index)
  358. player_inv:set_stack(to_list, to_index, stack)
  359. player_inv:set_stack(from_list, from_index, nil)
  360. armor:set_player_armor(player)
  361. armor:update_inventory(player)
  362. end,
  363. allow_put = function(inv, listname, index, stack, player)
  364. return 1
  365. end,
  366. allow_take = function(inv, listname, index, stack, player)
  367. return stack:get_count()
  368. end,
  369. allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
  370. return count
  371. end,
  372. }, name)
  373. armor_inv:set_size("armor", 6)
  374. player_inv:set_size("armor", 6)
  375. for i=1, 6 do
  376. local stack = player_inv:get_stack("armor", i)
  377. armor_inv:set_stack("armor", i, stack)
  378. end
  379. armor.def[name] = {
  380. state = 0,
  381. count = 0,
  382. level = 0,
  383. heal = 0,
  384. jump = 1,
  385. speed = 1,
  386. gravity = 1,
  387. fire = 0,
  388. water = 0,
  389. radiation = 0,
  390. }
  391. armor.textures[name] = {
  392. skin = armor.default_skin..".png",
  393. armor = "3d_armor_trans.png",
  394. wielditem = "3d_armor_trans.png",
  395. preview = armor.default_skin.."_preview.png",
  396. }
  397. if skin_mod == "skins" then
  398. local skin = skins.skins[name]
  399. if skin and skins.get_type(skin) == skins.type.MODEL then
  400. armor.textures[name].skin = skin..".png"
  401. end
  402. elseif skin_mod == "simple_skins" then
  403. local skin = skins.skins[name]
  404. if skin then
  405. armor.textures[name].skin = skin..".png"
  406. end
  407. elseif skin_mod == "u_skins" then
  408. local skin = u_skins.u_skins[name]
  409. if skin and u_skins.get_type(skin) == u_skins.type.MODEL then
  410. armor.textures[name].skin = skin..".png"
  411. end
  412. elseif skin_mod == "wardrobe" then
  413. local skin = wardrobe.playerSkins[name]
  414. if skin then
  415. armor.textures[name].skin = skin
  416. end
  417. end
  418. if minetest.get_modpath("player_textures") then
  419. local filename = minetest.get_modpath("player_textures").."/textures/player_"..name
  420. local f = io.open(filename..".png")
  421. if f then
  422. f:close()
  423. armor.textures[name].skin = "player_"..name..".png"
  424. end
  425. end
  426. for i=1, ARMOR_INIT_TIMES do
  427. minetest.after(ARMOR_INIT_DELAY * i, function(player)
  428. armor:set_player_armor(player)
  429. if not inv_mod then
  430. armor:update_inventory(player)
  431. end
  432. end, player)
  433. end
  434. end)
  435. if ARMOR_DROP == true or ARMOR_DESTROY == true then
  436. armor.drop_armor = function(pos, stack)
  437. local obj = minetest.add_item(pos, stack)
  438. if obj then
  439. obj:setvelocity({x=math.random(-1, 1), y=5, z=math.random(-1, 1)})
  440. end
  441. end
  442. armor.on_dieplayer = function(player, bp)
  443. local name, player_inv, armor_inv = armor:get_valid_player(player, "[on_dieplayer]")
  444. local pos = vector.new(bp)
  445. if not name then
  446. return
  447. end
  448. local drop = {}
  449. for i=1, player_inv:get_size("armor") do
  450. local stack = armor_inv:get_stack("armor", i)
  451. if stack:get_count() > 0 then
  452. table.insert(drop, stack)
  453. armor_inv:set_stack("armor", i, nil)
  454. player_inv:set_stack("armor", i, nil)
  455. end
  456. end
  457. armor:set_player_armor(player)
  458. if inv_mod == "unified_inventory" then
  459. unified_inventory.set_inventory_formspec(player, "craft")
  460. elseif inv_mod == "inventory_plus" then
  461. local formspec = inventory_plus.get_formspec(player,"main")
  462. inventory_plus.set_inventory_formspec(player, formspec)
  463. else
  464. armor:update_inventory(player)
  465. end
  466. local location = minetest.pos_to_string(pos)
  467. if ARMOR_DESTROY == false then
  468. local node = minetest.get_node(pos)
  469. if node.name == "bones:bones" then
  470. local meta = minetest.get_meta(pos)
  471. local inv = meta:get_inventory()
  472. for _,stack in ipairs(drop) do
  473. if stack:get_count() > 0 and inv:room_for_item("main", stack) then
  474. inv:add_item("main", stack)
  475. minetest.log("action", "Put " .. stack:to_string() .. " in bones @ " .. location .. ".")
  476. else
  477. armor.drop_armor(pos, stack)
  478. end
  479. end
  480. else
  481. minetest.log("warning", "Failed to add armor to bones node at " ..
  482. minetest.pos_to_string(pos) .. "!")
  483. for _,stack in ipairs(drop) do
  484. armor.drop_armor(pos, stack)
  485. end
  486. end
  487. end
  488. end
  489. end
  490. minetest.register_on_player_hpchange(function(player, hp_change)
  491. local name, player_inv, armor_inv = armor:get_valid_player(player, "[on_hpchange]")
  492. if name and hp_change < 0 then
  493. -- used for insta kill tools/commands like /kill (doesnt damage armor)
  494. if hp_change < -100 then
  495. return hp_change
  496. end
  497. local heal_max = 0
  498. local state = 0
  499. local items = 0
  500. for i=1, 6 do
  501. local stack = player_inv:get_stack("armor", i)
  502. if stack:get_count() > 0 then
  503. local use = stack:get_definition().groups["armor_use"] or 0
  504. local heal = stack:get_definition().groups["armor_heal"] or 0
  505. local item = stack:get_name()
  506. stack:add_wear(use)
  507. armor_inv:set_stack("armor", i, stack)
  508. player_inv:set_stack("armor", i, stack)
  509. state = state + stack:get_wear()
  510. items = items + 1
  511. if stack:get_count() == 0 then
  512. local desc = minetest.registered_items[item].description
  513. if desc then
  514. minetest.chat_send_player(name, "# Server: Your " .. desc .. " got destroyed!")
  515. ambiance.sound_play("default_tool_breaks", player:get_pos(), 1.0, 20)
  516. end
  517. armor:set_player_armor(player)
  518. armor:update_inventory(player)
  519. end
  520. heal_max = heal_max + heal
  521. end
  522. end
  523. armor.def[name].state = state
  524. armor.def[name].count = items
  525. heal_max = heal_max * ARMOR_HEAL_MULTIPLIER
  526. if heal_max > math.random(100) then
  527. hp_change = 0
  528. end
  529. armor:update_armor(player)
  530. end
  531. return hp_change
  532. end, true)
  533. -- Register button once.
  534. if inv_mod and inv_mod == "inventory_plus" then
  535. inventory_plus.register_button("armor", "Armor")
  536. end