123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514 |
- local modname = minetest.get_current_modname()
- local modpath = minetest.get_modpath(modname)
- local worldpath = minetest.get_worldpath()
- local last_punch_time = {}
- local attack_chance = {}
- local pending_players = {}
- local knockback = {}
- local timer = 0
- -- support for i18n
- armor_i18n = { }
- armor_i18n.gettext, armor_i18n.ngettext = dofile(modpath.."/intllib.lua")
- -- local functions
- local S = armor_i18n.gettext
- local F = minetest.formspec_escape
- dofile(modpath.."/api.lua")
- -- Legacy Config Support
- local input = io.open(modpath.."/armor.conf", "r")
- if input then
- dofile(modpath.."/armor.conf")
- input:close()
- input = nil
- end
- input = io.open(worldpath.."/armor.conf", "r")
- if input then
- dofile(worldpath.."/armor.conf")
- input:close()
- input = nil
- end
- for name, _ in pairs(armor.config) do
- local global = "ARMOR_"..name:upper()
- if minetest.global_exists(global) then
- armor.config[name] = _G[global]
- end
- end
- if minetest.global_exists("ARMOR_MATERIALS") then
- armor.materials = table.copy(ARMOR_MATERIALS)
- end
- if minetest.global_exists("ARMOR_FIRE_NODES") then
- armor.fire_nodes = table.copy(ARMOR_FIRE_NODES)
- end
- -- Load Configuration
- for name, config in pairs(armor.config) do
- local setting = minetest.settings:get("armor_"..name)
- if type(config) == "number" then
- setting = tonumber(setting)
- elseif type(config) == "boolean" then
- setting = minetest.settings:get_bool("armor_"..name)
- end
- if setting ~= nil then
- armor.config[name] = setting
- end
- end
- for material, _ in pairs(armor.materials) do
- local key = "material_"..material
- if armor.config[key] == false then
- armor.materials[material] = nil
- end
- end
- -- Mod Compatibility
- if minetest.get_modpath("technic") then
- armor.formspec = armor.formspec..
- "label[5,2.5;"..F(S("Radiation"))..": armor_group_radiation]"
- armor:register_armor_group("radiation")
- end
- local skin_mods = {"skins", "u_skins", "simple_skins", "wardrobe", "custom_skin"}
- for _, mod in pairs(skin_mods) do
- local path = minetest.get_modpath(mod)
- if path then
- local dir_list = minetest.get_dir_list(path.."/textures")
- for _, fn in pairs(dir_list) do
- if fn:find("_preview.png$") then
- armor:add_preview(fn)
- end
- end
- armor.skin_mod = mod
- end
- end
- if not minetest.get_modpath("moreores") then
- armor.materials.mithril = nil
- end
- if not minetest.get_modpath("ethereal") then
- armor.materials.crystal = nil
- end
- dofile(modpath.."/armor.lua")
- -- Armor Initialization
- armor.formspec = armor.formspec..
- "label[5,1;"..F(S("Level"))..": armor_level]"..
- "label[5,1.5;"..F(S("Block"))..": armor_attr_block]"
- if armor.config.fire_protect then
- armor.formspec = armor.formspec.."label[5,2;"..F(S("Fire"))..": armor_attr_fire]"
- end
- armor:register_on_destroy(function(player, index, stack)
- local name = player:get_player_name()
- local def = stack:get_definition()
- if name and def and def.description then
- minetest.chat_send_player(name, S("Your @1 got destroyed!", def.description))
- end
- end)
- local function validate_armor_inventory(player)
- -- Workaround for detached inventory swap exploit
- local _, inv = armor:get_valid_player(player, "[validate_armor_inventory]")
- if not inv then
- return
- end
- local armor_prev = {}
- local player_attributes = player:get_meta()
- local armor_list_string = player_attributes:get_string("3d_armor_inventory")
- if armor_list_string then
- local armor_list = armor:deserialize_inventory_list(armor_list_string)
- for i, stack in ipairs(armor_list) do
- if stack:get_count() > 0 then
- armor_prev[stack:get_name()] = i
- end
- end
- end
- local elements = {}
- local player_inv = player:get_inventory()
- for i = 1, 8 do
- local stack = inv:get_stack("armor", i)
- if stack:get_count() > 0 then
- local item = stack:get_name()
- local element = armor:get_element(item)
- if element and not elements[element] then
- if armor_prev[item] then
- armor_prev[item] = nil
- else
- -- Item was not in previous inventory
- armor:run_callbacks("on_equip", player, i, stack)
- end
- elements[element] = true;
- else
- inv:remove_item("armor", stack)
- -- The following code returns invalid items to the player's main
- -- inventory but could open up the possibity for a hacked client
- -- to receive items back they never really had. I am not certain
- -- so remove the is_singleplayer check at your own risk :]
- if player_inv:room_for_item("main", stack) then
- player_inv:add_item("main", stack)
- end
- end
- end
- end
- for item, i in pairs(armor_prev) do
- local stack = ItemStack(item)
- -- Previous item is not in current inventory
- armor:run_callbacks("on_unequip", player, i, stack)
- end
- end
- local function init_player_armor(player)
- local name = player:get_player_name()
- local pos = player:get_pos()
- if not name or not pos then
- return false
- end
- local armor_inv = minetest.create_detached_inventory(name.."_armor", {
- on_put = function(inv, listname, index, stack, player)
- validate_armor_inventory(player)
- armor:save_armor_inventory(player)
- armor:set_player_armor(player)
- end,
- on_take = function(inv, listname, index, stack, player)
- validate_armor_inventory(player)
- armor:save_armor_inventory(player)
- armor:set_player_armor(player)
- end,
- on_move = function(inv, from_list, from_index, to_list, to_index, count, player)
- validate_armor_inventory(player)
- armor:save_armor_inventory(player)
- armor:set_player_armor(player)
- end,
- allow_put = function(inv, listname, index, put_stack, player)
- local element = armor:get_element(put_stack:get_name())
- if not element then
- return 0
- end
- for i = 1, 8 do
- local stack = inv:get_stack("armor", i)
- local def = stack:get_definition() or {}
- if def.groups and def.groups["armor_"..element]
- and i ~= index then
- return 0
- end
- end
- return 1
- end,
- allow_take = function(inv, listname, index, stack, player)
- local player_inv = player:get_inventory()
- if player_inv:room_for_item('main', stack) then
- return stack:get_count()
- else
- return 0
- end
- end,
- allow_move = function(inv, from_list, from_index, to_list, to_index, count, player)
- return count
- end,
- }, name)
- armor_inv:set_size("armor", 8)
- if not armor:load_armor_inventory(player) and armor.migrate_old_inventory then
- local player_inv = player:get_inventory()
- player_inv:set_size("armor", 8)
- for i=1, 8 do
- local stack = player_inv:get_stack("armor", i)
- armor_inv:set_stack("armor", i, stack)
- end
- armor:save_armor_inventory(player)
- player_inv:set_size("armor", 0)
- end
- for i=1, 8 do
- local stack = armor_inv:get_stack("armor", i)
- if stack:get_count() > 0 then
- armor:run_callbacks("on_equip", player, i, stack)
- end
- end
- armor.def[name] = {
- init_time = minetest.get_gametime(),
- level = 0,
- state = 0,
- count = 0,
- groups = {},
- }
- for _, phys in pairs(armor.physics) do
- armor.def[name][phys] = 1
- end
- for _, attr in pairs(armor.attributes) do
- armor.def[name][attr] = 0
- end
- for group, _ in pairs(armor.registered_groups) do
- armor.def[name].groups[group] = 0
- end
- local skin = armor:get_player_skin(name)
- armor.textures[name] = {
- skin = skin,
- armor = "3d_armor_trans.png",
- wielditem = "3d_armor_trans.png",
- preview = armor.default_skin.."_preview.png",
- }
- local texture_path = minetest.get_modpath("player_textures")
- if texture_path then
- local dir_list = minetest.get_dir_list(texture_path.."/textures")
- for _, fn in pairs(dir_list) do
- if fn == "player_"..name..".png" then
- armor.textures[name].skin = fn
- break
- end
- end
- end
- armor:set_player_armor(player)
- return true
- end
- -- Armor Player Model
- default.player_register_model("3d_armor_character.b3d", {
- animation_speed = 30,
- textures = {
- armor.default_skin..".png",
- "3d_armor_trans.png",
- "3d_armor_trans.png",
- },
- animations = {
- stand = {x=0, y=79},
- lay = {x=162, y=166},
- walk = {x=168, y=187},
- mine = {x=189, y=198},
- walk_mine = {x=200, y=219},
- sit = {x=81, y=160},
- },
- })
- minetest.register_on_player_receive_fields(function(player, formname, fields)
- local name = armor:get_valid_player(player, "[on_player_receive_fields]")
- if not name then
- return
- end
- for field, _ in pairs(fields) do
- if string.find(field, "skins_set") then
- minetest.after(0, function(player)
- local skin = armor:get_player_skin(name)
- armor.textures[name].skin = skin
- armor:set_player_armor(player)
- end, player)
- end
- end
- end)
- minetest.register_on_joinplayer(function(player)
- default.player_set_model(player, "3d_armor_character.b3d")
- minetest.after(0, function(player)
- if init_player_armor(player) == false then
- pending_players[player] = 0
- end
- end, player)
- end)
- minetest.register_on_leaveplayer(function(player)
- local name = player:get_player_name()
- if name then
- armor.def[name] = nil
- armor.textures[name] = nil
- end
- pending_players[player] = nil
- end)
- if armor.config.drop == true or armor.config.destroy == true then
- minetest.register_on_dieplayer(function(player)
- local name, armor_inv = armor:get_valid_player(player, "[on_dieplayer]")
- if not name then
- return
- end
- local drop = {}
- for i=1, armor_inv:get_size("armor") do
- local stack = armor_inv:get_stack("armor", i)
- if stack:get_count() > 0 then
- table.insert(drop, stack)
- armor:run_callbacks("on_unequip", player, i, stack)
- armor_inv:set_stack("armor", i, nil)
- end
- end
- armor:save_armor_inventory(player)
- armor:set_player_armor(player)
- local pos = player:get_pos()
- if pos and armor.config.destroy == false then
- minetest.after(armor.config.bones_delay, function()
- local meta = nil
- local maxp = vector.add(pos, 8)
- local minp = vector.subtract(pos, 8)
- local bones = minetest.find_nodes_in_area(minp, maxp, {"bones:bones"})
- for _, p in pairs(bones) do
- local m = minetest.get_meta(p)
- if m:get_string("owner") == name then
- meta = m
- break
- end
- end
- if meta then
- local inv = meta:get_inventory()
- for _,stack in ipairs(drop) do
- if inv:room_for_item("main", stack) then
- inv:add_item("main", stack)
- else
- armor.drop_armor(pos, stack)
- end
- end
- else
- for _,stack in ipairs(drop) do
- armor.drop_armor(pos, stack)
- end
- end
- end)
- end
- end)
- end
- if armor.config.punch_damage == true then
- minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities)
- local name = player:get_player_name()
- if name then
- last_punch_time[name] = minetest.get_gametime()
- local attackchance = tool_capabilities.damage_groups['attack_chance']
- attack_chance[name] = attackchance
- end
- end)
- end
- -- Prevent knockback for blocked attacks
- local old_calculate_knockback = minetest.calculate_knockback
- function minetest.calculate_knockback(player, ...)
- local name = player:get_player_name()
- if knockback[name] then
- return 0
- end
- return old_calculate_knockback(player, ...)
- end
- minetest.register_on_player_hpchange(function(player, hp_change)
- if player and hp_change < 0 then
- local name = player:get_player_name()
- if name then
- local time = last_punch_time[name] or 0
- local attackchance = attack_chance[name] or 100
- if time ~= 'fire' then
- local hunger = hbhunger.hunger[name] or 30
- if player:get_breath() > 0 and hunger >= 1 then
- local dmg_resist = math.floor(armor.def[name].dmg_resist)
- local block = armor.def[name].block
- local should_damage = (hp_change*-1) > (dmg_resist/2) --Should armor be damaged by the attack
- if block >= math.random(attackchance) then --Armor blocked the attack
- hp_change = 0
- knockback[name] = true
- else
- hp_change = (hp_change + dmg_resist)
- if hp_change >= 0 then
- knockback[name] = true
- else
- knockback[name] = false
- end
- end
- if should_damage then
- armor:punch(player)
- end
- end
- end
- end
- end
- return hp_change
- end, true)
- minetest.register_globalstep(function(dtime)
- timer = timer + dtime
- if timer > armor.config.init_delay then
- for player, count in pairs(pending_players) do
- local remove = init_player_armor(player) == true
- pending_players[player] = count + 1
- if remove == false and count > armor.config.init_times then
- minetest.log("warning", S("3d_armor: Failed to initialize player"))
- remove = true
- end
- if remove == true then
- pending_players[player] = nil
- end
- end
- timer = 0
- end
- end)
- -- Fire Protection and water breating, added by TenPlus1
- if armor.config.fire_protect == true then
- -- override hot nodes so they do not hurt player anywhere but mod
- for _, row in pairs(armor.fire_nodes) do
- if minetest.registered_nodes[row[1]] then
- minetest.override_item(row[1], {damage_per_second = 0})
- end
- end
- else
- print (S("[3d_armor] Fire Nodes disabled"))
- end
- minetest.register_globalstep(function(dtime)
- armor.timer = armor.timer + dtime
- if armor.timer < armor.config.update_time then
- return
- end
- for _,player in pairs(minetest.get_connected_players()) do
- local name = player:get_player_name()
- local pos = player:get_pos()
- local hp = player:get_hp()
- if not name or not pos or not hp then
- return
- end
- -- water breathing
- if armor.config.water_protect == true then
- if armor.def[name].water > 0 and player:get_breath() < 40 then
- local breath_level = player:get_breath()
- local armor_level = armor.def[name].water
- local new_breath = (breath_level + armor_level)
- player:set_breath(new_breath)
- armor:punch(player, 'water')
- end
- end
- -- healing
- local max_hp = player:get_properties().hp_max
- if hp < max_hp and player:get_breath() >= 1 and hbhunger.hunger[name] >= 1 then
- local heal = armor.def[name].heal
- if heal >= 1 then
- local new_hp = math.floor(hp + heal)
- player:set_hp(new_hp)
- armor:punch(player)
- end
- end
- -- fire protection
- if armor.config.fire_protect == true then
- local fire_damage = true
- pos.y = pos.y + 1.4 -- head level
- local node_head = minetest.get_node(pos).name
- pos.y = pos.y - 1.2 -- feet level
- local node_feet = minetest.get_node(pos).name
- -- is player inside a hot node?
- for _, row in pairs(armor.fire_nodes) do
- -- check fire protection, if not enough then get hurt
- if row[1] == node_head or row[1] == node_feet then
- if fire_damage == true then --runs when player takes damage.
- armor:punch(player, 'fire')
- last_punch_time[name] = 'fire'
- fire_damage = false
- end
- if hp > 0 and armor.def[name].fire < row[2] then --Only runs if player is dead.
- hp = hp - row[3] * armor.config.update_time
- player:set_hp(hp)
- break
- end
- end
- end
- end
- end
- armor.timer = 0
- end)
|