health/init.lua

368 lines
11 KiB
Lua

local dbg
if moddebug then dbg=moddebug.dbg("health") else dbg={v1=function() end,v2=function() end,v3=function() end} end
health = {}
dofile(minetest.get_modpath("health").."/playerattribs.lua")
dofile(minetest.get_modpath("health").."/eating.lua")
dofile(minetest.get_modpath("health").."/protection.lua")
-- These store the active hud bars of each type, indexed by player.
-- Each entry is a table with the id and the last sent value.
health.bars_health = {}
health.bars_hunger = {}
health.bars_air = {}
health.bars_armor = {}
health.use_armor = minetest.get_modpath("3d_armor") ~= nil
local hud_layout = {
health_pos = {x=0.5, y=0.9},
health_offset = {x=-175, y=2},
hunger_pos = {x=0.5, y=0.9},
hunger_offset = {x=15, y=2},
air_pos = {x=0.5, y=0.9},
air_offset = {x=15, y=-15},
armor_pos = {x=0.5, y=0.9},
armor_offset = {x=-175, y=-15},
}
local function replace_hud(player)
local playername = player:get_player_name()
dbg.v2("Replacing hud for "..playername)
-- Hide the built-in hud items
player:hud_set_flags({crosshair = true, hotbar = true, healthbar = false, wielditem = true, breathbar = false})
-- Replace the hotbar
player:hud_set_hotbar_image("health_hotbar.png")
player:hud_set_hotbar_selected_image("health_hotbar_selected.png")
-- Hunger bar
player:hud_add({
hud_elem_type = "statbar",
position = hud_layout.hunger_pos,
scale = {x=1, y=1},
text = "health_hunger_bg.png",
number = 20,
alignment = {x=-1,y=-1},
offset = hud_layout.hunger_offset,
})
health.bars_hunger[playername] = {player:hud_add({
hud_elem_type = "statbar",
position = hud_layout.hunger_pos,
scale = {x=1, y=1},
text = "health_hunger_fg.png",
number = 20,
alignment = {x=-1,y=-1},
offset = hud_layout.hunger_offset,
}), -1}
-- Health bar
player:hud_add({
hud_elem_type = "statbar",
position = hud_layout.health_pos,
scale = {x=1, y=1},
text = "health_heart_bg.png",
number = 20,
alignment = {x=-1,y=-1},
offset = hud_layout.health_offset,
})
health.bars_health[playername] = {player:hud_add({
hud_elem_type = "statbar",
position = hud_layout.health_pos,
scale = {x=1, y=1},
text = "health_heart_fg.png",
number = player:get_hp(),
alignment = {x=-1,y=-1},
offset = hud_layout.health_offset,
}), -1}
-- Breath bar
health.bars_air[playername] = {player:hud_add({
hud_elem_type = "statbar",
position = hud_layout.air_pos,
scale = {x=1, y=1},
text = "health_air_fg.png",
number = 0,
alignment = {x=-1,y=-1},
offset = hud_layout.air_offset,
}), -1}
-- Armor bar
if health.use_armor then
player:hud_add({
hud_elem_type = "statbar",
position = hud_layout.armor_pos,
scale = {x=1, y=1},
text = "health_armor_bg.png",
number = 20,
alignment = {x=-1,y=-1},
offset = hud_layout.armor_offset,
})
health.bars_armor[playername] = {player:hud_add({
hud_elem_type = "statbar",
position = hud_layout.armor_pos,
scale = {x=1, y=1},
text = "health_armor_fg.png",
number = 0,
alignment = {x=-1,y=-1},
offset = hud_layout.armor_offset,
}), -1}
end
end
-- Returns a value from 0-20 (for the bar) that represents the player's
-- current level of armor protection.
local function calc_armor_level(player)
if not player or not armor.def then
return 0
end
local playername = player:get_player_name()
local lvl = armor.def[playername].level or 0
lvl = lvl / 5
if lvl > 20 then lvl = 20 end
return lvl
end
local function update_bar(player, bar, value)
local barid = bar[1]
local lastsent = bar[2]
-- Don't update if it's the same, that will generate unnecessary network
-- packets.
if value == lastsent then return end
dbg.v3("Updating hud bar "..barid.." for "..player:get_player_name().." to "..value)
bar[2] = value
player:hud_change(barid, "number", value)
end
local function update_hud(player, hasair)
local playername = player:get_player_name()
local air = player:get_breath() * 2
if player:get_breath() > 10 and not hasair then air = 0 end
update_bar(player, health.bars_air[playername], air)
update_bar(player, health.bars_health[playername], player:get_hp())
if health.use_armor then
local armor = calc_armor_level(player)
update_bar(player, health.bars_armor[playername], armor)
end
local h = health.get_attr(playername, "hunger")
if h > 20 then h = 20 end
update_bar(player, health.bars_hunger[playername], h)
end
local function has_air_supply(player)
if not health.use_armor then return false end
local inv = player:get_inventory()
if not inv:contains_item("armor", "3d_armor:helmet_gold") then return false end
if not inv:contains_item("armor", "3d_armor:boots_gold") then return false end
if not inv:contains_item("armor", "3d_armor:leggings_gold") then return false end
if not inv:contains_item("armor", "3d_armor:chestplate_gold") then return false end
return true
end
minetest.register_on_joinplayer(function(player)
local playername = player:get_player_name()
health.load_attribs(playername)
replace_hud(player)
end)
minetest.register_on_leaveplayer(function(player)
local playername = player:get_player_name()
health.bars_hunger[playername] = nil
health.bars_health[playername] = nil
health.bars_armor[playername] = nil
health.bars_air[playername] = nil
end)
minetest.register_on_respawnplayer(function(player)
local playername = player:get_player_name()
health.reset_attribs(playername)
end)
-- Rate for all processing (including hud update)
local rate_all = 0.5
local tick_all = 0
-- Rate for hunger processing
local rate_hunger = 5 * 60
local tick_hunger = 30
-- Rate for starving processing
local rate_starving = 2 * 60
local tick_starving = 15
-- Rate for poison processing
local rate_poison = 60
local tick_poison = 0
-- Rate for sleep processing
local rate_sleep = 30
local tick_sleep = 5
minetest.register_globalstep(function(dtime)
tick_all = tick_all + dtime
tick_hunger = tick_hunger + dtime
tick_starving = tick_starving + dtime
tick_poison = tick_poison + dtime
tick_sleep = tick_sleep + dtime
if tick_all > rate_all then
tick_all = 0
for _, player in pairs(minetest.get_connected_players()) do
local playername = player:get_player_name()
local hp_change = 0
local hasair = has_air_supply(player)
if player:get_breath() < 10 and hasair then
player:set_breath(10)
end
if tick_poison > rate_poison then
tick_poison = 0
local poisonedcount = health.get_attr(playername, "poisonedcount")
if poisonedcount > 0 and minetest.setting_getbool("enable_damage") then
hp_change = hp_change - health.get_attr(playername, "poisonedamount")
poisonedcount = poisonedcount - 1
if poisonedcount <= 0 then
health.set_attr(playername, "poisonedamount", 0)
end
health.set_attr(playername, "poisonedcount", poisonedcount)
end
end
if tick_starving > rate_starving then
tick_starving = 0
local hunger = health.get_attr(playername, "hunger")
if hunger < 2 and minetest.setting_getbool("enable_damage") then
hp_change = hp_change - 1
end
end
if tick_hunger > rate_hunger then
tick_hunger = 0
local hunger = health.get_attr(playername, "hunger")
if hunger > 0 then
hunger = hunger - 1
health.set_attr(playername, "hunger", hunger)
end
end
if tick_sleep > rate_sleep then
tick_sleep = 0
if health.get_attr(playername, "asleep") ~= 0 then
hp_change = hp_change + 1
end
end
local frozen = health.get_attr(playername, "frozen")
if frozen > 0 then
frozen = frozen - rate_all
if frozen < 0 then frozen = 0 end
health.set_attr(playername, "frozen", frozen)
player:setpos(health.get_attr(playername, "frozen_pos"))
end
local speed = health.get_attr(playername, "speed")
if speed > 0 then
speed = speed - rate_all
if speed < 0 then speed = 0 end
health.set_attr(playername, "speed", speed)
end
local levitating = health.get_attr(playername, "levitating")
if levitating > 0 then
levitating = levitating - rate_all
if levitating < 0 then
levitating = 0
end
health.set_attr(playername, "levitating", levitating)
end
if hp_change ~= 0 then
local hp = player:get_hp()
hp = hp + hp_change
if hp < 0 then hp = 0 end
if hp > 20 then hp = 20 end
player:set_hp(hp)
end
-- Determine and set physics settings
-- First get 'normal' settings to work with, if unaffected by any
-- health issues. The realms mod can override what is normal,
-- according to being located in a realm.
local ph_speed = 1
local ph_jump = 1
local ph_gravity = 1
if realms then
ph_speed, ph_jump, ph_gravity = realms.get_phys(player:getpos())
end
-- Now override normal (for the location) settings if necessary...
if health.get_attr(playername, "asleep") ~= 0 then
ph_speed = 0
ph_jump = 0
ph_gravity = 0
else
if frozen > 0 then
ph_speed = 0
elseif speed > 0 then
ph_speed = 3
end
if levitating > 0 then
local basepos = player:getpos()
local hh = 6
while hh > 0 do
basepos.y = basepos.y - 1
if minetest.get_node(basepos).name ~= "air" then
break
end
hh = hh - 1
end
if hh == 6 then
ph_gravity = -0.1
elseif hh == 5 then
ph_gravity = -0.05
elseif hh == 4 then
ph_gravity = 0
elseif hh == 3 then
ph_gravity = 0.05
else
ph_gravity = 0.1
end
ph_jump = 0
end
end
player:set_physics_override({
speed = ph_speed,
gravity = ph_gravity,
jump = ph_jump,
sneak = true,
sneak_glitch = true,
})
update_hud(player, hasair)
end
end
end)