init.lua 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. local modname = minetest.get_current_modname()
  2. local modpath = minetest.get_modpath(modname)
  3. local worldpath = minetest.get_worldpath()
  4. local last_punch_time = {}
  5. local pending_players = {}
  6. local timer = 0
  7. -- support for i18n
  8. armor_i18n = { }
  9. armor_i18n.gettext, armor_i18n.ngettext = dofile(modpath.."/intllib.lua")
  10. -- local functions
  11. local S = armor_i18n.gettext
  12. local F = minetest.formspec_escape
  13. dofile(modpath.."/api.lua")
  14. -- Legacy Config Support
  15. local input = io.open(modpath.."/armor.conf", "r")
  16. if input then
  17. dofile(modpath.."/armor.conf")
  18. input:close()
  19. input = nil
  20. end
  21. input = io.open(worldpath.."/armor.conf", "r")
  22. if input then
  23. dofile(worldpath.."/armor.conf")
  24. input:close()
  25. input = nil
  26. end
  27. for name, _ in pairs(armor.config) do
  28. local global = "ARMOR_"..name:upper()
  29. if minetest.global_exists(global) then
  30. armor.config[name] = _G[global]
  31. end
  32. end
  33. if minetest.global_exists("ARMOR_MATERIALS") then
  34. armor.materials = table.copy(ARMOR_MATERIALS)
  35. end
  36. if minetest.global_exists("ARMOR_FIRE_NODES") then
  37. armor.fire_nodes = table.copy(ARMOR_FIRE_NODES)
  38. end
  39. -- Load Configuration
  40. for name, config in pairs(armor.config) do
  41. local setting = minetest.settings:get("armor_"..name)
  42. if type(config) == "number" then
  43. setting = tonumber(setting)
  44. elseif type(config) == "boolean" then
  45. setting = minetest.settings:get_bool("armor_"..name)
  46. end
  47. if setting ~= nil then
  48. armor.config[name] = setting
  49. end
  50. end
  51. for material, _ in pairs(armor.materials) do
  52. local key = "material_"..material
  53. if armor.config[key] == false then
  54. armor.materials[material] = nil
  55. end
  56. end
  57. -- Mod Compatibility
  58. if minetest.get_modpath("technic") then
  59. armor.formspec = armor.formspec..
  60. "label[5,2.5;"..F(S("Radiation"))..": armor_group_radiation]"
  61. armor:register_armor_group("radiation")
  62. end
  63. local skin_mods = {"skins", "u_skins", "simple_skins", "wardrobe", "custom_skin"}
  64. for _, mod in pairs(skin_mods) do
  65. local path = minetest.get_modpath(mod)
  66. if path then
  67. local dir_list = minetest.get_dir_list(path.."/textures")
  68. for _, fn in pairs(dir_list) do
  69. if fn:find("_preview.png$") then
  70. armor:add_preview(fn)
  71. end
  72. end
  73. armor.skin_mod = mod
  74. end
  75. end
  76. if not minetest.get_modpath("moreores") then
  77. armor.materials.mithril = nil
  78. end
  79. if not minetest.get_modpath("ethereal") then
  80. armor.materials.crystal = nil
  81. end
  82. dofile(modpath.."/armor.lua")
  83. -- Armor Initialization
  84. armor.formspec = armor.formspec..
  85. "label[5,1;"..F(S("Level"))..": armor_level]"..
  86. "label[5,1.5;"..F(S("Block"))..": armor_attr_block]"
  87. if armor.config.fire_protect then
  88. armor.formspec = armor.formspec.."label[5,2;"..F(S("Fire"))..": armor_attr_fire]"
  89. end
  90. armor:register_on_destroy(function(player, index, stack)
  91. local name = player:get_player_name()
  92. local def = stack:get_definition()
  93. if name and def and def.description then
  94. minetest.chat_send_player(name, S("Your @1 got destroyed!", def.description))
  95. end
  96. end)
  97. local function validate_armor_inventory(player)
  98. -- Workaround for detached inventory swap exploit
  99. local _, inv = armor:get_valid_player(player, "[validate_armor_inventory]")
  100. if not inv then
  101. return
  102. end
  103. local armor_prev = {}
  104. local armor_list_string = player:get_attribute("3d_armor_inventory")
  105. if armor_list_string then
  106. local armor_list = armor:deserialize_inventory_list(armor_list_string)
  107. for i, stack in ipairs(armor_list) do
  108. if stack:get_count() > 0 then
  109. armor_prev[stack:get_name()] = i
  110. end
  111. end
  112. end
  113. local elements = {}
  114. local player_inv = player:get_inventory()
  115. for i = 1, 6 do
  116. local stack = inv:get_stack("armor", i)
  117. if stack:get_count() > 0 then
  118. local item = stack:get_name()
  119. local element = armor:get_element(item)
  120. if element and not elements[element] then
  121. if armor_prev[item] then
  122. armor_prev[item] = nil
  123. else
  124. -- Item was not in previous inventory
  125. armor:run_callbacks("on_equip", player, i, stack)
  126. end
  127. elements[element] = true;
  128. else
  129. inv:remove_item("armor", stack)
  130. -- The following code returns invalid items to the player's main
  131. -- inventory but could open up the possibity for a hacked client
  132. -- to receive items back they never really had. I am not certain
  133. -- so remove the is_singleplayer check at your own risk :]
  134. if player_inv:room_for_item("main", stack) then
  135. player_inv:add_item("main", stack)
  136. end
  137. end
  138. end
  139. end
  140. for item, i in pairs(armor_prev) do
  141. local stack = ItemStack(item)
  142. -- Previous item is not in current inventory
  143. armor:run_callbacks("on_unequip", player, i, stack)
  144. end
  145. end
  146. local function init_player_armor(player)
  147. local name = player:get_player_name()
  148. local pos = player:get_pos()
  149. if not name or not pos then
  150. return false
  151. end
  152. local armor_inv = minetest.create_detached_inventory(name.."_armor", {
  153. on_put = function(inv, listname, index, stack, player)
  154. validate_armor_inventory(player)
  155. armor:save_armor_inventory(player)
  156. armor:set_player_armor(player)
  157. end,
  158. on_take = function(inv, listname, index, stack, player)
  159. validate_armor_inventory(player)
  160. armor:save_armor_inventory(player)
  161. armor:set_player_armor(player)
  162. end,
  163. on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
  164. validate_armor_inventory(player)
  165. armor:save_armor_inventory(player)
  166. armor:set_player_armor(player)
  167. end,
  168. allow_put = function(inv, listname, index, put_stack, player)
  169. local element = armor:get_element(put_stack:get_name())
  170. if not element then
  171. return 0
  172. end
  173. for i = 1, 6 do
  174. local stack = inv:get_stack("armor", i)
  175. local def = stack:get_definition() or {}
  176. if def.groups and def.groups["armor_"..element]
  177. and i ~= index then
  178. return 0
  179. end
  180. end
  181. return 1
  182. end,
  183. allow_take = function(inv, listname, index, stack, player)
  184. local player_inv = player:get_inventory()
  185. if player_inv:room_for_item('main', stack) then
  186. return stack:get_count()
  187. else
  188. return 0
  189. end
  190. end,
  191. allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
  192. return count
  193. end,
  194. }, name)
  195. armor_inv:set_size("armor", 6)
  196. if not armor:load_armor_inventory(player) and armor.migrate_old_inventory then
  197. local player_inv = player:get_inventory()
  198. player_inv:set_size("armor", 6)
  199. for i=1, 6 do
  200. local stack = player_inv:get_stack("armor", i)
  201. armor_inv:set_stack("armor", i, stack)
  202. end
  203. armor:save_armor_inventory(player)
  204. player_inv:set_size("armor", 0)
  205. end
  206. for i=1, 6 do
  207. local stack = armor_inv:get_stack("armor", i)
  208. if stack:get_count() > 0 then
  209. armor:run_callbacks("on_equip", player, i, stack)
  210. end
  211. end
  212. armor.def[name] = {
  213. init_time = minetest.get_gametime(),
  214. level = 0,
  215. state = 0,
  216. count = 0,
  217. groups = {},
  218. }
  219. for _, phys in pairs(armor.physics) do
  220. armor.def[name][phys] = 1
  221. end
  222. for _, attr in pairs(armor.attributes) do
  223. armor.def[name][attr] = 0
  224. end
  225. for group, _ in pairs(armor.registered_groups) do
  226. armor.def[name].groups[group] = 0
  227. end
  228. local skin = armor:get_player_skin(name)
  229. armor.textures[name] = {
  230. skin = skin,
  231. armor = "3d_armor_trans.png",
  232. wielditem = "3d_armor_trans.png",
  233. preview = armor.default_skin.."_preview.png",
  234. }
  235. local texture_path = minetest.get_modpath("player_textures")
  236. if texture_path then
  237. local dir_list = minetest.get_dir_list(texture_path.."/textures")
  238. for _, fn in pairs(dir_list) do
  239. if fn == "player_"..name..".png" then
  240. armor.textures[name].skin = fn
  241. break
  242. end
  243. end
  244. end
  245. armor:set_player_armor(player)
  246. return true
  247. end
  248. -- Armor Player Model
  249. default.player_register_model("3d_armor_character.b3d", {
  250. animation_speed = 30,
  251. textures = {
  252. armor.default_skin..".png",
  253. "3d_armor_trans.png",
  254. "3d_armor_trans.png",
  255. },
  256. animations = {
  257. stand = {x=0, y=79},
  258. lay = {x=162, y=166},
  259. walk = {x=168, y=187},
  260. mine = {x=189, y=198},
  261. walk_mine = {x=200, y=219},
  262. sit = {x=81, y=160},
  263. },
  264. })
  265. minetest.register_on_player_receive_fields(function(player, formname, fields)
  266. local name = armor:get_valid_player(player, "[on_player_receive_fields]")
  267. if not name then
  268. return
  269. end
  270. for field, _ in pairs(fields) do
  271. if string.find(field, "skins_set") then
  272. minetest.after(0, function(player)
  273. local skin = armor:get_player_skin(name)
  274. armor.textures[name].skin = skin
  275. armor:set_player_armor(player)
  276. end, player)
  277. end
  278. end
  279. end)
  280. minetest.register_on_joinplayer(function(player)
  281. default.player_set_model(player, "3d_armor_character.b3d")
  282. minetest.after(0, function(player)
  283. if init_player_armor(player) == false then
  284. pending_players[player] = 0
  285. end
  286. end, player)
  287. end)
  288. minetest.register_on_leaveplayer(function(player)
  289. local name = player:get_player_name()
  290. if name then
  291. armor.def[name] = nil
  292. armor.textures[name] = nil
  293. end
  294. pending_players[player] = nil
  295. end)
  296. if armor.config.drop == true or armor.config.destroy == true then
  297. minetest.register_on_dieplayer(function(player)
  298. local name, armor_inv = armor:get_valid_player(player, "[on_dieplayer]")
  299. if not name then
  300. return
  301. end
  302. local drop = {}
  303. for i=1, armor_inv:get_size("armor") do
  304. local stack = armor_inv:get_stack("armor", i)
  305. if stack:get_count() > 0 then
  306. table.insert(drop, stack)
  307. armor:run_callbacks("on_unequip", player, i, stack)
  308. armor_inv:set_stack("armor", i, nil)
  309. end
  310. end
  311. armor:save_armor_inventory(player)
  312. armor:set_player_armor(player)
  313. local pos = player:get_pos()
  314. if pos and armor.config.destroy == false then
  315. minetest.after(armor.config.bones_delay, function()
  316. local meta = nil
  317. local maxp = vector.add(pos, 8)
  318. local minp = vector.subtract(pos, 8)
  319. local bones = minetest.find_nodes_in_area(minp, maxp, {"bones:bones"})
  320. for _, p in pairs(bones) do
  321. local m = minetest.get_meta(p)
  322. if m:get_string("owner") == name then
  323. meta = m
  324. break
  325. end
  326. end
  327. if meta then
  328. local inv = meta:get_inventory()
  329. for _,stack in ipairs(drop) do
  330. if inv:room_for_item("main", stack) then
  331. inv:add_item("main", stack)
  332. else
  333. armor.drop_armor(pos, stack)
  334. end
  335. end
  336. else
  337. for _,stack in ipairs(drop) do
  338. armor.drop_armor(pos, stack)
  339. end
  340. end
  341. end)
  342. end
  343. end)
  344. end
  345. if armor.config.punch_damage == true then
  346. minetest.register_on_punchplayer(function(player, hitter,
  347. time_from_last_punch, tool_capabilities)
  348. local name = player:get_player_name()
  349. if name then
  350. armor:punch(player, hitter, time_from_last_punch, tool_capabilities)
  351. last_punch_time[name] = minetest.get_gametime()
  352. end
  353. end)
  354. end
  355. minetest.register_on_player_hpchange(function(player, hp_change)
  356. if player and hp_change < 0 then
  357. local name = player:get_player_name()
  358. if name then
  359. local time = last_punch_time[name] or 0
  360. if time ~= 'fire' then
  361. if player:get_breath() > 0 and hbhunger.hunger[name] >= 1 then
  362. local block = armor.def[name].block
  363. if block >= math.random(100) then
  364. hp_change = 0
  365. armor:punch(player)
  366. else
  367. armor:punch(player)
  368. end
  369. end
  370. end
  371. end
  372. end
  373. return hp_change
  374. end, true)
  375. minetest.register_globalstep(function(dtime)
  376. timer = timer + dtime
  377. if timer > armor.config.init_delay then
  378. for player, count in pairs(pending_players) do
  379. local remove = init_player_armor(player) == true
  380. pending_players[player] = count + 1
  381. if remove == false and count > armor.config.init_times then
  382. minetest.log("warning", S("3d_armor: Failed to initialize player"))
  383. remove = true
  384. end
  385. if remove == true then
  386. pending_players[player] = nil
  387. end
  388. end
  389. timer = 0
  390. end
  391. end)
  392. -- Fire Protection and water breating, added by TenPlus1
  393. if armor.config.fire_protect == true then
  394. -- override hot nodes so they do not hurt player anywhere but mod
  395. for _, row in pairs(armor.fire_nodes) do
  396. if minetest.registered_nodes[row[1]] then
  397. minetest.override_item(row[1], {damage_per_second = 0})
  398. end
  399. end
  400. else
  401. print (S("[3d_armor] Fire Nodes disabled"))
  402. end
  403. minetest.register_globalstep(function(dtime)
  404. armor.timer = armor.timer + dtime
  405. if armor.timer < armor.config.update_time then
  406. return
  407. end
  408. for _,player in pairs(minetest.get_connected_players()) do
  409. local name = player:get_player_name()
  410. local pos = player:get_pos()
  411. local hp = player:get_hp()
  412. if not name or not pos or not hp then
  413. return
  414. end
  415. -- water breathing
  416. if armor.config.water_protect == true then
  417. if armor.def[name].water > 0 and player:get_breath() < 40 then
  418. local breath_level = player:get_breath()
  419. local armor_level = armor.def[name].water
  420. local new_breath = (breath_level + armor_level)
  421. player:set_breath(new_breath)
  422. armor:punch(player, 'water')
  423. end
  424. end
  425. -- healing
  426. if hp < 50 and player:get_breath() >= 1 and hbhunger.hunger[name] >= 1 then
  427. local heal = armor.def[name].heal
  428. if heal >= 1 then
  429. local new_hp = math.floor(hp + heal)
  430. player:set_hp(new_hp)
  431. armor:punch(player)
  432. end
  433. end
  434. -- fire protection
  435. if armor.config.fire_protect == true then
  436. local fire_damage = true
  437. pos.y = pos.y + 1.4 -- head level
  438. local node_head = minetest.get_node(pos).name
  439. pos.y = pos.y - 1.2 -- feet level
  440. local node_feet = minetest.get_node(pos).name
  441. -- is player inside a hot node?
  442. for _, row in pairs(armor.fire_nodes) do
  443. -- check fire protection, if not enough then get hurt
  444. if row[1] == node_head or row[1] == node_feet then
  445. if fire_damage == true then --runs when player takes damage.
  446. armor:punch(player, 'fire')
  447. last_punch_time[name] = 'fire'
  448. fire_damage = false
  449. end
  450. if hp > 0 and armor.def[name].fire < row[2] then --Only runs if player is dead.
  451. hp = hp - row[3] * armor.config.update_time
  452. player:set_hp(hp)
  453. break
  454. end
  455. end
  456. end
  457. end
  458. end
  459. armor.timer = 0
  460. end)